Compare commits

..

46 Commits

Author SHA1 Message Date
Alexandre Fonseca
e842863f45 MSA-1695: This is an adaptation of standard smartthings Dimmer to be used with Fibaro Roller Shutter 2 (model FGR-222, firmware 2.5). 2017-01-09 16:27:14 -08:00
Jack Chi
c028515fcd Merge pull request #1546 from skt123/dimmer_switch
[CHF-487] Health Check for Z-Wave Dimmer Switch
2016-12-29 10:29:43 -08:00
Jack Chi
751c98d123 Merge pull request #1550 from parijatdas/enerwave
[CHF-488] [CHF-489] [CHF-490] Implementation of HealthCheck for Enerwave Duplex Receptacle ZW15R, Enerwave On/Off Switch ZW15S and Leviton 15A Switch VRS15-1LZ
2016-12-29 10:27:18 -08:00
Jack Chi
5b874e8f3a Merge pull request #1545 from pchomal/dimmerswitch_generic
[CHF-477] Health Check for Z-Wave Dimmer Switch Generic
2016-12-27 11:36:51 -08:00
Parijat Das
9e10405527 Added fingerprints for the following devices:
1. Enerwave Duplex Receptacle ZW15R
2. Enerwave On/Off Switch ZW15S
3. Leviton 15A Switch VRS15-1LZ
2016-12-20 18:33:50 +05:30
sushant.k1
32ceaff54d [CHF-487]
Added Health Check Implementation for:
1. 1,000-Watt In-Wall Smart Dimmer Switch (GE 12725)
2. In-Wall Smart Fan Control (GE 12730)
2016-12-20 12:08:16 +05:30
piyush.c
5b1da30a47 [CHF-477]
Health Check implementation for Z-Wave Dimmer Switch Generic with checkinterval of 32min
2016-12-20 10:39:08 +05:30
Vinay Rao
0a82077b24 Merge pull request #1519 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-12-07 14:15:16 -08:00
Vinay Rao
38ef9e5c77 Merge pull request #1517 from SmartThingsCommunity/production
Rolling down production hotfix to staging
2016-12-07 12:10:01 -08:00
Vinay Rao
6a71615ca5 Merge pull request #1505 from varzac/sendevent-appengine-fix
DPROT-215 Don't use sendEvent for AppEngine parse events
2016-12-02 11:39:24 -08:00
Zach Varberg
9939591005 Don't use sendEvent for AppEngine parse events
With the changes made for
https://smartthings.atlassian.net/browse/DPROT-200 there were a few DTHs
that were using sendEvent to directly send events generated during
parse.  However, because using sendEvent didn't result in parse
returning an event AppEngine would send the description up to the cloud
DTH to be handled.  In some cases this resulted in multiple events for
the same device trigger.

This resolves: https://smartthings.atlassian.net/browse/DPROT-215
2016-12-01 14:52:24 -06:00
Vinay Rao
d7f2bc1d79 Merge pull request #1502 from bflorian/PROB-1426-life360-logging
PROB-1426 adding logging to Life360
2016-11-28 13:20:16 -08:00
bflorian
3c5d727d4c PROB-1426 adding logging to Life360 2016-11-28 16:00:42 -05:00
Vinay Rao
bbad6dfa7a Merge pull request #1501 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-11-28 10:35:24 -08:00
Vinay Rao
df6c815aa4 Merge pull request #1500 from SmartThingsCommunity/production
Rolling down production hotfix to staging
2016-11-28 10:34:01 -08:00
Tom Manley
d16ac00eb6 Merge pull request #1493 from tpmanley/hotfix/zll-dimmer-join-failure
zll-dimmer-bulb: Fix bug in configure preventing devices from joining
2016-11-23 23:30:08 -06:00
Tom Manley
1941f56007 zll-dimmer-bulb: Fix bug in configure preventing devices from joining
The configureHealthCheck method and therefore the configure method was
returning a boolean which was causing an exception during the initial
join that would prevent the device from showing up to the user.

https://smartthings.atlassian.net/browse/PROB-1428
2016-11-23 23:01:05 -06:00
Vinay Rao
f34df19a65 Merge pull request #1490 from SmartThingsCommunity/master
Rolling up master to staging
2016-11-22 13:00:00 -08:00
Vinay Rao
af40246655 Merge pull request #1489 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-11-22 12:59:05 -08:00
Vinay Rao
20239ca982 Merge pull request #1488 from SmartThingsCommunity/staging
Rolling up to production for deploy
2016-11-22 12:43:20 -08:00
Vinay Rao
b20c0e371f Merge pull request #1487 from SmartThingsCommunity/production
Rolling down production hotfix to staging
2016-11-22 12:42:25 -08:00
Vinay Rao
60756e6dc6 Merge pull request #1478 from yafenzhang/MSA-1591-15
MSA-1591: New Device Handle for Osram Lightify Light(ZHA)
2016-11-21 09:26:08 -08:00
bflorian
ee2e1ee04f Merge pull request #1483 from juano2310/staging
DVCSMP-2211 - Update Button Number
2016-11-18 14:55:18 -08:00
juano2310
e443cb641c DVCSMP-2211 - Update Button Number 2016-11-18 11:28:30 -05:00
Vinay Rao
b9993a9bf2 Merge pull request #1480 from SmartThingsCommunity/master
Rolling up master to staging
2016-11-16 13:38:01 -08:00
Ingvar Marstorp
092971c786 Merge pull request #1334 from marstorp/boseHealth
CHF-420 Add device health check to Bose DTH
2016-11-16 11:06:30 -08:00
Vinay Rao
a8ee927893 Merge pull request #1479 from varzac/fix-motion-sensor
[DPROT-200] Fix broken generation of smartsense-motion events
2016-11-16 08:55:13 -08:00
Zach Varberg
51979f0030 Fix broken generation of smartsense-motion events
When the change went out to allow messages to be passed up to the cloud
the smartsense-motion DTH was broken with the changes for
https://smartthings.atlassian.net/browse/DPROT-200
2016-11-16 10:44:48 -06:00
Zhang Yafen
61d1205e7d MSA-1591: 1.New Device Handle for Osram Lightify Light(ZHA)
(1) SYLVANIA Smart BR30 Softw White
(2) SYLVANIA Smart PAR38 Softw White
(3) SYLVANIA Smart Outdoor RGBW Flex
2. Modify deviceJoinName of NAFTA Lightify product, all the lightify product which use ZHA protocol will be named using "SYLVANIA Smart XXXX".
2016-11-16 19:57:46 +08:00
Vinay Rao
1dfd1818b4 Merge pull request #1471 from SmartThingsCommunity/master
Rolling up master to staging
2016-11-15 14:05:57 -08:00
Vinay Rao
1de73b643c Merge pull request #1470 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-11-15 14:04:17 -08:00
Vinay Rao
e5c21ef720 Merge pull request #1469 from SmartThingsCommunity/staging
Rolling up staging for production deploy
2016-11-15 13:18:56 -08:00
Vinay Rao
fcb504f57e Merge pull request #1468 from dkirker/sengled_whitetemp
DEVC-523: Add Element Plus bulb
2016-11-15 12:15:27 -08:00
Donald Kirker
b9229c6ef8 Add Element Plus bulb. 2016-11-15 12:05:44 -08:00
Vinay Rao
91a9856a32 Merge pull request #1467 from varzac/revert-eti-binding-table-fixes
[DVCSMP-2175] Revert eti binding table fixes
2016-11-15 11:32:44 -08:00
Zach Varberg
a84ffdde91 Revert "ETI Clear binding table entries to other devices"
This reverts commit 969852602c.
2016-11-15 13:24:23 -06:00
Zach Varberg
3034cc8bcb Revert "Do not delete all binding table entries"
This reverts commit 65bb10d6d6.
2016-11-15 13:23:57 -06:00
Vinay Rao
918e9d9397 Merge pull request #1465 from yafenzhang/MSA-1587-13
MSA-1587: New Device Handle for Osram Lightify Light(ZLL)
2016-11-15 04:09:04 -08:00
Zhang Yafen
0c040120cc MSA-1587: 1.New Device Handle for Osram Lightify Light saled in Europe market(ZLL)
(1) OSRAM LIGHTIFY Gardenpole RGBW
(2) OSRAM LIGHTIFY Outdoor Flex RGBW
(3) OSRAM LIGHTIFY RGBW PAR 16 50
(4) OSRAM LIGHTIFY Flex RGBW, this product uses ZLL, so move it from zigbee-white-color-temperature-bulb to zll-white-color-temperature-bulb
(5) OSRAM LIGHTIFY Classic B40 Tunable White, this product uses ZLL, so move it from zigbee-white-color-temperature-bulb to zll-white-color-temperature-bulb
2016-11-15 18:55:32 +08:00
Vinay Rao
6c84c052cb Merge pull request #1462 from jackchi/health-nov15-hotfix
[CHF-442][DVCSMP-2179] Hotfix for EcoBee thermostat & Osram Tunable White
2016-11-14 15:58:06 -08:00
Vinay Rao
f017bff6ef Merge pull request #1464 from varzac/fix-zigbee-white-tunable-binding-table
[DVCSMP-2175] Do not delete all binding table entries
2016-11-14 14:14:21 -08:00
Zach Varberg
65bb10d6d6 Do not delete all binding table entries
There was a bug when comparing the destination address for binding table
entries that would cause all binding table entries to be deleted.  This
fixes that.

