Compare commits

..

2 Commits

Author SHA1 Message Date
Amol Mundayoor
9dd201be3b Adding changes mentioned by @strelitzia123 2016-11-22 10:49:51 -08:00
Brian Steere
9433598381 Update netatmo-connect to use non-deprecated api
devicedata has been deprecated in favor of getstationsdata and will be removed at the end of November.

*Note* I haven't actually tested this change due to time limitations. Someone should check this out and verify it behaves properly before merging.
2016-10-14 10:22:36 -05:00
17 changed files with 323 additions and 335 deletions

View File

@@ -148,12 +148,14 @@ def generateEvent(Map results) {
handlerName: name] handlerName: name]
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) { if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) {
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger() 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, unit: temperatureScale, isStateChange: isChange, displayed: isDisplayed] event << [value: sendValue, unit: temperatureScale, isStateChange: isChange, displayed: isDisplayed]
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") { } else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger() def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
event << [value: sendValue, unit: temperatureScale, displayed: false] event << [value: sendValue, unit: temperatureScale, displayed: false]
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){ } else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
isChange = isStateChange(device, name, value.toString()) isChange = isStateChange(device, name, value.toString())
@@ -251,6 +253,7 @@ void setCoolingSetpoint(setpoint) {
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint") def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint") def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
if (coolingSetpoint > maxCoolingSetpoint) { if (coolingSetpoint > maxCoolingSetpoint) {
coolingSetpoint = maxCoolingSetpoint coolingSetpoint = maxCoolingSetpoint
} else if (coolingSetpoint < minCoolingSetpoint) { } else if (coolingSetpoint < minCoolingSetpoint) {
@@ -280,6 +283,7 @@ void setCoolingSetpoint(setpoint) {
} }
void resumeProgram() { void resumeProgram() {
log.debug "resumeProgram() is called" log.debug "resumeProgram() is called"
sendEvent("name":"thermostatStatus", "value":"resuming schedule", "description":statusText, displayed: false) sendEvent("name":"thermostatStatus", "value":"resuming schedule", "description":statusText, displayed: false)
def deviceId = device.deviceNetworkId.split(/\./).last() def deviceId = device.deviceNetworkId.split(/\./).last()
@@ -350,6 +354,7 @@ def switchFanMode() {
} }
def switchToFanMode(nextMode) { def switchToFanMode(nextMode) {
log.debug "switching to fan mode: $nextMode" log.debug "switching to fan mode: $nextMode"
def returnCommand def returnCommand
@@ -515,56 +520,63 @@ def fanAuto() {
} }
def generateSetpointEvent() { def generateSetpointEvent() {
log.debug "Generate SetPoint Event" log.debug "Generate SetPoint Event"
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
log.debug "Current Mode = ${mode}"
def heatingSetpoint = device.currentValue("heatingSetpoint") def heatingSetpoint = device.currentValue("heatingSetpoint")
log.debug "Heating Setpoint = ${heatingSetpoint}"
def coolingSetpoint = device.currentValue("coolingSetpoint") def coolingSetpoint = device.currentValue("coolingSetpoint")
log.debug "Cooling Setpoint = ${coolingSetpoint}"
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint") def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint")
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint") def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint") def minHeatingSetpoint = device.currentValue("minHeatingSetpoint")
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint") def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
if(location.temperatureScale == "C") { if(location.temperatureScale == "C")
maxHeatingSetpoint = maxHeatingSetpoint > 40 ? roundC(convertFtoC(maxHeatingSetpoint)) : roundC(maxHeatingSetpoint) {
maxCoolingSetpoint = maxCoolingSetpoint > 40 ? roundC(convertFtoC(maxCoolingSetpoint)) : roundC(maxCoolingSetpoint) maxHeatingSetpoint = roundC(maxHeatingSetpoint)
minHeatingSetpoint = minHeatingSetpoint > 40 ? roundC(convertFtoC(minHeatingSetpoint)) : roundC(minHeatingSetpoint) maxCoolingSetpoint = roundC(maxCoolingSetpoint)
minCoolingSetpoint = minCoolingSetpoint > 40 ? roundC(convertFtoC(minCoolingSetpoint)) : roundC(minCoolingSetpoint) minHeatingSetpoint = roundC(minHeatingSetpoint)
heatingSetpoint = heatingSetpoint > 40 ? roundC(convertFtoC(heatingSetpoint)) : roundC(heatingSetpoint) minCoolingSetpoint = roundC(minCoolingSetpoint)
coolingSetpoint = coolingSetpoint > 40 ? roundC(convertFtoC(coolingSetpoint)) : roundC(coolingSetpoint) heatingSetpoint = roundC(heatingSetpoint)
} else { coolingSetpoint = roundC(coolingSetpoint)
maxHeatingSetpoint = maxHeatingSetpoint < 40 ? roundC(convertCtoF(maxHeatingSetpoint)) : maxHeatingSetpoint
maxCoolingSetpoint = maxCoolingSetpoint < 40 ? roundC(convertCtoF(maxCoolingSetpoint)) : maxCoolingSetpoint
minHeatingSetpoint = minHeatingSetpoint < 40 ? roundC(convertCtoF(minHeatingSetpoint)) : minHeatingSetpoint
minCoolingSetpoint = minCoolingSetpoint < 40 ? roundC(convertCtoF(minCoolingSetpoint)) : minCoolingSetpoint
heatingSetpoint = heatingSetpoint < 40 ? roundC(convertCtoF(heatingSetpoint)) : heatingSetpoint
coolingSetpoint = coolingSetpoint < 40 ? roundC(convertCtoF(coolingSetpoint)) : coolingSetpoint
} }
log.debug "Current Mode = ${mode}"
log.debug "Heating Setpoint = ${heatingSetpoint}"
log.debug "Cooling Setpoint = ${coolingSetpoint}"
sendEvent("name":"maxHeatingSetpoint", "value":maxHeatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"maxHeatingSetpoint", "value":maxHeatingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"maxCoolingSetpoint", "value":maxCoolingSetpoint, "unit":location.temperatureScale) sendEvent("name":"maxCoolingSetpoint", "value":maxCoolingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"minHeatingSetpoint", "value":minHeatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"minHeatingSetpoint", "value":minHeatingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"minCoolingSetpoint", "value":minCoolingSetpoint, "unit":location.temperatureScale) sendEvent("name":"minCoolingSetpoint", "value":minCoolingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
if (mode == "heat") { if (mode == "heat") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
} }
else if (mode == "cool") { else if (mode == "cool") {
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale) sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
} else if (mode == "auto") { } else if (mode == "auto") {
sendEvent("name":"thermostatSetpoint", "value":"Auto") sendEvent("name":"thermostatSetpoint", "value":"Auto")
} else if (mode == "off") { } else if (mode == "off") {
sendEvent("name":"thermostatSetpoint", "value":"Off") sendEvent("name":"thermostatSetpoint", "value":"Off")
} else if (mode == "auxHeatOnly") { } else if (mode == "auxHeatOnly") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
} }
} }
void raiseSetpoint() { void raiseSetpoint() {
@@ -573,31 +585,21 @@ void raiseSetpoint() {
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint") def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint")
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint") 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") def heatingSetpoint = device.currentValue("heatingSetpoint")
def coolingSetpoint = device.currentValue("coolingSetpoint") def coolingSetpoint = device.currentValue("coolingSetpoint")
def thermostatSetpoint = device.currentValue("thermostatSetpoint") def thermostatSetpoint = device.currentValue("thermostatSetpoint")
if (location.temperatureScale == "C") {
maxHeatingSetpoint = maxHeatingSetpoint > 40 ? convertFtoC(maxHeatingSetpoint) : maxHeatingSetpoint
maxCoolingSetpoint = maxCoolingSetpoint > 40 ? convertFtoC(maxCoolingSetpoint) : maxCoolingSetpoint
heatingSetpoint = heatingSetpoint > 40 ? convertFtoC(heatingSetpoint) : heatingSetpoint
coolingSetpoint = coolingSetpoint > 40 ? convertFtoC(coolingSetpoint) : coolingSetpoint
thermostatSetpoint = thermostatSetpoint > 40 ? convertFtoC(thermostatSetpoint) : thermostatSetpoint
} else {
maxHeatingSetpoint = maxHeatingSetpoint < 40 ? convertCtoF(maxHeatingSetpoint) : maxHeatingSetpoint
maxCoolingSetpoint = maxCoolingSetpoint < 40 ? convertCtoF(maxCoolingSetpoint) : maxCoolingSetpoint
heatingSetpoint = heatingSetpoint < 40 ? convertCtoF(heatingSetpoint) : heatingSetpoint
coolingSetpoint = coolingSetpoint < 40 ? convertCtoF(coolingSetpoint) : coolingSetpoint
thermostatSetpoint = thermostatSetpoint < 40 ? convertCtoF(thermostatSetpoint) : thermostatSetpoint
}
log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}" log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0 if (device.latestState('thermostatSetpoint')) {
targetvalue = device.latestState('thermostatSetpoint').value
targetvalue = location.temperatureScale == "F"? targetvalue.toInteger() : targetvalue.toDouble()
} else {
targetvalue = 0
}
targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5 targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue > maxHeatingSetpoint) { if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue > maxHeatingSetpoint) {
@@ -620,29 +622,20 @@ void lowerSetpoint() {
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint") def minHeatingSetpoint = device.currentValue("minHeatingSetpoint")
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint") 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") def heatingSetpoint = device.currentValue("heatingSetpoint")
def coolingSetpoint = device.currentValue("coolingSetpoint") def coolingSetpoint = device.currentValue("coolingSetpoint")
def thermostatSetpoint = device.currentValue("thermostatSetpoint") def thermostatSetpoint = device.currentValue("thermostatSetpoint")
if (location.temperatureScale == "C") {
minHeatingSetpoint = minHeatingSetpoint > 40 ? convertFtoC(minHeatingSetpoint) : minHeatingSetpoint
minCoolingSetpoint = minCoolingSetpoint > 40 ? convertFtoC(minCoolingSetpoint) : minCoolingSetpoint
heatingSetpoint = heatingSetpoint > 40 ? convertFtoC(heatingSetpoint) : heatingSetpoint
coolingSetpoint = coolingSetpoint > 40 ? convertFtoC(coolingSetpoint) : coolingSetpoint
thermostatSetpoint = thermostatSetpoint > 40 ? convertFtoC(thermostatSetpoint) : thermostatSetpoint
} else {
minHeatingSetpoint = minHeatingSetpoint < 40 ? convertCtoF(minHeatingSetpoint) : minHeatingSetpoint
minCoolingSetpoint = minCoolingSetpoint < 40 ? convertCtoF(minCoolingSetpoint) : minCoolingSetpoint
heatingSetpoint = heatingSetpoint < 40 ? convertCtoF(heatingSetpoint) : heatingSetpoint
coolingSetpoint = coolingSetpoint < 40 ? convertCtoF(coolingSetpoint) : coolingSetpoint
thermostatSetpoint = thermostatSetpoint < 40 ? convertCtoF(thermostatSetpoint) : 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')) {
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0 targetvalue = device.latestState('thermostatSetpoint').value
targetvalue = location.temperatureScale == "F"? targetvalue.toInteger() : targetvalue.toDouble()
} else {
targetvalue = 0
}
targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5 targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue < minHeatingSetpoint) { if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue < minHeatingSetpoint) {
@@ -660,6 +653,7 @@ void lowerSetpoint() {
//called by raiseSetpoint() and lowerSetpoint() //called by raiseSetpoint() and lowerSetpoint()
void alterSetpoint(temp) { void alterSetpoint(temp) {
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
if (mode == "off" || mode == "auto") { if (mode == "off" || mode == "auto") {
@@ -672,18 +666,6 @@ void alterSetpoint(temp) {
def targetHeatingSetpoint def targetHeatingSetpoint
def targetCoolingSetpoint def targetCoolingSetpoint
def temperatureScaleHasChanged = false
if (location.temperatureScale == "C") {
if ( heatingSetpoint > 40.0 || coolingSetpoint > 40.0 ) {
temperatureScaleHasChanged = true
}
} else {
if ( heatingSetpoint < 40.0 || coolingSetpoint < 40.0 ) {
temperatureScaleHasChanged = true
}
}
//step1: check thermostatMode, enforce limits before sending request to cloud //step1: check thermostatMode, enforce limits before sending request to cloud
if (mode == "heat" || mode == "auxHeatOnly"){ if (mode == "heat" || mode == "auxHeatOnly"){
if (temp.value > coolingSetpoint){ if (temp.value > coolingSetpoint){
@@ -725,18 +707,17 @@ void alterSetpoint(temp) {
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false) sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
} }
} }
if ( temperatureScaleHasChanged )
generateSetpointEvent()
generateStatusEvent() generateStatusEvent()
} }
} }
def generateStatusEvent() { def generateStatusEvent() {
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint") def heatingSetpoint = device.currentValue("heatingSetpoint")
def coolingSetpoint = device.currentValue("coolingSetpoint") def coolingSetpoint = device.currentValue("coolingSetpoint")
def temperature = device.currentValue("temperature") def temperature = device.currentValue("temperature")
def statusText def statusText
log.debug "Generate Status Event for Mode = ${mode}" log.debug "Generate Status Event for Mode = ${mode}"
@@ -746,25 +727,36 @@ def generateStatusEvent() {
log.debug "HVAC Mode = ${mode}" log.debug "HVAC Mode = ${mode}"
if (mode == "heat") { if (mode == "heat") {
if (temperature >= heatingSetpoint) if (temperature >= heatingSetpoint)
statusText = "Right Now: Idle" statusText = "Right Now: Idle"
else else
statusText = "Heating to ${heatingSetpoint} ${location.temperatureScale}" 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} ${location.temperatureScale}" statusText = "Cooling to ${coolingSetpoint} ${location.temperatureScale}"
} else if (mode == "auto") {
statusText = "Right Now: Auto"
} else if (mode == "off") {
statusText = "Right Now: Off"
} else if (mode == "auxHeatOnly") {
statusText = "Emergency Heat"
} else {
statusText = "?"
}
} else if (mode == "auto") {
statusText = "Right Now: Auto"
} else if (mode == "off") {
statusText = "Right Now: Off"
} else if (mode == "auxHeatOnly") {
statusText = "Emergency Heat"
} else {
statusText = "?"
}
log.debug "Generate Status Event = ${statusText}" log.debug "Generate Status Event = ${statusText}"
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true) sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true)
} }
@@ -778,7 +770,7 @@ def roundC (tempC) {
} }
def convertFtoC (tempF) { def convertFtoC (tempF) {
return ((Math.round(((tempF - 32)*(5/9)) * 2))/2).toDouble() return String.format("%.1f", (Math.round(((tempF - 32)*(5/9)) * 2))/2)
} }
def convertCtoF (tempC) { def convertCtoF (tempC) {

View File

@@ -57,7 +57,7 @@ metadata {
} }
void installed() { void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
} }
// parse events into attributes // parse events into attributes
@@ -172,3 +172,6 @@ def verifyPercent(percent) {
} }
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -45,7 +45,7 @@ metadata {
} }
void installed() { void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
} }
// parse events into attributes // parse events into attributes
@@ -87,3 +87,6 @@ def parse(description) {
results results
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -66,7 +66,7 @@ metadata {
} }
void installed() { void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
} }
// parse events into attributes // parse events into attributes
@@ -188,3 +188,6 @@ def verifyPercent(percent) {
} }
} }
def ping() {
log.trace "${parent.ping(this)}"
}

