-
Notifications
You must be signed in to change notification settings - Fork 664
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
156 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ definition( | |
namespace: 'ethayer', | ||
author: 'Erik Thayer', | ||
description: 'Manage locks and users', | ||
category: 'My Apps', | ||
category: 'Safety & Security', | ||
iconUrl: 'https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png', | ||
iconX2Url: 'https://s3.amazonaws.com/smartapp-icons/Convenience/[email protected]', | ||
iconX3Url: 'https://s3.amazonaws.com/smartapp-icons/Convenience/[email protected]' | ||
|
@@ -13,18 +13,75 @@ import groovy.json.JsonBuilder | |
|
||
preferences { | ||
page(name: 'mainPage', title: 'Users', install: true, uninstall: true,submitOnChange: true) | ||
page(name: "lockInfoPage") | ||
page(name: "infoRefreshPage") | ||
} | ||
|
||
def mainPage() { | ||
dynamicPage(name: 'mainPage', install: true, uninstall: true, submitOnChange: true) { | ||
section('Create') { | ||
app(name: 'lockUsers', appName: "Lock User", namespace: "ethayer", title: "New User", multiple: true) | ||
} | ||
section('Which Locks?') { | ||
section('Locks') { | ||
if (locks) { | ||
def i = 0 | ||
locks.each { lock-> | ||
i++ | ||
href(name: "toLockInfoPage${i}", page: "lockInfoPage", params: [id: lock.id], required: false, title: lock.displayName ) | ||
} | ||
} | ||
} | ||
section('Global Settings') { | ||
input 'locks', 'capability.lockCodes', title: 'Select Locks', required: true, multiple: true, submitOnChange: true | ||
input(name: "overwriteMode", title: "Overwrite?", type: "bool", required: true, defaultValue: true, description: 'Overwrite mode automatically deletes codes not in the users list') | ||
href(name: "toInfoRefreshPage", page: "infoRefreshPage", title: "Refresh Lock Data", description: 'Tap to refresh') | ||
} | ||
} | ||
} | ||
|
||
def infoRefreshPage() { | ||
dynamicPage(name:"infoRefreshPage", title:"Lock Info") { | ||
section() { | ||
doPoll() | ||
paragraph "Lock info refreshing soon." | ||
href(name: "toMainPage", page: "mainPage", title: "Back") | ||
} | ||
} | ||
} | ||
|
||
def lockInfoPage(params) { | ||
dynamicPage(name:"lockInfoPage", title:"Lock Info") { | ||
def lock = getLock(params) | ||
if (lock) { | ||
section("${lock.displayName}") { | ||
if (state."lock${lock.id}".codes != null) { | ||
def i = 0 | ||
def setCode = '' | ||
def child | ||
def usage | ||
def para | ||
state."lock${lock.id}".codes.each { code-> | ||
i++ | ||
child = findAssignedChildApp(lock, i) | ||
setCode = state."lock${lock.id}".codes."slot${i}" | ||
para = "Slot ${i}\nCode: ${setCode}" | ||
if (child) { | ||
para = para + child.getLockUserInfo(lock) | ||
} | ||
paragraph para | ||
|
||
} | ||
} else { | ||
paragraph "No Lock data received yet. Requires custom device driver. Will be populated on next poll event." | ||
doPoll() | ||
} | ||
} | ||
} else { | ||
section() { | ||
paragraph "Error: Can't find lock!" | ||
} | ||
} | ||
} | ||
} | ||
|
||
def installed() { | ||
|
@@ -39,18 +96,36 @@ def updated() { | |
} | ||
|
||
def initialize() { | ||
// nothing needed here, since the child apps will handle preferences/subscriptions | ||
// this just logs some messages for demo/information purposes | ||
def children = getChildApps() | ||
|
||
initalizeLockData() | ||
setAccess() | ||
subscribe(locks, "reportAllCodes", pollCodeReport, [filterEvents:false]) | ||
log.debug "there are ${children.size()} lock users" | ||
} | ||
|
||
log.debug "there are ${children.size()} child smartapps" | ||
childApps.each {child -> | ||
log.debug "child app: ${child.label}" | ||
def initalizeLockData() { | ||
locks.each { lock-> | ||
if (state."lock${lock.id}" == null) { | ||
state."lock${lock.id}" = [:] | ||
} | ||
} | ||
} | ||
|
||
def getLock(params) { | ||
def id = '' | ||
// Assign params to id. Sometimes parameters are double nested. | ||
if (params.id) { | ||
id = params.id | ||
} else if (params.params){ | ||
id = params.params.id | ||
} else if (state.lastLock) { | ||
id = state.lastLock | ||
} | ||
state.lastLock = id | ||
return locks.find{it.id == id} | ||
} | ||
|
||
def availableSlots(selectedSlot) { | ||
def options = [] | ||
(1..30).each { slot-> | ||
|
@@ -86,12 +161,48 @@ def pollCodeReport(evt) { | |
needPoll = true | ||
} | ||
} | ||
if (needPoll) { | ||
def unmangedCodesNotReady = false | ||
if (overwriteMode) { | ||
unmangedCodesNotReady = removeUnmanagedCodes(evt) | ||
} | ||
if (needPoll || unmangedCodesNotReady) { | ||
log.debug 'asking for poll!' | ||
runIn(20, doPoll) | ||
} | ||
} | ||
|
||
def removeUnmanagedCodes(evt) { | ||
def codeData = new JsonSlurper().parseText(evt.data) | ||
def lock = locks.find{it.id == evt.deviceId} | ||
def array = [] | ||
def codes = [:] | ||
def codeSlots = 30 | ||
if (codeData.codes) { | ||
codeSlots = codeData.codes | ||
} | ||
|
||
(1..codeSlots).each { slot -> | ||
def child = findAssignedChildApp(lock, slot) | ||
if (!child) { | ||
def currentCode = codeData."code${slot}" | ||
// there is no app associated | ||
if (currentCode != '') { | ||
// Code is set, We should be disabled. | ||
array << ["code${slot}", ''] | ||
} | ||
} | ||
} | ||
def json = new groovy.json.JsonBuilder(array).toString() | ||
if (json != '[]') { | ||
//Lock has codes we don't want | ||
lock.updateCodes(json) | ||
return true | ||
} else { | ||
//Lock is clean | ||
return false | ||
} | ||
} | ||
|
||
// def doErrorPoll() { | ||
// def needPoll = false | ||
// def children = getChildApps() | ||
|
@@ -130,3 +241,26 @@ def setAccess() { | |
def doPoll() { | ||
locks.poll() | ||
} | ||
|
||
def populateDiscovery(codeData, lock) { | ||
def codes = [:] | ||
def codeSlots = 30 | ||
if (codeData.codes) { | ||
codeSlots = codeData.codes | ||
} | ||
(1..codeSlots).each { slot-> | ||
codes."slot${slot}" = codeData."code${slot}" | ||
} | ||
state."lock${lock.id}".codes = codes | ||
} | ||
|
||
def findAssignedChildApp(lock, slot) { | ||
def children = getChildApps() | ||
def childApp = false | ||
children.each { child -> | ||
if (child.userSlot?.toInteger() == slot) { | ||
childApp = child | ||
} | ||
} | ||
return childApp | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,8 @@ definition ( | |
namespace: 'ethayer', | ||
author: 'Erik Thayer', | ||
description: 'App to manager users. This is a child app.', | ||
category: 'My Apps', | ||
category: 'Safety & Security', | ||
|
||
// the parent option allows you to specify the parent app in the form <namespace>/<app name> | ||
parent: 'ethayer:Lock Manager', | ||
iconUrl: 'https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png', | ||
iconX2Url: 'https://s3.amazonaws.com/smartapp-icons/Convenience/[email protected]', | ||
|
@@ -440,7 +439,6 @@ def isValidCode() { | |
if (userCode?.isNumber()) { | ||
return true | ||
} else { | ||
log.debug 'Not a number!' | ||
return false | ||
} | ||
} | ||
|
@@ -686,6 +684,8 @@ def pollCodeReport(evt) { | |
def active = isActive(lock.id) | ||
def currentCode = codeData."code${userSlot}" | ||
def array = [] | ||
|
||
parent.populateDiscovery(codeData, lock) | ||
setKnownCode(currentCode, lock) | ||
|
||
if (active) { | ||
|
@@ -812,3 +812,14 @@ def sendMessage(msg) { | |
} | ||
} | ||
} | ||
|
||
def getLockUserInfo(lock) { | ||
def para = "\n${app.label}" | ||
def usage = state."lock${lock.id}".usage | ||
para += " // Usage: ${usage}" | ||
if (!state."lock${lock.id}".enabled) { | ||
def reason = state."lock${lock.id}".disabledReason | ||
para += "\n ${reason}" | ||
} | ||
return para | ||
} |