Yale Assure lock code management with zigbee2mqtt

I’ve recently installed two Yale Assure locks with Zigbee modules. I was already running zigbee2mqtt and couldn’t find a thorough description of how to use this to set lock codes from the frontend (although there are a few threads on doing the same with ZHA). I’m noting my configuration here in case it helps others, and to invite feedback on any areas where it could be improved.

First to note that several aspects of the lock should appear automatically through zigbee2mqtt once it’s added to the network, including: current state, ability to change state (lock/unlock), the last action, action user, current auto relock time, battery, and sound volume. What is ‘somewhat’ exposed through the zigbee2mqtt interface, but isn’t in the front-end includes setting and enabling user pin codes - it was these aspects that I wanted to pull out and make easy to use from the HA frontend. There is some useful detail on setting and getting these parameters on the zigbee2mqtt page for the lock here.

The first step is to set up mqtt entities in your configuration.yaml file for both the user code (i.e. number) and the code status (i.e. enabled/disabled), for which I use text and switch entities respectively. You can set up as many different users as you like, but I went with the two permanent residents and two extra guest codes (more on those below). I also set them all to be the same device at the automatically set up mqtt device, but unfortunately (although it does group them) it doesn’t appear that you can add them to an existing device.

configuration.yaml additions (duplicate and renumber for more users)
mqtt:  
  text:
    - name: "Lock User 1 Code"
      unique_id: "front_door_lock_user_1_code"
      state_topic: "zigbee2mqtt/Front Door Lock"
      value_template: "{{ value_json['users']['1']['pin_code'] }}"
      mode: 'text'
      min: 4
      max: 8
      pattern: '^[0-9]*$'
      command_topic: "zigbee2mqtt/Front Door Lock/set"
      command_template: '{"pin_code": {"user": "1", "user_type": "unrestricted", "pin_code": "{{ value }}", "user_enabled": {{ "true" if states("switch.front_door_lock_lock_user_1_code_status") == "on" else "false" }} }}' 
      optimistic: false
      qos: 0
      retain: false
      device:
        name: "Front Door Lock"
        identifiers:
          - "lock.front_door_lock"
  switch:
    - name: "Lock User 1 Code Status"
      unique_id: "front_door_lock_user_1_code_status"
      state_topic: "zigbee2mqtt/Front Door Lock"
      value_template: "{{ value_json['users']['1']['status'] }}"
      state_on: "enabled"
      state_off: "disabled"
      command_topic: "zigbee2mqtt/Front Door Lock/set"
      command_template: '{"pin_code": {"user": "1", "user_type": "unrestricted", "pin_code": "{{ states("text.front_door_lock_lock_user_1_code") }}", "user_enabled": {{ value }} }}'
      payload_on: "true"
      payload_off: "false"
      optimistic: false
      qos: 0
      retain: false
      device:
        name: "Front Door Lock"
        identifiers:
          - "lock.front_door_lock"

