mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-11 05:11:51 +00:00
Compare commits
1 Commits
DVCSMP-180
...
MSA-1338-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d787c1b41e |
@@ -403,21 +403,39 @@ def refresh() {
|
||||
|
||||
if (device.getDataValue("manufacturer") == "SmartThings") {
|
||||
log.debug "Refreshing Values for manufacturer: SmartThings "
|
||||
/* These values of Motion Threshold Multiplier(0x01) and Motion Threshold (0x0276)
|
||||
seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer.
|
||||
Separating these out in a separate if-else because I do not want to touch Centralite part
|
||||
as of now.
|
||||
*/
|
||||
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x01, [mfgCode: manufacturerCode])
|
||||
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0002, 0x21, 0x0276, [mfgCode: manufacturerCode])
|
||||
refreshCmds = refreshCmds + [
|
||||
/* These values of Motion Threshold Multiplier(01) and Motion Threshold (7602)
|
||||
seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer.
|
||||
Separating these out in a separate if-else because I do not want to touch Centralite part
|
||||
as of now.
|
||||
*/
|
||||
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global write 0xFC02 0 0x20 {01}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
|
||||
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global write 0xFC02 2 0x21 {7602}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
|
||||
]
|
||||
} else {
|
||||
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x02, [mfgCode: manufacturerCode])
|
||||
refreshCmds = refreshCmds + [
|
||||
/* sensitivity - default value (8) */
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global write 0xFC02 0 0x20 {02}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
|
||||
]
|
||||
}
|
||||
|
||||
//Common refresh commands
|
||||
refreshCmds += zigbee.readAttribute(0x0402, 0x0000) +
|
||||
zigbee.readAttribute(0x0001, 0x0020) +
|
||||
zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode])
|
||||
refreshCmds = refreshCmds + [
|
||||
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
|
||||
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200",
|
||||
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global read 0xFC02 0x0010",
|
||||
"send 0x${device.deviceNetworkId} 1 1","delay 400"
|
||||
]
|
||||
|
||||
return refreshCmds + enrollResponse()
|
||||
}
|
||||
@@ -425,15 +443,38 @@ def refresh() {
|
||||
def configure() {
|
||||
sendEvent(name: "checkInterval", value: 7200, displayed: false)
|
||||
|
||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||
log.debug "Configuring Reporting"
|
||||
|
||||
def configCmds = enrollResponse() +
|
||||
zigbee.batteryConfig() +
|
||||
zigbee.temperatureConfig() +
|
||||
zigbee.configureReporting(0xFC02, 0x0010, 0x18, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
|
||||
zigbee.configureReporting(0xFC02, 0x0012, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||
zigbee.configureReporting(0xFC02, 0x0013, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||
zigbee.configureReporting(0xFC02, 0x0014, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode])
|
||||
def configCmds = [
|
||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", "delay 200", //checkin time 6 hrs
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0xFC02 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {0100}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {0100}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {0100}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
|
||||
]
|
||||
|
||||
return configCmds + refresh()
|
||||
}
|
||||
|
||||
@@ -21,13 +21,6 @@ metadata {
|
||||
capability "Motion Sensor"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
|
||||
fingerprint mfr: "011F", prod: "0001", model: "0001", deviceJoinName: "Schlage Motion Sensor" // Schlage motion
|
||||
fingerprint mfr: "014A", prod: "0001", model: "0001", deviceJoinName: "Ecolink Motion Sensor" // Ecolink motion
|
||||
fingerprint mfr: "014A", prod: "0004", model: "0001", deviceJoinName: "Ecolink Motion Sensor" // Ecolink motion +
|
||||
fingerprint mfr: "0060", prod: "0001", model: "0002", deviceJoinName: "Everspring Motion Sensor" // Everspring SP814
|
||||
fingerprint mfr: "0060", prod: "0001", model: "0003", deviceJoinName: "Everspring Motion Sensor" // Everspring HSP02
|
||||
fingerprint mfr: "011A", prod: "0601", model: "0901", deviceJoinName: "Enerwave Motion Sensor" // Enerwave ZWN-BPC
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -132,9 +125,9 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd)
|
||||
}
|
||||
if (!state.lastbat || (new Date().time) - state.lastbat > 53*60*60*1000) {
|
||||
result << response(zwave.batteryV1.batteryGet())
|
||||
} else {
|
||||
result << response(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
result << response("delay 1200")
|
||||
}
|
||||
result << response(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
/**
|
||||
* Copyright 2016 SmartThings
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Z-Wave Plus Window Shade", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Window Shade"
|
||||
capability "Battery"
|
||||
capability "Refresh"
|
||||
capability "Health Check"
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
|
||||
command "stop"
|
||||
|
||||
capability "Switch Level" // until we get a Window Shade Level capability
|
||||
capability "Switch" // temporary for use with Routines
|
||||
|
||||
// This device handler is specifically for position-aware window coverings
|
||||
//
|
||||
fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Shade"
|
||||
fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Shade"
|
||||
fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Window Blinds"
|
||||
fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Roller Shade"
|
||||
}
|
||||
|
||||
simulator {
|
||||
status "open": "command: 2603, payload: FF"
|
||||
status "closed": "command: 2603, payload: 00"
|
||||
status "10%": "command: 2603, payload: 0A"
|
||||
status "66%": "command: 2603, payload: 42"
|
||||
status "99%": "command: 2603, payload: 63"
|
||||
status "battery 100%": "command: 8003, payload: 64"
|
||||
status "battery low": "command: 8003, payload: FF"
|
||||
|
||||
// reply messages
|
||||
reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE"
|
||||
reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE"
|
||||
reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE"
|
||||
reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label:'${name}', action:"close", icon:"st.doors.garage.garage-open", backgroundColor:"#79b821", nextState:"closing"
|
||||
attributeState "closed", label:'${name}', action:"open", icon:"st.doors.garage.garage-closed", backgroundColor:"#ffffff", nextState:"opening"
|
||||
attributeState "partiallyOpen", label:'Open', action:"close", icon:"st.doors.garage.garage-open", backgroundColor:"#79b821", nextState:"closing"
|
||||
attributeState "opening", label:'${name}', action:"stop", icon:"st.doors.garage.garage-opening", backgroundColor:"#79b821", nextState:"partiallyOpen"
|
||||
attributeState "closing", label:'${name}', action:"stop", icon:"st.doors.garage.garage-closing", backgroundColor:"#ffffff", nextState:"partiallyOpen"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"setLevel"
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled"
|
||||
state "disabled", label:'', action:"", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
preferences {
|
||||
input "preset", "number", title: "Default half-open position (1-100)", defaultValue: 50, required: false, displayDuringSetup: false
|
||||
}
|
||||
|
||||
main(["windowShade"])
|
||||
details(["windowShade", "refresh", "battery"])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = null
|
||||
//if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/)
|
||||
// TODO: Workaround manual parsing of v4 multilevel report
|
||||
def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value
|
||||
if (cmd) {
|
||||
result = zwaveEvent(cmd)
|
||||
}
|
||||
log.debug "Parsed '$description' to ${result.inspect()}"
|
||||
return result
|
||||
}
|
||||
|
||||
def getCheckInterval() {
|
||||
// These are battery-powered devices, and it's not very critical
|
||||
// to know whether they're online or not – 12 hrs
|
||||
12 * 60 * 60
|
||||
}
|
||||
|
||||
def installed() {
|
||||
sendEvent(name: "checkInterval", value: checkInterval, displayed: false)
|
||||
}
|
||||
|
||||
def updated() {
|
||||
if (device.latestValue("checkInterval") != checkInterval) {
|
||||
sendEvent(name: "checkInterval", value: checkInterval, displayed: false)
|
||||
}
|
||||
if (!device.latestState("battery")) {
|
||||
response(zwave.batteryV1.batteryGet())
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||
handleLevelReport(cmd)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
|
||||
handleLevelReport(cmd)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) {
|
||||
handleLevelReport(cmd)
|
||||
}
|
||||
|
||||
private handleLevelReport(physicalgraph.zwave.Command cmd) {
|
||||
def descriptionText = null
|
||||
def shadeValue = null
|
||||
|
||||
def level = cmd.value as Integer
|
||||
if (level >= 99) {
|
||||
level = 99
|
||||
shadeValue = "open"
|
||||
} else if (level <= 0) {
|
||||
level = 0 // unlike dimmer switches, the level isn't saved when closed
|
||||
shadeValue = "closed"
|
||||
} else {
|
||||
shadeValue = "partiallyOpen"
|
||||
descriptionText = "${device.displayName} shade is ${level}% open"
|
||||
}
|
||||
def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false)
|
||||
def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange)
|
||||
|
||||
def result = [stateEvent, levelEvent]
|
||||
if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) {
|
||||
log.debug "requesting battery"
|
||||
state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row
|
||||
result << response(["delay 15000", zwave.batteryV1.batteryGet().format()])
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStopLevelChange cmd) {
|
||||
[ createEvent(name: "windowShade", value: "partiallyOpen", displayed: false, descriptionText: "$device.displayName shade stopped"),
|
||||
response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
||||
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
|
||||
updateDataValue("MSR", msr)
|
||||
if (cmd.manufacturerName) {
|
||||
updateDataValue("manufacturer", cmd.manufacturerName)
|
||||
}
|
||||
createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [ name: "battery", unit: "%" ]
|
||||
if (cmd.batteryLevel == 0xFF) {
|
||||
map.value = 1
|
||||
map.descriptionText = "${device.displayName} has a low battery"
|
||||
map.isStateChange = true
|
||||
} else {
|
||||
map.value = cmd.batteryLevel
|
||||
}
|
||||
state.lastbatt = now()
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
log.debug "unhandled $cmd"
|
||||
return []
|
||||
}
|
||||
|
||||
def open() {
|
||||
log.debug "open()"
|
||||
/*delayBetween([
|
||||
zwave.basicV1.basicSet(value: 0xFF).format(),
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format()
|
||||
], 1000)*/
|
||||
zwave.basicV1.basicSet(value: 0xFF).format()
|
||||
}
|
||||
|
||||
def close() {
|
||||
log.debug "close()"
|
||||
/*delayBetween([
|
||||
zwave.basicV1.basicSet(value: 0x00).format(),
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format()
|
||||
], 1000)*/
|
||||
zwave.basicV1.basicSet(value: 0).format()
|
||||
}
|
||||
|
||||
def on() {
|
||||
open()
|
||||
}
|
||||
|
||||
def off() {
|
||||
close()
|
||||
}
|
||||
|
||||
def setLevel(value, duration = null) {
|
||||
log.debug "setLevel(${value.inspect()})"
|
||||
Integer level = value as Integer
|
||||
if (level < 0) level = 0
|
||||
if (level > 99) level = 99
|
||||
if (!preset && level > 0 && level < 95) state.preset = level
|
||||
delayBetween([
|
||||
zwave.basicV1.basicSet(value: level).format(),
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def presetPosition() {
|
||||
setLevel(preset ?: state.preset ?: 50)
|
||||
}
|
||||
|
||||
def stop() {
|
||||
log.debug "stop()"
|
||||
zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format()
|
||||
}
|
||||
|
||||
def ping() {
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "refresh()"
|
||||
delayBetween([
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format(),
|
||||
zwave.batteryV1.batteryGet().format()
|
||||
], 1500)
|
||||
}
|
||||
@@ -13,14 +13,14 @@
|
||||
* Documented Header
|
||||
*
|
||||
* Change 2: 2014-03-15
|
||||
* Fixed bug where we weren't coming on when changing
|
||||
* Fixed bug where we weren't coming on when changing
|
||||
* levels down.
|
||||
*
|
||||
* Change 3: 2014-04-02 (lieberman)
|
||||
* Change 3: 2014-04-02 (lieberman)
|
||||
* Changed sendEvent() to createEvent() in parse()
|
||||
*
|
||||
* Change 4: 2014-04-12 (wackford)
|
||||
* Added current power usage tile
|
||||
* Added current power usage tile
|
||||
*
|
||||
* Change 5: 2014-09-14 (wackford)
|
||||
* a. Changed createEvent() to sendEvent() in parse() to
|
||||
@@ -33,7 +33,7 @@
|
||||
* b. added refresh on udate
|
||||
* c. added uninstallFromChildDevice to handle removing from settings
|
||||
* d. Changed to allow bulb to 100%, was possible to get past logic at 99
|
||||
*
|
||||
*
|
||||
* Change 7: 2014-11-09 (wackford)
|
||||
* a. Added bulbpower calcs to device. TCP is broken
|
||||
* b. Changed to set dim level first then on. Much easier on the eys coming from bright.
|
||||
@@ -42,7 +42,7 @@
|
||||
* Code
|
||||
*****************************************************************
|
||||
*/
|
||||
// for the UI
|
||||
// for the UI
|
||||
metadata {
|
||||
definition (name: "TCP Bulb", namespace: "wackford", author: "Todd Wackford") {
|
||||
capability "Switch"
|
||||
@@ -52,28 +52,26 @@ metadata {
|
||||
capability "Switch Level"
|
||||
|
||||
attribute "stepsize", "string"
|
||||
|
||||
|
||||
command "levelUp"
|
||||
command "levelDown"
|
||||
command "on"
|
||||
command "off"
|
||||
command "setBulbPower"
|
||||
command "on"
|
||||
command "off"
|
||||
command "setBulbPower"
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
}
|
||||
|
||||
preferences {
|
||||
|
||||
preferences {
|
||||
input "stepsize", "number", title: "Step Size", description: "Dimmer Step Size", defaultValue: 5
|
||||
}
|
||||
|
||||
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "on", label:'${name}', action:"off", icon:"st.switches.light.on", backgroundColor:"#79b821"
|
||||
state "off", label:'${name}', action:"on", icon:"st.switches.light.off", backgroundColor:"#ffffff"
|
||||
}
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "level", action:"switch level.setLevel"
|
||||
@@ -84,15 +82,15 @@ metadata {
|
||||
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
|
||||
state "level", label: 'Level ${currentValue}%'
|
||||
}
|
||||
standardTile("lUp", "device.switchLevel", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
|
||||
state "default", action:"levelUp", icon:"st.illuminance.illuminance.bright"
|
||||
}
|
||||
standardTile("lDown", "device.switchLevel", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
|
||||
state "default", action:"levelDown", icon:"st.illuminance.illuminance.light"
|
||||
}
|
||||
valueTile( "power", "device.power", inactiveLabel: false, decoration: "flat") {
|
||||
state "power", label: '${currentValue} Watts'
|
||||
}
|
||||
standardTile("lUp", "device.switchLevel", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
|
||||
state "default", action:"levelUp", icon:"st.illuminance.illuminance.bright"
|
||||
}
|
||||
standardTile("lDown", "device.switchLevel", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
|
||||
state "default", action:"levelDown", icon:"st.illuminance.illuminance.light"
|
||||
}
|
||||
valueTile( "power", "device.power", inactiveLabel: false, decoration: "flat") {
|
||||
state "power", label: '${currentValue} Watts'
|
||||
}
|
||||
|
||||
main(["switch"])
|
||||
details(["switch", "lUp", "lDown", "levelSliderControl", "level" , "power", "refresh" ])
|
||||
@@ -103,10 +101,10 @@ metadata {
|
||||
def parse(description) {
|
||||
//log.debug "parse() - $description"
|
||||
def results = []
|
||||
|
||||
if ( description == "updated" )
|
||||
return
|
||||
|
||||
|
||||
if ( description == "updated" )
|
||||
return
|
||||
|
||||
if (description?.name && description?.value)
|
||||
{
|
||||
results << createEvent(name: "${description?.name}", value: "${description?.value}")
|
||||
@@ -116,73 +114,73 @@ def parse(description) {
|
||||
// handle commands
|
||||
def setBulbPower(value) {
|
||||
state.bulbPower = value
|
||||
log.debug "In child with bulbPower of ${state.bulbPower}"
|
||||
log.debug "In child with bulbPower of ${state.bulbPower}"
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.debug "Executing 'on'"
|
||||
sendEvent(name:"switch",value:on)
|
||||
sendEvent(name:"switch",value:on)
|
||||
parent.on(this)
|
||||
|
||||
def levelSetting = device.latestValue("level") as Float ?: 1.0
|
||||
def bulbPowerMax = device.latestValue("setBulbPower") as Float
|
||||
def calculatedPower = bulbPowerMax * (levelSetting / 100)
|
||||
sendEvent(name: "power", value: calculatedPower.round(1))
|
||||
|
||||
if (device.latestValue("level") == null) {
|
||||
//def bulbPowerMax = device.latestValue("setBulbPower") as Float
|
||||
//def calculatedPower = bulbPowerMax * (levelSetting / 100)
|
||||
//sendEvent(name: "power", value: calculatedPower.round(1))
|
||||
|
||||
if (device.latestValue("level") == null) {
|
||||
sendEvent( name: "level", value: 1.0 )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "Executing 'off'"
|
||||
sendEvent(name:"switch",value:off)
|
||||
sendEvent(name:"switch",value:off)
|
||||
parent.off(this)
|
||||
sendEvent(name: "power", value: 0.0)
|
||||
sendEvent(name: "power", value: 0.0)
|
||||
}
|
||||
|
||||
def levelUp() {
|
||||
def level = device.latestValue("level") as Integer ?: 0
|
||||
def step = state.stepsize as float
|
||||
|
||||
level+= step
|
||||
|
||||
if ( level > 100 )
|
||||
level = 100
|
||||
|
||||
setLevel(level)
|
||||
def step = state.stepsize as float
|
||||
|
||||
level+= step
|
||||
|
||||
if ( level > 100 )
|
||||
level = 100
|
||||
|
||||
setLevel(level)
|
||||
}
|
||||
|
||||
def levelDown() {
|
||||
def level = device.latestValue("level") as Integer ?: 0
|
||||
def step = state.stepsize as float
|
||||
|
||||
level-= step
|
||||
|
||||
def step = state.stepsize as float
|
||||
|
||||
level-= step
|
||||
|
||||
if ( level < 1 )
|
||||
level = 1
|
||||
|
||||
setLevel(level)
|
||||
level = 1
|
||||
|
||||
setLevel(level)
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
log.debug "in setLevel with value: ${value}"
|
||||
def level = value as Integer
|
||||
|
||||
sendEvent( name: "level", value: level )
|
||||
sendEvent( name: "switch.setLevel", value:level )
|
||||
|
||||
sendEvent( name: "level", value: level )
|
||||
sendEvent( name: "switch.setLevel", value:level )
|
||||
parent.setLevel( this, level )
|
||||
|
||||
|
||||
|
||||
if (( level > 0 ) && ( level <= 100 ))
|
||||
on()
|
||||
else
|
||||
off()
|
||||
|
||||
def levelSetting = level as float
|
||||
def bulbPowerMax = device.latestValue("setBulbPower") as float
|
||||
def calculatedPower = bulbPowerMax * (levelSetting / 100)
|
||||
sendEvent(name: "power", value: calculatedPower.round(1))
|
||||
if (( level > 0 ) && ( level <= 100 ))
|
||||
on()
|
||||
else
|
||||
off()
|
||||
|
||||
//def levelSetting = level as float
|
||||
//def bulbPowerMax = device.latestValue("setBulbPower") as float
|
||||
//def calculatedPower = bulbPowerMax * (levelSetting / 100)
|
||||
//sendEvent(name: "power", value: calculatedPower.round(1))
|
||||
}
|
||||
|
||||
def poll() {
|
||||
@@ -200,29 +198,29 @@ def installed() {
|
||||
}
|
||||
|
||||
def updated() {
|
||||
initialize()
|
||||
refresh()
|
||||
initialize()
|
||||
refresh()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
if ( !settings.stepsize )
|
||||
state.stepsize = 10 //set the default stepsize
|
||||
else
|
||||
state.stepsize = settings.stepsize
|
||||
state.stepsize = 10 //set the default stepsize
|
||||
else
|
||||
state.stepsize = settings.stepsize
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Method :uninstalled(args)
|
||||
(args) :none
|
||||
returns:Nothing
|
||||
ERRORS :No error handling is done
|
||||
|
||||
Purpose:This is standard ST method.
|
||||
Gets called when "remove" is selected in child device "preferences"
|
||||
tile. It also get's called when "deleteChildDevice(child)" is
|
||||
called from parent service manager app.
|
||||
*******************************************************************************/
|
||||
Method :uninstalled(args)
|
||||
(args) :none
|
||||
returns:Nothing
|
||||
ERRORS :No error handling is done
|
||||
|
||||
Purpose:This is standard ST method.
|
||||
Gets called when "remove" is selected in child device "preferences"
|
||||
tile. It also get's called when "deleteChildDevice(child)" is
|
||||
called from parent service manager app.
|
||||
*******************************************************************************/
|
||||
def uninstalled() {
|
||||
log.debug "Executing 'uninstall' in device type"
|
||||
parent.uninstallFromChildDevice(this)
|
||||
parent.uninstallFromChildDevice(this)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,679 @@
|
||||
/**
|
||||
* TCP Bulbs (Connect)
|
||||
*
|
||||
* Copyright 2014 Todd Wackford
|
||||
*
|
||||
* 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.security.MessageDigest;
|
||||
|
||||
private apiUrl() { "https://tcp.greenwavereality.com/gwr/gop.php?" }
|
||||
|
||||
definition(
|
||||
name: "TCP Bulbs - more reliable",
|
||||
namespace: "mmacaula",
|
||||
author: "Mike Macaulay",
|
||||
description: "Connect your TCP bulbs to SmartThings using Cloud to Cloud integration. You must create a remote login acct on TCP Mobile App.",
|
||||
category: "SmartThings Labs",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/tcp.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/tcp@2x.png",
|
||||
singleInstance: true
|
||||
)
|
||||
|
||||
|
||||
preferences {
|
||||
def msg = """Tap 'Next' after you have entered in your TCP Mobile remote credentials.
|
||||
|
||||
Once your credentials are accepted, SmartThings will scan your TCP installation for Bulbs."""
|
||||
|
||||
page(name: "selectDevices", title: "Connect Your TCP Lights to SmartThings", install: false, uninstall: true, nextPage: "chooseBulbs") {
|
||||
section("TCP Connected Remote Credentials") {
|
||||
input "username", "text", title: "Enter TCP Remote Email/UserName", required: true
|
||||
input "password", "password", title: "Enter TCP Remote Password", required: true
|
||||
paragraph msg
|
||||
}
|
||||
}
|
||||
|
||||
page(name: "chooseBulbs", title: "Choose Bulbs to Control With SmartThings", content: "initialize")
|
||||
}
|
||||
|
||||
def installed() {
|
||||
debugOut "Installed with settings: ${settings}"
|
||||
|
||||
unschedule()
|
||||
unsubscribe()
|
||||
|
||||
setupBulbs()
|
||||
|
||||
log.debug "schedule every 5 minutes syncronizeDevices)"
|
||||
runEvery5Minutes(syncronizeDevices)
|
||||
}
|
||||
|
||||
def updated() {
|
||||
debugOut "Updated with settings: ${settings}"
|
||||
|
||||
unschedule()
|
||||
|
||||
setupBulbs()
|
||||
|
||||
log.debug "schedule update every 5 minutes syncronizeDevices)"
|
||||
runEvery5Minutes(syncronizeDevices)
|
||||
}
|
||||
|
||||
def uninstalled()
|
||||
{
|
||||
unschedule() //in case we have hanging runIn()'s
|
||||
}
|
||||
|
||||
private removeChildDevices(delete)
|
||||
{
|
||||
debugOut "deleting ${delete.size()} bulbs"
|
||||
debugOut "deleting ${delete}"
|
||||
delete.each {
|
||||
deleteChildDevice(it.device.deviceNetworkId)
|
||||
}
|
||||
}
|
||||
|
||||
def uninstallFromChildDevice(childDevice)
|
||||
{
|
||||
def errorMsg = "uninstallFromChildDevice was called and "
|
||||
if (!settings.selectedBulbs) {
|
||||
debugOut errorMsg += "had empty list passed in"
|
||||
return
|
||||
}
|
||||
|
||||
def dni = childDevice.device.deviceNetworkId
|
||||
|
||||
if ( !dni ) {
|
||||
debugOut errorMsg += "could not find dni of device"
|
||||
return
|
||||
}
|
||||
|
||||
def newDeviceList = settings.selectedBulbs - dni
|
||||
app.updateSetting("selectedBulbs", newDeviceList)
|
||||
|
||||
debugOut errorMsg += "completed succesfully"
|
||||
}
|
||||
|
||||
|
||||
def setupBulbs() {
|
||||
debugOut "In setupBulbs"
|
||||
|
||||
def bulbs = state.devices
|
||||
def deviceFile = "TCP Bulb"
|
||||
|
||||
selectedBulbs.each { did ->
|
||||
//see if this is a selected bulb and install it if not already
|
||||
def d = getChildDevice(did)
|
||||
|
||||
if(!d) {
|
||||
def newBulb = bulbs.find { (it.did) == did }
|
||||
d = addChildDevice("wackford", deviceFile, did, null, [name: "${newBulb?.name}", label: "${newBulb?.name}", completedSetup: true])
|
||||
|
||||
/*if ( isRoom(did) ) { //change to the multi light group icon for a room device
|
||||
d.setIcon("switch", "on", "st.lights.multi-light-bulb-on")
|
||||
d.setIcon("switch", "off", "st.lights.multi-light-bulb-off")
|
||||
d.save()
|
||||
}*/
|
||||
|
||||
} else {
|
||||
debugOut "We already added this device"
|
||||
}
|
||||
}
|
||||
|
||||
// Delete any that are no longer in settings
|
||||
def delete = getChildDevices().findAll { !selectedBulbs?.contains(it.deviceNetworkId) }
|
||||
removeChildDevices(delete)
|
||||
|
||||
//we want to ensure syncronization between rooms and bulbs
|
||||
//syncronizeDevices()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
|
||||
atomicState.token = ""
|
||||
|
||||
getToken()
|
||||
|
||||
if ( atomicState.token == "error" ) {
|
||||
return dynamicPage(name:"chooseBulbs", title:"TCP Login Failed!\r\nTap 'Done' to try again", nextPage:"", install:false, uninstall: false) {
|
||||
section("") {}
|
||||
}
|
||||
} else {
|
||||
"we're good to go"
|
||||
debugOut "We have Token."
|
||||
}
|
||||
|
||||
//getGatewayData() //we really don't need anything from the gateway
|
||||
|
||||
deviceDiscovery()
|
||||
|
||||
def options = devicesDiscovered() ?: []
|
||||
|
||||
def msg = """Tap 'Done' after you have selected the desired devices."""
|
||||
|
||||
return dynamicPage(name:"chooseBulbs", title:"TCP and SmartThings Connected!", nextPage:"", install:true, uninstall: true) {
|
||||
section("Tap Below to View Device List") {
|
||||
input "selectedBulbs", "enum", required:false, title:"Select Bulb/Fixture", multiple:true, options:options
|
||||
paragraph msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def deviceDiscovery() {
|
||||
def data = "<gip><version>1</version><token>${atomicState.token}</token></gip>"
|
||||
|
||||
def Params = [
|
||||
cmd: "RoomGetCarousel",
|
||||
data: "${data}",
|
||||
fmt: "json"
|
||||
]
|
||||
|
||||
def cmd = toQueryString(Params)
|
||||
|
||||
def rooms = ""
|
||||
log.debug 'trying to discover devices'
|
||||
apiPost(cmd) { response ->
|
||||
rooms = response.data.gip.room
|
||||
}
|
||||
|
||||
debugOut "rooms data = ${rooms}"
|
||||
|
||||
def devices = []
|
||||
def bulbIndex = 1
|
||||
def lastRoomName = null
|
||||
def deviceList = []
|
||||
|
||||
if ( rooms[1] == null ) {
|
||||
def roomId = rooms.rid
|
||||
def roomName = rooms.name
|
||||
devices = rooms.device
|
||||
if ( devices[1] != null ) {
|
||||
debugOut "Room Device Data: did:${roomId} roomName:${roomName}"
|
||||
//deviceList += ["name" : "${roomName}", "did" : "${roomId}", "type" : "room"]
|
||||
devices.each({
|
||||
debugOut "Bulb Device Data: did:${it?.did} room:${roomName} BulbName:${it?.name}"
|
||||
deviceList += ["name" : "${roomName} ${it?.name}", "did" : "${it?.did}", "type" : "bulb"]
|
||||
})
|
||||
} else {
|
||||
debugOut "Bulb Device Data: did:${it?.did} room:${roomName} BulbName:${it?.name}"
|
||||
deviceList += ["name" : "${roomName} ${it?.name}", "did" : "${it?.did}", "type" : "bulb"]
|
||||
}
|
||||
} else {
|
||||
rooms.each({
|
||||
devices = it.device
|
||||
def roomName = it.name
|
||||
if ( devices[1] != null ) {
|
||||
def roomId = it?.rid
|
||||
debugOut "Room Device Data: did:${roomId} roomName:${roomName}"
|
||||
//deviceList += ["name" : "${roomName}", "did" : "${roomId}", "type" : "room"]
|
||||
devices.each({
|
||||
debugOut "Bulb Device Data: did:${it?.did} room:${roomName} BulbName:${it?.name}"
|
||||
deviceList += ["name" : "${roomName} ${it?.name}", "did" : "${it?.did}", "type" : "bulb"]
|
||||
})
|
||||
} else {
|
||||
debugOut "Bulb Device Data: did:${devices?.did} room:${roomName} BulbName:${devices?.name}"
|
||||
deviceList += ["name" : "${roomName} ${devices?.name}", "did" : "${devices?.did}", "type" : "bulb"]
|
||||
}
|
||||
})
|
||||
}
|
||||
devices = ["devices" : deviceList]
|
||||
state.devices = devices.devices
|
||||
}
|
||||
|
||||
Map devicesDiscovered() {
|
||||
def devices = state.devices
|
||||
def map = [:]
|
||||
if (devices instanceof java.util.Map) {
|
||||
devices.each {
|
||||
def value = "${it?.name}"
|
||||
def key = it?.did
|
||||
map["${key}"] = value
|
||||
}
|
||||
} else { //backwards compatable
|
||||
devices.each {
|
||||
def value = "${it?.name}"
|
||||
def key = it?.did
|
||||
map["${key}"] = value
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
def getGatewayData() {
|
||||
debugOut "In getGatewayData"
|
||||
|
||||
def data = "<gip><version>1</version><token>${atomicState.token}</token></gip>"
|
||||
|
||||
def qParams = [
|
||||
cmd: "GatewayGetInfo",
|
||||
data: "${data}",
|
||||
fmt: "json"
|
||||
]
|
||||
|
||||
def cmd = toQueryString(qParams)
|
||||
|
||||
apiPost(cmd) { response ->
|
||||
debugOut "the gateway reponse is ${response.data.gip.gateway}"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def getToken(Closure callback) {
|
||||
|
||||
atomicState.token = ""
|
||||
|
||||
if (password) {
|
||||
def hashedPassword = generateMD5(password)
|
||||
|
||||
def data = "<gip><version>1</version><email>${username}</email><password>${hashedPassword}</password></gip>"
|
||||
|
||||
def qParams = [
|
||||
cmd : "GWRLogin",
|
||||
data: "${data}",
|
||||
fmt : "json"
|
||||
]
|
||||
|
||||
def cmd = toQueryString(qParams)
|
||||
|
||||
apiPost(cmd) { response ->
|
||||
def status = response.data.gip.rc
|
||||
|
||||
//sendNotificationEvent("Get token status ${status}")
|
||||
|
||||
if (status != "200") {//success code = 200
|
||||
def errorText = response.data.gip.error
|
||||
debugOut "Error logging into TCP Gateway. Error = ${errorText}"
|
||||
atomicState.token = "error"
|
||||
} else {
|
||||
atomicState.token = response.data.gip.token
|
||||
if(callback){
|
||||
callback.call()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn "Unable to log into TCP Gateway. Error = Password is null"
|
||||
atomicState.token = "error"
|
||||
}
|
||||
}
|
||||
|
||||
def apiPost(String data, Integer retryCount = 0, Closure callback) {
|
||||
debugOut "In apiPost with data: ${data}"
|
||||
def params = [
|
||||
uri: apiUrl(),
|
||||
body: data
|
||||
]
|
||||
|
||||
httpPost(params) {
|
||||
response ->
|
||||
def rc = response.data.gip.rc
|
||||
|
||||
if ( rc == "200" ) {
|
||||
debugOut ("Return Code = ${rc} = Command Succeeded.")
|
||||
callback.call(response)
|
||||
|
||||
} else if ( rc.startsWith("4") || rc.startsWith("5") ) {
|
||||
debugOut "Return Code = ${rc} = Error: Something happened!" //Error code from gateway
|
||||
sendNotificationEvent("Return Code = ${rc} = Error: Something happened! Retry # ${retryCount}" )
|
||||
log.debug "Refreshing Token"
|
||||
if(retryCount > 5){
|
||||
// give up, send a notification
|
||||
sendNotificationEvent("TCP Lighting is having Communication Errors. Error code = ${rc}. Gave up after ${retryCount} tries")
|
||||
}
|
||||
getToken({ ->
|
||||
def updatedTokenData = data.replaceFirst("<token>[^<]*</token>", '<token>${atomicState.token}</token>')
|
||||
// try again if we got our token
|
||||
sendNotificationEvent('re-fetched token, trying again')
|
||||
|
||||
apiPost(updatedTokenData, retryCount++, callback)
|
||||
})
|
||||
//callback.call(response) //stubbed out so getToken works (we had race issue)
|
||||
|
||||
} else {
|
||||
log.error "Return Code = ${rc} = Error!" //Error code from gateway
|
||||
sendNotificationEvent("TCP Lighting is having Communication Errors. Error code = ${rc}. Check that TCP Gateway is online")
|
||||
callback.call(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//this is not working. TCP power reporting is broken. Leave it here for future fix
|
||||
def calculateCurrentPowerUse(deviceCapability, usePercentage) {
|
||||
debugOut "In calculateCurrentPowerUse()"
|
||||
|
||||
debugOut "deviceCapability: ${deviceCapability}"
|
||||
debugOut "usePercentage: ${usePercentage}"
|
||||
|
||||
def calcPower = usePercentage * 1000
|
||||
def reportPower = calcPower.round(1) as String
|
||||
|
||||
debugOut "report power = ${reportPower}"
|
||||
|
||||
return reportPower
|
||||
}
|
||||
|
||||
def generateSha256(String s) {
|
||||
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256")
|
||||
digest.update(s.bytes)
|
||||
new BigInteger(1, digest.digest()).toString(16).padLeft(40, '0')
|
||||
}
|
||||
|
||||
def generateMD5(String s) {
|
||||
MessageDigest digest = MessageDigest.getInstance("MD5")
|
||||
digest.update(s.bytes);
|
||||
new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
|
||||
}
|
||||
|
||||
String toQueryString(Map m) {
|
||||
return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&")
|
||||
}
|
||||
|
||||
def checkDevicesOnline(bulbs) {
|
||||
debugOut "In checkDevicesOnline()"
|
||||
|
||||
def onlineBulbs = []
|
||||
def thisBulb = []
|
||||
|
||||
bulbs.each {
|
||||
def dni = it?.did
|
||||
thisBulb = it
|
||||
|
||||
def data = "<gip><version>1</version><token>${atomicState.token}</token><did>${dni}</did></gip>"
|
||||
|
||||
def qParams = [
|
||||
cmd: "DeviceGetInfo",
|
||||
data: "${data}",
|
||||
fmt: "json"
|
||||
]
|
||||
|
||||
def cmd = toQueryString(qParams)
|
||||
|
||||
def bulbData = []
|
||||
|
||||
apiPost(cmd) { response ->
|
||||
bulbData = response.data.gip
|
||||
}
|
||||
|
||||
if ( bulbData?.offline == "1" ) {
|
||||
debugOut "${it?.name} is offline with offline value of ${bulbData?.offline}"
|
||||
|
||||
} else {
|
||||
debugOut "${it?.name} is online with offline value of ${bulbData?.offline}"
|
||||
onlineBulbs += thisBulb
|
||||
}
|
||||
}
|
||||
return onlineBulbs
|
||||
}
|
||||
|
||||
def syncronizeDevices() {
|
||||
debugOut "In syncronizeDevices"
|
||||
|
||||
def update = getChildDevices().findAll { selectedBulbs?.contains(it.deviceNetworkId) }
|
||||
|
||||
update.each {
|
||||
def dni = getChildDevice( it.deviceNetworkId )
|
||||
debugOut "dni = ${dni}"
|
||||
|
||||
if (isRoom(dni)) {
|
||||
pollRoom(dni)
|
||||
} else {
|
||||
poll(dni)
|
||||
}
|
||||
}
|
||||
getToken()
|
||||
|
||||
}
|
||||
|
||||
boolean isRoom(dni) {
|
||||
def device = state.devices.find() {(( it.type == 'room') && (it.did == "${dni}"))}
|
||||
}
|
||||
|
||||
boolean isBulb(dni) {
|
||||
def device = state.devices.find() {(( it.type == 'bulb') && (it.did == "${dni}"))}
|
||||
}
|
||||
|
||||
def debugEvent(message, displayEvent) {
|
||||
|
||||
def results = [
|
||||
name: "appdebug",
|
||||
descriptionText: message,
|
||||
displayed: displayEvent
|
||||
]
|
||||
log.debug "Generating AppDebug Event: ${results}"
|
||||
sendEvent (results)
|
||||
|
||||
}
|
||||
|
||||
def debugOut(msg) {
|
||||
log.debug msg
|
||||
//sendNotificationEvent(msg) //Uncomment this for troubleshooting only
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
Child Device Call In Methods
|
||||
**************************************************************************/
|
||||
def on(childDevice) {
|
||||
debugOut "On request from child device"
|
||||
|
||||
def dni = childDevice.device.deviceNetworkId
|
||||
def data = ""
|
||||
def cmd = ""
|
||||
|
||||
if ( isRoom(dni) ) { // this is a room, not a bulb
|
||||
data = "<gip><version>1</version><token>$atomicState.token</token><rid>${dni}</rid><type>power</type><value>1</value></gip>"
|
||||
cmd = "RoomSendCommand"
|
||||
} else {
|
||||
data = "<gip><version>1</version><token>$atomicState.token</token><did>${dni}</did><type>power</type><value>1</value></gip>"
|
||||
cmd = "DeviceSendCommand"
|
||||
}
|
||||
|
||||
def qParams = [
|
||||
cmd: cmd,
|
||||
data: "${data}",
|
||||
fmt: "json"
|
||||
]
|
||||
|
||||
cmd = toQueryString(qParams)
|
||||
|
||||
apiPost(cmd) { response ->
|
||||
debugOut "ON result: ${response.data}"
|
||||
}
|
||||
|
||||
//we want to ensure syncronization between rooms and bulbs
|
||||
//runIn(2, "syncronizeDevices")
|
||||
}
|
||||
|
||||
def off(childDevice) {
|
||||
debugOut "Off request from child device"
|
||||
|
||||
def dni = childDevice.device.deviceNetworkId
|
||||
def data = ""
|
||||
def cmd = ""
|
||||
|
||||
if ( isRoom(dni) ) { // this is a room, not a bulb
|
||||
data = "<gip><version>1</version><token>$atomicState.token</token><rid>${dni}</rid><type>power</type><value>0</value></gip>"
|
||||
cmd = "RoomSendCommand"
|
||||
} else {
|
||||
data = "<gip><version>1</version><token>$atomicState.token</token><did>${dni}</did><type>power</type><value>0</value></gip>"
|
||||
cmd = "DeviceSendCommand"
|
||||
}
|
||||
|
||||
def qParams = [
|
||||
cmd: cmd,
|
||||
data: "${data}",
|
||||
fmt: "json"
|
||||
]
|
||||
|
||||
cmd = toQueryString(qParams)
|
||||
|
||||
apiPost(cmd) { response ->
|
||||
debugOut "${response.data}"
|
||||
}
|
||||
|
||||
//we want to ensure syncronization between rooms and bulbs
|
||||
//runIn(2, "syncronizeDevices")
|
||||
}
|
||||
|
||||
def setLevel(childDevice, value) {
|
||||
debugOut "setLevel request from child device"
|
||||
|
||||
def dni = childDevice.device.deviceNetworkId
|
||||
def data = ""
|
||||
def cmd = ""
|
||||
|
||||
if ( isRoom(dni) ) { // this is a room, not a bulb
|
||||
data = "<gip><version>1</version><token>${atomicState.token}</token><rid>${dni}</rid><type>level</type><value>${value}</value></gip>"
|
||||
cmd = "RoomSendCommand"
|
||||
} else {
|
||||
data = "<gip><version>1</version><token>${atomicState.token}</token><did>${dni}</did><type>level</type><value>${value}</value></gip>"
|
||||
cmd = "DeviceSendCommand"
|
||||
}
|
||||
|
||||
def qParams = [
|
||||
cmd: cmd,
|
||||
data: "${data}",
|
||||
fmt: "json"
|
||||
]
|
||||
|
||||
cmd = toQueryString(qParams)
|
||||
|
||||
apiPost(cmd) { response ->
|
||||
debugOut "${response.data}"
|
||||
}
|
||||
|
||||
//we want to ensure syncronization between rooms and bulbs
|
||||
//runIn(2, "syncronizeDevices")
|
||||
}
|
||||
|
||||
// Really not called from child, but called from poll() if it is a room
|
||||
def pollRoom(dni) {
|
||||
debugOut "In pollRoom"
|
||||
def data = ""
|
||||
def cmd = ""
|
||||
def roomDeviceData = []
|
||||
|
||||
data = "<gip><version>1</version><token>${atomicState.token}</token><rid>${dni}</rid><fields>name,power,control,status,state</fields></gip>"
|
||||
cmd = "RoomGetDevices"
|
||||
|
||||
def qParams = [
|
||||
cmd: cmd,
|
||||
data: "${data}",
|
||||
fmt: "json"
|
||||
]
|
||||
|
||||
cmd = toQueryString(qParams)
|
||||
|
||||
apiPost(cmd) { response ->
|
||||
roomDeviceData = response.data.gip
|
||||
}
|
||||
|
||||
debugOut "Room Data: ${roomDeviceData}"
|
||||
|
||||
def totalPower = 0
|
||||
def totalLevel = 0
|
||||
def cnt = 0
|
||||
def onCnt = 0 //used to tally on/off states
|
||||
|
||||
roomDeviceData.device.each({
|
||||
if ( getChildDevice(it.did) ) {
|
||||
totalPower += it.other.bulbpower.toInteger()
|
||||
totalLevel += it.level.toInteger()
|
||||
onCnt += it.state.toInteger()
|
||||
cnt += 1
|
||||
}
|
||||
})
|
||||
|
||||
def avgLevel = totalLevel/cnt
|
||||
def usingPower = totalPower * (avgLevel / 100) as float
|
||||
def room = getChildDevice( dni )
|
||||
|
||||
//the device is a room but we use same type file
|
||||
sendEvent( dni, [name: "setBulbPower",value:"${totalPower}"] ) //used in child device calcs
|
||||
|
||||
//if all devices in room are on, room is on
|
||||
if ( cnt == onCnt ) { // all devices are on
|
||||
sendEvent( dni, [name: "switch",value:"on"] )
|
||||
sendEvent( dni, [name: "power",value:usingPower.round(1)] )
|
||||
|
||||
} else { //if any device in room is off, room is off
|
||||
sendEvent( dni, [name: "switch",value:"off"] )
|
||||
sendEvent( dni, [name: "power",value:0.0] )
|
||||
}
|
||||
|
||||
debugOut "Room Using Power: ${usingPower.round(1)}"
|
||||
}
|
||||
|
||||
def poll(childDevice) {
|
||||
debugOut "In poll() with ${childDevice}"
|
||||
|
||||
|
||||
def dni = childDevice.device.deviceNetworkId
|
||||
|
||||
def bulbData = []
|
||||
def data = ""
|
||||
def cmd = ""
|
||||
|
||||
if ( isRoom(dni) ) { // this is a room, not a bulb
|
||||
pollRoom(dni)
|
||||
return
|
||||
}
|
||||
|
||||
data = "<gip><version>1</version><token>${atomicState.token}</token><did>${dni}</did></gip>"
|
||||
cmd = "DeviceGetInfo"
|
||||
|
||||
def qParams = [
|
||||
cmd: cmd,
|
||||
data: "${data}",
|
||||
fmt: "json"
|
||||
]
|
||||
|
||||
cmd = toQueryString(qParams)
|
||||
|
||||
apiPost(cmd) { response ->
|
||||
bulbData = response.data.gip
|
||||
debugOut "This Bulbs Data Return = ${bulbData}"
|
||||
|
||||
def bulb = getChildDevice( dni )
|
||||
|
||||
//set the devices power max setting to do calcs within the device type
|
||||
if ( bulbData.other.bulbpower )
|
||||
sendEvent( dni, [name: "setBulbPower",value:"${bulbData.other.bulbpower}"] )
|
||||
|
||||
if (( bulbData.state == "1" ) && ( bulb?.currentValue("switch") != "on" ))
|
||||
sendEvent( dni, [name: "switch",value:"on"] )
|
||||
|
||||
if (( bulbData.state == "0" ) && ( bulb?.currentValue("switch") != "off" ))
|
||||
sendEvent( dni, [name: "switch",value:"off"] )
|
||||
|
||||
//if ( bulbData.level != bulb?.currentValue("level")) {
|
||||
// sendEvent( dni, [name: "level",value: "${bulbData.level}"] )
|
||||
// sendEvent( dni, [name: "setLevel",value: "${bulbData.level}"] )
|
||||
//}
|
||||
|
||||
if (( bulbData.state == "1" ) && ( bulbData.other.bulbpower )) {
|
||||
def levelSetting = bulbData.level as float
|
||||
def bulbPowerMax = bulbData.other.bulbpower as float
|
||||
def calculatedPower = bulbPowerMax * (levelSetting / 100)
|
||||
sendEvent( dni, [name: "power", value: calculatedPower.round(1)] )
|
||||
}
|
||||
|
||||
if (( bulbData.state == "0" ) && ( bulbData.other.bulbpower ))
|
||||
sendEvent( dni, [name: "power", value: 0.0] )
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user