Another Schlage Z-Wave lock code manager (ZWave JS)

I just wanted something simple that could set and clear codes on my Schlage BE469. Everything I found on here was pretty complicated in terms of UI, requiring multiple repeating cards for each code slot, and creating a whole ton of extra entities in the system. This is what I wound up building, far more compact and simple:

I’m very new to HA so there was probably a better way to do it, but here’s what I did:

  1. Created entities for Code Slot and Lock Code in the Helpers section. I used a dropdown for the the code slot with all of the possible code indexes on it (icon is mdi:account-box-multiple-outline), and a number ranging from 0 to 9999 for the code (icon is mdi:dialpad).

  2. Created Scripts for Set Lock Code and Delete Lock Code. Each script only contains one action: Call Service, and the service is either ZWave JS: Set a usercode on a lock or ZWave JS: Clear a usercode from a lock. You can then select the lock entity you want the script to target.

Then you need to change the view to YAML to set the data values with templates. I did the following, you can replace input_number.lock_code and input_select.code_slot with whatever IDs your helper entities have above, if they’re different than mine.

Set Lock Code is this:

service: zwave_js.set_lock_usercode
target:
  entity_id: lock.front_door
data:
  usercode: '{{ "%04d" % (states("input_number.lock_code") | int) }}'
  code_slot: '{{ states("input_select.code_slot") | int }}'

Delete Lock Code is this:

service: zwave_js.clear_lock_usercode
target:
  entity_id: lock.front_door
data:
  code_slot: '{{ states("input_select.code_slot") | int }}'
  1. With the scripts set up, I went into the dashboard editor and created the card for them. I just started with an Entities card and added my two helper entities and then the two scripts. That’s all it takes to get it working plainly.

  2. I did a little customization of the script UIs in the YAML. For some reason this resulted in configuration errors and wouldn’t let me save unless I specc’ed out every single property, even the null ones I didn’t care about. Doing so let me change the “RUN” text and add an “are you sure” pop-up, though, so I think it was worthwhile. Here’s what it looks like:

type: entities
entities:
  - entity: input_select.code_slot
  - entity: input_number.lock_code
  - entity: script.set_lock_code
    action_name: Set
    type: button
    name: Set Lock Code
    tap_action:
      action: call-service
      url_path: null
      navigation_path: null
      service: script.set_lock_code
      type: null
      confirmation:
        text: Change the lock code for this slot?
    view: null
    conditions: null
    url: null
    entities: null
    attribute: null
    service: null
    text: null
  - entity: script.delete_lock_code
    action_name: Delete
    type: button
    name: Delete Lock Code
    tap_action:
      action: call-service
      url_path: null
      navigation_path: null
      service: script.delete_lock_code
      type: null
      confirmation:
        text: Delete the lock code for this slot?
    view: null
    conditions: null
    url: null
    entities: null
    attribute: null
    service: null
    text: null
title: Lock Codes

Anyway, there’s probably a better/easier/more self-contained way than everything I did above, but I’m very happy with the result, and that I have a nice, compact UI with which to access these features when I need them.

4 Likes

Thanks so much for posting this, it’s almost exactly what I was looking for! Just something basic that doesn’t add a lot of bloat to home assistant. I am curious if anyone knows a way in z-wave js to capture the user code for a given code slot? I would like to modify this to pull up the user code (if one exists) when selecting the Code Slot from the drop down menu. That way I don’t accidently over write a code that is already there. For what it’s worth I’m using a Yale YRD256 set of locks. Thanks again!

Edit: I exported the node and found this in the .json…Now I just need someone FAR smarter than me to help me extract it in a template that I can use in yaml?!

    {
      "id": "19-99-0-userCode-2",
      "nodeId": 19,
      "commandClass": 99,
      "commandClassName": "User Code",
      "endpoint": 0,
      "property": "userCode",
      "propertyName": "userCode",
      "propertyKey": 2,
      "propertyKeyName": "2",
      "type": "string",
      "readable": true,
      "writeable": true,
      "label": "User Code (2)",
      "stateless": false,
      "commandClassVersion": 1,
      "minLength": 4,
      "maxLength": 10,
      "list": false,
      "value": "1234",
      "lastUpdate": 1643764656985,
      "newValue": "1234"
    },

