Add Matter discovery schemas for BooleanState sensors (#117870)

Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Marcel van der Veldt <m.vanderveldt@outlook.com>
This commit is contained in:
Ludovic BOUÉ 2024-06-21 18:57:18 +02:00 committed by GitHub
parent cb563f25fa
commit 2ad5b1c3a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 280 additions and 34 deletions

View file

@ -7,6 +7,7 @@ from dataclasses import dataclass
from chip.clusters import Objects as clusters
from chip.clusters.Objects import uint
from chip.clusters.Types import Nullable, NullValue
from matter_server.client.models import device_types
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
@ -73,17 +74,6 @@ DISCOVERY_SCHEMAS = [
vendor_id=(4107,),
product_name=("Hue motion sensor",),
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="ContactSensor",
device_class=BinarySensorDeviceClass.DOOR,
# value is inverted on matter to what we expect
measurement_to_ha=lambda x: not x,
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.BooleanState.Attributes.StateValue,),
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
@ -109,4 +99,50 @@ DISCOVERY_SCHEMAS = [
# only add binary battery sensor if a regular percentage based is not available
absent_attributes=(clusters.PowerSource.Attributes.BatPercentRemaining,),
),
# BooleanState sensors (tied to device type)
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="ContactSensor",
device_class=BinarySensorDeviceClass.DOOR,
# value is inverted on matter to what we expect
measurement_to_ha=lambda x: not x,
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.BooleanState.Attributes.StateValue,),
device_type=(device_types.ContactSensor,),
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="WaterLeakDetector",
translation_key="water_leak",
device_class=BinarySensorDeviceClass.MOISTURE,
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.BooleanState.Attributes.StateValue,),
device_type=(device_types.WaterLeakDetector,),
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="WaterFreezeDetector",
translation_key="water_freeze",
device_class=BinarySensorDeviceClass.COLD,
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.BooleanState.Attributes.StateValue,),
device_type=(device_types.WaterFreezeDetector,),
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="RainSensor",
translation_key="rain",
device_class=BinarySensorDeviceClass.MOISTURE,
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.BooleanState.Attributes.StateValue,),
device_type=(device_types.RainSensor,),
),
]

View file

