mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-18 05:10:52 +00:00
Compare commits
75 Commits
revert-490
...
MSA-882-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1298dc55ae | ||
|
|
ce26a2941e | ||
|
|
81fb356a21 | ||
|
|
aadbcc2eee | ||
|
|
4a49d4c15d | ||
|
|
0e3bd5aa74 | ||
|
|
bfd68228bc | ||
|
|
a2e8a8303b | ||
|
|
56580634e8 | ||
|
|
a82717744e | ||
|
|
b1096a67af | ||
|
|
ae945d1a15 | ||
|
|
12220da750 | ||
|
|
3f89bbb6d9 | ||
|
|
6fe60146a4 | ||
|
|
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) {
|
if (cmd) {
|
||||||
result = zwaveEvent(cmd)
|
result = zwaveEvent(cmd)
|
||||||
}
|
}
|
||||||
// log.debug "Parsed ${description.inspect()} to ${result.inspect()}"
|
log.debug "Parsed ${description.inspect()} to ${result.inspect()}"
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.multiinstancev1.MultiInstanceCmdEncap cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.multiinstancev1.MultiInstanceCmdEncap cmd) {
|
||||||
def encapsulated = null
|
def encapsulated = cmd.encapsulatedCommand([0x31: 1, 0x84: 2, 0x60: 1, 0x85: 1, 0x70: 1])
|
||||||
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])
|
|
||||||
}
|
|
||||||
return encapsulated ? zwaveEvent(encapsulated) : null
|
return encapsulated ? zwaveEvent(encapsulated) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -487,11 +487,6 @@ def enrollResponse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map parseAxis(String description) {
|
private Map parseAxis(String description) {
|
||||||
def hexToSignedInt = { hexVal ->
|
|
||||||
def unsignedVal = hexToInt(hexVal)
|
|
||||||
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
|
|
||||||
}
|
|
||||||
|
|
||||||
def z = hexToSignedInt(description[0..3])
|
def z = hexToSignedInt(description[0..3])
|
||||||
def y = hexToSignedInt(description[10..13])
|
def y = hexToSignedInt(description[10..13])
|
||||||
def x = hexToSignedInt(description[20..23])
|
def x = hexToSignedInt(description[20..23])
|
||||||
@@ -518,6 +513,11 @@ private Map parseAxis(String description) {
|
|||||||
getXyzResult(xyzResults, description)
|
getXyzResult(xyzResults, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hexToSignedInt(hexVal) {
|
||||||
|
def unsignedVal = hexToInt(hexVal)
|
||||||
|
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
|
||||||
|
}
|
||||||
|
|
||||||
def garageEvent(zValue) {
|
def garageEvent(zValue) {
|
||||||
def absValue = zValue.abs()
|
def absValue = zValue.abs()
|
||||||
def contactValue = null
|
def contactValue = null
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
|
|||||||
case 32:
|
case 32:
|
||||||
map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", isStateChange: true ]
|
map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", isStateChange: true ]
|
||||||
allCodesDeleted()
|
allCodesDeleted()
|
||||||
|
break
|
||||||
case 33:
|
case 33:
|
||||||
map = [ name: "codeReport", value: cmd.alarmLevel, data: [ code: "" ], isStateChange: true ]
|
map = [ name: "codeReport", value: cmd.alarmLevel, data: [ code: "" ], isStateChange: true ]
|
||||||
map.descriptionText = "$device.displayName code $cmd.alarmLevel was deleted"
|
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 = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: code ] ]
|
||||||
map.descriptionText = "$device.displayName code $cmd.userIdentifier is set"
|
map.descriptionText = "$device.displayName code $cmd.userIdentifier is set"
|
||||||
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
|
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
|
||||||
map.isStateChange = (code != decrypt(state[name]))
|
map.isStateChange = true
|
||||||
}
|
}
|
||||||
result << createEvent(map)
|
result << createEvent(map)
|
||||||
} else {
|
} else {
|
||||||
map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: "" ] ]
|
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
|
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.descriptionText = "$device.displayName code $cmd.userIdentifier was reset"
|
||||||
map.displayed = map.isStateChange = false
|
map.displayed = map.isStateChange = true
|
||||||
result << createEvent(map)
|
result << createEvent(map)
|
||||||
state["set$name"] = state["reset$name"]
|
state["set$name"] = state["reset$name"]
|
||||||
result << response(setCode(cmd.userIdentifier, 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.descriptionText = "$device.displayName code $cmd.userIdentifier is not set"
|
||||||
}
|
}
|
||||||
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
|
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
|
||||||
map.isStateChange = state[name] as Boolean
|
map.isStateChange = true
|
||||||
result << createEvent(map)
|
result << createEvent(map)
|
||||||
}
|
}
|
||||||
code = ""
|
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])
|
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
|
||||||
}
|
}
|
||||||
log.debug "created ${d.displayName} with id $dni"
|
log.debug "created ${d.displayName} with id $dni"
|
||||||
|
d.refresh()
|
||||||
} else {
|
} else {
|
||||||
log.debug "$dni in not longer paired to the Hue Bridge or ID changed"
|
log.debug "$dni in not longer paired to the Hue Bridge or ID changed"
|
||||||
}
|
}
|
||||||
@@ -333,8 +334,8 @@ def addBulbs() {
|
|||||||
//backwards compatable
|
//backwards compatable
|
||||||
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
|
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
|
||||||
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name])
|
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name])
|
||||||
|
d.refresh()
|
||||||
}
|
}
|
||||||
d.refresh()
|
|
||||||
} else {
|
} else {
|
||||||
log.debug "found ${d.displayName} with id $dni already exists, type: '$d.typeName'"
|
log.debug "found ${d.displayName} with id $dni already exists, type: '$d.typeName'"
|
||||||
if (bulbs instanceof java.util.Map) {
|
if (bulbs instanceof java.util.Map) {
|
||||||
@@ -774,4 +775,4 @@ private Boolean hasAllHubsOver(String desiredFirmware) {
|
|||||||
|
|
||||||
private List getRealHubFirmwareVersions() {
|
private List getRealHubFirmwareVersions() {
|
||||||
return location.hubs*.firmwareVersionString.findAll { it }
|
return location.hubs*.firmwareVersionString.findAll { it }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,60 +316,40 @@ private def parseEventMessage(String description) {
|
|||||||
parts.each { part ->
|
parts.each { part ->
|
||||||
part = part.trim()
|
part = part.trim()
|
||||||
if (part.startsWith('devicetype:')) {
|
if (part.startsWith('devicetype:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "devicetype:"
|
||||||
event.devicetype = valueString
|
event.devicetype = part.trim()
|
||||||
}
|
}
|
||||||
else if (part.startsWith('mac:')) {
|
else if (part.startsWith('mac:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "mac:"
|
||||||
if (valueString) {
|
event.mac = part.trim()
|
||||||
event.mac = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('networkAddress:')) {
|
else if (part.startsWith('networkAddress:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "networkAddress:"
|
||||||
if (valueString) {
|
event.ip = part.trim()
|
||||||
event.ip = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('deviceAddress:')) {
|
else if (part.startsWith('deviceAddress:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "deviceAddress:"
|
||||||
if (valueString) {
|
event.port = part.trim()
|
||||||
event.port = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('ssdpPath:')) {
|
else if (part.startsWith('ssdpPath:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "ssdpPath:"
|
||||||
if (valueString) {
|
event.ssdpPath = part.trim()
|
||||||
event.ssdpPath = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('ssdpUSN:')) {
|
else if (part.startsWith('ssdpUSN:')) {
|
||||||
part -= "ssdpUSN:"
|
part -= "ssdpUSN:"
|
||||||
def valueString = part.trim()
|
event.ssdpUSN = part.trim()
|
||||||
if (valueString) {
|
|
||||||
event.ssdpUSN = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('ssdpTerm:')) {
|
else if (part.startsWith('ssdpTerm:')) {
|
||||||
part -= "ssdpTerm:"
|
part -= "ssdpTerm:"
|
||||||
def valueString = part.trim()
|
event.ssdpTerm = part.trim()
|
||||||
if (valueString) {
|
|
||||||
event.ssdpTerm = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('headers')) {
|
else if (part.startsWith('headers')) {
|
||||||
part -= "headers:"
|
part -= "headers:"
|
||||||
def valueString = part.trim()
|
event.headers = part.trim()
|
||||||
if (valueString) {
|
|
||||||
event.headers = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('body')) {
|
else if (part.startsWith('body')) {
|
||||||
part -= "body:"
|
part -= "body:"
|
||||||
def valueString = part.trim()
|
event.body = part.trim()
|
||||||
if (valueString) {
|
|
||||||
event.body = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event
|
event
|
||||||
|
|||||||
@@ -473,68 +473,48 @@ private def parseXmlBody(def body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def parseDiscoveryMessage(String description) {
|
private def parseDiscoveryMessage(String description) {
|
||||||
def device = [:]
|
def event = [:]
|
||||||
def parts = description.split(',')
|
def parts = description.split(',')
|
||||||
parts.each { part ->
|
parts.each { part ->
|
||||||
part = part.trim()
|
part = part.trim()
|
||||||
if (part.startsWith('devicetype:')) {
|
if (part.startsWith('devicetype:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "devicetype:"
|
||||||
device.devicetype = valueString
|
event.devicetype = part.trim()
|
||||||
}
|
}
|
||||||
else if (part.startsWith('mac:')) {
|
else if (part.startsWith('mac:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "mac:"
|
||||||
if (valueString) {
|
event.mac = part.trim()
|
||||||
device.mac = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('networkAddress:')) {
|
else if (part.startsWith('networkAddress:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "networkAddress:"
|
||||||
if (valueString) {
|
event.ip = part.trim()
|
||||||
device.ip = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('deviceAddress:')) {
|
else if (part.startsWith('deviceAddress:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "deviceAddress:"
|
||||||
if (valueString) {
|
event.port = part.trim()
|
||||||
device.port = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('ssdpPath:')) {
|
else if (part.startsWith('ssdpPath:')) {
|
||||||
def valueString = part.split(":")[1].trim()
|
part -= "ssdpPath:"
|
||||||
if (valueString) {
|
event.ssdpPath = part.trim()
|
||||||
device.ssdpPath = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('ssdpUSN:')) {
|
else if (part.startsWith('ssdpUSN:')) {
|
||||||
part -= "ssdpUSN:"
|
part -= "ssdpUSN:"
|
||||||
def valueString = part.trim()
|
event.ssdpUSN = part.trim()
|
||||||
if (valueString) {
|
|
||||||
device.ssdpUSN = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('ssdpTerm:')) {
|
else if (part.startsWith('ssdpTerm:')) {
|
||||||
part -= "ssdpTerm:"
|
part -= "ssdpTerm:"
|
||||||
def valueString = part.trim()
|
event.ssdpTerm = part.trim()
|
||||||
if (valueString) {
|
|
||||||
device.ssdpTerm = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('headers')) {
|
else if (part.startsWith('headers')) {
|
||||||
part -= "headers:"
|
part -= "headers:"
|
||||||
def valueString = part.trim()
|
event.headers = part.trim()
|
||||||
if (valueString) {
|
|
||||||
device.headers = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (part.startsWith('body')) {
|
else if (part.startsWith('body')) {
|
||||||
part -= "body:"
|
part -= "body:"
|
||||||
def valueString = part.trim()
|
event.body = part.trim()
|
||||||
if (valueString) {
|
|
||||||
device.body = valueString
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
device
|
event
|
||||||
}
|
}
|
||||||
|
|
||||||
def doDeviceSync(){
|
def doDeviceSync(){
|
||||||
|
|||||||
Reference in New Issue
Block a user