Thank you @dposluns! I installed this today and it works great! No more messing with those silly steps at the lock to add or remove a temporary code for someone!

lock

Well with a LOT of help on the templating from @tom_l I was able to modify this script to be a little more user friendly on the front end, as well as include a user name that I can use for presence announcements when some arrives. I thought I would share it, so hopefully someone can take it and make it even better. The original premise is still in place. I created the following helpers for each lock:

->input_select.code_slot (as per the original script w/ 6 code slots for my needs)
->input_text.code_slot_front_user_0x (6 individual Code Slots for each lock to store the individual user names)
->input_number.front_lock_code_0x(6 individual Code Slots for each lock to store the individual user codes)

I ended up putting the following in a package because the script editor kept destroying the code when I put it into the scripts.yaml

   
script:

  set_lock_frontdoor_code:
    alias: Set Front Door Lock Code
    sequence:
    - service: zwave_js.set_lock_usercode
      target:
        entity_id: lock.lock_front_door
      data:
        usercode: >
          {% if is_state('input_select.code_slot','01') %}
            {{ "%04d" % states('input_number.front_lock_code_01')|int }}
          {% elif is_state('input_select.code_slot','02') %}
            {{ "%04d" % states('input_number.front_lock_code_02')|int }}          
          {% elif is_state('input_select.code_slot','03') %}
            {{ "%04d" % states('input_number.front_lock_code_03')|int }}           
          {% elif is_state('input_select.code_slot','04') %}
            {{ "%04d" % states('input_number.front_lock_code_04')|int }} 
          {% elif is_state('input_select.code_slot','05') %}
            {{ "%04d" % states('input_number.front_lock_code_05')|int }}         
          {% elif is_state('input_select.code_slot','06') %}
            {{ "%04d" % states('input_number.front_lock_code_06')|int }}         
          {% endif %}
        code_slot: '{{ states("input_select.code_slot") | int }}'
    mode: single
    icon: mdi:arrow-right-bold-circle
  delete_lock_frontdoor_code:
    alias: Delete Lock Code
    sequence:
    - service: zwave_js.clear_lock_usercode
      target:
        entity_id: lock.lock_front_door
      data:
        code_slot: '{{ states("input_select.code_slot") | int }}'
    - service: input_number.set_value
      data:
        entity_id: >
          {% if is_state('input_select.code_slot','01') %}
            input_number.front_lock_code_01
          {% elif is_state('input_select.code_slot','02') %}
            input_number.front_lock_code_02
          {% elif is_state('input_select.code_slot','03') %}
            input_number.front_lock_code_03
          {% elif is_state('input_select.code_slot','04') %}
            input_number.front_lock_code_04
          {% elif is_state('input_select.code_slot','05') %}
            input_number.front_lock_code_05
          {% elif is_state('input_select.code_slot','06') %}
            input_number.front_lock_code_06
          {% endif %}
        value: 0 
    - service: input_text.set_value
      data:
        entity_id: >
          {% if is_state('input_select.code_slot','01') %}
            input_text.code_slot_front_user_01
          {% elif is_state('input_select.code_slot','02') %}
            input_text.code_slot_front_user_02
          {% elif is_state('input_select.code_slot','03') %}
            input_text.code_slot_front_user_03
          {% elif is_state('input_select.code_slot','04') %}
            input_text.code_slot_front_user_04
          {% elif is_state('input_select.code_slot','05') %}
            input_text.code_slot_front_user_05
          {% elif is_state('input_select.code_slot','06') %}
            input_text.code_slot_front_user_06
          {% endif %}
        value: ""
    mode: single
    icon: mdi:delete-circle

The lovelace yaml now needs custom integration: State-Switch

