KeyMaster Z-Wave lock manager and scheduler

If you’re using the auto-generated dashboard, then I would strongly recommend you stop doing that if you’re using Keymaster, please take some time and build a more useful to you dashboard. Please note that you aren’t the only one that finds the abundance of entities that Keymaster creates a bit overwhelming :wink:

Now that that particular comment is out of the way, Keymaster does offer a generated dashboard that you can use the documentation related it is here. If you have a lot of code slots defined for management, the page can get very big and kinda slow so you may want to take that and create a more custom dashboard after the fact, but at least you’ll have a better understanding of what all the entities are for.

For instance, this is the dashboard I’ve created for one of my rental properties:


the sections are roll-ups and by default all but the Airbnb section is rolled up, I expanded the others for the screen shot. Selecting one of the slots then brings me to a sub-view that is just that slots data
Selection_017
with the details exactly the same as they were from the Keymaster generated dashboard

Hi tykeal. My apologies for being such a noob but I am struggling with this one. If time permits and you are up for it can I ask for some additional help. So what I found is using the code below that whenever the bottom portion where it says ‘the2’ is when I am manually keying in the code and would like for it to only show when these occur. Would you be able to help remove the Manual ones (or all others in this case). Also I purposely add ‘the1’ and ‘the2’ for testing and I’ve yet to ever see ‘the1’ pop up. Its either ‘the2’ on manual code entry, and the others are ‘the’ which is not even listed in the code which confuses me.

