Skip to content

Commit 85b517e

Browse files
Improve library loading (#4188)
1 parent 085fea6 commit 85b517e

3 files changed

Lines changed: 50 additions & 7 deletions

File tree

custom_components/battery_notes/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,10 @@ async def async_setup_entry(
190190
async def _async_delayed_discovery(now: datetime) -> None: # noqa: ARG001
191191
"""Update the library and do discovery."""
192192
library_updater = LibraryUpdater(hass)
193+
193194
await library_updater.copy_schema()
194195
await library_updater.get_library_updates(startup=True)
196+
await hass.data[DATA_LIBRARY].load_libraries()
195197

196198
if domain_config.enable_autodiscovery:
197199
await discovery_manager.start_discovery()

custom_components/battery_notes/library.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import asyncio
56
import json
67
import logging
78
import os
@@ -64,15 +65,32 @@ class Library: # pylint: disable=too-few-public-methods
6465
def __init__(self, hass: HomeAssistant) -> None:
6566
"""Init."""
6667
self.hass = hass
68+
self._load_lock = asyncio.Lock()
69+
self._is_loading = False
6770

6871
async def load_libraries(self):
6972
"""Load the user and default libraries."""
73+
async with self._load_lock:
74+
if self._is_loading:
75+
_LOGGER.debug("Library already loading, skipping duplicate load")
76+
return
77+
78+
self._is_loading = True
79+
try:
80+
await self._do_load_libraries()
81+
finally:
82+
self._is_loading = False
83+
84+
async def _do_load_libraries(self):
85+
"""Load libraries internally (must be called with lock held)."""
7086

7187
def _load_library_json(library_file: str) -> dict[str, Any]:
7288
"""Load library json file."""
7389
with open(library_file, encoding="utf-8") as file:
7490
return cast(dict[str, Any], json.load(file))
7591

92+
new_manufacturer_devices: dict[str, list[LibraryDevice]] = {}
93+
7694
# User Library
7795
domain_config = self.hass.data.get(MY_KEY)
7896
if domain_config and domain_config.user_library != "":
@@ -89,9 +107,9 @@ def _load_library_json(library_file: str) -> dict[str, Any]:
89107
for json_device in user_json_data["devices"]:
90108
library_device = LibraryDevice.from_json(json_device)
91109
manufacturer = library_device.manufacturer.casefold()
92-
if manufacturer not in self._manufacturer_devices:
93-
self._manufacturer_devices[manufacturer] = []
94-
self._manufacturer_devices[manufacturer].append(library_device)
110+
if manufacturer not in new_manufacturer_devices:
111+
new_manufacturer_devices[manufacturer] = []
112+
new_manufacturer_devices[manufacturer].append(library_device)
95113
_LOGGER.debug("Loaded %s user devices", len(user_json_data["devices"]))
96114

97115
except FileNotFoundError:
@@ -115,6 +133,12 @@ def _load_library_json(library_file: str) -> dict[str, Any]:
115133
"User library file not found at %s",
116134
json_user_path,
117135
)
136+
except (json.JSONDecodeError, KeyError, ValueError) as err:
137+
_LOGGER.error(
138+
"Failed to parse user library file at %s: %s",
139+
json_user_path,
140+
err,
141+
)
118142

119143
# Default Library
120144
json_default_path = self.hass.config.path(
@@ -130,18 +154,26 @@ def _load_library_json(library_file: str) -> dict[str, Any]:
130154
for json_device in default_json_data["devices"]:
131155
library_device = LibraryDevice.from_json(json_device)
132156
manufacturer = library_device.manufacturer.casefold()
133-
if manufacturer not in self._manufacturer_devices:
134-
self._manufacturer_devices[manufacturer] = []
135-
self._manufacturer_devices[manufacturer].append(library_device)
157+
if manufacturer not in new_manufacturer_devices:
158+
new_manufacturer_devices[manufacturer] = []
159+
new_manufacturer_devices[manufacturer].append(library_device)
136160
_LOGGER.debug(
137161
"Loaded %s default devices", len(default_json_data[LIBRARY_DEVICES])
138162
)
139163

164+
self._manufacturer_devices = new_manufacturer_devices
165+
140166
except FileNotFoundError:
141167
_LOGGER.error(
142168
"library.json file not found at %s",
143169
json_default_path,
144170
)
171+
except (json.JSONDecodeError, KeyError, ValueError) as err:
172+
_LOGGER.error(
173+
"Failed to parse library file at %s: %s",
174+
json_default_path,
175+
err,
176+
)
145177

146178
async def get_device_battery_details(
147179
self,
@@ -159,6 +191,7 @@ async def get_device_battery_details(
159191
# device_to_find = ModelInfo("Philips", "Hue dimmer switch (929002398602)", None, None)
160192
# device_to_find = ModelInfo("Philips", "Hue dimmer switch", "929002398602", None)
161193
# device_to_find = ModelInfo("Philips", "Hue dimmer switch", "929002398602", "1")
194+
# device_to_find = ModelInfo("LUMI", "lumi.sensor_magnet.aq2", None, None)
162195

163196
# Get all devices matching manufacturer & model
164197
matching_devices = None
@@ -215,7 +248,8 @@ async def get_device_battery_details(
215248
@property
216249
def is_loaded(self) -> bool:
217250
"""Library loaded successfully."""
218-
return bool(self._manufacturer_devices)
251+
252+
return bool(self._manufacturer_devices) and not self._is_loading
219253

220254
def device_basic_match(
221255
self, library_device: LibraryDevice, device_to_find: ModelInfo

custom_components/battery_notes/library_updater.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import asyncio
56
import json
67
import logging
78
import os
@@ -52,6 +53,7 @@ class LibraryUpdater:
5253
def __init__(self, hass: HomeAssistant):
5354
"""Initialize the library updater."""
5455
self.hass = hass
56+
self._update_lock = asyncio.Lock()
5557

5658
domain_config = self.hass.data.get(MY_KEY)
5759
if not domain_config:
@@ -91,6 +93,11 @@ async def timer_update(self, now: datetime) -> None: # noqa: ARG002
9193
@callback
9294
async def get_library_updates(self, startup: bool = False) -> None:
9395
"""Make a call to get the latest library.json."""
96+
async with self._update_lock:
97+
await self._do_get_library_updates(startup)
98+
99+
async def _do_get_library_updates(self, startup: bool = False) -> None:
100+
"""Get library updates internally (must be called with lock held)."""
94101

95102
def _update_library_json(library_file: str, content: str) -> None:
96103
os.makedirs(os.path.dirname(library_file), exist_ok=True)

0 commit comments

Comments
 (0)