cards:
  - type: entities
    title: Front Door Lock Codes
    entities:
      - input_select.code_slot
  - type: custom:state-switch
    entity: input_select.code_slot
    states:
      '01':
        cards: null
        type: entities
        entities:
          - entity: input_text.code_slot_front_user_01
          - entity: input_number.front_lock_code_01
          - entity: script.set_lock_frontdoor_code
            action_name: Set
            type: button
            name: Set Lock Code
            tap_action:
              action: call-service
              service: script.set_lock_frontdoor_code
              confirmation:
                text: Change the lock code for this slot?
          - entity: script.delete_lock_frontdoor_code
            action_name: Delete
            type: button
            name: Delete Lock Code
            tap_action:
              action: call-service
              service: script.delete_lock_frontdoor_code
              confirmation:
                text: Delete the lock code for this slot?
      '02':
        cards: null
        type: entities
        entities:
          - entity: input_text.code_slot_front_user_02
          - entity: input_number.front_lock_code_02
          - entity: script.set_lock_frontdoor_code
            action_name: Set
            type: button
            name: Set Lock Code
            tap_action:
              action: call-service
              service: script.set_lock_frontdoor_code
              confirmation:
                text: Change the lock code for this slot?
          - entity: script.delete_lock_frontdoor_code
            action_name: Delete
            type: button
            name: Delete Lock Code
            tap_action:
              action: call-service
              service: script.delete_lock_frontdoor_code
              confirmation:
                text: Delete the lock code for this slot?
      '03':
        cards: null
        type: entities
        entities:
          - entity: input_text.code_slot_front_user_03
          - entity: input_number.front_lock_code_03
          - entity: script.set_lock_frontdoor_code
            action_name: Set
            type: button
            name: Set Lock Code
            tap_action:
              action: call-service
              service: script.set_lock_frontdoor_code
              confirmation:
                text: Change the lock code for this slot?
          - entity: script.delete_lock_frontdoor_code
            action_name: Delete
            type: button
            name: Delete Lock Code
            tap_action:
              action: call-service
              service: script.delete_lock_frontdoor_code
              confirmation:
                text: Delete the lock code for this slot?
      '04':
        cards: null
        type: entities
        entities:
          - entity: input_text.code_slot_front_user_04
          - entity: input_number.front_lock_code_04
          - entity: script.set_lock_frontdoor_code
            action_name: Set
            type: button
            name: Set Lock Code
            tap_action:
              action: call-service
              service: script.set_lock_frontdoor_code
              confirmation:
                text: Change the lock code for this slot?
          - entity: script.delete_lock_frontdoor_code
            action_name: Delete
            type: button
            name: Delete Lock Code
            tap_action:
              action: call-service
              service: script.delete_lock_frontdoor_code
              confirmation:
                text: Delete the lock code for this slot?
      '05':
        cards: null
        type: entities
        entities:
          - entity: input_text.code_slot_front_user_05
          - entity: input_number.front_lock_code_05
          - entity: script.set_lock_frontdoor_code
            action_name: Set
            type: button
            name: Set Lock Code
            tap_action:
              action: call-service
              service: script.set_lock_frontdoor_code
              confirmation:
                text: Change the lock code for this slot?
          - entity: script.delete_lock_frontdoor_code
            action_name: Delete
            type: button
            name: Delete Lock Code
            tap_action:
              action: call-service
              service: script.delete_lock_frontdoor_code
              confirmation:
                text: Delete the lock code for this slot?
      '06':
        cards: null
        type: entities
        entities:
          - entity: input_text.code_slot_front_user_06
          - entity: input_number.front_lock_code_06
          - entity: script.set_lock_frontdoor_code
            action_name: Set
            type: button
            name: Set Lock Code
            tap_action:
              action: call-service
              service: script.set_lock_frontdoor_code
              confirmation:
                text: Change the lock code for this slot?
          - entity: script.delete_lock_frontdoor_code
            action_name: Delete
            type: button
            name: Delete Lock Code
            tap_action:
              action: call-service
              service: script.delete_lock_frontdoor_code
              confirmation:
                text: Delete the lock code for this slot?
type: vertical-stack

Obviously you would need to create a card and code for each individual lock you want to control, but for me it’s still way less bulky than Keymaster (which I think is a brilliant integration! Just more than I need). This has been tested and confirmed working on my three Yale YRD256 locks. Hopefully it works for you. Good luck!

