Fix Matter cover entity not created when tilt attribute is null for Shelly devices (#182042)

This commit is contained in:
Joachim Wilke
2026-09-13 11:13:41 +02:00
committed by GitHub
parent c9b7abfe8a
commit 37131d940c
8 changed files with 795 additions and 3 deletions
+17 -3
View File
@@ -174,9 +174,10 @@ class MatterCover(MatterEntity, CoverEntity):
self.current_cover_position,
)
if self._entity_info.endpoint.has_attribute(
has_tilt_position_attribute = self._entity_info.endpoint.has_attribute(
None, clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths
):
)
if has_tilt_position_attribute:
# current tilt position is inverted in matter (100 is closed, 0 is open)
current_cover_tilt_position = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths
@@ -206,9 +207,17 @@ class MatterCover(MatterEntity, CoverEntity):
commands = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.AcceptedCommandList
)
feature_map = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.FeatureMap
)
if clusters.WindowCovering.Commands.GoToLiftPercentage.command_id in commands:
supported_features |= CoverEntityFeature.SET_POSITION
if clusters.WindowCovering.Commands.GoToTiltPercentage.command_id in commands:
# Some devices report GoToTiltPercentage in AcceptedCommandList even
# without tilt support, so also require the FeatureMap Tilt bit.
if (
clusters.WindowCovering.Commands.GoToTiltPercentage.command_id in commands
and feature_map & clusters.WindowCovering.Bitmaps.Feature.kTilt
):
supported_features |= CoverEntityFeature.SET_TILT_POSITION
self._attr_supported_features = supported_features
@@ -271,6 +280,11 @@ DISCOVERY_SCHEMAS = [
clusters.WindowCovering.Attributes.OperationalStatus,
clusters.WindowCovering.Attributes.Type,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
),
# tilt is optional, not required: some devices (e.g. Shelly 2PM Gen4)
# report it as present but null instead of omitting it when tilt is
# disabled, which would otherwise fail schema matching
optional_attributes=(
clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
),
),
+1
View File
@@ -93,6 +93,7 @@ FIXTURES = [
"mock_window_covering_full",
"mock_window_covering_lift",
"mock_window_covering_pa_lift",
"mock_window_covering_pa_lift_null_tilt",
"mock_window_covering_pa_tilt",
"mock_window_covering_tilt",
"onoff_light_with_levelcontrol_present",
@@ -0,0 +1,315 @@
{
"node_id": 400,
"date_commissioned": "2023-03-29T08:23:30.740085",
"last_interview": "2023-03-29T08:23:30.740087",
"interview_version": 2,
"available": true,
"attributes": {
"0/29/0": [
{
"0": 22,
"1": 1
}
],
"0/29/1": [
29, 30, 31, 40, 42, 43, 44, 45, 48, 49, 50, 51, 54, 60, 62, 63, 64, 65
],
"0/29/2": [41],
"0/29/3": [1],
"0/29/65532": 0,
"0/29/65533": 1,
"0/29/65528": [],
"0/29/65529": [],
"0/29/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"0/30/0": [],
"0/30/65532": 0,
"0/30/65533": 1,
"0/30/65528": [],
"0/30/65529": [],
"0/30/65531": [0, 65528, 65529, 65531, 65532, 65533],
"0/31/0": [
{
"1": 5,
"2": 2,
"3": [112233],
"4": null,
"254": 2
}
],
"0/31/1": [],
"0/31/2": 4,
"0/31/3": 3,
"0/31/4": 3,
"0/31/65532": 0,
"0/31/65533": 1,
"0/31/65528": [],
"0/31/65529": [],
"0/31/65531": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
"0/40/0": 1,
"0/40/1": "Shelly",
"0/40/2": 4895,
"0/40/3": "Shelly 2PM Gen4",
"0/40/4": 12288,
"0/40/5": "",
"0/40/6": "XX",
"0/40/7": 1,
"0/40/8": "1.0",
"0/40/9": 1,
"0/40/10": "v1.0",
"0/40/11": "20200101",
"0/40/12": "",
"0/40/13": "",
"0/40/14": "",
"0/40/15": "mock-shelly-2pm-gen4-null-tilt-0001",
"0/40/16": false,
"0/40/17": true,
"0/40/18": "7630EF9998EDF03C",
"0/40/19": {
"0": 3,
"1": 3
},
"0/40/65532": 0,
"0/40/65533": 1,
"0/40/65528": [],
"0/40/65529": [],
"0/40/65531": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
65528, 65529, 65531, 65532, 65533
],
"0/42/0": [],
"0/42/1": true,
"0/42/2": 1,
"0/42/3": null,
"0/42/65532": 0,
"0/42/65533": 1,
"0/42/65528": [],
"0/42/65529": [0],
"0/42/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"0/43/0": "en-US",
"0/43/1": [
"en-US",
"de-DE",
"fr-FR",
"en-GB",
"es-ES",
"zh-CN",
"it-IT",
"ja-JP"
],
"0/43/65532": 0,
"0/43/65533": 1,
"0/43/65528": [],
"0/43/65529": [],
"0/43/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
"0/44/0": 0,
"0/44/1": 0,
"0/44/2": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 7],
"0/44/65532": 0,
"0/44/65533": 1,
"0/44/65528": [],
"0/44/65529": [],
"0/44/65531": [0, 1, 2, 65528, 65529, 65531, 65532, 65533],
"0/45/0": 0,
"0/45/65532": 0,
"0/45/65533": 1,
"0/45/65528": [],
"0/45/65529": [],
"0/45/65531": [0, 65528, 65529, 65531, 65532, 65533],
"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/65532": 0,
"0/48/65533": 1,
"0/48/65528": [1, 3, 5],
"0/48/65529": [0, 2, 4],
"0/48/65531": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
"0/49/0": 1,
"0/49/1": [
{
"0": "TE9OR0FOLUlPVA==",
"1": true
}
],
"0/49/2": 10,
"0/49/3": 30,
"0/49/4": true,
"0/49/5": 0,
"0/49/6": "TE9OR0FOLUlPVA==",
"0/49/7": null,
"0/49/65532": 1,
"0/49/65533": 1,
"0/49/65528": [1, 5, 7],
"0/49/65529": [0, 2, 4, 6, 8],
"0/49/65531": [0, 1, 2, 3, 4, 5, 6, 7, 65528, 65529, 65531, 65532, 65533],
"0/50/65532": 0,
"0/50/65533": 1,
"0/50/65528": [1],
"0/50/65529": [0],
"0/50/65531": [65528, 65529, 65531, 65532, 65533],
"0/51/0": [
{
"0": "WIFI_STA_DEF",
"1": true,
"2": null,
"3": null,
"4": "hPcDB5/k",
"5": ["wKgIhg=="],
"6": ["/oAAAAAAAACG9wP//gef5A==", "JA4DsgZ+bsCG9wP//gef5A=="],
"7": 1
}
],
"0/51/1": 35,
"0/51/2": 123,
"0/51/3": 0,
"0/51/4": 6,
"0/51/5": [],
"0/51/6": [],
"0/51/7": [],
"0/51/8": false,
"0/51/65532": 0,
"0/51/65533": 1,
"0/51/65528": [],
"0/51/65529": [0],
"0/51/65531": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65531, 65532, 65533
],
"0/54/0": "mJfMGB1w",
"0/54/1": 0,
"0/54/2": 3,
"0/54/3": 1,
"0/54/4": -36,
"0/54/65532": 0,
"0/54/65533": 1,
"0/54/65528": [],
"0/54/65529": [],
"0/54/65531": [0, 1, 2, 3, 4, 65528, 65529, 65531, 65532, 65533],
"0/60/0": 0,
"0/60/1": null,
"0/60/2": null,
"0/60/65532": 0,
"0/60/65533": 1,
"0/60/65528": [],
"0/60/65529": [0, 1, 2],
"0/60/65531": [0, 1, 2, 65528, 65529, 65531, 65532, 65533],
"0/62/0": [
{
"1": "FTABAQEkAgE3AyQTAhgmBIAigScmBYAlTTo3BiQVASQRARgkBwEkCAEwCUEE5Rw88GvXEUXr+cPYgKd00rIWyiHM8eu4Bhrzf1v83yBI2Qa+pwfOsKyvzxiuHLMfzhdC3gre4najpimi8AsX+TcKNQEoARgkAgE2AwQCBAEYMAQUWh6NlHAMbG5gz+vqlF51fulr3z8wBRR+D1hE33RhFC/mJWrhhZs6SVStQBgwC0DD5IxVgOrftUA47K1bQHaCNuWqIxf/8oMfcI0nMvTtXApwbBAJI/LjjCwMZJVFBE3W/FC6dQWSEuF8ES745tLBGA==",
"2": "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQTAhgkBwEkCAEwCUEEzpstYxy3lXF69g6H2vQ6uoqkdUsppJ4NcSyQcXQ8sQrF5HuzoVnDpevHfy0GAWHbXfE4VI0laTHvm/Wkj037ZjcKNQEpARgkAmAwBBR+D1hE33RhFC/mJWrhhZs6SVStQDAFFFCCK5NYv6CrD5/0S26zXBUwG0WBGDALQI5YKo3C3xvdqCrho2yZIJVJpJY2n9V/tmh7ESBBOHrY0b+K8Pf7hKhd5V0vzbCCbkhv1BNEne+lhcS2N6qhMNgY",
"254": 2
}
],
"0/62/1": [
{
"1": "BFLMrM1satBpU0DN4sri/S4AVo/ugmZCndBfPO33Q+ZCKDZzNhMOB014+hZs0KL7vPssavT7Tb9nt0W+kpeAe0U=",
"2": 65521,
"3": 1,
"4": 1,
"5": "",
"254": 2
}
],
"0/62/2": 5,
"0/62/3": 2,
"0/62/4": [
"FTABAQAkAgE3AycUBZIG4P1iqI0kFQEYJgRBkLUrJgXBw5YtNwYnFAWSBuD9YqiNJBUBGCQHASQIATAJQQRruztKRDFfiVjMY19sSsnKqBZJlZrQ/ClUtTYatvOZxbTC53iCqhwHaIJthMWs7ICwtSX1Vr5lGkzDXQjH/oQ6Nwo1ASkBGCQCYDAEFJd2wRMLYsFFA1PRCdMviVipH3OWMAUUl3bBEwtiwUUDU9EJ0y+JWKkfc5YYMAtASJa3FJ84kws+OOWNEMgRvcZA/d0AJVmmoqoWrorxxfpVKujZuN8Kc193rwBckfxd69s3OS1y8HCZTtooCemIpBg=",
"FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQUARgkBwEkCAEwCUEEUsyszWxq0GlTQM3iyuL9LgBWj+6CZkKd0F887fdD5kIoNnM2Ew4HTXj6FmzQovu8+yxq9PtNv2e3Rb6Sl4B7RTcKNQEpARgkAmAwBBRQgiuTWL+gqw+f9Etus1wVMBtFgTAFFFCCK5NYv6CrD5/0S26zXBUwG0WBGDALQFyHXux9szIosC1gP+/1/7BX3PfGaX2GF172oHSAoMXnLJ7OawkzgWIykEj7oRIjKv3XRR27y3KhV83817SfCOkY"
],
"0/62/5": 2,
"0/62/65532": 0,
"0/62/65533": 1,
"0/62/65528": [1, 3, 5, 8],
"0/62/65529": [0, 2, 4, 6, 7, 9, 10, 11],
"0/62/65531": [0, 1, 2, 3, 4, 5, 65528, 65529, 65531, 65532, 65533],
"0/63/0": [],
"0/63/1": [],
"0/63/2": 3,
"0/63/3": 3,
"0/63/65532": 0,
"0/63/65533": 1,
"0/63/65528": [2, 5],
"0/63/65529": [0, 1, 3, 4],
"0/63/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"0/64/0": [
{
"0": "room",
"1": "bedroom 2"
},
{
"0": "orientation",
"1": "North"
},
{
"0": "floor",
"1": "2"
},
{
"0": "direction",
"1": "up"
}
],
"0/64/65532": 0,
"0/64/65533": 1,
"0/64/65528": [],
"0/64/65529": [],
"0/64/65531": [0, 65528, 65529, 65531, 65532, 65533],
"0/65/0": [],
"0/65/65532": 0,
"0/65/65533": 1,
"0/65/65528": [],
"0/65/65529": [],
"0/65/65531": [0, 65528, 65529, 65531, 65532, 65533],
"1/3/0": 0,
"1/3/1": 2,
"1/3/65532": 0,
"1/3/65533": 4,
"1/3/65528": [],
"1/3/65529": [0, 64],
"1/3/65531": [0, 1, 65528, 65529, 65531, 65532, 65533],
"1/4/0": 128,
"1/4/65532": 1,
"1/4/65533": 4,
"1/4/65528": [0, 1, 2, 3],
"1/4/65529": [0, 1, 2, 3, 4, 5],
"1/4/65531": [0, 65528, 65529, 65531, 65532, 65533],
"1/29/0": [
{
"0": 514,
"1": 1
}
],
"1/29/1": [3, 4, 29, 258],
"1/29/2": [],
"1/29/3": [],
"1/29/65532": 0,
"1/29/65533": 1,
"1/29/65528": [],
"1/29/65529": [],
"1/29/65531": [0, 1, 2, 3, 65528, 65529, 65531, 65532, 65533],
"1/258/0": 0,
"1/258/7": 13,
"1/258/8": 0,
"1/258/10": 0,
"1/258/11": 6,
"1/258/13": 0,
"1/258/14": 6,
"1/258/23": 1,
"1/258/65532": 5,
"1/258/65533": 5,
"1/258/65528": [],
"1/258/65529": [0, 1, 2, 5, 8],
"1/258/65531": [
0, 7, 8, 9, 10, 11, 12, 13, 14, 15, 23, 26, 65528, 65529, 65531, 65532,
65533
],
"1/258/9": null,
"1/258/12": null,
"1/258/15": null,
"1/258/26": 0
},
"attribute_subscriptions": []
}
@@ -2436,6 +2436,57 @@
'state': 'off',
})
# ---
# name: test_binary_sensors[mock_window_covering_pa_lift_null_tilt][binary_sensor.shelly_2pm_gen4_configuration_status-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': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.shelly_2pm_gen4_configuration_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Configuration status',
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.PROBLEM: 'problem'>,
'original_icon': None,
'original_name': 'Configuration status',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'config_status_operational',
'unique_id': '00000000000004D2-0000000000000190-MatterNodeDevice-1-WindowCoveringConfigStatusOperational-258-7',
'unit_of_measurement': None,
})
# ---
# name: test_binary_sensors[mock_window_covering_pa_lift_null_tilt][binary_sensor.shelly_2pm_gen4_configuration_status-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'problem',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Shelly 2PM Gen4 Configuration status',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.shelly_2pm_gen4_configuration_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_binary_sensors[mock_window_covering_pa_tilt][binary_sensor.mock_pa_tilt_window_covering_configuration_status-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
@@ -3809,6 +3809,57 @@
'state': 'unknown',
})
# ---
# name: test_buttons[mock_window_covering_pa_lift_null_tilt][button.shelly_2pm_gen4_identify-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': 'button',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'button.shelly_2pm_gen4_identify',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Identify',
'options': dict({
}),
'original_device_class': <ButtonDeviceClass.IDENTIFY: 'identify'>,
'original_icon': None,
'original_name': 'Identify',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '00000000000004D2-0000000000000190-MatterNodeDevice-1-IdentifyButton-3-1',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[mock_window_covering_pa_lift_null_tilt][button.shelly_2pm_gen4_identify-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'identify',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Shelly 2PM Gen4 Identify',
}),
'context': <ANY>,
'entity_id': 'button.shelly_2pm_gen4_identify',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_buttons[onoff_light_with_levelcontrol_present][button.d215s_identify-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
@@ -269,6 +269,60 @@
'state': 'open',
})
# ---
# name: test_covers[mock_window_covering_pa_lift_null_tilt][cover.shelly_2pm_gen4-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': 'cover',
'entity_category': None,
'entity_id': 'cover.shelly_2pm_gen4',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': None,
'options': dict({
}),
'original_device_class': <CoverDeviceClass.SHADE: 'shade'>,
'original_icon': None,
'original_name': None,
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': <CoverEntityFeature: 15>,
'translation_key': None,
'unique_id': '00000000000004D2-0000000000000190-MatterNodeDevice-1-MatterCoverPositionAwareLiftAndTilt-258-10',
'unit_of_measurement': None,
})
# ---
# name: test_covers[mock_window_covering_pa_lift_null_tilt][cover.shelly_2pm_gen4-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<CoverEntityStateAttribute.CURRENT_POSITION: 'current_position'>: 100,
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'shade',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Shelly 2PM Gen4',
<CoverEntityStateAttribute.IS_CLOSED: 'is_closed'>: False,
<EntityStateAttribute.SUPPORTED_FEATURES: 'supported_features'>: <CoverEntityFeature: 15>,
}),
'context': <ANY>,
'entity_id': 'cover.shelly_2pm_gen4',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'open',
})
# ---
# name: test_covers[mock_window_covering_pa_tilt][cover.mock_pa_tilt_window_covering-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
@@ -25721,6 +25721,286 @@
'state': '-36',
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_boot_reason-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'unspecified',
'power_on_reboot',
'brown_out_reset',
'software_watchdog_reset',
'hardware_watchdog_reset',
'software_update_completed',
'software_reset',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.shelly_2pm_gen4_boot_reason',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Boot reason',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Boot reason',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'boot_reason',
'unique_id': '00000000000004D2-0000000000000190-MatterNodeDevice-0-GeneralDiagnosticsBootReason-51-4',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_boot_reason-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'enum',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Shelly 2PM Gen4 Boot reason',
<SensorEntityCapabilityAttribute.OPTIONS: 'options'>: list([
'unspecified',
'power_on_reboot',
'brown_out_reset',
'software_watchdog_reset',
'hardware_watchdog_reset',
'software_update_completed',
'software_reset',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.shelly_2pm_gen4_boot_reason',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'software_reset',
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_reboot_count-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.shelly_2pm_gen4_reboot_count',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Reboot count',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Reboot count',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'reboot_count',
'unique_id': '00000000000004D2-0000000000000190-MatterNodeDevice-0-GeneralDiagnosticsRebootCount-51-1',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_reboot_count-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Shelly 2PM Gen4 Reboot count',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'context': <ANY>,
'entity_id': 'sensor.shelly_2pm_gen4_reboot_count',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '35',
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_target_opening_position-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': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.shelly_2pm_gen4_target_opening_position',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Target opening position',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Target opening position',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'window_covering_target_position',
'unique_id': '00000000000004D2-0000000000000190-MatterNodeDevice-1-TargetPositionLiftPercent100ths-258-11',
'unit_of_measurement': <UnitOfRatio.PERCENTAGE: '%'>,
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_target_opening_position-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Shelly 2PM Gen4 Target opening position',
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfRatio.PERCENTAGE: '%'>,
}),
'context': <ANY>,
'entity_id': 'sensor.shelly_2pm_gen4_target_opening_position',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '100',
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_uptime-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': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.shelly_2pm_gen4_uptime',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Uptime',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.UPTIME: 'uptime'>,
'original_icon': None,
'original_name': 'Uptime',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'uptime',
'unique_id': '00000000000004D2-0000000000000190-MatterNodeDevice-0-GeneralDiagnosticsUpTime-51-2',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_uptime-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'uptime',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Shelly 2PM Gen4 Uptime',
}),
'context': <ANY>,
'entity_id': 'sensor.shelly_2pm_gen4_uptime',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2025-01-01T13:57:57+00:00',
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_wi_fi_rssi-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.shelly_2pm_gen4_wi_fi_rssi',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Wi-Fi RSSI',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.SIGNAL_STRENGTH: 'signal_strength'>,
'original_icon': None,
'original_name': 'Wi-Fi RSSI',
'platform': 'matter',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'wifi_rssi',
'unique_id': '00000000000004D2-0000000000000190-MatterNodeDevice-0-WiFiDiagnosticsRssi-54-4',
'unit_of_measurement': 'dBm',
})
# ---
# name: test_sensors[mock_window_covering_pa_lift_null_tilt][sensor.shelly_2pm_gen4_wi_fi_rssi-state]
StateSnapshot({
'attributes': ReadOnlyDict({
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'signal_strength',
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Shelly 2PM Gen4 Wi-Fi RSSI',
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.MEASUREMENT: 'measurement'>,
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: 'dBm',
}),
'context': <ANY>,
'entity_id': 'sensor.shelly_2pm_gen4_wi_fi_rssi',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '-36',
})
# ---
# name: test_sensors[mock_window_covering_pa_tilt][sensor.mock_pa_tilt_window_covering_reboot_count-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
+26
View File
@@ -51,6 +51,7 @@ async def test_covers(
[
("mock_window_covering_lift", "cover.mock_lift_window_covering"),
("mock_window_covering_pa_lift", "cover.longan_link_wncv_da01"),
("mock_window_covering_pa_lift_null_tilt", "cover.shelly_2pm_gen4"),
("mock_window_covering_tilt", "cover.mock_tilt_window_covering"),
("mock_window_covering_pa_tilt", "cover.mock_pa_tilt_window_covering"),
("mock_window_covering_full", "cover.mock_full_window_covering"),
@@ -121,6 +122,7 @@ async def test_cover(
[
("mock_window_covering_lift", "cover.mock_lift_window_covering"),
("mock_window_covering_pa_lift", "cover.longan_link_wncv_da01"),
("mock_window_covering_pa_lift_null_tilt", "cover.shelly_2pm_gen4"),
("mock_window_covering_full", "cover.mock_full_window_covering"),
],
)
@@ -248,6 +250,30 @@ async def test_cover_position_aware_lift(
assert state.state == CoverState.CLOSED
@pytest.mark.parametrize(
("node_fixture", "entity_id"),
[
("mock_window_covering_pa_lift_null_tilt", "cover.shelly_2pm_gen4"),
],
)
async def test_cover_position_aware_lift_null_tilt(
hass: HomeAssistant,
matter_node: MatterNode,
entity_id: str,
) -> None:
"""Test null tilt attribute while tilt is disabled (Shelly 2PM Gen4).
See home-assistant/core#149876.
"""
state = hass.states.get(entity_id)
assert state
assert (
state.attributes["supported_features"] & CoverEntityFeature.SET_TILT_POSITION
== 0
)
assert "current_tilt_position" not in state.attributes
@pytest.mark.parametrize(
("node_fixture", "entity_id"),
[