[Deprecated] Ring Alarm Keypad G2 - New Dev/Driver/Thread Links in 1st post!

Merged, thank you!

1 Like

I see the problem now.

Arm away (etc) events from the keypad are received by parseEntryControl -> armAway -> armAwayEnd

However, the Arm Away/Arm Home/Disarm buttons on the device page (provided by capability SecurityKeypad) call armAway/disarm directly.

The sendEvent armingIn that HSM subscribes to is only inside parseEntryControl.

So, the device page goes direct (never touches parseEntryControl) -- which never get the opportunity to send the armingIn event.

I moved the armingIn event inside armHomeEnd/armAwayEnd/armNightEnd/disarmEnd and all seems to work.

  • rule machine can arm/disarm HSM and keypads reflect correctly
  • device page arms/disarms HSM and keypads reflect correctly
  • keypads arm/disarm HSM and other keypads stay in sync

w00t.

Can you test this before merging ?

Seems to work.

Nope, sorry. I've moved all my keypads over to a Ring hub. Feel free to branch off this driver and continue to offer it up to the community.

doh! I'd appreciate if someones reading this can give it a test.

code is temporarily here

EDIT: code was merged, fingers crossed.

I’ll give it a shot. I have just one keypad but I’ll order another if this works. Thanks for working on it.

During Black Friday I almost bought more into the ring ecosystem, but I held off for another year.

I’ll reply back with an update soon.

I'm not an expert on GitHub stuff (yet, anyway) and I would hate to bust something too badly...

But, I was looking at the driver and made some changes (on my hub):

  1. I added a preference for the "Home Arm" button:
  • After "validateCheck", I inserted this:
       input name: "partialFunctionValue", type: "enum", title: "Home Button Action", options: [
            ["armHome":"Arm Home (default)"],
            ["armNight":"Arm Night"],
        ], defaultValue: "armHome", description: "After setting this, press \"Save Preferences\" below then press the \"Set Partial Function\" button above"
  • Then, in the "setPartialFunction" function, below the "if (logEnable)" line, I added this (and I fixed the spelling of "setPartialFuntion" in that logging line also) :
    if (!mode) {
        mode = partialFunctionValue
        if (logEnable) log.debug "In setPartialFunction (${version()}) - set mode from preferences: ${mode}"
    }
  1. I added code to set "validCode" to FALSE in 4 places in the "parseEntryControl" function:
  • In "case 2" (check mark), in the else clause for the "if (validateCheck)" test
  • In "case 17" (police), before the first existing "SendEvent"
  • In "case 16" (fire), before the first existing "SendEvent"
  • In "case 19" (medical), before the first existing "SendEvent"

using this line of code:

sendEvent(name:"validCode", value: "false", isStateChange: true)
  1. Updated the "armNightEnd" function to set the keypad status:
  • Uncommented the "keypadUpdateStatus" call
  • Updated the "keypadUpdateStatus" function to send the "0x0A" status to the keypad (like Armed Home) while still setting the status to "0x00" in the driver for HSM, inserting a few lines then updating the existing "sendToDevice" call as follows
    int kpstatus = status
    if (kpstatus == 0x00) kpstatus = 0x0A

    sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:kpstatus, propertyId:2, value:0xFF]]).format())
Full Driver Code
/*
    Ring Keypad Gen 2 - Community Driver

    Copyright 2020 -> 2021 Hubitat Inc.  All Rights Reserved
    Special Thanks to Bryan Copeland (@bcopeland) for writing and releasing this code to the community!

    1.2.9 - 12/04/22 - Force "validCode" false for Police, Fire, Medical buttons and when "Check Mark" codes aren't validated, add preference to allow Partial Function to be set, set the keypad status in armNightEnd
    1.2.8 - 12/01/22 - Fix device page buttons by sending armingIn event in the arm/disarm subs
    1.2.7 - 12/01/22 - Fix Arm Night on device page
    1.2.6 - 08/05/22 - Adjusted for use with HSM. To sync keypads without using HPM, a separate app will be available in Bundle Manager (Ring Keypad Sync).
    1.2.4 - 07/01/22 - Rollback to working version
    1.2.2 - 06/09/22 - @dkilgore90 add "validCode" attribute and "validateCheck" preference
    1.2.1 - 04/14/22 - Bug hunting
    1.2.0 - 04/04/22 - Fixed Tones
    ---
    1.0.0 - 11/11/21 - Initial Community Release
*/

import groovy.transform.Field
import groovy.json.JsonOutput

def version() {
    return "1.2.8.1"
}

