Skip to content
Snippets Groups Projects
Unverified Commit a58b3721 authored by TheJulianJES's avatar TheJulianJES Committed by GitHub
Browse files

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
parent 59511cc3
No related branches found
No related tags found
No related merge requests found
...@@ -4,6 +4,8 @@ from __future__ import annotations ...@@ -4,6 +4,8 @@ from __future__ import annotations
import functools import functools
from typing import Any from typing import Any
import zigpy.types as t
from zigpy.zcl.clusters.general import OnOff
from zigpy.zcl.clusters.security import IasZone from zigpy.zcl.clusters.security import IasZone
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
...@@ -119,11 +121,21 @@ class Occupancy(BinarySensor): ...@@ -119,11 +121,21 @@ class Occupancy(BinarySensor):
@STRICT_MATCH(channel_names=CHANNEL_ON_OFF) @STRICT_MATCH(channel_names=CHANNEL_ON_OFF)
class Opening(BinarySensor): class Opening(BinarySensor):
"""ZHA BinarySensor.""" """ZHA OnOff BinarySensor."""
SENSOR_ATTR = "on_off" SENSOR_ATTR = "on_off"
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.OPENING _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) @MULTI_MATCH(channel_names=CHANNEL_BINARY_INPUT)
class BinaryInput(BinarySensor): class BinaryInput(BinarySensor):
...@@ -144,10 +156,9 @@ class BinaryInput(BinarySensor): ...@@ -144,10 +156,9 @@ class BinaryInput(BinarySensor):
manufacturers="Philips", manufacturers="Philips",
models={"SML001", "SML002"}, models={"SML001", "SML002"},
) )
class Motion(BinarySensor): class Motion(Opening):
"""ZHA BinarySensor.""" """ZHA OnOff BinarySensor with motion device class."""
SENSOR_ATTR = "on_off"
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.MOTION _attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.MOTION
......
...@@ -3,6 +3,7 @@ from unittest.mock import patch ...@@ -3,6 +3,7 @@ from unittest.mock import patch
import pytest import pytest
import zigpy.profiles.zha import zigpy.profiles.zha
import zigpy.zcl.clusters.general as general
import zigpy.zcl.clusters.measurement as measurement import zigpy.zcl.clusters.measurement as measurement
import zigpy.zcl.clusters.security as security import zigpy.zcl.clusters.security as security
...@@ -40,6 +41,16 @@ DEVICE_OCCUPANCY = { ...@@ -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) @pytest.fixture(autouse=True)
def binary_sensor_platform_only(): def binary_sensor_platform_only():
"""Only set up the binary_sensor and required base platforms to speed up tests.""" """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( ...@@ -212,3 +223,30 @@ async def test_binary_sensor_migration_already_migrated(
assert entity_id is not None assert entity_id is not None
assert hass.states.get(entity_id).state == STATE_ON # matches attribute cache assert hass.states.get(entity_id).state == STATE_ON # matches attribute cache
assert hass.states.get(entity_id).attributes["migrated_to_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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment