Merge pull request #1316 from SmartThingsCommunity/staging

Rolling up staging to prod for deployment
This commit is contained in:
Vinay Rao
2016-10-04 14:41:53 -07:00
committed by GitHub
31 changed files with 777 additions and 295 deletions

View File

@@ -87,16 +87,27 @@ def beep() {
up to this long from the time you send the message to the time you hear a sound. 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}", "raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 200",
"send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId",
"delay 7000", "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",
"delay 7000", "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",
"delay 7000", "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",
"delay 7000", "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",
] ]
} }

View File

@@ -105,11 +105,21 @@ def parseDescriptionAsMap(description) {
// Commands to device // Commands to device
def on() { def on() {
'zcl on-off on' [
'zcl on-off on',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
]
} }
def off() { 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) { def setLevel(value) {

View File

@@ -23,10 +23,8 @@ Works with:
## Device Health ## Device Health
A Category C6 Connected Cree LED Bulb with maxReportTime of 10 min. A Category C6 Connected Cree LED Bulb with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime for Zigbee device. Check-in interval = 12 mins
This gives the device twice the amount of time to respond before it is marked as offline.
Check-in interval = 2*10 = 20 min
## Troubleshooting ## Troubleshooting

View File

@@ -37,7 +37,7 @@ metadata {
attribute "minHeatingSetpoint", "number" attribute "minHeatingSetpoint", "number"
attribute "maxCoolingSetpoint", "number" attribute "maxCoolingSetpoint", "number"
attribute "minCoolingSetpoint", "number" attribute "minCoolingSetpoint", "number"
attribute "deviceTemperatureUnit", "number" attribute "deviceTemperatureUnit", "string"
} }
tiles { tiles {

View File

@@ -7,6 +7,8 @@
metadata { metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") { definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") {
capability "Health Check"
attribute "networkAddress", "string" attribute "networkAddress", "string"
// Used to indicate if bridge is reachable or not, i.e. is the bridge connected to the network // Used to indicate if bridge is reachable or not, i.e. is the bridge connected to the network
// Possible values "Online" or "Offline" // Possible values "Online" or "Offline"
@@ -42,6 +44,10 @@ metadata {
} }
} }
void installed() {
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "Parsing '${description}'" log.debug "Parsing '${description}'"
@@ -70,13 +76,8 @@ def parse(description) {
def bulbs = new groovy.json.JsonSlurper().parseText(msg.body) def bulbs = new groovy.json.JsonSlurper().parseText(msg.body)
if (bulbs.state) { if (bulbs.state) {
log.info "Bridge response: $msg.body" 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" log.debug "HUE BRIDGE ALREADY PRESENT"
parent.hubVerification(device.hub.id, msg.body) parent.hubVerification(device.hub.id, msg.body)
} }
@@ -85,3 +86,7 @@ def parse(description) {
} }
results results
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -174,7 +174,7 @@ void setColorTemperature(value) {
void refresh() { void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent.manualRefresh() parent?.manualRefresh()
} }
def verifyPercent(percent) { def verifyPercent(percent) {

View File

@@ -47,9 +47,21 @@ def parse(String description) {
// Commands to device // Commands to device
def on() { def on() {
'zcl on-off on' [
'zcl on-off on',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
]
} }
def off() { def off() {
'zcl on-off off' [
'zcl on-off off',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
]
} }

View File

@@ -23,10 +23,10 @@ Works with:
## Device Health ## Device Health
A Category C1 smart power outlet with maxReportTime of 10 min. A Category C1 smart power outlet with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime for Zigbee device. 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. 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 ## Troubleshooting

View File

@@ -23,10 +23,10 @@ Works with:
## Device Health ## Device Health
A Category C2 moisture sensor with maxReportTime of 1 hr. A Category C2 moisture sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime for Zigbee device. 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. 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 ## Battery Specification

View File

@@ -22,10 +22,10 @@ Works with:
## Device Health ## Device Health
A Category C2 motion sensor with maxReportTime of 1 hr. A Category C2 motion sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime for Zigbee device. 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. 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 ## Battery Specification

View File

@@ -26,10 +26,10 @@ Works with:
## Device Health ## Device Health
A Category C2 multi sensor with maxReportTime of 1 hr. A Category C2 multi sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime for Zigbee device. 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. 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 ## Battery Specification

View File

@@ -24,10 +24,10 @@ Works with:
## Device Health ## Device Health
A Category C2 open/closed sensor with maxReportTime of 1 hr. A Category C2 open/closed sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime for Zigbee device. 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. 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 ## Battery Specification

View File

@@ -24,10 +24,10 @@ Works with:
## Device Health ## Device Health
A Category C2 SmartSense Temp/Humidity Sensor with maxReportTime of 1 hr. A Category C2 SmartSense Temp/Humidity Sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime for Zigbee device. 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. 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 ## Battery Specification

View File

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

View File

@@ -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)

View File

@@ -13,7 +13,7 @@
*/ */
metadata { metadata {
definition (name: "ZigBee Dimmer", namespace: "smartthings", author: "SmartThings") { definition (name: "ZigBee Dimmer", namespace: "smartthings", author: "SmartThings", category: "C1") {
capability "Actuator" capability "Actuator"
capability "Configuration" capability "Configuration"
capability "Refresh" capability "Refresh"

View File

@@ -31,6 +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: "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: "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: "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) { tiles(scale: 2) {

View File

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

View File

@@ -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)

View File

@@ -17,7 +17,7 @@
*/ */
metadata { metadata {
definition (name: "ZigBee RGBW Bulb", namespace: "smartthings", author: "SmartThings") { definition (name: "ZigBee RGBW Bulb", namespace: "smartthings", author: "SmartThings", category: "C6") {
capability "Actuator" capability "Actuator"
capability "Color Control" capability "Color Control"

View File

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

View File

@@ -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)

View File

@@ -17,7 +17,7 @@
*/ */
metadata { 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 "Actuator"
capability "Color Temperature" capability "Color Temperature"

View File

@@ -21,13 +21,18 @@ definition(
name: "Smart Windows", name: "Smart Windows",
namespace: "egid", namespace: "egid",
author: "Eric Gideon", 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", 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" iconX2Url: "https://s3.amazonaws.com/smartthings-device-icons/Home/home9-icn@2x.png"
) )
preferences { 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..." ) { section( "Set the temperature range for your comfort zone..." ) {
input "minTemp", "number", title: "Minimum temperature" input "minTemp", "number", title: "Minimum temperature"
input "maxTemp", "number", title: "Maximum temperature" input "maxTemp", "number", title: "Maximum temperature"
@@ -39,9 +44,11 @@ preferences {
input "inTemp", "capability.temperatureMeasurement", title: "Indoor" input "inTemp", "capability.temperatureMeasurement", title: "Indoor"
input "outTemp", "capability.temperatureMeasurement", title: "Outdoor (optional)", required: false 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" ) { section( "Notifications" ) {
input "sendPushMessage", "enum", title: "Send a push notification?", metadata:[values:["Yes","No"]], required:false input "sendPushMessage", "enum", title: "Send a push notification?", metadata:[values:["Yes","No"]], required:false
input "retryPeriod", "number", title: "Minutes between notifications:" input "retryPeriod", "number", title: "Minutes between notifications:"
@@ -125,7 +132,11 @@ def temperatureHandler(evt) {
} }
def weatherCheck() { 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 def currentTemp = json?.current_observation?.temp_f
if ( currentTemp ) { if ( currentTemp ) {

View File

@@ -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')]
}
}

View File

@@ -18,8 +18,13 @@ definition(
) )
preferences { 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?") { 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. // 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) { if((now() - (30 * 60 * 1000) > state.lastCheck["time"]) && open) {
log.info("Something's open - let's check the weather.") 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) def weather = isStormy(response)
if(weather) { if(weather) {

View File

@@ -4,6 +4,9 @@
* Author: Juan Risso * Author: Juan Risso
* Date: 2013-12-19 * Date: 2013-12-19
*/ */
include 'asynchttp_v1'
definition( definition(
name: "Jawbone UP (Connect)", name: "Jawbone UP (Connect)",
namespace: "juano2310", 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]) def childDevice = addChildDevice('juano2310', "Jawbone User", "${app.id}.${member.xid}",null,[name:"Jawbone UP - " + member.first, completedSetup: true])
if (childDevice) { if (childDevice) {
log.debug "Child Device Successfully Created" log.debug "Child Device Successfully Created"
generateInitialEvent (member, childDevice) childDevice?.generateSleepingEvent(false)
pollChild(childDevice)
} }
} }
} }
@@ -349,66 +353,66 @@ def uninstalled() {
} }
def pollChild(childDevice) { def pollChild(childDevice) {
def member = state.member def childMap = [ value: "$childDevice.device.deviceNetworkId}"]
generatePollingEvents (member, childDevice)
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) { def responseGoals(response, dni) {
// lets figure out if the member is currently "home" (At the place) if (response.hasError()) {
def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals" log.error "response has error: $response.errorMessage"
def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves" } else {
def urlsleeps = "https://jawbone.com/nudge/api/users/@me/sleeps" def goals
def goals = null try {
def moves = null // json response already parsed into JSONElement object
def sleeps = null goals = response.json.data
httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response -> } catch (e) {
goals = response.data.data log.error "error parsing json from response: $e"
} }
httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response -> if (goals) {
moves = response.data.data.items[0] def childDevice = getChildDevice(dni.value)
} log.debug "Goal = ${goals.move_steps} Steps"
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) childDevice?.sendEvent(name:"goal", value: goals.move_steps)
//setColor(moves.details.steps,goals.move_steps,childDevice) } else {
log.debug "did not get json results from response body: $response.data"
} }
catch (e) {
// eat it
} }
} }
def generateInitialEvent (member, childDevice) { def responseMoves(response, dni) {
// lets figure out if the member is currently "home" (At the place) if (response.hasError()) {
def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals" log.error "response has error: $response.errorMessage"
def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves" } else {
def urlsleeps = "https://jawbone.com/nudge/api/users/@me/sleeps" def moves
def goals = null try {
def moves = null // json response already parsed into JSONElement object
def sleeps = null moves = response.json.data.items[0]
httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response -> } catch (e) {
goals = response.data.data log.error "error parsing json from response: $e"
} }
httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response -> if (moves) {
moves = response.data.data.items[0] def childDevice = getChildDevice(dni.value)
}
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 "Moves = ${moves.details.steps} Steps"
log.debug "Sleeping state = false"
childDevice?.generateSleepingEvent(false)
childDevice?.sendEvent(name:"steps", value: moves.details.steps) childDevice?.sendEvent(name:"steps", value: moves.details.steps)
childDevice?.sendEvent(name:"goal", value: goals.move_steps) } else {
//setColor(moves.details.steps,goals.move_steps,childDevice) log.debug "did not get json results from response body: $response.data"
} }
catch (e) {
// eat it
} }
} }

View File

@@ -454,17 +454,23 @@ def sendStopEvent(source) {
eventData.value += "cancelled" 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) sendControllerEvent(eventData)
sendTimeRemainingEvent(0)
} }
def sendTimeRemainingEvent(percentComplete) { def sendTimeRemainingEvent(percentComplete, displayed = true) {
log.trace "sendTimeRemainingEvent(${percentComplete})" log.trace "sendTimeRemainingEvent(${percentComplete})"
def percentCompleteEventData = [ def percentCompleteEventData = [
name: "percentComplete", name: "percentComplete",
value: percentComplete as int, value: percentComplete as int,
displayed: true, displayed: displayed,
isStateChange: true isStateChange: true
] ]
sendControllerEvent(percentCompleteEventData) sendControllerEvent(percentCompleteEventData)
@@ -474,7 +480,7 @@ def sendTimeRemainingEvent(percentComplete) {
def timeRemainingEventData = [ def timeRemainingEventData = [
name: "timeRemaining", name: "timeRemaining",
value: displayableTime(timeRemaining), value: displayableTime(timeRemaining),
displayed: true, displayed: displayed,
isStateChange: true isStateChange: true
] ]
sendControllerEvent(timeRemainingEventData) sendControllerEvent(timeRemainingEventData)
@@ -608,8 +614,6 @@ private completion() {
handleCompletionMessaging() handleCompletionMessaging()
handleCompletionModesAndPhrases() handleCompletionModesAndPhrases()
sendTimeRemainingEvent(100)
} }
private handleCompletionSwitches() { private handleCompletionSwitches() {

View File

@@ -131,10 +131,7 @@ def bulbDiscovery() {
def refreshInterval = 3 def refreshInterval = 3
state.inBulbDiscovery = true state.inBulbDiscovery = true
def bridge = null def bridge = null
if (selectedHue) {
bridge = getChildDevice(selectedHue)
subscribe(bridge, "bulbList", bulbListData)
}
state.bridgeRefreshCount = 0 state.bridgeRefreshCount = 0
def allLightsFound = bulbsDiscovered() ?: [:] def allLightsFound = bulbsDiscovered() ?: [:]
@@ -259,10 +256,6 @@ Map bulbsDiscovered() {
return bulbmap return bulbmap
} }
def bulbListData(evt) {
state.bulbs = evt.jsonData
}
Map getHueBulbs() { Map getHueBulbs() {
state.bulbs = state.bulbs ?: [:] state.bulbs = state.bulbs ?: [:]
} }
@@ -316,29 +309,6 @@ def uninstalled(){
state.username = null 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) { private upgradeDeviceType(device, newHueType) {
def deviceType = getDeviceType(newHueType) def deviceType = getDeviceType(newHueType)
@@ -570,11 +540,8 @@ void lightsHandler(physicalgraph.device.HubResponse hubResponse) {
if (isValidSource(hubResponse.mac)) { if (isValidSource(hubResponse.mac)) {
def body = hubResponse.json def body = hubResponse.json
if (!body?.state?.on) { //check if first time poll made it here by mistake if (!body?.state?.on) { //check if first time poll made it here by mistake
def bulbs = getHueBulbs()
log.debug "Adding bulbs to state!" log.debug "Adding bulbs to state!"
body.each { k, v -> updateBulbState(body, hubResponse.hubId)
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub: hubResponse.hubId]
}
} }
} }
} }
@@ -690,11 +657,8 @@ def locationHandler(evt) {
} else { } else {
//GET /api/${state.username}/lights response (application/json) //GET /api/${state.username}/lights response (application/json)
if (!body?.state?.on) { //check if first time poll made it here by mistake if (!body?.state?.on) { //check if first time poll made it here by mistake
def bulbs = getHueBulbs()
log.debug "Adding bulbs to state!" log.debug "Adding bulbs to state!"
body.each { k,v -> updateBulbState(body, parsedEvent.hub)
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:parsedEvent.hub]
}
} }
} }
} }
@@ -754,7 +718,7 @@ private void checkBridgeStatus() {
} }
if (it.value.lastActivity < time) { // it.value.lastActivity != null && 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") d.sendEvent(name: "status", value: "Offline")
state.bulbs?.each { state.bulbs?.each {
@@ -779,6 +743,31 @@ def isInBulbDiscovery() {
return state.inBulbDiscovery 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 //CHILD DEVICE METHODS
///////////////////////////////////// /////////////////////////////////////
@@ -1173,7 +1162,14 @@ def setColor(childDevice, huesettings) {
} }
def ping(childDevice) { 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) childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Light is reachable", displayed: false, isStateChange: true)
return "Device is Online" return "Device is Online"
} else { } else {

View File

@@ -34,6 +34,7 @@
* locks | lock | lock, unlock | locked, unlocked * locks | lock | lock, unlock | locked, unlocked
* ---------------------+-------------------+-----------------------------+------------------------------------ * ---------------------+-------------------+-----------------------------+------------------------------------
*/ */
include 'asynchttp_v1'
definition( definition(
name: "Logitech Harmony (Connect)", name: "Logitech Harmony (Connect)",
@@ -109,24 +110,26 @@ def authPage() {
//device discovery request every 5 //25 seconds //device discovery request every 5 //25 seconds
int deviceRefreshCount = !state.deviceRefreshCount ? 0 : state.deviceRefreshCount as int int deviceRefreshCount = !state.deviceRefreshCount ? 0 : state.deviceRefreshCount as int
state.deviceRefreshCount = deviceRefreshCount + 1 state.deviceRefreshCount = deviceRefreshCount + 1
def refreshInterval = 3 def refreshInterval = 5
def huboptions = state.HarmonyHubs ?: [] def huboptions = state.HarmonyHubs ?: []
def actoptions = state.HarmonyActivities ?: [] def actoptions = state.HarmonyActivities ?: []
def numFoundHub = huboptions.size() ?: 0 def numFoundHub = huboptions.size() ?: 0
def numFoundAct = actoptions.size() ?: 0 def numFoundAct = actoptions.size() ?: 0
if((deviceRefreshCount % 5) == 0) { if((deviceRefreshCount % 5) == 0) {
discoverDevices() discoverDevices()
} }
return dynamicPage(name:"Credentials", title:"Discovery Started!", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) { 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.") { 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 // Virtual activity flag
if (numFoundHub > 0 && numFoundAct > 0 && true) if (numFoundHub > 0 && numFoundAct > 0 && true)
section("You can also add activities as virtual switches for other convenient integrations") { 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.") {} section("Connection to the hub timed out. Please restart the hub and try again.") {}
@@ -380,8 +383,6 @@ def discovery() {
log.debug "valid Token" log.debug "valid Token"
state.Harmonydevices = response.data state.Harmonydevices = response.data
state.resethub = false state.resethub = false
getActivityList()
poll()
} else { } else {
log.debug "Error: $response.status" log.debug "Error: $response.status"
} }
@@ -430,56 +431,90 @@ def addDevice() {
} }
def activity(dni,mode) { def activity(dni,mode) {
def Params = [auth: state.HarmonyAccessToken] def tokenParam = [auth: state.HarmonyAccessToken]
def msg = "Command failed" def url
def url = ''
if (dni == "all") { if (dni == "all") {
url = "https://home.myharmony.com/cloudapi/activity/off?${toQueryString(Params)}" url = "https://home.myharmony.com/cloudapi/activity/off?${toQueryString(tokenParam)}"
} else { } else {
def aux = dni.split('-') def aux = dni.split('-')
def hubId = aux[1] def hubId = aux[1]
if (mode == "hub" || (aux.size() <= 2) || (aux[2] == "off")){ 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 { } else {
def activityId = aux[2] def activityId = aux[2]
url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/${activityId}/${mode}?${toQueryString(Params)}" url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/${activityId}/${mode}?${toQueryString(tokenParam)}"
} }
} }
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"
}
} else {
def ResponseValues
try { try {
httpPostJson(uri: url) { response -> // json response already parsed into JSONElement object
if (response.data.code == 200 || dni == "all") { ResponseValues = response.json
msg = "Command sent succesfully" } catch (e) {
state.aux = 0 log.error "Logitech Harmony - error parsing json from response: $e"
}
if (ResponseValues) {
if (ResponseValues.code == 200) {
log.trace "Command sent succesfully"
poll()
} else { } else {
msg = "Command failed. Error: $response.data.code" log.trace "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 { } else {
msg = ex log.debug "Logitech Harmony - did not get json results from response body: $response.data"
state.aux = 0
} }
} catch(Exception ex) {
msg = ex
} }
runIn(10, "poll", [overwrite: true])
return msg
} }
def poll() { def poll() {
// GET THE LIST OF ACTIVITIES // GET THE LIST OF ACTIVITIES
if (state.HarmonyAccessToken) { if (state.HarmonyAccessToken) {
getActivityList() getActivityList()
def Params = [auth: state.HarmonyAccessToken] def tokenParam = [auth: state.HarmonyAccessToken]
def url = "https://home.myharmony.com/cloudapi/state?${toQueryString(Params)}" 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 { try {
httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> // 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 = [:] def map = [:]
response.data.hubs.each { ResponseValues.hubs.each {
if (it.value.message == "OK") { if (it.value.message == "OK") {
map["${it.key}"] = "${it.value.response.data.currentAvActivity},${it.value.response.data.activityStatus}" map["${it.key}"] = "${it.value.response.data.currentAvActivity},${it.value.response.data.activityStatus}"
def hub = getChildDevice("harmony-${it.key}") def hub = getChildDevice("harmony-${it.key}")
@@ -487,12 +522,12 @@ def poll() {
if (it.value.response.data.currentAvActivity == "-1") { if (it.value.response.data.currentAvActivity == "-1") {
hub.sendEvent(name: "currentActivity", value: "--", descriptionText: "There isn't any activity running", display: false) hub.sendEvent(name: "currentActivity", value: "--", descriptionText: "There isn't any activity running", display: false)
} else { } else {
def currentActivity = getActivityName(it.value.response.data.currentAvActivity,it.key) 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) hub.sendEvent(name: "currentActivity", value: currentActivity, descriptionText: "Current activity is ${currentActivity}", display: false)
} }
} }
} else { } else {
log.trace it.value.message log.trace "Logitech Harmony - error response: $it.value.message"
} }
} }
def activities = getChildDevices() def activities = getChildDevices()
@@ -516,36 +551,48 @@ def poll() {
} }
} }
} }
return "Poll completed $map - $state.hubs" } else {
} log.debug "Logitech Harmony - did not get json results from response body: $response.data"
} 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"
} }
} }
} }
def getActivityList() { def getActivityList() {
// GET ACTIVITY'S NAME
if (state.HarmonyAccessToken) { if (state.HarmonyAccessToken) {
def Params = [auth: state.HarmonyAccessToken] def tokenParam = [auth: state.HarmonyAccessToken]
def url = "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(Params)}" 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 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 { try {
httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> // json response already parsed into JSONElement object
response.data.hubs.each { 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}") def hub = getChildDevice("harmony-${it.key}")
if (hub) { if (hub) {
def hubname = getHubName("${it.key}") def hubname = getHubName("${it.key}")
def activities = [] def activities = []
def aux = it.value.response.data.activities.size() def aux = it.value.response?.data.activities.size()
if (aux >= 1) { if (aux >= 1) {
activities = it.value.response.data.activities.collect { activities = it.value.response.data.activities.collect {
[id: it.key, name: it.value['name'], type: it.value['type']] [id: it.key, name: it.value['name'], type: it.value['type']]
@@ -556,16 +603,10 @@ def getActivityList() {
hub.sendEvent(name: "activities", value: new groovy.json.JsonBuilder(activities).toString(), descriptionText: "Activities are ${activities.collect { it.name }?.join(', ')}", display: false) hub.sendEvent(name: "activities", value: new groovy.json.JsonBuilder(activities).toString(), descriptionText: "Activities are ${activities.collect { it.name }?.join(', ')}", display: false)
} }
} }
} } else {
} catch (groovyx.net.http.HttpResponseException e) { log.debug "Logitech Harmony - did not get json results from response body: $response.data"
log.trace e
} catch (java.net.SocketTimeoutException e) {
log.trace e
} catch(Exception e) {
log.trace e
} }
} }
return activity
} }
def getActivityName(activity,hubId) { def getActivityName(activity,hubId) {
@@ -746,7 +787,7 @@ def addSubscription() {
def attribute = data.attributeName def attribute = data.attributeName
def callbackUrl = data.callbackUrl def callbackUrl = data.callbackUrl
log.debug "addSubscription, params: ${params}, request: ${data}" log.debug "Logitech Harmony - addSubscription, params: ${params}, request: ${data}"
if (!attribute) { if (!attribute) {
render status: 400, data: '{"msg": "attributeName is required"}' render status: 400, data: '{"msg": "attributeName is required"}'
} else { } else {
@@ -808,6 +849,7 @@ def deviceHandler(evt) {
def deviceInfo = state[evt.deviceId] def deviceInfo = state[evt.deviceId]
if (state.harmonyHubs) { if (state.harmonyHubs) {
state.harmonyHubs.each { harmonyHub -> state.harmonyHubs.each { harmonyHub ->
log.trace "Logitech Harmony - Sending data to $harmonyHub.name"
sendToHarmony(evt, harmonyHub.callbackUrl) sendToHarmony(evt, harmonyHub.callbackUrl)
} }
} else if (deviceInfo) { } else if (deviceInfo) {

View File

@@ -26,6 +26,15 @@ definition(
) )
preferences { 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." }
}
if (location.channelName != 'samsungtv') {
section( "Set your location" ) { input "zipCode", "text", title: "Zip code" }
}
section ("In addition to push notifications, send text alerts to...") { section ("In addition to push notifications, send text alerts to...") {
input("recipients", "contact", title: "Send notifications to") { input("recipients", "contact", title: "Send notifications to") {
input "phone1", "phone", title: "Phone Number 1", required: false input "phone1", "phone", title: "Phone Number 1", required: false
@@ -33,10 +42,6 @@ preferences {
input "phone3", "phone", title: "Phone Number 3", required: false input "phone3", "phone", title: "Phone Number 3", required: false
} }
} }
section ("Zip code (optional, defaults to location coordinates)...") {
input "zipcode", "text", title: "Zip Code", required: false
}
} }
def installed() { def installed() {
@@ -61,7 +66,7 @@ def checkForSevereWeather() {
def alerts def alerts
if(locationIsDefined()) { if(locationIsDefined()) {
if(zipcodeIsValid()) { if(zipcodeIsValid()) {
alerts = getWeatherFeature("alerts", zipcode)?.alerts alerts = getWeatherFeature("alerts", zipCode)?.alerts
} else { } else {
log.warn "Severe Weather Alert: Invalid zipcode entered, defaulting to location's zipcode" log.warn "Severe Weather Alert: Invalid zipcode entered, defaulting to location's zipcode"
alerts = getWeatherFeature("alerts")?.alerts alerts = getWeatherFeature("alerts")?.alerts