This is a fix for: https://smartthings.atlassian.net/browse/DVCSMP-2175
2016-11-14 14:38:15 -06:00
jackchi
3f93de247b [CHF-442][DVCSMP-2179] Hotfix for EcoBee thermostat & Osram RT5/6 Tunable White naming 2016-11-14 12:05:10 -08:00
Vinay Rao
2b7af3ef8d Merge pull request #1461 from varzac/fix-smartpower-dimming-outlet
[DVCSMP-2227] Handle all messages in smartpower dimming outlet
2016-11-14 11:27:06 -08:00
Zach Varberg
cf9d123aa0 Handle all messages in smartpower dimming outlet
Previously the implementation of isKnownDescription didn't cover all
possible messages that could be parsed and this caused null pointer
exceptions with certain messages.  This now handles all the
possibilities.

This resolves: https://smartthings.atlassian.net/browse/DVCSMP-2227
2016-11-14 11:08:30 -06:00
marstorp
4b44460b0b CHF-420 Add device health check to Bose DTH 2016-10-11 10:58:49 -07:00
28 changed files with 494 additions and 809 deletions

View File

@@ -0,0 +1,345 @@
/**
* Device Type Definition File
*
* Device Type: Fibaro Roller Shutter
* File Name: fibaro-roller-shutter.groovy
* Initial Release: 2015-06-00
* Author: SmartThings
*
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
// Automatically generated. Make future change here.
definition (name: "Fibaro Roller Shutter", namespace: "ADF", author: "ADF") {
capability "Switch Level"
capability "Actuator"
capability "Switch"
capability "Polling"
capability "Refresh"
capability "Sensor"
command "resetParams2StDefaults"
command "listCurrentParams"
command "updateZwaveParam"
fingerprint deviceId: "0x1101", inClusters: "0x72,0x86,0x70,0x85,0x8E,0x26,0x7A,0x27,0x73,0xEF,0x26,0x2B"
}
simulator {
status "on": "command: 2003, payload: FF"
status "off": "command: 2003, payload: 00"
status "09%": "command: 2003, payload: 09"
status "10%": "command: 2003, payload: 0A"
status "33%": "command: 2003, payload: 21"
status "66%": "command: 2003, payload: 42"
status "99%": "command: 2003, payload: 63"
// reply messages
reply "2001FF,delay 5000,2602": "command: 2603, payload: FF"
reply "200100,delay 5000,2602": "command: 2603, payload: 00"
reply "200119,delay 5000,2602": "command: 2603, payload: 19"
reply "200132,delay 5000,2602": "command: 2603, payload: 32"
reply "20014B,delay 5000,2602": "command: 2603, payload: 4B"
reply "200163,delay 5000,2602": "command: 2603, payload: 63"
}
tiles {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
attributeState("on", label:"open", action:"switch.off", icon:"st.doors.garage.garage-open", backgroundColor:"#79b821", nextState: "turningOff")
attributeState("off", label:"close", action:"switch.on", icon:"st.doors.garage.garage-closed", backgroundColor:"#ffffff", nextState: "turningOn")
attributeState("turningOn", label:"opening", action:"switch.off", icon:"st.doors.garage.garage-opening", backgroundColor:"#79b821", nextState: "turningOff")
attributeState("turningOff", label:"closing", action:"switch.on", icon:"st.doors.garage.garage-closing", backgroundColor:"#ffffff", nextState:"turningOn")
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState("level", action:"switch level.setLevel")
}
}
standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("on", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state("on", label:'open', action:"switch.on", icon:"st.doors.garage.garage-opening")
}
standardTile("off", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state("off", label:'close', action:"switch.off", icon:"st.doors.garage.garage-closing")
}
standardTile("stop", "device.level", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state("default", label:'stop/my', action:"switch level.setLevel", icon:"st.Transportation.transportation13")
}
main(["switch"])
details(["switch", "refresh", "on", "off", "stop"])
}
}
def parse(String description) {
def item1 = [
canBeCurrentState: false,
linkText: getLinkText(device),
isStateChange: false,
displayed: false,
descriptionText: description,
value: description
]
def result
def cmd = zwave.parse(description, [0x26: 1, 0x70: 2, 072: 2])
//log.debug "cmd: ${cmd}"
if (cmd) {
result = createEvent(cmd, item1)
}
else {
item1.displayed = displayed(description, item1.isStateChange)
result = [item1]
}
if(result?.descriptionText)
log.debug "Parse returned ${result?.descriptionText}"
result
}
def createEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, Map item1) {
def result = doCreateEvent(cmd, item1)
for (int i = 0; i < result.size(); i++) {
result[i].type = "physical"
}
result
}
def createEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd, Map item1) {
def result = doCreateEvent(cmd, item1)
for (int i = 0; i < result.size(); i++) {
result[i].type = "physical"
}
result
}
def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStartLevelChange cmd, Map item1) {
[]
}
def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd, Map item1) {
[response(zwave.basicV1.basicGet())]
}
def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd, Map item1) {
def result = doCreateEvent(cmd, item1)
for (int i = 0; i < result.size(); i++) {
result[i].type = "physical"
}
result
}
def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd, Map item1) {
def result = doCreateEvent(cmd, item1)
result[0].descriptionText = "${item1.linkText} is ${item1.value}"
result[0].handlerName = cmd.value ? "statusOn" : "statusOff"
for (int i = 0; i < result.size(); i++) {
result[i].type = "digital"
}
result
}
def doCreateEvent(physicalgraph.zwave.Command cmd, Map item1) {
def result = [item1]
item1.name = "switch"
item1.value = cmd.value ? "on" : "off"
item1.handlerName = item1.value
item1.descriptionText = "${item1.linkText} was turned ${item1.value}"
item1.canBeCurrentState = true
item1.isStateChange = isStateChange(device, item1.name, item1.value)
item1.displayed = item1.isStateChange
if (cmd.value >= 5) {
def item2 = new LinkedHashMap(item1)
item2.name = "level"
item2.value = cmd.value as String
item2.unit = "%"
item2.descriptionText = "${item1.linkText} dimmed ${item2.value} %"
item2.canBeCurrentState = true
item2.isStateChange = isStateChange(device, item2.name, item2.value)
item2.displayed = false
result << item2
}
result
}
def createEvent(physicalgraph.zwave.Command cmd, Map map) {
// Handles any Z-Wave commands we aren't interested in
log.debug "UNHANDLED COMMAND $cmd"
}
def on() {
log.info "on"
delayBetween([zwave.basicV1.basicSet(value: 0xFF).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
}
def off() {
delayBetween ([zwave.basicV1.basicSet(value: 0x00).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
}
def setLevel(value) {
def level = Math.min(value as Integer, 99)
delayBetween ([zwave.basicV1.basicSet(value: level).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
}
def setLevel(value, duration) {
def level = Math.min(value as Integer, 99)
def dimmingDuration = duration < 128 ? duration : 128 + Math.round(duration / 60)
zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format()
}
def poll() {
zwave.switchMultilevelV1.switchMultilevelGet().format()
}
def refresh() {
zwave.switchMultilevelV1.switchMultilevelGet().format()
}
/**
* Configures the device to settings needed by SmarthThings at device discovery time. Assumes
* device is already at default parameter settings.
*
* @param none
*
* @return none
*/
def configure() {
log.debug "Configuring Device..."
def cmds = []
// send associate to group 3 to get sensor data reported only to hub
cmds << zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format()
delayBetween(cmds, 500)
}
def createEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd, Map item1) {
log.debug "${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd.configurationValue}'"
}
/**
* This method will allow the user to update device parameters (behavior) from an app.
* the user can write his/her own app to envoke this method.
* No type or value checking is done to compare to what device capability or reaction.
* It is up to user to read OEM documentation prio to envoking this method.
*
* <p>THIS IS AN ADVANCED OPERATION. USE AT YOUR OWN RISK! READ OEM DOCUMENTATION!
*
* @param List[paramNumber:80,value:10,size:1]
*
*
* @return none
*/
def updateZwaveParam(params) {
if ( params ) {
def pNumber = params.paramNumber
def pSize = params.size
def pValue = [params.value]
log.debug "Make sure device is awake and in recieve mode"
log.debug "Updating ${device.displayName} parameter number '${pNumber}' with value '${pValue}' with size of '${pSize}'"
def cmds = []
cmds << zwave.configurationV1.configurationSet(configurationValue: pValue, parameterNumber: pNumber, size: pSize).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: pNumber).format()
delayBetween(cmds, 1000)
}
}
/**
* Sets all of available Fibaro parameters back to the device defaults except for what
* SmartThings needs to support the stock functionality as released.
*
* <p>THIS IS AN ADVANCED OPERATION. USE AT YOUR OWN RISK! READ OEM DOCUMENTATION!
*
* @param none
*
* @return none
*/
def resetParams2StDefaults() {
log.debug "Resetting ${device.displayName} parameters to SmartThings compatible defaults"
def cmds = []
cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 1, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 6, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 7, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 8, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [5], parameterNumber: 9, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 10, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 11, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [99], parameterNumber: 12, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 13, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 14, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 15, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 16, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 17, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 18, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 19, size: 1).format()
//Param 20 is different for 50Hz or 60 Hz uncomment the line that will reflect the power frequency used
//cmds << zwave.configurationV1.configurationSet(configurationValue: [101], parameterNumber: 20, size: 1).format() //60 Hz (US)
//cmds << zwave.configurationV1.configurationSet(configurationValue: [110], parameterNumber: 20, size: 1).format() //50 Hz (UK)
cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 30, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [600], parameterNumber: 39, size: 1).format()
//Param 40 not needed by SmartThings
//cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 40, size: 1).format()
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 41, size: 1).format()
delayBetween(cmds, 500)
}
/**
* Lists all of available Fibaro parameters and thier current settings out to the
* logging window in the IDE. This will be called from the "Fibaro Tweaker" or
* user's own app.
*
* <p>THIS IS AN ADVANCED OPERATION. USE AT YOUR OWN RISK! READ OEM DOCUMENTATION!
*
* @param none
*
* @return none
*/
def listCurrentParams() {
log.debug "Listing of current parameter settings of ${device.displayName}"
def cmds = []
cmds << zwave.configurationV1.configurationGet(parameterNumber: 1).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 6).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 8).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 9).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 11).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 13).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 14).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 15).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 16).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 17).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 18).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 19).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 20).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 30).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 39).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 40).format()
cmds << zwave.configurationV1.configurationGet(parameterNumber: 41).format()
delayBetween(cmds, 500)
}

