From 569aeff0549ddf91bc832b881d41a29396f1e619 Mon Sep 17 00:00:00 2001 From: jvmahon Date: Thu, 22 May 2025 06:42:05 -0400 Subject: [PATCH] Add matter attributes (#140843) * Add Matter attributes * Add Matter attributes * Add Matter attributes * Add Matter attributes * Update strings.json * Update homeassistant/components/matter/select.py Co-authored-by: Marcel van der Veldt * Update select.py Deleted items to be added as switch entities instead. * Update strings.json * Update select.py * Update strings.json * Fix * Update strings.json * Update strings.json * Fix * Update select.py --------- Co-authored-by: Marcel van der Veldt Co-authored-by: Joostlek --- homeassistant/components/matter/number.py | 30 ++ homeassistant/components/matter/select.py | 23 ++ homeassistant/components/matter/sensor.py | 36 +++ homeassistant/components/matter/strings.json | 24 ++ .../matter/snapshots/test_number.ambr | 114 +++++++ .../matter/snapshots/test_select.ambr | 120 ++++++++ .../matter/snapshots/test_sensor.ambr | 284 ++++++++++++++++++ 7 files changed, 631 insertions(+) diff --git a/homeassistant/components/matter/number.py b/homeassistant/components/matter/number.py index 2c7a9651c60..4b469fa85e4 100644 --- a/homeassistant/components/matter/number.py +++ b/homeassistant/components/matter/number.py @@ -183,4 +183,34 @@ DISCOVERY_SCHEMAS = [ ), vendor_id=(4874,), ), + MatterDiscoverySchema( + platform=Platform.NUMBER, + entity_description=MatterNumberEntityDescription( + key="PIROccupiedToUnoccupiedDelay", + entity_category=EntityCategory.CONFIG, + translation_key="pir_occupied_to_unoccupied_delay", + native_max_value=65534, + native_min_value=0, + native_unit_of_measurement=UnitOfTime.SECONDS, + mode=NumberMode.BOX, + ), + entity_class=MatterNumber, + required_attributes=( + clusters.OccupancySensing.Attributes.PIROccupiedToUnoccupiedDelay, + ), + ), + MatterDiscoverySchema( + platform=Platform.NUMBER, + entity_description=MatterNumberEntityDescription( + key="AutoRelockTimer", + entity_category=EntityCategory.CONFIG, + translation_key="auto_relock_timer", + native_max_value=65534, + native_min_value=0, + native_unit_of_measurement=UnitOfTime.SECONDS, + mode=NumberMode.BOX, + ), + entity_class=MatterNumber, + required_attributes=(clusters.DoorLock.Attributes.AutoRelockTime,), + ), ] diff --git a/homeassistant/components/matter/select.py b/homeassistant/components/matter/select.py index 6e77be93705..39e1db3bf6f 100644 --- a/homeassistant/components/matter/select.py +++ b/homeassistant/components/matter/select.py @@ -436,4 +436,27 @@ DISCOVERY_SCHEMAS = [ # don't discover this entry if the supported rinses list is empty secondary_value_is_not=[], ), + MatterDiscoverySchema( + platform=Platform.SELECT, + entity_description=MatterSelectEntityDescription( + key="DoorLockSoundVolume", + entity_category=EntityCategory.CONFIG, + translation_key="door_lock_sound_volume", + options=["silent", "low", "medium", "high"], + measurement_to_ha={ + 0: "silent", + 1: "low", + 3: "medium", + 2: "high", + }.get, + ha_to_native_value={ + "silent": 0, + "low": 1, + "medium": 3, + "high": 2, + }.get, + ), + entity_class=MatterAttributeSelectEntity, + required_attributes=(clusters.DoorLock.Attributes.SoundVolume,), + ), ] diff --git a/homeassistant/components/matter/sensor.py b/homeassistant/components/matter/sensor.py index e0d2050c833..83248955279 100644 --- a/homeassistant/components/matter/sensor.py +++ b/homeassistant/components/matter/sensor.py @@ -948,6 +948,42 @@ DISCOVERY_SCHEMAS = [ # don't discover this entry if the supported state list is empty secondary_value_is_not=[], ), + MatterDiscoverySchema( + platform=Platform.SENSOR, + entity_description=MatterSensorEntityDescription( + key="MinPINCodeLength", + translation_key="min_pin_code_length", + entity_category=EntityCategory.DIAGNOSTIC, + device_class=None, + ), + entity_class=MatterSensor, + required_attributes=(clusters.DoorLock.Attributes.MinPINCodeLength,), + ), + MatterDiscoverySchema( + platform=Platform.SENSOR, + entity_description=MatterSensorEntityDescription( + key="MaxPINCodeLength", + translation_key="max_pin_code_length", + entity_category=EntityCategory.DIAGNOSTIC, + device_class=None, + ), + entity_class=MatterSensor, + required_attributes=(clusters.DoorLock.Attributes.MaxPINCodeLength,), + ), + MatterDiscoverySchema( + platform=Platform.SENSOR, + entity_description=MatterSensorEntityDescription( + key="TargetPositionLiftPercent100ths", + entity_category=EntityCategory.DIAGNOSTIC, + translation_key="window_covering_target_position", + measurement_to_ha=lambda x: round((10000 - x) / 100), + native_unit_of_measurement=PERCENTAGE, + ), + entity_class=MatterSensor, + required_attributes=( + clusters.WindowCovering.Attributes.TargetPositionLiftPercent100ths, + ), + ), MatterDiscoverySchema( platform=Platform.SENSOR, entity_description=MatterSensorEntityDescription( diff --git a/homeassistant/components/matter/strings.json b/homeassistant/components/matter/strings.json index 129c6a3ab54..daff0115505 100644 --- a/homeassistant/components/matter/strings.json +++ b/homeassistant/components/matter/strings.json @@ -176,6 +176,12 @@ }, "temperature_offset": { "name": "Temperature offset" + }, + "pir_occupied_to_unoccupied_delay": { + "name": "Occupied to unoccupied delay" + }, + "auto_relock_timer": { + "name": "Automatic relock timer" } }, "light": { @@ -235,6 +241,15 @@ }, "water_heater_mode": { "name": "Water heater mode" + }, + "door_lock_sound_volume": { + "name": "Sound volume", + "state": { + "silent": "Silent", + "low": "[%key:common::state::low%]", + "medium": "[%key:common::state::medium%]", + "high": "[%key:common::state::high%]" + } } }, "sensor": { @@ -341,6 +356,15 @@ }, "evse_user_max_charge_current": { "name": "User max charge current" + }, + "min_pin_code_length": { + "name": "Min PIN code length" + }, + "max_pin_code_length": { + "name": "Max PIN code length" + }, + "window_covering_target_position": { + "name": "Target opening position" } }, "switch": { diff --git a/tests/components/matter/snapshots/test_number.ambr b/tests/components/matter/snapshots/test_number.ambr index eb0a12bfc4d..3240538f0a5 100644 --- a/tests/components/matter/snapshots/test_number.ambr +++ b/tests/components/matter/snapshots/test_number.ambr @@ -395,6 +395,120 @@ 'state': '1.0', }) # --- +# name: test_numbers[door_lock][number.mock_door_lock_automatic_relock_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 65534, + 'min': 0, + 'mode': , + 'step': 1.0, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.mock_door_lock_automatic_relock_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Automatic relock timer', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'auto_relock_timer', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-AutoRelockTimer-257-35', + 'unit_of_measurement': , + }) +# --- +# name: test_numbers[door_lock][number.mock_door_lock_automatic_relock_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Door Lock Automatic relock timer', + 'max': 65534, + 'min': 0, + 'mode': , + 'step': 1.0, + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'number.mock_door_lock_automatic_relock_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '60', + }) +# --- +# name: test_numbers[door_lock_with_unbolt][number.mock_door_lock_automatic_relock_timer-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'max': 65534, + 'min': 0, + 'mode': , + 'step': 1.0, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.mock_door_lock_automatic_relock_timer', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Automatic relock timer', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'auto_relock_timer', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-AutoRelockTimer-257-35', + 'unit_of_measurement': , + }) +# --- +# name: test_numbers[door_lock_with_unbolt][number.mock_door_lock_automatic_relock_timer-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Door Lock Automatic relock timer', + 'max': 65534, + 'min': 0, + 'mode': , + 'step': 1.0, + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'number.mock_door_lock_automatic_relock_timer', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '60', + }) +# --- # name: test_numbers[eve_thermo][number.eve_thermo_temperature_offset-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/matter/snapshots/test_select.ambr b/tests/components/matter/snapshots/test_select.ambr index 713f0b25f45..edd0224ccac 100644 --- a/tests/components/matter/snapshots/test_select.ambr +++ b/tests/components/matter/snapshots/test_select.ambr @@ -433,6 +433,66 @@ 'state': 'off', }) # --- +# name: test_selects[door_lock][select.mock_door_lock_sound_volume-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'silent', + 'low', + 'medium', + 'high', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_door_lock_sound_volume', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Sound volume', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'door_lock_sound_volume', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-DoorLockSoundVolume-257-36', + 'unit_of_measurement': None, + }) +# --- +# name: test_selects[door_lock][select.mock_door_lock_sound_volume-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Door Lock Sound volume', + 'options': list([ + 'silent', + 'low', + 'medium', + 'high', + ]), + }), + 'context': , + 'entity_id': 'select.mock_door_lock_sound_volume', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'silent', + }) +# --- # name: test_selects[door_lock_with_unbolt][select.mock_door_lock_power_on_behavior_on_startup-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -493,6 +553,66 @@ 'state': 'off', }) # --- +# name: test_selects[door_lock_with_unbolt][select.mock_door_lock_sound_volume-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'silent', + 'low', + 'medium', + 'high', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.mock_door_lock_sound_volume', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Sound volume', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'door_lock_sound_volume', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-DoorLockSoundVolume-257-36', + 'unit_of_measurement': None, + }) +# --- +# name: test_selects[door_lock_with_unbolt][select.mock_door_lock_sound_volume-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Door Lock Sound volume', + 'options': list([ + 'silent', + 'low', + 'medium', + 'high', + ]), + }), + 'context': , + 'entity_id': 'select.mock_door_lock_sound_volume', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'silent', + }) +# --- # name: test_selects[eve_energy_plug][select.eve_energy_plug_power_on_behavior_on_startup-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/matter/snapshots/test_sensor.ambr b/tests/components/matter/snapshots/test_sensor.ambr index 454e6e67a4c..00c9a178c2b 100644 --- a/tests/components/matter/snapshots/test_sensor.ambr +++ b/tests/components/matter/snapshots/test_sensor.ambr @@ -1271,6 +1271,194 @@ 'state': '180.0', }) # --- +# name: test_sensors[door_lock][sensor.mock_door_lock_max_pin_code_length-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.mock_door_lock_max_pin_code_length', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Max PIN code length', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'max_pin_code_length', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-MaxPINCodeLength-257-23', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[door_lock][sensor.mock_door_lock_max_pin_code_length-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Door Lock Max PIN code length', + }), + 'context': , + 'entity_id': 'sensor.mock_door_lock_max_pin_code_length', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '8', + }) +# --- +# name: test_sensors[door_lock][sensor.mock_door_lock_min_pin_code_length-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.mock_door_lock_min_pin_code_length', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Min PIN code length', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'min_pin_code_length', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-MinPINCodeLength-257-24', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[door_lock][sensor.mock_door_lock_min_pin_code_length-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Door Lock Min PIN code length', + }), + 'context': , + 'entity_id': 'sensor.mock_door_lock_min_pin_code_length', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '6', + }) +# --- +# name: test_sensors[door_lock_with_unbolt][sensor.mock_door_lock_max_pin_code_length-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.mock_door_lock_max_pin_code_length', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Max PIN code length', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'max_pin_code_length', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-MaxPINCodeLength-257-23', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[door_lock_with_unbolt][sensor.mock_door_lock_max_pin_code_length-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Door Lock Max PIN code length', + }), + 'context': , + 'entity_id': 'sensor.mock_door_lock_max_pin_code_length', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '8', + }) +# --- +# name: test_sensors[door_lock_with_unbolt][sensor.mock_door_lock_min_pin_code_length-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.mock_door_lock_min_pin_code_length', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Min PIN code length', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'min_pin_code_length', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-MinPINCodeLength-257-24', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[door_lock_with_unbolt][sensor.mock_door_lock_min_pin_code_length-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Door Lock Min PIN code length', + }), + 'context': , + 'entity_id': 'sensor.mock_door_lock_min_pin_code_length', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '6', + }) +# --- # name: test_sensors[eve_contact_sensor][sensor.eve_door_battery-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -4976,6 +5164,102 @@ 'state': 'unknown', }) # --- +# name: test_sensors[window_covering_full][sensor.mock_full_window_covering_target_opening_position-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.mock_full_window_covering_target_opening_position', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Target opening position', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'window_covering_target_position', + 'unique_id': '00000000000004D2-0000000000000032-MatterNodeDevice-1-TargetPositionLiftPercent100ths-258-11', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensors[window_covering_full][sensor.mock_full_window_covering_target_opening_position-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock Full Window Covering Target opening position', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.mock_full_window_covering_target_opening_position', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '100', + }) +# --- +# name: test_sensors[window_covering_pa_lift][sensor.longan_link_wncv_da01_target_opening_position-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.longan_link_wncv_da01_target_opening_position', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Target opening position', + 'platform': 'matter', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'window_covering_target_position', + 'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-TargetPositionLiftPercent100ths-258-11', + 'unit_of_measurement': '%', + }) +# --- +# name: test_sensors[window_covering_pa_lift][sensor.longan_link_wncv_da01_target_opening_position-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Longan link WNCV DA01 Target opening position', + 'unit_of_measurement': '%', + }), + 'context': , + 'entity_id': 'sensor.longan_link_wncv_da01_target_opening_position', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '100', + }) +# --- # name: test_sensors[yandex_smart_socket][sensor.yndx_00540_current-entry] EntityRegistryEntrySnapshot({ 'aliases': set({