metadata {
    definition (name: "Ring Alarm Keypad G2 Community", namespace: "hubitat", author: "Community") {
        capability "Actuator"
        capability "Sensor"
        capability "Configuration"
        capability "SecurityKeypad"
        capability "Battery"
        capability "Alarm"
        capability "PowerSource"
        capability "LockCodes"
        capability "Motion Sensor"
        capability "PushableButton"
        capability "HoldableButton"

        command "entry"
        command "keypadUpdateStatus", ["string"]
        command "setArmNightDelay", ["number"]
        command "setArmAwayDelay", ["number"]
        command "setArmHomeDelay", ["number"]
        command "setArmNightDelay", ["number"]
        command "setPartialFunction"
        command "resetKeypad"
        command "playTone", [[name: "Play Tone", type: "STRING", description: "Tone_1, Tone_2, etc."]]
        command "volAnnouncement", [[name:"Announcement Volume", type:"NUMBER", description: "Volume level (1-10)"]]
        command "volKeytone", [[name:"Keytone Volume", type:"NUMBER", description: "Volume level (1-10)"]]
        command "volSiren", [[name:"Chime Tone Volume", type:"NUMBER", description: "Volume level (1-10)"]]

        attribute "alarmStatusChangeTime", "STRING"
        attribute "alarmStatusChangeEpochms", "NUMBER"
        attribute "armingIn", "NUMBER"
        attribute "armAwayDelay", "NUMBER"
        attribute "armHomeDelay", "NUMBER"
        attribute "armNightDelay", "NUMBER"
        attribute "lastCodeName", "STRING"
        attribute "lastCodeTime", "STRING"
        attribute "lastCodeEpochms", "NUMBER"
        attribute "motion", "STRING"
        attribute "validCode", "ENUM", ["true", "false"]
        attribute "volAnnouncement", "NUMBER"
        attribute "volKeytone", "NUMBER"
        attribute "volSiren", "NUMBER"

        fingerprint mfr:"0346", prod:"0101", deviceId:"0301", inClusters:"0x5E,0x98,0x9F,0x6C,0x55", deviceJoinName: "Ring Alarm Keypad G2"
    }
    preferences {
        input name: "about", type: "paragraph", element: "paragraph", title: "Ring Alarm Keypad G2 Community Driver", description: "${version()}<br>Note:<br>The first 3 Tones are alarm sounds that also flash the Red Indicator Bar on the keypads. The rest are more pleasant sounds that could be used for a variety of things."
        configParams.each { input it.value.input }
        input name: "theTone", type: "enum", title: "Chime tone", options: [
            ["Tone_1":"(Tone_1) Siren (default)"],
            ["Tone_2":"(Tone_2) 3 Beeps"],
            ["Tone_3":"(Tone_3) 4 Beeps"],
            ["Tone_4":"(Tone_4) Navi"],
            ["Tone_5":"(Tone_5) Guitar"],
            ["Tone_6":"(Tone_6) Windchimes"],
            ["Tone_7":"(Tone_7) DoorBell 1"],
            ["Tone_8":"(Tone_8) DoorBell 2"],
            ["Tone_9":"(Tone_9) Invalid Code Sound"],
        ], defaultValue: "Tone_1", description: ""
        input name: "instantArming", type: "bool", title: "Enable set alarm without code", defaultValue: false, description: ""
        input name: "validateCheck", type: "bool", title: "Validate codes submitted with checkmark", defaultValue: false, description: ""
        input name: "partialFunctionValue", type: "enum", title: "Home Button Action", options: [
            ["armHome":"Arm Home (default)"],
            ["armNight":"Arm Night"],
        ], defaultValue: "armHome", description: "After setting this, press \"Save Preferences\" below then press the \"Set Partial Function\" button above"
        input name: "proximitySensor", type: "bool", title: "Disable the Proximity Sensor", defaultValue: false, description: ""
        input name: "optEncrypt", type: "bool", title: "Enable lockCode encryption", defaultValue: false, description: ""
        input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
        input name: "txtEnable", type: "bool", title: "Enable descriptionText logging", defaultValue: true
    }
}

@Field static Map configParams = [
        //4: [input: [name: "configParam4", type: "enum", title: "Announcement Volume", description:"", defaultValue:7, options:[0:"0",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",10:"10"]],parameterSize:1],
        //5: [input: [name: "configParam5", type: "enum", title: "Keytone Volume", description:"", defaultValue:6, options:[0:"0",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",10:"10"]],parameterSize:1],
        //6: [input: [name: "configParam6", type: "enum", title: "Siren Volume", description:"", defaultValue:10, options:[0:"0",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",10:"10"]],parameterSize:1],
        7: [input: [name: "configParam7", type: "number", title: "Long press Emergency Duration", description:"", defaultValue: 3, range:"2..5"],parameterSize:1],
        8: [input: [name: "configParam8", type: "number", title: "Long press Number pad Duration", description:"", defaultValue: 3, range:"2..5"],parameterSize:1],
        12: [input: [name: "configParam12", type: "number", title: "Security Mode Brightness", description:"", defaultValue: 100, range:"0..100"],parameterSize:1],
        13: [input: [name: "configParam13", type: "number", title: "Key Backlight Brightness", description:"", defaultValue: 100, range:"0..100"],parameterSize:1],
]
@Field static Map armingStates = [
        0x00: [securityKeypadState: "armed night", hsmCmd: "armNight"],
        0x02: [securityKeypadState: "disarmed", hsmCmd: "disarm"],
        0x0A: [securityKeypadState: "armed home", hsmCmd: "armHome"],
        0x0B: [securityKeypadState: "armed away", hsmCmd: "armAway"]
]
@Field static Map CMD_CLASS_VERS=[0x86:2, 0x70:1, 0x20:1, 0x86:3]

void logsOff(){
    log.warn "debug logging disabled..."
    device.updateSetting("logEnable", [value:"false", type:"bool"])
}

void updated() {
    log.info "updated..."
    log.warn "debug logging is: ${logEnable == true}"
    log.warn "description logging is: ${txtEnable == true}"
    log.warn "encryption is: ${optEncrypt == true}"
    unschedule()
    if (logEnable) runIn(1800,logsOff)
    sendToDevice(runConfigs())
    updateEncryption()
    proximitySensorHandler()
    volAnnouncement()
    volKeytone()
    volSiren()
}

void installed() {
    initializeVars()
}

void uninstalled() {

}

void initializeVars() {
    // first run only
    sendEvent(name:"codeLength", value: 4)
    sendEvent(name:"maxCodes", value: 100)
    sendEvent(name:"lockCodes", value: "")
    sendEvent(name:"armHomeDelay", value: 5)
    sendEvent(name:"armAwayDelay", value: 5)
    sendEvent(name:"armNightDelay", value: 5)
    sendEvent(name:"volAnnouncement", value: 7)
    sendEvent(name:"volKeytone", value: 5)
    sendEvent(name:"volSiren", value: 75)
    sendEvent(name:"securityKeypad", value:"disarmed")
    state.keypadConfig=[entryDelay:5, exitDelay: 5, armNightDelay:5, armAwayDelay:5, armHomeDelay: 5, codeLength: 4, partialFunction: "armHome"]
    state.keypadStatus=2
    state.initialized=true
}

void resetKeypad() {
    state.initialized=false
    configure()
    getCodes()
}

void configure() {
    if (logEnable) log.debug "configure()"
    if (!state.initialized) initializeVars()
    if (!state.keypadConfig) initializeVars()
    keypadUpdateStatus(state.keypadStatus, state.type, state.code)
    runIn(5,pollDeviceData)
}

void pollDeviceData() {
    List<String> cmds = []
    cmds.add(zwave.versionV3.versionGet().format())
    cmds.add(zwave.manufacturerSpecificV2.deviceSpecificGet(deviceIdType: 1).format())
    cmds.add(zwave.batteryV1.batteryGet().format())
    cmds.add(zwave.notificationV8.notificationGet(notificationType: 8, event: 0).format())
    cmds.add(zwave.notificationV8.notificationGet(notificationType: 7, event: 0).format())
    cmds.addAll(processAssociations())
    sendToDevice(cmds)
}