View File

@@ -120,6 +120,15 @@ def configure() {
return cmd
}
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
sendEvent(name: "numberOfButtons", value: 4)
}

View File

@@ -109,6 +109,15 @@ def configure() {
return cmds
}
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
sendEvent(name: "numberOfButtons", value: 4)
}

View File

@@ -27,7 +27,7 @@ metadata {
capability "Switch"
capability "Refresh"
capability "Music Player"
capability "Polling"
capability "Health Check"
/**
* Define all commands, ie, if you have a custom action not
@@ -236,7 +236,33 @@ def parse(String event) {
* @return action(s) to take or null
*/
def installed() {
onAction("refresh")
// Notify health check about this device with timeout interval 12 minutes
sendEvent(name: "checkInterval", value: 12 * 60, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
startPoll()
}
/**
* Called by health check if no events been generated in the last 12 minutes
* If device doesn't respond it will be marked offline (not available)
*/
def ping() {
TRACE("ping")
boseSendGetNowPlaying()
}
/**
* Schedule a 2 minute poll of the device to refresh the
* tiles so the user gets the correct information.
*/
def startPoll() {
TRACE("startPoll")
unschedule()
// Schedule 2 minute polling of speaker status (song average length is 3-4 minutes)
def sec = Math.round(Math.floor(Math.random() * 60))
//def cron = "$sec 0/5 * * * ?" // every 5 min
def cron = "$sec 0/2 * * * ?" // every 2 min
log.debug "schedule('$cron', boseSendGetNowPlaying)"
schedule(cron, boseSendGetNowPlaying)
}
/**
@@ -316,14 +342,6 @@ def onAction(String user, data=null) {
return actions
}
/**
* Called every so often (every 5 minutes actually) to refresh the
* tiles so the user gets the correct information.
*/
def poll() {
return boseRefreshNowPlaying()
}
/**
* Joins this speaker into the everywhere zone
*/
@@ -837,6 +855,10 @@ def boseRefreshNowPlaying(delay=0) {
return boseGET("/now_playing")
}
def boseSendGetNowPlaying() {
sendHubCommand(boseGET("/now_playing"))
}
/**
* Requests the list of presets
*
@@ -1014,4 +1036,8 @@ def boseGetDeviceID() {
*/
def getDeviceIP() {
return parent.resolveDNI2Address(device.deviceNetworkId)
}
def TRACE(text) {
log.trace "${text}"
}

View File

@@ -183,6 +183,15 @@ def updateState(String name, String value) {
device.updateDataValue(name, value)
}
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
sendEvent(name: "numberOfButtons", value: 3)
}

View File

@@ -22,9 +22,10 @@ metadata {
capability "Sensor"
capability "Health Check"
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "Z-Wave Wall Dimmer"
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "Z-Wave Wall Dimmer"
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "Z-Wave Plug-In Dimmer"
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "GE In-Wall Smart Dimmer "
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "GE In-Wall Smart Dimmer "
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "GE Plug-In Smart Dimmer "
fingerprint mfr:"0063", prod:"4944", model:"3034", deviceJoinName: "GE In-Wall Smart Fan Control"
}
simulator {

View File

@@ -125,7 +125,7 @@ metadata {
void installed() {
// The device refreshes every 5 minutes by default so if we miss 2 refreshes we can consider it offline
// Using 12 minutes because in testing, device health team found that there could be "jitter"
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "cloud", hubHardwareId: device.hub.hardwareID], displayed: false)
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "cloud"], displayed: false)
}
// Device Watch will ping the device to proactively determine if the device has gone offline

