Skip to content

Commit

Permalink
Fix device availability updating entities and enhance tests (#152)
Browse files Browse the repository at this point in the history
* Fix device availability updating entities and enhance tests

* use emit to ensure the event is actually fired
  • Loading branch information
dmulcahey authored Aug 9, 2024
1 parent 5bfeb62 commit 193425c
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 14 deletions.
56 changes: 47 additions & 9 deletions tests/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ def _update_last_seen(*args, **kwargs): # pylint: disable=unused-argument

basic_ch.read_attributes.side_effect = _update_last_seen

for entity in zha_device.platform_entities.values():
entity.emit = mock.MagicMock(wraps=entity.emit)

# we want to test the device availability handling alone
zha_gateway.global_updater.stop()

# successfully ping zigpy device, but zha_device is not yet available
await _send_time_changed(
zha_gateway, zha_gateway._device_availability_checker.__polling_interval + 1
Expand All @@ -170,6 +176,11 @@ def _update_last_seen(*args, **kwargs): # pylint: disable=unused-argument
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is False

for entity in zha_device.platform_entities.values():
entity.emit.assert_not_called()
assert not entity.available
entity.emit.reset_mock()

# There was traffic from the device: pings, but not yet available
await _send_time_changed(
zha_gateway, zha_gateway._device_availability_checker.__polling_interval + 1
Expand All @@ -178,6 +189,11 @@ def _update_last_seen(*args, **kwargs): # pylint: disable=unused-argument
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is False

for entity in zha_device.platform_entities.values():
entity.emit.assert_not_called()
assert not entity.available
entity.emit.reset_mock()

# There was traffic from the device: don't try to ping, marked as available
await _send_time_changed(
zha_gateway, zha_gateway._device_availability_checker.__polling_interval + 1
Expand All @@ -187,23 +203,24 @@ def _update_last_seen(*args, **kwargs): # pylint: disable=unused-argument
assert zha_device.available is True
assert zha_device.on_network is True

for entity in zha_device.platform_entities.values():
entity.emit.assert_called()
assert entity.available
entity.emit.reset_mock()

assert "Device is not on the network, marking unavailable" not in caplog.text
zha_device.on_network = False

assert zha_device.available is False
assert zha_device.on_network is False

sleep_time = max(
zha_gateway.global_updater.__polling_interval,
zha_gateway._device_availability_checker.__polling_interval,
)
sleep_time += 2

await asyncio.sleep(sleep_time)
await zha_gateway.async_block_till_done(wait_background_tasks=True)

assert "Device is not on the network, marking unavailable" in caplog.text

for entity in zha_device.platform_entities.values():
entity.emit.assert_called()
assert not entity.available
entity.emit.reset_mock()


@patch(
"zha.zigbee.cluster_handlers.general.BasicClusterHandler.async_initialize",
Expand All @@ -227,6 +244,12 @@ async def test_check_available_unsuccessful(
time.time() - zha_device.consider_unavailable_time - 2
)

for entity in zha_device.platform_entities.values():
entity.emit = mock.MagicMock(wraps=entity.emit)

# we want to test the device availability handling alone
zha_gateway.global_updater.stop()

# unsuccessfully ping zigpy device, but zha_device is still available
await _send_time_changed(
zha_gateway, zha_gateway._device_availability_checker.__polling_interval + 1
Expand All @@ -236,6 +259,11 @@ async def test_check_available_unsuccessful(
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is True

for entity in zha_device.platform_entities.values():
entity.emit.assert_not_called()
assert entity.available
entity.emit.reset_mock()

# still no traffic, but zha_device is still available
await _send_time_changed(
zha_gateway, zha_gateway._device_availability_checker.__polling_interval + 1
Expand All @@ -245,6 +273,11 @@ async def test_check_available_unsuccessful(
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is True

for entity in zha_device.platform_entities.values():
entity.emit.assert_not_called()
assert entity.available
entity.emit.reset_mock()

# not even trying to update, device is unavailable
await _send_time_changed(
zha_gateway, zha_gateway._device_availability_checker.__polling_interval + 1
Expand All @@ -254,6 +287,11 @@ async def test_check_available_unsuccessful(
assert basic_ch.read_attributes.await_args[0][0] == ["manufacturer"]
assert zha_device.available is False

for entity in zha_device.platform_entities.values():
entity.emit.assert_called()
assert not entity.available
entity.emit.reset_mock()


@patch(
"zha.zigbee.cluster_handlers.general.BasicClusterHandler.async_initialize",
Expand Down
10 changes: 5 additions & 5 deletions zha/zigbee/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,10 @@ def on_network(self):
@on_network.setter
def on_network(self, new_on_network: bool) -> None:
"""Set device on_network flag."""
self._on_network = new_on_network
self.update_available(new_on_network)
self._on_network = new_on_network
if not new_on_network:
self.debug("Device is not on the network, marking unavailable")

@property
def power_configuration_ch(self) -> ClusterHandler | None:
Expand Down Expand Up @@ -510,10 +512,6 @@ async def _check_available(self, *_: Any) -> None:
# don't flip the availability state of the coordinator
if self.is_active_coordinator:
return
if not self._on_network:
self.debug("Device is not on the network, marking unavailable")
self.update_available(False)
return
if self.last_seen is None:
self.debug("last_seen is None, marking the device unavailable")
self.update_available(False)
Expand Down Expand Up @@ -586,6 +584,8 @@ def update_available(self, available: bool) -> None:
return
if availability_changed and not available:
self.debug("Device availability changed and device became unavailable")
for entity in self.platform_entities.values():
entity.maybe_emit_state_changed_event()
self.emit_zha_event(
{
"device_event_type": "device_offline",
Expand Down

0 comments on commit 193425c

Please sign in to comment.