mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-08 05:31:56 +00:00
ICP-1103, ICP-1125, ICP-1194 CT Thermostat issues (#2181)
* ICP-1103, ICP-1125, ICP-1194 CT Thermostat issues - Added modes and changed mode names to reflect specification in thermostat capability - Changed how device is initialized poll by sending all commands separated by 3 seconds instead of sending a request and on its response send the next. This to give all commands a chance in case one is dropped by the network. - Reduced the time from 30 to 1 second between setting and requesting a setpoint using the setHeating/setCooling method. This is now the same as changing using the ST DTH tiles. - Also modified the UI to use the multi attribute tile and using arrows instead of sliders for changing setpoints. * Update ct100-thermostat.groovy
This commit is contained in:
committed by
Vinay Rao
parent
3d0fb9cdde
commit
6b4309fa95
@@ -6,7 +6,6 @@ metadata {
|
|||||||
capability "Relative Humidity Measurement"
|
capability "Relative Humidity Measurement"
|
||||||
capability "Thermostat"
|
capability "Thermostat"
|
||||||
capability "Battery"
|
capability "Battery"
|
||||||
capability "Configuration"
|
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Health Check"
|
capability "Health Check"
|
||||||
@@ -15,161 +14,173 @@ metadata {
|
|||||||
|
|
||||||
command "switchMode"
|
command "switchMode"
|
||||||
command "switchFanMode"
|
command "switchFanMode"
|
||||||
command "quickSetCool"
|
command "lowerHeatingSetpoint"
|
||||||
command "quickSetHeat"
|
command "raiseHeatingSetpoint"
|
||||||
|
command "lowerCoolSetpoint"
|
||||||
|
command "raiseCoolSetpoint"
|
||||||
|
|
||||||
fingerprint deviceId: "0x08", inClusters: "0x43,0x40,0x44,0x31,0x80,0x85,0x60"
|
fingerprint deviceId: "0x08", inClusters: "0x43,0x40,0x44,0x31,0x80,0x85,0x60"
|
||||||
fingerprint mfr:"0098", prod:"6401", model:"0107", deviceJoinName: "2Gig CT100 Programmable Thermostat"
|
fingerprint mfr:"0098", prod:"6401", model:"0107", deviceJoinName: "2Gig CT100 Programmable Thermostat"
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulator metadata
|
|
||||||
simulator {
|
|
||||||
status "off" : "command: 4003, payload: 00"
|
|
||||||
status "heat" : "command: 4003, payload: 01"
|
|
||||||
status "cool" : "command: 4003, payload: 02"
|
|
||||||
status "auto" : "command: 4003, payload: 03"
|
|
||||||
status "emergencyHeat" : "command: 4003, payload: 04"
|
|
||||||
|
|
||||||
status "fanAuto" : "command: 4403, payload: 00"
|
|
||||||
status "fanOn" : "command: 4403, payload: 01"
|
|
||||||
status "fanCirculate" : "command: 4403, payload: 06"
|
|
||||||
|
|
||||||
status "heat 60" : "command: 4303, payload: 01 09 3C"
|
|
||||||
status "heat 72" : "command: 4303, payload: 01 09 48"
|
|
||||||
|
|
||||||
status "cool 76" : "command: 4303, payload: 02 09 4C"
|
|
||||||
status "cool 80" : "command: 4303, payload: 02 09 50"
|
|
||||||
|
|
||||||
status "temp 58" : "command: 3105, payload: 01 2A 02 44"
|
|
||||||
status "temp 62" : "command: 3105, payload: 01 2A 02 6C"
|
|
||||||
status "temp 78" : "command: 3105, payload: 01 2A 03 0C"
|
|
||||||
status "temp 86" : "command: 3105, payload: 01 2A 03 34"
|
|
||||||
|
|
||||||
status "idle" : "command: 4203, payload: 00"
|
|
||||||
status "heating" : "command: 4203, payload: 01"
|
|
||||||
status "cooling" : "command: 4203, payload: 02"
|
|
||||||
|
|
||||||
// reply messages
|
|
||||||
reply "2502": "command: 2503, payload: FF"
|
|
||||||
}
|
|
||||||
|
|
||||||
tiles {
|
tiles {
|
||||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
multiAttributeTile(name:"temperature", type:"generic", width:3, height:2, canChangeIcon: true) {
|
||||||
state("temperature", label:'${currentValue}°',
|
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
||||||
|
attributeState("temperature", label:'${currentValue}°', icon: "st.alarm.temperature.normal",
|
||||||
backgroundColors:[
|
backgroundColors:[
|
||||||
[value: 32, color: "#153591"],
|
// Celsius
|
||||||
|
[value: 0, color: "#153591"],
|
||||||
|
[value: 7, color: "#1e9cbb"],
|
||||||
|
[value: 15, color: "#90d2a7"],
|
||||||
|
[value: 23, color: "#44b621"],
|
||||||
|
[value: 28, color: "#f1d801"],
|
||||||
|
[value: 35, color: "#d04e00"],
|
||||||
|
[value: 37, color: "#bc2323"],
|
||||||
|
// Fahrenheit
|
||||||
|
[value: 40, 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: 92, color: "#d04e00"],
|
[value: 95, color: "#d04e00"],
|
||||||
[value: 98, color: "#bc2323"]
|
[value: 96, color: "#bc2323"]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
tileAttribute("device.batteryIcon", key: "SECONDARY_CONTROL") {
|
||||||
state "off", label:'${name}', action:"switchMode", nextState:"to_heat"
|
attributeState "ok_battery", label:'${currentValue}%', icon:"st.arlo.sensor_battery_4"
|
||||||
state "heat", label:'${name}', action:"switchMode", nextState:"to_cool"
|
attributeState "low_battery", label:'Low Battery', icon:"st.arlo.sensor_battery_0"
|
||||||
state "cool", label:'${name}', action:"switchMode", nextState:"..."
|
|
||||||
state "auto", label:'${name}', action:"switchMode", nextState:"..."
|
|
||||||
state "emergency heat", label:'${name}', action:"switchMode", nextState:"..."
|
|
||||||
state "to_heat", label: "heat", action:"switchMode", nextState:"to_cool"
|
|
||||||
state "to_cool", label: "cool", action:"switchMode", nextState:"..."
|
|
||||||
state "...", label: "...", action:"off", nextState:"off"
|
|
||||||
}
|
}
|
||||||
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
|
|
||||||
state "fanAuto", label:'${name}', action:"switchFanMode"
|
|
||||||
state "fanOn", label:'${name}', action:"switchFanMode"
|
|
||||||
state "fanCirculate", label:'${name}', action:"switchFanMode"
|
|
||||||
}
|
}
|
||||||
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
standardTile("mode", "device.thermostatMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
||||||
state "setHeatingSetpoint", action:"quickSetHeat", backgroundColor:"#e86d13"
|
state "off", action:"switchMode", nextState:"to_heat", icon: "st.thermostat.heating-cooling-off"
|
||||||
|
state "heat", action:"switchMode", nextState:"to_cool", icon: "st.thermostat.heat"
|
||||||
|
state "cool", action:"switchMode", nextState:"...", icon: "st.thermostat.cool"
|
||||||
|
state "auto", action:"switchMode", nextState:"...", icon: "st.thermostat.auto"
|
||||||
|
state "emergency heat", action:"switchMode", nextState:"...", icon: "st.thermostat.emergency-heat"
|
||||||
|
state "to_heat", action:"switchMode", nextState:"to_cool", icon: "st.secondary.secondary"
|
||||||
|
state "to_cool", action:"switchMode", nextState:"...", icon: "st.secondary.secondary"
|
||||||
|
state "...", label: "...", action:"off", nextState:"off", icon: "st.secondary.secondary"
|
||||||
}
|
}
|
||||||
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
|
standardTile("fanMode", "device.thermostatFanMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
||||||
state "heat", label:'${currentValue}° heat', backgroundColor:"#ffffff"
|
state "auto", action:"switchFanMode", icon: "st.thermostat.fan-auto"
|
||||||
|
state "on", action:"switchFanMode", icon: "st.thermostat.fan-on"
|
||||||
|
state "circulate", action:"switchFanMode", icon: "st.thermostat.fan-circulate"
|
||||||
}
|
}
|
||||||
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
valueTile("humidity", "device.humidity", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
||||||
state "setCoolingSetpoint", action:"quickSetCool", backgroundColor: "#00a0dc"
|
state "humidity", label:'${currentValue}%', icon:"st.Weather.weather12"
|
||||||
}
|
}
|
||||||
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
|
standardTile("lowerHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||||
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
state "heatingSetpoint", action:"lowerHeatingSetpoint", icon:"st.thermostat.thermostat-left"
|
||||||
}
|
}
|
||||||
valueTile("humidity", "device.humidity", inactiveLabel: false, decoration: "flat") {
|
valueTile("heatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
state "heatingSetpoint", label:'${currentValue}° heat', backgroundColor:"#ffffff"
|
||||||
}
|
}
|
||||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
standardTile("raiseHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||||
state "battery", label:'${currentValue}% battery', unit:""
|
state "heatingSetpoint", action:"raiseHeatingSetpoint", icon:"st.thermostat.thermostat-right"
|
||||||
}
|
}
|
||||||
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
standardTile("lowerCoolSetpoint", "device.coolingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "coolingSetpoint", action:"lowerCoolSetpoint", icon:"st.thermostat.thermostat-left"
|
||||||
|
}
|
||||||
|
valueTile("coolingSetpoint", "device.coolingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "coolingSetpoint", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
||||||
|
}
|
||||||
|
standardTile("raiseCoolSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "heatingSetpoint", action:"raiseCoolSetpoint", icon:"st.thermostat.thermostat-right"
|
||||||
|
}
|
||||||
|
|
||||||
|
standardTile("refresh", "device.thermostatMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
}
|
}
|
||||||
main "temperature"
|
main "temperature"
|
||||||
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh", "humidity", "battery"])
|
details(["temperature", "mode", "fanMode", "humidity", "lowerHeatingSetpoint", "heatingSetpoint", "raiseHeatingSetpoint", "lowerCoolSetpoint", "coolingSetpoint", "raiseCoolSetpoint", "refresh"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
// If not set update ManufacturerSpecific data
|
||||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
if (!getDataValue("manufacturer")) {
|
||||||
|
sendHubCommand(new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()))
|
||||||
|
}
|
||||||
|
initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
def installed() {
|
def installed() {
|
||||||
|
// Configure device
|
||||||
|
def cmds = []
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format())
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())
|
||||||
|
sendHubCommand(cmds)
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
def initialize() {
|
||||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
|
// Poll device for additional data that will be updated by refresh tile
|
||||||
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description)
|
def parse(String description)
|
||||||
{
|
{
|
||||||
def result = []
|
def result = null
|
||||||
if (description == "updated") {
|
if (description == "updated") {
|
||||||
} else {
|
} else {
|
||||||
def zwcmd = zwave.parse(description, [0x42:2, 0x43:2, 0x31: 2, 0x60: 3])
|
def zwcmd = zwave.parse(description, [0x42:2, 0x43:2, 0x31: 2, 0x60: 3])
|
||||||
if (zwcmd) {
|
if (zwcmd) {
|
||||||
result += zwaveEvent(zwcmd)
|
result = zwaveEvent(zwcmd)
|
||||||
|
// Check battery level at least once every 2 days
|
||||||
|
if (!state.lastbatt || now() - state.lastbatt > 48*60*60*1000) {
|
||||||
|
sendHubCommand(new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format()))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug "$device.displayName couldn't parse $description"
|
log.debug "$device.displayName couldn't parse $description"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return null
|
return []
|
||||||
}
|
}
|
||||||
if (result.size() == 1 && (!state.lastbatt || now() - state.lastbatt > 48*60*60*1000)) {
|
return [result]
|
||||||
result << response(zwave.batteryV1.batteryGet().format())
|
|
||||||
}
|
|
||||||
log.debug "$device.displayName parsed '$description' to $result"
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiInstanceCmdEncap cmd) {
|
||||||
def result = null
|
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 3])
|
||||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x42:2, 0x43:2, 0x31: 2])
|
log.debug ("multiinstancev1.MultiInstanceCmdEncap: command from instance ${cmd.instance}: ${encapsulatedCommand}")
|
||||||
log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}")
|
|
||||||
if (encapsulatedCommand) {
|
if (encapsulatedCommand) {
|
||||||
result = zwaveEvent(encapsulatedCommand)
|
zwaveEvent(encapsulatedCommand)
|
||||||
if (cmd.sourceEndPoint == 1) { // indicates a response to refresh() vs an unrequested update
|
|
||||||
def event = ([] + result)[0] // in case zwaveEvent returns a list
|
|
||||||
def resp = nextRefreshQuery(event?.name)
|
|
||||||
if (resp) {
|
|
||||||
log.debug("sending next refresh query: $resp")
|
|
||||||
result = [] + result + response(["delay 200", resp])
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
|
||||||
{
|
{
|
||||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
def sendCmd = []
|
||||||
def temp = convertTemperatureIfNeeded(cmd.scaledValue, cmdScale, cmd.precision)
|
|
||||||
def unit = getTemperatureScale()
|
def unit = getTemperatureScale()
|
||||||
def map1 = [ value: temp, unit: unit, displayed: false ]
|
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||||
|
def setpoint = getTempInLocalScale(cmd.scaledValue, cmdScale)
|
||||||
switch (cmd.setpointType) {
|
switch (cmd.setpointType) {
|
||||||
case 1:
|
case 1:
|
||||||
map1.name = "heatingSetpoint"
|
//map1.name = "heatingSetpoint"
|
||||||
|
sendEvent(name: "heatingSetpoint", value: setpoint, unit: unit, displayed: false)
|
||||||
|
updateThermostatSetpoint("heatingSetpoint", setpoint)
|
||||||
|
// Enforce coolingSetpoint limits, as device doesn't
|
||||||
|
if (setpoint > getTempInLocalScale("coolingSetpoint")) {
|
||||||
|
sendCmd << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
|
||||||
|
setpointType: 2, scale: cmd.scale, precision: cmd.precision, scaledValue: cmd.scaledValue).format())
|
||||||
|
sendCmd << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
|
||||||
|
sendHubCommand(sendCmd)
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
map1.name = "coolingSetpoint"
|
//map1.name = "coolingSetpoint"
|
||||||
|
sendEvent(name: "coolingSetpoint", value: setpoint, unit: unit, displayed: false)
|
||||||
|
updateThermostatSetpoint("coolingSetpoint", setpoint)
|
||||||
|
// Enforce heatingSetpoint limits, as device doesn't
|
||||||
|
if (setpoint < getTempInLocalScale("heatingSetpoint")) {
|
||||||
|
sendCmd << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
|
||||||
|
setpointType: 1, scale: cmd.scale, precision: cmd.precision, scaledValue: cmd.scaledValue).format())
|
||||||
|
sendCmd << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format())
|
||||||
|
sendHubCommand(sendCmd)
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
log.debug "unknown setpointType $cmd.setpointType"
|
log.debug "unknown setpointType $cmd.setpointType"
|
||||||
@@ -180,33 +191,55 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpo
|
|||||||
state.size = cmd.size
|
state.size = cmd.size
|
||||||
state.scale = cmd.scale
|
state.scale = cmd.scale
|
||||||
state.precision = cmd.precision
|
state.precision = cmd.precision
|
||||||
|
|
||||||
def mode = device.latestValue("thermostatMode")
|
|
||||||
if (mode && map1.name.startsWith(mode) || (mode == "emergency heat" && map1.name == "heatingSetpoint")) {
|
|
||||||
def map2 = [ name: "thermostatSetpoint", value: temp, unit: unit ]
|
|
||||||
[ createEvent(map1), createEvent(map2) ]
|
|
||||||
} else {
|
|
||||||
createEvent(map1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd)
|
// thermostatSetpoint is not displayed by any tile as it can't be predictable calculated due to
|
||||||
{
|
// the device's quirkiness but it is defined by the capability so it must be set, set it to the most likely value
|
||||||
|
def updateThermostatSetpoint(setpoint, value) {
|
||||||
|
def scale = getTemperatureScale()
|
||||||
|
def heatingSetpoint = (setpoint == "heatingSetpoint") ? value : getTempInLocalScale("heatingSetpoint")
|
||||||
|
def coolingSetpoint = (setpoint == "coolingSetpoint") ? value : getTempInLocalScale("coolingSetpoint")
|
||||||
|
def mode = device.currentValue("thermostatMode")
|
||||||
|
def thermostatSetpoint = heatingSetpoint // corresponds to (mode == "heat" || mode == "emergency heat")
|
||||||
|
if (mode == "cool") {
|
||||||
|
thermostatSetpoint = coolingSetpoint
|
||||||
|
}
|
||||||
|
// Just set to average of heating + cooling for mode off and auto
|
||||||
|
if (mode == "off" || mode == "auto") {
|
||||||
|
thermostatSetpoint = getTempInLocalScale((heatingSetpoint + coolingSetpoint)/2, scale)
|
||||||
|
}
|
||||||
|
sendEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) {
|
||||||
def map = [:]
|
def map = [:]
|
||||||
if (cmd.sensorType == 1) {
|
if (cmd.sensorType == 1) {
|
||||||
map.name = "temperature"
|
map.name = "temperature"
|
||||||
map.unit = getTemperatureScale()
|
map.unit = getTemperatureScale()
|
||||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C", cmd.precision)
|
map.value = getTempInLocalScale(cmd.scaledSensorValue, (cmd.scale == 1 ? "F" : "C"))
|
||||||
} else if (cmd.sensorType == 5) {
|
} else if (cmd.sensorType == 5) {
|
||||||
map.name = "humidity"
|
map.name = "humidity"
|
||||||
map.unit = "%"
|
map.unit = "%"
|
||||||
map.value = cmd.scaledSensorValue
|
map.value = cmd.scaledSensorValue
|
||||||
}
|
}
|
||||||
createEvent(map)
|
sendEvent(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd)
|
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd) {
|
||||||
{
|
def map = [:]
|
||||||
|
if (cmd.sensorType == 1) {
|
||||||
|
map.name = "temperature"
|
||||||
|
map.unit = getTemperatureScale()
|
||||||
|
map.value = getTempInLocalScale(cmd.scaledSensorValue, (cmd.scale == 1 ? "F" : "C"))
|
||||||
|
} else if (cmd.sensorType == 5) {
|
||||||
|
map.value = cmd.scaledSensorValue
|
||||||
|
map.unit = "%"
|
||||||
|
map.name = "humidity"
|
||||||
|
}
|
||||||
|
sendEvent(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd) {
|
||||||
def map = [name: "thermostatOperatingState" ]
|
def map = [name: "thermostatOperatingState" ]
|
||||||
switch (cmd.operatingState) {
|
switch (cmd.operatingState) {
|
||||||
case physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport.OPERATING_STATE_IDLE:
|
case physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport.OPERATING_STATE_IDLE:
|
||||||
@@ -231,12 +264,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.Thermosta
|
|||||||
map.value = "vent economizer"
|
map.value = "vent economizer"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
def result = createEvent(map)
|
sendEvent(map)
|
||||||
if (result.isStateChange && device.latestValue("thermostatMode") == "auto" && (result.value == "heating" || result.value == "cooling")) {
|
|
||||||
def thermostatSetpoint = device.latestValue("${result.value}Setpoint")
|
|
||||||
result = [result, createEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: getTemperatureScale())]
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanStateReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanStateReport cmd) {
|
||||||
@@ -252,203 +280,256 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanSt
|
|||||||
map.value = "running high"
|
map.value = "running high"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
createEvent(map)
|
sendEvent(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
|
||||||
def map = [name: "thermostatMode"]
|
def map = [name: "thermostatMode", data:[supportedThermostatModes: state.supportedModes]]
|
||||||
def thermostatSetpoint = null
|
|
||||||
switch (cmd.mode) {
|
switch (cmd.mode) {
|
||||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF:
|
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF:
|
||||||
map.value = "off"
|
map.value = "off"
|
||||||
break
|
break
|
||||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_HEAT:
|
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_HEAT:
|
||||||
map.value = "heat"
|
map.value = "heat"
|
||||||
thermostatSetpoint = device.latestValue("heatingSetpoint")
|
|
||||||
break
|
break
|
||||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUXILIARY_HEAT:
|
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUXILIARY_HEAT:
|
||||||
map.value = "emergency heat"
|
map.value = "emergency heat"
|
||||||
thermostatSetpoint = device.latestValue("heatingSetpoint")
|
|
||||||
break
|
break
|
||||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_COOL:
|
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_COOL:
|
||||||
map.value = "cool"
|
map.value = "cool"
|
||||||
thermostatSetpoint = device.latestValue("coolingSetpoint")
|
|
||||||
break
|
break
|
||||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUTO:
|
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUTO:
|
||||||
map.value = "auto"
|
map.value = "auto"
|
||||||
def temp = device.latestValue("temperature")
|
|
||||||
def heatingSetpoint = device.latestValue("heatingSetpoint")
|
|
||||||
def coolingSetpoint = device.latestValue("coolingSetpoint")
|
|
||||||
if (temp && heatingSetpoint && coolingSetpoint) {
|
|
||||||
if (temp < (heatingSetpoint + coolingSetpoint) / 2.0) {
|
|
||||||
thermostatSetpoint = heatingSetpoint
|
|
||||||
} else {
|
|
||||||
thermostatSetpoint = coolingSetpoint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
state.lastTriedMode = map.value
|
state.lastTriedMode = map.value
|
||||||
if (thermostatSetpoint) {
|
sendEvent(map)
|
||||||
[ createEvent(map), createEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: getTemperatureScale()) ]
|
updateThermostatSetpoint(null, null)
|
||||||
} else {
|
|
||||||
createEvent(map)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) {
|
||||||
def map = [name: "thermostatFanMode", displayed: false]
|
def map = [name: "thermostatFanMode", data:[supportedThermostatFanModes: state.supportedFanModes]]
|
||||||
switch (cmd.fanMode) {
|
switch (cmd.fanMode) {
|
||||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
|
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
|
||||||
map.value = "fanAuto"
|
map.value = "auto"
|
||||||
break
|
break
|
||||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW:
|
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW:
|
||||||
map.value = "fanOn"
|
map.value = "on"
|
||||||
break
|
break
|
||||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
|
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
|
||||||
map.value = "fanCirculate"
|
map.value = "circulate"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
state.lastTriedFanMode = map.value
|
state.lastTriedFanMode = map.value
|
||||||
createEvent(map)
|
sendEvent(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
|
||||||
def supportedModes = ""
|
def supportedModes = []
|
||||||
if(cmd.off) { supportedModes += "off " }
|
if(cmd.heat) { supportedModes << "heat" }
|
||||||
if(cmd.heat) { supportedModes += "heat " }
|
if(cmd.cool) { supportedModes << "cool" }
|
||||||
if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
|
// Make sure off is before auto, this ensures the right setpoint is used based on current temperature when auto is set
|
||||||
if(cmd.cool) { supportedModes += "cool " }
|
if(cmd.off) { supportedModes << "off" }
|
||||||
if(cmd.auto) { supportedModes += "auto " }
|
if(cmd.auto) { supportedModes << "auto" }
|
||||||
|
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
|
||||||
|
|
||||||
state.supportedModes = supportedModes
|
state.supportedModes = supportedModes
|
||||||
[ createEvent(name:"supportedModes", value: supportedModes, displayed: false),
|
sendEvent(name: "supportedThermostatModes", value: supportedModes, isStateChange: true, displayed: false)
|
||||||
response(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet()) ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
|
||||||
def supportedFanModes = ""
|
def supportedFanModes = []
|
||||||
if(cmd.auto) { supportedFanModes += "fanAuto " }
|
if(cmd.auto) { supportedFanModes << "auto" }
|
||||||
if(cmd.low) { supportedFanModes += "fanOn " }
|
if(cmd.low) { supportedFanModes << "on" }
|
||||||
if(cmd.circulation) { supportedFanModes += "fanCirculate " }
|
if(cmd.circulation) { supportedFanModes << "circulate" }
|
||||||
|
|
||||||
state.supportedFanModes = supportedFanModes
|
state.supportedFanModes = supportedFanModes
|
||||||
[ createEvent(name:"supportedFanModes", value: supportedModes, displayed: false),
|
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, isStateChange: true, displayed: false)
|
||||||
response(refresh()) ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||||
log.debug "Zwave event received: $cmd"
|
log.debug "Zwave BasicReport: $cmd"
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||||
def map = [ name: "battery", unit: "%" ]
|
def batteryState = cmd.batteryLevel
|
||||||
if (cmd.batteryLevel == 0xFF) {
|
def map = [name: "battery", unit: "%", value: cmd.batteryLevel]
|
||||||
|
if ((cmd.batteryLevel == 0xFF) || (cmd.batteryLevel == 0x00)) { // Special value for low battery alert
|
||||||
map.value = 1
|
map.value = 1
|
||||||
map.descriptionText = "${device.displayName} battery is low"
|
map.descriptionText = "${device.displayName} battery is low"
|
||||||
map.isStateChange = true
|
map.isStateChange = true
|
||||||
} else {
|
batteryState = "low_battery"
|
||||||
map.value = cmd.batteryLevel
|
|
||||||
}
|
}
|
||||||
state.lastbatt = now()
|
state.lastbatt = now()
|
||||||
createEvent(map)
|
sendEvent(name: "batteryIcon", value: batteryState, displayed: false)
|
||||||
|
sendEvent(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||||
log.warn "Unexpected zwave command $cmd"
|
log.warn "Unexpected zwave command $cmd"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
||||||
|
log.debug "ManufacturerSpecificReport ${cmd}: value:${cmd}"
|
||||||
|
if (cmd.manufacturerName) {
|
||||||
|
updateDataValue("manufacturer", cmd.manufacturerName)
|
||||||
|
}
|
||||||
|
if (cmd.productTypeId) {
|
||||||
|
updateDataValue("productTypeId", cmd.productTypeId.toString())
|
||||||
|
}
|
||||||
|
if (cmd.productId) {
|
||||||
|
updateDataValue("productId", cmd.productId.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
// Use encapsulation to differentiate refresh cmds from what the thermostat sends proactively on change
|
// Only allow refresh every 2 minutes to prevent flooding the Zwave network
|
||||||
def cmd = zwave.sensorMultilevelV2.sensorMultilevelGet()
|
def timeNow = now()
|
||||||
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:1).encapsulate(cmd).format()
|
if (!state.refreshTriggeredAt || (2 * 60 * 1000 < (timeNow - state.refreshTriggeredAt))) {
|
||||||
}
|
state.refreshTriggeredAt = timeNow
|
||||||
|
// refresh will request battery, prevent multiple request by setting lastbatt now
|
||||||
def nextRefreshQuery(name) {
|
state.lastbatt = timeNow
|
||||||
def cmd = null
|
// use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved
|
||||||
switch (name) {
|
runIn(2, "poll", [overwrite: true])
|
||||||
case "temperature":
|
|
||||||
cmd = zwave.thermostatModeV2.thermostatModeGet()
|
|
||||||
break
|
|
||||||
case "thermostatMode":
|
|
||||||
cmd = zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1)
|
|
||||||
break
|
|
||||||
case "heatingSetpoint":
|
|
||||||
cmd = zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2)
|
|
||||||
break
|
|
||||||
case "coolingSetpoint":
|
|
||||||
cmd = zwave.thermostatFanModeV3.thermostatFanModeGet()
|
|
||||||
break
|
|
||||||
case "thermostatFanMode":
|
|
||||||
cmd = zwave.thermostatOperatingStateV2.thermostatOperatingStateGet()
|
|
||||||
break
|
|
||||||
case "thermostatOperatingState":
|
|
||||||
// get humidity, multilevel sensor get to endpoint 2
|
|
||||||
cmd = zwave.sensorMultilevelV2.sensorMultilevelGet()
|
|
||||||
return zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:2).encapsulate(cmd).format()
|
|
||||||
default: return null
|
|
||||||
}
|
}
|
||||||
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:1).encapsulate(cmd).format()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def quickSetHeat(degrees) {
|
def poll() {
|
||||||
setHeatingSetpoint(degrees, 1000)
|
def cmds = []
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.multiChannelV3.multiInstanceCmdEncap(instance: 1).encapsulate(zwave.sensorMultilevelV3.sensorMultilevelGet()).format()) // temperature
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format())
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()) // HeatingSetpoint
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()) // CoolingSetpoint
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format())
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.multiChannelV3.multiInstanceCmdEncap(instance: 2).encapsulate(zwave.sensorMultilevelV3.sensorMultilevelGet()).format()) // humidity
|
||||||
|
def time = getTimeAndDay()
|
||||||
|
log.debug "time: $time"
|
||||||
|
if (time) {
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.clockV1.clockSet(time).format())
|
||||||
|
}
|
||||||
|
// Add 3 seconds delay between each command to avoid flooding the Z-Wave network choking the hub
|
||||||
|
sendHubCommand(cmds, 3000)
|
||||||
}
|
}
|
||||||
|
|
||||||
def setHeatingSetpoint(degrees, delay = 30000) {
|
def raiseHeatingSetpoint() {
|
||||||
setHeatingSetpoint(degrees.toDouble(), delay)
|
alterSetpoint(null, true, "heatingSetpoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
def setHeatingSetpoint(Double degrees, Integer delay = 30000) {
|
def lowerHeatingSetpoint() {
|
||||||
log.trace "setHeatingSetpoint($degrees, $delay)"
|
alterSetpoint(null, false, "heatingSetpoint")
|
||||||
def deviceScale = state.scale ?: 1
|
}
|
||||||
def deviceScaleString = deviceScale == 2 ? "C" : "F"
|
|
||||||
|
def raiseCoolSetpoint() {
|
||||||
|
alterSetpoint(null, true, "coolingSetpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
def lowerCoolSetpoint() {
|
||||||
|
alterSetpoint(null, false, "coolingSetpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjusts nextHeatingSetpoint either .5° C/1° F) if raise true/false
|
||||||
|
def alterSetpoint(degrees, raise, setpoint) {
|
||||||
def locationScale = getTemperatureScale()
|
def locationScale = getTemperatureScale()
|
||||||
def p = (state.precision == null) ? 1 : state.precision
|
def heatingSetpoint = getTempInLocalScale("heatingSetpoint")
|
||||||
|
def coolingSetpoint = getTempInLocalScale("coolingSetpoint")
|
||||||
def convertedDegrees
|
def targetvalue = (setpoint == "heatingSetpoint") ? heatingSetpoint : coolingSetpoint
|
||||||
if (locationScale == "C" && deviceScaleString == "F") {
|
def delta = (locationScale == "F") ? 1 : 0.5
|
||||||
convertedDegrees = celsiusToFahrenheit(degrees)
|
if (raise != null) {
|
||||||
} else if (locationScale == "F" && deviceScaleString == "C") {
|
targetvalue += raise ? delta : - delta
|
||||||
convertedDegrees = fahrenheitToCelsius(degrees)
|
} else if (degrees) {
|
||||||
|
targetvalue = degrees
|
||||||
} else {
|
} else {
|
||||||
convertedDegrees = degrees
|
log.warn "alterSetpoint called with neither up/down/degree information"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
def data = enforceSetpointLimits(setpoint, [targetvalue: targetvalue, heatingSetpoint: heatingSetpoint, coolingSetpoint: coolingSetpoint])
|
||||||
|
// update UI without waiting for the device to respond, this to give user a smoother UI experience
|
||||||
|
// also, as runIn's have to overwrite and user can change heating/cooling setpoint separately separate runIn's have to be used
|
||||||
|
if (data.targetHeatingSetpoint) {
|
||||||
|
sendEvent("name": "heatingSetpoint", "value": data.targetHeatingSetpoint, unit: locationScale, eventType: "ENTITY_UPDATE")//, displayed: false)
|
||||||
|
runIn(4, "updateHeatingSetpoint", [data: data, overwrite: true])
|
||||||
|
}
|
||||||
|
if (data.targetCoolingSetpoint) {
|
||||||
|
sendEvent("name": "coolingSetpoint", "value": data.targetCoolingSetpoint, unit: locationScale, eventType: "ENTITY_UPDATE")//, displayed: false)
|
||||||
|
runIn(4, "updateCoolingSetpoint", [data: data, overwrite: true])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateHeatingSetpoint(data) {
|
||||||
|
updateSetpoints(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateCoolingSetpoint(data) {
|
||||||
|
updateSetpoints(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
def enforceSetpointLimits(setpoint, data) {
|
||||||
|
// Enforce max/min for setpoints
|
||||||
|
def maxSetpoint = getTempInLocalScale(95, "F")
|
||||||
|
def minSetpoint = getTempInLocalScale(35, "F")
|
||||||
|
def targetvalue = data.targetvalue
|
||||||
|
def heatingSetpoint = null
|
||||||
|
def coolingSetpoint = null
|
||||||
|
|
||||||
|
if (targetvalue > maxSetpoint) {
|
||||||
|
targetvalue = maxSetpoint
|
||||||
|
} else if (targetvalue < minSetpoint) {
|
||||||
|
targetvalue = minSetpoint
|
||||||
|
}
|
||||||
|
// Enforce limits, for now make sure heating <= cooling, and cooling >= heating
|
||||||
|
if (setpoint == "heatingSetpoint") {
|
||||||
|
heatingSetpoint = targetvalue
|
||||||
|
coolingSetpoint = (heatingSetpoint > data.coolingSetpoint) ? heatingSetpoint : null
|
||||||
|
}
|
||||||
|
if (setpoint == "coolingSetpoint") {
|
||||||
|
coolingSetpoint = targetvalue
|
||||||
|
heatingSetpoint = (coolingSetpoint < data.heatingSetpoint) ? coolingSetpoint : null
|
||||||
|
}
|
||||||
|
return [targetHeatingSetpoint: heatingSetpoint, targetCoolingSetpoint: coolingSetpoint]
|
||||||
|
}
|
||||||
|
|
||||||
|
def setHeatingSetpoint(degrees) {
|
||||||
|
if (degrees) {
|
||||||
|
def data = enforceSetpointLimits("heatingSetpoint",
|
||||||
|
[targetvalue: degrees.toDouble(), heatingSetpoint: getTempInLocalScale("heatingSetpoint"), coolingSetpoint: getTempInLocalScale("coolingSetpoint")])
|
||||||
|
updateSetpoints(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def setCoolingSetpoint(degrees) {
|
||||||
|
if (degrees) {
|
||||||
|
def data = enforceSetpointLimits("coolingSetpoint",
|
||||||
|
[targetvalue: degrees.toDouble(), heatingSetpoint: getTempInLocalScale("heatingSetpoint"), coolingSetpoint: getTempInLocalScale("coolingSetpoint")])
|
||||||
|
updateSetpoints(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateSetpoints(data) {
|
||||||
|
def cmds = []
|
||||||
|
if (data.targetHeatingSetpoint) {
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
|
||||||
|
setpointType: 1, scale: state.scale, precision: state.precision, scaledValue: convertToDeviceScale(data.targetHeatingSetpoint)).format())
|
||||||
|
}
|
||||||
|
if (data.targetCoolingSetpoint) {
|
||||||
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
|
||||||
|
setpointType: 2, scale: state.scale, precision: state.precision, scaledValue: convertToDeviceScale(data.targetCoolingSetpoint)).format())
|
||||||
}
|
}
|
||||||
|
|
||||||
delayBetween([
|
// Always request both setpoints in case thermostat changed both
|
||||||
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format(),
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format())
|
||||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
|
||||||
], delay)
|
sendHubCommand(cmds)
|
||||||
}
|
}
|
||||||
|
|
||||||
def quickSetCool(degrees) {
|
def convertToDeviceScale(setpoint) {
|
||||||
setCoolingSetpoint(degrees, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
def setCoolingSetpoint(degrees, delay = 30000) {
|
|
||||||
setCoolingSetpoint(degrees.toDouble(), delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
|
|
||||||
log.trace "setCoolingSetpoint($degrees, $delay)"
|
|
||||||
def deviceScale = state.scale ?: 1
|
|
||||||
def deviceScaleString = deviceScale == 2 ? "C" : "F"
|
|
||||||
def locationScale = getTemperatureScale()
|
def locationScale = getTemperatureScale()
|
||||||
def p = (state.precision == null) ? 1 : state.precision
|
def deviceScale = (state.scale == 1) ? "F" : "C"
|
||||||
|
return (deviceScale == locationScale) ? setpoint :
|
||||||
def convertedDegrees
|
(deviceScale == "F" ? celsiusToFahrenheit(setpoint.toBigDecimal()) : roundC(fahrenheitToCelsius(setpoint.toBigDecimal())))
|
||||||
if (locationScale == "C" && deviceScaleString == "F") {
|
|
||||||
convertedDegrees = celsiusToFahrenheit(degrees)
|
|
||||||
} else if (locationScale == "F" && deviceScaleString == "C") {
|
|
||||||
convertedDegrees = fahrenheitToCelsius(degrees)
|
|
||||||
} else {
|
|
||||||
convertedDegrees = degrees
|
|
||||||
}
|
|
||||||
|
|
||||||
delayBetween([
|
|
||||||
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 2, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format(),
|
|
||||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()
|
|
||||||
], delay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -456,78 +537,56 @@ def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
|
|||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
log.debug "ping() called"
|
log.debug "ping() called"
|
||||||
refresh()
|
// Just get Operating State as it is not reported when it chnages and there's no need to flood more commands
|
||||||
}
|
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()))
|
||||||
|
|
||||||
def configure() {
|
|
||||||
delayBetween([
|
|
||||||
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
|
|
||||||
], 2300)
|
|
||||||
}
|
|
||||||
|
|
||||||
def modes() {
|
|
||||||
["off", "heat", "cool", "auto", "emergency heat"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def switchMode() {
|
def switchMode() {
|
||||||
def currentMode = device.currentState("thermostatMode")?.value
|
def currentMode = device.currentValue("thermostatMode")
|
||||||
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
|
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
|
||||||
def supportedModes = getDataByName("supportedModes")
|
def supportedModes = state.supportedModes
|
||||||
def modeOrder = modes()
|
if (supportedModes) {
|
||||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] }
|
||||||
def nextMode = next(lastTriedMode)
|
def nextMode = next(lastTriedMode)
|
||||||
if (supportedModes?.contains(currentMode)) {
|
setThermostatMode(nextMode)
|
||||||
while (!supportedModes.contains(nextMode) && nextMode != "off") {
|
|
||||||
nextMode = next(nextMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.lastTriedMode = nextMode
|
state.lastTriedMode = nextMode
|
||||||
delayBetween([
|
} else {
|
||||||
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[nextMode]).format(),
|
log.warn "supportedModes not defined"
|
||||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
}
|
||||||
], 1000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def switchToMode(nextMode) {
|
def switchToMode(nextMode) {
|
||||||
def supportedModes = getDataByName("supportedModes")
|
def supportedModes = state.supportedModes
|
||||||
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
if (supportedModes && supportedModes.contains(nextMode)) {
|
||||||
if (nextMode in modes()) {
|
setThermostatMode(nextMode)
|
||||||
state.lastTriedMode = nextMode
|
state.lastTriedMode = nextMode
|
||||||
"$nextMode"()
|
|
||||||
} else {
|
} else {
|
||||||
log.debug("no mode method '$nextMode'")
|
log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def switchFanMode() {
|
def switchFanMode() {
|
||||||
def currentMode = device.currentState("thermostatFanMode")?.value
|
def currentMode = device.currentState("thermostatFanMode")?.value
|
||||||
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off"
|
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off"
|
||||||
def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanOn"
|
def supportedFanModes = state.supportedFanModes
|
||||||
def modeOrder = ["fanAuto", "fanCirculate", "fanOn"]
|
if (supportedFanModes) {
|
||||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] }
|
||||||
def nextMode = next(lastTriedMode)
|
def nextMode = next(lastTriedMode)
|
||||||
while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") {
|
setThermostatFanMode(nextMode)
|
||||||
nextMode = next(nextMode)
|
state.lastTriedFanMode = nextMode
|
||||||
|
} else {
|
||||||
|
log.warn "supportedFanModes not defined"
|
||||||
}
|
}
|
||||||
switchToFanMode(nextMode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def switchToFanMode(nextMode) {
|
def switchToFanMode(nextMode) {
|
||||||
def supportedFanModes = getDataByName("supportedFanModes")
|
def supportedFanModes = state.supportedFanModes
|
||||||
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
if (supportedFanModes && supportedFanModes.contains(nextMode)) {
|
||||||
|
setThermostatFanMode(nextMode)
|
||||||
def returnCommand
|
state.lastTriedFanMode = nextMode
|
||||||
if (nextMode == "fanAuto") {
|
|
||||||
returnCommand = fanAuto()
|
|
||||||
} else if (nextMode == "fanOn") {
|
|
||||||
returnCommand = fanOn()
|
|
||||||
} else if (nextMode == "fanCirculate") {
|
|
||||||
returnCommand = fanCirculate()
|
|
||||||
} else {
|
} else {
|
||||||
log.debug("no fan mode '$nextMode'")
|
log.debug("FanMode $nextMode is not supported by ${device.displayName}")
|
||||||
}
|
}
|
||||||
if(returnCommand) state.lastTriedFanMode = nextMode
|
|
||||||
returnCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def getDataByName(String name) {
|
def getDataByName(String name) {
|
||||||
@@ -543,10 +602,10 @@ def getModeMap() { [
|
|||||||
]}
|
]}
|
||||||
|
|
||||||
def setThermostatMode(String value) {
|
def setThermostatMode(String value) {
|
||||||
delayBetween([
|
def cmds = []
|
||||||
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format(),
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format())
|
||||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
|
||||||
], standardDelay)
|
sendHubCommand(cmds)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getFanModeMap() { [
|
def getFanModeMap() { [
|
||||||
@@ -556,69 +615,70 @@ def getFanModeMap() { [
|
|||||||
]}
|
]}
|
||||||
|
|
||||||
def setThermostatFanMode(String value) {
|
def setThermostatFanMode(String value) {
|
||||||
delayBetween([
|
def cmds = []
|
||||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format(),
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format())
|
||||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
|
||||||
], standardDelay)
|
sendHubCommand(cmds)
|
||||||
}
|
}
|
||||||
|
|
||||||
def off() {
|
def off() {
|
||||||
delayBetween([
|
switchToMode("off")
|
||||||
zwave.thermostatModeV2.thermostatModeSet(mode: 0).format(),
|
|
||||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
|
||||||
], standardDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def heat() {
|
def heat() {
|
||||||
delayBetween([
|
switchToMode("heat")
|
||||||
zwave.thermostatModeV2.thermostatModeSet(mode: 1).format(),
|
|
||||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
|
||||||
], standardDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def emergencyHeat() {
|
def emergencyHeat() {
|
||||||
delayBetween([
|
switchToMode("emergency heat")
|
||||||
zwave.thermostatModeV2.thermostatModeSet(mode: 4).format(),
|
|
||||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
|
||||||
], standardDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def cool() {
|
def cool() {
|
||||||
delayBetween([
|
switchToMode("cool")
|
||||||
zwave.thermostatModeV2.thermostatModeSet(mode: 2).format(),
|
|
||||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
|
||||||
], standardDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def auto() {
|
def auto() {
|
||||||
delayBetween([
|
switchToMode("auto")
|
||||||
zwave.thermostatModeV2.thermostatModeSet(mode: 3).format(),
|
|
||||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
|
||||||
], standardDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanOn() {
|
def fanOn() {
|
||||||
delayBetween([
|
switchToFanMode("on")
|
||||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 1).format(),
|
|
||||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
|
||||||
], standardDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanAuto() {
|
def fanAuto() {
|
||||||
delayBetween([
|
switchToFanMode("auto")
|
||||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 0).format(),
|
|
||||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
|
||||||
], standardDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanCirculate() {
|
def fanCirculate() {
|
||||||
delayBetween([
|
switchToFanMode("circulate")
|
||||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 6).format(),
|
|
||||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
|
||||||
], standardDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getStandardDelay() {
|
private getTimeAndDay() {
|
||||||
1000
|
def timeNow = now()
|
||||||
|
// Need to check that location have timeZone as SC may have created the location without setting it
|
||||||
|
// Don't update clock more than once a day
|
||||||
|
if (location.timeZone && (!state.timeClockSet || (24 * 60 * 60 * 1000 < (timeNow - state.timeClockSet)))) {
|
||||||
|
def currentDate = Calendar.getInstance(location.timeZone)
|
||||||
|
state.timeClockSet = timeNow
|
||||||
|
return [hour: currentDate.get(Calendar.HOUR_OF_DAY), minute: currentDate.get(Calendar.MINUTE), weekday: currentDate.get(Calendar.DAY_OF_WEEK)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get stored temperature from currentState in current local scale
|
||||||
|
def getTempInLocalScale(state) {
|
||||||
|
def temp = device.currentState(state)
|
||||||
|
if (temp && temp.value && temp.unit) {
|
||||||
|
return getTempInLocalScale(temp.value.toBigDecimal(), temp.unit)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// get/convert temperature to current local scale
|
||||||
|
def getTempInLocalScale(temp, scale) {
|
||||||
|
def scaledTemp = convertTemperatureIfNeeded(temp.toBigDecimal(), scale).toDouble()
|
||||||
|
return (getTemperatureScale() == "F" ? scaledTemp.round(0).toInteger() : roundC(scaledTemp))
|
||||||
|
}
|
||||||
|
|
||||||
|
def roundC (tempC) {
|
||||||
|
return (Math.round(tempC.toDouble() * 2))/2
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user