Skip to content

Commit 168c60b

Browse files
authored
Clean up when removing a device from the network (#91)
1 parent 4d4ea91 commit 168c60b

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ def device_joined(
385385
"""Return a newly joined ZHAWS device."""
386386

387387
async def _zha_device(zigpy_dev: zigpy.device.Device) -> Device:
388+
zha_gateway.application_controller.devices[zigpy_dev.ieee] = zigpy_dev
388389
await zha_gateway.async_device_initialized(zigpy_dev)
389390
await zha_gateway.async_block_till_done()
390391

tests/test_gateway.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,59 @@ async def test_gateway_create_group_with_id_without_id(
305305
assert zha_group3.group_id == 0x0003
306306

307307

308+
async def test_remove_device_cleans_up_group_membership(
309+
zha_gateway: Gateway,
310+
device_light_1, # pylint: disable=redefined-outer-name
311+
device_light_2, # pylint: disable=redefined-outer-name
312+
coordinator, # pylint: disable=redefined-outer-name
313+
caplog: pytest.LogCaptureFixture,
314+
) -> None:
315+
"""Test group membership cleanup when removing a device in a group."""
316+
317+
zha_gateway.coordinator_zha_device = coordinator
318+
coordinator._zha_gateway = zha_gateway
319+
device_light_1._zha_gateway = zha_gateway
320+
device_light_2._zha_gateway = zha_gateway
321+
322+
member_ieee_addresses = [device_light_1.ieee, device_light_2.ieee]
323+
members = [
324+
GroupMemberReference(ieee=device_light_1.ieee, endpoint_id=1),
325+
GroupMemberReference(ieee=device_light_2.ieee, endpoint_id=1),
326+
]
327+
328+
# test creating a group with 2 members
329+
zha_group: Group = await zha_gateway.async_create_zigpy_group("Test Group", members)
330+
await zha_gateway.async_block_till_done()
331+
332+
assert zha_group is not None
333+
assert len(zha_group.members) == 2
334+
for member in zha_group.members:
335+
assert member.device.ieee in member_ieee_addresses
336+
assert member.group == zha_group
337+
assert member.endpoint is not None
338+
339+
await zha_gateway.async_remove_device(coordinator.ieee)
340+
await zha_gateway.async_block_till_done()
341+
assert len(zha_group.members) == 2
342+
assert (
343+
f"Removing the active coordinator ({str(coordinator.ieee)}) is not allowed"
344+
in caplog.text
345+
)
346+
347+
non_existent_ieee = zigpy.types.EUI64.convert("01:2d:6f:70:7a:40:79:e8")
348+
await zha_gateway.async_remove_device(non_existent_ieee)
349+
await zha_gateway.async_block_till_done()
350+
assert len(zha_group.members) == 2
351+
assert f"Device: {str(non_existent_ieee)} could not be found" in caplog.text
352+
353+
await zha_gateway.async_remove_device(device_light_1.ieee)
354+
await zha_gateway.async_block_till_done()
355+
356+
assert len(zha_group.members) == 1
357+
assert zha_group.members[0].device.ieee == device_light_2.ieee
358+
assert device_light_1.ieee not in zha_gateway.devices
359+
360+
308361
@patch(
309362
"zha.application.gateway.Gateway.load_devices",
310363
MagicMock(),

zha/application/gateway.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,21 @@ async def async_create_zigpy_group(
670670
await asyncio.gather(*tasks)
671671
return self.groups.get(group_id)
672672

673+
async def async_remove_device(self, ieee: EUI64) -> None:
674+
"""Remove a device from ZHA."""
675+
if not (device := self.devices.get(ieee)):
676+
_LOGGER.debug("Device: %s could not be found", ieee)
677+
return
678+
if device.is_active_coordinator:
679+
_LOGGER.info("Removing the active coordinator (%s) is not allowed", ieee)
680+
return
681+
for group_id, group in self.groups.items():
682+
for member_ieee_endpoint_id in list(group.zigpy_group.members.keys()):
683+
if member_ieee_endpoint_id[0] == ieee:
684+
await device.async_remove_from_group(group_id)
685+
686+
await self.application_controller.remove(ieee)
687+
673688
async def async_remove_zigpy_group(self, group_id: int) -> None:
674689
"""Remove a Zigbee group from Zigpy."""
675690
if not (group := self.groups.get(group_id)):

0 commit comments

Comments
 (0)