mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 21:03:00 +00:00
Compare commits
79 Commits
PROD_2017.
...
PROD_2017.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6e99745ce | ||
|
|
d2b16ab9c4 | ||
|
|
7297ad2622 | ||
|
|
6b4309fa95 | ||
|
|
3d0fb9cdde | ||
|
|
f0f72b2bce | ||
|
|
19de3e0145 | ||
|
|
a06aff2bbb | ||
|
|
dfad749e3c | ||
|
|
748529b81b | ||
|
|
fc70b5ce55 | ||
|
|
36e63133fc | ||
|
|
838c466312 | ||
|
|
97bfe61baa | ||
|
|
34df40d5b4 | ||
|
|
545be046f0 | ||
|
|
a5041e0fcb | ||
|
|
6996a07969 | ||
|
|
5beae1d9fc | ||
|
|
e5738978b0 | ||
|
|
07064eb8cc | ||
|
|
f519a2d828 | ||
|
|
4da49283bf | ||
|
|
7389edf795 | ||
|
|
23154372d2 | ||
|
|
1b0437c633 | ||
|
|
b3d9578140 | ||
|
|
7549979be5 | ||
|
|
771926c337 | ||
|
|
f647be3a62 | ||
|
|
728b169a08 | ||
|
|
d2f981fd34 | ||
|
|
2536c69083 | ||
|
|
a58dd2094d | ||
|
|
c549a5bed0 | ||
|
|
ef5fffc4bc | ||
|
|
c370e88a6b | ||
|
|
ce8c50c630 | ||
|
|
b069669c11 | ||
|
|
4d61d28b42 | ||
|
|
4547d0543b | ||
|
|
6b1e41198c | ||
|
|
59e6f1251b | ||
|
|
e8939a77d7 | ||
|
|
5aee3a1861 | ||
|
|
17ae692aa0 | ||
|
|
f02428b99f | ||
|
|
34174b730c | ||
|
|
12f874408a | ||
|
|
513e44912c | ||
|
|
f752a01906 | ||
|
|
505ec4ff0f | ||
|
|
fe0f555f10 | ||
|
|
6e1701f955 | ||
|
|
9f5378c2b6 | ||
|
|
39e828b16d | ||
|
|
42353044e6 | ||
|
|
78f06a0b9d | ||
|
|
7f13dd356d | ||
|
|
4042bbbf98 | ||
|
|
20407441d9 | ||
|
|
52925d6d99 | ||
|
|
e6c17131af | ||
|
|
cc70865fbf | ||
|
|
91a77afb2d | ||
|
|
006ffa8a0b | ||
|
|
17465c87c0 | ||
|
|
370b435874 | ||
|
|
be9bdae4cc | ||
|
|
c278e035f6 | ||
|
|
e53d9c910c | ||
|
|
c13014936b | ||
|
|
d6b0f6a8ed | ||
|
|
62c810ba90 | ||
|
|
0b81793b0f | ||
|
|
fb8e4a2416 | ||
|
|
320c8918f8 | ||
|
|
0f3656cd12 | ||
|
|
45663ffb86 |
18
build.gradle
18
build.gradle
@@ -9,7 +9,7 @@ apply plugin: 'smartthings-slack'
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.11"
|
||||
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.12"
|
||||
}
|
||||
repositories {
|
||||
mavenLocal()
|
||||
@@ -19,7 +19,7 @@ buildscript {
|
||||
username smartThingsArtifactoryUserName
|
||||
password smartThingsArtifactoryPassword
|
||||
}
|
||||
url "https://artifactory.smartthings.com/libs-release-local"
|
||||
url "https://smartthings.jfrog.io/smartthings/libs-release-local"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ repositories {
|
||||
username smartThingsArtifactoryUserName
|
||||
password smartThingsArtifactoryPassword
|
||||
}
|
||||
url "https://artifactory.smartthings.com/libs-release-local"
|
||||
url "https://smartthings.jfrog.io/smartthings/libs-release-local"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ sourceSets {
|
||||
|
||||
dependencies {
|
||||
devicetypesCompile 'org.codehaus.groovy:groovy-all:2.4.7'
|
||||
devicetypesCompile 'smartthings:appengine-z-wave:0.1.2'
|
||||
devicetypesCompile 'smartthings:appengine-zigbee:0.1.11'
|
||||
devicetypesCompile 'smartthings:appengine-z-wave:0.1.3'
|
||||
devicetypesCompile 'smartthings:appengine-zigbee:0.1.12'
|
||||
smartappsCompile 'org.codehaus.groovy:groovy-all:2.4.7'
|
||||
smartappsCompile 'smartthings:appengine-common:0.1.8'
|
||||
smartappsCompile 'smartthings:appengine-common:0.1.9'
|
||||
smartappsCompile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.1'
|
||||
smartappsCompile 'org.grails:grails-web:2.3.11'
|
||||
smartappsCompile 'org.json:json:20140107'
|
||||
@@ -74,19 +74,19 @@ slackSendMessage {
|
||||
String username
|
||||
switch (branch) {
|
||||
case 'master':
|
||||
username = 'Hickory'
|
||||
username = 'DEV'
|
||||
iconUrl = wolverine
|
||||
color = '#35D0F2'
|
||||
messageText = 'Began deployment of _SmartThingsPublic[master]_ branch to the _Dev_ environments.'
|
||||
break
|
||||
case 'staging':
|
||||
username = 'Dickory'
|
||||
username = 'STG'
|
||||
iconUrl = beach
|
||||
color = '#FFDE20'
|
||||
messageText = 'Began deployment of _SmartThingsPublic[staging]_ branch to the _Staging_ environments.'
|
||||
break
|
||||
case 'production':
|
||||
username = 'Dock'
|
||||
username = 'PRD'
|
||||
iconUrl = drinks
|
||||
color = '#FF1D23'
|
||||
messageText = 'Began deployment of _SmartThingsPublic[production]_ branch to the _Prod_ environments.'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Spruce Sensor -Pre-release V2 10/8/2015
|
||||
* Spruce Sensor -updated with SLP model number 5/2017
|
||||
*
|
||||
* Copyright 2014 Plaid Systems
|
||||
*
|
||||
@@ -14,25 +14,33 @@
|
||||
*
|
||||
-------10/20/2015 Updates--------
|
||||
-Fix/add battery reporting interval to update
|
||||
-remove polling and/or refresh(?)
|
||||
-remove polling and/or refresh
|
||||
|
||||
-------5/2017 Updates--------
|
||||
-Add fingerprints for SLP
|
||||
-add device health, check every 60mins + 2mins
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "NCauffman") {
|
||||
definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "Plaid Systems") {
|
||||
|
||||
capability "Configuration"
|
||||
capability "Battery"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Temperature Measurement"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
//capability "Polling"
|
||||
|
||||
attribute "maxHum", "string"
|
||||
attribute "minHum", "string"
|
||||
|
||||
|
||||
command "resetHumidity"
|
||||
command "refresh"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01", deviceJoinName: "Spruce Sensor"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP1", deviceJoinName: "Spruce Sensor"
|
||||
}
|
||||
|
||||
preferences {
|
||||
@@ -65,7 +73,7 @@ metadata {
|
||||
[value: 64, color: "#44B621"],
|
||||
[value: 80, color: "#3D79D9"],
|
||||
[value: 96, color: "#0A50C2"]
|
||||
]
|
||||
], icon:"st.Weather.weather12"
|
||||
}
|
||||
|
||||
valueTile("maxHum", "device.maxHum", canChangeIcon: false, canChangeBackground: false) {
|
||||
@@ -293,6 +301,11 @@ def setConfig(){
|
||||
sendEvent(name: 'configuration',value: configInterval, descriptionText: "Configuration initialized")
|
||||
}
|
||||
|
||||
def installed(){
|
||||
//check every 1 hour + 2mins
|
||||
sendEvent(name: "checkInterval", value: 1 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
//when device preferences are changed
|
||||
def updated(){
|
||||
log.debug "device updated"
|
||||
@@ -303,6 +316,8 @@ def updated(){
|
||||
sendEvent(name: 'configuration',value: 0, descriptionText: "Settings changed and will update at next report. Measure interval set to ${interval} mins")
|
||||
}
|
||||
}
|
||||
//check every 1 hour + 2mins
|
||||
sendEvent(name: "checkInterval", value: 1 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
//poll
|
||||
@@ -395,4 +410,4 @@ private byte[] reverseArray(byte[] array) {
|
||||
i++;
|
||||
}
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
2
devicetypes/smartthings/aeon-key-fob.src/.st-ignore
Normal file
2
devicetypes/smartthings/aeon-key-fob.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
34
devicetypes/smartthings/aeon-key-fob.src/README.md
Normal file
34
devicetypes/smartthings/aeon-key-fob.src/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Aeon Labs Key Fob
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Aeon Labs Key Fob](http://aeotec.com/z-wave-key-fob-remote-control)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents device has commands
|
||||
* **Button** - represents a device with one or more buttons
|
||||
* **Holdable Button** - represents a device with one or more holdable buttons
|
||||
* **Configuration** - allows for configuration of devices
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Aeon Key Fob is a ZWave totally sleepy device and is marked offline only in the case when Hub is offline.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the Aeon Labs Key Fob from SmartThings can be found in the following link:
|
||||
* [Aeotec Key Fob Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202294120-Aeon-Labs-Key-Fob)
|
||||
@@ -1,3 +1,4 @@
|
||||
import groovy.json.JsonOutput
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
@@ -19,6 +20,7 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x80,0x84,0x85"
|
||||
fingerprint mfr: "0086", prod: "0001", model: "0026", deviceJoinName: "Aeon Panic Button"
|
||||
@@ -131,6 +133,9 @@ def updated() {
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
// Arrival sensors only goes OFFLINE when Hub is off
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||
|
||||
def zwMap = getZwaveInfo()
|
||||
def buttons = 4 // Default for Key Fob
|
||||
|
||||
|
||||
2
devicetypes/smartthings/aeon-minimote.src/.st-ignore
Normal file
2
devicetypes/smartthings/aeon-minimote.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
33
devicetypes/smartthings/aeon-minimote.src/README.md
Normal file
33
devicetypes/smartthings/aeon-minimote.src/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Aeon Minimote
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Aeotec Minimote](http://aeotec.com/small-z-wave-remote-control)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents device has commands
|
||||
* **Button** - represents a device with one or more buttons
|
||||
* **Holdable Button** - represents a device with one or more holdable buttons
|
||||
* **Configuration** - allows for configuration of devices
|
||||
* **Sensor** - detects sensor events
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Aeon Minimote is a ZWave totally sleepy device and is marked offline only in the case when Hub is offline.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the Aeotec Minimote from SmartThings can be found in the following link:
|
||||
* [Aeotec Minimote Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202087904-Aeotec-Minimote)
|
||||
@@ -1,3 +1,4 @@
|
||||
import groovy.json.JsonOutput
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
@@ -18,6 +19,7 @@ metadata {
|
||||
capability "Holdable Button"
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B", outClusters: "0x26,0x2B"
|
||||
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B,0x85,0x84", outClusters: "0x26" // old style with numbered buttons
|
||||
@@ -119,5 +121,7 @@ def updated() {
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
// Arrival sensors only goes OFFLINE when Hub is off
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||
sendEvent(name: "numberOfButtons", value: 4)
|
||||
}
|
||||
|
||||
@@ -27,13 +27,9 @@ Works with:
|
||||
## Device Health
|
||||
|
||||
Aeon Labs MultiSensor 6 is polled by the hub.
|
||||
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.
|
||||
Aeon MultiSensor reports in once every hour.
|
||||
|
||||
* __32min__ checkInterval
|
||||
* __122min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -130,13 +130,13 @@ metadata {
|
||||
}
|
||||
|
||||
def installed(){
|
||||
// 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])
|
||||
// Device-Watch simply pings if no device events received for 122min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// 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])
|
||||
// Device-Watch simply pings if no device events received for 122min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"
|
||||
|
||||
|
||||
@@ -27,13 +27,9 @@ Works with:
|
||||
## Device Health
|
||||
|
||||
Aeon Labs MultiSensor (Gen 5) is polled by the hub.
|
||||
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.
|
||||
Aeon MultiSensor Gen5 reports in once every hour.
|
||||
|
||||
* __32min__ checkInterval
|
||||
* __122min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -100,12 +100,12 @@ metadata {
|
||||
|
||||
def installed(){
|
||||
// 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 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// 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 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description)
|
||||
|
||||
@@ -28,13 +28,9 @@ Works with:
|
||||
## Device Health
|
||||
|
||||
Aeon Labs MultiSensor is polled by the hub.
|
||||
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.
|
||||
Aeon MultiSensor reports in once every hour.
|
||||
|
||||
* __32min__ checkInterval
|
||||
* __122min__ checkInterval
|
||||
|
||||
## Battery Specification
|
||||
|
||||
|
||||
@@ -96,12 +96,12 @@ metadata {
|
||||
|
||||
def installed(){
|
||||
// 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 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// 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 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
|
||||
@@ -6,7 +6,6 @@ metadata {
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Thermostat"
|
||||
capability "Battery"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
@@ -15,161 +14,173 @@ metadata {
|
||||
|
||||
command "switchMode"
|
||||
command "switchFanMode"
|
||||
command "quickSetCool"
|
||||
command "quickSetHeat"
|
||||
command "lowerHeatingSetpoint"
|
||||
command "raiseHeatingSetpoint"
|
||||
command "lowerCoolSetpoint"
|
||||
command "raiseCoolSetpoint"
|
||||
|
||||
fingerprint deviceId: "0x08", inClusters: "0x43,0x40,0x44,0x31,0x80,0x85,0x60"
|
||||
fingerprint mfr:"0098", prod:"6401", model:"0107", deviceJoinName: "2Gig CT100 Programmable Thermostat"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
status "off" : "command: 4003, payload: 00"
|
||||
status "heat" : "command: 4003, payload: 01"
|
||||
status "cool" : "command: 4003, payload: 02"
|
||||
status "auto" : "command: 4003, payload: 03"
|
||||
status "emergencyHeat" : "command: 4003, payload: 04"
|
||||
|
||||
status "fanAuto" : "command: 4403, payload: 00"
|
||||
status "fanOn" : "command: 4403, payload: 01"
|
||||
status "fanCirculate" : "command: 4403, payload: 06"
|
||||
|
||||
status "heat 60" : "command: 4303, payload: 01 09 3C"
|
||||
status "heat 72" : "command: 4303, payload: 01 09 48"
|
||||
|
||||
status "cool 76" : "command: 4303, payload: 02 09 4C"
|
||||
status "cool 80" : "command: 4303, payload: 02 09 50"
|
||||
|
||||
status "temp 58" : "command: 3105, payload: 01 2A 02 44"
|
||||
status "temp 62" : "command: 3105, payload: 01 2A 02 6C"
|
||||
status "temp 78" : "command: 3105, payload: 01 2A 03 0C"
|
||||
status "temp 86" : "command: 3105, payload: 01 2A 03 34"
|
||||
|
||||
status "idle" : "command: 4203, payload: 00"
|
||||
status "heating" : "command: 4203, payload: 01"
|
||||
status "cooling" : "command: 4203, payload: 02"
|
||||
|
||||
// reply messages
|
||||
reply "2502": "command: 2503, payload: FF"
|
||||
}
|
||||
|
||||
tiles {
|
||||
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"]
|
||||
]
|
||||
)
|
||||
multiAttributeTile(name:"temperature", type:"generic", width:3, height:2, canChangeIcon: true) {
|
||||
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
|
||||
attributeState("temperature", label:'${currentValue}°', icon: "st.alarm.temperature.normal",
|
||||
backgroundColors:[
|
||||
// Celsius
|
||||
[value: 0, color: "#153591"],
|
||||
[value: 7, color: "#1e9cbb"],
|
||||
[value: 15, color: "#90d2a7"],
|
||||
[value: 23, color: "#44b621"],
|
||||
[value: 28, color: "#f1d801"],
|
||||
[value: 35, color: "#d04e00"],
|
||||
[value: 37, color: "#bc2323"],
|
||||
// Fahrenheit
|
||||
[value: 40, color: "#153591"],
|
||||
[value: 44, color: "#1e9cbb"],
|
||||
[value: 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", 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("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", inactiveLabel: false, decoration: "flat") {
|
||||
state "fanAuto", label:'${name}', action:"switchFanMode"
|
||||
state "fanOn", label:'${name}', action:"switchFanMode"
|
||||
state "fanCirculate", label:'${name}', action:"switchFanMode"
|
||||
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"
|
||||
}
|
||||
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "setHeatingSetpoint", action:"quickSetHeat", backgroundColor:"#e86d13"
|
||||
valueTile("humidity", "device.humidity", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
||||
state "humidity", label:'${currentValue}%', icon:"st.Weather.weather12"
|
||||
}
|
||||
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "heat", label:'${currentValue}° heat', backgroundColor:"#ffffff"
|
||||
standardTile("lowerHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||
state "heatingSetpoint", action:"lowerHeatingSetpoint", icon:"st.thermostat.thermostat-left"
|
||||
}
|
||||
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "setCoolingSetpoint", action:"quickSetCool", backgroundColor: "#00a0dc"
|
||||
valueTile("heatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||
state "heatingSetpoint", label:'${currentValue}° heat', backgroundColor:"#ffffff"
|
||||
}
|
||||
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
||||
standardTile("raiseHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||
state "heatingSetpoint", action:"raiseHeatingSetpoint", icon:"st.thermostat.thermostat-right"
|
||||
}
|
||||
valueTile("humidity", "device.humidity", inactiveLabel: false, decoration: "flat") {
|
||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
||||
standardTile("lowerCoolSetpoint", "device.coolingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||
state "coolingSetpoint", action:"lowerCoolSetpoint", icon:"st.thermostat.thermostat-left"
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
valueTile("coolingSetpoint", "device.coolingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||
state "coolingSetpoint", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||
standardTile("raiseCoolSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
|
||||
state "heatingSetpoint", action:"raiseCoolSetpoint", icon:"st.thermostat.thermostat-right"
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.thermostatMode", width:2, height:2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
main "temperature"
|
||||
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh", "humidity", "battery"])
|
||||
details(["temperature", "mode", "fanMode", "humidity", "lowerHeatingSetpoint", "heatingSetpoint", "raiseHeatingSetpoint", "lowerCoolSetpoint", "coolingSetpoint", "raiseCoolSetpoint", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// 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])
|
||||
// If not set update ManufacturerSpecific data
|
||||
if (!getDataValue("manufacturer")) {
|
||||
sendHubCommand(new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()))
|
||||
}
|
||||
initialize()
|
||||
}
|
||||
|
||||
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)
|
||||
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 result = []
|
||||
def result = null
|
||||
if (description == "updated") {
|
||||
} else {
|
||||
def zwcmd = zwave.parse(description, [0x42:2, 0x43:2, 0x31: 2, 0x60: 3])
|
||||
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 {
|
||||
log.debug "$device.displayName couldn't parse $description"
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
return null
|
||||
return []
|
||||
}
|
||||
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
|
||||
return [result]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
|
||||
def result = null
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x42:2, 0x43:2, 0x31: 2])
|
||||
log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}")
|
||||
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiInstanceCmdEncap cmd) {
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 3])
|
||||
log.debug ("multiinstancev1.MultiInstanceCmdEncap: command from instance ${cmd.instance}: ${encapsulatedCommand}")
|
||||
if (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])
|
||||
}
|
||||
}
|
||||
zwaveEvent(encapsulatedCommand)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
|
||||
{
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
def temp = convertTemperatureIfNeeded(cmd.scaledValue, cmdScale, cmd.precision)
|
||||
def sendCmd = []
|
||||
def unit = getTemperatureScale()
|
||||
def map1 = [ value: temp, unit: unit, displayed: false ]
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
def setpoint = getTempInLocalScale(cmd.scaledValue, cmdScale)
|
||||
switch (cmd.setpointType) {
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
log.debug "unknown setpointType $cmd.setpointType"
|
||||
@@ -180,33 +191,55 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpo
|
||||
state.size = cmd.size
|
||||
state.scale = cmd.scale
|
||||
state.precision = cmd.precision
|
||||
|
||||
def mode = device.latestValue("thermostatMode")
|
||||
if (mode && map1.name.startsWith(mode) || (mode == "emergency heat" && map1.name == "heatingSetpoint")) {
|
||||
def map2 = [ name: "thermostatSetpoint", value: temp, unit: unit ]
|
||||
[ createEvent(map1), createEvent(map2) ]
|
||||
} else {
|
||||
createEvent(map1)
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd)
|
||||
{
|
||||
// thermostatSetpoint is not displayed by any tile as it can't be predictable calculated due to
|
||||
// the device's quirkiness but it is defined by the capability so it must be set, set it to the most likely value
|
||||
def updateThermostatSetpoint(setpoint, value) {
|
||||
def scale = getTemperatureScale()
|
||||
def heatingSetpoint = (setpoint == "heatingSetpoint") ? value : getTempInLocalScale("heatingSetpoint")
|
||||
def coolingSetpoint = (setpoint == "coolingSetpoint") ? value : getTempInLocalScale("coolingSetpoint")
|
||||
def mode = device.currentValue("thermostatMode")
|
||||
def thermostatSetpoint = heatingSetpoint // corresponds to (mode == "heat" || mode == "emergency heat")
|
||||
if (mode == "cool") {
|
||||
thermostatSetpoint = coolingSetpoint
|
||||
}
|
||||
// Just set to average of heating + cooling for mode off and auto
|
||||
if (mode == "off" || mode == "auto") {
|
||||
thermostatSetpoint = getTempInLocalScale((heatingSetpoint + coolingSetpoint)/2, scale)
|
||||
}
|
||||
sendEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: scale)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.sensorType == 1) {
|
||||
map.name = "temperature"
|
||||
map.unit = getTemperatureScale()
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C", cmd.precision)
|
||||
map.value = getTempInLocalScale(cmd.scaledSensorValue, (cmd.scale == 1 ? "F" : "C"))
|
||||
} else if (cmd.sensorType == 5) {
|
||||
map.name = "humidity"
|
||||
map.unit = "%"
|
||||
map.value = cmd.scaledSensorValue
|
||||
}
|
||||
createEvent(map)
|
||||
sendEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd)
|
||||
{
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.sensorType == 1) {
|
||||
map.name = "temperature"
|
||||
map.unit = getTemperatureScale()
|
||||
map.value = getTempInLocalScale(cmd.scaledSensorValue, (cmd.scale == 1 ? "F" : "C"))
|
||||
} else if (cmd.sensorType == 5) {
|
||||
map.value = cmd.scaledSensorValue
|
||||
map.unit = "%"
|
||||
map.name = "humidity"
|
||||
}
|
||||
sendEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd) {
|
||||
def map = [name: "thermostatOperatingState" ]
|
||||
switch (cmd.operatingState) {
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport.OPERATING_STATE_IDLE:
|
||||
@@ -231,12 +264,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.Thermosta
|
||||
map.value = "vent economizer"
|
||||
break
|
||||
}
|
||||
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
|
||||
sendEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanStateReport cmd) {
|
||||
@@ -252,203 +280,256 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanSt
|
||||
map.value = "running high"
|
||||
break
|
||||
}
|
||||
createEvent(map)
|
||||
sendEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
|
||||
def map = [name: "thermostatMode"]
|
||||
def thermostatSetpoint = null
|
||||
def map = [name: "thermostatMode", data:[supportedThermostatModes: state.supportedModes]]
|
||||
switch (cmd.mode) {
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF:
|
||||
map.value = "off"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_HEAT:
|
||||
map.value = "heat"
|
||||
thermostatSetpoint = device.latestValue("heatingSetpoint")
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUXILIARY_HEAT:
|
||||
map.value = "emergency heat"
|
||||
thermostatSetpoint = device.latestValue("heatingSetpoint")
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_COOL:
|
||||
map.value = "cool"
|
||||
thermostatSetpoint = device.latestValue("coolingSetpoint")
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_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
|
||||
}
|
||||
state.lastTriedMode = map.value
|
||||
if (thermostatSetpoint) {
|
||||
[ createEvent(map), createEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: getTemperatureScale()) ]
|
||||
} else {
|
||||
createEvent(map)
|
||||
}
|
||||
sendEvent(map)
|
||||
updateThermostatSetpoint(null, null)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) {
|
||||
def map = [name: "thermostatFanMode", displayed: false]
|
||||
def map = [name: "thermostatFanMode", data:[supportedThermostatFanModes: state.supportedFanModes]]
|
||||
switch (cmd.fanMode) {
|
||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
|
||||
map.value = "fanAuto"
|
||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
|
||||
map.value = "auto"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW:
|
||||
map.value = "fanOn"
|
||||
map.value = "on"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
|
||||
map.value = "fanCirculate"
|
||||
map.value = "circulate"
|
||||
break
|
||||
}
|
||||
state.lastTriedFanMode = map.value
|
||||
createEvent(map)
|
||||
sendEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
|
||||
def supportedModes = ""
|
||||
if(cmd.off) { supportedModes += "off " }
|
||||
if(cmd.heat) { supportedModes += "heat " }
|
||||
if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
|
||||
if(cmd.cool) { supportedModes += "cool " }
|
||||
if(cmd.auto) { supportedModes += "auto " }
|
||||
def supportedModes = []
|
||||
if(cmd.heat) { supportedModes << "heat" }
|
||||
if(cmd.cool) { supportedModes << "cool" }
|
||||
// Make sure off is before auto, this ensures the right setpoint is used based on current temperature when auto is set
|
||||
if(cmd.off) { supportedModes << "off" }
|
||||
if(cmd.auto) { supportedModes << "auto" }
|
||||
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
|
||||
|
||||
state.supportedModes = supportedModes
|
||||
[ createEvent(name:"supportedModes", value: supportedModes, displayed: false),
|
||||
response(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet()) ]
|
||||
sendEvent(name: "supportedThermostatModes", value: supportedModes, isStateChange: true, displayed: false)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
|
||||
def supportedFanModes = ""
|
||||
if(cmd.auto) { supportedFanModes += "fanAuto " }
|
||||
if(cmd.low) { supportedFanModes += "fanOn " }
|
||||
if(cmd.circulation) { supportedFanModes += "fanCirculate " }
|
||||
def supportedFanModes = []
|
||||
if(cmd.auto) { supportedFanModes << "auto" }
|
||||
if(cmd.low) { supportedFanModes << "on" }
|
||||
if(cmd.circulation) { supportedFanModes << "circulate" }
|
||||
|
||||
state.supportedFanModes = supportedFanModes
|
||||
[ createEvent(name:"supportedFanModes", value: supportedModes, displayed: false),
|
||||
response(refresh()) ]
|
||||
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, isStateChange: true, displayed: false)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||
log.debug "Zwave event received: $cmd"
|
||||
log.debug "Zwave BasicReport: $cmd"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [ name: "battery", unit: "%" ]
|
||||
if (cmd.batteryLevel == 0xFF) {
|
||||
def batteryState = cmd.batteryLevel
|
||||
def map = [name: "battery", unit: "%", value: cmd.batteryLevel]
|
||||
if ((cmd.batteryLevel == 0xFF) || (cmd.batteryLevel == 0x00)) { // Special value for low battery alert
|
||||
map.value = 1
|
||||
map.descriptionText = "${device.displayName} battery is low"
|
||||
map.isStateChange = true
|
||||
} else {
|
||||
map.value = cmd.batteryLevel
|
||||
batteryState = "low_battery"
|
||||
}
|
||||
state.lastbatt = now()
|
||||
createEvent(map)
|
||||
sendEvent(name: "batteryIcon", value: batteryState, displayed: false)
|
||||
sendEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.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() {
|
||||
// Use encapsulation to differentiate refresh cmds from what the thermostat sends proactively on change
|
||||
def cmd = zwave.sensorMultilevelV2.sensorMultilevelGet()
|
||||
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:1).encapsulate(cmd).format()
|
||||
}
|
||||
|
||||
def nextRefreshQuery(name) {
|
||||
def cmd = null
|
||||
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
|
||||
// Only allow refresh every 2 minutes to prevent flooding the Zwave network
|
||||
def timeNow = now()
|
||||
if (!state.refreshTriggeredAt || (2 * 60 * 1000 < (timeNow - state.refreshTriggeredAt))) {
|
||||
state.refreshTriggeredAt = timeNow
|
||||
// refresh will request battery, prevent multiple request by setting lastbatt now
|
||||
state.lastbatt = timeNow
|
||||
// use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved
|
||||
runIn(2, "poll", [overwrite: true])
|
||||
}
|
||||
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:1).encapsulate(cmd).format()
|
||||
}
|
||||
|
||||
def quickSetHeat(degrees) {
|
||||
setHeatingSetpoint(degrees, 1000)
|
||||
def poll() {
|
||||
def cmds = []
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.multiChannelV3.multiInstanceCmdEncap(instance: 1).encapsulate(zwave.sensorMultilevelV3.sensorMultilevelGet()).format()) // temperature
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()) // HeatingSetpoint
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()) // CoolingSetpoint
|
||||
cmds << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.multiChannelV3.multiInstanceCmdEncap(instance: 2).encapsulate(zwave.sensorMultilevelV3.sensorMultilevelGet()).format()) // humidity
|
||||
def time = getTimeAndDay()
|
||||
log.debug "time: $time"
|
||||
if (time) {
|
||||
cmds << new physicalgraph.device.HubAction(zwave.clockV1.clockSet(time).format())
|
||||
}
|
||||
// Add 3 seconds delay between each command to avoid flooding the Z-Wave network choking the hub
|
||||
sendHubCommand(cmds, 3000)
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(degrees, delay = 30000) {
|
||||
setHeatingSetpoint(degrees.toDouble(), delay)
|
||||
def raiseHeatingSetpoint() {
|
||||
alterSetpoint(null, true, "heatingSetpoint")
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(Double degrees, Integer delay = 30000) {
|
||||
log.trace "setHeatingSetpoint($degrees, $delay)"
|
||||
def deviceScale = state.scale ?: 1
|
||||
def deviceScaleString = deviceScale == 2 ? "C" : "F"
|
||||
def lowerHeatingSetpoint() {
|
||||
alterSetpoint(null, false, "heatingSetpoint")
|
||||
}
|
||||
|
||||
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 p = (state.precision == null) ? 1 : state.precision
|
||||
|
||||
def convertedDegrees
|
||||
if (locationScale == "C" && deviceScaleString == "F") {
|
||||
convertedDegrees = celsiusToFahrenheit(degrees)
|
||||
} else if (locationScale == "F" && deviceScaleString == "C") {
|
||||
convertedDegrees = fahrenheitToCelsius(degrees)
|
||||
def heatingSetpoint = getTempInLocalScale("heatingSetpoint")
|
||||
def coolingSetpoint = getTempInLocalScale("coolingSetpoint")
|
||||
def targetvalue = (setpoint == "heatingSetpoint") ? heatingSetpoint : coolingSetpoint
|
||||
def delta = (locationScale == "F") ? 1 : 0.5
|
||||
if (raise != null) {
|
||||
targetvalue += raise ? delta : - delta
|
||||
} else if (degrees) {
|
||||
targetvalue = degrees
|
||||
} else {
|
||||
convertedDegrees = degrees
|
||||
log.warn "alterSetpoint called with neither up/down/degree information"
|
||||
return
|
||||
}
|
||||
def data = enforceSetpointLimits(setpoint, [targetvalue: targetvalue, heatingSetpoint: heatingSetpoint, coolingSetpoint: coolingSetpoint])
|
||||
// update UI without waiting for the device to respond, this to give user a smoother UI experience
|
||||
// also, as runIn's have to overwrite and user can change heating/cooling setpoint separately separate runIn's have to be used
|
||||
if (data.targetHeatingSetpoint) {
|
||||
sendEvent("name": "heatingSetpoint", "value": data.targetHeatingSetpoint, unit: locationScale, eventType: "ENTITY_UPDATE")//, displayed: false)
|
||||
runIn(4, "updateHeatingSetpoint", [data: data, overwrite: true])
|
||||
}
|
||||
if (data.targetCoolingSetpoint) {
|
||||
sendEvent("name": "coolingSetpoint", "value": data.targetCoolingSetpoint, unit: locationScale, eventType: "ENTITY_UPDATE")//, displayed: false)
|
||||
runIn(4, "updateCoolingSetpoint", [data: data, overwrite: true])
|
||||
}
|
||||
}
|
||||
|
||||
def updateHeatingSetpoint(data) {
|
||||
updateSetpoints(data)
|
||||
}
|
||||
|
||||
def updateCoolingSetpoint(data) {
|
||||
updateSetpoints(data)
|
||||
}
|
||||
|
||||
def enforceSetpointLimits(setpoint, data) {
|
||||
// Enforce max/min for setpoints
|
||||
def maxSetpoint = getTempInLocalScale(95, "F")
|
||||
def minSetpoint = getTempInLocalScale(35, "F")
|
||||
def targetvalue = data.targetvalue
|
||||
def heatingSetpoint = null
|
||||
def coolingSetpoint = null
|
||||
|
||||
if (targetvalue > maxSetpoint) {
|
||||
targetvalue = maxSetpoint
|
||||
} else if (targetvalue < minSetpoint) {
|
||||
targetvalue = minSetpoint
|
||||
}
|
||||
// Enforce limits, for now make sure heating <= cooling, and cooling >= heating
|
||||
if (setpoint == "heatingSetpoint") {
|
||||
heatingSetpoint = targetvalue
|
||||
coolingSetpoint = (heatingSetpoint > data.coolingSetpoint) ? heatingSetpoint : null
|
||||
}
|
||||
if (setpoint == "coolingSetpoint") {
|
||||
coolingSetpoint = targetvalue
|
||||
heatingSetpoint = (coolingSetpoint < data.heatingSetpoint) ? coolingSetpoint : null
|
||||
}
|
||||
return [targetHeatingSetpoint: heatingSetpoint, targetCoolingSetpoint: coolingSetpoint]
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(degrees) {
|
||||
if (degrees) {
|
||||
def data = enforceSetpointLimits("heatingSetpoint",
|
||||
[targetvalue: degrees.toDouble(), heatingSetpoint: getTempInLocalScale("heatingSetpoint"), coolingSetpoint: getTempInLocalScale("coolingSetpoint")])
|
||||
updateSetpoints(data)
|
||||
}
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(degrees) {
|
||||
if (degrees) {
|
||||
def data = enforceSetpointLimits("coolingSetpoint",
|
||||
[targetvalue: degrees.toDouble(), heatingSetpoint: getTempInLocalScale("heatingSetpoint"), coolingSetpoint: getTempInLocalScale("coolingSetpoint")])
|
||||
updateSetpoints(data)
|
||||
}
|
||||
}
|
||||
|
||||
def updateSetpoints(data) {
|
||||
def cmds = []
|
||||
if (data.targetHeatingSetpoint) {
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
|
||||
setpointType: 1, scale: state.scale, precision: state.precision, scaledValue: convertToDeviceScale(data.targetHeatingSetpoint)).format())
|
||||
}
|
||||
if (data.targetCoolingSetpoint) {
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointSet(
|
||||
setpointType: 2, scale: state.scale, precision: state.precision, scaledValue: convertToDeviceScale(data.targetCoolingSetpoint)).format())
|
||||
}
|
||||
|
||||
delayBetween([
|
||||
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format(),
|
||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
|
||||
], delay)
|
||||
// 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 quickSetCool(degrees) {
|
||||
setCoolingSetpoint(degrees, 1000)
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(degrees, delay = 30000) {
|
||||
setCoolingSetpoint(degrees.toDouble(), delay)
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
|
||||
log.trace "setCoolingSetpoint($degrees, $delay)"
|
||||
def deviceScale = state.scale ?: 1
|
||||
def deviceScaleString = deviceScale == 2 ? "C" : "F"
|
||||
def convertToDeviceScale(setpoint) {
|
||||
def locationScale = getTemperatureScale()
|
||||
def p = (state.precision == null) ? 1 : state.precision
|
||||
|
||||
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)
|
||||
def deviceScale = (state.scale == 1) ? "F" : "C"
|
||||
return (deviceScale == locationScale) ? setpoint :
|
||||
(deviceScale == "F" ? celsiusToFahrenheit(setpoint.toBigDecimal()) : roundC(fahrenheitToCelsius(setpoint.toBigDecimal())))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -456,78 +537,56 @@ def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
|
||||
* */
|
||||
def ping() {
|
||||
log.debug "ping() called"
|
||||
refresh()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
|
||||
], 2300)
|
||||
}
|
||||
|
||||
def modes() {
|
||||
["off", "heat", "cool", "auto", "emergency heat"]
|
||||
// Just get Operating State as it is not reported when it chnages and there's no need to flood more commands
|
||||
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()))
|
||||
}
|
||||
|
||||
def switchMode() {
|
||||
def currentMode = device.currentState("thermostatMode")?.value
|
||||
def currentMode = device.currentValue("thermostatMode")
|
||||
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
|
||||
def supportedModes = getDataByName("supportedModes")
|
||||
def modeOrder = modes()
|
||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
if (supportedModes?.contains(currentMode)) {
|
||||
while (!supportedModes.contains(nextMode) && nextMode != "off") {
|
||||
nextMode = next(nextMode)
|
||||
}
|
||||
def supportedModes = state.supportedModes
|
||||
if (supportedModes) {
|
||||
def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
setThermostatMode(nextMode)
|
||||
state.lastTriedMode = nextMode
|
||||
} else {
|
||||
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 supportedModes = getDataByName("supportedModes")
|
||||
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||
if (nextMode in modes()) {
|
||||
def supportedModes = state.supportedModes
|
||||
if (supportedModes && supportedModes.contains(nextMode)) {
|
||||
setThermostatMode(nextMode)
|
||||
state.lastTriedMode = nextMode
|
||||
"$nextMode"()
|
||||
} else {
|
||||
log.debug("no mode method '$nextMode'")
|
||||
log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}")
|
||||
}
|
||||
}
|
||||
|
||||
def switchFanMode() {
|
||||
def currentMode = device.currentState("thermostatFanMode")?.value
|
||||
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off"
|
||||
def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanOn"
|
||||
def modeOrder = ["fanAuto", "fanCirculate", "fanOn"]
|
||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") {
|
||||
nextMode = next(nextMode)
|
||||
def supportedFanModes = state.supportedFanModes
|
||||
if (supportedFanModes) {
|
||||
def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
setThermostatFanMode(nextMode)
|
||||
state.lastTriedFanMode = nextMode
|
||||
} else {
|
||||
log.warn "supportedFanModes not defined"
|
||||
}
|
||||
switchToFanMode(nextMode)
|
||||
}
|
||||
|
||||
def switchToFanMode(nextMode) {
|
||||
def supportedFanModes = getDataByName("supportedFanModes")
|
||||
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||
|
||||
def returnCommand
|
||||
if (nextMode == "fanAuto") {
|
||||
returnCommand = fanAuto()
|
||||
} else if (nextMode == "fanOn") {
|
||||
returnCommand = fanOn()
|
||||
} else if (nextMode == "fanCirculate") {
|
||||
returnCommand = fanCirculate()
|
||||
def supportedFanModes = state.supportedFanModes
|
||||
if (supportedFanModes && supportedFanModes.contains(nextMode)) {
|
||||
setThermostatFanMode(nextMode)
|
||||
state.lastTriedFanMode = nextMode
|
||||
} else {
|
||||
log.debug("no fan mode '$nextMode'")
|
||||
log.debug("FanMode $nextMode is not supported by ${device.displayName}")
|
||||
}
|
||||
if(returnCommand) state.lastTriedFanMode = nextMode
|
||||
returnCommand
|
||||
}
|
||||
|
||||
def getDataByName(String name) {
|
||||
@@ -543,10 +602,10 @@ def getModeMap() { [
|
||||
]}
|
||||
|
||||
def setThermostatMode(String value) {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
], standardDelay)
|
||||
def cmds = []
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
|
||||
sendHubCommand(cmds)
|
||||
}
|
||||
|
||||
def getFanModeMap() { [
|
||||
@@ -556,69 +615,70 @@ def getFanModeMap() { [
|
||||
]}
|
||||
|
||||
def setThermostatFanMode(String value) {
|
||||
delayBetween([
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||
], standardDelay)
|
||||
def cmds = []
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
|
||||
sendHubCommand(cmds)
|
||||
}
|
||||
|
||||
def off() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 0).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
], standardDelay)
|
||||
switchToMode("off")
|
||||
}
|
||||
|
||||
def heat() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 1).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
], standardDelay)
|
||||
switchToMode("heat")
|
||||
}
|
||||
|
||||
def emergencyHeat() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 4).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
], standardDelay)
|
||||
switchToMode("emergency heat")
|
||||
}
|
||||
|
||||
def cool() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 2).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
], standardDelay)
|
||||
switchToMode("cool")
|
||||
}
|
||||
|
||||
def auto() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 3).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
], standardDelay)
|
||||
switchToMode("auto")
|
||||
}
|
||||
|
||||
def fanOn() {
|
||||
delayBetween([
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 1).format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||
], standardDelay)
|
||||
switchToFanMode("on")
|
||||
}
|
||||
|
||||
def fanAuto() {
|
||||
delayBetween([
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 0).format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||
], standardDelay)
|
||||
switchToFanMode("auto")
|
||||
}
|
||||
|
||||
def fanCirculate() {
|
||||
delayBetween([
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 6).format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||
], standardDelay)
|
||||
switchToFanMode("circulate")
|
||||
}
|
||||
|
||||
private getStandardDelay() {
|
||||
1000
|
||||
private getTimeAndDay() {
|
||||
def timeNow = now()
|
||||
// Need to check that location have timeZone as SC may have created the location without setting it
|
||||
// Don't update clock more than once a day
|
||||
if (location.timeZone && (!state.timeClockSet || (24 * 60 * 60 * 1000 < (timeNow - state.timeClockSet)))) {
|
||||
def currentDate = Calendar.getInstance(location.timeZone)
|
||||
state.timeClockSet = timeNow
|
||||
return [hour: currentDate.get(Calendar.HOUR_OF_DAY), minute: currentDate.get(Calendar.MINUTE), weekday: currentDate.get(Calendar.DAY_OF_WEEK)]
|
||||
}
|
||||
}
|
||||
|
||||
// Get stored temperature from currentState in current local scale
|
||||
def getTempInLocalScale(state) {
|
||||
def temp = device.currentState(state)
|
||||
if (temp && temp.value && temp.unit) {
|
||||
return getTempInLocalScale(temp.value.toBigDecimal(), temp.unit)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// get/convert temperature to current local scale
|
||||
def getTempInLocalScale(temp, scale) {
|
||||
def scaledTemp = convertTemperatureIfNeeded(temp.toBigDecimal(), scale).toDouble()
|
||||
return (getTemperatureScale() == "F" ? scaledTemp.round(0).toInteger() : roundC(scaledTemp))
|
||||
}
|
||||
|
||||
def roundC (tempC) {
|
||||
return (Math.round(tempC.toDouble() * 2))/2
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ metadata {
|
||||
state "heat", action:"switchMode", nextState: "updating", icon: "st.thermostat.heat"
|
||||
state "cool", action:"switchMode", nextState: "updating", icon: "st.thermostat.cool"
|
||||
state "auto", action:"switchMode", nextState: "updating", icon: "st.thermostat.auto"
|
||||
state "emergency heat", action:"switchMode", icon: "st.thermostat.emergency-heat" // emergency heat = auxHeatOnly
|
||||
state "auxheatonly", action:"switchMode", icon: "st.thermostat.emergency-heat"
|
||||
state "updating", label:"Working", icon: "st.secondary.secondary"
|
||||
}
|
||||
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
|
||||
@@ -156,47 +156,49 @@ void poll() {
|
||||
def generateEvent(Map results) {
|
||||
log.debug "parsing data $results"
|
||||
if(results) {
|
||||
results.each { name, value ->
|
||||
|
||||
def linkText = getLinkText(device)
|
||||
def isChange = false
|
||||
def isDisplayed = true
|
||||
def supportedThermostatModes = ["off"]
|
||||
def thermostatMode = null
|
||||
|
||||
results.each { name, value ->
|
||||
def event = [name: name, linkText: linkText, descriptionText: getThermostatDescriptionText(name, value, linkText),
|
||||
handlerName: name]
|
||||
|
||||
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) {
|
||||
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger()
|
||||
isChange = isTemperatureStateChange(device, name, value.toString())
|
||||
isDisplayed = isChange
|
||||
event << [value: sendValue, unit: temperatureScale, isStateChange: isChange, displayed: isDisplayed]
|
||||
event << [value: sendValue, unit: temperatureScale]
|
||||
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
|
||||
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger()
|
||||
event << [value: sendValue, unit: temperatureScale, displayed: false]
|
||||
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
event << [value: value.toString(), isStateChange: isChange, displayed: false]
|
||||
if (value == true) {
|
||||
supportedThermostatModes << ((name == "auxHeatMode") ? "auxheatonly" : name - "Mode")
|
||||
}
|
||||
return // as we don't want to send this event here, proceed to next name/value pair
|
||||
} else if (name=="thermostatFanMode"){
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
event << [value: value.toString(), isStateChange: isChange, displayed: false]
|
||||
sendEvent(name: "supportedThermostatFanModes", value: fanModes(), displayed: false)
|
||||
event << [value: value.toString(), data:[supportedThermostatFanModes: fanModes()]]
|
||||
} else if (name=="humidity") {
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
event << [value: value.toString(), isStateChange: isChange, displayed: false, unit: "%"]
|
||||
event << [value: value.toString(), displayed: false, unit: "%"]
|
||||
} else if (name == "deviceAlive") {
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
event['isStateChange'] = isChange
|
||||
event['displayed'] = false
|
||||
} else if (name == "thermostatMode") {
|
||||
def mode = value.toString()
|
||||
mode = (mode == "auxHeatOnly") ? "emergency heat" : mode
|
||||
isChange = isStateChange(device, name, mode)
|
||||
event << [value: mode, isStateChange: isChange, displayed: isDisplayed]
|
||||
thermostatMode = value.toLowerCase()
|
||||
return // as we don't want to send this event here, proceed to next name/value pair
|
||||
} else {
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
isDisplayed = isChange
|
||||
event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed]
|
||||
event << [value: value.toString()]
|
||||
}
|
||||
sendEvent(event)
|
||||
}
|
||||
if (state.supportedThermostatModes != supportedThermostatModes) {
|
||||
state.supportedThermostatModes = supportedThermostatModes
|
||||
sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: false)
|
||||
}
|
||||
if (thermostatMode) {
|
||||
sendEvent(name: "thermostatMode", value: thermostatMode, data:[supportedThermostatModes:state.supportedThermostatModes], linkText: linkText,
|
||||
descriptionText: getThermostatDescriptionText("thermostatMode", thermostatMode, linkText), handlerName: "thermostatMode")
|
||||
}
|
||||
generateSetpointEvent ()
|
||||
generateStatusEvent ()
|
||||
}
|
||||
@@ -322,15 +324,7 @@ void resumeProgram() {
|
||||
}
|
||||
|
||||
def modes() {
|
||||
if (state.modes) {
|
||||
log.debug "Modes = ${state.modes}"
|
||||
return state.modes
|
||||
}
|
||||
else {
|
||||
state.modes = parent.availableModes(this)
|
||||
log.debug "Modes = ${state.modes}"
|
||||
return state.modes
|
||||
}
|
||||
return state.supportedThermostatModes
|
||||
}
|
||||
|
||||
def fanModes() {
|
||||
@@ -413,11 +407,13 @@ def setThermostatFanMode(String mode) {
|
||||
}
|
||||
|
||||
def generateModeEvent(mode) {
|
||||
sendEvent(name: "thermostatMode", value: mode, descriptionText: "$device.displayName is in ${mode} mode", displayed: true)
|
||||
sendEvent(name: "thermostatMode", value: mode, data:[supportedThermostatModes: state.supportedThermostatModes],
|
||||
descriptionText: "$device.displayName is in ${mode} mode")
|
||||
}
|
||||
|
||||
def generateFanModeEvent(fanMode) {
|
||||
sendEvent(name: "thermostatFanMode", value: fanMode, descriptionText: "$device.displayName fan is in ${fanMode} mode", displayed: true)
|
||||
sendEvent(name: "thermostatFanMode", value: fanMode, data:[supportedThermostatFanModes: fanModes()],
|
||||
descriptionText: "$device.displayName fan is in ${fanMode} mode")
|
||||
}
|
||||
|
||||
def generateOperatingStateEvent(operatingState) {
|
||||
@@ -453,14 +449,14 @@ def heat() {
|
||||
}
|
||||
|
||||
def emergencyHeat() {
|
||||
auxHeatOnly()
|
||||
auxheatonly()
|
||||
}
|
||||
|
||||
def auxHeatOnly() {
|
||||
log.debug "auxHeatOnly = emergency heat"
|
||||
def auxheatonly() {
|
||||
log.debug "auxheatonly()"
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
if (parent.setMode ("auxHeatOnly", deviceId))
|
||||
generateModeEvent("emergency heat") // emergency heat = auxHeatOnly
|
||||
generateModeEvent("auxheatonly")
|
||||
else {
|
||||
log.debug "Error setting new mode."
|
||||
def currentMode = device.currentState("thermostatMode")?.value
|
||||
@@ -593,7 +589,7 @@ def generateSetpointEvent() {
|
||||
} else if (mode == "off") {
|
||||
sendEvent("name":"thermostatSetpoint", "value":averageSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":"Off", displayed: false)
|
||||
} else if (mode == "emergency heat") { // emergency heat = auxHeatOnly
|
||||
} else if (mode == "auxheatonly") {
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale, displayed: false)
|
||||
}
|
||||
@@ -632,7 +628,7 @@ void raiseSetpoint() {
|
||||
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0
|
||||
targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5
|
||||
|
||||
if ((mode == "heat" || mode == "emergency heat") && targetvalue > maxHeatingSetpoint) { // emergency heat = auxHeatOnly
|
||||
if ((mode == "heat" || mode == "auxheatonly") && targetvalue > maxHeatingSetpoint) {
|
||||
targetvalue = maxHeatingSetpoint
|
||||
} else if (mode == "cool" && targetvalue > maxCoolingSetpoint) {
|
||||
targetvalue = maxCoolingSetpoint
|
||||
@@ -678,7 +674,7 @@ void lowerSetpoint() {
|
||||
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0
|
||||
targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5
|
||||
|
||||
if ((mode == "heat" || mode == "emergency heat") && targetvalue < minHeatingSetpoint) { // emergency heat = auxHeatOnly
|
||||
if ((mode == "heat" || mode == "auxheatonly") && targetvalue < minHeatingSetpoint) {
|
||||
targetvalue = minHeatingSetpoint
|
||||
} else if (mode == "cool" && targetvalue < minCoolingSetpoint) {
|
||||
targetvalue = minCoolingSetpoint
|
||||
@@ -719,7 +715,7 @@ void alterSetpoint(temp) {
|
||||
}
|
||||
|
||||
//step1: check thermostatMode, enforce limits before sending request to cloud
|
||||
if (mode == "heat" || mode == "emergency heat"){ // emergency heat = auxHeatOnly
|
||||
if (mode == "heat" || mode == "auxheatonly"){
|
||||
if (temp.value > coolingSetpoint){
|
||||
targetHeatingSetpoint = temp.value
|
||||
targetCoolingSetpoint = temp.value
|
||||
@@ -754,7 +750,7 @@ void alterSetpoint(temp) {
|
||||
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
|
||||
} else {
|
||||
log.error "Error alterSetpoint()"
|
||||
if (mode == "heat" || mode == "emergency heat"){ // emergency heat = auxHeatOnly
|
||||
if (mode == "heat" || mode == "auxheatonly"){
|
||||
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
||||
sendEvent("name": "displayThermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
||||
} else if (mode == "cool") {
|
||||
@@ -783,7 +779,7 @@ def generateStatusEvent() {
|
||||
log.debug "Cooling set point = ${coolingSetpoint}"
|
||||
log.debug "HVAC Mode = ${mode}"
|
||||
|
||||
if (mode == "heat") {
|
||||
if (mode == "heat" || mode == "auxheatonly") {
|
||||
if (temperature >= heatingSetpoint) {
|
||||
statusText = "Right Now: Idle"
|
||||
} else {
|
||||
@@ -806,8 +802,6 @@ def generateStatusEvent() {
|
||||
}
|
||||
} else if (mode == "off") {
|
||||
statusText = "Right Now: Off"
|
||||
} else if (mode == "emergency heat") { // emergency heat = auxHeatOnly
|
||||
statusText = "Emergency Heat"
|
||||
} else {
|
||||
statusText = "?"
|
||||
}
|
||||
|
||||
@@ -300,15 +300,21 @@ def setColor(value) {
|
||||
value.hex = "#${hex(value.red)}${hex(value.green)}${hex(value.blue)}"
|
||||
}
|
||||
|
||||
sendEvent(name: "hue", value: value.hue, displayed: false)
|
||||
sendEvent(name: "saturation", value: value.saturation, displayed: false)
|
||||
sendEvent(name: "color", value: value.hex, displayed: false)
|
||||
if (value.level) {
|
||||
sendEvent(name: "level", value: value.level)
|
||||
}
|
||||
if (value.switch) {
|
||||
sendEvent(name: "switch", value: value.switch)
|
||||
}
|
||||
if(value.hue) {
|
||||
sendEvent(name: "hue", value: value.hue, displayed: false)
|
||||
}
|
||||
if(value.saturation) {
|
||||
sendEvent(name: "saturation", value: value.saturation, displayed: false)
|
||||
}
|
||||
if(value.hex?.trim()) {
|
||||
sendEvent(name: "color", value: value.hex, displayed: false)
|
||||
}
|
||||
if (value.level) {
|
||||
sendEvent(name: "level", value: value.level)
|
||||
}
|
||||
if (value.switch?.trim()) {
|
||||
sendEvent(name: "switch", value: value.switch)
|
||||
}
|
||||
|
||||
sendRGB(value.rh, value.gh, value.bh)
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ metadata {
|
||||
|
||||
// tile definitions
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
|
||||
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
@@ -48,14 +48,16 @@ metadata {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main "valve"
|
||||
details(["valve","refresh"])
|
||||
main "contact"
|
||||
details(["contact","refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
def installed(){
|
||||
// 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])
|
||||
|
||||
response(refresh())
|
||||
}
|
||||
|
||||
def updated(){
|
||||
@@ -85,11 +87,17 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
}
|
||||
|
||||
def open() {
|
||||
zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00).format()
|
||||
delayBetween([
|
||||
zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00).format(),
|
||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||
], 500)
|
||||
}
|
||||
|
||||
def close() {
|
||||
zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF).format()
|
||||
delayBetween([
|
||||
zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF).format(),
|
||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||
], 500)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,6 +113,6 @@ def refresh() {
|
||||
|
||||
def createEventWithDebug(eventMap) {
|
||||
def event = createEvent(eventMap)
|
||||
log.debug "Event created with ${event?.descriptionText}"
|
||||
log.debug "Event created with ${event?.name}:${event?.value} - ${event?.descriptionText}"
|
||||
return event
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
capability "Outlet"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet"
|
||||
|
||||
@@ -105,6 +105,8 @@ def parse(String description) {
|
||||
} else {
|
||||
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
|
||||
}
|
||||
} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) {
|
||||
map = translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value)))
|
||||
}
|
||||
}
|
||||
} else if (map.name == "temperature") {
|
||||
@@ -129,6 +131,10 @@ def parse(String description) {
|
||||
private Map parseIasMessage(String description) {
|
||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||
|
||||
translateZoneStatus(zs)
|
||||
}
|
||||
|
||||
private Map translateZoneStatus(ZoneStatus zs) {
|
||||
return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry')
|
||||
}
|
||||
|
||||
@@ -197,7 +203,8 @@ def ping() {
|
||||
def refresh() {
|
||||
log.debug "Refreshing Temperature and Battery"
|
||||
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
||||
zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
|
||||
|
||||
return refreshCmds + zigbee.enrollResponse()
|
||||
}
|
||||
|
||||
@@ -111,6 +111,8 @@ def parse(String description) {
|
||||
def value = descMap.value.endsWith("01") ? "active" : "inactive"
|
||||
log.debug "Doing a read attr motion event"
|
||||
map = getMotionResult(value)
|
||||
} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) {
|
||||
map = translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value)))
|
||||
}
|
||||
}
|
||||
} else if (map.name == "temperature") {
|
||||
@@ -135,6 +137,10 @@ def parse(String description) {
|
||||
private Map parseIasMessage(String description) {
|
||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||
|
||||
translateZoneStatus(zs)
|
||||
}
|
||||
|
||||
private Map translateZoneStatus(ZoneStatus zs) {
|
||||
// Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion
|
||||
return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive')
|
||||
}
|
||||
@@ -165,8 +171,8 @@ private Map getBatteryResult(rawValue) {
|
||||
def pct = batteryMap[volts]
|
||||
result.value = pct
|
||||
} else {
|
||||
def minVolts = 2.1
|
||||
def maxVolts = 3.0
|
||||
def minVolts = 2.4
|
||||
def maxVolts = 2.7
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
@@ -200,7 +206,8 @@ def refresh() {
|
||||
log.debug "refresh called"
|
||||
|
||||
def refreshCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
||||
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000)
|
||||
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||
zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS)
|
||||
|
||||
return refreshCmds + zigbee.enrollResponse()
|
||||
}
|
||||
|
||||
@@ -134,8 +134,9 @@ def parse(String description) {
|
||||
} else {
|
||||
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
|
||||
}
|
||||
} else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) {
|
||||
maps += translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value)))
|
||||
} else {
|
||||
|
||||
maps += handleAcceleration(descMap)
|
||||
}
|
||||
}
|
||||
@@ -229,6 +230,11 @@ private List<Map> parseAxis(List<Map> attrData) {
|
||||
|
||||
private List<Map> parseIasMessage(String description) {
|
||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||
|
||||
translateZoneStatus(zs)
|
||||
}
|
||||
|
||||
private List<Map> translateZoneStatus(ZoneStatus zs) {
|
||||
List<Map> results = []
|
||||
|
||||
if (garageSensor != "Yes") {
|
||||
@@ -268,7 +274,7 @@ private Map getBatteryResult(rawValue) {
|
||||
result.value = pct
|
||||
} else {
|
||||
def minVolts = 2.1
|
||||
def maxVolts = 3.0
|
||||
def maxVolts = 2.7
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
@@ -313,7 +319,7 @@ def refresh() {
|
||||
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
||||
zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode]) +
|
||||
zigbee.enrollResponse()
|
||||
zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + zigbee.enrollResponse()
|
||||
|
||||
return refreshCmds
|
||||
}
|
||||
|
||||
@@ -49,6 +49,6 @@ def arrived() {
|
||||
|
||||
|
||||
def departed() {
|
||||
log.trace "Executing 'arrived'"
|
||||
log.trace "Executing 'departed'"
|
||||
sendEvent(name: "presence", value: "not present")
|
||||
}
|
||||
|
||||
2
devicetypes/smartthings/zigbee-button.src/.st-ignore
Normal file
2
devicetypes/smartthings/zigbee-button.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
42
devicetypes/smartthings/zigbee-button.src/README.md
Normal file
42
devicetypes/smartthings/zigbee-button.src/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# ZigBee Button
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [OSRAM LIGHTIFY Dimming Switch](https://support.smartthings.com/hc/en-us/articles/115000236823-SYLVANIA-Dimming-Switch)
|
||||
* [Iris Smart Button](https://support.smartthings.com/hc/en-us/articles/115000190186-Iris-Smart-Button)
|
||||
* [Iris KeyFob](https://support.smartthings.com/hc/en-us/articles/217409686-Iris-Smart-Fob)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - It represents that a device has commands.
|
||||
* **Battery** - It defines that the device has a battery
|
||||
* **Button** - It defines that a device has one or more buttons
|
||||
* **Holdable Button** - It defines that a device has one or more holdable buttons
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - it represents that a Device has attributes.
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
|
||||
## Device Health
|
||||
|
||||
ZigBee Button is marked offline only in the case when Hub is offline.
|
||||
|
||||
|
||||
## 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.
|
||||
It may also happen that you need to reset the device.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [OSRAM LIGHTIFY Dimming Switch Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/115000236823-SYLVANIA-Dimming-Switch)
|
||||
* [Iris Smart Button Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/115000190186-Iris-Smart-Button)
|
||||
* [Iris KeyFob Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/217409686-Iris-Smart-Fob)
|
||||
@@ -13,6 +13,8 @@
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import physicalgraph.zigbee.zcl.DataType
|
||||
|
||||
metadata {
|
||||
@@ -24,6 +26,7 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
command "enrollResponse"
|
||||
|
||||
@@ -249,6 +252,8 @@ def updated() {
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
// Arrival sensors only goes OFFLINE when Hub is off
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||
if ((device.getDataValue("manufacturer") == "OSRAM") && (device.getDataValue("model") == "LIGHTIFY Dimming Switch")) {
|
||||
sendEvent(name: "numberOfButtons", value: 2)
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ metadata {
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
|
||||
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
@@ -58,8 +58,8 @@ metadata {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main(["valve"])
|
||||
details(["valve", "battery", "refresh"])
|
||||
main(["contact"])
|
||||
details(["contact", "battery", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,39 @@
|
||||
# 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-)
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
@@ -21,8 +21,6 @@ metadata {
|
||||
|
||||
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)"
|
||||
}
|
||||
|
||||
@@ -57,6 +55,10 @@ metadata {
|
||||
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 = []
|
||||
createSmokeOrCOEvents("allClear", cmds) // allClear to set inital states for smoke and CO
|
||||
cmds.each { cmd -> sendEvent(cmd) }
|
||||
}
|
||||
|
||||
def updated() {
|
||||
@@ -105,6 +107,12 @@ def createSmokeOrCOEvents(name, results) {
|
||||
results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false)
|
||||
name = "clear"
|
||||
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":
|
||||
text = "$device.displayName test cleared"
|
||||
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
|
||||
|
||||
@@ -15,10 +15,9 @@ metadata {
|
||||
definition (name: "Z-Wave Thermostat", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Temperature Measurement"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Thermostat"
|
||||
capability "Configuration"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
@@ -117,7 +116,7 @@ metadata {
|
||||
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", action:"polling.poll", icon:"st.secondary.refresh"
|
||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
main "temperature"
|
||||
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"])
|
||||
@@ -125,13 +124,20 @@ metadata {
|
||||
}
|
||||
|
||||
def installed(){
|
||||
// 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])
|
||||
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()))
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated(){
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
// 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])
|
||||
unschedule()
|
||||
runEvery5Minutes("refresh")
|
||||
refresh()
|
||||
}
|
||||
|
||||
def parse(String description)
|
||||
@@ -149,6 +155,7 @@ def parse(String description)
|
||||
]
|
||||
if (map.name == "thermostatMode") {
|
||||
state.lastTriedMode = map.value
|
||||
map.data = [supportedThermostatModes:state.supportedThermostatModes]
|
||||
if (map.value == "cool") {
|
||||
map2.value = device.latestValue("coolingSetpoint")
|
||||
log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}"
|
||||
@@ -172,6 +179,7 @@ def parse(String description)
|
||||
}
|
||||
} else if (map.name == "thermostatFanMode" && map.isStateChange) {
|
||||
state.lastTriedFanMode = map.value
|
||||
map.data = [supportedThermostatFanModes: state.supportedThermostatFanModes]
|
||||
}
|
||||
log.debug "Parse returned $result"
|
||||
result
|
||||
@@ -305,26 +313,26 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
|
||||
def supportedModes = ""
|
||||
if(cmd.off) { supportedModes += "off " }
|
||||
if(cmd.heat) { supportedModes += "heat " }
|
||||
if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergency heat " }
|
||||
if(cmd.cool) { supportedModes += "cool " }
|
||||
if(cmd.auto) { supportedModes += "auto " }
|
||||
def supportedModes = []
|
||||
if(cmd.off) { supportedModes << "off" }
|
||||
if(cmd.heat) { supportedModes << "heat" }
|
||||
if(cmd.cool) { supportedModes << "cool" }
|
||||
if(cmd.auto) { supportedModes << "auto" }
|
||||
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
|
||||
|
||||
state.supportedModes = supportedModes
|
||||
// No events to be generated, return empty map
|
||||
state.supportedThermostatModes = supportedModes
|
||||
sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false)
|
||||
return [:]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
|
||||
def supportedFanModes = ""
|
||||
if(cmd.auto) { supportedFanModes += "auto " } // "fanAuto "
|
||||
if(cmd.low) { supportedFanModes += "on " } // "fanOn"
|
||||
if(cmd.circulation) { supportedFanModes += "circulate " } // "fanCirculate"
|
||||
def supportedFanModes = []
|
||||
if(cmd.auto) { supportedFanModes << "auto" } // "fanAuto "
|
||||
if(cmd.circulation) { supportedFanModes << "circulate" } // "fanCirculate"
|
||||
if(cmd.low) { supportedFanModes << "on" } // "fanOn"
|
||||
|
||||
state.supportedFanModes = supportedFanModes
|
||||
// No events to be generated, return empty map
|
||||
state.supportedThermostatFanModes = supportedFanModes
|
||||
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false)
|
||||
return [:]
|
||||
}
|
||||
|
||||
@@ -337,15 +345,17 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
}
|
||||
|
||||
// Command Implementations
|
||||
def poll() {
|
||||
delayBetween([
|
||||
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 refresh() {
|
||||
def cmds = []
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
|
||||
cmds << new physicalgraph.device.HubAction(zwave.sensorMultilevelV2.sensorMultilevelGet().format()) // current temperature
|
||||
cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format())
|
||||
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 quickSetHeat(degrees) {
|
||||
@@ -416,28 +426,14 @@ def ping() {
|
||||
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() {
|
||||
["off", "heat", "cool", "auto", "emergency heat"]
|
||||
return state.supportedThermostatModes
|
||||
}
|
||||
|
||||
def switchMode() {
|
||||
def currentMode = device.currentState("thermostatMode")?.value
|
||||
def lastTriedMode = state.lastTriedMode ?: currentMode ?: "off"
|
||||
def supportedModes = getDataByName("supportedModes")
|
||||
def lastTriedMode = state.lastTriedMode ?: currentMode ?: ["off"]
|
||||
def supportedModes = getDataByName("supportedThermostatModes")
|
||||
def modeOrder = modes()
|
||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
@@ -454,7 +450,7 @@ def switchMode() {
|
||||
}
|
||||
|
||||
def switchToMode(nextMode) {
|
||||
def supportedModes = getDataByName("supportedModes")
|
||||
def supportedModes = getDataByName("supportedThermostatModes")
|
||||
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||
if (nextMode in modes()) {
|
||||
state.lastTriedMode = nextMode
|
||||
@@ -466,9 +462,9 @@ def switchToMode(nextMode) {
|
||||
|
||||
def switchFanMode() {
|
||||
def currentMode = device.currentState("thermostatFanMode")?.value
|
||||
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off"
|
||||
def supportedModes = getDataByName("supportedFanModes") ?: "auto on" // "fanAuto fanOn"
|
||||
def modeOrder = ["auto", "circulate", "on"] // "fanAuto", "fanCirculate", "fanOn"
|
||||
def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: ["off"]
|
||||
def supportedModes = getDataByName("supportedThermostatFanModes") ?: ["auto", "on"]
|
||||
def modeOrder = state.supportedThermostatFanModes
|
||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto"
|
||||
@@ -478,7 +474,7 @@ def switchFanMode() {
|
||||
}
|
||||
|
||||
def switchToFanMode(nextMode) {
|
||||
def supportedFanModes = getDataByName("supportedFanModes")
|
||||
def supportedFanModes = getDataByName("supportedThermostatFanModes")
|
||||
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||
|
||||
def returnCommand
|
||||
|
||||
@@ -58,6 +58,7 @@ metadata {
|
||||
def installed() {
|
||||
// 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])
|
||||
response(refresh())
|
||||
}
|
||||
|
||||
def updated() {
|
||||
|
||||
@@ -2,26 +2,11 @@ import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
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(
|
||||
name: "OpenT2T SmartApp Test",
|
||||
namespace: "opent2t",
|
||||
author: "OpenT2T",
|
||||
description: "Test app to test end to end SmartThings scenarios via OpenT2T",
|
||||
author: "Microsoft",
|
||||
description: "SmartApp for end to end SmartThings scenarios via OpenT2T",
|
||||
category: "SmartThings Labs",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||
@@ -55,16 +40,16 @@ definition(
|
||||
|
||||
//Device Inputs
|
||||
preferences {
|
||||
section("Allow OpenT2T to control these things...") {
|
||||
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 "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 "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
|
||||
section("Allow Microsoft to control these things...") {
|
||||
// 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 "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 "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 "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 "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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,36 +67,32 @@ def getInputs() {
|
||||
return inputList
|
||||
}
|
||||
|
||||
|
||||
//API external Endpoints
|
||||
mappings {
|
||||
path("/devices") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
GET: "getDevices"
|
||||
]
|
||||
}
|
||||
path("/devices/:id") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
GET: "getDevice"
|
||||
]
|
||||
}
|
||||
path("/update/:id") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
PUT: "updateDevice"
|
||||
]
|
||||
}
|
||||
path("/deviceSubscription") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
POST : "registerDeviceChange",
|
||||
DELETE: "unregisterDeviceChange"
|
||||
]
|
||||
}
|
||||
path("/locationSubscription") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
POST : "registerDeviceGraph",
|
||||
DELETE: "unregisterDeviceGraph"
|
||||
]
|
||||
@@ -195,7 +176,10 @@ def registerDeviceChange() {
|
||||
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
|
||||
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||
} 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
|
||||
state.deviceSubscriptionMap.remove(deviceId)
|
||||
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
|
||||
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||
}
|
||||
|
||||
@@ -308,16 +292,16 @@ def deviceEventHandler(evt) {
|
||||
def evtDeviceType = getDeviceType(evtDevice)
|
||||
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") {
|
||||
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 {
|
||||
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]
|
||||
|
||||
@@ -327,10 +311,10 @@ def deviceEventHandler(evt) {
|
||||
params.uri = "${it}"
|
||||
if (state.verificationKeyMap[it] != null) {
|
||||
def key = state.verificationKeyMap[it]
|
||||
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
||||
params.headers = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
||||
}
|
||||
log.trace "POST URI: ${params.uri}"
|
||||
log.trace "Header: ${params.header}"
|
||||
log.trace "Headers: ${params.headers}"
|
||||
log.trace "Payload: ${params.body}"
|
||||
try {
|
||||
httpPostJson(params) { resp ->
|
||||
@@ -360,10 +344,10 @@ def locationEventHandler(evt) {
|
||||
params.uri = "${it}"
|
||||
if (state.verificationKeyMap[it] != null) {
|
||||
def key = state.verificationKeyMap[it]
|
||||
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
||||
params.headers = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
||||
}
|
||||
log.trace "POST URI: ${params.uri}"
|
||||
log.trace "Header: ${params.header}"
|
||||
log.trace "Headers: ${params.headers}"
|
||||
log.trace "Payload: ${params.body}"
|
||||
try {
|
||||
httpPostJson(params) { resp ->
|
||||
@@ -382,6 +366,7 @@ def locationEventHandler(evt) {
|
||||
|
||||
private ComputHMACValue(key, data) {
|
||||
try {
|
||||
log.debug "data hased: ${data}"
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1")
|
||||
Mac mac = Mac.getInstance("HmacSHA1")
|
||||
mac.init(secretKeySpec)
|
||||
@@ -504,7 +489,8 @@ private getDeviceType(device) {
|
||||
|
||||
//Loop through the device capability list to determine the device type.
|
||||
capabilities.each { capability ->
|
||||
switch (capability.name.toLowerCase()) {
|
||||
switch(capability.name.toLowerCase())
|
||||
{
|
||||
case "switch":
|
||||
deviceType = "switch"
|
||||
|
||||
@@ -649,7 +635,8 @@ private mapDeviceCommands(command, value) {
|
||||
if (value == 1 || value == "1" || value == "lock") {
|
||||
resultCommand = "lock"
|
||||
resultValue = ""
|
||||
} else if (value == 0 || value == "0" || value == "unlock") {
|
||||
}
|
||||
else if (value == 0 || value == "0" || value == "unlock") {
|
||||
resultCommand = "unlock"
|
||||
resultValue = ""
|
||||
}
|
||||
|
||||
@@ -27,10 +27,9 @@ definition(
|
||||
|
||||
preferences {
|
||||
page(name: "selectButton")
|
||||
page(name: "configureButton1")
|
||||
page(name: "configureButton2")
|
||||
page(name: "configureButton3")
|
||||
page(name: "configureButton4")
|
||||
for (def i=1; i<=8; i++) {
|
||||
page(name: "configureButton$i")
|
||||
}
|
||||
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
@@ -60,22 +59,45 @@ def selectButton() {
|
||||
}
|
||||
}
|
||||
|
||||
def createPage(pageNum) {
|
||||
if ((state.numButton == pageNum) || (pageNum == 8))
|
||||
state.installCondition = true
|
||||
dynamicPage(name: "configureButton$pageNum", title: "Set up button $pageNum here",
|
||||
nextPage: "configureButton${pageNum+1}", install: state.installCondition, uninstall: configured(), getButtonSections(pageNum))
|
||||
}
|
||||
|
||||
def configureButton1() {
|
||||
dynamicPage(name: "configureButton1", title: "Now let's decide how to use the first button",
|
||||
nextPage: "configureButton2", uninstall: configured(), getButtonSections(1))
|
||||
state.numButton = buttonDevice.currentState("numberOfButtons")?.longValue ?: 4
|
||||
log.debug "state variable numButton: ${state.numButton}"
|
||||
state.installCondition = false
|
||||
createPage(1)
|
||||
}
|
||||
def configureButton2() {
|
||||
dynamicPage(name: "configureButton2", title: "If you have a second button, set it up here",
|
||||
nextPage: "configureButton3", uninstall: configured(), getButtonSections(2))
|
||||
createPage(2)
|
||||
}
|
||||
|
||||
def configureButton3() {
|
||||
dynamicPage(name: "configureButton3", title: "If you have a third button, you can do even more here",
|
||||
nextPage: "configureButton4", uninstall: configured(), getButtonSections(3))
|
||||
createPage(3)
|
||||
}
|
||||
|
||||
def configureButton4() {
|
||||
dynamicPage(name: "configureButton4", title: "If you have a fourth button, you rule, and can set it up here",
|
||||
install: true, uninstall: true, getButtonSections(4))
|
||||
createPage(4)
|
||||
}
|
||||
|
||||
def configureButton5() {
|
||||
createPage(5)
|
||||
}
|
||||
|
||||
def configureButton6() {
|
||||
createPage(6)
|
||||
}
|
||||
|
||||
def configureButton7() {
|
||||
createPage(7)
|
||||
}
|
||||
|
||||
def configureButton8() {
|
||||
createPage(8)
|
||||
}
|
||||
|
||||
def getButtonSections(buttonNumber) {
|
||||
|
||||
@@ -202,7 +202,8 @@ def inputSelectionPage() {
|
||||
|
||||
section("options variations") {
|
||||
paragraph "tap these elements and look at the differences when selecting an option"
|
||||
input(type: "enum", name: "selectionSimple", title: "Simple options", description: "no separators in the selectable options", groupedOptions: addGroup(englishOptions + spanishOptions))
|
||||
input(type: "enum", name: "selectionSimple", title: "Simple options", description: "no separators in the selectable options", options: ["Thing 1", "Thing 2", "(Complicated) Thing 3"])
|
||||
input(type: "enum", name: "selectionSimpleGrouped", title: "Simple (Grouped) options", description: "no separators in the selectable options", groupedOptions: addGroup(englishOptions + spanishOptions))
|
||||
input(type: "enum", name: "selectionGrouped", title: "Grouped options", description: "separate groups of options with headers", groupedOptions: groupedOptions)
|
||||
}
|
||||
|
||||
@@ -214,15 +215,15 @@ def inputSelectionPage() {
|
||||
|
||||
section("segmented") {
|
||||
paragraph "segmented should only work if there are either 2 or 3 options to choose from"
|
||||
input(type: "enum", name: "selectionSegmented1", style: "segmented", title: "1 option", groupedOptions: addGroup(["One"]))
|
||||
input(type: "enum", name: "selectionSegmented4", style: "segmented", title: "4 options", groupedOptions: addGroup(["One", "Two", "Three", "Four"]))
|
||||
input(type: "enum", name: "selectionSegmented1", style: "segmented", title: "1 option", options: ["One"])
|
||||
input(type: "enum", name: "selectionSegmented4", style: "segmented", title: "4 options", options: ["One", "Two", "Three", "Four"])
|
||||
|
||||
paragraph "multiple and required will have no effect on segmented selection elements. There will always be exactly 1 option selected"
|
||||
input(type: "enum", name: "selectionSegmented2", style: "segmented", title: "2 options", options: ["One", "Two"])
|
||||
input(type: "enum", name: "selectionSegmented3", style: "segmented", title: "3 options", options: ["One", "Two", "Three"])
|
||||
|
||||
paragraph "specifying defaultValue still works with segmented selection elements"
|
||||
input(type: "enum", name: "selectionSegmentedWithDefault", title: "defaulted to 'two'", groupedOptions: addGroup(["One", "Two", "Three"]), defaultValue: "Two")
|
||||
input(type: "enum", name: "selectionSegmentedWithDefault", style: "segmented", title: "defaulted to 'two'", options: ["One", "Two", "Three"], defaultValue: "Two")
|
||||
}
|
||||
|
||||
section("required: true") {
|
||||
@@ -231,6 +232,8 @@ def inputSelectionPage() {
|
||||
|
||||
section("multiple: true") {
|
||||
input(type: "enum", name: "selectionMultiple", title: "This allows multiple selections", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true)
|
||||
input(type: "enum", name: "selectionMultipleDefault1", title: "This allows multiple selections with a single default", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true, defaultValue: "an option")
|
||||
input(type: "enum", name: "selectionMultipleDefault2", title: "This allows multiple selections with multiple defaults", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true, defaultValue: ["an option", "another option"])
|
||||
}
|
||||
|
||||
section("with image") {
|
||||
|
||||
@@ -72,7 +72,7 @@ def authPage() {
|
||||
log.debug "have LIFX access token"
|
||||
|
||||
def options = locationOptions() ?: []
|
||||
def count = options.size()
|
||||
def count = options.size().toString()
|
||||
|
||||
return dynamicPage(name:"Credentials", title:"", nextPage:"", install:true, uninstall: true) {
|
||||
section("Select your location") {
|
||||
|
||||
Reference in New Issue
Block a user