Skip to content

Commit

Permalink
Catch JSONDecodeError on load/save cache files (#2059)
Browse files Browse the repository at this point in the history
  • Loading branch information
SukramJ authored Feb 5, 2025
1 parent d4d638f commit 104a62a
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 34 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Version 2025.2.2 (2025-02-03)

- Catch get_metadata XMLRPC fault
- Catch JSONDecodeError on load/save cache files
- Ignore devices with unknown battery
- Set battery to UNKNOWN for HmIP-PCBS-BAT
- Sort battery list for correct wildcard search
Expand Down
48 changes: 27 additions & 21 deletions hahomematic/caches/persistent.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,17 @@ async def save(self) -> DataOperationResult:
return DataOperationResult.NO_SAVE

def _perform_save() -> DataOperationResult:
with open(file=self._file_path, mode="wb") as file_pointer:
file_pointer.write(
orjson.dumps(
self._persistent_cache,
option=orjson.OPT_NON_STR_KEYS,
try:
with open(file=self._file_path, mode="wb") as file_pointer:
file_pointer.write(
orjson.dumps(
self._persistent_cache,
option=orjson.OPT_NON_STR_KEYS,
)
)
)
self.last_hash_saved = self.cache_hash
self.last_hash_saved = self.cache_hash
except json.JSONDecodeError:
return DataOperationResult.SAVE_FAIL
return DataOperationResult.SAVE_SUCCESS

async with self._save_load_semaphore:
Expand All @@ -114,12 +117,15 @@ async def load(self) -> DataOperationResult:

def _perform_load() -> DataOperationResult:
with open(file=self._file_path, encoding=UTF_8) as file_pointer:
data = json.loads(file_pointer.read(), object_hook=regular_to_default_dict_hook)
if (converted_hash := hash_sha256(value=data)) == self.last_hash_saved:
return DataOperationResult.NO_LOAD
self._persistent_cache.clear()
self._persistent_cache.update(data)
self.last_hash_saved = converted_hash
try:
data = json.loads(file_pointer.read(), object_hook=regular_to_default_dict_hook)
if (converted_hash := hash_sha256(value=data)) == self.last_hash_saved:
return DataOperationResult.NO_LOAD
self._persistent_cache.clear()
self._persistent_cache.update(data)
self.last_hash_saved = converted_hash
except json.JSONDecodeError:
return DataOperationResult.LOAD_FAIL
return DataOperationResult.LOAD_SUCCESS

async with self._save_load_semaphore:
Expand Down Expand Up @@ -248,12 +254,12 @@ async def load(self) -> DataOperationResult:
if not self._central.config.use_caches:
_LOGGER.debug("load: not caching paramset descriptions for %s", self._central.name)
return DataOperationResult.NO_LOAD
result = await super().load()
for (
interface_id,
device_descriptions,
) in self._raw_device_descriptions.items():
self._convert_device_descriptions(interface_id, device_descriptions)
if (result := await super().load()) == DataOperationResult.LOAD_SUCCESS:
for (
interface_id,
device_descriptions,
) in self._raw_device_descriptions.items():
self._convert_device_descriptions(interface_id, device_descriptions)
return result

async def clear(self) -> None:
Expand Down Expand Up @@ -384,8 +390,8 @@ async def load(self) -> DataOperationResult:
if not self._central.config.use_caches:
_LOGGER.debug("load: not caching device descriptions for %s", self._central.name)
return DataOperationResult.NO_LOAD
result = await super().load()
self._init_address_parameter_list()
if (result := await super().load()) == DataOperationResult.LOAD_SUCCESS:
self._init_address_parameter_list()
return result

async def save(self) -> DataOperationResult:
Expand Down
24 changes: 11 additions & 13 deletions hahomematic/central/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from typing import Any, Final, cast

from aiohttp import ClientSession
import orjson
import voluptuous as vol

from hahomematic import client as hmcl
Expand Down Expand Up @@ -59,6 +58,7 @@
TIMEOUT,
UN_IGNORE_WILDCARD,
BackendSystemEvent,
DataOperationResult,
DataPointCategory,
DataPointKey,
DescriptionMarker,
Expand Down Expand Up @@ -817,20 +817,18 @@ def has_clients(self) -> bool:
"""Check if clients exists in central."""
return len(self._clients) > 0

async def _load_caches(self) -> None:
async def _load_caches(self) -> bool:
"""Load files to caches."""
try:
await self._device_descriptions.load()
await self._paramset_descriptions.load()
await self._device_details.load()
await self._data_cache.load()
except orjson.JSONDecodeError as ex: # pragma: no cover
_LOGGER.warning(
"LOAD_CACHES failed: Unable to load caches for %s: %s",
self.name,
reduce_args(args=ex.args),
)
if DataOperationResult.LOAD_FAIL in (
await self._device_descriptions.load(),
await self._paramset_descriptions.load(),
):
_LOGGER.warning("LOAD_CACHES failed: Unable to load caches for %s. Clearing files", self.name)
await self.clear_caches()
return False
await self._device_details.load()
await self._data_cache.load()
return True

async def _create_devices(self, new_device_addresses: Mapping[str, set[str]]) -> None:
"""Trigger creation of the objects that expose the functionality."""
Expand Down

0 comments on commit 104a62a

Please sign in to comment.