Compare commits

...

17 Commits

Author SHA1 Message Date
Vinay Rao
0f1f5fee2f MSA-736: test submission 2015-12-09 20:54:59 -06:00
Yaima
30844676b6 Merge pull request #342 from Yaima/master
Display issue with Wattvision integration
2015-12-08 15:27:44 -08:00
Yaima Valdivia
94aa114ccb Display issue with Wattvision integration
https://smartthings.atlassian.net/browse/DVCSMP-1300
2015-12-08 15:00:35 -08:00
Yaima
a9e68d086c Merge pull request #340 from Yaima/master
Added both() command
2015-12-08 14:56:07 -08:00
Warodom Khamphanchai
d427ab8709 Merge pull request #339 from kwarodom/ecobee
Ecobee: retries to get refresh tokens if exceptions are caught
2015-12-07 15:23:40 -08:00
Yaima Valdivia
a205a94d78 Added both() command
https://smartthings.atlassian.net/browse/DVCSMP-1155
2015-12-07 14:37:10 -08:00
Warodom Khamphanchai
e775752496 Ecobee: retries to get refresh tokens if exceptions are caught in refreshAuthToken 2015-12-07 12:50:29 -08:00
bflorian
9318e9a311 Merge pull request #338 from bflorian/SSQA-65-internation-phone-text
SSQA-65, add text about international phone numbers
2015-12-05 10:25:02 -05:00
bflorian
e1c52454c6 SSQA-65, add text about international phone numbers 2015-12-05 10:24:15 -05:00
Warodom Khamphanchai
145fce2062 Merge pull request #337 from kwarodom/ecobee
Ecobee: round temperature value, add ability to set temperature setpoint temporarily or permanently
2015-12-04 17:07:47 -08:00
Warodom Khamphanchai
e9996b9fd7 Ecobee: round temperature value, add ability to set temperature setpoint temporarily or permanently 2015-12-04 16:16:24 -08:00
Warodom Khamphanchai
8be585e544 Merge pull request #231 from kwarodom/ecobee
DVCSMP-1174 - Fix Ecobee not respond to routines, DVCSMP-604 - Add accessory sensor
2015-12-04 09:22:13 -08:00
Tom Manley
e114fafd56 Merge pull request #308 from tpmanley/bug/threeAxis-events
Generate both Three Axis and Acceleration events when included in same msg
2015-12-03 20:58:21 -06:00
Warodom Khamphanchai
e6367a7832 Fix Ecobee access_token doesn't get refresh after it's expired 2015-12-02 17:37:59 -08:00
Warodom Khamphanchai
c5da3fe4a0 DVCSMP-535
- Fix activity feed displays unformatted text
- update oauth/callback url, use getApiServerUrl() for proxying to corresponding shard
2015-12-02 17:37:59 -08:00
Warodom Khamphanchai
1b9d2fe9ce DVCSMP-1174
FIX - Ecobee - Thermostat isn't responding to routines
2015-12-02 17:37:59 -08:00
Tom Manley
290e8e4129 The x,y,z attributes are often sent in a separate Attribute Report from the
accelerometer attribute. Sometimes, however, they are all sent in the same
Attribute Report. When that happens, only the accelerometer attribute was
being handled and the x,y,z attributes were not. Now they are all handled
if they arrive in the same message.

Resolves:
https://smartthings.atlassian.net/browse/DVCSMP-1271
2015-11-24 14:03:29 -06:00
8 changed files with 1097 additions and 1114 deletions

View File