void keypadUpdateStatus(Integer status,String type="digital", String code) {
    int kpstatus = status
    if (kpstatus == 0x00) kpstatus = 0x0A

    sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:kpstatus, propertyId:2, value:0xFF]]).format())
    state.keypadStatus = status
    if (state.code != "") { type = "physical" }
    eventProcess(name: "securityKeypad", value: armingStates[status].securityKeypadState, type: type, data: state.code)
    state.code = ""
    state.type = "digital"
}

void armNight(delay) {
    if (logEnable) log.debug "In armNight (${version()}) - delay: ${delay}"
    def sk = device.currentValue("securityKeypad")
    if(sk != "armed night") {
        if (delay > 0 ) {
            exitDelay(delay)
            runIn(delay, armNightEnd)
        } else {
            armNightEnd()
        }
    } else {
        if (logEnable) log.debug "In armNight - securityKeypad already set to 'armed night', so skipping."
    }
}

void armNightEnd() {
    if (!state.code) { state.code = "" }
    if (!state.type) { state.type = "physical" }
    def sk = device.currentValue("securityKeypad")
    if(sk != "armed night") {
        keypadUpdateStatus(0x00, state.type, state.code)

        Date now = new Date()
        long ems = now.getTime()
        sendEvent(name:"armingIn", value: state.keypadConfig.armNightDelay, data:[armMode: armingStates[0x0B].securityKeypadState, armCmd: armingStates[0x0B].hsmCmd], isStateChange:true)
        sendEvent(name:"alarmStatusChangeTime", value: "${now}", isStateChange:true)
        sendEvent(name:"alarmStatusChangeEpochms", value: "${ems}", isStateChange:true)
    }
}

void armAway(delay) {
    if (logEnable) log.debug "In armAway (${version()}) - delay: ${delay}"
    def sk = device.currentValue("securityKeypad")
    if(sk != "armed away") {
        if (delay > 0 ) {
            exitDelay(delay)
            runIn(delay, armAwayEnd)
        } else {
            armAwayEnd()
        }
    } else {
        if (logEnable) log.debug "In armAway - securityKeypad already set to 'armed away', so skipping."
    }
}

void armAwayEnd() {
    if (!state.code) { state.code = "" }
    if (!state.type) { state.type = "physical" }
    def sk = device.currentValue("securityKeypad")
    if(sk != "armed away") {
        keypadUpdateStatus(0x0B, state.type, state.code)

        Date now = new Date()
        long ems = now.getTime()
        sendEvent(name:"armingIn", value: state.keypadConfig.armAwayDelay, data:[armMode: armingStates[0x0B].securityKeypadState, armCmd: armingStates[0x0B].hsmCmd], isStateChange:true)
        sendEvent(name:"alarmStatusChangeTime", value: "${now}", isStateChange:true)
        sendEvent(name:"alarmStatusChangeEpochms", value: "${ems}", isStateChange:true)
        changeStatus("set")
    }
}

void armHome(delay) {
    if (logEnable) log.debug "In armHome (${version()}) - delay: ${delay}"
    def sk = device.currentValue("securityKeypad")
    if(sk != "armed home") {
        if (delay > 0) {
            exitDelay(delay)
            runIn(delay, armHomeEnd)
        } else {
            armHomeEnd()
        }
    } else {
        if (logEnable) log.debug "In armHome - securityKeypad already set to 'armed home', so skipping."
    }
}

void armHomeEnd() {
    if (!state.code) { state.code = "" }
    if (!state.type) { state.type = "physical" }
    def sk = device.currentValue("securityKeypad")
    if(sk != "armed home") {
        keypadUpdateStatus(0x0A, state.type, state.code)

        Date now = new Date()
        long ems = now.getTime()
        sendEvent(name:"armingIn", value: state.keypadConfig.armHomeDelay, data:[armMode: armingStates[0x0A].securityKeypadState, armCmd: armingStates[0x0A].hsmCmd], isStateChange:true)
        sendEvent(name:"alarmStatusChangeTime", value: "${now}", isStateChange:true)
        sendEvent(name:"alarmStatusChangeEpochms", value: "${ems}", isStateChange:true)
        changeStatus("set")
    }
}

void disarm(delay) {
    if (logEnable) log.debug "In disarm (${version()}) - delay: ${delay}"
    def sk = device.currentValue("securityKeypad")
    if(sk != "disarmed") {
        if (delay > 0 ) {
            exitDelay(delay)
            runIn(delay, disarmEnd)
        } else {
            disarmEnd()
        }
    } else {
        if (logEnable) log.debug "In disarm - securityKeypad already set to 'disarmed', so skipping."
    }
}

void disarmEnd() {
    if (!state.code) { state.code = "" }
    if (!state.type) { state.type = "physical" }
    def sk = device.currentValue("securityKeypad")
    if(sk != "disarmed") {
        keypadUpdateStatus(0x02, state.type, state.code)

        Date now = new Date()
        long ems = now.getTime()
        sendEvent(name:"armingIn", value: 0, data:[armMode: armingStates[0x02].securityKeypadState, armCmd: armingStates[0x02].hsmCmd], isStateChange:true)
        sendEvent(name:"alarmStatusChangeTime", value: "${now}", isStateChange:true)
        sendEvent(name:"alarmStatusChangeEpochms", value: "${ems}", isStateChange:true)

        changeStatus("off")
        unschedule(armHomeEnd)
        unschedule(armAwayEnd)
        unschedule(armNightEnd)
        unschedule(changeStatus)
    }
}

void exitDelay(delay){
    if (logEnable) log.debug "In exitDelay (${version()}) - delay: ${delay}"
    if (delay) {
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x12, propertyId:7, value:delay.toInteger()]]).format())
        // update state so that a disarm command during the exit delay resets the indicator lights
        state.keypadStatus = "18"
        type = state.code != "" ? "physical" : "digital"
        eventProcess(name: "securityKeypad", value: "exit delay", type: type, data: state.code)
    }
}

def changeStatus(data) {
    if (logEnable) log.debug "In changeStatus (${version()}) - data: ${data}"
    sendEvent(name: "alarm", value: data, isStateChange: true)
}

void setEntryDelay(delay){
    if (logEnable) log.debug "In setEntryDelay (${version()}) - delay: ${delay}"
    state.keypadConfig.entryDelay = delay != null ? delay.toInteger() : 0
}

