Restore state for ZHA OnOff binary sensors (#90749)

* Restore state for ZHA OnOff binary sensors

* Let `Motion` extend `Opening`

`Motion` is just a specified version of `Opening` that only changes the device class for some motion sensors.
Since we have more "special code" in the OnOff/Opening sensor now, we also want to make sure that gets applied to `Motion` binary sensors.

* Improve comment and type

* Add test to verify that binary sensors restore last HA state
This commit is contained in:
TheJulianJES 2023-04-04 04:27:57 +02:00 committed by GitHub
parent 59511cc3f7
commit a58b3721ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 4 deletions

View file

@ -4,6 +4,8 @@ from __future__ import annotations
import functools
from typing import Any
import zigpy.types as t
from zigpy.zcl.clusters.general import OnOff
from zigpy.zcl.clusters.security import IasZone
from homeassistant.components.binary_sensor import (
@ -119,11 +121,21 @@ class Occupancy(BinarySensor):
@STRICT_MATCH(channel_names=CHANNEL_ON_OFF)
class Opening(BinarySensor):
"""ZHA BinarySensor."""
"""ZHA OnOff BinarySensor."""
SENSOR_ATTR = "on_off"
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.OPENING
# Client/out cluster attributes aren't stored in the zigpy database, but are properly stored in the runtime cache.
# We need to manually restore the last state from the sensor state to the runtime cache for now.
@callback
def async_restore_last_state(self, last_state):
"""Restore previous state to zigpy cache."""
self._channel.cluster.update_attribute(
OnOff.attributes_by_name[self.SENSOR_ATTR].id,
t.Bool.true if last_state.state == STATE_ON else t.Bool.false,
)
@MULTI_MATCH(channel_names=CHANNEL_BINARY_INPUT)
class BinaryInput(BinarySensor):
@ -144,10 +156,9 @@ class BinaryInput(BinarySensor):
manufacturers="Philips",
models={"SML001", "SML002"},
)
class Motion(BinarySensor):
"""ZHA BinarySensor."""
class Motion(Opening):
"""ZHA OnOff BinarySensor with motion device class."""
SENSOR_ATTR = "on_off"
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.MOTION

View file

@ -3,6 +3,7 @@ from unittest.mock import patch
import pytest
import zigpy.profiles.zha
import zigpy.zcl.clusters.general as general
import zigpy.zcl.clusters.measurement as measurement
import zigpy.zcl.clusters.security as security
@ -40,6 +41,16 @@ DEVICE_OCCUPANCY = {
}
DEVICE_ONOFF = {
1: {
SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SENSOR,
SIG_EP_INPUT: [],
SIG_EP_OUTPUT: [general.OnOff.cluster_id],
}
}
@pytest.fixture(autouse=True)
def binary_sensor_platform_only():
"""Only set up the binary_sensor and required base platforms to speed up tests."""
@ -212,3 +223,30 @@ async def test_binary_sensor_migration_already_migrated(
assert entity_id is not None
assert hass.states.get(entity_id).state == STATE_ON # matches attribute cache
assert hass.states.get(entity_id).attributes["migrated_to_cache"]
@pytest.mark.parametrize(
"restored_state",
[
STATE_ON,
STATE_OFF,
],
)
async def test_onoff_binary_sensor_restore_state(
hass: HomeAssistant,
zigpy_device_mock,
core_rs,
zha_device_restored,
restored_state,
) -> None:
"""Test ZHA OnOff binary_sensor restores last state from HA."""
entity_id = "binary_sensor.fakemanufacturer_fakemodel_opening"
core_rs(entity_id, state=restored_state, attributes={})
zigpy_device = zigpy_device_mock(DEVICE_ONOFF)
zha_device = await zha_device_restored(zigpy_device)
entity_id = await find_entity_id(Platform.BINARY_SENSOR, zha_device, hass)
assert entity_id is not None
assert hass.states.get(entity_id).state == restored_state