mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-28 21:04:16 +00:00
Compare commits
1 Commits
MSA-1987-1
...
MSA-1969-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6417957ab4 |
@@ -0,0 +1,212 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Ian Lindsay
|
||||||
|
*
|
||||||
|
* Code snippets taken from Tim Slagle
|
||||||
|
* https://community.smartthings.com/u/tslagle13
|
||||||
|
* here:
|
||||||
|
* https://community.smartthings.com/t/generic-camera-device-using-local-connection-new-version-now-available/3269/75?u=l0kiscot
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
preferences {
|
||||||
|
input("devicekey", "text", title: "Device Key", description: "Your OpenGarage.io device key")
|
||||||
|
input("ipadd", "text", title: "IP address", description: "The IP address of your OpenGarage.io unit")
|
||||||
|
input("port", "text", title: "Port", description: "The port of your OpenGarage.io unit")
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
definition (name: "OpenGarage.io Handler", namespace: "littlegumSmartHome", author: "Ian Lindsay") {
|
||||||
|
capability "Door Control"
|
||||||
|
capability "Garage Door Control"
|
||||||
|
capability "Refresh"
|
||||||
|
}
|
||||||
|
|
||||||
|
tiles (scale: 2){
|
||||||
|
standardTile("garagedoor", "device.garagedoor", width: 6, height: 4) {
|
||||||
|
state "open", label: '${name}', action: "close", icon: "st.doors.garage.garage-open", backgroundColor: "#e54444"
|
||||||
|
state "closed", label: '${name}', action: "open", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821"
|
||||||
|
}
|
||||||
|
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 6, height: 2) {
|
||||||
|
state "default", action: "refresh.refresh", icon: "st.secondary.refresh"
|
||||||
|
}
|
||||||
|
|
||||||
|
main("garagedoor")
|
||||||
|
details(["garagedoor", "refresh"])
|
||||||
|
}
|
||||||
|
simulator {
|
||||||
|
// simulator metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def installed() {
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
def initialize() {
|
||||||
|
log.debug "initialize triggered"
|
||||||
|
// initialize state
|
||||||
|
state.doorStatus = 1 // 1 means open, 0 means closed
|
||||||
|
api("getstatus", [])
|
||||||
|
}
|
||||||
|
|
||||||
|
def open() {
|
||||||
|
log.debug "Executing 'close'"
|
||||||
|
api("openclose", [])
|
||||||
|
}
|
||||||
|
|
||||||
|
def close() {
|
||||||
|
log.debug "Executing 'close'"
|
||||||
|
api("openclose", [])
|
||||||
|
}
|
||||||
|
|
||||||
|
def refresh() {
|
||||||
|
log.debug "Refreshing Values "
|
||||||
|
|
||||||
|
api("getstatus", [])
|
||||||
|
}
|
||||||
|
|
||||||
|
def api(method, args = [], success = {}) {
|
||||||
|
def methods = [
|
||||||
|
"getstatus": [gdipadd: "${ipadd}", gdport:"${port}", gdpath:"/jc", gdtype: "get"],
|
||||||
|
"openclose": [gdipadd: "${ipadd}", gdport:"${port}", gdpath:"/cc?dkey=${devicekey}&click=1", gdtype: "get"]
|
||||||
|
]
|
||||||
|
|
||||||
|
def request = methods.getAt(method)
|
||||||
|
|
||||||
|
doRequest(request.gdipadd, request.gdport, request.gdpath, request.gdtype, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
private doRequest(gdipadd, gdport, gdpath, gdtype, success) {
|
||||||
|
log.debug(gdipadd)
|
||||||
|
|
||||||
|
//if(type == "post") {
|
||||||
|
// httpPost(uri , "", success)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//else if(type == "get") {
|
||||||
|
// httpGet(uri, success)
|
||||||
|
//}
|
||||||
|
|
||||||
|
def host = gdipadd
|
||||||
|
def hosthex = convertIPToHex(host)
|
||||||
|
def porthex = Long.toHexString(Long.parseLong((gdport)))
|
||||||
|
if (porthex.length() < 4) { porthex = "00" + porthex }
|
||||||
|
|
||||||
|
//log.debug "Port in Hex is $porthex"
|
||||||
|
//log.debug "Hosthex is : $hosthex"
|
||||||
|
device.deviceNetworkId = "$hosthex:$porthex"
|
||||||
|
|
||||||
|
//log.debug "The device id configured is: $device.deviceNetworkId"
|
||||||
|
|
||||||
|
//def path = gdpath //"/SnapshotJPEG?Resolution=640x480&Quality=Clarity"
|
||||||
|
log.debug "path is: $gdpath"
|
||||||
|
|
||||||
|
def headers = [:] //"HOST:" + getHostAddress() + ""
|
||||||
|
headers.put("HOST", "$host:$gdport")
|
||||||
|
|
||||||
|
try {
|
||||||
|
def hubAction = new physicalgraph.device.HubAction(
|
||||||
|
method: method,
|
||||||
|
path: gdpath,
|
||||||
|
headers: headers
|
||||||
|
)
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.debug "Hit Exception on $hubAction"
|
||||||
|
log.debug e
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse(description) {
|
||||||
|
|
||||||
|
def msg = parseLanMessage(description)
|
||||||
|
|
||||||
|
log.debug msg
|
||||||
|
|
||||||
|
def headersAsString = msg.header // => headers as a string
|
||||||
|
def headerMap = msg.headers // => headers as a Map
|
||||||
|
def body = msg.body // => request body as a string
|
||||||
|
def status = msg.status // => http status code of the response
|
||||||
|
//def json = msg.json // => any JSON included in response body, as a data structure of lists and maps
|
||||||
|
//def xml = msg.xml // => any XML included in response body, as a document tree structure
|
||||||
|
//def data = msg.data // => either JSON or XML in response body (whichever is specified by content-type header in response)
|
||||||
|
|
||||||
|
def slurper = new groovy.json.JsonSlurper()
|
||||||
|
def json = slurper.parseText(msg.body)
|
||||||
|
|
||||||
|
log.debug json
|
||||||
|
|
||||||
|
def result
|
||||||
|
log.debug "before state.doorStatus: $state.doorStatus"
|
||||||
|
|
||||||
|
// open / close event
|
||||||
|
if(json.result){
|
||||||
|
if(state.doorStatus){
|
||||||
|
log.debug "door open - so closing"
|
||||||
|
state.doorStatus = 0
|
||||||
|
result = createEvent(name: "garagedoor", value: "closed")
|
||||||
|
} else {
|
||||||
|
log.debug "door closed - so opening"
|
||||||
|
state.doorStatus = 1
|
||||||
|
result = createEvent(name: "garagedoor", value: "open")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//status update request
|
||||||
|
if(json.mac){
|
||||||
|
if(json.door){
|
||||||
|
log.debug "door is open - refreshing setting"
|
||||||
|
state.doorStatus = 1
|
||||||
|
result = createEvent(name: "garagedoor", value: "open")
|
||||||
|
} else {
|
||||||
|
log.debug "door is closed - refreshing setting"
|
||||||
|
state.doorStatus = 0
|
||||||
|
result = createEvent(name: "garagedoor", value: "closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug "after state.doorStatus: $state.doorStatus"
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long converIntToLong(ipAddress) {
|
||||||
|
long result = 0
|
||||||
|
def parts = ipAddress.split("\\.")
|
||||||
|
for (int i = 3; i >= 0; i--) {
|
||||||
|
result |= (Long.parseLong(parts[3 - i]) << (i * 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result & 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String convertIPToHex(ipAddress) {
|
||||||
|
return Long.toHexString(converIntToLong(ipAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer convertHexToInt(hex) {
|
||||||
|
Integer.parseInt(hex,16)
|
||||||
|
}
|
||||||
|
private String convertHexToIP(hex) {
|
||||||
|
log.debug("Convert hex to ip: $hex") // a0 00 01 6
|
||||||
|
[convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
|
||||||
|
}
|
||||||
|
|
||||||
|
private getHostAddress() {
|
||||||
|
def parts = device.deviceNetworkId.split(":")
|
||||||
|
log.debug device.deviceNetworkId
|
||||||
|
def ip = convertHexToIP(parts[0])
|
||||||
|
def port = convertHexToInt(parts[1])
|
||||||
|
return ip + ":" + port
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ Cloud Execution
|
|||||||
|
|
||||||
Works with:
|
Works with:
|
||||||
|
|
||||||
* [FortrezZ Siren Strobe Alarm](https://www.smartthings.com/products/fortrezz-siren-strobe-alarm)
|
* [FortrezZ Siren Strobe Alarm](https://www.smartthings.com/works-with-smartthings/other/fortrezz-water-valve)
|
||||||
|
|
||||||
## Table of contents
|
## Table of contents
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ metadata {
|
|||||||
|
|
||||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008"
|
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008"
|
||||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart A19 Soft White"
|
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart A19 Soft White"
|
||||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM 10 Year", deviceJoinName: "SYLVANIA Smart 10-Year A19"
|
|
||||||
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, 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, 0003, 0004, 0005, 0006, 0008, 0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
|
||||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY PAR38 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart PAR38 Soft White"
|
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY PAR38 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart PAR38 Soft White"
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
.st-ignore
|
|
||||||
README.md
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Z-Wave Water Valve
|
|
||||||
|
|
||||||
Cloud Execution
|
|
||||||
|
|
||||||
Works with:
|
|
||||||
|
|
||||||
* [Leak Intelligence Leak Gopher Water Shutoff Valve](https://www.smartthings.com/works-with-smartthings/other/leak-intelligence-leak-gopher-water-shutoff-valve)
|
|
||||||
|
|
||||||
|
|
||||||
## Table of contents
|
|
||||||
|
|
||||||
* [Capabilities](#capabilities)
|
|
||||||
* [Health](#device-health)
|
|
||||||
* [Troubleshooting](#Troubleshooting)
|
|
||||||
|
|
||||||
## Capabilities
|
|
||||||
|
|
||||||
* **Actuator** - represents that a Device has commands
|
|
||||||
* **Health Check** - indicates ability to get device health notifications
|
|
||||||
* **Valve** - allows for the control of a valve device
|
|
||||||
* **Polling** - represents that poll() can be implemented for the device
|
|
||||||
* **Refresh** - _refresh()_ command for status updates
|
|
||||||
* **Sensor** - detects sensor events
|
|
||||||
|
|
||||||
## Device Health
|
|
||||||
|
|
||||||
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
|
|
||||||
|
|
||||||
* __32min__ checkInterval
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
|
||||||
Pairing needs to be tried again by placing the device closer to the hub.
|
|
||||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
|
||||||
* [Leak Intelligence Leak Gopher Water Shutoff Valve Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/209631423-Leak-Gopher-Z-Wave-Valve-Control)
|
|
||||||
|
|
||||||
|
|
||||||
@@ -14,14 +14,12 @@
|
|||||||
metadata {
|
metadata {
|
||||||
definition (name: "Z-Wave Water Valve", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "Z-Wave Water Valve", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Health Check"
|
|
||||||
capability "Valve"
|
capability "Valve"
|
||||||
capability "Polling"
|
capability "Polling"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
|
|
||||||
fingerprint deviceId: "0x1006", inClusters: "0x25"
|
fingerprint deviceId: "0x1006", inClusters: "0x25"
|
||||||
fingerprint mfr:"0173", prod:"0003", model:"0002", deviceJoinName: "Leak Intelligence Leak Gopher Water Shutoff Valve"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulator metadata
|
// simulator metadata
|
||||||
@@ -55,14 +53,7 @@ metadata {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def installed() {
|
|
||||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
|
||||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
|
||||||
}
|
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
|
||||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
|
||||||
response(refresh())
|
response(refresh())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,13 +114,6 @@ def poll() {
|
|||||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* PING is used by Device-Watch in attempt to reach the Device
|
|
||||||
* */
|
|
||||||
def ping() {
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "refresh() is called"
|
log.debug "refresh() is called"
|
||||||
def commands = [zwave.switchBinaryV1.switchBinaryGet().format()]
|
def commands = [zwave.switchBinaryV1.switchBinaryGet().format()]
|
||||||
|
|||||||
@@ -1,423 +0,0 @@
|
|||||||
definition(
|
|
||||||
name: 'Lock Manager',
|
|
||||||
namespace: 'ethayer',
|
|
||||||
author: 'Erik Thayer',
|
|
||||||
description: 'Manage locks and users',
|
|
||||||
category: 'Safety & Security',
|
|
||||||
iconUrl: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lm.jpg',
|
|
||||||
iconX2Url: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lm2x.jpg',
|
|
||||||
iconX3Url: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lm3x.jpg'
|
|
||||||
)
|
|
||||||
import groovy.json.JsonSlurper
|
|
||||||
import groovy.json.JsonBuilder
|
|
||||||
|
|
||||||
preferences {
|
|
||||||
page name: 'mainPage', title: 'Installed', install: true, uninstall: true, submitOnChange: true
|
|
||||||
page name: 'infoRefreshPage'
|
|
||||||
page name: 'notificationPage'
|
|
||||||
page name: 'helloHomePage'
|
|
||||||
page name: 'lockInfoPage'
|
|
||||||
page name: 'keypadPage'
|
|
||||||
page name: 'askAlexaPage'
|
|
||||||
}
|
|
||||||
|
|
||||||
def mainPage() {
|
|
||||||
dynamicPage(name: 'mainPage', install: true, uninstall: true, submitOnChange: true) {
|
|
||||||
section('Create') {
|
|
||||||
app(name: 'locks', appName: 'Lock', namespace: 'ethayer', title: 'New Lock', multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/new-lock.png')
|
|
||||||
app(name: 'lockUsers', appName: 'Lock User', namespace: 'ethayer', title: 'New User', multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/user-plus.png')
|
|
||||||
app(name: 'keypads', appName: 'Keypad', namespace: 'ethayer', title: 'New Keypad', multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/keypad-plus.png')
|
|
||||||
}
|
|
||||||
section('Locks') {
|
|
||||||
def lockApps = getLockApps()
|
|
||||||
lockApps = lockApps.sort{ it.lock.id }
|
|
||||||
if (lockApps) {
|
|
||||||
def i = 0
|
|
||||||
lockApps.each { lockApp ->
|
|
||||||
i++
|
|
||||||
href(name: "toLockInfoPage${i}", page: 'lockInfoPage', params: [id: lockApp.lock.id], required: false, title: lockApp.label, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png' )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
section('Global Settings') {
|
|
||||||
href(name: 'toNotificationPage', page: 'notificationPage', title: 'Notification Settings', description: notificationPageDescription(), state: notificationPageDescription() ? 'complete' : '', image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/bullhorn.png')
|
|
||||||
|
|
||||||
def actions = location.helloHome?.getPhrases()*.label
|
|
||||||
if (actions) {
|
|
||||||
href(name: 'toHelloHomePage', page: 'helloHomePage', title: 'Hello Home Settings', image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/home.png')
|
|
||||||
}
|
|
||||||
|
|
||||||
def keypadApps = getKeypadApps()
|
|
||||||
if (keypadApps) {
|
|
||||||
href(name: 'toKeypadPage', page: 'keypadPage', title: 'Keypad Routines (optional)', image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/keypad.png')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
section('Advanced', hideable: true, hidden: true) {
|
|
||||||
input(name: 'overwriteMode', title: 'Overwrite?', type: 'bool', required: true, defaultValue: true, description: 'Overwrite mode automatically deletes codes not in the users list')
|
|
||||||
input(name: 'enableDebug', title: 'Enable IDE debug messages?', type: 'bool', required: true, defaultValue: false, description: 'Show activity from Lock Manger in logs for debugging.')
|
|
||||||
paragraph 'Lock Manager © 2017 v1.4'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def lockInfoPage(params) {
|
|
||||||
dynamicPage(name:"lockInfoPage", title:"Lock Info") {
|
|
||||||
def lockApp = getLockAppByIndex(params)
|
|
||||||
if (lockApp) {
|
|
||||||
section("${lockApp.label}") {
|
|
||||||
def complete = lockApp.isCodeComplete()
|
|
||||||
def refreshComplete = lockApp.isRefreshComplete()
|
|
||||||
if (!complete) {
|
|
||||||
paragraph 'App is learning codes. They will appear here when received.\n Lock may require special DTH to work properly'
|
|
||||||
lockApp.lock.poll()
|
|
||||||
}
|
|
||||||
if (!refreshComplete) {
|
|
||||||
paragraph 'App is in refresh mode.'
|
|
||||||
}
|
|
||||||
def codeData = lockApp.codeData()
|
|
||||||
if (codeData) {
|
|
||||||
def setCode = ''
|
|
||||||
def usage
|
|
||||||
def para
|
|
||||||
def image
|
|
||||||
def sortedCodes = codeData.sort{it.value.slot}
|
|
||||||
sortedCodes.each { data ->
|
|
||||||
data = data.value
|
|
||||||
if (data.codeState != 'unknown') {
|
|
||||||
def userApp = lockApp.findSlotUserApp(data.slot)
|
|
||||||
para = "Slot ${data.slot}"
|
|
||||||
if (data.code) {
|
|
||||||
para = para + "\nCode: ${data.code}"
|
|
||||||
}
|
|
||||||
if (userApp) {
|
|
||||||
para = para + userApp.getLockUserInfo(lockApp.lock)
|
|
||||||
image = userApp.lockInfoPageImage(lockApp.lock)
|
|
||||||
} else {
|
|
||||||
image = 'https://dl.dropboxusercontent.com/u/54190708/LockManager/times-circle-o.png'
|
|
||||||
}
|
|
||||||
if (data.codeState == 'refresh') {
|
|
||||||
para = para +'\nPending refresh...'
|
|
||||||
}
|
|
||||||
paragraph para, image: image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
section('Lock Settings') {
|
|
||||||
def pinLength = lockApp.pinLength()
|
|
||||||
if (pinLength) {
|
|
||||||
paragraph "Required Length: ${pinLength}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
section() {
|
|
||||||
paragraph 'Error: Can\'t find lock!'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def notificationPage() {
|
|
||||||
dynamicPage(name: 'notificationPage', title: 'Global Notification Settings') {
|
|
||||||
section {
|
|
||||||
paragraph 'These settings will apply to all users. Settings on individual users will override these settings'
|
|
||||||
|
|
||||||
input('recipients', 'contact', title: 'Send notifications to', submitOnChange: true, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/book.png')
|
|
||||||
href(name: 'toAskAlexaPage', title: 'Ask Alexa', page: 'askAlexaPage', image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/Alexa.png')
|
|
||||||
if (!recipients) {
|
|
||||||
input(name: 'phone', type: 'text', title: 'Text This Number', description: 'Phone number', required: false, submitOnChange: true)
|
|
||||||
paragraph 'For multiple SMS recipients, separate phone numbers with a semicolon(;)'
|
|
||||||
input(name: 'notification', type: 'bool', title: 'Send A Push Notification', description: 'Notification', required: false, submitOnChange: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phone != null || notification || recipients) {
|
|
||||||
input(name: 'notifyAccess', title: 'on User Entry', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/unlock-alt.png')
|
|
||||||
input(name: 'notifyLock', title: 'on Lock', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png')
|
|
||||||
input(name: 'notifyAccessStart', title: 'when granting access', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/check-circle-o.png')
|
|
||||||
input(name: 'notifyAccessEnd', title: 'when revoking access', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/times-circle-o.png')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
section('Only During These Times (optional)') {
|
|
||||||
input(name: 'notificationStartTime', type: 'time', title: 'Notify Starting At This Time', description: null, required: false)
|
|
||||||
input(name: 'notificationEndTime', type: 'time', title: 'Notify Ending At This Time', description: null, required: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def helloHomePage() {
|
|
||||||
dynamicPage(name: 'helloHomePage', title: 'Global Hello Home Settings (optional)') {
|
|
||||||
def actions = location.helloHome?.getPhrases()*.label
|
|
||||||
actions?.sort()
|
|
||||||
section('Hello Home Phrases') {
|
|
||||||
input(name: 'manualUnlockRoutine', title: 'On Manual Unlock', type: 'enum', options: actions, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/unlock-alt.png')
|
|
||||||
input(name: 'manualLockRoutine', title: 'On Manual Lock', type: 'enum', options: actions, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png')
|
|
||||||
|
|
||||||
input(name: 'codeUnlockRoutine', title: 'On Code Unlock', type: 'enum', options: actions, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/unlock-alt.png' )
|
|
||||||
|
|
||||||
paragraph 'Supported on some locks:'
|
|
||||||
input(name: 'codeLockRoutine', title: 'On Code Lock', type: 'enum', options: actions, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png')
|
|
||||||
|
|
||||||
paragraph 'These restrictions apply to all the above:'
|
|
||||||
input "userNoRunPresence", "capability.presenceSensor", title: "DO NOT run Actions if any of these are present:", multiple: true, required: false
|
|
||||||
input "userDoRunPresence", "capability.presenceSensor", title: "ONLY run Actions if any of these are present:", multiple: true, required: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def askAlexaPage() {
|
|
||||||
dynamicPage(name: 'askAlexaPage', title: 'Ask Alexa Message Settings') {
|
|
||||||
section('Que Messages with the Ask Alexa app') {
|
|
||||||
paragraph 'These settings apply to all users. These settings are overridable on the user level'
|
|
||||||
input(name: 'alexaAccess', title: 'on User Entry', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/unlock-alt.png')
|
|
||||||
input(name: 'alexaLock', title: 'on Lock', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png')
|
|
||||||
input(name: 'alexaAccessStart', title: 'when granting access', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/check-circle-o.png')
|
|
||||||
input(name: 'alexaAccessEnd', title: 'when revoking access', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/times-circle-o.png')
|
|
||||||
}
|
|
||||||
section('Only During These Times (optional)') {
|
|
||||||
input(name: 'alexaStartTime', type: 'time', title: 'Notify Starting At This Time', description: null, required: false)
|
|
||||||
input(name: 'alexaEndTime', type: 'time', title: 'Notify Ending At This Time', description: null, required: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def keypadPage() {
|
|
||||||
dynamicPage(name: 'keypadPage',title: 'Keypad Settings (optional)', install: true, uninstall: true) {
|
|
||||||
def actions = location.helloHome?.getPhrases()*.label
|
|
||||||
actions?.sort()
|
|
||||||
section("Settings") {
|
|
||||||
paragraph 'settings here are for all users. When any user enters their passcode, run these routines'
|
|
||||||
input(name: 'armRoutine', title: 'Arm/Away routine', type: 'enum', options: actions, required: false, multiple: true)
|
|
||||||
input(name: 'disarmRoutine', title: 'Disarm routine', type: 'enum', options: actions, required: false, multiple: true)
|
|
||||||
input(name: 'stayRoutine', title: 'Arm/Stay routine', type: 'enum', options: actions, required: false, multiple: true)
|
|
||||||
input(name: 'nightRoutine', title: 'Arm/Night routine', type: 'enum', options: actions, required: false, multiple: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def fancyString(listOfStrings) {
|
|
||||||
listOfStrings.removeAll([null])
|
|
||||||
def fancify = { list ->
|
|
||||||
return list.collect {
|
|
||||||
def label = it
|
|
||||||
if (list.size() > 1 && it == list[-1]) {
|
|
||||||
label = "and ${label}"
|
|
||||||
}
|
|
||||||
label
|
|
||||||
}.join(", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
return fancify(listOfStrings)
|
|
||||||
}
|
|
||||||
|
|
||||||
def notificationPageDescription() {
|
|
||||||
def parts = []
|
|
||||||
def msg = ""
|
|
||||||
if (settings.phone) {
|
|
||||||
parts << "SMS to ${phone}"
|
|
||||||
}
|
|
||||||
if (settings.recipients) {
|
|
||||||
parts << 'Sent to Address Book'
|
|
||||||
}
|
|
||||||
if (settings.notification) {
|
|
||||||
parts << 'Push Notification'
|
|
||||||
}
|
|
||||||
msg += fancyString(parts)
|
|
||||||
parts = []
|
|
||||||
|
|
||||||
if (settings.notifyAccess) {
|
|
||||||
parts << 'on entry'
|
|
||||||
}
|
|
||||||
if (settings.notifyLock) {
|
|
||||||
parts << 'on lock'
|
|
||||||
}
|
|
||||||
if (settings.notifyAccessStart) {
|
|
||||||
parts << 'when granting access'
|
|
||||||
}
|
|
||||||
if (settings.notifyAccessEnd) {
|
|
||||||
parts << 'when revoking access'
|
|
||||||
}
|
|
||||||
if (settings.notificationStartTime) {
|
|
||||||
parts << "starting at ${settings.notificationStartTime}"
|
|
||||||
}
|
|
||||||
if (settings.notificationEndTime) {
|
|
||||||
parts << "ending at ${settings.notificationEndTime}"
|
|
||||||
}
|
|
||||||
if (parts.size()) {
|
|
||||||
msg += ': '
|
|
||||||
msg += fancyString(parts)
|
|
||||||
}
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
def installed() {
|
|
||||||
log.debug "Installed with settings: ${settings}"
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
def updated() {
|
|
||||||
log.debug "Updated with settings: ${settings}"
|
|
||||||
unsubscribe()
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
def initialize() {
|
|
||||||
def children = getChildApps()
|
|
||||||
log.debug "there are ${children.size()} lock users"
|
|
||||||
}
|
|
||||||
|
|
||||||
def getLockAppByIndex(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
|
|
||||||
|
|
||||||
def lockApp = false
|
|
||||||
def lockApps = getLockApps()
|
|
||||||
if (lockApps) {
|
|
||||||
def i = 0
|
|
||||||
lockApps.each { app ->
|
|
||||||
if (app.lock.id == state.lastLock) {
|
|
||||||
lockApp = app
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lockApp
|
|
||||||
}
|
|
||||||
|
|
||||||
def availableSlots(selectedSlot) {
|
|
||||||
def options = []
|
|
||||||
(1..30).each { slot->
|
|
||||||
def children = getChildApps()
|
|
||||||
def available = true
|
|
||||||
children.each { child ->
|
|
||||||
def userSlot = child.userSlot
|
|
||||||
if (!selectedSlot) {
|
|
||||||
selectedSlot = 0
|
|
||||||
}
|
|
||||||
if (!userSlot) {
|
|
||||||
userSlot = 0
|
|
||||||
}
|
|
||||||
if (userSlot.toInteger() == slot && selectedSlot.toInteger() != slot) {
|
|
||||||
available = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (available) {
|
|
||||||
options << ["${slot}": "Slot ${slot}"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
def keypadMatchingUser(usedCode){
|
|
||||||
def correctUser = false
|
|
||||||
def userApps = getUserApps()
|
|
||||||
userApps.each { userApp ->
|
|
||||||
def code
|
|
||||||
log.debug userApp.userCode
|
|
||||||
if (userApp.isActiveKeypad()) {
|
|
||||||
code = userApp.userCode.take(4)
|
|
||||||
log.debug "code: ${code} used: ${usedCode}"
|
|
||||||
if (code.toInteger() == usedCode.toInteger()) {
|
|
||||||
correctUser = userApp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return correctUser
|
|
||||||
}
|
|
||||||
|
|
||||||
def findAssignedChildApp(lock, slot) {
|
|
||||||
def childApp
|
|
||||||
def userApps = getUserApps()
|
|
||||||
userApps.each { child ->
|
|
||||||
if (child.userSlot?.toInteger() == slot) {
|
|
||||||
childApp = child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return childApp
|
|
||||||
}
|
|
||||||
|
|
||||||
def getUserApps() {
|
|
||||||
def userApps = []
|
|
||||||
def children = getChildApps()
|
|
||||||
children.each { child ->
|
|
||||||
if (child.userSlot) {
|
|
||||||
userApps.push(child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return userApps
|
|
||||||
}
|
|
||||||
|
|
||||||
def getKeypadApps() {
|
|
||||||
def keypadApps = []
|
|
||||||
def children = getChildApps()
|
|
||||||
children.each { child ->
|
|
||||||
if (child.keypad) {
|
|
||||||
keypadApps.push(child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return keypadApps
|
|
||||||
}
|
|
||||||
|
|
||||||
def getLockApps() {
|
|
||||||
def lockApps = []
|
|
||||||
def children = getChildApps()
|
|
||||||
children.each { child ->
|
|
||||||
if (child.lock) {
|
|
||||||
lockApps.push(child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lockApps
|
|
||||||
}
|
|
||||||
|
|
||||||
def setAccess() {
|
|
||||||
def lockApps = getLockApps()
|
|
||||||
lockApps.each { lockApp ->
|
|
||||||
lockApp.makeRequest()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def debuggerOn() {
|
|
||||||
// needed for child apps
|
|
||||||
return enableDebug
|
|
||||||
}
|
|
||||||
|
|
||||||
def debugger(message) {
|
|
||||||
def doDebugger = debuggerOn()
|
|
||||||
if (enableDebug) {
|
|
||||||
return log.debug(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def anyoneHome(sensors) {
|
|
||||||
def result = false
|
|
||||||
if(sensors.findAll { it?.currentPresence == "present" }) {
|
|
||||||
result = true
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
def executeHelloPresenceCheck(routines) {
|
|
||||||
if (userNoRunPresence && userDoRunPresence == null) {
|
|
||||||
if (!anyoneHome(userNoRunPresence)) {
|
|
||||||
location.helloHome.execute(routines)
|
|
||||||
}
|
|
||||||
} else if (userDoRunPresence && userNoRunPresence == null) {
|
|
||||||
if (anyoneHome(userDoRunPresence)) {
|
|
||||||
location.helloHome.execute(routines)
|
|
||||||
}
|
|
||||||
} else if (userDoRunPresence && userNoRunPresence) {
|
|
||||||
if (anyoneHome(userDoRunPresence) && !anyoneHome(userNoRunPresence)) {
|
|
||||||
location.helloHome.execute(routines)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
location.helloHome.execute(routines)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -765,6 +765,7 @@ def turnOffSwitch() {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
device.off();
|
device.off();
|
||||||
|
|
||||||
return [Device_id: params.id, result_action: "200"]
|
return [Device_id: params.id, result_action: "200"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -788,7 +789,6 @@ def getTempSensorsStatus(id) {
|
|||||||
return []
|
return []
|
||||||
} else {
|
} else {
|
||||||
def bat = getBatteryStatus(device.id)
|
def bat = getBatteryStatus(device.id)
|
||||||
def scale = [Scale: location.temperatureScale]
|
return [temperature: device.currentValue('temperature')] + bat
|
||||||
return [temperature: device.currentValue('temperature')] + bat + scale
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,17 +162,6 @@ def registerAllDeviceSubscriptions() {
|
|||||||
registerChangeHandler(inputs)
|
registerChangeHandler(inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Subscribe to events from a list of devices
|
|
||||||
def registerChangeHandler(myList) {
|
|
||||||
myList.each { myDevice ->
|
|
||||||
def theAtts = myDevice.supportedAttributes
|
|
||||||
theAtts.each { att ->
|
|
||||||
subscribe(myDevice, att.name, deviceEventHandler)
|
|
||||||
log.info "Registering for ${myDevice.displayName}.${att.name}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Endpoints function: Subscribe to events from a specific device
|
//Endpoints function: Subscribe to events from a specific device
|
||||||
def registerDeviceChange() {
|
def registerDeviceChange() {
|
||||||
def subscriptionEndpt = params.subscriptionURL
|
def subscriptionEndpt = params.subscriptionURL
|
||||||
|
|||||||
Reference in New Issue
Block a user