alias: SS - Lock Logging2
description: Custom Keymaster Notification
mode: single
triggers:
  - event_type: keymaster_lock_state_changed
    event_data:
      state: unlocked
    variables:
      sn: |-
        {%- if trigger.event.data.code_slot_name == "" -%}
          Manual
        {%- else -%}
          {{ trigger.event.data.code_slot_name }}
        {%- endif -%}
      verb: |-
        {%- if trigger.event.data.code_slot_name == "" -%}
          unlock of
        {%- else -%}
          unlocked
        {%- endif -%}
      door: |-
        {%- if trigger.event.data.lockname == "front_door" -%}
          Front Door
        {%- elif trigger.event.data.lockname == "laundry_room" -%}
          Laundry Room Door
        {%- else -%}
          Garage Side Door
        {%- endif -%}
      code_slot: trigger.event.data.code_slot
      notify_state: >-
        {{ states('input_boolean.notify_' + trigger.event.data.lockname + '_ +
        code_slot | string') }}
    trigger: event
conditions: []
actions:
  - data:
      name: "{{ trigger.event.data.lockname }}"
      entity_id: "{{ trigger.event.data.entity_id }}"
      message: "{{ sn }} {{ verb }} the1 {{ door }}"
    action: logbook.log
  - if:
      - condition: template
        value_template: "{{ sn != 'Manual' }}"
    then:
      - metadata: {}
        data:
          message: "{{ sn }} {{ verb }} the2 {{ door }}"
          title: "{{ door }}"
          data:
            tag: "{{ door }}"
        action: notify.mobile_app_ss_s24u

I’m confused by your statements here.

You’re stating that you get a mobile notification when you use a door code correct?

Reading the code you’ve provided that should be the only time you get a mobile notification. You should not receive a mobile notification for any event except for when a code programmed into the lock is used.

As for your the1 that would be going to the Home Assistant logbook and should be capturing all unlock events, both manual, and code based. It is not going to go to your mobile notification.

“Manual” means that no door code was used. As in, a key was used or the lock was manually opened from the inside.

If you are still receiving other notifications as well then you probably have another notification script in play. The one above should be used in place of the manual notify script that Keymaster tries to use. Ideally you don’t have notifications turned on at all for your lock so Keymaster won’t try to use any of the notify scripts and it just relies on the automation you have.

Getting the below error on my newly added second BE469ZP Schlage lock. Everything I look at seems to be setup correctly. I have the script and it’s formatted exactly the same as the existing front door script that I’ve had. Anyone point me in the right direction?

alias: keymaster_back_door_manual_notify
sequence:
-data:
title: “{{ title }}”
message: “{{ message }}”
data:
ttl: 0
priority: high
action: notify.family
mode: parallel
description: “”

image

@tykeal thanks so much for providing this response and additional context, which sounds very similar to what I’m trying to create inside of HA. Part of the challenge of HA’s documentation is there’s al lot that’s assumed which can steepen the learning curve with many of us coming from existing solutions with entirely different paradigms and terminology.

After re-pasting the Lovelace code into a new dashboard I can confirm I was able to get that custom dashboard usable for creating those lock codes, which was a great feeling to see working! However, it seems like if I change Keymaster’s settings to add additional slots, I do need to re-paste that Lovelace code back into the dashboard, which I assume is expected?

My next step is try and get a calendar of bookings incorporated as triggers for code generation. Since I couldn’t find anyway to do this with the native CalDAV tool (which wants an authenticated session versus open ICS feed), I setup the ICS Calendar HACS integration, which seems to work wonderfully to generate a calendar of bookings, which I’m hoping I can use as a means to activate/deactivate guest codes (?).

You mentioned AirBnB codes you’re using, were you able to create those automatically off a calendar feed or are those just manually created by hand?

Thanks again for the help here. Definitely a great feeling when you see HA integrations like this working as shown and can see what might be possible if you could connect one tool to another.

Yes, I’m using Rental Control which I wrote to sit in top of Keymaster.

You will probably want to uninstall the ICS integration as it uses an older version of the ICS library and will end up in conflict with Rental Control

1 Like

Thank you! You were completely right. When messing around I had some other automation that I inconveniently named something different (no reference to lock in there) so my searching wasnt returning anything. Thanks again for the help.

I am using Keymaster 0.0.98. I had to re-configure everything because I had to reset and newly pair one of my locks.
Now in the UI everything looks different than when I installed it last time and I can not figure out how to change the time window for the “Night Auto-Lock” function. It just does it on sunset which is currently around 6pm, but I know I had it set at a certain time of night before… For now I changed it to the same as day, auto lock after 15mins. It would be nice to have it change to auto lock after 5 min after let’s say 9:30pm…
How do I set that up?

I would recommend that if you want specific times for the lock to be auto-locking at different timeouts that you do so via a custom automation and not use the auto-lock option from KeyMaster. As you just stated, KeyMaster is following the sun for when it should be using the day or night timings.

Thanks Andrew. I’ll give this a shot!

Not sure what I am doing wrong. I installed keymaster tonight. I made sure that the packages prereq is configured in configuration.yaml. After I install the integration, setup my lock, and install the Lovelace UI, it’s sort of working but the UI is showing some errors and I can’t set the lock code. Can anyone offer any advice? I have removed the integration and added it several times. The lock itself is setup in HA and I can lock/unlock it fine from it’s device.

image

For those that are interested. I started working on this a couple of years ago and then stopped because I got frustrated with it. Well tonight I finally went back to it with some help from GPT.
You have to have appdaemon for this to work. I’m sure it could be done in yaml but well yaml and I don’t get along so well.
Ever modify a code and it just gets stuck in the Adding/Deleting forever?
Well not anymore

import appdaemon.plugins.hass.hassapi as hass

class lock_code_status(hass.Hass):

    def initialize(self):
        # Get lock name and code range from configuration
        self.lock_name = self.args["lock_name"]
        self.lock_code_range = self._parse_lock_code_range(self.args.get("lock_code", ""))
        
        # Initialize timers dictionary for both Adding and Deleting logic
        self.adding_timers = {}
        self.deleting_timers = {}

        # Listen for state changes of dynamically constructed sensors
        for code_slot in self.lock_code_range:
            sensor_entity = f"sensor.connected_{self.lock_name}_{code_slot}"
            self.listen_state(self.monitor_lock_code, sensor_entity, code_slot=code_slot)
            self.log(f"Monitoring {sensor_entity} for Adding/Deleting states.")

    def _parse_lock_code_range(self, lock_code_range):
        """Parses the lock code range string and returns a list of codes."""
        lock_codes = []
        
        if "-" in lock_code_range:
            # Handle the case of a range, e.g., "1-20"
            start, end = lock_code_range.split("-")
            lock_codes = list(range(int(start), int(end) + 1))
        else:
            # Handle the case of a list of codes (e.g., [1,2,3,...])
            lock_codes = [int(code) for code in lock_code_range.split(",")]
        
        return lock_codes

    def monitor_lock_code(self, entity, attribute, old, new, kwargs):
        code_slot = kwargs["code_slot"]
        input_boolean_slot = f"input_boolean.enabled_{self.lock_name}_{code_slot}"

        self.log(f"State change for {entity}: {old} -> {new}")

        if new == "Adding":
            self.log(f"Lock code {code_slot} is in Adding state. Starting timer.")
            self._start_timer(self.adding_timers, code_slot, 60, input_boolean_slot, self.handle_lock_code_timeout)

        elif new == "Connected":
            self.log(f"Lock code {code_slot} is Connected. Canceling Adding timer.")
            self._cancel_timer(self.adding_timers, code_slot)

        elif new == "Deleting":
            self.log(f"Lock code {code_slot} is in Deleting state. Starting timer.")
            # Start a timer with a 5-second delay for re-enabling and then disabling again.
            self._start_timer(self.deleting_timers, code_slot, 60, input_boolean_slot, self.handle_lock_code_timeout)

        elif new == "Disconnected":
            self.log(f"Lock code {code_slot} is Disconnected. Canceling Deleting timer.")
            self._cancel_timer(self.deleting_timers, code_slot)

    def handle_lock_code_timeout(self, kwargs):
        code_slot = kwargs["code_slot"]
        input_boolean_slot = kwargs["input_boolean_slot"]

        self.log(f"Lock code {code_slot} timed out. Toggling {input_boolean_slot}.")
        self.call_service("homeassistant/turn_off", entity_id=input_boolean_slot)
        
        if kwargs.get("deleting", False):
            # Add specific logic for deleting state to enable for 5 seconds
            self.run_in(self.reenable_lock_code, 5, input_boolean_slot=input_boolean_slot, code_slot=code_slot, deleting=True)
        else:
            # For adding logic, handle the timer as usual
            self.run_in(self.reenable_lock_code, 5, input_boolean_slot=input_boolean_slot, code_slot=code_slot)

    def reenable_lock_code(self, kwargs):
        input_boolean_slot = kwargs["input_boolean_slot"]
        code_slot = kwargs["code_slot"]
        self.log(f"Re-enabling {input_boolean_slot}.")
        self.call_service("homeassistant/turn_on", entity_id=input_boolean_slot)

        # Turn off the input_boolean slot again after 5 seconds for deletion
        if kwargs.get("deleting", False):
            self.run_in(self.disable_lock_code, 5, input_boolean_slot=input_boolean_slot, code_slot=code_slot)

    def disable_lock_code(self, kwargs):
        input_boolean_slot = kwargs["input_boolean_slot"]
        self.log(f"Disabling {input_boolean_slot}.")
        self.call_service("homeassistant/turn_off", entity_id=input_boolean_slot)

    def _start_timer(self, timer_dict, code_slot, duration, input_boolean_slot, callback):
        # Cancel any existing timer for the code slot
        self._cancel_timer(timer_dict, code_slot)

        # Start a new timer and store it
        timer_dict[code_slot] = self.run_in(callback, duration, 
                                            input_boolean_slot=input_boolean_slot, 
                                            code_slot=code_slot)

    def _cancel_timer(self, timer_dict, code_slot):
        # Cancel and remove the timer if it exists
        if code_slot in timer_dict:
            self.cancel_timer(timer_dict[code_slot])
            del timer_dict[code_slot]

This will monitor your lock code status and if its stuck for 60s in a Adding/Deleting state then it will do what is necessary to attempt to resolve this.
All you need to do is go to your apps.yml file inside of your appdaemon directory and add

lock_code_status_check_front:
  module: lock_code_enable
  class: lock_code_status
  lock_name: front_door_lock
  lock_code: "1-10"

Of course repeat this for however many locks you may have.

At my house I have 2 doors that are just finicky and no matter what I do to make them play nice they just are slow to do as they’re told. So it helps now to have this so that it will attempt to fix the problem for me.

I hope that this helps out some others as well. Biggest problem I had was my AirBnB unit when Rental Control would enable a code for me and it would just get stuck in Adding and then my guest couldn’t get in. Not anymore I hope.

The code will build the 2 entities that it needs automatically for you so long as you give it the range of code slots that you want it to monitor.

3 Likes

Very cool.

I’m gonna look into this as I like it being handled automatically.

After digging through many threads of issues I found that manually running the automation that references synchronization of the code slot usually fixes it too. Oddly I noticed that sometimes changing the codes triggers these automations automatically, but not always. Perhaps that’s where things are getting hung up.

Since my case doesn’t require on the fly updates at this point, I didn’t dig further.

Thank you, I will install appdaemon and test this out. Currently I’m using an automation and it doesn’t work consistently.

You’re welcome. Let me know how it goes for you.

Have you tested these actions to fix the adding issue? I have a test machine and I’m able to duplicate the stuck at adding problem. With this it didn’t fix the issue. An automation that turns of ‘Enabled’, then cleared the lock code, then turned back on ‘Enabled’ after a delay, works.

Sometimes the automation runs a couple of times, then I add a restart add-on that restarts ZWAVE_JS and it helps.

Yeah I tested it out on my setup and it worked for me. Make sure that the lock name is just the name don’t put lock. in front of it. Not saying that you did but for anyone else who uses this. For me, the thing would just sit on adding so what I’ve found was when I would manually disable it, then enable it again it would finally go through. So that was why I wrote the automation in this manner.

There’s a new beta on the horizon that should help with the issues too.

If you want to give it a whirl:

2 Likes

Thanks for sharing this. Keymaster works great with my BE468ZP for a couple of days, then it will start getting stuck in adding / deleting. If I restart HA everything works again for a bit, but that’s less than ideal.

It is not necessarily a Keymaster issue, but Keymaster can handle it better. If your situation is so bad, I would recommend putting a repeater close to the lock. A repeater can be a zwave outlet or an actual zwave extender/repeater. That will make communication better.