Add Matter Boolean State Configuration alarm switches (#173233)

Co-authored-by: Ludovic BOUÉ <lboue@users.noreply.github.com>
Co-authored-by: Ludovic BOUÉ <938089+lboue@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
WardZhou
2026-09-05 15:43:16 +02:00
committed by GitHub
co-authored by Ludovic BOUÉ Ludovic BOUÉ Claude Sonnet 5 Copilot Autofix powered by AI
parent 535cc28263
commit 932838840b
6 changed files with 713 additions and 1 deletions
@@ -154,6 +154,9 @@
}
},
"switch": {
"audible_alarm_enabled": {
"default": "mdi:bullhorn"
},
"child_lock": {
"default": "mdi:lock",
"state": {
@@ -173,6 +176,9 @@
"off": "mdi:volume-high",
"on": "mdi:volume-mute"
}
},
"visual_alarm_enabled": {
"default": "mdi:alarm-light"
}
}
},
@@ -690,6 +690,9 @@
}
},
"switch": {
"audible_alarm_enabled": {
"name": "Audible alarm enabled"
},
"child_lock": {
"name": "Child lock"
},
@@ -710,6 +713,9 @@
},
"switch": {
"name": "[%key:component::switch::title%]"
},
"visual_alarm_enabled": {
"name": "Visual alarm enabled"
}
},
"vacuum": {
+144 -1
View File
@@ -1,12 +1,15 @@
"""Matter switches."""
from asyncio import Lock
from collections.abc import Callable
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Any, override
from weakref import WeakKeyDictionary
from chip.clusters import Objects as clusters
from chip.clusters.Objects import ClusterCommand, NullValue
from matter_server.client.models import device_types
from matter_server.client.models.node import MatterEndpoint
from homeassistant.components.switch import (
SwitchDeviceClass,
@@ -28,6 +31,29 @@ EVSE_SUPPLY_STATE_MAP = {
clusters.EnergyEvse.Enums.SupplyStateEnum.kDisabledDiagnostics: False,
}
ALARM_MODE_VISUAL = clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kVisual
ALARM_MODE_AUDIBLE = clusters.BooleanStateConfiguration.Bitmaps.AlarmModeBitmap.kAudible
BOOLEAN_STATE_CONFIGURATION_FEATURE_VISUAL = (
clusters.BooleanStateConfiguration.Bitmaps.Feature.kVisual
)
BOOLEAN_STATE_CONFIGURATION_FEATURE_AUDIBLE = (
clusters.BooleanStateConfiguration.Bitmaps.Feature.kAudible
)
@dataclass
class _AlarmEnabledState:
"""Track pending alarm state for an endpoint."""
lock: Lock = field(default_factory=Lock)
pending_alarms_enabled: int | None = None
ALARM_ENABLED_STATES: WeakKeyDictionary[MatterEndpoint, _AlarmEnabledState] = (
WeakKeyDictionary()
)
async def async_setup_entry(
hass: HomeAssistant,
@@ -183,6 +209,83 @@ class MatterNumericSwitch(MatterSwitch):
self._attr_is_on = value
@dataclass(frozen=True, kw_only=True)
class MatterAlarmEnabledSwitchEntityDescription(MatterSwitchEntityDescription):
"""Describe Matter alarm enabled Switch entities."""
alarm_mode: int
class MatterAlarmEnabledSwitch(MatterSwitch):
"""Representation of a Matter Boolean State Configuration alarm switch."""
entity_description: MatterAlarmEnabledSwitchEntityDescription
async def _async_set_alarm_enabled(self, value: bool) -> None:
"""Set the enabled state for an alarm mode."""
state = ALARM_ENABLED_STATES.setdefault(self._endpoint, _AlarmEnabledState())
async with state.lock:
alarms_enabled = state.pending_alarms_enabled
if alarms_enabled is None:
alarms_enabled = (
self.get_matter_attribute_value(
clusters.BooleanStateConfiguration.Attributes.AlarmsEnabled
)
or 0
)
if value:
alarms_enabled |= self.entity_description.alarm_mode
else:
alarms_enabled &= ~self.entity_description.alarm_mode
state.pending_alarms_enabled = alarms_enabled
try:
await self.send_device_command(
clusters.BooleanStateConfiguration.Commands.EnableDisableAlarm(
alarmsToEnableDisable=alarms_enabled,
)
)
except BaseException:
if state.pending_alarms_enabled == alarms_enabled:
state.pending_alarms_enabled = None
raise
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn alarm mode on."""
await self._async_set_alarm_enabled(True)
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn alarm mode off."""
await self._async_set_alarm_enabled(False)
@callback
@override
def _update_from_device(self) -> None:
"""Update from device."""
alarm_mode = self.entity_description.alarm_mode
alarms_supported = (
self.get_matter_attribute_value(
clusters.BooleanStateConfiguration.Attributes.AlarmsSupported
)
or 0
)
self._attr_available = self._attr_available and bool(
alarms_supported & alarm_mode
)
alarms_enabled = (
self.get_matter_attribute_value(
clusters.BooleanStateConfiguration.Attributes.AlarmsEnabled
)
or 0
)
if state := ALARM_ENABLED_STATES.get(self._endpoint):
state.pending_alarms_enabled = alarms_enabled
self._attr_is_on = bool(alarms_enabled & alarm_mode)
# Discovery schema(s) to map Matter Attributes to HA entities
DISCOVERY_SCHEMAS = [
MatterDiscoverySchema(
@@ -346,4 +449,44 @@ DISCOVERY_SCHEMAS = [
entity_class=MatterNumericSwitch,
required_attributes=(clusters.EveCluster.Attributes.ChildLock,),
),
MatterDiscoverySchema(
platform=Platform.SWITCH,
entity_description=MatterAlarmEnabledSwitchEntityDescription(
key="BooleanStateConfigurationVisualAlarmEnabled",
entity_category=EntityCategory.CONFIG,
translation_key="visual_alarm_enabled",
alarm_mode=ALARM_MODE_VISUAL,
),
entity_class=MatterAlarmEnabledSwitch,
required_attributes=(
clusters.BooleanStateConfiguration.Attributes.AlarmsEnabled,
clusters.BooleanStateConfiguration.Attributes.AcceptedCommandList,
clusters.BooleanStateConfiguration.Attributes.AlarmsSupported,
),
secondary_value_contains=(
clusters.BooleanStateConfiguration.Commands.EnableDisableAlarm.command_id
),
featuremap_contains=BOOLEAN_STATE_CONFIGURATION_FEATURE_VISUAL,
allow_multi=True,
),
MatterDiscoverySchema(
platform=Platform.SWITCH,
entity_description=MatterAlarmEnabledSwitchEntityDescription(
key="BooleanStateConfigurationAudibleAlarmEnabled",
entity_category=EntityCategory.CONFIG,
translation_key="audible_alarm_enabled",
alarm_mode=ALARM_MODE_AUDIBLE,
),
entity_class=MatterAlarmEnabledSwitch,
required_attributes=(
clusters.BooleanStateConfiguration.Attributes.AlarmsEnabled,
clusters.BooleanStateConfiguration.Attributes.AcceptedCommandList,
clusters.BooleanStateConfiguration.Attributes.AlarmsSupported,
),
secondary_value_contains=(
clusters.BooleanStateConfiguration.Commands.EnableDisableAlarm.command_id
),
featuremap_contains=BOOLEAN_STATE_CONFIGURATION_FEATURE_AUDIBLE,
allow_multi=True,
),
]
@@ -0,0 +1,342 @@
{
"node_id": 320,
"date_commissioned": "2026-02-07T03:40:58.175000",
"last_interview": "2026-04-15T04:08:35.891000",
"interview_version": 6,
"available": true,
"is_bridge": false,
"attributes": {
"0/29/65533": 2,
"0/29/65532": 0,
"0/29/0": [
{
"0": 18,
"1": 1
},
{
"0": 17,
"1": 1
},
{
"0": 22,
"1": 1
}
],
"0/29/1": [29, 31, 40, 42, 47, 48, 49, 51, 53, 60, 62, 63, 70],
"0/29/2": [41],
"0/29/3": [1],
"0/29/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"0/29/65529": [],
"0/29/65528": [],
"0/31/1": [],
"0/31/65533": 1,
"0/31/65532": 0,
"0/31/0": [
{
"1": 5,
"2": 2,
"3": [112233],
"4": null,
"254": 3
}
],
"0/31/2": 4,
"0/31/3": 3,
"0/31/4": 4,
"0/31/65531": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
"0/31/65529": [],
"0/31/65528": [],
"0/40/11": "20251122",
"0/40/12": "E2493",
"0/40/16": false,
"0/40/17": true,
"0/40/24": 1,
"0/40/65533": 3,
"0/40/0": 17,
"0/40/1": "IKEA of Sweden",
"0/40/2": 4476,
"0/40/3": "KLIPPBOK water leak sensor",
"0/40/4": 32774,
"0/40/5": "",
"0/40/6": "**REDACTED**",
"0/40/7": 512,
"0/40/8": "P2.0",
"0/40/9": 16777229,
"0/40/10": "1.0.13",
"0/40/18": "5022e21a483c1d4f68fa7b871fb705f9",
"0/40/19": {
"0": 3,
"1": 3
},
"0/40/21": 16973824,
"0/40/22": 1,
"0/40/65532": 0,
"0/40/65531": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 17, 18, 19, 21, 22, 65528,
65529, 65531, 65532, 65533
],
"0/40/65529": [],
"0/40/65528": [],
"0/42/65533": 1,
"0/42/0": [],
"0/42/1": true,
"0/42/2": 1,
"0/42/3": null,
"0/42/65532": 0,
"0/42/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"0/42/65529": [0],
"0/42/65528": [],
"0/47/65532": 10,
"0/47/11": 2991,
"0/47/12": 200,
"0/47/14": 0,
"0/47/15": false,
"0/47/16": 2,
"0/47/19": "AAA",
"0/47/20": 1,
"0/47/25": 1,
"0/47/65533": 2,
"0/47/0": 1,
"0/47/1": 0,
"0/47/2": "Primary Battery",
"0/47/31": [],
"0/47/65531": [
0, 1, 2, 11, 12, 14, 15, 16, 19, 20, 25, 31, 65528, 65529, 65531, 65532,
65533
],
"0/47/65529": [],
"0/47/65528": [],
"0/48/65533": 1,
"0/48/65532": 0,
"0/48/0": 0,
"0/48/1": {
"0": 60,
"1": 900
},
"0/48/2": 0,
"0/48/3": 0,
"0/48/4": true,
"0/48/65531": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
"0/48/65529": [0, 2, 4],
"0/48/65528": [1, 3, 5],
"0/49/65532": 2,
"0/49/2": 10,
"0/49/3": 20,
"0/49/9": 4,
"0/49/10": 4,
"0/49/65533": 2,
"0/49/0": 1,
"0/49/1": [
{
"0": "/yXnNCiUSvw=",
"1": true
}
],
"0/49/4": true,
"0/49/5": 0,
"0/49/6": "/yXnNCiUSvw=",
"0/49/7": null,
"0/49/65531": [
0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 65528, 65529, 65531, 65532, 65533
],
"0/49/65529": [0, 3, 4, 6, 8],
"0/49/65528": [1, 5, 7],
"0/51/3": 1565,
"0/51/4": 6,
"0/51/5": [],
"0/51/6": [],
"0/51/7": [],
"0/51/65533": 2,
"0/51/65532": 0,
"0/51/0": [
{
"0": "MyHome1895415629",
"1": true,
"2": null,
"3": null,
"4": "2kBsLtRvnPA=",
"5": [],
"6": [
"/Rfu+JATAAAvUPrv0kGOrQ==",
"/dH8OtkWot0AAAD//gDMNg==",
"/dH8OtkWot29tss/JqJ3Mw==",
"/oAAAAAAAADYQGwu1G+c8A=="
],
"7": 4
}
],
"0/51/1": 7,
"0/51/2": 218,
"0/51/8": false,
"0/51/65531": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533
],
"0/51/65529": [0, 1],
"0/51/65528": [2],
"0/53/63": null,
"0/53/64": null,
"0/53/65533": 2,
"0/53/65532": 0,
"0/53/0": 25,
"0/53/1": 2,
"0/53/2": "MyHome1895415629",
"0/53/3": 49399,
"0/53/4": 18385355265015040764,
"0/53/5": "QP3R/DrZFqLd",
"0/53/7": [
{
"0": 6260342793904889918,
"1": 218,
"2": 52224,
"3": 878085,
"4": 189075,
"5": 3,
"6": -47,
"7": -48,
"8": 59,
"9": 0,
"10": true,
"11": true,
"12": true,
"13": false
}
],
"0/53/8": [
{
"0": 6260342793904889918,
"1": 52224,
"2": 51,
"3": 0,
"4": 0,
"5": 3,
"6": 0,
"7": 218,
"8": true,
"9": true
}
],
"0/53/9": 2137771856,
"0/53/10": 68,
"0/53/11": 140,
"0/53/12": 230,
"0/53/13": 30,
"0/53/59": {
"0": 672,
"1": 8335
},
"0/53/60": "AB//wA==",
"0/53/61": {
"0": true,
"1": false,
"2": true,
"3": true,
"4": true,
"5": true,
"6": false,
"7": true,
"8": true,
"9": true,
"10": true,
"11": true
},
"0/53/62": [],
"0/53/65531": [
0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 59, 60, 61, 62, 65528, 65529,
65531, 65532, 65533
],
"0/53/65529": [],
"0/53/65528": [],
"0/60/65532": 1,
"0/60/65533": 1,
"0/60/0": 0,
"0/60/1": null,
"0/60/2": null,
"0/60/65531": [0, 1, 2, 65528, 65529, 65531, 65532, 65533],
"0/60/65529": [0, 1, 2],
"0/60/65528": [],
"0/62/65533": 1,
"0/62/0": [
{
"1": "FTABAQgkAgE3AyQTAhgmBL3MNy8mBT0D5UM3BiQVAiURQAEYJAcBJAgBMAlBBB5ZHrs6JznmDwMq4t9+CjNwTfXtyTVZjyVzVOjlI+cY/hetXDmsCC0TNewgULVLEXlQxezlY85QYFPMlaSruqU3CjUBKAEYJAIBNgMEAgQBGDAEFPMDw70atbx08VczmceWWiv83ov+MAUU/eZfRuhWvUFT8WNU1R/sUE0q70YYMAtAIdqlW4sHK/DRdVQpbywUoGRVIc3G7esTDJgnEG3OcDLWXSHwdDfuuFNLN589EbBOEuOVCPCn+6Im65TuI1tJoxg=",
"2": "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQTAhgkBwEkCAEwCUEEJB6axSR6Xj7Ab+FB5+C+slsdDtj0qCvcRHCCpCYTX6svgMPs/yVVEfvJgIUXZ5gkLS9jK1CpsF4u8MZR6qsNZzcKNQEpARgkAmAwBBT95l9G6Fa9QVPxY1TVH+xQTSrvRjAFFLyrP0JigOFmlOJIyXL9CANMKs5NGDALQNykW7UIqcgXgx+UezCVYPRU8/CpHh9CJBqL/7wKfTM62ujWJlrH0P5DEZ5bV9ZihCk4Wg/DMM2BUuUcTOEEqzEY",
"254": 3
}
],
"0/62/1": [
{
"1": "BCZ12LdJK3WZUiquu2PD6iWSaeQK6J6DWw86GihFX4HiWOG1JQip6ILp0IFNffrIGwriEteEhksN56MylydpF/s=",
"2": 4939,
"3": 2,
"4": 320,
"5": "Home",
"254": 3
}
],
"0/62/2": 5,
"0/62/3": 3,
"0/62/4": [
"FTABAQEkAgE3AycUZweWCkFUxWIYJgQAxikuJgUAzMFTNwYnFGcHlgpBVMViGCQHASQIATAJQQTNp4X8uzw+lDd+17mb6etHu369TrObsUl8Z+oc0Tv7ETpM91vCk6f7cF6b1UzZnUOizSZh/oir5rLjiYmaiyNsNwo1ASkBGCQCYDAEFNTPAx7gkSWXYv+kfe6NSGPo/UiMMAUU1M8DHuCRJZdi/6R97o1IY+j9SIwYMAtA2+qZy7xOxqbY0kyFYHKHQ7oPs6lduN8CwhHGHO11PViHOa8Ghkg8k9z8WbloMSd5Q/ZQyIRJsBFVd2hIBNu1/Rg=",
"FTABFAPDl5npnEb4b11grHG1AdwYLxLOJAIBNwMnFGHDECgeEBh7JxVfPgmR153o9xgmBK5z+S8mBa6C9Y03BicUYcMQKB4QGHsnFV8+CZHXnej3GCQHASQIATAJQQRHUJH4AsX5PMwf+CaBm3BgQsag6Si880dCh4Z/nWvAzlsLLY9CTa0+LBwePhweon0IdT3tsHIq1K3OOg5YsbG4Nwo1ASkBGCQCYDAEFLtqXB3f+f0UnYFZUcYXZ654P2A6MAUUu2pcHd/5/RSdgVlRxhdnrng/YDoYMAtAtfPiyCDVwl93HpnYluDMZCQ2mEpiZNTgZSHQDfsMbdSQoYery2KtqPJkglA5+XUKRm57nbTJKENE9evkOD0Ctxg=",
"FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQUARgkBwEkCAEwCUEEJnXYt0krdZlSKq67Y8PqJZJp5AronoNbDzoaKEVfgeJY4bUlCKnogunQgU19+sgbCuIS14SGSw3nozKXJ2kX+zcKNQEpARgkAmAwBBS8qz9CYoDhZpTiSMly/QgDTCrOTTAFFLyrP0JigOFmlOJIyXL9CANMKs5NGDALQIfPjG1LeoSoRd3sJ2NeaS3VrHyftI8l6dOwafhoGMQdCRwyadYABiUG/Po1BnWmg4laSh88nP3zAAnQ2j0l4tAY"
],
"0/62/5": 3,
"0/62/65532": 0,
"0/62/65531": [0, 1, 2, 3, 4, 5, 65528, 65529, 65531, 65532, 65533],
"0/62/65529": [0, 2, 4, 6, 7, 9, 10, 11],
"0/62/65528": [1, 3, 5, 8],
"0/63/65533": 2,
"0/63/65532": 0,
"0/63/0": [],
"0/63/1": [],
"0/63/2": 4,
"0/63/3": 3,
"0/63/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"0/63/65529": [0, 1, 3, 4],
"0/63/65528": [2, 5],
"0/70/65533": 2,
"0/70/65532": 0,
"0/70/0": 120,
"0/70/1": 1000,
"0/70/2": 1000,
"0/70/65531": [0, 1, 2, 65528, 65529, 65531, 65532, 65533],
"0/70/65529": [],
"0/70/65528": [],
"1/29/65533": 2,
"1/29/65532": 0,
"1/29/0": [
{
"0": 67,
"1": 1
}
],
"1/29/1": [3, 29, 69, 128],
"1/29/2": [],
"1/29/3": [],
"1/29/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"1/29/65529": [],
"1/29/65528": [],
"1/128/65532": 7,
"1/128/3": 0,
"1/128/4": 0,
"1/128/5": 3,
"1/128/6": 3,
"1/128/65533": 1,
"1/128/65531": [3, 4, 5, 6, 65528, 65529, 65531, 65532, 65533],
"1/128/65529": [0, 1],
"1/128/65528": [],
"1/3/65533": 4,
"1/3/0": 0,
"1/3/1": 2,
"1/3/65532": 0,
"1/3/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
"1/3/65529": [0],
"1/3/65528": [],
"1/69/65533": 1,
"1/69/0": false,
"1/69/65532": 0,
"1/69/65531": [0, 65528, 65529, 65531, 65532, 65533],
"1/69/65529": [],
"1/69/65528": []
},
"attribute_subscriptions": []
}
@@ -1,4 +1,104 @@
# serializer version: 1
# name: test_boolean_state_configuration_alarm_enabled_switches[ikea_klippbok_water_leak][switch.klippbok_water_leak_sensor_audible_alarm_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.klippbok_water_leak_sensor_audible_alarm_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Audible alarm enabled',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Audible alarm enabled',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'audible_alarm_enabled',
'unique_id': '00000000000004D2-0000000000000140-MatterNodeDevice-1-BooleanStateConfigurationAudibleAlarmEnabled-128-5',
'unit_of_measurement': None,
})
# ---
# name: test_boolean_state_configuration_alarm_enabled_switches[ikea_klippbok_water_leak][switch.klippbok_water_leak_sensor_audible_alarm_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'KLIPPBOK water leak sensor Audible alarm enabled',
}),
'context': <ANY>,
'entity_id': 'switch.klippbok_water_leak_sensor_audible_alarm_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_boolean_state_configuration_alarm_enabled_switches[ikea_klippbok_water_leak][switch.klippbok_water_leak_sensor_visual_alarm_enabled-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.klippbok_water_leak_sensor_visual_alarm_enabled',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Visual alarm enabled',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Visual alarm enabled',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'visual_alarm_enabled',
'unique_id': '00000000000004D2-0000000000000140-MatterNodeDevice-1-BooleanStateConfigurationVisualAlarmEnabled-128-5',
'unit_of_measurement': None,
})
# ---
# name: test_boolean_state_configuration_alarm_enabled_switches[ikea_klippbok_water_leak][switch.klippbok_water_leak_sensor_visual_alarm_enabled-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'KLIPPBOK water leak sensor Visual alarm enabled',
}),
'context': <ANY>,
'entity_id': 'switch.klippbok_water_leak_sensor_visual_alarm_enabled',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switches[eve_energy_20ecn4101][switch.eve_energy_20ecn4101_child_lock_top-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
+115
View File
@@ -1,5 +1,6 @@
"""Test Matter switches."""
import asyncio
from unittest.mock import MagicMock, call
from chip.clusters import Objects as clusters
@@ -234,6 +235,120 @@ async def test_evse_sensor(
)
@pytest.mark.parametrize("node_fixture", ["ikea_klippbok_water_leak"])
async def test_boolean_state_configuration_alarm_enabled_switches(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
matter_client: MagicMock,
matter_node: MatterNode,
snapshot: SnapshotAssertion,
) -> None:
"""Test Boolean State Configuration alarm enabled switches."""
visual_entity_id = "switch.klippbok_water_leak_sensor_visual_alarm_enabled"
audible_entity_id = "switch.klippbok_water_leak_sensor_audible_alarm_enabled"
visual_entry = entity_registry.async_get(visual_entity_id)
audible_entry = entity_registry.async_get(audible_entity_id)
assert visual_entry
assert audible_entry
visual_state = hass.states.get(visual_entity_id)
audible_state = hass.states.get(audible_entity_id)
assert visual_state
assert audible_state
assert visual_entry == snapshot(name=f"{visual_entity_id}-entry")
assert visual_state == snapshot(name=f"{visual_entity_id}-state")
assert audible_entry == snapshot(name=f"{audible_entity_id}-entry")
assert audible_state == snapshot(name=f"{audible_entity_id}-state")
assert visual_state.state == "on"
assert audible_state.state == "on"
await hass.services.async_call(
"switch",
"turn_off",
{"entity_id": visual_entity_id},
blocking=True,
)
assert matter_client.send_device_command.call_count == 1
assert matter_client.send_device_command.call_args == call(
node_id=matter_node.node_id,
endpoint_id=1,
command=clusters.BooleanStateConfiguration.Commands.EnableDisableAlarm(
alarmsToEnableDisable=2,
),
)
set_node_attribute(matter_node, 1, 128, 5, 2)
await trigger_subscription_callback(hass, matter_client)
visual_state = hass.states.get(visual_entity_id)
audible_state = hass.states.get(audible_entity_id)
assert visual_state
assert audible_state
assert visual_state.state == "off"
assert audible_state.state == "on"
await hass.services.async_call(
"switch",
"turn_on",
{"entity_id": visual_entity_id},
blocking=True,
)
assert matter_client.send_device_command.call_count == 2
assert matter_client.send_device_command.call_args == call(
node_id=matter_node.node_id,
endpoint_id=1,
command=clusters.BooleanStateConfiguration.Commands.EnableDisableAlarm(
alarmsToEnableDisable=3,
),
)
@pytest.mark.parametrize("node_fixture", ["ikea_klippbok_water_leak"])
async def test_boolean_state_configuration_alarm_enabled_switches_are_serialized(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test alarm switch changes to the shared bitmap are serialized."""
visual_entity_id = "switch.klippbok_water_leak_sensor_visual_alarm_enabled"
audible_entity_id = "switch.klippbok_water_leak_sensor_audible_alarm_enabled"
command_started = asyncio.Event()
allow_commands = asyncio.Event()
async def send_device_command(*args: object, **kwargs: object) -> None:
command_started.set()
await allow_commands.wait()
matter_client.send_device_command.side_effect = send_device_command
task = hass.async_create_task(
hass.services.async_call(
"switch",
"turn_off",
{"entity_id": [visual_entity_id, audible_entity_id]},
blocking=True,
)
)
await command_started.wait()
await asyncio.sleep(0)
assert matter_client.send_device_command.call_count == 1
allow_commands.set()
await task
assert matter_client.send_device_command.call_count == 2
assert matter_client.send_device_command.call_args_list[-1] == call(
node_id=matter_node.node_id,
endpoint_id=1,
command=clusters.BooleanStateConfiguration.Commands.EnableDisableAlarm(
alarmsToEnableDisable=0,
),
)
@pytest.mark.parametrize("node_fixture", ["mock_speaker"])
async def test_speaker_mute_uses_onoff_commands(
hass: HomeAssistant,