diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 141a3c8b..027d3c1d 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1154,9 +1154,7 @@ async def test_last_feeding_size_sensor_v2( @pytest.mark.looptime -async def test_device_counter_sensors( - zha_gateway: Gateway, caplog: pytest.LogCaptureFixture -) -> None: +async def test_device_counter_sensors(zha_gateway: Gateway) -> None: """Test quirks defined sensor.""" coordinator = zha_gateway.coordinator_zha_device @@ -1179,11 +1177,36 @@ async def test_device_counter_sensors( assert entity.state["state"] == 2 - coordinator.available = False - await asyncio.sleep(120) + +@pytest.mark.looptime +async def test_device_unavailable_skips_entity_polling( + zha_gateway: Gateway, + elec_measurement_zha_dev: Device, # pylint: disable=redefined-outer-name + caplog: pytest.LogCaptureFixture, +) -> None: + """Test quirks defined sensor.""" + + assert not elec_measurement_zha_dev.is_coordinator + assert not elec_measurement_zha_dev.is_active_coordinator + entity_id = "sensor.fakemanufacturer_fakemodel_e769900a_basic_rssi" + entity = get_entity(elec_measurement_zha_dev, entity_id) + assert entity is not None + + assert entity.state["state"] is None + + # simulate counter increment on application + elec_measurement_zha_dev.device.rssi = 60 + + await asyncio.sleep(zha_gateway.global_updater.__polling_interval + 2) + await zha_gateway.async_block_till_done(wait_background_tasks=True) + + assert entity.state["state"] == 60 + + elec_measurement_zha_dev.on_network = False + await asyncio.sleep(zha_gateway.global_updater.__polling_interval * 2) await zha_gateway.async_block_till_done(wait_background_tasks=True) assert ( - "counter_1: skipping polling for updated state, available: False, allow polled requests: True" - in caplog.text + "00:0d:6f:00:0a:90:69:e7-1-0-rssi: skipping polling for updated state, " + "available: False, allow polled requests: True" in caplog.text ) diff --git a/zha/zigbee/device.py b/zha/zigbee/device.py index fddff71f..0b0e3344 100644 --- a/zha/zigbee/device.py +++ b/zha/zigbee/device.py @@ -237,7 +237,7 @@ def __init__( CONF_CONSIDER_UNAVAILABLE_BATTERY, CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY, ) - self._available: bool = self.is_coordinator or ( + self._available: bool = self.is_active_coordinator or ( self.last_seen is not None and time.time() - self.last_seen < self.consider_unavailable_time ) @@ -370,14 +370,14 @@ def is_end_device(self) -> bool | None: @property def is_groupable(self) -> bool: """Return true if this device has a group cluster.""" - return self.is_coordinator or ( + return self.is_active_coordinator or ( self.available and bool(self.async_get_groupable_endpoints()) ) @cached_property def skip_configuration(self) -> bool: """Return true if the device should not issue configuration related commands.""" - return self._zigpy_device.skip_configuration or bool(self.is_coordinator) + return self._zigpy_device.skip_configuration or bool(self.is_active_coordinator) @cached_property def gateway(self): @@ -401,7 +401,7 @@ def device_automation_triggers(self) -> dict[tuple[str, str], dict[str, str]]: @property def available(self): """Return True if device is available.""" - return self._available and self.on_network + return self.is_active_coordinator or (self._available and self.on_network) @available.setter def available(self, new_availability: bool) -> None: @@ -521,7 +521,7 @@ def async_update_sw_build_id(self, sw_version: int) -> None: async def _check_available(self, *_: Any) -> None: # don't flip the availability state of the coordinator - if self.is_coordinator: + if self.is_active_coordinator: return if not self._on_network: self.debug("Device is not on the network, marking unavailable")