View File

@@ -39,7 +39,7 @@ metadata {
}
def generatePresenceEvent(boolean present) {
log.debug "Here in generatePresenceEvent!"
log.info "Life360 generatePresenceEvent($present)"
def value = formatValue(present)
def linkText = getLinkText(device)
def descriptionText = formatDescriptionText(linkText, present)

View File

@@ -71,7 +71,7 @@ def parse(String description) {
def event = [:]
def finalResult = isKnownDescription(description)
if (finalResult != "false") {
if (finalResult) {
log.info finalResult
if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
@@ -212,13 +212,16 @@ def isKnownDescription(description) {
else if (descMap.cluster == "0B04" || descMap.clusterId == "0B04"){
isDescriptionPower(descMap)
}
else {
return [:]
}
}
else if(description?.startsWith("on/off:")) {
def switchValue = description?.endsWith("1") ? "on" : "off"
return [type: "switch", value : switchValue]
}
else {
return "false"
return [:]
}
}
@@ -252,7 +255,7 @@ def isDescriptionOnOff(descMap) {
return [type: "switch", value : switchValue]
}
else {
return "false"
return [:]
}
}
@@ -279,10 +282,9 @@ def isDescriptionLevel(descMap) {
if (dimmerValue != -1){
return [type: "level", value : dimmerValue]
}
else {
return "false"
return [:]
}
}
@@ -304,7 +306,7 @@ def isDescriptionPower(descMap) {
return [type: "power", value : powerValue]
}
else {
return "false"
return [:]
}
}

View File

@@ -128,7 +128,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07) {
if (cluster.data[0] == 0x00){
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
}
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"

View File

@@ -132,7 +132,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07) {
if (cluster.data[0] == 0x00) {
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
}
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"

View File

@@ -57,6 +57,7 @@ def parse(String description) {
private Map parseBasicMessage(description) {
def name = parseName(description)
def results = [:]
if (name != null) {
def value = parseValue(description)
def linkText = getLinkText(device)
@@ -64,7 +65,7 @@ private Map parseBasicMessage(description) {
def handlerName = value
def isStateChange = isStateChange(device, name, value)
def results = [
results = [
name : name,
value : value,
linkText : linkText,
@@ -73,8 +74,6 @@ private Map parseBasicMessage(description) {
isStateChange : isStateChange,
displayed : displayed(description, isStateChange)
]
} else {
results = [:]
}
log.debug "Parse returned $results.descriptionText"
return results

View File

@@ -161,7 +161,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07) {
if(cluster.data[0] == 0x00) {
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
}
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
@@ -339,7 +339,7 @@ private Map getContactResult(value) {
log.debug "Contact: ${device.displayName} value = ${value}"
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true)
sendEvent(name: 'status', value: value, descriptionText: descriptionText, translatable: true)
return [name: 'status', value: value, descriptionText: descriptionText, translatable: true]
}
private getAccelerationResult(numValue) {

View File

@@ -119,7 +119,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07){
if (cluster.data[0] == 0x00) {
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
}
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"

View File

@@ -103,7 +103,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07) {
if (cluster.data[0] == 0x00){
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]]
}
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"

View File

@@ -126,6 +126,15 @@ private hold(button) {
sendEvent(name: "button", value: "held", data: [buttonNumber: button], descriptionText: "$device.displayName button $button was held", isStateChange: true)
}
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
sendEvent(name: "numberOfButtons", value: 4)
}

View File

@@ -23,9 +23,11 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "OSRAM LIGHTIFY LED Smart Connected Light"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart A19 Soft White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Bulb"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY PAR38 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart PAR38 Soft White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart BR30 Soft White"
}
tiles(scale: 2) {

View File

@@ -28,8 +28,8 @@ metadata {
capability "Switch Level"
capability "Health Check"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Gardenspot RGB", deviceJoinName: "OSRAM LIGHTIFY Gardenspot mini RGB"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Gardenspot RGB", deviceJoinName: "OSRAM LIGHTIFY Gardenspot mini RGB"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Gardenspot RGB", deviceJoinName: "SYLVANIA Smart Gardenspot mini RGB"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Gardenspot RGB", deviceJoinName: "SYLVANIA Smart Gardenspot mini RGB"
}
// UI tile definitions

View File

@@ -32,11 +32,12 @@ metadata {
attribute "colorName", "string"
command "setGenericName"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Flex RGBW", deviceJoinName: "OSRAM LIGHTIFY LED FLEXIBLE STRIP RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Flex RGBW", deviceJoinName: "OSRAM LIGHTIFY LED FLEXIBLE STRIP RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 RGBW", deviceJoinName: "OSRAM LIGHTIFY LED A19 RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR RGBW", deviceJoinName: "OSRAM LIGHTIFY LED BR30 RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT RGBW", deviceJoinName: "OSRAM LIGHTIFY LED RT 5/6 RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Flex RGBW", deviceJoinName: "SYLVANIA Smart Flex RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Flex RGBW", deviceJoinName: "OSRAM LIGHTIFY Flex RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 RGBW", deviceJoinName: "SYLVANIA Smart A19 RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR RGBW", deviceJoinName: "SYLVANIA Smart BR30 RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT RGBW", deviceJoinName: "SYLVANIA Smart RT5/6 RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY FLEX OUTDOOR RGBW", deviceJoinName: "SYLVANIA Smart Outdoor RGBW Flex"
}
// UI tile definitions

View File

@@ -32,11 +32,12 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04", outClusters: "0019"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Flood BR30 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT Tunable White", deviceJoinName: "OSRAM LIGHTIFY RT5/6 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 Tunable White", deviceJoinName: "OSRAM LIGHTIFY LED Tunable White 60W"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR Tunable White", deviceJoinName: "SYLVANIA Smart BR30 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY RT Tunable White", deviceJoinName: "SYLVANIA Smart RT5/6 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Classic A60 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 Tunable White", deviceJoinName: "SYLVANIA Smart A19 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic B40 TW - LIGHTIFY", deviceJoinName: "OSRAM LIGHTIFY Classic B40 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-A19NAE26", deviceJoinName: "Sengled Element plus"
}
// UI tile definitions
@@ -83,105 +84,24 @@ def parse(String description) {
}
}
else {
Map bindingTable = parseBindingTableResponse(description)
if (bindingTable) {
List<String> cmds = []
bindingTable.table_entries.inject(cmds) { acc, entry ->
// The binding entry is not for our hub and should be deleted
if (entry["dstAddr"] != zigbeeEui) {
acc.addAll(removeBinding(entry.clusterId, entry.srcAddr, entry.srcEndpoint, entry.dstAddr, entry.dstEndpoint))
}
acc
}
// There are more entries that we haven't examined yet
if (bindingTable.numTableEntries > bindingTable.startIndex + bindingTable.numEntriesReturned) {
def startPos
if (cmds) {
log.warn "Removing binding entries for other devices: $cmds"
// Since we are removing some entries, we should start in the same spot as we just read since values
// will fill in the newly vacated spots
startPos = bindingTable.startIndex
} else {
// Since we aren't removing anything we move forward to the next set of table entries
startPos = bindingTable.startIndex + bindingTable.numEntriesReturned
}
cmds.addAll(requestBindingTable(startPos))
}
sendHubCommand(cmds.collect { it ->
new physicalgraph.device.HubAction(it)
}, 2000)
} else {
def cluster = zigbee.parse(description)
def cluster = zigbee.parse(description)
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) {
if (cluster.data[0] == 0x00) {
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
else {
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) {
if (cluster.data[0] == 0x00) {
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug "${cluster}"
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug "${cluster}"
}
}
}
def parseBindingTableResponse(description) {
Map descMap = zigbee.parseDescriptionAsMap(description)
if (descMap["clusterInt"] == 0x8033) {
def header_field_lengths = ["transactionSeqNo": 1, "status": 1, "numTableEntries": 1, "startIndex": 1, "numEntriesReturned": 1]
def field_values = [:]
def data = descMap["data"]
header_field_lengths.each { k, v ->
field_values[k] = Integer.parseInt(data.take(v).join(""), 16);
data = data.drop(v);
}
List<Map> table = []
if (field_values.numEntriesReturned) {
def table_entry_lengths = ["srcAddr": 8, "srcEndpoint": 1, "clusterId": 2, "dstAddrMode": 1]
for (def i : 0..(field_values.numEntriesReturned - 1)) {
def entryMap = [:]
table_entry_lengths.each { k, v ->
def val = data.take(v).reverse().join("")
entryMap[k] = val.length() < 8 ? Integer.parseInt(val, 16) : val
data = data.drop(v)
}
switch (entryMap.dstAddrMode) {
case 0x01:
entryMap["dstAddr"] = data.take(2).reverse().join("")
data = data.drop(2)
break
case 0x03:
entryMap["dstAddr"] = data.take(8).reverse().join("")
data = data.drop(8)
entryMap["dstEndpoint"] = Integer.parseInt(data.take(1).join(""), 16)
data = data.drop(1)
break
}
table << entryMap
}
}
field_values["table_entries"] = table
return field_values
}
return [:]
}
def requestBindingTable(startPos=0) {
return ["zdo mgmt-bind 0x${zigbee.deviceNetworkId} $startPos"]
}
def removeBinding(cluster, srcAddr, srcEndpoint, destAddr, destEndpoint) {
return ["zdo unbind unicast 0x${zigbee.deviceNetworkId} {${srcAddr}} $srcEndpoint $cluster {${destAddr}} $destEndpoint"]
}
def off() {
zigbee.off()
}
@@ -211,7 +131,8 @@ def configure() {
// enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
refresh() + requestBindingTable(0) + ["delay 2000"]
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
refresh()
}
def setColorTemperature(value) {

View File

@@ -127,8 +127,8 @@ def configureHealthCheck() {
def configure() {
log.debug "configure()"
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
configureHealthCheck()
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
}
def updated() {

View File

@@ -27,6 +27,10 @@ metadata {
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019"
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019"
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", "manufacturer":"OSRAM", "model":"Classic A60 RGBW", deviceJoinName: "OSRAM LIGHTIFY LED Classic A60 RGBW"
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "PAR 16 50 RGBW - LIGHTIFY", deviceJoinName: "OSRAM LIGHTIFY RGBW PAR 16 50"
fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,0300,1000,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Flex RGBW", deviceJoinName: "OSRAM LIGHTIFY Flex RGBW"
fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,0300,1000,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Gardenpole RGBW-Lightify", deviceJoinName: "OSRAM LIGHTIFY Gardenpole RGBW"
fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,0300,1000,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Outdoor Flex RGBW", deviceJoinName: "OSRAM LIGHTIFY Outdoor Flex RGBW"
}
// UI tile definitions

View File

@@ -32,6 +32,7 @@ metadata {
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000, 0B04, FC0F", outClusters: "0019", "manufacturer":"OSRAM", "model":"Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Classic A60 Tunable White"
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000, FC0F", outClusters: "0019", "manufacturer":"OSRAM", "model":"PAR16 50 TW", deviceJoinName: "OSRAM LIGHTIFY LED PAR16 50 Tunable White"
fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic B40 TW - LIGHTIFY", deviceJoinName: "OSRAM LIGHTIFY Classic B40 Tunable White"
}
// UI tile definitions

View File

@@ -24,6 +24,10 @@ metadata {
fingerprint inClusters: "0x26", deviceJoinName: "Z-Wave Dimmer"
fingerprint mfr:"001D", prod:"1902", deviceJoinName: "Z-Wave Dimmer"
fingerprint mfr:"001D", prod:"1B03", model:"0334", deviceJoinName: "Leviton Universal Dimmer"
fingerprint mfr:"011A", prod:"0102", model:"0201", deviceJoinName: "Enerwave In-Wall Dimmer"
fingerprint mfr:"001D", prod:"1001", model:"0334", deviceJoinName: "Leviton 3-Speed Fan Controller"
fingerprint mfr:"001D", prod:"0602", model:"0334", deviceJoinName: "Leviton Magnetic Low Voltage Dimmer"
fingerprint mfr:"001D", prod:"0401", model:"0334", deviceJoinName: "Leviton 600W Incandescent Dimmer"
}
simulator {

View File

@@ -25,6 +25,9 @@ metadata {
fingerprint mfr:"0063", prod:"4F50", model:"3031", deviceJoinName: "GE Plug-in Outdoor Switch"
fingerprint mfr:"001D", prod:"1D04", model:"0334", deviceJoinName: "Leviton Outlet"
fingerprint mfr:"001D", prod:"1C02", model:"0334", deviceJoinName: "Leviton Switch"
fingerprint mfr:"001D", prod:"0301", model:"0334", deviceJoinName: "Leviton 15A Switch"
fingerprint mfr:"011A", prod:"0101", model:"0102", deviceJoinName: "Enerwave On/Off Switch"
fingerprint mfr:"011A", prod:"0101", model:"0603", deviceJoinName: "Enerwave Duplex Receptacle"
}
// simulator metadata

View File

@@ -1,662 +0,0 @@
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
/**
* OpenT2T SmartApp Test
*
* Copyright 2016 OpenT2T
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
definition(
name: "OpenT2T SmartApp Test",
namespace: "opent2t",
author: "OpenT2T",
description: "Test app to test end to end SmartThings scenarios via OpenT2T",
category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
/** --------------------+---------------+-----------------------+------------------------------------
* Device Type | Attribute Name| Commands | Attribute Values
* --------------------+---------------+-----------------------+------------------------------------
* switches | switch | on, off | on, off
* motionSensors | motion | | active, inactive
* contactSensors | contact | | open, closed
* presenceSensors | presence | | present, 'not present'
* temperatureSensors | temperature | | <numeric, F or C according to unit>
* accelerationSensors | acceleration | | active, inactive
* waterSensors | water | | wet, dry
* lightSensors | illuminance | | <numeric, lux>
* humiditySensors | humidity | | <numeric, percent>
* locks | lock | lock, unlock | locked, unlocked
* garageDoors | door | open, close | unknown, closed, open, closing, opening
* cameras | image | take | <String>
* thermostats | thermostat | setHeatingSetpoint, | temperature, heatingSetpoint, coolingSetpoint,
* | | setCoolingSetpoint, | thermostatSetpoint, thermostatMode,
* | | off, heat, cool, auto,| thermostatFanMode, thermostatOperatingState
* | | emergencyHeat, |
* | | setThermostatMode, |
* | | fanOn, fanAuto, |
* | | fanCirculate, |
* | | setThermostatFanMode |
* --------------------+---------------+-----------------------+------------------------------------
*/
//Device Inputs
preferences {
section("Allow OpenT2T to control these things...") {
input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false, hideWhenEmpty: true
input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false, hideWhenEmpty: true
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false, hideWhenEmpty: true
input "thermostats", "capability.thermostat", title: "Which Thermostat?", multiple: true, required: false, hideWhenEmpty: true
input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false, hideWhenEmpty: true
}
}
def getInputs() {
def inputList = []
inputList += contactSensors?: []
inputList += garageDoors?: []
inputList += locks?: []
inputList += cameras?: []
inputList += motionSensors?: []
inputList += presenceSensors?: []
inputList += switches?: []
inputList += thermostats?: []
inputList += waterSensors?: []
return inputList
}
//API external Endpoints
mappings {
path("/devices") {
action: [
GET: "getDevices"
]
}
path("/devices/:id") {
action: [
GET: "getDevice"
]
}
path("/update/:id") {
action: [
PUT: "updateDevice"
]
}
path("/deviceSubscription") {
action: [
POST: "registerDeviceChange",
DELETE: "unregisterDeviceChange"
]
}
path("/locationSubscription") {
action: [
POST: "registerDeviceGraph",
DELETE: "unregisterDeviceGraph"
]
}
}
def installed() {
log.debug "Installing with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updating with settings: ${settings}"
//Initialize state variables if didn't exist.
if( state.deviceSubscriptionMap == null ){
state.deviceSubscriptionMap = [:]
log.debug "deviceSubscriptionMap created."
}
if( state.locationSubscriptionMap == null ){
state.locationSubscriptionMap = [:]
log.debug "locationSubscriptionMap created."
}
if(state.verificationKeyMap == null){
state.verificationKeyMap = [:]
log.debug "verificationKeyMap created."
}
unsubscribe()
registerAllDeviceSubscriptions()
}
def initialize() {
log.debug "Initializing with settings: ${settings}"
state.deviceSubscriptionMap = [:]
log.debug "deviceSubscriptionMap created."
state.locationSubscriptionMap = [:]
log.debug "locationSubscriptionMap created."
state.verificationKeyMap = [:]
log.debug "verificationKeyMap created."
registerAllDeviceSubscriptions()
}
/*** Subscription Functions ***/
//Subscribe events for all devices
def registerAllDeviceSubscriptions() {
registerChangeHandler(inputs)
}
//Subscribe to events from a list of devices
def registerChangeHandler(myList) {
myList.each { myDevice ->
def theAtts = myDevice.supportedAttributes
theAtts.each {att ->
subscribe(myDevice, att.name, deviceEventHandler)
log.info "Registering for ${myDevice.displayName}.${att.name}"
}
}
}
//Endpoints function: Subscribe to events from a specific device
def registerDeviceChange() {
def subscriptionEndpt = params.subscriptionURL
def deviceId = params.deviceId
def myDevice = findDevice(deviceId)
if( myDevice == null ){
httpError(404, "Cannot find device with device ID ${deviceId}.")
}
def theAtts = myDevice.supportedAttributes
try {
theAtts.each {att ->
subscribe(myDevice, att.name, deviceEventHandler)
}
log.info "Subscribing for ${myDevice.displayName}"
if(subscriptionEndpt != null){
if(state.deviceSubscriptionMap[deviceId] == null){
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
} else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)){
// state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
// For now, we will only have one subscription endpoint per device
state.deviceSubscriptionMap.remove(deviceId)
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
}
if(params.key != null){
state.verificationKeyMap[subscriptionEndpt] = params.key
log.info "Added verification key: ${params.key} for ${subscriptionEndpt}"
}
}
} catch (e) {
httpError(500, "something went wrong: $e")
}
log.info "Current subscription map is ${state.deviceSubscriptionMap}"
log.info "Current verification key map is ${state.verificationKeyMap}"
return ["succeed"]
}
//Endpoints function: Unsubscribe to events from a specific device
def unregisterDeviceChange() {
def subscriptionEndpt = params.subscriptionURL
def deviceId = params.deviceId
def myDevice = findDevice(deviceId)
if( myDevice == null ){
httpError(404, "Cannot find device with device ID ${deviceId}.")
}
try {
if(subscriptionEndpt != null && subscriptionEndpt != "undefined"){
if (state.deviceSubscriptionMap[deviceId]?.contains(subscriptionEndpt)){
if(state.deviceSubscriptionMap[deviceId].size() == 1){
state.deviceSubscriptionMap.remove(deviceId)
} else {
state.deviceSubscriptionMap[deviceId].remove(subscriptionEndpt)
}
state.verificationKeyMap.remove(subscriptionEndpt)
log.info "Removed subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
}
} else {
state.deviceSubscriptionMap.remove(deviceId)
log.info "Unsubscriping for ${myDevice.displayName}"
}
} catch (e) {
httpError(500, "something went wrong: $e")
}
log.info "Current subscription map is ${state.deviceSubscriptionMap}"
log.info "Current verification key map is ${state.verificationKeyMap}"
}
//Endpoints function: Subscribe to device additiona/removal updated in a location
def registerDeviceGraph() {
def subscriptionEndpt = params.subscriptionURL
if (subscriptionEndpt != null && subscriptionEndpt != "undefined"){
subscribe(location, "DeviceCreated", locationEventHandler, [filterEvents: false])
subscribe(location, "DeviceUpdated", locationEventHandler, [filterEvents: false])
subscribe(location, "DeviceDeleted", locationEventHandler, [filterEvents: false])
if(state.locationSubscriptionMap[location.id] == null){
state.locationSubscriptionMap.put(location.id, [subscriptionEndpt])
log.info "Added subscription URL: ${subscriptionEndpt} for Location ${location.name}"
}else if (!state.locationSubscriptionMap[location.id].contains(subscriptionEndpt)){
state.locationSubscriptionMap[location.id] << subscriptionEndpt
log.info "Added subscription URL: ${subscriptionEndpt} for Location ${location.name}"
}
if(params.key != null){
state.verificationKeyMap[subscriptionEndpt] = params.key
log.info "Added verification key: ${params.key} for ${subscriptionEndpt}"
}
log.info "Current location subscription map is ${state.locationSubscriptionMap}"
log.info "Current verification key map is ${state.verificationKeyMap}"
return ["succeed"]
} else {
httpError(400, "missing input parameter: subscriptionURL")
}
}
//Endpoints function: Unsubscribe to events from a specific device
def unregisterDeviceGraph() {
def subscriptionEndpt = params.subscriptionURL
try {
if(subscriptionEndpt != null && subscriptionEndpt != "undefined"){
if (state.locationSubscriptionMap[location.id]?.contains(subscriptionEndpt)){
if(state.locationSubscriptionMap[location.id].size() == 1){
state.locationSubscriptionMap.remove(location.id)
} else {
state.locationSubscriptionMap[location.id].remove(subscriptionEndpt)
}
state.verificationKeyMap.remove(subscriptionEndpt)
log.info "Removed subscription URL: ${subscriptionEndpt} for Location ${location.name}"
}
}else{
httpError(400, "missing input parameter: subscriptionURL")
}
} catch (e) {
httpError(500, "something went wrong: $e")
}
log.info "Current location subscription map is ${state.locationSubscriptionMap}"
log.info "Current verification key map is ${state.verificationKeyMap}"
}
//When events are triggered, send HTTP post to web socket servers
def deviceEventHandler(evt) {
def evtDevice = evt.device
def evtDeviceType = getDeviceType(evtDevice)
def deviceData = [];
if(evt.data != null){
def evtData = parseJson(evt.data)
log.info "Received event for ${evtDevice.displayName}, data: ${evtData}, description: ${evt.descriptionText}"
}
if(evtDeviceType == "thermostat") {
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status:evtDevice.status, deviceType:evtDeviceType, manufacturer:evtDevice.manufacturerName, model:evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationMode: getLocationModeInfo(), locationId: location.id ]
} else {
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status:evtDevice.status, deviceType:evtDeviceType, manufacturer:evtDevice.manufacturerName, model:evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationId: location.id ]
}
def params = [ body: deviceData ]
//send event to all subscriptions urls
log.debug "Current subscription urls for ${evtDevice.displayName} is ${state.deviceSubscriptionMap[evtDevice.id]}"
state.deviceSubscriptionMap[evtDevice.id].each {
params.uri = "${it}"
if(state.verificationKeyMap[it] != null ){
def key = state.verificationKeyMap[it]
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
}
log.trace "POST URI: ${params.uri}"
log.trace "Header: ${params.header}"
log.trace "Payload: ${params.body}"
try{
httpPostJson(params) { resp ->
log.trace "response status code: ${resp.status}"
log.trace "response data: ${resp.data}"
}
} catch (e) {
log.error "something went wrong: $e"
}
}
}
def locationEventHandler(evt) {
log.info "Received event for location ${location.name}/${location.id}, Event: ${evt.name}, description: ${evt.descriptionText}, apiServerUrl: ${apiServerUrl("")}"
switch(evt.name){
case "DeviceCreated":
case "DeviceDeleted":
def evtDevice = evt.device
def evtDeviceType = getDeviceType(evtDevice)
def params = [ body: [ eventType:evt.name, deviceId: evtDevice.id, locationId: location.id ] ]
if (evt.name == "DeviceDeleted" && state.deviceSubscriptionMap[deviceId] != null){
state.deviceSubscriptionMap.remove(evtDevice.id)
}
state.locationSubscriptionMap[location.id].each {
params.uri = "${it}"
if(state.verificationKeyMap[it] != null ){
def key = state.verificationKeyMap[it]
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
}
log.trace "POST URI: ${params.uri}"
log.trace "Header: ${params.header}"
log.trace "Payload: ${params.body}"
try{
httpPostJson(params) { resp ->
log.trace "response status code: ${resp.status}"
log.trace "response data: ${resp.data}"
}
} catch (e) {
log.error "something went wrong: $e"
}
}
case "DeviceUpdated":
default:
break
}
}
private ComputHMACValue(key, data){
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1")
Mac mac = Mac.getInstance("HmacSHA1")
mac.init(secretKeySpec)
byte[] digest = mac.doFinal(data.getBytes("UTF-8"))
return byteArrayToString(digest)
} catch (InvalidKeyException e) {
log.error "Invalid key exception while converting to HMac SHA1"
}
}
private def byteArrayToString(byte[] data) {
BigInteger bigInteger = new BigInteger(1, data)
String hash = bigInteger.toString(16)
return hash
}
/*** Device Query/Update Functions ***/
//Endpoints function: return all device data in json format
def getDevices() {
def deviceData = []
inputs?.each {
def deviceType = getDeviceType(it)
if(deviceType == "thermostat") {
deviceData << [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it, deviceType), locationMode: getLocationModeInfo()]
} else {
deviceData << [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it, deviceType)]
}
}
log.debug "getDevices, return: ${deviceData}"
return deviceData
}
//Endpoints function: get device data
def getDevice() {
def it = findDevice(params.id)
def deviceType = getDeviceType(it)
def device
if(deviceType == "thermostat") {
device = [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it,deviceType), locationMode: getLocationModeInfo()]
} else {
device = [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it, deviceType)]
}
log.debug "getDevice, return: ${device}"
return device
}
//Endpoints function: update device data
void updateDevice() {
def device = findDevice(params.id)
request.JSON.each {
def command = it.key
def value = it.value
if (command){
def commandList = mapDeviceCommands(command, value)
command = commandList[0]
value = commandList[1]
if (command == "setAwayMode") {
log.info "Setting away mode to ${value}"
if (location.modes?.find {it.name == value}) {
location.setMode(value)
}
}else if (command == "thermostatSetpoint"){
switch(device.currentThermostatMode){
case "cool":
log.info "Update: ${device.displayName}, [${command}, ${value}]"
device.setCoolingSetpoint(value)
break
case "heat":
case "emergency heat":
log.info "Update: ${device.displayName}, [${command}, ${value}]"
device.setHeatingSetpoint(value)
break
default:
httpError(501, "this mode: ${device.currentThermostatMode} does not allow changing thermostat setpoint.")
break
}
}else if (!device) {
log.error "updateDevice, Device not found"
httpError(404, "Device not found")
} else if (!device.hasCommand(command)) {
log.error "updateDevice, Device does not have the command"
httpError(404, "Device does not have such command")
} else {
if (command == "setColor") {
log.info "Update: ${device.displayName}, [${command}, ${value}]"
device."$command"(hex: value)
} else if(value.isNumber()) {
def intValue = value as Integer
log.info "Update: ${device.displayName}, [${command}, ${intValue}(int)]"
device."$command"(intValue)
} else if (value){
log.info "Update: ${device.displayName}, [${command}, ${value}]"
device."$command"(value)
} else {
log.info "Update: ${device.displayName}, [${command}]"
device."$command"()
}
}
}
}
}
/*** Private Functions ***/
//Return current location mode info
private getLocationModeInfo() {
return [mode: location.mode, supported: location.modes.name]
}
//Map each device to a type given it's capabilities
private getDeviceType(device) {
def deviceType
def capabilities = device.capabilities
log.debug "capabilities: [${device}, ${capabilities}]"
log.debug "supported commands: [${device}, ${device.supportedCommands}]"
//Loop through the device capability list to determine the device type.
capabilities.each {capability ->
switch(capability.name.toLowerCase())
{
case "switch":
deviceType = "switch"
//If the device also contains "Switch Level" capability, identify it as a "light" device.
if (capabilities.any{it.name.toLowerCase() == "switch level"}){
//If the device also contains "Power Meter" capability, identify it as a "dimmerSwitch" device.
if (capabilities.any{it.name.toLowerCase() == "power meter"}){
deviceType = "dimmerSwitch"
return deviceType
} else {
deviceType = "light"
return deviceType
}
}
break
case "garageDoorControl":
deviceType = "garageDoor"
return deviceType
case "lock":
deviceType = "lock"
return deviceType
case "video camera":
deviceType = "camera"
return deviceType
case "thermostat":
deviceType = "thermostat"
return deviceType
case "acceleration sensor":
case "contact sensor":
case "motion sensor":
case "presence sensor":
case "water sensor":
deviceType = "genericSensor"
return deviceType
default:
break
}
}
return deviceType
}
//Return a specific device give the device ID.
private findDevice(deviceId) {
return inputs?.find { it.id == deviceId }
}
//Return a list of device attributes
private deviceAttributeList(device, deviceType) {
def attributeList = [:]
def allAttributes = device.supportedAttributes
allAttributes.each { attribute ->
try {
def currentState = device.currentState(attribute.name)
if(currentState != null ){
switch(attribute.name){
case 'temperature':
attributeList.putAll([ (attribute.name): currentState.value, 'temperatureScale':location.temperatureScale ])
break;
default:
attributeList.putAll([(attribute.name): currentState.value ])
break;
}
if( deviceType == "genericSensor" ){
def key = attribute.name + "_lastUpdated"
attributeList.putAll([ (key): currentState.isoDate ])
}
} else {
attributeList.putAll([ (attribute.name): null ]);
}
} catch(e) {
attributeList.putAll([ (attribute.name): null ]);
}
}
return attributeList
}
//Map device command and value.
//input command and value are from UWP,
//returns resultCommand and resultValue that corresponds with function and value in SmartApps
private mapDeviceCommands(command, value) {
log.debug "mapDeviceCommands: [${command}, ${value}]"
def resultCommand = command
def resultValue = value
switch (command) {
case "switch":
if (value == 1 || value == "1" || value == "on") {
resultCommand = "on"
resultValue = ""
} else if (value == 0 || value == "0" || value == "off") {
resultCommand = "off"
resultValue = ""
}
break
// light attributes
case "level":
resultCommand = "setLevel"
resultValue = value
break
case "hue":
resultCommand = "setHue"
resultValue = value
break
case "saturation":
resultCommand = "setSaturation"
resultValue = value
break
case "colorTemperature":
resultCommand = "setColorTemperature"
resultValue = value
break
case "color":
resultCommand = "setColor"
resultValue = value
// thermostat attributes
case "hvacMode":
resultCommand = "setThermostatMode"
resultValue = value
break
case "fanMode":
resultCommand = "setThermostatFanMode"
resultValue = value
break
case "awayMode":
resultCommand = "setAwayMode"
resultValue = value
break
case "coolingSetpoint":
resultCommand = "setCoolingSetpoint"
resultValue = value
break
case "heatingSetpoint":
resultCommand = "setHeatingSetpoint"
resultValue = value
break
case "thermostatSetpoint":
resultCommand = "thermostatSetpoint"
resultValue = value
break
// lock attributes
case "locked":
if (value == 1 || value == "1" || value == "lock") {
resultCommand = "lock"
resultValue = ""
}
else if (value == 0 || value == "0" || value == "unlock") {
resultCommand = "unlock"
resultValue = ""
}
break
default:
break
}
return [resultCommand,resultValue]
}

