mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-21 13:10:51 +00:00
Merge pull request #516 from Yaima/master
Ecobee 3 - https://smartthings.atlassian.net/browse/DEVC-285
This commit is contained in:
@@ -22,10 +22,6 @@ metadata {
|
|||||||
capability "Polling"
|
capability "Polling"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator {
|
|
||||||
// TODO: define status and reply messages here
|
|
||||||
}
|
|
||||||
|
|
||||||
tiles {
|
tiles {
|
||||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||||
state("temperature", label:'${currentValue}°', unit:"F",
|
state("temperature", label:'${currentValue}°', unit:"F",
|
||||||
@@ -56,16 +52,12 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "refresh..."
|
log.debug "refresh called"
|
||||||
poll()
|
poll()
|
||||||
}
|
}
|
||||||
|
|
||||||
void poll() {
|
void poll() {
|
||||||
log.debug "Executing 'poll' using parent SmartApp"
|
log.debug "Executing 'poll' using parent SmartApp"
|
||||||
parent.pollChildren(this)
|
parent.pollChild(this)
|
||||||
}
|
|
||||||
|
|
||||||
//generate custom mobile activity feeds event
|
|
||||||
def generateActivityFeedsEvent(notificationMessage) {
|
|
||||||
sendEvent(name: "notificationMessage", value: "$device.displayName $notificationMessage", descriptionText: "$device.displayName $notificationMessage", displayed: true)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,34 +19,39 @@ metadata {
|
|||||||
definition (name: "Ecobee Thermostat", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "Ecobee Thermostat", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Thermostat"
|
capability "Thermostat"
|
||||||
|
capability "Temperature Measurement"
|
||||||
capability "Polling"
|
capability "Polling"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
|
capability "Relative Humidity Measurement"
|
||||||
|
|
||||||
command "generateEvent"
|
command "generateEvent"
|
||||||
command "raiseSetpoint"
|
command "raiseSetpoint"
|
||||||
command "lowerSetpoint"
|
command "lowerSetpoint"
|
||||||
command "resumeProgram"
|
command "resumeProgram"
|
||||||
command "switchMode"
|
command "switchMode"
|
||||||
|
|
||||||
attribute "thermostatSetpoint","number"
|
attribute "thermostatSetpoint","number"
|
||||||
attribute "thermostatStatus","string"
|
attribute "thermostatStatus","string"
|
||||||
|
attribute "maxHeatingSetpoint", "number"
|
||||||
|
attribute "minHeatingSetpoint", "number"
|
||||||
|
attribute "maxCoolingSetpoint", "number"
|
||||||
|
attribute "minCoolingSetpoint", "number"
|
||||||
|
attribute "deviceTemperatureUnit", "number"
|
||||||
}
|
}
|
||||||
|
|
||||||
simulator { }
|
tiles {
|
||||||
|
|
||||||
tiles {
|
|
||||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||||
state("temperature", label:'${currentValue}°', unit:"F",
|
state("temperature", label:'${currentValue}°', unit:"F",
|
||||||
backgroundColors:[
|
backgroundColors:[
|
||||||
[value: 31, color: "#153591"],
|
[value: 31, color: "#153591"],
|
||||||
[value: 44, color: "#1e9cbb"],
|
[value: 44, color: "#1e9cbb"],
|
||||||
[value: 59, color: "#90d2a7"],
|
[value: 59, color: "#90d2a7"],
|
||||||
[value: 74, color: "#44b621"],
|
[value: 74, color: "#44b621"],
|
||||||
[value: 84, color: "#f1d801"],
|
[value: 84, color: "#f1d801"],
|
||||||
[value: 95, color: "#d04e00"],
|
[value: 95, color: "#d04e00"],
|
||||||
[value: 96, color: "#bc2323"]
|
[value: 96, color: "#bc2323"]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||||
@@ -94,8 +99,11 @@ metadata {
|
|||||||
state "resume", action:"resumeProgram", nextState: "updating", label:'Resume Schedule', icon:"st.samsung.da.oven_ic_send"
|
state "resume", action:"resumeProgram", nextState: "updating", label:'Resume Schedule', icon:"st.samsung.da.oven_ic_send"
|
||||||
state "updating", label:"Working", icon: "st.secondary.secondary"
|
state "updating", label:"Working", icon: "st.secondary.secondary"
|
||||||
}
|
}
|
||||||
|
valueTile("humidity", "device.humidity", decoration: "flat") {
|
||||||
|
state "humidity", label:'${currentValue}% humidity'
|
||||||
|
}
|
||||||
main "temperature"
|
main "temperature"
|
||||||
details(["temperature", "upButtonControl", "thermostatSetpoint", "currentStatus", "downButtonControl", "mode", "resumeProgram", "refresh"])
|
details(["temperature", "upButtonControl", "thermostatSetpoint", "currentStatus", "downButtonControl", "mode", "resumeProgram", "refresh", "humidity"])
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences {
|
preferences {
|
||||||
@@ -107,8 +115,6 @@ metadata {
|
|||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.debug "Parsing '${description}'"
|
log.debug "Parsing '${description}'"
|
||||||
// TODO: handle '' attribute
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -133,16 +139,24 @@ def generateEvent(Map results) {
|
|||||||
def isChange = false
|
def isChange = false
|
||||||
def isDisplayed = true
|
def isDisplayed = true
|
||||||
def event = [name: name, linkText: linkText, descriptionText: getThermostatDescriptionText(name, value, linkText),
|
def event = [name: name, linkText: linkText, descriptionText: getThermostatDescriptionText(name, value, linkText),
|
||||||
handlerName: name]
|
handlerName: name]
|
||||||
|
|
||||||
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint") {
|
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) {
|
||||||
def sendValue = value? convertTemperatureIfNeeded(value.toDouble(), "F", 1): value //API return temperature value in F
|
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
|
||||||
|
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||||
isChange = isTemperatureStateChange(device, name, value.toString())
|
isChange = isTemperatureStateChange(device, name, value.toString())
|
||||||
isDisplayed = isChange
|
isDisplayed = isChange
|
||||||
event << [value: sendValue, isStateChange: isChange, displayed: isDisplayed]
|
event << [value: sendValue, isStateChange: isChange, displayed: isDisplayed]
|
||||||
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
|
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
|
||||||
|
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
|
||||||
|
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||||
|
event << [value: sendValue, displayed: false]
|
||||||
|
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
|
||||||
isChange = isStateChange(device, name, value.toString())
|
isChange = isStateChange(device, name, value.toString())
|
||||||
event << [value: value.toString(), isStateChange: isChange, displayed: false]
|
event << [value: value.toString(), isStateChange: isChange, displayed: false]
|
||||||
|
} else if (name=="humidity") {
|
||||||
|
isChange = isStateChange(device, name, value.toString())
|
||||||
|
event << [value: value.toString(), isStateChange: isChange, displayed: false, unit: "%"]
|
||||||
} else {
|
} else {
|
||||||
isChange = isStateChange(device, name, value.toString())
|
isChange = isStateChange(device, name, value.toString())
|
||||||
isDisplayed = isChange
|
isDisplayed = isChange
|
||||||
@@ -158,13 +172,19 @@ def generateEvent(Map results) {
|
|||||||
//return descriptionText to be shown on mobile activity feed
|
//return descriptionText to be shown on mobile activity feed
|
||||||
private getThermostatDescriptionText(name, value, linkText) {
|
private getThermostatDescriptionText(name, value, linkText) {
|
||||||
if(name == "temperature") {
|
if(name == "temperature") {
|
||||||
return "$linkText temperature is $value°F"
|
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
|
||||||
|
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||||
|
return "$linkText temperature is $sendValue ${location.temperatureScale}"
|
||||||
|
|
||||||
} else if(name == "heatingSetpoint") {
|
} else if(name == "heatingSetpoint") {
|
||||||
return "heating setpoint is $value°F"
|
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
|
||||||
|
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||||
|
return "heating setpoint is $sendValue ${location.temperatureScale}"
|
||||||
|
|
||||||
} else if(name == "coolingSetpoint"){
|
} else if(name == "coolingSetpoint"){
|
||||||
return "cooling setpoint is $value°F"
|
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
|
||||||
|
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||||
|
return "cooling setpoint is $sendValue ${location.temperatureScale}"
|
||||||
|
|
||||||
} else if (name == "thermostatMode") {
|
} else if (name == "thermostatMode") {
|
||||||
return "thermostat mode is ${value}"
|
return "thermostat mode is ${value}"
|
||||||
@@ -172,26 +192,26 @@ private getThermostatDescriptionText(name, value, linkText) {
|
|||||||
} else if (name == "thermostatFanMode") {
|
} else if (name == "thermostatFanMode") {
|
||||||
return "thermostat fan mode is ${value}"
|
return "thermostat fan mode is ${value}"
|
||||||
|
|
||||||
|
} else if (name == "humidity") {
|
||||||
|
return "humidity is ${value} %"
|
||||||
} else {
|
} else {
|
||||||
return "${name} = ${value}"
|
return "${name} = ${value}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setHeatingSetpoint(setpoint) {
|
void setHeatingSetpoint(setpoint) {
|
||||||
setHeatingSetpoint(setpoint.toDouble())
|
log.debug "***heating setpoint $setpoint"
|
||||||
}
|
def heatingSetpoint = setpoint.toDouble()
|
||||||
|
|
||||||
void setHeatingSetpoint(Double setpoint) {
|
|
||||||
// def mode = device.currentValue("thermostatMode")
|
|
||||||
def heatingSetpoint = setpoint
|
|
||||||
def coolingSetpoint = device.currentValue("coolingSetpoint").toDouble()
|
def coolingSetpoint = device.currentValue("coolingSetpoint").toDouble()
|
||||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||||
|
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint").toDouble()
|
||||||
|
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint").toDouble()
|
||||||
|
|
||||||
//enforce limits of heatingSetpoint
|
//enforce limits of heatingSetpoint
|
||||||
if (heatingSetpoint > 79) {
|
if (heatingSetpoint > maxHeatingSetpoint) {
|
||||||
heatingSetpoint = 79
|
heatingSetpoint = maxHeatingSetpoint
|
||||||
} else if (heatingSetpoint < 45) {
|
} else if (heatingSetpoint < minHeatingSetpoint) {
|
||||||
heatingSetpoint = 45
|
heatingSetpoint = minHeatingSetpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
//enforce limits of heatingSetpoint vs coolingSetpoint
|
//enforce limits of heatingSetpoint vs coolingSetpoint
|
||||||
@@ -201,32 +221,34 @@ void setHeatingSetpoint(Double setpoint) {
|
|||||||
|
|
||||||
log.debug "Sending setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
|
log.debug "Sending setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
|
||||||
|
|
||||||
|
def coolingValue = location.temperatureScale == "C"? convertCtoF(coolingSetpoint) : coolingSetpoint
|
||||||
|
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
|
||||||
|
|
||||||
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
||||||
if (parent.setHold (this, heatingSetpoint, coolingSetpoint, deviceId, sendHoldType)) {
|
if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||||
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
|
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
|
||||||
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
|
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
|
||||||
log.debug "Done setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
|
log.debug "Done setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
|
||||||
generateSetpointEvent()
|
generateSetpointEvent()
|
||||||
generateStatusEvent()
|
generateStatusEvent()
|
||||||
} else {
|
} else {
|
||||||
log.error "Error setHeatingSetpoint(setpoint)" //This error is handled by the connect app
|
log.error "Error setHeatingSetpoint(setpoint)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCoolingSetpoint(setpoint) {
|
void setCoolingSetpoint(setpoint) {
|
||||||
setCoolingSetpoint(setpoint.toDouble())
|
log.debug "***cooling setpoint $setpoint"
|
||||||
}
|
|
||||||
|
|
||||||
void setCoolingSetpoint(Double setpoint) {
|
|
||||||
// def mode = device.currentValue("thermostatMode")
|
|
||||||
def heatingSetpoint = device.currentValue("heatingSetpoint").toDouble()
|
def heatingSetpoint = device.currentValue("heatingSetpoint").toDouble()
|
||||||
def coolingSetpoint = setpoint
|
def coolingSetpoint = setpoint.toDouble()
|
||||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||||
|
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint").toDouble()
|
||||||
|
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint").toDouble()
|
||||||
|
|
||||||
if (coolingSetpoint > 92) {
|
|
||||||
coolingSetpoint = 92
|
if (coolingSetpoint > maxCoolingSetpoint) {
|
||||||
} else if (coolingSetpoint < 65) {
|
coolingSetpoint = maxCoolingSetpoint
|
||||||
coolingSetpoint = 65
|
} else if (coolingSetpoint < minCoolingSetpoint) {
|
||||||
|
coolingSetpoint = minCoolingSetpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
//enforce limits of heatingSetpoint vs coolingSetpoint
|
//enforce limits of heatingSetpoint vs coolingSetpoint
|
||||||
@@ -236,15 +258,18 @@ void setCoolingSetpoint(Double setpoint) {
|
|||||||
|
|
||||||
log.debug "Sending setCoolingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
|
log.debug "Sending setCoolingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
|
||||||
|
|
||||||
|
def coolingValue = location.temperatureScale == "C"? convertCtoF(coolingSetpoint) : coolingSetpoint
|
||||||
|
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
|
||||||
|
|
||||||
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
||||||
if (parent.setHold (this, heatingSetpoint, coolingSetpoint, deviceId, sendHoldType)) {
|
if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||||
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
|
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
|
||||||
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
|
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
|
||||||
log.debug "Done setCoolingSetpoint>> coolingSetpoint = ${coolingSetpoint}, heatingSetpoint = ${heatingSetpoint}"
|
log.debug "Done setCoolingSetpoint>> coolingSetpoint = ${coolingSetpoint}, heatingSetpoint = ${heatingSetpoint}"
|
||||||
generateSetpointEvent()
|
generateSetpointEvent()
|
||||||
generateStatusEvent()
|
generateStatusEvent()
|
||||||
} else {
|
} else {
|
||||||
log.error "Error setCoolingSetpoint(setpoint)" //This error is handled by the connect app
|
log.error "Error setCoolingSetpoint(setpoint)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,25 +473,21 @@ def auto() {
|
|||||||
def fanOn() {
|
def fanOn() {
|
||||||
log.debug "fanOn"
|
log.debug "fanOn"
|
||||||
// parent.setFanMode (this,"on")
|
// parent.setFanMode (this,"on")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanAuto() {
|
def fanAuto() {
|
||||||
log.debug "fanAuto"
|
log.debug "fanAuto"
|
||||||
// parent.setFanMode (this,"auto")
|
// parent.setFanMode (this,"auto")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanCirculate() {
|
def fanCirculate() {
|
||||||
log.debug "fanCirculate"
|
log.debug "fanCirculate"
|
||||||
// parent.setFanMode (this,"circulate")
|
// parent.setFanMode (this,"circulate")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanOff() {
|
def fanOff() {
|
||||||
log.debug "fanOff"
|
log.debug "fanOff"
|
||||||
// parent.setFanMode (this,"off")
|
// parent.setFanMode (this,"off")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def generateSetpointEvent() {
|
def generateSetpointEvent() {
|
||||||
@@ -476,20 +497,41 @@ def generateSetpointEvent() {
|
|||||||
def mode = device.currentValue("thermostatMode")
|
def mode = device.currentValue("thermostatMode")
|
||||||
log.debug "Current Mode = ${mode}"
|
log.debug "Current Mode = ${mode}"
|
||||||
|
|
||||||
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
|
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||||
log.debug "Heating Setpoint = ${heatingSetpoint}"
|
log.debug "Heating Setpoint = ${heatingSetpoint}"
|
||||||
|
|
||||||
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
|
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||||
log.debug "Cooling Setpoint = ${coolingSetpoint}"
|
log.debug "Cooling Setpoint = ${coolingSetpoint}"
|
||||||
|
|
||||||
|
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint")
|
||||||
|
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
|
||||||
|
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint")
|
||||||
|
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
|
||||||
|
|
||||||
|
if(location.temperatureScale == "C")
|
||||||
|
{
|
||||||
|
maxHeatingSetpoint = roundC(maxHeatingSetpoint)
|
||||||
|
maxCoolingSetpoint = roundC(maxCoolingSetpoint)
|
||||||
|
minHeatingSetpoint = roundC(minHeatingSetpoint)
|
||||||
|
minCoolingSetpoint = roundC(minCoolingSetpoint)
|
||||||
|
heatingSetpoint = roundC(heatingSetpoint)
|
||||||
|
coolingSetpoint = roundC(coolingSetpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEvent("name":"maxHeatingSetpoint", "value":maxHeatingSetpoint, "unit":location.temperatureScale)
|
||||||
|
sendEvent("name":"maxCoolingSetpoint", "value":maxCoolingSetpoint, "unit":location.temperatureScale)
|
||||||
|
sendEvent("name":"minHeatingSetpoint", "value":minHeatingSetpoint, "unit":location.temperatureScale)
|
||||||
|
sendEvent("name":"minCoolingSetpoint", "value":minCoolingSetpoint, "unit":location.temperatureScale)
|
||||||
|
|
||||||
|
|
||||||
if (mode == "heat") {
|
if (mode == "heat") {
|
||||||
|
|
||||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString())
|
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint )
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (mode == "cool") {
|
else if (mode == "cool") {
|
||||||
|
|
||||||
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString())
|
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint)
|
||||||
|
|
||||||
} else if (mode == "auto") {
|
} else if (mode == "auto") {
|
||||||
|
|
||||||
@@ -499,9 +541,9 @@ def generateSetpointEvent() {
|
|||||||
|
|
||||||
sendEvent("name":"thermostatSetpoint", "value":"Off")
|
sendEvent("name":"thermostatSetpoint", "value":"Off")
|
||||||
|
|
||||||
} else if (mode == "emergencyHeat") {
|
} else if (mode == "auxHeatOnly") {
|
||||||
|
|
||||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString())
|
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,26 +552,30 @@ def generateSetpointEvent() {
|
|||||||
void raiseSetpoint() {
|
void raiseSetpoint() {
|
||||||
def mode = device.currentValue("thermostatMode")
|
def mode = device.currentValue("thermostatMode")
|
||||||
def targetvalue
|
def targetvalue
|
||||||
|
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint")
|
||||||
|
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
|
||||||
|
|
||||||
|
|
||||||
if (mode == "off" || mode == "auto") {
|
if (mode == "off" || mode == "auto") {
|
||||||
log.warn "this mode: $mode does not allow raiseSetpoint"
|
log.warn "this mode: $mode does not allow raiseSetpoint"
|
||||||
} else {
|
} else {
|
||||||
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
|
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||||
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
|
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||||
def thermostatSetpoint = device.currentValue("thermostatSetpoint").toInteger()
|
def thermostatSetpoint = device.currentValue("thermostatSetpoint")
|
||||||
log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
|
log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
|
||||||
|
|
||||||
if (device.latestState('thermostatSetpoint')) {
|
if (device.latestState('thermostatSetpoint')) {
|
||||||
targetvalue = device.latestState('thermostatSetpoint').value as Integer
|
targetvalue = device.latestState('thermostatSetpoint').value
|
||||||
|
targetvalue = location.temperatureScale == "F"? targetvalue.toInteger() : targetvalue.toDouble()
|
||||||
} else {
|
} else {
|
||||||
targetvalue = 0
|
targetvalue = 0
|
||||||
}
|
}
|
||||||
targetvalue = targetvalue + 1
|
targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5
|
||||||
|
|
||||||
if (mode == "heat" && targetvalue > 79) {
|
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue > maxHeatingSetpoint) {
|
||||||
targetvalue = 79
|
targetvalue = maxHeatingSetpoint
|
||||||
} else if (mode == "cool" && targetvalue > 92) {
|
} else if (mode == "cool" && targetvalue > maxCoolingSetpoint) {
|
||||||
targetvalue = 92
|
targetvalue = maxCoolingSetpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
|
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
|
||||||
@@ -543,25 +589,29 @@ void raiseSetpoint() {
|
|||||||
void lowerSetpoint() {
|
void lowerSetpoint() {
|
||||||
def mode = device.currentValue("thermostatMode")
|
def mode = device.currentValue("thermostatMode")
|
||||||
def targetvalue
|
def targetvalue
|
||||||
|
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint")
|
||||||
|
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
|
||||||
|
|
||||||
|
|
||||||
if (mode == "off" || mode == "auto") {
|
if (mode == "off" || mode == "auto") {
|
||||||
log.warn "this mode: $mode does not allow lowerSetpoint"
|
log.warn "this mode: $mode does not allow lowerSetpoint"
|
||||||
} else {
|
} else {
|
||||||
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
|
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||||
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
|
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||||
def thermostatSetpoint = device.currentValue("thermostatSetpoint").toInteger()
|
def thermostatSetpoint = device.currentValue("thermostatSetpoint")
|
||||||
log.debug "lowerSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
|
log.debug "lowerSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
|
||||||
if (device.latestState('thermostatSetpoint')) {
|
if (device.latestState('thermostatSetpoint')) {
|
||||||
targetvalue = device.latestState('thermostatSetpoint').value as Integer
|
targetvalue = device.latestState('thermostatSetpoint').value
|
||||||
|
targetvalue = location.temperatureScale == "F"? targetvalue.toInteger() : targetvalue.toDouble()
|
||||||
} else {
|
} else {
|
||||||
targetvalue = 0
|
targetvalue = 0
|
||||||
}
|
}
|
||||||
targetvalue = targetvalue - 1
|
targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5
|
||||||
|
|
||||||
if (mode == "heat" && targetvalue.toInteger() < 45) {
|
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue < minHeatingSetpoint) {
|
||||||
targetvalue = 45
|
targetvalue = minHeatingSetpoint
|
||||||
} else if (mode == "cool" && targetvalue.toInteger() < 65) {
|
} else if (mode == "cool" && targetvalue < minCoolingSetpoint) {
|
||||||
targetvalue = 65
|
targetvalue = minCoolingSetpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
|
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
|
||||||
@@ -575,15 +625,15 @@ void lowerSetpoint() {
|
|||||||
void alterSetpoint(temp) {
|
void alterSetpoint(temp) {
|
||||||
|
|
||||||
def mode = device.currentValue("thermostatMode")
|
def mode = device.currentValue("thermostatMode")
|
||||||
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
|
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||||
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
|
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||||
|
|
||||||
def targetHeatingSetpoint
|
def targetHeatingSetpoint
|
||||||
def targetCoolingSetpoint
|
def targetCoolingSetpoint
|
||||||
|
|
||||||
//step1: check thermostatMode, enforce limits before sending request to cloud
|
//step1: check thermostatMode, enforce limits before sending request to cloud
|
||||||
if (mode == "heat"){
|
if (mode == "heat" || mode == "auxHeatOnly"){
|
||||||
if (temp.value > coolingSetpoint){
|
if (temp.value > coolingSetpoint){
|
||||||
targetHeatingSetpoint = temp.value
|
targetHeatingSetpoint = temp.value
|
||||||
targetCoolingSetpoint = temp.value
|
targetCoolingSetpoint = temp.value
|
||||||
@@ -602,19 +652,22 @@ void alterSetpoint(temp) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to ${targetHeatingSetpoint} " +
|
log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to $targetHeatingSetpoint " +
|
||||||
"coolingSetpoint to ${targetCoolingSetpoint} with holdType : ${holdType}"
|
"coolingSetpoint to $targetCoolingSetpoint with holdType : ${holdType}"
|
||||||
|
|
||||||
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
|
||||||
//step2: call parent.setHold to send http request to 3rd party cloud
|
|
||||||
if (parent.setHold(this, targetHeatingSetpoint, targetCoolingSetpoint, deviceId, sendHoldType)) {
|
def coolingValue = location.temperatureScale == "C"? convertCtoF(targetCoolingSetpoint) : targetCoolingSetpoint
|
||||||
sendEvent("name": "thermostatSetpoint", "value": temp.value.toString(), displayed: false)
|
def heatingValue = location.temperatureScale == "C"? convertCtoF(targetHeatingSetpoint) : targetHeatingSetpoint
|
||||||
|
|
||||||
|
if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||||
|
sendEvent("name": "thermostatSetpoint", "value": temp.value, displayed: false)
|
||||||
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint)
|
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint)
|
||||||
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint)
|
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint)
|
||||||
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
|
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
|
||||||
} else {
|
} else {
|
||||||
log.error "Error alterSetpoint()"
|
log.error "Error alterSetpoint()"
|
||||||
if (mode == "heat"){
|
if (mode == "heat" || mode == "auxHeatOnly"){
|
||||||
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
||||||
} else if (mode == "cool") {
|
} else if (mode == "cool") {
|
||||||
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
|
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
|
||||||
@@ -626,9 +679,9 @@ void alterSetpoint(temp) {
|
|||||||
def generateStatusEvent() {
|
def generateStatusEvent() {
|
||||||
|
|
||||||
def mode = device.currentValue("thermostatMode")
|
def mode = device.currentValue("thermostatMode")
|
||||||
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
|
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||||
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
|
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||||
def temperature = device.currentValue("temperature").toInteger()
|
def temperature = device.currentValue("temperature")
|
||||||
|
|
||||||
def statusText
|
def statusText
|
||||||
|
|
||||||
@@ -643,14 +696,14 @@ def generateStatusEvent() {
|
|||||||
if (temperature >= heatingSetpoint)
|
if (temperature >= heatingSetpoint)
|
||||||
statusText = "Right Now: Idle"
|
statusText = "Right Now: Idle"
|
||||||
else
|
else
|
||||||
statusText = "Heating to ${heatingSetpoint}° F"
|
statusText = "Heating to ${heatingSetpoint} ${location.temperatureScale}"
|
||||||
|
|
||||||
} else if (mode == "cool") {
|
} else if (mode == "cool") {
|
||||||
|
|
||||||
if (temperature <= coolingSetpoint)
|
if (temperature <= coolingSetpoint)
|
||||||
statusText = "Right Now: Idle"
|
statusText = "Right Now: Idle"
|
||||||
else
|
else
|
||||||
statusText = "Cooling to ${coolingSetpoint}° F"
|
statusText = "Cooling to ${coolingSetpoint} ${location.temperatureScale}"
|
||||||
|
|
||||||
} else if (mode == "auto") {
|
} else if (mode == "auto") {
|
||||||
|
|
||||||
@@ -660,7 +713,7 @@ def generateStatusEvent() {
|
|||||||
|
|
||||||
statusText = "Right Now: Off"
|
statusText = "Right Now: Off"
|
||||||
|
|
||||||
} else if (mode == "emergencyHeat") {
|
} else if (mode == "auxHeatOnly") {
|
||||||
|
|
||||||
statusText = "Emergency Heat"
|
statusText = "Emergency Heat"
|
||||||
|
|
||||||
@@ -673,7 +726,18 @@ def generateStatusEvent() {
|
|||||||
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true)
|
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
//generate custom mobile activity feeds event
|
|
||||||
def generateActivityFeedsEvent(notificationMessage) {
|
def generateActivityFeedsEvent(notificationMessage) {
|
||||||
sendEvent(name: "notificationMessage", value: "$device.displayName $notificationMessage", descriptionText: "$device.displayName $notificationMessage", displayed: true)
|
sendEvent(name: "notificationMessage", value: "$device.displayName $notificationMessage", descriptionText: "$device.displayName $notificationMessage", displayed: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def roundC (tempC) {
|
||||||
|
return (Math.round(tempC.toDouble() * 2))/2
|
||||||
|
}
|
||||||
|
|
||||||
|
def convertFtoC (tempF) {
|
||||||
|
return String.format("%.1f", (Math.round(((tempF - 32)*(5/9)) * 2))/2)
|
||||||
|
}
|
||||||
|
|
||||||
|
def convertCtoF (tempC) {
|
||||||
|
return (Math.round(tempC * (9/5)) + 32).toInteger()
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ definition(
|
|||||||
category: "SmartThings Labs",
|
category: "SmartThings Labs",
|
||||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee.png",
|
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee.png",
|
||||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee@2x.png",
|
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee@2x.png",
|
||||||
singleInstance: true
|
singleInstance: true
|
||||||
) {
|
) {
|
||||||
appSetting "clientId"
|
appSetting "clientId"
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ def authPage() {
|
|||||||
description = "Click to enter Ecobee Credentials"
|
description = "Click to enter Ecobee Credentials"
|
||||||
}
|
}
|
||||||
|
|
||||||
def redirectUrl = buildRedirectUrl //"${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${atomicState.accessToken}"
|
def redirectUrl = buildRedirectUrl
|
||||||
log.debug "RedirectUrl = ${redirectUrl}"
|
log.debug "RedirectUrl = ${redirectUrl}"
|
||||||
// get rid of next button until the user is actually auth'd
|
// get rid of next button until the user is actually auth'd
|
||||||
if (!oauthTokenProvided) {
|
if (!oauthTokenProvided) {
|
||||||
@@ -103,7 +103,7 @@ def oauthInitUrl() {
|
|||||||
scope: "smartRead,smartWrite",
|
scope: "smartRead,smartWrite",
|
||||||
client_id: smartThingsClientId,
|
client_id: smartThingsClientId,
|
||||||
state: atomicState.oauthInitState,
|
state: atomicState.oauthInitState,
|
||||||
redirect_uri: callbackUrl //"https://graph.api.smartthings.com/oauth/callback"
|
redirect_uri: callbackUrl
|
||||||
]
|
]
|
||||||
|
|
||||||
redirect(location: "${apiEndpoint}/authorize?${toQueryString(oauthParams)}")
|
redirect(location: "${apiEndpoint}/authorize?${toQueryString(oauthParams)}")
|
||||||
@@ -115,14 +115,13 @@ def callback() {
|
|||||||
def code = params.code
|
def code = params.code
|
||||||
def oauthState = params.state
|
def oauthState = params.state
|
||||||
|
|
||||||
//verify oauthState == atomicState.oauthInitState, so the callback corresponds to the authentication request
|
|
||||||
if (oauthState == atomicState.oauthInitState){
|
if (oauthState == atomicState.oauthInitState){
|
||||||
|
|
||||||
def tokenParams = [
|
def tokenParams = [
|
||||||
grant_type: "authorization_code",
|
grant_type: "authorization_code",
|
||||||
code : code,
|
code : code,
|
||||||
client_id : smartThingsClientId,
|
client_id : smartThingsClientId,
|
||||||
redirect_uri: callbackUrl //"https://graph.api.smartthings.com/oauth/callback"
|
redirect_uri: callbackUrl
|
||||||
]
|
]
|
||||||
|
|
||||||
def tokenUrl = "https://www.ecobee.com/home/token?${toQueryString(tokenParams)}"
|
def tokenUrl = "https://www.ecobee.com/home/token?${toQueryString(tokenParams)}"
|
||||||
@@ -247,32 +246,32 @@ def getEcobeeThermostats() {
|
|||||||
]
|
]
|
||||||
|
|
||||||
def stats = [:]
|
def stats = [:]
|
||||||
try {
|
try {
|
||||||
httpGet(deviceListParams) { resp ->
|
httpGet(deviceListParams) { resp ->
|
||||||
|
|
||||||
if (resp.status == 200) {
|
if (resp.status == 200) {
|
||||||
resp.data.thermostatList.each { stat ->
|
resp.data.thermostatList.each { stat ->
|
||||||
atomicState.remoteSensors = stat.remoteSensors
|
atomicState.remoteSensors = stat.remoteSensors
|
||||||
def dni = [app.id, stat.identifier].join('.')
|
def dni = [app.id, stat.identifier].join('.')
|
||||||
stats[dni] = getThermostatDisplayName(stat)
|
stats[dni] = getThermostatDisplayName(stat)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug "http status: ${resp.status}"
|
log.debug "http status: ${resp.status}"
|
||||||
//refresh the auth token
|
//refresh the auth token
|
||||||
if (resp.status == 500 && resp.data.status.code == 14) {
|
if (resp.data.status.code == 14) {
|
||||||
log.debug "Storing the failed action to try later"
|
log.debug "Storing the failed action to try later"
|
||||||
atomicState.action = "getEcobeeThermostats"
|
atomicState.action = "getEcobeeThermostats"
|
||||||
log.debug "Refreshing your auth_token!"
|
log.debug "Refreshing your auth_token!"
|
||||||
refreshAuthToken()
|
refreshAuthToken()
|
||||||
} else {
|
} else {
|
||||||
log.error "Authentication error, invalid authentication method, lack of credentials, etc."
|
log.error "Authentication error, invalid authentication method, lack of credentials, etc."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
log.debug "___exception getEcobeeThermostats(): " + e
|
log.debug "___exception getEcobeeThermostats(): " + e
|
||||||
refreshAuthToken()
|
refreshAuthToken()
|
||||||
}
|
}
|
||||||
atomicState.thermostats = stats
|
atomicState.thermostats = stats
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
@@ -317,7 +316,7 @@ def initialize() {
|
|||||||
def devices = thermostats.collect { dni ->
|
def devices = thermostats.collect { dni ->
|
||||||
def d = getChildDevice(dni)
|
def d = getChildDevice(dni)
|
||||||
if(!d) {
|
if(!d) {
|
||||||
d = addChildDevice(app.namespace, getChildName(), dni, null, ["label":"Ecobee Thermostat:${atomicState.thermostats[dni]}"])
|
d = addChildDevice(app.namespace, getChildName(), dni, null, ["label":"${atomicState.thermostats[dni]}" ?: "Ecobee Thermostat"])
|
||||||
log.debug "created ${d.displayName} with id $dni"
|
log.debug "created ${d.displayName} with id $dni"
|
||||||
} else {
|
} else {
|
||||||
log.debug "found ${d.displayName} with id $dni already exists"
|
log.debug "found ${d.displayName} with id $dni already exists"
|
||||||
@@ -328,7 +327,7 @@ def initialize() {
|
|||||||
def sensors = ecobeesensors.collect { dni ->
|
def sensors = ecobeesensors.collect { dni ->
|
||||||
def d = getChildDevice(dni)
|
def d = getChildDevice(dni)
|
||||||
if(!d) {
|
if(!d) {
|
||||||
d = addChildDevice(app.namespace, getSensorChildName(), dni, null, ["label":"Ecobee Sensor:${atomicState.sensors[dni]}"])
|
d = addChildDevice(app.namespace, getSensorChildName(), dni, null, ["label":"${atomicState.sensors[dni]}" ?:"Ecobee Sensor"])
|
||||||
log.debug "created ${d.displayName} with id $dni"
|
log.debug "created ${d.displayName} with id $dni"
|
||||||
} else {
|
} else {
|
||||||
log.debug "found ${d.displayName} with id $dni already exists"
|
log.debug "found ${d.displayName} with id $dni already exists"
|
||||||
@@ -354,21 +353,17 @@ def initialize() {
|
|||||||
|
|
||||||
atomicState.thermostatData = [:] //reset Map to store thermostat data
|
atomicState.thermostatData = [:] //reset Map to store thermostat data
|
||||||
|
|
||||||
//send activity feeds to tell that device is connected
|
//send activity feeds to tell that device is connected
|
||||||
def notificationMessage = "is connected to SmartThings"
|
def notificationMessage = "is connected to SmartThings"
|
||||||
sendActivityFeeds(notificationMessage)
|
sendActivityFeeds(notificationMessage)
|
||||||
state.timeSendPush = null
|
atomicState.timeSendPush = null
|
||||||
|
atomicState.reAttempt = 0
|
||||||
|
|
||||||
pollHandler() //first time polling data data from thermostat
|
pollHandler() //first time polling data data from thermostat
|
||||||
|
|
||||||
//automatically update devices status every 5 mins
|
//automatically update devices status every 5 mins
|
||||||
runEvery5Minutes("poll")
|
runEvery5Minutes("poll")
|
||||||
|
|
||||||
//since access_token expires every 2 hours
|
|
||||||
runEvery1Hour("refreshAuthToken")
|
|
||||||
|
|
||||||
atomicState.reAttempt = 0
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def pollHandler() {
|
def pollHandler() {
|
||||||
@@ -389,18 +384,10 @@ def pollHandler() {
|
|||||||
def pollChildren(child = null) {
|
def pollChildren(child = null) {
|
||||||
def thermostatIdsString = getChildDeviceIdsString()
|
def thermostatIdsString = getChildDeviceIdsString()
|
||||||
log.debug "polling children: $thermostatIdsString"
|
log.debug "polling children: $thermostatIdsString"
|
||||||
|
def data = ""
|
||||||
|
|
||||||
def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + thermostatIdsString + '","includeExtendedRuntime":"true","includeSettings":"true","includeRuntime":"true","includeSensors":true}}'
|
def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + thermostatIdsString + '","includeExtendedRuntime":"true","includeSettings":"true","includeRuntime":"true","includeSensors":true}}'
|
||||||
def result = false
|
def result = false
|
||||||
// // TODO: test this:
|
|
||||||
//
|
|
||||||
// def jsonRequestBody = toJson([
|
|
||||||
// selection:[
|
|
||||||
// selectionType: "thermostats",
|
|
||||||
// selectionMatch: getChildDeviceIdsString(),
|
|
||||||
// includeRuntime: true
|
|
||||||
// ]
|
|
||||||
// ])
|
|
||||||
|
|
||||||
def pollParams = [
|
def pollParams = [
|
||||||
uri: apiEndpoint,
|
uri: apiEndpoint,
|
||||||
@@ -411,11 +398,6 @@ def pollChildren(child = null) {
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
httpGet(pollParams) { resp ->
|
httpGet(pollParams) { resp ->
|
||||||
|
|
||||||
// if (resp.data) {
|
|
||||||
// debugEventFromParent(child, "pollChildren(child) >> resp.status = ${resp.status}, resp.data = ${resp.data}")
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(resp.status == 200) {
|
if(resp.status == 200) {
|
||||||
log.debug "poll results returned resp.data ${resp.data}"
|
log.debug "poll results returned resp.data ${resp.data}"
|
||||||
atomicState.remoteSensors = resp.data.thermostatList.remoteSensors
|
atomicState.remoteSensors = resp.data.thermostatList.remoteSensors
|
||||||
@@ -426,20 +408,41 @@ def pollChildren(child = null) {
|
|||||||
|
|
||||||
log.debug "updating dni $dni"
|
log.debug "updating dni $dni"
|
||||||
|
|
||||||
def data = [
|
data = [
|
||||||
coolMode: (stat.settings.coolStages > 0),
|
coolMode: (stat.settings.coolStages > 0),
|
||||||
heatMode: (stat.settings.heatStages > 0),
|
heatMode: (stat.settings.heatStages > 0),
|
||||||
|
deviceTemperatureUnit: stat.settings.useCelsius,
|
||||||
|
minHeatingSetpoint: (stat.settings.heatRangeLow / 10),
|
||||||
|
maxHeatingSetpoint: (stat.settings.heatRangeHigh / 10),
|
||||||
|
minCoolingSetpoint: (stat.settings.coolRangeLow / 10),
|
||||||
|
maxCoolingSetpoint: (stat.settings.coolRangeHigh / 10),
|
||||||
autoMode: stat.settings.autoHeatCoolFeatureEnabled,
|
autoMode: stat.settings.autoHeatCoolFeatureEnabled,
|
||||||
auxHeatMode: (stat.settings.hasHeatPump) && (stat.settings.hasForcedAir || stat.settings.hasElectric || stat.settings.hasBoiler),
|
auxHeatMode: (stat.settings.hasHeatPump) && (stat.settings.hasForcedAir || stat.settings.hasElectric || stat.settings.hasBoiler),
|
||||||
temperature: stat.runtime.actualTemperature / 10,
|
temperature: (stat.runtime.actualTemperature / 10),
|
||||||
heatingSetpoint: stat.runtime.desiredHeat / 10,
|
heatingSetpoint: stat.runtime.desiredHeat / 10,
|
||||||
coolingSetpoint: stat.runtime.desiredCool / 10,
|
coolingSetpoint: stat.runtime.desiredCool / 10,
|
||||||
thermostatMode: stat.settings.hvacMode
|
thermostatMode: stat.settings.hvacMode,
|
||||||
|
humidity: stat.runtime.actualHumidity
|
||||||
]
|
]
|
||||||
data["temperature"] = data["temperature"] ? data["temperature"].toDouble().toInteger() : data["temperature"]
|
|
||||||
data["heatingSetpoint"] = data["heatingSetpoint"] ? data["heatingSetpoint"].toDouble().toInteger() : data["heatingSetpoint"]
|
if (location.temperatureScale == "F")
|
||||||
data["coolingSetpoint"] = data["coolingSetpoint"] ? data["coolingSetpoint"].toDouble().toInteger() : data["coolingSetpoint"]
|
{
|
||||||
// debugEventFromParent(child, "Event Data = ${data}")
|
data["temperature"] = data["temperature"] ? Math.round(data["temperature"].toDouble()) : data["temperature"]
|
||||||
|
data["heatingSetpoint"] = data["heatingSetpoint"] ? Math.round(data["heatingSetpoint"].toDouble()) : data["heatingSetpoint"]
|
||||||
|
data["coolingSetpoint"] = data["coolingSetpoint"] ? Math.round(data["coolingSetpoint"].toDouble()) : data["coolingSetpoint"]
|
||||||
|
data["minHeatingSetpoint"] = data["minHeatingSetpoint"] ? Math.round(data["minHeatingSetpoint"].toDouble()) : data["minHeatingSetpoint"]
|
||||||
|
data["maxHeatingSetpoint"] = data["maxHeatingSetpoint"] ? Math.round(data["maxHeatingSetpoint"].toDouble()) : data["maxHeatingSetpoint"]
|
||||||
|
data["minCoolingSetpoint"] = data["minCoolingSetpoint"] ? Math.round(data["minCoolingSetpoint"].toDouble()) : data["minCoolingSetpoint"]
|
||||||
|
data["maxCoolingSetpoint"] = data["maxCoolingSetpoint"] ? Math.round(data["maxCoolingSetpoint"].toDouble()) : data["maxCoolingSetpoint"]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data?.deviceTemperatureUnit == false && location.temperatureScale == "F") {
|
||||||
|
data["deviceTemperatureUnit"] = "F"
|
||||||
|
|
||||||
|
} else {
|
||||||
|
data["deviceTemperatureUnit"] = "C"
|
||||||
|
}
|
||||||
|
|
||||||
collector[dni] = [data:data]
|
collector[dni] = [data:data]
|
||||||
return collector
|
return collector
|
||||||
@@ -450,9 +453,8 @@ def pollChildren(child = null) {
|
|||||||
log.error "polling children & got http status ${resp.status}"
|
log.error "polling children & got http status ${resp.status}"
|
||||||
|
|
||||||
//refresh the auth token
|
//refresh the auth token
|
||||||
if (resp.status == 500 && resp.data.status.code == 14) {
|
if (resp.data.status.code == 14) {
|
||||||
log.debug "Storing the failed action to try later"
|
atomicState.action = "pollChildren"
|
||||||
atomicState.action = "pollChildren";
|
|
||||||
log.debug "Refreshing your auth_token!"
|
log.debug "Refreshing your auth_token!"
|
||||||
refreshAuthToken()
|
refreshAuthToken()
|
||||||
}
|
}
|
||||||
@@ -463,7 +465,6 @@ def pollChildren(child = null) {
|
|||||||
}
|
}
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
log.debug "___exception polling children: " + e
|
log.debug "___exception polling children: " + e
|
||||||
// debugEventFromParent(child, "___exception polling children: " + e)
|
|
||||||
refreshAuthToken()
|
refreshAuthToken()
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -476,18 +477,14 @@ def pollChild(child){
|
|||||||
if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")){
|
if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")){
|
||||||
if(atomicState.thermostats[child.device.deviceNetworkId] != null) {
|
if(atomicState.thermostats[child.device.deviceNetworkId] != null) {
|
||||||
def tData = atomicState.thermostats[child.device.deviceNetworkId]
|
def tData = atomicState.thermostats[child.device.deviceNetworkId]
|
||||||
// debugEventFromParent(child, "pollChild(child)>> data for ${child.device.deviceNetworkId} : ${tData.data}") //TODO comment
|
|
||||||
log.info "pollChild(child)>> data for ${child.device.deviceNetworkId} : ${tData.data}"
|
log.info "pollChild(child)>> data for ${child.device.deviceNetworkId} : ${tData.data}"
|
||||||
child.generateEvent(tData.data) //parse received message from parent
|
child.generateEvent(tData.data) //parse received message from parent
|
||||||
// return tData.data
|
|
||||||
} else if(atomicState.thermostats[child.device.deviceNetworkId] == null) {
|
} else if(atomicState.thermostats[child.device.deviceNetworkId] == null) {
|
||||||
// debugEventFromParent(child, "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId} after polling") //TODO comment
|
|
||||||
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId}"
|
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId}"
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// debugEventFromParent(child, "ERROR: pollChildren(child) for ${child.device.deviceNetworkId} after polling") //TODO comment
|
|
||||||
log.info "ERROR: pollChildren(child) for ${child.device.deviceNetworkId} after polling"
|
log.info "ERROR: pollChildren(child) for ${child.device.deviceNetworkId} after polling"
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -513,9 +510,6 @@ def availableModes(child) {
|
|||||||
{
|
{
|
||||||
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId} after polling"
|
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId} after polling"
|
||||||
|
|
||||||
// TODO: flag device as in error state
|
|
||||||
// child.errorState = true
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,8 +536,6 @@ def currentMode(child) {
|
|||||||
if(!tData) {
|
if(!tData) {
|
||||||
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId} after polling"
|
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId} after polling"
|
||||||
|
|
||||||
// TODO: flag device as in error state
|
|
||||||
// child.errorState = true
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -561,8 +553,12 @@ def updateSensorData() {
|
|||||||
def occupancy = ""
|
def occupancy = ""
|
||||||
it.capability.each {
|
it.capability.each {
|
||||||
if (it.type == "temperature") {
|
if (it.type == "temperature") {
|
||||||
temperature = it.value as Double
|
if (location.temperatureScale == "F") {
|
||||||
temperature = (temperature / 10).toInteger()
|
temperature = Math.round(it.value.toDouble() / 10)
|
||||||
|
} else {
|
||||||
|
temperature = convertFtoC(it.value.toDouble() / 10)
|
||||||
|
}
|
||||||
|
|
||||||
} else if (it.type == "occupancy") {
|
} else if (it.type == "occupancy") {
|
||||||
if(it.value == "true")
|
if(it.value == "true")
|
||||||
occupancy = "active"
|
occupancy = "active"
|
||||||
@@ -575,7 +571,6 @@ def updateSensorData() {
|
|||||||
if(d) {
|
if(d) {
|
||||||
d.sendEvent(name:"temperature", value: temperature)
|
d.sendEvent(name:"temperature", value: temperature)
|
||||||
d.sendEvent(name:"motion", value: occupancy)
|
d.sendEvent(name:"motion", value: occupancy)
|
||||||
// debugEventFromParent(d, "temperature : ${temperature}, motion:${occupancy}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -595,64 +590,63 @@ def toQueryString(Map m) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private refreshAuthToken() {
|
private refreshAuthToken() {
|
||||||
log.debug "refreshing auth token"
|
log.debug "refreshing auth token"
|
||||||
|
|
||||||
if(!atomicState.refreshToken) {
|
if(!atomicState.refreshToken) {
|
||||||
log.warn "Can not refresh OAuth token since there is no refreshToken stored"
|
log.warn "Can not refresh OAuth token since there is no refreshToken stored"
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
def refreshParams = [
|
def refreshParams = [
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
uri : apiEndpoint,
|
uri : apiEndpoint,
|
||||||
path : "/token",
|
path : "/token",
|
||||||
query : [grant_type: 'refresh_token', code: "${atomicState.refreshToken}", client_id: smartThingsClientId],
|
query : [grant_type: 'refresh_token', code: "${atomicState.refreshToken}", client_id: smartThingsClientId],
|
||||||
]
|
]
|
||||||
|
|
||||||
log.debug refreshParams
|
log.debug refreshParams
|
||||||
|
|
||||||
def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials."
|
def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials."
|
||||||
//changed to httpPost
|
//changed to httpPost
|
||||||
try {
|
try {
|
||||||
def jsonMap
|
def jsonMap
|
||||||
httpPost(refreshParams) { resp ->
|
httpPost(refreshParams) { resp ->
|
||||||
|
|
||||||
if(resp.status == 200) {
|
if(resp.status == 200) {
|
||||||
log.debug "Token refreshed...calling saved RestAction now!"
|
log.debug "Token refreshed...calling saved RestAction now!"
|
||||||
|
|
||||||
debugEvent("Token refreshed ... calling saved RestAction now!")
|
debugEvent("Token refreshed ... calling saved RestAction now!")
|
||||||
|
|
||||||
log.debug resp
|
log.debug resp
|
||||||
|
|
||||||
jsonMap = resp.data
|
jsonMap = resp.data
|
||||||
|
|
||||||
if(resp.data) {
|
if(resp.data) {
|
||||||
|
|
||||||
log.debug resp.data
|
log.debug resp.data
|
||||||
debugEvent("Response = ${resp.data}")
|
debugEvent("Response = ${resp.data}")
|
||||||
|
|
||||||
atomicState.refreshToken = resp?.data?.refresh_token
|
atomicState.refreshToken = resp?.data?.refresh_token
|
||||||
atomicState.authToken = resp?.data?.access_token
|
atomicState.authToken = resp?.data?.access_token
|
||||||
|
|
||||||
debugEvent("Refresh Token = ${atomicState.refreshToken}")
|
debugEvent("Refresh Token = ${atomicState.refreshToken}")
|
||||||
debugEvent("OAUTH Token = ${atomicState.authToken}")
|
debugEvent("OAUTH Token = ${atomicState.authToken}")
|
||||||
|
|
||||||
if(atomicState.action && atomicState.action != "") {
|
if(atomicState.action && atomicState.action != "") {
|
||||||
log.debug "Executing next action: ${atomicState.action}"
|
log.debug "Executing next action: ${atomicState.action}"
|
||||||
|
|
||||||
"${atomicState.action}"()
|
"${atomicState.action}"()
|
||||||
|
|
||||||
//remove saved action
|
atomicState.action = ""
|
||||||
atomicState.action = ""
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
atomicState.action = ""
|
atomicState.action = ""
|
||||||
} else {
|
} else {
|
||||||
log.debug "refresh failed ${resp.status} : ${resp.status.code}"
|
log.debug "refresh failed ${resp.status} : ${resp.status.code}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
} catch (groovyx.net.http.HttpResponseException e) {
|
||||||
log.error "refreshAuthToken() >> Error: e.statusCode ${e.statusCode}"
|
log.error "refreshAuthToken() >> Error: e.statusCode ${e.statusCode}"
|
||||||
def reAttemptPeriod = 300 // in sec
|
def reAttemptPeriod = 300 // in sec
|
||||||
if (e.statusCode != 401) { //this issue might comes from exceed 20sec app execution, connectivity issue etc.
|
if (e.statusCode != 401) { //this issue might comes from exceed 20sec app execution, connectivity issue etc.
|
||||||
runIn(reAttemptPeriod, "refreshAuthToken")
|
runIn(reAttemptPeriod, "refreshAuthToken")
|
||||||
@@ -665,20 +659,16 @@ private refreshAuthToken() {
|
|||||||
sendPushAndFeeds(notificationMessage)
|
sendPushAndFeeds(notificationMessage)
|
||||||
atomicState.reAttempt = 0
|
atomicState.reAttempt = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def resumeProgram(child, deviceId) {
|
def resumeProgram(child, deviceId) {
|
||||||
|
|
||||||
// def thermostatIdsString = getChildDeviceIdsString()
|
|
||||||
// log.debug "resumeProgram children: $thermostatIdsString"
|
|
||||||
|
|
||||||
def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + deviceId + '","includeRuntime":true},"functions": [{"type": "resumeProgram"}]}'
|
def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + deviceId + '","includeRuntime":true},"functions": [{"type": "resumeProgram"}]}'
|
||||||
//, { "type": "sendMessage", "params": { "text": "Setpoint Updated" } }
|
|
||||||
def result = sendJson(jsonRequestBody)
|
def result = sendJson(jsonRequestBody)
|
||||||
// debugEventFromParent(child, "resumeProgram(child) with result ${result}")
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,27 +677,16 @@ def setHold(child, heating, cooling, deviceId, sendHoldType) {
|
|||||||
int h = heating * 10
|
int h = heating * 10
|
||||||
int c = cooling * 10
|
int c = cooling * 10
|
||||||
|
|
||||||
// log.debug "setpoints____________ - h: $heating - $h, c: $cooling - $c"
|
|
||||||
// def thermostatIdsString = getChildDeviceIdsString()
|
|
||||||
|
|
||||||
def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + deviceId + '","includeRuntime":true},"functions": [{ "type": "setHold", "params": { "coolHoldTemp": '+c+',"heatHoldTemp": '+h+', "holdType": '+sendHoldType+' } } ]}'
|
def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + deviceId + '","includeRuntime":true},"functions": [{ "type": "setHold", "params": { "coolHoldTemp": '+c+',"heatHoldTemp": '+h+', "holdType": '+sendHoldType+' } } ]}'
|
||||||
// def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + thermostatIdsString + '","includeRuntime":true},"functions": [{"type": "resumeProgram"}, { "type": "setHold", "params": { "coolHoldTemp": '+c+',"heatHoldTemp": '+h+', "holdType": "indefinite" } } ]}'
|
|
||||||
def result = sendJson(child, jsonRequestBody)
|
def result = sendJson(child, jsonRequestBody)
|
||||||
// debugEventFromParent(child, "setHold: heating: ${h}, cooling: ${c} with result ${result}")
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
def setMode(child, mode, deviceId) {
|
def setMode(child, mode, deviceId) {
|
||||||
// def thermostatIdsString = getChildDeviceIdsString()
|
|
||||||
// log.debug "setCoolingSetpoint children: $thermostatIdsString"
|
|
||||||
|
|
||||||
def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + deviceId + '","includeRuntime":true},"thermostat": {"settings":{"hvacMode":"'+"${mode}"+'"}}}'
|
def jsonRequestBody = '{"selection":{"selectionType":"thermostats","selectionMatch":"' + deviceId + '","includeRuntime":true},"thermostat": {"settings":{"hvacMode":"'+"${mode}"+'"}}}'
|
||||||
|
|
||||||
// log.debug "Mode Request Body = ${jsonRequestBody}"
|
|
||||||
// debugEvent ("Mode Request Body = ${jsonRequestBody}")
|
|
||||||
|
|
||||||
def result = sendJson(jsonRequestBody)
|
def result = sendJson(jsonRequestBody)
|
||||||
// debugEventFromParent(child, "setMode to ${mode} with result ${result}")
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,8 +703,6 @@ def sendJson(child = null, String jsonBody) {
|
|||||||
try{
|
try{
|
||||||
httpPost(cmdParams) { resp ->
|
httpPost(cmdParams) { resp ->
|
||||||
|
|
||||||
// debugEventFromParent(child, "sendJson >> resp.status ${resp.status}, resp.data: ${resp.data}")
|
|
||||||
|
|
||||||
if(resp.status == 200) {
|
if(resp.status == 200) {
|
||||||
|
|
||||||
log.debug "updated ${resp.data}"
|
log.debug "updated ${resp.data}"
|
||||||
@@ -741,8 +718,7 @@ def sendJson(child = null, String jsonBody) {
|
|||||||
debugEvent ("sent Json & got http status ${resp.status} - ${resp.status.code}")
|
debugEvent ("sent Json & got http status ${resp.status} - ${resp.status.code}")
|
||||||
|
|
||||||
//refresh the auth token
|
//refresh the auth token
|
||||||
if (resp.status == 500 && resp.status.code == 14) {
|
if (resp.status.code == 14) {
|
||||||
//log.debug "Storing the failed action to try later"
|
|
||||||
log.debug "Refreshing your auth_token!"
|
log.debug "Refreshing your auth_token!"
|
||||||
debugEvent ("Refreshing OAUTH Token")
|
debugEvent ("Refreshing OAUTH Token")
|
||||||
refreshAuthToken()
|
refreshAuthToken()
|
||||||
@@ -757,7 +733,7 @@ def sendJson(child = null, String jsonBody) {
|
|||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
log.debug "Exception Sending Json: " + e
|
log.debug "Exception Sending Json: " + e
|
||||||
debugEvent ("Exception Sending JSON: " + e)
|
debugEvent ("Exception Sending JSON: " + e)
|
||||||
refreshAuthToken()
|
refreshAuthToken()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -794,25 +770,37 @@ def debugEventFromParent(child, message) {
|
|||||||
|
|
||||||
//send both push notification and mobile activity feeds
|
//send both push notification and mobile activity feeds
|
||||||
def sendPushAndFeeds(notificationMessage){
|
def sendPushAndFeeds(notificationMessage){
|
||||||
log.warn "sendPushAndFeeds >> notificationMessage: ${notificationMessage}"
|
log.warn "sendPushAndFeeds >> notificationMessage: ${notificationMessage}"
|
||||||
log.warn "sendPushAndFeeds >> atomicState.timeSendPush: ${atomicState.timeSendPush}"
|
log.warn "sendPushAndFeeds >> atomicState.timeSendPush: ${atomicState.timeSendPush}"
|
||||||
if (atomicState.timeSendPush){
|
if (atomicState.timeSendPush){
|
||||||
if (now() - atomicState.timeSendPush > 86400000){ // notification is sent to remind user once a day
|
if (now() - atomicState.timeSendPush > 86400000){ // notification is sent to remind user once a day
|
||||||
sendPush("Your Ecobee thermostat " + notificationMessage)
|
sendPush("Your Ecobee thermostat " + notificationMessage)
|
||||||
sendActivityFeeds(notificationMessage)
|
sendActivityFeeds(notificationMessage)
|
||||||
atomicState.timeSendPush = now()
|
atomicState.timeSendPush = now()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sendPush("Your Ecobee thermostat " + notificationMessage)
|
sendPush("Your Ecobee thermostat " + notificationMessage)
|
||||||
sendActivityFeeds(notificationMessage)
|
sendActivityFeeds(notificationMessage)
|
||||||
atomicState.timeSendPush = now()
|
atomicState.timeSendPush = now()
|
||||||
}
|
}
|
||||||
atomicState.authToken = null
|
atomicState.authToken = null
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendActivityFeeds(notificationMessage) {
|
def sendActivityFeeds(notificationMessage) {
|
||||||
def devices = getChildDevices()
|
def devices = getChildDevices()
|
||||||
devices.each { child ->
|
devices.each { child ->
|
||||||
child.generateActivityFeedsEvent(notificationMessage) //parse received message from parent
|
child.generateActivityFeedsEvent(notificationMessage) //parse received message from parent
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def roundC (tempC) {
|
||||||
|
return String.format("%.1f", (Math.round(tempC * 2))/2)
|
||||||
|
}
|
||||||
|
|
||||||
|
def convertFtoC (tempF) {
|
||||||
|
return String.format("%.1f", (Math.round(((tempF - 32)*(5/9)) * 2))/2)
|
||||||
|
}
|
||||||
|
|
||||||
|
def convertCtoF (tempC) {
|
||||||
|
return (Math.round(tempC * (9/5)) + 32).toInteger()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user