ZHA: User code management on Zigbee door locks

Hi all, @VladStar sorry for that bug… it seems like kwikset vs yale handles the slot numbers differently, I didn’t run into that with my kwikset locks.
@Bruno_Dantas this is great! Glad to see you’re able to leverage all the functions and put them together into this! A perfect use for the integration.

1 Like

Thanks @jkuntz805 ! I was able to implement this card in my dashboard and update the scripts to reflect service calls to Hubitat, so it’s easy-peasy to add new codes and looks pretty too!

Background: I use Hubitat to connect to all my Zigbee and Zwave devices, so the usual OTS integrations like KeyMaster probably won’t work to manage locks without significant effort since I don’t have any Zwave services in HA. My Yale Zigbee lock was the last thing still on SmartThings, as the Hubitat UI for lock code management isn’t very wife friendly. But now I’m off ST and wife will be content.

For any other Hubitat users who come through this thread, I am using the Hubitat integration to HA through Maker API. It’s fairly trivial to add/remove codes for any lock in Hubitat using this card, with only slight modification of the associated scripts (I’m not a developer).

Feature Request: Ideally, all codes added through these scripts get logged to a file, which is then read by the Lock Codes display section of the card to reflect current users/codes without having to manually update a static list.

Another aspect of user code management is the ability to read back a user PIN code - even though it’s kind of icky from a security standpoint, but the spec allows it.

It is fairly simple on Yale locks to get user codes by issuing a cluster command, the argument is the “user_id”:

service: zha.issue_zigbee_cluster_command
data:
  ieee: 00:00:00:00:00:00:00:00
  endpoint_id: 1
  cluster_id: 257
  command: 6
  command_type: server
  args: 1

Unfortunately it looks like zha has no support for turning the reply into a zha_event, is there any interest in adding support for this? Right now the only way is to get the pin code value is to enable debug logging and grep for get_ping in the logs.

Edit: reading the ZHA component source, it looks like almost everything is there to read a pin code, there are just a couple of callbacks missing, is anyone working on this?

2 Likes

I would be interested in being able to retrieve pins. Could be handy for my single use codes if I forget them.

https://github.com/firstof9/keymaster/tree/zha-support It seems like firstof9 is working on zha support in keymaster

1 Like

This is super useful thanks. Reopening old thread sorry, but wanted to check on the Code Slot format - are they just any integer from 1 to ? I’ve only configured the master code on the unit itself so far. Using Yale Assure SL if they are lock-dependent… Thanks

1 Like

Yes, it’s just a number. My Schlage, for example, has 30 code slots available.

1 Like

Was ZHA pin support ever implemented?
If not what are your current workarounds?
Thx

I am still using this:

1 Like

Thank you.

But still no way to retrieve/read the existing pin codes right?

To the best of my knowledge - correct

1 Like

services now support returning data. Any chance of getting a get_pin_code service implemented?

1 Like

See another topic that I’ve created in the past. Cluster command (that supposed to get a pin code from the Yale locks) still isn’t working in 2023.10.5.

I’m not getting that error when I issue get_pin_code through cluster management. I’m getting a result - although admittedly the result only contains UserStatus and not the pincode as far as I can tell.

This may depend on the protocol, brand and specific model of the lock.

Do you mind sharing the details? How do you craft your request and what response you are getting.

Model: YRD256 TSDB (Zigbee module for Yale Assure SL)

I’m using “Manage Zigbee device > Clusters > Commands” with “Door Lock Cluster” selecting command get_pin_code and setting a code slot / user ID.

This returns an error:
Failed to issue cluster command with status: <UserStatus.Enabled: 1>

Which I assume means the command succeeded and the response was <UserState.Enabled: 1>. This is the same response if I issue a get_user_state command instead.

IIUC, get_pin_code is meant to return user state, user type, and pin code, but on this device it apperas to only be returning user state.

Also, shameless plug: I’ve also posted a feature request for more generically having ZHA services that return data now that services support this: ZHA: services that return data

2 Likes

Thank you for the reply!

I am getting the same response as you
Failed to issue cluster command with status: <UserStatus.Enabled: 1>
for 0x0a (Get User Status), which tells me that this valid response in not properly recognized
and
Failed to issue cluster command with status: <Status.UNSUP_CLUSTER_COMMAND: 129>
for 0x06 (Get PIN Code).

I have a feeling that Yale device handlers AKA quirks need to be modified in order to properly handle the responses. Sadly, I don’t have enough knowledge and enough time to figure this out, this requires some deep dive into ZHA programming.

Few helpful links, in case someone has the ability to take it to the next level:

My configuration.yaml with ZHA/zigpy debugging enabled:

logger:
  default: error
  logs:
    zigpy: debug
    zigpy_xbee: debug
    zigpy_deconz: debug
    homeassistant.components.zha: debug

And here is a part of my homeassistant.log after sending 0x0a and 0x06 commands to the Yale YRD210 PB DB zigbee lock:

