Vera Z-Wave Lock Code Management through MQTT

Hey, so I’m not sure how many people this applies to, but I’ve recently started using MQTT for my lock code management between Vera and HA, since the other methods (rest commands/sensors/etc) proved to be pretty unreliable.
I started with this MQTT plugin for Vera: GitHub - vosmont/vera-mqtt
Then added a multi-string container (I actually have several… likely due to my lack of knowledge on how to work with LUA and LUUP all that well… I created several versions by creating numerous devices, changing the references in all the docs from VContainer1 to VContainer[and some number]): GitHub - dklinkman/vera-multistringcontainer: Original container modified for AltUI

Also, on Vera, I created the following scenes (If you want to turn the same code on and off – I use this to enable my parents lock codes only when I’m gone… otherwise they can knock):

local waittime=30000;

luup.call_action(‘urn:micasaverde-com:serviceId:DoorLock1’, ‘SetPin’, {UserCodeName=‘USERCODENAME’, newPin=PINNUMBER, user=USERCODESLOT},LOCKDEVICENUMBER)

luup.sleep(waittime)

luup.call_action(‘urn:micasaverde-com:serviceId:HomeAutomationGateway1’, “RunScene”, {SceneNum = “SCENENUMBERTOCHECKLOCKCODES”}, 0)

SCENENUMBERTOCHECKLOCKCODES (MQTT on Vera Publishes the Containers to HA… you need to reset the values to 0 so it will republish it):

local waittime=10000;
luup.variable_set(“urn:upnp-org:serviceId:VContainer3”, “Variable1”,“”, 68)
luup.variable_set(“urn:upnp-org:serviceId:VContainer1”, “Variable1”,“”, 24)

luup.sleep(waittime)

local lockcodes= luup.variable_get(“urn:micasaverde-com:serviceId:DoorLock1”,“PinCodes”,LOCKDEVICENUMBER)
if string.find(lockcodes, “Parents”) then
luup.variable_set(“urn:upnp-org:serviceId:VContainer3”, “Variable1”,“On”, 68)
else
luup.variable_set(“urn:upnp-org:serviceId:VContainer3”, “Variable1”,“Off”,68)
end
if string.find(lockcodes, “Temporary”) then
luup.variable_set(“urn:upnp-org:serviceId:VContainer1”, “Variable1”,“On”, 24)
else
luup.variable_set(“urn:upnp-org:serviceId:VContainer1”, “Variable1”,“Off”, 24)
end

To clear that code:

local waittime=30000;

luup.call_action(‘urn:micasaverde-com:serviceId:DoorLock1’, ‘ClearPin’, {UserCode=‘1’}, LOCKDEVICENUMBER)

luup.sleep(waittime)

luup.call_action(‘urn:micasaverde-com:serviceId:HomeAutomationGateway1’, “RunScene”, {SceneNum = “SCENENUMBERTOCHECKLOCKCODES”}, 0)

For a variable code (this took me a while to figure out), first you need to use the VContainer, with the parent as the MQTT client. Use whatever MQTT Topic as whatever you want, and the target as

urn:upnp-org:serviceId:VContainer4,Variable1=

Then a scene for when that is greater than 0:

local waittime=15000;
local tempcode = luup.variable_get(“urn:upnp-org:serviceId:VContainer4”, “Variable1”, 76)
luup.call_action(‘urn:micasaverde-com:serviceId:DoorLock1’, ‘SetPin’, {UserCodeName=‘Temporary’, newPin=tempcode, user=9}, LOCKDEVICENUMBER)
luup.sleep(waittime)
luup.sleep(waittime)
luup.call_action(‘urn:micasaverde-com:serviceId:HomeAutomationGateway1’, “RunScene”, {SceneNum = “SCENENUMBERTOCHECKLOCKCODES”}, 0)
luup.sleep(waittime)
luup.variable_set(“urn:upnp-org:serviceId:VContainer4”, “Variable1”,“0”, 76) # So this resets it back to 0

To get the last Usercode entered (MQTT on Vera Publishes the Container):

local locklastusercode = luup.variable_get(“urn:micasaverde-com:serviceId:DoorLock1”, “sl_UserCode”, LOCKDEVICENUMBER)

if string.find(locklastusercode, “USERCODENAME1”) then
luup.variable_set(“urn:upnp-org:serviceId:VContainer2”, “Variable1”,“USERCODENAME1”, 63)
elseif string.find(locklastusercode, “USERCODENAME2”) then
luup.variable_set(“urn:upnp-org:serviceId:VContainer2”, “Variable1”,“USERCODENAME2”, 63)
elseif string.find(locklastusercode, “USERCODENAME3”) then
luup.variable_set(“urn:upnp-org:serviceId:VContainer2”, “Variable1”,“USERCODENAME3”, 63)
elseif string.find(locklastusercode, “USERCODENAME4”) then
luup.variable_set(“urn:upnp-org:serviceId:VContainer2”, “Variable1”,“USERCODENAME4”, 63)
elseif string.find(locklastusercode, “USERCODENAME5”) then
luup.variable_set(“urn:upnp-org:serviceId:VContainer2”, “Variable1”,“USERCODENAME5”, 63)
else
luup.variable_set(“urn:upnp-org:serviceId:VContainer2”, “Variable1”,“Error”, 63)
end