View File

@@ -19,9 +19,9 @@
author: "SmartThings",
description: "Control your Bose SoundTouch speakers",
category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconUrl: "https://d3azp77rte0gip.cloudfront.net/smartapps/fcf1d93a-ba0b-4324-b96f-e5b5487dfaf5/images/BoseST_icon.png",
iconX2Url: "https://d3azp77rte0gip.cloudfront.net/smartapps/fcf1d93a-ba0b-4324-b96f-e5b5487dfaf5/images/BoseST_icon@2x.png",
iconX3Url: "https://d3azp77rte0gip.cloudfront.net/smartapps/fcf1d93a-ba0b-4324-b96f-e5b5487dfaf5/images/BoseST_icon@2x-1.png",
singleInstance: true
)
@@ -104,7 +104,7 @@ def deviceDiscovery()
return dynamicPage(name:"deviceDiscovery", title:"Discovery Started!", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) {
section("Please wait while we discover your ${getDeviceName()}. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") {
input "selecteddevice", "enum", required:false, title:"Select ${getDeviceName()} (${numFound} found)", multiple:true, options:devices
input "selecteddevice", "enum", required:false, title:"Select ${getDeviceName()} (${numFound} found)", multiple:true, options:devices, submitOnChange: true
}
}
}
@@ -196,6 +196,8 @@ def addDevice(){
d = addChildDevice(getNameSpace(), getDeviceName(), dni, newDevice?.value.hub, [label:"${deviceName}"])
d.boseSetDeviceID(newDevice.value.deviceID)
log.trace "Created ${d.displayName} with id $dni"
// sync DTH with device, done here as it currently don't work from the DTH's installed() method
d.refresh()
} else {
log.trace "${d.displayName} with id $dni already exists"
}

