mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-17 21:03:30 +00:00
Compare commits
93 Commits
MSA-1462-3
...
PROD_2016.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e373b6f92e | ||
|
|
43a1ae6371 | ||
|
|
a441b94a33 | ||
|
|
2a58d7ff62 | ||
|
|
260917d515 | ||
|
|
c1478d3e96 | ||
|
|
8b9bff15dc | ||
|
|
75c1ede16c | ||
|
|
a7acc384a2 | ||
|
|
c6998e5f1d | ||
|
|
f95e906d6e | ||
|
|
4891e3b947 | ||
|
|
ae91f9bff5 | ||
|
|
bb87ad2cf0 | ||
|
|
5dff03fb69 | ||
|
|
629d768575 | ||
|
|
dd7c6b90d5 | ||
|
|
4523498dab | ||
|
|
e89e45e000 | ||
|
|
78ec280e83 | ||
|
|
1f144d36e4 | ||
|
|
f5bd580c9e | ||
|
|
d5329dbde3 | ||
|
|
48818cfb06 | ||
|
|
079919260b | ||
|
|
570922e2ac | ||
|
|
53ed9b4d2b | ||
|
|
7149a81c85 | ||
|
|
30274f0cd7 | ||
|
|
8869cd3af0 | ||
|
|
fb0caa6446 | ||
|
|
3d05d42cb8 | ||
|
|
3184615e87 | ||
|
|
0f70362e0a | ||
|
|
bc817f8530 | ||
|
|
01b8399893 | ||
|
|
81318bafac | ||
|
|
60c2006bfc | ||
|
|
1e4f1223e7 | ||
|
|
b78bce55b2 | ||
|
|
01593c3973 | ||
|
|
6862785d6c | ||
|
|
763d7411e2 | ||
|
|
c703543f36 | ||
|
|
5db6ecda3e | ||
|
|
43b836f413 | ||
|
|
006b5e7bea | ||
|
|
62c8c19805 | ||
|
|
48e9a4bd6a | ||
|
|
07a4c0decc | ||
|
|
6aa09bb052 | ||
|
|
2f889de11a | ||
|
|
5584020e96 | ||
|
|
4ef2e694c2 | ||
|
|
826993cc45 | ||
|
|
ae3306928b | ||
|
|
8777ec5f6d | ||
|
|
91eb59a10d | ||
|
|
324ac13afb | ||
|
|
878eb66b8b | ||
|
|
8dfc270c2d | ||
|
|
614573a15c | ||
|
|
9c7b0875ba | ||
|
|
7568cbf781 | ||
|
|
1858c280a5 | ||
|
|
6b7d0968f6 | ||
|
|
159d3acf4f | ||
|
|
e8101630a3 | ||
|
|
f5ba78b221 | ||
|
|
19b8a7eeb9 | ||
|
|
1b37d649a5 | ||
|
|
dedb0f8465 | ||
|
|
06acc13575 | ||
|
|
c051d719cc | ||
|
|
1e54b93b0c | ||
|
|
bac37f9ca2 | ||
|
|
f0ecb65c09 | ||
|
|
1c0ddd2571 | ||
|
|
b7e0cbda09 | ||
|
|
f80e094bd9 | ||
|
|
ce9ac624d0 | ||
|
|
f3f5cc42c9 | ||
|
|
313fe8b734 | ||
|
|
0d693386d1 | ||
|
|
d1aee1e874 | ||
|
|
3528e7da51 | ||
|
|
fe2fbc3b97 | ||
|
|
5c2e06c98d | ||
|
|
26df619b4f | ||
|
|
af2ea04442 | ||
|
|
383f72580a | ||
|
|
5e6b4f74e0 | ||
|
|
22185c5440 |
30
build.gradle
30
build.gradle
@@ -19,7 +19,7 @@ buildscript {
|
|||||||
username smartThingsArtifactoryUserName
|
username smartThingsArtifactoryUserName
|
||||||
password smartThingsArtifactoryPassword
|
password smartThingsArtifactoryPassword
|
||||||
}
|
}
|
||||||
url "http://artifactory.smartthings.com/libs-release-local"
|
url "https://artifactory.smartthings.com/libs-release-local"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,9 +27,37 @@ buildscript {
|
|||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
jcenter()
|
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 {
|
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 {
|
slackSendMessage {
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ machine:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
override:
|
override:
|
||||||
- echo "Nothing to do."
|
- ./gradlew dependencies -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||||
|
post:
|
||||||
|
- ./gradlew compileSmartappsGroovy compileDevicetypesGroovy -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") {
|
definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") {
|
||||||
|
capability "Sensor"
|
||||||
capability "Relative Humidity Measurement"
|
capability "Relative Humidity Measurement"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") {
|
definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") {
|
||||||
|
capability "Sensor"
|
||||||
capability "Relative Humidity Measurement"
|
capability "Relative Humidity Measurement"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") {
|
definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") {
|
||||||
|
capability "Sensor"
|
||||||
capability "Relative Humidity Measurement"
|
capability "Relative Humidity Measurement"
|
||||||
capability "Temperature Measurement"
|
capability "Temperature Measurement"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") {
|
definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") {
|
||||||
|
capability "Sensor"
|
||||||
|
|
||||||
attribute "rain", "number"
|
attribute "rain", "number"
|
||||||
attribute "rainSumHour", "number"
|
attribute "rainSumHour", "number"
|
||||||
attribute "rainSumDay", "number"
|
attribute "rainSumDay", "number"
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ metadata {
|
|||||||
tiles(scale: 2) {
|
tiles(scale: 2) {
|
||||||
multiAttributeTile(name:"FGMS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
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") {
|
tileAttribute("device.motion", key:"PRIMARY_CONTROL") {
|
||||||
attributeState("inactive", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
|
attributeState("inactive", label:"no motion", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
|
||||||
attributeState("active", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
|
attributeState("active", label:"motion", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
|
||||||
}
|
}
|
||||||
|
|
||||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||||
@@ -278,4 +278,4 @@ private encap(physicalgraph.zwave.Command cmd) {
|
|||||||
} else {
|
} else {
|
||||||
crc16(cmd)
|
crc16(cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ metadata {
|
|||||||
|
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Polling"
|
|
||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Switch"
|
capability "Switch"
|
||||||
capability "Switch Level"
|
capability "Switch Level"
|
||||||
@@ -67,12 +66,6 @@ def parse(String description) {
|
|||||||
def resultMap = zigbee.getEvent(description)
|
def resultMap = zigbee.getEvent(description)
|
||||||
if (resultMap) {
|
if (resultMap) {
|
||||||
sendEvent(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 {
|
else {
|
||||||
log.debug "DID NOT PARSE MESSAGE for description : $description"
|
log.debug "DID NOT PARSE MESSAGE for description : $description"
|
||||||
@@ -96,27 +89,24 @@ def setLevel(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.levelRefresh()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig()
|
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
def poll() {
|
def healthPoll() {
|
||||||
zigbee.onOffRefresh() + zigbee.levelRefresh()
|
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||||
|
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
|
unschedule()
|
||||||
|
schedule("0 0/5 * * * ? *", "healthPoll")
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
sendEvent(name: "checkInterval", value: 1200, displayed: false, data: [protocol: "zigbee"])
|
// Device-Watch allows 2 check-in misses from device
|
||||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
sendEvent(name: "checkInterval", value: 60 * 12, 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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
void installed() {
|
||||||
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
|
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ def parse(description) {
|
|||||||
log.trace "HUE BRIDGE, GENERATING EVENT: $map.name: $map.value"
|
log.trace "HUE BRIDGE, GENERATING EVENT: $map.name: $map.value"
|
||||||
results << createEvent(name: "${map.name}", value: "${map.value}")
|
results << createEvent(name: "${map.name}", value: "${map.value}")
|
||||||
} else {
|
} else {
|
||||||
log.trace "Parsing description"
|
log.trace "Parsing description"
|
||||||
def msg = parseLanMessage(description)
|
def msg = parseLanMessage(description)
|
||||||
if (msg.body) {
|
if (msg.body) {
|
||||||
def contentType = msg.headers["Content-Type"]
|
def contentType = msg.headers["Content-Type"]
|
||||||
@@ -72,13 +72,13 @@ def parse(description) {
|
|||||||
log.info "Bridge response: $msg.body"
|
log.info "Bridge response: $msg.body"
|
||||||
} else {
|
} else {
|
||||||
// Sending Bulbs List to parent"
|
// Sending Bulbs List to parent"
|
||||||
if (parent.state.inBulbDiscovery)
|
if (parent.isInBulbDiscovery())
|
||||||
log.info parent.bulbListHandler(device.hub.id, msg.body)
|
log.info parent.bulbListHandler(device.hub.id, msg.body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (contentType?.contains("xml")) {
|
else if (contentType?.contains("xml")) {
|
||||||
log.debug "HUE BRIDGE ALREADY PRESENT"
|
log.debug "HUE BRIDGE ALREADY PRESENT"
|
||||||
parent.hubVerification(device.hub.id, msg.body)
|
parent.hubVerification(device.hub.id, msg.body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
void installed() {
|
||||||
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
|
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
void installed() {
|
||||||
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
|
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installed() {
|
void installed() {
|
||||||
sendEvent(name: "checkInterval", value: 60 * 30, data: [protocol: "lan"], displayed: false)
|
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse events into attributes
|
// parse events into attributes
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ def parse(String description) {
|
|||||||
|
|
||||||
if (descMap.cluster == "0300") {
|
if (descMap.cluster == "0300") {
|
||||||
if(descMap.attrId == "0000"){ //Hue Attribute
|
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"
|
log.debug "Hue value returned is $hueValue"
|
||||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ def setLevel(value) {
|
|||||||
|
|
||||||
//input Hue Integer values; returns color name for saturation 100%
|
//input Hue Integer values; returns color name for saturation 100%
|
||||||
private getColorName(hueValue){
|
private getColorName(hueValue){
|
||||||
if(hueValue>360 || hueValue<0)
|
if(hueValue>100 || hueValue<0)
|
||||||
return
|
return
|
||||||
|
|
||||||
hueValue = Math.round(hueValue / 100 * 360)
|
hueValue = Math.round(hueValue / 100 * 360)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
Osram Flex RGBW Light Strip
|
Osram Flex RGBW Light Strip
|
||||||
|
|
||||||
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
|
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "OSRAM LIGHTIFY LED Flexible Strip RGBW", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "OSRAM LIGHTIFY LED Flexible Strip RGBW", namespace: "smartthings", author: "SmartThings") {
|
||||||
|
|
||||||
capability "Color Temperature"
|
capability "Color Temperature"
|
||||||
capability "Actuator"
|
capability "Actuator"
|
||||||
capability "Switch"
|
capability "Switch"
|
||||||
@@ -18,7 +18,7 @@ metadata {
|
|||||||
capability "Refresh"
|
capability "Refresh"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
capability "Color Control"
|
capability "Color Control"
|
||||||
|
|
||||||
attribute "colorName", "string"
|
attribute "colorName", "string"
|
||||||
|
|
||||||
command "setAdjustedColor"
|
command "setAdjustedColor"
|
||||||
@@ -49,7 +49,7 @@ metadata {
|
|||||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
}
|
}
|
||||||
|
|
||||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
|
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
|
||||||
state "colorTemperature", action:"color temperature.setColorTemperature"
|
state "colorTemperature", action:"color temperature.setColorTemperature"
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ def parse(String description) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(descMap.attrId == "0000"){ //Hue Attribute
|
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"
|
log.debug "Hue value returned is $hueValue"
|
||||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||||
}
|
}
|
||||||
@@ -274,7 +274,7 @@ private getGenericName(value){
|
|||||||
|
|
||||||
//input Hue Integer values; returns color name for saturation 100%
|
//input Hue Integer values; returns color name for saturation 100%
|
||||||
private getColorName(hueValue){
|
private getColorName(hueValue){
|
||||||
if(hueValue>360 || hueValue<0)
|
if(hueValue>100 || hueValue<0)
|
||||||
return
|
return
|
||||||
|
|
||||||
hueValue = Math.round(hueValue / 100 * 360)
|
hueValue = Math.round(hueValue / 100 * 360)
|
||||||
@@ -449,7 +449,7 @@ def setColor(value){
|
|||||||
def level = hex(value.level * 255 / 100)
|
def level = hex(value.level * 255 / 100)
|
||||||
cmd << zigbeeSetLevel(level)
|
cmd << zigbeeSetLevel(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.switch == "off") {
|
if (value.switch == "off") {
|
||||||
cmd << "delay 150"
|
cmd << "delay 150"
|
||||||
cmd << off()
|
cmd << off()
|
||||||
|
|||||||
@@ -101,12 +101,6 @@ def parse(String description) {
|
|||||||
else {
|
else {
|
||||||
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
||||||
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
|
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 {
|
else {
|
||||||
@@ -126,15 +120,7 @@ def on() {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.onOffRefresh()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -142,8 +128,10 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
sendEvent(name: "checkInterval", value: 1200, displayed: false, data: [protocol: "zigbee"])
|
// Device-Watch allows 2 check-in misses from device
|
||||||
zigbee.onOffConfig() + powerConfig() + refresh()
|
sendEvent(name: "checkInterval", value: 60 * 12, 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)
|
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
|
||||||
|
|||||||
@@ -101,13 +101,6 @@ def parse(String description) {
|
|||||||
map = parseIasMessage(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"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
@@ -233,6 +226,8 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
if (roundedPct <= 0)
|
||||||
|
roundedPct = 1
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
@@ -283,15 +278,7 @@ private Map getMoistureResult(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -305,23 +292,19 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
// Device-Watch allows 2 check-in misses from device
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
||||||
|
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
def configCmds = [
|
def enrollCmds = [
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
"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() {
|
def enrollResponse() {
|
||||||
|
|||||||
@@ -105,13 +105,6 @@ def parse(String description) {
|
|||||||
map = parseIasMessage(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"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
@@ -248,6 +241,8 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
if (roundedPct <= 0)
|
||||||
|
roundedPct = 1
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
@@ -294,15 +289,7 @@ private Map getMotionResult(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -316,24 +303,19 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
// Device-Watch allows 2 check-in misses from device
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
||||||
|
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
|
|
||||||
def configCmds = [
|
def enrollCmds = [
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
"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() {
|
def enrollResponse() {
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
if (roundedPct <= 0)
|
||||||
|
roundedPct = 1
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,13 +127,6 @@ def parse(String description) {
|
|||||||
map = parseIasMessage(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
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
if (description?.startsWith('enroll request')) {
|
if (description?.startsWith('enroll request')) {
|
||||||
@@ -313,6 +306,8 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
if (roundedPct <= 0)
|
||||||
|
roundedPct = 1
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||||
}
|
}
|
||||||
@@ -376,15 +371,7 @@ private getAccelerationResult(numValue) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -414,13 +401,16 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
// Device-Watch allows 2 check-in misses from device
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
||||||
|
|
||||||
log.debug "Configuring Reporting"
|
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() +
|
def configCmds = enrollResponse() +
|
||||||
zigbee.batteryConfig() +
|
zigbee.batteryConfig() +
|
||||||
zigbee.temperatureConfig() +
|
zigbee.temperatureConfig(30, 300) +
|
||||||
zigbee.configureReporting(0xFC02, 0x0010, 0x18, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
|
zigbee.configureReporting(0xFC02, 0x0010, 0x18, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
|
||||||
zigbee.configureReporting(0xFC02, 0x0012, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
zigbee.configureReporting(0xFC02, 0x0012, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||||
zigbee.configureReporting(0xFC02, 0x0013, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
zigbee.configureReporting(0xFC02, 0x0013, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ def getTemperature(value) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
if (roundedPct <= 0)
|
||||||
|
roundedPct = 1
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,13 +92,6 @@ def parse(String description) {
|
|||||||
map = parseIasMessage(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"
|
log.debug "Parse returned $map"
|
||||||
def result = map ? createEvent(map) : null
|
def result = map ? createEvent(map) : null
|
||||||
|
|
||||||
@@ -207,6 +200,8 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
if (roundedPct <= 0)
|
||||||
|
roundedPct = 1
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
@@ -246,15 +241,7 @@ private Map getContactResult(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -268,23 +255,19 @@ def refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
// Device-Watch allows 2 check-in misses from device
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
||||||
|
|
||||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||||
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
def configCmds = [
|
def enrollCmds = [
|
||||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
"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() {
|
def enrollResponse() {
|
||||||
|
|||||||
@@ -83,13 +83,6 @@ def parse(String description) {
|
|||||||
map = parseCustomMessage(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"
|
log.debug "Parse returned $map"
|
||||||
return map ? createEvent(map) : null
|
return map ? createEvent(map) : null
|
||||||
}
|
}
|
||||||
@@ -214,6 +207,8 @@ private Map getBatteryResult(rawValue) {
|
|||||||
def maxVolts = 3.0
|
def maxVolts = 3.0
|
||||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
def roundedPct = Math.round(pct * 100)
|
def roundedPct = Math.round(pct * 100)
|
||||||
|
if (roundedPct <= 0)
|
||||||
|
roundedPct = 1
|
||||||
result.value = Math.min(100, roundedPct)
|
result.value = Math.min(100, roundedPct)
|
||||||
result.descriptionText = "${linkText} battery was ${result.value}%"
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
}
|
}
|
||||||
@@ -251,14 +246,7 @@ private Map getHumidityResult(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
if (state.lastActivity < (now() - (1000 * device.currentValue("checkInterval"))) ){
|
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh()
|
def refresh()
|
||||||
@@ -276,23 +264,19 @@ def refresh()
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
sendEvent(name: "checkInterval", value: 14400, displayed: false, data: [protocol: "zigbee"])
|
// Device-Watch allows 2 check-in misses from device
|
||||||
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
||||||
|
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
def configCmds = [
|
def humidityConfigCmds = [
|
||||||
"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",
|
|
||||||
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
|
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
|
||||||
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}",
|
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}",
|
||||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
"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) {
|
private hex(value) {
|
||||||
|
|||||||
@@ -54,12 +54,6 @@ def parse(String description) {
|
|||||||
|
|
||||||
def event = zigbee.getEvent(description)
|
def event = zigbee.getEvent(description)
|
||||||
if (event) {
|
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) {}
|
if (event.name=="level" && event.value==0) {}
|
||||||
else {
|
else {
|
||||||
sendEvent(event)
|
sendEvent(event)
|
||||||
@@ -86,15 +80,7 @@ def setLevel(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.onOffRefresh()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -103,7 +89,8 @@ def refresh() {
|
|||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
// Device-Watch allows 2 check-in misses from device
|
||||||
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
||||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||||
|
zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,12 +85,6 @@ def parse(String description) {
|
|||||||
def event = zigbee.getEvent(description)
|
def event = zigbee.getEvent(description)
|
||||||
if (event) {
|
if (event) {
|
||||||
log.debug 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) {}
|
if (event.name=="level" && event.value==0) {}
|
||||||
else {
|
else {
|
||||||
if (event.name=="colorTemperature") {
|
if (event.name=="colorTemperature") {
|
||||||
@@ -105,7 +99,7 @@ def parse(String description) {
|
|||||||
|
|
||||||
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
||||||
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
|
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")
|
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
|
||||||
}
|
}
|
||||||
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
|
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
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.onOffRefresh()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -147,9 +133,10 @@ def refresh() {
|
|||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
// Device-Watch allows 2 check-in misses from device
|
||||||
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
sendEvent(name: "checkInterval", value: 60 * 12, 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)
|
// 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) {
|
def setColorTemperature(value) {
|
||||||
|
|||||||
@@ -74,12 +74,6 @@ def parse(String description) {
|
|||||||
log.debug "description is $description"
|
log.debug "description is $description"
|
||||||
def event = zigbee.getEvent(description)
|
def event = zigbee.getEvent(description)
|
||||||
if (event) {
|
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) {}
|
if (event.name=="level" && event.value==0) {}
|
||||||
else {
|
else {
|
||||||
if (event.name=="colorTemperature") {
|
if (event.name=="colorTemperature") {
|
||||||
@@ -110,15 +104,7 @@ def setLevel(value) {
|
|||||||
* PING is used by Device-Watch in attempt to reach the Device
|
* PING is used by Device-Watch in attempt to reach the Device
|
||||||
* */
|
* */
|
||||||
def ping() {
|
def ping() {
|
||||||
|
return zigbee.onOffRefresh()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
@@ -127,9 +113,10 @@ def refresh() {
|
|||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
log.debug "Configuring Reporting and Bindings."
|
log.debug "Configuring Reporting and Bindings."
|
||||||
// Enrolls device to Device-Watch with 3 x Reporting interval 30min
|
// Device-Watch allows 2 check-in misses from device
|
||||||
sendEvent(name: "checkInterval", value: 1800, displayed: false, data: [protocol: "zigbee"])
|
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee"])
|
||||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
// 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) {
|
def setColorTemperature(value) {
|
||||||
|
|||||||
@@ -65,7 +65,16 @@ void updateSwitch() {
|
|||||||
private void updateAll(devices) {
|
private void updateAll(devices) {
|
||||||
def command = request.JSON?.command
|
def command = request.JSON?.command
|
||||||
if (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) {
|
if (!device) {
|
||||||
httpError(404, "Device not found")
|
httpError(404, "Device not found")
|
||||||
} else {
|
} 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.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def authPage() {
|
|||||||
if (canInstallLabs()) {
|
if (canInstallLabs()) {
|
||||||
|
|
||||||
def redirectUrl = getBuildRedirectUrl()
|
def redirectUrl = getBuildRedirectUrl()
|
||||||
log.debug "Redirect url = ${redirectUrl}"
|
// log.debug "Redirect url = ${redirectUrl}"
|
||||||
|
|
||||||
if (state.authToken) {
|
if (state.authToken) {
|
||||||
description = "Tap 'Next' to proceed"
|
description = "Tap 'Next' to proceed"
|
||||||
@@ -113,13 +113,13 @@ def oauthInitUrl() {
|
|||||||
scope: "read_station"
|
scope: "read_station"
|
||||||
]
|
]
|
||||||
|
|
||||||
log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
|
// log.debug "REDIRECT URL: ${getVendorAuthPath() + toQueryString(oauthParams)}"
|
||||||
|
|
||||||
redirect (location: getVendorAuthPath() + toQueryString(oauthParams))
|
redirect (location: getVendorAuthPath() + toQueryString(oauthParams))
|
||||||
}
|
}
|
||||||
|
|
||||||
def callback() {
|
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 code = params.code
|
||||||
def oauthState = params.state
|
def oauthState = params.state
|
||||||
@@ -135,7 +135,7 @@ def callback() {
|
|||||||
scope: "read_station"
|
scope: "read_station"
|
||||||
]
|
]
|
||||||
|
|
||||||
log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
|
// log.debug "TOKEN URL: ${getVendorTokenPath() + toQueryString(tokenParams)}"
|
||||||
|
|
||||||
def tokenUrl = getVendorTokenPath()
|
def tokenUrl = getVendorTokenPath()
|
||||||
def params = [
|
def params = [
|
||||||
@@ -144,7 +144,7 @@ def callback() {
|
|||||||
body: tokenParams
|
body: tokenParams
|
||||||
]
|
]
|
||||||
|
|
||||||
log.debug "PARAMS: ${params}"
|
// log.debug "PARAMS: ${params}"
|
||||||
|
|
||||||
httpPost(params) { resp ->
|
httpPost(params) { resp ->
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ def callback() {
|
|||||||
state.refreshToken = data.refresh_token
|
state.refreshToken = data.refresh_token
|
||||||
state.authToken = data.access_token
|
state.authToken = data.access_token
|
||||||
state.tokenExpires = now() + (data.expires_in * 1000)
|
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 ->
|
response.data.each {key, value ->
|
||||||
def data = slurper.parseText(key);
|
def data = slurper.parseText(key);
|
||||||
log.debug "Data: $data"
|
// log.debug "Data: $data"
|
||||||
|
|
||||||
state.refreshToken = data.refresh_token
|
state.refreshToken = data.refresh_token
|
||||||
state.accessToken = data.access_token
|
state.accessToken = data.access_token
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ mappings {
|
|||||||
path("/receivedToken") { action: [ POST: "receivedToken", GET: "receivedToken"] }
|
path("/receivedToken") { action: [ POST: "receivedToken", GET: "receivedToken"] }
|
||||||
path("/receiveToken") { action: [ POST: "receiveToken", GET: "receiveToken"] }
|
path("/receiveToken") { action: [ POST: "receiveToken", GET: "receiveToken"] }
|
||||||
path("/hookCallback") { action: [ POST: "hookEventHandler", GET: "hookEventHandler"] }
|
path("/hookCallback") { action: [ POST: "hookEventHandler", GET: "hookEventHandler"] }
|
||||||
path("/oauth/initialize") {action: [GET: "oauthInitUrl"]}
|
path("/oauth/initialize") {action: [GET: "oauthInitUrl"]}
|
||||||
path("/oauth/callback") { action: [ GET: "callback" ] }
|
path("/oauth/callback") { action: [ GET: "callback" ] }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ def callback() {
|
|||||||
} else {
|
} else {
|
||||||
log.warn "No authQueryString"
|
log.warn "No authQueryString"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.JawboneAccessToken) {
|
if (state.JawboneAccessToken) {
|
||||||
log.debug "Access token already exists"
|
log.debug "Access token already exists"
|
||||||
setup()
|
setup()
|
||||||
@@ -73,7 +73,7 @@ def callback() {
|
|||||||
|
|
||||||
def authPage() {
|
def authPage() {
|
||||||
log.debug "authPage"
|
log.debug "authPage"
|
||||||
def description = null
|
def description = null
|
||||||
if (state.JawboneAccessToken == null) {
|
if (state.JawboneAccessToken == null) {
|
||||||
if (!state.accessToken) {
|
if (!state.accessToken) {
|
||||||
log.debug "About to create access token"
|
log.debug "About to create access token"
|
||||||
@@ -82,12 +82,13 @@ def authPage() {
|
|||||||
description = "Click to enter Jawbone Credentials"
|
description = "Click to enter Jawbone Credentials"
|
||||||
def redirectUrl = buildRedirectUrl
|
def redirectUrl = buildRedirectUrl
|
||||||
log.debug "RedirectURL = ${redirectUrl}"
|
log.debug "RedirectURL = ${redirectUrl}"
|
||||||
def donebutton= state.JawboneAccessToken != null
|
def donebutton= state.JawboneAccessToken != null
|
||||||
return dynamicPage(name: "Credentials", title: "Jawbone UP", nextPage: null, uninstall: true, install: donebutton) {
|
return dynamicPage(name: "Credentials", title: "Jawbone UP", nextPage: null, uninstall: true, install: donebutton) {
|
||||||
|
section { paragraph title: "Note:", "This device has not been officially tested and certified to “Work with SmartThings”. You can connect it to your SmartThings home but performance may vary and we will not be able to provide support or assistance." }
|
||||||
section { href url:redirectUrl, style:"embedded", required:true, title:"Jawbone UP", state: hast ,description:description }
|
section { href url:redirectUrl, style:"embedded", required:true, title:"Jawbone UP", state: hast ,description:description }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
description = "Jawbone Credentials Already Entered."
|
description = "Jawbone Credentials Already Entered."
|
||||||
return dynamicPage(name: "Credentials", title: "Jawbone UP", uninstall: true, install:true) {
|
return dynamicPage(name: "Credentials", title: "Jawbone UP", uninstall: true, install:true) {
|
||||||
section { href url: buildRedirectUrl("receivedToken"), style:"embedded", state: "complete", title:"Jawbone UP", description:description }
|
section { href url: buildRedirectUrl("receivedToken"), style:"embedded", state: "complete", title:"Jawbone UP", description:description }
|
||||||
}
|
}
|
||||||
@@ -107,7 +108,7 @@ def receiveToken(redirectUrl = null) {
|
|||||||
def params = [
|
def params = [
|
||||||
uri: "https://jawbone.com/auth/oauth2/token?${toQueryString(oauthParams)}",
|
uri: "https://jawbone.com/auth/oauth2/token?${toQueryString(oauthParams)}",
|
||||||
]
|
]
|
||||||
httpGet(params) { response ->
|
httpGet(params) { response ->
|
||||||
log.debug "${response.data}"
|
log.debug "${response.data}"
|
||||||
log.debug "Setting access token to ${response.data.access_token}, refresh token to ${response.data.refresh_token}"
|
log.debug "Setting access token to ${response.data.access_token}, refresh token to ${response.data.refresh_token}"
|
||||||
state.JawboneAccessToken = response.data.access_token
|
state.JawboneAccessToken = response.data.access_token
|
||||||
@@ -149,7 +150,7 @@ def connectionStatus(message, redirectUrl = null) {
|
|||||||
<meta http-equiv="refresh" content="3; url=${redirectUrl}" />
|
<meta http-equiv="refresh" content="3; url=${redirectUrl}" />
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
def html = """
|
def html = """
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@@ -229,12 +230,12 @@ def validateCurrentToken() {
|
|||||||
log.debug "validateCurrentToken"
|
log.debug "validateCurrentToken"
|
||||||
def url = "https://jawbone.com/nudge/api/v.1.1/users/@me/refreshToken"
|
def url = "https://jawbone.com/nudge/api/v.1.1/users/@me/refreshToken"
|
||||||
def requestBody = "secret=${appSettings.clientSecret}"
|
def requestBody = "secret=${appSettings.clientSecret}"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
httpPost(uri: url, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ], body: requestBody) {response ->
|
httpPost(uri: url, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ], body: requestBody) {response ->
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
log.debug "${response.data}"
|
log.debug "${response.data}"
|
||||||
log.debug "Setting refresh token to ${response.data.data.refresh_token}"
|
log.debug "Setting refresh token"
|
||||||
state.refreshToken = response.data.data.refresh_token
|
state.refreshToken = response.data.data.refresh_token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +259,7 @@ def validateCurrentToken() {
|
|||||||
state.remove("refreshToken")
|
state.remove("refreshToken")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug "Setting access token to ${data.access_token}, refresh token to ${data.refresh_token}"
|
log.debug "Setting access token"
|
||||||
state.JawboneAccessToken = data.access_token
|
state.JawboneAccessToken = data.access_token
|
||||||
state.refreshToken = data.refresh_token
|
state.refreshToken = data.refresh_token
|
||||||
}
|
}
|
||||||
@@ -271,10 +272,10 @@ def validateCurrentToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def initialize() {
|
def initialize() {
|
||||||
log.debug "Callback URL - Webhook"
|
log.debug "Callback URL - Webhook"
|
||||||
def localServerUrl = getApiServerUrl()
|
def localServerUrl = getApiServerUrl()
|
||||||
def hookUrl = "${localServerUrl}/api/token/${state.accessToken}/smartapps/installations/${app.id}/hookCallback"
|
def hookUrl = "${localServerUrl}/api/token/${state.accessToken}/smartapps/installations/${app.id}/hookCallback"
|
||||||
def webhook = "https://jawbone.com/nudge/api/v.1.1/users/@me/pubsub?webhook=$hookUrl"
|
def webhook = "https://jawbone.com/nudge/api/v.1.1/users/@me/pubsub?webhook=$hookUrl"
|
||||||
httpPost(uri: webhook, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ])
|
httpPost(uri: webhook, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,16 +285,16 @@ def setup() {
|
|||||||
|
|
||||||
if (state.JawboneAccessToken) {
|
if (state.JawboneAccessToken) {
|
||||||
def urlmember = "https://jawbone.com/nudge/api/users/@me/"
|
def urlmember = "https://jawbone.com/nudge/api/users/@me/"
|
||||||
def member = null
|
def member = null
|
||||||
httpGet(uri: urlmember, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
httpGet(uri: urlmember, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
||||||
member = response.data.data
|
member = response.data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
if (member) {
|
if (member) {
|
||||||
state.member = member
|
state.member = member
|
||||||
def externalId = "${app.id}.${member.xid}"
|
def externalId = "${app.id}.${member.xid}"
|
||||||
|
|
||||||
// find the appropriate child device based on my app id and the device network id
|
// find the appropriate child device based on my app id and the device network id
|
||||||
def deviceWrapper = getChildDevice("${externalId}")
|
def deviceWrapper = getChildDevice("${externalId}")
|
||||||
|
|
||||||
// invoke the generatePresenceEvent method on the child device
|
// invoke the generatePresenceEvent method on the child device
|
||||||
@@ -312,7 +313,7 @@ def setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def installed() {
|
def installed() {
|
||||||
|
|
||||||
if (!state.accessToken) {
|
if (!state.accessToken) {
|
||||||
log.debug "About to create access token"
|
log.debug "About to create access token"
|
||||||
createAccessToken()
|
createAccessToken()
|
||||||
@@ -324,7 +325,7 @@ def installed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
|
|
||||||
if (!state.accessToken) {
|
if (!state.accessToken) {
|
||||||
log.debug "About to create access token"
|
log.debug "About to create access token"
|
||||||
createAccessToken()
|
createAccessToken()
|
||||||
@@ -348,29 +349,29 @@ def uninstalled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def pollChild(childDevice) {
|
def pollChild(childDevice) {
|
||||||
def member = state.member
|
def member = state.member
|
||||||
generatePollingEvents (member, childDevice)
|
generatePollingEvents (member, childDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
def generatePollingEvents (member, childDevice) {
|
def generatePollingEvents (member, childDevice) {
|
||||||
// lets figure out if the member is currently "home" (At the place)
|
// lets figure out if the member is currently "home" (At the place)
|
||||||
def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals"
|
def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals"
|
||||||
def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves"
|
def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves"
|
||||||
def urlsleeps = "https://jawbone.com/nudge/api/users/@me/sleeps"
|
def urlsleeps = "https://jawbone.com/nudge/api/users/@me/sleeps"
|
||||||
def goals = null
|
def goals = null
|
||||||
def moves = null
|
def moves = null
|
||||||
def sleeps = null
|
def sleeps = null
|
||||||
httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
||||||
goals = response.data.data
|
goals = response.data.data
|
||||||
}
|
}
|
||||||
httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
||||||
moves = response.data.data.items[0]
|
moves = response.data.data.items[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
try { // we are going to just ignore any errors
|
try { // we are going to just ignore any errors
|
||||||
log.debug "Member = ${member.first}"
|
log.debug "Member = ${member.first}"
|
||||||
log.debug "Moves Goal = ${goals.move_steps} Steps"
|
log.debug "Moves Goal = ${goals.move_steps} Steps"
|
||||||
log.debug "Moves = ${moves.details.steps} Steps"
|
log.debug "Moves = ${moves.details.steps} Steps"
|
||||||
|
|
||||||
childDevice?.sendEvent(name:"steps", value: moves.details.steps)
|
childDevice?.sendEvent(name:"steps", value: moves.details.steps)
|
||||||
childDevice?.sendEvent(name:"goal", value: goals.move_steps)
|
childDevice?.sendEvent(name:"goal", value: goals.move_steps)
|
||||||
@@ -378,29 +379,29 @@ def generatePollingEvents (member, childDevice) {
|
|||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
// eat it
|
// eat it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def generateInitialEvent (member, childDevice) {
|
def generateInitialEvent (member, childDevice) {
|
||||||
// lets figure out if the member is currently "home" (At the place)
|
// lets figure out if the member is currently "home" (At the place)
|
||||||
def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals"
|
def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals"
|
||||||
def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves"
|
def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves"
|
||||||
def urlsleeps = "https://jawbone.com/nudge/api/users/@me/sleeps"
|
def urlsleeps = "https://jawbone.com/nudge/api/users/@me/sleeps"
|
||||||
def goals = null
|
def goals = null
|
||||||
def moves = null
|
def moves = null
|
||||||
def sleeps = null
|
def sleeps = null
|
||||||
httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
||||||
goals = response.data.data
|
goals = response.data.data
|
||||||
}
|
}
|
||||||
httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
||||||
moves = response.data.data.items[0]
|
moves = response.data.data.items[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
try { // we are going to just ignore any errors
|
try { // we are going to just ignore any errors
|
||||||
log.debug "Member = ${member.first}"
|
log.debug "Member = ${member.first}"
|
||||||
log.debug "Moves Goal = ${goals.move_steps} Steps"
|
log.debug "Moves Goal = ${goals.move_steps} Steps"
|
||||||
log.debug "Moves = ${moves.details.steps} Steps"
|
log.debug "Moves = ${moves.details.steps} Steps"
|
||||||
log.debug "Sleeping state = false"
|
log.debug "Sleeping state = false"
|
||||||
childDevice?.generateSleepingEvent(false)
|
childDevice?.generateSleepingEvent(false)
|
||||||
childDevice?.sendEvent(name:"steps", value: moves.details.steps)
|
childDevice?.sendEvent(name:"steps", value: moves.details.steps)
|
||||||
childDevice?.sendEvent(name:"goal", value: goals.move_steps)
|
childDevice?.sendEvent(name:"goal", value: goals.move_steps)
|
||||||
@@ -408,27 +409,27 @@ def generateInitialEvent (member, childDevice) {
|
|||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
// eat it
|
// eat it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def setColor (steps,goal,childDevice) {
|
def setColor (steps,goal,childDevice) {
|
||||||
def result = steps * 100 / goal
|
def result = steps * 100 / goal
|
||||||
if (result < 25)
|
if (result < 25)
|
||||||
childDevice?.sendEvent(name:"steps", value: "steps", label: steps)
|
childDevice?.sendEvent(name:"steps", value: "steps", label: steps)
|
||||||
else if ((result >= 25) && (result < 50))
|
else if ((result >= 25) && (result < 50))
|
||||||
childDevice?.sendEvent(name:"steps", value: "steps1", label: steps)
|
childDevice?.sendEvent(name:"steps", value: "steps1", label: steps)
|
||||||
else if ((result >= 50) && (result < 75))
|
else if ((result >= 50) && (result < 75))
|
||||||
childDevice?.sendEvent(name:"steps", value: "steps1", label: steps)
|
childDevice?.sendEvent(name:"steps", value: "steps1", label: steps)
|
||||||
else if (result >= 75)
|
else if (result >= 75)
|
||||||
childDevice?.sendEvent(name:"steps", value: "stepsgoal", label: steps)
|
childDevice?.sendEvent(name:"steps", value: "stepsgoal", label: steps)
|
||||||
}
|
}
|
||||||
|
|
||||||
def hookEventHandler() {
|
def hookEventHandler() {
|
||||||
// log.debug "In hookEventHandler method."
|
// log.debug "In hookEventHandler method."
|
||||||
log.debug "request = ${request}"
|
log.debug "request = ${request}"
|
||||||
|
|
||||||
def json = request.JSON
|
def json = request.JSON
|
||||||
|
|
||||||
// get some stuff we need
|
// get some stuff we need
|
||||||
def userId = json.events.user_xid[0]
|
def userId = json.events.user_xid[0]
|
||||||
def json_type = json.events.type[0]
|
def json_type = json.events.type[0]
|
||||||
@@ -437,39 +438,39 @@ def hookEventHandler() {
|
|||||||
//log.debug json
|
//log.debug json
|
||||||
log.debug "Userid = ${userId}"
|
log.debug "Userid = ${userId}"
|
||||||
log.debug "Notification Type: " + json_type
|
log.debug "Notification Type: " + json_type
|
||||||
log.debug "Notification Action: " + json_action
|
log.debug "Notification Action: " + json_action
|
||||||
|
|
||||||
// find the appropriate child device based on my app id and the device network id
|
// find the appropriate child device based on my app id and the device network id
|
||||||
def externalId = "${app.id}.${userId}"
|
def externalId = "${app.id}.${userId}"
|
||||||
def childDevice = getChildDevice("${externalId}")
|
def childDevice = getChildDevice("${externalId}")
|
||||||
|
|
||||||
if (childDevice) {
|
if (childDevice) {
|
||||||
switch (json_action) {
|
switch (json_action) {
|
||||||
case "enter_sleep_mode":
|
case "enter_sleep_mode":
|
||||||
childDevice?.generateSleepingEvent(true)
|
childDevice?.generateSleepingEvent(true)
|
||||||
break
|
break
|
||||||
case "exit_sleep_mode":
|
case "exit_sleep_mode":
|
||||||
childDevice?.generateSleepingEvent(false)
|
childDevice?.generateSleepingEvent(false)
|
||||||
break
|
break
|
||||||
case "creation":
|
case "creation":
|
||||||
childDevice?.sendEvent(name:"steps", value: 0)
|
childDevice?.sendEvent(name:"steps", value: 0)
|
||||||
break
|
break
|
||||||
case "updation":
|
case "updation":
|
||||||
def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals"
|
def urlgoals = "https://jawbone.com/nudge/api/users/@me/goals"
|
||||||
def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves"
|
def urlmoves = "https://jawbone.com/nudge/api/users/@me/moves"
|
||||||
def goals = null
|
def goals = null
|
||||||
def moves = null
|
def moves = null
|
||||||
httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
httpGet(uri: urlgoals, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
||||||
goals = response.data.data
|
goals = response.data.data
|
||||||
}
|
}
|
||||||
httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
httpGet(uri: urlmoves, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) {response ->
|
||||||
moves = response.data.data.items[0]
|
moves = response.data.data.items[0]
|
||||||
}
|
}
|
||||||
log.debug "Goal = ${goals.move_steps} Steps"
|
log.debug "Goal = ${goals.move_steps} Steps"
|
||||||
log.debug "Steps = ${moves.details.steps} Steps"
|
log.debug "Steps = ${moves.details.steps} Steps"
|
||||||
childDevice?.sendEvent(name:"steps", value: moves.details.steps)
|
childDevice?.sendEvent(name:"steps", value: moves.details.steps)
|
||||||
childDevice?.sendEvent(name:"goal", value: goals.move_steps)
|
childDevice?.sendEvent(name:"goal", value: goals.move_steps)
|
||||||
//setColor(moves.details.steps,goals.move_steps,childDevice)
|
//setColor(moves.details.steps,goals.move_steps,childDevice)
|
||||||
break
|
break
|
||||||
case "deletion":
|
case "deletion":
|
||||||
app.delete()
|
app.delete()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* for the specific language governing permissions and limitations under the License.
|
* for the specific language governing permissions and limitations under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
definition(
|
definition(
|
||||||
name: "Smart Home Ventilation",
|
name: "Smart Home Ventilation",
|
||||||
namespace: "MichaelStruck",
|
namespace: "MichaelStruck",
|
||||||
@@ -164,7 +164,7 @@ def installed() {
|
|||||||
def updated() {
|
def updated() {
|
||||||
unschedule()
|
unschedule()
|
||||||
turnOffSwitch() //Turn off all switches if the schedules are changed while in mid-schedule
|
turnOffSwitch() //Turn off all switches if the schedules are changed while in mid-schedule
|
||||||
unsubscribe
|
unsubscribe()
|
||||||
log.debug "Updated with settings: ${settings}"
|
log.debug "Updated with settings: ${settings}"
|
||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
@@ -174,12 +174,12 @@ def init() {
|
|||||||
schedule (midnightTime, midNight)
|
schedule (midnightTime, midNight)
|
||||||
subscribe(location, "mode", locationHandler)
|
subscribe(location, "mode", locationHandler)
|
||||||
startProcess()
|
startProcess()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common methods
|
// Common methods
|
||||||
|
|
||||||
def startProcess () {
|
def startProcess () {
|
||||||
createDayArray()
|
createDayArray()
|
||||||
state.dayCount=state.data.size()
|
state.dayCount=state.data.size()
|
||||||
if (state.dayCount){
|
if (state.dayCount){
|
||||||
state.counter = 0
|
state.counter = 0
|
||||||
@@ -190,7 +190,7 @@ def startProcess () {
|
|||||||
def startDay() {
|
def startDay() {
|
||||||
def start = convertEpoch(state.data[state.counter].start)
|
def start = convertEpoch(state.data[state.counter].start)
|
||||||
def stop = convertEpoch(state.data[state.counter].stop)
|
def stop = convertEpoch(state.data[state.counter].stop)
|
||||||
|
|
||||||
runOnce(start, turnOnSwitch, [overwrite: true])
|
runOnce(start, turnOnSwitch, [overwrite: true])
|
||||||
runOnce(stop, incDay, [overwrite: true])
|
runOnce(stop, incDay, [overwrite: true])
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ def locationHandler(evt) {
|
|||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
startProcess()
|
startProcess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def midNight(){
|
def midNight(){
|
||||||
@@ -238,7 +238,7 @@ def turnOffSwitch() {
|
|||||||
}
|
}
|
||||||
log.debug "Home ventilation switches are off."
|
log.debug "Home ventilation switches are off."
|
||||||
}
|
}
|
||||||
|
|
||||||
def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) {
|
def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) {
|
||||||
def title = ""
|
def title = ""
|
||||||
def dayListClean = "On "
|
def dayListClean = "On "
|
||||||
@@ -252,7 +252,7 @@ def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) {
|
|||||||
dayListClean = "${dayListClean}, "
|
dayListClean = "${dayListClean}, "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dayListClean = "Every day"
|
dayListClean = "Every day"
|
||||||
}
|
}
|
||||||
@@ -272,7 +272,7 @@ def schedDesc(on1, off1, on2, off2, on3, off3, on4, off4, modeList, dayList) {
|
|||||||
modeListClean = "${modeListClean} ${modePrefix}"
|
modeListClean = "${modeListClean} ${modePrefix}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
modeListClean = "${modeListClean}all modes"
|
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)}"
|
title += "\nSchedule 2: ${humanReadableTime(on2)} to ${humanReadableTime(off2)}"
|
||||||
}
|
}
|
||||||
if (on3 && off3) {
|
if (on3 && off3) {
|
||||||
title += "\nSchedule 3: ${humanReadableTime(on3)} to ${humanReadableTime(off3)}"
|
title += "\nSchedule 3: ${humanReadableTime(on3)} to ${humanReadableTime(off3)}"
|
||||||
}
|
}
|
||||||
if (on4 && off4) {
|
if (on4 && off4) {
|
||||||
title += "\nSchedule 4: ${humanReadableTime(on4)} to ${humanReadableTime(off4)}"
|
title += "\nSchedule 4: ${humanReadableTime(on4)} to ${humanReadableTime(off4)}"
|
||||||
}
|
}
|
||||||
if (on1 || on2 || on3 || on4) {
|
if (on1 || on2 || on3 || on4) {
|
||||||
title += "\n$modeListClean"
|
title += "\n$modeListClean"
|
||||||
title += "\n$dayListClean"
|
title += "\n$dayListClean"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!on1 && !on2 && !on3 && !on4) {
|
if (!on1 && !on2 && !on3 && !on4) {
|
||||||
title="Click to configure scenario"
|
title="Click to configure scenario"
|
||||||
}
|
}
|
||||||
@@ -374,7 +374,7 @@ def createDayArray() {
|
|||||||
timeOk(timeOnD1, timeOffD1)
|
timeOk(timeOnD1, timeOffD1)
|
||||||
timeOk(timeOnD2, timeOffD2)
|
timeOk(timeOnD2, timeOffD2)
|
||||||
timeOk(timeOnD3, timeOffD3)
|
timeOk(timeOnD3, timeOffD3)
|
||||||
timeOk(timeOnD4, timeOffD4)
|
timeOk(timeOnD4, timeOffD4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.data.sort{it.start}
|
state.data.sort{it.start}
|
||||||
@@ -384,7 +384,7 @@ def createDayArray() {
|
|||||||
|
|
||||||
private def textAppName() {
|
private def textAppName() {
|
||||||
def text = "Smart Home Ventilation"
|
def text = "Smart Home Ventilation"
|
||||||
}
|
}
|
||||||
|
|
||||||
private def textVersion() {
|
private def textVersion() {
|
||||||
def text = "Version 2.1.2 (05/31/2015)"
|
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 " +
|
"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 " +
|
"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."
|
"at 12:15 am or later."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ definition(
|
|||||||
name: "Monitor on Sense",
|
name: "Monitor on Sense",
|
||||||
namespace: "resteele",
|
namespace: "resteele",
|
||||||
author: "Rachel Steele",
|
author: "Rachel Steele",
|
||||||
description: "Turn on Monitor when vibration is sensed",
|
description: "Turn on switch when vibration is sensed",
|
||||||
category: "My Apps",
|
category: "My Apps",
|
||||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||||
@@ -25,10 +25,10 @@ definition(
|
|||||||
|
|
||||||
|
|
||||||
preferences {
|
preferences {
|
||||||
section("When the keyboard is used...") {
|
section("When vibration is sensed...") {
|
||||||
input "accelerationSensor", "capability.accelerationSensor", title: "Which Sensor?"
|
input "accelerationSensor", "capability.accelerationSensor", title: "Which Sensor?"
|
||||||
}
|
}
|
||||||
section("Turn on/off a light...") {
|
section("Turn on switch...") {
|
||||||
input "switch1", "capability.switch"
|
input "switch1", "capability.switch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,5 +47,3 @@ def updated() {
|
|||||||
def accelerationActiveHandler(evt) {
|
def accelerationActiveHandler(evt) {
|
||||||
switch1.on()
|
switch1.on()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -114,13 +114,16 @@ def beaconHandler(evt) {
|
|||||||
|
|
||||||
if (allOk) {
|
if (allOk) {
|
||||||
def data = new groovy.json.JsonSlurper().parseText(evt.data)
|
def data = new groovy.json.JsonSlurper().parseText(evt.data)
|
||||||
log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
|
// removed logging of device names. can be added back for debugging
|
||||||
|
//log.debug "<beacon-control> data: $data - phones: " + phones*.deviceNetworkId
|
||||||
|
|
||||||
def beaconName = getBeaconName(evt)
|
def beaconName = getBeaconName(evt)
|
||||||
log.debug "<beacon-control> beaconName: $beaconName"
|
// removed logging of device names. can be added back for debugging
|
||||||
|
//log.debug "<beacon-control> beaconName: $beaconName"
|
||||||
|
|
||||||
def phoneName = getPhoneName(data)
|
def phoneName = getPhoneName(data)
|
||||||
log.debug "<beacon-control> phoneName: $phoneName"
|
// removed logging of device names. can be added back for debugging
|
||||||
|
//log.debug "<beacon-control> phoneName: $phoneName"
|
||||||
if (phoneName != null) {
|
if (phoneName != null) {
|
||||||
def action = data.presence == "1" ? "arrived" : "left"
|
def action = data.presence == "1" ? "arrived" : "left"
|
||||||
def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName"
|
def msg = "$phoneName has $action ${action == 'arrived' ? 'at ' : ''}the $beaconName"
|
||||||
|
|||||||
@@ -49,13 +49,15 @@ preferences {
|
|||||||
|
|
||||||
def installed() {
|
def installed() {
|
||||||
log.debug "Installed with settings: ${settings}"
|
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)
|
subscribe(people, "presence", presence)
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
log.debug "Updated with settings: ${settings}"
|
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()
|
unsubscribe()
|
||||||
subscribe(people, "presence", presence)
|
subscribe(people, "presence", presence)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,10 +64,12 @@ def meterHandler(evt) {
|
|||||||
def lastValue = atomicState.lastValue as double
|
def lastValue = atomicState.lastValue as double
|
||||||
atomicState.lastValue = meterValue
|
atomicState.lastValue = meterValue
|
||||||
|
|
||||||
|
def dUnit = evt.unit ?: "Watts"
|
||||||
|
|
||||||
def aboveThresholdValue = aboveThreshold as int
|
def aboveThresholdValue = aboveThreshold as int
|
||||||
if (meterValue > aboveThresholdValue) {
|
if (meterValue > aboveThresholdValue) {
|
||||||
if (lastValue < aboveThresholdValue) { // only send notifications when crossing the threshold
|
if (lastValue < aboveThresholdValue) { // only send notifications when crossing the threshold
|
||||||
def msg = "${meter} reported ${evt.value} ${evt.unit} which is above your threshold of ${aboveThreshold}."
|
def msg = "${meter} reported ${evt.value} ${dUnit} which is above your threshold of ${aboveThreshold}."
|
||||||
sendMessage(msg)
|
sendMessage(msg)
|
||||||
} else {
|
} else {
|
||||||
// log.debug "not sending notification for ${evt.description} because the threshold (${aboveThreshold}) has already been crossed"
|
// log.debug "not sending notification for ${evt.description} because the threshold (${aboveThreshold}) has already been crossed"
|
||||||
@@ -78,7 +80,7 @@ def meterHandler(evt) {
|
|||||||
def belowThresholdValue = belowThreshold as int
|
def belowThresholdValue = belowThreshold as int
|
||||||
if (meterValue < belowThresholdValue) {
|
if (meterValue < belowThresholdValue) {
|
||||||
if (lastValue > belowThresholdValue) { // only send notifications when crossing the threshold
|
if (lastValue > belowThresholdValue) { // only send notifications when crossing the threshold
|
||||||
def msg = "${meter} reported ${evt.value} ${evt.unit} which is below your threshold of ${belowThreshold}."
|
def msg = "${meter} reported ${evt.value} ${dUnit} which is below your threshold of ${belowThreshold}."
|
||||||
sendMessage(msg)
|
sendMessage(msg)
|
||||||
} else {
|
} else {
|
||||||
// log.debug "not sending notification for ${evt.description} because the threshold (${belowThreshold}) has already been crossed"
|
// log.debug "not sending notification for ${evt.description} because the threshold (${belowThreshold}) has already been crossed"
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ def waterWetHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.value && it.value == "wet" } > 1
|
def alreadySentSms = recentEvents.count { it.value && it.value == "wet" } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
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 {
|
} else {
|
||||||
def msg = "${alarm.displayName} is wet!"
|
def msg = "${alarm.displayName} is wet!"
|
||||||
log.debug "$alarm is wet, texting $phone"
|
log.debug "$alarm is wet, texting phone number"
|
||||||
|
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts(msg, recipients)
|
sendNotificationToContacts(msg, recipients)
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ def takeAction(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
def sendTextMessage() {
|
def sendTextMessage() {
|
||||||
log.debug "$multisensor was open too long, texting $phone"
|
log.debug "$multisensor was open too long, texting phone"
|
||||||
|
|
||||||
updateSmsHistory()
|
updateSmsHistory()
|
||||||
def openMinutes = maxOpenTime * (state.smsHistory?.size() ?: 1)
|
def openMinutes = maxOpenTime * (state.smsHistory?.size() ?: 1)
|
||||||
|
|||||||
@@ -761,7 +761,7 @@ String displayableTime(timeRemaining) {
|
|||||||
return "${minutes}:00"
|
return "${minutes}:00"
|
||||||
}
|
}
|
||||||
def fraction = "0.${parts[1]}" as double
|
def fraction = "0.${parts[1]}" as double
|
||||||
def seconds = "${60 * fraction as int}".padRight(2, "0")
|
def seconds = "${60 * fraction as int}".padLeft(2, "0")
|
||||||
return "${minutes}:${seconds}"
|
return "${minutes}:${seconds}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1101,4 +1101,4 @@ def hasStartLevel() {
|
|||||||
|
|
||||||
def hasEndLevel() {
|
def hasEndLevel() {
|
||||||
return (endLevel != null && endLevel != "")
|
return (endLevel != null && endLevel != "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ preferences {
|
|||||||
|
|
||||||
def installed() {
|
def installed() {
|
||||||
log.debug "Installed with settings: ${settings}"
|
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)
|
subscribe(people, "presence", presence)
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
log.debug "Updated with settings: ${settings}"
|
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()
|
unsubscribe()
|
||||||
subscribe(people, "presence", presence)
|
subscribe(people, "presence", presence)
|
||||||
}
|
}
|
||||||
@@ -71,11 +71,10 @@ def presence(evt)
|
|||||||
def person = getPerson(evt)
|
def person = getPerson(evt)
|
||||||
def recentNotPresent = person.statesSince("presence", t0).find{it.value == "not present"}
|
def recentNotPresent = person.statesSince("presence", t0).find{it.value == "not present"}
|
||||||
if (recentNotPresent) {
|
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 {
|
else {
|
||||||
def message = "${person.displayName} arrived at home, changing mode to '${newMode}'"
|
def message = "${person.displayName} arrived at home, changing mode to '${newMode}'"
|
||||||
log.info message
|
|
||||||
send(message)
|
send(message)
|
||||||
setLocationMode(newMode)
|
setLocationMode(newMode)
|
||||||
}
|
}
|
||||||
@@ -106,6 +105,4 @@ private send(msg) {
|
|||||||
sendSms(phone, msg)
|
sendSms(phone, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug msg
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,12 +57,11 @@ def scheduleCheck()
|
|||||||
def message = message1 ?: "SmartThings - Habit Helper Reminder!"
|
def message = message1 ?: "SmartThings - Habit Helper Reminder!"
|
||||||
|
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
log.debug "Texting reminder: ($message) to contacts:${recipients?.size()}"
|
log.debug "Texting reminder to contacts:${recipients?.size()}"
|
||||||
sendNotificationToContacts(message, recipients)
|
sendNotificationToContacts(message, recipients)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
log.debug "Texting reminder"
|
||||||
log.debug "Texting reminder: ($message) to $phone1"
|
|
||||||
sendSms(phone1, message)
|
sendSms(phone1, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ def bulbDiscovery() {
|
|||||||
|
|
||||||
return dynamicPage(name:"bulbDiscovery", title:"Light Discovery Started!", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) {
|
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.") {
|
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
|
paragraph title: "Previously added Hue Lights (${existingLights.size()} added)", existingLightsDescription
|
||||||
}
|
}
|
||||||
section {
|
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
|
* 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. Also, set ID number on bridge if not done previously.
|
* for the bridge and all connected lights. Also, set ID number on bridge if not done previously.
|
||||||
*/
|
*/
|
||||||
private void checkBridgeStatus() {
|
private void checkBridgeStatus() {
|
||||||
def bridges = getHueBridges()
|
def bridges = getHueBridges()
|
||||||
// Check if each bridge has been heard from within the last 16 minutes (3 poll intervals times 5 minutes plus buffer)
|
// 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 * 30)
|
def time = now() - (1000 * 60 * 11)
|
||||||
bridges.each {
|
bridges.each {
|
||||||
def d = getChildDevice(it.value.mac)
|
def d = getChildDevice(it.value.mac)
|
||||||
if(d) {
|
if(d) {
|
||||||
@@ -740,16 +740,21 @@ private void checkBridgeStatus() {
|
|||||||
d.sendEvent(name: "idNumber", value: it.value.idNumber)
|
d.sendEvent(name: "idNumber", value: it.value.idNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
|
if (it.value.lastActivity < time) { // it.value.lastActivity != null &&
|
||||||
log.warn "Bridge $it.key is Offline"
|
log.warn "Bridge $it.key is Offline"
|
||||||
d.sendEvent(name: "status", value: "Offline")
|
d.sendEvent(name: "status", value: "Offline")
|
||||||
// set all lights to offline since bridge is not reachable
|
|
||||||
state.bulbs?.each {it.value.online = false}
|
state.bulbs?.each {
|
||||||
} else {
|
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)
|
d.sendEvent(name: "status", value: "Online")//setOnline(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def isValidSource(macAddress) {
|
def isValidSource(macAddress) {
|
||||||
@@ -757,6 +762,10 @@ def isValidSource(macAddress) {
|
|||||||
return (vbridges?.find {"${it.value.mac}" == macAddress}) != null
|
return (vbridges?.find {"${it.value.mac}" == macAddress}) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def isInBulbDiscovery() {
|
||||||
|
return state.inBulbDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
//CHILD DEVICE METHODS
|
//CHILD DEVICE METHODS
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
@@ -792,10 +801,12 @@ def parse(childDevice, description) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Philips Hue priority for color is xy > ct > hs
|
// Philips Hue priority for color is xy > ct > hs
|
||||||
|
// For SmartThings, try to always send hue, sat and hex
|
||||||
private sendColorEvents(device, xy, hue, sat, ct, colormode = null) {
|
private sendColorEvents(device, xy, hue, sat, ct, colormode = null) {
|
||||||
if (device == null || (xy == null && hue == null && sat == null && ct == null))
|
if (device == null || (xy == null && hue == null && sat == null && ct == null))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def events = [:]
|
||||||
// For now, only care about changing color temperature if requested by user
|
// For now, only care about changing color temperature if requested by user
|
||||||
if (ct != null && (colormode == "ct" || (xy == null && hue == null && sat == null))) {
|
if (ct != null && (colormode == "ct" || (xy == null && hue == null && sat == null))) {
|
||||||
// for some reason setting Hue to their specified minimum off 153 yields 154, dealt with below
|
// for some reason setting Hue to their specified minimum off 153 yields 154, dealt with below
|
||||||
@@ -809,13 +820,13 @@ private sendColorEvents(device, xy, hue, sat, ct, colormode = null) {
|
|||||||
if (hue != null) {
|
if (hue != null) {
|
||||||
// 0-65535
|
// 0-65535
|
||||||
def value = Math.min(Math.round(hue * 100 / 65535), 65535) as int
|
def value = Math.min(Math.round(hue * 100 / 65535), 65535) as int
|
||||||
device.sendEvent([name: "hue", value: value, descriptionText: "Color has changed"])
|
events["hue"] = [name: "hue", value: value, descriptionText: "Color has changed", displayed: false]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sat != null) {
|
if (sat != null) {
|
||||||
// 0-254
|
// 0-254
|
||||||
def value = Math.round(sat * 100 / 254) as int
|
def value = Math.round(sat * 100 / 254) as int
|
||||||
device.sendEvent([name: "saturation", value: value, descriptionText: "Color has changed"])
|
events["saturation"] = [name: "saturation", value: value, descriptionText: "Color has changed", displayed: false]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following is used to decide what to base hex calculations on since it is preferred to return a colorchange in hex
|
// Following is used to decide what to base hex calculations on since it is preferred to return a colorchange in hex
|
||||||
@@ -827,17 +838,28 @@ private sendColorEvents(device, xy, hue, sat, ct, colormode = null) {
|
|||||||
def model = state.bulbs[id]?.modelid
|
def model = state.bulbs[id]?.modelid
|
||||||
def hex = colorFromXY(xy, model)
|
def hex = colorFromXY(xy, model)
|
||||||
|
|
||||||
// TODO Disabled until a solution for the jumping color picker can be figured out
|
// Create Hue and Saturation events if not previously existing
|
||||||
//device.sendEvent([name: "color", value: hex.toUpperCase(), descriptionText: "Color has changed", displayed: false])
|
def hsv = hexToHsv(hex)
|
||||||
|
if (events["hue"] == null)
|
||||||
|
events["hue"] = [name: "hue", value: hsv[0], descriptionText: "Color has changed", displayed: false]
|
||||||
|
if (events["saturation"] == null)
|
||||||
|
events["saturation"] = [name: "saturation", value: hsv[1], descriptionText: "Color has changed", displayed: false]
|
||||||
|
|
||||||
|
events["color"] = [name: "color", value: hex.toUpperCase(), descriptionText: "Color has changed", displayed: true]
|
||||||
} else if (colormode == "hs" || colormode == null) {
|
} else if (colormode == "hs" || colormode == null) {
|
||||||
// colormode is "hs" or "xy" is missing, default to follow hue/sat which is already handled above
|
// colormode is "hs" or "xy" is missing, default to follow hue/sat which is already handled above
|
||||||
|
def hueValue = (hue != null) ? events["hue"].value : Integer.parseInt("$device.currentHue")
|
||||||
|
def satValue = (sat != null) ? events["saturation"].value : Integer.parseInt("$device.currentSaturation")
|
||||||
|
|
||||||
// TODO Disabled until the standard behavior of lights is defined (hue and sat events are sent above)
|
|
||||||
//def hex = colorUtil.hslToHex((int) device.currentHue, (int) device.currentSaturation)
|
def hex = hsvToHex(hueValue, satValue)
|
||||||
// device.sendEvent([name: "color", value: hex.toUpperCase(), descriptionText: "Color has changed"])
|
events["color"] = [name: "color", value: hex.toUpperCase(), descriptionText: "Color has changed", displayed: true]
|
||||||
}
|
}
|
||||||
|
|
||||||
return debug
|
boolean sendColorChanged = false
|
||||||
|
events.each {
|
||||||
|
device.sendEvent(it.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendBasicEvents(device, param, value) {
|
private sendBasicEvents(device, param, value) {
|
||||||
@@ -878,8 +900,6 @@ private handleCommandResponse(body) {
|
|||||||
def updates = [:]
|
def updates = [:]
|
||||||
|
|
||||||
body.each { payload ->
|
body.each { payload ->
|
||||||
log.debug $payload
|
|
||||||
|
|
||||||
if (payload?.success) {
|
if (payload?.success) {
|
||||||
def childDeviceNetworkId = app.id + "/"
|
def childDeviceNetworkId = app.id + "/"
|
||||||
def eventType
|
def eventType
|
||||||
@@ -951,6 +971,7 @@ private handlePoll(body) {
|
|||||||
} else {
|
} else {
|
||||||
state.bulbs[bulb.key]?.online = false
|
state.bulbs[bulb.key]?.online = false
|
||||||
log.warn "$device is not reachable by Hue bridge"
|
log.warn "$device is not reachable by Hue bridge"
|
||||||
|
device.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", displayed: false, isStateChange: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1091,26 +1112,22 @@ def setColor(childDevice, huesettings) {
|
|||||||
def sat = null
|
def sat = null
|
||||||
def xy = null
|
def xy = null
|
||||||
|
|
||||||
// For now ignore model to get a consistent color if same color is set across multiple devices
|
// Prefer hue/sat over hex to make sure it works with the majority of the smartapps
|
||||||
// def model = state.bulbs[getId(childDevice)]?.modelid
|
if (huesettings.hue != null || huesettings.sat != null) {
|
||||||
if (huesettings.hex != null) {
|
// If both hex and hue/sat are set, send all values to bridge to get hue/sat in response from bridge to
|
||||||
|
// generate hue/sat events even though bridge will prioritize XY when setting color
|
||||||
|
if (huesettings.hue != null)
|
||||||
|
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
|
||||||
|
if (huesettings.saturation != null)
|
||||||
|
value.sat = Math.min(Math.round(huesettings.saturation * 254 / 100), 254)
|
||||||
|
} else if (huesettings.hex != null && false) {
|
||||||
|
// For now ignore model to get a consistent color if same color is set across multiple devices
|
||||||
|
// def model = state.bulbs[getId(childDevice)]?.modelid
|
||||||
// value.xy = calculateXY(huesettings.hex, model)
|
// value.xy = calculateXY(huesettings.hex, model)
|
||||||
// Once groups, or scenes are introduced it might be a good idea to use unique models again
|
// Once groups, or scenes are introduced it might be a good idea to use unique models again
|
||||||
value.xy = calculateXY(huesettings.hex)
|
value.xy = calculateXY(huesettings.hex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If both hex and hue/sat are set, send all values to bridge to get hue/sat in response from bridge to
|
|
||||||
// generate hue/sat events even though bridge will prioritize XY when setting color
|
|
||||||
if (huesettings.hue != null)
|
|
||||||
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
|
|
||||||
else
|
|
||||||
value.hue = Math.min(Math.round(childDevice.device?.currentValue("hue") * 65535 / 100), 65535)
|
|
||||||
|
|
||||||
if (huesettings.saturation != null)
|
|
||||||
value.sat = Math.min(Math.round(huesettings.saturation * 254 / 100), 254)
|
|
||||||
else
|
|
||||||
value.sat = Math.min(Math.round(childDevice.device?.currentValue("saturation") * 254 / 100), 254)
|
|
||||||
|
|
||||||
/* Disabled for now due to bad behavior via Lightning Wizard
|
/* Disabled for now due to bad behavior via Lightning Wizard
|
||||||
if (!value.xy) {
|
if (!value.xy) {
|
||||||
// Below will translate values to hex->XY to take into account the color support of the different hue types
|
// Below will translate values to hex->XY to take into account the color support of the different hue types
|
||||||
@@ -1165,9 +1182,8 @@ private poll() {
|
|||||||
def host = getBridgeIP()
|
def host = getBridgeIP()
|
||||||
def uri = "/api/${state.username}/lights/"
|
def uri = "/api/${state.username}/lights/"
|
||||||
log.debug "GET: $host$uri"
|
log.debug "GET: $host$uri"
|
||||||
sendHubCommand(new physicalgraph.device.HubAction("""GET ${uri} HTTP/1.1
|
sendHubCommand(new physicalgraph.device.HubAction("GET ${uri} HTTP/1.1\r\n" +
|
||||||
HOST: ${host}
|
"HOST: ${host}\r\n\r\n", physicalgraph.device.Protocol.LAN, selectedHue))
|
||||||
""", physicalgraph.device.Protocol.LAN, selectedHue))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isOnline(id) {
|
private isOnline(id) {
|
||||||
@@ -1183,13 +1199,11 @@ private put(path, body) {
|
|||||||
log.debug "PUT: $host$uri"
|
log.debug "PUT: $host$uri"
|
||||||
log.debug "BODY: ${bodyJSON}"
|
log.debug "BODY: ${bodyJSON}"
|
||||||
|
|
||||||
sendHubCommand(new physicalgraph.device.HubAction("""PUT $uri HTTP/1.1
|
sendHubCommand(new physicalgraph.device.HubAction("PUT $uri HTTP/1.1\r\n" +
|
||||||
HOST: ${host}
|
"HOST: ${host}\r\n" +
|
||||||
Content-Length: ${length}
|
"Content-Length: ${length}\r\n" +
|
||||||
|
"\r\n" +
|
||||||
${bodyJSON}
|
"${bodyJSON}", physicalgraph.device.Protocol.LAN, "${selectedHue}"))
|
||||||
""", physicalgraph.device.Protocol.LAN, "${selectedHue}"))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1647,3 +1661,101 @@ private boolean checkPointInLampsReach(p, colorPoints) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an RGB color in hex to HSV.
|
||||||
|
* Algorithm based on http://en.wikipedia.org/wiki/HSV_color_space.
|
||||||
|
*
|
||||||
|
* @param colorStr color value in hex (#ff03d3)
|
||||||
|
*
|
||||||
|
* @return HSV representation in an array (0-100) [hue, sat, value]
|
||||||
|
*/
|
||||||
|
def hexToHsv(colorStr){
|
||||||
|
def r = Integer.valueOf( colorStr.substring( 1, 3 ), 16 ) / 255
|
||||||
|
def g = Integer.valueOf( colorStr.substring( 3, 5 ), 16 ) / 255
|
||||||
|
def b = Integer.valueOf( colorStr.substring( 5, 7 ), 16 ) / 255;
|
||||||
|
|
||||||
|
def max = Math.max(Math.max(r, g), b)
|
||||||
|
def min = Math.min(Math.min(r, g), b)
|
||||||
|
|
||||||
|
def h, s, v = max;
|
||||||
|
|
||||||
|
def d = max - min;
|
||||||
|
s = max == 0 ? 0 : d / max;
|
||||||
|
|
||||||
|
if(max == min){
|
||||||
|
h = 0;
|
||||||
|
}else{
|
||||||
|
switch(max){
|
||||||
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||||
|
case g: h = (b - r) / d + 2; break;
|
||||||
|
case b: h = (r - g) / d + 4; break;
|
||||||
|
}
|
||||||
|
h /= 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [(h * 100).round(), (s * 100).round(), (v * 100).round()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts HSV color to RGB in hex.
|
||||||
|
* Algorithm based on http://en.wikipedia.org/wiki/HSV_color_space.
|
||||||
|
*
|
||||||
|
* @param hue hue 0-100
|
||||||
|
* @param sat saturation 0-100
|
||||||
|
* @param value value 0-100 (defaults to 100)
|
||||||
|
|
||||||
|
* @return the color in hex (#ff03d3)
|
||||||
|
*/
|
||||||
|
def hsvToHex(hue, sat, value = 100){
|
||||||
|
def r, g, b;
|
||||||
|
def h = hue / 100
|
||||||
|
def s = sat / 100
|
||||||
|
def v = value / 100
|
||||||
|
|
||||||
|
def i = Math.floor(h * 6);
|
||||||
|
def f = h * 6 - i;
|
||||||
|
def p = v * (1 - s);
|
||||||
|
def q = v * (1 - f * s);
|
||||||
|
def t = v * (1 - (1 - f) * s);
|
||||||
|
|
||||||
|
switch (i % 6) {
|
||||||
|
case 0:
|
||||||
|
r = v
|
||||||
|
g = t
|
||||||
|
b = p
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
r = q
|
||||||
|
g = v
|
||||||
|
b = p
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
r = p
|
||||||
|
g = v
|
||||||
|
b = t
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
r = p
|
||||||
|
g = q
|
||||||
|
b = v
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
r = t
|
||||||
|
g = p
|
||||||
|
b = v
|
||||||
|
break
|
||||||
|
case 5:
|
||||||
|
r = v
|
||||||
|
g = p
|
||||||
|
b = q
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converting float components to int components.
|
||||||
|
def r1 = String.format("%02X", (int) (r * 255.0f));
|
||||||
|
def g1 = String.format("%02X", (int) (g * 255.0f));
|
||||||
|
def b1 = String.format("%02X", (int) (b * 255.0f));
|
||||||
|
|
||||||
|
return "#$r1$g1$b1"
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,14 +53,14 @@ def accelerationActiveHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
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 {
|
} else {
|
||||||
if (location.contactBookEnabled) {
|
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)
|
sendNotificationToContacts("${accelerationSensor.label ?: accelerationSensor.name} moved", recipients)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug "$accelerationSensor has moved, texting $phone1"
|
log.debug "accelerationSensor has moved, sending text message"
|
||||||
sendSms(phone1, "${accelerationSensor.label ?: accelerationSensor.name} moved")
|
sendSms(phone1, "${accelerationSensor.label ?: accelerationSensor.name} moved")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,10 +69,10 @@ def temperatureHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.doubleValue >= tooHot } > 1
|
def alreadySentSms = recentEvents.count { it.doubleValue >= tooHot } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
if (alreadySentSms) {
|
||||||
log.debug "SMS already sent to $phone1 within the last $deltaMinutes minutes"
|
log.debug "SMS already sent within the last $deltaMinutes minutes"
|
||||||
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
||||||
} else {
|
} 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"
|
def tempScale = location.temperatureScale ?: "F"
|
||||||
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
|
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
|
||||||
switch1?.on()
|
switch1?.on()
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ def authPage() {
|
|||||||
}
|
}
|
||||||
def description = "Tap to enter LIFX credentials"
|
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 = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" // this triggers oauthInit() below
|
||||||
// def redirectUrl = "${apiServerUrl}"
|
// def redirectUrl = "${apiServerUrl}"
|
||||||
log.debug "app id: ${app.id}"
|
// log.debug "app id: ${app.id}"
|
||||||
log.debug "redirect url: ${redirectUrl}"
|
// log.debug "redirect url: ${redirectUrl}"s
|
||||||
return dynamicPage(name: "Credentials", title: "Connect to LIFX", nextPage: null, uninstall: true, install:true) {
|
return dynamicPage(name: "Credentials", title: "Connect to LIFX", nextPage: null, uninstall: true, install:true) {
|
||||||
section {
|
section {
|
||||||
href(url:redirectUrl, required:true, title:"Connect to LIFX", description:"Tap here to connect your LIFX account")
|
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)
|
def childDevice = getChildDevice(device.id)
|
||||||
selectors.add("${device.id}")
|
selectors.add("${device.id}")
|
||||||
if (!childDevice) {
|
if (!childDevice) {
|
||||||
log.info("Adding device ${device.id}: ${device.product}")
|
// log.info("Adding device ${device.id}: ${device.product}")
|
||||||
def data = [
|
def data = [
|
||||||
label: device.label,
|
label: device.label,
|
||||||
level: Math.round((device.brightness ?: 1) * 100),
|
level: Math.round((device.brightness ?: 1) * 100),
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ definition(
|
|||||||
}
|
}
|
||||||
|
|
||||||
preferences(oauthPage: "deviceAuthorization") {
|
preferences(oauthPage: "deviceAuthorization") {
|
||||||
page(name: "Credentials", title: "Connect to your Logitech Harmony device", content: "authPage", install: false, nextPage: "deviceAuthorization")
|
page(name: "Credentials", title: "Connect to your Logitech Harmony device", content: "authPage", install: false, nextPage: "deviceAuthorization")
|
||||||
page(name: "deviceAuthorization", title: "Logitech Harmony device authorization", install: true) {
|
page(name: "deviceAuthorization", title: "Logitech Harmony device authorization", install: true) {
|
||||||
section("Allow Logitech Harmony to control these things...") {
|
section("Allow Logitech Harmony to control these things...") {
|
||||||
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
||||||
@@ -102,7 +102,8 @@ def authPage() {
|
|||||||
description = "Click to enter Harmony Credentials"
|
description = "Click to enter Harmony Credentials"
|
||||||
def redirectUrl = buildRedirectUrl
|
def redirectUrl = buildRedirectUrl
|
||||||
return dynamicPage(name: "Credentials", title: "Harmony", nextPage: null, uninstall: true, install:false) {
|
return dynamicPage(name: "Credentials", title: "Harmony", nextPage: null, uninstall: true, install:false) {
|
||||||
section { href url:redirectUrl, style:"embedded", required:true, title:"Harmony", description:description }
|
section { paragraph title: "Note:", "This device has not been officially tested and certified to “Work with SmartThings”. You can connect it to your SmartThings home but performance may vary and we will not be able to provide support or assistance." }
|
||||||
|
section { href url:redirectUrl, style:"embedded", required:true, title:"Harmony", description:description }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//device discovery request every 5 //25 seconds
|
//device discovery request every 5 //25 seconds
|
||||||
@@ -314,8 +315,6 @@ def installed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
unsubscribe()
|
|
||||||
unschedule()
|
|
||||||
if (!state.accessToken) {
|
if (!state.accessToken) {
|
||||||
log.debug "About to create access token"
|
log.debug "About to create access token"
|
||||||
createAccessToken()
|
createAccessToken()
|
||||||
|
|||||||
@@ -111,16 +111,23 @@ private sendMessage(evt) {
|
|||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts(msg, recipients, options)
|
sendNotificationToContacts(msg, recipients, options)
|
||||||
} else {
|
} else {
|
||||||
if (pushAndPhone != 'No') {
|
|
||||||
log.debug 'sending push'
|
|
||||||
options.method = 'push'
|
|
||||||
sendNotification(msg, options)
|
|
||||||
}
|
|
||||||
if (phone) {
|
if (phone) {
|
||||||
options.phone = phone
|
options.phone = phone
|
||||||
log.debug 'sending SMS'
|
if (pushAndPhone != 'No') {
|
||||||
sendNotification(msg, options)
|
log.debug 'Sending push and SMS'
|
||||||
|
options.method = 'both'
|
||||||
|
} else {
|
||||||
|
log.debug 'Sending SMS'
|
||||||
|
options.method = 'phone'
|
||||||
|
}
|
||||||
|
} else if (pushAndPhone != 'No') {
|
||||||
|
log.debug 'Sending push'
|
||||||
|
options.method = 'push'
|
||||||
|
} else {
|
||||||
|
log.debug 'Sending nothing'
|
||||||
|
options.method = 'none'
|
||||||
}
|
}
|
||||||
|
sendNotification(msg, options)
|
||||||
}
|
}
|
||||||
if (frequency) {
|
if (frequency) {
|
||||||
state[evt.deviceId] = now()
|
state[evt.deviceId] = now()
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ def updated() {
|
|||||||
|
|
||||||
def presenceHandler(evt) {
|
def presenceHandler(evt) {
|
||||||
if (evt.value == "present") {
|
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}")
|
sendPush("${presence.label ?: presence.name} has arrived at the ${location}")
|
||||||
} else if (evt.value == "not present") {
|
} 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}")
|
sendPush("${presence.label ?: presence.name} has left the ${location}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ def updated() {
|
|||||||
|
|
||||||
def presenceHandler(evt) {
|
def presenceHandler(evt) {
|
||||||
if (evt.value == "present") {
|
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) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts("${presence.label ?: presence.name} has arrived at the ${location}", recipients)
|
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}")
|
sendSms(phone1, "${presence.label ?: presence.name} has arrived at the ${location}")
|
||||||
}
|
}
|
||||||
} else if (evt.value == "not present") {
|
} 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) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts("${presence.label ?: presence.name} has left the ${location}", recipients)
|
sendNotificationToContacts("${presence.label ?: presence.name} has left the ${location}", recipients)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ def updated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def subscribe() {
|
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(doorSensor, "contact", garageDoorContact)
|
||||||
|
|
||||||
subscribe(cars, "presence", carPresence)
|
subscribe(cars, "presence", carPresence)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ def updated() {
|
|||||||
private subscribeToEvents()
|
private subscribeToEvents()
|
||||||
{
|
{
|
||||||
subscribe intrusionMotions, "motion", intruderMotion
|
subscribe intrusionMotions, "motion", intruderMotion
|
||||||
subscribe residentMotions, "motion", residentMotion
|
// subscribe residentMotions, "motion", residentMotion
|
||||||
subscribe intrusionContacts, "contact", contact
|
subscribe intrusionContacts, "contact", contact
|
||||||
subscribe alarms, "alarm", alarm
|
subscribe alarms, "alarm", alarm
|
||||||
subscribe(app, appTouch)
|
subscribe(app, appTouch)
|
||||||
@@ -156,6 +156,7 @@ def residentMotion(evt)
|
|||||||
// startReArmSequence()
|
// startReArmSequence()
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
unsubscribe(residentMotions)
|
||||||
}
|
}
|
||||||
|
|
||||||
def contact(evt)
|
def contact(evt)
|
||||||
@@ -214,7 +215,7 @@ def checkForReArm()
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.warn "checkForReArm: lastIntruderMotion was null, unable to check for re-arming intrusion detection"
|
log.warn "checkForReArm: lastIntruderMotion was null, unable to check for re-arming intrusion detection"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private startAlarmSequence()
|
private startAlarmSequence()
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ def updated()
|
|||||||
|
|
||||||
def contactOpenHandler(evt) {
|
def contactOpenHandler(evt) {
|
||||||
log.trace "$evt.value: $evt, $settings"
|
log.trace "$evt.value: $evt, $settings"
|
||||||
log.debug "$contact1 was opened, texting $phone1"
|
log.debug "$contact1 was opened, sending text"
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts("Your ${contact1.label ?: contact1.name} was opened", recipients)
|
sendNotificationToContacts("Your ${contact1.label ?: contact1.name} was opened", recipients)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def updated() {
|
|||||||
|
|
||||||
def motionActiveHandler(evt) {
|
def motionActiveHandler(evt) {
|
||||||
log.trace "$evt.value: $evt, $settings"
|
log.trace "$evt.value: $evt, $settings"
|
||||||
|
|
||||||
if (presence1.latestValue("presence") == "not present") {
|
if (presence1.latestValue("presence") == "not present") {
|
||||||
// Don't send a continuous stream of text messages
|
// Don't send a continuous stream of text messages
|
||||||
def deltaSeconds = 10
|
def deltaSeconds = 10
|
||||||
@@ -60,14 +60,14 @@ def motionActiveHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
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 {
|
} else {
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
log.debug "$motion1 has moved while you were out, sending notifications to: ${recipients?.size()}"
|
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)
|
sendNotificationToContacts("${motion1.label} ${motion1.name} moved while you were out", recipients)
|
||||||
}
|
}
|
||||||
else {
|
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")
|
sendSms(phone1, "${motion1.label} ${motion1.name} moved while you were out")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,13 +53,13 @@ def accelerationActiveHandler(evt) {
|
|||||||
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
def alreadySentSms = recentEvents.count { it.value && it.value == "active" } > 1
|
||||||
|
|
||||||
if (alreadySentSms) {
|
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 {
|
} else {
|
||||||
if (location.contactBookEnabled) {
|
if (location.contactBookEnabled) {
|
||||||
sendNotificationToContacts("Gun case has moved!", recipients)
|
sendNotificationToContacts("Gun case has moved!", recipients)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug "$accelerationSensor has moved, texting $phone1"
|
log.debug "$accelerationSensor has moved, texting phone"
|
||||||
sendSms(phone1, "Gun case has moved!")
|
sendSms(phone1, "Gun case has moved!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ def firstPage()
|
|||||||
def lightSwitchesDiscovered = lightSwitchesDiscovered()
|
def lightSwitchesDiscovered = lightSwitchesDiscovered()
|
||||||
|
|
||||||
return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: true) {
|
return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: true) {
|
||||||
|
section { paragraph title: "Note:", "This device has not been officially tested and certified to “Work with SmartThings”. You can connect it to your SmartThings home but performance may vary and we will not be able to provide support or assistance." }
|
||||||
section("Select a device...") {
|
section("Select a device...") {
|
||||||
input "selectedSwitches", "enum", required:false, title:"Select Wemo Switches \n(${switchesDiscovered.size() ?: 0} found)", multiple:true, options:switchesDiscovered
|
input "selectedSwitches", "enum", required:false, title:"Select Wemo Switches \n(${switchesDiscovered.size() ?: 0} found)", multiple:true, options:switchesDiscovered
|
||||||
input "selectedMotions", "enum", required:false, title:"Select Wemo Motions \n(${motionsDiscovered.size() ?: 0} found)", multiple:true, options:motionsDiscovered
|
input "selectedMotions", "enum", required:false, title:"Select Wemo Motions \n(${motionsDiscovered.size() ?: 0} found)", multiple:true, options:motionsDiscovered
|
||||||
@@ -681,4 +682,4 @@ private Boolean hasAllHubsOver(String desiredFirmware) {
|
|||||||
|
|
||||||
private List getRealHubFirmwareVersions() {
|
private List getRealHubFirmwareVersions() {
|
||||||
return location.hubs*.firmwareVersionString.findAll { it }
|
return location.hubs*.firmwareVersionString.findAll { it }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def authPage() {
|
|||||||
|
|
||||||
def oauthInitUrl() {
|
def oauthInitUrl() {
|
||||||
def token = getToken()
|
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
|
// store these for validate after the user takes the oauth journey
|
||||||
state.oauth_request_token = token.oauth_token
|
state.oauth_request_token = token.oauth_token
|
||||||
@@ -76,7 +76,7 @@ def getToken() {
|
|||||||
]
|
]
|
||||||
def requestTokenBaseUrl = "https://oauth.withings.com/account/request_token"
|
def requestTokenBaseUrl = "https://oauth.withings.com/account/request_token"
|
||||||
def url = buildSignedUrl(requestTokenBaseUrl, params)
|
def url = buildSignedUrl(requestTokenBaseUrl, params)
|
||||||
log.debug "getToken - url: $url"
|
//log.debug "getToken - url: $url"
|
||||||
|
|
||||||
return getJsonFromUrl(url)
|
return getJsonFromUrl(url)
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ def exchangeToken() {
|
|||||||
|
|
||||||
def requestTokenBaseUrl = "https://oauth.withings.com/account/access_token"
|
def requestTokenBaseUrl = "https://oauth.withings.com/account/access_token"
|
||||||
def url = buildSignedUrl(requestTokenBaseUrl, params, tokenSecret)
|
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)
|
def token = getJsonFromUrl(url)
|
||||||
|
|
||||||
@@ -198,8 +198,8 @@ def exchangeToken() {
|
|||||||
|
|
||||||
def load() {
|
def load() {
|
||||||
def json = get(getMeasurement(new Date() - 30))
|
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)
|
parse(data:json)
|
||||||
|
|
||||||
def html = """
|
def html = """
|
||||||
|
|||||||
Reference in New Issue
Block a user