Z-Wave Lock PIN Scheduler

Posted on
Mon May 08, 2017 4:03 pm
jay (support) offline
Site Admin
User avatar
Posts: 18200
Joined: Mar 19, 2008
Location: Austin, Texas

Z-Wave Lock PIN Scheduler

I've written a relatively simple script that will allow any external system to schedule Z-Wave lock PINS to be activated/deactivated by Indigo at specific date/times. The only requirement for the external system is that it be able to write a specifically formatted string to an Indigo variable. The script, Indigo, and the awesome ZWave Lock manager plugin (which you need to install) will handle the rest.

ss115.png
ss115.png (51.62 KiB) Viewed 1911 times


The script operates pretty simply: it looks in a specified variable folder for variables named id_X, where X is the user number (i.e. id_1 would be for user 1). The value of the variable is a formatted string that the script uses to determine what to do. The format of the string is:

Code: Select all
ACTION;PIN;START_DATE_TIME;END_DATE_TIME


ACTION is either "start" or "end". PIN is the PIN for the user. START_DATE_TIME is a string representing the date and time that the PIN should become active and END_DATE_TIME is the date/time the PIN should be disabled. The default format for the date/time strings is:

Code: Select all
YYYY/MM/DD HH:MM


where the hours (HH) are specified in 24 hour time. You can change the date/time format to anything you want by changing the DATE_FORMAT string at the top of the script (it uses the standard Python datetime specifiers). So, for example, if we wanted the PIN for user 2 to become 1234 on May 8th, 2017 at 4:00PM, and to deactivate on May 10th, 2017 at 11:00AM, we would set the value of variable id_2 to:

Code: Select all
start;1234;2017/05/08 16:00;2017/05/10 11:00


The script (if run every hour for instance) would cycle through all the variables in the specified folder, looking at each value by splitting up it's contents. If the action is "start", it will set the pin code and set the variable value to:

Code: Select all
end;1234;2017/05/08 16:00;2017/05/10 11:00


in preparation for clearing the code later. When the 10th rolls around, the script will again go through each variable and when it finds this one with the action of "end" at 11am, it will delete the PIN and set the variable value to nothing "" which represents no upcoming schedules.

