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/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/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index a16d28c..1b8d775 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 { 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/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' + ] } 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 b22c2ef..df463cd 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-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 2c6bcfc..b4edfd1 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 Touch Screen Deadbolt Lock" + } tiles(scale: 2) { multiAttributeTile(name:"toggle", type:"generic", width:6, height:4){ 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 d42cd8a..d04f51f 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 bcbfe7c..855109a 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" 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/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 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/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}" 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..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,17 +454,23 @@ def sendStopEvent(source) { eventData.value += "cancelled" } + // 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) - sendTimeRemainingEvent(0) } -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 +480,7 @@ def sendTimeRemainingEvent(percentComplete) { def timeRemainingEventData = [ name: "timeRemaining", value: displayableTime(timeRemaining), - displayed: true, + displayed: displayed, isStateChange: true ] sendControllerEvent(timeRemainingEventData) @@ -608,8 +614,6 @@ private completion() { handleCompletionMessaging() handleCompletionModesAndPhrases() - - sendTimeRemainingEvent(100) } private handleCompletionSwitches() { 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 { 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) { 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