void setExitDelay(Map delays){
    if (logEnable) log.debug "In setExitDelay (${version()}) - delay: ${delays}"
    state.keypadConfig.exitDelay = (delays?.awayDelay ?: 0).toInteger()
    state.keypadConfig.armNightDelay = (delays?.nightDelay ?: 0).toInteger()
    state.keypadConfig.armHomeDelay = (delays?.homeDelay ?: 0).toInteger()
    state.keypadConfig.armAwayDelay = (delays?.awayDelay ?: 0).toInteger()
}

void setExitDelay(delay){
    if (logEnable) log.debug "In setExitDelay (${version()}) - delay: ${delay}"
    state.keypadConfig.exitDelay = delay != null ? delay.toInteger() : 0
}

void setArmNightDelay(delay){
    if (logEnable) log.debug "In setArmNightDelay (${version()}) - delay: ${delay}"
    sendEvent(name:"armNightDelay", value: delay)
    state.keypadConfig.armNightDelay = delay != null ? delay.toInteger() : 0
}

void setArmAwayDelay(delay){
    if (logEnable) log.debug "In setArmAwayDelay (${version()}) - delay: ${delay}"
    sendEvent(name:"armAwayDelay", value: delay)
    state.keypadConfig.armAwayDelay = delay != null ? delay.toInteger() : 0
}

void setArmHomeDelay(delay){
    if (logEnable) log.debug "In setArmHomeDelay (${version()}) - delay: ${delay}"
    sendEvent(name:"armHomeDelay", value: delay)
    state.keypadConfig.armHomeDelay = delay != null ? delay.toInteger() : 0

}
void setCodeLength(pincodelength) {
    if (logEnable) log.debug "In setCodeLength (${version()}) - pincodelength: ${pincodelength}"
    eventProcess(name:"codeLength", value: pincodelength, descriptionText: "${device.displayName} codeLength set to ${pincodelength}")
    state.keypadConfig.codeLength = pincodelength
    // set zwave entry code key buffer
    // 6F06XX10
    sendToDevice("6F06" + hubitat.helper.HexUtils.integerToHexString(pincodelength.toInteger()+1,1).padLeft(2,'0') + "0F")
}

void setPartialFunction(mode = null) {
    if (logEnable) log.debug "In setPartialFunction (${version()}) - mode: ${mode}"
    if (!mode) {
        mode = partialFunctionValue
        if (logEnable) log.debug "In setPartialFunction (${version()}) - set mode from preferences: ${mode}"
    }
    if ( !(mode in ["armHome","armNight"]) ) {
        if (txtEnable) log.warn "custom command used by HSM"
    } else if (mode in ["armHome","armNight"]) {
        state.keypadConfig.partialFunction = mode == "armHome" ? "armHome" : "armNight"
    }
}

// alarm capability commands

void off() {
    if (logEnable) log.debug "In off (${version()})"
    eventProcess(name:"alarm", value:"off")
    changeStatus("off")
    sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:state.keypadStatus, propertyId:2, value:0xFF]]).format())
}

void both() {
    if (logEnable) log.debug "In both (${version()})"
    siren()
}

void siren() {
    if (logEnable) log.debug "In Siren (${version()})"
    eventProcess(name:"alarm", value:"siren")
    changeStatus("siren")
    sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x0C, propertyId:2, value:0xFF]]).format())
}

void strobe() {
    if (logEnable) log.debug "In strobe (${version()})"
    eventProcess(name:"alarm", value:"strobe")
    changeStatus("strobe")
    List<String> cmds=[]
    cmds.add(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x0C, propertyId:2, value:0xFF]]).format())
    cmds.add(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x0C, propertyId:2, value:0x00]]).format())
    sendToDevice(cmds)
}

void zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd) {
    // this is redundant/ambiguous and I don't care what happens here
}

