mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-11 21:03:07 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f8aac9b4c |
@@ -1,406 +0,0 @@
|
||||
/**
|
||||
* Garadget Device Handler
|
||||
*
|
||||
* Copyright 2016 Stuart Buchanan based loosely based on original code by Krishnaraj Varma with thanks
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 12/02/2016 V1.3 updated with to remove token and DeviceId parameters from inputs to retrieving from dni
|
||||
*/
|
||||
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
|
||||
preferences {
|
||||
input("prdt", "text", title: "sensor scan interval in mS (default: 1000)")
|
||||
input("pmtt", "text", title: "door moving time in mS(default: 10000)")
|
||||
input("prlt", "text", title: "button press time mS (default: 300)")
|
||||
input("prlp", "text", title: "delay between consecutive button presses in mS (default: 1000)")
|
||||
input("psrr", "text", title: "number of sensor reads used in averaging (default: 3)")
|
||||
input("psrt", "text", title: "reflection threshold below which the door is considered open (default: 25)")
|
||||
input("paot", "text", title: "alert for open timeout in seconds (default: 320)")
|
||||
input("pans", "text", title: " alert for night time start in minutes from midnight (default: 1320)")
|
||||
input("pane", "text", title: " alert for night time end in minutes from midnight (default: 360)")
|
||||
}
|
||||
|
||||
metadata {
|
||||
definition (name: "Garadget", namespace: "fuzzysb", author: "Stuart Buchanan") {
|
||||
|
||||
capability "Switch"
|
||||
capability "Contact Sensor"
|
||||
capability "Signal Strength"
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
capability "Refresh"
|
||||
capability "Polling"
|
||||
capability "Configuration"
|
||||
|
||||
attribute "reflection", "string"
|
||||
attribute "status", "string"
|
||||
attribute "time", "string"
|
||||
attribute "lastAction", "string"
|
||||
attribute "reflection", "string"
|
||||
attribute "ver", "string"
|
||||
|
||||
command "stop"
|
||||
command "statusCommand"
|
||||
command "setConfigCommand"
|
||||
command "doorConfigCommand"
|
||||
command "netConfigCommand"
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.status", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label:'${name}', action:"switch.off", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e"
|
||||
attributeState "opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffa81e"
|
||||
attributeState "closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#6699ff"
|
||||
attributeState "closed", label:'${name}', action:"switch.on", icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821"
|
||||
}
|
||||
tileAttribute ("device.lastAction", key: "SECONDARY_CONTROL") {
|
||||
attributeState "default", label: 'Time In State: ${currentValue}'
|
||||
}
|
||||
}
|
||||
standardTile("contact", "device.contact", width: 1, height: 1) {
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
}
|
||||
valueTile("reflection", "reflection", decoration: "flat", width: 2, height: 1) {
|
||||
state "reflection", label:'Reflection\r\n${currentValue}%'
|
||||
}
|
||||
valueTile("rssi", "device.rssi", decoration: "flat", width: 1, height: 1) {
|
||||
state "rssi", label:'Wifi\r\n${currentValue} dBm', unit: "",backgroundColors:[
|
||||
[value: 16, color: "#5600A3"],
|
||||
[value: -31, color: "#153591"],
|
||||
[value: -44, color: "#1e9cbb"],
|
||||
[value: -59, color: "#90d2a7"],
|
||||
[value: -74, color: "#44b621"],
|
||||
[value: -84, color: "#f1d801"],
|
||||
[value: -95, color: "#d04e00"],
|
||||
[value: -96, color: "#bc2323"]
|
||||
]
|
||||
}
|
||||
standardTile("refresh", "refresh", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", action:"polling.poll", icon:"st.secondary.refresh"
|
||||
}
|
||||
standardTile("stop", "stop") {
|
||||
state "default", label:"", action: "stop", icon:"http://cdn.device-icons.smartthings.com/sonos/stop-btn@2x.png"
|
||||
}
|
||||
valueTile("ip", "ip", decoration: "flat", width: 2, height: 1) {
|
||||
state "ip", label:'IP Address\r\n${currentValue}'
|
||||
}
|
||||
valueTile("ssid", "ssid", decoration: "flat", width: 2, height: 1) {
|
||||
state "ssid", label:'Wifi SSID\r\n${currentValue}'
|
||||
}
|
||||
valueTile("ver", "ver", decoration: "flat", width: 1, height: 1) {
|
||||
state "ver", label:'Version\r\n${currentValue}'
|
||||
}
|
||||
standardTile("configure", "device.button", width: 1, height: 1, decoration: "flat") {
|
||||
state "default", label: "", backgroundColor: "#ffffff", action: "configure", icon:"st.secondary.configure"
|
||||
}
|
||||
|
||||
main "status"
|
||||
details(["status", "contact", "reflection", "ver", "configure", "lastAction", "rssi", "stop", "ip", "ssid", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
// handle commands
|
||||
def poll() {
|
||||
log.debug "Executing 'poll'"
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
statusCommand()
|
||||
netConfigCommand()
|
||||
doorConfigCommand()
|
||||
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "Resetting Sensor Parameters to SmartThings Compatible Defaults"
|
||||
SetConfigCommand()
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
private parseDoorStatusResponse(resp) {
|
||||
log.debug("Executing parseDoorStatusResponse: "+resp.data)
|
||||
log.debug("Output status: "+resp.status)
|
||||
if(resp.status == 200) {
|
||||
log.debug("returnedresult: "+resp.data.result)
|
||||
def results = (resp.data.result).tokenize('|')
|
||||
def statusvalues = (results[0]).tokenize('=')
|
||||
def timevalues = (results[1]).tokenize('=')
|
||||
def sensorvalues = (results[2]).tokenize('=')
|
||||
def signalvalues = (results[3]).tokenize('=')
|
||||
def status = statusvalues[1]
|
||||
sendEvent(name: 'status', value: status)
|
||||
if(status == "open" || status == "closed"){
|
||||
sendEvent(name: 'contact', value: status)
|
||||
}
|
||||
def time = timevalues[1]
|
||||
sendEvent(name: 'lastAction', value: time)
|
||||
def sensor = sensorvalues[1]
|
||||
sendEvent(name: 'reflection', value: sensor)
|
||||
def signal = signalvalues[1]
|
||||
sendEvent(name: 'rssi', value: signal)
|
||||
|
||||
}else if(resp.status == 201){
|
||||
log.debug("Something was created/updated")
|
||||
}
|
||||
}
|
||||
|
||||
private parseDoorConfigResponse(resp) {
|
||||
log.debug("Executing parseResponse: "+resp.data)
|
||||
log.debug("Output status: "+resp.status)
|
||||
if(resp.status == 200) {
|
||||
log.debug("returnedresult: "+resp.data.result)
|
||||
def results = (resp.data.result).tokenize('|')
|
||||
def vervalues = (results[0]).tokenize('=')
|
||||
def rdtvalues = (results[1]).tokenize('=')
|
||||
def mttvalues = (results[2]).tokenize('=')
|
||||
def rltvalues = (results[3]).tokenize('=')
|
||||
def rlpvalues = (results[4]).tokenize('=')
|
||||
def srrvalues = (results[5]).tokenize('=')
|
||||
def srtvalues = (results[6]).tokenize('=')
|
||||
def aotvalues = (results[7]).tokenize('=')
|
||||
def ansvalues = (results[8]).tokenize('=')
|
||||
def anevalues = (results[9]).tokenize('=')
|
||||
def ver = vervalues[1]
|
||||
sendEvent(name: 'ver', value: ver)
|
||||
log.debug("Firmware Version: "+ver)
|
||||
def rdt = rdtvalues[1]
|
||||
log.debug("Sensor Scan Interval (ms): "+rdt )
|
||||
def mtt = mttvalues[1]
|
||||
state.mtt = mtt
|
||||
sendEvent(name: 'mtt', value: mtt)
|
||||
log.debug("Door Moving Time (ms): "+mtt )
|
||||
def rlt = rltvalues[1]
|
||||
log.debug("Button Press Time (ms): "+rlt )
|
||||
def rlp = rlpvalues[1]
|
||||
log.debug("Delay Between Consecutive Button Presses (ms): "+rlp )
|
||||
def srr = srrvalues[1]
|
||||
log.debug("number of sensor reads used in averaging: "+srr )
|
||||
def srt = srtvalues[1]
|
||||
log.debug("reflection threshold below which the door is considered open: "+srt )
|
||||
def aot = aotvalues[1]
|
||||
log.debug("alert for open timeout in seconds: "+aot )
|
||||
def ans = ansvalues[1]
|
||||
log.debug("alert for night time start in minutes from midnight: "+ans )
|
||||
def ane = anevalues[1]
|
||||
log.debug("alert for night time end in minutes from midnight: "+ane )
|
||||
|
||||
}else if(resp.status == 201){
|
||||
log.debug("Something was created/updated")
|
||||
}
|
||||
}
|
||||
|
||||
private parseNetConfigResponse(resp) {
|
||||
log.debug("Executing parseResponse: "+resp.data)
|
||||
log.debug("Output status: "+resp.status)
|
||||
if(resp.status == 200) {
|
||||
log.debug("returnedresult: "+resp.data.result)
|
||||
def results = (resp.data.result).tokenize('|')
|
||||
def ipvalues = (results[0]).tokenize('=')
|
||||
def snetvalues = (results[1]).tokenize('=')
|
||||
def dgwvalues = (results[2]).tokenize('=')
|
||||
def macvalues = (results[3]).tokenize('=')
|
||||
def ssidvalues = (results[4]).tokenize('=')
|
||||
def ip = ipvalues[1]
|
||||
sendEvent(name: 'ip', value: ip)
|
||||
log.debug("IP Address: "+ip)
|
||||
def snet = snetvalues[1]
|
||||
log.debug("Subnet Mask: "+snet)
|
||||
def dgw = dgwvalues[1]
|
||||
log.debug("Default Gateway: "+dgw)
|
||||
def mac = macvalues[1]
|
||||
log.debug("Mac Address: "+mac)
|
||||
def ssid = ssidvalues[1]
|
||||
sendEvent(name: 'ssid', value: ssid)
|
||||
log.debug("Wifi SSID : "+ssid)
|
||||
}else if(resp.status == 201){
|
||||
log.debug("Something was created/updated")
|
||||
}
|
||||
}
|
||||
|
||||
private parseResponse(resp) {
|
||||
log.debug("Executing parseResponse: "+resp.data)
|
||||
log.debug("Output status: "+resp.status)
|
||||
if(resp.status == 200) {
|
||||
log.debug("Executing parseResponse.successTrue")
|
||||
def id = resp.data.id
|
||||
def name = resp.data.name
|
||||
def connected = resp.data.connected
|
||||
def returnValue = resp.data.return_value
|
||||
}else if(resp.status == 201){
|
||||
log.debug("Something was created/updated")
|
||||
}
|
||||
}
|
||||
|
||||
private getDeviceDetails() {
|
||||
def fullDni = device.deviceNetworkId
|
||||
return fullDni
|
||||
}
|
||||
|
||||
private sendCommand(method, args = []) {
|
||||
def DefaultUri = "https://api.particle.io"
|
||||
def cdni = getDeviceDetails().tokenize(':')
|
||||
def deviceId = cdni[0]
|
||||
def token = cdni[1]
|
||||
def methods = [
|
||||
'doorStatus': [
|
||||
uri: "${DefaultUri}",
|
||||
path: "/v1/devices/${deviceId}/doorStatus",
|
||||
requestContentType: "application/json",
|
||||
query: [access_token: token]
|
||||
],
|
||||
'doorConfig': [
|
||||
uri: "${DefaultUri}",
|
||||
path: "/v1/devices/${deviceId}/doorConfig",
|
||||
requestContentType: "application/json",
|
||||
query: [access_token: token]
|
||||
],
|
||||
'netConfig': [
|
||||
uri: "${DefaultUri}",
|
||||
path: "/v1/devices/${deviceId}/netConfig",
|
||||
requestContentType: "application/json",
|
||||
query: [access_token: token]
|
||||
],
|
||||
'setState': [
|
||||
uri: "${DefaultUri}",
|
||||
path: "/v1/devices/${deviceId}/setState",
|
||||
requestContentType: "application/json",
|
||||
query: [access_token: token],
|
||||
body: args[0]
|
||||
],
|
||||
'setConfig': [
|
||||
uri: "${DefaultUri}",
|
||||
path: "/v1/devices/${deviceId}/setConfig",
|
||||
requestContentType: "application/json",
|
||||
query: [access_token: token],
|
||||
body: args[0]
|
||||
]
|
||||
]
|
||||
|
||||
def request = methods.getAt(method)
|
||||
|
||||
log.debug "Http Params ("+request+")"
|
||||
|
||||
try{
|
||||
log.debug "Executing 'sendCommand'"
|
||||
|
||||
if (method == "doorStatus"){
|
||||
httpGet(request) { resp ->
|
||||
parseDoorStatusResponse(resp)
|
||||
}
|
||||
}else if (method == "doorConfig"){
|
||||
log.debug "calling doorConfig Method"
|
||||
httpGet(request) { resp ->
|
||||
parseDoorConfigResponse(resp)
|
||||
}
|
||||
}else if (method == "netConfig"){
|
||||
log.debug "calling netConfig Method"
|
||||
httpGet(request) { resp ->
|
||||
parseNetConfigResponse(resp)
|
||||
}
|
||||
}else if (method == "setState"){
|
||||
log.debug "calling setState Method"
|
||||
httpPost(request) { resp ->
|
||||
parseResponse(resp)
|
||||
}
|
||||
}else if (method == "setConfig"){
|
||||
log.debug "calling setState Method"
|
||||
httpPost(request) { resp ->
|
||||
parseResponse(resp)
|
||||
}
|
||||
}else{
|
||||
httpGet(request)
|
||||
}
|
||||
} catch(Exception e){
|
||||
log.debug("___exception: " + e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def on() {
|
||||
log.debug "Executing 'on'"
|
||||
openCommand()
|
||||
statusCommand()
|
||||
log.info("waiting for ${state.mtt} ms")
|
||||
"delay ${state.mtt}"
|
||||
log.info("Initiating Refresh after Transition time")
|
||||
statusCommand()
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "Executing 'off'"
|
||||
closeCommand()
|
||||
statusCommand()
|
||||
log.info("waiting for ${state.mtt} ms")
|
||||
"delay ${state.mtt}"
|
||||
log.info("Initiating Refresh after Transition time")
|
||||
statusCommand()
|
||||
}
|
||||
|
||||
def stop(){
|
||||
log.debug "Executing 'sendCommand.setState'"
|
||||
def jsonbody = new groovy.json.JsonOutput().toJson(arg:"stop")
|
||||
sendCommand("setState",[jsonbody])
|
||||
statusCommand()
|
||||
}
|
||||
|
||||
def statusCommand(){
|
||||
log.debug "Executing 'sendCommand.statusCommand'"
|
||||
sendCommand("doorStatus",[])
|
||||
}
|
||||
|
||||
def openCommand(){
|
||||
log.debug "Executing 'sendCommand.setState'"
|
||||
def jsonbody = new groovy.json.JsonOutput().toJson(arg:"open")
|
||||
sendCommand("setState",[jsonbody])
|
||||
}
|
||||
|
||||
def closeCommand(){
|
||||
log.debug "Executing 'sendCommand.setState'"
|
||||
def jsonbody = new groovy.json.JsonOutput().toJson(arg:"close")
|
||||
sendCommand("setState",[jsonbody])
|
||||
}
|
||||
|
||||
def doorConfigCommand(){
|
||||
log.debug "Executing 'sendCommand.doorConfig'"
|
||||
sendCommand("doorConfig",[])
|
||||
}
|
||||
|
||||
def SetConfigCommand(){
|
||||
def crdt = prdt ?: 1000
|
||||
def cmtt = pmtt ?: 10000
|
||||
def crlt = prlt ?: 300
|
||||
def crlp = prlp ?: 1000
|
||||
def csrr = psrr ?: 3
|
||||
def csrt = psrt ?: 25
|
||||
def caot = paot ?: 320
|
||||
def cans = pans ?: 1320
|
||||
def cane = pane ?: 360
|
||||
log.debug "Executing 'sendCommand.setConfig'"
|
||||
def jsonbody = new groovy.json.JsonOutput().toJson(arg:"rdt=" + crdt +"|mtt=" + cmtt + "|rlt=" + crlt + "|rlp=" + crlp +"|srr=" + csrr + "|srt=" + csrt)
|
||||
sendCommand("setConfig",[jsonbody])
|
||||
jsonbody = new groovy.json.JsonOutput().toJson(arg:"aot=" + caot + "|ans=" + cans + "|ane=" + cane)
|
||||
sendCommand("setConfig",[jsonbody])
|
||||
}
|
||||
|
||||
def netConfigCommand(){
|
||||
log.debug "Executing 'sendCommand.netConfig'"
|
||||
sendCommand("netConfig",[])
|
||||
}
|
||||
@@ -48,8 +48,8 @@ metadata {
|
||||
}
|
||||
|
||||
standardTile("motion", "device.motion") {
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
//DEPRECATED - Using the smartsense-motion-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
|
||||
|
||||
metadata {
|
||||
definition (name: "SmartSense Motion/Temp Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Motion Sensor"
|
||||
@@ -27,6 +25,10 @@ metadata {
|
||||
|
||||
command "enrollResponse"
|
||||
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305-S"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -231,7 +233,7 @@ private Map getBatteryResult(rawValue) {
|
||||
def volts = rawValue / 10
|
||||
def descriptionText
|
||||
|
||||
if (rawValue == 0 || rawValue == 255) {}
|
||||
if (rawValue == 0) {}
|
||||
else {
|
||||
if (volts > 3.5) {
|
||||
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
//DEPRECATED - Using the smartsense-multi-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
|
||||
|
||||
metadata {
|
||||
definition (name: "SmartSense Open/Closed Accelerometer Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||
@@ -24,7 +23,8 @@
|
||||
capability "Refresh"
|
||||
capability "Temperature Measurement"
|
||||
command "enrollResponse"
|
||||
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -225,8 +225,7 @@ def getTemperature(value) {
|
||||
|
||||
def volts = rawValue / 10
|
||||
def descriptionText
|
||||
if (rawValue == 0 || rawValue == 255) {}
|
||||
else if (volts > 3.5) {
|
||||
if (volts > 3.5) {
|
||||
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -220,8 +220,7 @@ private Map getBatteryResult(rawValue) {
|
||||
|
||||
def volts = rawValue / 10
|
||||
def descriptionText
|
||||
if (rawValue == 0 || rawValue == 255) {}
|
||||
else if (volts > 3.5) {
|
||||
if (volts > 3.5) {
|
||||
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -196,8 +196,7 @@ private Map getBatteryResult(rawValue) {
|
||||
|
||||
def volts = rawValue / 10
|
||||
def descriptionText
|
||||
if (rawValue == 0 || rawValue == 255) {}
|
||||
else if (volts > 3.5) {
|
||||
if (volts > 3.5) {
|
||||
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Controlled Power Off
|
||||
*
|
||||
* Copyright 2016 Andrew Crow
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
definition(
|
||||
name: "Controlled Power Off",
|
||||
namespace: "acrow311",
|
||||
author: "Andrew Crow",
|
||||
description: "Application used to power off devices that should be allowed to finish their cycle before being shut down such as air conditions and tankless water heaters. Application will monitor electric usage and delay shutdown until usage has returned to inactive state.",
|
||||
category: "My Apps",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||
oauth: true)
|
||||
|
||||
|
||||
preferences {
|
||||
section("Shutdown when device not active") {
|
||||
input(name: "meter", type: "capability.powerMeter", title: "When This Power Meter...", required: true, multiple: false, description: null)
|
||||
input(name: "threshold", type: "number", title: "Reports Below...", required: true, description: "In watts, enter integer value")
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
unsubscribe()
|
||||
subscribe(meter, "power", meterHandler)
|
||||
}
|
||||
|
||||
def meterHandler(evt) {
|
||||
def meterValue = evt.value as double
|
||||
def thresholdValue = threshold as int
|
||||
def switchState = meter.currentValue("switch") == "on" // Get current switch status (on = true)
|
||||
|
||||
if (switchState) { // If the switch is already off, do nothing
|
||||
if (meterValue < thresholdValue) { // If the power consumption is low enough, turn off switch
|
||||
log.info "${meter} reported energy ${meterValue} below ${threshold}. Turning off switch."
|
||||
sendNotificationEvent("${meter} not running, shutting down.")
|
||||
meter.off()
|
||||
} else { // Power consumption too high - device connected to switch in use - do not shutdown.
|
||||
sendNotificationEvent("Power consumption too high to shut off ${meter}. Attempting again in a moment.")
|
||||
log.info "${meter} reported energy ${meterValue} above ${threshold}. Leaving switch on."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
/**
|
||||
* Garadget Connect
|
||||
*
|
||||
* Copyright 2016 Stuart Buchanan
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.text.DecimalFormat
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.json.JsonOutput
|
||||
|
||||
private apiUrl() { "https://api.particle.io" }
|
||||
private getVendorName() { "Garadget" }
|
||||
private getVendorTokenPath(){ "https://api.particle.io/oauth/token" }
|
||||
private getVendorIcon() { "https://dl.dropboxusercontent.com/s/lkrub180btbltm8/garadget_128.png" }
|
||||
private getClientId() { appSettings.clientId }
|
||||
private getClientSecret() { appSettings.clientSecret }
|
||||
private getServerUrl() { if(!appSettings.serverUrl){return getApiServerUrl()} }
|
||||
|
||||
|
||||
// Automatically generated. Make future change here.
|
||||
definition(
|
||||
name: "Garadget (Connect)",
|
||||
namespace: "fuzzysb",
|
||||
author: "Stuart Buchanan",
|
||||
description: "Garadget Integration",
|
||||
category: "SmartThings Labs",
|
||||
iconUrl: "https://dl.dropboxusercontent.com/s/lkrub180btbltm8/garadget_128.png",
|
||||
iconX2Url: "https://dl.dropboxusercontent.com/s/w8tvaedewwq56kr/garadget_256.png",
|
||||
iconX3Url: "https://dl.dropboxusercontent.com/s/5hiec37e0y5py06/garadget_512.png",
|
||||
oauth: true,
|
||||
singleInstance: true
|
||||
) {
|
||||
appSetting "serverUrl"
|
||||
}
|
||||
|
||||
preferences {
|
||||
page(name: "startPage", title: "Garadget Integration", content: "startPage", install: false)
|
||||
page(name: "Credentials", title: "Fetch OAuth2 Credentials", content: "authPage", install: false)
|
||||
page(name: "mainPage", title: "Garadget Integration", content: "mainPage")
|
||||
page(name: "completePage", title: "${getVendorName()} is now connected to SmartThings!", content: "completePage")
|
||||
page(name: "listDevices", title: "Garadget Devices", content: "listDevices", install: false)
|
||||
page(name: "badCredentials", title: "Invalid Credentials", content: "badAuthPage", install: false)
|
||||
}
|
||||
|
||||
mappings {
|
||||
path("/receivedToken"){action: [POST: "receivedToken", GET: "receivedToken"]}
|
||||
}
|
||||
|
||||
def startPage() {
|
||||
if (state.garadgetAccessToken) { return mainPage() }
|
||||
else { return authPage() }
|
||||
}
|
||||
|
||||
def mainPage(){
|
||||
|
||||
def result = [success:false]
|
||||
|
||||
|
||||
if (!state.garadgetAccessToken) {
|
||||
createAccessToken()
|
||||
log.debug "About to create Smarthings Garadget access token."
|
||||
getToken(garadgetUsername, garadgetPassword)
|
||||
}
|
||||
if (state.garadgetAccessToken){
|
||||
result.success = true
|
||||
}
|
||||
|
||||
|
||||
if(result.success == true) {
|
||||
return completePage()
|
||||
} else {
|
||||
return badAuthPage()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
def completePage(){
|
||||
def description = "Tap 'Next' to proceed"
|
||||
return dynamicPage(name: "completePage", title: "Credentials Accepted!", nextPage: listDevices , uninstall: true, install:false) {
|
||||
section { href url: buildRedirectUrl("receivedToken"), style:"embedded", required:false, title:"${getVendorName()} is now connected to SmartThings!", description:description }
|
||||
}
|
||||
}
|
||||
|
||||
def badAuthPage(){
|
||||
log.debug "In badAuthPage"
|
||||
log.error "login result false"
|
||||
return dynamicPage(name: "badCredentials", title: "Garadget", install:false, uninstall:true, nextPage: Credentials) {
|
||||
section("") {
|
||||
paragraph "Please check your username and password"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def authPage() {
|
||||
log.debug "In authPage"
|
||||
if(canInstallLabs()) {
|
||||
def description = null
|
||||
|
||||
|
||||
log.debug "Prompting for Auth Details."
|
||||
|
||||
description = "Tap to enter Credentials."
|
||||
|
||||
return dynamicPage(name: "Credentials", title: "Authorize Connection", nextPage: mainPage, uninstall: false , install:false) {
|
||||
section("Generate Username and Password") {
|
||||
input "garadgetUsername", "text", title: "Your Garadget Username", required: true
|
||||
input "garadgetPassword", "password", title: "Your Garadget Password", required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
def upgradeNeeded = """To use SmartThings Labs, your Hub should be completely up to date.
|
||||
|
||||
To update your Hub, access Location Settings in the Main Menu (tap the gear next to your location name), select your Hub, and choose "Update Hub"."""
|
||||
|
||||
|
||||
return dynamicPage(name:"Credentials", title:"Upgrade needed!", nextPage:"", install:false, uninstall: true) {
|
||||
section {
|
||||
paragraph "$upgradeNeeded"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def createChildDevice(deviceFile, dni, name, label) {
|
||||
log.debug "In createChildDevice"
|
||||
try{
|
||||
def childDevice = addChildDevice("fuzzysb", deviceFile, dni, null, [name: name, label: label, completedSetup: true])
|
||||
} catch (e) {
|
||||
log.error "Error creating device: ${e}"
|
||||
}
|
||||
}
|
||||
|
||||
def listDevices() {
|
||||
log.debug "In listDevices"
|
||||
|
||||
def options = getDeviceList()
|
||||
|
||||
dynamicPage(name: "listDevices", title: "Choose devices", install: true) {
|
||||
section("Devices") {
|
||||
input "devices", "enum", title: "Select Device(s)", required: false, multiple: true, options: options, submitOnChange: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def buildRedirectUrl(endPoint) {
|
||||
log.debug "In buildRedirectUrl"
|
||||
log.debug("returning: " + getServerUrl() + "/api/token/${state.accessToken}/smartapps/installations/${app.id}/${endPoint}")
|
||||
return getServerUrl() + "/api/token/${state.accessToken}/smartapps/installations/${app.id}/${endPoint}"
|
||||
}
|
||||
|
||||
def receivedToken() {
|
||||
log.debug "In receivedToken"
|
||||
|
||||
def html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>${getVendorName()} Connection</title>
|
||||
<style type="text/css">
|
||||
* { box-sizing: border-box; }
|
||||
@font-face {
|
||||
font-family: 'Swiss 721 W01 Thin';
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot');
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.woff') format('woff'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.ttf') format('truetype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.svg#swis721_th_btthin') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Swiss 721 W01 Light';
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot');
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.woff') format('woff'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.ttf') format('truetype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.svg#swis721_lt_btlight') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
.container {
|
||||
width: 560px;
|
||||
padding: 40px;
|
||||
/*background: #eee;*/
|
||||
text-align: center;
|
||||
}
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
img:nth-child(2) {
|
||||
margin: 0 30px;
|
||||
}
|
||||
p {
|
||||
font-size: 2.2em;
|
||||
font-family: 'Swiss 721 W01 Thin';
|
||||
text-align: center;
|
||||
color: #666666;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
/*
|
||||
p:last-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
*/
|
||||
span {
|
||||
font-family: 'Swiss 721 W01 Light';
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img src=""" + getVendorIcon() + """ alt="Vendor icon" />
|
||||
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/connected-device-icn%402x.png" alt="connected device icon" />
|
||||
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png" alt="SmartThings logo" />
|
||||
<p>Tap 'Done' to continue to Devices.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
render contentType: 'text/html', data: html
|
||||
}
|
||||
|
||||
def getDeviceList() {
|
||||
def garadgetDevices = []
|
||||
|
||||
httpGet( apiUrl() + "/v1/devices?access_token=${state.garadgetAccessToken}"){ resp ->
|
||||
def restDevices = resp.data
|
||||
restDevices.each { garadget ->
|
||||
if (garadget.connected == true)
|
||||
garadgetDevices << ["${garadget.id}|${garadget.name}":"${garadget.name}"]
|
||||
}
|
||||
|
||||
}
|
||||
return garadgetDevices.sort()
|
||||
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
unschedule()
|
||||
initialize()
|
||||
}
|
||||
|
||||
def uninstalled() {
|
||||
log.debug "Uninstalling Garadget (Connect)"
|
||||
deleteToken()
|
||||
removeChildDevices(getChildDevices())
|
||||
log.debug "Garadget (Connect) Uninstalled"
|
||||
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
log.debug "Initialized with settings: ${settings}"
|
||||
// Pull the latest device info into state
|
||||
getDeviceList();
|
||||
def children = getChildDevices()
|
||||
if(settings.devices) {
|
||||
settings.devices.each { device ->
|
||||
def item = device.tokenize('|')
|
||||
def deviceId = item[0]
|
||||
def deviceName = item[1]
|
||||
def existingDevices = children.find{ d -> d.deviceNetworkId.contains(deviceId) }
|
||||
if(!existingDevices) {
|
||||
try {
|
||||
createChildDevice("Garadget", deviceId + ":" + state.garadgetAccessToken, "${deviceName}", deviceName)
|
||||
} catch (Exception e) {
|
||||
log.error "Error creating device: ${e}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Do the initial poll
|
||||
poll()
|
||||
// Schedule it to run every 5 minutes
|
||||
runEvery5Minutes("poll")
|
||||
}
|
||||
|
||||
def getToken(garadgetUsername, garadgetPassword){
|
||||
log.debug "Executing 'sendCommand.setState'"
|
||||
def body = ("grant_type=password&username=${garadgetUsername}&password=${garadgetPassword}&expires_in=0")
|
||||
sendCommand("createToken","particle","particle", body)
|
||||
}
|
||||
|
||||
private sendCommand(method, user, pass, command) {
|
||||
def userpassascii = "${user}:${pass}"
|
||||
def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
|
||||
def headers = [:]
|
||||
headers.put("Authorization", userpass)
|
||||
def methods = [
|
||||
'createToken': [
|
||||
uri: getVendorTokenPath(),
|
||||
requestContentType: "application/x-www-form-urlencoded",
|
||||
headers: headers,
|
||||
body: command
|
||||
],
|
||||
'deleteToken': [
|
||||
uri: apiUrl() + "/v1/access_tokens/${state.garadgetAccessToken}",
|
||||
requestContentType: "application/x-www-form-urlencoded",
|
||||
headers: headers,
|
||||
]
|
||||
]
|
||||
def request = methods.getAt(method)
|
||||
log.debug "Http Params ("+request+")"
|
||||
|
||||
try{
|
||||
if (method == "createToken"){
|
||||
log.debug "Executing createToken 'sendCommand'"
|
||||
httpPost(request) { resp ->
|
||||
parseResponse(resp)
|
||||
}
|
||||
}else if (method == "deleteToken"){
|
||||
log.debug "Executing deleteToken 'sendCommand'"
|
||||
httpDelete(request) { resp ->
|
||||
parseResponse(resp)
|
||||
}
|
||||
}else{
|
||||
log.debug "Executing default HttpGet 'sendCommand'"
|
||||
httpGet(request) { resp ->
|
||||
parseResponse(resp)
|
||||
}
|
||||
}
|
||||
} catch(Exception e){
|
||||
log.debug("___exception: " + e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private parseResponse(resp) {
|
||||
log.debug("Executing parseResponse: "+resp.data)
|
||||
log.debug("Output status: "+resp.status)
|
||||
if(resp.status == 200) {
|
||||
log.debug("Executing parseResponse.successTrue")
|
||||
state.garadgetAccessToken = resp.data.access_token
|
||||
log.debug("Access Token: "+ state.garadgetAccessToken)
|
||||
state.garadgetRefreshToken = resp.data.refresh_token
|
||||
log.debug("Refresh Token: "+ state.garadgetRefreshToken)
|
||||
state.garadgetTokenExpires = resp.data.expires_in
|
||||
log.debug("Token Expires: "+ state.garadgetTokenExpires)
|
||||
log.debug "Created new Garadget token"
|
||||
}else if(resp.status == 201){
|
||||
log.debug("Something was created/updated")
|
||||
}
|
||||
}
|
||||
|
||||
def poll() {
|
||||
log.debug "In Poll"
|
||||
getDeviceList();
|
||||
getAllChildDevices().each {
|
||||
it.statusCommand()
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean canInstallLabs() {
|
||||
return hasAllHubsOver("000.011.00603")
|
||||
}
|
||||
|
||||
private List getRealHubFirmwareVersions() {
|
||||
return location.hubs*.firmwareVersionString.findAll { it }
|
||||
}
|
||||
|
||||
private Boolean hasAllHubsOver(String desiredFirmware) {
|
||||
return realHubFirmwareVersions.every { fw -> fw >= desiredFirmware }
|
||||
}
|
||||
|
||||
void deleteToken() {
|
||||
try{
|
||||
sendCommand("deleteToken","${garadgetUsername}","${garadgetPassword}",[])
|
||||
log.debug "Deleted the existing Garadget Access Token"
|
||||
} catch (e) {log.debug "Couldn't delete Garadget Token, There was an error (${e}), moving on"}
|
||||
}
|
||||
|
||||
private removeChildDevices(delete) {
|
||||
try {
|
||||
delete.each {
|
||||
deleteChildDevice(it.deviceNetworkId)
|
||||
log.info "Successfully Removed Child Device: ${it.displayName} (${it.deviceNetworkId})"
|
||||
}
|
||||
}
|
||||
catch (e) { log.error "There was an error (${e}) when trying to delete the child device" }
|
||||
}
|
||||
@@ -545,15 +545,10 @@ def updateSensorData() {
|
||||
def occupancy = ""
|
||||
it.capability.each {
|
||||
if (it.type == "temperature") {
|
||||
if (it.value == "unknown") {
|
||||
temperature = "--"
|
||||
if (location.temperatureScale == "F") {
|
||||
temperature = Math.round(it.value.toDouble() / 10)
|
||||
} else {
|
||||
if (location.temperatureScale == "F") {
|
||||
temperature = Math.round(it.value.toDouble() / 10)
|
||||
} else {
|
||||
temperature = convertFtoC(it.value.toDouble() / 10)
|
||||
}
|
||||
|
||||
temperature = convertFtoC(it.value.toDouble() / 10)
|
||||
}
|
||||
|
||||
} else if (it.type == "occupancy") {
|
||||
|
||||
Reference in New Issue
Block a user