mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 21:03:00 +00:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1298dc55ae | ||
|
|
ce26a2941e | ||
|
|
81fb356a21 | ||
|
|
aadbcc2eee | ||
|
|
4a49d4c15d | ||
|
|
0e3bd5aa74 | ||
|
|
bfd68228bc | ||
|
|
a2e8a8303b | ||
|
|
56580634e8 | ||
|
|
a82717744e | ||
|
|
b1096a67af | ||
|
|
ae945d1a15 | ||
|
|
12220da750 | ||
|
|
3f89bbb6d9 | ||
|
|
fe2b36cd30 | ||
|
|
55c17383e1 | ||
|
|
bd9a1d1dc5 | ||
|
|
a6bec43f2a | ||
|
|
6fe60146a4 | ||
|
|
5ec82217ac | ||
|
|
208f0d97df | ||
|
|
e444c8d020 | ||
|
|
9f75bbfbde | ||
|
|
996ac27ba2 | ||
|
|
16b87c5eda | ||
|
|
05ad96d583 | ||
|
|
0a6bb51974 | ||
|
|
9ab74c3ba6 | ||
|
|
8f08a0819c | ||
|
|
d34d1d3615 | ||
|
|
5085d7f184 | ||
|
|
dc927e0659 | ||
|
|
991637b1c1 | ||
|
|
1372d4005a | ||
|
|
9439efd7b9 | ||
|
|
70b8a042a7 | ||
|
|
451b7a4923 | ||
|
|
22c810e9df | ||
|
|
cc80373b89 | ||
|
|
ea98194abf | ||
|
|
94f11c583f | ||
|
|
4a6e000cee | ||
|
|
c3cf4089f4 | ||
|
|
71f9f19465 | ||
|
|
f8317a6d2c | ||
|
|
2edda411e2 | ||
|
|
dd19b6e73f | ||
|
|
ac74c6126d | ||
|
|
80e02416f3 | ||
|
|
653f0e28ca | ||
|
|
49dc1e96ba | ||
|
|
76fcca90d3 | ||
|
|
c197f4263e | ||
|
|
54f976229e | ||
|
|
3ba148d5d2 | ||
|
|
041733373b | ||
|
|
b67783a235 | ||
|
|
b90e2a1982 | ||
|
|
ae444dfe09 | ||
|
|
df55116ac6 | ||
|
|
7d0b3ef796 | ||
|
|
c3d5f60250 | ||
|
|
f55ea8b2f8 | ||
|
|
968c9cc647 | ||
|
|
3e7ce67ea4 | ||
|
|
479651a330 | ||
|
|
39b00c2a04 | ||
|
|
5a52e69911 | ||
|
|
7f4384cd85 | ||
|
|
42479a94b4 | ||
|
|
57e668f1f2 | ||
|
|
0321a7f071 | ||
|
|
855ed02ffa | ||
|
|
60fd008d4a | ||
|
|
df764d57c3 | ||
|
|
a96bb027c8 | ||
|
|
6b62f88bb7 | ||
|
|
848bbdcf2b | ||
|
|
12288accda | ||
|
|
3533943827 |
@@ -0,0 +1,627 @@
|
||||
/**
|
||||
* Spruce Controller - Pre Release V2 10/11/2015
|
||||
*
|
||||
* Copyright 2015 Plaid Systems
|
||||
*
|
||||
* Author: NC
|
||||
* Date: 2015-11
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
-----------V3 updates-11-2015------------
|
||||
-Start program button updated to signal schedule check in Scheduler
|
||||
11/17 alarm "0" -> 0 (ln 305)
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "Spruce Controller", namespace: "plaidsystems", author: "NCauffman") {
|
||||
capability "Switch"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Actuator"
|
||||
capability "Valve"
|
||||
|
||||
attribute "switch", "string"
|
||||
attribute "switch1", "string"
|
||||
attribute "switch2", "string"
|
||||
attribute "switch8", "string"
|
||||
attribute "switch5", "string"
|
||||
attribute "switch3", "string"
|
||||
attribute "switch4", "string"
|
||||
attribute "switch6", "string"
|
||||
attribute "switch7", "string"
|
||||
attribute "switch9", "string"
|
||||
attribute "switch10", "string"
|
||||
attribute "switch11", "string"
|
||||
attribute "switch12", "string"
|
||||
attribute "switch13", "string"
|
||||
attribute "switch14", "string"
|
||||
attribute "switch15", "string"
|
||||
attribute "switch16", "string"
|
||||
attribute "status", "string"
|
||||
|
||||
command "programOn"
|
||||
command "programOff"
|
||||
command "on"
|
||||
command "off"
|
||||
command "z1on"
|
||||
command "z1off"
|
||||
command "z2on"
|
||||
command "z2off"
|
||||
command "z3on"
|
||||
command "z3off"
|
||||
command "z4on"
|
||||
command "z4off"
|
||||
command "z5on"
|
||||
command "z5off"
|
||||
command "z6on"
|
||||
command "z6off"
|
||||
command "z7on"
|
||||
command "z7off"
|
||||
command "z8on"
|
||||
command "z8off"
|
||||
command "z9on"
|
||||
command "z9off"
|
||||
command "z10on"
|
||||
command "z10off"
|
||||
command "z11on"
|
||||
command "z11off"
|
||||
command "z12on"
|
||||
command "z12off"
|
||||
command "z13on"
|
||||
command "z13off"
|
||||
command "z14on"
|
||||
command "z14off"
|
||||
command "z15on"
|
||||
command "z15off"
|
||||
command "z16on"
|
||||
command "z16off"
|
||||
command "offtime"
|
||||
|
||||
command "refresh"
|
||||
command "rain"
|
||||
command "manual"
|
||||
command "setDisplay"
|
||||
|
||||
command "settingsMap"
|
||||
command "writeTime"
|
||||
command "writeType"
|
||||
command "notify"
|
||||
command "updated"
|
||||
|
||||
fingerprint endpointId: "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18", profileId: "0104", deviceId: "0002", deviceVersion: "00", inClusters: "0000,0003,0004,0005,0006,000F", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZ16-01"
|
||||
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
// status messages
|
||||
|
||||
// reply messages
|
||||
|
||||
}
|
||||
preferences {
|
||||
input description: "Press Configure button after making changes to these preferences", displayDuringSetup: true, type: "paragraph", element: "paragraph", title: ""
|
||||
input "RainEnable", "bool", title: "Rain Sensor Attached?", required: false, displayDuringSetup: true
|
||||
input "ManualTime", "number", title: "Automatic shutoff time when a zone is turned on manually?", required: false, displayDuringSetup: true
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
|
||||
standardTile("status", "device.status") {
|
||||
state "schedule", label: 'Schedule Set', icon: "http://www.plaidsystems.com/smartthings/st_spruce_leaf_225_t.png"
|
||||
state "finished", label: 'Spruce Finished', icon: "st.Outdoor.outdoor5", backgroundColor: "#46c2e8"
|
||||
state "raintoday", label: 'Rain Today', icon: "st.custom.wuk.nt_chancerain"
|
||||
state "rainy", label: 'Previous Rain', icon: "st.custom.wuk.nt_chancerain"
|
||||
state "raintom", label: 'Rain Tomorrow', icon: "st.custom.wuk.nt_chancerain"
|
||||
state "donewweek", label: 'Spruce Finished', icon: "st.Outdoor.outdoor5", backgroundColor: "#52c435"
|
||||
state "skipping", label: 'Skip Today', icon: "st.Outdoor.outdoor20", backgroundColor: "#36cfe3"
|
||||
state "moisture", label: '', icon: "st.Weather.weather2", backgroundColor: "#36cfe3"
|
||||
state "pause", label: 'PAUSE', icon: "st.contact.contact.open", backgroundColor: "#f2a51f"
|
||||
state "active", label: 'Active', icon: "st.Outdoor.outdoor12", backgroundColor: "#3DC72E"
|
||||
state "season", label: 'Seasonal Adjustment', icon: "st.Outdoor.outdoor17", backgroundColor: "#ffb900"
|
||||
state "disable", label: 'Disabled', icon: "st.secondary.off", backgroundColor: "#888888"
|
||||
state "warning", label: '', icon: "st.categories.damageAndDanger", backgroundColor: "#ffff7f"
|
||||
state "alarm", label: 'Alarm', icon: "st.categories.damageAndDanger", backgroundColor: "#f9240c"
|
||||
}
|
||||
standardTile("switch", "device.switch") {
|
||||
//state "programOff", label: 'Start Program', action: "programOn", icon: "st.sonos.play-icon", backgroundColor: "#a9a9a9"
|
||||
state "off", label: 'Start Program', action: "programOn", icon: "st.sonos.play-icon", backgroundColor: "#a9a9a9"
|
||||
state "programOn", label: 'Initialize Program', action: "programOff", icon: "st.contact.contact.open", backgroundColor: "#f6e10e"
|
||||
state "on", label: 'Program Running', action: "off", icon: "st.Outdoor.outdoor12", backgroundColor: "#3DC72E"
|
||||
}
|
||||
standardTile("rainsensor", "device.rainsensor") {
|
||||
state "rainSensrooff", label: 'Rain Sensor Clear', icon: "st.Weather.weather14", backgroundColor: "#a9a9a9"
|
||||
state "rainSensoron", label: 'Rain Detected', icon: "st.Weather.weather10", backgroundColor: "#f6e10e"
|
||||
}
|
||||
standardTile("switch1", "device.switch1") {
|
||||
state "z1off", label: '1', action: "z1on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z1on", label: '1', action: "z1off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch2", "device.switch2") {
|
||||
state "z2off", label: '2', action: "z2on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z2on", label: '2', action: "z2off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch3", "device.switch3", inactiveLabel: false) {
|
||||
state "z3off", label: '3', action: "z3on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z3on", label: '3', action: "z3off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch4", "device.switch4", inactiveLabel: false) {
|
||||
state "z4off", label: '4', action: "z4on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z4on", label: '4', action: "z4off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch5", "device.switch5", inactiveLabel: false) {
|
||||
state "z5off", label: '5', action: "z5on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z5on", label: '5', action: "z5off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch6", "device.switch6", inactiveLabel: false) {
|
||||
state "z6off", label: '6', action: "z6on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z6on", label: '6', action: "z6off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch7", "device.switch7", inactiveLabel: false) {
|
||||
state "z7off", label: '7', action: "z7on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z7on", label: '7', action: "z7off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch8", "device.switch8", inactiveLabel: false) {
|
||||
state "z8off", label: '8', action: "z8on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z8on", label: '8', action: "z8off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch9", "device.switch9", inactiveLabel: false) {
|
||||
state "z9off", label: '9', action: "z9on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z9on", label: '9', action: "z9off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch10", "device.switch10", inactiveLabel: false) {
|
||||
state "z10off", label: '10', action: "z10on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z10on", label: '10', action: "z10off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch11", "device.switch11", inactiveLabel: false) {
|
||||
state "z11off", label: '11', action: "z11on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z11on", label: '11', action: "z11off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch12", "device.switch12", inactiveLabel: false) {
|
||||
state "z12off", label: '12', action: "z12on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z12on", label: '12', action: "z12off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch13", "device.switch13", inactiveLabel: false) {
|
||||
state "z13off", label: '13', action: "z13on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z13on", label: '13', action: "z13off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch14", "device.switch14", inactiveLabel: false) {
|
||||
state "z14off", label: '14', action: "z14on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z14on", label: '14', action: "z14off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch15", "device.switch15", inactiveLabel: false) {
|
||||
state "z15off", label: '15', action: "z15on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z15on", label: '15', action: "z15off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("switch16", "device.switch16", inactiveLabel: false) {
|
||||
state "z16off", label: '16', action: "z16on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z16on", label: '16', action: "z16off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", action: "refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
|
||||
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
||||
}
|
||||
|
||||
main (["status"])
|
||||
details(["status","rainsensor","switch","switch1","switch2","switch3","switch4","switch5","switch6","switch7","switch8","switch9","switch10","switch11","switch12","switch13","switch14","switch15","switch16","refresh","configure"])
|
||||
}
|
||||
}
|
||||
|
||||
def programOn(){
|
||||
sendEvent(name: "switch", value: "programOn", descriptionText: "Program turned on")
|
||||
}
|
||||
|
||||
def programOff(){
|
||||
sendEvent(name: "switch", value: "off", descriptionText: "Program turned off")
|
||||
off()
|
||||
}
|
||||
|
||||
def updated(){
|
||||
log.debug "updated"
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
//log.debug "Parse description $description"
|
||||
def result = null
|
||||
def map = [:]
|
||||
if (description?.startsWith("read attr -")) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
//log.debug "Desc Map: $descMap"
|
||||
//using 000F cluster instead of 0006 (switch) because ST does not differentiate between EPs and processes all as switch
|
||||
if (descMap.cluster == "000F" && descMap.attrId == "0055") {
|
||||
log.debug "Zone"
|
||||
map = getZone(descMap)
|
||||
}
|
||||
else if (descMap.cluster == "0009" && descMap.attrId == "0000") {
|
||||
log.debug "Alarm"
|
||||
map = getAlarm(descMap)
|
||||
}
|
||||
}
|
||||
|
||||
if (map) {
|
||||
result = createEvent(map)
|
||||
}
|
||||
log.debug "Parse returned $map $result"
|
||||
return result
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
|
||||
def getZone(descMap){
|
||||
def map = [:]
|
||||
|
||||
def EP = Integer.parseInt(descMap.endpoint.trim(), 16)
|
||||
|
||||
String onoff
|
||||
if(descMap.value == "00"){
|
||||
onoff = "off"
|
||||
}
|
||||
else onoff = "on"
|
||||
|
||||
if (EP == 1){
|
||||
map.name = "switch"
|
||||
map.value = onoff
|
||||
map.descriptionText = "${device.displayName} turned sprinkler program $onoff"
|
||||
}
|
||||
|
||||
else if (EP == 18) {
|
||||
map.name = "rainsensor"
|
||||
map.value = "rainSensor" + onoff
|
||||
map.descriptionText = "${device.displayName} rain sensor is $onoff"
|
||||
}
|
||||
else {
|
||||
EP -= 1
|
||||
map.name = "switch" + EP
|
||||
map.value = "z" + EP + onoff
|
||||
map.descriptionText = "${device.displayName} turned Zone $EP $onoff"
|
||||
}
|
||||
|
||||
map.isStateChange = true
|
||||
map.displayed = true
|
||||
return map
|
||||
}
|
||||
|
||||
def getAlarm(descMap){
|
||||
def map = [:]
|
||||
map.name = "status"
|
||||
def alarmID = Integer.parseInt(descMap.value.trim(), 16)
|
||||
log.debug "${alarmID}"
|
||||
if(alarmID <= 0) map.descriptionText = "${device.displayName} has rebooted, no other alarms"
|
||||
else map.descriptionText = "${device.displayName} rebooted, reported error on zone ${alarmID - 1}, please check zone is working correctly"
|
||||
map.value = "alarm"
|
||||
map.isStateChange = true
|
||||
map.displayed = true
|
||||
return map
|
||||
}
|
||||
|
||||
//status notify and change status
|
||||
def notify(value, text){
|
||||
sendEvent(name:"status", value:"$value", descriptionText:"$text", isStateChange: true, display: false)
|
||||
|
||||
}
|
||||
|
||||
//prefrences - rain sensor, manual time
|
||||
def rain() {
|
||||
log.debug "Rain $RainEnable"
|
||||
if (RainEnable) "st wattr 0x${device.deviceNetworkId} 18 0x0F 0x51 0x10 {01}"
|
||||
else "st wattr 0x${device.deviceNetworkId} 18 0x0F 0x51 0x10 {00}"
|
||||
}
|
||||
def manual(){
|
||||
log.debug "Time $ManualTime"
|
||||
def mTime = 10
|
||||
if (ManualTime) mTime = ManualTime
|
||||
def manualTime = hex(mTime)
|
||||
"st wattr 0x${device.deviceNetworkId} 1 6 0x4002 0x21 {00${manualTime}}"
|
||||
|
||||
}
|
||||
|
||||
//write switch time settings map
|
||||
def settingsMap(WriteTimes, attrType){
|
||||
log.debug WriteTimes
|
||||
|
||||
def i = 1
|
||||
def runTime
|
||||
def sendCmds = []
|
||||
while(i <= 17){
|
||||
|
||||
if (WriteTimes."${i}"){
|
||||
runTime = hex(Integer.parseInt(WriteTimes."${i}"))
|
||||
log.debug "${i} : $runTime"
|
||||
|
||||
if (attrType == 4001) sendCmds.push("st wattr 0x${device.deviceNetworkId} ${i} 0x06 0x4001 0x21 {00${runTime}}")
|
||||
else sendCmds.push("st wattr 0x${device.deviceNetworkId} ${i} 0x06 0x4002 0x21 {00${runTime}}")
|
||||
sendCmds.push("delay 500")
|
||||
}
|
||||
i++
|
||||
}
|
||||
return sendCmds
|
||||
}
|
||||
|
||||
//send switch time
|
||||
def writeType(wEP, cycle){
|
||||
log.debug "wt ${wEP} ${cycle}"
|
||||
"st wattr 0x${device.deviceNetworkId} ${wEP} 0x06 0x4001 0x21 {00" + hex(cycle) + "}"
|
||||
}
|
||||
//send switch off time
|
||||
def writeTime(wEP, runTime){
|
||||
"st wattr 0x${device.deviceNetworkId} ${wEP} 0x06 0x4002 0x21 {00" + hex(runTime) + "}"
|
||||
}
|
||||
|
||||
//set reporting and binding
|
||||
def configure() {
|
||||
|
||||
String zigbeeId = swapEndianHex(device.hub.zigbeeId)
|
||||
log.debug "Confuguring Reporting and Bindings ${device.deviceNetworkId} ${device.zigbeeId}"
|
||||
sendEvent(name: 'configuration',value: 100, descriptionText: "Configuration initialized")
|
||||
|
||||
def configCmds = [
|
||||
//program on/off
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x09 {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
//zones 1-8
|
||||
"zdo bind 0x${device.deviceNetworkId} 2 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 3 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 4 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 5 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 6 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 7 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 8 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 9 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
//zones 9-16
|
||||
"zdo bind 0x${device.deviceNetworkId} 10 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 11 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 12 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 13 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 14 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 15 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 16 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 17 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
//rain sensor
|
||||
"zdo bind 0x${device.deviceNetworkId} 18 1 0x0F {${device.zigbeeId}} {}",
|
||||
|
||||
"zcl global send-me-a-report 6 0 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 2", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 3", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 4", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 5", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 6", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 7", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 8", "delay 500",
|
||||
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 9", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 10", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 11", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 12", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 13", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 14", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 15", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 16", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 17", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 18", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x09 0x00 0x21 1 0 {00}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
||||
]
|
||||
return configCmds + rain() + manual()
|
||||
}
|
||||
|
||||
|
||||
|
||||
private hex(value) {
|
||||
new BigInteger(Math.round(value).toString()).toString(16)
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
int i = 0;
|
||||
int j = array.length - 1;
|
||||
byte tmp;
|
||||
while (j > i) {
|
||||
tmp = array[j];
|
||||
array[j] = array[i];
|
||||
array[i] = tmp;
|
||||
j--;
|
||||
i++;
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
|
||||
log.debug "refresh"
|
||||
def refreshCmds = [
|
||||
|
||||
"st rattr 0x${device.deviceNetworkId} 1 0x0F 0x55", "delay 500",
|
||||
|
||||
"st rattr 0x${device.deviceNetworkId} 2 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 3 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 4 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 5 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 6 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 7 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 8 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 9 0x0F 0x55", "delay 500",
|
||||
|
||||
"st rattr 0x${device.deviceNetworkId} 10 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 11 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 12 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 13 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 14 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 15 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 16 0x0F 0x55", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 17 0x0F 0x55", "delay 500",
|
||||
|
||||
"st rattr 0x${device.deviceNetworkId} 18 0x0F 0x51","delay 500",
|
||||
|
||||
]
|
||||
return refreshCmds + rain() + manual()
|
||||
}
|
||||
|
||||
// Commands to device
|
||||
//zones on - 8
|
||||
def on() {
|
||||
//sendEvent(name:"status", value:"active", descriptionText:"Program Running", isStateChange: true, display: false)
|
||||
log.debug "on"
|
||||
"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
|
||||
}
|
||||
def off() {
|
||||
log.debug "off"
|
||||
"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
|
||||
}
|
||||
def z1on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 2 6 1 {}"
|
||||
}
|
||||
def z1off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 2 6 0 {}"
|
||||
}
|
||||
def z2on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 3 6 1 {}"
|
||||
}
|
||||
def z2off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 3 6 0 {}"
|
||||
}
|
||||
def z3on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 4 6 1 {}"
|
||||
}
|
||||
def z3off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 4 6 0 {}"
|
||||
}
|
||||
def z4on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 5 6 1 {}"
|
||||
}
|
||||
def z4off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 5 6 0 {}"
|
||||
}
|
||||
def z5on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 6 6 1 {}"
|
||||
}
|
||||
def z5off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 6 6 0 {}"
|
||||
}
|
||||
def z6on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 7 6 1 {}"
|
||||
}
|
||||
def z6off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 7 6 0 {}"
|
||||
}
|
||||
def z7on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 8 6 1 {}"
|
||||
}
|
||||
def z7off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 8 6 0 {}"
|
||||
}
|
||||
def z8on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 9 6 1 {}"
|
||||
}
|
||||
def z8off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 9 6 0 {}"
|
||||
}
|
||||
|
||||
//zones 9 - 16
|
||||
def z9on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 10 6 1 {}"
|
||||
}
|
||||
def z9off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 10 6 0 {}"
|
||||
}
|
||||
def z10on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 11 6 1 {}"
|
||||
}
|
||||
def z10off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 11 6 0 {}"
|
||||
}
|
||||
def z11on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 12 6 1 {}"
|
||||
}
|
||||
def z11off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 12 6 0 {}"
|
||||
}
|
||||
def z12on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 13 6 1 {}"
|
||||
}
|
||||
def z12off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 13 6 0 {}"
|
||||
}
|
||||
def z13on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 14 6 1 {}"
|
||||
}
|
||||
def z13off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 14 6 0 {}"
|
||||
}
|
||||
def z14on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 15 6 1 {}"
|
||||
}
|
||||
def z14off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 15 6 0 {}"
|
||||
}
|
||||
def z15on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 16 6 1 {}"
|
||||
}
|
||||
def z15off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 16 6 0 {}"
|
||||
}
|
||||
def z16on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 17 6 1 {}"
|
||||
}
|
||||
def z16off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 17 6 0 {}"
|
||||
}
|
||||
397
devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy
Normal file
397
devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy
Normal file
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* Spruce Sensor -Pre-release V2 10/8/2015
|
||||
*
|
||||
* Copyright 2014 Plaid Systems
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
-------10/20/2015 Updates--------
|
||||
-Fix/add battery reporting interval to update
|
||||
-remove polling and/or refresh(?)
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "NCauffman") {
|
||||
|
||||
capability "Configuration"
|
||||
capability "Battery"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Temperature Measurement"
|
||||
capability "Sensor"
|
||||
//capability "Polling"
|
||||
|
||||
attribute "maxHum", "string"
|
||||
attribute "minHum", "string"
|
||||
|
||||
command "resetHumidity"
|
||||
command "refresh"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01"
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph", title: ""
|
||||
input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
|
||||
input "interval", "number", title: "Measurement Interval 1-120 minutes (default: 10 minutes)", description: "Set how often you would like to check soil moisture in minutes", range: "1..120", defaultValue: 10, displayDuringSetup: false
|
||||
input "resetMinMax", "bool", title: "Reset Humidity min and max", required: false, displayDuringSetup: false
|
||||
}
|
||||
|
||||
tiles {
|
||||
valueTile("temperature", "device.temperature", canChangeIcon: false, canChangeBackground: false) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
backgroundColors:[
|
||||
[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"]
|
||||
]
|
||||
}
|
||||
valueTile("humidity", "device.humidity", width: 2, height: 2, canChangeIcon: false, canChangeBackground: true) {
|
||||
state "humidity", label:'${currentValue}%', unit:"",
|
||||
backgroundColors:[
|
||||
[value: 0, color: "#635C0C"],
|
||||
[value: 16, color: "#EBEB21"],
|
||||
[value: 22, color: "#C7DE6A"],
|
||||
[value: 42, color: "#9AD290"],
|
||||
[value: 64, color: "#44B621"],
|
||||
[value: 80, color: "#3D79D9"],
|
||||
[value: 96, color: "#0A50C2"]
|
||||
]
|
||||
}
|
||||
|
||||
valueTile("maxHum", "device.maxHum", canChangeIcon: false, canChangeBackground: false) {
|
||||
state "maxHum", label:'High ${currentValue}%', unit:"",
|
||||
backgroundColors:[
|
||||
[value: 0, color: "#635C0C"],
|
||||
[value: 16, color: "#EBEB21"],
|
||||
[value: 22, color: "#C7DE6A"],
|
||||
[value: 42, color: "#9AD290"],
|
||||
[value: 64, color: "#44B621"],
|
||||
[value: 80, color: "#3D79D9"],
|
||||
[value: 96, color: "#0A50C2"]
|
||||
]
|
||||
}
|
||||
valueTile("minHum", "device.minHum", canChangeIcon: false, canChangeBackground: false) {
|
||||
state "minHum", label:'Low ${currentValue}%', unit:"",
|
||||
backgroundColors:[
|
||||
[value: 0, color: "#635C0C"],
|
||||
[value: 16, color: "#EBEB21"],
|
||||
[value: 22, color: "#C7DE6A"],
|
||||
[value: 42, color: "#9AD290"],
|
||||
[value: 64, color: "#44B621"],
|
||||
[value: 80, color: "#3D79D9"],
|
||||
[value: 96, color: "#0A50C2"]
|
||||
]
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", decoration: "flat", canChangeIcon: false, canChangeBackground: false) {
|
||||
state "battery", label:'${currentValue}% battery'
|
||||
}
|
||||
|
||||
main (["humidity"])
|
||||
details(["humidity","maxHum","minHum","temperature","battery"])
|
||||
}
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
log.debug "Parse description $description config: ${device.latestValue('configuration')} interval: $interval"
|
||||
|
||||
Map map = [:]
|
||||
|
||||
if (description?.startsWith('catchall:')) {
|
||||
map = parseCatchAllMessage(description)
|
||||
}
|
||||
else if (description?.startsWith('read attr -')) {
|
||||
map = parseReportAttributeMessage(description)
|
||||
}
|
||||
else if (description?.startsWith('temperature: ') || description?.startsWith('humidity: ')) {
|
||||
map = parseCustomMessage(description)
|
||||
}
|
||||
def result = map ? createEvent(map) : null
|
||||
|
||||
//check in configuration change
|
||||
if (!device.latestValue('configuration')) result = poll()
|
||||
if (device.latestValue('configuration').toInteger() != interval && interval != null) {
|
||||
result = poll()
|
||||
}
|
||||
log.debug "result: $result"
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Map parseCatchAllMessage(String description) {
|
||||
Map resultMap = [:]
|
||||
def linkText = getLinkText(device)
|
||||
//log.debug "Catchall"
|
||||
def descMap = zigbee.parse(description)
|
||||
|
||||
//check humidity configuration is complete
|
||||
if (descMap.command == 0x07 && descMap.clusterId == 0x0405){
|
||||
def configInterval = 10
|
||||
if (interval != null) configInterval = interval
|
||||
sendEvent(name: 'configuration',value: configInterval, descriptionText: "Configuration Successful")
|
||||
//setConfig()
|
||||
log.debug "config complete"
|
||||
//return resultMap = [name: 'configuration', value: configInterval, descriptionText: "Settings configured successfully"]
|
||||
}
|
||||
else if (descMap.command == 0x0001){
|
||||
def hexString = "${hex(descMap.data[5])}" + "${hex(descMap.data[4])}"
|
||||
def intString = Integer.parseInt(hexString, 16)
|
||||
//log.debug "command: $descMap.command clusterid: $descMap.clusterId $hexString $intString"
|
||||
|
||||
if (descMap.clusterId == 0x0402){
|
||||
def value = getTemperature(hexString)
|
||||
resultMap = getTemperatureResult(value)
|
||||
}
|
||||
else if (descMap.clusterId == 0x0405){
|
||||
def value = Math.round(new BigDecimal(intString / 100)).toString()
|
||||
resultMap = getHumidityResult(value)
|
||||
|
||||
}
|
||||
else return null
|
||||
}
|
||||
else return null
|
||||
|
||||
return resultMap
|
||||
}
|
||||
|
||||
private Map parseReportAttributeMessage(String description) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
log.debug "Desc Map: $descMap"
|
||||
log.debug "Report Attributes"
|
||||
|
||||
Map resultMap = [:]
|
||||
if (descMap.cluster == "0001" && descMap.attrId == "0000") {
|
||||
resultMap = getBatteryResult(descMap.value)
|
||||
}
|
||||
return resultMap
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
|
||||
private Map parseCustomMessage(String description) {
|
||||
Map resultMap = [:]
|
||||
|
||||
log.debug "parseCustom"
|
||||
if (description?.startsWith('temperature: ')) {
|
||||
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
|
||||
resultMap = getTemperatureResult(value)
|
||||
}
|
||||
else if (description?.startsWith('humidity: ')) {
|
||||
def pct = (description - "humidity: " - "%").trim()
|
||||
if (pct.isNumber()) {
|
||||
def value = Math.round(new BigDecimal(pct)).toString()
|
||||
resultMap = getHumidityResult(value)
|
||||
} else {
|
||||
log.error "invalid humidity: ${pct}"
|
||||
}
|
||||
}
|
||||
return resultMap
|
||||
}
|
||||
|
||||
private Map getHumidityResult(value) {
|
||||
def linkText = getLinkText(device)
|
||||
def maxHumValue = 0
|
||||
def minHumValue = 0
|
||||
if (device.currentValue("maxHum") != null) maxHumValue = device.currentValue("maxHum").toInteger()
|
||||
if (device.currentValue("minHum") != null) minHumValue = device.currentValue("minHum").toInteger()
|
||||
log.debug "Humidity max: ${maxHumValue} min: ${minHumValue}"
|
||||
def compare = value.toInteger()
|
||||
|
||||
if (compare > maxHumValue) {
|
||||
sendEvent(name: 'maxHum', value: value, unit: '%', descriptionText: "${linkText} soil moisture high is ${value}%")
|
||||
}
|
||||
else if (((compare < minHumValue) || (minHumValue <= 2)) && (compare != 0)) {
|
||||
sendEvent(name: 'minHum', value: value, unit: '%', descriptionText: "${linkText} soil moisture low is ${value}%")
|
||||
}
|
||||
|
||||
return [
|
||||
name: 'humidity',
|
||||
value: value,
|
||||
unit: '%',
|
||||
descriptionText: "${linkText} soil moisture is ${value}%"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
def getTemperature(value) {
|
||||
def celsius = (Integer.parseInt(value, 16).shortValue()/100)
|
||||
//log.debug "Report Temp $value : $celsius C"
|
||||
if(getTemperatureScale() == "C"){
|
||||
return celsius
|
||||
} else {
|
||||
return celsiusToFahrenheit(celsius) as Integer
|
||||
}
|
||||
}
|
||||
|
||||
private Map getTemperatureResult(value) {
|
||||
log.debug "Temperature: $value"
|
||||
def linkText = getLinkText(device)
|
||||
|
||||
if (tempOffset) {
|
||||
def offset = tempOffset as int
|
||||
def v = value as int
|
||||
value = v + offset
|
||||
}
|
||||
def descriptionText = "${linkText} is ${value}°${temperatureScale}"
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText
|
||||
]
|
||||
}
|
||||
|
||||
private Map getBatteryResult(value) {
|
||||
log.debug 'Battery'
|
||||
def linkText = getLinkText(device)
|
||||
|
||||
def result = [
|
||||
name: 'battery'
|
||||
]
|
||||
|
||||
def min = 2500
|
||||
def percent = ((Integer.parseInt(value, 16) - min) / 5)
|
||||
percent = Math.max(0, Math.min(percent, 100.0))
|
||||
result.value = Math.round(percent)
|
||||
|
||||
def descriptionText
|
||||
if (percent < 10) result.descriptionText = "${linkText} battery is getting low $percent %."
|
||||
else result.descriptionText = "${linkText} battery is ${result.value}%"
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
def resetHumidity(){
|
||||
def linkText = getLinkText(device)
|
||||
def minHumValue = 0
|
||||
def maxHumValue = 0
|
||||
sendEvent(name: 'minHum', value: minHumValue, unit: '%', descriptionText: "${linkText} min soil moisture reset to ${minHumValue}%")
|
||||
sendEvent(name: 'maxHum', value: maxHumValue, unit: '%', descriptionText: "${linkText} max soil moisture reset to ${maxHumValue}%")
|
||||
}
|
||||
|
||||
def setConfig(){
|
||||
def configInterval = 100
|
||||
if (interval != null) configInterval = interval
|
||||
sendEvent(name: 'configuration',value: configInterval, descriptionText: "Configuration initialized")
|
||||
}
|
||||
|
||||
//when device preferences are changed
|
||||
def updated(){
|
||||
log.debug "device updated"
|
||||
if (!device.latestValue('configuration')) configure()
|
||||
else{
|
||||
if (resetMinMax == true) resetHumidity()
|
||||
if (device.latestValue('configuration').toInteger() != interval && interval != null){
|
||||
sendEvent(name: 'configuration',value: 0, descriptionText: "Settings changed and will update at next report. Measure interval set to ${interval} mins")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//poll
|
||||
def poll() {
|
||||
log.debug "poll called"
|
||||
List cmds = []
|
||||
if (!device.latestValue('configuration')) cmds += configure()
|
||||
else if (device.latestValue('configuration').toInteger() != interval && interval != null) {
|
||||
cmds += intervalUpdate()
|
||||
}
|
||||
//cmds += refresh()
|
||||
log.debug "commands $cmds"
|
||||
return cmds?.collect { new physicalgraph.device.HubAction(it) }
|
||||
}
|
||||
|
||||
//update intervals
|
||||
def intervalUpdate(){
|
||||
log.debug "intervalUpdate"
|
||||
def minReport = 10
|
||||
def maxReport = 610
|
||||
if (interval != null) {
|
||||
minReport = interval
|
||||
maxReport = interval * 61
|
||||
}
|
||||
[
|
||||
"zcl global send-me-a-report 0x405 0x0000 0x21 $minReport $maxReport {6400}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
"zcl global send-me-a-report 1 0x0000 0x21 0x0C 0 {0500}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
]
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "refresh"
|
||||
[
|
||||
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 1 0x405 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} 1 1 0"
|
||||
]
|
||||
}
|
||||
|
||||
//configure
|
||||
def configure() {
|
||||
//set minReport = measurement in minutes
|
||||
def minReport = 10
|
||||
def maxReport = 610
|
||||
|
||||
//String zigbeeId = swapEndianHex(device.hub.zigbeeId)
|
||||
//log.debug "zigbeeid ${device.zigbeeId} deviceId ${device.deviceNetworkId}"
|
||||
if (!device.zigbeeId) sendEvent(name: 'configuration',value: 0, descriptionText: "Device Zigbee Id not found, remove and attempt to rejoin device")
|
||||
else sendEvent(name: 'configuration',value: 100, descriptionText: "Configuration initialized")
|
||||
//log.debug "Configuring Reporting and Bindings. min: $minReport max: $maxReport "
|
||||
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x402 {${device.zigbeeId}} {}", "delay 500",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x405 {${device.zigbeeId}} {}", "delay 500",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}", "delay 1000",
|
||||
|
||||
//temperature
|
||||
"zcl global send-me-a-report 0x402 0x0000 0x29 1 0 {3200}",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
|
||||
//min = soil measure interval
|
||||
"zcl global send-me-a-report 0x405 0x0000 0x21 $minReport $maxReport {6400}",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
|
||||
//min = battery measure interval 1 = 1 hour
|
||||
"zcl global send-me-a-report 1 0x0000 0x21 0x0C 0 {0500}",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
||||
] + refresh()
|
||||
}
|
||||
|
||||
private hex(value) {
|
||||
new BigInteger(Math.round(value).toString()).toString(16)
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
int i = 0;
|
||||
int j = array.length - 1;
|
||||
byte tmp;
|
||||
while (j > i) {
|
||||
tmp = array[j];
|
||||
array[j] = array[i];
|
||||
array[i] = tmp;
|
||||
j--;
|
||||
i++;
|
||||
}
|
||||
return array
|
||||
}
|
||||
@@ -80,19 +80,12 @@ def parse(String description) {
|
||||
if (cmd) {
|
||||
result = zwaveEvent(cmd)
|
||||
}
|
||||
// log.debug "Parsed ${description.inspect()} to ${result.inspect()}"
|
||||
log.debug "Parsed ${description.inspect()} to ${result.inspect()}"
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.multiinstancev1.MultiInstanceCmdEncap cmd) {
|
||||
def encapsulated = null
|
||||
if (cmd.respondsTo("encapsulatedCommand")) {
|
||||
encapsulated = cmd.encapsulatedCommand()
|
||||
} else {
|
||||
def hex1 = { n -> String.format("%02X", n) }
|
||||
def sorry = "command: ${hex1(cmd.commandClass)}${hex1(cmd.command)}, payload: " + cmd.parameter.collect{ hex1(it) }.join(" ")
|
||||
encapsulated = zwave.parse(sorry, [0x31: 1, 0x84: 2, 0x60: 1, 0x85: 1, 0x70: 1])
|
||||
}
|
||||
def encapsulated = cmd.encapsulatedCommand([0x31: 1, 0x84: 2, 0x60: 1, 0x85: 1, 0x70: 1])
|
||||
return encapsulated ? zwaveEvent(encapsulated) : null
|
||||
}
|
||||
|
||||
|
||||
@@ -441,28 +441,28 @@ def configure() {
|
||||
"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}", //checkin time 6 hrs
|
||||
"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}",
|
||||
"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}",
|
||||
"zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}",
|
||||
"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}",
|
||||
"zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {01}",
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {01}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
|
||||
"zcl mfg-code ${manufacturerCode}",
|
||||
"zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {01}",
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {01}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
|
||||
"zcl mfg-code ${manufacturerCode}",
|
||||
"zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {01}",
|
||||
"zcl mfg-code ${manufacturerCode}", "delay 200",
|
||||
"zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {01}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
|
||||
]
|
||||
|
||||
@@ -481,17 +481,12 @@ def enrollResponse() {
|
||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
//Enroll Response
|
||||
"raw 0x500 {01 23 00 00 00}",
|
||||
"raw 0x500 {01 23 00 00 00}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
|
||||
]
|
||||
}
|
||||
|
||||
private Map parseAxis(String description) {
|
||||
def hexToSignedInt = { hexVal ->
|
||||
def unsignedVal = hexToInt(hexVal)
|
||||
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
|
||||
}
|
||||
|
||||
def z = hexToSignedInt(description[0..3])
|
||||
def y = hexToSignedInt(description[10..13])
|
||||
def x = hexToSignedInt(description[20..23])
|
||||
@@ -518,6 +513,11 @@ private Map parseAxis(String description) {
|
||||
getXyzResult(xyzResults, description)
|
||||
}
|
||||
|
||||
private hexToSignedInt(hexVal) {
|
||||
def unsignedVal = hexToInt(hexVal)
|
||||
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
|
||||
}
|
||||
|
||||
def garageEvent(zValue) {
|
||||
def absValue = zValue.abs()
|
||||
def contactValue = null
|
||||
|
||||
@@ -275,6 +275,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
|
||||
case 32:
|
||||
map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", isStateChange: true ]
|
||||
allCodesDeleted()
|
||||
break
|
||||
case 33:
|
||||
map = [ name: "codeReport", value: cmd.alarmLevel, data: [ code: "" ], isStateChange: true ]
|
||||
map.descriptionText = "$device.displayName code $cmd.alarmLevel was deleted"
|
||||
@@ -341,14 +342,14 @@ def zwaveEvent(UserCodeReport cmd) {
|
||||
map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: code ] ]
|
||||
map.descriptionText = "$device.displayName code $cmd.userIdentifier is set"
|
||||
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
|
||||
map.isStateChange = (code != decrypt(state[name]))
|
||||
map.isStateChange = true
|
||||
}
|
||||
result << createEvent(map)
|
||||
} else {
|
||||
map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: "" ] ]
|
||||
if (state.blankcodes && state["reset$name"]) { // we deleted this code so we can tell that our new code gets set
|
||||
map.descriptionText = "$device.displayName code $cmd.userIdentifier was reset"
|
||||
map.displayed = map.isStateChange = false
|
||||
map.displayed = map.isStateChange = true
|
||||
result << createEvent(map)
|
||||
state["set$name"] = state["reset$name"]
|
||||
result << response(setCode(cmd.userIdentifier, state["reset$name"]))
|
||||
@@ -360,7 +361,7 @@ def zwaveEvent(UserCodeReport cmd) {
|
||||
map.descriptionText = "$device.displayName code $cmd.userIdentifier is not set"
|
||||
}
|
||||
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
|
||||
map.isStateChange = state[name] as Boolean
|
||||
map.isStateChange = true
|
||||
result << createEvent(map)
|
||||
}
|
||||
code = ""
|
||||
|
||||
@@ -0,0 +1,970 @@
|
||||
/**
|
||||
* Total Comfort API
|
||||
*
|
||||
* Based on Code by Eric Thomas
|
||||
*
|
||||
* 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
|
||||
* lgk v 3 added optional outdoor temp sensors and preferences for it, also made api login required.
|
||||
* 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.
|
||||
* lgk version 4 supports celsius and fahrenheit with option, and now colors.
|
||||
* lgk version 5, due to intermittant update failures added last update date/time tile so that you can see when it happended
|
||||
* not there is a new input tzoffset which defaults to my time ie -5 which you must set .
|
||||
* lgk version 6 add support for actually knowing the fan is on or not (added tile),
|
||||
* and also the actual operating state ie heating,cooling or idle via new response variables.
|
||||
* lgk version 7, change the new operating state to be a value vs standard tile
|
||||
* to work around a bug smartthings caused in the latest 2.08 release with text wrapping.
|
||||
* related also added icons to the operating state, and increase the width of the last update
|
||||
* to avoid wrapping.
|
||||
* 2-14-16 llb added full path to icons so the show on android Fire Tablet
|
||||
* 2-14-16 llb added check for indoor humidity sensor to avoid 128 value if not supported
|
||||
* 2-14-16 llb modified operating state tile to show control by Hold or Schedule
|
||||
*
|
||||
*/
|
||||
preferences {
|
||||
input("username", "text", title: "Username", description: "Your Total Comfort User Name", required: true)
|
||||
input("password", "password", title: "Password", description: "Your Total Comfort password",required: true)
|
||||
input("honeywelldevice", "text", title: "Device ID", description: "Your Device ID", required: true)
|
||||
input ("enableOutdoorTemps", "enum", title: "Do you have the optional outdoor temperature sensor and want to enable it?", options: ["Yes", "No"], required: false, defaultValue: "No")
|
||||
input ("tempScale", "enum", title: "Fahrenheit or Celsius?", options: ["F", "C"], required: false, defaultValue: "F")
|
||||
input("tzOffset", "number", title: "Time zone offset +/-xx?", required: false, defaultValue: -5, description: "Time Zone Offset ie -5.")
|
||||
}
|
||||
|
||||
metadata {
|
||||
definition (name: "Total Comfort API", namespace:
|
||||
"Total Comfort API", author: "Eric Thomas, modified lg kahn") {
|
||||
capability "Polling"
|
||||
capability "Thermostat"
|
||||
capability "Refresh"
|
||||
capability "Temperature Measurement"
|
||||
capability "Sensor"
|
||||
capability "Relative Humidity Measurement"
|
||||
command "heatLevelUp"
|
||||
command "heatLevelDown"
|
||||
command "coolLevelUp"
|
||||
command "coolLevelDown"
|
||||
attribute "outdoorHumidity", "number"
|
||||
attribute "outdoorTemperature", "number"
|
||||
attribute "lastUpdate", "string"
|
||||
|
||||
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
}
|
||||
|
||||
tiles {
|
||||
valueTile("temperature", "device.temperature", width: 2, height: 2, canChangeIcon: true) {
|
||||
state("temperature", label: '${currentValue}°',
|
||||
icon: "http://cdn.device-icons.smartthings.com/Weather/weather2-icn@3x.png",
|
||||
unit:"F", backgroundColors: [
|
||||
[value: -14, color: "#1e9cbb"],
|
||||
[value: -10, color: "#90d2a7"],
|
||||
[value: -5, color: "#44b621"],
|
||||
[value: -2, color: "#f1d801"],
|
||||
[value: 0, color: "#153591"],
|
||||
[value: 7, color: "#1e9cbb"],
|
||||
[value: 15, color: "#90d2a7"],
|
||||
[value: 23, color: "#44b621"],
|
||||
[value: 29, color: "#f1d801"],
|
||||
[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("thermostatMode", "device.thermostatMode", inactiveLabel: false, canChangeIcon: true) {
|
||||
state "off", label:'${name}', action:"thermostat.cool", icon: "http://cdn.device-icons.smartthings.com/Outdoor/outdoor19.png"
|
||||
state "cool", label:'${name}', action:"thermostat.heat", icon: "http://cdn.device-icons.smartthings.com/Weather/weather7.png", backgroundColor: '#1e9cbb'
|
||||
state "heat", label:'${name}', action:"thermostat.auto", icon: "http://cdn.device-icons.smartthings.com/Weather/weather14.png", backgroundColor: '#E14902'
|
||||
state "auto", label:'${name}', action:"thermostat.off", icon: "http://cdn.device-icons.smartthings.com/Weather/weather3.png", backgroundColor: '#44b621'
|
||||
}
|
||||
standardTile("thermostatFanMode", "device.thermostatFanMode", inactiveLabel: false, canChangeIcon: true) {
|
||||
state "auto", label:'${name}', action:"thermostat.fanAuto", icon: "http://cdn.device-icons.smartthings.com/Appliances/appliances11.png", backgroundColor: '#44b621'
|
||||
state "circulate", label:'${name}', action:"thermostat.fanCirculate", icon: "http://cdn.device-icons.smartthings.com/Appliances/appliances11.png", backgroundColor: '#44b621'
|
||||
state "on", label:'${name}', action:"thermostat.fanOn", icon: "http://cdn.device-icons.smartthings.com/Appliances/appliances11.png", backgroundColor: '#44b621'
|
||||
}
|
||||
|
||||
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 3, width: 1, inactiveLabel: false) {
|
||||
state "setCoolingSetpoint", label:'Set temperarure to', action:"thermostat.setCoolingSetpoint",
|
||||
backgroundColors:[
|
||||
[value: 0, color: "#153591"],
|
||||
[value: 7, color: "#1e9cbb"],
|
||||
[value: 15, color: "#90d2a7"],
|
||||
[value: 23, color: "#44b621"],
|
||||
[value: 29, color: "#f1d801"],
|
||||
[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"]
|
||||
]
|
||||
}
|
||||
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false)
|
||||
{
|
||||
state "default", label:'Cool\n${currentValue}°', unit:"F",
|
||||
backgroundColors: [
|
||||
[value: 0, color: "#153591"],
|
||||
[value: 7, color: "#1e9cbb"],
|
||||
[value: 15, color: "#90d2a7"],
|
||||
[value: 23, color: "#44b621"],
|
||||
[value: 29, color: "#f1d801"],
|
||||
[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"]
|
||||
]
|
||||
}
|
||||
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false)
|
||||
{
|
||||
state "default", label:'Heat\n${currentValue}°', unit: "F",
|
||||
backgroundColors:[
|
||||
[value: 0, color: "#153591"],
|
||||
[value: 7, color: "#1e9cbb"],
|
||||
[value: 15, color: "#90d2a7"],
|
||||
[value: 23, color: "#44b621"],
|
||||
[value: 29, color: "#f1d801"],
|
||||
[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"]
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
//tile added for operating state - Create the tiles for each possible state, look at other examples if you wish to change the icons here.
|
||||
|
||||
valueTile("thermostatOperatingState", "device.thermostatOperatingState", inactiveLabel: false) {
|
||||
state 'default', label:'${currentValue}',
|
||||
backgroundColors:[
|
||||
[value: 0, color: "#911535"]]
|
||||
state "Unknown", label:'${name}', backgroundColor : '#cc0000', icon: ""
|
||||
}
|
||||
|
||||
standardTile("fanOperatingState", "device.fanOperatingState", inactiveLabel: false) {
|
||||
state "On", label:'${name}',icon: "http://cdn.device-icons.smartthings.com/Appliances/appliances11.png", backgroundColor : '#53a7c0'
|
||||
state "Idle", label:'${name}',icon: "http://cdn.device-icons.smartthings.com/Appliances/appliances11.png"
|
||||
state "Unknown", label:'${name}',icon: "http://cdn.device-icons.smartthings.com/Appliances/appliances11.png", backgroundColor : '#cc0000'
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", action:"polling.poll", icon:"http://cdn.device-icons.smartthings.com/secondary/refresh@2x.png"
|
||||
}
|
||||
|
||||
standardTile("heatLevelUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false) {
|
||||
state "heatLevelUp", label:' ', action:"heatLevelUp", icon:"http://cdn.device-icons.smartthings.com/thermostat/thermostat-up.png"
|
||||
}
|
||||
standardTile("heatLevelDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false) {
|
||||
state "heatLevelDown", label:' ', action:"heatLevelDown", icon:"http://cdn.device-icons.smartthings.com/thermostat/thermostat-down.png"
|
||||
}
|
||||
standardTile("coolLevelUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false) {
|
||||
state "coolLevelUp", label:' ', action:"coolLevelUp", icon:"http://cdn.device-icons.smartthings.com/thermostat/thermostat-up.png"
|
||||
}
|
||||
standardTile("coolLevelDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false) {
|
||||
state "coolLevelDown", label:' ', action:"coolLevelDown", icon:"http://cdn.device-icons.smartthings.com/thermostat/thermostat-down.png"
|
||||
}
|
||||
|
||||
valueTile("relativeHumidity", "device.relativeHumidity", inactiveLabel: false)
|
||||
{
|
||||
state "default", label:'Humidity\n${currentValue}%',
|
||||
icon: "http://cdn.device-icons.smartthings.com/Weather/weather12-icn@3x.png",
|
||||
unit:"%", backgroundColors : [
|
||||
[value: 01, color: "#724529"],
|
||||
[value: 11, color: "#724529"],
|
||||
[value: 21, color: "#724529"],
|
||||
[value: 35, color: "#44b621"],
|
||||
[value: 49, color: "#44b621"],
|
||||
[value: 50, color: "#1e9cbb"]
|
||||
]
|
||||
}
|
||||
|
||||
standardTile("thermostatMode", "device.thermostatMode", inactiveLabel: false, canChangeIcon: true)
|
||||
{
|
||||
}
|
||||
|
||||
/* lgk new tiles for outside temp and hummidity */
|
||||
valueTile("outdoorTemperature", "device.outdoorTemperature", width: 1, height: 1, canChangeIcon: true) {
|
||||
state("temperature", label: 'Outdoor\n ${currentValue}°',
|
||||
icon: "http://cdn.device-icons.smartthings.com/Weather/weather2-icn@3x.png",
|
||||
unit:"F", backgroundColors: [
|
||||
[value: -31, color: "#003591"],
|
||||
[value: -10, color: "#90d2a7"],
|
||||
[value: -5, color: "#44b621"],
|
||||
[value: -2, color: "#f1d801"],
|
||||
[value: 0, color: "#153591"],
|
||||
[value: 7, color: "#1e9cbb"],
|
||||
[value: 00, color: "#cccccc"],
|
||||
[value: 31, color: "#153500"],
|
||||
[value: 44, color: "#1e9cbb"],
|
||||
[value: 59, color: "#90d2a7"],
|
||||
[value: 74, color: "#44b621"],
|
||||
[value: 84, color: "#f1d801"],
|
||||
[value: 95, color: "#d04e00"],
|
||||
[value: 96, color: "#bc2323"]
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
valueTile("outdoorHumidity", "device.outdoorHumidity", inactiveLabel: false){
|
||||
state "default", label:'Outdoor\n ${currentValue}%',
|
||||
icon: "http://cdn.device-icons.smartthings.com/Weather/weather12-icn@3x.png",
|
||||
unit:"%", backgroundColors : [
|
||||
[value: 01, color: "#724529"],
|
||||
[value: 11, color: "#724529"],
|
||||
[value: 21, color: "#724529"],
|
||||
[value: 35, color: "#44b621"],
|
||||
[value: 49, color: "#44b621"],
|
||||
[value: 70, color: "#449c00"],
|
||||
[value: 90, color: "#009cbb"]
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
valueTile("status", "device.lastUpdate", width: 3, height: 1, decoration: "flat") {
|
||||
state "default", label: 'Last Update: ${currentValue}'
|
||||
}
|
||||
|
||||
main "temperature"
|
||||
details(["temperature", "thermostatMode", "thermostatFanMode",
|
||||
"heatLevelUp", "heatingSetpoint" , "heatLevelDown", "coolLevelUp",
|
||||
"coolingSetpoint", "coolLevelDown" ,"thermostatOperatingState","fanOperatingState",
|
||||
"refresh","relativeHumidity","outdoorTemperature","outdoorHumidity", "status"])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def coolLevelUp()
|
||||
{
|
||||
state.DisplayUnits = settings.tempScale
|
||||
if (state.DisplayUnits == "F")
|
||||
{
|
||||
int nextLevel = device.currentValue("coolingSetpoint") + 1
|
||||
|
||||
if( nextLevel > 99){
|
||||
nextLevel = 99
|
||||
}
|
||||
log.debug "Setting cool set point up to: ${nextLevel}"
|
||||
setCoolingSetpoint(nextLevel)
|
||||
}
|
||||
else
|
||||
{
|
||||
int nextLevel = device.currentValue("coolingSetpoint") + 0.5
|
||||
|
||||
if( nextLevel > 37){
|
||||
nextLevel = 37
|
||||
}
|
||||
log.debug "Setting cool set point up to: ${nextLevel}"
|
||||
setCoolingSetpoint(nextLevel)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def coolLevelDown()
|
||||
{
|
||||
state.DisplayUnits = settings.tempScale
|
||||
if (state.DisplayUnits == "F")
|
||||
{
|
||||
int nextLevel = device.currentValue("coolingSetpoint") - 1
|
||||
|
||||
if( nextLevel < 50){
|
||||
nextLevel = 50
|
||||
}
|
||||
log.debug "Setting cool set point down to: ${nextLevel}"
|
||||
setCoolingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
double nextLevel = device.currentValue("coolingSetpoint") - 0.5
|
||||
|
||||
if( nextLevel < 10){
|
||||
nextLevel = 10
|
||||
}
|
||||
log.debug "Setting cool set point down to: ${nextLevel}"
|
||||
setCoolingSetpoint(nextLevel)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def heatLevelUp()
|
||||
{
|
||||
state.DisplayUnits = settings.tempScale
|
||||
if (state.DisplayUnits == "F")
|
||||
{
|
||||
log.debug "in fahrenheit level up"
|
||||
int nextLevel = device.currentValue("heatingSetpoint") + 1
|
||||
|
||||
if( nextLevel > 90){
|
||||
nextLevel = 90
|
||||
}
|
||||
log.debug "Setting heat set point up to: ${nextLevel}"
|
||||
setHeatingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
log.debug "in celsius level uo"
|
||||
double nextLevel = device.currentValue("heatingSetpoint") + 0.5
|
||||
|
||||
if( nextLevel > 33){
|
||||
nextLevel = 33
|
||||
}
|
||||
log.debug "Setting heat set point up to: ${nextLevel}"
|
||||
setHeatingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
def heatLevelDown()
|
||||
{
|
||||
state.DisplayUnits = settings.tempScale
|
||||
if (state.DisplayUnits == "F")
|
||||
{
|
||||
log.debug "in fahrenheit level down"
|
||||
int nextLevel = device.currentValue("heatingSetpoint") - 1
|
||||
|
||||
if( nextLevel < 40){
|
||||
nextLevel = 40
|
||||
}
|
||||
log.debug "Setting heat set point down to: ${nextLevel}"
|
||||
setHeatingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
log.debug "in celsius level down"
|
||||
double nextLevel = device.currentValue("heatingSetpoint") - 0.5
|
||||
|
||||
if( nextLevel < 4){
|
||||
nextLevel = 4
|
||||
}
|
||||
log.debug "Setting heat set point down to: ${nextLevel}"
|
||||
setHeatingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
|
||||
}
|
||||
|
||||
// handle commands
|
||||
|
||||
def setHeatingSetpoint(Double temp)
|
||||
{
|
||||
data.SystemSwitch = 'null'
|
||||
data.HeatSetpoint = temp
|
||||
data.CoolSetpoint = 'null'
|
||||
data.HeatNextPeriod = 'null'
|
||||
data.CoolNextPeriod = 'null'
|
||||
data.StatusHeat='1'
|
||||
data.StatusCool='1'
|
||||
data.FanMode = 'null'
|
||||
setStatus()
|
||||
|
||||
if(data.SetStatus==1)
|
||||
{
|
||||
sendEvent(name: 'heatingSetpoint', value: temp as double)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(temp) {
|
||||
data.SystemSwitch = 'null'
|
||||
data.HeatSetpoint = temp
|
||||
data.CoolSetpoint = 'null'
|
||||
data.HeatNextPeriod = 'null'
|
||||
data.CoolNextPeriod = 'null'
|
||||
data.StatusHeat='1'
|
||||
data.StatusCool='1'
|
||||
data.FanMode = 'null'
|
||||
setStatus()
|
||||
|
||||
if(data.SetStatus==1)
|
||||
{
|
||||
sendEvent(name: 'heatingSetpoint', value: temp as Integer)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(double temp) {
|
||||
data.SystemSwitch = 'null'
|
||||
data.HeatSetpoint = 'null'
|
||||
data.CoolSetpoint = temp
|
||||
data.HeatNextPeriod = 'null'
|
||||
data.CoolNextPeriod = 'null'
|
||||
data.StatusHeat='1'
|
||||
data.StatusCool='1'
|
||||
data.FanMode = 'null'
|
||||
setStatus()
|
||||
|
||||
if(data.SetStatus==1)
|
||||
{
|
||||
sendEvent(name: 'coolingSetpoint', value: temp as double)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(temp) {
|
||||
data.SystemSwitch = 'null'
|
||||
data.HeatSetpoint = 'null'
|
||||
data.CoolSetpoint = temp
|
||||
data.HeatNextPeriod = 'null'
|
||||
data.CoolNextPeriod = 'null'
|
||||
data.StatusHeat='1'
|
||||
data.StatusCool='1'
|
||||
data.FanMode = 'null'
|
||||
setStatus()
|
||||
|
||||
if(data.SetStatus==1)
|
||||
{
|
||||
sendEvent(name: 'coolingSetpoint', value: temp as Integer)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def setTargetTemp(temp) {
|
||||
data.SystemSwitch = 'null'
|
||||
data.HeatSetpoint = temp
|
||||
data.CoolSetpoint = temp
|
||||
data.HeatNextPeriod = 'null'
|
||||
data.CoolNextPeriod = 'null'
|
||||
data.StatusHeat='1'
|
||||
data.StatusCool='1'
|
||||
data.FanMode = 'null'
|
||||
setStatus()
|
||||
}
|
||||
|
||||
|
||||
|
||||
def setTargetTemp(double temp) {
|
||||
data.SystemSwitch = 'null'
|
||||
data.HeatSetpoint = temp
|
||||
data.CoolSetpoint = temp
|
||||
data.HeatNextPeriod = 'null'
|
||||
data.CoolNextPeriod = 'null'
|
||||
data.StatusHeat='1'
|
||||
data.StatusCool='1'
|
||||
data.FanMode = 'null'
|
||||
setStatus()
|
||||
}
|
||||
|
||||
|
||||
def off() {
|
||||
setThermostatMode(2)
|
||||
}
|
||||
|
||||
def auto() {
|
||||
setThermostatMode(4)
|
||||
}
|
||||
|
||||
def heat() {
|
||||
setThermostatMode(1)
|
||||
}
|
||||
|
||||
def emergencyHeat() {
|
||||
|
||||
}
|
||||
|
||||
def cool() {
|
||||
setThermostatMode(3)
|
||||
}
|
||||
|
||||
def setThermostatMode(mode) {
|
||||
data.SystemSwitch = mode
|
||||
data.HeatSetpoint = 'null'
|
||||
data.CoolSetpoint = 'null'
|
||||
data.HeatNextPeriod = 'null'
|
||||
data.CoolNextPeriod = 'null'
|
||||
data.StatusHeat=1
|
||||
data.StatusCool=1
|
||||
data.FanMode = 'null'
|
||||
|
||||
setStatus()
|
||||
|
||||
def switchPos
|
||||
|
||||
if(mode==1)
|
||||
switchPos = 'heat'
|
||||
if(mode==2)
|
||||
switchPos = 'off'
|
||||
if(mode==3)
|
||||
switchPos = 'cool'
|
||||
/* lgk modified my therm has pos 5 for auto vision pro */
|
||||
if(mode==4 || swithPos == 5)
|
||||
switchPos = 'auto'
|
||||
|
||||
if(data.SetStatus==1)
|
||||
{
|
||||
sendEvent(name: 'thermostatMode', value: switchPos)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def fanOn() {
|
||||
setThermostatFanMode(1)
|
||||
}
|
||||
|
||||
def fanAuto() {
|
||||
setThermostatFanMode(0)
|
||||
}
|
||||
|
||||
def fanCirculate() {
|
||||
setThermostatFanMode(2)
|
||||
}
|
||||
|
||||
def setThermostatFanMode(mode) {
|
||||
|
||||
data.SystemSwitch = 'null'
|
||||
data.HeatSetpoint = 'null'
|
||||
data.CoolSetpoint = 'null'
|
||||
data.HeatNextPeriod = 'null'
|
||||
data.CoolNextPeriod = 'null'
|
||||
data.StatusHeat='null'
|
||||
data.StatusCool='null'
|
||||
data.FanMode = mode
|
||||
|
||||
setStatus()
|
||||
|
||||
def fanMode
|
||||
|
||||
if(mode==0)
|
||||
fanMode = 'auto'
|
||||
if(mode==1)
|
||||
fanMode = 'on'
|
||||
if(mode==2)
|
||||
fanMode = 'circulate'
|
||||
|
||||
if(data.SetStatus==1)
|
||||
{
|
||||
sendEvent(name: 'thermostatFanMode', value: fanMode)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
def poll() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
|
||||
def setStatus() {
|
||||
|
||||
data.SetStatus = 0
|
||||
|
||||
login()
|
||||
log.debug "Executing 'setStatus'"
|
||||
def today= new Date()
|
||||
log.debug "https://www.mytotalconnectcomfort.com/portal/Device/SubmitControlScreenChanges"
|
||||
log.debug "setting heat setpoint to $data.HeatSetpoint"
|
||||
log.debug "setting cool setpoint to $data.CoolSetpoint"
|
||||
|
||||
def params = [
|
||||
uri: "https://www.mytotalconnectcomfort.com/portal/Device/SubmitControlScreenChanges",
|
||||
headers: [
|
||||
'Accept': 'application/json, text/javascript, */*; q=0.01',
|
||||
'DNT': '1',
|
||||
'Accept-Encoding': 'gzip,deflate,sdch',
|
||||
'Cache-Control': 'max-age=0',
|
||||
'Accept-Language': 'en-US,en,q=0.8',
|
||||
'Connection': 'keep-alive',
|
||||
'Host': 'rs.alarmnet.com',
|
||||
'Referer': "https://www.mytotalconnectcomfort.com/portal/Device/Control/${settings.honeywelldevice}",
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36',
|
||||
'Cookie': data.cookiess ],
|
||||
body: [ DeviceID: "${settings.honeywelldevice}", SystemSwitch : data.SystemSwitch ,HeatSetpoint :
|
||||
data.HeatSetpoint, CoolSetpoint: data.CoolSetpoint, HeatNextPeriod:
|
||||
data.HeatNextPeriod,CoolNextPeriod:data.CoolNextPeriod,StatusHeat:data.StatusHeat,
|
||||
StatusCool:data.StatusCool,FanMode:data.FanMode,ThermostatUnits: settings.tempScale]
|
||||
|
||||
]
|
||||
|
||||
log.debug "params = $params"
|
||||
httpPost(params) { response ->
|
||||
log.debug "Request was successful, $response.status"
|
||||
|
||||
}
|
||||
|
||||
log.debug "SetStatus is 1 now"
|
||||
data.SetStatus = 1
|
||||
|
||||
}
|
||||
|
||||
def getStatus() {
|
||||
log.debug "Executing getStatus"
|
||||
log.debug "enable outside temps = $enableOutdoorTemps"
|
||||
def today= new Date()
|
||||
log.debug "https://www.mytotalconnectcomfort.com/portal/Device/CheckDataSession/${settings.honeywelldevice}?_=$today.time"
|
||||
|
||||
def params = [
|
||||
uri: "https://www.mytotalconnectcomfort.com/portal/Device/CheckDataSession/${settings.honeywelldevice}",
|
||||
headers: [
|
||||
'Accept': '*/*',
|
||||
'DNT': '1',
|
||||
'Cache' : 'false',
|
||||
'dataType': 'json',
|
||||
'Accept-Encoding': 'plain',
|
||||
'Cache-Control': 'max-age=0',
|
||||
'Accept-Language': 'en-US,en,q=0.8',
|
||||
'Connection': 'keep-alive',
|
||||
'Referer': 'https://www.mytotalconnectcomfort.com/portal',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36',
|
||||
'Cookie': data.cookiess ],
|
||||
]
|
||||
|
||||
log.debug "doing request"
|
||||
|
||||
httpGet(params) { response ->
|
||||
log.debug "Request was successful, $response.status"
|
||||
//log.debug "data = $response.data"
|
||||
log.debug "ld = $response.data.latestData"
|
||||
|
||||
def curTemp = response.data.latestData.uiData.DispTemperature
|
||||
def fanMode = response.data.latestData.fanData.fanMode
|
||||
def switchPos = response.data.latestData.uiData.SystemSwitchPosition
|
||||
def coolSetPoint = response.data.latestData.uiData.CoolSetpoint
|
||||
def heatSetPoint = response.data.latestData.uiData.HeatSetpoint
|
||||
def statusCool = response.data.latestData.uiData.StatusCool
|
||||
def statusHeat = response.data.latestData.uiData.StatusHeat
|
||||
def curHumidity = response.data.latestData.uiData.IndoorHumidity
|
||||
def Boolean hasOutdoorHumid = response.data.latestData.uiData.OutdoorHumidityAvailable
|
||||
def Boolean hasOutdoorTemp = response.data.latestData.uiData.OutdoorTemperatureAvailable
|
||||
def curOutdoorHumidity = response.data.latestData.uiData.OutdoorHumidity
|
||||
def curOutdoorTemp = response.data.latestData.uiData.OutdoorTemperature
|
||||
def displayUnits = response.data.latestData.uiData.DisplayUnits
|
||||
def fanIsRunning = response.data.latestData.fanData.fanIsRunning
|
||||
def equipmentStatus = response.data.latestData.uiData.EquipmentOutputStatus
|
||||
def Boolean hasIndoorHumid = response.data.latestData.uiData.IndoorHumiditySensorAvailable
|
||||
def curSetpointStatus = response.data.latestData.uiData.CurrentSetpointStatus
|
||||
def TempHoldTime = response.data.latestData.uiData.TemporaryHoldUntilTime
|
||||
def int TempHoldTimeHrs = TempHoldTime / 60
|
||||
def TempHoldTimeMins = TempHoldTime % 60
|
||||
|
||||
/*ld = [fanData:[fanModeCirculateAllowed:true, fanModeAutoAllowed:true, fanModeFollowScheduleAllowed:false,
|
||||
fanIsRunning:false, fanModeOnAllowed:true, fanMode:0],
|
||||
drData:[Load:127.5, HeatSetpLimit:0,
|
||||
OptOutable:false, DeltaHeatSP:-0.01, CoolSetpLimit:0, Phase:-1, DeltaCoolSP:-0.01],
|
||||
uiData:[OutdoorTemperature:128.0000, TemporaryHoldUntilTime:0, ScheduleHeatSp:67.0000,
|
||||
DeviceID:453824, DispTemperatureAvailable:true, VacationHold:0, VacationHoldUntilTime:0,
|
||||
CoolSetpoint:76.0000, ScheduleCoolSp:76.0000, SwitchHeatAllowed:true, CoolNextPeriod:67,
|
||||
IndoorHumidity:31.0000, SwitchAutoAllowed:true, SetpointChangeAllowed:true, HeatLowerSetptLimit:40.0000,
|
||||
OutdoorHumidStatus:128, SwitchOffAllowed:true, OutdoorHumidityAvailable:false,
|
||||
StatusCool:0, OutdoorTemperatureAvailable:false, EquipmentOutputStatus:0, StatusHeat:0,
|
||||
CurrentSetpointStatus:0, HoldUntilCapable:true, CoolUpperSetptLimit:99.0000, SwitchCoolAllowed:true,
|
||||
OutdoorHumidity:128.0000, DualSetpointStatus:false, SwitchEmergencyHeatAllowed:false, Commercial:false,
|
||||
CoolLowerSetptLimit:50.0000, OutdoorHumiditySensorNotFault:true, IndoorHumiditySensorAvailable:true,
|
||||
ScheduleCapable:true, DisplayUnits:F, DispTemperature:70.0000, Deadband:3.0000, HeatUpperSetptLimit:90.0000,
|
||||
IsInVacationHoldMode:false, OutdoorTemperatureSensorNotFault:true, HeatSetpoint:67.0000, DispTemperatureStatus:0,
|
||||
HeatNextPeriod:67, IndoorHumiditySensorNotFault:true, OutdoorTempStatus:128, IndoorHumidStatus:0,
|
||||
SystemSwitchPosition:4], canControlHumidification:false, hasFan:true]
|
||||
|
||||
log.trace("Fan operating state: ${response.data.latestData.fanData.fanIsRunning}")
|
||||
log.trace("EquipmentOutputStatus: ${response.data.latestData.uiData.EquipmentOutputStatus}")
|
||||
log.trace("IndoorHumidity: ${response.data.latestData.uiData.IndoorHumidity}")
|
||||
|
||||
log.trace("OutdoorTemp = ${response.data.latestData.uiData.OutdoorTemperature}")
|
||||
log.trace("fanMode: ${response.data.latestData.fanData.fanMode}")
|
||||
log.trace("SystenSwitchPosition: ${response.data.latestData.uiData.SystemSwitchPosition}")
|
||||
log.trace("StatusCool: ${response.data.latestData.uiData.StatusCool}")
|
||||
log.trace("StatusHeat: ${response.data.latestData.uiData.StatusHeat}")
|
||||
|
||||
log.trace("IndoorHumiditySensorAvailable: ${response.data.latestData.uiData.IndoorHumiditySensorAvailable}")
|
||||
log.trace("IndoorHumidityAvailable: ${response.data.latestData.uiData.IndoorHumidityAvailable}")
|
||||
|
||||
log.debug "OutdoorHumidityAvailable: response.data.latestData.uiData.OutdoorHumidityAvailable"
|
||||
log.debug "OutdoorTemperatureAvailable: $response.data.latestData.uiData.OutdoorTemperatureAvailable"
|
||||
|
||||
log.debug "OutdoorHumiditySensorNotFault = $response.data.latestData.uiData.OutdoorHumiditySensorNotFault"
|
||||
log.debug "OutdoorTemperatureSensorNotFault = $response.data.latestData.uiData.OutdoorTemperatureSensorNotFault"
|
||||
|
||||
log.debug "IndoorHumiditySensorNotFault: $response.data.latestData.uiData.IndoorHumiditySensorNotFault"
|
||||
log.debug "IndoorHumidStatus: $response.data.latestData.uiData.IndoorHumidStatus"
|
||||
log.debug "OutdoorHumidStatus: $response.data.latestData.uiData.OutdoorHumidStatus"
|
||||
log.debug "OutdoorHumidity: = $response.data.latestData.uiData.OutdoorHumidity"
|
||||
log.debug "OutdoorTemperature = $response.data.latestData.uiData.OutdoorTemperature"
|
||||
|
||||
log.debug "got curOutdoorTemp = $curOutdoorTemp"
|
||||
log.debug "got curOutdoorHumidity = $curOutdoorHumidity"
|
||||
log.debug "hasOutdoorHumid = $hasOutdoorHumid"
|
||||
log.debug "hasOutdoorTemp = $hasOutdoorTemp"
|
||||
*/
|
||||
|
||||
// log.debug "displayUnits = $displayUnits"
|
||||
state.DisplayUnits = $displayUnits
|
||||
|
||||
//Operating State Section
|
||||
//Set the operating state to off
|
||||
|
||||
def operatingState = "Unknown"
|
||||
|
||||
// lgk operating state not working here.. shows both on ie 1 when heat doesnt go on to 67 and heat till 76 and current is 73
|
||||
//Check the status of heat and cool
|
||||
|
||||
// lgk old method now use equipment status
|
||||
if (equipmentStatus == 1) {
|
||||
operatingState = "HEAT ON"
|
||||
} else if (equipmentStatus == 2) {
|
||||
operatingState = "COOL ON"
|
||||
} else if (equipmentStatus == 0) {
|
||||
operatingState = "IDLE"
|
||||
} else {
|
||||
operatingState = "Unknown"
|
||||
}
|
||||
|
||||
if(curSetpointStatus == 0) {
|
||||
operatingState = "$operatingState\nControl By\nFollowing\nSchedule"
|
||||
} else if(curSetpointStatus == 1) {
|
||||
operatingState = "$operatingState\nControl By\nHold Until\n" + String.format("%02d:%02d", TempHoldTimeHrs, TempHoldTimeMins)
|
||||
} else if(curSetpointStatus == 2) {
|
||||
operatingState = "$operatingState\nControl By\nPermanent\nHold"
|
||||
|
||||
} else {
|
||||
operatingState = "unknown"
|
||||
}
|
||||
|
||||
/*
|
||||
if(statusCool == 1 && (switchPos == 3 || switchPos == 5 || swithPos == 4)) {
|
||||
operatingState = "cooling"
|
||||
} else if (statusHeat == 1 && (switchPos == 1 || switchPos == 5 || switchPos == 4)) {
|
||||
operatingState = "heating"
|
||||
} else if (statusCool == 0 && statusHeat == 0) {
|
||||
operatingState = "idle"
|
||||
|
||||
} else {
|
||||
operatingState = "unknown"
|
||||
}
|
||||
*/
|
||||
|
||||
log.trace("Set operating State to: ${operatingState}")
|
||||
|
||||
// set fast state
|
||||
def fanState = "Unknown"
|
||||
|
||||
if (fanIsRunning == true)
|
||||
fanState = "On"
|
||||
else fanState = "Idle"
|
||||
|
||||
log.trace("Set Fan operating State to: ${fanState}")
|
||||
|
||||
//End Operating State
|
||||
|
||||
// log.debug curTemp
|
||||
// log.debug fanMode
|
||||
// log.debug switchPos
|
||||
|
||||
//fan mode 0=auto, 2=circ, 1=on
|
||||
|
||||
if(fanMode==0)
|
||||
fanMode = 'auto'
|
||||
if(fanMode==1)
|
||||
fanMode = 'on'
|
||||
if(fanMode==2)
|
||||
fanMode = 'circulate'
|
||||
|
||||
if(switchPos==1)
|
||||
switchPos = 'heat'
|
||||
if(switchPos==2)
|
||||
switchPos = 'off'
|
||||
if(switchPos==3)
|
||||
switchPos = 'cool'
|
||||
if(switchPos==4 || switchPos==5)
|
||||
switchPos = 'auto'
|
||||
|
||||
def formattedCoolSetPoint = String.format("%5.1f", coolSetPoint)
|
||||
def formattedHeatSetPoint = String.format("%5.1f", heatSetPoint)
|
||||
def formattedTemp = String.format("%5.1f", curTemp)
|
||||
|
||||
def finalCoolSetPoint = formattedCoolSetPoint as BigDecimal
|
||||
def finalHeatSetPoint = formattedHeatSetPoint as BigDecimal
|
||||
def finalTemp = formattedTemp as BigDecimal
|
||||
|
||||
//Send events
|
||||
sendEvent(name: 'thermostatOperatingState', value: operatingState)
|
||||
sendEvent(name: 'fanOperatingState', value: fanState)
|
||||
sendEvent(name: 'thermostatFanMode', value: fanMode)
|
||||
sendEvent(name: 'thermostatMode', value: switchPos)
|
||||
sendEvent(name: 'coolingSetpoint', value: finalCoolSetPoint )
|
||||
sendEvent(name: 'heatingSetpoint', value: finalHeatSetPoint )
|
||||
sendEvent(name: 'temperature', value: finalTemp, state: switchPos)
|
||||
if (hasIndoorHumid){sendEvent(name: 'relativeHumidity', value: curHumidity as Integer)}
|
||||
|
||||
if (settings.tzOffset == null)
|
||||
settings.tzOffset = -5
|
||||
|
||||
def now = new Date()
|
||||
def tf = new java.text.SimpleDateFormat("MM/dd/yyyy h:mm a")
|
||||
tf.setTimeZone(TimeZone.getTimeZone("GMT${settings.tzOffset}"))
|
||||
def newtime = "${tf.format(now)}" as String
|
||||
sendEvent(name: "lastUpdate", value: newtime, descriptionText: "Last Update: $newtime")
|
||||
|
||||
|
||||
if (enableOutdoorTemps == "Yes")
|
||||
{
|
||||
|
||||
if (hasOutdoorHumid)
|
||||
{
|
||||
sendEvent(name: 'outdoorHumidity', value: curOutdoorHumidity as Integer)
|
||||
}
|
||||
|
||||
if (hasOutdoorTemp)
|
||||
{
|
||||
sendEvent(name: 'outdoorTemperature', value: curOutdoorTemp as Integer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def getHumidifierStatus()
|
||||
{
|
||||
/*
|
||||
$.ajax({
|
||||
url: humUrl,
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
/portal/Device/Menu/GetHumData/454832';
|
||||
*/
|
||||
def params = [
|
||||
uri: "https://www.mytotalconnectcomfort.com/portal/Device/Menu/GetHumData/${settings.honeywelldevice}",
|
||||
headers: [
|
||||
'Accept': '*/*',
|
||||
'DNT': '1',
|
||||
'dataType': 'json',
|
||||
'cache': 'false',
|
||||
'Accept-Encoding': 'plain',
|
||||
'Cache-Control': 'max-age=0',
|
||||
'Accept-Language': 'en-US,en,q=0.8',
|
||||
'Connection': 'keep-alive',
|
||||
'Host': 'rs.alarmnet.com',
|
||||
'Referer': 'https://www.mytotalconnectcomfort.com/portal/Menu/${settings.honeywelldevice}',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36',
|
||||
'Cookie': data.cookiess ],
|
||||
]
|
||||
httpGet(params) { response ->
|
||||
log.debug "GetHumidity Request was successful, $response.status"
|
||||
log.debug "response = $response.data"
|
||||
|
||||
// log.debug "ld = $response.data.latestData"
|
||||
// log.debug "humdata = $response.data.latestData.humData"
|
||||
|
||||
log.trace("lowerLimit: ${response.data.latestData.humData.lowerLimit}")
|
||||
log.trace("upperLimit: ${response.data.humData.upperLimit}")
|
||||
log.trace("SetPoint: ${response.data.humData.Setpoint}")
|
||||
log.trace("DeviceId: ${response.data.humData.DeviceId}")
|
||||
log.trace("IndoorHumidity: ${response.data.humData.IndoorHumidity}")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def api(method, args = [], success = {}) {
|
||||
|
||||
}
|
||||
|
||||
// Need to be logged in before this is called. So don't call this. Call api.
|
||||
def doRequest(uri, args, type, success) {
|
||||
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
def unit = getTemperatureScale()
|
||||
log.debug "units = $unit"
|
||||
login()
|
||||
//getHumidifierStatus()
|
||||
getStatus()
|
||||
}
|
||||
|
||||
def login() {
|
||||
log.debug "Executing 'login'"
|
||||
|
||||
def params = [
|
||||
uri: 'https://www.mytotalconnectcomfort.com/portal',
|
||||
headers: [
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'Accept-Encoding': 'sdch',
|
||||
'Host': 'www.mytotalconnectcomfort.com',
|
||||
'DNT': '1',
|
||||
'Origin': 'www.mytotalconnectcomfort.com/portal/',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36'
|
||||
],
|
||||
body: [timeOffset: '240', UserName: "${settings.username}", Password: "${settings.password}", RememberMe: 'false']
|
||||
]
|
||||
|
||||
data.cookiess = ''
|
||||
|
||||
httpPost(params) { response ->
|
||||
log.debug "Request was successful, $response.status"
|
||||
log.debug response.headers
|
||||
response.getHeaders('Set-Cookie').each {
|
||||
String cookie = it.value.split(';|,')[0]
|
||||
log.debug "Adding cookie to collection: $cookie"
|
||||
if(cookie != ".ASPXAUTH_TH_A=") {
|
||||
data.cookiess = data.cookiess+cookie+';'
|
||||
}
|
||||
}
|
||||
log.debug "cookies: $data.cookiess"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def isLoggedIn() {
|
||||
if(!data.auth) {
|
||||
log.debug "No data.auth"
|
||||
return false
|
||||
}
|
||||
|
||||
def now = new Date().getTime();
|
||||
return data.auth.expires_in > now
|
||||
}
|
||||
|
||||
|
||||
def updated()
|
||||
{
|
||||
log.debug "in updated"
|
||||
state.DisplayUnits = settings.tempScale
|
||||
log.debug "display units now = $state.DisplayUnits"
|
||||
|
||||
}
|
||||
|
||||
def installed() {
|
||||
state.DisplayUnits = settings.tempScale
|
||||
|
||||
log.debug "display units now = $state.DisplayUnits"
|
||||
}
|
||||
1617
smartapps/plaidsystems/spruce-scheduler.src/spruce-scheduler.groovy
Normal file
1617
smartapps/plaidsystems/spruce-scheduler.src/spruce-scheduler.groovy
Normal file
File diff suppressed because it is too large
Load Diff
@@ -326,6 +326,7 @@ def addBulbs() {
|
||||
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
|
||||
}
|
||||
log.debug "created ${d.displayName} with id $dni"
|
||||
d.refresh()
|
||||
} else {
|
||||
log.debug "$dni in not longer paired to the Hue Bridge or ID changed"
|
||||
}
|
||||
@@ -333,8 +334,8 @@ def addBulbs() {
|
||||
//backwards compatable
|
||||
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
|
||||
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name])
|
||||
d.refresh()
|
||||
}
|
||||
d.refresh()
|
||||
} else {
|
||||
log.debug "found ${d.displayName} with id $dni already exists, type: '$d.typeName'"
|
||||
if (bulbs instanceof java.util.Map) {
|
||||
@@ -774,4 +775,4 @@ private Boolean hasAllHubsOver(String desiredFirmware) {
|
||||
|
||||
private List getRealHubFirmwareVersions() {
|
||||
return location.hubs*.firmwareVersionString.findAll { it }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,60 +316,40 @@ private def parseEventMessage(String description) {
|
||||
parts.each { part ->
|
||||
part = part.trim()
|
||||
if (part.startsWith('devicetype:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
event.devicetype = valueString
|
||||
part -= "devicetype:"
|
||||
event.devicetype = part.trim()
|
||||
}
|
||||
else if (part.startsWith('mac:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
if (valueString) {
|
||||
event.mac = valueString
|
||||
}
|
||||
part -= "mac:"
|
||||
event.mac = part.trim()
|
||||
}
|
||||
else if (part.startsWith('networkAddress:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
if (valueString) {
|
||||
event.ip = valueString
|
||||
}
|
||||
part -= "networkAddress:"
|
||||
event.ip = part.trim()
|
||||
}
|
||||
else if (part.startsWith('deviceAddress:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
if (valueString) {
|
||||
event.port = valueString
|
||||
}
|
||||
part -= "deviceAddress:"
|
||||
event.port = part.trim()
|
||||
}
|
||||
else if (part.startsWith('ssdpPath:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
if (valueString) {
|
||||
event.ssdpPath = valueString
|
||||
}
|
||||
part -= "ssdpPath:"
|
||||
event.ssdpPath = part.trim()
|
||||
}
|
||||
else if (part.startsWith('ssdpUSN:')) {
|
||||
part -= "ssdpUSN:"
|
||||
def valueString = part.trim()
|
||||
if (valueString) {
|
||||
event.ssdpUSN = valueString
|
||||
}
|
||||
event.ssdpUSN = part.trim()
|
||||
}
|
||||
else if (part.startsWith('ssdpTerm:')) {
|
||||
part -= "ssdpTerm:"
|
||||
def valueString = part.trim()
|
||||
if (valueString) {
|
||||
event.ssdpTerm = valueString
|
||||
}
|
||||
event.ssdpTerm = part.trim()
|
||||
}
|
||||
else if (part.startsWith('headers')) {
|
||||
part -= "headers:"
|
||||
def valueString = part.trim()
|
||||
if (valueString) {
|
||||
event.headers = valueString
|
||||
}
|
||||
event.headers = part.trim()
|
||||
}
|
||||
else if (part.startsWith('body')) {
|
||||
part -= "body:"
|
||||
def valueString = part.trim()
|
||||
if (valueString) {
|
||||
event.body = valueString
|
||||
}
|
||||
event.body = part.trim()
|
||||
}
|
||||
}
|
||||
event
|
||||
|
||||
@@ -473,68 +473,48 @@ private def parseXmlBody(def body) {
|
||||
}
|
||||
|
||||
private def parseDiscoveryMessage(String description) {
|
||||
def device = [:]
|
||||
def event = [:]
|
||||
def parts = description.split(',')
|
||||
parts.each { part ->
|
||||
part = part.trim()
|
||||
if (part.startsWith('devicetype:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
device.devicetype = valueString
|
||||
part -= "devicetype:"
|
||||
event.devicetype = part.trim()
|
||||
}
|
||||
else if (part.startsWith('mac:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
if (valueString) {
|
||||
device.mac = valueString
|
||||
}
|
||||
part -= "mac:"
|
||||
event.mac = part.trim()
|
||||
}
|
||||
else if (part.startsWith('networkAddress:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
if (valueString) {
|
||||
device.ip = valueString
|
||||
}
|
||||
part -= "networkAddress:"
|
||||
event.ip = part.trim()
|
||||
}
|
||||
else if (part.startsWith('deviceAddress:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
if (valueString) {
|
||||
device.port = valueString
|
||||
}
|
||||
part -= "deviceAddress:"
|
||||
event.port = part.trim()
|
||||
}
|
||||
else if (part.startsWith('ssdpPath:')) {
|
||||
def valueString = part.split(":")[1].trim()
|
||||
if (valueString) {
|
||||
device.ssdpPath = valueString
|
||||
}
|
||||
part -= "ssdpPath:"
|
||||
event.ssdpPath = part.trim()
|
||||
}
|
||||
else if (part.startsWith('ssdpUSN:')) {
|
||||
part -= "ssdpUSN:"
|
||||
def valueString = part.trim()
|
||||
if (valueString) {
|
||||
device.ssdpUSN = valueString
|
||||
}
|
||||
event.ssdpUSN = part.trim()
|
||||
}
|
||||
else if (part.startsWith('ssdpTerm:')) {
|
||||
part -= "ssdpTerm:"
|
||||
def valueString = part.trim()
|
||||
if (valueString) {
|
||||
device.ssdpTerm = valueString
|
||||
}
|
||||
event.ssdpTerm = part.trim()
|
||||
}
|
||||
else if (part.startsWith('headers')) {
|
||||
part -= "headers:"
|
||||
def valueString = part.trim()
|
||||
if (valueString) {
|
||||
device.headers = valueString
|
||||
}
|
||||
event.headers = part.trim()
|
||||
}
|
||||
else if (part.startsWith('body')) {
|
||||
part -= "body:"
|
||||
def valueString = part.trim()
|
||||
if (valueString) {
|
||||
device.body = valueString
|
||||
}
|
||||
event.body = part.trim()
|
||||
}
|
||||
}
|
||||
device
|
||||
event
|
||||
}
|
||||
|
||||
def doDeviceSync(){
|
||||
|
||||
Reference in New Issue
Block a user