void parseEntryControl(Short command, List<Short> commandBytes) {
    if (logEnable) log.debug "In parseEntryControl (${version()})"
    //log.debug "parse: ${command}, ${commandBytes}"
    if (command == 0x01) {
        Map ecn = [:]
        ecn.sequenceNumber = commandBytes[0]
        ecn.dataType = commandBytes[1]
        ecn.eventType = commandBytes[2]
        ecn.eventDataLength = commandBytes[3]
        def currentStatus = device.currentValue('securityKeypad')
        def alarmStatus = device.currentValue('alarm')
        String code=""
        if (ecn.eventDataLength>0) {
            for (int i in 4..(ecn.eventDataLength+3)) {
                if (logEnable) log.debug "character ${i}, value ${commandBytes[i]}"
                code += (char) commandBytes[i]
            }
        }
        if (logEnable) log.debug "Entry control: ${ecn} keycache: ${code}"
        switch (ecn.eventType) {
            case 5:    // Away Mode Button
                if (logEnable) log.debug "In case 5 - Away Mode Button"
                if (validatePin(code) || instantArming) {
                    if(currentStatus == "disarmed") {
                        if (logEnable) log.debug "In case 5 - Passed - currentStatus: ${currentStatus}"
                        state.type="physical"
                        if (!state.keypadConfig.armAwayDelay) { state.keypadConfig.armAwayDelay = 0 }
                        armAway(state.keypadConfig.armAwayDelay)
                    } else {
                        if (logEnable) log.debug "In case 5 - Failed - Please Disarm Alarm before changing alarm type - currentStatus: ${currentStatus}"
                    }
                } else {
                    if (logEnable) log.debug "In case 5 - Failed - Invalid PIN - currentStatus: ${currentStatus}"
                    sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x09, propertyId:2, value:0xFF]]).format())
                }
                break
            case 6:    // Home Mode Button
                if (logEnable) log.debug "In case 6 - Home Mode Button"
                if (validatePin(code) || instantArming) {
                    if(currentStatus == "disarmed") {
                        if (logEnable) log.debug "In case 6 - Passed"
                        state.type="physical"
                        if(!state.keypadConfig.partialFunction) state.keypadConfig.partialFunction="armHome"
                        if (state.keypadConfig.partialFunction == "armHome") {
                            if (logEnable) log.debug "In case 6 - Partial Passed"
                            if (!state.keypadConfig.armHomeDelay) { state.keypadConfig.armHomeDelay = 0 }
                            armHome(state.keypadConfig.armHomeDelay)
                        }
                    } else {
                        if(alarmStatus == "active") {
                            if (logEnable) log.debug "In case 6 - Silenced - Alarm will sound again in 10 seconds - currentStatus: ${currentStatus}"
                            changeStatus("silent")
                            runIn(10, changeStatus, [data:"active"])
                        } else {
                            if (logEnable) log.debug "In case 6 - Failed - Please Disarm Alarm before changing alarm type - currentStatus: ${currentStatus}"
                        }
                    }
                } else {
                    if (logEnable) log.debug "In case 6 - Home Mode Failed - Invalid PIN - currentStatus: ${currentStatus}"
                    sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x09, propertyId:2, value:0xFF]]).format())
                }
                break
            case 3:    // Disarm Mode Button
                if (logEnable) log.debug "In case 3 - Disarm Mode Button"
                if (validatePin(code)) {
                    if (logEnable) log.debug "In case 3 - Code Passed"
                    state.type="physical"
                    disarm()
                } else {
                    if (logEnable) log.debug "In case 3 - Disarm Failed - Invalid PIN - currentStatus: ${currentStatus}"
                    sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x09, propertyId:2, value:0xFF]]).format())
                }
                break
            // Added all buttons
            case 2:    // Code sent after hitting the Check Mark
                state.type="physical"
                Date now = new Date()
                long ems = now.getTime()
                if(!code) code = "check mark"
                if (validateCheck) {
                    if (validatePin(code)) {
                        if (logEnable) log.debug "In case 2 (check mark) - Code Passed"
                    } else {
                        if (logEnable) log.debug "In case 2 (check mark) - Code Failed - Invalid PIN - currentStatus: ${currentStatus}"
                        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x09, propertyId:2, value:0xFF]]).format())
                    }
                } else {
                    sendEvent(name:"validCode", value: "false", isStateChange: true)
                    sendEvent(name:"lastCodeName", value: "${code}", isStateChange:true)
                    sendEvent(name:"lastCodeTime", value: "${now}", isStateChange:true)
                    sendEvent(name:"lastCodeEpochms", value: "${ems}", isStateChange:true)
                }
                break
            case 17:    // Police Button
                state.type="physical"
                Date now = new Date()
                long ems = now.getTime()
                sendEvent(name:"validCode", value: "false", isStateChange: true)
                sendEvent(name:"lastCodeName", value: "police", isStateChange:true)
                sendEvent(name:"lastCodeTime", value: "${now}", isStateChange:true)
                sendEvent(name:"lastCodeEpochms", value: "${ems}", isStateChange:true)
                sendEvent(name: "held", value: 11, isStateChange: true)
                break
            case 16:    // Fire Button
                state.type="physical"
                Date now = new Date()
                long ems = now.getTime()
                sendEvent(name:"validCode", value: "false", isStateChange: true)
                sendEvent(name:"lastCodeName", value: "fire", isStateChange:true)
                sendEvent(name:"lastCodeTime", value: "${now}", isStateChange:true)
                sendEvent(name:"lastCodeEpochms", value: "${ems}", isStateChange:true)
                sendEvent(name: "held", value: 12, isStateChange: true)
                break
            case 19:    // Medical Button
                state.type="physical"
                Date now = new Date()
                long ems = now.getTime()
                sendEvent(name:"validCode", value: "false", isStateChange: true)
                sendEvent(name:"lastCodeName", value: "medical", isStateChange:true)
                sendEvent(name:"lastCodeTime", value: "${now}", isStateChange:true)
                sendEvent(name:"lastCodeEpochms", value: "${ems}", isStateChange:true)
                sendEvent(name: "held", value: 13, isStateChange: true)
                break
            case 1:     // Button pressed or held, idle timeout reached without explicit submission
                state.type="physical"
                handleButtons(code)
                break
        }
    }
}

void handleButtons(String code) {
    List<String> buttons = code.split('')
    for (String btn : buttons) {
        try {
            int val = Integer.parseInt(btn)
            sendEvent(name: "pushed", value: val, isStateChange: true)
        } catch (NumberFormatException e) {
            // Handle button holds here
            char ch = btn
            char a = 'A'
            int pos = ch - a + 1
            sendEvent(name: "held", value: pos, isStateChange: true)
        }
    }
}

void push(btn) {
    state.type = "digital"
    sendEvent(name: "pushed", value: btn, isStateChange: true)
}

void hold(btn) {
    state.type = "digital"
    sendEvent(name: "held", value: btn, isStateChange:true)
    switch (btn) {
        case 11:
            Date now = new Date()
            long ems = now.getTime()
            sendEvent(name:"lastCodeName", value: "police", isStateChange:true)
            sendEvent(name:"lastCodeTime", value: "${now}", isStateChange:true)
            sendEvent(name:"lastCodeEpochms", value: "${ems}", isStateChange:true)
            break
        case 12:
            Date now = new Date()
            long ems = now.getTime()
            sendEvent(name:"lastCodeName", value: "fire", isStateChange:true)
            sendEvent(name:"lastCodeTime", value: "${now}", isStateChange:true)
            sendEvent(name:"lastCodeEpochms", value: "${ems}", isStateChange:true)
            break
        case 13:
            Date now = new Date()
            long ems = now.getTime()
            sendEvent(name:"lastCodeName", value: "medical", isStateChange:true)
            sendEvent(name:"lastCodeTime", value: "${now}", isStateChange:true)
            sendEvent(name:"lastCodeEpochms", value: "${ems}", isStateChange:true)
            break
    }
}

void zwaveEvent(hubitat.zwave.commands.notificationv8.NotificationReport cmd) {
    Map evt = [:]
    if (cmd.notificationType == 8) {
        // power management
        switch (cmd.event) {
            case 1:
                // Power has been applied
                if (txtEnable) log.info "${device.displayName} Power has been applied"
                break
            case 2:
                // AC mains disconnected
                evt.name = "powerSource"
                evt.value = "battery"
                evt.descriptionText = "${device.displayName} AC mains disconnected"
                eventProcess(evt)
                break
            case 3:
                // AC mains re-connected
                evt.name = "powerSource"
                evt.value = "mains"
                evt.descriptionText = "${device.displayName} AC mains re-connected"
                eventProcess(evt)
                break
            case 12:
                // battery is charging
                if (txtEnable) log.info "${device.displayName} Battery is charging"
                break
        }
    }
}

void entry(){
    int intDelay = state.keypadConfig.entryDelay ? state.keypadConfig.entryDelay.toInteger() : 0
    if (intDelay) entry(intDelay)
}

void entry(entranceDelay){
    if (logEnable) log.debug "In entry (${version()}) - delay: ${entranceDelay}"
    if (entranceDelay) {
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x11, propertyId:7, value:entranceDelay.toInteger()]]).format())
    }
}