@@ -0,0 +1,71 @@
/**
* Ecobee Sensor
*
* Copyright 2015 Juan Risso
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Ecobee Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Sensor"
capability "Temperature Measurement"
capability "Motion Sensor"
capability "Refresh"
capability "Polling"
}
simulator {
// TODO: define status and reply messages here
}
tiles {
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°', unit:"F",
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
)
}
standardTile("motion", "device.motion") {
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main (["temperature","motion"])
details(["temperature","motion","refresh"])
}
}
def refresh() {
log.debug "refresh..."
poll()
}
void poll() {
log.debug "Executing 'poll' using parent SmartApp"
parent.pollChildren(this)
}
//generate custom mobile activity feeds event
def generateActivityFeedsEvent(notificationMessage) {
sendEvent(name: "notificationMessage", value: "$device.displayName $notificationMessage", descriptionText: "$device.displayName $notificationMessage", displayed: true)
}

View File

@@ -22,31 +22,31 @@ metadata {
capability "Polling"
capability "Sensor"
capability "Refresh"
command "generateEvent"
command "raiseSetpoint"
command "lowerSetpoint"
command "resumeProgram"
command "switchMode"
attribute "thermostatSetpoint","number"
attribute "thermostatStatus","string"
}
simulator { }
tiles {
tiles {
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°', unit:"F",
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
)
}
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
@@ -54,27 +54,27 @@ metadata {
state "heat", action:"switchMode", nextState: "updating", icon: "st.thermostat.heat"
state "cool", action:"switchMode", nextState: "updating", icon: "st.thermostat.cool"
state "auto", action:"switchMode", nextState: "updating", icon: "st.thermostat.auto"
state "auxHeatOnly", action:"switchMode", icon: "st.thermostat.emergency-heat"
state "updating", label:"Working", icon: "st.secondary.secondary"
state "auxHeatOnly", action:"switchMode", icon: "st.thermostat.emergency-heat"
state "updating", label:"Working", icon: "st.secondary.secondary"
}
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
state "auto", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "on"
state "on", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "off"
state "off", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "circulate"
state "off", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "circulate"
state "circulate", label:'Fan: ${currentValue}', action:"switchFanMode", nextState: "auto"
}
standardTile("upButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") {
state "setpoint", action:"raiseSetpoint", backgroundColor:"#d04e00", icon:"st.thermostat.thermostat-up"
standardTile("upButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") {
state "setpoint", action:"raiseSetpoint", icon:"st.thermostat.thermostat-up"
}
valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") {
state "thermostatSetpoint", label:'${currentValue}'
valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") {
state "thermostatSetpoint", label:'${currentValue}°'
}
valueTile("currentStatus", "device.thermostatStatus", height: 1, width: 2, decoration: "flat") {
state "thermostatStatus", label:'${currentValue}', backgroundColor:"#ffffff"
}
}
standardTile("downButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") {
state "setpoint", action:"lowerSetpoint", backgroundColor:"#d04e00", icon:"st.thermostat.thermostat-down"
}
state "setpoint", action:"lowerSetpoint", icon:"st.thermostat.thermostat-down"
}
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#d04e00"
}
@@ -91,218 +91,196 @@ metadata {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("resumeProgram", "device.resumeProgram", inactiveLabel: false, decoration: "flat") {
state "resume", label:'Resume Program', action:"device.resumeProgram", icon:"st.sonos.play-icon"
state "resume", action:"resumeProgram", nextState: "updating", label:'Resume Schedule', icon:"st.samsung.da.oven_ic_send"
state "updating", label:"Working", icon: "st.secondary.secondary"
}
main "temperature"
details(["temperature", "upButtonControl", "thermostatSetpoint", "currentStatus", "downButtonControl", "mode", "resumeProgram", "refresh"])
details(["temperature", "upButtonControl", "thermostatSetpoint", "currentStatus", "downButtonControl", "mode", "resumeProgram", "refresh"])
}
preferences {
input "holdType", "enum", title: "Hold Type", description: "When changing temperature, use Temporary or Permanent hold (default)", required: false, options:["Temporary", "Permanent"]
}
}
/*
preferences {
input "highTemperature", "number", title: "Auto Mode High Temperature:", defaultValue: 80
input "lowTemperature", "number", title: "Auto Mode Low Temperature:", defaultValue: 70
input name: "holdType", type: "enum", title: "Hold Type", description: "When changing temperature, use Temporary or Permanent hold", required: true, options:["Temporary", "Permanent"]
}
*/
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle '' attribute
}
def refresh()
{
log.debug "refresh called"
poll()
log.debug "refresh ended"
def refresh() {
log.debug "refresh called"
poll()
log.debug "refresh ended"
}
def go()
{
log.debug "before:go tile tapped"
poll()
log.debug "after"
}
void poll() {
log.debug "Executing 'poll' using parent SmartApp"
def results = parent.pollChild(this)
parseEventData(results)
generateStatusEvent()
generateEvent(results) //parse received message from parent
}
def parseEventData(Map results)
{
def generateEvent(Map results) {
log.debug "parsing data $results"
if(results)
{
results.each { name, value ->
if(results) {
results.each { name, value ->
def linkText = getLinkText(device)
def isChange = false
def isDisplayed = true
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint") {
isChange = isTemperatureStateChange(device, name, value.toString())
isDisplayed = isChange
sendEvent(
name: name,
value: value,
unit: "F",
linkText: linkText,
descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name,
isStateChange: isChange,
displayed: isDisplayed)
}
else {
isChange = isStateChange(device, name, value.toString())
isDisplayed = isChange
sendEvent(
name: name,
value: value.toString(),
linkText: linkText,
descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name,
isStateChange: isChange,
displayed: isDisplayed)
}
}
generateSetpointEvent ()
generateStatusEvent ()
}
}
def isChange = false
def isDisplayed = true
def event = [name: name, linkText: linkText, descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name]
void generateEvent(Map results)
{
log.debug "parsing data $results"
if(results)
{
results.each { name, value ->
def linkText = getLinkText(device)
def isChange = false
def isDisplayed = true
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
isChange = isTemperatureStateChange(device, name, value.toString())
isDisplayed = isChange
sendEvent(
name: name,
value: value,
unit: "F",
linkText: linkText,
descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name,
isStateChange: isChange,
displayed: isDisplayed)
}
else {
isChange = isStateChange(device, name, value.toString())
isDisplayed = isChange
sendEvent(
name: name,
value: value.toString(),
linkText: linkText,
descriptionText: getThermostatDescriptionText(name, value, linkText),
handlerName: name,
isStateChange: isChange,
displayed: isDisplayed)
}
isDisplayed = isChange
event << [value: sendValue, isStateChange: isChange, displayed: isDisplayed]
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
isChange = isStateChange(device, name, value.toString())
event << [value: value.toString(), isStateChange: isChange, displayed: false]
} else {
isChange = isStateChange(device, name, value.toString())
isDisplayed = isChange
event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed]
}
sendEvent(event)
}
generateSetpointEvent ()
generateStatusEvent()
generateSetpointEvent ()
generateStatusEvent ()
}
}
private getThermostatDescriptionText(name, value, linkText)
{
if(name == "temperature")
{
return "$linkText was $value°F"
//return descriptionText to be shown on mobile activity feed
private getThermostatDescriptionText(name, value, linkText) {
if(name == "temperature") {
return "$linkText temperature is $value°F"
} else if(name == "heatingSetpoint") {
return "heating setpoint is $value°F"
} else if(name == "coolingSetpoint"){
return "cooling setpoint is $value°F"
} else if (name == "thermostatMode") {
return "thermostat mode is ${value}"
} else if (name == "thermostatFanMode") {
return "thermostat fan mode is ${value}"
} else {
return "${name} = ${value}"
}
else if(name == "heatingSetpoint")
{
return "latest heating setpoint was $value°F"
}
void setHeatingSetpoint(setpoint) {
setHeatingSetpoint(setpoint.toDouble())
}
void setHeatingSetpoint(Double setpoint) {
// def mode = device.currentValue("thermostatMode")
def heatingSetpoint = setpoint
def coolingSetpoint = device.currentValue("coolingSetpoint").toDouble()
def deviceId = device.deviceNetworkId.split(/\./).last()
//enforce limits of heatingSetpoint
if (heatingSetpoint > 79) {
heatingSetpoint = 79
} else if (heatingSetpoint < 45) {
heatingSetpoint = 45
}
else if(name == "coolingSetpoint")
{
return "latest cooling setpoint was $value°F"
//enforce limits of heatingSetpoint vs coolingSetpoint
if (heatingSetpoint >= coolingSetpoint) {
coolingSetpoint = heatingSetpoint
}
log.debug "Sending setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
if (parent.setHold (this, heatingSetpoint, coolingSetpoint, deviceId, sendHoldType)) {
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
log.debug "Done setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
generateSetpointEvent()
generateStatusEvent()
} else {
log.error "Error setHeatingSetpoint(setpoint)" //This error is handled by the connect app
}
else if (name == "thermostatMode")
{
return "thermostat mode is ${value}"
}
else
{
return "${name} = ${value}"
}
}
void setHeatingSetpoint(degreesF) {
setHeatingSetpoint(degreesF.toDouble())
void setCoolingSetpoint(setpoint) {
setCoolingSetpoint(setpoint.toDouble())
}
void setHeatingSetpoint(Double degreesF) {
log.debug "setHeatingSetpoint({$degreesF})"
sendEvent("name":"heatingSetpoint", "value":degreesF)
Double coolingSetpoint = device.currentValue("coolingSetpoint")
log.debug "coolingSetpoint: $coolingSetpoint"
parent.setHold(this, degreesF, coolingSetpoint)
void setCoolingSetpoint(Double setpoint) {
// def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint").toDouble()
def coolingSetpoint = setpoint
def deviceId = device.deviceNetworkId.split(/\./).last()
if (coolingSetpoint > 92) {
coolingSetpoint = 92
} else if (coolingSetpoint < 65) {
coolingSetpoint = 65
}
//enforce limits of heatingSetpoint vs coolingSetpoint
if (heatingSetpoint >= coolingSetpoint) {
heatingSetpoint = coolingSetpoint
}
log.debug "Sending setCoolingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
if (parent.setHold (this, heatingSetpoint, coolingSetpoint, deviceId, sendHoldType)) {
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
log.debug "Done setCoolingSetpoint>> coolingSetpoint = ${coolingSetpoint}, heatingSetpoint = ${heatingSetpoint}"
generateSetpointEvent()
generateStatusEvent()
} else {
log.error "Error setCoolingSetpoint(setpoint)" //This error is handled by the connect app
}
}
void setCoolingSetpoint(degreesF) {
setCoolingSetpoint(degreesF.toDouble())
}
void resumeProgram() {
void setCoolingSetpoint(Double degreesF) {
log.debug "setCoolingSetpoint({$degreesF})"
sendEvent("name":"coolingSetpoint", "value":degreesF)
Double heatingSetpoint = device.currentValue("heatingSetpoint")
parent.setHold(this, heatingSetpoint, degreesF)
}
log.debug "resumeProgram() is called"
sendEvent("name":"thermostatStatus", "value":"resuming schedule", "description":statusText, displayed: false)
def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.resumeProgram(this, deviceId)) {
sendEvent("name":"thermostatStatus", "value":"setpoint is updating", "description":statusText, displayed: false)
runIn(5, "poll")
log.debug "resumeProgram() is done"
sendEvent("name":"resumeProgram", "value":"resume", descriptionText: "resumeProgram is done", displayed: false, isStateChange: true)
} else {
sendEvent("name":"thermostatStatus", "value":"failed resume click refresh", "description":statusText, displayed: false)
log.error "Error resumeProgram() check parent.resumeProgram(this, deviceId)"
}
def configure() {
}
def resumeProgram() {
parent.resumeProgram(this)
}
def modes() {
if (state.modes) {
log.debug "Modes = ${state.modes}"
return state.modes
}
else {
state.modes = parent.availableModes(this)
log.debug "Modes = ${state.modes}"
return state.modes
}
log.debug "Modes = ${state.modes}"
return state.modes
}
else {
state.modes = parent.availableModes(this)
log.debug "Modes = ${state.modes}"
return state.modes
}
}
def fanModes() {
["off", "on", "auto", "circulate"]
}
def switchMode() {
log.debug "in switchMode"
def currentMode = device.currentState("thermostatMode")?.value
@@ -314,7 +292,7 @@ def switchMode() {
}
def switchToMode(nextMode) {
log.debug "In switchToMode = ${nextMode}"
log.debug "In switchToMode = ${nextMode}"
if (nextMode in modes()) {
state.lastTriedMode = nextMode
"$nextMode"()
@@ -376,300 +354,326 @@ def getDataByName(String name) {
def setThermostatMode(String value) {
log.debug "setThermostatMode({$value})"
}
def setThermostatFanMode(String value) {
log.debug "setThermostatFanMode({$value})"
}
def generateModeEvent(mode) {
sendEvent(name: "thermostatMode", value: mode, descriptionText: "$device.displayName is in ${mode} mode", displayed: true, isStateChange: true)
sendEvent(name: "thermostatMode", value: mode, descriptionText: "$device.displayName is in ${mode} mode", displayed: true)
}
def generateFanModeEvent(fanMode) {
sendEvent(name: "thermostatFanMode", value: fanMode, descriptionText: "$device.displayName fan is in ${mode} mode", displayed: true, isStateChange: true)
sendEvent(name: "thermostatFanMode", value: fanMode, descriptionText: "$device.displayName fan is in ${mode} mode", displayed: true)
}
def generateOperatingStateEvent(operatingState) {
sendEvent(name: "thermostatOperatingState", value: operatingState, descriptionText: "$device.displayName is ${operatingState}", displayed: true, isStateChange: true)
sendEvent(name: "thermostatOperatingState", value: operatingState, descriptionText: "$device.displayName is ${operatingState}", displayed: true)
}
def off() {
log.debug "off"
generateModeEvent("off")
if (parent.setMode (this,"off"))
generateModeEvent("off")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode (this,"off", deviceId))
generateModeEvent("off")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
}
def heat() {
log.debug "heat"
generateModeEvent("heat")
if (parent.setMode (this,"heat"))
generateModeEvent("heat")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode (this,"heat", deviceId))
generateModeEvent("heat")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
}
def auxHeatOnly() {
log.debug "auxHeatOnly"
generateModeEvent("auxHeatOnly")
if (parent.setMode (this,"auxHeatOnly"))
generateModeEvent("auxHeatOnly")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode (this,"auxHeatOnly", deviceId))
generateModeEvent("auxHeatOnly")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
}
def cool() {
log.debug "cool"
generateModeEvent("cool")
if (parent.setMode (this,"cool"))
generateModeEvent("cool")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode (this,"cool", deviceId))
generateModeEvent("cool")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
}
def auto() {
log.debug "auto"
generateModeEvent("auto")
if (parent.setMode (this,"auto"))
generateModeEvent("auto")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode (this,"auto", deviceId))
generateModeEvent("auto")
else {
log.debug "Error setting new mode."
def currentMode = device.currentState("thermostatMode")?.value
generateModeEvent(currentMode) // reset the tile back
}
generateSetpointEvent()
generateStatusEvent()
}
def fanOn() {
log.debug "fanOn"
parent.setFanMode (this,"on")
// parent.setFanMode (this,"on")
}
def fanAuto() {
log.debug "fanAuto"
parent.setFanMode (this,"auto")
// parent.setFanMode (this,"auto")
}
def fanCirculate() {
log.debug "fanCirculate"
parent.setFanMode (this,"circulate")
// parent.setFanMode (this,"circulate")
}
def fanOff() {
log.debug "fanOff"
parent.setFanMode (this,"off")
// parent.setFanMode (this,"off")
}
def generateSetpointEvent() {
log.debug "Generate SetPoint Event"
log.debug "Generate SetPoint Event"
def mode = device.currentValue("thermostatMode")
log.debug "Current Mode = ${mode}"
log.debug "Current Mode = ${mode}"
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
log.debug "Heating Setpoint = ${heatingSetpoint}"
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
log.debug "Heating Setpoint = ${heatingSetpoint}"
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
log.debug "Cooling Setpoint = ${coolingSetpoint}"
if (mode == "heat") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()+"°")
}
else if (mode == "cool") {
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString()+"°")
log.debug "Cooling Setpoint = ${coolingSetpoint}"
} else if (mode == "auto") {
sendEvent("name":"thermostatSetpoint", "value":"Auto")
if (mode == "heat") {
} else if (mode == "off") {
sendEvent("name":"thermostatSetpoint", "value":"Off")
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString())
} else if (mode == "emergencyHeat") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()+"°")
}
else if (mode == "cool") {
}
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString())
} else if (mode == "auto") {
sendEvent("name":"thermostatSetpoint", "value":"Auto")
} else if (mode == "off") {
sendEvent("name":"thermostatSetpoint", "value":"Off")
} else if (mode == "emergencyHeat") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString())
}
}
void raiseSetpoint() {
log.debug "Raise SetPoint"
def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
log.debug "Current Mode = ${mode}"
if (mode == "heat") {
heatingSetpoint++
if (heatingSetpoint > 99)
heatingSetpoint = 99
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()+"°")
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
parent.setHold (this, heatingSetpoint, coolingSetpoint)
log.debug "New Heating Setpoint = ${heatingSetpoint}"
}
else if (mode == "cool") {
coolingSetpoint++
if (coolingSetpoint > 99)
coolingSetpoint = 99
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString()+"°")
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
parent.setHold (this, heatingSetpoint, coolingSetpoint)
log.debug "New Cooling Setpoint = ${coolingSetpoint}"
}
generateStatusEvent()
def targetvalue
if (mode == "off" || mode == "auto") {
log.warn "this mode: $mode does not allow raiseSetpoint"
} else {
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def thermostatSetpoint = device.currentValue("thermostatSetpoint").toInteger()
log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
if (device.latestState('thermostatSetpoint')) {
targetvalue = device.latestState('thermostatSetpoint').value as Integer
} else {
targetvalue = 0
}
targetvalue = targetvalue + 1
if (mode == "heat" && targetvalue > 79) {
targetvalue = 79
} else if (mode == "cool" && targetvalue > 92) {
targetvalue = 92
}
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
log.info "In mode $mode raiseSetpoint() to $targetvalue"
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
}
}
//called by tile when user hit raise temperature button on UI
void lowerSetpoint() {
log.debug "Lower SetPoint"
def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
log.debug "Current Mode = ${mode}, Current Heating Setpoint = ${heatingSetpoint}, Current Cooling Setpoint = ${coolingSetpoint}"
if (mode == "heat" || mode == "emergencyHeat") {
heatingSetpoint--
if (heatingSetpoint < 32)
heatingSetpoint = 32
def targetvalue
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint.toString()+"°")
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
parent.setHold (this, heatingSetpoint, coolingSetpoint)
log.debug "New Heating Setpoint = ${heatingSetpoint}"
}
else if (mode == "cool") {
coolingSetpoint--
if (coolingSetpoint < 32)
coolingSetpoint = 32
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint.toString()+"°")
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
parent.setHold (this, heatingSetpoint, coolingSetpoint)
log.debug "New Cooling Setpoint = ${coolingSetpoint}"
}
generateStatusEvent()
if (mode == "off" || mode == "auto") {
log.warn "this mode: $mode does not allow lowerSetpoint"
} else {
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def thermostatSetpoint = device.currentValue("thermostatSetpoint").toInteger()
log.debug "lowerSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
if (device.latestState('thermostatSetpoint')) {
targetvalue = device.latestState('thermostatSetpoint').value as Integer
} else {
targetvalue = 0
}
targetvalue = targetvalue - 1
if (mode == "heat" && targetvalue.toInteger() < 45) {
targetvalue = 45
} else if (mode == "cool" && targetvalue.toInteger() < 65) {
targetvalue = 65
}
sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
log.info "In mode $mode lowerSetpoint() to $targetvalue"
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
}
}
//called by raiseSetpoint() and lowerSetpoint()
void alterSetpoint(temp) {
def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def deviceId = device.deviceNetworkId.split(/\./).last()
def targetHeatingSetpoint
def targetCoolingSetpoint
//step1: check thermostatMode, enforce limits before sending request to cloud
if (mode == "heat"){
if (temp.value > coolingSetpoint){
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
} else {
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = coolingSetpoint
}
} else if (mode == "cool") {
//enforce limits before sending request to cloud
if (temp.value < heatingSetpoint){
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
} else {
targetHeatingSetpoint = heatingSetpoint
targetCoolingSetpoint = temp.value
}
}
log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to ${targetHeatingSetpoint} " +
"coolingSetpoint to ${targetCoolingSetpoint} with holdType : ${holdType}"
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)) {
sendEvent("name": "thermostatSetpoint", "value": temp.value.toString(), displayed: false)
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint)
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint)
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
} else {
log.error "Error alterSetpoint()"
if (mode == "heat"){
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
} else if (mode == "cool") {
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
}
}
generateStatusEvent()
}
def generateStatusEvent() {
def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def temperature = device.currentValue("temperature").toInteger()
def statusText
log.debug "Generate Status Event for Mode = ${mode}"
log.debug "Temperature = ${temperature}"
log.debug "Heating set point = ${heatingSetpoint}"
log.debug "Cooling set point = ${coolingSetpoint}"
log.debug "HVAC Mode = ${mode}"
if (mode == "heat") {
if (temperature >= heatingSetpoint)
statusText = "Right Now: Idle"
else
statusText = "Heating to ${heatingSetpoint}° F"
} else if (mode == "cool") {
if (temperature <= coolingSetpoint)
statusText = "Right Now: Idle"
else
statusText = "Cooling to ${coolingSetpoint}° F"
} else if (mode == "auto") {
statusText = "Right Now: Auto"
} else if (mode == "off") {
statusText = "Right Now: Off"
} else if (mode == "emergencyHeat") {
statusText = "Emergency Heat"
} else {
statusText = "?"
}
log.debug "Generate Status Event = ${statusText}"
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true, isStateChange: true)
def heatingSetpoint = device.currentValue("heatingSetpoint").toInteger()
def coolingSetpoint = device.currentValue("coolingSetpoint").toInteger()
def temperature = device.currentValue("temperature").toInteger()
def statusText
log.debug "Generate Status Event for Mode = ${mode}"
log.debug "Temperature = ${temperature}"
log.debug "Heating set point = ${heatingSetpoint}"
log.debug "Cooling set point = ${coolingSetpoint}"
log.debug "HVAC Mode = ${mode}"
if (mode == "heat") {
if (temperature >= heatingSetpoint)
statusText = "Right Now: Idle"
else
statusText = "Heating to ${heatingSetpoint}° F"
} else if (mode == "cool") {
if (temperature <= coolingSetpoint)
statusText = "Right Now: Idle"
else
statusText = "Cooling to ${coolingSetpoint}° F"
} else if (mode == "auto") {
statusText = "Right Now: Auto"
} else if (mode == "off") {
statusText = "Right Now: Off"
} else if (mode == "emergencyHeat") {
statusText = "Emergency Heat"
} else {
statusText = "?"
}
log.debug "Generate Status Event = ${statusText}"
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true)
}
//generate custom mobile activity feeds event
def generateActivityFeedsEvent(notificationMessage) {
sendEvent(name: "notificationMessage", value: "$device.displayName $notificationMessage", descriptionText: "$device.displayName $notificationMessage", displayed: true)
}

