-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Port Yale lock quirks to quirks v2 and add lock setting entities #5262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
206ee89
cdf8dd8
d1d0d45
a5e6c5b
c231b80
ae77a17
010ddc5
0827e60
ba03aa8
b118ebd
6b668ba
4f78524
d4782d4
7952329
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| """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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| """Device handler for Yale Zigbee Network Modules.""" | ||
|
|
||
| from zigpy import types as t | ||
| from zigpy.zcl.clusters.closures import DoorLock | ||
|
|
||
| from zhaquirks import DoublingPowerConfigurationCluster | ||
| from zhaquirks.builder import ( | ||
| NumberDeviceClass, | ||
| QuirkBuilder, | ||
| ReportingConfig, | ||
| SensorDeviceClass, | ||
| UnitOfTime, | ||
| ) | ||
|
|
||
|
|
||
| def lang_converter(lang: str) -> str: | ||
| """Convert language string into frontend UI strings.""" | ||
| 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.""" | ||
| mapping = { | ||
| 0: "Silent", | ||
| 1: "Low volume", | ||
| 2: "High volume", | ||
| } | ||
| 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") | ||
| .applies_to("Yale", "YRD210 PB DB") | ||
| .applies_to("Yale", "YRL220 TS LL") | ||
| .replaces(DoublingPowerConfigurationCluster) | ||
| .binary_sensor( | ||
| attribute_name=DoorLock.AttributeDefs.auto_relock_time.name, | ||
| cluster_id=DoorLock.cluster_id, | ||
| attribute_converter=lambda value: bool(value > 0), | ||
| reporting_config=ReportingConfig( | ||
| min_interval=3600, | ||
| max_interval=10800, | ||
| reportable_change=1, | ||
| ), | ||
| unique_id_suffix="auto_relock_enabled", | ||
| translation_key="auto_relock_enabled", | ||
| fallback_name="Auto-relock enabled", | ||
| ) | ||
| .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, | ||
| cluster_id=DoorLock.cluster_id, | ||
| unique_id_suffix="inside_status_led", | ||
| translation_key="inside_status_led", | ||
| fallback_name="Inside status LED", | ||
| ) | ||
| .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", | ||
| ) | ||
| .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, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to the integrator's guide, this value should be able to be set to 0 to disable auto-relock from Zigbee, however, on my lock, this does not work, the auto-relock time only goes from 10-180, all other values fail. |
||
| cluster_id=DoorLock.cluster_id, | ||
| step=10, | ||
| min_value=10, | ||
| max_value=180, | ||
| unique_id_suffix="auto_relock_time", | ||
| device_class=NumberDeviceClass.DURATION, | ||
| unit=UnitOfTime.SECONDS, | ||
| 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, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should also be set from Zigbee, but the value won't update. |
||
| 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, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as this, Yale did mention that only 0 and 2 (mute and high vol) can be set on push-button type locks, however from my testing none can be set. |
||
| 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() | ||
| ) | ||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to hardcode this or does HA have a built-in converter? According to this document, these should be ISO-639-1 two character codes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to Yale's official integrators guide, these are the only three languages that all their locks support, so I think this will do for now.