@@ -58,9 +58,6 @@ def __init__(
5858 self .new_devices_callbacks : list [Callable [[set [str ]], None ]] = []
5959 self .new_zones_callbacks : list [Callable [[str , set [str ]], None ]] = []
6060 self .new_areas_callbacks : list [Callable [[str , set [int ]], None ]] = []
61- self ._devices_last_update : set [str ] = set ()
62- self ._zones_last_update : dict [str , set [str ]] = {}
63- self ._areas_last_update : dict [str , set [int ]] = {}
6461
6562 @override
6663 @callback
@@ -87,11 +84,15 @@ def _on_data_update(self) -> None:
8784 """Handle data updates and process dynamic entity management."""
8885 if self .data is not None :
8986 self ._async_add_remove_devices ()
90- for mower_id in self .data :
91- if self .data [mower_id ].capabilities .stay_out_zones :
92- self ._async_add_remove_stay_out_zones ()
93- if self .data [mower_id ].capabilities .work_areas :
94- self ._async_add_remove_work_areas ()
87+ if any (
88+ mower_data .capabilities .stay_out_zones
89+ for mower_data in self .data .values ()
90+ ):
91+ self ._async_add_remove_stay_out_zones ()
92+ if any (
93+ mower_data .capabilities .work_areas for mower_data in self .data .values ()
94+ ):
95+ self ._async_add_remove_work_areas ()
9596
9697 @callback
9798 def handle_websocket_updates (self , ws_data : MowerDictionary ) -> None :
@@ -161,44 +162,36 @@ async def client_listen(
161162 )
162163
163164 def _async_add_remove_devices (self ) -> None :
164- """Add new device, remove non-existing device ."""
165+ """Add new devices and remove orphaned devices from the registry ."""
165166 current_devices = set (self .data )
167+ device_registry = dr .async_get (self .hass )
166168
167- # Skip update if no changes
168- if current_devices == self ._devices_last_update :
169- return
169+ registered_devices : set [str ] = {
170+ str (mower_id )
171+ for device in device_registry .devices .get_devices_for_config_entry_id (
172+ self .config_entry .entry_id
173+ )
174+ for domain , mower_id in device .identifiers
175+ if domain == DOMAIN
176+ }
170177
171- # Process removed devices
172- removed_devices = self ._devices_last_update - current_devices
173- if removed_devices :
174- _LOGGER .debug ("Removed devices: %s" , ", " .join (map (str , removed_devices )))
175- self ._remove_device (removed_devices )
178+ orphaned_devices = registered_devices - current_devices
179+ if orphaned_devices :
180+ _LOGGER .debug ("Removing orphaned devices: %s" , orphaned_devices )
181+ device_registry = dr .async_get (self .hass )
182+ for mower_id in orphaned_devices :
183+ dev = device_registry .async_get_device (identifiers = {(DOMAIN , mower_id )})
184+ if dev is not None :
185+ device_registry .async_update_device (
186+ device_id = dev .id ,
187+ remove_config_entry_id = self .config_entry .entry_id ,
188+ )
176189
177- # Process new device
178- new_devices = current_devices - self ._devices_last_update
190+ new_devices = current_devices - registered_devices
179191 if new_devices :
180- _LOGGER .debug ("New devices found: %s" , ", " .join (map (str , new_devices )))
181- self ._add_new_devices (new_devices )
182-
183- # Update device state
184- self ._devices_last_update = current_devices
185-
186- def _remove_device (self , removed_devices : set [str ]) -> None :
187- """Remove device from the registry."""
188- device_registry = dr .async_get (self .hass )
189- for mower_id in removed_devices :
190- if device := device_registry .async_get_device (
191- identifiers = {(DOMAIN , str (mower_id ))}
192- ):
193- device_registry .async_update_device (
194- device_id = device .id ,
195- remove_config_entry_id = self .config_entry .entry_id ,
196- )
197-
198- def _add_new_devices (self , new_devices : set [str ]) -> None :
199- """Add new device and trigger callbacks."""
200- for mower_callback in self .new_devices_callbacks :
201- mower_callback (new_devices )
192+ _LOGGER .debug ("New devices found: %s" , new_devices )
193+ for mower_callback in self .new_devices_callbacks :
194+ mower_callback (new_devices )
202195
203196 def _async_add_remove_stay_out_zones (self ) -> None :
204197 """Add new stay-out zones, remove non-existing stay-out zones."""
@@ -209,42 +202,39 @@ def _async_add_remove_stay_out_zones(self) -> None:
209202 and mower_data .stay_out_zones is not None
210203 }
211204
212- if not self ._zones_last_update :
213- self ._zones_last_update = current_zones
214- return
215-
216- if current_zones == self ._zones_last_update :
217- return
205+ entity_registry = er .async_get (self .hass )
206+ entries = er .async_entries_for_config_entry (
207+ entity_registry , self .config_entry .entry_id
208+ )
218209
219- self ._zones_last_update = self ._update_stay_out_zones (current_zones )
210+ registered_zones : dict [str , set [str ]] = {}
211+ for mower_id in self .data :
212+ registered_zones [mower_id ] = set ()
213+ for entry in entries :
214+ uid = entry .unique_id
215+ if uid .startswith (f"{ mower_id } _" ) and uid .endswith ("_stay_out_zones" ):
216+ zone_id = uid .removeprefix (f"{ mower_id } _" ).removesuffix (
217+ "_stay_out_zones"
218+ )
219+ registered_zones [mower_id ].add (zone_id )
220220
221- def _update_stay_out_zones (
222- self , current_zones : dict [str , set [str ]]
223- ) -> dict [str , set [str ]]:
224- """Update stay-out zones by adding and removing as needed."""
225- new_zones = {
226- mower_id : zones - self ._zones_last_update .get (mower_id , set ())
227- for mower_id , zones in current_zones .items ()
228- }
229- removed_zones = {
230- mower_id : self ._zones_last_update .get (mower_id , set ()) - zones
231- for mower_id , zones in current_zones .items ()
232- }
221+ for mower_id , current_ids in current_zones .items ():
222+ known_ids = registered_zones .get (mower_id , set ())
233223
234- for mower_id , zones in new_zones .items ():
235- for zone_callback in self .new_zones_callbacks :
236- zone_callback (mower_id , set (zones ))
224+ new_zones = current_ids - known_ids
225+ removed_zones = known_ids - current_ids
237226
238- entity_registry = er .async_get (self .hass )
239- for mower_id , zones in removed_zones .items ():
240- for entity_entry in er .async_entries_for_config_entry (
241- entity_registry , self .config_entry .entry_id
242- ):
243- for zone in zones :
244- if entity_entry .unique_id .startswith (f"{ mower_id } _{ zone } " ):
245- entity_registry .async_remove (entity_entry .entity_id )
227+ if new_zones :
228+ _LOGGER .debug ("New stay-out zones: %s" , new_zones )
229+ for zone_callback in self .new_zones_callbacks :
230+ zone_callback (mower_id , new_zones )
246231
247- return current_zones
232+ if removed_zones :
233+ _LOGGER .debug ("Removing stay-out zones: %s" , removed_zones )
234+ for entry in entries :
235+ for zone_id in removed_zones :
236+ if entry .unique_id == f"{ mower_id } _{ zone_id } _stay_out_zones" :
237+ entity_registry .async_remove (entry .entity_id )
248238
249239 def _async_add_remove_work_areas (self ) -> None :
250240 """Add new work areas, remove non-existing work areas."""
@@ -254,39 +244,36 @@ def _async_add_remove_work_areas(self) -> None:
254244 if mower_data .capabilities .work_areas and mower_data .work_areas is not None
255245 }
256246
257- if not self ._areas_last_update :
258- self ._areas_last_update = current_areas
259- return
260-
261- if current_areas == self ._areas_last_update :
262- return
263-
264- self ._areas_last_update = self ._update_work_areas (current_areas )
265-
266- def _update_work_areas (
267- self , current_areas : dict [str , set [int ]]
268- ) -> dict [str , set [int ]]:
269- """Update work areas by adding and removing as needed."""
270- new_areas = {
271- mower_id : areas - self ._areas_last_update .get (mower_id , set ())
272- for mower_id , areas in current_areas .items ()
273- }
274- removed_areas = {
275- mower_id : self ._areas_last_update .get (mower_id , set ()) - areas
276- for mower_id , areas in current_areas .items ()
277- }
278-
279- for mower_id , areas in new_areas .items ():
280- for area_callback in self .new_areas_callbacks :
281- area_callback (mower_id , set (areas ))
282-
283247 entity_registry = er .async_get (self .hass )
284- for mower_id , areas in removed_areas .items ():
285- for entity_entry in er .async_entries_for_config_entry (
286- entity_registry , self .config_entry .entry_id
287- ):
288- for area in areas :
289- if entity_entry .unique_id .startswith (f"{ mower_id } _{ area } _" ):
290- entity_registry .async_remove (entity_entry .entity_id )
248+ entries = er .async_entries_for_config_entry (
249+ entity_registry , self .config_entry .entry_id
250+ )
291251
292- return current_areas
252+ registered_areas : dict [str , set [int ]] = {}
253+ for mower_id in self .data :
254+ registered_areas [mower_id ] = set ()
255+ for entry in entries :
256+ uid = entry .unique_id
257+ if uid .startswith (f"{ mower_id } _" ) and uid .endswith ("_work_area" ):
258+ parts = uid .removeprefix (f"{ mower_id } _" ).split ("_" )
259+ area_id_str = parts [0 ] if parts else None
260+ if area_id_str and area_id_str .isdigit ():
261+ registered_areas [mower_id ].add (int (area_id_str ))
262+
263+ for mower_id , current_ids in current_areas .items ():
264+ known_ids = registered_areas .get (mower_id , set ())
265+
266+ new_areas = current_ids - known_ids
267+ removed_areas = known_ids - current_ids
268+
269+ if new_areas :
270+ _LOGGER .debug ("New work areas: %s" , new_areas )
271+ for area_callback in self .new_areas_callbacks :
272+ area_callback (mower_id , new_areas )
273+
274+ if removed_areas :
275+ _LOGGER .debug ("Removing work areas: %s" , removed_areas )
276+ for entry in entries :
277+ for area_id in removed_areas :
278+ if entry .unique_id .startswith (f"{ mower_id } _{ area_id } _" ):
279+ entity_registry .async_remove (entry .entity_id )
0 commit comments