Thanks to @dposluns for sharing the original code and inspiration behind my hack! :slight_smile:

1 Like

Great to see that you’ve done so much with this!

As for capturing the existing lock codes from the lock to display on the card, I couldn’t find a straightforward way to do that with the zwave_js integration, which is frustrating because they definitely are sent from the lock and exist in the ZWave JS UI, it’s just not propagated outside of that UI to any entities in HomeAssistant as far as I can tell.

That said, if I need to look them up I can do so by launching the ZWave JS UI from the sidebar, which is a bit inconvenient but still works fine on my phone, so I’ve settled on that as a compromise.

Yup…I was banging my head against the wall trying to figure it out as well! I finally gave up and decided to store them in the helper entities to restore my sanity. lol

Hello
Does this work with mini keypad RFID as i don’t have a lock entity id?

I doubt it would work natively since it’s built around Zwave entities and services. I don’t know much about coding for RFID but I’m sure it would require a re-write.

Did you ever figure this out?

@StarFox2873 no…I just ended up using helpers to store the variables and it seems to work pretty well.

Can anyone help me out with the state-switch type? I have no idea how to get that custom type set up.

Hello Guys, I’m very new with home assistant, as i was a vera user until controller died hehe, i have already added all my zwave devices to home assistant, but i just saw i cannot add/delete codes of my schlage deadbolt from home assistant. I feel a bit dumb for asking, but as i’m still familiarizing with the software… would someone tell me step by step how to achieve this? I promise that in the future i will help other community members! but as for now, i’m still confused trying to figure things out!

Thanks in advance!

Just wanted to say you can query the lock for existing codes.
See Z-wave lock user codes not found error

In the example provided, they are querying user codes (command class 99) for user code 10.
Sadly you can’t pass a list of slots to query, you have to query them one at a time replacing 10 with the number of the slot you want to know about. My lock supports up to 30 codes, so 1 … 30

One more thing to note, the example leaves out the target device which I had to include to get it to work. Example:

service: zwave_js.invoke_cc_api
data:
  command_class: "99"
  method_name: get
  parameters:
    - 10
target:
  entity_id: lock.front_door

Thanks for posting this here. It is very interesting, but I don’t see the ability to capture the code and assign it to an entity. Now, if we could figure out an automated way to do that…that would be AWESOME!

Hey I just tried this and the event fires successfully but doesn’t return the slot code. Is this what you get when you fire the event? Using a Schlage BE469ZP

event_type: zwave_js.invoke_cc_api
data:
  service: zwave_js.invoke_cc_api
  data:
    command_class: "99"
    method_name: get
    parameters:
      - 1
  target:
    entity_id: lock.back_door_lock
origin: REMOTE
time_fired: "2023-10-18T16:57:15.752431+00:00"
context:
  id: 01HD1VGN7828RANJ6J5F6DPF63
  parent_id: null
  user_id: d06a039c8c194908bc76c369ceda5c64

So I figured out how I need to view the debug logs on the Z Wave JS integration to see the user code. But the user code always shows up as ****? Not sure what i’m doing wrong here, wonder if the fact that my BE469ZP is at Security S0 is an issue…

2023-10-18T17:28:37.991Z CNTRLR   [Node 021] [User Code] userIdStatus[5]: metadata updated          [Endpoint 0]
2023-10-18T17:28:37.992Z CNTRLR   [Node 021] [+] [User Code] userIdStatus[5]: 1                     [Endpoint 0]
2023-10-18T17:28:37.993Z DRIVER « [Node 021] [REQ] [BridgeApplicationCommand]
                                  │ RSSI: -87 dBm
                                  └─[SecurityCCCommandEncapsulation]
                                    │ sequenced: false
                                    └─[UserCodeCCReport]
                                        user id:   5
                                        id status: Enabled
                                        user code: ****

You definitely should be connecting via S2 access control.
Once you execute the service, the requested code is populated in your Z-Wave JS control panel under User Code v1
I am also using the BE469ZP with firmware 3.3.
I like using Smart Start to provision my device. You may have to enter your DSK manually though.