4848)
4949from pyalarmdotcomajax .websockets .client import WebSocketClient , WebSocketState
5050
51- __version__ = "0.5.8 "
51+ __version__ = "0.5.9 "
5252
5353log = logging .getLogger (__name__ )
5454
@@ -98,6 +98,8 @@ class AlarmController:
9898 KEEP_ALIVE_SIGNAL_INTERVAL_S = 60
9999 SESSION_REFRESH_DEFAULT_INTERVAL_MS = 780000 # 13 minutes. Sessions expire at 15.
100100
101+ SCENE_REFRESH_INTERVAL_M = 60
102+
101103 # LOGIN & SESSION: END
102104
103105 def __init__ (
@@ -161,6 +163,13 @@ def __init__(
161163 self ._last_session_refresh : datetime = datetime .now ()
162164 self ._session_timer : SessionTimer | None = None
163165
166+ #
167+ # SCENE REFRESH ATTRIBUTES
168+ #
169+
170+ self ._last_scene_update : datetime | None = None
171+ self ._scene_object_cache : list [dict ] = []
172+
164173 #
165174 # CLI ATTRIBUTES
166175 #
@@ -230,7 +239,10 @@ async def async_update(self) -> None:
230239
231240 if not self ._active_system_id :
232241 self ._active_system_id = await self ._async_get_active_system ()
233- has_image_sensors = await self ._async_has_image_sensors (self ._active_system_id )
242+ has_image_sensors = await self ._async_device_type_present (
243+ self ._active_system_id , DeviceType .IMAGE_SENSOR
244+ )
245+ has_scenes = await self ._async_device_type_present (self ._active_system_id , DeviceType .SCENE )
234246
235247 await self ._async_get_trouble_conditions ()
236248
@@ -248,6 +260,21 @@ async def async_update(self) -> None:
248260
249261 extension_results = await self ._async_update__query_multi_device_extensions (raw_devices )
250262
263+ #
264+ # QUERY SCENES
265+ #
266+ # Scenes have no state, so we only need to update for new/deleted scenes. We refresh less frequently than we do for stateful devices to save time.
267+
268+ if has_scenes :
269+ # Refresh scene cache if stale.
270+ if not self ._last_scene_update or (
271+ datetime .now () > self ._last_scene_update + timedelta (minutes = self .SCENE_REFRESH_INTERVAL_M )
272+ ):
273+ self ._scene_object_cache = await self ._async_get_devices_by_device_type (DeviceType .SCENE )
274+ self ._last_scene_update = datetime .now ()
275+
276+ raw_devices .extend (self ._scene_object_cache )
277+
251278 #
252279 # QUERY IMAGE SENSORS
253280 #
@@ -259,8 +286,7 @@ async def async_update(self) -> None:
259286
260287 if has_image_sensors :
261288 # Get detailed image sensor data and add to raw device list.
262- image_sensors = await self ._async_get_devices_by_device_type (DeviceType .IMAGE_SENSOR )
263- raw_devices .extend (image_sensors )
289+ raw_devices .extend (await self ._async_get_devices_by_device_type (DeviceType .IMAGE_SENSOR ))
264290
265291 # Get recent images
266292 device_type_specific_data = await self ._async_get_recent_images ()
@@ -276,7 +302,7 @@ async def async_update(self) -> None:
276302 for partition_raw in raw_devices
277303 if partition_raw ["type" ] == AttributeRegistry .get_relationship_id_from_devicetype (DeviceType .PARTITION )
278304 ]:
279- partition_instance : AllDevices_t = await self ._async_update__build_device (
305+ partition_instance : AllDevices_t = await self ._async_update__build_hardware_device (
280306 partition_raw , device_type_specific_data , extension_results
281307 )
282308
@@ -287,13 +313,15 @@ async def async_update(self) -> None:
287313
288314 raw_devices .remove (partition_raw )
289315
316+ # device_type: DeviceType = AttributeRegistry.get_devicetype_from_relationship_id(raw_device["type"])
317+
290318 #
291319 # BUILD DEVICES
292320 #
293321
294322 for device_raw in raw_devices :
295323 try :
296- device_instance : AllDevices_t = await self ._async_update__build_device (
324+ device_instance : AllDevices_t = await self ._async_update__build_hardware_device (
297325 device_raw , device_type_specific_data , extension_results
298326 )
299327
@@ -785,7 +813,7 @@ async def _reload_session_context(self) -> None:
785813
786814 self ._last_session_refresh = datetime .now ()
787815
788- async def _async_update__build_device (
816+ async def _async_update__build_hardware_device (
789817 self ,
790818 raw_device : dict ,
791819 device_type_specific_data : dict [str , DeviceTypeSpecificData ],
@@ -819,7 +847,7 @@ async def _async_update__build_device(
819847 children .append ((sub_device ["id" ], DeviceType (family_name )))
820848
821849 #
822- # BUILD DEVICE INSTANCE
850+ # BUILD HARDWARE DEVICE INSTANCE
823851 #
824852
825853 entity_id = raw_device ["id" ]
@@ -831,8 +859,8 @@ async def _async_update__build_device(
831859 raw_device_data = raw_device ,
832860 user_profile = self ._user_profile ,
833861 children = children ,
834- device_type_specific_data = device_type_specific_data .get (entity_id ),
835862 send_action_callback = self .async_send_command ,
863+ device_type_specific_data = device_type_specific_data .get (entity_id ),
836864 config_change_callback = (
837865 extension_controller .submit_change
838866 if (extension_controller := device_extension_results .get ("controller" ))
@@ -979,18 +1007,20 @@ async def _async_get_recent_images(self) -> dict[str, DeviceTypeSpecificData]:
9791007 else :
9801008 return device_type_specific_data
9811009
982- async def _async_has_image_sensors (self , system_id : str , retry_on_failure : bool = True ) -> bool :
983- """Check whether image sensors are present in system.
1010+ async def _async_device_type_present (
1011+ self , system_id : str , device_type : DeviceType , retry_on_failure : bool = True
1012+ ) -> bool :
1013+ """Check whether a specific device type is present in system.
9841014
985- Check is required because image sensors are not shown in the device catalog endpoint.
1015+ Check is required because some devices are not shown in the device catalog endpoint.
9861016 """
9871017
9881018 # TODO: Needs changes to support multi-system environments
9891019
9901020 try :
991- log .info (f"Checking system { system_id } for image sensors ." )
1021+ log .info (f"Checking system { system_id } for { device_type } s ." )
9921022
993- # Find image sensors .
1023+ # Find devices .
9941024
9951025 async with self ._websession .get (
9961026 url = AttributeRegistry .get_endpoints (DeviceType .SYSTEM )["primary" ].format (c .URL_BASE , system_id ),
@@ -1000,14 +1030,16 @@ async def _async_has_image_sensors(self, system_id: str, retry_on_failure: bool
10001030
10011031 await self ._async_handle_server_errors (json_rsp , "image sensors" , retry_on_failure )
10021032
1003- return len (json_rsp ["data" ].get ("relationships" , {}).get ("imageSensors" , {}).get ("data" , [])) > 0
1033+ device_type_id = AttributeRegistry .get_type_id_from_devicetype (device_type )
1034+
1035+ return len (json_rsp ["data" ].get ("relationships" , {}).get (device_type_id , {}).get ("data" , [])) > 0
10041036
10051037 except (aiohttp .ClientResponseError , KeyError ) as err :
1006- log .exception ("Failed to get image sensors ." )
1038+ log .exception (f "Failed to get { device_type } s ." )
10071039 raise UnexpectedResponse from err
10081040 except TryAgain :
10091041 if retry_on_failure :
1010- return await self ._async_has_image_sensors (system_id , retry_on_failure = False )
1042+ return await self ._async_device_type_present (system_id , device_type , retry_on_failure = False )
10111043
10121044 raise
10131045
0 commit comments