From 206ee89ece2b7b3e8a9707f910bcb7c8ac48f21c Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Tue, 18 Aug 2026 18:41:35 +1000 Subject: [PATCH 01/14] RFC: refactor quirk for Yale locks - Moved to quirk v2 - Exposed a few state sensors --- zhaquirks/yale/lock.py | 111 +++++++++++++++++++++++++++++++++++ zhaquirks/yale/realliving.py | 108 ---------------------------------- 2 files changed, 111 insertions(+), 108 deletions(-) create mode 100644 zhaquirks/yale/lock.py delete mode 100644 zhaquirks/yale/realliving.py diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py new file mode 100644 index 0000000000..e02ef1b1b5 --- /dev/null +++ b/zhaquirks/yale/lock.py @@ -0,0 +1,111 @@ +"""Device handler for Yale Zigbee Network Modules.""" + +from zigpy.zcl.clusters.closures import DoorLock + +from zhaquirks import DoublingPowerConfigurationCluster +from zhaquirks.builder import ( + QuirkBuilder, + ReportingConfig, + SensorDeviceClass, + UnitOfTime, +) + + +def lang_converter(lang: str) -> str: + """Convert language string into frontend UI strings.""" + if lang is None: + return None + + mapping = { + "en": "English", + "fr": "French", + "es": "Spanish", + } + return mapping.get(lang.strip().lower(), lang) + +def sound_volume_converter(level: int | None) -> str | None: + """Convert lock volume int into frontend UI strings.""" + if level is None: + return None + + mapping = { + 0: "Silent", + 1: "Low volume", + 2: "High volume", + } + return mapping.get(level, f"Unknown ({level})") + +( + QuirkBuilder("Yale", "YRD220/240 TSDB") + .applies_to("Yale", "YRD210 PB DB") + .applies_to("Yale", "YRL220 TS LL") + .replaces(DoublingPowerConfigurationCluster) + .binary_sensor( + endpoint_id=1, + cluster_id=DoorLock.cluster_id, + attribute_name=DoorLock.AttributeDefs.enable_one_touch_locking.name, + reporting_config=ReportingConfig( + min_interval=300, + max_interval=3600, + ), + unique_id_suffix="one_touch_locking_enabled", + translation_key="one_touch_locking_enabled", + fallback_name="One touch locking enabled", + ) + .binary_sensor( + endpoint_id=1, + cluster_id=DoorLock.cluster_id, + attribute_name=DoorLock.AttributeDefs.enable_inside_status_led.name, + reporting_config=ReportingConfig( + min_interval=300, + max_interval=3600, + ), + unique_id_suffix="inside_status_led_enabled", + translation_key="inside_status_led_enabled", + fallback_name="Inside status LED enabled", + ) + .sensor( + endpoint_id=1, + cluster_id=DoorLock.cluster_id, + attribute_name=DoorLock.AttributeDefs.auto_relock_time.name, + reporting_config=ReportingConfig( + min_interval=300, + max_interval=3600, + reportable_change=1, + ), + unique_id_suffix="auto_relock_time", + device_class=SensorDeviceClass.DURATION, + unit=UnitOfTime.SECONDS, + translation_key="automatic_relock_time", + fallback_name="Automatic relock time", + ) + .sensor( + endpoint_id=1, + cluster_id=DoorLock.cluster_id, + attribute_name=DoorLock.AttributeDefs.language.name, + reporting_config=ReportingConfig( + min_interval=300, + max_interval=3600, + ), + unique_id_suffix="announcement_language", + device_class=SensorDeviceClass.ENUM, + attribute_converter=lang_converter, + translation_key="announcement_language", + fallback_name="Announcement language", + ) + .sensor( + endpoint_id=1, + cluster_id=DoorLock.cluster_id, + attribute_name=DoorLock.AttributeDefs.sound_volume.name, + reporting_config=ReportingConfig( + min_interval=300, + max_interval=3600, + ), + unique_id_suffix="keypad_sound_volume", + device_class=SensorDeviceClass.ENUM, + attribute_converter=sound_volume_converter, + translation_key="keypad_sound_volume", + fallback_name="Keypad sound volume", + ) + .add_to_registry() +) diff --git a/zhaquirks/yale/realliving.py b/zhaquirks/yale/realliving.py deleted file mode 100644 index 383fb13e96..0000000000 --- a/zhaquirks/yale/realliving.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Device handler for Yale Real Living.""" - -from zigpy.profiles import zha -from zigpy.zcl.clusters.closures import DoorLock -from zigpy.zcl.clusters.general import Alarms, Basic, Identify, Ota, PollControl, Time - -from zhaquirks import DoublingPowerConfigurationCluster -from zhaquirks.const import ( - DEVICE_TYPE, - ENDPOINTS, - INPUT_CLUSTERS, - MODELS_INFO, - OUTPUT_CLUSTERS, - PROFILE_ID, -) -from zhaquirks.legacy import CustomDevice - - -class YRD210PBDB220TSLL(CustomDevice): - """Yale YRD210 PB BP and Yale YRL220 TS LL Locks.""" - - signature = { - # - MODELS_INFO: [("Yale", "YRD210 PB DB"), ("Yale", "YRL220 TS LL")], - ENDPOINTS: { - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.DOOR_LOCK, - INPUT_CLUSTERS: [ - Basic.cluster_id, - DoublingPowerConfigurationCluster.cluster_id, - Identify.cluster_id, - Alarms.cluster_id, - Time.cluster_id, - DoorLock.cluster_id, - PollControl.cluster_id, - ], - OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], - } - }, - } - - replacement = { - ENDPOINTS: { - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.DOOR_LOCK, - INPUT_CLUSTERS: [ - Basic.cluster_id, - DoublingPowerConfigurationCluster, - Identify.cluster_id, - Alarms.cluster_id, - Time.cluster_id, - DoorLock.cluster_id, - PollControl.cluster_id, - ], - OUTPUT_CLUSTERS: [DoorLock.cluster_id, Ota.cluster_id], - } - } - } - - -class YRD220240TSDB(CustomDevice): - """Yale YRD220/240 TSDB Lock.""" - - signature = { - # - MODELS_INFO: [("Yale", "YRD220/240 TSDB")], - ENDPOINTS: { - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.DOOR_LOCK, - INPUT_CLUSTERS: [ - Basic.cluster_id, - DoublingPowerConfigurationCluster.cluster_id, - Alarms.cluster_id, - Time.cluster_id, - PollControl.cluster_id, - DoorLock.cluster_id, - ], - OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], - } - }, - } - - replacement = { - ENDPOINTS: { - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.DOOR_LOCK, - INPUT_CLUSTERS: [ - Basic.cluster_id, - DoublingPowerConfigurationCluster, - Alarms.cluster_id, - Time.cluster_id, - PollControl.cluster_id, - DoorLock.cluster_id, - ], - OUTPUT_CLUSTERS: [DoorLock.cluster_id, Ota.cluster_id], - } - } - } From cdf8dd816edb7bcc66f94a8c226aeaf9734b8395 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 08:49:29 +0000 Subject: [PATCH 02/14] Apply pre-commit auto fixes --- zhaquirks/yale/lock.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index e02ef1b1b5..cfafa8478a 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -23,6 +23,7 @@ def lang_converter(lang: str) -> str: } return mapping.get(lang.strip().lower(), lang) + def sound_volume_converter(level: int | None) -> str | None: """Convert lock volume int into frontend UI strings.""" if level is None: @@ -35,6 +36,7 @@ def sound_volume_converter(level: int | None) -> str | None: } return mapping.get(level, f"Unknown ({level})") + ( QuirkBuilder("Yale", "YRD220/240 TSDB") .applies_to("Yale", "YRD210 PB DB") From d1d0d45c173c6df4ec2f378023d53bc82f095fc4 Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Tue, 18 Aug 2026 20:29:23 +1000 Subject: [PATCH 03/14] remove unreachable statement --- zhaquirks/yale/lock.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index cfafa8478a..0c2dd4c79d 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -13,9 +13,6 @@ def lang_converter(lang: str) -> str: """Convert language string into frontend UI strings.""" - if lang is None: - return None - mapping = { "en": "English", "fr": "French", @@ -26,9 +23,6 @@ def lang_converter(lang: str) -> str: def sound_volume_converter(level: int | None) -> str | None: """Convert lock volume int into frontend UI strings.""" - if level is None: - return None - mapping = { 0: "Silent", 1: "Low volume", From a5e6c5b75d3f81346a0d8a70c7d10ec7636ffe6f Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Tue, 18 Aug 2026 20:44:30 +1000 Subject: [PATCH 04/14] add reportable_change to binary sensors --- zhaquirks/yale/lock.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index 0c2dd4c79d..26ac7aa494 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -41,8 +41,9 @@ def sound_volume_converter(level: int | None) -> str | None: cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.enable_one_touch_locking.name, reporting_config=ReportingConfig( - min_interval=300, - max_interval=3600, + min_interval=30, + max_interval=900, + reportable_change=1, ), unique_id_suffix="one_touch_locking_enabled", translation_key="one_touch_locking_enabled", @@ -53,8 +54,9 @@ def sound_volume_converter(level: int | None) -> str | None: cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.enable_inside_status_led.name, reporting_config=ReportingConfig( - min_interval=300, - max_interval=3600, + min_interval=30, + max_interval=900, + reportable_change=1, ), unique_id_suffix="inside_status_led_enabled", translation_key="inside_status_led_enabled", @@ -65,8 +67,8 @@ def sound_volume_converter(level: int | None) -> str | None: cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.auto_relock_time.name, reporting_config=ReportingConfig( - min_interval=300, - max_interval=3600, + min_interval=30, + max_interval=900, reportable_change=1, ), unique_id_suffix="auto_relock_time", @@ -80,8 +82,9 @@ def sound_volume_converter(level: int | None) -> str | None: cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.language.name, reporting_config=ReportingConfig( - min_interval=300, - max_interval=3600, + min_interval=30, + max_interval=900, + reportable_change=1, ), unique_id_suffix="announcement_language", device_class=SensorDeviceClass.ENUM, @@ -94,8 +97,9 @@ def sound_volume_converter(level: int | None) -> str | None: cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.sound_volume.name, reporting_config=ReportingConfig( - min_interval=300, - max_interval=3600, + min_interval=30, + max_interval=900, + reportable_change=1 ), unique_id_suffix="keypad_sound_volume", device_class=SensorDeviceClass.ENUM, From c231b8011f9570b60a4eed32f3422fbd19aff3fc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 10:44:42 +0000 Subject: [PATCH 05/14] Apply pre-commit auto fixes --- zhaquirks/yale/lock.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index 26ac7aa494..1f9dca72ba 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -97,9 +97,7 @@ def sound_volume_converter(level: int | None) -> str | None: cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.sound_volume.name, reporting_config=ReportingConfig( - min_interval=30, - max_interval=900, - reportable_change=1 + min_interval=30, max_interval=900, reportable_change=1 ), unique_id_suffix="keypad_sound_volume", device_class=SensorDeviceClass.ENUM, From ae77a1771d58ce9d6dde7ac7ea9d66c5f32f08a7 Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:04:47 +1000 Subject: [PATCH 06/14] fix formatting --- zhaquirks/yale/lock.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index 1f9dca72ba..73c285228e 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -97,7 +97,9 @@ def sound_volume_converter(level: int | None) -> str | None: cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.sound_volume.name, reporting_config=ReportingConfig( - min_interval=30, max_interval=900, reportable_change=1 + min_interval=30, + max_interval=900, + reportable_change=1, ), unique_id_suffix="keypad_sound_volume", device_class=SensorDeviceClass.ENUM, From 010ddc5fd8a9c61ace36297e0237e11affc25bd4 Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:05:12 +1000 Subject: [PATCH 07/14] remove v1 tests v2 tests are not needed as this is purely declarative --- tests/test_quirks.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_quirks.py b/tests/test_quirks.py index 36f68f1ee3..b818e5ad79 100644 --- a/tests/test_quirks.py +++ b/tests/test_quirks.py @@ -913,9 +913,6 @@ def check_for_duplicate_cluster_ids(clusters) -> None: zhaquirks.ikea.twobtnremote.IkeaTradfriRemote2BtnZLL, # # -- other devices -- - # adds DoorLock cluster to output clusters (Yale door locks): - zhaquirks.yale.realliving.YRD210PBDB220TSLL, - zhaquirks.yale.realliving.YRD220240TSDB, # remove LevelControl input cluster (Adurolight remote): zhaquirks.aduro.adurolightncc.AdurolightNCC, # add a bunch of output clusters (Zhongxing motion sensor): From 0827e600f2c7cd05b9aae5d63e6432893fb831b0 Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:30:11 +1000 Subject: [PATCH 08/14] ah tests --- tests/test_yale.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/test_yale.py diff --git a/tests/test_yale.py b/tests/test_yale.py new file mode 100644 index 0000000000..859abea550 --- /dev/null +++ b/tests/test_yale.py @@ -0,0 +1,42 @@ +"""Tests for Yale locks.""" + +import pytest + +import zhaquirks +from zhaquirks.yale.lock import lang_converter, sound_volume_converter + +zhaquirks.setup() + +@pytest.mark.parametrize( + ("raw_lang", "expected"), + [ + # accounted for mappings + ("en", "English"), + ("fr", "French"), + ("es", "Spanish"), + # fallback + ("de", "de"), + ("unknown_code", "unknown_code"), + ], +) +def test_lang_converter(raw_lang: str, expected: str): + """Test language converter.""" + assert lang_converter(raw_lang) == expected + + +@pytest.mark.parametrize( + ("raw_level", "expected"), + [ + # accounted for mappings + (0, "Silent"), + (1, "Low volume"), + (2, "High volume"), + # fallback + (3, "Unknown (3)"), + (-1, "Unknown (-1)"), + (None, "Unknown (None)"), + ], +) +def test_sound_volume_converter(raw_level: int | None, expected: str): + """Test sound volume level conversions.""" + assert sound_volume_converter(raw_level) == expected From ba03aa8364d889a9f500f2cfd197710a6f465b73 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 11:30:23 +0000 Subject: [PATCH 09/14] Apply pre-commit auto fixes --- tests/test_yale.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_yale.py b/tests/test_yale.py index 859abea550..f6bac193fd 100644 --- a/tests/test_yale.py +++ b/tests/test_yale.py @@ -7,6 +7,7 @@ zhaquirks.setup() + @pytest.mark.parametrize( ("raw_lang", "expected"), [ From b118ebdeaade9511a53ec1eb8d867df369a39aa2 Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Fri, 21 Aug 2026 21:08:25 +1000 Subject: [PATCH 10/14] move some sensors to become numbers and switches --- zhaquirks/yale/lock.py | 69 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index 73c285228e..8dd48ee790 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -4,6 +4,7 @@ from zhaquirks import DoublingPowerConfigurationCluster from zhaquirks.builder import ( + NumberDeviceClass, QuirkBuilder, ReportingConfig, SensorDeviceClass, @@ -37,53 +38,59 @@ def sound_volume_converter(level: int | None) -> str | None: .applies_to("Yale", "YRL220 TS LL") .replaces(DoublingPowerConfigurationCluster) .binary_sensor( - endpoint_id=1, + attribute_name=DoorLock.AttributeDefs.auto_relock_time.name, cluster_id=DoorLock.cluster_id, - attribute_name=DoorLock.AttributeDefs.enable_one_touch_locking.name, + attribute_converter=lambda value: bool(value > 0), reporting_config=ReportingConfig( - min_interval=30, - max_interval=900, + min_interval=3600, + max_interval=10800, reportable_change=1, ), - unique_id_suffix="one_touch_locking_enabled", - translation_key="one_touch_locking_enabled", - fallback_name="One touch locking enabled", + unique_id_suffix="auto_relock_enabled", + translation_key="auto_relock_enabled", + fallback_name="Auto-relock enabled", ) - .binary_sensor( - endpoint_id=1, + .switch( + attribute_name=DoorLock.AttributeDefs.enable_one_touch_locking.name, cluster_id=DoorLock.cluster_id, + unique_id_suffix="one_touch_locking", + translation_key="one_touch_locking", + fallback_name="One touch locking", + ) + .switch( attribute_name=DoorLock.AttributeDefs.enable_inside_status_led.name, - reporting_config=ReportingConfig( - min_interval=30, - max_interval=900, - reportable_change=1, - ), - unique_id_suffix="inside_status_led_enabled", - translation_key="inside_status_led_enabled", - fallback_name="Inside status LED enabled", + cluster_id=DoorLock.cluster_id, + unique_id_suffix="inside_status_led", + translation_key="inside_status_led", + fallback_name="Inside status LED", ) - .sensor( - endpoint_id=1, + .switch( + attribute_name=DoorLock.AttributeDefs.enable_privacy_mode_button.name, cluster_id=DoorLock.cluster_id, + unique_id_suffix="button_privacy", + translation_key="button_privacy", + fallback_name="Button privacy", + ) + .number( attribute_name=DoorLock.AttributeDefs.auto_relock_time.name, - reporting_config=ReportingConfig( - min_interval=30, - max_interval=900, - reportable_change=1, - ), + cluster_id=DoorLock.cluster_id, + step=10, + min_value=10, + max_value=90, + multiplier=1, unique_id_suffix="auto_relock_time", - device_class=SensorDeviceClass.DURATION, + device_class=NumberDeviceClass.DURATION, unit=UnitOfTime.SECONDS, - translation_key="automatic_relock_time", - fallback_name="Automatic relock time", + translation_key="auto_relock_time", + fallback_name="Auto-relock time", ) .sensor( endpoint_id=1, cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.language.name, reporting_config=ReportingConfig( - min_interval=30, - max_interval=900, + min_interval=3600, + max_interval=10800, reportable_change=1, ), unique_id_suffix="announcement_language", @@ -97,8 +104,8 @@ def sound_volume_converter(level: int | None) -> str | None: cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.sound_volume.name, reporting_config=ReportingConfig( - min_interval=30, - max_interval=900, + min_interval=3600, + max_interval=10800, reportable_change=1, ), unique_id_suffix="keypad_sound_volume", From 6b668bad3cc5f8935e553ebb9bd2f1b131609e2e Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Fri, 21 Aug 2026 21:11:24 +1000 Subject: [PATCH 11/14] remove reporting config for volume and language lock doesnt support --- zhaquirks/yale/lock.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index 8dd48ee790..bb9235c5c2 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -88,11 +88,6 @@ def sound_volume_converter(level: int | None) -> str | None: endpoint_id=1, cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.language.name, - reporting_config=ReportingConfig( - min_interval=3600, - max_interval=10800, - reportable_change=1, - ), unique_id_suffix="announcement_language", device_class=SensorDeviceClass.ENUM, attribute_converter=lang_converter, @@ -103,11 +98,6 @@ def sound_volume_converter(level: int | None) -> str | None: endpoint_id=1, cluster_id=DoorLock.cluster_id, attribute_name=DoorLock.AttributeDefs.sound_volume.name, - reporting_config=ReportingConfig( - min_interval=3600, - max_interval=10800, - reportable_change=1, - ), unique_id_suffix="keypad_sound_volume", device_class=SensorDeviceClass.ENUM, attribute_converter=sound_volume_converter, From 4f78524c54b571cf82c13187ffa42c67c19ef6c4 Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Sun, 23 Aug 2026 16:49:13 +1000 Subject: [PATCH 12/14] add selection for operating mode --- zhaquirks/yale/lock.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index bb9235c5c2..520d52ff6b 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -1,5 +1,6 @@ """Device handler for Yale Zigbee Network Modules.""" +from zigpy import types as t from zigpy.zcl.clusters.closures import DoorLock from zhaquirks import DoublingPowerConfigurationCluster @@ -31,6 +32,12 @@ def sound_volume_converter(level: int | None) -> str | None: } return mapping.get(level, f"Unknown ({level})") +class yale_lock_mode(t.enum8): + """Lock operation mode enum.""" + + Normal = 0x00 + Vacation = 0x01 + Privacy = 0x02 ( QuirkBuilder("Yale", "YRD220/240 TSDB") @@ -71,6 +78,14 @@ def sound_volume_converter(level: int | None) -> str | None: translation_key="button_privacy", fallback_name="Button privacy", ) + .enum( + attribute_name=DoorLock.AttributeDefs.operating_mode.name, + cluster_id=DoorLock.cluster_id, + enum_class=yale_lock_mode, + unique_id_suffix="operating_mode", + translation_key="operating_mode", + fallback_name="Operating mode", + ) .number( attribute_name=DoorLock.AttributeDefs.auto_relock_time.name, cluster_id=DoorLock.cluster_id, From d4782d4c74b63334cfe0890e64d41e563ae72e4a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 06:49:26 +0000 Subject: [PATCH 13/14] Apply pre-commit auto fixes --- zhaquirks/yale/lock.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index 520d52ff6b..2da6436163 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -32,6 +32,7 @@ def sound_volume_converter(level: int | None) -> str | None: } return mapping.get(level, f"Unknown ({level})") + class yale_lock_mode(t.enum8): """Lock operation mode enum.""" @@ -39,6 +40,7 @@ class yale_lock_mode(t.enum8): Vacation = 0x01 Privacy = 0x02 + ( QuirkBuilder("Yale", "YRD220/240 TSDB") .applies_to("Yale", "YRD210 PB DB") From 7952329ad0d35695d095b0d3b5316d74481ceb7a Mon Sep 17 00:00:00 2001 From: Irene <12470297+cydia2020@users.noreply.github.com> Date: Sun, 23 Aug 2026 16:50:43 +1000 Subject: [PATCH 14/14] extend allowed auto relock time to be 180 --- zhaquirks/yale/lock.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zhaquirks/yale/lock.py b/zhaquirks/yale/lock.py index 2da6436163..bdecca1e90 100644 --- a/zhaquirks/yale/lock.py +++ b/zhaquirks/yale/lock.py @@ -93,8 +93,7 @@ class yale_lock_mode(t.enum8): cluster_id=DoorLock.cluster_id, step=10, min_value=10, - max_value=90, - multiplier=1, + max_value=180, unique_id_suffix="auto_relock_time", device_class=NumberDeviceClass.DURATION, unit=UnitOfTime.SECONDS,