@ -45,6 +45,17 @@
}
},
"entity": {
"binary_sensor": {
"water_leak": {
"name": "Water leak"
},
"water_freeze": {
"name": "Water freeze"
},
"rain": {
"name": "Rain"
}
},
"climate": {
"thermostat": {
"name": "Thermostat"

View file

@ -0,0 +1,185 @@
{
"node_id": 32,
"date_commissioned": "2024-06-21T14:13:02.370603",
"last_interview": "2024-06-21T14:14:49.941142",
"interview_version": 6,
"available": true,
"is_bridge": true,
"attributes": {
"0/29/0": [
{
"0": 22,
"1": 1
}
],
"0/29/1": [29, 31, 40, 43, 44, 48, 49, 50, 51, 52, 56, 60, 62, 63],
"0/29/2": [31],
"0/29/3": [1, 2],
"0/29/65528": [],
"0/29/65529": [],
"0/29/65530": [],
"0/29/65531": [0, 1, 2, 3, 65528, 65529, 65530, 65531, 65532, 65533],
"0/29/65532": 0,
"0/29/65533": 1,
"0/31/65528": [],
"0/31/65529": [],
"0/31/65530": [],
"0/31/65531": [0, 2, 3, 4, 65528, 65529, 65530, 65531, 65532, 65533],
"0/31/65532": 0,
"0/31/65533": 1,
"0/40/0": 1,
"0/40/1": "Mock",
"0/40/2": 65521,
"0/40/3": "Water Leak Detector",
"0/40/4": 32768,
"0/40/5": "Water Leak Detector",
"0/40/6": "",
"0/40/7": 0,
"0/40/8": "",
"0/40/9": 234946562,
"0/40/10": "14.1.0.2",
"0/40/15": "",
"0/40/17": true,
"0/40/18": "",
"0/40/19": {
"0": 3,
"1": 3
},
"0/40/65528": [],
"0/40/65529": [],
"0/40/65530": [],
"0/40/65531": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 17, 18, 19, 65528, 65529, 65530,
65531, 65532, 65533
],
"0/40/65532": 0,
"0/40/65533": 2,
"0/43/0": "en",
"0/43/1": ["en"],
"0/43/65528": [],
"0/43/65529": [],
"0/43/65530": [],
"0/43/65531": [0, 1, 65528, 65529, 65530, 65531, 65532, 65533],
"0/43/65532": 0,
"0/43/65533": 1,
"0/44/0": 1,
"0/44/1": 4,
"0/44/2": [],
"0/44/65528": [],
"0/44/65529": [],
"0/44/65530": [],
"0/44/65531": [0, 1, 2, 65528, 65529, 65530, 65531, 65532, 65533],
"0/44/65532": 0,
"0/44/65533": 1,
"0/48/0": 0,
"0/48/1": {
"0": 60,
"1": 900
},
"0/48/2": 2,
"0/48/3": 2,
"0/48/4": false,
"0/48/65528": [],
"0/48/65529": [],
"0/48/65530": [],
"0/48/65531": [0, 1, 2, 3, 4, 65528, 65529, 65530, 65531, 65532, 65533],
"0/48/65532": 0,
"0/48/65533": 1,
"0/49/3": 30,
"0/49/65528": [],
"0/49/65529": [],
"0/49/65530": [],
"0/49/65531": [3, 4, 65528, 65529, 65530, 65531, 65532, 65533],
"0/49/65532": 4,
"0/49/65533": 1,
"0/50/65528": [],
"0/50/65529": [],
"0/50/65530": [],
"0/50/65531": [65528, 65529, 65530, 65531, 65532, 65533],
"0/50/65532": 0,
"0/50/65533": 1,
"0/51/1": 7,
"0/51/2": 17,
"0/51/8": false,
"0/51/65528": [],
"0/51/65529": [],
"0/51/65530": [],
"0/51/65531": [0, 1, 2, 8, 65528, 65529, 65530, 65531, 65532, 65533],
"0/51/65532": 0,
"0/51/65533": 1,
"0/52/65528": [],
"0/52/65529": [],
"0/52/65530": [],
"0/52/65531": [65528, 65529, 65530, 65531, 65532, 65533],
"0/52/65532": 0,
"0/52/65533": 1,
"0/56/0": 1718979287000000,
"0/56/1": 3,
"0/56/7": 1718982887000000,
"0/56/65528": [],
"0/56/65529": [],
"0/56/65530": [],
"0/56/65531": [0, 1, 7, 65528, 65529, 65530, 65531, 65532, 65533],
"0/56/65532": 0,
"0/56/65533": 2,
"0/60/0": 0,
"0/60/65528": [],
"0/60/65529": [],
"0/60/65530": [],
"0/60/65531": [0, 1, 2, 65528, 65529, 65530, 65531, 65532, 65533],
"0/60/65532": 0,
"0/60/65533": 1,
"0/62/2": 5,
"0/62/3": 3,
"0/62/5": 3,
"0/62/65528": [],
"0/62/65529": [],
"0/62/65530": [],
"0/62/65531": [0, 1, 2, 3, 4, 5, 65528, 65529, 65530, 65531, 65532, 65533],
"0/62/65532": 0,
"0/62/65533": 1,
"0/63/65528": [],
"0/63/65529": [],
"0/63/65530": [],
"0/63/65531": [65528, 65529, 65530, 65531, 65532, 65533],
"0/63/65532": 0,
"0/63/65533": 2,
"1/3/0": 0,
"1/3/1": 0,
"1/3/65528": [],
"1/3/65529": [],
"1/3/65530": [],
"1/3/65531": [0, 1, 65528, 65529, 65530, 65531, 65532, 65533],
"1/3/65532": 0,
"1/3/65533": 4,
"1/4/65528": [],
"1/4/65529": [],
"1/4/65530": [],
"1/4/65531": [0, 65528, 65529, 65530, 65531, 65532, 65533],
"1/4/65532": 0,
"1/4/65533": 4,
"1/29/0": [
{
"0": 67,
"1": 1
}
],
"1/29/1": [3, 4, 5, 29, 57, 69],
"1/29/2": [],
"1/29/3": [],
"1/29/65528": [],
"1/29/65529": [],
"1/29/65530": [],
"1/29/65531": [0, 1, 2, 3, 65528, 65529, 65530, 65531, 65532, 65533],
"1/29/65532": 0,
"1/29/65533": 1,
"1/69/0": true,
"1/69/65528": [],
"1/69/65529": [],
"1/69/65530": [],
"1/69/65531": [0, 65528, 65529, 65530, 65531, 65532, 65533],
"1/69/65532": 0,
"1/69/65533": 1
},
"attribute_subscriptions": []
}

View file

@ -32,29 +32,6 @@ def binary_sensor_platform() -> Generator[None]:
yield
# This tests needs to be adjusted to remove lingering tasks
@pytest.mark.parametrize("expected_lingering_tasks", [True])
async def test_contact_sensor(
hass: HomeAssistant,
matter_client: MagicMock,
eve_contact_sensor_node: MatterNode,
) -> None:
"""Test contact sensor."""
entity_id = "binary_sensor.eve_door_door"
state = hass.states.get(entity_id)
assert state
assert state.state == "on"
set_node_attribute(eve_contact_sensor_node, 1, 69, 0, True)
await trigger_subscription_callback(
hass, matter_client, data=(eve_contact_sensor_node.node_id, "1/69/0", True)
)
state = hass.states.get(entity_id)
assert state
assert state.state == "off"
@pytest.fixture(name="occupancy_sensor_node")
async def occupancy_sensor_node_fixture(
hass: HomeAssistant, matter_client: MagicMock
@ -87,6 +64,43 @@ async def test_occupancy_sensor(
assert state.state == "off"
# This tests needs to be adjusted to remove lingering tasks
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize(
("fixture", "entity_id"),
[
("eve-contact-sensor", "binary_sensor.eve_door_door"),
("leak-sensor", "binary_sensor.water_leak_detector_water_leak"),
],
)
async def test_boolean_state_sensors(
hass: HomeAssistant,
matter_client: MagicMock,
fixture: str,
entity_id: str,
) -> None:
"""Test if binary sensors get created from devices with Boolean State cluster."""
node = await setup_integration_with_node_fixture(
hass,
fixture,
matter_client,
)
state = hass.states.get(entity_id)
assert state
assert state.state == "on"
# invert the value
cur_attr_value = node.get_attribute_value(1, 69, 0)
set_node_attribute(node, 1, 69, 0, not cur_attr_value)
await trigger_subscription_callback(
hass, matter_client, data=(node.node_id, "1/69/0", not cur_attr_value)
)
state = hass.states.get(entity_id)
assert state
assert state.state == "off"
# This tests needs to be adjusted to remove lingering tasks
@pytest.mark.parametrize("expected_lingering_tasks", [True])
async def test_battery_sensor(