View File

@@ -115,31 +115,30 @@
}
}
def parse(String description) {
Map map = [:]
if (description?.startsWith('catchall:')) {
map = parseCatchAllMessage(description)
}
else if (description?.startsWith('read attr -')) {
map = parseReportAttributeMessage(description)
}
def parse(String description) {
Map map = [:]
if (description?.startsWith('catchall:')) {
map = parseCatchAllMessage(description)
}
else if (description?.startsWith('temperature: ')) {
map = parseCustomMessage(description)
}
else if (description?.startsWith('zone status')) {
map = parseIasMessage(description)
}
map = parseCustomMessage(description)
}
else if (description?.startsWith('zone status')) {
map = parseIasMessage(description)
}
def result = map ? createEvent(map) : null
if (description?.startsWith('enroll request')) {
List cmds = enrollResponse()
log.debug "enroll response: ${cmds}"
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
}
return result
}
if (description?.startsWith('enroll request')) {
List cmds = enrollResponse()
log.debug "enroll response: ${cmds}"
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
}
else if (description?.startsWith('read attr -')) {
result = parseReportAttributeMessage(description).each { createEvent(it) }
}
return result
}
private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
@@ -178,28 +177,40 @@ private boolean shouldProcessMessage(cluster) {
return !ignoredMessage
}
private Map parseReportAttributeMessage(String description) {
private List parseReportAttributeMessage(String description) {
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
Map resultMap = [:]
List result = []
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
def value = getTemperature(descMap.value)
resultMap = getTemperatureResult(value)
result << getTemperatureResult(value)
}
else if (descMap.cluster == "FC02" && descMap.attrId == "0010") {
resultMap = getAccelerationResult(descMap.value)
if (descMap.value.size() == 32) {
// value will look like 00ae29001403e2290013001629001201
// breaking this apart and swapping byte order where appropriate, this breaks down to:
// X (0x0012) = 0x0016
// Y (0x0013) = 0x03E2
// Z (0x0014) = 0x00AE
// note that there is a known bug in that the x,y,z attributes are interpreted in the wrong order
// this will be fixed in a future update
def threeAxisAttributes = descMap.value[0..-9]
result << parseAxis(threeAxisAttributes)
descMap.value = descMap.value[-2..-1]
}
result << getAccelerationResult(descMap.value)
}
else if (descMap.cluster == "FC02" && descMap.attrId == "0012") {
resultMap = parseAxis(descMap.value)
else if (descMap.cluster == "FC02" && descMap.attrId == "0012") {
result << parseAxis(descMap.value)
}
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
result << getBatteryResult(Integer.parseInt(descMap.value, 16))
}
return resultMap
return result
}
private Map parseCustomMessage(String description) {

View File

@@ -33,14 +33,14 @@ metadata {
state "power", label: '${currentValue} W'
}
tile(name: "powerChart", attribute: "powerContent", type: "HTML", url: '${currentValue}', width: 3, height: 2) { }
htmlTile(name: "powerContent", attribute: "powerContent", type: "HTML", whitelist: "www.wattvision.com" , url: '${currentValue}', width: 3, height: 2)
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
}
main "power"
details(["powerChart", "power", "refresh"])
details(["powerContent", "power", "refresh"])
}
}
@@ -74,10 +74,10 @@ public addWattvisionData(json) {
log.trace "Adding data from Wattvision"
def data = json.data
def data = parseJson(json.data.toString())
def units = json.units ?: "watts"
if (data) {
if (data.size() > 0) {
def latestData = data[-1]
data.each {
sendPowerEvent(it.t, it.v, units, (latestData == it))
@@ -103,3 +103,7 @@ private sendPowerEvent(time, value, units, isLatest = false) {
sendEvent(eventData)
}
def parseJson(String s) {
new groovy.json.JsonSlurper().parseText(s)
}

View File

@@ -25,6 +25,7 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "OSRAM LIGHTIFY LED Smart Connected Light"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Bulb"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
fingerprint profileId: "0104", inClusters: "0001, 0006, 0008, 000E", outClusters: "0019", manufacturer: "Nanoleaf", model: "IvyBulbs", deviceJoinName: "Nanoleaf Smart Bulbs"
}
tiles(scale: 2) {
@@ -86,4 +87,4 @@ def refresh() {
def configure() {
log.debug "Configuring Reporting and Bindings."
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
}
}

View File

@@ -115,6 +115,10 @@ def strobe() {
]
}
def both() {
on()
}
def refresh() {
log.debug "sending battery refresh command"
zwave.batteryV1.batteryGet().format()

File diff suppressed because it is too large Load Diff

View File

@@ -49,6 +49,7 @@ preferences {
section("Via a push notification and/or an SMS message"){
input("recipients", "contact", title: "Send notifications to") {
input "phone", "phone", title: "Phone Number (for SMS, optional)", required: false
paragraph "If outside the US please make sure to enter the proper country code"
input "pushAndPhone", "enum", title: "Both Push and SMS?", required: false, options: ["Yes", "No"]
}
}