Now on HA, I have input booleans for the lock codes I want to control. To clear it, I simply call the scenes from HA, which then updates the Input Boolean, or the Input Boolean will trigger the automation to call the scene, like this:

  • id: mqtt_lock_temp_code_on
    alias: MQTT Lock Temp Code On
    initial_state: true
    trigger:
    • platform: mqtt
      topic: “Vera/Events/Temp Lock Code”
      condition:
      condition: template
      value_template: “{{ trigger.payload_json.Variable1 == ‘On’ }}”
      action:

    • service: input_boolean.turn_on
      data:
      entity_id: input_boolean.temp_lock_code_set

    • id: mqtt_lock_temp_code_off
      alias: MQTT Lock Temp Code Off
      initial_state: true
      trigger:

      • platform: mqtt
        topic: “Vera/Events/Temp Lock Code”
        condition:
        condition: template
        value_template: “{{ trigger.payload_json.Variable1 == ‘Off’ }}”
        action:
      • service: automation.turn_off
        entity_id: automation.lock_temp_lock_code_used
      • delay: 00:00:02
      • service: input_boolean.turn_off
        data:
        entity_id: input_boolean.temp_lock_code_set
      • delay: 00:00:30
      • service: automation.turn_on
        entity_id: automation.lock_temp_lock_code_used

And to set a variable lock code, with input text (you can do this with an input number as well), I have this in an automation:

  • id: lock_set_temp_lock_code
    alias: ‘Lock Set Temporary Lock Code’
    trigger:
    • platform: state
      entity_id: input_text.lock_code_temp
      condition:
    • condition: template
      value_template: “{{ not is_state(‘input_text.lock_code_temp’, ‘Code’) }}”
    • condition: state
      entity_id: input_boolean.restart_complete
      state: ‘on’
      action:
  • service: mqtt.publish
    data_template:
    topic: “Vera/Events/SetTempLockCode”
    payload_template: “{{ states.input_text.lock_code_temp.state }}”
  • delay: 00:00:10
  • service: input_text.set_value
    data:
    entity_id: input_text.lock_code_temp
    value: ‘Code’
  • delay: 00:00:60
  • condition: state
    entity_id: input_boolean.temp_lock_code_set
    state: ‘off’
  • service: notify.html5
    data_template:
    message: “Temporary Lock Code Did Not Update”
    title: “Temporary Lock Code Not On”
    data:
    tag: ‘lock_not_updated’
    renotify: 0
    icon: ‘/local/lock.jpg’
    badge: ‘/local/lock-badge.png’

I really hope this helps some people trying to do the same thing… took me FOREVER to figure out. I’m sorry about the formatting btw, the preformatted text doesn’t seem to be working…

If anyone wants to take a look at my config, if it’ll help, it’s up here: GitHub - mrneilix/Home-AssistantConfig

WOW, that looks… involved!

Looks like someone else took a stab at something similar previously: ZWave Locks and Logging for the former Vera crowd and others

Has anyone been successful in doing HA lock management when using a Vera for Z-wave, or is it borked for everyone? I mean, is it all types of locks, or only specific ones? I see from your github that you have a Schlage Camelot.

Hey, sorry, I’ve been off it for a bit. I’ve actually stopped doing it for a bit since my lock randomly disappeared from Vera, but it started working again.

I’ve consolidated it all into 1 container. I did have scenes in Vera to add pre-set lock codes, but also I found it’s MQTT subscription wasn’t too reliable, so I made it run in a rest_command (this is in my secrets for the URL, but I’ll put it here):

  set_temp_lock_code:
    url: http://[IP AND PORT OF VERA]/data_request?id=variableset&DeviceNum=[DEVICENUMBER]&serviceId=urn:upnp-org:serviceId:VContainer1&Variable=Variable4&Value={{ states('sensor.lock_set_temp_code_input_number') }}`
    method: post

the automation/script is this:

