Compare commits

..

6 Commits

Author SHA1 Message Date
Vinay Rao
9a3614547e MSA-724: testing 2015-12-03 15:40:14 -06:00
Duncan McKee
90dee51255 Merge pull request #297 from mckeed/master
Z-Wave Lock: fix Security Exception DVCSMP-1265 DVCSMP-1059
2015-12-02 12:11:11 -06:00
Yaima
7b7fdd43cd Merge pull request #326 from Yaima/master
Implemented toggle() for locks
2015-12-01 10:52:29 -08:00
Yaima Valdivia
9059718818 Implemented toggle() for locks
https://smartthings.atlassian.net/browse/DVCSMP-672
2015-12-01 10:48:15 -08:00
Duncan McKee
8ae9b06022 Z-Wave Lock: fix double updated() commands DVCSMP-1265 2015-11-17 18:08:44 -05:00
Duncan McKee
71fc8e7f5f Fix Z-Wave Lock SecurityException DVCSMP-1265 2015-11-16 18:52:08 -05:00
4 changed files with 40 additions and 225 deletions

View File

@@ -25,6 +25,7 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "OSRAM LIGHTIFY LED Smart Connected Light"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Bulb"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
fingerprint profileId: "0104", inClusters: "0000, 0001, 0300, 0006, 0008", outClusters: "0019", manufacturer: "OSRAM", model: "ZHA_LIGHTIFY_COLOR_A19", deviceJoinName: "OSRAM LIGHTIFY COLOR SMART A19"
}
tiles(scale: 2) {
@@ -86,4 +87,4 @@ def refresh() {
def configure() {
log.debug "Configuring Reporting and Bindings."
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
}
}

View File

@@ -66,9 +66,20 @@ metadata {
import physicalgraph.zwave.commands.doorlockv1.*
import physicalgraph.zwave.commands.usercodev1.*
def updated() {
try {
if (!state.init) {
state.init = true
response(secureSequence([zwave.doorLockV1.doorLockOperationGet(), zwave.batteryV1.batteryGet()]))
}
} catch (e) {
log.warn "updated() threw $e"
}
}
def parse(String description) {
def result = null
if (description.startsWith("Err")) {
if (description.startsWith("Err 106")) {
if (state.sec) {
result = createEvent(descriptionText:description, displayed:false)
} else {
@@ -80,6 +91,8 @@ def parse(String description) {
displayed: true,
)
}
} else if (description == "updated") {
return null
} else {
def cmd = zwave.parse(description, [ 0x98: 1, 0x72: 2, 0x85: 2, 0x86: 1 ])
if (cmd) {
@@ -286,7 +299,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
}
break
case 167:
if (!state.lastbatt || (new Date().time) - state.lastbatt > 12*60*60*1000) {
if (!state.lastbatt || now() - state.lastbatt > 12*60*60*1000) {
map = [ descriptionText: "$device.displayName: battery low", isStateChange: true ]
result << response(secure(zwave.batteryV1.batteryGet()))
} else {
@@ -431,7 +444,7 @@ def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
} else {
map.value = cmd.batteryLevel
}
state.lastbatt = new Date().time
state.lastbatt = now()
createEvent(map)
}
@@ -499,15 +512,14 @@ def refresh() {
cmds << "delay 4200"
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format() // old Schlage locks use group 2 and don't secure the Association CC
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.associationQuery = new Date().time
} else if (new Date().time - state.associationQuery.toLong() > 9000) {
log.debug "setting association"
state.associationQuery = now()
} else if (secondsPast(state.associationQuery, 9)) {
cmds << "delay 6000"
cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.associationQuery = new Date().time
state.associationQuery = now()
}
log.debug "refresh sending ${cmds.inspect()}"
cmds
@@ -515,55 +527,22 @@ def refresh() {
def poll() {
def cmds = []
if (state.assoc != zwaveHubNodeId && secondsPast(state.associationQuery, 19 * 60)) {
log.debug "setting association"
cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
cmds << "delay 6000"
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
cmds << "delay 6000"
state.associationQuery = new Date().time
} else {
// Only check lock state if it changed recently or we haven't had an update in an hour
def latest = device.currentState("lock")?.date?.time
if (!latest || !secondsPast(latest, 6 * 60) || secondsPast(state.lastPoll, 55 * 60)) {
cmds << secure(zwave.doorLockV1.doorLockOperationGet())
state.lastPoll = (new Date()).time
} else if (!state.MSR) {
cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
} else if (!state.fw) {
cmds << zwave.versionV1.versionGet().format()
} else if (!state.codes) {
state.pollCode = 1
cmds << secure(zwave.userCodeV1.usersNumberGet())
} else if (state.pollCode && state.pollCode <= state.codes) {
cmds << requestCode(state.pollCode)
} else if (!state.lastbatt || (new Date().time) - state.lastbatt > 53*60*60*1000) {
cmds << secure(zwave.batteryV1.batteryGet())
} else if (!state.enc) {
encryptCodes()
state.enc = 1
}
// Only check lock state if it changed recently or we haven't had an update in an hour
def latest = device.currentState("lock")?.date?.time
if (!latest || !secondsPast(latest, 6 * 60) || secondsPast(state.lastPoll, 55 * 60)) {
cmds << secure(zwave.doorLockV1.doorLockOperationGet())
state.lastPoll = now()
} else if (!state.lastbatt || now() - state.lastbatt > 53*60*60*1000) {
cmds << secure(zwave.batteryV1.batteryGet())
state.lastbatt = now() //inside-214
}
log.debug "poll is sending ${cmds.inspect()}"
device.activity()
cmds ?: null
}
private def encryptCodes() {
def keys = new ArrayList(state.keySet().findAll { it.startsWith("code") })
keys.each { key ->
def match = (key =~ /^code(\d+)$/)
if (match) try {
def keynum = match[0][1].toInteger()
if (keynum > 30 && !state[key]) {
state.remove(key)
} else if (state[key] && !state[key].startsWith("~")) {
log.debug "encrypting $key: ${state[key].inspect()}"
state[key] = encrypt(state[key])
}
} catch (java.lang.NumberFormatException e) { }
if (cmds) {
log.debug "poll is sending ${cmds.inspect()}"
cmds
} else {
// workaround to keep polling from stopping due to lack of activity
sendEvent(descriptionText: "skipping poll", isStateChange: true, displayed: false)
null
}
}
@@ -672,7 +651,7 @@ private Boolean secondsPast(timestamp, seconds) {
return true
}
}
return (new Date().time - timestamp) > (seconds * 1000)
return (now() - timestamp) > (seconds * 1000)
}
private allCodesDeleted() {

View File

@@ -246,6 +246,9 @@ def toggle(devices) {
else if (devices*.currentValue('lock').contains('locked')) {
devices.unlock()
}
else if (devices*.currentValue('lock').contains('unlocked')) {
devices.lock()
}
else if (devices*.currentValue('alarm').contains('off')) {
devices.siren()
}

View File

@@ -1,168 +0,0 @@
/**
* Auto Lock
*
* Copyright 2015 Vikash Varma
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
// Automatically generated. Make future change here.
definition(
name: "Auto Lock",
namespace: "varma",
author: "Vikash Varma",
description: "Manage authorized users and send notification when lock is unlocked. Automatically lock it based on elapsed time or no motion detected near the lock",
category: "Safety & Security",
iconUrl: "http://ecx.images-amazon.com/images/I/51lIeDU229L._SL1500_.jpg",
iconX2Url: "http://ecx.images-amazon.com/images/I/51lIeDU229L._SL1500_.jpg",
oauth: true
)
import groovy.json.JsonSlurper
preferences {
page (name: "mainPage", title: "Automatically locks a deadbolt or lever lock using motion sensor or time elapsed", nextPage: "page2", uninstall: true) {
section {
input "lockDevice", "capability.lock", title:"Which Lock?"
input "lockOpt", "enum", title: "Preference", metadata: [values: ["Motion", "Time"]], multiple:false
input "maxusers", "number", title: "Maximum authorized users", required:true
input "pushNotify", "boolean" , title:"Send push notification when unlocked", default: true
}
}
page (name: "page2", nextPage: "adduser", title: "Automatically locks a deadbolt or lever lock using motion sensor or time elapsed", install: true, uninstall: true)
}
def page2() {
dynamicPage(name: "page2") {
section("Set $lockOpt Preference") {
if (lockOpt == "Motion" ) {
input "motion1", "capability.motionSensor", title:"Lock door after no motion"
} else {
input "minutesLater", "number", title: "Lock door after how many minutes?"
}
}
for (int i = 1; i <= settings.maxusers; i++) {
section("Add User #${i}") {
input "user${i}", "string", title: "Username", required:true
input "code${i}", "password", title: "Code", required:true
}
}
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
state.lockStatus = 1 // 0 = locked; 1 = unlocked
subscribe(lockDevice, "lock.locked", lockEvent)
if (lockOpt == 'Time') {
subscribe(lockDevice, "lock.unlocked", unlockEvent)
} else {
subscribe(motion1, "motion", motionHandler)
}
deleteLockCode()
state.maxusers = 0
for (int i=0; i< settings.maxusers; i++) {
runIn(i*180, setLockCode, [overwrite: false])
}
}
def setLockCode() {
log.trace "setLockCode start: state.maxuser=$state.maxusers"
if (state.maxusers < settings.maxusers ) {
state.maxusers = state.maxusers + 1
def lockCode = settings."code${state.maxusers}"
lockCode = lockCode +""
def msg = "$lockDevice added user $state.maxusers, code: $lockCode"
log.info msg
lockDevice.setCode(state.maxusers, lockCode)
} else {
log.debug "end scheduling,state.maxuser=$state.maxusers, settings.maxusers=$settings.maxusers"
}
log.trace "setLockCode end: state.maxuser=$state.maxusers"
}
def unlockEvent(evt) {
log.debug "Lock ${lockDevice} was: ${evt.value}"
state.lockStatus = 1
def delay = minutesLater * 60
log.debug "Locking $lockDevice.displayName in ${minutesLater} minutes"
runIn(delay, lockDoor)
def data = []
def unlockmsg = ""
if (evt.data != null) {
data = new JsonSlurper().parseText(evt.data)
if (data.usedCode <= settings.maxusers) {
def u = settings."user${data.usedCode}"
unlockmsg = "$lockDevice was unlocked by $u"
} else {
unlockmsg = "$lockDevice was unlocked by unknown user"
}
} else {
unlockmsg = "$lockDevice was unlocked by app"
}
log.debug "pushNotify=$pushNotify | unlockmsg=$unlockmsg"
if (pushNotify) {
sendPush(unlockmsg);
} else {
log.debug "noPush"
}
}
def lockEvent(evt) {
state.lockStatus = 0
log.debug "$lockDevice.displayName is locked"
unschedule( lockDoor )
}
def motionHandler(evt) {
if (evt.value == "inactive") {
lockDoor(evt)
}
}
def lockDoor(evt) {
if ( state.lockStatus ) {
sendPush("$app.name is locking $lockDevice.displayName")
lockDevice.lock()
state.lockStatus = 0
} else {
log.debug "$lolockDeviceck1.displayName already locked"
}
}
def deleteLockCode() {
log.debug "in deleteLockCode"
for (int i = settings.maxusers + 1 ; i <= state.maxusers; i++) {
lockDevice.deleteCode(i)
log.debug "Deleting code $i"
}
}
def checkCodeSetup() {
}