mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 05:11:52 +00:00
Compare commits
147 Commits
MSA-1818-1
...
MSA-1867-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11a8996931 | ||
|
|
100e696d02 | ||
|
|
3e988ce657 | ||
|
|
fe887121d3 | ||
|
|
7375abf9c0 | ||
|
|
00224c2d0b | ||
|
|
a79e0f70fd | ||
|
|
f05daf2f34 | ||
|
|
ed40ca7017 | ||
|
|
7aa8be4322 | ||
|
|
23a0b26cb9 | ||
|
|
00fc9e1ab3 | ||
|
|
3b2d955c55 | ||
|
|
44facec5df | ||
|
|
e3c1442278 | ||
|
|
59bfdc9b06 | ||
|
|
e274f2d3fe | ||
|
|
5e23c8e700 | ||
|
|
10c1d6f715 | ||
|
|
55e92b2ad6 | ||
|
|
18343ae4b8 | ||
|
|
6c4da81cd4 | ||
|
|
09e890091a | ||
|
|
2fb3294ce1 | ||
|
|
973d347d2f | ||
|
|
a2afb4cb1b | ||
|
|
693f2c1060 | ||
|
|
74ae369143 | ||
|
|
afa7784999 | ||
|
|
0e9abb0cd2 | ||
|
|
123de9aae4 | ||
|
|
e91907c30a | ||
|
|
237e226697 | ||
|
|
3b48629546 | ||
|
|
d28414c1e8 | ||
|
|
f79db67153 | ||
|
|
8c7cb54934 | ||
|
|
213d71da09 | ||
|
|
b7288b5beb | ||
|
|
364154e8a7 | ||
|
|
c51e035ff2 | ||
|
|
23cfae20b3 | ||
|
|
5e7ded1f73 | ||
|
|
36b4d48056 | ||
|
|
03b9f08eed | ||
|
|
fbdaeea9ae | ||
|
|
9fa9dee606 | ||
|
|
122a7d4146 | ||
|
|
32eb95f7d7 | ||
|
|
343ec9d856 | ||
|
|
1f09a22176 | ||
|
|
b1b356f370 | ||
|
|
c5455321d1 | ||
|
|
e6ca3d8ddf | ||
|
|
9aaab9b11d | ||
|
|
cbd15ae9cc | ||
|
|
fdd330abdf | ||
|
|
df29effc00 | ||
|
|
668f71a217 | ||
|
|
6fae023f7a | ||
|
|
78a509e8f5 | ||
|
|
42d31f29cc | ||
|
|
9b4f6974de | ||
|
|
ea5344f9c8 | ||
|
|
b5e1d652fd | ||
|
|
a03e8f20c5 | ||
|
|
ce52af0376 | ||
|
|
6908733a5c | ||
|
|
e0d307f6b5 | ||
|
|
0ec8cba885 | ||
|
|
bcfece27c4 | ||
|
|
26f9690190 | ||
|
|
eca8fda0f2 | ||
|
|
ce07c065c8 | ||
|
|
640f9413c3 | ||
|
|
1c52f80eae | ||
|
|
de4d52a28b | ||
|
|
6892965cf1 | ||
|
|
17ec6270d6 | ||
|
|
d233a65ef5 | ||
|
|
b36a985d92 | ||
|
|
8a3c9edf0a | ||
|
|
a560c5b040 | ||
|
|
3ee374028f | ||
|
|
7ac7bd3034 | ||
|
|
f734c5490b | ||
|
|
b93a05d450 | ||
|
|
f5186a41c7 | ||
|
|
c505f95e7a | ||
|
|
173c34150a | ||
|
|
f6b850500e | ||
|
|
4dac7b5379 | ||
|
|
a79d56e467 | ||
|
|
e2d8af48a8 | ||
|
|
0f00c3c7c0 | ||
|
|
0a4d56be04 | ||
|
|
1326881142 | ||
|
|
d30494172f | ||
|
|
2678480244 | ||
|
|
b5e8a46143 | ||
|
|
6237dbd179 | ||
|
|
65f5597ccc | ||
|
|
fced2d7c61 | ||
|
|
74552420ba | ||
|
|
be0c16c76d | ||
|
|
89f1302c99 | ||
|
|
16cb20685c | ||
|
|
30d8e6d835 | ||
|
|
6d0f9d101d | ||
|
|
4ba55e7f0a | ||
|
|
8a41fb6090 | ||
|
|
79c254cfac | ||
|
|
72248b0004 | ||
|
|
d8b35b23ea | ||
|
|
9a53e12427 | ||
|
|
7a7a08ea6e | ||
|
|
950a33dc74 | ||
|
|
2ae163b10b | ||
|
|
e3168793bd | ||
|
|
0baa986c61 | ||
|
|
8484f18a0e | ||
|
|
3c4d86bb31 | ||
|
|
1f69ab6634 | ||
|
|
b0ac006781 | ||
|
|
ef1ebc8347 | ||
|
|
aa1f2f21e8 | ||
|
|
cb6377886d | ||
|
|
7a605373d6 | ||
|
|
90fb9251a6 | ||
|
|
195e0babb2 | ||
|
|
065715f296 | ||
|
|
a544e2f019 | ||
|
|
10acb76b34 | ||
|
|
4fc046f57f | ||
|
|
ff2e70b011 | ||
|
|
79e2789f68 | ||
|
|
94a87e5c7f | ||
|
|
22be8ef2e8 | ||
|
|
2f20a339c3 | ||
|
|
ab79ceb857 | ||
|
|
a583a25ef3 | ||
|
|
2151e2dd3e | ||
|
|
bfd2b6c0fa | ||
|
|
fc312286a2 | ||
|
|
0846b6f34c | ||
|
|
ef8611d2ea | ||
|
|
648dee90b6 |
@@ -9,7 +9,7 @@ apply plugin: 'smartthings-slack'
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.8"
|
||||
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.11"
|
||||
}
|
||||
repositories {
|
||||
mavenLocal()
|
||||
|
||||
@@ -21,28 +21,30 @@ metadata {
|
||||
capability "Tamper Alert"
|
||||
capability "Temperature Measurement"
|
||||
capability "Water Sensor"
|
||||
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x9C, 0x31, 0x86", outClusters: ""
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x9C, 0x31, 0x86", outClusters: ""
|
||||
fingerprint mfr:"010F", prod:"0B01", model:"2002"
|
||||
fingerprint mfr:"010F", prod:"0B01", model:"1002"
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGFS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.water", key:"PRIMARY_CONTROL") {
|
||||
attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#00a0dc")
|
||||
attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#e86d13")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#00a0dc")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#cccccc")
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGFS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.water", key:"PRIMARY_CONTROL") {
|
||||
attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff")
|
||||
attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#00a0dc")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#cccccc")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#00A0DC")
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
backgroundColors:[
|
||||
[value: 31, color: "#153591"],
|
||||
@@ -54,22 +56,22 @@ metadata {
|
||||
[value: 96, color: "#bc2323"]
|
||||
]
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
main "FGFS"
|
||||
details(["FGFS","battery", "temperature"])
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
main "FGFS"
|
||||
details(["FGFS","battery", "temperature"])
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
log.debug "Parsing '${description}'"
|
||||
def result = []
|
||||
|
||||
if (description.startsWith("Err 106")) {
|
||||
|
||||
if (description.startsWith("Err 106")) {
|
||||
if (state.sec) {
|
||||
result = createEvent(descriptionText:description, displayed:false)
|
||||
} else {
|
||||
@@ -84,13 +86,13 @@ def parse(String description) {
|
||||
} else if (description == "updated") {
|
||||
return null
|
||||
} else {
|
||||
def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72:2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
|
||||
def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72:2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
|
||||
|
||||
if (cmd) {
|
||||
log.debug "Parsed '${cmd}'"
|
||||
zwaveEvent(cmd)
|
||||
}
|
||||
}
|
||||
if (cmd) {
|
||||
log.debug "Parsed '${cmd}'"
|
||||
zwaveEvent(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//security
|
||||
@@ -107,7 +109,7 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat
|
||||
//crc16
|
||||
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
|
||||
{
|
||||
def versions = [0x31: 5, 0x72: 2, 0x80: 1]
|
||||
def versions = [0x31: 5, 0x72: 2, 0x80: 1]
|
||||
def version = versions[cmd.commandClass as Integer]
|
||||
def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass)
|
||||
def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data)
|
||||
@@ -121,105 +123,124 @@ def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
|
||||
{
|
||||
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
|
||||
def cmds = []
|
||||
cmds << encap(zwave.batteryV1.batteryGet())
|
||||
cmds << "delay 500"
|
||||
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
|
||||
cmds << "delay 1200"
|
||||
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
[event, response(cmds)]
|
||||
def cmds = []
|
||||
// cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
|
||||
// cmds << "delay 500"
|
||||
cmds << encap(zwave.batteryV1.batteryGet())
|
||||
[event, response(cmds)]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
|
||||
log.debug "manufacturerId: ${cmd.manufacturerId}"
|
||||
log.debug "manufacturerName: ${cmd.manufacturerName}"
|
||||
log.debug "productId: ${cmd.productId}"
|
||||
log.debug "productTypeId: ${cmd.productTypeId}"
|
||||
log.debug "manufacturerName: ${cmd.manufacturerName}"
|
||||
log.debug "productId: ${cmd.productId}"
|
||||
log.debug "productTypeId: ${cmd.productTypeId}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
|
||||
log.debug "deviceIdData: ${cmd.deviceIdData}"
|
||||
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
|
||||
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
|
||||
log.debug "deviceIdType: ${cmd.deviceIdType}"
|
||||
|
||||
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
|
||||
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
|
||||
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
|
||||
log.debug "deviceIdType: ${cmd.deviceIdType}"
|
||||
|
||||
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) { //serial number in binary format
|
||||
String serialNumber = "h'"
|
||||
|
||||
cmd.deviceIdData.each{ data ->
|
||||
serialNumber += "${String.format("%02X", data)}"
|
||||
}
|
||||
|
||||
updateDataValue("serialNumber", serialNumber)
|
||||
log.debug "${device.displayName} - serial number: ${serialNumber}"
|
||||
}
|
||||
|
||||
cmd.deviceIdData.each{ data ->
|
||||
serialNumber += "${String.format("%02X", data)}"
|
||||
}
|
||||
|
||||
updateDataValue("serialNumber", serialNumber)
|
||||
log.debug "${device.displayName} - serial number: ${serialNumber}"
|
||||
}
|
||||
|
||||
def response_cmds = []
|
||||
if (!device.currentState("temperature")) {
|
||||
response_cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
|
||||
}
|
||||
if (!getDataValue("version") && !zwaveInfo.ver) {
|
||||
log.debug "Requesting Version Report"
|
||||
response_cmds << "delay 500"
|
||||
response_cmds << encap(zwave.versionV1.versionGet())
|
||||
}
|
||||
response_cmds << "delay 1000"
|
||||
response_cmds << encap(zwave.wakeUpV2.wakeUpNoMoreInformation())
|
||||
[[:], response(response_cmds)]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
|
||||
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
|
||||
log.debug "applicationVersion: ${cmd.applicationVersion}"
|
||||
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
|
||||
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
|
||||
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
|
||||
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
|
||||
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
|
||||
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
|
||||
log.debug "applicationVersion: ${cmd.applicationVersion}"
|
||||
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
|
||||
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
|
||||
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
|
||||
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def result = []
|
||||
def map = [:]
|
||||
map.name = "battery"
|
||||
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
|
||||
map.unit = "%"
|
||||
map.displayed = true
|
||||
createEvent(map)
|
||||
|
||||
result << createEvent(map)
|
||||
|
||||
if (!getDataValue("serialNumber")) {
|
||||
result << response(encap(zwave.manufacturerSpecificV2.deviceSpecificGet()))
|
||||
} else {
|
||||
result << response(encap(zwave.wakeUpV2.wakeUpNoMoreInformation()))
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.notificationType == 5) {
|
||||
switch (cmd.event) {
|
||||
case 2:
|
||||
map.name = "water"
|
||||
map.value = "wet"
|
||||
map.descriptionText = "${device.displayName} is ${map.value}"
|
||||
break
|
||||
|
||||
case 0:
|
||||
map.name = "water"
|
||||
map.value = "dry"
|
||||
map.descriptionText = "${device.displayName} is ${map.value}"
|
||||
break
|
||||
}
|
||||
} else if (cmd.notificationType == 7) {
|
||||
switch (cmd.event) {
|
||||
case 0:
|
||||
map.name = "tamper"
|
||||
map.value = "inactive"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
|
||||
if (cmd.notificationType == 5) {
|
||||
switch (cmd.event) {
|
||||
case 2:
|
||||
map.name = "water"
|
||||
map.value = "wet"
|
||||
map.descriptionText = "${device.displayName} is ${map.value}"
|
||||
break
|
||||
|
||||
case 3:
|
||||
map.name = "tamper"
|
||||
map.value = "active"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm activated"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
|
||||
case 0:
|
||||
map.name = "water"
|
||||
map.value = "dry"
|
||||
map.descriptionText = "${device.displayName} is ${map.value}"
|
||||
break
|
||||
}
|
||||
} else if (cmd.notificationType == 7) {
|
||||
switch (cmd.event) {
|
||||
case 0:
|
||||
map.name = "tamper"
|
||||
map.value = "inactive"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
|
||||
break
|
||||
|
||||
case 3:
|
||||
map.name = "tamper"
|
||||
map.value = "active"
|
||||
map.descriptionText = "${device.displayName}: tamper alarm activated"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.sensorType == 1) {
|
||||
// temperature
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
|
||||
map.unit = getTemperatureScale()
|
||||
map.name = "temperature"
|
||||
map.displayed = true
|
||||
// temperature
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
|
||||
map.unit = getTemperatureScale()
|
||||
map.name = "temperature"
|
||||
map.displayed = true
|
||||
}
|
||||
|
||||
createEvent(map)
|
||||
|
||||
createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
|
||||
@@ -228,19 +249,18 @@ def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLoca
|
||||
|
||||
def configure() {
|
||||
log.debug "Executing 'configure'"
|
||||
|
||||
def cmds = []
|
||||
|
||||
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGFS' default wake up interval
|
||||
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
|
||||
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
|
||||
cmds += zwave.versionV1.versionGet()
|
||||
cmds += zwave.batteryV1.batteryGet()
|
||||
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)
|
||||
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
|
||||
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
|
||||
// Device wakes up every 4 hours, this interval of 8h 2m allows us to miss one wakeup notification before marking offline
|
||||
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
encapSequence(cmds, 500)
|
||||
// default initial state
|
||||
sendEvent(name: "water", value: "dry")
|
||||
|
||||
def cmds = []
|
||||
|
||||
cmds << zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
|
||||
cmds << zwave.batteryV1.batteryGet() // other queries sent as response to BatteryReport
|
||||
|
||||
encapSequence(cmds, 200)
|
||||
}
|
||||
|
||||
private secure(physicalgraph.zwave.Command cmd) {
|
||||
@@ -249,7 +269,7 @@ private secure(physicalgraph.zwave.Command cmd) {
|
||||
|
||||
private crc16(physicalgraph.zwave.Command cmd) {
|
||||
//zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
|
||||
"5601${cmd.format()}0000"
|
||||
"5601${cmd.format()}0000"
|
||||
}
|
||||
|
||||
private encapSequence(commands, delay=200) {
|
||||
@@ -257,13 +277,10 @@ private encapSequence(commands, delay=200) {
|
||||
}
|
||||
|
||||
private encap(physicalgraph.zwave.Command cmd) {
|
||||
def secureClasses = [0x20, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
|
||||
|
||||
//todo: check if secure inclusion was successful
|
||||
//if not do not send security-encapsulated command
|
||||
if (secureClasses.find{ it == cmd.commandClassId }) {
|
||||
secure(cmd)
|
||||
} else {
|
||||
crc16(cmd)
|
||||
}
|
||||
}
|
||||
if (zwaveInfo.zw && !zwaveInfo.zw.contains("s")) {
|
||||
// Secure inclusion failed
|
||||
crc16(cmd)
|
||||
} else {
|
||||
secure(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Tamper Alert"
|
||||
capability "Temperature Measurement"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x20, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x30, 0x9C, 0x98, 0x7A", outClusters: ""
|
||||
}
|
||||
@@ -240,7 +241,9 @@ def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLoca
|
||||
|
||||
def configure() {
|
||||
log.debug "Executing 'configure'"
|
||||
|
||||
// Device-Watch simply pings if no device events received for 8 hrs & 2 minutes
|
||||
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
def cmds = []
|
||||
|
||||
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds: 7200, nodeid: zwaveHubNodeId)//FGMS' default wake up interval
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
39
devicetypes/keen-home/keen-home-smart-vent.src/README.md
Normal file
39
devicetypes/keen-home/keen-home-smart-vent.src/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Keen Home Smart Vent
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Keen Home Smart Vent](https://www.smartthings.com/works-with-smartthings/keen-home/keen-home-smart-vent)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Switch Level** - represents current light level, usually 0-100 in percent
|
||||
* **Sensor** - detects sensor events
|
||||
* **Temperature Measurement** - represents capability to measure temperature
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Keen Home Smart Vent with reporting interval of 10 mins.
|
||||
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
|
||||
|
||||
* __22min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links
|
||||
for the different models:
|
||||
* [Keen Home Smart Vent Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205302050-Keen-Home-Smart-Vent)
|
||||
@@ -11,6 +11,7 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Temperature Measurement"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
command "getLevel"
|
||||
command "getOnOff"
|
||||
@@ -20,10 +21,7 @@ metadata {
|
||||
command "setZigBeeIdTile"
|
||||
command "clearObstruction"
|
||||
|
||||
fingerprint endpoint: "1",
|
||||
profileId: "0104",
|
||||
inClusters: "0000,0001,0003,0004,0005,0006,0008,0020,0402,0403,0B05,FC01,FC02",
|
||||
outClusters: "0019"
|
||||
fingerprint endpoint: "1", profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0006,0008,0020,0402,0403,0B05,FC01,FC02", outClusters: "0019"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
@@ -40,10 +38,10 @@ metadata {
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", action: "switch.off", icon: "st.vents.vent-open-text", backgroundColor: "#53a7c0"
|
||||
state "on", action: "switch.off", icon: "st.vents.vent-open-text", backgroundColor: "#00a0dc"
|
||||
state "off", action: "switch.on", icon: "st.vents.vent-closed", backgroundColor: "#ffffff"
|
||||
state "obstructed", action: "clearObstruction", icon: "st.vents.vent-closed", backgroundColor: "#ff0000"
|
||||
state "clearing", action: "", icon: "st.vents.vent-closed", backgroundColor: "#ffff33"
|
||||
state "obstructed", action: "clearObstruction", icon: "st.vents.vent-closed", backgroundColor: "#e86d13"
|
||||
state "clearing", action: "", icon: "st.vents.vent-closed", backgroundColor: "#ffffff"
|
||||
}
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "level", action:"switch level.setLevel"
|
||||
@@ -466,15 +464,27 @@ def refresh() {
|
||||
getBattery()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
return refresh()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "CONFIGURE"
|
||||
|
||||
// Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time)
|
||||
// enrolls with default periodic reporting until newer 5 min interval is confirmed
|
||||
sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
// get ZigBee ID by hidden tile because that's the only way we can do it
|
||||
setZigBeeIdTile()
|
||||
|
||||
def configCmds = [
|
||||
// bind reporting clusters to hub
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x0006 {${device.zigbeeId}} {}", "delay 500",
|
||||
//commenting out switch cluster bind as using wrapper onOffConfig of zigbee class
|
||||
//"zdo bind 0x${device.deviceNetworkId} 1 1 0x0006 {${device.zigbeeId}} {}", "delay 500",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x0008 {${device.zigbeeId}} {}", "delay 500",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x0402 {${device.zigbeeId}} {}", "delay 500",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x0403 {${device.zigbeeId}} {}", "delay 500",
|
||||
@@ -510,5 +520,5 @@ def configure() {
|
||||
// "send 0x${device.deviceNetworkId} 1 1", "delay 1500",
|
||||
]
|
||||
|
||||
return configCmds + refresh()
|
||||
return configCmds + zigbee.onOffConfig() + refresh()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/**
|
||||
* Spruce Controller - Pre Release V2 10/11/2015
|
||||
*
|
||||
* Spruce Controller V2_4 Big Tiles *
|
||||
* Copyright 2015 Plaid Systems
|
||||
*
|
||||
* Author: NC
|
||||
@@ -21,82 +20,96 @@
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "Spruce Controller", namespace: "plaidsystems", author: "NCauffman") {
|
||||
capability "Switch"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Actuator"
|
||||
capability "Valve"
|
||||
definition (name: 'Spruce Controller', namespace: 'plaidsystems', author: 'Plaid Systems') {
|
||||
capability 'Switch'
|
||||
capability 'Configuration'
|
||||
capability 'Refresh'
|
||||
capability 'Actuator'
|
||||
capability 'Valve'
|
||||
|
||||
attribute "switch", "string"
|
||||
attribute "switch1", "string"
|
||||
attribute "switch2", "string"
|
||||
attribute "switch8", "string"
|
||||
attribute "switch5", "string"
|
||||
attribute "switch3", "string"
|
||||
attribute "switch4", "string"
|
||||
attribute "switch6", "string"
|
||||
attribute "switch7", "string"
|
||||
attribute "switch9", "string"
|
||||
attribute "switch10", "string"
|
||||
attribute "switch11", "string"
|
||||
attribute "switch12", "string"
|
||||
attribute "switch13", "string"
|
||||
attribute "switch14", "string"
|
||||
attribute "switch15", "string"
|
||||
attribute "switch16", "string"
|
||||
attribute "status", "string"
|
||||
attribute 'switch', 'string'
|
||||
attribute 'switch1', 'string'
|
||||
attribute 'switch2', 'string'
|
||||
attribute 'switch8', 'string'
|
||||
attribute 'switch5', 'string'
|
||||
attribute 'switch3', 'string'
|
||||
attribute 'switch4', 'string'
|
||||
attribute 'switch6', 'string'
|
||||
attribute 'switch7', 'string'
|
||||
attribute 'switch9', 'string'
|
||||
attribute 'switch10', 'string'
|
||||
attribute 'switch11', 'string'
|
||||
attribute 'switch12', 'string'
|
||||
attribute 'switch13', 'string'
|
||||
attribute 'switch14', 'string'
|
||||
attribute 'switch15', 'string'
|
||||
attribute 'switch16', 'string'
|
||||
attribute 'rainsensor', 'string'
|
||||
attribute 'status', 'string'
|
||||
attribute 'tileMessage', 'string'
|
||||
attribute 'minutes', 'string'
|
||||
attribute 'VALUE_UP', 'string'
|
||||
attribute 'VALUE_DOWN', 'string'
|
||||
|
||||
command "programOn"
|
||||
command "programOff"
|
||||
command "on"
|
||||
command "off"
|
||||
command "z1on"
|
||||
command "z1off"
|
||||
command "z2on"
|
||||
command "z2off"
|
||||
command "z3on"
|
||||
command "z3off"
|
||||
command "z4on"
|
||||
command "z4off"
|
||||
command "z5on"
|
||||
command "z5off"
|
||||
command "z6on"
|
||||
command "z6off"
|
||||
command "z7on"
|
||||
command "z7off"
|
||||
command "z8on"
|
||||
command "z8off"
|
||||
command "z9on"
|
||||
command "z9off"
|
||||
command "z10on"
|
||||
command "z10off"
|
||||
command "z11on"
|
||||
command "z11off"
|
||||
command "z12on"
|
||||
command "z12off"
|
||||
command "z13on"
|
||||
command "z13off"
|
||||
command "z14on"
|
||||
command "z14off"
|
||||
command "z15on"
|
||||
command "z15off"
|
||||
command "z16on"
|
||||
command "z16off"
|
||||
command "offtime"
|
||||
command 'levelUp'
|
||||
command 'levelDown'
|
||||
command 'programOn'
|
||||
command 'programOff'
|
||||
command 'programWait'
|
||||
command 'programEnd'
|
||||
|
||||
command "refresh"
|
||||
command "rain"
|
||||
command "manual"
|
||||
command "setDisplay"
|
||||
command 'on'
|
||||
command 'off'
|
||||
command 'zon'
|
||||
command 'zoff'
|
||||
command 'z1on'
|
||||
command 'z1off'
|
||||
command 'z2on'
|
||||
command 'z2off'
|
||||
command 'z3on'
|
||||
command 'z3off'
|
||||
command 'z4on'
|
||||
command 'z4off'
|
||||
command 'z5on'
|
||||
command 'z5off'
|
||||
command 'z6on'
|
||||
command 'z6off'
|
||||
command 'z7on'
|
||||
command 'z7off'
|
||||
command 'z8on'
|
||||
command 'z8off'
|
||||
command 'z9on'
|
||||
command 'z9off'
|
||||
command 'z10on'
|
||||
command 'z10off'
|
||||
command 'z11on'
|
||||
command 'z11off'
|
||||
command 'z12on'
|
||||
command 'z12off'
|
||||
command 'z13on'
|
||||
command 'z13off'
|
||||
command 'z14on'
|
||||
command 'z14off'
|
||||
command 'z15on'
|
||||
command 'z15off'
|
||||
command 'z16on'
|
||||
command 'z16off'
|
||||
|
||||
command "settingsMap"
|
||||
command "writeTime"
|
||||
command "writeType"
|
||||
command "notify"
|
||||
command "updated"
|
||||
|
||||
fingerprint endpointId: "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18", profileId: "0104", deviceId: "0002", deviceVersion: "00", inClusters: "0000,0003,0004,0005,0006,000F", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZ16-01"
|
||||
command 'config'
|
||||
command 'refresh'
|
||||
command 'rain'
|
||||
command 'manual'
|
||||
command 'manualTime'
|
||||
command 'settingsMap'
|
||||
command 'writeTime'
|
||||
command 'writeType'
|
||||
command 'notify'
|
||||
command 'updated'
|
||||
|
||||
//ST release
|
||||
//fingerprint endpointId: '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18', profileId: '0104', deviceId: '0002', deviceVersion: '00', inClusters: '0000,0003,0004,0005,0006,000F', outClusters: '0003, 0019', manufacturer: 'PLAID SYSTEMS', model: 'PS-SPRZ16-01'
|
||||
//new release
|
||||
fingerprint endpointId: "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18", profileId: "0104", deviceId: "0002", deviceVersion: "00", inClusters: "0000,0003,0004,0005,0006,0009,000A,000F", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZ16-01"
|
||||
|
||||
}
|
||||
|
||||
@@ -104,162 +117,230 @@ metadata {
|
||||
simulator {
|
||||
// status messages
|
||||
|
||||
// reply messages
|
||||
|
||||
// reply messages
|
||||
}
|
||||
|
||||
preferences {
|
||||
input description: "Press Configure button after making changes to these preferences", displayDuringSetup: true, type: "paragraph", element: "paragraph", title: ""
|
||||
input "RainEnable", "bool", title: "Rain Sensor Attached?", required: false, displayDuringSetup: true
|
||||
input "ManualTime", "number", title: "Automatic shutoff time when a zone is turned on manually?", required: false, displayDuringSetup: true
|
||||
}
|
||||
input description: 'If you have a rain sensor wired to the rain sensor input on the Spruce controller, turn it on here.', displayDuringSetup: true, type: 'paragraph', element: 'paragraph', title: 'Rain Sensor'
|
||||
input description: 'The SYNC SETTINGS button must be pressed after making a change to the Rain sensor:', displayDuringSetup: false, type: 'paragraph', element: 'paragraph', title: ''
|
||||
input 'RainEnable', 'bool', title: 'Rain Sensor Attached?', required: false, displayDuringSetup: true
|
||||
input description: 'Adjust manual water time with arrows on main tile. The time indicated in the first small tile indicates the time the zone will water when manually switched on.', displayDuringSetup: false, type: 'paragraph', element: 'paragraph', title: ''
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
|
||||
standardTile("status", "device.status") {
|
||||
state "schedule", label: 'Schedule Set', icon: "http://www.plaidsystems.com/smartthings/st_spruce_leaf_225_t.png"
|
||||
state "finished", label: 'Spruce Finished', icon: "st.Outdoor.outdoor5", backgroundColor: "#46c2e8"
|
||||
state "raintoday", label: 'Rain Today', icon: "st.custom.wuk.nt_chancerain"
|
||||
state "rainy", label: 'Previous Rain', icon: "st.custom.wuk.nt_chancerain"
|
||||
state "raintom", label: 'Rain Tomorrow', icon: "st.custom.wuk.nt_chancerain"
|
||||
state "donewweek", label: 'Spruce Finished', icon: "st.Outdoor.outdoor5", backgroundColor: "#00A0DC"
|
||||
state "skipping", label: 'Skip Today', icon: "st.Outdoor.outdoor20", backgroundColor: "#36cfe3"
|
||||
state "moisture", label: '', icon: "st.Weather.weather2", backgroundColor: "#36cfe3"
|
||||
state "pause", label: 'PAUSE', icon: "st.contact.contact.open", backgroundColor: "#e86d13"
|
||||
state "active", label: 'Active', icon: "st.Outdoor.outdoor12", backgroundColor: "#3DC72E"
|
||||
state "season", label: 'Seasonal Adjustment', icon: "st.Outdoor.outdoor17", backgroundColor: "#ffb900"
|
||||
state "disable", label: 'Disabled', icon: "st.secondary.off", backgroundColor: "#cccccc"
|
||||
state "warning", label: '', icon: "st.categories.damageAndDanger", backgroundColor: "#ffff7f"
|
||||
state "alarm", label: 'Alarm', icon: "st.categories.damageAndDanger", backgroundColor: "#f9240c"
|
||||
multiAttributeTile(name:"switchall", type:"generic", width:6, height:4) {
|
||||
tileAttribute('device.status', key: 'PRIMARY_CONTROL') {
|
||||
attributeState 'schedule', label: 'Ready', icon: 'http://www.plaidsystems.com/smartthings/st_spruce_leaf_225_top.png'
|
||||
attributeState 'finished', label: 'Finished', icon: 'st.Outdoor.outdoor5', backgroundColor: '#46c2e8'
|
||||
attributeState 'raintoday', label: 'Rain Today', icon: 'http://www.plaidsystems.com/smartthings/st_rain.png', backgroundColor: '#d65fe3'
|
||||
attributeState 'rainy', label: 'Rain', icon: 'http://www.plaidsystems.com/smartthings/st_rain.png', backgroundColor: '#d65fe3'
|
||||
attributeState 'raintom', label: 'Rain Tomorrow', icon: 'http://www.plaidsystems.com/smartthings/st_rain.png', backgroundColor: '#d65fe3'
|
||||
attributeState 'donewweek', label: 'Finished', icon: 'st.Outdoor.outdoor5', backgroundColor: '#00A0DC'
|
||||
attributeState 'skipping', label: 'Skip', icon: 'st.Outdoor.outdoor20', backgroundColor: '#46c2e8'
|
||||
attributeState 'moisture', label: 'Ready', icon: 'st.Weather.weather2', backgroundColor: '#46c2e8'
|
||||
attributeState 'pause', label: 'PAUSE', icon: 'st.contact.contact.open', backgroundColor: '#e86d13'
|
||||
attributeState 'delayed', label: 'Delayed', icon: 'st.contact.contact.open', backgroundColor: '#e86d13'
|
||||
attributeState 'active', label: 'Active', icon: 'st.Outdoor.outdoor12', backgroundColor: '#3DC72E'
|
||||
attributeState 'season', label: 'Adjust', icon: 'st.Outdoor.outdoor17', backgroundColor: '#ffb900'
|
||||
attributeState 'disable', label: 'Off', icon: 'st.secondary.off', backgroundColor: '#cccccc'
|
||||
attributeState 'warning', label: 'Warning', icon: 'http://www.plaidsystems.com/smartthings/st_spruce_leaf_225_top_yellow.png'
|
||||
attributeState 'alarm', label: 'Alarm', icon: 'http://www.plaidsystems.com/smartthings/st_spruce_leaf_225_s_red.png', backgroundColor: '#e66565'
|
||||
}
|
||||
|
||||
tileAttribute("device.minutes", key: "VALUE_CONTROL") {
|
||||
attributeState "VALUE_UP", action: "levelUp"
|
||||
attributeState "VALUE_DOWN", action: "levelDown"
|
||||
}
|
||||
|
||||
tileAttribute("device.tileMessage", key: "SECONDARY_CONTROL") {
|
||||
attributeState "tileMessage", label: '${currentValue}'
|
||||
}
|
||||
|
||||
}
|
||||
standardTile("switch", "device.switch") {
|
||||
//state "programOff", label: 'Start Program', action: "programOn", icon: "st.sonos.play-icon", backgroundColor: "#a9a9a9"
|
||||
state "off", label: 'Start Program', action: "programOn", icon: "st.sonos.play-icon", backgroundColor: "#a9a9a9"
|
||||
state "programOn", label: 'Initialize Program', action: "programOff", icon: "st.contact.contact.open", backgroundColor: "#f6e10e"
|
||||
state "on", label: 'Program Running', action: "off", icon: "st.Outdoor.outdoor12", backgroundColor: "#3DC72E"
|
||||
}
|
||||
standardTile("rainsensor", "device.rainsensor") {
|
||||
state "rainSensrooff", label: 'Rain Sensor Clear', icon: "st.Weather.weather14", backgroundColor: "#a9a9a9"
|
||||
state "rainSensoron", label: 'Rain Detected', icon: "st.Weather.weather10", backgroundColor: "#f6e10e"
|
||||
}
|
||||
standardTile("switch1", "device.switch1") {
|
||||
state "z1off", label: '1', action: "z1on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z1on", label: '1', action: "z1off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
}
|
||||
standardTile("switch2", "device.switch2") {
|
||||
state "z2off", label: '2', action: "z2on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z2on", label: '2', action: "z2off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
valueTile('minutes', 'device.minutes'){
|
||||
state 'minutes', label: '${currentValue} min'
|
||||
}
|
||||
valueTile('dummy', 'device.minutes'){
|
||||
state 'minutes', label: ''
|
||||
}
|
||||
standardTile('switch', 'device.switch', width:2, height:2) {
|
||||
state 'off', label: 'Start', action: 'programOn', icon: 'st.Outdoor.outdoor12', backgroundColor: '#a9a9a9'
|
||||
state 'programOn', label: 'Wait', action: 'programOff', icon: 'st.contact.contact.open', backgroundColor: '#f6e10e'
|
||||
state 'programWait', label: 'Wait', action: 'programEnd', icon: 'st.contact.contact.open', backgroundColor: '#f6e10e'
|
||||
state 'on', label: 'Running', action: 'programEnd', icon: 'st.Outdoor.outdoor12', backgroundColor: '#3DC72E'
|
||||
}
|
||||
standardTile("switch3", "device.switch3", inactiveLabel: false) {
|
||||
state "z3off", label: '3', action: "z3on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z3on", label: '3', action: "z3off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile("rainsensor", "device.rainsensor", decoration: 'flat') {
|
||||
state "rainSensoroff", label: 'sensor', icon: 'http://www.plaidsystems.com/smartthings/st_drop_on.png'
|
||||
state "rainSensoron", label: 'sensor', icon: 'http://www.plaidsystems.com/smartthings/st_drop_on_blue_small.png'
|
||||
state "disable", label: 'sensor', icon: 'http://www.plaidsystems.com/smartthings/st_drop_x_small.png'
|
||||
state "enable", label: 'sensor', icon: 'http://www.plaidsystems.com/smartthings/st_drop_on.png'
|
||||
}
|
||||
standardTile("switch4", "device.switch4", inactiveLabel: false) {
|
||||
state "z4off", label: '4', action: "z4on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z4on", label: '4', action: "z4off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile('switch1', 'device.switch1', inactiveLabel: false) {
|
||||
state 'z1off', label: '1', action: 'z1on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z1on', label: '1', action: 'z1off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile("switch5", "device.switch5", inactiveLabel: false) {
|
||||
state "z5off", label: '5', action: "z5on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z5on", label: '5', action: "z5off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
}
|
||||
standardTile("switch6", "device.switch6", inactiveLabel: false) {
|
||||
state "z6off", label: '6', action: "z6on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z6on", label: '6', action: "z6off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
}
|
||||
standardTile("switch7", "device.switch7", inactiveLabel: false) {
|
||||
state "z7off", label: '7', action: "z7on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z7on", label: '7', action: "z7off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
}
|
||||
standardTile("switch8", "device.switch8", inactiveLabel: false) {
|
||||
state "z8off", label: '8', action: "z8on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z8on", label: '8', action: "z8off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
}
|
||||
standardTile("switch9", "device.switch9", inactiveLabel: false) {
|
||||
state "z9off", label: '9', action: "z9on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z9on", label: '9', action: "z9off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
}
|
||||
standardTile("switch10", "device.switch10", inactiveLabel: false) {
|
||||
state "z10off", label: '10', action: "z10on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z10on", label: '10', action: "z10off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile('switch2', 'device.switch2', inactiveLabel: false) {
|
||||
state 'z2off', label: '2', action: 'z2on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z2on', label: '2', action: 'z2off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile("switch11", "device.switch11", inactiveLabel: false) {
|
||||
state "z11off", label: '11', action: "z11on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z11on", label: '11', action: "z11off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile('switch3', 'device.switch3', inactiveLabel: false) {
|
||||
state 'z3off', label: '3', action: 'z3on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z3on', label: '3', action: 'z3off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile("switch12", "device.switch12", inactiveLabel: false) {
|
||||
state "z12off", label: '12', action: "z12on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z12on", label: '12', action: "z12off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile('switch4', 'device.switch4', inactiveLabel: false) {
|
||||
state 'z4off', label: '4', action: 'z4on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z4on', label: '4', action: 'z4off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile("switch13", "device.switch13", inactiveLabel: false) {
|
||||
state "z13off", label: '13', action: "z13on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z13on", label: '13', action: "z13off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile('switch5', 'device.switch5', inactiveLabel: false) {
|
||||
state 'z5off', label: '5', action: 'z5on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z5on', label: '5', action: 'z5off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile("switch14", "device.switch14", inactiveLabel: false) {
|
||||
state "z14off", label: '14', action: "z14on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z14on", label: '14', action: "z14off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile('switch6', 'device.switch6', inactiveLabel: false) {
|
||||
state 'z6off', label: '6', action: 'z6on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z6on', label: '6', action: 'z6off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile("switch15", "device.switch15", inactiveLabel: false) {
|
||||
state "z15off", label: '15', action: "z15on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z15on", label: '15', action: "z15off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile('switch7', 'device.switch7', inactiveLabel: false) {
|
||||
state 'z7off', label: '7', action: 'z7on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z7on', label: '7', action: 'z7off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile('switch8', 'device.switch8', inactiveLabel: false) {
|
||||
state 'z8off', label: '8', action: 'z8on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z8on', label: '8', action: 'z8off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile('switch9', 'device.switch9', inactiveLabel: false) {
|
||||
state 'z9off', label: '9', action: 'z9on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z9on', label: '9', action: 'z9off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile('switch10', 'device.switch10', inactiveLabel: false) {
|
||||
state 'z10off', label: '10', action: 'z10on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z10on', label: '10', action: 'z10off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile('switch11', 'device.switch11', inactiveLabel: false) {
|
||||
state 'z11off', label: '11', action: 'z11on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z11on', label: '11', action: 'z11off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile('switch12', 'device.switch12', inactiveLabel: false) {
|
||||
state 'z12off', label: '12', action: 'z12on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z12on', label: '12', action: 'z12off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile('switch13', 'device.switch13', inactiveLabel: false) {
|
||||
state 'z13off', label: '13', action: 'z13on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z13on', label: '13', action: 'z13off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile('switch14', 'device.switch14', inactiveLabel: false) {
|
||||
state 'z14off', label: '14', action: 'z14on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z14on', label: '14', action: 'z14off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile('switch15', 'device.switch15', inactiveLabel: false) {
|
||||
state 'z15off', label: '15', action: 'z15on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z15on', label: '15', action: 'z15off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile("switch16", "device.switch16", inactiveLabel: false) {
|
||||
state "z16off", label: '16', action: "z16on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
state "z16on", label: '16', action: "z16off", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
standardTile('switch16', 'device.switch16', inactiveLabel: false) {
|
||||
state 'z16off', label: '16', action: 'z16on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff'
|
||||
state 'z16on', label: '16', action: 'z16off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC'
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", action: "refresh", icon:"st.secondary.refresh"
|
||||
standardTile('refresh', 'device.switch', inactiveLabel: false, decoration: 'flat') {
|
||||
state 'default', action: 'refresh', icon:'st.secondary.refresh'//-icon'
|
||||
}
|
||||
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
|
||||
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
||||
}
|
||||
standardTile('configure', 'device.configure', inactiveLabel: false, decoration: 'flat') {
|
||||
state 'configure', label:'', action:'configuration.configure', icon:'http://www.plaidsystems.com/smartthings/st_syncsettings.png'//sync_icon_small.png'
|
||||
}
|
||||
|
||||
main (["status"])
|
||||
details(["status","rainsensor","switch","switch1","switch2","switch3","switch4","switch5","switch6","switch7","switch8","switch9","switch10","switch11","switch12","switch13","switch14","switch15","switch16","refresh","configure"])
|
||||
}
|
||||
main (['switchall'])
|
||||
details(['switchall','minutes','rainsensor','switch1','switch2','switch3','switch4','switch','switch5','switch6','switch7','switch8','switch9','switch10','switch11','switch12','refresh','configure','switch13','switch14','switch15','switch16'])
|
||||
}
|
||||
}
|
||||
|
||||
//used for schedule
|
||||
def programOn(){
|
||||
sendEvent(name: "switch", value: "programOn", descriptionText: "Program turned on")
|
||||
sendEvent(name: 'switch', value: 'programOn', descriptionText: 'Program turned on')
|
||||
}
|
||||
|
||||
def programWait(){
|
||||
sendEvent(name: 'switch', value: 'programWait', descriptionText: "Initializing Schedule")
|
||||
}
|
||||
|
||||
def programEnd(){
|
||||
//sets switch to off and tells schedule switch is off/schedule complete with manaual
|
||||
sendEvent(name: 'switch', value: 'off', descriptionText: 'Program manually turned off')
|
||||
zoff()
|
||||
}
|
||||
|
||||
def programOff(){
|
||||
sendEvent(name: "switch", value: "off", descriptionText: "Program turned off")
|
||||
sendEvent(name: 'switch', value: 'off', descriptionText: 'Program turned off')
|
||||
off()
|
||||
}
|
||||
|
||||
def updated(){
|
||||
log.debug "updated"
|
||||
|
||||
//set minutes
|
||||
def levelUp(){
|
||||
def newvalue = 1
|
||||
if (device.latestValue('minutes') != null) newvalue = device.latestValue('minutes').toInteger()+1
|
||||
if (newvalue >= 60) newvalue = 60
|
||||
def value = newvalue.toString()
|
||||
log.debug value
|
||||
sendEvent(name: 'minutes', value: "${value}", descriptionText: "Manual Time set to ${value}", display: false)
|
||||
}
|
||||
|
||||
def levelDown(){
|
||||
def newvalue = device.latestValue('minutes').toInteger()-1
|
||||
if (newvalue <= 0) newvalue = 1
|
||||
def value = newvalue.toString()
|
||||
log.debug value
|
||||
sendEvent(name: 'minutes', value: "${value}", descriptionText: "Manual Time set to ${value}", display: false)
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
//log.debug "Parse description $description"
|
||||
log.debug "Parse description ${description}"
|
||||
def result = null
|
||||
def map = [:]
|
||||
if (description?.startsWith("read attr -")) {
|
||||
if (description?.startsWith('read attr -')) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
//log.debug "Desc Map: $descMap"
|
||||
//using 000F cluster instead of 0006 (switch) because ST does not differentiate between EPs and processes all as switch
|
||||
if (descMap.cluster == "000F" && descMap.attrId == "0055") {
|
||||
log.debug "Zone"
|
||||
if (descMap.cluster == '000F' && descMap.attrId == '0055') {
|
||||
log.debug 'Zone'
|
||||
map = getZone(descMap)
|
||||
}
|
||||
else if (descMap.cluster == "0009" && descMap.attrId == "0000") {
|
||||
log.debug "Alarm"
|
||||
else if (descMap.cluster == '0009' && descMap.attrId == '0000') {
|
||||
log.debug 'Alarm'
|
||||
map = getAlarm(descMap)
|
||||
}
|
||||
}
|
||||
|
||||
if (map) {
|
||||
result = createEvent(map)
|
||||
}
|
||||
log.debug "Parse returned $map $result"
|
||||
else if (description?.startsWith('catchall: 0104 0009')){
|
||||
log.debug 'Sync settings to controller complete'
|
||||
if (device.latestValue('status') != 'alarm'){
|
||||
def configEvt = createEvent(name: 'status', value: 'schedule', descriptionText: "Sync settings to controller complete")
|
||||
def configMsg = createEvent(name: 'tileMessage', value: 'Sync settings to controller complete', descriptionText: "Sync settings to controller complete", displayed: false)
|
||||
result = [configEvt, configMsg]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
if (map) {
|
||||
result = createEvent(map)
|
||||
//configure after reboot
|
||||
if (map.value == 'warning' || map.value == 'alarm'){
|
||||
def cmds = config()
|
||||
def alarmEvt = createEvent(name: 'tileMessage', value: map.descriptionText, descriptionText: "${map.descriptionText}", displayed: false)
|
||||
result = cmds?.collect { new physicalgraph.device.HubAction(it) } + createEvent(map) + alarmEvt
|
||||
return result
|
||||
}
|
||||
else if (map.name == 'rainsensor'){
|
||||
def rainEvt = createEvent(name: 'tileMessage', value: map.descriptionText, descriptionText: "${map.descriptionText}", displayed: false)
|
||||
result = [createEvent(map), rainEvt]
|
||||
return result
|
||||
}
|
||||
}
|
||||
if (map) log.debug "Parse returned ${map} ${result}"
|
||||
return result
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
(description - 'read attr - ').split(',').inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(':')
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
@@ -270,27 +351,28 @@ def getZone(descMap){
|
||||
def EP = Integer.parseInt(descMap.endpoint.trim(), 16)
|
||||
|
||||
String onoff
|
||||
if(descMap.value == "00"){
|
||||
onoff = "off"
|
||||
if(descMap.value == '00'){
|
||||
onoff = 'off'
|
||||
}
|
||||
else onoff = "on"
|
||||
else onoff = 'on'
|
||||
|
||||
if (EP == 1){
|
||||
map.name = "switch"
|
||||
map.name = 'switch'
|
||||
map.value = onoff
|
||||
map.descriptionText = "${device.displayName} turned sprinkler program $onoff"
|
||||
map.descriptionText = "${device.displayName} turned sprinkler program ${onoff}"
|
||||
}
|
||||
|
||||
else if (EP == 18) {
|
||||
map.name = "rainsensor"
|
||||
map.value = "rainSensor" + onoff
|
||||
map.descriptionText = "${device.displayName} rain sensor is $onoff"
|
||||
map.name = 'rainsensor'
|
||||
log.debug "Rain enable: ${RainEnable}, sensor: ${onoff}"
|
||||
map.value = 'rainSensor' + onoff
|
||||
map.descriptionText = "${device.displayName} rain sensor is ${onoff}"
|
||||
}
|
||||
else {
|
||||
EP -= 1
|
||||
map.name = "switch" + EP
|
||||
map.value = "z" + EP + onoff
|
||||
map.descriptionText = "${device.displayName} turned Zone $EP $onoff"
|
||||
map.name = 'switch' + EP
|
||||
map.value = 'z' + EP + onoff
|
||||
map.descriptionText = "${device.displayName} turned Zone $EP ${onoff}"
|
||||
}
|
||||
|
||||
map.isStateChange = true
|
||||
@@ -300,37 +382,59 @@ def getZone(descMap){
|
||||
|
||||
def getAlarm(descMap){
|
||||
def map = [:]
|
||||
map.name = "status"
|
||||
map.name = 'status'
|
||||
def alarmID = Integer.parseInt(descMap.value.trim(), 16)
|
||||
log.debug "${alarmID}"
|
||||
if(alarmID <= 0) map.descriptionText = "${device.displayName} has rebooted, no other alarms"
|
||||
else map.descriptionText = "${device.displayName} rebooted, reported error on zone ${alarmID - 1}, please check zone is working correctly"
|
||||
map.value = "alarm"
|
||||
map.isStateChange = true
|
||||
map.value = 'alarm'
|
||||
map.displayed = true
|
||||
map.isStateChange = true
|
||||
if(alarmID <= 0){
|
||||
map.descriptionText = "${device.displayName} reboot, no other alarms"
|
||||
map.value = 'warning'
|
||||
//map.isStateChange = false
|
||||
}
|
||||
else map.descriptionText = "${device.displayName} reboot, reported zone ${alarmID - 1} error, please check zone is working correctly, press SYNC SETTINGS button to clear"
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
//status notify and change status
|
||||
def notify(value, text){
|
||||
sendEvent(name:"status", value:"$value", descriptionText:"$text", isStateChange: true, display: false)
|
||||
def notify(String val, String txt){
|
||||
sendEvent(name: 'status', value: val, descriptionText: txt, isStateChange: true, display: false)
|
||||
|
||||
//String txtShort = txt.take(100)
|
||||
sendEvent(name: 'tileMessage', value: txt, descriptionText: "", isStateChange: true, display: false)
|
||||
}
|
||||
|
||||
def updated(){
|
||||
log.debug "updated"
|
||||
|
||||
}
|
||||
|
||||
//prefrences - rain sensor, manual time
|
||||
def rain() {
|
||||
log.debug "Rain $RainEnable"
|
||||
log.debug "Rain sensor: ${RainEnable}"
|
||||
if (RainEnable) sendEvent(name: 'rainsensor', value: 'enable', descriptionText: "${device.displayName} rain sensor is enabled", isStateChange: true)
|
||||
else sendEvent(name: 'rainsensor', value: 'disable', descriptionText: "${device.displayName} rain sensor is disabled", isStateChange: true)
|
||||
|
||||
if (RainEnable) "st wattr 0x${device.deviceNetworkId} 18 0x0F 0x51 0x10 {01}"
|
||||
else "st wattr 0x${device.deviceNetworkId} 18 0x0F 0x51 0x10 {00}"
|
||||
}
|
||||
def manual(){
|
||||
log.debug "Time $ManualTime"
|
||||
def mTime = 10
|
||||
if (ManualTime) mTime = ManualTime
|
||||
def manualTime = hex(mTime)
|
||||
"st wattr 0x${device.deviceNetworkId} 1 6 0x4002 0x21 {00${manualTime}}"
|
||||
|
||||
def manualTime(value){
|
||||
sendEvent(name: 'minutes', value: "${value}", descriptionText: "Manual Time set to ${value}", display: false)
|
||||
}
|
||||
|
||||
def manual(){
|
||||
def newManaul = 10
|
||||
if (device.latestValue('minutes')) newManaul = device.latestValue('minutes').toInteger()
|
||||
log.debug "Manual Zone runtime ${newManaul} mins"
|
||||
def manualTime = hex(newManaul)
|
||||
|
||||
}
|
||||
def sendCmds = []
|
||||
sendCmds.push("st wattr 0x${device.deviceNetworkId} 1 6 0x4002 0x21 {00${manualTime}}")
|
||||
return sendCmds
|
||||
}
|
||||
|
||||
//write switch time settings map
|
||||
def settingsMap(WriteTimes, attrType){
|
||||
@@ -366,13 +470,20 @@ def writeTime(wEP, runTime){
|
||||
|
||||
//set reporting and binding
|
||||
def configure() {
|
||||
|
||||
sendEvent(name: 'status', value: 'schedule', descriptionText: "Syncing settings to controller")
|
||||
sendEvent(name: 'minutes', value: "10", descriptionText: "Manual Time set to 10 mins", display: false)
|
||||
sendEvent(name: 'tileMessage', value: 'Syncing settings to controller', descriptionText: 'Syncing settings to controller')
|
||||
config()
|
||||
}
|
||||
|
||||
def config(){
|
||||
|
||||
String zigbeeId = swapEndianHex(device.hub.zigbeeId)
|
||||
log.debug "Confuguring Reporting and Bindings ${device.deviceNetworkId} ${device.zigbeeId}"
|
||||
sendEvent(name: 'configuration',value: 100, descriptionText: "Configuration initialized")
|
||||
log.debug "Configuring Reporting and Bindings ${device.deviceNetworkId} ${device.zigbeeId}"
|
||||
|
||||
def configCmds = [
|
||||
//program on/off
|
||||
//program on/off
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x09 {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x0F {${device.zigbeeId}} {}", "delay 1000",
|
||||
@@ -458,38 +569,16 @@ def configure() {
|
||||
"zcl global send-me-a-report 0x09 0x00 0x21 1 0 {00}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
|
||||
]
|
||||
return configCmds + rain() + manual()
|
||||
return configCmds + rain()
|
||||
}
|
||||
|
||||
|
||||
|
||||
private hex(value) {
|
||||
new BigInteger(Math.round(value).toString()).toString(16)
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
int i = 0;
|
||||
int j = array.length - 1;
|
||||
byte tmp;
|
||||
while (j > i) {
|
||||
tmp = array[j];
|
||||
array[j] = array[i];
|
||||
array[i] = tmp;
|
||||
j--;
|
||||
i++;
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
|
||||
log.debug "refresh"
|
||||
def refreshCmds = [
|
||||
|
||||
log.debug "refresh pressed"
|
||||
sendEvent(name: 'tileMessage', value: 'Refresh', descriptionText: 'Refresh')
|
||||
|
||||
def refreshCmds = [
|
||||
|
||||
"st rattr 0x${device.deviceNetworkId} 1 0x0F 0x55", "delay 500",
|
||||
|
||||
"st rattr 0x${device.deviceNetworkId} 2 0x0F 0x55", "delay 500",
|
||||
@@ -513,64 +602,96 @@ def refresh() {
|
||||
"st rattr 0x${device.deviceNetworkId} 18 0x0F 0x51","delay 500",
|
||||
|
||||
]
|
||||
return refreshCmds + rain() + manual()
|
||||
|
||||
return refreshCmds
|
||||
}
|
||||
|
||||
private hex(value) {
|
||||
new BigInteger(Math.round(value).toString()).toString(16)
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
int i = 0;
|
||||
int j = array.length - 1;
|
||||
byte tmp;
|
||||
while (j > i) {
|
||||
tmp = array[j];
|
||||
array[j] = array[i];
|
||||
array[i] = tmp;
|
||||
j--;
|
||||
i++;
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
//on & off redefined for Alexa to start manual schedule
|
||||
def on() {
|
||||
log.debug 'Alexa on'
|
||||
//schedule subscribes to programOn
|
||||
sendEvent(name: 'switch', value: 'programOn', descriptionText: 'Alexa turned program on')
|
||||
}
|
||||
def off() {
|
||||
log.debug 'Alexa off'
|
||||
sendEvent(name: 'switch', value: 'off', descriptionText: 'Alexa turned program off')
|
||||
zoff()
|
||||
}
|
||||
|
||||
// Commands to device
|
||||
//zones on - 8
|
||||
def on() {
|
||||
//sendEvent(name:"status", value:"active", descriptionText:"Program Running", isStateChange: true, display: false)
|
||||
log.debug "on"
|
||||
"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
|
||||
def zon() {
|
||||
"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
|
||||
}
|
||||
def off() {
|
||||
log.debug "off"
|
||||
"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
|
||||
def zoff() {
|
||||
"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
|
||||
}
|
||||
def z1on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 2 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 2 6 1 {}"
|
||||
}
|
||||
def z1off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 2 6 0 {}"
|
||||
}
|
||||
def z2on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 3 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 3 6 1 {}"
|
||||
}
|
||||
def z2off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 3 6 0 {}"
|
||||
}
|
||||
def z3on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 4 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 4 6 1 {}"
|
||||
}
|
||||
def z3off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 4 6 0 {}"
|
||||
}
|
||||
def z4on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 5 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 5 6 1 {}"
|
||||
}
|
||||
def z4off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 5 6 0 {}"
|
||||
}
|
||||
def z5on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 6 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 6 6 1 {}"
|
||||
}
|
||||
def z5off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 6 6 0 {}"
|
||||
}
|
||||
def z6on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 7 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 7 6 1 {}"
|
||||
}
|
||||
def z6off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 7 6 0 {}"
|
||||
}
|
||||
def z7on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 8 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 8 6 1 {}"
|
||||
}
|
||||
def z7off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 8 6 0 {}"
|
||||
}
|
||||
def z8on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 9 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 9 6 1 {}"
|
||||
}
|
||||
def z8off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 9 6 0 {}"
|
||||
@@ -578,50 +699,51 @@ def z8off() {
|
||||
|
||||
//zones 9 - 16
|
||||
def z9on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 10 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 10 6 1 {}"
|
||||
}
|
||||
def z9off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 10 6 0 {}"
|
||||
}
|
||||
def z10on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 11 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 11 6 1 {}"
|
||||
}
|
||||
def z10off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 11 6 0 {}"
|
||||
}
|
||||
def z11on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 12 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 12 6 1 {}"
|
||||
}
|
||||
def z11off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 12 6 0 {}"
|
||||
}
|
||||
def z12on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 13 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 13 6 1 {}"
|
||||
}
|
||||
def z12off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 13 6 0 {}"
|
||||
}
|
||||
def z13on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 14 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 14 6 1 {}"
|
||||
}
|
||||
def z13off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 14 6 0 {}"
|
||||
}
|
||||
def z14on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 15 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 15 6 1 {}"
|
||||
}
|
||||
def z14off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 15 6 0 {}"
|
||||
}
|
||||
def z15on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 16 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 16 6 1 {}"
|
||||
}
|
||||
def z15off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 16 6 0 {}"
|
||||
}
|
||||
def z16on() {
|
||||
"st cmd 0x${device.deviceNetworkId} 17 6 1 {}"
|
||||
return manual() + "st cmd 0x${device.deviceNetworkId} 17 6 1 {}"
|
||||
}
|
||||
def z16off() {
|
||||
"st cmd 0x${device.deviceNetworkId} 17 6 0 {}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ metadata
|
||||
{
|
||||
standardTile("mainTile", "device.status", width: 1, height: 1, icon: "st.Entertainment.entertainment11")
|
||||
{
|
||||
state "default", label: "Simple Sync", icon: "st.Home.home2", backgroundColor: "#55A7FF"
|
||||
state "default", label: "Simple Sync", icon: "st.Home.home2", backgroundColor: "#00a0dc"
|
||||
}
|
||||
|
||||
def detailTiles = ["mainTile"]
|
||||
|
||||
@@ -82,8 +82,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"motion", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
|
||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC"
|
||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc"
|
||||
}
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
43
devicetypes/smartthings/aeon-multisensor-gen5.src/README.md
Normal file
43
devicetypes/smartthings/aeon-multisensor-gen5.src/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Aeon Multisensor Gen5
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Aeon Labs MultiSensor (Gen 5)](https://www.smartthings.com/works-with-smartthings/sensors/aeon-labs-multisensor-gen-5)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Motion Sensor** - can detect motion
|
||||
* **Temperature Measurement** - defines device measures current temperature
|
||||
* **Relative Humidity Measurement** - allow reading the relative humidity from devices that support it
|
||||
* **Illuminance Measurement** - gives the illuminance reading from devices that support it
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
|
||||
## Device Health
|
||||
|
||||
Aeon Labs MultiSensor (Gen 5) is polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Aeon Labs MultiSensor (Gen 5) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/206157226-Aeon-Labs-MultiSensor-Gen-5-)
|
||||
@@ -20,10 +20,12 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
command "configureAfterSecure"
|
||||
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x98,0x7A", outClusters:"0x5A"
|
||||
fingerprint mfr:"0086", prod:"0102", model:"004A", deviceJoinName: "Aeon Labs MultiSensor (Gen 5)"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -64,8 +66,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"motion", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
|
||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00a0dc"
|
||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc"
|
||||
}
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
@@ -98,6 +100,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description)
|
||||
{
|
||||
def result = null
|
||||
@@ -244,6 +251,13 @@ def configureAfterSecure() {
|
||||
secureSequence(request) + ["delay 20000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()]
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
secure(zwave.batteryV1.batteryGet())
|
||||
}
|
||||
|
||||
def configure() {
|
||||
// log.debug "configure()"
|
||||
//["delay 30000"] + secure(zwave.securityV1.securityCommandsSupportedGet())
|
||||
|
||||
@@ -59,8 +59,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"motion", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
|
||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00a0dc"
|
||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc"
|
||||
}
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
|
||||
@@ -53,7 +53,7 @@ metadata {
|
||||
// tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
valueTile("power", "device.power", decoration: "flat") {
|
||||
|
||||
2
devicetypes/smartthings/aeon-siren.src/.st-ignore
Normal file
2
devicetypes/smartthings/aeon-siren.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
37
devicetypes/smartthings/aeon-siren.src/README.md
Normal file
37
devicetypes/smartthings/aeon-siren.src/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Aeon Siren
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Aeon Labs Siren (Gen 5)](https://www.smartthings.com/works-with-smartthings/aeon-labs/aeon-labs-siren-gen-5)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Alarm** - allows for interacting with devices that serve as alarms
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Aeon Labs Siren (Gen 5) is polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Aeon Labs Siren (Gen 5) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204555240-Aeon-Labs-Siren-Gen-5-)
|
||||
@@ -20,10 +20,11 @@ metadata {
|
||||
capability "Actuator"
|
||||
capability "Alarm"
|
||||
capability "Switch"
|
||||
capability "Health Check"
|
||||
|
||||
command "test"
|
||||
|
||||
fingerprint deviceId: "0x1005", inClusters: "0x5E,0x98"
|
||||
fingerprint deviceId: "0x1005", inClusters: "0x5E,0x98", deviceJoinName: "Aeon Labs Siren (Gen 5)"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -58,6 +59,9 @@ metadata {
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
if(!state.sound) state.sound = 1
|
||||
if(!state.volume) state.volume = 3
|
||||
|
||||
@@ -148,3 +152,10 @@ def test() {
|
||||
private secure(physicalgraph.zwave.Command cmd) {
|
||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
secure(zwave.basicV1.basicGet())
|
||||
}
|
||||
@@ -42,8 +42,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
|
||||
state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0"
|
||||
state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ebeef2"
|
||||
state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#00a0dc"
|
||||
state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("beep", "device.beep", decoration: "flat") {
|
||||
state "beep", label:'', action:"tone.beep", icon:"st.secondary.beep", backgroundColor:"#ffffff"
|
||||
|
||||
@@ -28,6 +28,8 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Music Player"
|
||||
capability "Health Check"
|
||||
capability "Sensor"
|
||||
capability "Actuator"
|
||||
|
||||
/**
|
||||
* Define all commands, ie, if you have a custom action not
|
||||
@@ -67,10 +69,10 @@ metadata {
|
||||
}
|
||||
|
||||
standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "forceOff", icon: "st.Electronics.electronics16", backgroundColor: "#79b821", nextState:"turningOff"
|
||||
state "on", label: '${name}', action: "forceOff", icon: "st.Electronics.electronics16", backgroundColor: "#00a0dc", nextState:"turningOff"
|
||||
state "turningOff", label:'TURNING OFF', icon:"st.Electronics.electronics16", backgroundColor:"#ffffff"
|
||||
state "off", label: '${name}', action: "forceOn", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'TURNING ON', icon:"st.Electronics.electronics16", backgroundColor:"#79b821"
|
||||
state "turningOn", label:'TURNING ON', icon:"st.Electronics.electronics16", backgroundColor:"#00a0dc"
|
||||
}
|
||||
valueTile("1", "device.station1", decoration: "flat", canChangeIcon: false) {
|
||||
state "station1", label:'${currentValue}', action:"preset1"
|
||||
|
||||
@@ -81,13 +81,13 @@ metadata {
|
||||
state "fanCirculate", label:'${name}', action:"switchFanMode"
|
||||
}
|
||||
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "setHeatingSetpoint", action:"quickSetHeat", backgroundColor:"#d04e00"
|
||||
state "setHeatingSetpoint", action:"quickSetHeat", backgroundColor:"#e86d13"
|
||||
}
|
||||
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "heat", label:'${currentValue}° heat', backgroundColor:"#ffffff"
|
||||
}
|
||||
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "setCoolingSetpoint", action:"quickSetCool", backgroundColor: "#1e9cbb"
|
||||
state "setCoolingSetpoint", action:"quickSetCool", backgroundColor: "#00a0dc"
|
||||
}
|
||||
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "cool", label:'${currentValue}° cool', backgroundColor:"#ffffff"
|
||||
|
||||
@@ -32,7 +32,7 @@ metadata {
|
||||
command "switchMode"
|
||||
command "switchFanMode"
|
||||
|
||||
attribute "thermostatSetpoint", "number"
|
||||
attribute "displayThermostatSetpoint", "string" // Added to be able to show "Auto"/"Off" keeping attribute thermostatSetpoint a number
|
||||
attribute "thermostatStatus", "string"
|
||||
attribute "maxHeatingSetpoint", "number"
|
||||
attribute "minHeatingSetpoint", "number"
|
||||
@@ -70,7 +70,7 @@ metadata {
|
||||
state "heat", action:"switchMode", nextState: "updating", icon: "st.thermostat.heat"
|
||||
state "cool", action:"switchMode", nextState: "updating", icon: "st.thermostat.cool"
|
||||
state "auto", action:"switchMode", nextState: "updating", icon: "st.thermostat.auto"
|
||||
state "auxHeatOnly", action:"switchMode", icon: "st.thermostat.emergency-heat"
|
||||
state "emergency heat", label:"auxHeatOnly", action:"switchMode", icon: "st.thermostat.emergency-heat" // emergency heat = auxHeatOnly
|
||||
state "updating", label:"Working", icon: "st.secondary.secondary"
|
||||
}
|
||||
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
|
||||
@@ -81,8 +81,8 @@ metadata {
|
||||
standardTile("upButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "setpoint", action:"raiseSetpoint", icon:"st.thermostat.thermostat-up"
|
||||
}
|
||||
valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") {
|
||||
state "thermostatSetpoint", label:'${currentValue}'
|
||||
valueTile("displayThermostatSetpoint", "device.displayThermostatSetpoint", width: 1, height: 1, decoration: "flat") {
|
||||
state "displayThermostatSetpoint", label:'${currentValue}'
|
||||
}
|
||||
valueTile("currentStatus", "device.thermostatStatus", height: 1, width: 2, decoration: "flat") {
|
||||
state "thermostatStatus", label:'${currentValue}', backgroundColor:"#ffffff"
|
||||
@@ -113,7 +113,7 @@ metadata {
|
||||
state "humidity", label:'${currentValue}%'
|
||||
}
|
||||
main "temperature"
|
||||
details(["temperature", "upButtonControl", "thermostatSetpoint", "currentStatus", "downButtonControl", "mode", "fanMode","humidity", "resumeProgram", "refresh"])
|
||||
details(["temperature", "upButtonControl", "displayThermostatSetpoint", "currentStatus", "downButtonControl", "mode", "fanMode","humidity", "resumeProgram", "refresh"])
|
||||
}
|
||||
|
||||
preferences {
|
||||
@@ -452,10 +452,10 @@ def emergencyHeat() {
|
||||
}
|
||||
|
||||
def auxHeatOnly() {
|
||||
log.debug "auxHeatOnly"
|
||||
log.debug "auxHeatOnly = emergency heat"
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
if (parent.setMode ("auxHeatOnly", deviceId))
|
||||
generateModeEvent("auxHeatOnly")
|
||||
generateModeEvent("emergency heat") // emergency heat = auxHeatOnly
|
||||
else {
|
||||
log.debug "Error setting new mode."
|
||||
def currentMode = device.currentState("thermostatMode")?.value
|
||||
@@ -574,17 +574,23 @@ def generateSetpointEvent() {
|
||||
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
def averageSetpoint = roundC((heatingSetpoint + coolingSetpoint) / 2)
|
||||
if (mode == "heat") {
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale, displayed: false)
|
||||
}
|
||||
else if (mode == "cool") {
|
||||
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale, displayed: false)
|
||||
} else if (mode == "auto") {
|
||||
sendEvent("name":"thermostatSetpoint", "value":"Auto")
|
||||
sendEvent("name":"thermostatSetpoint", "value":averageSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":"Auto", displayed: false)
|
||||
} else if (mode == "off") {
|
||||
sendEvent("name":"thermostatSetpoint", "value":"Off")
|
||||
} else if (mode == "auxHeatOnly") {
|
||||
sendEvent("name":"thermostatSetpoint", "value":averageSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":"Off", displayed: false)
|
||||
} else if (mode == "emergency heat") { // emergency heat = auxHeatOnly
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale, displayed: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,13 +627,14 @@ void raiseSetpoint() {
|
||||
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0
|
||||
targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5
|
||||
|
||||
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue > maxHeatingSetpoint) {
|
||||
if ((mode == "heat" || mode == "emergency heat") && targetvalue > maxHeatingSetpoint) { // emergency heat = auxHeatOnly
|
||||
targetvalue = maxHeatingSetpoint
|
||||
} else if (mode == "cool" && targetvalue > maxCoolingSetpoint) {
|
||||
targetvalue = maxCoolingSetpoint
|
||||
}
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false)
|
||||
log.info "In mode $mode raiseSetpoint() to $targetvalue"
|
||||
|
||||
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
|
||||
@@ -666,13 +673,14 @@ void lowerSetpoint() {
|
||||
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0
|
||||
targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5
|
||||
|
||||
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue < minHeatingSetpoint) {
|
||||
if ((mode == "heat" || mode == "emergency heat") && targetvalue < minHeatingSetpoint) { // emergency heat = auxHeatOnly
|
||||
targetvalue = minHeatingSetpoint
|
||||
} else if (mode == "cool" && targetvalue < minCoolingSetpoint) {
|
||||
targetvalue = minCoolingSetpoint
|
||||
}
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false)
|
||||
sendEvent("name":"displayThermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false)
|
||||
log.info "In mode $mode lowerSetpoint() to $targetvalue"
|
||||
|
||||
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
|
||||
@@ -706,7 +714,7 @@ void alterSetpoint(temp) {
|
||||
}
|
||||
|
||||
//step1: check thermostatMode, enforce limits before sending request to cloud
|
||||
if (mode == "heat" || mode == "auxHeatOnly"){
|
||||
if (mode == "heat" || mode == "emergency heat"){ // emergency heat = auxHeatOnly
|
||||
if (temp.value > coolingSetpoint){
|
||||
targetHeatingSetpoint = temp.value
|
||||
targetCoolingSetpoint = temp.value
|
||||
@@ -735,15 +743,18 @@ void alterSetpoint(temp) {
|
||||
|
||||
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) {
|
||||
sendEvent("name": "thermostatSetpoint", "value": temp.value, displayed: false)
|
||||
sendEvent("name": "displayThermostatSetpoint", "value": temp.value, displayed: false)
|
||||
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint, "unit": location.temperatureScale)
|
||||
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint, "unit": location.temperatureScale)
|
||||
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
|
||||
} else {
|
||||
log.error "Error alterSetpoint()"
|
||||
if (mode == "heat" || mode == "auxHeatOnly"){
|
||||
if (mode == "heat" || mode == "emergency heat"){ // emergency heat = auxHeatOnly
|
||||
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
||||
sendEvent("name": "displayThermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
||||
} else if (mode == "cool") {
|
||||
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
|
||||
sendEvent("name": "displayThermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,6 +770,7 @@ def generateStatusEvent() {
|
||||
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||
def temperature = device.currentValue("temperature")
|
||||
def statusText
|
||||
def operatingState = "idle"
|
||||
|
||||
log.debug "Generate Status Event for Mode = ${mode}"
|
||||
log.debug "Temperature = ${temperature}"
|
||||
@@ -767,20 +779,29 @@ def generateStatusEvent() {
|
||||
log.debug "HVAC Mode = ${mode}"
|
||||
|
||||
if (mode == "heat") {
|
||||
if (temperature >= heatingSetpoint)
|
||||
if (temperature >= heatingSetpoint) {
|
||||
statusText = "Right Now: Idle"
|
||||
else
|
||||
} else {
|
||||
statusText = "Heating to ${heatingSetpoint} ${location.temperatureScale}"
|
||||
operatingState = "heating"
|
||||
}
|
||||
} else if (mode == "cool") {
|
||||
if (temperature <= coolingSetpoint)
|
||||
if (temperature <= coolingSetpoint) {
|
||||
statusText = "Right Now: Idle"
|
||||
else
|
||||
} else {
|
||||
statusText = "Cooling to ${coolingSetpoint} ${location.temperatureScale}"
|
||||
operatingState = "cooling"
|
||||
}
|
||||
} else if (mode == "auto") {
|
||||
statusText = "Right Now: Auto"
|
||||
if (temperature < heatingSetpoint) {
|
||||
operatingState = "heating"
|
||||
} else if (temperature > coolingSetpoint) {
|
||||
operatingState = "cooling"
|
||||
}
|
||||
} else if (mode == "off") {
|
||||
statusText = "Right Now: Off"
|
||||
} else if (mode == "auxHeatOnly") {
|
||||
} else if (mode == "emergency heat") { // emergency heat = auxHeatOnly
|
||||
statusText = "Emergency Heat"
|
||||
} else {
|
||||
statusText = "?"
|
||||
@@ -788,6 +809,7 @@ def generateStatusEvent() {
|
||||
|
||||
log.debug "Generate Status Event = ${statusText}"
|
||||
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true)
|
||||
sendEvent("name":"thermostatOperatingState", "value":operatingState, "description":operatingState, displayed: false)
|
||||
}
|
||||
|
||||
def generateActivityFeedsEvent(notificationMessage) {
|
||||
|
||||
2
devicetypes/smartthings/econet-vent.src/.st-ignore
Normal file
2
devicetypes/smartthings/econet-vent.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
43
devicetypes/smartthings/econet-vent.src/README.md
Normal file
43
devicetypes/smartthings/econet-vent.src/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# EcoNet Vent
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [EcoNet Controls Z-Wave Vent](https://www.smartthings.com/works-with-smartthings/econet-controls/econet-controls-z-wave-vent)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Switch Level** - allows for the control of the level attribute of a light
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Switch** - allows for the control of a switch device
|
||||
* **Battery** - defines that the device has a battery
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
* **Polling** - allows for the polling of devices that support it
|
||||
* **Configuration** - allow configuration of devices that support it
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
EcoNet Controls Z-Wave Vent is polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [EcoNet Controls Z-Wave Vent Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204556420-EcoNet-EV100-Vent)
|
||||
@@ -26,11 +26,13 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Polling"
|
||||
capability "Configuration"
|
||||
capability "Health Check"
|
||||
|
||||
command "open"
|
||||
command "close"
|
||||
|
||||
fingerprint deviceId: "0x1100", inClusters: "0x26,0x72,0x86,0x77,0x80,0x20"
|
||||
fingerprint mfr:"0157", prod:"0100", model:"0100", deviceJoinName: "EcoNet Controls Z-Wave Vent"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -53,7 +55,7 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", action:"switch.off", icon:"st.vents.vent-open-text", backgroundColor:"#53a7c0"
|
||||
state "on", action:"switch.off", icon:"st.vents.vent-open-text", backgroundColor:"#00a0dc"
|
||||
state "off", action:"switch.on", icon:"st.vents.vent-closed", backgroundColor:"#ffffff"
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
@@ -85,6 +87,8 @@ def parse(String description) {
|
||||
|
||||
//send the command to stop polling
|
||||
def updated() {
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
response("poll stop")
|
||||
}
|
||||
|
||||
@@ -169,6 +173,13 @@ def setLevel(value, duration) {
|
||||
setLevel(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
delayBetween([
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format(),
|
||||
|
||||
@@ -56,9 +56,9 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00a0dc", nextState:"turningOff"
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00a0dc", nextState:"turningOff"
|
||||
state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,40 @@
|
||||
# Fibaro Door Window Sensor
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Fibaro Door/Window Sensor](https://www.smartthings.com/works-with-smartthings/sensors/fibaro-doorwindow-sensor)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery-specification)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Contact Sensor** - can detect contact (possible values: open,closed)
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Fibaro Door/Window Sensor is a Z-wave sleepy device and wakes up every 4 hours.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins.
|
||||
|
||||
* __482min__ checkInterval
|
||||
|
||||
## Battery Specification
|
||||
|
||||
One 1/2AA 3.6V battery is required.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Fibaro Door/Window Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204075194-Fibaro-Door-Window-Sensor)
|
||||
@@ -39,7 +39,8 @@
|
||||
capability "Contact Sensor"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Configuration"
|
||||
capability "Configuration"
|
||||
capability "Health Check"
|
||||
|
||||
command "resetParams2StDefaults"
|
||||
command "listCurrentParams"
|
||||
@@ -266,6 +267,9 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
|
||||
*/
|
||||
def configure() {
|
||||
log.debug "Configuring Device..."
|
||||
// Device wakes up every 4 hours, this interval allows us to miss one wakeup notification before marking offline
|
||||
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
def cmds = []
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 1, size: 2).format()
|
||||
// send associate to group 3 to get sensor data reported only to hub
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* Device Type Definition File
|
||||
*
|
||||
* Device Type: Fibaro Flood Sensor
|
||||
* File Name: fibaro-flood-sensor.groovy
|
||||
* Initial Release: 2014-12-10
|
||||
* @author: Todd Wackford
|
||||
* Email: todd@wackford.net
|
||||
* @version: 1.0
|
||||
* Device Type: Fibaro Flood Sensor
|
||||
* File Name: fibaro-flood-sensor.groovy
|
||||
* Initial Release: 2014-12-10
|
||||
* @author: Todd Wackford
|
||||
* Email: todd@wackford.net
|
||||
* @version: 1.0
|
||||
*
|
||||
* Copyright 2014 SmartThings
|
||||
*
|
||||
@@ -26,8 +26,8 @@
|
||||
* not displayed to the user. We do this so we can receive events and display on device
|
||||
* activity. If the user wants to display the tamper tile, adjust the tile display lines
|
||||
* with the following:
|
||||
* main(["water", "temperature", "tamper"])
|
||||
* details(["water", "temperature", "battery", "tamper"])
|
||||
* main(["water", "temperature", "tamper"])
|
||||
* details(["water", "temperature", "battery", "tamper"])
|
||||
*
|
||||
* @param none
|
||||
*
|
||||
@@ -39,13 +39,18 @@ metadata {
|
||||
capability "Temperature Measurement"
|
||||
capability "Configuration"
|
||||
capability "Battery"
|
||||
|
||||
command "resetParams2StDefaults"
|
||||
command "listCurrentParams"
|
||||
command "updateZwaveParam"
|
||||
command "test"
|
||||
|
||||
capability "Health Check"
|
||||
capability "Sensor"
|
||||
|
||||
command "resetParams2StDefaults"
|
||||
command "listCurrentParams"
|
||||
command "updateZwaveParam"
|
||||
command "test"
|
||||
|
||||
fingerprint deviceId: "0xA102", inClusters: "0x30,0x9C,0x60,0x85,0x8E,0x72,0x70,0x86,0x80,0x84"
|
||||
fingerprint mfr:"010F", prod:"0000", model:"2002"
|
||||
fingerprint mfr:"010F", prod:"0000", model:"1002"
|
||||
fingerprint mfr:"010F", prod:"0B00", model:"1001"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -72,7 +77,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("water", "device.water", width: 2, height: 2) {
|
||||
state "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
|
||||
state "wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
|
||||
state "wet", icon:"st.alarm.water.wet", backgroundColor:"#00a0dc"
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
@@ -86,9 +91,9 @@ metadata {
|
||||
[value: 96, color: "#bc2323"]
|
||||
]
|
||||
}
|
||||
standardTile("tamper", "device.tamper") {
|
||||
state("secure", label:"secure", icon:"st.locks.lock.locked", backgroundColor:"#ffffff")
|
||||
state("tampered", label:"tampered", icon:"st.locks.lock.unlocked", backgroundColor:"#53a7c0")
|
||||
standardTile("tamper", "device.tamper") {
|
||||
state("secure", label:"secure", icon:"st.locks.lock.locked", backgroundColor:"#ffffff")
|
||||
state("tampered", label:"tampered", icon:"st.locks.lock.unlocked", backgroundColor:"#00a0dc")
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
@@ -106,26 +111,17 @@ metadata {
|
||||
def parse(String description)
|
||||
{
|
||||
def result = []
|
||||
|
||||
if (description == "updated") {
|
||||
if (!state.MSR) {
|
||||
result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds: 60*60, nodeid:zwaveHubNodeId))
|
||||
result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
|
||||
}
|
||||
} else {
|
||||
def cmd = zwave.parse(description, [0x31: 2, 0x30: 1, 0x70: 2, 0x71: 1, 0x84: 1, 0x80: 1, 0x9C: 1, 0x72: 2, 0x56: 2, 0x60: 3])
|
||||
|
||||
if (cmd) {
|
||||
result += zwaveEvent(cmd) //createEvent(zwaveEvent(cmd))
|
||||
}
|
||||
}
|
||||
|
||||
result << response(zwave.batteryV1.batteryGet().format())
|
||||
|
||||
if ( result[0] != null ) {
|
||||
|
||||
def cmd = zwave.parse(description, [0x31: 2, 0x30: 1, 0x70: 2, 0x71: 1, 0x84: 1, 0x80: 1, 0x9C: 1, 0x72: 2, 0x56: 2, 0x60: 3])
|
||||
|
||||
if (cmd) {
|
||||
result += zwaveEvent(cmd) //createEvent(zwaveEvent(cmd))
|
||||
}
|
||||
|
||||
if ( result[0] != null ) {
|
||||
log.debug "Parse returned ${result}"
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,10 +138,9 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
|
||||
def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)]
|
||||
if (!isConfigured()) {
|
||||
// we're still in the process of configuring a newly joined device
|
||||
result += lateConfigure(true)
|
||||
result << lateConfigure(true)
|
||||
} else {
|
||||
result += response(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
log.debug "We're done with WakeUp!"
|
||||
result << response(zwave.wakeUpV1.wakeUpNoMoreInformation())
|
||||
}
|
||||
result
|
||||
}
|
||||
@@ -153,7 +148,7 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd)
|
||||
{
|
||||
def map = [:]
|
||||
|
||||
|
||||
switch (cmd.sensorType) {
|
||||
case 1:
|
||||
// temperature
|
||||
@@ -184,7 +179,7 @@ def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cm
|
||||
def map = [:]
|
||||
map.value = cmd.sensorValue ? "active" : "inactive"
|
||||
map.name = "acceleration"
|
||||
|
||||
|
||||
if (map.value == "active") {
|
||||
map.descriptionText = "$device.displayName detected vibration"
|
||||
}
|
||||
@@ -199,49 +194,49 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
|
||||
log.debug "BasicSet with CMD = ${cmd}"
|
||||
|
||||
if (!isConfigured()) {
|
||||
def result = []
|
||||
def map = [:]
|
||||
|
||||
map.name = "water"
|
||||
log.debug "BasicSet with CMD = ${cmd}"
|
||||
|
||||
if (!isConfigured()) {
|
||||
def result = []
|
||||
def map = [:]
|
||||
|
||||
map.name = "water"
|
||||
map.value = cmd.value ? "wet" : "dry"
|
||||
map.descriptionText = "${device.displayName} is ${map.value}"
|
||||
|
||||
// If we are getting a BasicSet, and isConfigured == false, then we are likely NOT properly configured.
|
||||
result += lateConfigure(true)
|
||||
|
||||
result << createEvent(map)
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// If we are getting a BasicSet, and isConfigured == false, then we are likely NOT properly configured.
|
||||
result += lateConfigure(true)
|
||||
|
||||
result << createEvent(map)
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd)
|
||||
{
|
||||
def map = [:]
|
||||
|
||||
|
||||
if (cmd.sensorType == 0x05) {
|
||||
map.name = "water"
|
||||
map.value = cmd.sensorState ? "wet" : "dry"
|
||||
map.descriptionText = "${device.displayName} is ${map.value}"
|
||||
|
||||
log.debug "CMD = SensorAlarmReport: ${cmd}"
|
||||
setConfigured()
|
||||
} else if ( cmd.sensorType == 0) {
|
||||
map.name = "tamper"
|
||||
map.isStateChange = true
|
||||
map.value = cmd.sensorState ? "tampered" : "secure"
|
||||
map.descriptionText = "${device.displayName} has been tampered with"
|
||||
runIn(30, "resetTamper") //device does not send alarm cancelation
|
||||
|
||||
} else if ( cmd.sensorType == 1) {
|
||||
map.name = "tamper"
|
||||
map.value = cmd.sensorState ? "tampered" : "secure"
|
||||
map.descriptionText = "${device.displayName} has been tampered with"
|
||||
runIn(30, "resetTamper") //device does not send alarm cancelation
|
||||
|
||||
|
||||
log.debug "CMD = SensorAlarmReport: ${cmd}"
|
||||
setConfigured()
|
||||
} else if ( cmd.sensorType == 0) {
|
||||
map.name = "tamper"
|
||||
map.isStateChange = true
|
||||
map.value = cmd.sensorState ? "tampered" : "secure"
|
||||
map.descriptionText = "${device.displayName} has been tampered with"
|
||||
runIn(30, "resetTamper") //device does not send alarm cancelation
|
||||
|
||||
} else if ( cmd.sensorType == 1) {
|
||||
map.name = "tamper"
|
||||
map.value = cmd.sensorState ? "tampered" : "secure"
|
||||
map.descriptionText = "${device.displayName} has been tampered with"
|
||||
runIn(30, "resetTamper") //device does not send alarm cancelation
|
||||
|
||||
} else {
|
||||
map.descriptionText = "${device.displayName}: ${cmd}"
|
||||
}
|
||||
@@ -250,10 +245,10 @@ def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd)
|
||||
|
||||
def resetTamper() {
|
||||
def map = [:]
|
||||
map.name = "tamper"
|
||||
map.value = "secure"
|
||||
map.descriptionText = "$device.displayName is secure"
|
||||
sendEvent(map)
|
||||
map.name = "tamper"
|
||||
map.value = "secure"
|
||||
map.descriptionText = "$device.displayName is secure"
|
||||
sendEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
@@ -267,10 +262,10 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
|
||||
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
|
||||
log.debug "msr: $msr"
|
||||
device.updateDataValue(["MSR", msr])
|
||||
|
||||
if ( msr == "010F-0B00-2001" ) { //this is the msr and device type for the fibaro flood sensor
|
||||
result += lateConfigure(true)
|
||||
}
|
||||
|
||||
if ( msr == "010F-0B00-2001" ) { //this is the msr and device type for the fibaro flood sensor
|
||||
result += lateConfigure(true)
|
||||
}
|
||||
|
||||
result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)
|
||||
result
|
||||
@@ -282,17 +277,17 @@ def setConfigured() {
|
||||
|
||||
def isConfigured() {
|
||||
Boolean configured = device.getDataValue(["configured"]) as Boolean
|
||||
|
||||
return configured
|
||||
|
||||
return configured
|
||||
}
|
||||
|
||||
def lateConfigure(setConf = False) {
|
||||
def res = response(configure())
|
||||
|
||||
if (setConf)
|
||||
setConfigured()
|
||||
|
||||
return res
|
||||
|
||||
if (setConf)
|
||||
setConfigured()
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,26 +299,34 @@ def lateConfigure(setConf = False) {
|
||||
*/
|
||||
def configure() {
|
||||
log.debug "Configuring Device..."
|
||||
def cmds = []
|
||||
|
||||
// send associate to group 2 to get alarm data
|
||||
cmds << zwave.associationV2.associationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]).format()
|
||||
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format()
|
||||
|
||||
// send associate to group 3 to get sensor data reported only to hub
|
||||
cmds << zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format()
|
||||
// Device wakes up every 4 hours, this interval allows us to miss one wakeup notification before marking offline
|
||||
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
// temp hysteresis set to .5 degrees celcius
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,50], parameterNumber: 12, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format()
|
||||
// default initial state
|
||||
sendEvent(name: "water", value: "dry")
|
||||
|
||||
def cmds = []
|
||||
|
||||
// send associate to group 2 to get alarm data
|
||||
cmds << zwave.associationV2.associationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]).format()
|
||||
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format()
|
||||
|
||||
// send associate to group 3 to get sensor data reported only to hub
|
||||
cmds << zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]).format()
|
||||
|
||||
// reporting frequency of temps and battery set to one hour
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,60*60], parameterNumber: 10, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format()
|
||||
|
||||
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
||||
|
||||
// cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format()
|
||||
|
||||
// temp hysteresis set to .5 degrees celcius
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,50], parameterNumber: 12, size: 2).format()
|
||||
// cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format()
|
||||
|
||||
cmds << zwave.batteryV1.batteryGet().format()
|
||||
|
||||
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
||||
|
||||
delayBetween(cmds, 100)
|
||||
}
|
||||
|
||||
@@ -349,18 +352,18 @@ def test() {
|
||||
* @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 (triple-click?)"
|
||||
log.debug "Updating ${device.displayName} parameter number '${pNumber}' with value '${pValue}' with size of '${pSize}'"
|
||||
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 (triple-click?)"
|
||||
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)
|
||||
}
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: pValue, parameterNumber: pNumber, size: pSize).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: pNumber).format()
|
||||
delayBetween(cmds, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,26 +380,26 @@ def updateZwaveParam(params) {
|
||||
def resetParams2StDefaults() {
|
||||
log.debug "Resetting ${device.displayName} parameters to SmartThings compatible defaults"
|
||||
def cmds = []
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 1, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 2, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 7, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 9, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,60*60], parameterNumber: 10, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,50], parameterNumber: 12, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 13, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [5,220], parameterNumber: 50, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [13,172], parameterNumber: 51, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0,0,225], parameterNumber: 61, size: 4).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,255,0,0], parameterNumber: 62, size: 4).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 63, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 73, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 74, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 75, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 76, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 77, size: 1).format()
|
||||
|
||||
delayBetween(cmds, 1200)
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 1, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [3], parameterNumber: 2, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 5, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [255], parameterNumber: 7, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 9, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,60*60], parameterNumber: 10, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,50], parameterNumber: 12, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 13, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [5,220], parameterNumber: 50, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [13,172], parameterNumber: 51, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0,0,225], parameterNumber: 61, size: 4).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,255,0,0], parameterNumber: 62, size: 4).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 63, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 73, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 74, size: 1).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 75, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 76, size: 2).format()
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 77, size: 1).format()
|
||||
|
||||
delayBetween(cmds, 1200)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,25 +416,25 @@ def resetParams2StDefaults() {
|
||||
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: 2).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 5).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 9).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 13).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 50).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 51).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 61).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 62).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 63).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 73).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 74).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 75).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 76).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 77).format()
|
||||
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 1).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 2).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 5).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 7).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 9).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 12).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 13).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 50).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 51).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 61).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 62).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 63).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 73).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 74).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 75).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 76).format()
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: 77).format()
|
||||
|
||||
delayBetween(cmds, 1200)
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
capability "Illuminance Measurement"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
command "resetParams2StDefaults"
|
||||
command "listCurrentParams"
|
||||
@@ -81,8 +82,8 @@
|
||||
|
||||
tiles {
|
||||
standardTile("motion", "device.motion", width: 2, height: 2) {
|
||||
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
||||
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00a0dc"
|
||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc"
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
@@ -125,6 +126,9 @@
|
||||
*/
|
||||
def configure() {
|
||||
log.debug "Configuring Device For SmartThings Use"
|
||||
// Device-Watch simply pings if no device events received for 8 hrs & 2 minutes
|
||||
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
def cmds = []
|
||||
|
||||
// send associate to group 3 to get sensor data reported only to hub
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
39
devicetypes/smartthings/fortrezz-water-valve.src/README.md
Normal file
39
devicetypes/smartthings/fortrezz-water-valve.src/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# FortrezZ Water Valve
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [FortrezZ Water Valve](https://www.smartthings.com/works-with-smartthings/other/fortrezz-water-valve)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Valve** - allows for the control of a valve device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
|
||||
## Device Health
|
||||
|
||||
FortrezZ Water Valve is polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [FortrezZ Water Valve Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202088434-FortrezZ-Water-Valve-Shutoff)
|
||||
@@ -14,11 +14,13 @@
|
||||
metadata {
|
||||
definition (name: "Fortrezz Water Valve", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Health Check"
|
||||
capability "Valve"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
fingerprint deviceId: "0x1000", inClusters: "0x25,0x72,0x86,0x71,0x22,0x70"
|
||||
fingerprint mfr:"0084", prod:"0213", model:"0215", deviceJoinName: "FortrezZ Water Valve"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
@@ -48,6 +50,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
log.trace description
|
||||
def result = null
|
||||
@@ -76,6 +83,13 @@ def close() {
|
||||
zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF).format()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("motion", "device.motion", width: 2, height: 2) {
|
||||
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
||||
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC"
|
||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc"
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
|
||||
@@ -7,6 +7,7 @@ metadata {
|
||||
definition (name: "Logitech Harmony Hub C2C", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Media Controller"
|
||||
capability "Refresh"
|
||||
capability "Health Check"
|
||||
|
||||
command "activityoff"
|
||||
command "alloff"
|
||||
@@ -38,6 +39,16 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "installed()"
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "cloud", scheme:"untracked"]), displayed: false)
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "updated()"
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "cloud", scheme:"untracked"]), displayed: false)
|
||||
}
|
||||
|
||||
def startActivity(String activityId) {
|
||||
log.debug "Executing 'Start Activity'"
|
||||
log.trace parent.activity("$device.deviceNetworkId-$activityId","start")
|
||||
@@ -58,6 +69,10 @@ def poll() {
|
||||
log.trace parent.poll()
|
||||
}
|
||||
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "Executing 'Refresh'"
|
||||
log.trace parent.poll()
|
||||
|
||||
@@ -21,6 +21,7 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Battery"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
command "enrollResponse"
|
||||
|
||||
@@ -31,8 +32,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("motion", "device.motion", width: 2, height: 2) {
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC")
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc")
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
|
||||
|
||||
@@ -24,6 +24,7 @@ metadata {
|
||||
capability "Contact Sensor"
|
||||
capability "Refresh"
|
||||
capability "Health Check"
|
||||
capability "Sensor"
|
||||
|
||||
command "enrollResponse"
|
||||
|
||||
@@ -41,8 +42,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00a0dc")
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
|
||||
|
||||
2
devicetypes/smartthings/plant-link.src/.st-ignore
Normal file
2
devicetypes/smartthings/plant-link.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
35
devicetypes/smartthings/plant-link.src/README.md
Normal file
35
devicetypes/smartthings/plant-link.src/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Plant Link
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [OSO Technologies PlantLink Soil Moisture Sensor](https://www.smartthings.com/works-with-smartthings/oso-technologies/oso-technologies-plantlink-soil-moisture-sensor)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Relative Humidity Measurement** - allows reading the relative humidity from devices that support it
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Plant Link sensor is a Z-wave sleepy device and checks in every 15 minutes.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links
|
||||
for the different models:
|
||||
* [OSO Technologies PlantLink Soil Moisture Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/206868986-PlantLink-Soil-Moisture-Sensor)
|
||||
@@ -21,8 +21,10 @@ metadata {
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Battery"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0405,FC08", outClusters: "0003"
|
||||
fingerprint endpoint: "1", profileId: "0104", inClusters: "0000,0001,0003,0B04", outClusters: "0003", manufacturer: "", model: "", deviceJoinName: "OSO Technologies PlantLink Soil Moisture Sensor"
|
||||
}
|
||||
|
||||
tiles {
|
||||
@@ -48,6 +50,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "Parse description $description"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* Date: 2013-03-05
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "SmartAlert Siren", namespace: "smartthings", author: "SmartThings") {
|
||||
definition (name: "SmartAlert Siren", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "x.com.st.smokedetector") {
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Sensor"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* Date: 2013-12-04
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "SmartPower Dimming Outlet", namespace: "smartthings", author: "SmartThings") {
|
||||
definition (name: "SmartPower Dimming Outlet", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.smartplug") {
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Power Meter"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
metadata {
|
||||
// Automatically generated. Make future change here.
|
||||
definition(name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") {
|
||||
definition(name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.smartplug") {
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Power Meter"
|
||||
|
||||
@@ -33,6 +33,7 @@ metadata {
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-Seu", deviceJoinName: "Water Leak Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-L", deviceJoinName: "Iris Smart Water Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-G", deviceJoinName: "Centralite Water Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "moisturev4", deviceJoinName: "Water Leak Sensor"
|
||||
}
|
||||
|
||||
@@ -58,7 +59,7 @@ metadata {
|
||||
multiAttributeTile(name: "water", type: "generic", width: 6, height: 4) {
|
||||
tileAttribute("device.water", key: "PRIMARY_CONTROL") {
|
||||
attributeState "dry", label: "Dry", icon: "st.alarm.water.dry", backgroundColor: "#ffffff"
|
||||
attributeState "wet", label: "Wet", icon: "st.alarm.water.wet", backgroundColor: "#53a7c0"
|
||||
attributeState "wet", label: "Wet", icon: "st.alarm.water.wet", backgroundColor: "#00A0DC"
|
||||
}
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
@@ -106,6 +107,12 @@ def parse(String description) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (map.name == "temperature") {
|
||||
if (tempOffset) {
|
||||
map.value = (int) map.value + (int) tempOffset
|
||||
}
|
||||
map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F'
|
||||
map.translatable = true
|
||||
}
|
||||
|
||||
log.debug "Parse returned $map"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
36
devicetypes/smartthings/smartsense-moisture.src/README.md
Normal file
36
devicetypes/smartthings/smartsense-moisture.src/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Smartsense Moisture
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [FortrezZ Moisture Sensor](https://www.smartthings.com/works-with-smartthings/fortrezz/fortrezz-moisture-sensor)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Water Sensor** - can detect presence of water (dry or wet)
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Temperature Measurement** - represents capability to measure temperature
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Smartsense Moisture is a Z-wave sleepy device type and checks in every 4 hours.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins.
|
||||
|
||||
* __482min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links
|
||||
for the different models:
|
||||
* [FortrezZ Moisture Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200930740-FortrezZ-Moisture-Sensor)
|
||||
@@ -17,9 +17,11 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Temperature Measurement"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint deviceId: "0x2001", inClusters: "0x30,0x9C,0x9D,0x85,0x80,0x72,0x31,0x84,0x86"
|
||||
fingerprint deviceId: "0x2101", inClusters: "0x71,0x70,0x85,0x80,0x72,0x31,0x84,0x86"
|
||||
fingerprint mfr:"0084", prod:"0063", model:"010C", deviceJoinName: "FortrezZ Moisture Sensor"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -89,6 +91,11 @@ def parse(String description) {
|
||||
return result
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// Device-Watch simply pings if no device events received for 482min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 4 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd)
|
||||
{
|
||||
[descriptionText: "${device.displayName} woke up", isStateChange: false]
|
||||
|
||||
@@ -34,6 +34,7 @@ metadata {
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326-L", deviceJoinName: "Iris Motion Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3328-G", deviceJoinName: "Centralite Micro Motion Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv5", deviceJoinName: "Motion Sensor"
|
||||
}
|
||||
@@ -112,6 +113,12 @@ def parse(String description) {
|
||||
map = getMotionResult(value)
|
||||
}
|
||||
}
|
||||
} else if (map.name == "temperature") {
|
||||
if (tempOffset) {
|
||||
map.value = (int) map.value + (int) tempOffset
|
||||
}
|
||||
map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F'
|
||||
map.translatable = true
|
||||
}
|
||||
|
||||
log.debug "Parse returned $map"
|
||||
|
||||
@@ -30,8 +30,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"motion", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
|
||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
||||
attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC"
|
||||
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc"
|
||||
}
|
||||
}
|
||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
||||
|
||||
@@ -33,6 +33,7 @@ metadata {
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "Multipurpose Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3323-G", deviceJoinName: "Centralite Micro Door Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor"
|
||||
|
||||
attribute "status", "string"
|
||||
|
||||
@@ -52,14 +52,14 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
|
||||
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
|
||||
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13"
|
||||
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00a0dc"
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#00a0dc")
|
||||
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#cccccc")
|
||||
}
|
||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||
state("temperature", label:'${currentValue}°',
|
||||
|
||||
@@ -45,8 +45,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name: "contact", type: "generic", width: 6, height: 4) {
|
||||
tileAttribute("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
|
||||
attributeState "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
|
||||
attributeState "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#e86d13"
|
||||
attributeState "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#00A0DC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,12 @@ def parse(String description) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (map.name == "temperature") {
|
||||
if (tempOffset) {
|
||||
map.value = (int) map.value + (int) tempOffset
|
||||
}
|
||||
map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F'
|
||||
map.translatable = true
|
||||
}
|
||||
|
||||
log.debug "Parse returned $map"
|
||||
|
||||
@@ -88,6 +88,12 @@ def parse(String description) {
|
||||
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
|
||||
}
|
||||
}
|
||||
} else if (map.name == "temperature") {
|
||||
if (tempOffset) {
|
||||
map.value = (int) map.value + (int) tempOffset
|
||||
}
|
||||
map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F'
|
||||
map.translatable = true
|
||||
}
|
||||
|
||||
log.debug "Parse returned $map"
|
||||
@@ -129,10 +135,7 @@ def refresh() {
|
||||
return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware
|
||||
zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware
|
||||
zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) +
|
||||
zigbee.configureReporting(0xFC45, 0x0000, DataType.INT16, 30, 3600, 100) +
|
||||
zigbee.batteryConfig() +
|
||||
zigbee.temperatureConfig(30, 300)
|
||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
||||
}
|
||||
|
||||
def configure() {
|
||||
@@ -144,5 +147,10 @@ def configure() {
|
||||
|
||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
||||
return refresh()
|
||||
return refresh() +
|
||||
zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0x104E]) + // New firmware
|
||||
zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0xC2DF]) + // Original firmware
|
||||
zigbee.batteryConfig() +
|
||||
zigbee.temperatureConfig(30, 300)
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Temperature Measurement"
|
||||
capability "Health Check"
|
||||
capability "Sensor"
|
||||
|
||||
command "enrollResponse"
|
||||
|
||||
@@ -41,8 +42,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00A0DC")
|
||||
}
|
||||
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
||||
|
||||
@@ -44,7 +44,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("button", "device.button", width: 2, height: 2) {
|
||||
state "default", label: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
|
||||
state "button 1 pushed", label: "pushed #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
|
||||
state "button 1 pushed", label: "pushed #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "ZigBee Dimmer Power", namespace: "smartthings", author: "SmartThings") {
|
||||
definition (name: "ZigBee Dimmer Power", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch") {
|
||||
capability "Actuator"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
|
||||
@@ -40,6 +40,7 @@ metadata {
|
||||
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"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0000, 0003, 0006", manufacturer: "LINGAN", model: "LIGHTIFY A01 RGBW", deviceJoinName: "LINGAN LIGHTIFY LED Smart Connected Light"
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "ZigBee Switch Power", namespace: "smartthings", author: "SmartThings") {
|
||||
definition (name: "ZigBee Switch Power", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch") {
|
||||
capability "Actuator"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
|
||||
2
devicetypes/smartthings/zigbee-switch.src/.st-ignore
Normal file
2
devicetypes/smartthings/zigbee-switch.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
35
devicetypes/smartthings/zigbee-switch.src/README.md
Normal file
35
devicetypes/smartthings/zigbee-switch.src/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Leviton Switch (ZigBee)
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Leviton Switch (ZigBee)](https://www.smartthings.com/works-with-smartthings/leviton/leviton-switch)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
A Zigbee Switch with reporting interval of 10 mins.
|
||||
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
|
||||
|
||||
* __22min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Leviton Switch Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/209686003-How-to-connect-Leviton-ZigBee-devices)
|
||||
@@ -13,11 +13,12 @@
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "ZigBee Switch", namespace: "smartthings", author: "SmartThings") {
|
||||
definition (name: "ZigBee Switch", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch") {
|
||||
capability "Actuator"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Switch"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0003, 0006, 0019, 0406", manufacturer: "Leviton", model: "ZSS-10", deviceJoinName: "Leviton Switch"
|
||||
@@ -75,11 +76,20 @@ def on() {
|
||||
zigbee.on()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
return refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
zigbee.onOffRefresh() + zigbee.onOffConfig()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
// Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time)
|
||||
sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
zigbee.onOffRefresh() + zigbee.onOffConfig()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Copyright 2016 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.
|
||||
*
|
||||
*/
|
||||
import groovy.transform.Field
|
||||
|
||||
@Field Boolean hasConfiguredHealthCheck = false
|
||||
|
||||
metadata {
|
||||
definition (name: "ZLL White Color Temperature Bulb 5000K", namespace: "smartthings", author: "SmartThings") {
|
||||
|
||||
capability "Actuator"
|
||||
capability "Color Temperature"
|
||||
capability "Configuration"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Health Check"
|
||||
|
||||
attribute "colorName", "string"
|
||||
command "setGenericName"
|
||||
|
||||
fingerprint profileId: "C05E", deviceId: "0220", inClusters: "0000, 0004, 0003, 0006, 0008, 0005, 0300", outClusters: "0019", manufacturer: "Eaton", model: "Halo_RL5601", deviceJoinName: "Halo RL56"
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
tileAttribute ("colorName", key: "SECONDARY_CONTROL") {
|
||||
attributeState "colorName", label:'${currentValue}'
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2700..5000)") {
|
||||
state "colorTemperature", action:"color temperature.setColorTemperature"
|
||||
}
|
||||
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "colorTemperature", label: '${currentValue} K'
|
||||
}
|
||||
|
||||
main(["switch"])
|
||||
details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
def event = zigbee.getEvent(description)
|
||||
if (event) {
|
||||
if (event.name == "colorTemperature") {
|
||||
event.unit = "K"
|
||||
}
|
||||
sendEvent(event)
|
||||
}
|
||||
else {
|
||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||
log.debug zigbee.parseDescriptionAsMap(description)
|
||||
}
|
||||
}
|
||||
|
||||
def off() {
|
||||
zigbee.off() + ["delay 1500"] + zigbee.onOffRefresh()
|
||||
}
|
||||
|
||||
def on() {
|
||||
zigbee.on() + ["delay 1500"] + zigbee.onOffRefresh()
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
|
||||
// Do NOT config if the device is the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, and maybe other weird things with the others
|
||||
if (!((device.getDataValue("manufacturer") == "Eaton") && (device.getDataValue("model") == "Halo_LT01"))) {
|
||||
cmds = cmds + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
|
||||
}
|
||||
|
||||
cmds
|
||||
}
|
||||
|
||||
def poll() {
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
return zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
def healthPoll() {
|
||||
log.debug "healthPoll()"
|
||||
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
|
||||
}
|
||||
|
||||
def configureHealthCheck() {
|
||||
Integer hcIntervalMinutes = 12
|
||||
if (!hasConfiguredHealthCheck) {
|
||||
log.debug "Configuring Health Check, Reporting"
|
||||
unschedule("healthPoll")
|
||||
runEvery5Minutes("healthPoll")
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
hasConfiguredHealthCheck = true
|
||||
}
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "configure()"
|
||||
configureHealthCheck()
|
||||
// Implementation note: for the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, so be sure this is before the call to onOffRefresh
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "updated()"
|
||||
configureHealthCheck()
|
||||
}
|
||||
|
||||
def setColorTemperature(value) {
|
||||
setGenericName(value)
|
||||
zigbee.setColorTemperature(value) + ["delay 1500"] + zigbee.colorTemperatureRefresh()
|
||||
}
|
||||
|
||||
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
|
||||
def setGenericName(value){
|
||||
if (value != null) {
|
||||
def genericName = ""
|
||||
if (value < 3300) {
|
||||
genericName = "Soft White"
|
||||
} else if (value < 4150) {
|
||||
genericName = "Moonlight"
|
||||
} else if (value <= 5000) {
|
||||
genericName = "Cool White"
|
||||
} else {
|
||||
genericName = "Daylight"
|
||||
}
|
||||
sendEvent(name: "colorName", value: genericName, displayed: false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Zooz Power Strip Outlet
|
||||
*
|
||||
* Copyright 2017 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 {
|
||||
definition (name: "Zooz Power Strip Outlet", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Switch"
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
}
|
||||
|
||||
tiles {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"turningOn"
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on() {
|
||||
parent.childOn(device.deviceNetworkId)
|
||||
}
|
||||
|
||||
void off() {
|
||||
parent.childOff(device.deviceNetworkId)
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* Zooz ZEN20 Power Strip Outlet
|
||||
*
|
||||
* Implementation of the Zooz ZEN20 power strip that uses the new composite device capabilities to provide individual
|
||||
* control of each outlet from SmartApps as well as the mobile app. Incorporates contributions from:
|
||||
*
|
||||
* Eric Maycock (https://github.com/erocm123/SmartThingsPublic/blob/master/devicetypes/erocm123/zooz-power-strip.src/zooz-power-strip.groovy)
|
||||
* Robert Vandervoort (https://github.com/robertvandervoort/SmartThings/blob/master/zooZ-Strip-ZEN20/device_type-zooZ-strip-ZEN20_v1.0)
|
||||
*
|
||||
* Copyright 2017 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 {
|
||||
definition (name: "Zooz Power Strip", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
|
||||
fingerprint manufacturer: "015D", prod: "0651", model: "F51C", deviceJoinName: "Zooz ZEN 20 Power Strip"
|
||||
}
|
||||
|
||||
tiles {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"turningOn"
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
}
|
||||
childDeviceTiles("outlets")
|
||||
standardTile("refresh", "device.switch", width: 1, height: 1, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Installation and update //
|
||||
/////////////////////////////
|
||||
def installed() {
|
||||
createChildDevices()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
if (!childDevices) {
|
||||
createChildDevices()
|
||||
}
|
||||
else if (device.label != state.oldLabel) {
|
||||
childDevices.each {
|
||||
def newLabel = "${device.displayName} (CH${channelNumber(it.deviceNetworkId)})"
|
||||
it.setLabel(newLabel)
|
||||
}
|
||||
state.oldLabel = device.label
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// Event Generation //
|
||||
//////////////////////
|
||||
def parse(String description) {
|
||||
trace "parse('$description')"
|
||||
def result = []
|
||||
if (description.startsWith("Err")) {
|
||||
result = createEvent(descriptionText:description, isStateChange:true)
|
||||
} else if (description != "updated") {
|
||||
def cmd = zwave.parse(description, [0x60: 3, 0x32: 3, 0x25: 1, 0x20: 1])
|
||||
if (cmd) {
|
||||
result += zwaveEvent(cmd, 1)
|
||||
}
|
||||
else {
|
||||
log.warn "Unparsed description $description"
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep) {
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x32: 3, 0x25: 1, 0x20: 1])
|
||||
if (encapsulatedCommand) {
|
||||
zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer)
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, endpoint) {
|
||||
trace "zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport $cmd, $endpoint)"
|
||||
zwaveBinaryEvent(cmd, endpoint)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, endpoint) {
|
||||
trace "zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport $cmd, $endpoint)"
|
||||
zwaveBinaryEvent(cmd, endpoint)
|
||||
}
|
||||
|
||||
def zwaveBinaryEvent(cmd, endpoint) {
|
||||
def result = []
|
||||
def children = childDevices
|
||||
def childDevice = children.find{it.deviceNetworkId.endsWith("$endpoint")}
|
||||
childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off")
|
||||
|
||||
if (cmd.value) {
|
||||
// One on and the strip is on
|
||||
result << createEvent(name: "switch", value: "on")
|
||||
} else {
|
||||
// All off and the strip is off
|
||||
if (! children.any { it.currentValue("switch") == "on" }) {
|
||||
result << createEvent(name: "switch", value: "off")
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd, ep) {
|
||||
updateDataValue("MSR", String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId))
|
||||
return null
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd, ep) {
|
||||
trace "applicationVersion $cmd.applicationVersion"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd, ep) {
|
||||
log.warn("${device.displayName}: Unhandled ${cmd}" + (ep ? " from endpoint $ep" : ""))
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Installation and update //
|
||||
/////////////////////////////
|
||||
def on() {
|
||||
def cmds = []
|
||||
def cmd = zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF)
|
||||
cmds << zwave.multiChannelV3.multiChannelCmdEncap(bitAddress: true, destinationEndPoint:0x1F).encapsulate(cmd).format()
|
||||
cmds << "delay 400"
|
||||
cmds.addAll(refresh())
|
||||
return cmds
|
||||
}
|
||||
|
||||
def off() {
|
||||
def cmds = []
|
||||
def cmd = zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00)
|
||||
cmds << zwave.multiChannelV3.multiChannelCmdEncap(bitAddress: true, destinationEndPoint:0x1F).encapsulate(cmd).format()
|
||||
cmds << "delay 400"
|
||||
cmds.addAll(refresh())
|
||||
return cmds
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// Child Device API //
|
||||
//////////////////////
|
||||
void childOn(String dni) {
|
||||
onOffCmd(0xFF, channelNumber(dni))
|
||||
}
|
||||
|
||||
void childOff(String dni) {
|
||||
onOffCmd(0, channelNumber(dni))
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
def cmds = (1..5).collect { endpoint ->
|
||||
encap(zwave.switchBinaryV1.switchBinaryGet(), endpoint)
|
||||
}
|
||||
delayBetween(cmds, 100)
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// Local Methods //
|
||||
///////////////////
|
||||
private channelNumber(String dni) {
|
||||
dni.split("-ep")[-1] as Integer
|
||||
}
|
||||
|
||||
private void onOffCmd(value, endpoint = null) {
|
||||
def actions = [
|
||||
new physicalgraph.device.HubAction(encap(zwave.basicV1.basicSet(value: value), endpoint)),
|
||||
new physicalgraph.device.HubAction(encap(zwave.switchBinaryV1.switchBinaryGet(), endpoint)),
|
||||
]
|
||||
sendHubCommand(actions, 500)
|
||||
}
|
||||
|
||||
private void createChildDevices() {
|
||||
state.oldLabel = device.label
|
||||
for (i in 1..5) {
|
||||
addChildDevice("Zooz Power Strip Outlet", "${device.deviceNetworkId}-ep${i}", null,
|
||||
[completedSetup: true, label: "${device.displayName} (CH${i})",
|
||||
isComponent: true, componentName: "ch$i", componentLabel: "Channel $i"])
|
||||
}
|
||||
}
|
||||
|
||||
private encap(cmd, endpoint) {
|
||||
if (endpoint) {
|
||||
zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd).format()
|
||||
} else {
|
||||
cmd.format()
|
||||
}
|
||||
}
|
||||
|
||||
private trace(msg) {
|
||||
//log.trace(msg)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.unknown.zwave.device", backgroundColor: "#79b821"
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.unknown.zwave.device", backgroundColor: "#00A0DC"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.unknown.zwave.device", backgroundColor: "#ffffff"
|
||||
}
|
||||
standardTile("switchOn", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
@@ -33,7 +33,7 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.unknown.zwave.device", backgroundColor: "#79b821"
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.unknown.zwave.device", backgroundColor: "#00A0DC"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.unknown.zwave.device", backgroundColor: "#ffffff"
|
||||
}
|
||||
standardTile("switchOn", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,36 @@
|
||||
# Z-Wave Door Window Sensor
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Aeon Labs Door/Window Sensor (Gen 5)](https://www.smartthings.com/works-with-smartthings/aeon-labs/aeon-labs-doorwindow-sensor-gen-5)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines that the device has a battery
|
||||
* **Contact Sensor** - allows reading the value of a contact sensor device
|
||||
|
||||
## Device Health
|
||||
|
||||
Z-Wave Door Window Sensor is a Z-wave sleepy device and checks in every 4 hours.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins.
|
||||
|
||||
* __482min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following links
|
||||
for the different models:
|
||||
* [Aeon Labs Door/Window Sensor (Gen 5) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/211834163-How-to-connect-Aeon-Labs-door-window-sensors)
|
||||
@@ -22,12 +22,14 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Configuration"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint deviceId: "0x2001", inClusters: "0x30,0x80,0x84,0x85,0x86,0x72"
|
||||
fingerprint deviceId: "0x07", inClusters: "0x30"
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x98"
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82"
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x80,0x71,0x85,0x70,0x72,0x86,0x30,0x31,0x84,0x59,0x73,0x5A,0x8F,0x98,0x7A", outClusters:"0x20" // Philio multi+
|
||||
fingerprint mfr:"0086", prod:"0002", model:"001D", deviceJoinName: "Aeon Labs Door/Window Sensor (Gen 5)"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
@@ -41,8 +43,8 @@ metadata {
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
|
||||
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
|
||||
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#e86d13"
|
||||
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#00A0DC"
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
@@ -78,6 +80,8 @@ def parse(String description) {
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// Device-Watch simply pings if no device events received for 482min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 4 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
def cmds = []
|
||||
if (!state.MSR) {
|
||||
cmds = [
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -0,0 +1,41 @@
|
||||
# Z-wave Garage Door Opener
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Linear GoControl Garage Door Opener](https://www.smartthings.com/works-with-smartthings/other/linear-gocontrol-garage-door-opener)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Door Control** - allow for the control of a door
|
||||
* **Garage Door Control** - allow for the control of a garage door
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Contact Sensor** - can detect contact (with possible values - open/closed)
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
|
||||
## Device Health
|
||||
|
||||
Linear GoControl Garage Door Opener is polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Linear GoControl Garage Door Opener Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204831116-GoControl-Linear-Garage-Door-Opener-GD00Z-4-)
|
||||
@@ -18,12 +18,14 @@ metadata {
|
||||
capability "Actuator"
|
||||
capability "Door Control"
|
||||
capability "Garage Door Control"
|
||||
capability "Health Check"
|
||||
capability "Contact Sensor"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
fingerprint deviceId: "0x4007", inClusters: "0x98"
|
||||
fingerprint deviceId: "0x4006", inClusters: "0x98"
|
||||
fingerprint mfr:"014F", prod:"4744", model:"3030", deviceJoinName: "Linear GoControl Garage Door Opener"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -39,11 +41,11 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("toggle", "device.door", width: 2, height: 2) {
|
||||
state("unknown", label:'${name}', action:"refresh.refresh", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e")
|
||||
state("closed", label:'${name}', action:"door control.open", icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821", nextState:"opening")
|
||||
state("open", label:'${name}', action:"door control.close", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e", nextState:"closing")
|
||||
state("opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
|
||||
state("closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
|
||||
state("unknown", label:'${name}', action:"refresh.refresh", icon:"st.doors.garage.garage-open", backgroundColor:"#ffffff")
|
||||
state("closed", label:'${name}', action:"door control.open", icon:"st.doors.garage.garage-closed", backgroundColor:"#00a0dc", nextState:"opening")
|
||||
state("open", label:'${name}', action:"door control.close", icon:"st.doors.garage.garage-open", backgroundColor:"#e86d13", nextState:"closing")
|
||||
state("opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#e86d13")
|
||||
state("closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#00a0dc")
|
||||
|
||||
}
|
||||
standardTile("open", "device.door", inactiveLabel: false, decoration: "flat") {
|
||||
@@ -63,6 +65,11 @@ metadata {
|
||||
|
||||
import physicalgraph.zwave.commands.barrieroperatorv1.*
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = null
|
||||
if (description.startsWith("Err")) {
|
||||
@@ -287,6 +294,13 @@ def close() {
|
||||
secure(zwave.barrierOperatorV1.barrierOperatorSet(requestedBarrierState: BarrierOperatorSet.REQUESTED_BARRIER_STATE_CLOSE))
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
secure(zwave.barrierOperatorV1.barrierOperatorGet())
|
||||
}
|
||||
|
||||
2
devicetypes/smartthings/zwave-lock.src/.st-ignore
Normal file
2
devicetypes/smartthings/zwave-lock.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
45
devicetypes/smartthings/zwave-lock.src/README.md
Normal file
45
devicetypes/smartthings/zwave-lock.src/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Z-Wave Switch
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Yale Key Free Touchscreen Deadbolt (YRD240)](https://www.smartthings.com/works-with-smartthings/yale/yale-key-free-touchscreen-deadbolt-yrd240)
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Lock** - allows for the control of a lock device
|
||||
* **Lock Codes** - allows for the lock code control of a lock device
|
||||
* **Polling** - represents that poll() can be implemented for the device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Z-Wave Locks are polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [General Z-Wave/ZigBee Yale Lock Troubleshooting](https://support.smartthings.com/hc/en-us/articles/205138400-How-to-connect-Yale-locks)
|
||||
|
||||
|
||||
@@ -20,11 +20,13 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Lock Codes"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
command "unlockwtimeout"
|
||||
|
||||
fingerprint deviceId: "0x4003", inClusters: "0x98"
|
||||
fingerprint deviceId: "0x4004", inClusters: "0x98"
|
||||
fingerprint mfr:"0129", prod:"0002", model:"0000", deviceJoinName: "Yale Key Free Touchscreen Deadbolt"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -38,10 +40,10 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"toggle", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.lock", key: "PRIMARY_CONTROL") {
|
||||
attributeState "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821", nextState:"unlocking"
|
||||
attributeState "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#00A0DC", nextState:"unlocking"
|
||||
attributeState "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff", nextState:"locking"
|
||||
attributeState "unknown", label:"unknown", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking"
|
||||
attributeState "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#79b821"
|
||||
attributeState "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#00A0DC"
|
||||
attributeState "unlocking", label:'unlocking', icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff"
|
||||
}
|
||||
}
|
||||
@@ -67,6 +69,8 @@ import physicalgraph.zwave.commands.doorlockv1.*
|
||||
import physicalgraph.zwave.commands.usercodev1.*
|
||||
|
||||
def updated() {
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
try {
|
||||
if (!state.init) {
|
||||
state.init = true
|
||||
@@ -504,6 +508,13 @@ def unlockwtimeout() {
|
||||
lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_UNSECURED_WITH_TIMEOUT)
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
def cmds = [secure(zwave.doorLockV1.doorLockOperationGet())]
|
||||
if (state.assoc == zwaveHubNodeId) {
|
||||
|
||||
@@ -25,11 +25,13 @@ metadata {
|
||||
capability "Switch Level"
|
||||
capability "Sensor"
|
||||
capability "Actuator"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
command "reset"
|
||||
|
||||
fingerprint inClusters: "0x26,0x32"
|
||||
fingerprint mfr:"0086", prod:"0003", model:"001B", deviceJoinName: "Aeon Labs Micro Smart Dimmer 2E"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -99,6 +101,9 @@ def parse(String description) {
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
response(refresh())
|
||||
}
|
||||
|
||||
@@ -163,6 +168,14 @@ def poll() {
|
||||
], 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
log.debug "ping() called"
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
delayBetween([
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format(),
|
||||
|
||||
@@ -22,10 +22,12 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Light"
|
||||
capability "Health Check"
|
||||
|
||||
command "reset"
|
||||
|
||||
fingerprint inClusters: "0x25,0x32"
|
||||
fingerprint mfr:"0086", prod:"0003", model:"0012", deviceJoinName: "Aeon Labs Micro Smart Switch 2E"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
@@ -51,7 +53,7 @@ metadata {
|
||||
// tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
valueTile("power", "device.power") {
|
||||
@@ -73,6 +75,8 @@ metadata {
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
try {
|
||||
if (!state.MSR) {
|
||||
response(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())
|
||||
@@ -179,6 +183,14 @@ def poll() {
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
log.debug "ping() called"
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
delayBetween([
|
||||
zwave.switchBinaryV1.switchBinaryGet().format(),
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
39
devicetypes/smartthings/zwave-motion-sensor.src/README.md
Normal file
39
devicetypes/smartthings/zwave-motion-sensor.src/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Z-wave Motion Sensor
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Ecolink PIR Motion Detector with Pet Immunity](https://www.smartthings.com/works-with-smartthings/sensors/ecolink-pir-motion-detector-with-pet-immunity)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery-specification)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Motion Sensor** - can detect motion
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Ecolink PIR Motion Detector with Pet Immunity is a Z-wave sleepy device and wakes up every 4 hours.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins.
|
||||
|
||||
* __482min__ checkInterval
|
||||
|
||||
## Battery Specification
|
||||
|
||||
One CR123A Lithium 3V battery is required.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [Ecolink PIR Motion Detector with Pet Immunity Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202294400-Ecolink-PIR-Motion-Detector-PIRZWAVE2-ECO-)
|
||||
@@ -21,6 +21,7 @@ metadata {
|
||||
capability "Motion Sensor"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint mfr: "011F", prod: "0001", model: "0001", deviceJoinName: "Schlage Motion Sensor" // Schlage motion
|
||||
fingerprint mfr: "014A", prod: "0001", model: "0001", deviceJoinName: "Ecolink Motion Sensor" // Ecolink motion
|
||||
@@ -38,8 +39,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("motion", "device.motion", width: 2, height: 2) {
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC")
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc")
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
state("battery", label:'${currentValue}% battery', unit:"")
|
||||
@@ -50,6 +51,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device wakes up every 4 hours, this interval allows us to miss one wakeup notification before marking offline
|
||||
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = null
|
||||
if (description.startsWith("Err")) {
|
||||
|
||||
@@ -37,8 +37,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
|
||||
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
|
||||
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13"
|
||||
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00a0dc"
|
||||
}
|
||||
}
|
||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
||||
|
||||
@@ -32,8 +32,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("sensor", "device.sensor", width: 2, height: 2) {
|
||||
state("inactive", label:'inactive', icon:"st.unknown.zwave.sensor", backgroundColor:"#ffffff")
|
||||
state("active", label:'active', icon:"st.unknown.zwave.sensor", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'inactive', icon:"st.unknown.zwave.sensor", backgroundColor:"#cccccc")
|
||||
state("active", label:'active', icon:"st.unknown.zwave.sensor", backgroundColor:"#00A0DC")
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
|
||||
2
devicetypes/smartthings/zwave-siren.src/.st-ignore
Normal file
2
devicetypes/smartthings/zwave-siren.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
42
devicetypes/smartthings/zwave-siren.src/README.md
Normal file
42
devicetypes/smartthings/zwave-siren.src/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Z-wave Siren
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [FortrezZ Siren Strobe Alarm](https://www.smartthings.com/works-with-smartthings/other/fortrezz-water-valve)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Alarm** - allows for interacting with devices that serve as alarms
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Polling** - represents that poll() can be implemented for the device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
|
||||
## Device Health
|
||||
|
||||
FortrezZ Siren Strobe Alarm is polled by the hub.
|
||||
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
|
||||
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
|
||||
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [FortrezZ Siren Strobe Alarm Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202294760-FortrezZ-Siren-Strobe-Alarm)
|
||||
@@ -20,6 +20,7 @@ metadata {
|
||||
capability "Actuator"
|
||||
capability "Alarm"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
@@ -27,6 +28,7 @@ metadata {
|
||||
|
||||
|
||||
fingerprint inClusters: "0x20,0x25,0x86,0x80,0x85,0x72,0x71"
|
||||
fingerprint mfr:"0084", prod:"0313", model:"010B", deviceJoinName: "FortrezZ Siren Strobe Alarm"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -58,6 +60,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def createEvents(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [ name: "battery", unit: "%" ]
|
||||
if (cmd.batteryLevel == 0xFF) {
|
||||
@@ -119,6 +126,13 @@ def both() {
|
||||
on()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "sending battery refresh command"
|
||||
zwave.batteryV1.batteryGet().format()
|
||||
|
||||
2
devicetypes/smartthings/zwave-smoke-alarm.src/.st-ignore
Normal file
2
devicetypes/smartthings/zwave-smoke-alarm.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
40
devicetypes/smartthings/zwave-smoke-alarm.src/README.md
Normal file
40
devicetypes/smartthings/zwave-smoke-alarm.src/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Z-wave Smoke Alarm
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO)](https://www.smartthings.com/works-with-smartthings/sensors/first-alert-smoke-detector-and-carbon-monoxide-alarm-zcombo)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery-specification)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Smoke Detector** - measure smoke and optionally carbon monoxide levels
|
||||
* **Carbon Monoxide Detector** - measure carbon monoxide levels
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO) is a Z-wave sleepy device and checks in every 1 hour.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*60 + 2)mins = 122 mins.
|
||||
|
||||
* __122min__ checkInterval
|
||||
|
||||
## Battery Specification
|
||||
|
||||
Two AA 1.5V batteries are required.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
|
||||
* [First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/201581984-First-Alert-Smoke-Detector-and-Carbon-Monoxide-Alarm-ZCOMBO-)
|
||||
@@ -17,10 +17,12 @@ metadata {
|
||||
capability "Carbon Monoxide Detector"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
attribute "alarmState", "string"
|
||||
|
||||
fingerprint deviceId: "0xA100", inClusters: "0x20,0x80,0x70,0x85,0x71,0x72,0x86"
|
||||
fingerprint mfr:"0138", prod:"0001", model:"0002", deviceJoinName: "First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO)"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -51,6 +53,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device checks in every hour, this interval allows us to miss one check-in notification before marking offline
|
||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def results = []
|
||||
if (description.startsWith("Err")) {
|
||||
@@ -65,7 +72,6 @@ def parse(String description) {
|
||||
return results
|
||||
}
|
||||
|
||||
|
||||
def createSmokeOrCOEvents(name, results) {
|
||||
def text = null
|
||||
switch (name) {
|
||||
|
||||
@@ -47,7 +47,7 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
|
||||
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
|
||||
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Polling"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
attribute "thermostatFanState", "string"
|
||||
|
||||
@@ -30,6 +31,7 @@ metadata {
|
||||
|
||||
fingerprint deviceId: "0x08"
|
||||
fingerprint inClusters: "0x43,0x40,0x44,0x31"
|
||||
fingerprint mfr:"0039", prod:"0011", model:"0001", deviceJoinName: "Honeywell Z-Wave Thermostat"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
@@ -123,6 +125,11 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated(){
|
||||
// Device-Watch simply pings if no device events received for 32min(checkInterval)
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def parse(String description)
|
||||
{
|
||||
def map = createEvent(zwaveEvent(zwave.parse(description, [0x42:1, 0x43:2, 0x31: 3])))
|
||||
@@ -393,6 +400,14 @@ def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
|
||||
], delay)
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
log.debug "ping() called"
|
||||
poll()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
|
||||
|
||||
@@ -33,7 +33,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("water", "device.water", width: 2, height: 2) {
|
||||
state "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
|
||||
state "wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
|
||||
state "wet", icon:"st.alarm.water.wet", backgroundColor:"#00A0DC"
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
|
||||
2
devicetypes/zenwithin/zen-thermostat.src/.st-ignore
Normal file
2
devicetypes/zenwithin/zen-thermostat.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
37
devicetypes/zenwithin/zen-thermostat.src/README.md
Normal file
37
devicetypes/zenwithin/zen-thermostat.src/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Zen Thermostat
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Zen Thermostat](https://www.smartthings.com/works-with-smartthings/zen/zen-thermostat)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Thermostat** - allows for the control of a thermostat device
|
||||
* **Temperature Measurement** - get the temperature from a Device that reports current temperature
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - it represents that a Device has attributes
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Zen Thermostat with reporting interval of 5 mins.
|
||||
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
|
||||
|
||||
* __12min__ checkInterval
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
|
||||
Pairing needs to be tried again by placing the device closer to the hub.
|
||||
Other troubleshooting tips are listed as follows:
|
||||
* [Zen Thermostat Troubleshooting:](https://support.smartthings.com/hc/en-us/articles/204356564-Zen-Thermostat)
|
||||
@@ -12,8 +12,9 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
fingerprint profileId: "0104", endpointId: "01", inClusters: "0000,0001,0003,0004,0005,0020,0201,0202,0204,0B05", outClusters: "000A, 0019"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint profileId: "0104", endpointId: "01", inClusters: "0000,0001,0003,0004,0005,0020,0201,0202,0204,0B05", outClusters: "000A, 0019", manufacturer: "Zen Within", model: "Zen-01", deviceJoinName: "Zen Thermostat"
|
||||
|
||||
//attribute "temperatureUnit", "number"
|
||||
|
||||
@@ -467,8 +468,12 @@ def fanAuto() {
|
||||
"st wattr 0x${device.deviceNetworkId} 1 0x202 0 0x30 {05}"
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
// =============== SmartThings Default Fucntions: refresh, configure, poll ===============
|
||||
def refresh()
|
||||
@@ -502,6 +507,7 @@ def poll()
|
||||
|
||||
def configure()
|
||||
{
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
log.debug "configure() - binding & attribute report"
|
||||
[
|
||||
//Set long poll interval to 2 qs
|
||||
@@ -511,7 +517,7 @@ def configure()
|
||||
//Thermostat - Cluster 201
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0x201 {${device.zigbeeId}} {}", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x201 0 0x29 5 300 {3200}",
|
||||
"zcl global send-me-a-report 0x201 0 0x29 5 300 {3200}",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
|
||||
|
||||
"zcl global send-me-a-report 0x201 0x0011 0x29 5 300 {3200}",
|
||||
|
||||
@@ -0,0 +1,794 @@
|
||||
/**
|
||||
* Gideon
|
||||
*
|
||||
* Copyright 2016 Nicola Russo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
definition(
|
||||
name: "Gideon Smart Home",
|
||||
namespace: "gideon.api",
|
||||
author: "Braindrain Solutions ltd",
|
||||
description: "Gideon Smart Home SmartApp allows you to connect and control all of your SmartThings devices through the Gideon app, making your SmartThings devices even smarter.",
|
||||
category: "Family",
|
||||
iconUrl: "http://s33.postimg.org/t77u7y7v3/logo.png",
|
||||
iconX2Url: "http://s33.postimg.org/t77u7y7v3/logo.png",
|
||||
iconX3Url: "http://s33.postimg.org/t77u7y7v3/logo.png",
|
||||
oauth: [displayName: "Gideon Smart Home API app", displayLink: "gideon.ai"])
|
||||
|
||||
preferences {
|
||||
section("Control these contact sensors...") {
|
||||
input "contact", "capability.contactSensor", multiple:true, required:false
|
||||
}
|
||||
section("Control these switch levels...") {
|
||||
input "switchlevels", "capability.switchLevel", multiple:true, required:false
|
||||
}
|
||||
/* section("Control these thermostats...") {
|
||||
input "thermostats", "capability.thermostat", multiple:true, required:false
|
||||
}*/
|
||||
section("Control the color for these devices...") {
|
||||
input "colors", "capability.colorControl", multiple:true, required:false
|
||||
}
|
||||
section("Control the color temperature for these devices...") {
|
||||
input "kelvin", "capability.colorTemperature", multiple:true, required:false
|
||||
}
|
||||
section("Control these switches...") {
|
||||
input "switches", "capability.switch", multiple:true, required:false
|
||||
}
|
||||
section("Control these smoke alarms...") {
|
||||
input "smoke_alarms", "capability.smokeDetector", multiple:true, required:false
|
||||
}
|
||||
section("Control these window shades...") {
|
||||
input "shades", "capability.windowShade", multiple:true, required:false
|
||||
}
|
||||
section("Control these garage doors...") {
|
||||
input "garage", "capability.garageDoorControl", multiple:true, required:false
|
||||
}
|
||||
section("Control these water sensors...") {
|
||||
input "water_sensors", "capability.waterSensor", multiple:true, required:false
|
||||
}
|
||||
section("Control these motion sensors...") {
|
||||
input "motions", "capability.motionSensor", multiple:true, required:false
|
||||
}
|
||||
section("Control these presence sensors...") {
|
||||
input "presence_sensors", "capability.presenceSensor", multiple:true, required:false
|
||||
}
|
||||
section("Control these outlets...") {
|
||||
input "outlets", "capability.outlet", multiple:true, required:false
|
||||
}
|
||||
section("Control these power meters...") {
|
||||
input "meters", "capability.powerMeter", multiple:true, required:false
|
||||
}
|
||||
section("Control these locks...") {
|
||||
input "locks", "capability.lock", multiple:true, required:false
|
||||
}
|
||||
section("Control these temperature sensors...") {
|
||||
input "temperature_sensors", "capability.temperatureMeasurement", multiple:true, required:false
|
||||
}
|
||||
section("Control these batteries...") {
|
||||
input "batteries", "capability.battery", multiple:true, required:false
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
|
||||
unsubscribe()
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
}
|
||||
|
||||
private device(it, type) {
|
||||
it ? [id: it.id, label: it.label, type: type] : null
|
||||
}
|
||||
|
||||
//API Mapping
|
||||
mappings {
|
||||
path("/getalldevices") {
|
||||
action: [
|
||||
GET: "getAllDevices"
|
||||
]
|
||||
}
|
||||
/*
|
||||
path("/thermostat/setcool/:id/:temp") {
|
||||
action: [
|
||||
GET: "setCoolTemp"
|
||||
]
|
||||
}
|
||||
path("/thermostat/setheat/:id/:temp") {
|
||||
action: [
|
||||
GET: "setHeatTemp"
|
||||
]
|
||||
}
|
||||
path("/thermostat/setfanmode/:id/:mode") {
|
||||
action: [
|
||||
GET: "setFanMode"
|
||||
]
|
||||
}
|
||||
path("/thermostat/setmode/:id/:mode") {
|
||||
action: [
|
||||
GET: "setThermostatMode"
|
||||
]
|
||||
}
|
||||
path("/thermostat/:id") {
|
||||
action: [
|
||||
GET: "getThermostatStatus"
|
||||
]
|
||||
}
|
||||
*/
|
||||
path("/light/dim/:id/:dim") {
|
||||
action: [
|
||||
GET: "setLevelStatus"
|
||||
]
|
||||
}
|
||||
path("/light/kelvin/:id/:kelvin") {
|
||||
action: [
|
||||
GET: "setKelvin"
|
||||
]
|
||||
}
|
||||
path("/colorlight/:id/:hue/:sat") {
|
||||
action: [
|
||||
GET: "setColor"
|
||||
]
|
||||
}
|
||||
path("/light/status/:id") {
|
||||
action: [
|
||||
GET: "getLightStatus"
|
||||
]
|
||||
}
|
||||
path("/light/on/:id") {
|
||||
action: [
|
||||
GET: "turnOnLight"
|
||||
]
|
||||
}
|
||||
path("/light/off/:id") {
|
||||
action: [
|
||||
GET: "turnOffLight"
|
||||
]
|
||||
}
|
||||
path("/doorlocks/lock/:id") {
|
||||
action: [
|
||||
GET: "lockDoorLock"
|
||||
]
|
||||
}
|
||||
path("/doorlocks/unlock/:id") {
|
||||
action: [
|
||||
GET: "unlockDoorLock"
|
||||
]
|
||||
}
|
||||
path("/doorlocks/:id") {
|
||||
action: [
|
||||
GET: "getDoorLockStatus"
|
||||
]
|
||||
}
|
||||
path("/contacts/:id") {
|
||||
action: [
|
||||
GET: "getContactStatus"
|
||||
]
|
||||
}
|
||||
path("/smoke/:id") {
|
||||
action: [
|
||||
GET: "getSmokeStatus"
|
||||
]
|
||||
}
|
||||
path("/shades/open/:id") {
|
||||
action: [
|
||||
GET: "openShade"
|
||||
]
|
||||
}
|
||||
path("/shades/preset/:id") {
|
||||
action: [
|
||||
GET: "presetShade"
|
||||
]
|
||||
}
|
||||
path("/shades/close/:id") {
|
||||
action: [
|
||||
GET: "closeShade"
|
||||
]
|
||||
}
|
||||
path("/shades/:id") {
|
||||
action: [
|
||||
GET: "getShadeStatus"
|
||||
]
|
||||
}
|
||||
path("/garage/open/:id") {
|
||||
action: [
|
||||
GET: "openGarage"
|
||||
]
|
||||
}
|
||||
path("/garage/close/:id") {
|
||||
action: [
|
||||
GET: "closeGarage"
|
||||
]
|
||||
}
|
||||
path("/garage/:id") {
|
||||
action: [
|
||||
GET: "getGarageStatus"
|
||||
]
|
||||
}
|
||||
path("/watersensors/:id") {
|
||||
action: [
|
||||
GET: "getWaterSensorStatus"
|
||||
]
|
||||
}
|
||||
path("/tempsensors/:id") {
|
||||
action: [
|
||||
GET: "getTempSensorsStatus"
|
||||
]
|
||||
}
|
||||
path("/meters/:id") {
|
||||
action: [
|
||||
GET: "getMeterStatus"
|
||||
]
|
||||
}
|
||||
path("/batteries/:id") {
|
||||
action: [
|
||||
GET: "getBatteryStatus"
|
||||
]
|
||||
}
|
||||
path("/presences/:id") {
|
||||
action: [
|
||||
GET: "getPresenceStatus"
|
||||
]
|
||||
}
|
||||
path("/motions/:id") {
|
||||
action: [
|
||||
GET: "getMotionStatus"
|
||||
]
|
||||
}
|
||||
path("/outlets/:id") {
|
||||
action: [
|
||||
GET: "getOutletStatus"
|
||||
]
|
||||
}
|
||||
path("/outlets/turnon/:id") {
|
||||
action: [
|
||||
GET: "turnOnOutlet"
|
||||
]
|
||||
}
|
||||
path("/outlets/turnoff/:id") {
|
||||
action: [
|
||||
GET: "turnOffOutlet"
|
||||
]
|
||||
}
|
||||
path("/switches/turnon/:id") {
|
||||
action: [
|
||||
GET: "turnOnSwitch"
|
||||
]
|
||||
}
|
||||
path("/switches/turnoff/:id") {
|
||||
action: [
|
||||
GET: "turnOffSwitch"
|
||||
]
|
||||
}
|
||||
path("/switches/:id") {
|
||||
action: [
|
||||
GET: "getSwitchStatus"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
//API Methods
|
||||
def getAllDevices() {
|
||||
def locks_list = locks.collect{device(it,"Lock")}
|
||||
/*def thermo_list = thermostats.collect{device(it,"Thermostat")}*/
|
||||
def colors_list = colors.collect{device(it,"Color")}
|
||||
def kelvin_list = kelvin.collect{device(it,"Kelvin")}
|
||||
def contact_list = contact.collect{device(it,"Contact Sensor")}
|
||||
def smokes_list = smoke_alarms.collect{device(it,"Smoke Alarm")}
|
||||
def shades_list = shades.collect{device(it,"Window Shade")}
|
||||
def garage_list = garage.collect{device(it,"Garage Door")}
|
||||
def water_sensors_list = water_sensors.collect{device(it,"Water Sensor")}
|
||||
def presences_list = presence_sensors.collect{device(it,"Presence")}
|
||||
def motions_list = motions.collect{device(it,"Motion")}
|
||||
def outlets_list = outlets.collect{device(it,"Outlet")}
|
||||
def switches_list = switches.collect{device(it,"Switch")}
|
||||
def switchlevels_list = switchlevels.collect{device(it,"Switch Level")}
|
||||
def temp_list = temperature_sensors.collect{device(it,"Temperature")}
|
||||
def meters_list = meters.collect{device(it,"Power Meters")}
|
||||
def battery_list = batteries.collect{device(it,"Batteries")}
|
||||
return outlets_list + kelvin_list + colors_list + switchlevels_list + smokes_list + contact_list + water_sensors_list + shades_list + garage_list + locks_list + presences_list + motions_list + switches_list + temp_list + meters_list + battery_list
|
||||
}
|
||||
|
||||
//thermostat
|
||||
/*
|
||||
def setCoolTemp() {
|
||||
def device = thermostats.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
if(device.hasCommand("setCoolingSetpoint")) {
|
||||
device.setCoolingSetpoint(params.temp.toInteger());
|
||||
return [result_action: "200"]
|
||||
}
|
||||
else {
|
||||
httpError(510, "Not supported!")
|
||||
}
|
||||
}
|
||||
}
|
||||
def setHeatTemp() {
|
||||
def device = thermostats.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
if(device.hasCommand("setHeatingSetpoint")) {
|
||||
device.setHeatingSetpoint(params.temp.toInteger());
|
||||
return [result_action: "200"]
|
||||
}
|
||||
else {
|
||||
httpError(510, "Not supported!")
|
||||
}
|
||||
}
|
||||
}
|
||||
def setFanMode() {
|
||||
def device = thermostats.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
if(device.hasCommand("setThermostatFanMode")) {
|
||||
device.setThermostatFanMode(params.mode);
|
||||
return [result_action: "200"]
|
||||
}
|
||||
else {
|
||||
httpError(510, "Not supported!")
|
||||
}
|
||||
}
|
||||
}
|
||||
def setThermostatMode() {
|
||||
def device = thermostats.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
if(device.hasCommand("setThermostatMode")) {
|
||||
device.setThermostatMode(params.mode);
|
||||
return [result_action: "200"]
|
||||
}
|
||||
else {
|
||||
httpError(510, "Not supported!")
|
||||
}
|
||||
}
|
||||
}
|
||||
def getThermostatStatus() {
|
||||
def device = thermostats.find{ it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
return [ThermostatOperatingState: device.currentValue('thermostatOperatingState'), ThermostatSetpoint: device.currentValue('thermostatSetpoint'),
|
||||
ThermostatFanMode: device.currentValue('thermostatFanMode'), ThermostatMode: device.currentValue('thermostatMode')]
|
||||
}
|
||||
}
|
||||
*/
|
||||
//light
|
||||
def turnOnLight() {
|
||||
def device = switches.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
device.on();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def turnOffLight() {
|
||||
def device = switches.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
device.off();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def getLightStatus() {
|
||||
def device = switches.find{ it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
return [Status: device.currentValue('switch'), Dim: getLevelStatus(params.id), Color: getColorStatus(params.id), Kelvin: getKelvinStatus(params.id)]
|
||||
}
|
||||
}
|
||||
|
||||
//color control
|
||||
def setColor() {
|
||||
def device = colors.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
def map = [hue:params.hue.toInteger(), saturation:params.sat.toInteger()]
|
||||
|
||||
device.setColor(map);
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def getColorStatus(id) {
|
||||
def device = colors.find { it.id == id }
|
||||
if (!device) {
|
||||
return [Color: "none"]
|
||||
} else {
|
||||
return [hue: device.currentValue('hue'), saturation: device.currentValue('saturation')]
|
||||
}
|
||||
}
|
||||
|
||||
//kelvin control
|
||||
def setKelvin() {
|
||||
def device = kelvin.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.setColorTemperature(params.kelvin.toInteger());
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def getKelvinStatus(id) {
|
||||
def device = kelvin.find { it.id == id }
|
||||
if (!device) {
|
||||
return [kelvin: "none"]
|
||||
} else {
|
||||
return [kelvin: device.currentValue('colorTemperature')]
|
||||
}
|
||||
}
|
||||
|
||||
//switch level
|
||||
def getLevelStatus() {
|
||||
def device = switchlevels.find { it.id == params.id }
|
||||
if (!device) {
|
||||
[Level: "No dimmer"]
|
||||
} else {
|
||||
return [Level: device.currentValue('level')]
|
||||
}
|
||||
}
|
||||
|
||||
def getLevelStatus(id) {
|
||||
def device = switchlevels.find { it.id == id }
|
||||
if (!device) {
|
||||
[Level: "No dimmer"]
|
||||
} else {
|
||||
return [Level: device.currentValue('level')]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def setLevelStatus() {
|
||||
def device = switchlevels.find { it.id == params.id }
|
||||
def level = params.dim
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
device.setLevel(level.toInteger())
|
||||
return [result_action: "200", Level: device.currentValue('level')]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//contact sensors
|
||||
def getContactStatus() {
|
||||
def device = contact.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
def args = getTempSensorsStatus(device.id)
|
||||
return [Device_state: device.currentValue('contact')] + args
|
||||
}
|
||||
}
|
||||
|
||||
//smoke detectors
|
||||
def getSmokeStatus() {
|
||||
def device = smoke_alarms.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
def bat = getBatteryStatus(device.id)
|
||||
return [Device_state: device.currentValue('smoke')] + bat
|
||||
}
|
||||
}
|
||||
|
||||
//garage
|
||||
def getGarageStatus() {
|
||||
def device = garage.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
return [Device_state: device.currentValue('door')]
|
||||
}
|
||||
}
|
||||
|
||||
def openGarage() {
|
||||
def device = garage.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.open();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def closeGarage() {
|
||||
def device = garage.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.close();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
//shades
|
||||
def getShadeStatus() {
|
||||
def device = shades.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
return [Device_state: device.currentValue('windowShade')]
|
||||
}
|
||||
}
|
||||
|
||||
def openShade() {
|
||||
def device = shades.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.open();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def presetShade() {
|
||||
def device = shades.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.presetPosition();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def closeShade() {
|
||||
def device = shades.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.close();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
//water sensor
|
||||
def getWaterSensorStatus() {
|
||||
def device = water_sensors.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
def bat = getBatteryStatus(device.id)
|
||||
return [Device_state: device.currentValue('water')] + bat
|
||||
}
|
||||
}
|
||||
//batteries
|
||||
def getBatteryStatus() {
|
||||
def device = batteries.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
return [Device_state: device.latestValue("battery")]
|
||||
}
|
||||
}
|
||||
|
||||
def getBatteryStatus(id) {
|
||||
def device = batteries.find { it.id == id }
|
||||
if (!device) {
|
||||
return []
|
||||
} else {
|
||||
return [battery_state: device.latestValue("battery")]
|
||||
}
|
||||
}
|
||||
|
||||
//LOCKS
|
||||
def getDoorLockStatus() {
|
||||
def device = locks.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
def bat = getBatteryStatus(device.id)
|
||||
return [Device_state: device.currentValue('lock')] + bat
|
||||
}
|
||||
}
|
||||
|
||||
def lockDoorLock() {
|
||||
def device = locks.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.lock();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def unlockDoorLock() {
|
||||
def device = locks.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.unlock();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
//PRESENCE
|
||||
def getPresenceStatus() {
|
||||
|
||||
def device = presence_sensors.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
def bat = getBatteryStatus(device.id)
|
||||
return [Device_state: device.currentValue('presence')] + bat
|
||||
}
|
||||
}
|
||||
|
||||
//MOTION
|
||||
def getMotionStatus() {
|
||||
|
||||
def device = motions.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
def args = getTempSensorsStatus(device.id)
|
||||
return [Device_state: device.currentValue('motion')] + args
|
||||
}
|
||||
}
|
||||
|
||||
//OUTLET
|
||||
def getOutletStatus() {
|
||||
|
||||
def device = outlets.find { it.id == params.id }
|
||||
if (!device) {
|
||||
device = switches.find { it.id == params.id }
|
||||
if(!device) {
|
||||
httpError(404, "Device not found")
|
||||
}
|
||||
}
|
||||
def watt = getMeterStatus(device.id)
|
||||
|
||||
return [Device_state: device.currentValue('switch')] + watt
|
||||
}
|
||||
|
||||
def getMeterStatus() {
|
||||
|
||||
def device = meters.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
return [Device_id: device.id, Device_type: device.type, Current_watt: device.currentValue("power")]
|
||||
}
|
||||
}
|
||||
|
||||
def getMeterStatus(id) {
|
||||
|
||||
def device = meters.find { it.id == id }
|
||||
if (!device) {
|
||||
return []
|
||||
} else {
|
||||
return [Current_watt: device.currentValue("power")]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def turnOnOutlet() {
|
||||
def device = outlets.find { it.id == params.id }
|
||||
if (!device) {
|
||||
device = switches.find { it.id == params.id }
|
||||
if(!device) {
|
||||
httpError(404, "Device not found")
|
||||
}
|
||||
}
|
||||
|
||||
device.on();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
|
||||
def turnOffOutlet() {
|
||||
def device = outlets.find { it.id == params.id }
|
||||
if (!device) {
|
||||
device = switches.find { it.id == params.id }
|
||||
if(!device) {
|
||||
httpError(404, "Device not found")
|
||||
}
|
||||
}
|
||||
|
||||
device.off();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
|
||||
//SWITCH
|
||||
def getSwitchStatus() {
|
||||
def device = switches.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
return [Device_state: device.currentValue('switch'), Dim: getLevelStatus(params.id)]
|
||||
}
|
||||
}
|
||||
|
||||
def turnOnSwitch() {
|
||||
def device = switches.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.on();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
def turnOffSwitch() {
|
||||
def device = switches.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
|
||||
device.off();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//TEMPERATURE
|
||||
def getTempSensorsStatus() {
|
||||
def device = temperature_sensors.find { it.id == params.id }
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
def bat = getBatteryStatus(device.id)
|
||||
def scale = [Scale: location.temperatureScale]
|
||||
return [Device_state: device.currentValue('temperature')] + scale + bat
|
||||
}
|
||||
}
|
||||
|
||||
def getTempSensorsStatus(id) {
|
||||
def device = temperature_sensors.find { it.id == id }
|
||||
if (!device) {
|
||||
return []
|
||||
} else {
|
||||
def bat = getBatteryStatus(device.id)
|
||||
return [temperature: device.currentValue('temperature')] + bat
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ definition(
|
||||
* garageDoors | door | open, close | unknown, closed, open, closing, opening
|
||||
* cameras | image | take | <String>
|
||||
* thermostats | thermostat | setHeatingSetpoint, | temperature, heatingSetpoint, coolingSetpoint,
|
||||
* | | setCoolingSetpoint, | thermostatSetpoint, thermostatMode,
|
||||
* | | setCoolingSetpoint, | thermostatSetpoint, thermostatMode,
|
||||
* | | off, heat, cool, auto,| thermostatFanMode, thermostatOperatingState
|
||||
* | | emergencyHeat, |
|
||||
* | | setThermostatMode, |
|
||||
@@ -55,7 +55,7 @@ preferences {
|
||||
input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false
|
||||
input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false
|
||||
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
|
||||
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false
|
||||
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false
|
||||
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false
|
||||
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false
|
||||
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false
|
||||
@@ -66,54 +66,48 @@ preferences {
|
||||
|
||||
def getInputs() {
|
||||
def inputList = []
|
||||
inputList += contactSensors ?: []
|
||||
inputList += garageDoors ?: []
|
||||
inputList += locks ?: []
|
||||
inputList += cameras ?: []
|
||||
inputList += motionSensors ?: []
|
||||
inputList += presenceSensors ?: []
|
||||
inputList += switches ?: []
|
||||
inputList += thermostats ?: []
|
||||
inputList += waterSensors ?: []
|
||||
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("/subscriptionURL/:url") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
PUT: "updateEndpointURL"
|
||||
]
|
||||
}
|
||||
path("/connectionId/:connId") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
PUT: "updateConnectionId"
|
||||
]
|
||||
}
|
||||
path("/devices") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
GET: "getDevices"
|
||||
]
|
||||
}
|
||||
path("/devices/:id") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
GET: "getDevice"
|
||||
]
|
||||
}
|
||||
path("/update/:id") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
PUT: "updateDevice"
|
||||
]
|
||||
}
|
||||
path("/subscription/:id") {
|
||||
action:
|
||||
[
|
||||
POST : "registerDeviceChange",
|
||||
action: [
|
||||
POST: "registerDeviceChange",
|
||||
DELETE: "unregisterDeviceChange"
|
||||
]
|
||||
}
|
||||
@@ -145,7 +139,7 @@ def registerSubscriptions() {
|
||||
def registerChangeHandler(myList) {
|
||||
myList.each { myDevice ->
|
||||
def theAtts = myDevice.supportedAttributes
|
||||
theAtts.each { att ->
|
||||
theAtts.each {att ->
|
||||
subscribe(myDevice, att.name, eventHandler)
|
||||
log.info "Registering ${myDevice.displayName}.${att.name}"
|
||||
}
|
||||
@@ -157,7 +151,7 @@ def registerDeviceChange() {
|
||||
def myDevice = findDevice(params.id)
|
||||
def theAtts = myDevice.supportedAttributes
|
||||
try {
|
||||
theAtts.each { att ->
|
||||
theAtts.each {att ->
|
||||
subscribe(myDevice, att.name, eventHandler)
|
||||
log.info "Registering ${myDevice.displayName}.${att.name}"
|
||||
}
|
||||
@@ -186,16 +180,20 @@ def eventHandler(evt) {
|
||||
def evt_name = evt.name
|
||||
def evt_device = evt.device
|
||||
def evt_deviceType = getDeviceType(evt_device);
|
||||
def deviceInfo
|
||||
|
||||
if(evt_deviceType == "thermostat")
|
||||
{
|
||||
deviceInfo = [name: evt_device.displayName, id: evt_device.id, status:evt_device.getStatus(), deviceType:evt_deviceType, manufacturer:evt_device.getManufacturerName(), model:evt_device.getModelName(), attributes: deviceAttributeList(evt_device), locationMode: getLocationModeInfo()]
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceInfo = [name: evt_device.displayName, id: evt_device.id, status:evt_device.getStatus(), deviceType:evt_deviceType, manufacturer:evt_device.getManufacturerName(), model:evt_device.getModelName(), attributes: deviceAttributeList(evt_device)]
|
||||
}
|
||||
|
||||
def params = [
|
||||
uri : "${state.endpointURL}/${state.connectionId}",
|
||||
body: [
|
||||
name : evt_device.displayName,
|
||||
id : evt_device.id,
|
||||
deviceType : evt_deviceType,
|
||||
manufacturer: evt_device.getManufacturerName(),
|
||||
model : evt_device.getModelName(),
|
||||
attributes : deviceAttributeList(evt_device)
|
||||
]
|
||||
uri: "${state.endpointURL}/${state.connectionId}",
|
||||
body: [ deviceInfo ]
|
||||
]
|
||||
try {
|
||||
log.trace "POST URI: ${params.uri}"
|
||||
@@ -230,10 +228,13 @@ def getDevices() {
|
||||
def deviceData = []
|
||||
inputs?.each {
|
||||
def deviceType = getDeviceType(it)
|
||||
if (deviceType == "thermostat") {
|
||||
deviceData << [name: it.displayName, id: it.id, deviceType: deviceType, manufacturer: it.getManufacturerName(), model: it.getModelName(), attributes: deviceAttributeList(it), locationMode: getLocationModeInfo()]
|
||||
} else {
|
||||
deviceData << [name: it.displayName, id: it.id, deviceType: deviceType, manufacturer: it.getManufacturerName(), model: it.getModelName(), attributes: deviceAttributeList(it)]
|
||||
if(deviceType == "thermostat")
|
||||
{
|
||||
deviceData << [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it), locationMode: getLocationModeInfo()]
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceData << [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,10 +247,13 @@ def getDevice() {
|
||||
def it = findDevice(params.id)
|
||||
def deviceType = getDeviceType(it)
|
||||
def device
|
||||
if (deviceType == "thermostat") {
|
||||
device = [name: it.displayName, id: it.id, deviceType: deviceType, manufacturer: it.getManufacturerName(), model: it.getModelName(), attributes: deviceAttributeList(it), locationMode: getLocationModeInfo()]
|
||||
} else {
|
||||
device = [name: it.displayName, id: it.id, deviceType: deviceType, manufacturer: it.getManufacturerName(), model: it.getModelName(), attributes: deviceAttributeList(it)]
|
||||
if(deviceType == "thermostat")
|
||||
{
|
||||
device = [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it), locationMode: getLocationModeInfo()]
|
||||
}
|
||||
else
|
||||
{
|
||||
device = [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it)]
|
||||
}
|
||||
log.debug "getDevice, return: ${device}"
|
||||
return device
|
||||
@@ -261,18 +265,18 @@ void updateDevice() {
|
||||
request.JSON.each {
|
||||
def command = it.key
|
||||
def value = it.value
|
||||
if (command) {
|
||||
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 }) {
|
||||
if (location.modes?.find {it.name == value}) {
|
||||
location.setMode(value)
|
||||
}
|
||||
} else if (command == "thermostatSetpoint") {
|
||||
switch (device.currentThermostatMode) {
|
||||
}else if (command == "thermostatSetpoint"){
|
||||
switch(device.currentThermostatMode){
|
||||
case "cool":
|
||||
log.info "Update: ${device.displayName}, [${command}, ${value}]"
|
||||
device.setCoolingSetpoint(value)
|
||||
@@ -286,7 +290,7 @@ void updateDevice() {
|
||||
httpError(501, "this mode: ${device.currentThermostatMode} does not allow changing thermostat setpoint.")
|
||||
break
|
||||
}
|
||||
} else if (!device) {
|
||||
}else if (!device) {
|
||||
log.error "updateDevice, Device not found"
|
||||
httpError(404, "Device not found")
|
||||
} else if (!device.hasCommand(command)) {
|
||||
@@ -296,11 +300,11 @@ void updateDevice() {
|
||||
if (command == "setColor") {
|
||||
log.info "Update: ${device.displayName}, [${command}, ${value}]"
|
||||
device."$command"(hex: value)
|
||||
} else if (value.isNumber()) {
|
||||
} else if(value.isNumber()) {
|
||||
def intValue = value as Integer
|
||||
log.info "Update: ${device.displayName}, [${command}, ${intValue}(int)]"
|
||||
device."$command"(intValue)
|
||||
} else if (value) {
|
||||
} else if (value){
|
||||
log.info "Update: ${device.displayName}, [${command}, ${value}]"
|
||||
device."$command"(value)
|
||||
} else {
|
||||
@@ -322,19 +326,28 @@ private getLocationModeInfo() {
|
||||
//Map each device to a type given it's capabilities
|
||||
private getDeviceType(device) {
|
||||
def deviceType
|
||||
def caps = device.capabilities
|
||||
log.debug "capabilities: [${device}, ${caps}]"
|
||||
def capabilities = device.capabilities
|
||||
log.debug "capabilities: [${device}, ${capabilities}]"
|
||||
log.debug "supported commands: [${device}, ${device.supportedCommands}]"
|
||||
caps.each {
|
||||
switch (it.name.toLowerCase()) {
|
||||
|
||||
//Loop through the device capability list to determine the device type.
|
||||
capabilities.each {capability ->
|
||||
switch(capability.name.toLowerCase())
|
||||
{
|
||||
case "switch":
|
||||
deviceType = "switch"
|
||||
if (caps.any { it.name.toLowerCase() == "power meter" }) {
|
||||
return deviceType
|
||||
}
|
||||
if (caps.any { it.name.toLowerCase() == "switch level" }) {
|
||||
deviceType = "light"
|
||||
return deviceType
|
||||
|
||||
//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 "contact sensor":
|
||||
@@ -375,16 +388,16 @@ private findDevice(deviceId) {
|
||||
|
||||
//Return a list of device attributes
|
||||
private deviceAttributeList(device) {
|
||||
device.supportedAttributes.collectEntries { attribute ->
|
||||
device.supportedAttributes.collectEntries { attribute->
|
||||
try {
|
||||
[(attribute.name): device.currentValue(attribute.name)]
|
||||
} catch (e) {
|
||||
[(attribute.name): null]
|
||||
[ (attribute.name): device.currentValue(attribute.name) ]
|
||||
} catch(e) {
|
||||
[ (attribute.name): null ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Map device command and value.
|
||||
//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) {
|
||||
@@ -414,7 +427,7 @@ private mapDeviceCommands(command, value) {
|
||||
resultCommand = "setSaturation"
|
||||
resultValue = value
|
||||
break
|
||||
case "ct":
|
||||
case "colorTemperature":
|
||||
resultCommand = "setColorTemperature"
|
||||
resultValue = value
|
||||
break
|
||||
@@ -451,7 +464,8 @@ private mapDeviceCommands(command, value) {
|
||||
if (value == 1 || value == "1" || value == "lock") {
|
||||
resultCommand = "lock"
|
||||
resultValue = ""
|
||||
} else if (value == 0 || value == "0" || value == "unlock") {
|
||||
}
|
||||
else if (value == 0 || value == "0" || value == "unlock") {
|
||||
resultCommand = "unlock"
|
||||
resultValue = ""
|
||||
}
|
||||
@@ -460,6 +474,5 @@ private mapDeviceCommands(command, value) {
|
||||
break
|
||||
}
|
||||
|
||||
return [resultCommand, resultValue]
|
||||
return [resultCommand,resultValue]
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,12 +15,13 @@
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
include 'localization'
|
||||
|
||||
definition(
|
||||
name: "Hue (Connect)",
|
||||
namespace: "smartthings",
|
||||
author: "SmartThings",
|
||||
description: "Allows you to connect your Philips Hue lights with SmartThings and control them from your Things area or Dashboard in the SmartThings Mobile app. Adjust colors by going to the Thing detail screen for your Hue lights (tap the gear on Hue tiles).\n\nPlease update your Hue Bridge first, outside of the SmartThings app, using the Philips Hue app.",
|
||||
description: "Allows you to connect your Philips Hue lights with SmartThings and control them from your Things area or Dashboard in the SmartThings Mobile app. Please update your Hue Bridge first, outside of the SmartThings app, using the Philips Hue app.",
|
||||
category: "SmartThings Labs",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/hue.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/hue@2x.png",
|
||||
@@ -28,11 +29,11 @@ definition(
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name:"mainPage", title:"Hue Device Setup", content:"mainPage", refreshTimeout:5)
|
||||
page(name:"bridgeDiscovery", title:"Hue Bridge Discovery", content:"bridgeDiscovery", refreshTimeout:5)
|
||||
page(name:"bridgeDiscoveryFailed", title:"Bridge Discovery Failed", content:"bridgeDiscoveryFailed", refreshTimeout:0)
|
||||
page(name:"bridgeBtnPush", title:"Linking with your Hue", content:"bridgeLinking", refreshTimeout:5)
|
||||
page(name:"bulbDiscovery", title:"Hue Device Setup", content:"bulbDiscovery", refreshTimeout:5)
|
||||
page(name: "mainPage", title: "", content: "mainPage", refreshTimeout: 5)
|
||||
page(name: "bridgeDiscovery", title: "", content: "bridgeDiscovery", refreshTimeout: 5)
|
||||
page(name: "bridgeDiscoveryFailed", title: "", content: "bridgeDiscoveryFailed", refreshTimeout: 0)
|
||||
page(name: "bridgeBtnPush", title: "", content: "bridgeLinking", refreshTimeout: 5)
|
||||
page(name: "bulbDiscovery", title: "", content: "bulbDiscovery", refreshTimeout: 5)
|
||||
}
|
||||
|
||||
def mainPage() {
|
||||
@@ -47,15 +48,14 @@ def mainPage() {
|
||||
}
|
||||
}
|
||||
|
||||
def bridgeDiscovery(params=[:])
|
||||
{
|
||||
def bridgeDiscovery(params = [:]) {
|
||||
def bridges = bridgesDiscovered()
|
||||
int bridgeRefreshCount = !state.bridgeRefreshCount ? 0 : state.bridgeRefreshCount as int
|
||||
state.bridgeRefreshCount = bridgeRefreshCount + 1
|
||||
def refreshInterval = 3
|
||||
|
||||
def options = bridges ?: []
|
||||
def numFound = options.size() ?: 0
|
||||
def numFound = options.size() ?: "0"
|
||||
if (numFound == 0) {
|
||||
if (state.bridgeRefreshCount == 25) {
|
||||
log.trace "Cleaning old bridges memory"
|
||||
@@ -75,25 +75,26 @@ def bridgeDiscovery(params=[:])
|
||||
ssdpSubscribe()
|
||||
|
||||
//bridge discovery request every 15 //25 seconds
|
||||
if((bridgeRefreshCount % 5) == 0) {
|
||||
if ((bridgeRefreshCount % 5) == 0) {
|
||||
discoverBridges()
|
||||
}
|
||||
|
||||
//setup.xml request every 3 seconds except on discoveries
|
||||
if(((bridgeRefreshCount % 3) == 0) && ((bridgeRefreshCount % 5) != 0)) {
|
||||
if (((bridgeRefreshCount % 3) == 0) && ((bridgeRefreshCount % 5) != 0)) {
|
||||
verifyHueBridges()
|
||||
}
|
||||
|
||||
return dynamicPage(name:"bridgeDiscovery", title:"Discovery Started!", nextPage:"bridgeBtnPush", refreshInterval:refreshInterval, uninstall: true) {
|
||||
section("Please wait while we discover your Hue Bridge. Kindly note that you must first configure your Hue Bridge and Lights using the Philips Hue application. " +
|
||||
"Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") {
|
||||
input "selectedHue", "enum", required:false, title:"Select Hue Bridge (${numFound} found)", multiple:false, options:options, submitOnChange: true
|
||||
return dynamicPage(name: "bridgeDiscovery", title: "Discovery Started!", nextPage: "bridgeBtnPush", refreshInterval: refreshInterval, uninstall: true) {
|
||||
section("Please wait while we discover your Hue Bridge. Kindly note that you must first configure your Hue Bridge and Lights using the Philips Hue application. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") {
|
||||
|
||||
|
||||
input(name: "selectedHue", type: "enum", required: false, title: "Select Hue Bridge ({{numFound}} found)", messageArgs: [numFound: numFound], multiple: false, options: options, submitOnChange: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def bridgeDiscoveryFailed() {
|
||||
return dynamicPage(name:"bridgeDiscoveryFailed", title: "Bridge Discovery Failed", nextPage: "bridgeDiscovery") {
|
||||
return dynamicPage(name: "bridgeDiscoveryFailed", title: "Bridge Discovery Failed!", nextPage: "bridgeDiscovery") {
|
||||
section("Failed to discover any Hue Bridges. Please confirm that the Hue Bridge is connected to the same network as your SmartThings Hub, and that it has power.") {
|
||||
}
|
||||
}
|
||||
@@ -109,12 +110,12 @@ def bridgeLinking() {
|
||||
def paragraphText
|
||||
if (selectedHue) {
|
||||
if (state.refreshUsernameNeeded) {
|
||||
paragraphText = "The current Hue username is invalid.\n\nPlease press the button on your Hue Bridge to re-link. "
|
||||
paragraphText = "The current Hue username is invalid. Please press the button on your Hue Bridge to relink."
|
||||
} else {
|
||||
paragraphText = "Press the button on your Hue Bridge to setup a link. "
|
||||
paragraphText = "Press the button on your Hue Bridge to setup a link."
|
||||
}
|
||||
} else {
|
||||
paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next."
|
||||
paragraphText = "You haven't selected a Hue Bridge, please Press 'Done' and select one before clicking next."
|
||||
}
|
||||
if (state.username) { //if discovery worked
|
||||
if (state.refreshUsernameNeeded) {
|
||||
@@ -127,13 +128,13 @@ def bridgeLinking() {
|
||||
paragraphText = "Linking to your hub was a success! Please click 'Next'!"
|
||||
}
|
||||
|
||||
if((linkRefreshcount % 2) == 0 && !state.username) {
|
||||
if ((linkRefreshcount % 2) == 0 && !state.username) {
|
||||
sendDeveloperReq()
|
||||
}
|
||||
|
||||
return dynamicPage(name:"bridgeBtnPush", title:title, nextPage:nextPage, refreshInterval:refreshInterval) {
|
||||
return dynamicPage(name: "bridgeBtnPush", title: title, nextPage: nextPage, refreshInterval: refreshInterval) {
|
||||
section("") {
|
||||
paragraph """${paragraphText}"""
|
||||
paragraph "$paragraphText"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,18 +150,18 @@ def bulbDiscovery() {
|
||||
def allLightsFound = bulbsDiscovered() ?: [:]
|
||||
|
||||
// List lights currently not added to the user (editable)
|
||||
def newLights = allLightsFound.findAll {getChildDevice(it.key) == null} ?: [:]
|
||||
newLights = newLights.sort {it.value.toLowerCase()}
|
||||
def newLights = allLightsFound.findAll { getChildDevice(it.key) == null } ?: [:]
|
||||
newLights = newLights.sort { it.value.toLowerCase() }
|
||||
|
||||
// List lights already added to the user (not editable)
|
||||
def existingLights = allLightsFound.findAll {getChildDevice(it.key) != null} ?: [:]
|
||||
existingLights = existingLights.sort {it.value.toLowerCase()}
|
||||
def existingLights = allLightsFound.findAll { getChildDevice(it.key) != null } ?: [:]
|
||||
existingLights = existingLights.sort { it.value.toLowerCase() }
|
||||
|
||||
def numFound = newLights.size() ?: 0
|
||||
if (numFound == 0)
|
||||
def numFound = newLights.size() ?: "0"
|
||||
if (numFound == "0")
|
||||
app.updateSetting("selectedBulbs", "")
|
||||
|
||||
if((bulbRefreshCount % 5) == 0) {
|
||||
if ((bulbRefreshCount % 5) == 0) {
|
||||
discoverHueBulbs()
|
||||
}
|
||||
def selectedBridge = state.bridges.find { key, value -> value?.serialNumber?.equalsIgnoreCase(selectedHue) }
|
||||
@@ -178,14 +179,14 @@ def bulbDiscovery() {
|
||||
}
|
||||
}
|
||||
|
||||
if (bulbRefreshCount > 200 && numFound == 0) {
|
||||
def existingLightsSize = "${existingLights.size()}"
|
||||
if (bulbRefreshCount > 200 && numFound == "0") {
|
||||
// Time out after 10 minutes
|
||||
state.inBulbDiscovery = false
|
||||
bulbRefreshCount = 0
|
||||
return dynamicPage(name:"bulbDiscovery", title:"Light Discovery Failed!", nextPage:"", refreshInterval:0, install:true, uninstall: true) {
|
||||
section("Failed to discover any lights, please try again later. Click Done to exit.") {
|
||||
//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
|
||||
return dynamicPage(name: "bulbDiscovery", title: "Light Discovery Failed!", nextPage: "", refreshInterval: 0, install: true, uninstall: true) {
|
||||
section("Failed to discover any lights, please try again later. Click 'Done' to exit.") {
|
||||
paragraph title: "Previously added Hue Lights ({{existingLightsSize}} added)", messageArgs: [existingLightsSize: existingLightsSize], existingLightsDescription
|
||||
}
|
||||
section {
|
||||
href "bridgeDiscovery", title: title, description: "", state: selectedHue ? "complete" : "incomplete", params: [override: true]
|
||||
@@ -193,10 +194,10 @@ def bulbDiscovery() {
|
||||
}
|
||||
|
||||
} else {
|
||||
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.") {
|
||||
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
|
||||
input(name: "selectedBulbs", type: "enum", required: false, title: "Select Hue Lights to add ({{numFound}} found)", messageArgs: [numFound: numFound], multiple: true, submitOnChange: true, options: newLights)
|
||||
paragraph title: "Previously added Hue Lights ({{existingLightsSize}} added)", messageArgs: [existingLightsSize: existingLightsSize], existingLightsDescription
|
||||
}
|
||||
section {
|
||||
href "bridgeDiscovery", title: title, description: "", state: selectedHue ? "complete" : "incomplete", params: [override: true]
|
||||
@@ -217,19 +218,19 @@ private sendDeveloperReq() {
|
||||
def token = app.id
|
||||
def host = getBridgeIP()
|
||||
sendHubCommand(new physicalgraph.device.HubAction([
|
||||
method: "POST",
|
||||
path: "/api",
|
||||
method : "POST",
|
||||
path : "/api",
|
||||
headers: [
|
||||
HOST: host
|
||||
],
|
||||
body: [devicetype: "$token-0"]], "${selectedHue}", [callback: "usernameHandler"]))
|
||||
body : [devicetype: "$token-0"]], "${selectedHue}", [callback: "usernameHandler"]))
|
||||
}
|
||||
|
||||
private discoverHueBulbs() {
|
||||
def host = getBridgeIP()
|
||||
sendHubCommand(new physicalgraph.device.HubAction([
|
||||
method: "GET",
|
||||
path: "/api/${state.username}/lights",
|
||||
method : "GET",
|
||||
path : "/api/${state.username}/lights",
|
||||
headers: [
|
||||
HOST: host
|
||||
]], "${selectedHue}", [callback: "lightsHandler"]))
|
||||
@@ -238,8 +239,8 @@ private discoverHueBulbs() {
|
||||
private verifyHueBridge(String deviceNetworkId, String host) {
|
||||
log.trace "Verify Hue Bridge $deviceNetworkId"
|
||||
sendHubCommand(new physicalgraph.device.HubAction([
|
||||
method: "GET",
|
||||
path: "/description.xml",
|
||||
method : "GET",
|
||||
path : "/description.xml",
|
||||
headers: [
|
||||
HOST: host
|
||||
]], deviceNetworkId, [callback: "bridgeDescriptionHandler"]))
|
||||
@@ -266,18 +267,18 @@ Map bridgesDiscovered() {
|
||||
}
|
||||
|
||||
Map bulbsDiscovered() {
|
||||
def bulbs = getHueBulbs()
|
||||
def bulbs = getHueBulbs()
|
||||
def bulbmap = [:]
|
||||
if (bulbs instanceof java.util.Map) {
|
||||
bulbs.each {
|
||||
def value = "${it.value.name}"
|
||||
def key = app.id +"/"+ it.value.id
|
||||
def key = app.id + "/" + it.value.id
|
||||
bulbmap["${key}"] = value
|
||||
}
|
||||
} else { //backwards compatable
|
||||
bulbs.each {
|
||||
def value = "${it.name}"
|
||||
def key = app.id +"/"+ it.id
|
||||
def key = app.id + "/" + it.id
|
||||
logg += "$value - $key, "
|
||||
bulbmap["${key}"] = value
|
||||
}
|
||||
@@ -294,7 +295,7 @@ def getHueBridges() {
|
||||
}
|
||||
|
||||
def getVerifiedHueBridges() {
|
||||
getHueBridges().findAll{ it?.value?.verified == true }
|
||||
getHueBridges().findAll { it?.value?.verified == true }
|
||||
}
|
||||
|
||||
def installed() {
|
||||
@@ -331,7 +332,7 @@ def manualRefresh() {
|
||||
poll()
|
||||
}
|
||||
|
||||
def uninstalled(){
|
||||
def uninstalled() {
|
||||
// Remove bridgedevice connection to allow uninstall of smartapp even though bridge is listed
|
||||
// as user of smartapp
|
||||
app.updateSetting("bridgeDevice", null)
|
||||
@@ -371,7 +372,7 @@ private getDeviceType(hueType) {
|
||||
return null
|
||||
}
|
||||
|
||||
private addChildBulb(dni, hueType, name, hub, update=false, device = null) {
|
||||
private addChildBulb(dni, hueType, name, hub, update = false, device = null) {
|
||||
def deviceType = getDeviceType(hueType)
|
||||
|
||||
if (deviceType) {
|
||||
@@ -386,7 +387,7 @@ def addBulbs() {
|
||||
def bulbs = getHueBulbs()
|
||||
selectedBulbs?.each { dni ->
|
||||
def d = getChildDevice(dni)
|
||||
if(!d) {
|
||||
if (!d) {
|
||||
def newHueBulb
|
||||
if (bulbs instanceof java.util.Map) {
|
||||
newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni }
|
||||
@@ -419,11 +420,11 @@ def addBulbs() {
|
||||
|
||||
def addBridge() {
|
||||
def vbridges = getVerifiedHueBridges()
|
||||
def vbridge = vbridges.find {"${it.value.mac}" == selectedHue}
|
||||
def vbridge = vbridges.find { "${it.value.mac}" == selectedHue }
|
||||
|
||||
if(vbridge) {
|
||||
if (vbridge) {
|
||||
def d = getChildDevice(selectedHue)
|
||||
if(!d) {
|
||||
if (!d) {
|
||||
// compatibility with old devices
|
||||
def newbridge = true
|
||||
childDevices.each {
|
||||
@@ -458,15 +459,15 @@ def addBridge() {
|
||||
childDevice?.sendEvent(name: "idNumber", value: idNumber)
|
||||
if (vbridge.value.ip && vbridge.value.port) {
|
||||
if (vbridge.value.ip.contains(".")) {
|
||||
childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port)
|
||||
childDevice.updateDataValue("networkAddress", vbridge.value.ip + ":" + vbridge.value.port)
|
||||
childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port)
|
||||
childDevice.updateDataValue("networkAddress", vbridge.value.ip + ":" + vbridge.value.port)
|
||||
} else {
|
||||
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
|
||||
childDevice.updateDataValue("networkAddress", convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
|
||||
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
|
||||
childDevice.updateDataValue("networkAddress", convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
|
||||
}
|
||||
} else {
|
||||
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress))
|
||||
childDevice.updateDataValue("networkAddress", convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress))
|
||||
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress))
|
||||
childDevice.updateDataValue("networkAddress", convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress))
|
||||
}
|
||||
} else {
|
||||
log.error "Failed to create Hue Bridge device"
|
||||
@@ -484,14 +485,14 @@ def ssdpBridgeHandler(evt) {
|
||||
|
||||
def hub = evt?.hubId
|
||||
def parsedEvent = parseLanMessage(description)
|
||||
parsedEvent << ["hub":hub]
|
||||
parsedEvent << ["hub": hub]
|
||||
|
||||
def bridges = getHueBridges()
|
||||
log.trace bridges.toString()
|
||||
if (!(bridges."${parsedEvent.ssdpUSN.toString()}")) {
|
||||
//bridge does not exist
|
||||
log.trace "Adding bridge ${parsedEvent.ssdpUSN}"
|
||||
bridges << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
|
||||
bridges << ["${parsedEvent.ssdpUSN.toString()}": parsedEvent]
|
||||
} else {
|
||||
// update the values
|
||||
def ip = convertHexToIP(parsedEvent.networkAddress)
|
||||
@@ -512,7 +513,7 @@ def ssdpBridgeHandler(evt) {
|
||||
bridge.setDeviceNetworkId("${dniReceived}")
|
||||
dstate.mac = dniReceived
|
||||
// Check to see if selectedHue is a valid bridge, otherwise update it
|
||||
def isSelectedValid = bridges?.find {it.value?.mac == selectedHue}
|
||||
def isSelectedValid = bridges?.find { it.value?.mac == selectedHue }
|
||||
if (isSelectedValid == null) {
|
||||
log.warn "Correcting selectedHue in state"
|
||||
app.updateSetting("selectedHue", dniReceived)
|
||||
@@ -532,7 +533,7 @@ def ssdpBridgeHandler(evt) {
|
||||
dstate.ip = ip
|
||||
dstate.port = port
|
||||
dstate.name = "Philips hue ($ip)"
|
||||
d.sendEvent(name:"networkAddress", value: host)
|
||||
d.sendEvent(name: "networkAddress", value: host)
|
||||
d.updateDataValue("networkAddress", host)
|
||||
}
|
||||
if (dstate.mac != dniReceived) {
|
||||
@@ -541,7 +542,7 @@ def ssdpBridgeHandler(evt) {
|
||||
}
|
||||
if (selectedHue != dniReceived) {
|
||||
// Check to see if selectedHue is a valid bridge, otherwise update it
|
||||
def isSelectedValid = bridges?.find {it.value?.mac == selectedHue}
|
||||
def isSelectedValid = bridges?.find { it.value?.mac == selectedHue }
|
||||
if (isSelectedValid == null) {
|
||||
log.warn "Correcting selectedHue in state"
|
||||
app.updateSetting("selectedHue", dniReceived)
|
||||
@@ -556,7 +557,7 @@ void bridgeDescriptionHandler(physicalgraph.device.HubResponse hubResponse) {
|
||||
def body = hubResponse.xml
|
||||
if (body?.device?.modelName?.text()?.startsWith("Philips hue bridge")) {
|
||||
def bridges = getHueBridges()
|
||||
def bridge = bridges.find {it?.key?.contains(body?.device?.UDN?.text())}
|
||||
def bridge = bridges.find { it?.key?.contains(body?.device?.UDN?.text()) }
|
||||
if (bridge) {
|
||||
def idNumber = getBridgeIdNumber(body?.device?.serialNumber?.text())
|
||||
|
||||
@@ -565,10 +566,10 @@ void bridgeDescriptionHandler(physicalgraph.device.HubResponse hubResponse) {
|
||||
def name = body?.device?.friendlyName?.text()
|
||||
def index = name?.indexOf('(')
|
||||
if (index != -1) {
|
||||
name = name.substring(0,index)
|
||||
name = name.substring(0, index)
|
||||
name += " ($idNumber)"
|
||||
}
|
||||
bridge.value << [name:name, serialNumber:body?.device?.serialNumber?.text(), idNumber: idNumber, verified: true]
|
||||
bridge.value << [name: name, serialNumber: body?.device?.serialNumber?.text(), idNumber: idNumber, verified: true]
|
||||
} else {
|
||||
log.error "/description.xml returned a bridge that didn't exist"
|
||||
}
|
||||
@@ -611,7 +612,7 @@ def locationHandler(evt) {
|
||||
|
||||
def hub = evt?.hubId
|
||||
def parsedEvent = parseLanMessage(description)
|
||||
parsedEvent << ["hub":hub]
|
||||
parsedEvent << ["hub": hub]
|
||||
|
||||
if (parsedEvent?.ssdpTerm?.contains("urn:schemas-upnp-org:device:basic:1")) {
|
||||
//SSDP DISCOVERY EVENTS
|
||||
@@ -621,7 +622,7 @@ def locationHandler(evt) {
|
||||
if (!(bridges."${parsedEvent.ssdpUSN.toString()}")) {
|
||||
//bridge does not exist
|
||||
log.trace "Adding bridge ${parsedEvent.ssdpUSN}"
|
||||
bridges << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
|
||||
bridges << ["${parsedEvent.ssdpUSN.toString()}": parsedEvent]
|
||||
} else {
|
||||
// update the values
|
||||
def ip = convertHexToIP(parsedEvent.networkAddress)
|
||||
@@ -656,12 +657,12 @@ def locationHandler(evt) {
|
||||
networkAddress = d.latestState('networkAddress').stringValue
|
||||
}
|
||||
log.trace "Host: $host - $networkAddress"
|
||||
if(host != networkAddress) {
|
||||
if (host != networkAddress) {
|
||||
log.debug "Device's port or ip changed for device $d..."
|
||||
dstate.ip = ip
|
||||
dstate.port = port
|
||||
dstate.name = "Philips hue ($ip)"
|
||||
d.sendEvent(name:"networkAddress", value: host)
|
||||
d.sendEvent(name: "networkAddress", value: host)
|
||||
d.updateDataValue("networkAddress", host)
|
||||
}
|
||||
}
|
||||
@@ -674,14 +675,14 @@ def locationHandler(evt) {
|
||||
def body = new XmlSlurper().parseText(parsedEvent.body)
|
||||
if (body?.device?.modelName?.text().startsWith("Philips hue bridge")) {
|
||||
def bridges = getHueBridges()
|
||||
def bridge = bridges.find {it?.key?.contains(body?.device?.UDN?.text())}
|
||||
def bridge = bridges.find { it?.key?.contains(body?.device?.UDN?.text()) }
|
||||
if (bridge) {
|
||||
bridge.value << [name:body?.device?.friendlyName?.text(), serialNumber:body?.device?.serialNumber?.text(), verified: true]
|
||||
bridge.value << [name: body?.device?.friendlyName?.text(), serialNumber: body?.device?.serialNumber?.text(), verified: true]
|
||||
} else {
|
||||
log.error "/description.xml returned a bridge that didn't exist"
|
||||
}
|
||||
}
|
||||
} else if(headerString?.contains("json") && isValidSource(parsedEvent.mac)) {
|
||||
} else if (headerString?.contains("json") && isValidSource(parsedEvent.mac)) {
|
||||
log.trace "description.xml response (application/json)"
|
||||
def body = new groovy.json.JsonSlurper().parseText(parsedEvent.body)
|
||||
if (body.success != null) {
|
||||
@@ -706,7 +707,7 @@ def locationHandler(evt) {
|
||||
}
|
||||
}
|
||||
|
||||
def doDeviceSync(){
|
||||
def doDeviceSync() {
|
||||
log.trace "Doing Hue Device Sync!"
|
||||
|
||||
// Check if state.updating failed to clear
|
||||
@@ -787,7 +788,7 @@ private void checkBridgeStatus() {
|
||||
|
||||
def isValidSource(macAddress) {
|
||||
def vbridges = getVerifiedHueBridges()
|
||||
return (vbridges?.find {"${it.value.mac}" == macAddress}) != null
|
||||
return (vbridges?.find { "${it.value.mac}" == macAddress }) != null
|
||||
}
|
||||
|
||||
def isInBulbDiscovery() {
|
||||
@@ -801,19 +802,19 @@ private updateBulbState(messageBody, hub) {
|
||||
def toRemove = [:]
|
||||
toRemove << bulbs
|
||||
|
||||
messageBody.each { k,v ->
|
||||
messageBody.each { k, v ->
|
||||
|
||||
if (v instanceof Map) {
|
||||
if (bulbs[k] == null) {
|
||||
bulbs[k] = [:]
|
||||
}
|
||||
bulbs[k] << [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub, remove: false]
|
||||
bulbs[k] << [id: k, name: v.name, type: v.type, modelid: v.modelid, hub: hub, remove: false]
|
||||
toRemove.remove(k)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove bulbs from state that are no longer discovered
|
||||
toRemove.each { k,v ->
|
||||
toRemove.each { k, v ->
|
||||
log.warn "${bulbs[k].name} no longer exists on bridge, removing"
|
||||
bulbs.remove(k)
|
||||
}
|
||||
@@ -937,12 +938,11 @@ private sendBasicEvents(device, param, value) {
|
||||
*
|
||||
* Example payload
|
||||
* [,
|
||||
* {"success":{"/lights/5/state/bri":87}},
|
||||
* {"success":{"/lights/5/state/transitiontime":4}},
|
||||
* {"success":{"/lights/5/state/on":true}},
|
||||
* {"success":{"/lights/5/state/xy":[0.4152,0.5336]}},
|
||||
* {"success":{"/lights/5/state/alert":"none"}}
|
||||
* ]
|
||||
*{"success":{"/lights/5/state/bri":87}},
|
||||
*{"success":{"/lights/5/state/transitiontime":4}},
|
||||
*{"success":{"/lights/5/state/on":true}},
|
||||
*{"success":{"/lights/5/state/xy":[0.4152,0.5336]}},
|
||||
*{"success":{"/lights/5/state/alert":"none"}}* ]
|
||||
*
|
||||
* @param body a data structure of lists and maps based on a JSON data
|
||||
* @return empty array
|
||||
@@ -995,7 +995,7 @@ private handleCommandResponse(body) {
|
||||
*
|
||||
* Example payload
|
||||
*
|
||||
* {"5":{"state": {"on":true,"bri":102,"hue":25600,"sat":254,"effect":"none","xy":[0.1700,0.7000],"ct":153,"alert":"none",
|
||||
*{"5":{"state": {"on":true,"bri":102,"hue":25600,"sat":254,"effect":"none","xy":[0.1700,0.7000],"ct":153,"alert":"none",
|
||||
* "colormode":"xy","reachable":true}, "type": "Extended color light", "name": "Go", "modelid": "LLC020", "manufacturername": "Philips",
|
||||
* "uniqueid":"00:17:88:01:01:13:d5:11-0b", "swversion": "5.38.1.14378"},
|
||||
* "6":{"state": {"on":true,"bri":103,"hue":14910,"sat":144,"effect":"none","xy":[0.4596,0.4105],"ct":370,"alert":"none",
|
||||
@@ -1069,9 +1069,9 @@ def hubVerification(bodytext) {
|
||||
def body = new XmlSlurper().parseText(bodytext)
|
||||
if (body?.device?.modelName?.text().startsWith("Philips hue bridge")) {
|
||||
def bridges = getHueBridges()
|
||||
def bridge = bridges.find {it?.key?.contains(body?.device?.UDN?.text())}
|
||||
def bridge = bridges.find { it?.key?.contains(body?.device?.UDN?.text()) }
|
||||
if (bridge) {
|
||||
bridge.value << [name:body?.device?.friendlyName?.text(), serialNumber:body?.device?.serialNumber?.text(), verified: true]
|
||||
bridge.value << [name: body?.device?.friendlyName?.text(), serialNumber: body?.device?.serialNumber?.text(), verified: true]
|
||||
} else {
|
||||
log.error "/description.xml returned a bridge that didn't exist"
|
||||
}
|
||||
@@ -1107,7 +1107,7 @@ def setLevel(childDevice, percent) {
|
||||
else
|
||||
level = Math.min(Math.round(percent * 254 / 100), 254)
|
||||
|
||||
createSwitchEvent(childDevice, level > 0 ,percent)
|
||||
createSwitchEvent(childDevice, level > 0, percent)
|
||||
|
||||
// For Zigbee lights, if level is set to 0 ST just turns them off without changing level
|
||||
// that means that the light will still be on when on is called next time
|
||||
@@ -1137,7 +1137,7 @@ def setHue(childDevice, percent) {
|
||||
def id = getId(childDevice)
|
||||
updateInProgress()
|
||||
// 0 - 65535
|
||||
def level = Math.min(Math.round(percent * 65535 / 100), 65535)
|
||||
def level = Math.min(Math.round(percent * 65535 / 100), 65535)
|
||||
// TODO should this be done by app only or should we default to on?
|
||||
createSwitchEvent(childDevice, "on")
|
||||
put("lights/$id/state", [hue: level, on: true])
|
||||
@@ -1149,7 +1149,7 @@ def setColorTemperature(childDevice, huesettings) {
|
||||
def id = getId(childDevice)
|
||||
updateInProgress()
|
||||
// 153 (6500K) to 500 (2000K)
|
||||
def ct = hueSettings == 6500 ? 153 : Math.round(1000000/huesettings)
|
||||
def ct = hueSettings == 6500 ? 153 : Math.round(1000000 / huesettings)
|
||||
createSwitchEvent(childDevice, "on")
|
||||
put("lights/$id/state", [ct: ct, on: true])
|
||||
return "Setting color temperature to $ct"
|
||||
@@ -1267,7 +1267,7 @@ private getBridgeIP() {
|
||||
def d = getChildDevice(selectedHue)
|
||||
if (d) {
|
||||
if (d.getDeviceDataByName("networkAddress"))
|
||||
host = d.getDeviceDataByName("networkAddress")
|
||||
host = d.getDeviceDataByName("networkAddress")
|
||||
else
|
||||
host = d.latestState('networkAddress').stringValue
|
||||
}
|
||||
@@ -1291,26 +1291,26 @@ private getBridgeIP() {
|
||||
}
|
||||
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex,16)
|
||||
Integer.parseInt(hex, 16)
|
||||
}
|
||||
|
||||
def convertBulbListToMap() {
|
||||
try {
|
||||
if (state.bulbs instanceof java.util.List) {
|
||||
def map = [:]
|
||||
state.bulbs?.unique {it.id}.each { bulb ->
|
||||
map << ["${bulb.id}":["id":bulb.id, "name":bulb.name, "type": bulb.type, "modelid": bulb.modelid, "hub":bulb.hub, "online": bulb.online]]
|
||||
state.bulbs?.unique { it.id }.each { bulb ->
|
||||
map << ["${bulb.id}": ["id": bulb.id, "name": bulb.name, "type": bulb.type, "modelid": bulb.modelid, "hub": bulb.hub, "online": bulb.online]]
|
||||
}
|
||||
state.bulbs = map
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
catch (Exception e) {
|
||||
log.error "Caught error attempting to convert bulb list to map: $e"
|
||||
}
|
||||
}
|
||||
|
||||
private String convertHexToIP(hex) {
|
||||
[convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
|
||||
[convertHexToInt(hex[0..1]), convertHexToInt(hex[2..3]), convertHexToInt(hex[4..5]), convertHexToInt(hex[6..7])].join(".")
|
||||
}
|
||||
|
||||
private Boolean hasAllHubsOver(String desiredFirmware) {
|
||||
@@ -1327,7 +1327,7 @@ private List getRealHubFirmwareVersions() {
|
||||
* @param childDevice device to send event for
|
||||
* @param setSwitch The new switch state, "on" or "off"
|
||||
* @param setLevel Optional, switchLevel between 0-100, used if you set level to 0 for example since
|
||||
* that should generate "off" instead of level change
|
||||
* that should generate "off" instead of level change
|
||||
*/
|
||||
private void createSwitchEvent(childDevice, setSwitch, setLevel = null) {
|
||||
|
||||
@@ -1358,7 +1358,7 @@ private void createSwitchEvent(childDevice, setSwitch, setLevel = null) {
|
||||
private colorPointsForModel(model = null) {
|
||||
def result = null
|
||||
switch (model) {
|
||||
// Gamut A
|
||||
// Gamut A
|
||||
case "LLC001": /* Monet, Renoir, Mondriaan (gen II) */
|
||||
case "LLC005": /* Bloom (gen II) */
|
||||
case "LLC006": /* Iris (gen III) */
|
||||
@@ -1368,21 +1368,21 @@ private colorPointsForModel(model = null) {
|
||||
case "LLC013": /* Storylight */
|
||||
case "LST001": /* Light Strips */
|
||||
case "LLC010": /* Hue Living Colors Iris + */
|
||||
result = [r:[x: 0.704f, y: 0.296f], g:[x: 0.2151f, y: 0.7106f], b:[x: 0.138f, y: 0.08f]];
|
||||
result = [r: [x: 0.704f, y: 0.296f], g: [x: 0.2151f, y: 0.7106f], b: [x: 0.138f, y: 0.08f]];
|
||||
break
|
||||
// Gamut C
|
||||
// Gamut C
|
||||
case "LLC020": /* Hue Go */
|
||||
case "LST002": /* Hue LightStrips Plus */
|
||||
result = [r:[x: 0.692f, y: 0.308f], g:[x: 0.17f, y: 0.7f], b:[x: 0.153f, y: 0.048f]];
|
||||
result = [r: [x: 0.692f, y: 0.308f], g: [x: 0.17f, y: 0.7f], b: [x: 0.153f, y: 0.048f]];
|
||||
break
|
||||
// Gamut B
|
||||
// Gamut B
|
||||
case "LCT001": /* Hue A19 */
|
||||
case "LCT002": /* Hue BR30 */
|
||||
case "LCT003": /* Hue GU10 */
|
||||
case "LCT007": /* Hue A19 + */
|
||||
case "LLM001": /* Color Light Module + */
|
||||
default:
|
||||
result = [r:[x: 0.675f, y: 0.322f], g:[x: 0.4091f, y: 0.518f], b:[x: 0.167f, y: 0.04f]];
|
||||
result = [r: [x: 0.675f, y: 0.322f], g: [x: 0.4091f, y: 0.518f], b: [x: 0.167f, y: 0.04f]];
|
||||
|
||||
}
|
||||
return result;
|
||||
@@ -1401,9 +1401,9 @@ private colorPointsForModel(model = null) {
|
||||
private float[] calculateXY(colorStr, model = null) {
|
||||
|
||||
// #ffa013
|
||||
def cred = Integer.valueOf( colorStr.substring( 1, 3 ), 16 )
|
||||
def cgreen = Integer.valueOf( colorStr.substring( 3, 5 ), 16 )
|
||||
def cblue = Integer.valueOf( colorStr.substring( 5, 7 ), 16 )
|
||||
def cred = Integer.valueOf(colorStr.substring(1, 3), 16)
|
||||
def cgreen = Integer.valueOf(colorStr.substring(3, 5), 16)
|
||||
def cblue = Integer.valueOf(colorStr.substring(5, 7), 16)
|
||||
|
||||
float red = cred / 255.0f;
|
||||
float green = cgreen / 255.0f;
|
||||
@@ -1473,7 +1473,6 @@ private float[] calculateXY(colorStr, model = null) {
|
||||
// xy[0] = PHHueHelper.precision(4, xy[0]);
|
||||
// xy[1] = PHHueHelper.precision(4, xy[1]);
|
||||
|
||||
|
||||
// TODO needed, assume it just sets number of decimals?
|
||||
//xy[0] = PHHueHelper.precision(xy[0]);
|
||||
//xy[1] = PHHueHelper.precision(xy[1]);
|
||||
@@ -1489,10 +1488,10 @@ private float[] calculateXY(colorStr, model = null) {
|
||||
*
|
||||
* @param points the float array contain x and the y value. [x,y]
|
||||
* @param model the model of the lamp, example: "LCT001" for hue bulb. Used to calculate the color gamut.
|
||||
* If this value is empty the default gamut values are used.
|
||||
* If this value is empty the default gamut values are used.
|
||||
* @return the color value in hex (#ff03d3). If xy is null OR xy is not an array of size 2, Color. BLACK will be returned
|
||||
*/
|
||||
private String colorFromXY(points, model ) {
|
||||
private String colorFromXY(points, model) {
|
||||
|
||||
if (points == null || model == null) {
|
||||
log.warn "Input color missing"
|
||||
@@ -1550,9 +1549,9 @@ private String colorFromXY(points, model ) {
|
||||
// float b = X * 0.0556f - Y * 0.2040f + Z * 1.0570f;
|
||||
|
||||
// sRGB D65 conversion
|
||||
float r = x2 * 1.656492f - y2 * 0.354851f - z2 * 0.255038f;
|
||||
float g = -x2 * 0.707196f + y2 * 1.655397f + z2 * 0.036152f;
|
||||
float b = x2 * 0.051713f - y2 * 0.121364f + z2 * 1.011530f;
|
||||
float r = x2 * 1.656492f - y2 * 0.354851f - z2 * 0.255038f;
|
||||
float g = -x2 * 0.707196f + y2 * 1.655397f + z2 * 0.036152f;
|
||||
float b = x2 * 0.051713f - y2 * 0.121364f + z2 * 1.011530f;
|
||||
|
||||
if (r > b && r > g && r > 1.0f) {
|
||||
// red is too big
|
||||
@@ -1614,7 +1613,6 @@ private String colorFromXY(points, model ) {
|
||||
return "#$r1$g1$b1"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates crossProduct of two 2D vectors / points.
|
||||
*
|
||||
@@ -1643,8 +1641,8 @@ private getClosestPointToPoints(A, B, P) {
|
||||
def AP = [x: (P.x - A.x), y: (P.y - A.y)];
|
||||
def AB = [x: (B.x - A.x), y: (B.y - A.y)];
|
||||
|
||||
float ab2 = AB.x*AB.x + AB.y*AB.y;
|
||||
float ap_ab = AP.x*AB.x + AP.y*AB.y;
|
||||
float ab2 = AB.x * AB.x + AB.y * AB.y;
|
||||
float ap_ab = AP.x * AB.x + AP.y * AB.y;
|
||||
|
||||
float t = ap_ab / ab2;
|
||||
|
||||
@@ -1684,9 +1682,9 @@ private float getDistanceBetweenTwoPoints(one, two) {
|
||||
*/
|
||||
private boolean checkPointInLampsReach(p, colorPoints) {
|
||||
|
||||
def red = colorPoints.r;
|
||||
def red = colorPoints.r;
|
||||
def green = colorPoints.g;
|
||||
def blue = colorPoints.b;
|
||||
def blue = colorPoints.b;
|
||||
|
||||
def v1 = [x: (green.x - red.x), y: (green.y - red.y)];
|
||||
def v2 = [x: (blue.x - red.x), y: (blue.y - red.y)];
|
||||
@@ -1696,12 +1694,9 @@ private boolean checkPointInLampsReach(p, colorPoints) {
|
||||
float s = crossProduct(q, v2) / crossProduct(v1, v2);
|
||||
float t = crossProduct(v1, q) / crossProduct(v1, v2);
|
||||
|
||||
if ( (s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f))
|
||||
{
|
||||
if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) {
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1714,10 +1709,10 @@ private boolean checkPointInLampsReach(p, colorPoints) {
|
||||
*
|
||||
* @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 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)
|
||||
@@ -1727,10 +1722,10 @@ def hexToHsv(colorStr){
|
||||
def d = max - min
|
||||
s = max == 0 ? 0 : d / max
|
||||
|
||||
if(max == min){
|
||||
if (max == min) {
|
||||
h = 0
|
||||
}else{
|
||||
switch(max){
|
||||
} 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
|
||||
@@ -1745,13 +1740,13 @@ def hexToHsv(colorStr){
|
||||
* Converts HSV/HSB 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)
|
||||
* @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 hsvToHex(hue, sat, value = 100) {
|
||||
def r, g, b;
|
||||
def h = hue / 100
|
||||
def s = sat / 100
|
||||
@@ -1803,4 +1798,3 @@ def hsvToHex(hue, sat, value = 100){
|
||||
|
||||
return "#$r1$g1$b1"
|
||||
}
|
||||
|
||||
|
||||
19
smartapps/smartthings/hue-connect.src/i18n/ar-AE.properties
Normal file
19
smartapps/smartthings/hue-connect.src/i18n/ar-AE.properties
Normal file
@@ -0,0 +1,19 @@
|
||||
'''Allows you to connect your Philips Hue lights with SmartThings and control them from your Things area or Dashboard in the SmartThings Mobile app. Please update your Hue Bridge first, outside of the SmartThings app, using the Philips Hue app.'''=يوفر لك إمكانية توصيل مصابيح Philips Hue بـ SmartThings والتحكم بها من منطقة Things أو لوحة المعلومات في تطبيق SmartThings للهواتف المحمولة. يُرجى تحديث Hue Bridge أولاً، خارج تطبيق SmartThings، باستخدام تطبيق Philips Hue.
|
||||
'''Discovery Started!'''=بدأ الاكتشاف!
|
||||
'''Please wait while we discover your Hue Bridge. Kindly note that you must first configure your Hue Bridge and Lights using the Philips Hue application. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.'''=يُرجى الانتظار ريثما نكتشف Hue Bridge الخاص بك. تجدر الإشارة إلى أنه عليك أوّلاً تكوين Hue Bridge والمصابيح باستخدام تطبيق Philips Hue. قد يستغرق الاكتشاف خمس دقائق أو أكثر، لذا اجلس واسترخِ! اختر جهازك أدناه بمجرد اكتشافه.
|
||||
'''Select Hue Bridge ({{numFound}} found)'''=تحديد Hue Bridge (تم العثور على {{numFound}})
|
||||
'''Bridge Discovery Failed!'''=فشل اكتشاف Bridge
|
||||
'''Failed to discover any Hue Bridges. Please confirm that the Hue Bridge is connected to the same network as your SmartThings Hub, and that it has power.'''=فشل اكتشاف أي Hue Bridges. يُرجى التأكد من اتصال Hue Bridge بالشبكة نفسها التي يتصل بها موزع SmartThings الخاص بك، ومن أنه متصل بمصدر طاقة أيضاً.
|
||||
'''Linking with your Hue'''=الارتباط بجهاز Hue الخاص بك
|
||||
'''The current Hue username is invalid. Please press the button on your Hue Bridge to relink.'''=اسم مستخدم Hue الحالي غير صالح. يُرجى الضغط على الزر الموجود على Hue Bridge الخاص بك لإعادة الارتباط.
|
||||
'''Press the button on your Hue Bridge to setup a link.'''=اضغط على الزر الموجود على Hue Bridge الخاص بك لإعداد رابط.
|
||||
'''You haven't selected a Hue Bridge, please Press 'Done' and select one before clicking next.'''=لم تحدد Hue Bridge، يُرجى الضغط على 'تم' وتحديد واحد قبل النقر فوق التالي.
|
||||
'''Success!'''=نجحت العملية!
|
||||
'''Linking to your hub was a success! Please click 'Next'!'''=نجح الارتباط بالموزع الخاص بك! يُرجى النقر فوق ”التالي“!
|
||||
'''Find bridges'''=بحث عن أجهزة bridges
|
||||
'''Light Discovery Failed!'''=فشل اكتشاف المصباح!
|
||||
'''Failed to discover any lights, please try again later. Click 'Done' to exit.'''=فشل اكتشاف أي مصابيح، يُرجى المحاولة مرة أخرى لاحقاً. انقر فوق تم للخروج.
|
||||
'''Select Hue Lights to add ({{numFound}} found)'''=حدد مصابيح Hue التي تريد إضافتها (تم العثور على {{numFound}})
|
||||
'''Previously added Hue Lights ({{existingLightsSize}} added)'''=مصابيح Hue المضافة مُسبقاً (تمت إضافة {{existingLightsSize}})
|
||||
'''Light Discovery Started!'''=بدأ اكتشاف المصباح!
|
||||
'''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.'''=يُرجى الانتظار ريثما نكتشف مصابيح Hue الخاصة بك. قد يستغرق الاكتشاف خمس دقائق أو أكثر، لذا اجلس واسترخِ! اختر جهازك أدناه بمجرد اكتشافه.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user