void getCodes(){
    if (logEnable) log.debug "getCodes()"
    updateEncryption()
}

private updateEncryption(){
    String lockCodes = device.currentValue("lockCodes") //encrypted or decrypted
    if (lockCodes){
        if (optEncrypt && lockCodes[0] == "{") {   //resend encrypted
            sendEvent(name:"lockCodes",value: encrypt(lockCodes), isStateChange:true)
        } else if (!optEncrypt && lockCodes[0] != "{") { //resend decrypted
            sendEvent(name:"lockCodes", value: decrypt(lockCodes), isStateChange:true)
        } else {
            sendEvent(name:"lockCodes", value: lockCodes, isStateChange:true)
        }
    }
}

private Boolean validatePin(String pincode) {
    boolean retVal = false
    Map lockcodes = [:]
    if (optEncrypt) {
        try {
            lockcodes = parseJson(decrypt(device.currentValue("lockCodes")))
        } catch(e) {
            log.warn "Ring Alarm Keypad G2 Community - No lock codes found."
        }
    } else {
        try {
            lockcodes = parseJson(device.currentValue("lockCodes"))
        } catch(e) {
            log.warn "Ring Alarm Keypad G2 Community - No lock codes found."
        }
    }
    //log.debug "Lock codes: ${lockcodes}"
    if(lockcodes) {
        lockcodes.each {
            if(it.value["code"] == pincode) {
                Date now = new Date()
                long ems = now.getTime()
                //log.debug "found code: ${pincode} user: ${it.value['name']}"
                sendEvent(name:"validCode", value: "true", isStateChange: true)
                sendEvent(name:"lastCodeName", value: "${it.value['name']}", isStateChange:true)
                sendEvent(name:"lastCodeTime", value: "${now}", isStateChange:true)
                sendEvent(name:"lastCodeEpochms", value: "${ems}", isStateChange:true)
                retVal=true
                String code = JsonOutput.toJson(["${it.key}":["name": "${it.value.name}", "code": "${it.value.code}", "isInitiator": true]])
                if (optEncrypt) {
                    state.code=encrypt(code)
                } else {
                    state.code=code
                }
            }
        }
    }
    if (!retVal) {
        sendEvent(name:"validCode", value: "false", isStateChange: true)
    }
    return retVal
}

void setCode(codeposition, pincode, name) {
    if (logEnable) log.debug "setCode(${codeposition}, ${pincode}, ${name})"
    boolean newCode = true
    Map lockcodes = [:]
    if (device.currentValue("lockCodes") != null) {
        if (optEncrypt) {
            lockcodes = parseJson(decrypt(device.currentValue("lockCodes")))
        } else {
            lockcodes = parseJson(device.currentValue("lockCodes"))
        }
    }
    if (lockcodes["${codeposition}"]) { newCode = false }
    lockcodes["${codeposition}"] = ["code": "${pincode}", "name": "${name}"]
    if (optEncrypt) {
        sendEvent(name: "lockCodes", value: encrypt(JsonOutput.toJson(lockcodes)))
    } else {
        sendEvent(name: "lockCodes", value: JsonOutput.toJson(lockcodes), isStateChange: true)
    }
    if (newCode) {
        sendEvent(name: "codeChanged", value:"added")
    } else {
        sendEvent(name: "codeChanged", value: "changed")
    }
    //log.debug "Lock codes: ${lockcodes}"
}

void deleteCode(codeposition) {
    if (logEnable) log.debug "deleteCode(${codeposition})"
    Map lockcodes=[:]
    if (device.currentValue("lockCodes") != null) {
        if (optEncrypt) {
            lockcodes = parseJson(decrypt(device.currentValue("lockCodes")))
        } else {
            lockcodes = parseJson(device.currentValue("lockCodes"))
        }
    }
    lockcodes["${codeposition}"] = [:]
    lockcodes.remove("${codeposition}")
    if (optEncrypt) {
        sendEvent(name: "lockCodes", value: encrypt(JsonOutput.toJson(lockcodes)))
    } else {
        sendEvent(name: "lockCodes", value: JsonOutput.toJson(lockcodes), isStateChange: true)
    }
    sendEvent(name: "codeChanged", value: "deleted")
    //log.debug "remove ${codeposition} Lock codes: ${lockcodes}"
}

void zwaveEvent(hubitat.zwave.commands.indicatorv3.IndicatorReport cmd) {
    // Don't need to handle reports
}

// standard config

List<String> runConfigs() {
    List<String> cmds = []
    configParams.each { param, data ->
        if (settings[data.input.name]) {
            cmds.addAll(configCmd(param, data.parameterSize, settings[data.input.name]))
        }
    }
    return cmds
}

List<String> pollConfigs() {
    List<String> cmds = []
    configParams.each { param, data ->
        if (settings[data.input.name]) {
            cmds.add(zwave.configurationV1.configurationGet(parameterNumber: param.toInteger()).format())
        }
    }
    return cmds
}

List<String> configCmd(parameterNumber, size, scaledConfigurationValue) {
    List<String> cmds = []
    cmds.add(zwave.configurationV1.configurationSet(parameterNumber: parameterNumber.toInteger(), size: size.toInteger(), scaledConfigurationValue: scaledConfigurationValue.toInteger()).format())
    cmds.add(zwave.configurationV1.configurationGet(parameterNumber: parameterNumber.toInteger()).format())
    return cmds
}

void zwaveEvent(hubitat.zwave.commands.configurationv1.ConfigurationReport cmd) {
    if(configParams[cmd.parameterNumber.toInteger()]) {
        Map configParam = configParams[cmd.parameterNumber.toInteger()]
        int scaledValue
        cmd.configurationValue.reverse().eachWithIndex { v, index ->
            scaledValue = scaledValue | v << (8 * index)
        }
        device.updateSetting(configParam.input.name, [value: "${scaledValue}", type: configParam.input.type])
    }
}

// Battery v1

void zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {
    Map evt = [name: "battery", unit: "%"]
    if (cmd.batteryLevel == 0xFF) {
        evt.descriptionText = "${device.displayName} has a low battery"
        evt.value = 1
    } else {
        evt.value = cmd.batteryLevel
        evt.descriptionText = "${device.displayName} battery is ${evt.value}${evt.unit}"
    }
    evt.isStateChange = true
    if (txtEnable && evt.descriptionText) log.info evt.descriptionText
    sendEvent(evt)
}

// MSP V2

void zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
    if (logEnable) log.debug "Device Specific Report - DeviceIdType: ${cmd.deviceIdType}, DeviceIdFormat: ${cmd.deviceIdDataFormat}, Data: ${cmd.deviceIdData}"
    if (cmd.deviceIdType == 1) {
        String serialNumber = ""
        if (cmd.deviceIdDataFormat == 1) {
            cmd.deviceIdData.each { serialNumber += hubitat.helper.HexUtils.integerToHexString(it & 0xff,1).padLeft(2, '0')}
        } else {
            cmd.deviceIdData.each { serialNumber += (char) it }
        }
        device.updateDataValue("serialNumber", serialNumber)
    }
}

// Version V2

void zwaveEvent(hubitat.zwave.commands.versionv3.VersionReport cmd) {
    Double firmware0Version = cmd.firmware0Version + (cmd.firmware0SubVersion / 100)
    Double protocolVersion = cmd.zWaveProtocolVersion + (cmd.zWaveProtocolSubVersion / 100)
    if (logEnable) log.debug "Version Report - FirmwareVersion: ${firmware0Version}, ProtocolVersion: ${protocolVersion}, HardwareVersion: ${cmd.hardwareVersion}"
    device.updateDataValue("firmwareVersion", "${firmware0Version}")
    device.updateDataValue("protocolVersion", "${protocolVersion}")
    device.updateDataValue("hardwareVersion", "${cmd.hardwareVersion}")
    if (cmd.firmwareTargets > 0) {
        cmd.targetVersions.each { target ->
            Double targetVersion = target.version + (target.subVersion / 100)
            device.updateDataValue("firmware${target.target}Version", "${targetVersion}")
        }
    }
}

// Association V2

List<String> setDefaultAssociation() {
    List<String> cmds = []
    cmds.add(zwave.associationV2.associationSet(groupingIdentifier: 1, nodeId: zwaveHubNodeId).format())
    cmds.add(zwave.associationV2.associationGet(groupingIdentifier: 1).format())
    return cmds
}

List<String> processAssociations(){
    List<String> cmds = []
    cmds.addAll(setDefaultAssociation())
    return cmds
}

void zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd) {
    List<String> temp = []
    if (cmd.nodeId != []) {
        cmd.nodeId.each {
            temp.add(it.toString().format( '%02x', it.toInteger() ).toUpperCase())
        }
    }
    if (logEnable) log.debug "Association Report - Group: ${cmd.groupingIdentifier}, Nodes: $temp"
}

// event filter

void eventProcess(Map evt) {
    if (txtEnable && evt.descriptionText) log.info evt.descriptionText
    if (device.currentValue(evt.name).toString() != evt.value.toString()) {
        sendEvent(evt)
    }
}

// universal

void zwaveEvent(hubitat.zwave.Command cmd) {
    if (logEnable) log.debug "skip:${cmd}"
}

void zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
    hubitat.zwave.Command encapsulatedCommand = cmd.encapsulatedCommand(CMD_CLASS_VERS)
    if (encapsulatedCommand) {
        zwaveEvent(encapsulatedCommand)
    }
}

void zwaveEvent(hubitat.zwave.commands.supervisionv1.SupervisionGet cmd) {
    if (logEnable) log.debug "Supervision Get - SessionID: ${cmd.sessionID}, CC: ${cmd.commandClassIdentifier}, Command: ${cmd.commandIdentifier}"
    if (cmd.commandClassIdentifier == 0x6F) {
        parseEntryControl(cmd.commandIdentifier, cmd.commandByte)
    } else {
        hubitat.zwave.Command encapsulatedCommand = cmd.encapsulatedCommand(CMD_CLASS_VERS)
        if (encapsulatedCommand) {
            zwaveEvent(encapsulatedCommand)
        }
    }
    // device quirk requires this to be unsecure reply
    sendToDevice(zwave.supervisionV1.supervisionReport(sessionID: cmd.sessionID, reserved: 0, moreStatusUpdates: false, status: 0xFF, duration: 0).format())
}

void parse(String description) {
    if (logEnable) log.debug "parse - ${description}"
    ver = getDataValue("firmwareVersion")
    if(ver >= "1.18") {
        if(description.contains("6C01") && description.contains("FF 07 08 00")) {
            sendEvent(name:"motion", value: "active", isStateChange:true)
        } else if(description.contains("6C01") && description.contains("FF 07 00 01 08")) {
            sendEvent(name:"motion", value: "inactive", isStateChange:true)
        }
    }
    hubitat.zwave.Command cmd = zwave.parse(description, CMD_CLASS_VERS)
    if (cmd) {
        zwaveEvent(cmd)
    }
}

void sendToDevice(List<String> cmds, Long delay=300) {
    sendHubCommand(new hubitat.device.HubMultiAction(commands(cmds, delay), hubitat.device.Protocol.ZWAVE))
}

void sendToDevice(String cmd, Long delay=300) {
    sendHubCommand(new hubitat.device.HubAction(zwaveSecureEncap(cmd), hubitat.device.Protocol.ZWAVE))
}

List<String> commands(List<String> cmds, Long delay=300) {
    return delayBetween(cmds.collect{ zwaveSecureEncap(it) }, delay)
}

void proximitySensorHandler() {
    if(proximitySensor) {
        if (logEnable) log.debug "Turning the Proximity Sensor OFF"
        sendToDevice(new hubitat.zwave.commands.configurationv1.ConfigurationSet(parameterNumber: 15, size: 1, scaledConfigurationValue: 0).format())
    } else {
        if (logEnable) log.debug "Turning the Proximity Sensor ON"
        sendToDevice(new hubitat.zwave.commands.configurationv1.ConfigurationSet(parameterNumber: 15, size: 1, scaledConfigurationValue: 1).format())
    }
}

def volAnnouncement(newVol=null) {
    if (logEnable) log.debug "In volAnnouncement (${version()}) - newVol: ${newVol}"
    if(newVol) {
        def currentVol = device.currentValue('volAnnouncement')
        if(newVol.toString() == currentVol.toString()) {
            if (logEnable) log.debug "Announcement Volume hasn't changed, so skipping"
        } else {
            if (logEnable) log.debug "Setting the Announcement Volume to $newVol"
            nVol = newVol.toInteger()
            sendToDevice(new hubitat.zwave.commands.configurationv1.ConfigurationSet(parameterNumber: 4, size: 1, scaledConfigurationValue: nVol).format())
            sendEvent(name:"volAnnouncement", value: newVol, isStateChange:true)
        }
    } else {
        if (logEnable) log.debug "Announcement value not specified, so skipping"
    }
}