View File

@@ -50,7 +50,7 @@ metadata {
} }
void installed() { void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
} }
// parse events into attributes // parse events into attributes
@@ -93,3 +93,6 @@ void refresh() {
parent.manualRefresh() parent.manualRefresh()
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -55,7 +55,7 @@ metadata {
} }
void installed() { void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
} }
// parse events into attributes // parse events into attributes
@@ -107,3 +107,6 @@ void refresh() {
parent.manualRefresh() parent.manualRefresh()
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -141,6 +141,7 @@ def setLevel(percentage) {
percentage = 1 // clamp to 1% percentage = 1 // clamp to 1%
} }
if (percentage == 0) { if (percentage == 0) {
sendEvent(name: "level", value: 0) // Otherwise the level value tile does not update
return off() // if the brightness is set to 0, just turn it off return off() // if the brightness is set to 0, just turn it off
} }
parent.logErrors(logObject:log) { parent.logErrors(logObject:log) {

View File

@@ -71,6 +71,7 @@ def setLevel(percentage) {
percentage = 1 // clamp to 1% percentage = 1 // clamp to 1%
} }
if (percentage == 0) { if (percentage == 0) {
sendEvent(name: "level", value: 0) // Otherwise the level value tile does not update
return off() // if the brightness is set to 0, just turn it off return off() // if the brightness is set to 0, just turn it off
} }
parent.logErrors(logObject:log) { parent.logErrors(logObject:log) {

View File

@@ -143,7 +143,7 @@ def refresh() {
def configure() { def configure() {
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time) // Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
// enrolls with default periodic reporting until newer 5 min interval is confirmed // enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
refresh() + zigbee.onOffConfig(0, 300) + powerConfig() refresh() + zigbee.onOffConfig(0, 300) + powerConfig()

View File

@@ -264,6 +264,7 @@ def refresh()
{ {
log.debug "refresh temperature, humidity, and battery" log.debug "refresh temperature, humidity, and battery"
return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware
zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware
zigbee.readAttribute(0x0402, 0x0000) + zigbee.readAttribute(0x0402, 0x0000) +
zigbee.readAttribute(0x0001, 0x0020) zigbee.readAttribute(0x0001, 0x0020)
} }

View File

@@ -104,7 +104,7 @@ def configure() {
log.debug "Configuring Reporting and Bindings." log.debug "Configuring Reporting and Bindings."
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time) // Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
// enrolls with default periodic reporting until newer 5 min interval is confirmed // enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig()

View File

@@ -28,8 +28,8 @@ metadata {
capability "Switch Level" capability "Switch Level"
capability "Health Check" capability "Health Check"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Gardenspot RGB", deviceJoinName: "OSRAM LIGHTIFY Gardenspot mini RGB" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Gardenspot RGB"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Gardenspot RGB", deviceJoinName: "OSRAM LIGHTIFY Gardenspot mini RGB" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Gardenspot RGB"
} }
// UI tile definitions // UI tile definitions

View File

@@ -145,7 +145,7 @@ def configure() {
log.debug "Configuring Reporting and Bindings." log.debug "Configuring Reporting and Bindings."
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time) // Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
// enrolls with default periodic reporting until newer 5 min interval is confirmed // enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
refresh() refresh()

View File

@@ -128,7 +128,7 @@ def configure() {
log.debug "Configuring Reporting and Bindings." log.debug "Configuring Reporting and Bindings."
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time) // Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
// enrolls with default periodic reporting until newer 5 min interval is confirmed // enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
refresh() refresh()

View File

@@ -387,18 +387,18 @@ def getDeviceList() {
state.deviceDetail = [:] state.deviceDetail = [:]
state.deviceState = [:] state.deviceState = [:]
apiGet("/api/devicelist") { response -> apiGet("/api/getstationsdata") { response ->
response.data.body.devices.each { value -> response.data.body.devices.each { value ->
def key = value._id def key = value._id
deviceList[key] = "${value.station_name}: ${value.module_name}" deviceList[key] = "${value.station_name}: ${value.module_name}"
state.deviceDetail[key] = value state.deviceDetail[key] = value
state.deviceState[key] = value.dashboard_data state.deviceState[key] = value.dashboard_data
value.modules.each { value2 ->
def key2 = value2._id
deviceList[key2] = "${value.station_name}: ${value2.module_name}"
state.deviceDetail[key2] = value2
state.deviceState[key2] = value2.dashboard_data
} }
response.data.body.modules.each { value ->
def key = value._id
deviceList[key] = "${state.deviceDetail[value.main_device].station_name}: ${value.module_name}"
state.deviceDetail[key] = value
state.deviceState[key] = value.dashboard_data
} }
} }

View File

@@ -37,10 +37,7 @@ preferences {
def mainPage() { def mainPage() {
def bridges = bridgesDiscovered() def bridges = bridgesDiscovered()
if (state.username && bridges) {
if (state.refreshUsernameNeeded) {
return bridgeLinking()
} else if (state.username && bridges) {
return bulbDiscovery() return bulbDiscovery()
} else { } else {
return bridgeDiscovery() return bridgeDiscovery()
@@ -107,20 +104,11 @@ def bridgeLinking() {
def title = "Linking with your Hue" def title = "Linking with your Hue"
def paragraphText def paragraphText
if (selectedHue) { if (selectedHue) {
if (state.refreshUsernameNeeded) {
paragraphText = "The current Hue username is invalid.\n\nPlease press the button on your Hue Bridge to re-link. "
} else {
paragraphText = "Press the button on your Hue Bridge to setup a link. " paragraphText = "Press the button on your Hue Bridge to setup a link. "
}
} else { } else {
paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next." paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next."
} }
if (state.username) { //if discovery worked if (state.username) { //if discovery worked
if (state.refreshUsernameNeeded) {
state.refreshUsernameNeeded = false
// Issue one poll with new username to cancel local polling with old username
poll()
}
nextPage = "bulbDiscovery" nextPage = "bulbDiscovery"
title = "Success!" title = "Success!"
paragraphText = "Linking to your hub was a success! Please click 'Next'!" paragraphText = "Linking to your hub was a success! Please click 'Next'!"
@@ -299,7 +287,6 @@ def initialize() {
state.bridgeRefreshCount = 0 state.bridgeRefreshCount = 0
state.bulbRefreshCount = 0 state.bulbRefreshCount = 0
state.updating = false state.updating = false
setupDeviceWatch()
if (selectedHue) { if (selectedHue) {
addBridge() addBridge()
addBulbs() addBulbs()
@@ -322,14 +309,6 @@ def uninstalled(){
state.username = null state.username = null
} }
private setupDeviceWatch() {
def hub = location.hubs[0]
// Make sure that all child devices are enrolled in device watch
getChildDevices().each {
it.sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${hub?.hub?.hardwareID}\"}")
}
}
private upgradeDeviceType(device, newHueType) { private upgradeDeviceType(device, newHueType) {
def deviceType = getDeviceType(newHueType) def deviceType = getDeviceType(newHueType)
@@ -378,6 +357,7 @@ def addBulbs() {
if (d) { if (d) {
log.debug "created ${d.displayName} with id $dni" log.debug "created ${d.displayName} with id $dni"
d.completedSetup = true d.completedSetup = true
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"
@@ -434,11 +414,9 @@ def addBridge() {
d.completedSetup = true d.completedSetup = true
log.debug "created ${d.displayName} with id ${d.deviceNetworkId}" log.debug "created ${d.displayName} with id ${d.deviceNetworkId}"
def childDevice = getChildDevice(d.deviceNetworkId) def childDevice = getChildDevice(d.deviceNetworkId)
childDevice?.sendEvent(name: "status", value: "Online")
childDevice?.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true)
updateBridgeStatus(childDevice) updateBridgeStatus(childDevice)
childDevice.sendEvent(name: "idNumber", value: idNumber)
childDevice?.sendEvent(name: "idNumber", value: idNumber)
if (vbridge.value.ip && vbridge.value.port) { if (vbridge.value.ip && vbridge.value.port) {
if (vbridge.value.ip.contains(".")) { if (vbridge.value.ip.contains(".")) {
childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port) childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port)
@@ -715,14 +693,10 @@ def doDeviceSync(){
private void updateBridgeStatus(childDevice) { private void updateBridgeStatus(childDevice) {
// Update activity timestamp if child device is a valid bridge // Update activity timestamp if child device is a valid bridge
def vbridges = getVerifiedHueBridges() def vbridges = getVerifiedHueBridges()
def vbridge = vbridges.find { def vbridge = vbridges.find {"${it.value.mac}".toUpperCase() == childDevice?.device?.deviceNetworkId?.toUpperCase()}
"${it.value.mac}".toUpperCase() == childDevice?.device?.deviceNetworkId?.toUpperCase()
}
vbridge?.value?.lastActivity = now() vbridge?.value?.lastActivity = now()
if (vbridge && childDevice?.device?.currentValue("status") == "Offline") { if(vbridge) {
log.debug "$childDevice is back Online"
childDevice?.sendEvent(name: "status", value: "Online") childDevice?.sendEvent(name: "status", value: "Online")
childDevice?.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true)
} }
} }
@@ -744,24 +718,16 @@ private void checkBridgeStatus() {
} }
if (it.value.lastActivity < time) { // it.value.lastActivity != null && if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
if (d.currentStatus == "Online") { log.warn "Bridge $it.value.idNumber is Offline"
log.warn "$d is Offline"
d.sendEvent(name: "status", value: "Offline") d.sendEvent(name: "status", value: "Offline")
d.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false, isStateChange: true)
Calendar currentTime = Calendar.getInstance() state.bulbs?.each {
it.value.online = false
}
getChildDevices().each { getChildDevices().each {
def id = getId(it) it.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", isStateChange: true, displayed: false)
if (state.bulbs[id]?.online == true) {
state.bulbs[id]?.online = false
state.bulbs[id]?.unreachableSince = currentTime.getTimeInMillis()
it.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false, isStateChange: true)
} }
} } else {
}
} else if (d.currentStatus == "Offline") {
log.debug "$d is back Online"
d.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true)
d.sendEvent(name: "status", value: "Online")//setOnline(false) d.sendEvent(name: "status", value: "Online")//setOnline(false)
} }
} }
@@ -949,15 +915,8 @@ private handleCommandResponse(body) {
updates[childDeviceNetworkId]."$eventType" = v updates[childDeviceNetworkId]."$eventType" = v
} }
} }
} else if (payload?.error) { } else if (payload.error) {
log.warn "Error returned from Hue bridge, error = ${payload?.error}" log.warn "Error returned from Hue bridge error = ${body?.error}"
// Check for unauthorized user
if (payload?.error?.type?.value == 1) {
log.error "Hue username is not valid"
state.refreshUsernameNeeded = true
state.username = null
}
return []
} }
} }
@@ -965,10 +924,13 @@ private handleCommandResponse(body) {
updates.each { childDeviceNetworkId, params -> updates.each { childDeviceNetworkId, params ->
def device = getChildDevice(childDeviceNetworkId) def device = getChildDevice(childDeviceNetworkId)
def id = getId(device) def id = getId(device)
// If device is offline, then don't send events which will update device watch
if (isOnline(id)) {
sendBasicEvents(device, "on", params.on) sendBasicEvents(device, "on", params.on)
sendBasicEvents(device, "bri", params.bri) sendBasicEvents(device, "bri", params.bri)
sendColorEvents(device, params.xy, params.hue, params.sat, params.ct) sendColorEvents(device, params.xy, params.hue, params.sat, params.ct)
} }
}
return [] return []
} }
@@ -1003,35 +965,36 @@ private handlePoll(body) {
def device = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"} def device = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"}
if (device) { if (device) {
if (bulb.value.state?.reachable) { if (bulb.value.state?.reachable) {
if (state.bulbs[bulb.key]?.online == false || state.bulbs[bulb.key]?.online == null) { if (state.bulbs[bulb.key]?.online == false) {
// light just came back online, notify device watch // light just came back online, notify device watch
device.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true) def lastActivity = now()
device.sendEvent(name: "deviceWatch-status", value: "ONLINE", description: "Last Activity is on ${new Date((long) lastActivity)}", displayed: false, isStateChange: true)
log.debug "$device is Online" log.debug "$device is Online"
} }
// Mark light as "online" // Mark light as "online"
state.bulbs[bulb.key]?.unreachableSince = null state.bulbs[bulb.key]?.unreachableSince = null
state.bulbs[bulb.key]?.online = true state.bulbs[bulb.key]?.online = true
} else {
if (state.bulbs[bulb.key]?.unreachableSince == null) {
// Store the first time where device was reported as "unreachable"
state.bulbs[bulb.key]?.unreachableSince = currentTime.getTimeInMillis()
}
if (state.bulbs[bulb.key]?.online || state.bulbs[bulb.key]?.online == null) {
// Check if device was "unreachable" for more than 11 minutes and mark "offline" if necessary
if (state.bulbs[bulb.key]?.unreachableSince < time11.getTimeInMillis() || state.bulbs[bulb.key]?.online == null) {
log.warn "$device went Offline"
state.bulbs[bulb.key]?.online = false
device.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false, isStateChange: true)
}
}
log.warn "$device may not reachable by Hue bridge"
}
// If user just executed commands, then do not send events to avoid confusing the turning on/off state // If user just executed commands, then do not send events to avoid confusing the turning on/off state
if (!state.updating) { if (!state.updating) {
sendBasicEvents(device, "on", bulb.value?.state?.on) sendBasicEvents(device, "on", bulb.value?.state?.on)
sendBasicEvents(device, "bri", bulb.value?.state?.bri) sendBasicEvents(device, "bri", bulb.value?.state?.bri)
sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode) sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode)
} }
} else {
if (state.bulbs[bulb.key]?.unreachableSince == null) {
// Store the first time where device was reported as "unreachable"
state.bulbs[bulb.key]?.unreachableSince = currentTime.getTimeInMillis()
} else if (state.bulbs[bulb.key]?.online) {
// Check if device was "unreachable" for more than 11 minutes and mark "offline" if necessary
if (state.bulbs[bulb.key]?.unreachableSince < time11.getTimeInMillis()) {
log.warn "$device went Offline"
state.bulbs[bulb.key]?.online = false
device.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", displayed: false, isStateChange: true)
}
}
log.warn "$device may not reachable by Hue bridge"
}
} }
} }
return [] return []
@@ -1198,6 +1161,22 @@ def setColor(childDevice, huesettings) {
return "Setting color to $value" return "Setting color to $value"
} }
def ping(childDevice) {
if (childDevice.device?.deviceNetworkId?.equalsIgnoreCase(selectedHue)) {
if (childDevice.device?.currentValue("status")?.equalsIgnoreCase("Online")) {
childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Bridge is reachable", displayed: false, isStateChange: true)
return "Bridge is Online"
} else {
return "Bridge is Offline"
}
} else if (isOnline(getId(childDevice))) {
childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Light is reachable", displayed: false, isStateChange: true)
return "Device is Online"
} else {
return "Device is Offline"
}
}
private getId(childDevice) { private getId(childDevice) {
if (childDevice.device?.deviceNetworkId?.startsWith("HUE")) { if (childDevice.device?.deviceNetworkId?.startsWith("HUE")) {
return childDevice.device?.deviceNetworkId[3..-1] return childDevice.device?.deviceNetworkId[3..-1]
@@ -1787,4 +1766,3 @@ def hsvToHex(hue, sat, value = 100){
return "#$r1$g1$b1" return "#$r1$g1$b1"
} }