2023-11-02 14:14:52.588 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(timestamp=datetime.datetime(2023, 11, 2, 18, 14, 52, 588089, tzinfo=datetime.timezone.utc), src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x6051), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=34, profile_id=260, cluster_id=257, data=Serialized[b'\x19\xb7\n\x02\x00\x01'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=255, rssi=-60)
2023-11-02 14:14:52.588 DEBUG (MainThread) [zigpy.zcl] [0x6051:1:0x0101] Received ZCL frame: b'\x19\xb7\n\x02\x00\x01'
2023-11-02 14:14:52.588 DEBUG (MainThread) [zigpy.zcl] [0x6051:1:0x0101] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=0, direction=<Direction.Client_to_Server: 1>, disable_default_response=1, reserved=0, *is_cluster=True, *is_general=False), tsn=183, command_id=10, *direction=<Direction.Client_to_Server: 1>)
2023-11-02 14:14:52.589 DEBUG (MainThread) [zigpy.zcl] [0x6051:1:0x0101] Decoded ZCL frame: DoorLock:get_user_status_response(user_id=2, user_status=<UserStatus.Enabled: 1>)
2023-11-02 14:14:52.590 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0x6051](YRD210 PB DB): Issued cluster command: cluster_id: [257] cluster_type: [in] endpoint_id: [1] command: [10] command_type: [server] args: [None] params: [{'user_id': 2}] manufacturer: [None]
2023-11-02 14:14:52.590 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [140603830223680] Failed to issue cluster command with status: <UserStatus.Enabled: 1>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 230, in handle_call_service
    await hass.services.async_call(
  File "/usr/src/homeassistant/homeassistant/core.py", line 2035, in async_call
    response_data = await coro
                    ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 2072, in _execute_service
    return await target(service_call)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 986, in admin_handler
    await result
  File "/usr/src/homeassistant/homeassistant/components/zha/websocket_api.py", line 1361, in issue_zigbee_cluster_command
    await zha_device.issue_cluster_command(
  File "/usr/src/homeassistant/homeassistant/components/zha/core/device.py", line 839, in issue_cluster_command
    raise HomeAssistantError(
homeassistant.exceptions.HomeAssistantError: Failed to issue cluster command with status: <UserStatus.Enabled: 1>

-------------------------------

2023-11-02 14:16:11.847 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(timestamp=datetime.datetime(2023, 11, 2, 18, 16, 11, 847041, tzinfo=datetime.timezone.utc), src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x6051), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=36, profile_id=260, cluster_id=257, data=Serialized[b'\x08\xc2\x0b\x06\x81'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=255, rssi=-60)
2023-11-02 14:16:11.847 DEBUG (MainThread) [zigpy.zcl] [0x6051:1:0x0101] Received ZCL frame: b'\x08\xc2\x0b\x06\x81'
2023-11-02 14:16:11.848 DEBUG (MainThread) [zigpy.zcl] [0x6051:1:0x0101] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=0, direction=<Direction.Client_to_Server: 1>, disable_default_response=0, reserved=0, *is_cluster=False, *is_general=True), tsn=194, command_id=11, *direction=<Direction.Client_to_Server: 1>)
2023-11-02 14:16:11.848 DEBUG (MainThread) [zigpy.zcl] [0x6051:1:0x0101] Decoded ZCL frame: DoorLock:Default_Response(command_id=6, status=<Status.UNSUP_CLUSTER_COMMAND: 129>)
2023-11-02 14:16:11.860 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0x6051](YRD210 PB DB): Issued cluster command: cluster_id: [257] cluster_type: [in] endpoint_id: [1] command: [6] command_type: [server] args: [None] params: [{'user_id': 2}] manufacturer: [None]
2023-11-02 14:16:11.862 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [140603830223680] Failed to issue cluster command with status: <Status.UNSUP_CLUSTER_COMMAND: 129>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 230, in handle_call_service
    await hass.services.async_call(
  File "/usr/src/homeassistant/homeassistant/core.py", line 2035, in async_call
    response_data = await coro
                    ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 2072, in _execute_service
    return await target(service_call)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 986, in admin_handler
    await result
  File "/usr/src/homeassistant/homeassistant/components/zha/websocket_api.py", line 1361, in issue_zigbee_cluster_command
    await zha_device.issue_cluster_command(
  File "/usr/src/homeassistant/homeassistant/components/zha/core/device.py", line 839, in issue_cluster_command
    raise HomeAssistantError(
homeassistant.exceptions.HomeAssistantError: Failed to issue cluster command with status: <Status.UNSUP_CLUSTER_COMMAND: 129>

1 Like

As this thread helped me out a long time ago, I’m attempting to learn new things (how to create a HA integration) and give back a bit.

I’ve created a HACS enabled integration that will automatically generate the helpers, automations, and dashboard YAML, for user-code management of a ZHA keypad lock.

Posting this here so that those who come along in the future can find it.

2 Likes