def volKeytone(newVol=null) {
    if (logEnable) log.debug "In volKeytone (${version()}) - newVol: ${newVol}"
    if(newVol) {
        def currentVol = device.currentValue('volKeytone')
        if(newVol.toString() == currentVol.toString()) {
            if (logEnable) log.debug "Keytone Volume hasn't changed, so skipping"
        } else {
            if (logEnable) log.debug "Setting the Keytone Volume to $newVol"
            nVol = newVol.toInteger()
            sendToDevice(new hubitat.zwave.commands.configurationv1.ConfigurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: nVol).format())
            sendEvent(name:"volKeytone", value: newVol, isStateChange:true)
        }
    } else {
        if (logEnable) log.debug "Keytone value not specified, so skipping"
    }
}

def volSiren(newVol=null) {
    if (logEnable) log.debug "In volSiren (${version()}) - newVol: ${newVol}"
    if(newVol) {
        def currentVol = device.currentValue('volSiren')
        if(newVol.toString() == currentVol.toString()) {
            if (logEnable) log.debug "Siren Volume hasn't changed, so skipping"
            def sVol = currentVol.toInteger() * 10
        } else {
            if (logEnable) log.debug "Setting the Siren Volume to $newVol"
            sVol = newVol.toInteger() * 10
            sendToDevice(new hubitat.zwave.commands.configurationv1.ConfigurationSet(parameterNumber: 6, size: 1, scaledConfigurationValue: sVol).format())
            sendEvent(name:"volSiren", value: newVol, isStateChange:true)
        }
    } else {
        def currentVol = device.currentValue('volSiren')
        if(currentVol) {
            sVol = currentVol.toInteger() * 10
        } else {
            sVol = 90
        }
    }
    return sVol
}

def playTone(tone=null) {
    volSiren()
    if (logEnable) log.debug "In playTone (${version()}) - tone: ${tone} at Volume: ${sVol}"
    if(!tone) {
        tone = theTone
        if (logEnable) log.debug "In playTone - Tone is NULL, so setting tone to theTone: ${tone}"
    }
    if(tone == "Tone_1") {
        if (logEnable) log.debug "In playTone - Tone 1"    // Siren
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x0C, propertyId:2, value:sVol]]).format())
    } else if(tone == "Tone_2") {
        if (logEnable) log.debug "In playTone - Tone 2"    // 3 chirps
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x0E, propertyId:2, value:sVol]]).format())
    } else if(tone == "Tone_3") {
        if (logEnable) log.debug "In playTone - Tone 3"    // 4 chirps
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x0F, propertyId:2, value:sVol]]).format())
    } else if(tone == "Tone_4") {
        if (logEnable) log.debug "In playTone - Tone 4"    // Navi
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x60, propertyId:0x09, value:sVol]]).format())
    } else if(tone == "Tone_5") {
        if (logEnable) log.debug "In playTone - Tone 5"    // Guitar
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x61, propertyId:0x09, value:sVol]]).format())
    } else if(tone == "Tone_6") {
        if (logEnable) log.debug "In playTone - Tone 6"    // Windchimes
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x62, propertyId:0x09, value:sVol]]).format())
    } else if(tone == "Tone_7") {
        if (logEnable) log.debug "In playTone - Tone 7"    // Doorbell 1
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x63, propertyId:0x09, value:sVol]]).format())
    } else if(tone == "Tone_8") {
        if (logEnable) log.debug "In playTone - Tone 8"    // Doorbell 2
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x64, propertyId:0x09, value:sVol]]).format())
    } else if(tone == "Tone_9") {
        if (logEnable) log.debug "In playTone - Tone 9"    // Invalid Code Sound
        changeStatus("active")
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x09, propertyId:0x01, value:sVol]]).format())
    } else if(tone == "test") {
        if (logEnable) log.debug "In playTone - test"    // test
        changeStatus("active")
        //sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x61, propertyId:0x09, value:0xFF]]).format())
        //pauseExecution(5000)
        sendToDevice(zwave.indicatorV3.indicatorSet(indicatorCount:1, value: 0, indicatorValues:[[indicatorId:0x61, propertyId:0x09, value:sVol]]).format())
    }
}

I have a few questions about that driver...

  1. I can't tell what the "Keypad Update Status" function does. It accepts a string from the device page and appears to call the "keypadUpdateStatus" function in the driver--but that function expects 3 parameters (an integer and 2 strings). Not sure it could possibly work as-is??

  2. I don't understand why the "armNightEnd" function isn't calling the "keypadUpdateStatus" function (like all the other arm/disarm functions). In my code above, I tried something, guessing that the "0x00" code that HSM likes wasn't valid for the Ring Keypad--so I sent the "0x0A" code to the keypad and retained the "0x00" code for the "event" that HSM should handle.

Thoughts?

I'm rather new working on the driver too, and I updated armNightEnd to work at all recently. I didnt have it touching the keypad status because the g2 keypad doesn't have a Night mode and didn't know what it would do.

I might fork it off since Bryan is finished with it :frowning:

I'm interested in your other changes and will be taking a peek hopefully later today!

1 Like

What firmware are you guys running? I have to turn off proximity sensor to keep the keypad working. Have three of them they all stop working if I turn on the proximity sensor.

I have three, all the same.

I use the proximity sensor on all three.

I've got the prox sensor off on mine.

I figured setting it to "home" was closest.

That could easily be a config option.

I'm guessing the driver hates the latest firmware and hardware.

I just set up a new github repo for the driver -- I might take the maintainer job.

I made a few bug fixes that had to do with the device page and pulled your code in too.

If you're interested, you can use this URL to import

https://raw.githubusercontent.com/jkister/hubitat/master/drivers/rakg2/rakg2-driver.groovy
2 Likes

Can you put a note in that there's a new repo at
https://github.com/jkister/hubitat/tree/master/drivers/rakg2

Do you want the name changed?

You Rock! :slight_smile:

Thanks!

The only thing I would suggest is to start a new thread and get it in to HPM. The naming is up to you now! :grin:

Thanks for keeping this alive.

1 Like

Hey there! Love the driver so far! One question: How do I get the keypads to show up as chime devices? I can't see them under chime device, or tone device, or music player. I'd love to retire the chime from the Dome sirens... Thanks in advance!

See link in first post

@bobbyD, can you please close this thread? Thanks!

1 Like