Compare commits

..

24 Commits

Author SHA1 Message Date
Peter Dutcher
45dcf2101a MSA-1545: Stat 2016-10-22 22:00:52 -05:00
Kevin Shuk
a9aee8fd96 Merge pull request #1367 from surfous/DVCSMP-2134-2135-ZLL-bulb-hchk2
Add health check for:
* DVCSMP-2134 ZLL white color temperature bulb
* DVCSMP-2135 ZLL dimmer bulb
2016-10-19 22:48:28 -07:00
Kevin Shuk
5c015cf678 DVCSMP-2135 health check ZLL dimmable bulb
* checkInterval set to 12 min (2 misses + leeway)
* configure a healthPoll every 5 min
 * refreshes on/off state, switch level, and white color temperature
 * schedule and set interval from either configure() or updated()
  * but only once per execution
* ping refreshes level setting
2016-10-19 22:40:23 -07:00
Kevin Shuk
cf1a46e309 DVCSMP-2134 health check ZLL white color temp bulb
* checkInterval set to 12 min (2 misses + leeway)
* configure a healthPoll every 5 min
 * refreshes on/off state, switch level, and white color temperature
 * schedule and set interval from either configure() or updated()
  * but only once per execution
* ping refreshes level setting
2016-10-19 22:38:57 -07:00
Jack Chi
1ff77dc608 Merge pull request #1355 from skt123/zwave_switch
[CHF-425] Health Check Implementation for Z-wave switch
2016-10-18 22:53:40 -07:00
Jack Chi
afbec02217 Merge pull request #1281 from parijatdas/leviton
[CHF-404] [CHF-422] Health check for Leviton DZPA1 Plug-in Appliance Module and GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave)
2016-10-18 22:51:44 -07:00
Jack Chi
434a72bd13 Merge pull request #1292 from pchomal/zwave_dimmer
CHF-406
2016-10-18 22:50:14 -07:00
sushant.k1
3fba7c9422 Added Health Check Implementation for following Z-wave switches:
1. Plug-In Smart Switch (GE 12719)
2. In-Wall Smart Outlet (GE 12721)
3. In-Wall Smart Switch (GE 12722)
4. In-Wall Smart Toggle Switch (GE 12727)

Added Health Check Implementation for following Z-wave switches:
1. Plug-In Smart Switch (GE 12719)
2. In-Wall Smart Outlet (GE 12721)
3. In-Wall Smart Switch (GE 12722)
4. In-Wall Smart Toggle Switch (GE 12727)
2016-10-19 10:18:38 +05:30
piyush.c
b63d4a9156 CHF-406
Updated HealthCheck Implementation for Z-wave Dimmer (GE 12718, GE 12724, GE 12729) with checkInterval of 32min
2016-10-19 10:06:38 +05:30
Parijat Das
6eb29ad019 Added health check for Leviton DZPA1 Plug-in Appliance Module and GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave) along with the README.md 2016-10-19 08:45:53 +05:30
Vinay Rao
711cdc3ebf Merge pull request #1370 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-10-18 18:48:17 -07:00
Vinay Rao
f58a1ef589 Merge pull request #1369 from workingmonk/bug/osram_name
DVCSMP-1447 adding missing deviceJoinName in the generic dth
2016-10-18 18:47:35 -07:00
Vinay Rao
db5237ca33 adding missing deviceJoinName in the generic dth 2016-10-18 18:45:27 -07:00
Vinay Rao
7791c68a8a Merge pull request #1364 from SmartThingsCommunity/master
Rolling up master to staging
2016-10-18 14:40:53 -07:00
Vinay Rao
db4140ffd6 Merge pull request #1363 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-10-18 14:39:44 -07:00
Vinay Rao
ac422076c8 Merge pull request #1361 from SmartThingsCommunity/production
Rolling down production hotfix to staging
2016-10-18 14:02:37 -07:00
Lars Finander
c11c146690 Merge pull request #1349 from larsfinander/DVCSMP-2131_Philips_Hue_Handle_invalid_username_staging
DVCSMP-2131 Philips Hue: Handle case if bridge username becomes invalid
2016-10-18 10:39:02 -06:00
Lars Finander
9a5d506668 DVCSMP-2131 Philips Hue: Handle case if bridge username becomes invalid 2016-10-14 16:18:51 -06:00
Vinay Rao
9dac541473 Merge pull request #1338 from jackchi/health-checkInterval-value-fix
[CHF-417] Fix for onOffConfig older periodic values
2016-10-11 16:22:12 -07:00
jackchi
a6cc506803 [CHF-417] Fix for onOffConfig older periodic values 2016-10-11 16:18:06 -07:00
Vinay Rao
aba8a7ad4b Merge pull request #1337 from SmartThingsCommunity/master
Rolling up master to staging
2016-10-11 14:18:00 -07:00
Vinay Rao
b4c912ab80 Merge pull request #1336 from SmartThingsCommunity/staging
Rolling up staging to production for deploy
2016-10-11 13:23:51 -07:00
Vinay Rao
9f5eb7b85a Merge pull request #1316 from SmartThingsCommunity/staging
Rolling up staging to prod for deployment
2016-10-04 14:41:53 -07:00
Vinay Rao
aae7f23a22 Merge pull request #1302 from SmartThingsCommunity/staging
Rolling up staging for production deployment
2016-09-27 14:14:57 -07:00
19 changed files with 1067 additions and 24 deletions

