22
33from __future__ import annotations
44
5+ import asyncio
56import json
67import logging
78import 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
0 commit comments