If you need to prematurely turn off the code, just manually set the variable to have an action of "end" and a new end date (even one that's already past) and the next time the script runs it will disable the code.

You can use a standard Indigo schedule to run the script as often as you like - hourly, every 10 minutes, etc. It will always find start (or end) actions with times that are less than the current time and take the appropriate action.

To configure the script, you just edit the PIN_VARIABLE_FOLDER_ID to be the ID of the folder you create and the DEVICE_ID to the ID of your lock. You may also specify an ACTION_GROUP_ID - that's the ID of an action group that will get executed any time there's a fatal error of any type. This action group can do whatever you need to get your attention (send a notification, email, speak, whatever).

Just save the script below to a file on your Indigo Server, and then configure a schedule to execute the script file as often as you want it to monitor/change PINS. Running it every minute would likely not be a big deal for any Indigo Server, so if you want minute-level granularity that would be fine.

Code: Select all
'''
This script will cycle through each variable in the specified folder looking for strings formatted like this:

ACTION;PIN;START_DATE_TIME;END_DATE_TIME

ACTION is one of ("start" or "end")
PIN is the pin code
START_DATE_TIME is a date/time string that will add the pin
END_DATE_TIME is a date/time string represents when the pin should be removed

The variable should be named id_# = where # is the user number. Date strings by default are formatted:

YYYY/MM/DD HH:MM

where the hour is a 24 hour clock. This can be changed via the DATE_FORMAT constant below.
'''

try:
    import sys
    import indigo
except:
    print "Indigo Python scripts must be run from Indigo or from an IndigoPluginHost process."
    sys.exit(1)
   
from datetime import datetime

####################
# Things that you MUST change for the script to work correctly
####################
# ID of the folder that contains the variables for pin changes, name = id_# where # is the user
PIN_VARIABLE_FOLDER_ID = 1234567890
# ID of the lock device
DEVICE_ID = 987654310
# ID of an Action Group to execute if there's a fatal error in the script. If you leave it
# set to None no action group will be executed.
ACTION_GROUP_ID = None

####################
# Things that you don't have to change as long as you use the formats specified
####################
# The date should be in the form of "YYYY/MM/DD HH:MM" - hour is 24 hour clock
DATE_FORMAT = "%Y/%m/%d %H:%M"
# The string in the variable should have each component separated by this character
PIN_STRING_SEPARATOR = ";"
# Log message type that will show in the Event Log window and in log files
LOG_TYPE = "Pin Processor"

def set_pin(pin, user, deviceId):
    lock_manager_plugin = indigo.server.getPlugin("com.howartp.lockmanager")
    if lock_manager_plugin.isEnabled():
       lock_manager_plugin.executeAction("setUserPin", deviceId=deviceId, props={'userNo': user, 'userPin': pin})
    else:
        log("Lock manager plugin isn't enabled...", isError=True)

def delete_pin(user, deviceId):
    lock_manager_plugin = indigo.server.getPlugin("com.howartp.lockmanager")
    if lock_manager_plugin.isEnabled():
       lock_manager_plugin.executeAction("clearUserPin", deviceId=deviceId, props={'userNo': user})
    else:
        log("Lock manager plugin isn't enabled...", isError=True)
       
def log(message, isError=False):
    indigo.server.log(message, type=LOG_TYPE, isError=isError)
    if isError and ACTION_GROUP_ID:
        try:
            indigo.actionGroup.execute(ACTION_GROUP_ID)
        except:
            indigo.server.log("Couldn't execute error action group specified in the script.", type=LOG_TYPE, isError=isError)
   
try:
    var_folder = indigo.variables.folders[PIN_VARIABLE_FOLDER_ID]
except:
    log("Specified variable folder doesn't exist.", isError=True)
    return

var_list = [var for var in indigo.variables if var.folderId == PIN_VARIABLE_FOLDER_ID]
if len(var_list):
    for var in var_list:
        try:
            user = var.name.split("_")[1]
        except:
            log("Variable name ({}) doesn't appear to be formatted correctly, skipping".format(var.name), isError=True)
            continue
        try:
            if len(var.value):
                action, pin, start_date_string, end_date_string = var.value.split(PIN_STRING_SEPARATOR)
            else:
                continue
        except:
            log("Variable ({}) doesn't appear to have a propertly formatted string ({}), skipping...".format(var.name, var.value), isError=True)
            continue
        if action == "start":
            try:
                start_date = datetime.strptime(start_date_string, DATE_FORMAT)
            except:
                log("Error getting start date from variable '{}' with value '{}', skipping...".format(var.name, var.value), isError=True)
                continue
            if datetime.now() > start_date:
                try:
                    set_pin(pin, user, DEVICE_ID)
                    log("Pin added for user {}".format(user))
                    updated_var_value = "{};{};{};{}".format("end", pin, start_date_string, end_date_string)
                    indigo.variable.updateValue(var, value=updated_var_value)
                except:
                    log("Error setting pin - verify the device id in the script is correct", isError=True)
        elif action == "end":
            try:
                end_date = datetime.strptime(end_date_string, DATE_FORMAT)
            except ValueError as exc:
                log("Error deleting pin from variable '{}' with value '{}', skipping...".format(var.name, var.value), isError=True)
                continue
            if datetime.now() > end_date:
                try:
                    delete_pin(user, DEVICE_ID)
                    log("Pin deleted for user {}".format(user))
                    indigo.variable.updateValue(var, value="")
                except:
                    log("Error deleting pin - verify the device id in the script is correct", isError=True)
        else:
            log("Unknown action: {}".format(action), isError=True)
else:
    log("No variables found in the specified variable folder.", isError=True)


NOTE: I haven't done exhaustive testing on this script, so you'll probably want to do more. I think I've got all error cases covered with notifications, but it's possible I've missed some. YMMV.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Thu Apr 21, 2022 3:23 pm
whmoorejr offline
User avatar
Posts: 762
Joined: Jan 15, 2013
Location: Houston, TX

Re: Z-Wave Lock PIN Scheduler

I think this will work for a recurring PIN schedule

schedule a midnight action that repeats nightly for 9a to 5p access for user id_X

Code: Select all
from datetime import datetime

today = datetime.date.today()
tomorrow = datetime.date.today() + datetime.timedelta(days=1)
indigo.variable.updateValue(748482965, value="start;1234;" + today + "09:00;" + today + "17:00")  #where the number is the VarID for id_X

Bill
My Plugin: My People

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 1 guest