With this set up, I found that I was able to update the user state of the lock (confirmed via testing the lock, but was not able to see the changed state in HA. In order to read back the state I had to issue a get command on the pin code. To keep things correct in HA I therefore set up an automation to issue the get command every time it sees a pin_code_added message. I also found it was beneficial to add a short delay after the set command was seen to ensure that the lock had updated.

Automation to reread the lock codes after setting
alias: Reread lock codes when set
trigger:
  - platform: mqtt
    topic: zigbee2mqtt/Front Door Lock/action
    payload: pin_code_added
condition: []
action:
  - delay:
      hours: 0
      minutes: 0
      seconds: 15
      milliseconds: 0
  - action: mqtt.publish
    metadata: {}
    data:
      topic: zigbee2mqtt/Front Door Lock/get
      payload: "{\"pin_code\": \"\"}"
mode: single

Finally I added the created entities to the frontend using a multiple entity row (added via HACS) within the entities card. This allows changing of the code and enabling/disabling use of the code.

Multiple entity row configuration within frontend
type: entities
entities:
  - entity: switch.front_door_lock_lock_user_1_code_status
    name: User 1 Door Code
    icon: mdi:key-variant
    type: custom:multiple-entity-row
    toggle: true
    state_color: true
    entities:
      - entity: text.front_door_lock_lock_user_1_code
        name: Code

image

Duplication and renumbering of all the above code will allow management of multiple users. A few extensions to these basics that I’ve added include:

Synchronising codes across multiple locks.

We have the same lock on the front and back doors, so it was relatively straightforward to extend the automation to reread the lock codes to also update the back door codes to match the front door. I found it was necessary to introduce a few extra delays into the automation to ensure everything was kept in sync. As previously, duplicate the set command and renumber for however many users you have.

alias: Synchronise locks pin when a message is sent
trigger:
  - platform: mqtt
    topic: zigbee2mqtt/Front Door Lock/action
    payload: pin_code_added
condition: []
action:
  - delay:
      hours: 0
      minutes: 0
      seconds: 15
      milliseconds: 0
  - action: mqtt.publish
    metadata: {}
    data:
      topic: zigbee2mqtt/Front Door Lock/get
      payload: "{\"pin_code\": \"\"}"
  - delay:
      hours: 0
      minutes: 0
      seconds: 15
      milliseconds: 0
  - action: mqtt.publish
    metadata: {}
    data:
      topic: zigbee2mqtt/Back Door Lock/set
      payload: >-
        {"pin_code": {"user": "1", "user_type": "unrestricted", "pin_code": "{{
        states("text.front_door_lock_lock_user_1_code") }}", "user_enabled": {{
        'true' if states("switch.front_door_lock_lock_user_1_code_status") ==
        "on" else 'false' }} }}
  - action: mqtt.publish
    metadata: {}
    data:
      topic: zigbee2mqtt/Back Door Lock/get
      payload: "{\"pin_code\": \"\"}"
mode: single
A single use door code.

I wanted to be able to have codes automatically disable once they’re used. This was pretty straightforward by adding a new automation for this, triggered by use of a specific code on either the front or back doors (in my case I use user 3 for single use codes). I only need to set the front door code as the previous automation syncs the back door.

alias: Clear single-use code when used
trigger:
  - platform: state
    entity_id:
      - sensor.front_door_lock_action_user
    to: "3"
  - platform: state
    entity_id:
      - sensor.back_door_lock_action_user
    to: "3"
condition:
  - condition: or
    conditions:
      - condition: state
        entity_id: sensor.front_door_lock_action_source_name
        state: keypad
      - condition: state
        entity_id: sensor.back_door_lock_action_source_name
        state: keypad
action:
  - action: mqtt.publish
    metadata: {}
    data:
      topic: zigbee2mqtt/Front Door Lock/set
      payload: >-
        {"pin_code": {"user": "3", "user_type": "unrestricted", "pin_code": "{{
        states("text.front_door_lock_lock_user_3_code") }}", "user_enabled":
        false }}
mode: single
A 'guest' door code that is only enabled within a time window.

With the switches set up for enable/disable of codes you can set up helper input_datetime entities to enable and disable these via automation as follows. Note that here I’m addressing the entity as a switch, rather than sending the mqtt payload directly as I do for the single use code - both seem to work fine, although this way is probably cleaner.

alias: Disable guest code
trigger:
  - platform: time
    at: input_datetime.guest_code_end
condition: []
action:
  - type: turn_off
    device_id: 5c8e260f49152ae4c4874398e92b2b42
    entity_id: 1789d32c58eea727bfa8096aa358e130
    domain: switch
mode: single
alias: Enable guest code
trigger:
  - platform: time
    at: input_datetime.guest_code_start
condition: []
action:
  - type: turn_on
    device_id: 5c8e260f49152ae4c4874398e92b2b42
    entity_id: 1789d32c58eea727bfa8096aa358e130
    domain: switch
mode: single
The ability to randomly generate codes.

To make life a little easier when updating codes, I added the ability to generate a random number for the single use and guest codes. I did this with the following script - note that I’m not generating numbers below 1000 to ensure that the code has the right quantity of digits, but I suspect that a smarter use of the random code could fix this?

alias: Randomise guest code
sequence:
  - action: mqtt.publish
    metadata: {}
    data:
      topic: zigbee2mqtt/Front Door Lock/set
      payload: >-
        {"pin_code": {"user": "4", "user_type": "unrestricted", "pin_code": "{{
        range(1000, 10000) | random }}", "user_enabled": {{ 'true' if
        states("switch.front_door_lock_lock_user_4_code_status") == "on" else
        'false' }} }}

A similar script can be done for other users as necessary, and added to the multiple entity row.

Showing all of the above in an entities card it looks like this:
image