Compare commits

..

15 Commits

Author SHA1 Message Date
Vinay Rao
dd7c6b90d5 Merge pull request #1241 from SmartThingsCommunity/staging
Rolling up staging to prod for deploy
2016-09-13 12:28:13 -07:00
Vinay Rao
f5bd580c9e Merge pull request #1240 from juano2310/staging
SSVD-2733 -  Typo fix
2016-09-12 18:00:11 -07:00
juano2310
d5329dbde3 SSVD-2733 - Typo fix 2016-09-12 20:58:36 -04:00
Vinay Rao
48818cfb06 Merge pull request #1239 from larsfinander/CHF-348_philips_hue_staging
CHF-348 Philips Hue: Change offline time to 16 min
2016-09-12 16:23:41 -07:00
Lars Finander
079919260b CHF-348 Philips Hue: Change offline time to 16 min 2016-09-12 17:19:14 -06:00
Vinay Rao
570922e2ac Merge pull request #1238 from jackchi/healthcheck-5min-reporting
[CHF-348] Set 10 VD required devices to report in at 5 minutes
2016-09-12 15:52:29 -07:00
Lars Finander
53ed9b4d2b Merge pull request #1237 from larsfinander/add_hue_offline_events_staging
Add offline events to Philips Hue
2016-09-12 16:49:57 -06:00
jackchi
7149a81c85 [CHF-348] Set 10 VD required devices to report in at 5 minutes 2016-09-12 14:51:57 -07:00
Jack Chi
fb0caa6446 Merge pull request #1233 from jackchi/healthcheck-removehack
[CHF-201] Removing DTH workaround now that all events go to Kafka
2016-09-12 08:24:10 -07:00
Lars Finander
3d05d42cb8 Add offline events to Philips Hue
-Handles case when bridge goes offline
-Handles case when indiviudual lights go offline
2016-09-11 22:56:24 -06:00
Vinay Rao
0f70362e0a Merge pull request #1234 from larsfinander/DVCSMP-2024_OSRAM_Hue_incorrect_staging
DVCSMP-2024 OSRAM: Hue is incorrectly set to be 0-360
2016-09-09 16:49:31 -07:00
Lars Finander
bc817f8530 DVCSMP-2024 OSRAM: Hue is incorrectly set to be 0-360
-Hue changed to handle 0-100 according to SmartThings API
2016-09-09 17:41:46 -06:00
jackchi
01b8399893 [CHF-201] Removing DTH workaround now that all events go to Kafka 2016-09-09 14:58:03 -07:00
Lars Finander
60c2006bfc Merge pull request #1230 from larsfinander/SSVD-2798_Philips_Hue_discovery_issue
SSVD-2798 Philips Hue: Light discovery UI issue
2016-09-09 11:01:40 -06:00
Lars Finander
1e4f1223e7 SSVD-2798 Philips Hue: Light discovery UI issue
-Keep checked lights in "lights to add list" when page refreshes
2016-09-09 10:50:23 -06:00
44 changed files with 268 additions and 555 deletions

View File

