mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-19 13:20:53 +00:00
Compare commits
1 Commits
PROD_2017.
...
MSA-2099-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca42ee5526 |
@@ -73,7 +73,7 @@ metadata {
|
|||||||
[value: 64, color: "#44B621"],
|
[value: 64, color: "#44B621"],
|
||||||
[value: 80, color: "#3D79D9"],
|
[value: 80, color: "#3D79D9"],
|
||||||
[value: 96, color: "#0A50C2"]
|
[value: 96, color: "#0A50C2"]
|
||||||
], icon:"st.Weather.weather12"
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
valueTile("maxHum", "device.maxHum", canChangeIcon: false, canChangeBackground: false) {
|
valueTile("maxHum", "device.maxHum", canChangeIcon: false, canChangeBackground: false) {
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ def updated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def initialize() {
|
def initialize() {
|
||||||
// Device only goes OFFLINE when Hub is off
|
// Arrival sensors only goes OFFLINE when Hub is off
|
||||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false)
|
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||||
|
|
||||||
def zwMap = getZwaveInfo()
|
def zwMap = getZwaveInfo()
|
||||||
def buttons = 4 // Default for Key Fob
|
def buttons = 4 // Default for Key Fob
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ def configure() {
|
|||||||
return cmds
|
return cmds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def installed() {
|
def installed() {
|
||||||
initialize()
|
initialize()
|
||||||
}
|
}
|
||||||
@@ -120,7 +121,7 @@ def updated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def initialize() {
|
def initialize() {
|
||||||
// Device only goes OFFLINE when Hub is off
|
// Arrival sensors only goes OFFLINE when Hub is off
|
||||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false)
|
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||||
sendEvent(name: "numberOfButtons", value: 4)
|
sendEvent(name: "numberOfButtons", value: 4)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,13 @@ Works with:
|
|||||||
## Device Health
|
## Device Health
|
||||||
|
|
||||||
Aeon Labs MultiSensor 6 is polled by the hub.
|
Aeon Labs MultiSensor 6 is polled by the hub.
|
||||||
Aeon MultiSensor reports in once every hour.
|
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||||
|
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||||
|
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||||
|
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||||
|
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||||
|
|
||||||
* __122min__ checkInterval
|
* __32min__ checkInterval
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
@@ -130,13 +130,13 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def installed(){
|
def installed(){
|
||||||
// Device-Watch simply pings if no device events received for 122min(checkInterval)
|
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||||
sendEvent(name: "checkInterval", value: 2 * 60 * 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])
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
// Device-Watch simply pings if no device events received for 122min(checkInterval)
|
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||||
sendEvent(name: "checkInterval", value: 2 * 60 * 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])
|
||||||
log.debug "Updated with settings: ${settings}"
|
log.debug "Updated with settings: ${settings}"
|
||||||
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"
|
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,13 @@ Works with:
|
|||||||
## Device Health
|
## Device Health
|
||||||
|
|
||||||
Aeon Labs MultiSensor (Gen 5) is polled by the hub.
|
Aeon Labs MultiSensor (Gen 5) is polled by the hub.
|
||||||
Aeon MultiSensor Gen5 reports in once every hour.
|
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||||
|
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||||
|
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||||
|
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||||
|
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||||
|
|
||||||
* __122min__ checkInterval
|
* __32min__ checkInterval
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
@@ -100,12 +100,12 @@ metadata {
|
|||||||
|
|
||||||
def installed(){
|
def installed(){
|
||||||
// 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 * 60 * 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])
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated(){
|
def updated(){
|
||||||
// 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 * 60 * 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])
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description)
|
def parse(String description)
|
||||||
|
|||||||
@@ -28,9 +28,13 @@ Works with:
|
|||||||
## Device Health
|
## Device Health
|
||||||
|
|
||||||
Aeon Labs MultiSensor is polled by the hub.
|
Aeon Labs MultiSensor is polled by the hub.
|
||||||
Aeon MultiSensor reports in once every hour.
|
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||||
|
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||||
|
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||||
|
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||||
|
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||||
|
|
||||||
* __122min__ checkInterval
|
* __32min__ checkInterval
|
||||||
|
|
||||||
## Battery Specification
|
## Battery Specification
|
||||||
|
|
||||||
|
|||||||
@@ -96,12 +96,12 @@ metadata {
|
|||||||
|
|
||||||
def installed(){
|
def installed(){
|
||||||
// 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 * 60 * 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])
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated(){
|
def updated(){
|
||||||
// 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 * 60 * 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])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse incoming device messages to generate events
|
// Parse incoming device messages to generate events
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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"
|
||||||
@@ -14,173 +15,161 @@ metadata {
|
|||||||
|
|
||||||
command "switchMode"
|
command "switchMode"
|
||||||
command "switchFanMode"
|
command "switchFanMode"
|
||||||
command "lowerHeatingSetpoint"
|
command "quickSetCool"
|
||||||
command "raiseHeatingSetpoint"
|
command "quickSetHeat"
|
||||||
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles {
|
// simulator metadata
|
||||||
multiAttributeTile(name:"temperature", type:"generic", width:3, height:2, canChangeIcon: true) {
|
simulator {
|
||||||
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
status "off" : "command: 4003, payload: 00"
|
||||||
attributeState("temperature", label:'${currentValue}°', icon: "st.alarm.temperature.normal",
|
status "heat" : "command: 4003, payload: 01"
|
||||||
backgroundColors:[
|
status "cool" : "command: 4003, payload: 02"
|
||||||
// Celsius
|
status "auto" : "command: 4003, payload: 03"
|
||||||
[value: 0, color: "#153591"],
|
status "emergencyHeat" : "command: 4003, payload: 04"
|
||||||
[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: 59, color: "#90d2a7"],
|
|
||||||
[value: 74, color: "#44b621"],
|
|
||||||
[value: 84, color: "#f1d801"],
|
|
||||||
[value: 95, color: "#d04e00"],
|
|
||||||
[value: 96, color: "#bc2323"]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
tileAttribute("device.batteryIcon", key: "SECONDARY_CONTROL") {
|
|
||||||
attributeState "ok_battery", label:'${currentValue}%', icon:"st.arlo.sensor_battery_4"
|
|
||||||
attributeState "low_battery", label:'Low Battery', icon:"st.arlo.sensor_battery_0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
standardTile("mode", "device.thermostatMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
standardTile("fanMode", "device.thermostatFanMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
valueTile("humidity", "device.humidity", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
|
||||||
state "humidity", label:'${currentValue}%', icon:"st.Weather.weather12"
|
|
||||||
}
|
|
||||||
standardTile("lowerHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
|
||||||
state "heatingSetpoint", action:"lowerHeatingSetpoint", icon:"st.thermostat.thermostat-left"
|
|
||||||
}
|
|
||||||
valueTile("heatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
|
||||||
state "heatingSetpoint", label:'${currentValue}° heat', backgroundColor:"#ffffff"
|
|
||||||
}
|
|
||||||
standardTile("raiseHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
|
||||||
state "heatingSetpoint", action:"raiseHeatingSetpoint", icon:"st.thermostat.thermostat-right"
|
|
||||||
}
|
|
||||||
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") {
|
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 {
|
||||||
|
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||||
|
state("temperature", label:'${currentValue}°',
|
||||||
|
backgroundColors:[
|
||||||
|
[value: 32, color: "#153591"],
|
||||||
|
[value: 44, color: "#1e9cbb"],
|
||||||
|
[value: 59, color: "#90d2a7"],
|
||||||
|
[value: 74, color: "#44b621"],
|
||||||
|
[value: 84, color: "#f1d801"],
|
||||||
|
[value: 92, color: "#d04e00"],
|
||||||
|
[value: 98, color: "#bc2323"]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "off", label:'${name}', action:"switchMode", nextState:"to_heat"
|
||||||
|
state "heat", label:'${name}', action:"switchMode", nextState:"to_cool"
|
||||||
|
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) {
|
||||||
|
state "setHeatingSetpoint", action:"quickSetHeat", backgroundColor:"#e86d13"
|
||||||
|
}
|
||||||
|
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "heat", label:'${currentValue}° heat', backgroundColor:"#ffffff"
|
||||||
|
}
|
||||||
|
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||||
|
state "setCoolingSetpoint", action:"quickSetCool", backgroundColor: "#00a0dc"
|
||||||
|
}
|
||||||
|
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
||||||
|
}
|
||||||
|
valueTile("humidity", "device.humidity", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "humidity", label:'${currentValue}% humidity', unit:""
|
||||||
|
}
|
||||||
|
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "battery", label:'${currentValue}% battery', unit:""
|
||||||
|
}
|
||||||
|
standardTile("refresh", "device.thermostatMode", 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", "humidity", "lowerHeatingSetpoint", "heatingSetpoint", "raiseHeatingSetpoint", "lowerCoolSetpoint", "coolingSetpoint", "raiseCoolSetpoint", "refresh"])
|
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh", "humidity", "battery"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
// If not set update ManufacturerSpecific data
|
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||||
if (!getDataValue("manufacturer")) {
|
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
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 = null
|
def result = []
|
||||||
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 []
|
return null
|
||||||
}
|
}
|
||||||
return [result]
|
if (result.size() == 1 && (!state.lastbatt || now() - state.lastbatt > 48*60*60*1000)) {
|
||||||
|
result << response(zwave.batteryV1.batteryGet().format())
|
||||||
|
}
|
||||||
|
log.debug "$device.displayName parsed '$description' to $result"
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiInstanceCmdEncap cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
|
||||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 3])
|
def result = null
|
||||||
log.debug ("multiinstancev1.MultiInstanceCmdEncap: command from instance ${cmd.instance}: ${encapsulatedCommand}")
|
def encapsulatedCommand = cmd.encapsulatedCommand([0x42:2, 0x43:2, 0x31: 2])
|
||||||
|
log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}")
|
||||||
if (encapsulatedCommand) {
|
if (encapsulatedCommand) {
|
||||||
zwaveEvent(encapsulatedCommand)
|
result = 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 sendCmd = []
|
|
||||||
def unit = getTemperatureScale()
|
|
||||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||||
def setpoint = getTempInLocalScale(cmd.scaledValue, cmdScale)
|
def temp = convertTemperatureIfNeeded(cmd.scaledValue, cmdScale, cmd.precision)
|
||||||
|
def unit = getTemperatureScale()
|
||||||
|
def map1 = [ value: temp, unit: unit, displayed: false ]
|
||||||
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"
|
||||||
@@ -191,55 +180,33 @@ 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// thermostatSetpoint is not displayed by any tile as it can't be predictable calculated due to
|
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd)
|
||||||
// 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 = getTempInLocalScale(cmd.scaledSensorValue, (cmd.scale == 1 ? "F" : "C"))
|
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C", cmd.precision)
|
||||||
} 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
|
||||||
}
|
}
|
||||||
sendEvent(map)
|
createEvent(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport 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:
|
||||||
@@ -264,7 +231,12 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.Thermosta
|
|||||||
map.value = "vent economizer"
|
map.value = "vent economizer"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sendEvent(map)
|
def result = createEvent(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) {
|
||||||
@@ -280,256 +252,203 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanSt
|
|||||||
map.value = "running high"
|
map.value = "running high"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sendEvent(map)
|
createEvent(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
|
||||||
def map = [name: "thermostatMode", data:[supportedThermostatModes: state.supportedModes]]
|
def map = [name: "thermostatMode"]
|
||||||
|
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
|
||||||
sendEvent(map)
|
if (thermostatSetpoint) {
|
||||||
updateThermostatSetpoint(null, null)
|
[ createEvent(map), createEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: getTemperatureScale()) ]
|
||||||
|
} else {
|
||||||
|
createEvent(map)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) {
|
||||||
def map = [name: "thermostatFanMode", data:[supportedThermostatFanModes: state.supportedFanModes]]
|
def map = [name: "thermostatFanMode", displayed: false]
|
||||||
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 = "auto"
|
map.value = "fanAuto"
|
||||||
break
|
break
|
||||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW:
|
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW:
|
||||||
map.value = "on"
|
map.value = "fanOn"
|
||||||
break
|
break
|
||||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
|
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
|
||||||
map.value = "circulate"
|
map.value = "fanCirculate"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
state.lastTriedFanMode = map.value
|
state.lastTriedFanMode = map.value
|
||||||
sendEvent(map)
|
createEvent(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
|
||||||
def supportedModes = []
|
def supportedModes = ""
|
||||||
if(cmd.heat) { supportedModes << "heat" }
|
if(cmd.off) { supportedModes += "off " }
|
||||||
if(cmd.cool) { supportedModes << "cool" }
|
if(cmd.heat) { supportedModes += "heat " }
|
||||||
// Make sure off is before auto, this ensures the right setpoint is used based on current temperature when auto is set
|
if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
|
||||||
if(cmd.off) { supportedModes << "off" }
|
if(cmd.cool) { supportedModes += "cool " }
|
||||||
if(cmd.auto) { supportedModes << "auto" }
|
if(cmd.auto) { supportedModes += "auto " }
|
||||||
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
|
|
||||||
|
|
||||||
state.supportedModes = supportedModes
|
state.supportedModes = supportedModes
|
||||||
sendEvent(name: "supportedThermostatModes", value: supportedModes, isStateChange: true, displayed: false)
|
[ createEvent(name:"supportedModes", value: supportedModes, 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 << "auto" }
|
if(cmd.auto) { supportedFanModes += "fanAuto " }
|
||||||
if(cmd.low) { supportedFanModes << "on" }
|
if(cmd.low) { supportedFanModes += "fanOn " }
|
||||||
if(cmd.circulation) { supportedFanModes << "circulate" }
|
if(cmd.circulation) { supportedFanModes += "fanCirculate " }
|
||||||
|
|
||||||
state.supportedFanModes = supportedFanModes
|
state.supportedFanModes = supportedFanModes
|
||||||
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, isStateChange: true, displayed: false)
|
[ createEvent(name:"supportedFanModes", value: supportedModes, displayed: false),
|
||||||
|
response(refresh()) ]
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||||
log.debug "Zwave BasicReport: $cmd"
|
log.debug "Zwave event received: $cmd"
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||||
def batteryState = cmd.batteryLevel
|
def map = [ name: "battery", unit: "%" ]
|
||||||
def map = [name: "battery", unit: "%", value: cmd.batteryLevel]
|
if (cmd.batteryLevel == 0xFF) {
|
||||||
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
|
||||||
batteryState = "low_battery"
|
} else {
|
||||||
|
map.value = cmd.batteryLevel
|
||||||
}
|
}
|
||||||
state.lastbatt = now()
|
state.lastbatt = now()
|
||||||
sendEvent(name: "batteryIcon", value: batteryState, displayed: false)
|
createEvent(map)
|
||||||
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() {
|
||||||
// Only allow refresh every 2 minutes to prevent flooding the Zwave network
|
// Use encapsulation to differentiate refresh cmds from what the thermostat sends proactively on change
|
||||||
def timeNow = now()
|
def cmd = zwave.sensorMultilevelV2.sensorMultilevelGet()
|
||||||
if (!state.refreshTriggeredAt || (2 * 60 * 1000 < (timeNow - state.refreshTriggeredAt))) {
|
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:1).encapsulate(cmd).format()
|
||||||
state.refreshTriggeredAt = timeNow
|
}
|
||||||
// refresh will request battery, prevent multiple request by setting lastbatt now
|
|
||||||
state.lastbatt = timeNow
|
def nextRefreshQuery(name) {
|
||||||
// use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved
|
def cmd = null
|
||||||
runIn(2, "poll", [overwrite: true])
|
switch (name) {
|
||||||
|
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 poll() {
|
def quickSetHeat(degrees) {
|
||||||
def cmds = []
|
setHeatingSetpoint(degrees, 1000)
|
||||||
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 raiseHeatingSetpoint() {
|
def setHeatingSetpoint(degrees, delay = 30000) {
|
||||||
alterSetpoint(null, true, "heatingSetpoint")
|
setHeatingSetpoint(degrees.toDouble(), delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def lowerHeatingSetpoint() {
|
def setHeatingSetpoint(Double degrees, Integer delay = 30000) {
|
||||||
alterSetpoint(null, false, "heatingSetpoint")
|
log.trace "setHeatingSetpoint($degrees, $delay)"
|
||||||
}
|
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 heatingSetpoint = getTempInLocalScale("heatingSetpoint")
|
def p = (state.precision == null) ? 1 : state.precision
|
||||||
def coolingSetpoint = getTempInLocalScale("coolingSetpoint")
|
|
||||||
def targetvalue = (setpoint == "heatingSetpoint") ? heatingSetpoint : coolingSetpoint
|
def convertedDegrees
|
||||||
def delta = (locationScale == "F") ? 1 : 0.5
|
if (locationScale == "C" && deviceScaleString == "F") {
|
||||||
if (raise != null) {
|
convertedDegrees = celsiusToFahrenheit(degrees)
|
||||||
targetvalue += raise ? delta : - delta
|
} else if (locationScale == "F" && deviceScaleString == "C") {
|
||||||
} else if (degrees) {
|
convertedDegrees = fahrenheitToCelsius(degrees)
|
||||||
targetvalue = degrees
|
|
||||||
} else {
|
} else {
|
||||||
log.warn "alterSetpoint called with neither up/down/degree information"
|
convertedDegrees = degrees
|
||||||
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])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delayBetween([
|
||||||
|
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format(),
|
||||||
|
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
|
||||||
|
], delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def updateHeatingSetpoint(data) {
|
def quickSetCool(degrees) {
|
||||||
updateSetpoints(data)
|
setCoolingSetpoint(degrees, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
def updateCoolingSetpoint(data) {
|
def setCoolingSetpoint(degrees, delay = 30000) {
|
||||||
updateSetpoints(data)
|
setCoolingSetpoint(degrees.toDouble(), delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def enforceSetpointLimits(setpoint, data) {
|
def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
|
||||||
// Enforce max/min for setpoints
|
log.trace "setCoolingSetpoint($degrees, $delay)"
|
||||||
def maxSetpoint = getTempInLocalScale(95, "F")
|
def deviceScale = state.scale ?: 1
|
||||||
def minSetpoint = getTempInLocalScale(35, "F")
|
def deviceScaleString = deviceScale == 2 ? "C" : "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())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always request both setpoints in case thermostat changed both
|
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format())
|
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
|
|
||||||
sendHubCommand(cmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
def convertToDeviceScale(setpoint) {
|
|
||||||
def locationScale = getTemperatureScale()
|
def locationScale = getTemperatureScale()
|
||||||
def deviceScale = (state.scale == 1) ? "F" : "C"
|
def p = (state.precision == null) ? 1 : state.precision
|
||||||
return (deviceScale == locationScale) ? setpoint :
|
|
||||||
(deviceScale == "F" ? celsiusToFahrenheit(setpoint.toBigDecimal()) : roundC(fahrenheitToCelsius(setpoint.toBigDecimal())))
|
def convertedDegrees
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -537,56 +456,78 @@ def convertToDeviceScale(setpoint) {
|
|||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
log.debug "ping() called"
|
log.debug "ping() called"
|
||||||
// Just get Operating State as it is not reported when it chnages and there's no need to flood more commands
|
refresh()
|
||||||
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.currentValue("thermostatMode")
|
def currentMode = device.currentState("thermostatMode")?.value
|
||||||
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
|
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
|
||||||
def supportedModes = state.supportedModes
|
def supportedModes = getDataByName("supportedModes")
|
||||||
if (supportedModes) {
|
def modeOrder = modes()
|
||||||
def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] }
|
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||||
def nextMode = next(lastTriedMode)
|
def nextMode = next(lastTriedMode)
|
||||||
setThermostatMode(nextMode)
|
if (supportedModes?.contains(currentMode)) {
|
||||||
state.lastTriedMode = nextMode
|
while (!supportedModes.contains(nextMode) && nextMode != "off") {
|
||||||
} else {
|
nextMode = next(nextMode)
|
||||||
log.warn "supportedModes not defined"
|
}
|
||||||
}
|
}
|
||||||
|
state.lastTriedMode = nextMode
|
||||||
|
delayBetween([
|
||||||
|
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[nextMode]).format(),
|
||||||
|
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||||
|
], 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
def switchToMode(nextMode) {
|
def switchToMode(nextMode) {
|
||||||
def supportedModes = state.supportedModes
|
def supportedModes = getDataByName("supportedModes")
|
||||||
if (supportedModes && supportedModes.contains(nextMode)) {
|
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||||
setThermostatMode(nextMode)
|
if (nextMode in modes()) {
|
||||||
state.lastTriedMode = nextMode
|
state.lastTriedMode = nextMode
|
||||||
|
"$nextMode"()
|
||||||
} else {
|
} else {
|
||||||
log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}")
|
log.debug("no mode method '$nextMode'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 supportedFanModes = state.supportedFanModes
|
def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanOn"
|
||||||
if (supportedFanModes) {
|
def modeOrder = ["fanAuto", "fanCirculate", "fanOn"]
|
||||||
def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] }
|
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||||
def nextMode = next(lastTriedMode)
|
def nextMode = next(lastTriedMode)
|
||||||
setThermostatFanMode(nextMode)
|
while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") {
|
||||||
state.lastTriedFanMode = nextMode
|
nextMode = next(nextMode)
|
||||||
} else {
|
|
||||||
log.warn "supportedFanModes not defined"
|
|
||||||
}
|
}
|
||||||
|
switchToFanMode(nextMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
def switchToFanMode(nextMode) {
|
def switchToFanMode(nextMode) {
|
||||||
def supportedFanModes = state.supportedFanModes
|
def supportedFanModes = getDataByName("supportedFanModes")
|
||||||
if (supportedFanModes && supportedFanModes.contains(nextMode)) {
|
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||||
setThermostatFanMode(nextMode)
|
|
||||||
state.lastTriedFanMode = nextMode
|
def returnCommand
|
||||||
|
if (nextMode == "fanAuto") {
|
||||||
|
returnCommand = fanAuto()
|
||||||
|
} else if (nextMode == "fanOn") {
|
||||||
|
returnCommand = fanOn()
|
||||||
|
} else if (nextMode == "fanCirculate") {
|
||||||
|
returnCommand = fanCirculate()
|
||||||
} else {
|
} else {
|
||||||
log.debug("FanMode $nextMode is not supported by ${device.displayName}")
|
log.debug("no fan mode '$nextMode'")
|
||||||
}
|
}
|
||||||
|
if(returnCommand) state.lastTriedFanMode = nextMode
|
||||||
|
returnCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
def getDataByName(String name) {
|
def getDataByName(String name) {
|
||||||
@@ -602,10 +543,10 @@ def getModeMap() { [
|
|||||||
]}
|
]}
|
||||||
|
|
||||||
def setThermostatMode(String value) {
|
def setThermostatMode(String value) {
|
||||||
def cmds = []
|
delayBetween([
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format())
|
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format(),
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
|
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||||
sendHubCommand(cmds)
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getFanModeMap() { [
|
def getFanModeMap() { [
|
||||||
@@ -615,70 +556,69 @@ def getFanModeMap() { [
|
|||||||
]}
|
]}
|
||||||
|
|
||||||
def setThermostatFanMode(String value) {
|
def setThermostatFanMode(String value) {
|
||||||
def cmds = []
|
delayBetween([
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format())
|
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format(),
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
|
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||||
sendHubCommand(cmds)
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def off() {
|
def off() {
|
||||||
switchToMode("off")
|
delayBetween([
|
||||||
|
zwave.thermostatModeV2.thermostatModeSet(mode: 0).format(),
|
||||||
|
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||||
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def heat() {
|
def heat() {
|
||||||
switchToMode("heat")
|
delayBetween([
|
||||||
|
zwave.thermostatModeV2.thermostatModeSet(mode: 1).format(),
|
||||||
|
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||||
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def emergencyHeat() {
|
def emergencyHeat() {
|
||||||
switchToMode("emergency heat")
|
delayBetween([
|
||||||
|
zwave.thermostatModeV2.thermostatModeSet(mode: 4).format(),
|
||||||
|
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||||
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def cool() {
|
def cool() {
|
||||||
switchToMode("cool")
|
delayBetween([
|
||||||
|
zwave.thermostatModeV2.thermostatModeSet(mode: 2).format(),
|
||||||
|
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||||
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def auto() {
|
def auto() {
|
||||||
switchToMode("auto")
|
delayBetween([
|
||||||
|
zwave.thermostatModeV2.thermostatModeSet(mode: 3).format(),
|
||||||
|
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||||
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanOn() {
|
def fanOn() {
|
||||||
switchToFanMode("on")
|
delayBetween([
|
||||||
|
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 1).format(),
|
||||||
|
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||||
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanAuto() {
|
def fanAuto() {
|
||||||
switchToFanMode("auto")
|
delayBetween([
|
||||||
|
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 0).format(),
|
||||||
|
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||||
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
def fanCirculate() {
|
def fanCirculate() {
|
||||||
switchToFanMode("circulate")
|
delayBetween([
|
||||||
|
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 6).format(),
|
||||||
|
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||||
|
], standardDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTimeAndDay() {
|
private getStandardDelay() {
|
||||||
def timeNow = now()
|
1000
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -171,29 +171,13 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def pct = batteryMap[volts]
|
def pct = batteryMap[volts]
|
||||||
result.value = pct
|
result.value = pct
|
||||||
} else {
|
} else {
|
||||||
def minVolts = 2.4
|
def minVolts = 2.1
|
||||||
def maxVolts = 2.7
|
def maxVolts = 3.0
|
||||||
// Get the current battery percentage as a multiplier 0 - 1
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0
|
def roundedPct = Math.round(pct * 100)
|
||||||
// Find the corresponding voltage from our range
|
if (roundedPct <= 0)
|
||||||
curValVolts = curValVolts * (maxVolts - minVolts) + minVolts
|
roundedPct = 1
|
||||||
// Round to the nearest 10th of a volt
|
result.value = Math.min(100, roundedPct)
|
||||||
curValVolts = Math.round(10 * curValVolts) / 10.0
|
|
||||||
// Only update the battery reading if we don't have a last reading,
|
|
||||||
// OR we have received the same reading twice in a row
|
|
||||||
// OR we don't currently have a battery reading
|
|
||||||
// OR the value we just received is at least 2 steps off from the last reported value
|
|
||||||
if(state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
|
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
|
||||||
def roundedPct = Math.round(pct * 100)
|
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
} else {
|
|
||||||
// Don't update as we want to smooth the battery values
|
|
||||||
result = null
|
|
||||||
}
|
|
||||||
state.lastVolts = volts
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -274,28 +274,12 @@ private Map getBatteryResult(rawValue) {
|
|||||||
result.value = pct
|
result.value = pct
|
||||||
} else {
|
} else {
|
||||||
def minVolts = 2.1
|
def minVolts = 2.1
|
||||||
def maxVolts = 2.7
|
def maxVolts = 3.0
|
||||||
// Get the current battery percentage as a multiplier 0 - 1
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0
|
def roundedPct = Math.round(pct * 100)
|
||||||
// Find the corresponding voltage from our range
|
if (roundedPct <= 0)
|
||||||
curValVolts = curValVolts * (maxVolts - minVolts) + minVolts
|
roundedPct = 1
|
||||||
// Round to the nearest 10th of a volt
|
result.value = Math.min(100, roundedPct)
|
||||||
curValVolts = Math.round(10 * curValVolts) / 10.0
|
|
||||||
// Only update the battery reading if we don't have a last reading,
|
|
||||||
// OR we have received the same reading twice in a row
|
|
||||||
// OR we don't currently have a battery reading
|
|
||||||
// OR the value we just received is at least 2 steps off from the last reported value
|
|
||||||
if(state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
|
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
|
||||||
def roundedPct = Math.round(pct * 100)
|
|
||||||
if (roundedPct <= 0)
|
|
||||||
roundedPct = 1
|
|
||||||
result.value = Math.min(100, roundedPct)
|
|
||||||
} else {
|
|
||||||
// Don't update as we want to smooth the battery values
|
|
||||||
result = null
|
|
||||||
}
|
|
||||||
state.lastVolts = volts
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
.st-ignore
|
|
||||||
README.md
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# Z-wave Basic Smoke Alarm
|
|
||||||
|
|
||||||
Cloud Execution
|
|
||||||
|
|
||||||
Works with:
|
|
||||||
|
|
||||||
* [First Alert Smoke Detector (ZSMOKE)](https://www.smartthings.com/products/first-alert-smoke-detector)
|
|
||||||
|
|
||||||
## Table of contents
|
|
||||||
|
|
||||||
* [Capabilities](#capabilities)
|
|
||||||
* [Health](#device-health)
|
|
||||||
* [Battery](#battery-specification)
|
|
||||||
* [Troubleshooting](#troubleshooting)
|
|
||||||
|
|
||||||
## Capabilities
|
|
||||||
|
|
||||||
* **Smoke Detector** - measure smoke and optionally carbon monoxide levels
|
|
||||||
* **Sensor** - detects sensor events
|
|
||||||
* **Battery** - defines device uses a battery
|
|
||||||
* **Health Check** - indicates ability to get device health notifications
|
|
||||||
|
|
||||||
## Device Health
|
|
||||||
|
|
||||||
First Alert Smoke Detector (ZSMOKE) is a Z-wave sleepy device and checks in every 1 hour.
|
|
||||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*60 + 2)mins = 122 mins.
|
|
||||||
|
|
||||||
* __122min__ checkInterval
|
|
||||||
|
|
||||||
## Battery Specification
|
|
||||||
|
|
||||||
Two AA 1.5V batteries are required.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
|
||||||
Pairing needs to be tried again by placing the device closer to the hub.
|
|
||||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
|
||||||
* [First Alert Smoke Detector (ZSMOKE) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/207150556-First-Alert-Smoke-Detector-ZSMOKE-)
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2015 SmartThings
|
|
||||||
*
|
|
||||||
* 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: "Z-Wave Basic Smoke Alarm", namespace: "smartthings", author: "SmartThings") {
|
|
||||||
capability "Smoke Detector"
|
|
||||||
capability "Sensor"
|
|
||||||
capability "Battery"
|
|
||||||
capability "Health Check"
|
|
||||||
|
|
||||||
fingerprint deviceId: "0xA100", inClusters: "0x20,0x80,0x70,0x85,0x71,0x72,0x86"
|
|
||||||
fingerprint mfr:"0138", prod:"0001", model:"0001", deviceJoinName: "First Alert Smoke Detector"
|
|
||||||
}
|
|
||||||
|
|
||||||
simulator {
|
|
||||||
status "smoke": "command: 7105, payload: 01 FF"
|
|
||||||
status "clear": "command: 7105, payload: 01 00"
|
|
||||||
status "test": "command: 7105, payload: 0C FF"
|
|
||||||
status "battery 100%": "command: 8003, payload: 64"
|
|
||||||
status "battery 5%": "command: 8003, payload: 05"
|
|
||||||
}
|
|
||||||
|
|
||||||
tiles (scale: 2){
|
|
||||||
multiAttributeTile(name:"smoke", type: "lighting", width: 6, height: 4){
|
|
||||||
tileAttribute ("device.smoke", key: "PRIMARY_CONTROL") {
|
|
||||||
attributeState("clear", label:"clear", icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff")
|
|
||||||
attributeState("detected", label:"SMOKE", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
|
|
||||||
attributeState("tested", label:"TEST", icon:"st.alarm.smoke.test", backgroundColor:"#e86d13")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
|
||||||
state "battery", label:'${currentValue}% battery', unit:""
|
|
||||||
}
|
|
||||||
|
|
||||||
main "smoke"
|
|
||||||
details(["smoke", "battery"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def installed() {
|
|
||||||
// Device checks in every hour, this interval allows us to miss one check-in notification before marking offline
|
|
||||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
|
||||||
|
|
||||||
def cmds = []
|
|
||||||
createSmokeEvents("smokeClear", cmds)
|
|
||||||
cmds.each { cmd -> sendEvent(cmd) }
|
|
||||||
}
|
|
||||||
|
|
||||||
def updated() {
|
|
||||||
// Device checks in every hour, this interval allows us to miss one check-in notification before marking offline
|
|
||||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
|
||||||
}
|
|
||||||
|
|
||||||
def parse(String description) {
|
|
||||||
def results = []
|
|
||||||
if (description.startsWith("Err")) {
|
|
||||||
results << createEvent(descriptionText:description, displayed:true)
|
|
||||||
} else {
|
|
||||||
def cmd = zwave.parse(description, [ 0x80: 1, 0x84: 1, 0x71: 2, 0x72: 1 ])
|
|
||||||
if (cmd) {
|
|
||||||
zwaveEvent(cmd, results)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.debug "'$description' parsed to ${results.inspect()}"
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
def createSmokeEvents(name, results) {
|
|
||||||
def text = null
|
|
||||||
switch (name) {
|
|
||||||
case "smoke":
|
|
||||||
text = "$device.displayName smoke was detected!"
|
|
||||||
// these are displayed:false because the composite event is the one we want to see in the app
|
|
||||||
results << createEvent(name: "smoke", value: "detected", descriptionText: text)
|
|
||||||
break
|
|
||||||
case "tested":
|
|
||||||
text = "$device.displayName was tested"
|
|
||||||
results << createEvent(name: "smoke", value: "tested", descriptionText: text)
|
|
||||||
break
|
|
||||||
case "smokeClear":
|
|
||||||
text = "$device.displayName smoke is clear"
|
|
||||||
results << createEvent(name: "smoke", value: "clear", descriptionText: text)
|
|
||||||
name = "clear"
|
|
||||||
break
|
|
||||||
case "testClear":
|
|
||||||
text = "$device.displayName test cleared"
|
|
||||||
results << createEvent(name: "smoke", value: "clear", descriptionText: text)
|
|
||||||
name = "clear"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
|
|
||||||
if (cmd.zwaveAlarmType == physicalgraph.zwave.commands.alarmv2.AlarmReport.ZWAVE_ALARM_TYPE_SMOKE) {
|
|
||||||
if (cmd.zwaveAlarmEvent == 3) {
|
|
||||||
createSmokeEvents("tested", results)
|
|
||||||
} else {
|
|
||||||
createSmokeEvents((cmd.zwaveAlarmEvent == 1 || cmd.zwaveAlarmEvent == 2) ? "smoke" : "smokeClear", results)
|
|
||||||
}
|
|
||||||
} else switch(cmd.alarmType) {
|
|
||||||
case 1:
|
|
||||||
createSmokeEvents(cmd.alarmLevel ? "smoke" : "smokeClear", results)
|
|
||||||
break
|
|
||||||
case 12: // test button pressed
|
|
||||||
createSmokeEvents(cmd.alarmLevel ? "tested" : "testClear", results)
|
|
||||||
break
|
|
||||||
case 13: // sent every hour -- not sure what this means, just a wake up notification?
|
|
||||||
if (cmd.alarmLevel == 255) {
|
|
||||||
results << createEvent(descriptionText: "$device.displayName checked in", isStateChange: false)
|
|
||||||
} else {
|
|
||||||
results << createEvent(descriptionText: "$device.displayName code 13 is $cmd.alarmLevel", isStateChange:true, displayed:false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear smoke in case they pulled batteries and we missed the clear msg
|
|
||||||
if(device.currentValue("smoke") != "clear") {
|
|
||||||
createSmokeEvents("smokeClear", results)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check battery if we don't have a recent battery event
|
|
||||||
if (!state.lastbatt || (now() - state.lastbatt) >= 48*60*60*1000) {
|
|
||||||
results << response(zwave.batteryV1.batteryGet())
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
results << createEvent(displayed: true, descriptionText: "Alarm $cmd.alarmType ${cmd.alarmLevel == 255 ? 'activated' : cmd.alarmLevel ?: 'deactivated'}".toString())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SensorBinary and SensorAlarm aren't tested, but included to preemptively support future smoke alarms
|
|
||||||
//
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd, results) {
|
|
||||||
if (cmd.sensorType == physicalgraph.zwave.commandclasses.SensorBinaryV2.SENSOR_TYPE_SMOKE) {
|
|
||||||
createSmokeEvents(cmd.sensorValue ? "smoke" : "smokeClear", results)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd, results) {
|
|
||||||
if (cmd.sensorType == 1) {
|
|
||||||
createSmokeEvents(cmd.sensorState ? "smoke" : "smokeClear", results)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) {
|
|
||||||
results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
|
|
||||||
if (!state.lastbatt || (now() - state.lastbatt) >= 56*60*60*1000) {
|
|
||||||
results << response(zwave.batteryV1.batteryGet(), "delay 2000", zwave.wakeUpV1.wakeUpNoMoreInformation())
|
|
||||||
} else {
|
|
||||||
results << response(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) {
|
|
||||||
def map = [ name: "battery", unit: "%", isStateChange: true ]
|
|
||||||
state.lastbatt = now()
|
|
||||||
if (cmd.batteryLevel == 0xFF) {
|
|
||||||
map.value = 1
|
|
||||||
map.descriptionText = "$device.displayName battery is low!"
|
|
||||||
} else {
|
|
||||||
map.value = cmd.batteryLevel
|
|
||||||
}
|
|
||||||
results << createEvent(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.Command cmd, results) {
|
|
||||||
def event = [ displayed: false ]
|
|
||||||
event.linkText = device.label ?: device.name
|
|
||||||
event.descriptionText = "$event.linkText: $cmd"
|
|
||||||
results << createEvent(event)
|
|
||||||
}
|
|
||||||
@@ -88,21 +88,19 @@ import physicalgraph.zwave.commands.usercodev1.*
|
|||||||
def installed() {
|
def installed() {
|
||||||
// Device-Watch pings if no device events received for 1 hour (checkInterval)
|
// Device-Watch pings if no device events received for 1 hour (checkInterval)
|
||||||
sendEvent(name: "checkInterval", value: 1 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 1 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|
||||||
try {
|
|
||||||
if (!state.init) {
|
|
||||||
state.init = true
|
|
||||||
// Wait long enough for behind-the-scenes z-wave magic to finish, but be quick enough before hub goes back into inclusion and blocks us
|
|
||||||
response(["delay 2000"] + secureSequence([zwave.doorLockV1.doorLockOperationGet(), zwave.batteryV1.batteryGet()], 2200))
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log.warn "installed() threw $e"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
// Device-Watch pings if no device events received for 1 hour (checkInterval)
|
// Device-Watch pings if no device events received for 1 hour (checkInterval)
|
||||||
sendEvent(name: "checkInterval", value: 1 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 1 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
|
try {
|
||||||
|
if (!state.init) {
|
||||||
|
state.init = true
|
||||||
|
response(secureSequence([zwave.doorLockV1.doorLockOperationGet(), zwave.batteryV1.batteryGet()]))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.warn "updated() threw $e"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ def createEvents(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
|||||||
|
|
||||||
def poll() {
|
def poll() {
|
||||||
if (secondsPast(state.lastbatt, 36*60*60)) {
|
if (secondsPast(state.lastbatt, 36*60*60)) {
|
||||||
return zwave.batteryV1.batteryGet().format()
|
return zwave.batteryV1.batteryGet().format
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ metadata {
|
|||||||
|
|
||||||
attribute "alarmState", "string"
|
attribute "alarmState", "string"
|
||||||
|
|
||||||
|
fingerprint deviceId: "0xA100", inClusters: "0x20,0x80,0x70,0x85,0x71,0x72,0x86"
|
||||||
|
fingerprint mfr:"0138", prod:"0001", model:"0001", deviceJoinName: "First Alert Smoke Detector"
|
||||||
fingerprint mfr:"0138", prod:"0001", model:"0002", deviceJoinName: "First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO)"
|
fingerprint mfr:"0138", prod:"0001", model:"0002", deviceJoinName: "First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO)"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,10 +57,6 @@ metadata {
|
|||||||
def installed() {
|
def installed() {
|
||||||
// Device checks in every hour, this interval allows us to miss one check-in notification before marking offline
|
// Device checks in every hour, this interval allows us to miss one check-in notification before marking offline
|
||||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
|
|
||||||
def cmds = []
|
|
||||||
createSmokeOrCOEvents("allClear", cmds) // allClear to set inital states for smoke and CO
|
|
||||||
cmds.each { cmd -> sendEvent(cmd) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
@@ -107,12 +105,6 @@ def createSmokeOrCOEvents(name, results) {
|
|||||||
results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false)
|
results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false)
|
||||||
name = "clear"
|
name = "clear"
|
||||||
break
|
break
|
||||||
case "allClear":
|
|
||||||
text = "$device.displayName all clear"
|
|
||||||
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
|
|
||||||
results << createEvent(name: "carbonMonoxide", value: "clear", displayed: false)
|
|
||||||
name = "clear"
|
|
||||||
break
|
|
||||||
case "testClear":
|
case "testClear":
|
||||||
text = "$device.displayName test cleared"
|
text = "$device.displayName test cleared"
|
||||||
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
|
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ metadata {
|
|||||||
definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
|
capability "Relative Humidity Measurement"
|
||||||
capability "Thermostat"
|
capability "Thermostat"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Refresh"
|
capability "Polling"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Health Check"
|
capability "Health Check"
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ metadata {
|
|||||||
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
||||||
}
|
}
|
||||||
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
state "default", action:"polling.poll", icon:"st.secondary.refresh"
|
||||||
}
|
}
|
||||||
main "temperature"
|
main "temperature"
|
||||||
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"])
|
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"])
|
||||||
@@ -124,20 +125,13 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def installed(){
|
def installed(){
|
||||||
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()))
|
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||||
initialize()
|
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated(){
|
def updated(){
|
||||||
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])
|
||||||
unschedule()
|
|
||||||
runEvery5Minutes("refresh")
|
|
||||||
refresh()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description)
|
def parse(String description)
|
||||||
@@ -155,7 +149,6 @@ def parse(String description)
|
|||||||
]
|
]
|
||||||
if (map.name == "thermostatMode") {
|
if (map.name == "thermostatMode") {
|
||||||
state.lastTriedMode = map.value
|
state.lastTriedMode = map.value
|
||||||
map.data = [supportedThermostatModes:state.supportedThermostatModes]
|
|
||||||
if (map.value == "cool") {
|
if (map.value == "cool") {
|
||||||
map2.value = device.latestValue("coolingSetpoint")
|
map2.value = device.latestValue("coolingSetpoint")
|
||||||
log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}"
|
log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}"
|
||||||
@@ -179,7 +172,6 @@ def parse(String description)
|
|||||||
}
|
}
|
||||||
} else if (map.name == "thermostatFanMode" && map.isStateChange) {
|
} else if (map.name == "thermostatFanMode" && map.isStateChange) {
|
||||||
state.lastTriedFanMode = map.value
|
state.lastTriedFanMode = map.value
|
||||||
map.data = [supportedThermostatFanModes: state.supportedThermostatFanModes]
|
|
||||||
}
|
}
|
||||||
log.debug "Parse returned $result"
|
log.debug "Parse returned $result"
|
||||||
result
|
result
|
||||||
@@ -313,26 +305,26 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod
|
|||||||
}
|
}
|
||||||
|
|
||||||
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.off) { supportedModes += "off " }
|
||||||
if(cmd.heat) { supportedModes << "heat" }
|
if(cmd.heat) { supportedModes += "heat " }
|
||||||
if(cmd.cool) { supportedModes << "cool" }
|
if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
|
||||||
if(cmd.auto) { supportedModes << "auto" }
|
if(cmd.cool) { supportedModes += "cool " }
|
||||||
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
|
if(cmd.auto) { supportedModes += "auto " }
|
||||||
|
|
||||||
state.supportedThermostatModes = supportedModes
|
state.supportedModes = supportedModes
|
||||||
sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false)
|
// No events to be generated, return empty map
|
||||||
return [:]
|
return [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
|
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
|
||||||
def supportedFanModes = []
|
def supportedFanModes = ""
|
||||||
if(cmd.auto) { supportedFanModes << "auto" } // "fanAuto "
|
if(cmd.auto) { supportedFanModes += "auto " } // "fanAuto "
|
||||||
if(cmd.circulation) { supportedFanModes << "circulate" } // "fanCirculate"
|
if(cmd.low) { supportedFanModes += "on " } // "fanOn"
|
||||||
if(cmd.low) { supportedFanModes << "on" } // "fanOn"
|
if(cmd.circulation) { supportedFanModes += "circulate " } // "fanCirculate"
|
||||||
|
|
||||||
state.supportedThermostatFanModes = supportedFanModes
|
state.supportedFanModes = supportedFanModes
|
||||||
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false)
|
// No events to be generated, return empty map
|
||||||
return [:]
|
return [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,17 +337,15 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Command Implementations
|
// Command Implementations
|
||||||
def refresh() {
|
def poll() {
|
||||||
def cmds = []
|
delayBetween([
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
|
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
|
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
|
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
|
zwave.thermostatModeV2.thermostatModeGet().format(),
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.sensorMultilevelV2.sensorMultilevelGet().format()) // current temperature
|
zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format())
|
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format())
|
], 2300)
|
||||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format())
|
|
||||||
sendHubCommand(cmds)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def quickSetHeat(degrees) {
|
def quickSetHeat(degrees) {
|
||||||
@@ -426,14 +416,28 @@ def ping() {
|
|||||||
poll()
|
poll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def configure() {
|
||||||
|
delayBetween([
|
||||||
|
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
|
||||||
|
zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format(),
|
||||||
|
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(),
|
||||||
|
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
|
||||||
|
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
|
||||||
|
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
|
||||||
|
zwave.thermostatModeV2.thermostatModeGet().format(),
|
||||||
|
zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
|
||||||
|
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()
|
||||||
|
], 2300)
|
||||||
|
}
|
||||||
|
|
||||||
def modes() {
|
def modes() {
|
||||||
return state.supportedThermostatModes
|
["off", "heat", "cool", "auto", "emergency heat"]
|
||||||
}
|
}
|
||||||
|
|
||||||
def switchMode() {
|
def switchMode() {
|
||||||
def currentMode = device.currentState("thermostatMode")?.value
|
def currentMode = device.currentState("thermostatMode")?.value
|
||||||
def lastTriedMode = state.lastTriedMode ?: currentMode ?: ["off"]
|
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
|
||||||
def supportedModes = getDataByName("supportedThermostatModes")
|
def supportedModes = getDataByName("supportedModes")
|
||||||
def modeOrder = modes()
|
def modeOrder = modes()
|
||||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||||
def nextMode = next(lastTriedMode)
|
def nextMode = next(lastTriedMode)
|
||||||
@@ -450,7 +454,7 @@ def switchMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def switchToMode(nextMode) {
|
def switchToMode(nextMode) {
|
||||||
def supportedModes = getDataByName("supportedThermostatModes")
|
def supportedModes = getDataByName("supportedModes")
|
||||||
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||||
if (nextMode in modes()) {
|
if (nextMode in modes()) {
|
||||||
state.lastTriedMode = nextMode
|
state.lastTriedMode = nextMode
|
||||||
@@ -462,9 +466,9 @@ def switchToMode(nextMode) {
|
|||||||
|
|
||||||
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("supportedThermostatFanModes") ?: ["auto", "on"]
|
def supportedModes = getDataByName("supportedFanModes") ?: "auto on" // "fanAuto fanOn"
|
||||||
def modeOrder = state.supportedThermostatFanModes
|
def modeOrder = ["auto", "circulate", "on"] // "fanAuto", "fanCirculate", "fanOn"
|
||||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||||
def nextMode = next(lastTriedMode)
|
def nextMode = next(lastTriedMode)
|
||||||
while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto"
|
while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto"
|
||||||
@@ -474,7 +478,7 @@ def switchFanMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def switchToFanMode(nextMode) {
|
def switchToFanMode(nextMode) {
|
||||||
def supportedFanModes = getDataByName("supportedThermostatFanModes")
|
def supportedFanModes = getDataByName("supportedFanModes")
|
||||||
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||||
|
|
||||||
def returnCommand
|
def returnCommand
|
||||||
|
|||||||
@@ -2,11 +2,26 @@ import javax.crypto.Mac;
|
|||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenT2T SmartApp Test
|
||||||
|
*
|
||||||
|
* Copyright 2016 OpenT2T
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
definition(
|
definition(
|
||||||
name: "OpenT2T SmartApp Test",
|
name: "OpenT2T SmartApp Test",
|
||||||
namespace: "opent2t",
|
namespace: "opent2t",
|
||||||
author: "Microsoft",
|
author: "OpenT2T",
|
||||||
description: "SmartApp for end to end SmartThings scenarios via OpenT2T",
|
description: "Test app to test end to end SmartThings scenarios via OpenT2T",
|
||||||
category: "SmartThings Labs",
|
category: "SmartThings Labs",
|
||||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||||
@@ -40,16 +55,16 @@ definition(
|
|||||||
|
|
||||||
//Device Inputs
|
//Device Inputs
|
||||||
preferences {
|
preferences {
|
||||||
section("Allow Microsoft to control these things...") {
|
section("Allow OpenT2T to control these things...") {
|
||||||
// input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false, hideWhenEmpty: true
|
input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false, hideWhenEmpty: true
|
||||||
// input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false, hideWhenEmpty: true
|
input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false, hideWhenEmpty: true
|
||||||
// input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true
|
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true
|
||||||
// input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true
|
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true
|
||||||
// input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true
|
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true
|
||||||
// input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true
|
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true
|
||||||
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false, hideWhenEmpty: true
|
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false, hideWhenEmpty: true
|
||||||
input "thermostats", "capability.thermostat", title: "Which Thermostat?", multiple: true, required: false, hideWhenEmpty: true
|
input "thermostats", "capability.thermostat", title: "Which Thermostat?", multiple: true, required: false, hideWhenEmpty: true
|
||||||
// input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false, hideWhenEmpty: true
|
input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false, hideWhenEmpty: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,32 +82,36 @@ def getInputs() {
|
|||||||
return inputList
|
return inputList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//API external Endpoints
|
//API external Endpoints
|
||||||
mappings {
|
mappings {
|
||||||
path("/devices") {
|
path("/devices") {
|
||||||
action: [
|
action:
|
||||||
|
[
|
||||||
GET: "getDevices"
|
GET: "getDevices"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
path("/devices/:id") {
|
path("/devices/:id") {
|
||||||
action: [
|
action:
|
||||||
|
[
|
||||||
GET: "getDevice"
|
GET: "getDevice"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
path("/update/:id") {
|
path("/update/:id") {
|
||||||
action: [
|
action:
|
||||||
|
[
|
||||||
PUT: "updateDevice"
|
PUT: "updateDevice"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
path("/deviceSubscription") {
|
path("/deviceSubscription") {
|
||||||
action: [
|
action:
|
||||||
|
[
|
||||||
POST : "registerDeviceChange",
|
POST : "registerDeviceChange",
|
||||||
DELETE: "unregisterDeviceChange"
|
DELETE: "unregisterDeviceChange"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
path("/locationSubscription") {
|
path("/locationSubscription") {
|
||||||
action: [
|
action:
|
||||||
|
[
|
||||||
POST : "registerDeviceGraph",
|
POST : "registerDeviceGraph",
|
||||||
DELETE: "unregisterDeviceGraph"
|
DELETE: "unregisterDeviceGraph"
|
||||||
]
|
]
|
||||||
@@ -177,7 +196,7 @@ def registerDeviceChange() {
|
|||||||
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||||
} else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)) {
|
} else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)) {
|
||||||
// state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
|
// state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
|
||||||
// For now, we will only have one subscription endpoint per device
|
// For now, we will only have one subscription endpoint per device
|
||||||
state.deviceSubscriptionMap.remove(deviceId)
|
state.deviceSubscriptionMap.remove(deviceId)
|
||||||
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
|
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
|
||||||
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||||
@@ -292,16 +311,16 @@ def deviceEventHandler(evt) {
|
|||||||
def evtDeviceType = getDeviceType(evtDevice)
|
def evtDeviceType = getDeviceType(evtDevice)
|
||||||
def deviceData = [];
|
def deviceData = [];
|
||||||
|
|
||||||
|
if (evt.data != null) {
|
||||||
|
def evtData = parseJson(evt.data)
|
||||||
|
log.info "Received event for ${evtDevice.displayName}, data: ${evtData}, description: ${evt.descriptionText}"
|
||||||
|
}
|
||||||
|
|
||||||
if (evtDeviceType == "thermostat") {
|
if (evtDeviceType == "thermostat") {
|
||||||
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationMode: getLocationModeInfo(), locationId: location.id]
|
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationMode: getLocationModeInfo(), locationId: location.id]
|
||||||
} else {
|
} else {
|
||||||
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationId: location.id]
|
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationId: location.id]
|
||||||
}
|
}
|
||||||
|
|
||||||
if(evt.data != null){
|
|
||||||
def evtData = parseJson(evt.data)
|
|
||||||
log.info "Received event for ${evtDevice.displayName}, data: ${evtData}, description: ${evt.descriptionText}"
|
|
||||||
}
|
|
||||||
|
|
||||||
def params = [body: deviceData]
|
def params = [body: deviceData]
|
||||||
|
|
||||||
@@ -311,10 +330,10 @@ def deviceEventHandler(evt) {
|
|||||||
params.uri = "${it}"
|
params.uri = "${it}"
|
||||||
if (state.verificationKeyMap[it] != null) {
|
if (state.verificationKeyMap[it] != null) {
|
||||||
def key = state.verificationKeyMap[it]
|
def key = state.verificationKeyMap[it]
|
||||||
params.headers = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
||||||
}
|
}
|
||||||
log.trace "POST URI: ${params.uri}"
|
log.trace "POST URI: ${params.uri}"
|
||||||
log.trace "Headers: ${params.headers}"
|
log.trace "Header: ${params.header}"
|
||||||
log.trace "Payload: ${params.body}"
|
log.trace "Payload: ${params.body}"
|
||||||
try {
|
try {
|
||||||
httpPostJson(params) { resp ->
|
httpPostJson(params) { resp ->
|
||||||
@@ -344,10 +363,10 @@ def locationEventHandler(evt) {
|
|||||||
params.uri = "${it}"
|
params.uri = "${it}"
|
||||||
if (state.verificationKeyMap[it] != null) {
|
if (state.verificationKeyMap[it] != null) {
|
||||||
def key = state.verificationKeyMap[it]
|
def key = state.verificationKeyMap[it]
|
||||||
params.headers = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
||||||
}
|
}
|
||||||
log.trace "POST URI: ${params.uri}"
|
log.trace "POST URI: ${params.uri}"
|
||||||
log.trace "Headers: ${params.headers}"
|
log.trace "Header: ${params.header}"
|
||||||
log.trace "Payload: ${params.body}"
|
log.trace "Payload: ${params.body}"
|
||||||
try {
|
try {
|
||||||
httpPostJson(params) { resp ->
|
httpPostJson(params) { resp ->
|
||||||
@@ -366,7 +385,6 @@ def locationEventHandler(evt) {
|
|||||||
|
|
||||||
private ComputHMACValue(key, data) {
|
private ComputHMACValue(key, data) {
|
||||||
try {
|
try {
|
||||||
log.debug "data hased: ${data}"
|
|
||||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1")
|
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1")
|
||||||
Mac mac = Mac.getInstance("HmacSHA1")
|
Mac mac = Mac.getInstance("HmacSHA1")
|
||||||
mac.init(secretKeySpec)
|
mac.init(secretKeySpec)
|
||||||
@@ -489,8 +507,7 @@ private getDeviceType(device) {
|
|||||||
|
|
||||||
//Loop through the device capability list to determine the device type.
|
//Loop through the device capability list to determine the device type.
|
||||||
capabilities.each { capability ->
|
capabilities.each { capability ->
|
||||||
switch(capability.name.toLowerCase())
|
switch (capability.name.toLowerCase()) {
|
||||||
{
|
|
||||||
case "switch":
|
case "switch":
|
||||||
deviceType = "switch"
|
deviceType = "switch"
|
||||||
|
|
||||||
@@ -635,8 +652,7 @@ private mapDeviceCommands(command, value) {
|
|||||||
if (value == 1 || value == "1" || value == "lock") {
|
if (value == 1 || value == "1" || value == "lock") {
|
||||||
resultCommand = "lock"
|
resultCommand = "lock"
|
||||||
resultValue = ""
|
resultValue = ""
|
||||||
}
|
} else if (value == 0 || value == "0" || value == "unlock") {
|
||||||
else if (value == 0 || value == "0" || value == "unlock") {
|
|
||||||
resultCommand = "unlock"
|
resultCommand = "unlock"
|
||||||
resultValue = ""
|
resultValue = ""
|
||||||
}
|
}
|
||||||
|
|||||||
470
smartapps/rboy/5-2-day-thermostat.src/5-2-day-thermostat.groovy
Normal file
470
smartapps/rboy/5-2-day-thermostat.src/5-2-day-thermostat.groovy
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
/* **DISCLAIMER**
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
* Without limitation of the foregoing, Contributors/Regents expressly does not warrant that:
|
||||||
|
* 1. the software will meet your requirements or expectations;
|
||||||
|
* 2. the software or the software content will be free of bugs, errors, viruses or other defects;
|
||||||
|
* 3. any results, output, or data provided through or generated by the software will be accurate, up-to-date, complete or reliable;
|
||||||
|
* 4. the software will be compatible with third party software;
|
||||||
|
* 5. any errors in the software will be corrected.
|
||||||
|
* The user assumes all responsibility for selecting the software and for the results obtained from the use of the software. The user shall bear the entire risk as to the quality and the performance of the software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 5-2 Day Thermostat
|
||||||
|
*
|
||||||
|
* Base code from mwoodengr@hotmail.com, bugfixed and enhanced by RBoy
|
||||||
|
* Changes Copyright RBoy, redistribution of any changes or modified code is not allowed without permission
|
||||||
|
* Version 2.1.0
|
||||||
|
* 2015-10-3 - Fixed an issue with selecting multiple thermostats and for recent platform changes
|
||||||
|
* 2015-5-17 - Added ability to select mutiple thermostats simultaneously
|
||||||
|
* 2015-2-11 - Fixed issue with fan mode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
definition(
|
||||||
|
name: "5-2 Day Thermostat",
|
||||||
|
namespace: "rboy",
|
||||||
|
author: "RBoy",
|
||||||
|
description: "Weekday and Weekend Thermostat",
|
||||||
|
category: "Green Living",
|
||||||
|
iconUrl: "https://s3.amazonaws.com/smartapp-icons/GreenLiving/Cat-GreenLiving.png",
|
||||||
|
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/GreenLiving/Cat-GreenLiving@2x.png",
|
||||||
|
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/GreenLiving/Cat-GreenLiving@3x.png")
|
||||||
|
|
||||||
|
preferences {
|
||||||
|
section("Choose thermostat (s)") {
|
||||||
|
input "thermostats", "capability.thermostat", required: true, multiple:true
|
||||||
|
}
|
||||||
|
|
||||||
|
section("Switch HVAC mode (auto to cool/heat) based on the outside temperature (optional)") {
|
||||||
|
input "temperatureSensor", "capability.temperatureMeasurement", required: false
|
||||||
|
input "temperatureH", "number", title: "Switch to heating temperature", required: false, description: "Temperature below which switch to heat mode"
|
||||||
|
input "temperatureC", "number", title: "Switch to cooling temperature", required: false, description: "Temperature above which switch to cool mode"
|
||||||
|
}
|
||||||
|
|
||||||
|
section("Monday to Friday Schedule") {
|
||||||
|
input "time1", "time", title: "Wake Time", required: true
|
||||||
|
input "tempSetpoint1", "number", title: "Wake Heat Temp", required: true
|
||||||
|
input "tempSetpointA", "number", title: "Wake Cool Temp", required: true
|
||||||
|
input "time2", "time", title: "Leave Time", required: true
|
||||||
|
input "tempSetpoint2", "number", title: "Leave Heat Temp", required: true
|
||||||
|
input "tempSetpointB", "number", title: "Leave Cool Temp", required: true
|
||||||
|
input "time3", "time", title: "Return Time", required: true
|
||||||
|
input "tempSetpoint3", "number", title: "Return Heat Temp", required: true
|
||||||
|
input "tempSetpointC", "number", title: "Return Cool Temp", required: true
|
||||||
|
input "time4", "time", title: "Sleep Time", required: true
|
||||||
|
input "tempSetpoint4", "number", title: "Sleep Heat Temp", required: true
|
||||||
|
input "tempSetpointD", "number", title: "Sleep Cool Temp", required: true
|
||||||
|
}
|
||||||
|
section("Saturday and Sunday Schedule") {
|
||||||
|
input "time11", "time", title: "Wake Time", required: true
|
||||||
|
input "tempSetpoint11", "number", title: "Wake Heat Temp", required: true
|
||||||
|
input "tempSetpointAA", "number", title: "Wake Cool Temp", required: true
|
||||||
|
input "time21", "time", title: "Leave Time", required: true
|
||||||
|
input "tempSetpoint21", "number", title: "Leave Heat Temp", required: true
|
||||||
|
input "tempSetpointBB", "number", title: "Leave Cool Temp", required: true
|
||||||
|
input "time31", "time", title: "Return Time", required: true
|
||||||
|
input "tempSetpoint31", "number", title: "Return Heat Temp", required: true
|
||||||
|
input "tempSetpointCC", "number", title: "Return Cool Temp", required: true
|
||||||
|
input "time41", "time", title: "Sleep Time", required: true
|
||||||
|
input "tempSetpoint41", "number", title: "Sleep Heat Temp", required: true
|
||||||
|
input "tempSetpointDD", "number", title: "Sleep Cool Temp", required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def installed()
|
||||||
|
{
|
||||||
|
subscribeToEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updated()
|
||||||
|
{
|
||||||
|
unsubscribe()
|
||||||
|
subscribeToEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
def subscribeToEvents() {
|
||||||
|
subscribe(temperatureSensor, "temperature", temperatureHandler)
|
||||||
|
subscribe(location, modeChangeHandler)
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle mode changes, reinitialize the current temperature and timers after a mode change, this is to workaround the issue of the last timer firing while in a non running mode, resume operations when supported modes are set
|
||||||
|
def modeChangeHandler(evt) {
|
||||||
|
log.debug "Reinitializing thermostats on mode change notification, new mode $evt.value"
|
||||||
|
//sendNotificationEvent("$thermostats Reinitializing on mode change notification, new mode $evt.value")
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This section sets the HVAC mode based outside temperature. HVAC fan mode is set to "auto".
|
||||||
|
def temperatureHandler(evt) {
|
||||||
|
log.debug "Heat mode switch temperature $temperatureH, cool mode switch temperature $temperatureC"
|
||||||
|
|
||||||
|
if (temperatureH == null || temperatureC == null) { // We are in Auto mode or user doesn't want us to switch modes
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def extTemp = temperatureSensor.currentTemperature
|
||||||
|
log.debug "External temperature is: $extTemp"
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "HVAC current mode $thermostatState"
|
||||||
|
log.debug "HVAC Fan current mode $thermostatFan"
|
||||||
|
if (extTemp < temperatureH) {
|
||||||
|
if (thermostatState == "cool") {
|
||||||
|
def hvacmode = "heat"
|
||||||
|
thermostat.setThermostatMode(hvacmode)
|
||||||
|
log.debug "HVAC mode set to $hvacmode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (extTemp > temperatureC) {
|
||||||
|
if (thermostatState == "heat") {
|
||||||
|
def hvacmode = "cool"
|
||||||
|
thermostat.setThermostatMode(hvacmode)
|
||||||
|
log.debug "HVAC mode set to $hvacmode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thermostatFan != "fanAuto") {
|
||||||
|
thermostat.setThermostatFanMode("auto")
|
||||||
|
log.debug "HVAC fan mode set to auto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This section determines which day it is.
|
||||||
|
def initialize() {
|
||||||
|
log.trace "Initialized with $settings"
|
||||||
|
|
||||||
|
unschedule()
|
||||||
|
def calendar = Calendar.getInstance()
|
||||||
|
calendar.setTimeZone(location.timeZone)
|
||||||
|
def today = calendar.get(Calendar.DAY_OF_WEEK)
|
||||||
|
def timeNow = now()
|
||||||
|
def midnightToday = timeToday("2000-01-01T23:59:59.999-0000", location.timeZone)
|
||||||
|
log.debug("Current time is ${(new Date(timeNow)).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
log.debug("Midnight today is ${midnightToday.format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
log.trace("Weekday schedule1 ${timeToday(time1, location.timeZone).format("HH:mm z", location.timeZone)}")
|
||||||
|
log.trace("Weekday schedule2 ${timeToday(time2, location.timeZone).format("HH:mm z", location.timeZone)}")
|
||||||
|
log.trace("Weekday schedule3 ${timeToday(time3, location.timeZone).format("HH:mm z", location.timeZone)}")
|
||||||
|
log.trace("Weekday schedule4 ${timeToday(time4, location.timeZone).format("HH:mm z", location.timeZone)}")
|
||||||
|
log.trace("Weekend schedule1 ${timeToday(time11, location.timeZone).format("HH:mm z", location.timeZone)}")
|
||||||
|
log.trace("Weekend schedule2 ${timeToday(time21, location.timeZone).format("HH:mm z", location.timeZone)}")
|
||||||
|
log.trace("Weekend schedule3 ${timeToday(time31, location.timeZone).format("HH:mm z", location.timeZone)}")
|
||||||
|
log.trace("Weekend schedule4 ${timeToday(time41, location.timeZone).format("HH:mm z", location.timeZone)}")
|
||||||
|
|
||||||
|
// This section is where the time/temperature schedule is set
|
||||||
|
switch (today) {
|
||||||
|
case Calendar.MONDAY:
|
||||||
|
case Calendar.TUESDAY:
|
||||||
|
case Calendar.WEDNESDAY:
|
||||||
|
case Calendar.THURSDAY:
|
||||||
|
if (timeNow >= timeToday(time1, location.timeZone).time && timeNow < timeToday(time2, location.timeZone).time) { // Are we between 1st time and 2nd time
|
||||||
|
changeTemp1()
|
||||||
|
schedule(timeToday(time2, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time2, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time2, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time2, location.timeZone).time && timeNow < timeToday(time3, location.timeZone).time) { // Are we between 2nd time and 3rd time
|
||||||
|
changeTemp2()
|
||||||
|
schedule(timeToday(time3, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time3, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time3, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time3, location.timeZone).time && timeNow < timeToday(time4, location.timeZone).time) { // Are we between 3rd time and 4th time
|
||||||
|
changeTemp3()
|
||||||
|
schedule(timeToday(time4, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time4, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time4, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time4, location.timeZone).time && timeNow < midnightToday.time) { // Are we between 4th time and midnight, schedule next day
|
||||||
|
changeTemp4()
|
||||||
|
schedule(timeToday(time1, location.timeZone) + 1, initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${(timeToday(time1, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${(timeToday(time1, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= (midnightToday - 1).time && timeNow < timeToday(time1, location.timeZone).time) { // Are we between midnight yesterday and 1st time, schedule today
|
||||||
|
changeTemp4()
|
||||||
|
schedule(timeToday(time1, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time1, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time1, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case Calendar.FRIDAY:
|
||||||
|
if (timeNow >= timeToday(time1, location.timeZone).time && timeNow < timeToday(time2, location.timeZone).time) { // Are we between 1st time and 2nd time
|
||||||
|
changeTemp1()
|
||||||
|
schedule(timeToday(time2, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time2, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time2, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time2, location.timeZone).time && timeNow < timeToday(time3, location.timeZone).time) { // Are we between 2nd time and 3rd time
|
||||||
|
changeTemp2()
|
||||||
|
schedule(timeToday(time3, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time3, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time3, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time3, location.timeZone).time && timeNow < timeToday(time4, location.timeZone).time) { // Are we between 3rd time and 4th time
|
||||||
|
changeTemp3()
|
||||||
|
schedule(timeToday(time4, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time4, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time4, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time4, location.timeZone).time && timeNow < midnightToday.time) { // Are we between 4th time Friday and midnight, we schedule Saturday
|
||||||
|
changeTemp4()
|
||||||
|
schedule(timeToday(time11, location.timeZone) + 1, initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${(timeToday(time11, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${(timeToday(time11, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= (midnightToday - 1).time && timeNow < timeToday(time11, location.timeZone).time) { // Are we between midnight Friday and 1st time on Saturday, we schedule Saturday
|
||||||
|
changeTemp4()
|
||||||
|
schedule(timeToday(time11, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time11, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time11, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case Calendar.SATURDAY:
|
||||||
|
if (timeNow >= timeToday(time11, location.timeZone).time && timeNow < timeToday(time21, location.timeZone).time) { // Are we between 1st time and 2nd time
|
||||||
|
changeTemp11()
|
||||||
|
schedule(timeToday(time21, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time21, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time21, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time21, location.timeZone).time && timeNow < timeToday(time31, location.timeZone).time) { // Are we between 2nd time and 3rd time
|
||||||
|
changeTemp21()
|
||||||
|
schedule(timeToday(time31, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time31, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time31, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time31, location.timeZone).time && timeNow < timeToday(time41, location.timeZone).time) { // Are we between 3rd time and 4th time
|
||||||
|
changeTemp31()
|
||||||
|
schedule(timeToday(time41, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time41, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time41, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time41, location.timeZone).time && timeNow < midnightToday.time) { // Are we between 4th time and midnight, schedule the next day
|
||||||
|
changeTemp41()
|
||||||
|
schedule(timeToday(time11, location.timeZone) + 1, initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${(timeToday(time11, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${(timeToday(time11, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= (midnightToday - 1).time && timeNow < timeToday(time11, location.timeZone).time) { // Are we between midnight yesterday and 1st time, schedule today
|
||||||
|
changeTemp41()
|
||||||
|
schedule(timeToday(time11, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time11, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time11, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case Calendar.SUNDAY:
|
||||||
|
if (timeNow >= timeToday(time11, location.timeZone).time && timeNow < timeToday(time21, location.timeZone).time) { // Are we between 1st time and 2nd time
|
||||||
|
changeTemp11()
|
||||||
|
schedule(timeToday(time21, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time21, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time21, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time21, location.timeZone).time && timeNow < timeToday(time31, location.timeZone).time) { // Are we between 2nd time and 3rd time
|
||||||
|
changeTemp21()
|
||||||
|
schedule(timeToday(time31, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time31, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time31, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time31, location.timeZone).time && timeNow < timeToday(time41, location.timeZone).time) { // Are we between 3rd time and 4th time
|
||||||
|
changeTemp31()
|
||||||
|
schedule(timeToday(time41, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time41, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time41, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= timeToday(time41, location.timeZone).time && timeNow < midnightToday.time) { // Are we between 4th time Sunday and midnight, we schedule Monday
|
||||||
|
changeTemp41()
|
||||||
|
schedule(timeToday(time1, location.timeZone) + 1, initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${(timeToday(time1, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${(timeToday(time1, location.timeZone) + 1).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
else if (timeNow >= (midnightToday - 1).time && timeNow < timeToday(time1, location.timeZone).time) { // Are we between midnight Sunday and 1st time on Monday, we schedule Monday
|
||||||
|
changeTemp41()
|
||||||
|
schedule(timeToday(time1, location.timeZone), initialize)
|
||||||
|
log.info("$thermostats Scheduled next adjustment for ${timeToday(time1, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
//sendNotificationEvent("$thermostats Scheduled next adjustment for ${timeToday(time1, location.timeZone).format("EEE MMM dd yyyy HH:mm z", location.timeZone)}")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This section is where the thermostat temperature settings are set.
|
||||||
|
def changeTemp1() {
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
log.debug "Thermostat mode = $thermostatState"
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "Thermostat fan = $thermostatFan"
|
||||||
|
if (thermostatState == "auto") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint1)
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointA)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint1°, Cool $tempSetpointA°"
|
||||||
|
}
|
||||||
|
else if (thermostatState == "heat") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint1)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint1°"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointA)
|
||||||
|
log.info "Set $thermostat Cool $tempSetpointA°"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def changeTemp2() {
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
log.debug "Thermostat mode = $thermostatState"
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "Thermostat fan = $thermostatFan"
|
||||||
|
if (thermostatState == "auto") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint2)
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointB)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint2°, Cool $tempSetpointB°"
|
||||||
|
}
|
||||||
|
else if (thermostatState == "heat") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint2)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def changeTemp3() {
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
log.debug "Thermostat mode = $thermostatState"
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "Thermostat fan = $thermostatFan"
|
||||||
|
if (thermostatState == "auto") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint3)
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointC)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint3°, Cool $tempSetpointC°"
|
||||||
|
}
|
||||||
|
else if (thermostatState == "heat") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint3)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint3°"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointC)
|
||||||
|
log.info "Set $thermostat Cool $tempSetpointC°"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def changeTemp4() {
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
log.debug "Thermostat mode = $thermostatState"
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "Thermostat fan = $thermostatFan"
|
||||||
|
if (thermostatState == "auto") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint4)
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointD)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint4°, Cool $tempSetpointD°"
|
||||||
|
}
|
||||||
|
else if (thermostatState == "heat") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint4)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint4°"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointD)
|
||||||
|
log.info "Set $thermostat Cool $tempSetpointD°"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def changeTemp11() {
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
log.debug "Thermostat mode = $thermostatState"
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "Thermostat fan = $thermostatFan"
|
||||||
|
if (thermostatState == "auto") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint11)
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointAA)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint11°, Cool $tempSetpointAA°"
|
||||||
|
}
|
||||||
|
else if (thermostatState == "heat") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint11)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint11°"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointAA)
|
||||||
|
log.info "Set $thermostat Cool $tempSetpointAA°"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def changeTemp21() {
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
log.debug "Thermostat mode = $thermostatState"
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "Thermostat fan = $thermostatFan"
|
||||||
|
if (thermostatState == "auto") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint21)
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointBB)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint21°, Cool $tempSetpointBB°"
|
||||||
|
}
|
||||||
|
else if (thermostatState == "heat") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint21)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint21°"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointBB)
|
||||||
|
log.info "Set $thermostat Cool $tempSetpointBB°"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def changeTemp31() {
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
log.debug "Thermostat mode = $thermostatState"
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "Thermostat fan = $thermostatFan"
|
||||||
|
if (thermostatState == "auto") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint31)
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointCC)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint31°, Cool $tempSetpointCC°"
|
||||||
|
}
|
||||||
|
else if (thermostatState == "heat") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint31)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint31°"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointCC)
|
||||||
|
log.info "Set $thermostat Cool $tempSetpointCC°"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def changeTemp41() {
|
||||||
|
thermostats.each { thermostat ->
|
||||||
|
def thermostatState = thermostat.currentThermostatMode
|
||||||
|
log.debug "Thermostat mode = $thermostatState"
|
||||||
|
def thermostatFan = thermostat.currentThermostatFanMode
|
||||||
|
log.debug "Thermostat fan = $thermostatFan"
|
||||||
|
if (thermostatState == "auto") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint41)
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointDD)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint41°, Cool $tempSetpointDD°"
|
||||||
|
}
|
||||||
|
else if (thermostatState == "heat") {
|
||||||
|
thermostat.setHeatingSetpoint(tempSetpoint41)
|
||||||
|
log.info "Set $thermostat Heat $tempSetpoint41°"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thermostat.setCoolingSetpoint(tempSetpointDD)
|
||||||
|
log.info "Set $thermostat Cool $tempSetpointDD°"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user