- id: lock_set_temp_lock_code_input_number
  alias: 'Lock Set Temporary Lock Code Input Number'
  trigger:
    - platform: numeric_state
      entity_id: sensor.lock_set_temp_code_input_number
      above: 1000
  condition:
    - condition: state
      entity_id: input_boolean.restart_complete
      state: 'on'
  action:
    - delay: 00:00:01
    - service_template: >
          {% if is_state('input_boolean.temp_lock_code_set', 'on') %}
            script.lock_clear_temp_code
          {% else %}
            script.do_nothing
          {% endif %}
    - delay: 00:00:05
    - wait_template: "{{ is_state('input_boolean.temp_lock_code_set', 'off') }}"
      timeout: '00:01:00'
      continue_on_timeout: 'true'
    - service: rest_command.set_temp_lock_code
    - delay: 00:00:10
    - service: input_number.set_value
      data:
        entity_id: input_number.lock_code_temp
        value: '0.0'
    - delay: 00:00:60
    - condition: state
      entity_id: input_boolean.temp_lock_code_set
      state: 'off'
    - service: notify.html5
      data_template:
        message: "Temporary Lock Code Did Not Update"
        title: "Temporary Lock Code Not On"
        data:
          tag: 'lock_not_updated'
          renotify: 0
          icon: '/local/lock.jpg'
          badge: '/local/lock-badge.png'

I found that input numbers go to 1 decimal place, so i created a template sensor to make it an int, and to verify it’s 4 characters.

Then to add that to the lock, I have a scene that triggers when that variable goes over 0:

local waittimeon=60000;
local waittimeoff=100;
local waittimeclear=1000;
local tempcodeset= luup.variable_get("urn:upnp-org:serviceId:VContainer1", "Variable1", 24)
local tempcode = luup.variable_get("urn:upnp-org:serviceId:VContainer1", "Variable4", 24)
if (tempcodeset == "On") then
  luup.call_action('urn:micasaverde-com:serviceId:DoorLock1', 'ClearPin', {UserCode= 9}, 91)
  luup.sleep(waittimeon)
else
  luup.sleep(waittimeoff)
end
luup.call_action('urn:micasaverde-com:serviceId:DoorLock1', 'SetPin', {UserCodeName='Temporary', newPin=tempcode, user=9}, 91)
luup.sleep(waittimeclear)
luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable4","0", 24)

I set up MQTT automations to receive updates and then update my input booleans so I know when it fails

- id: mqtt_lock_parents_code_on
  alias: MQTT Lock Parents Code On
  initial_state: true
  trigger:
    - platform: mqtt
      topic: "Vera/Events/Lock Info"
  condition:
    condition: template
    value_template: "{{ trigger.payload_json.Variable2 == 'On' }}"
  action:
    - service: automation.turn_off
      entity_id: automation.lock_parents_lock_code_toggle
    - delay: 00:00:02
    - service: input_boolean.turn_on
      data:
        entity_id: input_boolean.parents_lock_code
    - delay: 00:00:30
    - service: automation.turn_on
      entity_id: automation.lock_parents_lock_code_toggle

The tricky part is when you have sensors and 1 VContainer, when MQTT sends one variable from the container, it resets the sensor value, so I made it so it updates all of them together here (and as above, I have the input booleans being set by the automation because of the sensor issue):

(Trigger1: Schlage Deadbolt: A PIN code was removed from the lock) (Trigger2: Schlage Deadbolt: A PIN code was programmed into the lock)

local waittime=10000;
luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable1","", 24)
luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable2","", 24)
luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable3","", 24)
luup.sleep(waittime)
local lockcodes= luup.variable_get("urn:micasaverde-com:serviceId:DoorLock1","PinCodes",91)
if string.find(lockcodes, "Parents") then
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable2","On", 24)
else
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable2","Off", 24)
end
if string.find(lockcodes, "Temporary") then
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable1","On", 24)
else
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable1","Off", 24)
end
luup.sleep(waittime)
luup.call_action('urn:micasaverde-com:serviceId:HomeAutomationGateway1', "RunScene", {SceneNum = "48"}, 0) 

The last line calls the scene below to check the last lock code used (Which is also triggered when a pin code is entered into the lock):

luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable3","", 24)
local locklastusercode = luup.variable_get("urn:micasaverde-com:serviceId:DoorLock1", "sl_UserCode", 91)
if string.find(locklastusercode, "[MYUSER1]") then
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable3","[MYUSER1]", 24)
elseif string.find(locklastusercode, "[MYUSER2]") then
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable3","[MYUSER2]", 24)
elseif string.find(locklastusercode, "[MYUSER3]") then
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable3","[MYUSER3]", 24)
elseif string.find(locklastusercode, "[MYUSER4]") then
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable3","[MYUSER4]", 24)
elseif string.find(locklastusercode, "[MYUSER5]") then
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable3","[MYUSER5]", 24)
else
  luup.variable_set("urn:upnp-org:serviceId:VContainer1", "Variable3",locklastusercode , 24)
end

You can message me if you have any questions though. It was a total pain in the ass to get working, but it all kinda makes sense to me now if you need help

I also have them all on my GitHub here, including the Vera Scenes in a text file in the home directory: https://github.com/mrneilix/Home-AssistantConfig