@@ -15,7 +15,6 @@
*/
metadata {
definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") {
capability "Sensor"
capability "Relative Humidity Measurement"
capability "Temperature Measurement"

View File

@@ -15,7 +15,6 @@
*/
metadata {
definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") {
capability "Sensor"
capability "Relative Humidity Measurement"
capability "Temperature Measurement"

View File

@@ -15,7 +15,6 @@
*/
metadata {
definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") {
capability "Sensor"
capability "Relative Humidity Measurement"
capability "Temperature Measurement"
}

View File

@@ -15,8 +15,6 @@
*/
metadata {
definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") {
capability "Sensor"
attribute "rain", "number"
attribute "rainSumHour", "number"
attribute "rainSumDay", "number"

View File

@@ -67,12 +67,6 @@ def parse(String description) {
def resultMap = zigbee.getEvent(description)
if (resultMap) {
sendEvent(resultMap)
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
}
else {
log.debug "DID NOT PARSE MESSAGE for description : $description"
@@ -96,15 +90,7 @@ def setLevel(value) {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.levelRefresh()
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.levelRefresh()
}
def refresh() {
@@ -117,6 +103,8 @@ def poll() {
def configure() {
log.debug "Configuring Reporting and Bindings."
sendEvent(name: "checkInterval", value: 1200, displayed: false, data: [protocol: "zigbee"])
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
// minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
}

View File

@@ -67,6 +67,6 @@ def refresh() {
void poll() {
log.debug "Executing 'poll' using parent SmartApp"
parent.poll()
parent.pollChild()
}

View File

@@ -133,7 +133,7 @@ def refresh() {
void poll() {
log.debug "Executing 'poll' using parent SmartApp"
parent.poll()
parent.pollChild()
}
def generateEvent(Map results) {

View File

@@ -57,7 +57,7 @@ metadata {
}
void installed() {
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes

View File

@@ -66,7 +66,7 @@ metadata {
}
void installed() {
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes

View File

@@ -50,7 +50,7 @@ metadata {
}
void installed() {
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes

View File

@@ -55,7 +55,7 @@ metadata {
}
void installed() {
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes

View File

@@ -91,7 +91,7 @@ def parse(String description) {
if (descMap.cluster == "0300") {
if(descMap.attrId == "0000"){ //Hue Attribute
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
log.debug "Hue value returned is $hueValue"
sendEvent(name: "hue", value: hueValue, displayed:false)
}
@@ -203,7 +203,7 @@ def setLevel(value) {
//input Hue Integer values; returns color name for saturation 100%
private getColorName(hueValue){
if(hueValue>360 || hueValue<0)
if(hueValue>100 || hueValue<0)
return
hueValue = Math.round(hueValue / 100 * 360)

View File

@@ -1,4 +1,4 @@
/*
/*
Osram Flex RGBW Light Strip
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
@@ -8,7 +8,7 @@
metadata {
definition (name: "OSRAM LIGHTIFY LED Flexible Strip RGBW", namespace: "smartthings", author: "SmartThings") {
capability "Color Temperature"
capability "Actuator"
capability "Switch"
@@ -18,7 +18,7 @@ metadata {
capability "Refresh"
capability "Sensor"
capability "Color Control"
attribute "colorName", "string"
command "setAdjustedColor"
@@ -49,7 +49,7 @@ metadata {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
@@ -118,7 +118,7 @@ def parse(String description) {
}
}
else if(descMap.attrId == "0000"){ //Hue Attribute
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
log.debug "Hue value returned is $hueValue"
sendEvent(name: "hue", value: hueValue, displayed:false)
}
@@ -274,7 +274,7 @@ private getGenericName(value){
//input Hue Integer values; returns color name for saturation 100%
private getColorName(hueValue){
if(hueValue>360 || hueValue<0)
if(hueValue>100 || hueValue<0)
return
hueValue = Math.round(hueValue / 100 * 360)
@@ -449,7 +449,7 @@ def setColor(value){
def level = hex(value.level * 255 / 100)
cmd << zigbeeSetLevel(level)
}
if (value.switch == "off") {
cmd << "delay 150"
cmd << off()

View File

@@ -101,12 +101,6 @@ def parse(String description) {
else {
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
}
}
else {
@@ -126,15 +120,7 @@ def on() {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.onOffRefresh()
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.onOffRefresh()
}
def refresh() {
@@ -142,8 +128,10 @@ def refresh() {
}
def configure() {
sendEvent(name: "checkInterval", value: 1200, displayed: false, data: [protocol: "zigbee"])
zigbee.onOffConfig() + powerConfig() + refresh()
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
zigbee.onOffConfig(0, 300) + powerConfig() + refresh()
}
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)

View File

@@ -101,13 +101,6 @@ def parse(String description) {
map = parseIasMessage(description)
}
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
log.debug "Parse returned $map"
def result = map ? createEvent(map) : null
@@ -285,15 +278,7 @@ private Map getMoistureResult(value) {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh() {
@@ -307,23 +292,19 @@ def refresh() {
}
def configure() {
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
log.debug "Configuring Reporting, IAS CIE, and Bindings."
def configCmds = [
def enrollCmds = [
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
]
return configCmds + refresh() // send refresh cmds as part of config
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
// battery minReport 30 seconds, maxReportTime 6 hrs by default
return enrollCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
}
def enrollResponse() {

View File

@@ -105,13 +105,6 @@ def parse(String description) {
map = parseIasMessage(description)
}
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
log.debug "Parse returned $map"
def result = map ? createEvent(map) : null
@@ -296,15 +289,7 @@ private Map getMotionResult(value) {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh() {
@@ -318,24 +303,19 @@ def refresh() {
}
def configure() {
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
log.debug "Configuring Reporting, IAS CIE, and Bindings."
def configCmds = [
def enrollCmds = [
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 0x402 0 0x29 300 3600 {6400}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
]
return configCmds + refresh() // send refresh cmds as part of config
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
// battery minReport 30 seconds, maxReportTime 6 hrs by default
return enrollCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
}
def enrollResponse() {

View File

@@ -127,13 +127,6 @@ def parse(String description) {
map = parseIasMessage(description)
}
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
def result = map ? createEvent(map) : null
if (description?.startsWith('enroll request')) {
@@ -378,15 +371,7 @@ private getAccelerationResult(numValue) {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh() {
@@ -416,13 +401,16 @@ def refresh() {
}
def configure() {
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
log.debug "Configuring Reporting"
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
// battery minReport 30 seconds, maxReportTime 6 hrs by default
def configCmds = enrollResponse() +
zigbee.batteryConfig() +
zigbee.temperatureConfig() +
zigbee.temperatureConfig(30, 300) +
zigbee.configureReporting(0xFC02, 0x0010, 0x18, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
zigbee.configureReporting(0xFC02, 0x0012, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
zigbee.configureReporting(0xFC02, 0x0013, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +

View File

@@ -92,13 +92,6 @@ def parse(String description) {
map = parseIasMessage(description)
}
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
log.debug "Parse returned $map"
def result = map ? createEvent(map) : null
@@ -248,15 +241,7 @@ private Map getContactResult(value) {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh() {
@@ -270,23 +255,19 @@ def refresh() {
}
def configure() {
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
log.debug "Configuring Reporting, IAS CIE, and Bindings."
def configCmds = [
def enrollCmds = [
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
]
return configCmds + refresh() // send refresh cmds as part of config
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
// battery minReport 30 seconds, maxReportTime 6 hrs by default
return enrollCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
}
def enrollResponse() {

View File

@@ -83,13 +83,6 @@ def parse(String description) {
map = parseCustomMessage(description)
}
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
log.debug "Parse returned $map"
return map ? createEvent(map) : null
}
@@ -253,14 +246,7 @@ private Map getHumidityResult(value) {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh()
@@ -278,23 +264,19 @@ def refresh()
}
def configure() {
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
log.debug "Configuring Reporting and Bindings."
def configCmds = [
"zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zdo bind 0x${device.deviceNetworkId} 1 1 0x402 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
def humidityConfigCmds = [
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}",
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
]
return configCmds + refresh() // send refresh cmds as part of config
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
// battery minReport 30 seconds, maxReportTime 6 hrs by default
return humidityConfigCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config
}
private hex(value) {

View File

@@ -54,12 +54,6 @@ def parse(String description) {
def event = zigbee.getEvent(description)
if (event) {
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
if (event.name=="level" && event.value==0) {}
else {
sendEvent(event)
@@ -86,15 +80,7 @@ def setLevel(value) {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.onOffRefresh()
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.onOffRefresh()
}
def refresh() {
@@ -103,7 +89,8 @@ def refresh() {
def configure() {
log.debug "Configuring Reporting and Bindings."
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
}

View File

@@ -85,12 +85,6 @@ def parse(String description) {
def event = zigbee.getEvent(description)
if (event) {
log.debug event
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
if (event.name=="level" && event.value==0) {}
else {
if (event.name=="colorTemperature") {
@@ -105,7 +99,7 @@ def parse(String description) {
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 360)
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
}
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
@@ -130,15 +124,7 @@ def off() {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.onOffRefresh()
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.onOffRefresh()
}
def refresh() {
@@ -147,9 +133,10 @@ def refresh() {
def configure() {
log.debug "Configuring Reporting and Bindings."
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, 0x20, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, 0x20, 1, 3600, 0x01) + zigbee.readAttribute(0x0006, 0x00) + zigbee.readAttribute(0x0008, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, 0x20, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, 0x20, 1, 3600, 0x01) + zigbee.readAttribute(0x0006, 0x00) + zigbee.readAttribute(0x0008, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
}
def setColorTemperature(value) {

View File

@@ -74,12 +74,6 @@ def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastActivity == null){
state.lastActivity = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
state.lastActivity = now()
if (event.name=="level" && event.value==0) {}
else {
if (event.name=="colorTemperature") {
@@ -110,15 +104,7 @@ def setLevel(value) {
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastActivity=${state.lastActivity}"
state.lastActivity = null
return zigbee.onOffRefresh()
} else {
log.info "ping, alive=yes, lastActivity=${state.lastActivity}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastActivity, description: "Last Activity is on ${new Date((long)state.lastActivity)}", displayed: false, isStateChange: true)
}
return zigbee.onOffRefresh()
}
def refresh() {
@@ -127,9 +113,10 @@ def refresh() {
def configure() {
log.debug "Configuring Reporting and Bindings."
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
// Device-Watch allows 3 check-in misses from device. 300 seconds x 3 = 15min
sendEvent(name: "checkInterval", value: 900, displayed: false, data: [protocol: "zigbee"])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
}
def setColorTemperature(value) {

View File

@@ -65,16 +65,7 @@ void updateSwitch() {
private void updateAll(devices) {
def command = request.JSON?.command
if (command) {
switch(command) {
case "on":
devices.on()
break
case "off":
devices.off()
break
default:
httpError(403, "Access denied. This command is not supported by current capability.")
}
devices."$command"()
}
}
@@ -86,16 +77,7 @@ private void update(devices) {
if (!device) {
httpError(404, "Device not found")
} else {
switch(command) {
case "on":
device.on()
break
case "off":
device.off()
break
default:
httpError(403, "Access denied. This command is not supported by current capability.")
}
device."$command"()
}
}
}

View File

@@ -58,7 +58,7 @@ def authPage() {
if (canInstallLabs()) {
def redirectUrl = getBuildRedirectUrl()
// log.debug "Redirect url = ${redirectUrl}"
log.debug "Redirect url = ${redirectUrl}"
if (state.authToken) {
description = "Tap 'Next' to proceed"
@@ -113,13 +113,13 @@ def oauthInitUrl() {
scope: "read_station"
]
// log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
redirect (location: getVendorAuthPath() + toQueryString(oauthParams))
}
def callback() {
// log.debug "callback()>> params: $params, params.code ${params.code}"
log.debug "callback()>> params: $params, params.code ${params.code}"
def code = params.code
def oauthState = params.state
@@ -135,7 +135,7 @@ def callback() {
scope: "read_station"
]
// log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
def tokenUrl = getVendorTokenPath()
def params = [
@@ -144,7 +144,7 @@ def callback() {
body: tokenParams
]
// log.debug "PARAMS: ${params}"
log.debug "PARAMS: ${params}"
httpPost(params) { resp ->
@@ -156,7 +156,7 @@ def callback() {
state.refreshToken = data.refresh_token
state.authToken = data.access_token
state.tokenExpires = now() + (data.expires_in * 1000)
// log.debug "swapped token: $resp.data"
log.debug "swapped token: $resp.data"
}
}
@@ -292,7 +292,7 @@ def refreshToken() {
response.data.each {key, value ->
def data = slurper.parseText(key);
// log.debug "Data: $data"
log.debug "Data: $data"
state.refreshToken = data.refresh_token
state.accessToken = data.access_token

View File

@@ -114,16 +114,13 @@ def beaconHandler(evt) {
if (allOk) {
def data = new groovy.json.JsonSlurper().parseText(evt.data)
// removed logging of device names. can be added back for debugging
//log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
def beaconName = getBeaconName(evt)
// removed logging of device names. can be added back for debugging
//log.debug "<beacon-control> beaconName: $beaconName"
log.debug "<beacon-control> beaconName: $beaconName"
def phoneName = getPhoneName(data)
// removed logging of device names. can be added back for debugging
//log.debug "<beacon-control> phoneName: $phoneName"
log.debug "<beacon-control> phoneName: $phoneName"
if (phoneName != null) {
def action = data.presence == "1" ? "arrived" : "left"
def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName"

View File

@@ -49,15 +49,13 @@ preferences {
def installed() {
log.debug "Installed with settings: ${settings}"
// commented out log statement because presence sensor label could contain user's name
//log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
subscribe(people, "presence", presence)
}
def updated() {
log.debug "Updated with settings: ${settings}"
// commented out log statement because presence sensor label could contain user's name
//log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
unsubscribe()
subscribe(people, "presence", presence)
}

View File

@@ -20,8 +20,6 @@
* JLH - 02-15-2014 - Fuller use of ecobee API
* 10-28-2015 DVCSMP-604 - accessory sensor, DVCSMP-1174, DVCSMP-1111 - not respond to routines
*/
include 'asynchttp_v1'
definition(
name: "Ecobee (Connect)",
namespace: "smartthings",
@@ -246,7 +244,9 @@ def getEcobeeThermostats() {
uri: apiEndpoint,
path: "/1/thermostat",
headers: ["Content-Type": "text/json", "Authorization": "Bearer ${atomicState.authToken}"],
query: [json: toJson(bodyParams)]
// TODO - the query string below is not consistent with the Ecobee docs:
// https://www.ecobee.com/home/developer/api/documentation/v1/operations/get-thermostats.shtml
query: [format: 'json', body: toJson(bodyParams)]
]
def stats = [:]
@@ -265,8 +265,9 @@ def getEcobeeThermostats() {
} catch (groovyx.net.http.HttpResponseException e) {
log.trace "Exception polling children: " + e.response.data.status
if (e.response.data.status.code == 14) {
atomicState.action = "getEcobeeThermostats"
log.debug "Refreshing your auth_token!"
refreshAuthToken([async: false, nextAction: "getEcobeeThermostats"])
refreshAuthToken()
}
}
atomicState.thermostats = stats
@@ -357,22 +358,16 @@ def initialize() {
atomicState.timeSendPush = null
atomicState.reAttempt = 0
initialPoll() //first time polling data data from thermostat
pollHandler() //first time polling data data from thermostat
//automatically update devices status every 5 mins
runEvery5Minutes("poll")
}
/**
* Polls the child devices (synchronously).
* This is used during app install/update, and is synchronous
* to maintain current behavior that will cause install/update to fail
* if polling fails.
*/
def initialPoll() {
log.debug "initialPoll()"
pollChildrenSync() // Hit the ecobee API for update on all thermostats
def pollHandler() {
log.debug "pollHandler()"
pollChildren(null) // Hit the ecobee API for update on all thermostats
atomicState.thermostats.each {stat ->
def dni = stat.key
@@ -385,101 +380,10 @@ def initialPoll() {
}
}
/**
* Polls Ecobee (asynchronously) for updated device state data.
* Called from within this Connect SmartApp as well as the child
* devices.
*/
def poll() {
log.debug "polling asynchronously"
asynchttp_v1.get('asyncPollResponseHandler', getPollParams())
}
/**
* Makes a (synchronous) request to the Ecobee API to get the data for the thermostats.
* This request is made synchronously here because it is called as part of the
* install/updated lifecycle, and changing it to asynchronous during the install/update
* lifecycle may change the behavior if there is an error in polling.
*
* If further analysis shows that polling can be done asynchronously during
* install/update without any adverse consequences, this should then be made
* asynchronous just as the scheduled polling is.
*/
def pollChildrenSync() {
def pollChildren(child = null) {
def thermostatIdsString = getChildDeviceIdsString()
log.debug "polling children: $thermostatIdsString"
def params = getPollParams()
params.query << ["Content-Type": "application/json"]
def result = false
log.debug "making synchronous poll request"
try{
httpGet(params) { resp ->
if(resp.status == 200) {
atomicState.remoteSensors = resp.data.thermostatList.remoteSensors
updateSensorData()
storeThermostatData(resp.data.thermostatList)
result = true
log.debug "updated ${atomicState.thermostats?.size()} stats: ${atomicState.thermostats}"
}
}
} catch (groovyx.net.http.HttpResponseException e) {
log.trace "Exception polling children: " + e.response.data.status
if (e.response.data.status.code == 14) {
log.debug "Refreshing your auth_token!"
refreshAuthToken([async: false, nextAction: "pollChildrenSync"])
}
}
return result
}
/**
* Response handler for asynchronous request to get thermostat data.
* Given a successful response, updates the sensor data, stores the thermostat
* data, and generates child device events.
*
* If the access token has expired, will issue a request to refresh the token
* (and pending successful token refresh, the poll request will be made again).
*/
def asyncPollResponseHandler(response, data) {
log.trace "async poll response handler"
if (!response.hasError()) {
if (response.status == 200) {
def json
try {
json = response.getJson()
} catch (e) {
log.error ("error parsing JSON", e)
}
if (json) {
atomicState.remoteSensors = json.thermostatList.remoteSensors
updateSensorData()
storeThermostatData(json.thermostatList)
generateChildThermostatEvent()
}
} else {
log.warn "Response returned non-200 response. Status: ${response.status}, data: ${response.getData()}"
}
} else {
log.trace "Exception polling children: ${response.getErrorMessage()}"
def errorJson
try {
errorJson = response.getErrorJson()
} catch (e) {
log.error("Unable to parse error json response", e)
}
if (errorJson?.status?.code == 14) {
log.debug "Refreshing your auth_token!"
refreshAuthToken([async: true, nextAction: "poll"])
} else {
log.warn "Error polling children that is not due to an expired token. Response: ${response.getErrorData()}"
}
}
}
private getPollParams() {
def thermostatIdsString = getChildDeviceIdsString()
def requestBody = [
selection: [
selectionType: "thermostats",
@@ -490,32 +394,66 @@ private getPollParams() {
includeSensors: true
]
]
return [
def result = false
def pollParams = [
uri: apiEndpoint,
path: "/1/thermostat",
headers: ["Authorization": "Bearer ${atomicState.authToken}"],
query: [json: toJson(requestBody)]
headers: ["Content-Type": "text/json", "Authorization": "Bearer ${atomicState.authToken}"],
// TODO - the query string below is not consistent with the Ecobee docs:
// https://www.ecobee.com/home/developer/api/documentation/v1/operations/get-thermostats.shtml
query: [format: 'json', body: toJson(requestBody)]
]
try{
httpGet(pollParams) { resp ->
if(resp.status == 200) {
log.debug "poll results returned resp.data ${resp.data}"
atomicState.remoteSensors = resp.data.thermostatList.remoteSensors
updateSensorData()
storeThermostatData(resp.data.thermostatList)
result = true
log.debug "updated ${atomicState.thermostats?.size()} stats: ${atomicState.thermostats}"
}
}
} catch (groovyx.net.http.HttpResponseException e) {
log.trace "Exception polling children: " + e.response.data.status
if (e.response.data.status.code == 14) {
atomicState.action = "pollChildren"
log.debug "Refreshing your auth_token!"
refreshAuthToken()
}
}
return result
}
/**
* Calls each child thermostat device to generate an event with the thermostat
* data.
*/
def generateChildThermostatEvent() {
log.trace("generateChildThermostatEvent")
getChildDevices().each { child ->
if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")){
if(atomicState.thermostats[child.device.deviceNetworkId] != null) {
def tData = atomicState.thermostats[child.device.deviceNetworkId]
log.debug "calling child.generateEvent($tData.data)"
child.generateEvent(tData.data) //parse received message from parent
} else if(atomicState.thermostats[child.device.deviceNetworkId] == null) {
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId}"
return null
}
}
}
// Poll Child is invoked from the Child Device itself as part of the Poll Capability
def pollChild() {
def devices = getChildDevices()
if (pollChildren()) {
devices.each { child ->
if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")) {
if(atomicState.thermostats[child.device.deviceNetworkId] != null) {
def tData = atomicState.thermostats[child.device.deviceNetworkId]
log.info "pollChild(child)>> data for ${child.device.deviceNetworkId} : ${tData.data}"
child.generateEvent(tData.data) //parse received message from parent
} else if(atomicState.thermostats[child.device.deviceNetworkId] == null) {
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId}"
return null
}
}
}
} else {
log.info "ERROR: pollChildren()"
return null
}
}
void poll() {
pollChild()
}
def availableModes(child) {
@@ -615,104 +553,47 @@ def toQueryString(Map m) {
return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&")
}
/**
* Uses the refresh token to get a new access token, then executes the nextAction.
* @param options - a map of options. valid options are async: true/false, which
* specifies if the refresh token request will be done asynchronously or not (default is false)
* nextAction: "nameOfMethod" specifies what method to execute after
* the token is refreshed (not required).
* (note: using a map as the parameter because we need to call it from a schedueled
* execution and we can only pass a data map to scheduled executions)
*/
private void refreshAuthToken(options) {
if(!atomicState.refreshToken) {
log.warn "Cannot not refresh OAuth token since there is no refreshToken stored"
} else {
def refreshParams = [
uri : apiEndpoint,
path : "/token",
query : [grant_type: 'refresh_token', code: "${atomicState.refreshToken}", client_id: smartThingsClientId],
]
if (options.async) {
refreshAuthTokenAsync(refreshParams, options.nextAction)
} else {
refreshAuthTokenSync(refreshParams, options.nextAction)
}
}
}
private refreshAuthToken() {
log.debug "refreshing auth token"
private void refreshAuthTokenSync(params, nextAction = null) {
try {
httpPost(refreshParams) { resp ->
if(resp.status == 200) {
log.debug "Token refreshed...calling saved RestAction now!"
debugEvent("Token refreshed ... calling saved RestAction now!")
saveTokenAndResumeAction(resp.data, nextAction)
if(!atomicState.refreshToken) {
log.warn "Can not refresh OAuth token since there is no refreshToken stored"
} else {
def refreshParams = [
method: 'POST',
uri : apiEndpoint,
path : "/token",
query : [grant_type: 'refresh_token', code: "${atomicState.refreshToken}", client_id: smartThingsClientId],
]
def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials."
//changed to httpPost
try {
def jsonMap
httpPost(refreshParams) { resp ->
if(resp.status == 200) {
log.debug "Token refreshed...calling saved RestAction now!"
debugEvent("Token refreshed ... calling saved RestAction now!")
saveTokenAndResumeAction(resp.data)
}
}
}
} catch (groovyx.net.http.HttpResponseException e) {
log.error "refreshAuthToken() >> Error: e.statusCode ${e.statusCode}"
reauthTokenErrorHandler(e.statusCode)
}
}
private void refreshAuthTokenAsync(refreshParams, nextAction = null) {
log.debug "making asynchronous refresh request"
asynchttp_v1.post('refreshTokenResponseHandler', refreshParams, [nextAction: nextAction])
}
/**
* The response handler for the request to refresh the authorization handler.
* Stores the new authorization token and refresh token, and executes any action
* (method) that failed due to the authorization token expiring.
*/
private void refreshTokenResponseHandler(response, data) {
if (!response.hasError()) {
if (response.status == 200) {
def json
try {
json = response.getJson()
} catch (e) {
log.error "error parsing json from response data: $response.data"
}
if (json) {
log.debug "asnyc refreshTokenHandler: Token refreshed...calling saved RestAction now!"
debugEvent("async Token refreshed ... calling saved RestAction now!")
saveTokenAndResumeAction(json, data.nextAction)
} else {
log.warn "successfully parsed json but result is empty or null"
}
} else {
log.debug "Non 200 response returned. Response code: ${response.code}, data: ${response.getData()}"
}
} else {
log.debug "async refreshTokenHandler: RESPONSE ERROR: ${response.getErrorJson()}"
reauthTokenErrorHandler(response.getErrorJson().code)
}
}
/**
* Retries refreshing the authorization token. Will attempt to get the refresh
* token later, in case there were errors retrieving it.
* Will retry a fixed number of times before sending a push notification to the
* user instructing them to reauthenticate
*/
private void reauthTokenErrorHandler(responseCode) {
def retryInterval = 300 // in seconds
def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials."
// might get non-401 error from exceeding 20 second app limit, connectivity issues, etc.
if (responseCode != 401) {
runIn(retryInterval, "refreshAuthToken", [async: true])
} else if (responseCode == 401) { // unauthorized
atomicState.reAttempt = atomicState.reAttempt + 1
log.warn "reAttempt refreshAuthToken to try = ${atomicState.reAttempt}"
if (atomicState.reAttempt <= 3) {
runIn(retryInterval, "refreshAuthToken", [async: true])
} else {
sendPushAndFeeds(notificationMessage)
atomicState.reAttempt = 0
}
}
} catch (groovyx.net.http.HttpResponseException e) {
log.error "refreshAuthToken() >> Error: e.statusCode ${e.statusCode}"
def reAttemptPeriod = 300 // in sec
if (e.statusCode != 401) { // this issue might comes from exceed 20sec app execution, connectivity issue etc.
runIn(reAttemptPeriod, "refreshAuthToken")
} else if (e.statusCode == 401) { // unauthorized
atomicState.reAttempt = atomicState.reAttempt + 1
log.warn "reAttempt refreshAuthToken to try = ${atomicState.reAttempt}"
if (atomicState.reAttempt <= 3) {
runIn(reAttemptPeriod, "refreshAuthToken")
} else {
sendPushAndFeeds(notificationMessage)
atomicState.reAttempt = 0
}
}
}
}
}
/**
@@ -722,20 +603,20 @@ private void reauthTokenErrorHandler(responseCode) {
*
* @param json - an object representing the parsed JSON response from Ecobee
*/
private void saveTokenAndResumeAction(json, String nextAction) {
def debugMessage = "token response, scope: ${json?.scope}, expires_in: ${json?.expires_in}, token_type: ${json?.token_type}"
log.debug "debugMessage"
private void saveTokenAndResumeAction(json) {
log.debug "token response json: $json"
if (json) {
debugEvent(debugMessage)
debugEvent("Response = $json")
atomicState.refreshToken = json?.refresh_token
atomicState.authToken = json?.access_token
if (nextAction) {
log.debug "got refresh token, will execute next action (passed in!): $nextAction"
"$nextAction"()
if (atomicState.action) {
log.debug "got refresh token, executing next action: ${atomicState.action}"
"${atomicState.action}"()
}
} else {
log.warn "did not get response body from refresh token response"
}
atomicState.action = ""
}
/**
@@ -875,6 +756,7 @@ private boolean sendCommandToEcobee(Map bodyParams) {
try{
httpPost(cmdParams) { resp ->
if(resp.status == 200) {
log.debug "updated ${resp.data}"
def returnStatus = resp.data.status.code
if (returnStatus == 0) {
log.debug "Successful call to ecobee API."
@@ -889,10 +771,11 @@ private boolean sendCommandToEcobee(Map bodyParams) {
log.trace "Exception Sending Json: " + e.response.data.status
debugEvent ("sent Json & got http status ${e.statusCode} - ${e.response.data.status.code}")
if (e.response.data.status.code == 14) {
// TODO - figure out why we're setting the next action to be poll
// TODO - figure out why we're setting the next action to be pollChildren
// after refreshing auth token. Is it to keep UI in sync, or just copy/paste error?
atomicState.action = "pollChildren"
log.debug "Refreshing your auth_token!"
refreshAuthToken([async: true, nextAction: "poll"])
refreshAuthToken()
} else {
debugEvent("Authentication error, invalid authentication method, lack of credentials, etc.")
log.error "Authentication error, invalid authentication method, lack of credentials, etc."

View File

@@ -64,7 +64,7 @@ def meterHandler(evt) {
def lastValue = atomicState.lastValue as double
atomicState.lastValue = meterValue
def dUnit ? evt.unit : "Watts"
def dUnit = evt.unit ?: "Watts"
def aboveThresholdValue = aboveThreshold as int
if (meterValue > aboveThresholdValue) {

View File

@@ -54,10 +54,10 @@ def waterWetHandler(evt) {
def alreadySentSms = recentEvents.count { it.value && it.value == "wet" } > 1
if (alreadySentSms) {
log.debug "SMS already sent within the last $deltaSeconds seconds"
log.debug "SMS already sent to $phone within the last $deltaSeconds seconds"
} else {
def msg = "${alarm.displayName} is wet!"
log.debug "$alarm is wet, texting phone number"
log.debug "$alarm is wet, texting $phone"
if (location.contactBookEnabled) {
sendNotificationToContacts(msg, recipients)

View File

@@ -90,7 +90,7 @@ def takeAction(){
}
def sendTextMessage() {
log.debug "$multisensor was open too long, texting phone"
log.debug "$multisensor was open too long, texting $phone"
updateSmsHistory()
def openMinutes = maxOpenTime * (state.smsHistory?.size() ?: 1)

View File

@@ -47,13 +47,13 @@ preferences {
def installed() {
log.debug "Installed with settings: ${settings}"
// log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
subscribe(people, "presence", presence)
}
def updated() {
log.debug "Updated with settings: ${settings}"
// log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}"
unsubscribe()
subscribe(people, "presence", presence)
}
@@ -71,10 +71,11 @@ def presence(evt)
def person = getPerson(evt)
def recentNotPresent = person.statesSince("presence", t0).find{it.value == "not present"}
if (recentNotPresent) {
log.debug "skipping notification of arrival of Person because last departure was only ${now() - recentNotPresent.date.time} msec ago"
log.debug "skipping notification of arrival of ${person.displayName} because last departure was only ${now() - recentNotPresent.date.time} msec ago"
}
else {
def message = "${person.displayName} arrived at home, changing mode to '${newMode}'"
log.info message
send(message)
setLocationMode(newMode)
}
@@ -105,4 +106,6 @@ private send(msg) {
sendSms(phone, msg)
}
}
log.debug msg
}

View File

@@ -57,11 +57,12 @@ def scheduleCheck()
def message = message1 ?: "SmartThings - Habit Helper Reminder!"
if (location.contactBookEnabled) {
log.debug "Texting reminder to contacts:${recipients?.size()}"
log.debug "Texting reminder: ($message) to contacts:${recipients?.size()}"
sendNotificationToContacts(message, recipients)
}
else {
log.debug "Texting reminder"
log.debug "Texting reminder: ($message) to $phone1"
sendSms(phone1, message)
}
}

View File

@@ -170,7 +170,7 @@ def bulbDiscovery() {
return dynamicPage(name:"bulbDiscovery", title:"Light Discovery Started!", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) {
section("Please wait while we discover your Hue Lights. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") {
input "selectedBulbs", "enum", required:false, title:"Select Hue Lights to add (${numFound} found)", multiple:true, options:newLights
input "selectedBulbs", "enum", required:false, title:"Select Hue Lights to add (${numFound} found)", multiple:true, submitOnChange: true, options:newLights
paragraph title: "Previously added Hue Lights (${existingLights.size()} added)", existingLightsDescription
}
section {
@@ -724,13 +724,13 @@ private void updateBridgeStatus(childDevice) {
}
/**
* Check if all Hue bridges have been heard from in the last 16 minutes, if not an Offline event will be sent
* for the bridge. Also, set ID number on bridge if not done previously.
* Check if all Hue bridges have been heard from in the last 11 minutes, if not an Offline event will be sent
* for the bridge and all connected lights. Also, set ID number on bridge if not done previously.
*/
private void checkBridgeStatus() {
def bridges = getHueBridges()
// Check if each bridge has been heard from within the last 16 minutes (3 poll intervals times 5 minutes plus buffer)
def time = now() - (1000 * 60 * 30)
// Check if each bridge has been heard from within the last 11 minutes (2 poll intervals times 5 minutes plus buffer)
def time = now() - (1000 * 60 * 11)
bridges.each {
def d = getChildDevice(it.value.mac)
if(d) {
@@ -740,16 +740,21 @@ private void checkBridgeStatus() {
d.sendEvent(name: "idNumber", value: it.value.idNumber)
}
if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
log.warn "Bridge $it.key is Offline"
d.sendEvent(name: "status", value: "Offline")
// set all lights to offline since bridge is not reachable
state.bulbs?.each {it.value.online = false}
} else {
if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
log.warn "Bridge $it.key is Offline"
d.sendEvent(name: "status", value: "Offline")
state.bulbs?.each {
it.value.online = false
}
getChildDevices().each {
it.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", isStateChange: true, displayed: false)
}
} else {
d.sendEvent(name: "status", value: "Online")//setOnline(false)
}
}
}
}
}
}
}
def isValidSource(macAddress) {
@@ -955,6 +960,7 @@ private handlePoll(body) {
} else {
state.bulbs[bulb.key]?.online = false
log.warn "$device is not reachable by Hue bridge"
device.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", displayed: false, isStateChange: true)
}
}
}

View File

@@ -53,14 +53,14 @@ def accelerationActiveHandler(evt) {
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
if (alreadySentSms) {
log.debug "SMS already sent within the last $deltaSeconds seconds"
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
} else {
if (location.contactBookEnabled) {
log.debug "accelerationSensor has moved, texting contacts: ${recipients?.size()}"
log.debug "$accelerationSensor has moved, texting contacts: ${recipients?.size()}"
sendNotificationToContacts("${accelerationSensor.label ?: accelerationSensor.name} moved", recipients)
}
else {
log.debug "accelerationSensor has moved, sending text message"
log.debug "$accelerationSensor has moved, texting $phone1"
sendSms(phone1, "${accelerationSensor.label ?: accelerationSensor.name} moved")
}
}

View File

@@ -69,10 +69,10 @@ def temperatureHandler(evt) {
def alreadySentSms = recentEvents.count { it.doubleValue >= tooHot } > 1
if (alreadySentSms) {
log.debug "SMS already sent within the last $deltaMinutes minutes"
log.debug "SMS already sent to $phone1 within the last $deltaMinutes minutes"
// TODO: Send "Temperature back to normal" SMS, turn switch off
} else {
log.debug "Temperature rose above $tooHot: sending SMS and activating $mySwitch"
log.debug "Temperature rose above $tooHot: sending SMS to $phone1 and activating $mySwitch"
def tempScale = location.temperatureScale ?: "F"
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
switch1?.on()

View File

@@ -50,9 +50,9 @@ def authPage() {
}
def description = "Tap to enter LIFX credentials"
def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" // this triggers oauthInit() below
// def redirectUrl = "${apiServerUrl}"
// log.debug "app id: ${app.id}"
// log.debug "redirect url: ${redirectUrl}"s
// def redirectUrl = "${apiServerUrl}"
log.debug "app id: ${app.id}"
log.debug "redirect url: ${redirectUrl}"
return dynamicPage(name: "Credentials", title: "Connect to LIFX", nextPage: null, uninstall: true, install:true) {
section {
href(url:redirectUrl, required:true, title:"Connect to LIFX", description:"Tap here to connect your LIFX account")
@@ -372,7 +372,7 @@ def updateDevices() {
def childDevice = getChildDevice(device.id)
selectors.add("${device.id}")
if (!childDevice) {
// log.info("Adding device ${device.id}: ${device.product}")
log.info("Adding device ${device.id}: ${device.product}")
def data = [
label: device.label,
level: Math.round((device.brightness ?: 1) * 100),

View File

@@ -41,10 +41,10 @@ def updated() {
def presenceHandler(evt) {
if (evt.value == "present") {
// log.debug "${presence.label ?: presence.name} has arrived at the ${location}"
log.debug "${presence.label ?: presence.name} has arrived at the ${location}"
sendPush("${presence.label ?: presence.name} has arrived at the ${location}")
} else if (evt.value == "not present") {
// log.debug "${presence.label ?: presence.name} has left the ${location}"
log.debug "${presence.label ?: presence.name} has left the ${location}"
sendPush("${presence.label ?: presence.name} has left the ${location}")
}
}

View File

@@ -47,7 +47,7 @@ def updated() {
def presenceHandler(evt) {
if (evt.value == "present") {
// log.debug "${presence.label ?: presence.name} has arrived at the ${location}"
log.debug "${presence.label ?: presence.name} has arrived at the ${location}"
if (location.contactBookEnabled) {
sendNotificationToContacts("${presence.label ?: presence.name} has arrived at the ${location}", recipients)
@@ -56,7 +56,7 @@ def presenceHandler(evt) {
sendSms(phone1, "${presence.label ?: presence.name} has arrived at the ${location}")
}
} else if (evt.value == "not present") {
// log.debug "${presence.label ?: presence.name} has left the ${location}"
log.debug "${presence.label ?: presence.name} has left the ${location}"
if (location.contactBookEnabled) {
sendNotificationToContacts("${presence.label ?: presence.name} has left the ${location}", recipients)

View File

@@ -67,7 +67,7 @@ def updated() {
}
def subscribe() {
// log.debug "present: ${cars.collect{it.displayName + ': ' + it.currentPresence}}"
log.debug "present: ${cars.collect{it.displayName + ': ' + it.currentPresence}}"
subscribe(doorSensor, "contact", garageDoorContact)
subscribe(cars, "presence", carPresence)

View File

@@ -71,7 +71,7 @@ def updated() {
private subscribeToEvents()
{
subscribe intrusionMotions, "motion", intruderMotion
// subscribe residentMotions, "motion", residentMotion
subscribe residentMotions, "motion", residentMotion
subscribe intrusionContacts, "contact", contact
subscribe alarms, "alarm", alarm
subscribe(app, appTouch)
@@ -156,7 +156,6 @@ def residentMotion(evt)
// startReArmSequence()
// }
//}
unsubscribe(residentMotions)
}
def contact(evt)
@@ -215,7 +214,7 @@ def checkForReArm()
}
else {
log.warn "checkForReArm: lastIntruderMotion was null, unable to check for re-arming intrusion detection"
}
}
}
private startAlarmSequence()

View File

@@ -48,7 +48,7 @@ def updated()
def contactOpenHandler(evt) {
log.trace "$evt.value: $evt, $settings"
log.debug "$contact1 was opened, sending text"
log.debug "$contact1 was opened, texting $phone1"
if (location.contactBookEnabled) {
sendNotificationToContacts("Your ${contact1.label ?: contact1.name} was opened", recipients)
}

View File

@@ -50,7 +50,7 @@ def updated() {
def motionActiveHandler(evt) {
log.trace "$evt.value: $evt, $settings"
if (presence1.latestValue("presence") == "not present") {
// Don't send a continuous stream of text messages
def deltaSeconds = 10
@@ -60,14 +60,14 @@ def motionActiveHandler(evt) {
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
if (alreadySentSms) {
log.debug "SMS already sent within the last $deltaSeconds seconds"
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
} else {
if (location.contactBookEnabled) {
log.debug "$motion1 has moved while you were out, sending notifications to: ${recipients?.size()}"
sendNotificationToContacts("${motion1.label} ${motion1.name} moved while you were out", recipients)
}
else {
log.debug "$motion1 has moved while you were out, sending text"
log.debug "$motion1 has moved while you were out, texting $phone1"
sendSms(phone1, "${motion1.label} ${motion1.name} moved while you were out")
}
}

View File

@@ -53,13 +53,13 @@ def accelerationActiveHandler(evt) {
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
if (alreadySentSms) {
log.debug "SMS already sent to phone within the last $deltaSeconds seconds"
log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds"
} else {
if (location.contactBookEnabled) {
sendNotificationToContacts("Gun case has moved!", recipients)
}
else {
log.debug "$accelerationSensor has moved, texting phone"
log.debug "$accelerationSensor has moved, texting $phone1"
sendSms(phone1, "Gun case has moved!")
}
}

View File

@@ -60,7 +60,7 @@ def authPage() {
def oauthInitUrl() {
def token = getToken()
//log.debug "initiateOauth got token: $token"
log.debug "initiateOauth got token: $token"
// store these for validate after the user takes the oauth journey
state.oauth_request_token = token.oauth_token
@@ -76,7 +76,7 @@ def getToken() {
]
def requestTokenBaseUrl = "https://oauth.withings.com/account/request_token"
def url = buildSignedUrl(requestTokenBaseUrl, params)
//log.debug "getToken - url: $url"
log.debug "getToken - url: $url"
return getJsonFromUrl(url)
}
@@ -182,7 +182,7 @@ def exchangeToken() {
def requestTokenBaseUrl = "https://oauth.withings.com/account/access_token"
def url = buildSignedUrl(requestTokenBaseUrl, params, tokenSecret)
//log.debug "signed url: $url with secret $tokenSecret"
log.debug "signed url: $url with secret $tokenSecret"
def token = getJsonFromUrl(url)
@@ -198,8 +198,8 @@ def exchangeToken() {
def load() {
def json = get(getMeasurement(new Date() - 30))
// removed logging of actual json payload. Can be put back for debugging
log.debug "swapped, then received json"
log.debug "swapped, then received: $json"
parse(data:json)
def html = """