View File

@@ -289,12 +289,12 @@ def initializeLife360Connection() {
state.life360AccessToken = result.data.access_token
return true;
}
log.debug "Response=${result.data}"
log.info "Life360 initializeLife360Connection, response=${result.data}"
return false;
}
catch (e) {
log.debug e
log.error "Life360 initializeLife360Connection, error: $e"
return false;
}
@@ -656,7 +656,7 @@ def generateInitialEvent (member, childDevice) {
try { // we are going to just ignore any errors
log.debug "Generate Initial Event for New Device for Member = ${member.id}"
log.info "Life360 generateInitialEvent($member, $childDevice)"
def place = state.places.find{it.id==settings.place}
@@ -677,6 +677,8 @@ def generateInitialEvent (member, childDevice) {
// log.debug "Distance Away = ${distanceAway}"
boolean isPresent = (distanceAway <= placeRadius)
log.info "Life360 generateInitialEvent, member: ($memberLatitude, $memberLongitude), place: ($placeLatitude, $placeLongitude), radius: $placeRadius, dist: $distanceAway, present: $isPresent"
// log.debug "External Id=${app.id}:${member.id}"
@@ -718,7 +720,7 @@ def haversine(lat1, lon1, lat2, lon2) {
def placeEventHandler() {
log.debug "In placeEventHandler method."
log.info "Life360 placeEventHandler: params=$params, settings.place=$settings.place"
// the POST to this end-point will look like:
// POST http://test.com/webhook?circleId=XXXX&placeId=XXXX&userId=XXXX&direction=arrive
@@ -729,8 +731,6 @@ def placeEventHandler() {
def direction = params?.direction
def timestamp = params?.timestamp
log.debug "Life360 Event: Circle: ${circleId}, Place: ${placeId}, User: ${userId}, Direction: ${direction}"
if (placeId == settings.place) {
def presenceState = (direction=="in")
@@ -745,10 +745,10 @@ def placeEventHandler() {
if (deviceWrapper) {
deviceWrapper.generatePresenceEvent(presenceState)
log.debug "Event raised on child device: ${externalId}"
log.debug "Life360 event raised on child device: ${externalId}"
}
else {
log.debug "Couldn't find child device associated with inbound Life360 event."
log.warn "Life360 couldn't find child device associated with inbound Life360 event."
}
}