From 22185c5440f3cc307319ea947a38af26c65f8b18 Mon Sep 17 00:00:00 2001 From: Adam Jensen Date: Tue, 24 May 2016 08:18:51 -0500 Subject: [PATCH 01/25] Added labels to the motion attribute in the main multiattributetile --- .../fibaro-motion-sensor-zw5.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy index 864420f..7504d93 100644 --- a/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy @@ -33,8 +33,8 @@ metadata { tiles(scale: 2) { multiAttributeTile(name:"FGMS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app tileAttribute("device.motion", key:"PRIMARY_CONTROL") { - attributeState("inactive", icon:"st.motion.motion.inactive", backgroundColor:"#79b821") - attributeState("active", icon:"st.motion.motion.active", backgroundColor:"#ffa81e") + attributeState("inactive", label:"no motion", icon:"st.motion.motion.inactive", backgroundColor:"#79b821") + attributeState("active", label:"motion", icon:"st.motion.motion.active", backgroundColor:"#ffa81e") } tileAttribute("device.tamper", key:"SECONDARY_CONTROL") { @@ -278,4 +278,4 @@ private encap(physicalgraph.zwave.Command cmd) { } else { crc16(cmd) } -} \ No newline at end of file +} From 5e6b4f74e05e1b680064e1710a198f267fff0e15 Mon Sep 17 00:00:00 2001 From: CosmicPuppy Date: Mon, 29 Aug 2016 13:49:49 -0700 Subject: [PATCH 02/25] To Netatmo DTHs, added Capability "Sensor" per http://docs.smartthings.com/en/latest/device-type-developers-guide/overview.html?highlight=sensor%20actuator#actuator-and-sensor. There are some SmartApps out there using the "Actuator" and "Sensor" Capabilities and this Device doesn't show up for them (e.g., SmartTiles V6). --- .../netatmo-additional-module.groovy | 1 + .../dianoga/netatmo-basestation.src/netatmo-basestation.groovy | 1 + .../netatmo-outdoor-module.src/netatmo-outdoor-module.groovy | 1 + devicetypes/dianoga/netatmo-rain.src/netatmo-rain.groovy | 2 ++ 4 files changed, 5 insertions(+) diff --git a/devicetypes/dianoga/netatmo-additional-module.src/netatmo-additional-module.groovy b/devicetypes/dianoga/netatmo-additional-module.src/netatmo-additional-module.groovy index 00916de..9717a28 100644 --- a/devicetypes/dianoga/netatmo-additional-module.src/netatmo-additional-module.groovy +++ b/devicetypes/dianoga/netatmo-additional-module.src/netatmo-additional-module.groovy @@ -15,6 +15,7 @@ */ metadata { definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") { + capability "Sensor" capability "Relative Humidity Measurement" capability "Temperature Measurement" diff --git a/devicetypes/dianoga/netatmo-basestation.src/netatmo-basestation.groovy b/devicetypes/dianoga/netatmo-basestation.src/netatmo-basestation.groovy index f0a844c..899a987 100644 --- a/devicetypes/dianoga/netatmo-basestation.src/netatmo-basestation.groovy +++ b/devicetypes/dianoga/netatmo-basestation.src/netatmo-basestation.groovy @@ -15,6 +15,7 @@ */ metadata { definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") { + capability "Sensor" capability "Relative Humidity Measurement" capability "Temperature Measurement" diff --git a/devicetypes/dianoga/netatmo-outdoor-module.src/netatmo-outdoor-module.groovy b/devicetypes/dianoga/netatmo-outdoor-module.src/netatmo-outdoor-module.groovy index 45ef2b2..9ea2db8 100644 --- a/devicetypes/dianoga/netatmo-outdoor-module.src/netatmo-outdoor-module.groovy +++ b/devicetypes/dianoga/netatmo-outdoor-module.src/netatmo-outdoor-module.groovy @@ -15,6 +15,7 @@ */ metadata { definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") { + capability "Sensor" capability "Relative Humidity Measurement" capability "Temperature Measurement" } diff --git a/devicetypes/dianoga/netatmo-rain.src/netatmo-rain.groovy b/devicetypes/dianoga/netatmo-rain.src/netatmo-rain.groovy index a882f23..1ed8474 100644 --- a/devicetypes/dianoga/netatmo-rain.src/netatmo-rain.groovy +++ b/devicetypes/dianoga/netatmo-rain.src/netatmo-rain.groovy @@ -15,6 +15,8 @@ */ metadata { definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") { + capability "Sensor" + attribute "rain", "number" attribute "rainSumHour", "number" attribute "rainSumDay", "number" From c051d719cca7c7d64512fd2149d62332232fa486 Mon Sep 17 00:00:00 2001 From: twack Date: Wed, 7 Sep 2016 09:14:19 -0700 Subject: [PATCH 03/25] 20160907_update_its_to_hot_for_security --- smartapps/smartthings/its-too-hot.src/its-too-hot.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smartapps/smartthings/its-too-hot.src/its-too-hot.groovy b/smartapps/smartthings/its-too-hot.src/its-too-hot.groovy index 5b8e10d..3ac1ab0 100644 --- a/smartapps/smartthings/its-too-hot.src/its-too-hot.groovy +++ b/smartapps/smartthings/its-too-hot.src/its-too-hot.groovy @@ -69,10 +69,10 @@ def temperatureHandler(evt) { def alreadySentSms = recentEvents.count { it.doubleValue >= tooHot } > 1 if (alreadySentSms) { - log.debug "SMS already sent to $phone1 within the last $deltaMinutes minutes" + log.debug "SMS already sent to within the last $delta Minutes minutes" // TODO: Send "Temperature back to normal" SMS, turn switch off } else { - log.debug "Temperature rose above $tooHot: sending SMS to $phone1 and activating $mySwitch" + log.debug "Temperature rose above $tooHot: sending SMS 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() From 06acc13575065537df2bcf4040211d3adcdcc7e7 Mon Sep 17 00:00:00 2001 From: Juan Pablo Risso Date: Wed, 7 Sep 2016 12:14:58 -0400 Subject: [PATCH 04/25] DVCSMP-1959 - Remove sensitive information from logs (#1206) --- .../garage-door-monitor.src/garage-door-monitor.groovy | 2 +- .../the-gun-case-moved.src/the-gun-case-moved.groovy | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/smartapps/smartthings/garage-door-monitor.src/garage-door-monitor.groovy b/smartapps/smartthings/garage-door-monitor.src/garage-door-monitor.groovy index 4cabc5c..cd07b23 100644 --- a/smartapps/smartthings/garage-door-monitor.src/garage-door-monitor.groovy +++ b/smartapps/smartthings/garage-door-monitor.src/garage-door-monitor.groovy @@ -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) diff --git a/smartapps/smartthings/the-gun-case-moved.src/the-gun-case-moved.groovy b/smartapps/smartthings/the-gun-case-moved.src/the-gun-case-moved.groovy index 196d57a..1cce9c2 100644 --- a/smartapps/smartthings/the-gun-case-moved.src/the-gun-case-moved.groovy +++ b/smartapps/smartthings/the-gun-case-moved.src/the-gun-case-moved.groovy @@ -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 $phone1 within the last $deltaSeconds seconds" + log.debug "SMS already sent to phone within the last $deltaSeconds seconds" } else { if (location.contactBookEnabled) { sendNotificationToContacts("Gun case has moved!", recipients) } else { - log.debug "$accelerationSensor has moved, texting $phone1" + log.debug "$accelerationSensor has moved, texting phone" sendSms(phone1, "Gun case has moved!") } } From dedb0f8465cbe97dc1fecb279a1a64ecf2b6fe3f Mon Sep 17 00:00:00 2001 From: twack Date: Wed, 7 Sep 2016 09:20:13 -0700 Subject: [PATCH 05/25] Update its-too-hot.groovy --- smartapps/smartthings/its-too-hot.src/its-too-hot.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/smartthings/its-too-hot.src/its-too-hot.groovy b/smartapps/smartthings/its-too-hot.src/its-too-hot.groovy index 3ac1ab0..6ffcebf 100644 --- a/smartapps/smartthings/its-too-hot.src/its-too-hot.groovy +++ b/smartapps/smartthings/its-too-hot.src/its-too-hot.groovy @@ -69,7 +69,7 @@ def temperatureHandler(evt) { def alreadySentSms = recentEvents.count { it.doubleValue >= tooHot } > 1 if (alreadySentSms) { - log.debug "SMS already sent to within the last $delta Minutes minutes" + log.debug "SMS already sent 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" From 159d3acf4fcfd575e4b5ca275739dbcc76d1f64d Mon Sep 17 00:00:00 2001 From: David Sainte-Claire Date: Wed, 7 Sep 2016 10:56:25 -0700 Subject: [PATCH 06/25] removed log messages from smartapp that may print sensitive information --- smartapps/smartthings/bon-voyage.src/bon-voyage.groovy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/smartapps/smartthings/bon-voyage.src/bon-voyage.groovy b/smartapps/smartthings/bon-voyage.src/bon-voyage.groovy index a843a71..cb9593d 100644 --- a/smartapps/smartthings/bon-voyage.src/bon-voyage.groovy +++ b/smartapps/smartthings/bon-voyage.src/bon-voyage.groovy @@ -49,13 +49,15 @@ preferences { def installed() { log.debug "Installed with settings: ${settings}" - log.debug "Current mode = ${location.mode}, people = ${people.collect{it.label + ': ' + it.currentPresence}}" + // 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}}" 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}}" + // 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}}" unsubscribe() subscribe(people, "presence", presence) } From 7568cbf781eb81b9d9f7a9c3b638e0fab43cbb97 Mon Sep 17 00:00:00 2001 From: Andrew Bresee Date: Wed, 7 Sep 2016 11:11:20 -0700 Subject: [PATCH 07/25] DVCSMP-1959: Security review - removing potentially confidential log statements (#1210) * Commented out log statement logging users access token * Commented out log.info logging potentially confidential device information * Remove log logging potentially confidential information on the app --- .../smartthings/lifx-connect.src/lifx-connect.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy b/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy index 587c311..7832b68 100644 --- a/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy +++ b/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy @@ -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}" + // def redirectUrl = "${apiServerUrl}" + // log.debug "app id: ${app.id}" + // log.debug "redirect url: ${redirectUrl}"s 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), From 614573a15c67a29a1638c9cab81e79d752c135b5 Mon Sep 17 00:00:00 2001 From: Rohan Desai Date: Wed, 7 Sep 2016 11:28:08 -0700 Subject: [PATCH 08/25] security fixes for SAs --- smartapps/smartthings/flood-alert.src/flood-alert.groovy | 4 ++-- .../greetings-earthling.src/greetings-earthling.groovy | 9 +++------ .../smartthings/habit-helper.src/habit-helper.groovy | 5 ++--- smartapps/smartthings/it-moved.src/it-moved.groovy | 6 +++--- .../presence-change-push.src/presence-change-push.groovy | 4 ++-- .../presence-change-text.src/presence-change-text.groovy | 4 ++-- 6 files changed, 14 insertions(+), 18 deletions(-) diff --git a/smartapps/smartthings/flood-alert.src/flood-alert.groovy b/smartapps/smartthings/flood-alert.src/flood-alert.groovy index 73cae0b..f44aa48 100644 --- a/smartapps/smartthings/flood-alert.src/flood-alert.groovy +++ b/smartapps/smartthings/flood-alert.src/flood-alert.groovy @@ -54,10 +54,10 @@ def waterWetHandler(evt) { def alreadySentSms = recentEvents.count { it.value && it.value == "wet" } > 1 if (alreadySentSms) { - log.debug "SMS already sent to $phone within the last $deltaSeconds seconds" + log.debug "SMS already sent within the last $deltaSeconds seconds" } else { def msg = "${alarm.displayName} is wet!" - log.debug "$alarm is wet, texting $phone" + log.debug "$alarm is wet, texting phone number" if (location.contactBookEnabled) { sendNotificationToContacts(msg, recipients) diff --git a/smartapps/smartthings/greetings-earthling.src/greetings-earthling.groovy b/smartapps/smartthings/greetings-earthling.src/greetings-earthling.groovy index 2797c09..e97c684 100644 --- a/smartapps/smartthings/greetings-earthling.src/greetings-earthling.groovy +++ b/smartapps/smartthings/greetings-earthling.src/greetings-earthling.groovy @@ -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,11 +71,10 @@ 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.displayName} because last departure was only ${now() - recentNotPresent.date.time} msec ago" + log.debug "skipping notification of arrival of Person 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) } @@ -106,6 +105,4 @@ private send(msg) { sendSms(phone, msg) } } - - log.debug msg } diff --git a/smartapps/smartthings/habit-helper.src/habit-helper.groovy b/smartapps/smartthings/habit-helper.src/habit-helper.groovy index 6e8194c..64d2052 100644 --- a/smartapps/smartthings/habit-helper.src/habit-helper.groovy +++ b/smartapps/smartthings/habit-helper.src/habit-helper.groovy @@ -57,12 +57,11 @@ def scheduleCheck() def message = message1 ?: "SmartThings - Habit Helper Reminder!" if (location.contactBookEnabled) { - log.debug "Texting reminder: ($message) to contacts:${recipients?.size()}" + log.debug "Texting reminder to contacts:${recipients?.size()}" sendNotificationToContacts(message, recipients) } else { - - log.debug "Texting reminder: ($message) to $phone1" + log.debug "Texting reminder" sendSms(phone1, message) } } diff --git a/smartapps/smartthings/it-moved.src/it-moved.groovy b/smartapps/smartthings/it-moved.src/it-moved.groovy index 1023807..a7a6899 100644 --- a/smartapps/smartthings/it-moved.src/it-moved.groovy +++ b/smartapps/smartthings/it-moved.src/it-moved.groovy @@ -53,14 +53,14 @@ def accelerationActiveHandler(evt) { def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1 if (alreadySentSms) { - log.debug "SMS already sent to $phone1 within the last $deltaSeconds seconds" + log.debug "SMS already sent 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, texting $phone1" + log.debug "accelerationSensor has moved, sending text message" sendSms(phone1, "${accelerationSensor.label ?: accelerationSensor.name} moved") } } diff --git a/smartapps/smartthings/presence-change-push.src/presence-change-push.groovy b/smartapps/smartthings/presence-change-push.src/presence-change-push.groovy index af5fa72..04e8c90 100644 --- a/smartapps/smartthings/presence-change-push.src/presence-change-push.groovy +++ b/smartapps/smartthings/presence-change-push.src/presence-change-push.groovy @@ -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}") } } diff --git a/smartapps/smartthings/presence-change-text.src/presence-change-text.groovy b/smartapps/smartthings/presence-change-text.src/presence-change-text.groovy index d4ad1f3..4c4d5a2 100644 --- a/smartapps/smartthings/presence-change-text.src/presence-change-text.groovy +++ b/smartapps/smartthings/presence-change-text.src/presence-change-text.groovy @@ -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) From 878eb66b8b9d2ebe019a0c1e20f1527d41bcec73 Mon Sep 17 00:00:00 2001 From: Andrew Bresee Date: Wed, 7 Sep 2016 13:30:35 -0700 Subject: [PATCH 09/25] Remove log logging personal information about a device (#1214) --- .../ridiculously-automated-garage-door.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/smartthings/ridiculously-automated-garage-door.src/ridiculously-automated-garage-door.groovy b/smartapps/smartthings/ridiculously-automated-garage-door.src/ridiculously-automated-garage-door.groovy index f491c6c..824d8d9 100644 --- a/smartapps/smartthings/ridiculously-automated-garage-door.src/ridiculously-automated-garage-door.groovy +++ b/smartapps/smartthings/ridiculously-automated-garage-door.src/ridiculously-automated-garage-door.groovy @@ -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) From 324ac13afbd555003827971c0498701e699c0cca Mon Sep 17 00:00:00 2001 From: Andrew Bresee Date: Wed, 7 Sep 2016 15:07:01 -0700 Subject: [PATCH 10/25] DVCSMP-1959: Unsubscribe from a pointless method (#1215) * Unsubscribe from a pointless method * Remove subscription to pointless method --- .../smartthings/smart-security.src/smart-security.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/smartapps/smartthings/smart-security.src/smart-security.groovy b/smartapps/smartthings/smart-security.src/smart-security.groovy index 6d15cff..175346e 100644 --- a/smartapps/smartthings/smart-security.src/smart-security.groovy +++ b/smartapps/smartthings/smart-security.src/smart-security.groovy @@ -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,6 +156,7 @@ def residentMotion(evt) // startReArmSequence() // } //} + unsubscribe(‘residentMotion’) } def contact(evt) @@ -214,7 +215,7 @@ def checkForReArm() } else { log.warn "checkForReArm: lastIntruderMotion was null, unable to check for re-arming intrusion detection" - } + } } private startAlarmSequence() From 91eb59a10d60f8d11f87e0d570fee45c57fff44e Mon Sep 17 00:00:00 2001 From: Andrew Bresee Date: Thu, 8 Sep 2016 04:45:30 -0700 Subject: [PATCH 11/25] DVCSMP-1959: Replace log logging personal phone number (#1217) * Replace log logging personal phone number * Removed commented out log --- .../text-me-when-it-opens.src/text-me-when-it-opens.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/smartthings/text-me-when-it-opens.src/text-me-when-it-opens.groovy b/smartapps/smartthings/text-me-when-it-opens.src/text-me-when-it-opens.groovy index 08a9887..cbf8698 100644 --- a/smartapps/smartthings/text-me-when-it-opens.src/text-me-when-it-opens.groovy +++ b/smartapps/smartthings/text-me-when-it-opens.src/text-me-when-it-opens.groovy @@ -48,7 +48,7 @@ def updated() def contactOpenHandler(evt) { log.trace "$evt.value: $evt, $settings" - log.debug "$contact1 was opened, texting $phone1" + log.debug "$contact1 was opened, sending text" if (location.contactBookEnabled) { sendNotificationToContacts("Your ${contact1.label ?: contact1.name} was opened", recipients) } From 8777ec5f6de15c3209fea61d0e235ede684245f6 Mon Sep 17 00:00:00 2001 From: Luke Bredeson Date: Thu, 8 Sep 2016 10:06:38 -0500 Subject: [PATCH 12/25] DVCSMP-2020: smart-security app contains invalid code --- smartapps/smartthings/smart-security.src/smart-security.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/smartthings/smart-security.src/smart-security.groovy b/smartapps/smartthings/smart-security.src/smart-security.groovy index 175346e..acc571e 100644 --- a/smartapps/smartthings/smart-security.src/smart-security.groovy +++ b/smartapps/smartthings/smart-security.src/smart-security.groovy @@ -156,7 +156,7 @@ def residentMotion(evt) // startReArmSequence() // } //} - unsubscribe(‘residentMotion’) + unsubscribe(residentMotions) } def contact(evt) From 826993cc4528223486ad73b92faca82c1177f4ff Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Wed, 31 Aug 2016 09:55:24 -0500 Subject: [PATCH 13/25] [DVCSMP-1979] Use async http for polling and refresh tokens. --- .../ecobee-sensor.src/ecobee-sensor.groovy | 2 +- .../ecobee-thermostat.groovy | 2 +- .../ecobee-connect.src/ecobee-connect.groovy | 333 ++++++++++++------ 3 files changed, 227 insertions(+), 110 deletions(-) diff --git a/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy b/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy index 381f6fc..1f028d9 100644 --- a/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy +++ b/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy @@ -67,6 +67,6 @@ def refresh() { void poll() { log.debug "Executing 'poll' using parent SmartApp" - parent.pollChild() + parent.poll() } diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index a16d28c..134fa5c 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -133,7 +133,7 @@ def refresh() { void poll() { log.debug "Executing 'poll' using parent SmartApp" - parent.pollChild() + parent.poll() } def generateEvent(Map results) { diff --git a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy index d8c2179..4418d22 100644 --- a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy +++ b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy @@ -20,6 +20,8 @@ * 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", @@ -244,9 +246,7 @@ def getEcobeeThermostats() { uri: apiEndpoint, path: "/1/thermostat", 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(bodyParams)] + query: [json: toJson(bodyParams)] ] def stats = [:] @@ -265,9 +265,8 @@ 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() + refreshAuthToken([async: false, nextAction: "getEcobeeThermostats"]) } } atomicState.thermostats = stats @@ -358,16 +357,22 @@ def initialize() { atomicState.timeSendPush = null atomicState.reAttempt = 0 - pollHandler() //first time polling data data from thermostat + initialPoll() //first time polling data data from thermostat //automatically update devices status every 5 mins runEvery5Minutes("poll") } -def pollHandler() { - log.debug "pollHandler()" - pollChildren(null) // Hit the ecobee API for update on all thermostats +/** + * 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 atomicState.thermostats.each {stat -> def dni = stat.key @@ -380,36 +385,38 @@ def pollHandler() { } } -def pollChildren(child = null) { - def thermostatIdsString = getChildDeviceIdsString() +/** + * 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() { log.debug "polling children: $thermostatIdsString" - def requestBody = [ - selection: [ - selectionType: "thermostats", - selectionMatch: thermostatIdsString, - includeExtendedRuntime: true, - includeSettings: true, - includeRuntime: true, - includeSensors: true - ] - ] + def params = getPollParams() + params.query << ["Content-Type": "application/json"] def result = false - - def pollParams = [ - uri: apiEndpoint, - path: "/1/thermostat", - 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)] - ] + log.debug "making synchronous poll request" try{ - httpGet(pollParams) { resp -> + httpGet(params) { 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) @@ -420,40 +427,95 @@ def pollChildren(child = null) { } 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() + refreshAuthToken([async: false, nextAction: "pollChildrenSync"]) } } return result } -// 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 - } - +/** + * 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()}" + } + } } -void poll() { - pollChild() +private getPollParams() { + def thermostatIdsString = getChildDeviceIdsString() + def requestBody = [ + selection: [ + selectionType: "thermostats", + selectionMatch: thermostatIdsString, + includeExtendedRuntime: true, + includeSettings: true, + includeRuntime: true, + includeSensors: true + ] + ] + return [ + uri: apiEndpoint, + path: "/1/thermostat", + headers: ["Authorization": "Bearer ${atomicState.authToken}"], + query: [json: toJson(requestBody)] + ] +} + +/** + * 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 + } + } + } } def availableModes(child) { @@ -553,47 +615,104 @@ def toQueryString(Map m) { return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&") } -private refreshAuthToken() { - log.debug "refreshing auth token" +/** + * 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) + } + } +} - 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) - } +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) } - } 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 - } - } - } - } + } + } 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 + } + } } /** @@ -603,20 +722,20 @@ private refreshAuthToken() { * * @param json - an object representing the parsed JSON response from Ecobee */ -private void saveTokenAndResumeAction(json) { - log.debug "token response json: $json" +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" if (json) { - debugEvent("Response = $json") + debugEvent(debugMessage) atomicState.refreshToken = json?.refresh_token atomicState.authToken = json?.access_token - if (atomicState.action) { - log.debug "got refresh token, executing next action: ${atomicState.action}" - "${atomicState.action}"() + if (nextAction) { + log.debug "got refresh token, will execute next action (passed in!): $nextAction" + "$nextAction"() } } else { log.warn "did not get response body from refresh token response" } - atomicState.action = "" } /** @@ -756,7 +875,6 @@ 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." @@ -771,11 +889,10 @@ 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 pollChildren + // TODO - figure out why we're setting the next action to be poll // 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() + refreshAuthToken([async: true, nextAction: "poll"]) } else { debugEvent("Authentication error, invalid authentication method, lack of credentials, etc.") log.error "Authentication error, invalid authentication method, lack of credentials, etc." From 5584020e9613854beb17306305d01696592d87eb Mon Sep 17 00:00:00 2001 From: David Sainte-Claire Date: Thu, 8 Sep 2016 10:31:24 -0700 Subject: [PATCH 14/25] removed logging of sensitive access tokens from smartapp --- smartapps/smartthings/withings.src/withings.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/smartapps/smartthings/withings.src/withings.groovy b/smartapps/smartthings/withings.src/withings.groovy index 24cfcd3..0e09ffc 100644 --- a/smartapps/smartthings/withings.src/withings.groovy +++ b/smartapps/smartthings/withings.src/withings.groovy @@ -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)) - - log.debug "swapped, then received: $json" + // removed logging of actual json payload. Can be put back for debugging + log.debug "swapped, then received json" parse(data:json) def html = """ From 2f889de11a0a210b538050d6e0beb79b5ae3dfce Mon Sep 17 00:00:00 2001 From: Rohan Desai Date: Thu, 8 Sep 2016 10:36:28 -0700 Subject: [PATCH 15/25] fixes for dynamic execution --- .../curb/curb-control.src/curb-control.groovy | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/smartapps/curb/curb-control.src/curb-control.groovy b/smartapps/curb/curb-control.src/curb-control.groovy index 3a1c83d..4cea160 100644 --- a/smartapps/curb/curb-control.src/curb-control.groovy +++ b/smartapps/curb/curb-control.src/curb-control.groovy @@ -65,7 +65,16 @@ void updateSwitch() { private void updateAll(devices) { def command = request.JSON?.command if (command) { - devices."$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.") + } } } @@ -77,7 +86,16 @@ private void update(devices) { if (!device) { httpError(404, "Device not found") } else { - device."$command"() + 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.") + } } } } From 6aa09bb05223d6634743213d72459e8f9b34f004 Mon Sep 17 00:00:00 2001 From: Andrew Bresee Date: Thu, 8 Sep 2016 10:40:35 -0700 Subject: [PATCH 16/25] Change log statement to not log personal phone number (#1224) --- .../text-me-when-theres-motion-and-im-not-here.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smartapps/smartthings/text-me-when-theres-motion-and-im-not-here.src/text-me-when-theres-motion-and-im-not-here.groovy b/smartapps/smartthings/text-me-when-theres-motion-and-im-not-here.src/text-me-when-theres-motion-and-im-not-here.groovy index a3fac54..1094255 100644 --- a/smartapps/smartthings/text-me-when-theres-motion-and-im-not-here.src/text-me-when-theres-motion-and-im-not-here.groovy +++ b/smartapps/smartthings/text-me-when-theres-motion-and-im-not-here.src/text-me-when-theres-motion-and-im-not-here.groovy @@ -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 to $phone1 within the last $deltaSeconds seconds" + log.debug "SMS already sent 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, texting $phone1" + log.debug "$motion1 has moved while you were out, sending text" sendSms(phone1, "${motion1.label} ${motion1.name} moved while you were out") } } From 48e9a4bd6afca9efbb9166faabe5c1ff64f2642d Mon Sep 17 00:00:00 2001 From: Rohan Desai Date: Thu, 8 Sep 2016 10:46:40 -0700 Subject: [PATCH 17/25] removed semi colons --- smartapps/curb/curb-control.src/curb-control.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/smartapps/curb/curb-control.src/curb-control.groovy b/smartapps/curb/curb-control.src/curb-control.groovy index 4cea160..3bd1ec4 100644 --- a/smartapps/curb/curb-control.src/curb-control.groovy +++ b/smartapps/curb/curb-control.src/curb-control.groovy @@ -68,10 +68,10 @@ private void updateAll(devices) { switch(command) { case "on": devices.on() - break; + break case "off": devices.off() - break; + break default: httpError(403, "Access denied. This command is not supported by current capability.") } @@ -89,10 +89,10 @@ private void update(devices) { switch(command) { case "on": device.on() - break; + break case "off": device.off() - break; + break default: httpError(403, "Access denied. This command is not supported by current capability.") } From 006b5e7bea50e7172c360b0884088ba961f1ec0f Mon Sep 17 00:00:00 2001 From: David Sainte-Claire Date: Thu, 8 Sep 2016 10:54:04 -0700 Subject: [PATCH 18/25] removed logging of device and phone information due to security concerns --- .../smartthings/beacon-control.src/beacon-control.groovy | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/smartapps/smartthings/beacon-control.src/beacon-control.groovy b/smartapps/smartthings/beacon-control.src/beacon-control.groovy index 17fddad..007b455 100644 --- a/smartapps/smartthings/beacon-control.src/beacon-control.groovy +++ b/smartapps/smartthings/beacon-control.src/beacon-control.groovy @@ -114,13 +114,16 @@ def beaconHandler(evt) { if (allOk) { def data = new groovy.json.JsonSlurper().parseText(evt.data) - log.debug " data: $data - phones: " + phones*.deviceNetworkId + // removed logging of device names. can be added back for debugging + //log.debug " data: $data - phones: " + phones*.deviceNetworkId def beaconName = getBeaconName(evt) - log.debug " beaconName: $beaconName" + // removed logging of device names. can be added back for debugging + //log.debug " beaconName: $beaconName" def phoneName = getPhoneName(data) - log.debug " phoneName: $phoneName" + // removed logging of device names. can be added back for debugging + //log.debug " phoneName: $phoneName" if (phoneName != null) { def action = data.presence == "1" ? "arrived" : "left" def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName" From 43b836f4130b2ef6ceaf696bba00ed19276ee4f9 Mon Sep 17 00:00:00 2001 From: Amol Mundayoor Date: Thu, 8 Sep 2016 11:07:31 -0700 Subject: [PATCH 19/25] Removing debug logs for netatmo --- .../netatmo-connect.src/netatmo-connect.groovy | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy b/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy index 7a9e946..dd219c7 100644 --- a/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy +++ b/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy @@ -58,7 +58,6 @@ def authPage() { if (canInstallLabs()) { def redirectUrl = getBuildRedirectUrl() - log.debug "Redirect url = ${redirectUrl}" if (state.authToken) { description = "Tap 'Next' to proceed" @@ -113,14 +112,10 @@ def oauthInitUrl() { scope: "read_station" ] - log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}" - redirect (location: getVendorAuthPath() + toQueryString(oauthParams)) } def callback() { - log.debug "callback()>> params: $params, params.code ${params.code}" - def code = params.code def oauthState = params.state @@ -135,8 +130,6 @@ def callback() { scope: "read_station" ] - log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}" - def tokenUrl = getVendorTokenPath() def params = [ uri: tokenUrl, @@ -144,8 +137,6 @@ def callback() { body: tokenParams ] - log.debug "PARAMS: ${params}" - httpPost(params) { resp -> def slurper = new JsonSlurper() @@ -156,7 +147,6 @@ 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" } } @@ -292,7 +282,6 @@ def refreshToken() { response.data.each {key, value -> def data = slurper.parseText(key); - log.debug "Data: $data" state.refreshToken = data.refresh_token state.accessToken = data.access_token From c703543f36843c6199c532fc0c9148add87168b8 Mon Sep 17 00:00:00 2001 From: Amol Mundayoor Date: Thu, 8 Sep 2016 11:20:27 -0700 Subject: [PATCH 20/25] Commenting out log.debug instead of removing them --- .../netatmo-connect.src/netatmo-connect.groovy | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy b/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy index dd219c7..65ed151 100644 --- a/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy +++ b/smartapps/dianoga/netatmo-connect.src/netatmo-connect.groovy @@ -58,6 +58,7 @@ def authPage() { if (canInstallLabs()) { def redirectUrl = getBuildRedirectUrl() + // log.debug "Redirect url = ${redirectUrl}" if (state.authToken) { description = "Tap 'Next' to proceed" @@ -112,10 +113,14 @@ def oauthInitUrl() { scope: "read_station" ] + // log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}" + redirect (location: getVendorAuthPath() + toQueryString(oauthParams)) } def callback() { + // log.debug "callback()>> params: $params, params.code ${params.code}" + def code = params.code def oauthState = params.state @@ -130,6 +135,8 @@ def callback() { scope: "read_station" ] + // log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}" + def tokenUrl = getVendorTokenPath() def params = [ uri: tokenUrl, @@ -137,6 +144,8 @@ def callback() { body: tokenParams ] + // log.debug "PARAMS: ${params}" + httpPost(params) { resp -> def slurper = new JsonSlurper() @@ -147,6 +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" } } @@ -282,6 +292,7 @@ def refreshToken() { response.data.each {key, value -> def data = slurper.parseText(key); + // log.debug "Data: $data" state.refreshToken = data.refresh_token state.accessToken = data.access_token From b78bce55b2cece1fb463879a3d1e8123d7ca9712 Mon Sep 17 00:00:00 2001 From: Jason Terhune Date: Thu, 8 Sep 2016 10:18:25 -0500 Subject: [PATCH 21/25] Configure gradle to compile devicetypes and smartapps. --- build.gradle | 30 ++++++++++++++++++- circle.yml | 6 ++-- .../energy-alerts.src/energy-alerts.groovy | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 2334d49..2667f45 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ buildscript { username smartThingsArtifactoryUserName password smartThingsArtifactoryPassword } - url "http://artifactory.smartthings.com/libs-release-local" + url "https://artifactory.smartthings.com/libs-release-local" } } } @@ -27,9 +27,37 @@ buildscript { repositories { mavenLocal() jcenter() + maven { + credentials { + username smartThingsArtifactoryUserName + password smartThingsArtifactoryPassword + } + url "https://artifactory.smartthings.com/libs-release-local" + } +} + +sourceSets { + devicetypes { + groovy { + srcDirs = ['devicetypes'] + } + } + smartapps { + groovy { + srcDirs = ['smartapps'] + } + } } dependencies { + devicetypesCompile 'org.codehaus.groovy:groovy-all:2.4.7' + devicetypesCompile 'smartthings:appengine-z-wave:0.1.2' + devicetypesCompile 'smartthings:appengine-zigbee:0.1.11' + smartappsCompile 'org.codehaus.groovy:groovy-all:2.4.7' + smartappsCompile 'smartthings:appengine-common:0.1.8' + smartappsCompile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.1' + smartappsCompile 'org.grails:grails-web:2.3.11' + smartappsCompile 'org.json:json:20140107' } slackSendMessage { diff --git a/circle.yml b/circle.yml index 3785924..0bd6c53 100644 --- a/circle.yml +++ b/circle.yml @@ -3,9 +3,9 @@ machine: version: oraclejdk8 -dependencies: - override: - - echo "Nothing to do." +checkout: + post: + - ./gradlew compileSmartappsGroovy compileDevicetypesGroovy test: override: diff --git a/smartapps/smartthings/energy-alerts.src/energy-alerts.groovy b/smartapps/smartthings/energy-alerts.src/energy-alerts.groovy index 4a4c9ff..bd394a1 100644 --- a/smartapps/smartthings/energy-alerts.src/energy-alerts.groovy +++ b/smartapps/smartthings/energy-alerts.src/energy-alerts.groovy @@ -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) { From 81318bafacca8d36ba982bfc6c1944cb5f2c123c Mon Sep 17 00:00:00 2001 From: jackchi Date: Fri, 9 Sep 2016 14:48:19 -0700 Subject: [PATCH 22/25] [CHF-201] Removing DTH workaround now that all events go to Kafka --- .../smartthings/cree-bulb.src/cree-bulb.groovy | 16 +--------------- .../smartpower-outlet.groovy | 16 +--------------- .../smartsense-moisture-sensor.groovy | 17 +---------------- .../smartsense-motion-sensor.groovy | 17 +---------------- .../smartsense-multi-sensor.groovy | 17 +---------------- .../smartsense-open-closed-sensor.groovy | 17 +---------------- .../smartsense-temp-humidity-sensor.groovy | 16 +--------------- .../zigbee-dimmer.src/zigbee-dimmer.groovy | 16 +--------------- .../zigbee-rgbw-bulb.groovy | 16 +--------------- .../zigbee-white-color-temperature-bulb.groovy | 16 +--------------- 10 files changed, 10 insertions(+), 154 deletions(-) diff --git a/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy b/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy index ed5e751..5837ba1 100644 --- a/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy +++ b/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy @@ -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() { diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index b978cc0..fe9d0be 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -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() { diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy index 2e9745d..f8ea7d9 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy @@ -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() { diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index c5128b1..a278644 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -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() { diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index 94575bd..5fa2139 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -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() { diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index 7fbd099..8bee02a 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -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() { diff --git a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy index b320853..26955c1 100644 --- a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy +++ b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy @@ -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() diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index 1326496..ed71f9a 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -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() { diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index e7137b6..f578311 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -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") { @@ -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() { diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index de8424d..6d06b35 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -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() { From 78ec280e83a136fc9ae80759f2fead86c122b75a Mon Sep 17 00:00:00 2001 From: Jason Terhune Date: Tue, 13 Sep 2016 08:37:20 -0500 Subject: [PATCH 23/25] Fix circle build. --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 0bd6c53..2def0cb 100644 --- a/circle.yml +++ b/circle.yml @@ -5,7 +5,7 @@ machine: checkout: post: - - ./gradlew compileSmartappsGroovy compileDevicetypesGroovy + - ./gradlew compileSmartappsGroovy compileDevicetypesGroovy -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" test: override: From e89e45e000fd95668e73ab88abe79c5a15b91b08 Mon Sep 17 00:00:00 2001 From: Jason Terhune Date: Tue, 13 Sep 2016 08:47:22 -0500 Subject: [PATCH 24/25] Another circle fix: dependencies task needs artifactory creds too. --- circle.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 2def0cb..a78f804 100644 --- a/circle.yml +++ b/circle.yml @@ -3,7 +3,9 @@ machine: version: oraclejdk8 -checkout: +dependencies: + override: + - ./gradlew dependencies -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" post: - ./gradlew compileSmartappsGroovy compileDevicetypesGroovy -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" From 4523498dabca165ed4a30b2af1c91dc09077ec43 Mon Sep 17 00:00:00 2001 From: Andrew Bresee Date: Tue, 13 Sep 2016 10:23:55 -0700 Subject: [PATCH 25/25] Add parens to unsubscribe method (#1231) --- .../smart-home-ventilation.groovy | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/smartapps/michaelstruck/smart-home-ventilation.src/smart-home-ventilation.groovy b/smartapps/michaelstruck/smart-home-ventilation.src/smart-home-ventilation.groovy index 071a251..57fa957 100644 --- a/smartapps/michaelstruck/smart-home-ventilation.src/smart-home-ventilation.groovy +++ b/smartapps/michaelstruck/smart-home-ventilation.src/smart-home-ventilation.groovy @@ -14,7 +14,7 @@ * for the specific language governing permissions and limitations under the License. * */ - + definition( name: "Smart Home Ventilation", namespace: "MichaelStruck", @@ -164,7 +164,7 @@ def installed() { def updated() { unschedule() turnOffSwitch() //Turn off all switches if the schedules are changed while in mid-schedule - unsubscribe + unsubscribe() log.debug "Updated with settings: ${settings}" init() } @@ -174,12 +174,12 @@ def init() { schedule (midnightTime, midNight) subscribe(location, "mode", locationHandler) startProcess() -} +} // Common methods def startProcess () { - createDayArray() + createDayArray() state.dayCount=state.data.size() if (state.dayCount){ state.counter = 0 @@ -190,7 +190,7 @@ def startProcess () { def startDay() { def start = convertEpoch(state.data[state.counter].start) def stop = convertEpoch(state.data[state.counter].stop) - + runOnce(start, turnOnSwitch, [overwrite: true]) runOnce(stop, incDay, [overwrite: true]) } @@ -218,7 +218,7 @@ def locationHandler(evt) { } if (!result) { startProcess() - } + } } def midNight(){ @@ -238,7 +238,7 @@ def turnOffSwitch() { } log.debug "Home ventilation switches are off." } - + def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) { def title = "" def dayListClean = "On " @@ -252,7 +252,7 @@ def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) { dayListClean = "${dayListClean}, " } } - } + } else { dayListClean = "Every day" } @@ -272,7 +272,7 @@ def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) { modeListClean = "${modeListClean} ${modePrefix}" } } - } + } else { modeListClean = "${modeListClean}all modes" } @@ -283,16 +283,16 @@ def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) { title += "\nSchedule 2: ${humanReadableTime(on2)} to ${humanReadableTime(off2)}" } if (on3 && off3) { - title += "\nSchedule 3: ${humanReadableTime(on3)} to ${humanReadableTime(off3)}" + title += "\nSchedule 3: ${humanReadableTime(on3)} to ${humanReadableTime(off3)}" } if (on4 && off4) { - title += "\nSchedule 4: ${humanReadableTime(on4)} to ${humanReadableTime(off4)}" + title += "\nSchedule 4: ${humanReadableTime(on4)} to ${humanReadableTime(off4)}" } if (on1 || on2 || on3 || on4) { title += "\n$modeListClean" - title += "\n$dayListClean" + title += "\n$dayListClean" } - + if (!on1 && !on2 && !on3 && !on4) { title="Click to configure scenario" } @@ -374,7 +374,7 @@ def createDayArray() { timeOk(timeOnD1, timeOffD1) timeOk(timeOnD2, timeOffD2) timeOk(timeOnD3, timeOffD3) - timeOk(timeOnD4, timeOffD4) + timeOk(timeOnD4, timeOffD4) } } state.data.sort{it.start} @@ -384,7 +384,7 @@ def createDayArray() { private def textAppName() { def text = "Smart Home Ventilation" -} +} private def textVersion() { def text = "Version 2.1.2 (05/31/2015)" @@ -416,4 +416,4 @@ private def textHelp() { "that each scenario does not overlap and run in separate modes (i.e. Home, Out of town, etc). Also note that you should " + "avoid scheduling the ventilation fan at exactly midnight; the app resets itself at that time. It is suggested to start any new schedule " + "at 12:15 am or later." -} \ No newline at end of file +}