From 4ad0a6fd9d56e26894812c54b34f656ba62eccfc Mon Sep 17 00:00:00 2001 From: Nicola Russo Date: Sun, 12 Jun 2016 08:35:14 -0500 Subject: [PATCH 01/15] MSA-1351: The Gideon AI smart app allows you to connect and control all of your smartThings devices with the Gideon AI app. Gideon AI smart app makes your smartThings device even smarter adding features like energy monitoring, vdeo surveillance history etc.. --- smartapps/gideon-api/gideon.src/gideon.groovy | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 smartapps/gideon-api/gideon.src/gideon.groovy diff --git a/smartapps/gideon-api/gideon.src/gideon.groovy b/smartapps/gideon-api/gideon.src/gideon.groovy new file mode 100644 index 0000000..b277d25 --- /dev/null +++ b/smartapps/gideon-api/gideon.src/gideon.groovy @@ -0,0 +1,253 @@ +/** + * Gideon + * + * Copyright 2016 Nicola Russo + * + * 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: "Gideon", + namespace: "gideon.api", + author: "Braindrain Solutions", + description: "Gideon AI Smart app allows you to connect and control all of your SmartThings devices through the Gideon AI app, making your SmartThings devices even smarter.", + category: "Family", + iconUrl: "http://s33.postimg.org/t77u7y7v3/logo.png", + iconX2Url: "http://s33.postimg.org/t77u7y7v3/logo.png", + iconX3Url: "http://s33.postimg.org/t77u7y7v3/logo.png", + oauth: [displayName: "Gideon AI API", displayLink: "gideon.ai"]) + + +preferences { + section("Control these switches...") { + input "switches", "capability.switch", multiple:true + } + section("Control these motion sensors...") { + input "motions", "capability.motionSensor", multiple:true + } + section("Control these presence sensors...") { + input "presence_sensors", "capability.presenceSensor", multiple:true + } + section("Control these outlets...") { + input "outlets", "capability.switch", multiple:true + } + section("Control these locks...") { + input "locks", "capability.lock", multiple:true + } + section("Control these locks...") { + input "temperature_sensors", "capability.temperatureMeasurement" + } +} + +def installed() { + log.debug "Installed with settings: ${settings}" + + initialize() +} + +def updated() { + log.debug "Updated with settings: ${settings}" + + unsubscribe() + initialize() +} + +def initialize() { + // TODO: subscribe to attributes, devices, locations, etc. + subscribe(outlet, "energy", outletHandler) + subscribe(outlet, "switch", outletHandler) +} + +// TODO: implement event handlers +def outletHandler(evt) { + log.debug "$outlet.currentEnergy" + //TODO call G API +} + + +private device(it, type) { + it ? [id: it.id, label: it.label, type: type] : null +} + +//API Mapping +mappings { + path("/getalldevices") { + action: [ + GET: "getAllDevices" + ] + } + path("/doorlocks/:id/:command") { + action: [ + GET: "updateDoorLock" + ] + } + path("/doorlocks/:id") { + action: [ + GET: "getDoorLockStatus" + ] + } + path("/tempsensors/:id") { + action: [ + GET: "getTempSensorsStatus" + ] + } + path("/presences/:id") { + action: [ + GET: "getPresenceStatus" + ] + } + path("/motions/:id") { + action: [ + GET: "getMotionStatus" + ] + } + path("/outlets/:id") { + action: [ + GET: "getOutletStatus" + ] + } + path("/outlets/:id/:command") { + action: [ + GET: "updateOutlet" + ] + } + path("/switches/:command") { + action: [ + PUT: "updateSwitch" + ] + } +} + +//API Methods +def getAllDevices() { + def locks_list = locks.collect{device(it,"Lock")} + def presences_list = presence_sensors.collect{device(it,"Presence")} + def motions_list = motions.collect{device(it,"Motion")} + def outlets_list = outlets.collect{device(it,"Outlet")} + def switches_list = switches.collect{device(it,"Switch")} + def temp_list = temperature_sensors.collect{device(it,"Temperature")} + return [Locks: locks_list, Presences: presences_list, Motions: motions_list, Outlets: outlets_list, Switches: switches_list, Temperatures: temp_list] +} + +//LOCKS +def getDoorLockStatus() { + def device = locks.find { it.id == params.id } + if (!device) { + httpError(404, "Device not found") + } else { + return [Device_state: device.currentValue('lock')] + } +} + +def updateDoorLock() { + def command = params.command + def device = locks.find { it.id == params.id } + if (command){ + if (!device) { + httpError(404, "Device not found") + } else { + if(command == "toggle") + { + if(device.currentValue('lock') == "locked") + device.unlock(); + else + device.lock(); + + return [Device_id: params.id, result_action: "200"] + } + } + } +} + +//PRESENCE +def getPresenceStatus() { + + def device = presence_sensors.find { it.id == params.id } + if (!device) { + httpError(404, "Device not found") + } else { + return [Device_state: device.currentValue('presence')] + } +} + +//MOTION +def getMotionStatus() { + + def device = motions.find { it.id == params.id } + if (!device) { + httpError(404, "Device not found") + } else { + return [Device_state: device.currentValue('motion')] + } +} + +//OUTLET +def getOutletStatus() { + + def device = outlets.find { it.id == params.id } + if (!device) { + httpError(404, "Device not found") + } else { + return [Device_state: device.currentSwitch, Current_watt: device.currentValue("energy")] + } +} + +def updateOutlet() { + + def command = params.command + def device = outlets.find { it.id == params.id } + if (command){ + if (!device) { + httpError(404, "Device not found") + } else { + if(command == "toggle") + { + if(device.currentSwitch == "on") + device.off(); + else + device.on(); + + return [Device_id: params.id, result_action: "200"] + } + } + } +} + +//SWITCH +def updateSwitch() { + def command = params.command + def device = switches.find { it.id == params.id } + if (command){ + if (!device) { + httpError(404, "Device not found") + } else { + if(command == "toggle") + { + if(device.currentSwitch == "on") + device.off(); + else + device.on(); + + return [Device_id: params.id, result_action: "200"] + } + } + } +} + +//TEMPERATURE +def getTempSensorsStatus() { + + def device = temperature_sensors.find { it.id == params.id } + if (!device) { + httpError(404, "Device not found") + } else { + return [Device_state: device.currentValue('temperature')] + } +} \ No newline at end of file From ced03d746d9905e87beda8239161ace42987c139 Mon Sep 17 00:00:00 2001 From: "sushant.k1" Date: Wed, 14 Sep 2016 15:39:54 +0530 Subject: [PATCH 02/15] Added Readme files for the following devices: 1. OSRAM Lightify LED On/Off/Dim 2. OSRAM Lightify LED RGBW 3. OSRAM Lightify Tunable 60 White Modified Readme files for the following devices: 1. SmartSense Moisture Sensor 2. SmartSense Temp/Humidity Sensor 3. SmartSense Multi Sensor 4. SmartSense Open/closed Sensor 5. SmartPower Outlet 6. Connected Cree Bulb 7. SmartSense Motion Sensor Added Readme files for the following devices: 1. OSRAM Lightify LED On/Off/Dim 2. OSRAM Lightify LED RGBW 3. OSRAM Lightify Tunable 60 White Modified Readme files for the following devices: 1. SmartSense Moisture Sensor 2. SmartSense Temp/Humidity Sensor 3. SmartSense Multi Sensor 4. SmartSense Open/closed Sensor 5. SmartPower Outlet 6. Connected Cree Bulb 7. SmartSense Motion Sensor Added Readme files for the following devices: 1. OSRAM Lightify LED On/Off/Dim 2. OSRAM Lightify LED RGBW 3. OSRAM Lightify Tunable 60 White Modified Readme files for the following devices: 1. SmartSense Moisture Sensor 2. SmartSense Temp/Humidity Sensor 3. SmartSense Multi Sensor 4. SmartSense Open/closed Sensor 5. SmartPower Outlet 6. Connected Cree Bulb 7. SmartSense Motion Sensor Added Readme files for the following devices: 1. OSRAM Lightify LED On/Off/Dim 2. OSRAM Lightify LED RGBW 3. OSRAM Lightify Tunable 60 White Modified Readme files for the following devices: 1. SmartSense Moisture Sensor 2. SmartSense Temp/Humidity Sensor 3. SmartSense Multi Sensor 4. SmartSense Open/closed Sensor 5. SmartPower Outlet 6. Connected Cree Bulb 7. SmartSense Motion Sensor --- .../smartthings/cree-bulb.src/README.md | 6 +-- .../smartpower-outlet.src/README.md | 6 +-- .../smartsense-moisture-sensor.src/README.md | 6 +-- .../smartsense-motion-sensor.src/README.md | 6 +-- .../smartsense-multi-sensor.src/README.md | 6 +-- .../README.md | 6 +-- .../README.md | 6 +-- .../smartthings/zigbee-dimmer.src/.st-ignore | 2 + .../smartthings/zigbee-dimmer.src/README.md | 36 ++++++++++++++++ .../zigbee-dimmer.src/zigbee-dimmer.groovy | 2 +- .../zigbee-rgbw-bulb.src/.st-ignore | 2 + .../zigbee-rgbw-bulb.src/README.md | 42 +++++++++++++++++++ .../zigbee-rgbw-bulb.groovy | 2 +- .../.st-ignore | 2 + .../README.md | 37 ++++++++++++++++ ...zigbee-white-color-temperature-bulb.groovy | 2 +- 16 files changed, 144 insertions(+), 25 deletions(-) create mode 100644 devicetypes/smartthings/zigbee-dimmer.src/.st-ignore create mode 100644 devicetypes/smartthings/zigbee-dimmer.src/README.md create mode 100644 devicetypes/smartthings/zigbee-rgbw-bulb.src/.st-ignore create mode 100644 devicetypes/smartthings/zigbee-rgbw-bulb.src/README.md create mode 100644 devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/.st-ignore create mode 100644 devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/README.md diff --git a/devicetypes/smartthings/cree-bulb.src/README.md b/devicetypes/smartthings/cree-bulb.src/README.md index 2b8093c..67f9699 100644 --- a/devicetypes/smartthings/cree-bulb.src/README.md +++ b/devicetypes/smartthings/cree-bulb.src/README.md @@ -23,10 +23,8 @@ Works with: ## Device Health -A Category C6 Connected Cree LED Bulb with maxReportTime of 10 min. -Check-in interval is double the value of maxReportTime for Zigbee device. -This gives the device twice the amount of time to respond before it is marked as offline. -Check-in interval = 2*10 = 20 min +A Category C6 Connected Cree LED Bulb with maxReportTime of 5 mins. +Check-in interval = 12 mins ## Troubleshooting diff --git a/devicetypes/smartthings/smartpower-outlet.src/README.md b/devicetypes/smartthings/smartpower-outlet.src/README.md index 4dda7bf..3a5fddb 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/README.md +++ b/devicetypes/smartthings/smartpower-outlet.src/README.md @@ -23,10 +23,10 @@ Works with: ## Device Health -A Category C1 smart power outlet with maxReportTime of 10 min. -Check-in interval is double the value of maxReportTime for Zigbee device. +A Category C1 smart power outlet with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. This gives the device twice the amount of time to respond before it is marked as offline. -Check-in interval = 2*10 = 20 min +Check-in interval = 12 mins ## Troubleshooting diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/README.md b/devicetypes/smartthings/smartsense-moisture-sensor.src/README.md index c346957..401a7e5 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/README.md +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/README.md @@ -23,10 +23,10 @@ Works with: ## Device Health -A Category C2 moisture sensor with maxReportTime of 1 hr. -Check-in interval is double the value of maxReportTime for Zigbee device. +A Category C2 moisture sensor with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. This gives the device twice the amount of time to respond before it is marked as offline. -Check-in interval = 2*60 = 120 min +Check-in interval = 12 mins ## Battery Specification diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/README.md b/devicetypes/smartthings/smartsense-motion-sensor.src/README.md index d177cc6..adcac31 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/README.md +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/README.md @@ -22,10 +22,10 @@ Works with: ## Device Health -A Category C2 motion sensor with maxReportTime of 1 hr. -Check-in interval is double the value of maxReportTime for Zigbee device. +A Category C2 motion sensor with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. This gives the device twice the amount of time to respond before it is marked as offline. -Check-in interval = 2*60 = 120 min +Check-in interval = 12 mins ## Battery Specification diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/README.md b/devicetypes/smartthings/smartsense-multi-sensor.src/README.md index 809a192..591475c 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/README.md +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/README.md @@ -26,10 +26,10 @@ Works with: ## Device Health -A Category C2 multi sensor with maxReportTime of 1 hr. -Check-in interval is double the value of maxReportTime for Zigbee device. +A Category C2 multi sensor with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. This gives the device twice the amount of time to respond before it is marked as offline. -Check-in interval = 2*60 = 120 min +Check-in interval = 12 mins ## Battery Specification diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/README.md b/devicetypes/smartthings/smartsense-open-closed-sensor.src/README.md index 4cf3bf3..c9454c5 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/README.md +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/README.md @@ -24,10 +24,10 @@ Works with: ## Device Health -A Category C2 open/closed sensor with maxReportTime of 1 hr. -Check-in interval is double the value of maxReportTime for Zigbee device. +A Category C2 open/closed sensor with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. This gives the device twice the amount of time to respond before it is marked as offline. -Check-in interval = 2*60 = 120 min +Check-in interval = 12 mins ## Battery Specification diff --git a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/README.md b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/README.md index 3d6d3c8..b24396a 100644 --- a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/README.md +++ b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/README.md @@ -24,10 +24,10 @@ Works with: ## Device Health -A Category C2 SmartSense Temp/Humidity Sensor with maxReportTime of 1 hr. -Check-in interval is double the value of maxReportTime for Zigbee device. +A Category C2 SmartSense Temp/Humidity Sensor with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. This gives the device twice the amount of time to respond before it is marked as offline. -Check-in interval = 2*60 = 120 min +Check-in interval = 12 mins ## Battery Specification diff --git a/devicetypes/smartthings/zigbee-dimmer.src/.st-ignore b/devicetypes/smartthings/zigbee-dimmer.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/zigbee-dimmer.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/zigbee-dimmer.src/README.md b/devicetypes/smartthings/zigbee-dimmer.src/README.md new file mode 100644 index 0000000..35997a5 --- /dev/null +++ b/devicetypes/smartthings/zigbee-dimmer.src/README.md @@ -0,0 +1,36 @@ +# OSRAM Lightify LED On/Off/Dim + + + +Works with: + +* [OSRAM Lightify LED On/Off/Dim](https://shop.smartthings.com/#!/products/osram-led-smart-bulb-on-off-dim) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) + +## Capabilities + +* **Actuator** - represents that a Device has commands +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Refresh** - _refresh()_ command for status updates +* **Switch** - can detect state (possible values: on/off) +* **Switch Level** - represents current light level, usually 0-100 in percent +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +A Category C1 Zigbee dimmer with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. +This gives the device twice the amount of time to respond before it is marked as offline. +Check-in interval = 12 mins + +## 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. +Other troubleshooting tips are listed as follows: +* [Troubleshooting:](https://support.smartthings.com/hc/en-us/articles/207191763-OSRAM-LIGHTIFY-LED-Smart-Connected-Light-A19-On-Off-Dim) diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index b977c8b..e739277 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -13,7 +13,7 @@ */ metadata { - definition (name: "ZigBee Dimmer", namespace: "smartthings", author: "SmartThings") { + definition (name: "ZigBee Dimmer", namespace: "smartthings", author: "SmartThings", category: "C1") { capability "Actuator" capability "Configuration" capability "Refresh" diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/.st-ignore b/devicetypes/smartthings/zigbee-rgbw-bulb.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/README.md b/devicetypes/smartthings/zigbee-rgbw-bulb.src/README.md new file mode 100644 index 0000000..abe51bb --- /dev/null +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/README.md @@ -0,0 +1,42 @@ +# OSRAM LIGHTIFY LED RGBW Bulb + + + +Works with: + +* [OSRAM LIGHTIFY LED RGBW Bulb](https://support.smartthings.com/hc/en-us/articles/207728173-OSRAM-LIGHTIFY-LED-Smart-Connected-Light-A19-RGBW) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) + +## Capabilities + +* **Actuator** - It represents that a device has commands. +* **Color Control** - It represents that the color attributes of a device can be controlled (hue, saturation, color value). +* **Color Temperature** - It represents color temperature capability measured in degree Kelvin. +* **Polling** - It represents that a device can be polled. +* **Switch** - can detect state (possible values: on/off) +* **Switch Level** - can detect current light level (0-100 in percent) +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Refresh** - _refresh()_ command for status updates +* **Health Check** - indicates ability to get device health notifications + + +## Device Health + +A Category C6 OSRAM LIGHTIFY LED RGBW Bulb with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. +This gives the device twice the amount of time to respond before it is marked as offline. +Check-in interval = 12 mins + + +## 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: +* [Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/207728173-OSRAM-LIGHTIFY-LED-Smart-Connected-Light-A19-RGBW) \ No newline at end of file diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 367e480..97e75fc 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -17,7 +17,7 @@ */ metadata { - definition (name: "ZigBee RGBW Bulb", namespace: "smartthings", author: "SmartThings") { + definition (name: "ZigBee RGBW Bulb", namespace: "smartthings", author: "SmartThings", category: "C6") { capability "Actuator" capability "Color Control" diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/.st-ignore b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/README.md b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/README.md new file mode 100644 index 0000000..3bd9229 --- /dev/null +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/README.md @@ -0,0 +1,37 @@ +# OSRAM Lightify Tunable 60 White + + + +Works with: + +* [OSRAM Lightify Tunable 60 White](http://www.osram.com/osram_com/tools-and-services/tools/lightify---smart-connected-light/lightify-for-home---what-is-light-to-you/lightify-products/lightify-classic-a60-tunable-white/index.jsp) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) + +## Capabilities + +* **Actuator** - represents that a Device has commands +* **Color Temperature** - represents color temperature, measured in degrees Kelvin. +* **Configuration** - _configure()_ command called when device is installed or device preferences updated. +* **Refresh** - _refresh()_ command for status updates +* **Switch** - can detect state (possible values: on/off) +* **Switch Level** - represents current light level, usually 0-100 in percent +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +A Category C1 OSRAM Lightify Tunable 60 White with maxReportTime of 5 mins. +Check-in interval is double the value of maxReportTime. +This gives the device twice the amount of time to respond before it is marked as offline. +Check-in interval = 12 mins + +## 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. +Other troubleshooting tips are listed as follows: +* [Troubleshooting:](https://support.smartthings.com/hc/en-us/articles/204576454-OSRAM-LIGHTIFY-Tunable-White-60-Bulb) diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index 5a37fab..07cb8be 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -17,7 +17,7 @@ */ metadata { - definition (name: "ZigBee White Color Temperature Bulb", namespace: "smartthings", author: "SmartThings") { + definition (name: "ZigBee White Color Temperature Bulb", namespace: "smartthings", author: "SmartThings", category: "C1") { capability "Actuator" capability "Color Temperature" From 230541a14519dcb8bb027f2397be4f5311a43a8b Mon Sep 17 00:00:00 2001 From: Zach Varberg Date: Wed, 14 Sep 2016 12:32:25 -0500 Subject: [PATCH 03/15] Add explicit send commands for zigbee raw Previously a few places depended on the dev-conn behavior of automatically appending a send command after a raw zigbee command. We intend to deprecate that behavior and so we are adding explicit sends where they were missing. --- .../arrival-sensor.src/arrival-sensor.groovy | 13 ++++++++++++- .../centralite-dimmer.groovy | 14 ++++++++++++-- .../smartpower-outlet-v1.groovy | 16 ++++++++++++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/devicetypes/smartthings/arrival-sensor.src/arrival-sensor.groovy b/devicetypes/smartthings/arrival-sensor.src/arrival-sensor.groovy index a8703fc..5b87cc1 100644 --- a/devicetypes/smartthings/arrival-sensor.src/arrival-sensor.groovy +++ b/devicetypes/smartthings/arrival-sensor.src/arrival-sensor.groovy @@ -87,16 +87,27 @@ def beep() { up to this long from the time you send the message to the time you hear a sound. */ + // Used source endpoint of 0x02 because we are using smartthings manufacturer specific cluster. [ "raw 0xFC05 {15 0A 11 00 00 15 01}", + "delay 200", + "send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId", "delay 7000", "raw 0xFC05 {15 0A 11 00 00 15 01}", + "delay 200", + "send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId", "delay 7000", "raw 0xFC05 {15 0A 11 00 00 15 01}", + "delay 200", + "send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId", "delay 7000", "raw 0xFC05 {15 0A 11 00 00 15 01}", + "delay 200", + "send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId", "delay 7000", - "raw 0xFC05 {15 0A 11 00 00 15 01}" + "raw 0xFC05 {15 0A 11 00 00 15 01}", + "delay 200", + "send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId", ] } diff --git a/devicetypes/smartthings/centralite-dimmer.src/centralite-dimmer.groovy b/devicetypes/smartthings/centralite-dimmer.src/centralite-dimmer.groovy index c6c7d1b..a17d591 100644 --- a/devicetypes/smartthings/centralite-dimmer.src/centralite-dimmer.groovy +++ b/devicetypes/smartthings/centralite-dimmer.src/centralite-dimmer.groovy @@ -105,11 +105,21 @@ def parseDescriptionAsMap(description) { // Commands to device def on() { - 'zcl on-off on' + [ + 'zcl on-off on', + 'delay 200', + "send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}", + 'delay 500' + ] } def off() { - 'zcl on-off off' + [ + 'zcl on-off off', + 'delay 200', + "send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}", + 'delay 500' + ] } def setLevel(value) { diff --git a/devicetypes/smartthings/smartpower-outlet-v1.src/smartpower-outlet-v1.groovy b/devicetypes/smartthings/smartpower-outlet-v1.src/smartpower-outlet-v1.groovy index ddde7cf..a65c5bc 100644 --- a/devicetypes/smartthings/smartpower-outlet-v1.src/smartpower-outlet-v1.groovy +++ b/devicetypes/smartthings/smartpower-outlet-v1.src/smartpower-outlet-v1.groovy @@ -47,9 +47,21 @@ def parse(String description) { // Commands to device def on() { - 'zcl on-off on' + [ + 'zcl on-off on', + 'delay 200', + "send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}", + 'delay 500' + + ] + } def off() { - 'zcl on-off off' + [ + 'zcl on-off off', + 'delay 200', + "send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}", + 'delay 500' + ] } From 1d180ac487678d6602bd4a97c1cf79e70feea978 Mon Sep 17 00:00:00 2001 From: twack Date: Thu, 22 Sep 2016 10:19:23 -0700 Subject: [PATCH 04/15] update generic zigbee lock for Yale Key-Free Deadbolt (PROD-736) --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 2c6bcfc..1d61f70 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -31,7 +31,8 @@ fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Push Button Deadbolt Lock" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL210 PB LL", deviceJoinName: "Yale Push Button Lever Lock" - } + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "Yale Key-Free Touchscreen Deadbolt Lock" + } tiles(scale: 2) { multiAttributeTile(name:"toggle", type:"generic", width:6, height:4){ From 1d6e22dc161a58d10a09121b2e5ac2d22584cc07 Mon Sep 17 00:00:00 2001 From: twack Date: Thu, 22 Sep 2016 11:23:29 -0700 Subject: [PATCH 05/15] removed tabs --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 1d61f70..14b4f57 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -31,7 +31,7 @@ fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Push Button Deadbolt Lock" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL210 PB LL", deviceJoinName: "Yale Push Button Lever Lock" - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "Yale Key-Free Touchscreen Deadbolt Lock" + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "Yale Key-Free Touchscreen Deadbolt Lock" } tiles(scale: 2) { From 40ed88e7fd7454987f70afb80bfa33b97ec6b857 Mon Sep 17 00:00:00 2001 From: twack Date: Thu, 22 Sep 2016 11:34:33 -0700 Subject: [PATCH 06/15] Changed name to be consistent Changed name to be consistent with "Yale Touch Screen Deadbolt Lock" --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 14b4f57..adeddf2 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -31,7 +31,7 @@ fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Push Button Deadbolt Lock" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL210 PB LL", deviceJoinName: "Yale Push Button Lever Lock" - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "Yale Key-Free Touchscreen Deadbolt Lock" + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "ale Touch Screen Deadbolt Lock" } tiles(scale: 2) { From dbfaef3e69ad3601cef0737370c5fe058fd3ff09 Mon Sep 17 00:00:00 2001 From: Juan Pablo Risso Date: Thu, 22 Sep 2016 14:55:25 -0400 Subject: [PATCH 07/15] DVCSMP-2076 - Async Update (#1279) --- .../jawbone-up-connect.groovy | 122 +++++++++--------- 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/smartapps/juano2310/jawbone-up-connect.src/jawbone-up-connect.groovy b/smartapps/juano2310/jawbone-up-connect.src/jawbone-up-connect.groovy index 4e120c1..a13a7d7 100644 --- a/smartapps/juano2310/jawbone-up-connect.src/jawbone-up-connect.groovy +++ b/smartapps/juano2310/jawbone-up-connect.src/jawbone-up-connect.groovy @@ -4,6 +4,9 @@ * Author: Juan Risso * Date: 2013-12-19 */ + +include 'asynchttp_v1' + definition( name: "Jawbone UP (Connect)", namespace: "juano2310", @@ -303,7 +306,8 @@ def setup() { def childDevice = addChildDevice('juano2310', "Jawbone User", "${app.id}.${member.xid}",null,[name:"Jawbone UP - " + member.first, completedSetup: true]) if (childDevice) { log.debug "Child Device Successfully Created" - generateInitialEvent (member, childDevice) + childDevice?.generateSleepingEvent(false) + pollChild(childDevice) } } } @@ -349,67 +353,67 @@ def uninstalled() { } def pollChild(childDevice) { - def member = state.member - generatePollingEvents (member, childDevice) + def childMap = [ value: "$childDevice.device.deviceNetworkId}"] + + def params = [ + uri: 'https://jawbone.com', + path: '/nudge/api/users/@me/goals', + headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ], + contentType: 'application/json' + ] + + asynchttp_v1.get('responseGoals', params, childMap) + + def params2 = [ + uri: 'https://jawbone.com', + path: '/nudge/api/users/@me/moves', + headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ], + contentType: 'application/json' + ] + + asynchttp_v1.get('responseMoves', params2, childMap) } -def generatePollingEvents (member, childDevice) { - // lets figure out if the member is currently "home" (At the place) - def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals" - def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves" - def urlsleeps = "https://jawbone.com/nudge/api/users/@me/sleeps" - def goals = null - def moves = null - def sleeps = null - httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response -> - goals = response.data.data - } - httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response -> - moves = response.data.data.items[0] - } - - try { // we are going to just ignore any errors - log.debug "Member = ${member.first}" - log.debug "Moves Goal = ${goals.move_steps} Steps" - log.debug "Moves = ${moves.details.steps} Steps" - - childDevice?.sendEvent(name:"steps", value: moves.details.steps) - childDevice?.sendEvent(name:"goal", value: goals.move_steps) - //setColor(moves.details.steps,goals.move_steps,childDevice) - } - catch (e) { - // eat it - } +def responseGoals(response, dni) { + if (response.hasError()) { + log.error "response has error: $response.errorMessage" + } else { + def goals + try { + // json response already parsed into JSONElement object + goals = response.json.data + } catch (e) { + log.error "error parsing json from response: $e" + } + if (goals) { + def childDevice = getChildDevice(dni.value) + log.debug "Goal = ${goals.move_steps} Steps" + childDevice?.sendEvent(name:"goal", value: goals.move_steps) + } else { + log.debug "did not get json results from response body: $response.data" + } + } } -def generateInitialEvent (member, childDevice) { - // lets figure out if the member is currently "home" (At the place) - def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals" - def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves" - def urlsleeps = "https://jawbone.com/nudge/api/users/@me/sleeps" - def goals = null - def moves = null - def sleeps = null - httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response -> - goals = response.data.data - } - httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response -> - moves = response.data.data.items[0] - } - - try { // we are going to just ignore any errors - log.debug "Member = ${member.first}" - log.debug "Moves Goal = ${goals.move_steps} Steps" - log.debug "Moves = ${moves.details.steps} Steps" - log.debug "Sleeping state = false" - childDevice?.generateSleepingEvent(false) - childDevice?.sendEvent(name:"steps", value: moves.details.steps) - childDevice?.sendEvent(name:"goal", value: goals.move_steps) - //setColor(moves.details.steps,goals.move_steps,childDevice) - } - catch (e) { - // eat it - } +def responseMoves(response, dni) { + if (response.hasError()) { + log.error "response has error: $response.errorMessage" + } else { + def moves + try { + // json response already parsed into JSONElement object + moves = response.json.data.items[0] + } catch (e) { + log.error "error parsing json from response: $e" + } + if (moves) { + def childDevice = getChildDevice(dni.value) + log.debug "Moves = ${moves.details.steps} Steps" + childDevice?.sendEvent(name:"steps", value: moves.details.steps) + } else { + log.debug "did not get json results from response body: $response.data" + } + } } def setColor (steps,goal,childDevice) { @@ -433,7 +437,7 @@ def hookEventHandler() { // get some stuff we need def userId = json.events.user_xid[0] def json_type = json.events.type[0] - def json_action = json.events.action[0] + def json_action = json.events.action[0] //log.debug json log.debug "Userid = ${userId}" From d58084c4389b776e3109bae531ff4c8c292b301a Mon Sep 17 00:00:00 2001 From: twack Date: Thu, 22 Sep 2016 12:06:29 -0700 Subject: [PATCH 08/15] Update zigbee-lock.groovy --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index adeddf2..b4edfd1 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -31,7 +31,7 @@ fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Push Button Deadbolt Lock" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock" fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL210 PB LL", deviceJoinName: "Yale Push Button Lever Lock" - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "ale Touch Screen Deadbolt Lock" + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock" } tiles(scale: 2) { From 79d20b0edb101cc79e24097f3750179b03666eb1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Risso Date: Thu, 22 Sep 2016 18:59:07 -0400 Subject: [PATCH 09/15] SSVD-2740 - Remove zipcode input (#1267) Limit to samsungtv channel --- .../smart-windows.src/smart-windows.groovy | 29 +++++++++++++------ .../ready-for-rain.src/ready-for-rain.groovy | 15 ++++++++-- .../severe-weather-alert.groovy | 25 +++++++++------- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/smartapps/egid/smart-windows.src/smart-windows.groovy b/smartapps/egid/smart-windows.src/smart-windows.groovy index 89cdb04..924da87 100644 --- a/smartapps/egid/smart-windows.src/smart-windows.groovy +++ b/smartapps/egid/smart-windows.src/smart-windows.groovy @@ -1,7 +1,7 @@ /** * Smart Windows * Compares two temperatures – indoor vs outdoor, for example – then sends an alert if windows are open (or closed!). - * + * * Copyright 2014 Eric Gideon * * Based in part on the "When it's going to rain" SmartApp by the SmartThings team, @@ -21,13 +21,18 @@ definition( name: "Smart Windows", namespace: "egid", author: "Eric Gideon", - description: "Compares two temperatures – indoor vs outdoor, for example – then sends an alert if windows are open (or closed!). If you don't use an external temperature device, your zipcode will be used instead.", + description: "Compares two temperatures – indoor vs outdoor, for example – then sends an alert if windows are open (or closed!). If you don't use an external temperature device, your location will be used instead.", iconUrl: "https://s3.amazonaws.com/smartthings-device-icons/Home/home9-icn.png", iconX2Url: "https://s3.amazonaws.com/smartthings-device-icons/Home/home9-icn@2x.png" ) preferences { + + if (!(location.zipCode || ( location.latitude && location.longitude )) && location.channelName == 'samsungtv') { + section { paragraph title: "Note:", "Location is required for this SmartApp. Go to 'Location Name' settings to setup your correct location." } + } + section( "Set the temperature range for your comfort zone..." ) { input "minTemp", "number", title: "Minimum temperature" input "maxTemp", "number", title: "Maximum temperature" @@ -39,9 +44,11 @@ preferences { input "inTemp", "capability.temperatureMeasurement", title: "Indoor" input "outTemp", "capability.temperatureMeasurement", title: "Outdoor (optional)", required: false } - section( "Set your location" ) { - input "zipCode", "text", title: "Zip code" - } + + if (location.channelName != 'samsungtv') { + section( "Set your location" ) { input "zipCode", "text", title: "Zip code" } + } + section( "Notifications" ) { input "sendPushMessage", "enum", title: "Send a push notification?", metadata:[values:["Yes","No"]], required:false input "retryPeriod", "number", title: "Minutes between notifications:" @@ -72,7 +79,7 @@ def temperatureHandler(evt) { def currentInTemp = evt.doubleValue def openWindows = sensors.findAll { it?.latestValue("contact") == 'open' } - + log.trace "Temp event: $evt" log.info "In: $currentInTemp; Out: $currentOutTemp" @@ -98,7 +105,7 @@ def temperatureHandler(evt) { if ( currentOutTemp < maxTemp && !openWindows ) { send( "Open some windows to cool down the house! Currently ${currentInTemp}°F inside and ${currentOutTemp}°F outside." ) } else if ( currentOutTemp > maxTemp && openWindows ) { - send( "It's gotten warmer outside! You should close these windows: ${openWindows.join(', ')}. Currently ${currentInTemp}°F inside and ${currentOutTemp}°F outside." ) + send( "It's gotten warmer outside! You should close these windows: ${openWindows.join(', ')}. Currently ${currentInTemp}°F inside and ${currentOutTemp}°F outside." ) } else { log.debug "No notifications sent. Everything is in the right place." } @@ -125,7 +132,11 @@ def temperatureHandler(evt) { } def weatherCheck() { - def json = getWeatherFeature("conditions", zipCode) + def json + if (location.channelName != 'samsungtv') + json = getWeatherFeature("conditions", zipCode) + else + json = getWeatherFeature("conditions") def currentTemp = json?.current_observation?.temp_f if ( currentTemp ) { @@ -150,4 +161,4 @@ private send(msg) { } log.info msg -} \ No newline at end of file +} diff --git a/smartapps/imbrianj/ready-for-rain.src/ready-for-rain.groovy b/smartapps/imbrianj/ready-for-rain.src/ready-for-rain.groovy index c88b91f..c7ffb39 100644 --- a/smartapps/imbrianj/ready-for-rain.src/ready-for-rain.groovy +++ b/smartapps/imbrianj/ready-for-rain.src/ready-for-rain.groovy @@ -18,8 +18,13 @@ definition( ) preferences { - section("Zip code?") { - input "zipcode", "text", title: "Zipcode?" + + if (!(location.zipCode || ( location.latitude && location.longitude )) && location.channelName == 'samsungtv') { + section { paragraph title: "Note:", "Location is required for this SmartApp. Go to 'Location Name' settings to setup your correct location." } + } + + if (location.channelName != 'samsungtv') { + section( "Set your location" ) { input "zipCode", "text", title: "Zip code" } } section("Things to check?") { @@ -60,7 +65,11 @@ def scheduleCheck(evt) { // Only need to poll if we haven't checked in a while - and if something is left open. if((now() - (30 * 60 * 1000) > state.lastCheck["time"]) && open) { log.info("Something's open - let's check the weather.") - def response = getWeatherFeature("forecast", zipcode) + def response + if (location.channelName != 'samsungtv') + response = getWeatherFeature("forecast", zipCode) + else + response = getWeatherFeature("forecast") def weather = isStormy(response) if(weather) { diff --git a/smartapps/smartthings/severe-weather-alert.src/severe-weather-alert.groovy b/smartapps/smartthings/severe-weather-alert.src/severe-weather-alert.groovy index f27671c..eafa863 100644 --- a/smartapps/smartthings/severe-weather-alert.src/severe-weather-alert.groovy +++ b/smartapps/smartthings/severe-weather-alert.src/severe-weather-alert.groovy @@ -26,17 +26,22 @@ definition( ) preferences { - section ("In addition to push notifications, send text alerts to...") { - input("recipients", "contact", title: "Send notifications to") { - input "phone1", "phone", title: "Phone Number 1", required: false - input "phone2", "phone", title: "Phone Number 2", required: false - input "phone3", "phone", title: "Phone Number 3", required: false - } + + if (!(location.zipCode || ( location.latitude && location.longitude )) && location.channelName == 'samsungtv') { + section { paragraph title: "Note:", "Location is required for this SmartApp. Go to 'Location Name' settings to setup your correct location." } } - section ("Zip code (optional, defaults to location coordinates)...") { - input "zipcode", "text", title: "Zip Code", required: false - } + if (location.channelName != 'samsungtv') { + section( "Set your location" ) { input "zipCode", "text", title: "Zip code" } + } + + section ("In addition to push notifications, send text alerts to...") { + input("recipients", "contact", title: "Send notifications to") { + input "phone1", "phone", title: "Phone Number 1", required: false + input "phone2", "phone", title: "Phone Number 2", required: false + input "phone3", "phone", title: "Phone Number 3", required: false + } + } } def installed() { @@ -61,7 +66,7 @@ def checkForSevereWeather() { def alerts if(locationIsDefined()) { if(zipcodeIsValid()) { - alerts = getWeatherFeature("alerts", zipcode)?.alerts + alerts = getWeatherFeature("alerts", zipCode)?.alerts } else { log.warn "Severe Weather Alert: Invalid zipcode entered, defaulting to location's zipcode" alerts = getWeatherFeature("alerts")?.alerts From c428267d63b011cdf4ef379096b21120c87f4c72 Mon Sep 17 00:00:00 2001 From: vlaminck Date: Mon, 26 Sep 2016 14:05:03 -0500 Subject: [PATCH 10/15] Fix: stop sending 100 percent from completion --- smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy | 2 -- 1 file changed, 2 deletions(-) diff --git a/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy b/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy index e5f60b7..3e31622 100644 --- a/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy +++ b/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy @@ -608,8 +608,6 @@ private completion() { handleCompletionMessaging() handleCompletionModesAndPhrases() - - sendTimeRemainingEvent(100) } private handleCompletionSwitches() { From 797a58cb688e9c5da55ee5a5082787034cacf0a5 Mon Sep 17 00:00:00 2001 From: vlaminck Date: Mon, 26 Sep 2016 14:14:55 -0500 Subject: [PATCH 11/15] Fix: hide reset events --- .../gentle-wake-up.src/gentle-wake-up.groovy | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy b/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy index 3e31622..9d8b527 100644 --- a/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy +++ b/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy @@ -455,16 +455,19 @@ def sendStopEvent(source) { } sendControllerEvent(eventData) - sendTimeRemainingEvent(0) + // send 100% completion + sendTimeRemainingEvent(100) + // send a non-displayed 0% completion to reset tiles + sendTimeRemainingEvent(0, false) } -def sendTimeRemainingEvent(percentComplete) { +def sendTimeRemainingEvent(percentComplete, displayed = true) { log.trace "sendTimeRemainingEvent(${percentComplete})" def percentCompleteEventData = [ name: "percentComplete", value: percentComplete as int, - displayed: true, + displayed: displayed, isStateChange: true ] sendControllerEvent(percentCompleteEventData) @@ -474,7 +477,7 @@ def sendTimeRemainingEvent(percentComplete) { def timeRemainingEventData = [ name: "timeRemaining", value: displayableTime(timeRemaining), - displayed: true, + displayed: displayed, isStateChange: true ] sendControllerEvent(timeRemainingEventData) From e4c1824afdbcca05386f512859ffe884e38d8912 Mon Sep 17 00:00:00 2001 From: vlaminck Date: Mon, 26 Sep 2016 14:18:31 -0500 Subject: [PATCH 12/15] Fix: reorder events so the feed makes more sense --- .../smartthings/gentle-wake-up.src/gentle-wake-up.groovy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy b/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy index 9d8b527..3fed2e8 100644 --- a/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy +++ b/smartapps/smartthings/gentle-wake-up.src/gentle-wake-up.groovy @@ -454,11 +454,14 @@ def sendStopEvent(source) { eventData.value += "cancelled" } - sendControllerEvent(eventData) - // send 100% completion + // send 100% completion event sendTimeRemainingEvent(100) + // send a non-displayed 0% completion to reset tiles sendTimeRemainingEvent(0, false) + + // send sessionStatus event last so the event feed is ordered properly + sendControllerEvent(eventData) } def sendTimeRemainingEvent(percentComplete, displayed = true) { From 5b0ca4b81528280031450f97a42052d12b52241d Mon Sep 17 00:00:00 2001 From: David Sainte-Claire Date: Tue, 27 Sep 2016 07:48:54 -0700 Subject: [PATCH 13/15] changed deviceTemperatureUnit attribute to be type string --- .../ecobee-thermostat.src/ecobee-thermostat.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index 134fa5c..6cb82f5 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -31,13 +31,13 @@ metadata { command "switchMode" command "switchFanMode" - attribute "thermostatSetpoint","number" - attribute "thermostatStatus","string" + attribute "thermostatSetpoint", "number" + attribute "thermostatStatus", "string" attribute "maxHeatingSetpoint", "number" attribute "minHeatingSetpoint", "number" attribute "maxCoolingSetpoint", "number" attribute "minCoolingSetpoint", "number" - attribute "deviceTemperatureUnit", "number" + attribute "deviceTemperatureUnit", "string" } tiles { From 218cc43520a023cb66d737a5ea5d233c23ae3d8f Mon Sep 17 00:00:00 2001 From: Juan Pablo Risso Date: Tue, 27 Sep 2016 15:43:37 -0400 Subject: [PATCH 14/15] DVCSMP-2089 - Harmony - Make HTTP Requests Async (#1299) --- .../logitech-harmony-connect.groovy | 290 ++++++++++-------- 1 file changed, 166 insertions(+), 124 deletions(-) diff --git a/smartapps/smartthings/logitech-harmony-connect.src/logitech-harmony-connect.groovy b/smartapps/smartthings/logitech-harmony-connect.src/logitech-harmony-connect.groovy index b7084c3..2597e2b 100644 --- a/smartapps/smartthings/logitech-harmony-connect.src/logitech-harmony-connect.groovy +++ b/smartapps/smartthings/logitech-harmony-connect.src/logitech-harmony-connect.groovy @@ -34,6 +34,7 @@ * locks | lock | lock, unlock | locked, unlocked * ---------------------+-------------------+-----------------------------+------------------------------------ */ +include 'asynchttp_v1' definition( name: "Logitech Harmony (Connect)", @@ -109,26 +110,28 @@ def authPage() { //device discovery request every 5 //25 seconds int deviceRefreshCount = !state.deviceRefreshCount ? 0 : state.deviceRefreshCount as int state.deviceRefreshCount = deviceRefreshCount + 1 - def refreshInterval = 3 + def refreshInterval = 5 def huboptions = state.HarmonyHubs ?: [] def actoptions = state.HarmonyActivities ?: [] def numFoundHub = huboptions.size() ?: 0 - def numFoundAct = actoptions.size() ?: 0 + def numFoundAct = actoptions.size() ?: 0 + if((deviceRefreshCount % 5) == 0) { discoverDevices() } + return dynamicPage(name:"Credentials", title:"Discovery Started!", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) { section("Please wait while we discover your Harmony Hubs and Activities. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") { - input "selectedhubs", "enum", required:false, title:"Select Harmony Hubs (${numFoundHub} found)", multiple:true, options:huboptions + input "selectedhubs", "enum", required:false, title:"Select Harmony Hubs (${numFoundHub} found)", multiple:true, submitOnChange: true, options:huboptions } - // Virtual activity flag - if (numFoundHub > 0 && numFoundAct > 0 && true) + // Virtual activity flag + if (numFoundHub > 0 && numFoundAct > 0 && true) section("You can also add activities as virtual switches for other convenient integrations") { - input "selectedactivities", "enum", required:false, title:"Select Harmony Activities (${numFoundAct} found)", multiple:true, options:actoptions + input "selectedactivities", "enum", required:false, title:"Select Harmony Activities (${numFoundAct} found)", multiple:true, submitOnChange: true, options:actoptions } - if (state.resethub) + if (state.resethub) section("Connection to the hub timed out. Please restart the hub and try again.") {} } } @@ -380,8 +383,6 @@ def discovery() { log.debug "valid Token" state.Harmonydevices = response.data state.resethub = false - getActivityList() - poll() } else { log.debug "Error: $response.status" } @@ -430,142 +431,182 @@ def addDevice() { } def activity(dni,mode) { - def Params = [auth: state.HarmonyAccessToken] - def msg = "Command failed" - def url = '' + def tokenParam = [auth: state.HarmonyAccessToken] + def url if (dni == "all") { - url = "https://home.myharmony.com/cloudapi/activity/off?${toQueryString(Params)}" + url = "https://home.myharmony.com/cloudapi/activity/off?${toQueryString(tokenParam)}" } else { def aux = dni.split('-') def hubId = aux[1] if (mode == "hub" || (aux.size() <= 2) || (aux[2] == "off")){ - url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/off?${toQueryString(Params)}" + url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/off?${toQueryString(tokenParam)}" } else { - def activityId = aux[2] - url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/${activityId}/${mode}?${toQueryString(Params)}" + def activityId = aux[2] + url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/${activityId}/${mode}?${toQueryString(tokenParam)}" } } - try { - httpPostJson(uri: url) { response -> - if (response.data.code == 200 || dni == "all") { - msg = "Command sent succesfully" - state.aux = 0 - } else { - msg = "Command failed. Error: $response.data.code" - } - } - } catch (groovyx.net.http.HttpResponseException ex) { - log.error ex - if (state.aux == 0) { - state.aux = 1 - activity(dni,mode) - } else { - msg = ex - state.aux = 0 - } - } catch(Exception ex) { - msg = ex + def params = [ + uri: url, + contentType: 'application/json' + ] + asynchttp_v1.post('activityResponse', params) + return "Command Sent" +} + +def activityResponse(response, data) { + if (response.hasError()) { + log.error "Logitech Harmony - response has error: $response.errorMessage" + if (response.status == 401) { // token is expired + state.remove("HarmonyAccessToken") + log.warn "Logitech Harmony - Access token has expired" } - runIn(10, "poll", [overwrite: true]) - return msg + } else { + def ResponseValues + try { + // json response already parsed into JSONElement object + ResponseValues = response.json + } catch (e) { + log.error "Logitech Harmony - error parsing json from response: $e" + } + if (ResponseValues) { + if (ResponseValues.code == 200) { + log.trace "Command sent succesfully" + poll() + } else { + log.trace "Command failed. Error: $response.data.code" + } + } else { + log.debug "Logitech Harmony - did not get json results from response body: $response.data" + } + } } def poll() { // GET THE LIST OF ACTIVITIES if (state.HarmonyAccessToken) { getActivityList() - def Params = [auth: state.HarmonyAccessToken] - def url = "https://home.myharmony.com/cloudapi/state?${toQueryString(Params)}" - try { - httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> - def map = [:] - response.data.hubs.each { - if (it.value.message == "OK") { - map["${it.key}"] = "${it.value.response.data.currentAvActivity},${it.value.response.data.activityStatus}" - def hub = getChildDevice("harmony-${it.key}") - if (hub) { - if (it.value.response.data.currentAvActivity == "-1") { - hub.sendEvent(name: "currentActivity", value: "--", descriptionText: "There isn't any activity running", display: false) - } else { - def currentActivity = getActivityName(it.value.response.data.currentAvActivity,it.key) - hub.sendEvent(name: "currentActivity", value: currentActivity, descriptionText: "Current activity is ${currentActivity}", display: false) - } - } - } else { - log.trace it.value.message - } - } - def activities = getChildDevices() - def activitynotrunning = true - activities.each { activity -> - def act = activity.deviceNetworkId.split('-') - if (act.size() > 2) { - def aux = map.find { it.key == act[1] } - if (aux) { - def aux2 = aux.value.split(',') - def childDevice = getChildDevice(activity.deviceNetworkId) - if ((act[2] == aux2[0]) && (aux2[1] == "1" || aux2[1] == "2")) { - childDevice?.sendEvent(name: "switch", value: "on") - if (aux2[1] == "1") - runIn(5, "poll", [overwrite: true]) - } else { - childDevice?.sendEvent(name: "switch", value: "off") - if (aux2[1] == "3") - runIn(5, "poll", [overwrite: true]) - } - } + def tokenParam = [auth: state.HarmonyAccessToken] + def params = [ + uri: "https://home.myharmony.com/cloudapi/state?${toQueryString(tokenParam)}", + headers: ["Accept": "application/json"], + contentType: 'application/json' + ] + asynchttp_v1.get('pollResponse', params) + } else { + log.warn "Logitech Harmony - Access token has expired" + } +} + +def pollResponse(response, data) { + if (response.hasError()) { + log.error "Logitech Harmony - response has error: $response.errorMessage" + if (response.status == 401) { // token is expired + state.remove("HarmonyAccessToken") + log.warn "Logitech Harmony - Access token has expired" + } + } else { + def ResponseValues + try { + // json response already parsed into JSONElement object + ResponseValues = response.json + } catch (e) { + log.error "Logitech Harmony - error parsing json from response: $e" + } + if (ResponseValues) { + def map = [:] + ResponseValues.hubs.each { + if (it.value.message == "OK") { + map["${it.key}"] = "${it.value.response.data.currentAvActivity},${it.value.response.data.activityStatus}" + def hub = getChildDevice("harmony-${it.key}") + if (hub) { + if (it.value.response.data.currentAvActivity == "-1") { + hub.sendEvent(name: "currentActivity", value: "--", descriptionText: "There isn't any activity running", display: false) + } else { + def currentActivity = getChildDevice("harmony-${it.key}-${it.value.response.data.currentAvActivity}").device.displayName + hub.sendEvent(name: "currentActivity", value: currentActivity, descriptionText: "Current activity is ${currentActivity}", display: false) + } + } + } else { + log.trace "Logitech Harmony - error response: $it.value.message" + } + } + def activities = getChildDevices() + def activitynotrunning = true + activities.each { activity -> + def act = activity.deviceNetworkId.split('-') + if (act.size() > 2) { + def aux = map.find { it.key == act[1] } + if (aux) { + def aux2 = aux.value.split(',') + def childDevice = getChildDevice(activity.deviceNetworkId) + if ((act[2] == aux2[0]) && (aux2[1] == "1" || aux2[1] == "2")) { + childDevice?.sendEvent(name: "switch", value: "on") + if (aux2[1] == "1") + runIn(5, "poll", [overwrite: true]) + } else { + childDevice?.sendEvent(name: "switch", value: "off") + if (aux2[1] == "3") + runIn(5, "poll", [overwrite: true]) } } - return "Poll completed $map - $state.hubs" } - } catch (groovyx.net.http.HttpResponseException e) { - if (e.statusCode == 401) { // token is expired - state.remove("HarmonyAccessToken") - log.warn "Harmony Access token has expired" - } - } catch (java.net.SocketTimeoutException e) { - log.warn "Connection to the hub timed out. Please restart the hub and try again." - state.resethub = true - } catch (e) { - log.info "Logitech Harmony - Error: $e" } + } else { + log.debug "Logitech Harmony - did not get json results from response body: $response.data" + } + } +} + +def getActivityList() { + if (state.HarmonyAccessToken) { + def tokenParam = [auth: state.HarmonyAccessToken] + def params = [ + uri: "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(tokenParam)}", + headers: ["Accept": "application/json"], + contentType: 'application/json' + ] + asynchttp_v1.get('activityListResponse', params) + } else { + log.warn "Logitech Harmony - Access token has expired" } } - -def getActivityList() { - // GET ACTIVITY'S NAME - if (state.HarmonyAccessToken) { - def Params = [auth: state.HarmonyAccessToken] - def url = "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(Params)}" - try { - httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> - response.data.hubs.each { - def hub = getChildDevice("harmony-${it.key}") - if (hub) { - def hubname = getHubName("${it.key}") - def activities = [] - def aux = it.value.response.data.activities.size() - if (aux >= 1) { - activities = it.value.response.data.activities.collect { - [id: it.key, name: it.value['name'], type: it.value['type']] - } - activities += [id: "off", name: "Activity OFF", type: "0"] - log.trace activities - } - hub.sendEvent(name: "activities", value: new groovy.json.JsonBuilder(activities).toString(), descriptionText: "Activities are ${activities.collect { it.name }?.join(', ')}", display: false) - } - } - } - } catch (groovyx.net.http.HttpResponseException e) { - log.trace e - } catch (java.net.SocketTimeoutException e) { - log.trace e - } catch(Exception e) { - log.trace e - } - } - return activity +def activityListResponse(response, data) { + if (response.hasError()) { + log.error "Logitech Harmony - response has error: $response.errorMessage" + if (response.status == 401) { // token is expired + state.remove("HarmonyAccessToken") + log.warn "Logitech Harmony - Access token has expired" + } + } else { + def ResponseValues + try { + // json response already parsed into JSONElement object + ResponseValues = response.json + } catch (e) { + log.error "Logitech Harmony - error parsing json from response: $e" + } + if (ResponseValues) { + ResponseValues.hubs.each { + def hub = getChildDevice("harmony-${it.key}") + if (hub) { + def hubname = getHubName("${it.key}") + def activities = [] + def aux = it.value.response?.data.activities.size() + if (aux >= 1) { + activities = it.value.response.data.activities.collect { + [id: it.key, name: it.value['name'], type: it.value['type']] + } + activities += [id: "off", name: "Activity OFF", type: "0"] + log.trace activities + } + hub.sendEvent(name: "activities", value: new groovy.json.JsonBuilder(activities).toString(), descriptionText: "Activities are ${activities.collect { it.name }?.join(', ')}", display: false) + } + } + } else { + log.debug "Logitech Harmony - did not get json results from response body: $response.data" + } + } } def getActivityName(activity,hubId) { @@ -746,7 +787,7 @@ def addSubscription() { def attribute = data.attributeName def callbackUrl = data.callbackUrl - log.debug "addSubscription, params: ${params}, request: ${data}" + log.debug "Logitech Harmony - addSubscription, params: ${params}, request: ${data}" if (!attribute) { render status: 400, data: '{"msg": "attributeName is required"}' } else { @@ -808,6 +849,7 @@ def deviceHandler(evt) { def deviceInfo = state[evt.deviceId] if (state.harmonyHubs) { state.harmonyHubs.each { harmonyHub -> + log.trace "Logitech Harmony - Sending data to $harmonyHub.name" sendToHarmony(evt, harmonyHub.callbackUrl) } } else if (deviceInfo) { From aa890ae3d5cf1fbc8429b4a1f50a6bbf0826ce4a Mon Sep 17 00:00:00 2001 From: Lars Finander Date: Mon, 3 Oct 2016 12:51:02 -0600 Subject: [PATCH 15/15] CHF-413 Philips Hue bulb shows unavailable after 16minutes and CHF-412 Hue Bridge shows OFFLINE instead of "Unavailable" --- .../hue-bridge.src/hue-bridge.groovy | 21 +++-- .../smartthings/hue-bulb.src/hue-bulb.groovy | 2 +- .../hue-connect.src/hue-connect.groovy | 78 +++++++++---------- 3 files changed, 51 insertions(+), 50 deletions(-) diff --git a/devicetypes/smartthings/hue-bridge.src/hue-bridge.groovy b/devicetypes/smartthings/hue-bridge.src/hue-bridge.groovy index 18e9a25..fee818f 100644 --- a/devicetypes/smartthings/hue-bridge.src/hue-bridge.groovy +++ b/devicetypes/smartthings/hue-bridge.src/hue-bridge.groovy @@ -7,9 +7,11 @@ metadata { // Automatically generated. Make future change here. definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") { + capability "Health Check" + attribute "networkAddress", "string" - // Used to indicate if bridge is reachable or not, i.e. is the bridge connected to the network - // Possible values "Online" or "Offline" + // Used to indicate if bridge is reachable or not, i.e. is the bridge connected to the network + // Possible values "Online" or "Offline" attribute "status", "string" // Id is the number on the back of the hub, Hue uses last six digits of Mac address // This is also used in the Hue application as ID @@ -42,6 +44,10 @@ metadata { } } +void installed() { + sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false) +} + // parse events into attributes def parse(description) { log.debug "Parsing '${description}'" @@ -70,13 +76,8 @@ def parse(description) { def bulbs = new groovy.json.JsonSlurper().parseText(msg.body) if (bulbs.state) { log.info "Bridge response: $msg.body" - } else { - // Sending Bulbs List to parent" - if (parent.isInBulbDiscovery()) - log.info parent.bulbListHandler(device.hub.id, msg.body) } - } - else if (contentType?.contains("xml")) { + } else if (contentType?.contains("xml")) { log.debug "HUE BRIDGE ALREADY PRESENT" parent.hubVerification(device.hub.id, msg.body) } @@ -85,3 +86,7 @@ def parse(description) { } results } + +def ping() { + log.debug "${parent.ping(this)}" +} diff --git a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy index d963d06..b5f25fe 100644 --- a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy +++ b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy @@ -174,7 +174,7 @@ void setColorTemperature(value) { void refresh() { log.debug "Executing 'refresh'" - parent.manualRefresh() + parent?.manualRefresh() } def verifyPercent(percent) { diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index d8ed36b..a9f7566 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -131,10 +131,7 @@ def bulbDiscovery() { def refreshInterval = 3 state.inBulbDiscovery = true def bridge = null - if (selectedHue) { - bridge = getChildDevice(selectedHue) - subscribe(bridge, "bulbList", bulbListData) - } + state.bridgeRefreshCount = 0 def allLightsFound = bulbsDiscovered() ?: [:] @@ -259,10 +256,6 @@ Map bulbsDiscovered() { return bulbmap } -def bulbListData(evt) { - state.bulbs = evt.jsonData -} - Map getHueBulbs() { state.bulbs = state.bulbs ?: [:] } @@ -316,29 +309,6 @@ def uninstalled(){ state.username = null } -// Handles events to add new bulbs -def bulbListHandler(hub, data = "") { - def msg = "Bulbs list not processed. Only while in settings menu." - def bulbs = [:] - if (state.inBulbDiscovery) { - def logg = "" - log.trace "Adding bulbs to state..." - state.bridgeProcessedLightList = true - def object = new groovy.json.JsonSlurper().parseText(data) - object.each { k,v -> - if (v instanceof Map) - bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub, online: v.state?.reachable] - } - } - def bridge = null - if (selectedHue) { - bridge = getChildDevice(selectedHue) - bridge?.sendEvent(name: "bulbList", value: hub, data: bulbs, isStateChange: true, displayed: false) - } - msg = "${bulbs.size()} bulbs found. ${bulbs}" - return msg -} - private upgradeDeviceType(device, newHueType) { def deviceType = getDeviceType(newHueType) @@ -570,11 +540,8 @@ void lightsHandler(physicalgraph.device.HubResponse hubResponse) { if (isValidSource(hubResponse.mac)) { def body = hubResponse.json if (!body?.state?.on) { //check if first time poll made it here by mistake - def bulbs = getHueBulbs() log.debug "Adding bulbs to state!" - body.each { k, v -> - bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub: hubResponse.hubId] - } + updateBulbState(body, hubResponse.hubId) } } } @@ -690,11 +657,8 @@ def locationHandler(evt) { } else { //GET /api/${state.username}/lights response (application/json) if (!body?.state?.on) { //check if first time poll made it here by mistake - def bulbs = getHueBulbs() log.debug "Adding bulbs to state!" - body.each { k,v -> - bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:parsedEvent.hub] - } + updateBulbState(body, parsedEvent.hub) } } } @@ -754,7 +718,7 @@ private void checkBridgeStatus() { } if (it.value.lastActivity < time) { // it.value.lastActivity != null && - log.warn "Bridge $it.key is Offline" + log.warn "Bridge $it.value.idNumber is Offline" d.sendEvent(name: "status", value: "Offline") state.bulbs?.each { @@ -779,6 +743,31 @@ def isInBulbDiscovery() { return state.inBulbDiscovery } +private updateBulbState(messageBody, hub) { + def bulbs = getHueBulbs() + + // Copy of bulbs used to locate old lights in state that are no longer on bridge + def toRemove = [:] + toRemove << bulbs + + messageBody.each { k,v -> + + if (v instanceof Map) { + if (bulbs[k] == null) { + bulbs[k] = [:] + } + bulbs[k] << [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub, remove: false] + toRemove.remove(k) + } + } + + // Remove bulbs from state that are no longer discovered + toRemove.each { k,v -> + log.warn "${bulbs[k].name} no longer exists on bridge, removing" + bulbs.remove(k) + } +} + ///////////////////////////////////// //CHILD DEVICE METHODS ///////////////////////////////////// @@ -1173,7 +1162,14 @@ def setColor(childDevice, huesettings) { } def ping(childDevice) { - if (isOnline(getId(childDevice))) { + if (childDevice.device?.deviceNetworkId?.equalsIgnoreCase(selectedHue)) { + if (childDevice.device?.currentValue("status")?.equalsIgnoreCase("Online")) { + childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Bridge is reachable", displayed: false, isStateChange: true) + return "Bridge is Online" + } else { + return "Bridge is Offline" + } + } else if (isOnline(getId(childDevice))) { childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Light is reachable", displayed: false, isStateChange: true) return "Device is Online" } else {