View File

@@ -0,0 +1,2 @@
.st-ignore
README.md

View File

@@ -0,0 +1,45 @@
# Z-wave Dimmer Switch
Works with:
* [GE Z-Wave In-Wall Smart Dimmer (GE 12724)](http://products.z-wavealliance.org/products/1197)
* [GE Z-Wave In-Wall Smart Dimmer (Toggle) (GE 12729)](http://products.z-wavealliance.org/products/1201)
* [GE Z-Wave Plug-in Smart Dimmer (GE 12718)](http://products.z-wavealliance.org/products/1191)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Troubleshooting](#Troubleshooting)
## Capabilities
* **Switch Level** - it's defined to accept two parameters, the level and the rate of dimming
* **Actuator** - represents that a Device has commands
* **Indicator** - gives you the ability to set the indicator LED light on a Z-Wave switch
* **Switch** - can detect state (possible values: on/off)
* **Polling** - represents that poll() can be implemented for the device
* **Refresh** - _refresh()_ command for status updates
* **Sensor** - detects sensor events
* **Health Check** - indicates ability to get device health notifications
## Device Health
Z-Wave Smart Dimmers (In-Wall, In-Wall(Toggle), Plug-In) are 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.
Check-in interval = 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.
## 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:
* [General Z-Wave Dimmer/Switch Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200955890-Troubleshooting-GE-in-wall-switch-or-dimmer-won-t-respond-to-commands-or-automations-Z-Wave-)
* [GE Z-Wave In-Wall Smart Dimmer (GE 12724) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200902600-GE-In-Wall-Paddle-Dimmer-Switch-GE-12724-Z-Wave-)
* [GE Z-Wave In-Wall Smart Dimmer (Toggle) (GE 12729) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/207568463-GE-In-Wall-Smart-Toggle-Dimmer-GE-12729-Z-Wave-)
* [GE Z-Wave Plug-in Smart Dimmer (GE 12718) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202088474-GE-Plug-In-Smart-Dimmer-GE-12718-Z-Wave-)

View File

@@ -20,6 +20,7 @@ metadata {
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Health Check"
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "Z-Wave Wall Dimmer"
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "Z-Wave Wall Dimmer"
@@ -82,6 +83,8 @@ metadata {
}
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])
switch (ledIndicator) {
case "on":
indicatorWhenOn()
@@ -215,6 +218,13 @@ def poll() {
zwave.switchMultilevelV1.switchMultilevelGet().format()
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
refresh()
}
def refresh() {
log.debug "refresh() is called"
def commands = []

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,9 @@
* for the specific language governing permissions and limitations under the License.
*
*/
import groovy.transform.Field
@Field Boolean hasConfiguredHealthCheck = false
metadata {
definition (name: "ZLL Dimmer Bulb", namespace: "smartthings", author: "SmartThings") {
@@ -21,6 +24,7 @@ metadata {
capability "Refresh"
capability "Switch"
capability "Switch Level"
capability "Health Check"
//fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0000,0019"
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0019"
@@ -96,7 +100,38 @@ def poll() {
refresh()
}
def configure() {
log.debug "Configuring Reporting and Bindings."
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.levelRefresh()
}
def healthPoll() {
log.debug "healthPoll()"
def cmds = refresh()
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
}
def configureHealthCheck() {
Integer hcIntervalMinutes = 12
if (!hasConfiguredHealthCheck) {
log.debug "Configuring Health Check, Reporting"
unschedule("healthPoll")
runEvery5Minutes("healthPoll")
// Device-Watch allows 2 check-in misses from device
sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
hasConfiguredHealthCheck = true
}
}
def configure() {
log.debug "configure()"
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
configureHealthCheck()
}
def updated() {
log.debug "updated()"
configureHealthCheck()
}

View File

@@ -11,6 +11,9 @@
* for the specific language governing permissions and limitations under the License.
*
*/
import groovy.transform.Field
@Field Boolean hasConfiguredHealthCheck = false
metadata {
definition (name: "ZLL White Color Temperature Bulb", namespace: "smartthings", author: "SmartThings") {
@@ -22,6 +25,7 @@ metadata {
capability "Refresh"
capability "Switch"
capability "Switch Level"
capability "Health Check"
attribute "colorName", "string"
command "setGenericName"
@@ -96,9 +100,41 @@ def poll() {
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.levelRefresh()
}
def healthPoll() {
log.debug "healthPoll()"
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
}
def configureHealthCheck() {
Integer hcIntervalMinutes = 12
if (!hasConfiguredHealthCheck) {
log.debug "Configuring Health Check, Reporting"
unschedule("healthPoll")
runEvery5Minutes("healthPoll")
// Device-Watch allows 2 check-in misses from device
sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
hasConfiguredHealthCheck = true
}
}
def configure() {
log.debug "Configuring Reporting and Bindings."
log.debug "configure()"
configureHealthCheck()
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
}
def updated() {
log.debug "updated()"
configureHealthCheck()
}
def setColorTemperature(value) {

View File

@@ -0,0 +1,2 @@
.st-ignore
README.md

View File

@@ -0,0 +1,39 @@
# Z-wave Switch
Works with:
* [Leviton Appliance Module (DZPA1-1LW)](https://support.smartthings.com/hc/en-us/articles/205881176-Leviton-Appliance-Module-DZPA1-1LW-)
* [GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave)](https://support.smartthings.com/hc/en-us/articles/200903080-GE-Plug-In-Outdoor-Smart-Switch-GE-12720-Z-Wave-)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
## Capabilities
* **Actuator** - represents that a Device has commands
* **Health Check** - indicates ability to get device health notifications
* **Switch** - can detect state (possible values: on/off)
* **Polling** - represents that poll() can be implemented for the device
* **Refresh** - _refresh()_ command for status updates
* **Sensor** - detects sensor events
## Device Health
A Category C5 Leviton Appliance Module (DZPA1-1LW) and GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave) 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.
## 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:
* [Leviton Appliance Module (DZPA1-1LW) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205881176-Leviton-Appliance-Module-DZPA1-1LW-)
* [GE Plug-In Outdoor Smart Switch (GE 12720) (Z-Wave) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200903080-GE-Plug-In-Outdoor-Smart-Switch-GE-12720-Z-Wave-)

View File

@@ -14,12 +14,15 @@
metadata {
definition (name: "Z-Wave Switch Generic", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Health Check"
capability "Switch"
capability "Polling"
capability "Refresh"
capability "Sensor"
fingerprint inClusters: "0x25", deviceJoinName: "Z-Wave Switch"
fingerprint mfr:"001D", prod:"1A02", deviceJoinName: "Z-Wave Switch"
fingerprint mfr:"0063", prod:"4F50", deviceJoinName: "Z-Wave Switch"
}
// simulator metadata
@@ -50,6 +53,11 @@ metadata {
}
}
def updated(){
// Device-Watch simply pings if no device events received for checkInterval duration of 32min = 2 * 15min + 2min lag time
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
def parse(String description) {
def result = null
def cmd = zwave.parse(description, [0x20: 1, 0x70: 1])
@@ -126,6 +134,13 @@ def poll() {
])
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
refresh()
}
def refresh() {
delayBetween([
zwave.switchBinaryV1.switchBinaryGet().format(),

View File

@@ -0,0 +1,2 @@
.st-ignore
README.md

View File

@@ -0,0 +1,49 @@
# Z-Wave Switch
Works with:
* [GE Z-Wave Plug-In Smart Switch (12719)](http://products.z-wavealliance.org/products/1193)
* [GE Z-Wave In-Wall Smart Outlet (12721)](http://products.z-wavealliance.org/products/1195)
* [GE Z-Wave In-Wall Smart Switch (12722)](http://products.z-wavealliance.org/products/1196)
* [GE Z-Wave In-Wall Smart Toggle Switch (12727)](http://products.z-wavealliance.org/products/1200)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Troubleshooting](#Troubleshooting)
## Capabilities
* **Actuator** - represents that a Device has commands
* **Indicator** - gives you the ability to set the indicator LED light on a Z-Wave switch
* **Switch** - can detect state (possible values: on/off)
* **Polling** - represents that poll() can be implemented for the device
* **Refresh** - _refresh()_ command for status updates
* **Sensor** - detects sensor events
* **Health Check** - indicates ability to get device health notifications
## Device Health
Z-Wave Switches (Plug-In, In-Wall(Toggle Switch, Switch, Outlet)) are 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.
## 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:
* [General Z-Wave Dimmer/Switch Troubleshooting](https://support.smartthings.com/hc/en-us/articles/200955890-Troubleshooting-GE-in-wall-switch-or-dimmer-won-t-respond-to-commands-or-automations-Z-Wave-)
* [GE Z-Wave Plug-In Smart Switch (12719) Troubleshooting](https://support.smartthings.com/hc/en-us/articles/200903070-GE-Plug-In-Smart-Switch-GE-12719-Z-Wave)
* [GE Z-Wave In-Wall Smart Outlet (12721) Troubleshooting](https://support.smartthings.com/hc/en-us/articles/200903020-GE-In-Wall-Smart-Outlet-GE-12721-Z-Wave)
* [GE Z-Wave In-Wall Smart Switch (12722) Troubleshooting](https://support.smartthings.com/hc/en-us/articles/200902540-GE-In-Wall-Smart-Switch-GE-12722-Z-Wave)
* [GE Z-Wave In-Wall Smart Toggle Switch (12727) Troubleshooting](https://support.smartthings.com/hc/en-us/articles/207568933-GE-In-Wall-Smart-Toggle-Switch-GE-12727-Z-Wave)

View File

@@ -19,6 +19,7 @@ metadata {
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Health Check"
fingerprint mfr:"0063", prod:"4952", deviceJoinName: "Z-Wave Wall Switch"
fingerprint mfr:"0063", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
@@ -64,6 +65,8 @@ metadata {
}
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])
switch (ledIndicator) {
case "on":
indicatorWhenOn()
@@ -156,6 +159,13 @@ def poll() {
])
}
/**
* PING is used by Device-Watch in attempt to reach the Device
**/
def ping() {
refresh()
}
def refresh() {
delayBetween([
zwave.switchBinaryV1.switchBinaryGet().format(),

View File

@@ -0,0 +1,779 @@
/**
* Filtrete 3M-50 WiFi Thermostat.
*
* For more information, please visit:
* <https://github.com/statusbits/smartthings/tree/master/RadioThermostat/>
*
* --------------------------------------------------------------------------
*
* Copyright (c) 2014 Statusbits.com
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --------------------------------------------------------------------------
*
* Version 1.0.3 (07/20/2015)
*/
import groovy.json.JsonSlurper
preferences {
input("confIpAddr", "string", title:"Thermostat IP Address",
required:true, displayDuringSetup: true)
input("confTcpPort", "number", title:"Thermostat TCP Port",
required:true, displayDuringSetup:true)
}
metadata {
definition (name:"Radio Thermostat", namespace:"statusbits", author:"geko@statusbits.com") {
capability "Thermostat"
capability "Temperature Measurement"
capability "Sensor"
capability "Refresh"
capability "Polling"
// Custom attributes
attribute "fanState", "string" // Fan operating state. Values: "on", "off"
attribute "hold", "string" // Target temperature Hold status. Values: "on", "off"
// Custom commands
command "heatLevelUp"
command "heatLevelDown"
command "coolLevelUp"
command "coolLevelDown"
command "holdOn"
command "holdOff"
}
tiles {
valueTile("temperature", "device.temperature") {
state "temperature", label:'${currentValue}°', unit:"F",
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel:false) {
state "default", label:'${currentValue}°', unit:"F",
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel:false) {
state "default", label:'${currentValue}°', unit:"F",
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
standardTile("heatLevelUp", "device.heatingSetpoint", inactiveLabel:false, decoration:"flat") {
state "default", label:'Heating', icon:"st.custom.buttons.add-icon", action:"heatLevelUp"
}
standardTile("heatLevelDown", "device.heatingSetpoint", inactiveLabel:false, decoration:"flat") {
state "default", label:'Heating', icon:"st.custom.buttons.subtract-icon", action:"heatLevelDown"
}
standardTile("coolLevelUp", "device.coolingSetpoint", inactiveLabel:false, decoration:"flat") {
state "default", label:'Cooling', icon:"st.custom.buttons.add-icon", action:"coolLevelUp"
}
standardTile("coolLevelDown", "device.coolingSetpoint", inactiveLabel:false, decoration:"flat") {
state "default", label:'Cooling', icon:"st.custom.buttons.subtract-icon", action:"coolLevelDown"
}
standardTile("operatingState", "device.thermostatOperatingState", inactiveLabel:false, decoration:"flat") {
state "default", label:'[State]'
state "idle", label:'', icon:"st.thermostat.heating-cooling-off"
state "heating", label:'', icon:"st.thermostat.heating"
state "cooling", label:'', icon:"st.thermostat.cooling"
}
standardTile("fanState", "device.fanState", inactiveLabel:false, decoration:"flat") {
state "default", label:'[Fan State]'
state "on", label:'', icon:"st.thermostat.fan-on"
state "off", label:'', icon:"st.thermostat.fan-off"
}
standardTile("mode", "device.thermostatMode", inactiveLabel:false) {
state "default", label:'[Mode]'
state "off", label:'', icon:"st.thermostat.heating-cooling-off", backgroundColor:"#FFFFFF", action:"thermostat.heat"
state "heat", label:'', icon:"st.thermostat.heat", backgroundColor:"#FFCC99", action:"thermostat.cool"
state "cool", label:'', icon:"st.thermostat.cool", backgroundColor:"#99CCFF", action:"thermostat.auto"
state "auto", label:'', icon:"st.thermostat.auto", backgroundColor:"#99FF99", action:"thermostat.off"
}
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel:false) {
state "default", label:'[Fan Mode]'
state "auto", label:'', icon:"st.thermostat.fan-auto", backgroundColor:"#A4FCA6", action:"thermostat.fanOn"
state "on", label:'', icon:"st.thermostat.fan-on", backgroundColor:"#FAFCA4", action:"thermostat.fanAuto"
}
standardTile("hold", "device.hold", inactiveLabel:false) {
state "default", label:'[Hold]'
state "on", label:'Hold On', icon:"st.Weather.weather2", backgroundColor:"#FFDB94", action:"holdOff"
state "off", label:'Hold Off', icon:"st.Weather.weather2", backgroundColor:"#FFFFFF", action:"holdOn"
}
standardTile("refresh", "device.thermostatMode", inactiveLabel:false, decoration:"flat") {
state "default", icon:"st.secondary.refresh", action:"refresh.refresh"
}
main(["temperature"])
details(["temperature", "operatingState", "fanState",
"heatingSetpoint", "heatLevelDown", "heatLevelUp",
"coolingSetpoint", "coolLevelDown", "coolLevelUp",
"mode", "fanMode", "hold", "refresh"])
}
simulator {
status "Temperature 72.0": "simulator:true, temp:72.00"
status "Cooling Setpoint 76.0": "simulator:true, t_cool:76.00"
status "Heating Setpoint 68.0": "simulator:true, t_cool:68.00"
status "Thermostat Mode Off": "simulator:true, tmode:0"
status "Thermostat Mode Heat": "simulator:true, tmode:1"
status "Thermostat Mode Cool": "simulator:true, tmode:2"
status "Thermostat Mode Auto": "simulator:true, tmode:3"
status "Fan Mode Auto": "simulator:true, fmode:0"
status "Fan Mode Circulate": "simulator:true, fmode:1"
status "Fan Mode On": "simulator:true, fmode:2"
status "State Off": "simulator:true, tstate:0"
status "State Heat": "simulator:true, tstate:1"
status "State Cool": "simulator:true, tstate:2"
status "Fan State Off": "simulator:true, fstate:0"
status "Fan State On": "simulator:true, fstate:1"
status "Hold Disabled": "simulator:true, hold:0"
status "Hold Enabled": "simulator:true, hold:1"
}
}
def updated() {
log.info "Radio Thermostat. ${textVersion()}. ${textCopyright()}"
LOG("$device.displayName updated with settings: ${settings.inspect()}")
state.hostAddress = "${settings.confIpAddr}:${settings.confTcpPort}"
state.dni = createDNI(settings.confIpAddr, settings.confTcpPort)
STATE()
}
def parse(String message) {
LOG("parse(${message})")
def msg = stringToMap(message)
if (msg.headers) {
// parse HTTP response headers
def headers = new String(msg.headers.decodeBase64())
def parsedHeaders = parseHttpHeaders(headers)
LOG("parsedHeaders: ${parsedHeaders}")
if (parsedHeaders.status != 200) {
log.error "Server error: ${parsedHeaders.reason}"
return null
}
// parse HTTP response body
if (!msg.body) {
log.error "HTTP response has no body"
return null
}
def body = new String(msg.body.decodeBase64())
def slurper = new JsonSlurper()
def tstat = slurper.parseText(body)
return parseTstatData(tstat)
} else if (msg.containsKey("simulator")) {
// simulator input
return parseTstatData(msg)
}
return null
}
// thermostat.setThermostatMode
def setThermostatMode(mode) {
LOG("setThermostatMode(${mode})")
switch (mode) {
case "off": return off()
case "heat": return heat()
case "cool": return cool()
case "auto": return auto()
case "emergency heat": return emergencyHeat()
}
log.error "Invalid thermostat mode: \'${mode}\'"
}
// thermostat.off
def off() {
LOG("off()")
if (device.currentValue("thermostatMode") == "off") {
return null
}
sendEvent([name:"thermostatMode", value:"off"])
return writeTstatValue('tmode', 0)
}
// thermostat.heat
def heat() {
LOG("heat()")
if (device.currentValue("thermostatMode") == "heat") {
return null
}
sendEvent([name:"thermostatMode", value:"heat"])
return writeTstatValue('tmode', 1)
}
// thermostat.cool
def cool() {
LOG("cool()")
if (device.currentValue("thermostatMode") == "cool") {
return null
}
sendEvent([name:"thermostatMode", value:"cool"])
return writeTstatValue('tmode', 2)
}
// thermostat.auto
def auto() {
LOG("auto()")
if (device.currentValue("thermostatMode") == "auto") {
return null
}
sendEvent([name:"thermostatMode", value:"auto"])
return writeTstatValue('tmode', 3)
}
// thermostat.emergencyHeat
def emergencyHeat() {
LOG("emergencyHeat()")
log.warn "'emergency heat' mode is not supported"
return null
}
// thermostat.setThermostatFanMode
def setThermostatFanMode(fanMode) {
LOG("setThermostatFanMode(${fanMode})")
switch (fanMode) {
case "auto": return fanAuto()
case "circulate": return fanCirculate()
case "on": return fanOn()
}
log.error "Invalid fan mode: \'${fanMode}\'"
}
// thermostat.fanAuto
def fanAuto() {
LOG("fanAuto()")
if (device.currentValue("thermostatFanMode") == "auto") {
return null
}
sendEvent([name:"thermostatFanMode", value:"auto"])
return writeTstatValue('fmode', 0)
}
// thermostat.fanCirculate
def fanCirculate() {
LOG("fanCirculate()")
log.warn "Fan 'Circulate' mode is not supported"
return null
}
// thermostat.fanOn
def fanOn() {
LOG("fanOn()")
if (device.currentValue("thermostatFanMode") == "on") {
return null
}
sendEvent([name:"thermostatFanMode", value:"on"])
return writeTstatValue('fmode', 2)
}
// thermostat.setHeatingSetpoint
def setHeatingSetpoint(tempHeat) {
LOG("setHeatingSetpoint(${tempHeat})")
def ev = [
name: "heatingSetpoint",
value: tempHeat,
unit: getTemperatureScale(),
]
sendEvent(ev)
if (getTemperatureScale() == "C") {
tempHeat = temperatureCtoF(tempHeat)
}
return writeTstatValue('it_heat', tempHeat)
}
// thermostat.setCoolingSetpoint
def setCoolingSetpoint(tempCool) {
LOG("setCoolingSetpoint(${tempCool})")
def ev = [
name: "coolingSetpoint",
value: tempCool,
unit: getTemperatureScale(),
]
sendEvent(ev)
if (getTemperatureScale() == "C") {
tempCool = temperatureCtoF(tempCool)
}
return writeTstatValue('it_cool', tempCool)
}
def heatLevelDown() {
LOG("heatLevelDown()")
def currentT = device.currentValue("heatingSetpoint")?.toFloat()
if (!currentT) {
return
}
def limit = 50
def step = 1
if (getTemperatureScale() == "C") {
limit = 10
step = 0.5
}
if (currentT > limit) {
setHeatingSetpoint(currentT - step)
}
}
def heatLevelUp() {
LOG("heatLevelUp()")
def currentT = device.currentValue("heatingSetpoint")?.toFloat()
if (!currentT) {
return
}
def limit = 95
def step = 1
if (getTemperatureScale() == "C") {
limit = 35
step = 0.5
}
if (currentT < limit) {
setHeatingSetpoint(currentT + step)
}
}
def coolLevelDown() {
LOG("coolLevelDown()")
def currentT = device.currentValue("coolingSetpoint")?.toFloat()
if (!currentT) {
return
}
def limit = 50
def step = 1
if (getTemperatureScale() == "C") {
limit = 10
step = 0.5
}
if (currentT > limit) {
setCoolingSetpoint(currentT - step)
}
}
def coolLevelUp() {
LOG("coolLevelUp()")
def currentT = device.currentValue("coolingSetpoint")?.toFloat()
if (!currentT) {
return
}
def limit = 95
def step = 1
if (getTemperatureScale() == "C") {
limit = 35
step = 0.5
}
if (currentT < limit) {
setCoolingSetpoint(currentT + step)
}
}
def holdOn() {
LOG("holdOn()")
if (device.currentValue("hold") == "on") {
return null
}
sendEvent([name:"hold", value:"on"])
writeTstatValue("hold", 1)
}
def holdOff() {
LOG("holdOff()")
if (device.currentValue("hold") == "off") {
return null
}
sendEvent([name:"hold", value:"off"])
writeTstatValue("hold", 0)
}
// polling.poll
def poll() {
LOG("poll()")
return refresh()
}
// refresh.refresh
def refresh() {
LOG("refresh()")
//STATE()
return apiGet("/tstat")
}
// Creates Device Network ID in 'AAAAAAAA:PPPP' format
private String createDNI(ipaddr, port) {
LOG("createDNI(${ipaddr}, ${port})")
def hexIp = ipaddr.tokenize('.').collect {
String.format('%02X', it.toInteger())
}.join()
def hexPort = String.format('%04X', port.toInteger())
return "${hexIp}:${hexPort}"
}
private updateDNI() {
if (device.deviceNetworkId != state.dni) {
device.deviceNetworkId = state.dni
}
}
private apiGet(String path) {
LOG("apiGet(${path})")
def headers = [
HOST: state.hostAddress,
Accept: "*/*"
]
def httpRequest = [
method: 'GET',
path: path,
headers: headers
]
updateDNI()
return new physicalgraph.device.HubAction(httpRequest)
}
private apiPost(String path, data) {
LOG("apiPost(${path}, ${data})")
def headers = [
HOST: state.hostAddress,
Accept: "*/*"
]
def httpRequest = [
method: 'POST',
path: path,
headers: headers,
body: data
]
updateDNI()
return new physicalgraph.device.HubAction(httpRequest)
}
private def writeTstatValue(name, value) {
LOG("writeTstatValue(${name}, ${value})")
def json = "{\"${name}\": ${value}}"
def hubActions = [
apiPost("/tstat", json),
delayHubAction(2000),
apiGet("/tstat")
]
return hubActions
}
private def delayHubAction(ms) {
return new physicalgraph.device.HubAction("delay ${ms}")
}
private parseHttpHeaders(String headers) {
def lines = headers.readLines()
def status = lines.remove(0).split()
def result = [
protocol: status[0],
status: status[1].toInteger(),
reason: status[2]
]
return result
}
private def parseTstatData(Map tstat) {
LOG("parseTstatData(${tstat})")
def events = []
if (tstat.containsKey("error_msg")) {
log.error "Thermostat error: ${tstat.error_msg}"
return null
}
if (tstat.containsKey("success")) {
// this is POST response - ignore
return null
}
if (tstat.containsKey("temp")) {
//Float temp = tstat.temp.toFloat()
def ev = [
name: "temperature",
value: scaleTemperature(tstat.temp.toFloat()),
unit: getTemperatureScale(),
]
events << createEvent(ev)
}
if (tstat.containsKey("t_cool")) {
def ev = [
name: "coolingSetpoint",
value: scaleTemperature(tstat.t_cool.toFloat()),
unit: getTemperatureScale(),
]
events << createEvent(ev)
}
if (tstat.containsKey("t_heat")) {
def ev = [
name: "heatingSetpoint",
value: scaleTemperature(tstat.t_heat.toFloat()),
unit: getTemperatureScale(),
]
events << createEvent(ev)
}
if (tstat.containsKey("tstate")) {
def value = parseThermostatState(tstat.tstate)
if (device.currentState("thermostatOperatingState")?.value != value) {
def ev = [
name: "thermostatOperatingState",
value: value
]
events << createEvent(ev)
}
}
if (tstat.containsKey("fstate")) {
def value = parseFanState(tstat.fstate)
if (device.currentState("fanState")?.value != value) {
def ev = [
name: "fanState",
value: value
]
events << createEvent(ev)
}
}
if (tstat.containsKey("tmode")) {
def value = parseThermostatMode(tstat.tmode)
if (device.currentState("thermostatMode")?.value != value) {
def ev = [
name: "thermostatMode",
value: value
]
events << createEvent(ev)
}
}
if (tstat.containsKey("fmode")) {
def value = parseFanMode(tstat.fmode)
if (device.currentState("thermostatFanMode")?.value != value) {
def ev = [
name: "thermostatFanMode",
value: value
]
events << createEvent(ev)
}
}
if (tstat.containsKey("hold")) {
def value = parseThermostatHold(tstat.hold)
if (device.currentState("hold")?.value != value) {
def ev = [
name: "hold",
value: value
]
events << createEvent(ev)
}
}
LOG("events: ${events}")
return events
}
private def parseThermostatState(val) {
def values = [
"idle", // 0
"heating", // 1
"cooling" // 2
]
return values[val.toInteger()]
}
private def parseFanState(val) {
def values = [
"off", // 0
"on" // 1
]
return values[val.toInteger()]
}
private def parseThermostatMode(val) {
def values = [
"off", // 0
"heat", // 1
"cool", // 2
"auto" // 3
]
return values[val.toInteger()]
}
private def parseFanMode(val) {
def values = [
"auto", // 0
"circulate",// 1 (not supported by CT30)
"on" // 2
]
return values[val.toInteger()]
}
private def parseThermostatHold(val) {
def values = [
"off", // 0
"on" // 1
]
return values[val.toInteger()]
}
private def scaleTemperature(Float temp) {
if (getTemperatureScale() == "C") {
return temperatureFtoC(temp)
}
return temp.round(1)
}
private def temperatureCtoF(Float tempC) {
Float t = (tempC * 1.8) + 32
return t.round(1)
}
private def temperatureFtoC(Float tempF) {
Float t = (tempF - 32) / 1.8
return t.round(1)
}
private def textVersion() {
return "Version 1.0.3 (08/25/2015)"
}
private def textCopyright() {
return "Copyright (c) 2014 Statusbits.com"
}
private def LOG(message) {
//log.trace message
}
private def STATE() {
log.trace "state: ${state}"
log.trace "deviceNetworkId: ${device.deviceNetworkId}"
log.trace "temperature: ${device.currentValue("temperature")}"
log.trace "heatingSetpoint: ${device.currentValue("heatingSetpoint")}"
log.trace "coolingSetpoint: ${device.currentValue("coolingSetpoint")}"
log.trace "thermostatMode: ${device.currentValue("thermostatMode")}"
log.trace "thermostatFanMode: ${device.currentValue("thermostatFanMode")}"
log.trace "thermostatOperatingState: ${device.currentValue("thermostatOperatingState")}"
log.trace "fanState: ${device.currentValue("fanState")}"
log.trace "hold: ${device.currentValue("hold")}"
}

View File

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

View File

@@ -37,7 +37,10 @@ preferences {
def mainPage() {
def bridges = bridgesDiscovered()
if (state.username && bridges) {
if (state.refreshUsernameNeeded) {
return bridgeLinking()
} else if (state.username && bridges) {
return bulbDiscovery()
} else {
return bridgeDiscovery()
@@ -102,13 +105,22 @@ def bridgeLinking() {
def nextPage = ""
def title = "Linking with your Hue"
def paragraphText
def paragraphText
if (selectedHue) {
if (state.refreshUsernameNeeded) {
paragraphText = "The current Hue username is invalid.\n\nPlease press the button on your Hue Bridge to re-link. "
} else {
paragraphText = "Press the button on your Hue Bridge to setup a link. "
} else {
paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next."
}
}
} else {
paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next."
}
if (state.username) { //if discovery worked
if (state.refreshUsernameNeeded) {
state.refreshUsernameNeeded = false
// Issue one poll with new username to cancel local polling with old username
poll()
}
nextPage = "bulbDiscovery"
title = "Success!"
paragraphText = "Linking to your hub was a success! Please click 'Next'!"
@@ -915,8 +927,15 @@ private handleCommandResponse(body) {
updates[childDeviceNetworkId]."$eventType" = v
}
}
} else if (payload.error) {
log.warn "Error returned from Hue bridge error = ${body?.error}"
} else if (payload?.error) {
log.warn "Error returned from Hue bridge, error = ${payload?.error}"
// Check for unauthorized user
if (payload?.error?.type?.value == 1) {
log.error "Hue username is not valid"
state.refreshUsernameNeeded = true
state.username = null
}
return []
}
}