mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 05:11:52 +00:00
Compare commits
393 Commits
netatmo-ap
...
MSA-1856-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78a509e8f5 | ||
|
|
42d31f29cc | ||
|
|
9b4f6974de | ||
|
|
ea5344f9c8 | ||
|
|
b5e1d652fd | ||
|
|
a03e8f20c5 | ||
|
|
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 | ||
|
|
a777c298ca | ||
|
|
94a87e5c7f | ||
|
|
22be8ef2e8 | ||
|
|
2f20a339c3 | ||
|
|
65ae2c0ddf | ||
|
|
7e806275ce | ||
|
|
1ed57aab45 | ||
|
|
4eea7ff90f | ||
|
|
b2264538c9 | ||
|
|
2f786f7ecd | ||
|
|
f45ab69045 | ||
|
|
91762f6a46 | ||
|
|
b55c650637 | ||
|
|
e391ee5f53 | ||
|
|
ab79ceb857 | ||
|
|
7b1b9a46bc | ||
|
|
a9a0d296ee | ||
|
|
905096b381 | ||
|
|
e23bed148d | ||
|
|
3b7dd3dd91 | ||
|
|
ca404bc2a2 | ||
|
|
44702cd5fe | ||
|
|
993f8199d6 | ||
|
|
ccfe5dabdf | ||
|
|
f054cb4de1 | ||
|
|
d47a4de072 | ||
|
|
4c5787fff0 | ||
|
|
b2f90954ae | ||
|
|
8a9c38d38e | ||
|
|
28c0cc9619 | ||
|
|
cb6304530e | ||
|
|
fe5a965f6f | ||
|
|
72c75882cf | ||
|
|
a006799ad3 | ||
|
|
2610f00a82 | ||
|
|
6381580e44 | ||
|
|
79aebafdc4 | ||
|
|
1c83a27c40 | ||
|
|
4a66eca03c | ||
|
|
31c037e7ab | ||
|
|
d6c85436d2 | ||
|
|
e822e7137e | ||
|
|
967e1798d5 | ||
|
|
6ef49e92bb | ||
|
|
e528b61ace | ||
|
|
a583a25ef3 | ||
|
|
a192b75e4c | ||
|
|
f97a0a4072 | ||
|
|
dd0aba3dc9 | ||
|
|
e787afd165 | ||
|
|
12be259779 | ||
|
|
379e70b343 | ||
|
|
9cb0ce716b | ||
|
|
2151e2dd3e | ||
|
|
400c55f4ba | ||
|
|
023b9aad2d | ||
|
|
ccb7b6e49d | ||
|
|
34f77a2989 | ||
|
|
318cdedaec | ||
|
|
011a5ddc74 | ||
|
|
649ed033d0 | ||
|
|
e687e76b3e | ||
|
|
1d2fe8af22 | ||
|
|
6e94220e9e | ||
|
|
e54a8644c3 | ||
|
|
0d4b681971 | ||
|
|
0aabce9fa3 | ||
|
|
843d8800ac | ||
|
|
bcc680ee5d | ||
|
|
245e85f850 | ||
|
|
af16720528 | ||
|
|
c6d6ba85f5 | ||
|
|
51832c8db2 | ||
|
|
f001eb0792 | ||
|
|
81550ee25c | ||
|
|
1cc9d8a90b | ||
|
|
bfd2b6c0fa | ||
|
|
9b6e7d3be8 | ||
|
|
7c5734b752 | ||
|
|
2a3c393534 | ||
|
|
91763e7b4d | ||
|
|
1263b72a72 | ||
|
|
bf45430061 | ||
|
|
054ccbeffe | ||
|
|
5cb84a269d | ||
|
|
6e28d83e96 | ||
|
|
2d82b05f90 | ||
|
|
1611fd0144 | ||
|
|
c416c39ac4 | ||
|
|
bc0f849dad | ||
|
|
459e69607b | ||
|
|
fc312286a2 | ||
|
|
cfdc61d72a | ||
|
|
0c26d75792 | ||
|
|
bf491270a9 | ||
|
|
871d75aea6 | ||
|
|
3905d48235 | ||
|
|
d91c02b970 | ||
|
|
e019d22aff | ||
|
|
900ab9f70e | ||
|
|
a8f95cc0b9 | ||
|
|
e8d205c775 | ||
|
|
06e4b7d9f0 | ||
|
|
16d7da81f1 | ||
|
|
1800ea2bad | ||
|
|
f164b8832c | ||
|
|
2d3fa22e07 | ||
|
|
54a4620c9b | ||
|
|
8e6d009d67 | ||
|
|
d8c89f6c6a | ||
|
|
ddc15172d6 | ||
|
|
c27904acfb | ||
|
|
1e02387387 | ||
|
|
65d4a811b0 | ||
|
|
db4f161e5d | ||
|
|
ff6e543a2e | ||
|
|
de1894bfbf | ||
|
|
0846b6f34c | ||
|
|
d0f8ec87bd | ||
|
|
1383ab1e07 | ||
|
|
d4f21b95d7 | ||
|
|
be2e19e406 | ||
|
|
54da556c17 | ||
|
|
8611d2e2d2 | ||
|
|
c650047f31 | ||
|
|
a15ca97988 | ||
|
|
41adc9777a | ||
|
|
f334f6505a | ||
|
|
ded7501b84 | ||
|
|
eb4d5dcfb8 | ||
|
|
6385443f20 | ||
|
|
fd1ad51880 | ||
|
|
550214ceb5 | ||
|
|
a36500a216 | ||
|
|
445c115c14 | ||
|
|
9900e532a4 | ||
|
|
7648fd4a17 | ||
|
|
a5d0c1d80a | ||
|
|
9686f3770b | ||
|
|
de37d0c813 | ||
|
|
bd3367fe0e | ||
|
|
30511d74af | ||
|
|
f0f02a2c00 | ||
|
|
61356ec8a7 | ||
|
|
57d20e2fca | ||
|
|
3216f63cc0 | ||
|
|
7cbc2d1780 | ||
|
|
5ad20fbd2a | ||
|
|
a17971d68c | ||
|
|
076ffecd19 | ||
|
|
52357e4c50 | ||
|
|
03a7991279 | ||
|
|
f969027191 | ||
|
|
8db0556696 | ||
|
|
5607a3e346 | ||
|
|
bd44027038 | ||
|
|
490ec329cb | ||
|
|
3109049122 | ||
|
|
930c4ed914 | ||
|
|
259516f21f | ||
|
|
b7a08a88e0 | ||
|
|
c028515fcd | ||
|
|
751c98d123 | ||
|
|
5b874e8f3a | ||
|
|
9e10405527 | ||
|
|
648dee90b6 | ||
|
|
32ceaff54d | ||
|
|
5b1da30a47 | ||
|
|
76e139153a | ||
|
|
a4bc248006 | ||
|
|
e7eb461b4e | ||
|
|
0a82077b24 | ||
|
|
083ed7cc9a | ||
|
|
38ef9e5c77 | ||
|
|
6a71615ca5 | ||
|
|
9939591005 | ||
|
|
d7f2bc1d79 | ||
|
|
3c5d727d4c | ||
|
|
bbad6dfa7a | ||
|
|
df6c815aa4 | ||
|
|
d16ac00eb6 | ||
|
|
1941f56007 | ||
|
|
f34df19a65 | ||
|
|
af40246655 | ||
|
|
20239ca982 | ||
|
|
b20c0e371f | ||
|
|
60756e6dc6 | ||
|
|
ee2e1ee04f | ||
|
|
e443cb641c | ||
|
|
b9993a9bf2 | ||
|
|
092971c786 | ||
|
|
a8ee927893 | ||
|
|
51979f0030 | ||
|
|
61d1205e7d | ||
|
|
1dfd1818b4 | ||
|
|
1de73b643c | ||
|
|
e5c21ef720 | ||
|
|
fcb504f57e | ||
|
|
b9229c6ef8 | ||
|
|
91a9856a32 | ||
|
|
a84ffdde91 | ||
|
|
3034cc8bcb | ||
|
|
918e9d9397 | ||
|
|
0c040120cc | ||
|
|
6c84c052cb | ||
|
|
f017bff6ef | ||
|
|
65bb10d6d6 | ||
|
|
3f93de247b | ||
|
|
2b7af3ef8d | ||
|
|
cf9d123aa0 | ||
|
|
8e37b1fce7 | ||
|
|
439dd634bd | ||
|
|
1db5f75ec5 | ||
|
|
7978f45996 | ||
|
|
0ab657d5f2 | ||
|
|
3d88fc0413 | ||
|
|
ede1296b6b | ||
|
|
470cdc7d97 | ||
|
|
9bc3ff5103 | ||
|
|
bae79192c5 | ||
|
|
0ae836b023 | ||
|
|
0b4d555d33 | ||
|
|
0f8beee455 | ||
|
|
6ffdc02ef1 | ||
|
|
6beb8bb50c | ||
|
|
83a9df6557 | ||
|
|
af8590ab01 | ||
|
|
9599397db8 | ||
|
|
f7dbabb6c8 | ||
|
|
7016e234d2 | ||
|
|
cf119b1d15 | ||
|
|
3e88f3c4bd | ||
|
|
8ca20ce87e | ||
|
|
c1d520a578 | ||
|
|
c0bb0554d8 | ||
|
|
b6790729c6 | ||
|
|
3675332b75 | ||
|
|
7431346187 | ||
|
|
6aa0ff97b3 | ||
|
|
bd1ace96de | ||
|
|
40c4520d08 | ||
|
|
1f69a42341 | ||
|
|
969852602c | ||
|
|
4115d8c65f | ||
|
|
492315b78a | ||
|
|
40acb36009 | ||
|
|
0a040aa51b | ||
|
|
8986c4f5d6 | ||
|
|
58d8a7dac5 | ||
|
|
a98d3dc2d6 | ||
|
|
3a377ba147 | ||
|
|
2d25a0e63f | ||
|
|
633a179074 | ||
|
|
49bc42b4ab | ||
|
|
d258c46aee | ||
|
|
8a66742bb5 | ||
|
|
add519433c | ||
|
|
f6dcaf6d09 | ||
|
|
b07b34f66c | ||
|
|
96659f0a73 | ||
|
|
699f80e9f7 | ||
|
|
d21dfc09fe | ||
|
|
d196125092 | ||
|
|
44088d626a | ||
|
|
2966c4d5a1 | ||
|
|
3343273d40 | ||
|
|
5c70da54a4 | ||
|
|
687c64d29d | ||
|
|
c9d1b168f7 | ||
|
|
2f87309fdf | ||
|
|
37524f17b2 | ||
|
|
47522facc7 | ||
|
|
4363661157 | ||
|
|
330b41941a | ||
|
|
26d286e0a0 | ||
|
|
ef2323f1b1 | ||
|
|
51452bc095 | ||
|
|
b7b29d8dbc | ||
|
|
b8111e8760 | ||
|
|
24ea8269a3 | ||
|
|
20df244dca | ||
|
|
583d42df13 | ||
|
|
f1309b2ee2 | ||
|
|
ec1ae2d0b1 | ||
|
|
5e48e710d4 | ||
|
|
07c5a3533f | ||
|
|
72b2016b7d | ||
|
|
a9aee8fd96 | ||
|
|
5c015cf678 | ||
|
|
cf1a46e309 | ||
|
|
7c5438880d | ||
|
|
d9888b3184 | ||
|
|
b582c3d832 | ||
|
|
1ff77dc608 | ||
|
|
afbec02217 | ||
|
|
434a72bd13 | ||
|
|
3fba7c9422 | ||
|
|
b63d4a9156 | ||
|
|
6eb29ad019 | ||
|
|
711cdc3ebf | ||
|
|
f58a1ef589 | ||
|
|
db5237ca33 | ||
|
|
7791c68a8a | ||
|
|
db4140ffd6 | ||
|
|
c15b1e88e1 | ||
|
|
ac422076c8 | ||
|
|
94f57dd249 | ||
|
|
c11c146690 | ||
|
|
9a5d506668 | ||
|
|
b12df3f360 | ||
|
|
9dac541473 | ||
|
|
a6cc506803 | ||
|
|
aba8a7ad4b | ||
|
|
b4c912ab80 | ||
|
|
4b44460b0b | ||
|
|
9f5eb7b85a | ||
|
|
6a1a2b0ed9 | ||
|
|
aae7f23a22 | ||
|
|
1674dc7339 |
@@ -1,10 +1,10 @@
|
||||
# SmartThings Public Github Repo
|
||||
# SmartThings Public GitHub Repo
|
||||
|
||||
An official list of SmartApps and Device Types from SmartThings.
|
||||
|
||||
Here are some links to help you get started coding right away:
|
||||
|
||||
* [Github-specific Documentation](http://docs.smartthings.com/en/latest/tools-and-ide/github-integration.html)
|
||||
* [GitHub-specific Documentation](http://docs.smartthings.com/en/latest/tools-and-ide/github-integration.html)
|
||||
* [Full Documentation](http://docs.smartthings.com)
|
||||
* [IDE & Simulator](http://ide.smartthings.com)
|
||||
* [Community Forums](http://community.smartthings.com)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -23,8 +23,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
|
||||
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#cccccc")
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#00A0DC")
|
||||
}
|
||||
|
||||
main "acceleration"
|
||||
|
||||
@@ -23,8 +23,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00A0DC")
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13")
|
||||
}
|
||||
main "contact"
|
||||
details "contact"
|
||||
|
||||
@@ -27,7 +27,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("toggle", "device.lock", width: 2, height: 2) {
|
||||
state "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff"
|
||||
state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821"
|
||||
state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#00A0DC"
|
||||
}
|
||||
standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'lock', action:"lock.lock", icon:"st.locks.lock.locked"
|
||||
|
||||
@@ -29,7 +29,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "on"
|
||||
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"
|
||||
}
|
||||
main "switch"
|
||||
details "switch"
|
||||
|
||||
@@ -24,7 +24,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("motion", "device.motion", width: 2, height: 2) {
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC")
|
||||
}
|
||||
main "motion"
|
||||
details "motion"
|
||||
|
||||
@@ -24,7 +24,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("presence", "device.presence", width: 2, height: 2) {
|
||||
state("not present", label:'not present', icon:"st.presence.tile.not-present", backgroundColor:"#ffffff")
|
||||
state("present", label:'present', icon:"st.presence.tile.present", backgroundColor:"#53a7c0")
|
||||
state("present", label:'present', icon:"st.presence.tile.present", backgroundColor:"#00A0DC")
|
||||
}
|
||||
main "presence"
|
||||
details "presence"
|
||||
|
||||
@@ -31,7 +31,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
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"
|
||||
}
|
||||
main "switch"
|
||||
details "switch"
|
||||
|
||||
@@ -35,8 +35,8 @@ metadata {
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2) {
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#79b821"
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#00A0DC"
|
||||
state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff"
|
||||
}
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 2, width: 1, inactiveLabel: false) {
|
||||
|
||||
@@ -79,8 +79,8 @@ metadata {
|
||||
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||
state "off", label:'${name}', action:"thermostat.emergencyHeat", backgroundColor:"#ffffff"
|
||||
state "emergencyHeat", label:'${name}', action:"thermostat.heat", backgroundColor:"#e86d13"
|
||||
state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#ffc000"
|
||||
state "cool", label:'${name}', action:"thermostat.off", backgroundColor:"#269bd2"
|
||||
state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#e86d13"
|
||||
state "cool", label:'${name}', action:"thermostat.off", backgroundColor:"#00A0DC"
|
||||
}
|
||||
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
|
||||
state "fanAuto", label:'${name}', action:"thermostat.fanOn", backgroundColor:"#ffffff"
|
||||
|
||||
@@ -24,7 +24,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"
|
||||
}
|
||||
|
||||
main "water"
|
||||
|
||||
@@ -37,7 +37,7 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
|
||||
state("present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0")
|
||||
state("present", labelIcon:"st.presence.tile.present", backgroundColor:"#00A0DC")
|
||||
state("not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff")
|
||||
}
|
||||
valueTile("inRange", "device.inRangeFriendly", inactiveLabel: true, height:1, width:3, decoration: "flat") {
|
||||
|
||||
@@ -38,7 +38,7 @@ metadata {
|
||||
// Main
|
||||
standardTile("main", "device.status", width: 1, height: 1, canChangeIcon: true) {
|
||||
state "paused", label:'Paused', action:"music Player.play", icon:"st.Electronics.electronics19", nextState:"playing", backgroundColor:"#ffffff"
|
||||
state "playing", label:'Playing', action:"music Player.pause", icon:"st.Electronics.electronics19", nextState:"paused", backgroundColor:"#79b821"
|
||||
state "playing", label:'Playing', action:"music Player.pause", icon:"st.Electronics.electronics19", nextState:"paused", backgroundColor:"#00A0DC"
|
||||
}
|
||||
|
||||
// Row 1
|
||||
|
||||
@@ -34,8 +34,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGK", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.contact", key:"PRIMARY_CONTROL") {
|
||||
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#e86d13")
|
||||
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#00a0dc")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
@@ -44,7 +44,7 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, , width: 2, height: 2, decoration: "flat") {
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGK", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.contact", key:"PRIMARY_CONTROL") {
|
||||
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#e86d13")
|
||||
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#00a0dc")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
@@ -41,7 +41,7 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, , width: 2, height: 2, decoration: "flat") {
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
|
||||
@@ -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:"#79b821")
|
||||
attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#ffa81e")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||
}
|
||||
}
|
||||
|
||||
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:"#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) {
|
||||
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: ""
|
||||
}
|
||||
@@ -38,8 +39,8 @@ metadata {
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#00a0dc")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#cccccc")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -112,10 +112,10 @@ metadata {
|
||||
multiAttributeTile(name:"dtlsDoorCtrl", type:"generic", width:6, height:4) {tileAttribute("device.doorState", key: "PRIMARY_CONTROL")
|
||||
{
|
||||
attributeState "unknown", label: '${name}', action:"openDoor", icon: "st.secondary.tools", nextState:"Sent"
|
||||
attributeState "open", label: '${name}', action:"closeDoor", icon: "st.doors.garage.garage-open", backgroundColor: "#0000ff", nextState:"Sent"
|
||||
attributeState "opening", label: '${name}', action:"closeDoor", icon: "st.doors.garage.garage-opening", backgroundColor: "#ffa81e"
|
||||
attributeState "closed", label: '${name}', action:"openDoor", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821", nextState:"Sent"
|
||||
attributeState "closing", label: '${name}', action:"openDoor", icon: "st.doors.garage.garage-closing", backgroundColor: "#ffa81e"
|
||||
attributeState "open", label: '${name}', action:"closeDoor", icon: "st.doors.garage.garage-open", backgroundColor: "#00A0DC", nextState:"Sent"
|
||||
attributeState "opening", label: '${name}', action:"closeDoor", icon: "st.doors.garage.garage-opening", backgroundColor: "#00A0DC"
|
||||
attributeState "closed", label: '${name}', action:"openDoor", icon: "st.doors.garage.garage-closed", backgroundColor: "#ffffff", nextState:"Sent"
|
||||
attributeState "closing", label: '${name}', action:"openDoor", icon: "st.doors.garage.garage-closing", backgroundColor: "#ffffff"
|
||||
attributeState "jammed", label: '${name}', action:"closeDoorHiI", icon: "st.doors.garage.garage-open", backgroundColor: "#ff0000", nextState:"Sent"
|
||||
attributeState "forced close", label: "forced", action:"openDoor", icon: "st.doors.garage.garage-closed", backgroundColor: "#ff8000", nextState:"Sent"
|
||||
attributeState "fault", label: 'FAULT', action:"openDoor", icon: "st.secondary.tools", backgroundColor: "#ff0000", nextState:"Sent"
|
||||
|
||||
@@ -24,7 +24,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("sleeping", "device.sleeping", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false) {
|
||||
state("sleeping", label: "Sleeping", icon:"st.Bedroom.bedroom12", backgroundColor:"#ffffff")
|
||||
state("not sleeping", label: "Awake", icon:"st.Health & Wellness.health12", backgroundColor:"#79b821")
|
||||
state("not sleeping", label: "Awake", icon:"st.Health & Wellness.health12", backgroundColor:"#00A0DC")
|
||||
}
|
||||
standardTile("steps", "device.steps", width: 2, height: 2, canChangeIcon: false, canChangeBackground: false) {
|
||||
state("steps", label: '${currentValue} Steps', icon:"st.Health & Wellness.health11", backgroundColor:"#ffffff")
|
||||
|
||||
@@ -40,10 +40,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"
|
||||
|
||||
@@ -122,13 +122,13 @@ metadata {
|
||||
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: "#52c435"
|
||||
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: "#f2a51f"
|
||||
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: "#888888"
|
||||
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"
|
||||
}
|
||||
@@ -144,67 +144,67 @@ metadata {
|
||||
}
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
state "z2on", label: '2', action: "z2off", 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: "#46c2e8"
|
||||
state "z3on", label: '3', action: "z3off", 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: "#46c2e8"
|
||||
state "z4on", label: '4', action: "z4off", 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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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: "#46c2e8"
|
||||
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"
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -47,9 +47,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}', icon:"st.switches.switch.on", backgroundColor:"#79b821"
|
||||
state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#00A0DC"
|
||||
state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff"
|
||||
}
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 2, width: 1, inactiveLabel: false) {
|
||||
|
||||
@@ -15,6 +15,7 @@ metadata {
|
||||
definition (name: "Aeon Key Fob", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Button"
|
||||
capability "Holdable Button"
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
@@ -36,14 +37,14 @@ 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 2 pushed", label: "pushed #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
|
||||
state "button 3 pushed", label: "pushed #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
|
||||
state "button 4 pushed", label: "pushed #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
|
||||
state "button 1 held", label: "held #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffa81e"
|
||||
state "button 2 held", label: "held #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffa81e"
|
||||
state "button 3 held", label: "held #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffa81e"
|
||||
state "button 4 held", label: "held #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffa81e"
|
||||
state "button 1 pushed", label: "pushed #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
|
||||
state "button 2 pushed", label: "pushed #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
|
||||
state "button 3 pushed", label: "pushed #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
|
||||
state "button 4 pushed", label: "pushed #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
|
||||
state "button 1 held", label: "held #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13"
|
||||
state "button 2 held", label: "held #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13"
|
||||
state "button 3 held", label: "held #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13"
|
||||
state "button 4 held", label: "held #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13"
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
@@ -118,3 +119,16 @@ def configure() {
|
||||
log.debug("Sending configuration: $cmd")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
def installed() {
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
sendEvent(name: "numberOfButtons", value: 4)
|
||||
}
|
||||
|
||||
@@ -37,9 +37,9 @@ metadata {
|
||||
}
|
||||
|
||||
standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00a0dc", nextState:"turningOff"
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00a0dc", nextState:"turningOff"
|
||||
state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
@@ -15,6 +15,7 @@ metadata {
|
||||
definition (name: "Aeon Minimote", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Button"
|
||||
capability "Holdable Button"
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
|
||||
@@ -107,3 +108,16 @@ def configure() {
|
||||
log.debug("Sending configuration: $cmds")
|
||||
return cmds
|
||||
}
|
||||
|
||||
|
||||
def installed() {
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
sendEvent(name: "numberOfButtons", value: 4)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -45,7 +45,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("energy", "device.energy", decoration: "flat") {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
@@ -60,7 +60,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") {
|
||||
@@ -78,7 +78,7 @@ metadata {
|
||||
|
||||
(1..4).each { n ->
|
||||
standardTile("switch$n", "switch$n", canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "off$n", icon: "st.switches.switch.on", backgroundColor: "#79b821"
|
||||
state "on", label: '${name}', action: "off$n", icon: "st.switches.switch.on", backgroundColor: "#00a0dc"
|
||||
state "off", label: '${name}', action: "on$n", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
valueTile("power$n", "power$n", decoration: "flat") {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "Arrival Sensor HA", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Tone"
|
||||
@@ -40,7 +39,7 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
|
||||
state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0"
|
||||
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") {
|
||||
@@ -60,7 +59,7 @@ def updated() {
|
||||
}
|
||||
|
||||
def configure() {
|
||||
def cmds = zigbee.configureReporting(0x0001, 0x0020, 0x20, 20, 20, 0x01)
|
||||
def cmds = zigbee.batteryConfig(20, 20, 0x01)
|
||||
log.debug "configure -- cmds: ${cmds}"
|
||||
return cmds
|
||||
}
|
||||
@@ -152,7 +151,7 @@ private handlePresenceEvent(present) {
|
||||
|
||||
private startTimer() {
|
||||
log.debug "Scheduling periodic timer"
|
||||
schedule("0 * * * * ?", checkPresenceCallback)
|
||||
runEvery1Minute("checkPresenceCallback")
|
||||
}
|
||||
|
||||
private stopTimer() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -27,7 +27,7 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Music Player"
|
||||
capability "Polling"
|
||||
capability "Health Check"
|
||||
|
||||
/**
|
||||
* Define all commands, ie, if you have a custom action not
|
||||
@@ -67,10 +67,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"
|
||||
@@ -236,7 +236,33 @@ def parse(String event) {
|
||||
* @return action(s) to take or null
|
||||
*/
|
||||
def installed() {
|
||||
onAction("refresh")
|
||||
// Notify health check about this device with timeout interval 12 minutes
|
||||
sendEvent(name: "checkInterval", value: 12 * 60, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
|
||||
startPoll()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by health check if no events been generated in the last 12 minutes
|
||||
* If device doesn't respond it will be marked offline (not available)
|
||||
*/
|
||||
def ping() {
|
||||
TRACE("ping")
|
||||
boseSendGetNowPlaying()
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a 2 minute poll of the device to refresh the
|
||||
* tiles so the user gets the correct information.
|
||||
*/
|
||||
def startPoll() {
|
||||
TRACE("startPoll")
|
||||
unschedule()
|
||||
// Schedule 2 minute polling of speaker status (song average length is 3-4 minutes)
|
||||
def sec = Math.round(Math.floor(Math.random() * 60))
|
||||
//def cron = "$sec 0/5 * * * ?" // every 5 min
|
||||
def cron = "$sec 0/2 * * * ?" // every 2 min
|
||||
log.debug "schedule('$cron', boseSendGetNowPlaying)"
|
||||
schedule(cron, boseSendGetNowPlaying)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,14 +342,6 @@ def onAction(String user, data=null) {
|
||||
return actions
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every so often (every 5 minutes actually) to refresh the
|
||||
* tiles so the user gets the correct information.
|
||||
*/
|
||||
def poll() {
|
||||
return boseRefreshNowPlaying()
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins this speaker into the everywhere zone
|
||||
*/
|
||||
@@ -837,6 +855,10 @@ def boseRefreshNowPlaying(delay=0) {
|
||||
return boseGET("/now_playing")
|
||||
}
|
||||
|
||||
def boseSendGetNowPlaying() {
|
||||
sendHubCommand(boseGET("/now_playing"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the list of presets
|
||||
*
|
||||
@@ -1014,4 +1036,8 @@ def boseGetDeviceID() {
|
||||
*/
|
||||
def getDeviceIP() {
|
||||
return parent.resolveDNI2Address(device.deviceNetworkId)
|
||||
}
|
||||
|
||||
def TRACE(text) {
|
||||
log.trace "${text}"
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* CentraLite Dimmer
|
||||
*
|
||||
* Author: SmartThings
|
||||
* Date: 2013-12-04
|
||||
*/
|
||||
//DEPRECATED - Using the generic DTH for this device. Users need to be moved before deleting this DTH
|
||||
metadata {
|
||||
definition (name: "CentraLite Dimmer", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Switch Level"
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Power Meter"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
// status messages
|
||||
status "on": "on/off: 1"
|
||||
status "off": "on/off: 0"
|
||||
|
||||
// reply messages
|
||||
reply "zcl on-off on": "on/off: 1"
|
||||
reply "zcl on-off off": "on/off: 0"
|
||||
}
|
||||
|
||||
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", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
tileAttribute ("power", key: "SECONDARY_CONTROL") {
|
||||
attributeState "power", label:'${currentValue} W'
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main "switch"
|
||||
details(["switch","refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "Parse description $description"
|
||||
def name = null
|
||||
def value = null
|
||||
if (description?.startsWith("catchall:")) {
|
||||
def msg = zigbee.parse(description)
|
||||
log.trace msg
|
||||
log.trace "data: $msg.data"
|
||||
} else if (description?.startsWith("read attr -")) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
log.debug "Read attr: $description"
|
||||
if (descMap.cluster == "0006" && descMap.attrId == "0000") {
|
||||
name = "switch"
|
||||
value = descMap.value.endsWith("01") ? "on" : "off"
|
||||
} else {
|
||||
def reportValue = description.split(",").find {it.split(":")[0].trim() == "value"}?.split(":")[1].trim()
|
||||
name = "power"
|
||||
// assume 16 bit signed for encoding and power divisor is 10
|
||||
value = Integer.parseInt(reportValue, 16) / 10
|
||||
}
|
||||
} else if (description?.startsWith("on/off:")) {
|
||||
log.debug "Switch command"
|
||||
name = "switch"
|
||||
value = description?.endsWith(" 1") ? "on" : "off"
|
||||
}
|
||||
|
||||
def result = createEvent(name: name, value: value)
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
|
||||
// Commands to device
|
||||
def on() {
|
||||
[
|
||||
'zcl on-off on',
|
||||
'delay 200',
|
||||
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
|
||||
'delay 500'
|
||||
]
|
||||
}
|
||||
|
||||
def off() {
|
||||
[
|
||||
'zcl on-off off',
|
||||
'delay 200',
|
||||
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
|
||||
'delay 500'
|
||||
]
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
log.trace "setLevel($value)"
|
||||
sendEvent(name: "level", value: value)
|
||||
def level = hexString(Math.round(value * 255/100))
|
||||
def cmd = "st cmd 0x${device.deviceNetworkId} 1 8 4 {${level} 2000}"
|
||||
log.debug cmd
|
||||
cmd
|
||||
}
|
||||
|
||||
def meter() {
|
||||
"st rattr 0x${device.deviceNetworkId} 1 0xB04 0x50B"
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
"st rattr 0x${device.deviceNetworkId} 1 0xB04 0x50B"
|
||||
}
|
||||
|
||||
def configure() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 8 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 1 0xB04 {${device.zigbeeId}} {}"
|
||||
]
|
||||
}
|
||||
|
||||
private hex(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
@@ -55,13 +55,13 @@ metadata {
|
||||
state "fanOn", label:'${name}', action:"thermostat.setThermostatFanMode"
|
||||
}
|
||||
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#d04e00"
|
||||
state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#e86d13"
|
||||
}
|
||||
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "heat", label:'${currentValue}° heat', unit:"F", backgroundColor:"#ffffff"
|
||||
}
|
||||
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "setCoolingSetpoint", action:"thermostat.setCoolingSetpoint", backgroundColor: "#1e9cbb"
|
||||
state "setCoolingSetpoint", action:"thermostat.setCoolingSetpoint", backgroundColor: "#00a0dc"
|
||||
}
|
||||
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "cool", label:'${currentValue}° cool', unit:"F", backgroundColor:"#ffffff"
|
||||
@@ -81,51 +81,47 @@ metadata {
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
log.debug "Parse description $description"
|
||||
def map = [:]
|
||||
if (description?.startsWith("read attr -")) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
log.debug "Desc Map: $descMap"
|
||||
if (descMap.cluster == "0201" && descMap.attrId == "0000") {
|
||||
List result = []
|
||||
def descMap = zigbee.parseDescriptionAsMap(description)
|
||||
log.debug "Desc Map: $descMap"
|
||||
List attrData = [[cluster: descMap.cluster ,attrId: descMap.attrId, value: descMap.value]]
|
||||
descMap.additionalAttrs.each {
|
||||
attrData << [cluster: descMap.cluster, attrId: it.attrId, value: it.value]
|
||||
}
|
||||
attrData.each {
|
||||
def map = [:]
|
||||
if (it.cluster == "0201" && it.attrId == "0000") {
|
||||
log.debug "TEMP"
|
||||
map.name = "temperature"
|
||||
map.value = getTemperature(descMap.value)
|
||||
map.value = getTemperature(it.value)
|
||||
map.unit = temperatureScale
|
||||
} else if (descMap.cluster == "0201" && descMap.attrId == "0011") {
|
||||
} else if (it.cluster == "0201" && it.attrId == "0011") {
|
||||
log.debug "COOLING SETPOINT"
|
||||
map.name = "coolingSetpoint"
|
||||
map.value = getTemperature(descMap.value)
|
||||
map.value = getTemperature(it.value)
|
||||
map.unit = temperatureScale
|
||||
} else if (descMap.cluster == "0201" && descMap.attrId == "0012") {
|
||||
} else if (it.cluster == "0201" && it.attrId == "0012") {
|
||||
log.debug "HEATING SETPOINT"
|
||||
map.name = "heatingSetpoint"
|
||||
map.value = getTemperature(descMap.value)
|
||||
map.value = getTemperature(it.value)
|
||||
map.unit = temperatureScale
|
||||
} else if (descMap.cluster == "0201" && descMap.attrId == "001c") {
|
||||
} else if (it.cluster == "0201" && it.attrId == "001c") {
|
||||
log.debug "MODE"
|
||||
map.name = "thermostatMode"
|
||||
map.value = getModeMap()[descMap.value]
|
||||
} else if (descMap.cluster == "0202" && descMap.attrId == "0000") {
|
||||
map.value = getModeMap()[it.value]
|
||||
} else if (it.cluster == "0202" && it.attrId == "0000") {
|
||||
log.debug "FAN MODE"
|
||||
map.name = "thermostatFanMode"
|
||||
map.value = getFanModeMap()[descMap.value]
|
||||
map.value = getFanModeMap()[it.value]
|
||||
}
|
||||
if (map) {
|
||||
result << createEvent(map)
|
||||
}
|
||||
log.debug "Parse returned $map"
|
||||
}
|
||||
|
||||
def result = null
|
||||
if (map) {
|
||||
result = createEvent(map)
|
||||
}
|
||||
log.debug "Parse returned $map"
|
||||
return result
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
|
||||
def getModeMap() { [
|
||||
"00":"off",
|
||||
"03":"cool",
|
||||
|
||||
@@ -16,7 +16,7 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Button"
|
||||
capability "Actuator"
|
||||
capability "Actuator"
|
||||
|
||||
//fingerprint deviceId: "0x1200", inClusters: "0x77 0x86 0x75 0x73 0x85 0x72 0xEF", outClusters: "0x26"
|
||||
}
|
||||
@@ -36,7 +36,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.Home.home30", backgroundColor: "#ffffff"
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.Home.home30", backgroundColor: "#79b821"
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.Home.home30", backgroundColor: "#00a0dc"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
@@ -74,20 +74,20 @@ def off() {
|
||||
}
|
||||
|
||||
def levelup() {
|
||||
def curlevel = device.currentValue('level') as Integer
|
||||
def curlevel = device.currentValue('level') as Integer
|
||||
if (curlevel <= 90)
|
||||
setLevel(curlevel + 10);
|
||||
setLevel(curlevel + 10);
|
||||
}
|
||||
|
||||
def leveldown() {
|
||||
def curlevel = device.currentValue('level') as Integer
|
||||
def curlevel = device.currentValue('level') as Integer
|
||||
if (curlevel >= 10)
|
||||
setLevel(curlevel - 10)
|
||||
setLevel(curlevel - 10)
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
log.trace "setLevel($value)"
|
||||
sendEvent(name: "level", value: value)
|
||||
sendEvent(name: "level", value: value)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
|
||||
@@ -106,11 +106,11 @@ def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelS
|
||||
if (cmd.upDown == true) {
|
||||
Integer buttonid = 2
|
||||
leveldown()
|
||||
checkbuttonEvent(buttonid)
|
||||
checkbuttonEvent(buttonid)
|
||||
} else if (cmd.upDown == false) {
|
||||
Integer buttonid = 3
|
||||
levelup()
|
||||
checkbuttonEvent(buttonid)
|
||||
checkbuttonEvent(buttonid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,12 +140,12 @@ def buttonEvent(button) {
|
||||
def result = []
|
||||
if (button == 1) {
|
||||
def mystate = device.currentValue('switch');
|
||||
if (mystate == "on")
|
||||
if (mystate == "on")
|
||||
off()
|
||||
else
|
||||
on()
|
||||
on()
|
||||
}
|
||||
updateState("currentButton", "$button")
|
||||
updateState("currentButton", "$button")
|
||||
// update the device state, recording the button press
|
||||
result << createEvent(name: "button", value: "pushed", data: [buttonNumber: button], descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
|
||||
result
|
||||
@@ -182,3 +182,16 @@ def updateState(String name, String value) {
|
||||
state[name] = value
|
||||
device.updateDataValue(name, value)
|
||||
}
|
||||
|
||||
|
||||
def installed() {
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
sendEvent(name: "numberOfButtons", value: 3)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Connected Cree LED Bulb
|
||||
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
@@ -23,8 +23,10 @@ Works with:
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C6 Connected Cree LED Bulb with maxReportTime of 5 mins.
|
||||
Check-in interval = 12 mins
|
||||
Connected Cree LED Bulb with cloud polling it every __5min__
|
||||
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
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0000,0019"
|
||||
}
|
||||
@@ -42,9 +43,9 @@ 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.light.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
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:"#79b821", nextState:"turningOff"
|
||||
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") {
|
||||
@@ -82,7 +83,7 @@ def on() {
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
zigbee.setLevel(value) + ["delay 500"] + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report
|
||||
zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +106,7 @@ def healthPoll() {
|
||||
def configure() {
|
||||
unschedule()
|
||||
runEvery5Minutes("healthPoll")
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
// Device-Watch allows 2 check-in misses from device + ping
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -33,10 +33,10 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("toggle", "device.lock", width: 2, height: 2) {
|
||||
state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821", nextState:"unlocking"
|
||||
state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#00a0dc", nextState:"unlocking"
|
||||
state "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff", nextState:"locking"
|
||||
state "unknown", label:"unknown", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking"
|
||||
state "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#79b821"
|
||||
state "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#00a0dc"
|
||||
state "unlocking", label:'unlocking', icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
2
devicetypes/smartthings/dimmer-switch.src/.st-ignore
Normal file
2
devicetypes/smartthings/dimmer-switch.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
51
devicetypes/smartthings/dimmer-switch.src/README.md
Normal file
51
devicetypes/smartthings/dimmer-switch.src/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Z-wave Dimmer Switch
|
||||
|
||||
Local Execution on V2 Hubs
|
||||
|
||||
Works with:
|
||||
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (GE 12724)](http://products.z-wavealliance.org/products/1197)
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (Toggle) (GE 12729)](http://products.z-wavealliance.org/products/1201)
|
||||
* [GE Z-Wave Plug-in Smart Dimmer (GE 12718)](http://products.z-wavealliance.org/products/1191)
|
||||
* [GE 1,000-Watt In-Wall Smart Dimmer Switch (GE 12725)](http://products.z-wavealliance.org/products/1198)
|
||||
* [GE In-Wall Smart Fan Control (GE 12730)](http://products.z-wavealliance.org/products/1202)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Switch Level** - it's defined to accept two parameters, the level and the rate of dimming
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Indicator** - gives you the ability to set the indicator LED light on a Z-Wave switch
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **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 Smart Dimmers (In-Wall, In-Wall(Toggle), Plug-In), 1000-watt In-Wall Smart Dimmer Switch and In-Wall Smart Fan Control 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.
|
||||
Check-in interval = 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 Dimmer/Switch Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200955890-Troubleshooting-GE-in-wall-switch-or-dimmer-won-t-respond-to-commands-or-automations-Z-Wave-)
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (GE 12724) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200902600-GE-In-Wall-Paddle-Dimmer-Switch-GE-12724-Z-Wave-)
|
||||
* [GE Z-Wave In-Wall Smart Dimmer (Toggle) (GE 12729) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/207568463-GE-In-Wall-Smart-Toggle-Dimmer-GE-12729-Z-Wave-)
|
||||
* [GE Z-Wave Plug-in Smart Dimmer (GE 12718) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202088474-GE-Plug-In-Smart-Dimmer-GE-12718-Z-Wave-)
|
||||
* [GE 1,000-Watt In-Wall Smart Dimmer Switch (GE 12725) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200879274)
|
||||
* [GE In-Wall Smart Fan Control (GE 12730) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200879274)
|
||||
@@ -20,10 +20,13 @@ metadata {
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "Z-Wave Wall Dimmer"
|
||||
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "Z-Wave Wall Dimmer"
|
||||
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "Z-Wave Plug-In Dimmer"
|
||||
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "GE In-Wall Smart Dimmer "
|
||||
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "GE In-Wall Smart Dimmer "
|
||||
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "GE Plug-In Smart Dimmer "
|
||||
fingerprint mfr:"0063", prod:"4944", model:"3034", deviceJoinName: "GE In-Wall Smart Fan Control"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -51,9 +54,9 @@ 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", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00a0dc", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", 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"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
@@ -82,6 +85,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])
|
||||
switch (ledIndicator) {
|
||||
case "on":
|
||||
indicatorWhenOn()
|
||||
@@ -215,6 +220,13 @@ def poll() {
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "refresh() is called"
|
||||
def commands = []
|
||||
@@ -226,17 +238,17 @@ def refresh() {
|
||||
}
|
||||
|
||||
void indicatorWhenOn() {
|
||||
sendEvent(name: "indicatorStatus", value: "when on", display: false)
|
||||
sendEvent(name: "indicatorStatus", value: "when on", displayed: false)
|
||||
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format()))
|
||||
}
|
||||
|
||||
void indicatorWhenOff() {
|
||||
sendEvent(name: "indicatorStatus", value: "when off", display: false)
|
||||
sendEvent(name: "indicatorStatus", value: "when off", displayed: false)
|
||||
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format()))
|
||||
}
|
||||
|
||||
void indicatorNever() {
|
||||
sendEvent(name: "indicatorStatus", value: "never", display: false)
|
||||
sendEvent(name: "indicatorStatus", value: "never", displayed: false)
|
||||
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format()))
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ metadata {
|
||||
}
|
||||
|
||||
standardTile("motion", "device.motion") {
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc")
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC")
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
@@ -23,6 +23,7 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Refresh"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Health Check"
|
||||
|
||||
command "generateEvent"
|
||||
command "raiseSetpoint"
|
||||
@@ -38,6 +39,7 @@ metadata {
|
||||
attribute "maxCoolingSetpoint", "number"
|
||||
attribute "minCoolingSetpoint", "number"
|
||||
attribute "deviceTemperatureUnit", "string"
|
||||
attribute "deviceAlive", "enum", ["true", "false"]
|
||||
}
|
||||
|
||||
tiles {
|
||||
@@ -120,6 +122,21 @@ metadata {
|
||||
|
||||
}
|
||||
|
||||
void installed() {
|
||||
// The device refreshes every 5 minutes by default so if we miss 2 refreshes we can consider it offline
|
||||
// Using 12 minutes because in testing, device health team found that there could be "jitter"
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "cloud"], displayed: false)
|
||||
}
|
||||
|
||||
// Device Watch will ping the device to proactively determine if the device has gone offline
|
||||
// If the device was online the last time we refreshed, trigger another refresh as part of the ping.
|
||||
def ping() {
|
||||
def isAlive = device.currentValue("deviceAlive") == "true" ? true : false
|
||||
if (isAlive) {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
log.debug "Parsing '${description}'"
|
||||
@@ -148,14 +165,12 @@ def generateEvent(Map results) {
|
||||
handlerName: name]
|
||||
|
||||
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) {
|
||||
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
|
||||
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger()
|
||||
isChange = isTemperatureStateChange(device, name, value.toString())
|
||||
isDisplayed = isChange
|
||||
event << [value: sendValue, unit: temperatureScale, isStateChange: isChange, displayed: isDisplayed]
|
||||
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
|
||||
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
|
||||
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
|
||||
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger()
|
||||
event << [value: sendValue, unit: temperatureScale, displayed: false]
|
||||
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
@@ -166,7 +181,11 @@ def generateEvent(Map results) {
|
||||
} else if (name=="humidity") {
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
event << [value: value.toString(), isStateChange: isChange, displayed: false, unit: "%"]
|
||||
} else {
|
||||
} else if (name == "deviceAlive") {
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
event['isStateChange'] = isChange
|
||||
event['displayed'] = false
|
||||
} else {
|
||||
isChange = isStateChange(device, name, value.toString())
|
||||
isDisplayed = isChange
|
||||
event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed]
|
||||
@@ -253,7 +272,6 @@ void setCoolingSetpoint(setpoint) {
|
||||
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
|
||||
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
|
||||
|
||||
|
||||
if (coolingSetpoint > maxCoolingSetpoint) {
|
||||
coolingSetpoint = maxCoolingSetpoint
|
||||
} else if (coolingSetpoint < minCoolingSetpoint) {
|
||||
@@ -283,7 +301,6 @@ void setCoolingSetpoint(setpoint) {
|
||||
}
|
||||
|
||||
void resumeProgram() {
|
||||
|
||||
log.debug "resumeProgram() is called"
|
||||
sendEvent("name":"thermostatStatus", "value":"resuming schedule", "description":statusText, displayed: false)
|
||||
def deviceId = device.deviceNetworkId.split(/\./).last()
|
||||
@@ -354,7 +371,6 @@ def switchFanMode() {
|
||||
}
|
||||
|
||||
def switchToFanMode(nextMode) {
|
||||
|
||||
log.debug "switching to fan mode: $nextMode"
|
||||
def returnCommand
|
||||
|
||||
@@ -520,63 +536,56 @@ def fanAuto() {
|
||||
}
|
||||
|
||||
def generateSetpointEvent() {
|
||||
|
||||
log.debug "Generate SetPoint Event"
|
||||
|
||||
def mode = device.currentValue("thermostatMode")
|
||||
log.debug "Current Mode = ${mode}"
|
||||
|
||||
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||
log.debug "Heating Setpoint = ${heatingSetpoint}"
|
||||
|
||||
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||
log.debug "Cooling Setpoint = ${coolingSetpoint}"
|
||||
|
||||
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint")
|
||||
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
|
||||
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint")
|
||||
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
|
||||
|
||||
if(location.temperatureScale == "C")
|
||||
{
|
||||
maxHeatingSetpoint = roundC(maxHeatingSetpoint)
|
||||
maxCoolingSetpoint = roundC(maxCoolingSetpoint)
|
||||
minHeatingSetpoint = roundC(minHeatingSetpoint)
|
||||
minCoolingSetpoint = roundC(minCoolingSetpoint)
|
||||
heatingSetpoint = roundC(heatingSetpoint)
|
||||
coolingSetpoint = roundC(coolingSetpoint)
|
||||
if(location.temperatureScale == "C") {
|
||||
maxHeatingSetpoint = maxHeatingSetpoint > 40 ? roundC(convertFtoC(maxHeatingSetpoint)) : roundC(maxHeatingSetpoint)
|
||||
maxCoolingSetpoint = maxCoolingSetpoint > 40 ? roundC(convertFtoC(maxCoolingSetpoint)) : roundC(maxCoolingSetpoint)
|
||||
minHeatingSetpoint = minHeatingSetpoint > 40 ? roundC(convertFtoC(minHeatingSetpoint)) : roundC(minHeatingSetpoint)
|
||||
minCoolingSetpoint = minCoolingSetpoint > 40 ? roundC(convertFtoC(minCoolingSetpoint)) : roundC(minCoolingSetpoint)
|
||||
heatingSetpoint = heatingSetpoint > 40 ? roundC(convertFtoC(heatingSetpoint)) : roundC(heatingSetpoint)
|
||||
coolingSetpoint = coolingSetpoint > 40 ? roundC(convertFtoC(coolingSetpoint)) : roundC(coolingSetpoint)
|
||||
} else {
|
||||
maxHeatingSetpoint = maxHeatingSetpoint < 40 ? roundC(convertCtoF(maxHeatingSetpoint)) : maxHeatingSetpoint
|
||||
maxCoolingSetpoint = maxCoolingSetpoint < 40 ? roundC(convertCtoF(maxCoolingSetpoint)) : maxCoolingSetpoint
|
||||
minHeatingSetpoint = minHeatingSetpoint < 40 ? roundC(convertCtoF(minHeatingSetpoint)) : minHeatingSetpoint
|
||||
minCoolingSetpoint = minCoolingSetpoint < 40 ? roundC(convertCtoF(minCoolingSetpoint)) : minCoolingSetpoint
|
||||
heatingSetpoint = heatingSetpoint < 40 ? roundC(convertCtoF(heatingSetpoint)) : heatingSetpoint
|
||||
coolingSetpoint = coolingSetpoint < 40 ? roundC(convertCtoF(coolingSetpoint)) : coolingSetpoint
|
||||
}
|
||||
|
||||
log.debug "Current Mode = ${mode}"
|
||||
log.debug "Heating Setpoint = ${heatingSetpoint}"
|
||||
log.debug "Cooling Setpoint = ${coolingSetpoint}"
|
||||
|
||||
sendEvent("name":"maxHeatingSetpoint", "value":maxHeatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"maxCoolingSetpoint", "value":maxCoolingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"minHeatingSetpoint", "value":minHeatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"minCoolingSetpoint", "value":minCoolingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
if (mode == "heat") {
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
}
|
||||
else if (mode == "cool") {
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
} else if (mode == "auto") {
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":"Auto")
|
||||
|
||||
} else if (mode == "off") {
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":"Off")
|
||||
|
||||
} else if (mode == "auxHeatOnly") {
|
||||
|
||||
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void raiseSetpoint() {
|
||||
@@ -585,21 +594,31 @@ void raiseSetpoint() {
|
||||
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint")
|
||||
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
|
||||
|
||||
|
||||
if (mode == "off" || mode == "auto") {
|
||||
log.warn "this mode: $mode does not allow raiseSetpoint"
|
||||
} else {
|
||||
|
||||
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||
def thermostatSetpoint = device.currentValue("thermostatSetpoint")
|
||||
|
||||
if (location.temperatureScale == "C") {
|
||||
maxHeatingSetpoint = maxHeatingSetpoint > 40 ? convertFtoC(maxHeatingSetpoint) : maxHeatingSetpoint
|
||||
maxCoolingSetpoint = maxCoolingSetpoint > 40 ? convertFtoC(maxCoolingSetpoint) : maxCoolingSetpoint
|
||||
heatingSetpoint = heatingSetpoint > 40 ? convertFtoC(heatingSetpoint) : heatingSetpoint
|
||||
coolingSetpoint = coolingSetpoint > 40 ? convertFtoC(coolingSetpoint) : coolingSetpoint
|
||||
thermostatSetpoint = thermostatSetpoint > 40 ? convertFtoC(thermostatSetpoint) : thermostatSetpoint
|
||||
} else {
|
||||
maxHeatingSetpoint = maxHeatingSetpoint < 40 ? convertCtoF(maxHeatingSetpoint) : maxHeatingSetpoint
|
||||
maxCoolingSetpoint = maxCoolingSetpoint < 40 ? convertCtoF(maxCoolingSetpoint) : maxCoolingSetpoint
|
||||
heatingSetpoint = heatingSetpoint < 40 ? convertCtoF(heatingSetpoint) : heatingSetpoint
|
||||
coolingSetpoint = coolingSetpoint < 40 ? convertCtoF(coolingSetpoint) : coolingSetpoint
|
||||
thermostatSetpoint = thermostatSetpoint < 40 ? convertCtoF(thermostatSetpoint) : thermostatSetpoint
|
||||
}
|
||||
|
||||
log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
|
||||
|
||||
if (device.latestState('thermostatSetpoint')) {
|
||||
targetvalue = device.latestState('thermostatSetpoint').value
|
||||
targetvalue = location.temperatureScale == "F"? targetvalue.toInteger() : targetvalue.toDouble()
|
||||
} else {
|
||||
targetvalue = 0
|
||||
}
|
||||
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0
|
||||
targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5
|
||||
|
||||
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue > maxHeatingSetpoint) {
|
||||
@@ -622,20 +641,29 @@ void lowerSetpoint() {
|
||||
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint")
|
||||
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
|
||||
|
||||
|
||||
if (mode == "off" || mode == "auto") {
|
||||
log.warn "this mode: $mode does not allow lowerSetpoint"
|
||||
} else {
|
||||
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||
def thermostatSetpoint = device.currentValue("thermostatSetpoint")
|
||||
log.debug "lowerSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
|
||||
if (device.latestState('thermostatSetpoint')) {
|
||||
targetvalue = device.latestState('thermostatSetpoint').value
|
||||
targetvalue = location.temperatureScale == "F"? targetvalue.toInteger() : targetvalue.toDouble()
|
||||
|
||||
if (location.temperatureScale == "C") {
|
||||
minHeatingSetpoint = minHeatingSetpoint > 40 ? convertFtoC(minHeatingSetpoint) : minHeatingSetpoint
|
||||
minCoolingSetpoint = minCoolingSetpoint > 40 ? convertFtoC(minCoolingSetpoint) : minCoolingSetpoint
|
||||
heatingSetpoint = heatingSetpoint > 40 ? convertFtoC(heatingSetpoint) : heatingSetpoint
|
||||
coolingSetpoint = coolingSetpoint > 40 ? convertFtoC(coolingSetpoint) : coolingSetpoint
|
||||
thermostatSetpoint = thermostatSetpoint > 40 ? convertFtoC(thermostatSetpoint) : thermostatSetpoint
|
||||
} else {
|
||||
targetvalue = 0
|
||||
minHeatingSetpoint = minHeatingSetpoint < 40 ? convertCtoF(minHeatingSetpoint) : minHeatingSetpoint
|
||||
minCoolingSetpoint = minCoolingSetpoint < 40 ? convertCtoF(minCoolingSetpoint) : minCoolingSetpoint
|
||||
heatingSetpoint = heatingSetpoint < 40 ? convertCtoF(heatingSetpoint) : heatingSetpoint
|
||||
coolingSetpoint = coolingSetpoint < 40 ? convertCtoF(coolingSetpoint) : coolingSetpoint
|
||||
thermostatSetpoint = thermostatSetpoint < 40 ? convertCtoF(thermostatSetpoint) : thermostatSetpoint
|
||||
}
|
||||
log.debug "lowerSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
|
||||
|
||||
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0
|
||||
targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5
|
||||
|
||||
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue < minHeatingSetpoint) {
|
||||
@@ -653,7 +681,6 @@ void lowerSetpoint() {
|
||||
|
||||
//called by raiseSetpoint() and lowerSetpoint()
|
||||
void alterSetpoint(temp) {
|
||||
|
||||
def mode = device.currentValue("thermostatMode")
|
||||
|
||||
if (mode == "off" || mode == "auto") {
|
||||
@@ -666,6 +693,18 @@ void alterSetpoint(temp) {
|
||||
def targetHeatingSetpoint
|
||||
def targetCoolingSetpoint
|
||||
|
||||
def temperatureScaleHasChanged = false
|
||||
|
||||
if (location.temperatureScale == "C") {
|
||||
if ( heatingSetpoint > 40.0 || coolingSetpoint > 40.0 ) {
|
||||
temperatureScaleHasChanged = true
|
||||
}
|
||||
} else {
|
||||
if ( heatingSetpoint < 40.0 || coolingSetpoint < 40.0 ) {
|
||||
temperatureScaleHasChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
//step1: check thermostatMode, enforce limits before sending request to cloud
|
||||
if (mode == "heat" || mode == "auxHeatOnly"){
|
||||
if (temp.value > coolingSetpoint){
|
||||
@@ -707,17 +746,18 @@ void alterSetpoint(temp) {
|
||||
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
|
||||
}
|
||||
}
|
||||
|
||||
if ( temperatureScaleHasChanged )
|
||||
generateSetpointEvent()
|
||||
generateStatusEvent()
|
||||
}
|
||||
}
|
||||
|
||||
def generateStatusEvent() {
|
||||
|
||||
def mode = device.currentValue("thermostatMode")
|
||||
def heatingSetpoint = device.currentValue("heatingSetpoint")
|
||||
def coolingSetpoint = device.currentValue("coolingSetpoint")
|
||||
def temperature = device.currentValue("temperature")
|
||||
|
||||
def statusText
|
||||
|
||||
log.debug "Generate Status Event for Mode = ${mode}"
|
||||
@@ -727,36 +767,25 @@ def generateStatusEvent() {
|
||||
log.debug "HVAC Mode = ${mode}"
|
||||
|
||||
if (mode == "heat") {
|
||||
|
||||
if (temperature >= heatingSetpoint)
|
||||
statusText = "Right Now: Idle"
|
||||
else
|
||||
statusText = "Heating to ${heatingSetpoint} ${location.temperatureScale}"
|
||||
|
||||
} else if (mode == "cool") {
|
||||
|
||||
if (temperature <= coolingSetpoint)
|
||||
statusText = "Right Now: Idle"
|
||||
else
|
||||
statusText = "Cooling to ${coolingSetpoint} ${location.temperatureScale}"
|
||||
|
||||
} else if (mode == "auto") {
|
||||
|
||||
statusText = "Right Now: Auto"
|
||||
|
||||
} else if (mode == "off") {
|
||||
|
||||
statusText = "Right Now: Off"
|
||||
|
||||
} else if (mode == "auxHeatOnly") {
|
||||
|
||||
statusText = "Emergency Heat"
|
||||
|
||||
} else {
|
||||
|
||||
statusText = "?"
|
||||
|
||||
}
|
||||
|
||||
log.debug "Generate Status Event = ${statusText}"
|
||||
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true)
|
||||
}
|
||||
@@ -770,7 +799,7 @@ def roundC (tempC) {
|
||||
}
|
||||
|
||||
def convertFtoC (tempF) {
|
||||
return String.format("%.1f", (Math.round(((tempF - 32)*(5/9)) * 2))/2)
|
||||
return ((Math.round(((tempF - 32)*(5/9)) * 2))/2).toDouble()
|
||||
}
|
||||
|
||||
def convertCtoF (tempC) {
|
||||
|
||||
@@ -53,7 +53,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") {
|
||||
|
||||
@@ -33,7 +33,7 @@ metadata {
|
||||
multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.water", key: "PRIMARY_CONTROL") {
|
||||
attributeState "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
|
||||
attributeState "wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
|
||||
attributeState "wet", icon:"st.alarm.water.wet", backgroundColor:"#00a0dc"
|
||||
}
|
||||
}
|
||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Configuration"
|
||||
capability "Configuration"
|
||||
capability "Color Control"
|
||||
capability "Power Meter"
|
||||
|
||||
|
||||
command "getDeviceData"
|
||||
command "softwhite"
|
||||
command "daylight"
|
||||
@@ -54,12 +54,12 @@
|
||||
command "setAdjustedColor"
|
||||
command "setWhiteLevel"
|
||||
command "test"
|
||||
|
||||
|
||||
attribute "whiteLevel", "string"
|
||||
|
||||
|
||||
fingerprint deviceId: "0x1101", inClusters: "0x27,0x72,0x86,0x26,0x60,0x70,0x32,0x31,0x85,0x33"
|
||||
}
|
||||
|
||||
|
||||
simulator {
|
||||
status "on": "command: 2003, payload: FF"
|
||||
status "off": "command: 2003, payload: 00"
|
||||
@@ -84,14 +84,14 @@
|
||||
}
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false) {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
}
|
||||
controlTile("whiteSliderControl", "device.whiteLevel", "slider", height: 1, width: 3, inactiveLabel: false) {
|
||||
state "whiteLevel", action:"setWhiteLevel", label:'White Level'
|
||||
}
|
||||
standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.illuminance.illuminance.bright", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.illuminance.illuminance.bright", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.illuminance.illuminance.dark", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'${name}', icon:"st.illuminance.illuminance.bright", backgroundColor:"#79b821"
|
||||
state "turningOn", label:'${name}', icon:"st.illuminance.illuminance.bright", backgroundColor:"#00A0DC"
|
||||
state "turningOff", label:'${name}', icon:"st.illuminance.illuminance.dark", backgroundColor:"#ffffff"
|
||||
}
|
||||
valueTile("power", "device.power", decoration: "flat") {
|
||||
@@ -183,24 +183,24 @@
|
||||
valueTile("hue", "device.hue", inactiveLabel: false, decoration: "flat") {
|
||||
state "hue", label: 'Hue ${currentValue} '
|
||||
}
|
||||
|
||||
|
||||
main(["switch"])
|
||||
details(["switch",
|
||||
"levelSliderControl",
|
||||
"rgbSelector",
|
||||
"whiteSliderControl",
|
||||
details(["switch",
|
||||
"levelSliderControl",
|
||||
"rgbSelector",
|
||||
"whiteSliderControl",
|
||||
/*"softwhite",
|
||||
"daylight",
|
||||
"warmwhite",
|
||||
"red",
|
||||
"green",
|
||||
"red",
|
||||
"green",
|
||||
"blue",
|
||||
"white",
|
||||
"cyan",
|
||||
"magenta",
|
||||
"orange",
|
||||
"purple",
|
||||
"yellow",
|
||||
"yellow",
|
||||
"fireplace",
|
||||
"storm",
|
||||
"deepfade",
|
||||
@@ -214,7 +214,7 @@
|
||||
|
||||
def setAdjustedColor(value) {
|
||||
log.debug "setAdjustedColor: ${value}"
|
||||
|
||||
|
||||
toggleTiles("off") //turn off the hard color tiles
|
||||
|
||||
def level = device.latestValue("level")
|
||||
@@ -223,19 +223,19 @@ def setAdjustedColor(value) {
|
||||
log.debug "level is: ${level}"
|
||||
value.level = level
|
||||
|
||||
def c = hexToRgb(value.hex)
|
||||
def c = hexToRgb(value.hex)
|
||||
value.rh = hex(c.r * (level/100))
|
||||
value.gh = hex(c.g * (level/100))
|
||||
value.bh = hex(c.b * (level/100))
|
||||
|
||||
setColor(value)
|
||||
|
||||
setColor(value)
|
||||
}
|
||||
|
||||
def setColor(value) {
|
||||
log.debug "setColor: ${value}"
|
||||
log.debug "hue is: ${value.hue}"
|
||||
log.debug "saturation is: ${value.saturation}"
|
||||
|
||||
|
||||
if (value.size() < 8)
|
||||
toggleTiles("off")
|
||||
|
||||
@@ -246,22 +246,22 @@ def setColor(value) {
|
||||
value.gh = hex(rgb.g)
|
||||
value.bh = hex(rgb.b)
|
||||
}
|
||||
|
||||
|
||||
if ((value.size() == 3) && (value.hue != null) && (value.saturation != null) && (value.level)) { //user passed in a level value too from outside (App)
|
||||
def rgb = hslToRGB(value.hue, value.saturation, 0.5)
|
||||
value.hex = rgbToHex(rgb)
|
||||
value.rh = hex(rgb.r * value.level/100)
|
||||
value.gh = hex(rgb.g * value.level/100)
|
||||
value.bh = hex(rgb.b * value.level/100)
|
||||
value.bh = hex(rgb.b * value.level/100)
|
||||
}
|
||||
|
||||
|
||||
if (( value.size() == 1) && (value.hex)) { //being called from outside of device (App) with only hex
|
||||
def rgbInt = hexToRgb(value.hex)
|
||||
value.rh = hex(rgbInt.r)
|
||||
value.gh = hex(rgbInt.g)
|
||||
value.bh = hex(rgbInt.b)
|
||||
}
|
||||
|
||||
|
||||
if (( value.size() == 2) && (value.hex) && (value.level)) { //being called from outside of device (App) with only hex and level
|
||||
|
||||
def rgbInt = hexToRgb(value.hex)
|
||||
@@ -269,7 +269,7 @@ def setColor(value) {
|
||||
value.gh = hex(rgbInt.g * value.level/100)
|
||||
value.bh = hex(rgbInt.b * value.level/100)
|
||||
}
|
||||
|
||||
|
||||
if (( value.size() == 1) && (value.colorName)) { //being called from outside of device (App) with only color name
|
||||
def colorData = getColorData(value.colorName)
|
||||
value.rh = colorData.rh
|
||||
@@ -277,7 +277,7 @@ def setColor(value) {
|
||||
value.bh = colorData.bh
|
||||
value.hex = "#${value.rh}${value.gh}${value.bh}"
|
||||
}
|
||||
|
||||
|
||||
if (( value.size() == 2) && (value.colorName) && (value.level)) { //being called from outside of device (App) with only color name and level
|
||||
def colorData = getColorData(value.colorName)
|
||||
value.rh = hex(colorData.r * value.level/100)
|
||||
@@ -285,7 +285,7 @@ def setColor(value) {
|
||||
value.bh = hex(colorData.b * value.level/100)
|
||||
value.hex = "#${hex(colorData.r)}${hex(colorData.g)}${hex(colorData.b)}"
|
||||
}
|
||||
|
||||
|
||||
if (( value.size() == 3) && (value.red != null) && (value.green != null) && (value.blue != null)) { //being called from outside of device (App) with only color values (0-255)
|
||||
value.rh = hex(value.red)
|
||||
value.gh = hex(value.green)
|
||||
@@ -299,7 +299,7 @@ def setColor(value) {
|
||||
value.bh = hex(value.blue * value.level/100)
|
||||
value.hex = "#${hex(value.red)}${hex(value.green)}${hex(value.blue)}"
|
||||
}
|
||||
|
||||
|
||||
sendEvent(name: "hue", value: value.hue, displayed: false)
|
||||
sendEvent(name: "saturation", value: value.saturation, displayed: false)
|
||||
sendEvent(name: "color", value: value.hex, displayed: false)
|
||||
@@ -309,26 +309,26 @@ def setColor(value) {
|
||||
if (value.switch) {
|
||||
sendEvent(name: "switch", value: value.switch)
|
||||
}
|
||||
|
||||
|
||||
sendRGB(value.rh, value.gh, value.bh)
|
||||
}
|
||||
|
||||
def setLevel(level) {
|
||||
log.debug "setLevel($level)"
|
||||
|
||||
|
||||
if (level == 0) { off() }
|
||||
else if (device.latestValue("switch") == "off") { on() }
|
||||
|
||||
|
||||
def colorHex = device.latestValue("color")
|
||||
if (colorHex == null)
|
||||
colorHex = "#FFFFFF"
|
||||
|
||||
|
||||
def c = hexToRgb(colorHex)
|
||||
|
||||
|
||||
def r = hex(c.r * (level/100))
|
||||
def g = hex(c.g * (level/100))
|
||||
def b = hex(c.b * (level/100))
|
||||
|
||||
|
||||
sendEvent(name: "level", value: level)
|
||||
sendEvent(name: "setLevel", value: level, displayed: false)
|
||||
sendRGB(r, g, b)
|
||||
@@ -337,14 +337,14 @@ def setLevel(level) {
|
||||
|
||||
def setWhiteLevel(value) {
|
||||
log.debug "setWhiteLevel: ${value}"
|
||||
def level = Math.min(value as Integer, 99)
|
||||
def level = Math.min(value as Integer, 99)
|
||||
level = 255 * level/99 as Integer
|
||||
def channel = 0
|
||||
|
||||
if (device.latestValue("switch") == "off") { on() }
|
||||
|
||||
|
||||
sendEvent(name: "whiteLevel", value: value)
|
||||
sendWhite(channel, value)
|
||||
sendWhite(channel, value)
|
||||
}
|
||||
|
||||
def sendWhite(channel, value) {
|
||||
@@ -367,20 +367,20 @@ def sendRGBW(redHex, greenHex, blueHex, whiteHex) {
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Device For SmartThings Use"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def cmds = []
|
||||
|
||||
|
||||
// send associate to group 3 to get sensor data reported only to hub
|
||||
cmds << zwave.associationV2.associationSet(groupingIdentifier:5, nodeId:[zwaveHubNodeId]).format()
|
||||
|
||||
|
||||
|
||||
|
||||
//cmds << sendEvent(name: "level", value: 50)
|
||||
//cmds << on()
|
||||
//cmds << doColorButton("Green")
|
||||
delayBetween(cmds, 500)
|
||||
|
||||
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
@@ -411,11 +411,11 @@ def parse(String description) {
|
||||
|
||||
def getDeviceData() {
|
||||
def cmd = []
|
||||
|
||||
cmd << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
|
||||
|
||||
cmd << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
|
||||
cmd << response(zwave.versionV1.versionGet())
|
||||
cmd << response(zwave.firmwareUpdateMdV1.firmwareMdGet())
|
||||
|
||||
|
||||
delayBetween(cmd, 500)
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ def createEvent(physicalgraph.zwave.commands.manufacturerspecificv2.Manufacturer
|
||||
log.debug "productTypeId: ${cmd.productTypeId}"
|
||||
}
|
||||
|
||||
def createEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd, Map item1) {
|
||||
def createEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd, Map item1) {
|
||||
updateDataValue("applicationVersion", "${cmd.applicationVersion}")
|
||||
log.debug "applicationVersion: ${cmd.applicationVersion}"
|
||||
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
|
||||
@@ -435,13 +435,13 @@ def createEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd, Map it
|
||||
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
|
||||
}
|
||||
|
||||
def createEvent(physicalgraph.zwave.commands.firmwareupdatemdv1.FirmwareMdReport cmd, Map item1) {
|
||||
def createEvent(physicalgraph.zwave.commands.firmwareupdatemdv1.FirmwareMdReport cmd, Map item1) {
|
||||
log.debug "checksum: ${cmd.checksum}"
|
||||
log.debug "firmwareId: ${cmd.firmwareId}"
|
||||
log.debug "manufacturerId: ${cmd.manufacturerId}"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.colorcontrolv1.CapabilityReport cmd, Map item1) {
|
||||
def zwaveEvent(physicalgraph.zwave.commands.colorcontrolv1.CapabilityReport cmd, Map item1) {
|
||||
|
||||
log.debug "In CapabilityReport"
|
||||
}
|
||||
@@ -546,7 +546,7 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport
|
||||
def value = "when off"
|
||||
if (cmd.configurationValue[0] == 1) {value = "when on"}
|
||||
if (cmd.configurationValue[0] == 2) {value = "never"}
|
||||
[name: "indicatorStatus", value: value, display: false]
|
||||
[name: "indicatorStatus", value: value, displayed: false]
|
||||
}
|
||||
*/
|
||||
def createEvent(physicalgraph.zwave.Command cmd, Map map) {
|
||||
@@ -557,7 +557,7 @@ def createEvent(physicalgraph.zwave.Command cmd, Map map) {
|
||||
def on() {
|
||||
log.debug "on()"
|
||||
sendEvent(name: "switch", value: "on")
|
||||
delayBetween([zwave.basicV1.basicSet(value: 0xFF).format(),
|
||||
delayBetween([zwave.basicV1.basicSet(value: 0xFF).format(),
|
||||
zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ def refresh() {
|
||||
* @return none
|
||||
*/
|
||||
def updateZwaveParam(params) {
|
||||
if ( params ) {
|
||||
if ( params ) {
|
||||
def pNumber = params.paramNumber
|
||||
def pSize = params.size
|
||||
def pValue = [params.value]
|
||||
@@ -601,9 +601,9 @@ def updateZwaveParam(params) {
|
||||
|
||||
def cmds = []
|
||||
cmds << zwave.configurationV1.configurationSet(configurationValue: pValue, parameterNumber: pNumber, size: pSize).format()
|
||||
|
||||
|
||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: pNumber).format()
|
||||
delayBetween(cmds, 1500)
|
||||
delayBetween(cmds, 1500)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,22 +612,22 @@ def test() {
|
||||
//value = [hue: 0, saturation: 100, level: 5]
|
||||
//value = [red: 255, green: 0, blue: 255, level: 60]
|
||||
//setColor(value)
|
||||
|
||||
|
||||
def cmd = []
|
||||
|
||||
|
||||
if ( !state.cnt ) {
|
||||
state.cnt = 6
|
||||
} else {
|
||||
state.cnt = state.cnt + 1
|
||||
}
|
||||
|
||||
|
||||
if ( state.cnt > 10 )
|
||||
state.cnt = 6
|
||||
|
||||
|
||||
// run programmed light show
|
||||
cmd << zwave.configurationV1.configurationSet(configurationValue: [state.cnt], parameterNumber: 72, size: 1).format()
|
||||
cmd << zwave.configurationV1.configurationGet(parameterNumber: 72).format()
|
||||
|
||||
cmd << zwave.configurationV1.configurationGet(parameterNumber: 72).format()
|
||||
|
||||
delayBetween(cmd, 500)
|
||||
|
||||
}
|
||||
@@ -638,23 +638,23 @@ def colorNameToRgb(color) {
|
||||
[name:"Soft White", r: 255, g: 241, b: 224 ],
|
||||
[name:"Daylight", r: 255, g: 255, b: 251 ],
|
||||
[name:"Warm White", r: 255, g: 244, b: 229 ],
|
||||
|
||||
|
||||
[name:"Red", r: 255, g: 0, b: 0 ],
|
||||
[name:"Green", r: 0, g: 255, b: 0 ],
|
||||
[name:"Blue", r: 0, g: 0, b: 255 ],
|
||||
|
||||
|
||||
[name:"Cyan", r: 0, g: 255, b: 255 ],
|
||||
[name:"Magenta", r: 255, g: 0, b: 33 ],
|
||||
[name:"Magenta", r: 255, g: 0, b: 33 ],
|
||||
[name:"Orange", r: 255, g: 102, b: 0 ],
|
||||
|
||||
|
||||
[name:"Purple", r: 170, g: 0, b: 255 ],
|
||||
[name:"Yellow", r: 255, g: 255, b: 0 ],
|
||||
[name:"White", r: 255, g: 255, b: 255 ]
|
||||
]
|
||||
|
||||
def colorData = [:]
|
||||
|
||||
def colorData = [:]
|
||||
colorData = colors.find { it.name == color }
|
||||
|
||||
|
||||
colorData
|
||||
}
|
||||
|
||||
@@ -670,7 +670,7 @@ def hexToRgb(colorHex) {
|
||||
def rrInt = Integer.parseInt(colorHex.substring(1,3),16)
|
||||
def ggInt = Integer.parseInt(colorHex.substring(3,5),16)
|
||||
def bbInt = Integer.parseInt(colorHex.substring(5,7),16)
|
||||
|
||||
|
||||
def colorData = [:]
|
||||
colorData = [r: rrInt, g: ggInt, b: bbInt]
|
||||
colorData
|
||||
@@ -681,7 +681,7 @@ def rgbToHex(rgb) {
|
||||
def g = hex(rgb.g)
|
||||
def b = hex(rgb.b)
|
||||
def hexColor = "#${r}${g}${b}"
|
||||
|
||||
|
||||
hexColor
|
||||
}
|
||||
|
||||
@@ -689,11 +689,11 @@ def hslToRGB(float var_h, float var_s, float var_l) {
|
||||
float h = var_h / 100
|
||||
float s = var_s / 100
|
||||
float l = var_l
|
||||
|
||||
|
||||
def r = 0
|
||||
def g = 0
|
||||
def b = 0
|
||||
|
||||
|
||||
if (s == 0) {
|
||||
r = l * 255
|
||||
g = l * 255
|
||||
@@ -705,26 +705,26 @@ def hslToRGB(float var_h, float var_s, float var_l) {
|
||||
} else {
|
||||
var_2 = (l + s) - (s * l)
|
||||
}
|
||||
|
||||
|
||||
float var_1 = 2 * l - var_2
|
||||
|
||||
|
||||
r = 255 * hueToRgb(var_1, var_2, h + (1 / 3))
|
||||
g = 255 * hueToRgb(var_1, var_2, h)
|
||||
b = 255 * hueToRgb(var_1, var_2, h - (1 / 3))
|
||||
b = 255 * hueToRgb(var_1, var_2, h - (1 / 3))
|
||||
}
|
||||
|
||||
|
||||
def rgb = [:]
|
||||
rgb = [r: r, g: g, b: b]
|
||||
|
||||
rgb
|
||||
rgb
|
||||
}
|
||||
|
||||
def hueToRgb(v1, v2, vh) {
|
||||
if (vh < 0) { vh += 1 }
|
||||
if (vh < 0) { vh += 1 }
|
||||
if (vh > 1) { vh -= 1 }
|
||||
if ((6 * vh) < 1) { return (v1 + (v2 - v1) * 6 * vh) }
|
||||
if ((2 * vh) < 1) { return (v2) }
|
||||
if ((3 * vh) < 2) { return (v1 + (v2 - $v1) * ((2 / 3 - vh) * 6)) }
|
||||
if ((3 * vh) < 2) { return (v1 + (v2 - $v1) * ((2 / 3 - vh) * 6)) }
|
||||
return (v1)
|
||||
}
|
||||
|
||||
@@ -735,49 +735,49 @@ def rgbToHSL(rgb) {
|
||||
def h = 0
|
||||
def s = 0
|
||||
def l = 0
|
||||
|
||||
|
||||
def var_min = [r,g,b].min()
|
||||
def var_max = [r,g,b].max()
|
||||
def del_max = var_max - var_min
|
||||
|
||||
|
||||
l = (var_max + var_min) / 2
|
||||
|
||||
|
||||
if (del_max == 0) {
|
||||
h = 0
|
||||
s = 0
|
||||
} else {
|
||||
if (l < 0.5) { s = del_max / (var_max + var_min) }
|
||||
if (l < 0.5) { s = del_max / (var_max + var_min) }
|
||||
else { s = del_max / (2 - var_max - var_min) }
|
||||
|
||||
def del_r = (((var_max - r) / 6) + (del_max / 2)) / del_max
|
||||
def del_g = (((var_max - g) / 6) + (del_max / 2)) / del_max
|
||||
def del_b = (((var_max - b) / 6) + (del_max / 2)) / del_max
|
||||
|
||||
if (r == var_max) { h = del_b - del_g }
|
||||
else if (g == var_max) { h = (1 / 3) + del_r - del_b }
|
||||
if (r == var_max) { h = del_b - del_g }
|
||||
else if (g == var_max) { h = (1 / 3) + del_r - del_b }
|
||||
else if (b == var_max) { h = (2 / 3) + del_g - del_r }
|
||||
|
||||
|
||||
if (h < 0) { h += 1 }
|
||||
if (h > 1) { h -= 1 }
|
||||
}
|
||||
def hsl = [:]
|
||||
def hsl = [:]
|
||||
hsl = [h: h * 100, s: s * 100, l: l]
|
||||
|
||||
|
||||
hsl
|
||||
}
|
||||
|
||||
def getColorData(colorName) {
|
||||
log.debug "getColorData: ${colorName}"
|
||||
|
||||
|
||||
def colorRGB = colorNameToRgb(colorName)
|
||||
def colorHex = rgbToHex(colorRGB)
|
||||
def colorHSL = rgbToHSL(colorRGB)
|
||||
|
||||
|
||||
def colorData = [:]
|
||||
colorData = [h: colorHSL.h,
|
||||
s: colorHSL.s,
|
||||
l: device.latestValue("level"),
|
||||
r: colorRGB.r,
|
||||
colorData = [h: colorHSL.h,
|
||||
s: colorHSL.s,
|
||||
l: device.latestValue("level"),
|
||||
r: colorRGB.r,
|
||||
g: colorRGB.g,
|
||||
b: colorRGB.b,
|
||||
rh: hex(colorRGB.r),
|
||||
@@ -785,8 +785,8 @@ def getColorData(colorName) {
|
||||
bh: hex(colorRGB.b),
|
||||
hex: colorHex,
|
||||
alpha: 1]
|
||||
|
||||
colorData
|
||||
|
||||
colorData
|
||||
}
|
||||
|
||||
def doColorButton(colorName) {
|
||||
@@ -798,7 +798,7 @@ def doColorButton(colorName) {
|
||||
def maxLevel = hex(99)
|
||||
|
||||
toggleTiles(colorName.toLowerCase().replaceAll("\\s",""))
|
||||
|
||||
|
||||
if ( colorName == "Fire Place" ) { updateZwaveParam([paramNumber:72, value:6, size:1]) }
|
||||
else if ( colorName == "Storm" ) { updateZwaveParam([paramNumber:72, value:7, size:1]) }
|
||||
else if ( colorName == "Deep Fade" ) { updateZwaveParam([paramNumber:72, value:8, size:1]) }
|
||||
@@ -808,8 +808,8 @@ def doColorButton(colorName) {
|
||||
else if ( colorName == "Daylight" ) { String.format("33050400${maxLevel}02${maxLevel}03${maxLevel}04${maxLevel}%02X", 100) }
|
||||
else {
|
||||
def c = getColorData(colorName)
|
||||
def newValue = ["hue": c.h, "saturation": c.s, "level": level, "red": c.r, "green": c.g, "blue": c.b, "hex": c.hex, "alpha": c.alpha]
|
||||
setColor(newValue)
|
||||
def newValue = ["hue": c.h, "saturation": c.s, "level": level, "red": c.r, "green": c.g, "blue": c.b, "hex": c.hex, "alpha": c.alpha]
|
||||
setColor(newValue)
|
||||
def r = hex(c.r * (level/100))
|
||||
def g = hex(c.g * (level/100))
|
||||
def b = hex(c.b * (level/100))
|
||||
@@ -823,19 +823,19 @@ def toggleTiles(color) {
|
||||
if ( !state.colorTiles ) {
|
||||
state.colorTiles = ["softwhite","daylight","warmwhite","red","green","blue","cyan","magenta","orange","purple","yellow","white","fireplace","storm","deepfade","litefade","police"]
|
||||
}
|
||||
|
||||
|
||||
def cmds = []
|
||||
|
||||
|
||||
state.colorTiles.each({
|
||||
if ( it == color ) {
|
||||
log.debug "Turning ${it} on"
|
||||
cmds << sendEvent(name: it, value: "on${it}", display: True, descriptionText: "${device.displayName} ${color} is 'ON'", isStateChange: true)
|
||||
cmds << sendEvent(name: it, value: "on${it}", displayed: True, descriptionText: "${device.displayName} ${color} is 'ON'", isStateChange: true)
|
||||
} else {
|
||||
//log.debug "Turning ${it} off"
|
||||
cmds << sendEvent(name: it, value: "off${it}", displayed: false)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
delayBetween(cmds, 2500)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -34,10 +36,10 @@ metadata {
|
||||
// tile definitions
|
||||
tiles {
|
||||
standardTile("contact", "device.contact", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#53a7c0", nextState:"closing"
|
||||
state "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#e86d13", nextState:"opening"
|
||||
state "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#ffe71e"
|
||||
state "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffe71e"
|
||||
state "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
state "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
state "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
state "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
|
||||
state "take", label: "Take", action: "Image Capture.take", icon: "st.camera.dropcam", backgroundColor: "#FFFFFF", nextState:"taking"
|
||||
state "taking", label:'Taking', action: "", icon: "st.camera.dropcam", backgroundColor: "#53a7c0"
|
||||
state "taking", label:'Taking', action: "", icon: "st.camera.dropcam", backgroundColor: "#00A0DC"
|
||||
state "image", label: "Take", action: "Image Capture.take", icon: "st.camera.dropcam", backgroundColor: "#FFFFFF", nextState:"taking"
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@ 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.light.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
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:"#79b821", nextState:"turningOff"
|
||||
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") {
|
||||
@@ -87,7 +87,7 @@ metadata {
|
||||
def parse(String description) {
|
||||
def resultMap = zigbee.getEvent(description)
|
||||
if (resultMap) {
|
||||
if ((resultMap.name == "level" && state.trigger == "setLevel") || resultMap.name != "level") { //doing this to account for weird level reporting bug with GE Link Bulbs
|
||||
if (resultMap.name != "level" || resultMap.value != 0) { // Ignore level reports of 0 sent when bulb turns off
|
||||
sendEvent(resultMap)
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ def parse(String description) {
|
||||
|
||||
def poll() {
|
||||
def refreshCmds = [
|
||||
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 500"
|
||||
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 2000"
|
||||
]
|
||||
|
||||
return refreshCmds + zigbee.onOffRefresh() + zigbee.levelRefresh()
|
||||
@@ -188,25 +188,22 @@ def updated() {
|
||||
}
|
||||
|
||||
def on() {
|
||||
state.trigger = "on/off"
|
||||
zigbee.on()
|
||||
}
|
||||
|
||||
def off() {
|
||||
state.trigger = "on/off"
|
||||
zigbee.off()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
def refreshCmds = [
|
||||
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 500"
|
||||
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 2000"
|
||||
]
|
||||
|
||||
return refreshCmds + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig()
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
state.trigger = "setLevel"
|
||||
def cmd
|
||||
def delayForRefresh = 500
|
||||
if (dimRate && (state?.rate != null)) {
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* GE/Jasco ZigBee Dimmer
|
||||
*
|
||||
* Author: SmartThings
|
||||
* Date: 2015-07-01
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "GE ZigBee Dimmer", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Power Meter"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
// status messages
|
||||
status "on": "on/off: 1"
|
||||
status "off": "on/off: 0"
|
||||
|
||||
// reply messages
|
||||
reply "zcl on-off on": "on/off: 1"
|
||||
reply "zcl on-off off": "on/off: 0"
|
||||
}
|
||||
|
||||
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", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
}
|
||||
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "level", label: 'Level ${currentValue}%'
|
||||
}
|
||||
valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) {
|
||||
state "power", label:'${currentValue} W'
|
||||
}
|
||||
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
main "switch"
|
||||
details(["switch", "level", "power","levelSliderControl","refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
|
||||
def finalResult = isKnownDescription(description)
|
||||
if (finalResult != "false") {
|
||||
log.info finalResult
|
||||
if (finalResult.type == "update") {
|
||||
log.info "$device updates: ${finalResult.value}"
|
||||
}
|
||||
else if (finalResult.type == "power") {
|
||||
def powerValue = (finalResult.value as Integer)/10
|
||||
sendEvent(name: "power", value: powerValue)
|
||||
|
||||
/*
|
||||
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
|
||||
|
||||
power level is an integer. The exact power level with correct units needs to be handled in the device type
|
||||
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
|
||||
*/
|
||||
}
|
||||
else {
|
||||
sendEvent(name: finalResult.type, value: finalResult.value)
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||
log.debug parseDescriptionAsMap(description)
|
||||
}
|
||||
}
|
||||
|
||||
// Commands to device
|
||||
def zigbeeCommand(cluster, attribute){
|
||||
["st cmd 0x${device.deviceNetworkId} ${endpointId} ${cluster} ${attribute} {}"]
|
||||
}
|
||||
|
||||
def off() {
|
||||
zigbeeCommand("6", "0")
|
||||
}
|
||||
|
||||
def on() {
|
||||
zigbeeCommand("6", "1")
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
value = value as Integer
|
||||
if (value == 0) {
|
||||
off()
|
||||
}
|
||||
else {
|
||||
sendEvent(name: "level", value: value)
|
||||
setLevelWithRate(value, "0000") + ["delay 1000"] + on() //value is between 0 to 100; GE does NOT switch on if OFF
|
||||
}
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
[
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0702 0x0400", "delay 500"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
def configure() {
|
||||
onOffConfig() + levelConfig() + powerConfig() + refresh()
|
||||
}
|
||||
|
||||
|
||||
private getEndpointId() {
|
||||
new BigInteger(device.endpointId, 16).toString()
|
||||
}
|
||||
|
||||
private hex(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex,16)
|
||||
}
|
||||
|
||||
//Need to reverse array of size 2
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
byte tmp;
|
||||
tmp = array[1];
|
||||
array[1] = array[0];
|
||||
array[0] = tmp;
|
||||
return array
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
if (description?.startsWith("read attr -")) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()): nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
else if (description?.startsWith("catchall: ")) {
|
||||
def seg = (description - "catchall: ").split(" ")
|
||||
def zigbeeMap = [:]
|
||||
zigbeeMap += [raw: (description - "catchall: ")]
|
||||
zigbeeMap += [profileId: seg[0]]
|
||||
zigbeeMap += [clusterId: seg[1]]
|
||||
zigbeeMap += [sourceEndpoint: seg[2]]
|
||||
zigbeeMap += [destinationEndpoint: seg[3]]
|
||||
zigbeeMap += [options: seg[4]]
|
||||
zigbeeMap += [messageType: seg[5]]
|
||||
zigbeeMap += [dni: seg[6]]
|
||||
zigbeeMap += [isClusterSpecific: Short.valueOf(seg[7], 16) != 0]
|
||||
zigbeeMap += [isManufacturerSpecific: Short.valueOf(seg[8], 16) != 0]
|
||||
zigbeeMap += [manufacturerId: seg[9]]
|
||||
zigbeeMap += [command: seg[10]]
|
||||
zigbeeMap += [direction: seg[11]]
|
||||
zigbeeMap += [data: seg.size() > 12 ? seg[12].split("").findAll { it }.collate(2).collect {
|
||||
it.join('')
|
||||
} : []]
|
||||
|
||||
zigbeeMap
|
||||
}
|
||||
}
|
||||
|
||||
def isKnownDescription(description) {
|
||||
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
if (descMap.cluster == "0006" || descMap.clusterId == "0006") {
|
||||
isDescriptionOnOff(descMap)
|
||||
}
|
||||
else if (descMap.cluster == "0008" || descMap.clusterId == "0008"){
|
||||
isDescriptionLevel(descMap)
|
||||
}
|
||||
else if (descMap.cluster == "0702" || descMap.clusterId == "0702"){
|
||||
isDescriptionPower(descMap)
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
else if(description?.startsWith("on/off:")) {
|
||||
def switchValue = description?.endsWith("1") ? "on" : "off"
|
||||
return [type: "switch", value : switchValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
def isDescriptionOnOff(descMap) {
|
||||
def switchValue = "undefined"
|
||||
if (descMap.cluster == "0006") { //cluster info from read attr
|
||||
value = descMap.value
|
||||
if (value == "01"){
|
||||
switchValue = "on"
|
||||
}
|
||||
else if (value == "00"){
|
||||
switchValue = "off"
|
||||
}
|
||||
}
|
||||
else if (descMap.clusterId == "0006") {
|
||||
//cluster info from catch all
|
||||
//command 0B is Default response and the last two bytes are [on/off][success]. on/off=00, success=00
|
||||
//command 01 is Read attr response. the last two bytes are [datatype][value]. boolean datatype=10; on/off value = 01/00
|
||||
if ((descMap.command=="0B" && descMap.raw.endsWith("0100")) || (descMap.command=="01" && descMap.raw.endsWith("1001"))){
|
||||
switchValue = "on"
|
||||
}
|
||||
else if ((descMap.command=="0B" && descMap.raw.endsWith("0000")) || (descMap.command=="01" && descMap.raw.endsWith("1000"))){
|
||||
switchValue = "off"
|
||||
}
|
||||
else if(descMap.command=="07"){
|
||||
return [type: "update", value : "switch (0006) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (switchValue != "undefined"){
|
||||
return [type: "switch", value : switchValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//@return - false or "success" or level [0-100]
|
||||
def isDescriptionLevel(descMap) {
|
||||
def dimmerValue = -1
|
||||
if (descMap.cluster == "0008"){
|
||||
//TODO: the message returned with catchall is command 0B with clusterId 0008. That is just a confirmation message
|
||||
def value = convertHexToInt(descMap.value)
|
||||
dimmerValue = Math.round(value * 100 / 255)
|
||||
if(dimmerValue==0 && value > 0) {
|
||||
dimmerValue = 1 //handling for non-zero hex value less than 3
|
||||
}
|
||||
}
|
||||
else if(descMap.clusterId == "0008") {
|
||||
if(descMap.command=="0B"){
|
||||
return [type: "update", value : "level updated successfully"] //device updating the level change was successful. no value sent.
|
||||
}
|
||||
else if(descMap.command=="07"){
|
||||
return [type: "update", value : "level (0008) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (dimmerValue != -1){
|
||||
return [type: "level", value : dimmerValue]
|
||||
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
def isDescriptionPower(descMap) {
|
||||
def powerValue = "undefined"
|
||||
if (descMap.cluster == "0702") {
|
||||
if (descMap.attrId == "0400") {
|
||||
powerValue = convertHexToInt(descMap.value)
|
||||
}
|
||||
}
|
||||
else if (descMap.clusterId == "0702") {
|
||||
if(descMap.command=="07"){
|
||||
return [type: "update", value : "power (0702) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (powerValue != "undefined"){
|
||||
return [type: "power", value : powerValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def onOffConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 6 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 6 0 0x10 0 600 {01}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
//level config for devices with min reporting interval as 5 seconds and reporting interval if no activity as 1hour (3600s)
|
||||
//min level change is 01
|
||||
def levelConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 8 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 8 0 0x20 1 3600 {01}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
|
||||
//min change in value is 05
|
||||
def powerConfig() {
|
||||
[
|
||||
//Meter (Power) Reporting
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0702 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 0x0702 0x0400 0x2A 1 600 {05}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
def setLevelWithRate(level, rate) {
|
||||
if(rate == null){
|
||||
rate = "0000"
|
||||
}
|
||||
level = convertToHexString(level * 255 / 100) //Converting the 0-100 range to 0-FF range in hex
|
||||
["st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {$level $rate}"]
|
||||
}
|
||||
|
||||
String convertToHexString(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* GE/Jasco ZigBee Switch
|
||||
*
|
||||
* Author: SmartThings
|
||||
* Date: 2015-07-01
|
||||
*/
|
||||
|
||||
metadata {
|
||||
// Automatically generated. Make future change here.
|
||||
definition (name: "GE ZigBee Switch", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Switch"
|
||||
capability "Power Meter"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
// status messages
|
||||
status "on": "on/off: 1"
|
||||
status "off": "on/off: 0"
|
||||
|
||||
// reply messages
|
||||
reply "zcl on-off on": "on/off: 1"
|
||||
reply "zcl on-off off": "on/off: 0"
|
||||
}
|
||||
|
||||
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", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
valueTile("power", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "power", label:'${currentValue} Watts'
|
||||
}
|
||||
main "switch"
|
||||
details(["switch", "power", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
|
||||
def finalResult = isKnownDescription(description)
|
||||
if (finalResult != "false") {
|
||||
log.info finalResult
|
||||
if (finalResult.type == "update") {
|
||||
log.info "$device updates: ${finalResult.value}"
|
||||
}
|
||||
else if (finalResult.type == "power") {
|
||||
def powerValue = (finalResult.value as Integer)/10
|
||||
sendEvent(name: "power", value: powerValue)
|
||||
|
||||
/*
|
||||
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
|
||||
|
||||
power level is an integer. The exact power level with correct units needs to be handled in the device type
|
||||
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
|
||||
*/
|
||||
}
|
||||
else {
|
||||
sendEvent(name: finalResult.type, value: finalResult.value)
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||
log.debug parseDescriptionAsMap(description)
|
||||
}
|
||||
}
|
||||
|
||||
// Commands to device
|
||||
def zigbeeCommand(cluster, attribute){
|
||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} ${cluster} ${attribute} {}"
|
||||
}
|
||||
|
||||
def off() {
|
||||
zigbeeCommand("6", "0")
|
||||
}
|
||||
|
||||
def on() {
|
||||
zigbeeCommand("6", "1")
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
[
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0702 0x0400", "delay 500"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
def configure() {
|
||||
onOffConfig() + powerConfig() + refresh()
|
||||
}
|
||||
|
||||
|
||||
private getEndpointId() {
|
||||
new BigInteger(device.endpointId, 16).toString()
|
||||
}
|
||||
|
||||
private hex(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex,16)
|
||||
}
|
||||
|
||||
//Need to reverse array of size 2
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
byte tmp;
|
||||
tmp = array[1];
|
||||
array[1] = array[0];
|
||||
array[0] = tmp;
|
||||
return array
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
if (description?.startsWith("read attr -")) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()): nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
else if (description?.startsWith("catchall: ")) {
|
||||
def seg = (description - "catchall: ").split(" ")
|
||||
def zigbeeMap = [:]
|
||||
zigbeeMap += [raw: (description - "catchall: ")]
|
||||
zigbeeMap += [profileId: seg[0]]
|
||||
zigbeeMap += [clusterId: seg[1]]
|
||||
zigbeeMap += [sourceEndpoint: seg[2]]
|
||||
zigbeeMap += [destinationEndpoint: seg[3]]
|
||||
zigbeeMap += [options: seg[4]]
|
||||
zigbeeMap += [messageType: seg[5]]
|
||||
zigbeeMap += [dni: seg[6]]
|
||||
zigbeeMap += [isClusterSpecific: Short.valueOf(seg[7], 16) != 0]
|
||||
zigbeeMap += [isManufacturerSpecific: Short.valueOf(seg[8], 16) != 0]
|
||||
zigbeeMap += [manufacturerId: seg[9]]
|
||||
zigbeeMap += [command: seg[10]]
|
||||
zigbeeMap += [direction: seg[11]]
|
||||
zigbeeMap += [data: seg.size() > 12 ? seg[12].split("").findAll { it }.collate(2).collect {
|
||||
it.join('')
|
||||
} : []]
|
||||
|
||||
zigbeeMap
|
||||
}
|
||||
}
|
||||
|
||||
def isKnownDescription(description) {
|
||||
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
if (descMap.cluster == "0006" || descMap.clusterId == "0006") {
|
||||
isDescriptionOnOff(descMap)
|
||||
}
|
||||
else if (descMap.cluster == "0702" || descMap.clusterId == "0702"){
|
||||
isDescriptionPower(descMap)
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
else if(description?.startsWith("on/off:")) {
|
||||
def switchValue = description?.endsWith("1") ? "on" : "off"
|
||||
return [type: "switch", value : switchValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
def isDescriptionOnOff(descMap) {
|
||||
def switchValue = "undefined"
|
||||
if (descMap.cluster == "0006") { //cluster info from read attr
|
||||
value = descMap.value
|
||||
if (value == "01"){
|
||||
switchValue = "on"
|
||||
}
|
||||
else if (value == "00"){
|
||||
switchValue = "off"
|
||||
}
|
||||
}
|
||||
else if (descMap.clusterId == "0006") {
|
||||
//cluster info from catch all
|
||||
//command 0B is Default response and the last two bytes are [on/off][success]. on/off=00, success=00
|
||||
//command 01 is Read attr response. the last two bytes are [datatype][value]. boolean datatype=10; on/off value = 01/00
|
||||
if ((descMap.command=="0B" && descMap.raw.endsWith("0100")) || (descMap.command=="01" && descMap.raw.endsWith("1001"))){
|
||||
switchValue = "on"
|
||||
}
|
||||
else if ((descMap.command=="0B" && descMap.raw.endsWith("0000")) || (descMap.command=="01" && descMap.raw.endsWith("1000"))){
|
||||
switchValue = "off"
|
||||
}
|
||||
else if(descMap.command=="07"){
|
||||
return [type: "update", value : "switch (0006) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (switchValue != "undefined"){
|
||||
return [type: "switch", value : switchValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def isDescriptionPower(descMap) {
|
||||
def powerValue = "undefined"
|
||||
if (descMap.cluster == "0702") {
|
||||
if (descMap.attrId == "0400") {
|
||||
powerValue = convertHexToInt(descMap.value)
|
||||
}
|
||||
}
|
||||
else if (descMap.clusterId == "0702") {
|
||||
if(descMap.command=="07"){
|
||||
return [type: "update", value : "power (0702) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (powerValue != "undefined"){
|
||||
return [type: "power", value : powerValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def onOffConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 6 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 6 0 0x10 0 600 {01}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
|
||||
//min change in value is 05
|
||||
def powerConfig() {
|
||||
[
|
||||
//Meter (Power) Reporting
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0702 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 0x0702 0x0400 0x2A 1 600 {05}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
String convertToHexString(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
@@ -31,7 +31,7 @@ metadata {
|
||||
tileAttribute("sessionStatus", key: "PRIMARY_CONTROL") {
|
||||
attributeState "cancelled", action: "timed session.start", icon: "http://f.cl.ly/items/322n181j2K3f281r2s0A/playbutton.png", backgroundColor: "#ffffff", nextState: "running"
|
||||
attributeState "stopped", action: "timed session.start", icon: "http://f.cl.ly/items/322n181j2K3f281r2s0A/playbutton.png", backgroundColor: "#ffffff", nextState: "cancelled"
|
||||
attributeState "running", action: "timed session.stop", icon: "http://f.cl.ly/items/0B3y3p2V3X2l3P3y3W09/stopbutton.png", backgroundColor: "#79b821", nextState: "cancelled"
|
||||
attributeState "running", action: "timed session.stop", icon: "http://f.cl.ly/items/0B3y3p2V3X2l3P3y3W09/stopbutton.png", backgroundColor: "#00A0DC", nextState: "cancelled"
|
||||
}
|
||||
tileAttribute("timeRemaining", key: "SECONDARY_CONTROL") {
|
||||
attributeState "timeRemaining", label:'${currentValue} remaining'
|
||||
@@ -45,7 +45,7 @@ metadata {
|
||||
standardTile("sessionStatusTile", "sessionStatus", width: 1, height: 1, canChangeIcon: true) {
|
||||
state "cancelled", label: "Stopped", action: "timed session.start", backgroundColor: "#ffffff", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png"
|
||||
state "stopped", label: "Stopped", action: "timed session.start", backgroundColor: "#ffffff", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png"
|
||||
state "running", label: "Running", action: "timed session.stop", backgroundColor: "#79b821", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png"
|
||||
state "running", label: "Running", action: "timed session.stop", backgroundColor: "#00A0DC", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png"
|
||||
}
|
||||
|
||||
// duration
|
||||
|
||||
@@ -32,7 +32,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("button", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label: 'Off', action: "switch.on", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#ffffff", nextState: "on"
|
||||
state "on", label: 'On', action: "switch.off", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#79b821", nextState: "off"
|
||||
state "on", label: 'On', action: "switch.off", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#00A0DC", nextState: "off"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
|
||||
@@ -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}°',
|
||||
|
||||
@@ -17,6 +17,7 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
command "setAdjustedColor"
|
||||
command "reset"
|
||||
@@ -30,9 +31,9 @@ metadata {
|
||||
tiles (scale: 2){
|
||||
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
@@ -57,7 +58,7 @@ metadata {
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}", displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
@@ -172,6 +173,3 @@ def verifyPercent(percent) {
|
||||
}
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.debug "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
metadata {
|
||||
// Automatically generated. Make future change here.
|
||||
definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Bridge"
|
||||
capability "Health Check"
|
||||
|
||||
attribute "networkAddress", "string"
|
||||
@@ -26,7 +27,7 @@ metadata {
|
||||
multiAttributeTile(name:"rich-control"){
|
||||
tileAttribute ("device.status", key: "PRIMARY_CONTROL") {
|
||||
attributeState "Offline", label: '${currentValue}', action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#ffffff"
|
||||
attributeState "Online", label: '${currentValue}', action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#79b821"
|
||||
attributeState "Online", label: '${currentValue}', action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#00A0DC"
|
||||
}
|
||||
}
|
||||
valueTile("doNotRemove", "v", decoration: "flat", height: 2, width: 6, inactiveLabel: false) {
|
||||
@@ -45,7 +46,7 @@ metadata {
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}", displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
@@ -87,6 +88,3 @@ def parse(description) {
|
||||
results
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.debug "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
command "setAdjustedColor"
|
||||
command "reset"
|
||||
@@ -31,9 +32,9 @@ metadata {
|
||||
tiles (scale: 2){
|
||||
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
@@ -66,7 +67,7 @@ metadata {
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}", displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
@@ -188,6 +189,3 @@ def verifyPercent(percent) {
|
||||
}
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.trace "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
command "refresh"
|
||||
}
|
||||
@@ -26,9 +27,9 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
@@ -50,7 +51,7 @@ metadata {
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}", displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
@@ -93,6 +94,3 @@ void refresh() {
|
||||
parent.manualRefresh()
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.debug "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
command "refresh"
|
||||
}
|
||||
@@ -27,9 +28,9 @@ metadata {
|
||||
tiles (scale: 2){
|
||||
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
@@ -55,7 +56,7 @@ metadata {
|
||||
}
|
||||
|
||||
void installed() {
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}", displayed: false)
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
@@ -107,6 +108,3 @@ void refresh() {
|
||||
parent.manualRefresh()
|
||||
}
|
||||
|
||||
def ping() {
|
||||
log.debug "${parent.ping(this)}"
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
|
||||
state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#53a7c0")
|
||||
state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#00A0DC")
|
||||
state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ffffff")
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ metadata {
|
||||
}
|
||||
|
||||
def generatePresenceEvent(boolean present) {
|
||||
log.debug "Here in generatePresenceEvent!"
|
||||
log.info "Life360 generatePresenceEvent($present)"
|
||||
def value = formatValue(present)
|
||||
def linkText = getLinkText(device)
|
||||
def descriptionText = formatDescriptionText(linkText, present)
|
||||
|
||||
@@ -11,9 +11,10 @@ metadata {
|
||||
capability "Color Temperature"
|
||||
capability "Switch"
|
||||
capability "Switch Level" // brightness
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -23,10 +24,9 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
|
||||
@@ -64,12 +64,8 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
if (description == 'updated') {
|
||||
return // don't poll when config settings is being updated as it may time out
|
||||
}
|
||||
poll()
|
||||
void installed() {
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"cloud\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device?.hub?.hardwareID}\"}")
|
||||
}
|
||||
|
||||
// handle commands
|
||||
@@ -141,7 +137,6 @@ def setLevel(percentage) {
|
||||
percentage = 1 // clamp to 1%
|
||||
}
|
||||
if (percentage == 0) {
|
||||
sendEvent(name: "level", value: 0) // Otherwise the level value tile does not update
|
||||
return off() // if the brightness is set to 0, just turn it off
|
||||
}
|
||||
parent.logErrors(logObject:log) {
|
||||
@@ -193,14 +188,17 @@ def off() {
|
||||
return []
|
||||
}
|
||||
|
||||
def poll() {
|
||||
log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
|
||||
def resp = parent.apiGET("/lights/${selector()}")
|
||||
if (resp.status == 404) {
|
||||
sendEvent(name: "switch", value: "unreachable")
|
||||
state.online = false
|
||||
sendEvent(name: "DeviceWatch-DeviceStatusUpdate", value: "offline", displayed: false)
|
||||
log.warn "$device is Offline"
|
||||
return []
|
||||
} else if (resp.status != 200) {
|
||||
log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
|
||||
log.error("Unexpected result in refresh(): [${resp.status}] ${resp.data}")
|
||||
return []
|
||||
}
|
||||
def data = resp.data[0]
|
||||
@@ -209,19 +207,20 @@ def poll() {
|
||||
sendEvent(name: "label", value: data.label)
|
||||
sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch", value: data.connected ? data.power : "unreachable")
|
||||
sendEvent(name: "switch", value: data.power)
|
||||
sendEvent(name: "color", value: colorUtil.hslToHex((data.color.hue / 3.6) as int, (data.color.saturation * 100) as int))
|
||||
sendEvent(name: "hue", value: data.color.hue / 3.6)
|
||||
sendEvent(name: "saturation", value: data.color.saturation * 100)
|
||||
sendEvent(name: "colorTemperature", value: data.color.kelvin)
|
||||
sendEvent(name: "model", value: "${data.product.company} ${data.product.name}")
|
||||
sendEvent(name: "model", value: data.product.name)
|
||||
|
||||
return []
|
||||
if (data.connected) {
|
||||
sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false)
|
||||
log.debug "$device is Online"
|
||||
} else {
|
||||
sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false)
|
||||
log.warn "$device is Offline"
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
poll()
|
||||
}
|
||||
|
||||
def selector() {
|
||||
|
||||
@@ -10,9 +10,10 @@ metadata {
|
||||
capability "Color Temperature"
|
||||
capability "Switch"
|
||||
capability "Switch Level" // brightness
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -22,13 +23,12 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
|
||||
}
|
||||
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"switch level.setLevel"
|
||||
}
|
||||
@@ -53,15 +53,10 @@ metadata {
|
||||
main "switch"
|
||||
details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
if (description == 'updated') {
|
||||
return // don't poll when config settings is being updated as it may time out
|
||||
}
|
||||
poll()
|
||||
void installed() {
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"cloud\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device?.hub?.hardwareID}\"}")
|
||||
}
|
||||
|
||||
// handle commands
|
||||
@@ -71,7 +66,6 @@ def setLevel(percentage) {
|
||||
percentage = 1 // clamp to 1%
|
||||
}
|
||||
if (percentage == 0) {
|
||||
sendEvent(name: "level", value: 0) // Otherwise the level value tile does not update
|
||||
return off() // if the brightness is set to 0, just turn it off
|
||||
}
|
||||
parent.logErrors(logObject:log) {
|
||||
@@ -123,14 +117,17 @@ def off() {
|
||||
return []
|
||||
}
|
||||
|
||||
def poll() {
|
||||
log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
|
||||
def resp = parent.apiGET("/lights/${selector()}")
|
||||
if (resp.status == 404) {
|
||||
sendEvent(name: "switch", value: "unreachable")
|
||||
state.online = false
|
||||
sendEvent(name: "DeviceWatch-DeviceStatusUpdate", value: "offline", displayed: false)
|
||||
log.warn "$device is Offline"
|
||||
return []
|
||||
} else if (resp.status != 200) {
|
||||
log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
|
||||
log.error("Unexpected result in refresh(): [${resp.status}] ${resp.data}")
|
||||
return []
|
||||
}
|
||||
def data = resp.data[0]
|
||||
@@ -138,16 +135,17 @@ def poll() {
|
||||
sendEvent(name: "label", value: data.label)
|
||||
sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100))
|
||||
sendEvent(name: "switch", value: data.connected ? data.power : "unreachable")
|
||||
sendEvent(name: "switch", value: data.power)
|
||||
sendEvent(name: "colorTemperature", value: data.color.kelvin)
|
||||
sendEvent(name: "model", value: data.product.name)
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
poll()
|
||||
if (data.connected) {
|
||||
sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false)
|
||||
log.debug "$device is Online"
|
||||
} else {
|
||||
sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false)
|
||||
log.warn "$device is Offline"
|
||||
}
|
||||
}
|
||||
|
||||
def selector() {
|
||||
|
||||
@@ -36,6 +36,7 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Refresh"
|
||||
capability "Contact Sensor"
|
||||
capability "Light"
|
||||
|
||||
attribute "powered", "string"
|
||||
|
||||
@@ -53,11 +54,11 @@ metadata {
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "doorClosed", label: "Closed", action: "on", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821"
|
||||
state "doorOpen", label: "Open", action: "on", icon: "st.doors.garage.garage-open", backgroundColor: "#ffa81e"
|
||||
state "doorOpening", label: "Opening", action: "on", icon: "st.doors.garage.garage-opening", backgroundColor: "#ffa81e"
|
||||
state "doorClosing", label: "Closing", action: "on", icon: "st.doors.garage.garage-closing", backgroundColor: "#ffa81e"
|
||||
state "on", label: "Actuate", action: "off", icon: "st.doors.garage.garage-closed", backgroundColor: "#53a7c0"
|
||||
state "doorClosed", label: "Closed", action: "on", icon: "st.doors.garage.garage-closed", backgroundColor: "#00A0DC"
|
||||
state "doorOpen", label: "Open", action: "on", icon: "st.doors.garage.garage-open", backgroundColor: "#e86d13"
|
||||
state "doorOpening", label: "Opening", action: "on", icon: "st.doors.garage.garage-opening", backgroundColor: "#e86d13"
|
||||
state "doorClosing", label: "Closing", action: "on", icon: "st.doors.garage.garage-closing", backgroundColor: "#00A0DC"
|
||||
state "on", label: "Actuate", action: "off", icon: "st.doors.garage.garage-closed", backgroundColor: "#00A0DC"
|
||||
state "off", label: '${name}', action: "on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
standardTile("contact", "device.contact", inactiveLabel: false) {
|
||||
|
||||
@@ -27,8 +27,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
|
||||
state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#53a7c0")
|
||||
state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ebeef2")
|
||||
state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#00A0DC")
|
||||
state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ffffff")
|
||||
}
|
||||
main "presence"
|
||||
details "presence"
|
||||
|
||||
@@ -32,7 +32,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label: 'Push', action: "momentary.push", backgroundColor: "#ffffff", nextState: "on"
|
||||
state "on", label: 'Push', action: "momentary.push", backgroundColor: "#53a7c0"
|
||||
state "on", label: 'Push', action: "momentary.push", backgroundColor: "#00A0DC"
|
||||
}
|
||||
main "switch"
|
||||
details "switch"
|
||||
@@ -43,8 +43,8 @@ def parse(String description) {
|
||||
}
|
||||
|
||||
def push() {
|
||||
sendEvent(name: "switch", value: "on", isStateChange: true, display: false)
|
||||
sendEvent(name: "switch", value: "off", isStateChange: true, display: false)
|
||||
sendEvent(name: "switch", value: "on", isStateChange: true, displayed: false)
|
||||
sendEvent(name: "switch", value: "off", isStateChange: true, displayed: false)
|
||||
sendEvent(name: "momentary", value: "pushed", isStateChange: true)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ metadata {
|
||||
// UI tile definitions
|
||||
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")
|
||||
}
|
||||
|
||||
main "motion"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Nyce Door/Window Sensor (Open/Close Sensor)
|
||||
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
@@ -23,7 +23,11 @@ Works with:
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C2 Nyce Door/Window sensor that has 12min check-in interval
|
||||
Nyce Door/Window sensor with reporting interval of 5 min.
|
||||
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
|
||||
|
||||
* __12min__ checkInterval
|
||||
|
||||
|
||||
## Battery Specification
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -31,7 +31,7 @@ metadata {
|
||||
tiles {
|
||||
standardTile("button", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label: 'Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "on"
|
||||
state "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "off"
|
||||
state "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState: "off"
|
||||
}
|
||||
main "button"
|
||||
details "button"
|
||||
|
||||
@@ -31,7 +31,7 @@ metadata {
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true, canChangeBackground: 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"
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,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"
|
||||
}
|
||||
|
||||
main "contact"
|
||||
@@ -40,14 +40,11 @@ metadata {
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
def name = null
|
||||
def value = description
|
||||
if (zigbee.isZoneType19(description)) {
|
||||
name = "contact"
|
||||
value = zigbee.translateStatusZoneType19(description) ? "open" : "closed"
|
||||
def resMap
|
||||
if (description.startsWith("zone")) {
|
||||
resMap = createEvent(name: "contact", value: zigbee.parseZoneStatus(description).isAlarm1Set() ? "open" : "closed")
|
||||
}
|
||||
|
||||
def result = createEvent(name: name, value: value)
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
log.debug "Parse returned $resMap"
|
||||
return resMap
|
||||
}
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
/*
|
||||
Osram Lightify Gardenspot Mini RGB
|
||||
|
||||
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
|
||||
that issue by using state variables
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "OSRAM LIGHTIFY Gardenspot mini RGB", namespace: "smartthings", author: "SmartThings") {
|
||||
|
||||
capability "Color Temperature"
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Configuration"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Color Control"
|
||||
|
||||
attribute "colorName", "string"
|
||||
|
||||
command "setAdjustedColor"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
// status messages
|
||||
status "on": "on/off: 1"
|
||||
status "off": "on/off: 0"
|
||||
|
||||
// reply messages
|
||||
reply "zcl on-off on": "on/off: 1"
|
||||
reply "zcl on-off off": "on/off: 0"
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {
|
||||
state "color", action:"setAdjustedColor"
|
||||
}
|
||||
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat") {
|
||||
state "colorName", label: '${currentValue}'
|
||||
}
|
||||
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
|
||||
state "level", label: 'Level ${currentValue}%'
|
||||
}
|
||||
|
||||
main(["switch"])
|
||||
details(["switch", "refresh", "colorName", "levelSliderControl", "level", "rgbSelector"])
|
||||
}
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
//log.info "description is $description"
|
||||
if (description?.startsWith("catchall:")) {
|
||||
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
|
||||
{
|
||||
def result = createEvent(name: "switch", value: "on")
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
|
||||
{
|
||||
if(!(description?.startsWith("catchall: 0104 0300"))){
|
||||
def result = createEvent(name: "switch", value: "off")
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (description?.startsWith("read attr -")) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
log.trace "descMap : $descMap"
|
||||
|
||||
if (descMap.cluster == "0300") {
|
||||
if(descMap.attrId == "0000"){ //Hue Attribute
|
||||
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
|
||||
log.debug "Hue value returned is $hueValue"
|
||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||
}
|
||||
else if(descMap.attrId == "0001"){ //Saturation Attribute
|
||||
def saturationValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
|
||||
log.debug "Saturation from refresh is $saturationValue"
|
||||
sendEvent(name: "saturation", value: saturationValue, displayed:false)
|
||||
}
|
||||
}
|
||||
else if(descMap.cluster == "0008"){
|
||||
def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
|
||||
log.debug "dimmer value is $dimmerValue"
|
||||
sendEvent(name: "level", value: dimmerValue)
|
||||
}
|
||||
}
|
||||
else {
|
||||
def name = description?.startsWith("on/off: ") ? "switch" : null
|
||||
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
|
||||
def result = createEvent(name: name, value: value)
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.debug "on()"
|
||||
sendEvent(name: "switch", value: "on")
|
||||
setLevel(state?.levelValue)
|
||||
}
|
||||
|
||||
def zigbeeOff() {
|
||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "off()"
|
||||
sendEvent(name: "switch", value: "off")
|
||||
zigbeeOff()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
[
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 1"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
def configure() {
|
||||
state.levelValue = 100
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
def configCmds = [
|
||||
|
||||
//Switch Reporting
|
||||
"zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1000",
|
||||
|
||||
//Level Control Reporting
|
||||
"zcl global send-me-a-report 8 0 0x20 5 3600 {0010}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",
|
||||
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 500",
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
|
||||
]
|
||||
return configCmds + refresh() // send refresh cmds as part of config
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
|
||||
def poll(){
|
||||
log.debug "Poll is calling refresh"
|
||||
refresh()
|
||||
}
|
||||
|
||||
def zigbeeSetLevel(level) {
|
||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}"
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
state.levelValue = (value==null) ? 100 : value
|
||||
log.trace "setLevel($value)"
|
||||
def cmds = []
|
||||
|
||||
if (value == 0) {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
cmds << zigbeeOff()
|
||||
}
|
||||
else if (device.latestValue("switch") == "off") {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
|
||||
sendEvent(name: "level", value: state.levelValue)
|
||||
def level = hex(state.levelValue * 255 / 100)
|
||||
cmds << zigbeeSetLevel(level)
|
||||
|
||||
//log.debug cmds
|
||||
cmds
|
||||
}
|
||||
|
||||
//input Hue Integer values; returns color name for saturation 100%
|
||||
private getColorName(hueValue){
|
||||
if(hueValue>100 || hueValue<0)
|
||||
return
|
||||
|
||||
hueValue = Math.round(hueValue / 100 * 360)
|
||||
|
||||
log.debug "hue value is $hueValue"
|
||||
|
||||
def colorName = "Color Mode"
|
||||
if(hueValue>=0 && hueValue <= 4){
|
||||
colorName = "Red"
|
||||
}
|
||||
else if (hueValue>=5 && hueValue <=21 ){
|
||||
colorName = "Brick Red"
|
||||
}
|
||||
else if (hueValue>=22 && hueValue <=30 ){
|
||||
colorName = "Safety Orange"
|
||||
}
|
||||
else if (hueValue>=31 && hueValue <=40 ){
|
||||
colorName = "Dark Orange"
|
||||
}
|
||||
else if (hueValue>=41 && hueValue <=49 ){
|
||||
colorName = "Amber"
|
||||
}
|
||||
else if (hueValue>=50 && hueValue <=56 ){
|
||||
colorName = "Gold"
|
||||
}
|
||||
else if (hueValue>=57 && hueValue <=65 ){
|
||||
colorName = "Yellow"
|
||||
}
|
||||
else if (hueValue>=66 && hueValue <=83 ){
|
||||
colorName = "Electric Lime"
|
||||
}
|
||||
else if (hueValue>=84 && hueValue <=93 ){
|
||||
colorName = "Lawn Green"
|
||||
}
|
||||
else if (hueValue>=94 && hueValue <=112 ){
|
||||
colorName = "Bright Green"
|
||||
}
|
||||
else if (hueValue>=113 && hueValue <=135 ){
|
||||
colorName = "Lime"
|
||||
}
|
||||
else if (hueValue>=136 && hueValue <=166 ){
|
||||
colorName = "Spring Green"
|
||||
}
|
||||
else if (hueValue>=167 && hueValue <=171 ){
|
||||
colorName = "Turquoise"
|
||||
}
|
||||
else if (hueValue>=172 && hueValue <=187 ){
|
||||
colorName = "Aqua"
|
||||
}
|
||||
else if (hueValue>=188 && hueValue <=203 ){
|
||||
colorName = "Sky Blue"
|
||||
}
|
||||
else if (hueValue>=204 && hueValue <=217 ){
|
||||
colorName = "Dodger Blue"
|
||||
}
|
||||
else if (hueValue>=218 && hueValue <=223 ){
|
||||
colorName = "Navy Blue"
|
||||
}
|
||||
else if (hueValue>=224 && hueValue <=251 ){
|
||||
colorName = "Blue"
|
||||
}
|
||||
else if (hueValue>=252 && hueValue <=256 ){
|
||||
colorName = "Han Purple"
|
||||
}
|
||||
else if (hueValue>=257 && hueValue <=274 ){
|
||||
colorName = "Electric Indigo"
|
||||
}
|
||||
else if (hueValue>=275 && hueValue <=289 ){
|
||||
colorName = "Electric Purple"
|
||||
}
|
||||
else if (hueValue>=290 && hueValue <=300 ){
|
||||
colorName = "Orchid Purple"
|
||||
}
|
||||
else if (hueValue>=301 && hueValue <=315 ){
|
||||
colorName = "Magenta"
|
||||
}
|
||||
else if (hueValue>=316 && hueValue <=326 ){
|
||||
colorName = "Hot Pink"
|
||||
}
|
||||
else if (hueValue>=327 && hueValue <=335 ){
|
||||
colorName = "Deep Pink"
|
||||
}
|
||||
else if (hueValue>=336 && hueValue <=339 ){
|
||||
colorName = "Raspberry"
|
||||
}
|
||||
else if (hueValue>=340 && hueValue <=352 ){
|
||||
colorName = "Crimson"
|
||||
}
|
||||
else if (hueValue>=353 && hueValue <=360 ){
|
||||
colorName = "Red"
|
||||
}
|
||||
|
||||
colorName
|
||||
}
|
||||
|
||||
private getEndpointId() {
|
||||
new BigInteger(device.endpointId, 16).toString()
|
||||
}
|
||||
|
||||
private hex(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
private evenHex(value){
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() % 2 != 0) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex,16)
|
||||
}
|
||||
|
||||
//Need to reverse array of size 2
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
byte tmp;
|
||||
tmp = array[1];
|
||||
array[1] = array[0];
|
||||
array[0] = tmp;
|
||||
return array
|
||||
}
|
||||
|
||||
def setAdjustedColor(value) {
|
||||
log.debug "setAdjustedColor: ${value}"
|
||||
def adjusted = value + [:]
|
||||
adjusted.level = null // needed because color picker always sends 100
|
||||
setColor(adjusted)
|
||||
}
|
||||
|
||||
def setColor(value){
|
||||
log.trace "setColor($value)"
|
||||
def max = 0xfe
|
||||
|
||||
if (value.hex) { sendEvent(name: "color", value: value.hex, displayed:false)}
|
||||
|
||||
def colorName = getColorName(value.hue)
|
||||
sendEvent(name: "colorName", value: colorName)
|
||||
|
||||
log.debug "color name is : $colorName"
|
||||
sendEvent(name: "hue", value: value.hue, displayed:false)
|
||||
sendEvent(name: "saturation", value: value.saturation, displayed:false)
|
||||
def scaledHueValue = evenHex(Math.round(value.hue * max / 100.0))
|
||||
def scaledSatValue = evenHex(Math.round(value.saturation * max / 100.0))
|
||||
|
||||
def cmd = []
|
||||
if (value.switch != "off" && device.latestValue("switch") == "off") {
|
||||
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 1 {}"
|
||||
cmd << "delay 150"
|
||||
}
|
||||
|
||||
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x00 {${scaledHueValue} 00 0000}"
|
||||
cmd << "delay 150"
|
||||
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x03 {${scaledSatValue} 0000}"
|
||||
|
||||
if (value.level) {
|
||||
state.levelValue = value.level
|
||||
sendEvent(name: "level", value: value.level)
|
||||
def level = hex(value.level * 255 / 100)
|
||||
cmd << zigbeeSetLevel(level)
|
||||
}
|
||||
|
||||
if (value.switch == "off") {
|
||||
cmd << "delay 150"
|
||||
cmd << off()
|
||||
}
|
||||
|
||||
cmd
|
||||
}
|
||||
@@ -1,459 +0,0 @@
|
||||
/*
|
||||
Osram Flex RGBW Light Strip
|
||||
|
||||
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
|
||||
that issue by using state variables
|
||||
*/
|
||||
//DEPRECATED - Using the generic DTH for this device. Users need to be moved before deleting this DTH
|
||||
|
||||
metadata {
|
||||
definition (name: "OSRAM LIGHTIFY LED Flexible Strip RGBW", namespace: "smartthings", author: "SmartThings") {
|
||||
|
||||
capability "Color Temperature"
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Configuration"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Color Control"
|
||||
|
||||
attribute "colorName", "string"
|
||||
|
||||
command "setAdjustedColor"
|
||||
|
||||
|
||||
//fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Flex RGBW"
|
||||
//fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Flex RGBW"
|
||||
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
// status messages
|
||||
status "on": "on/off: 1"
|
||||
status "off": "on/off: 0"
|
||||
|
||||
// reply messages
|
||||
reply "zcl on-off on": "on/off: 1"
|
||||
reply "zcl on-off off": "on/off: 0"
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
|
||||
state "colorTemperature", action:"color temperature.setColorTemperature"
|
||||
}
|
||||
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat") {
|
||||
state "colorTemperature", label: '${currentValue} K'
|
||||
}
|
||||
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat") {
|
||||
state "colorName", label: '${currentValue}'
|
||||
}
|
||||
|
||||
controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {
|
||||
state "color", action:"setAdjustedColor"
|
||||
}
|
||||
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
|
||||
state "level", label: 'Level ${currentValue}%'
|
||||
}
|
||||
|
||||
|
||||
main(["switch"])
|
||||
details(["switch", "refresh", "colorName", "levelSliderControl", "level", "colorTempSliderControl", "colorTemp", "rgbSelector"])
|
||||
}
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
//log.info "description is $description"
|
||||
if (description?.startsWith("catchall:")) {
|
||||
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
|
||||
{
|
||||
def result = createEvent(name: "switch", value: "on")
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
|
||||
{
|
||||
if(!(description?.startsWith("catchall: 0104 0300"))){
|
||||
def result = createEvent(name: "switch", value: "off")
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (description?.startsWith("read attr -")) { //for values returned after hitting refresh
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
log.trace "descMap : $descMap"
|
||||
|
||||
if (descMap.cluster == "0300") {
|
||||
if(descMap.attrId == "0007"){
|
||||
log.debug "in read attr"
|
||||
log.debug descMap.value
|
||||
def tempInMired = convertHexToInt(descMap.value)
|
||||
def tempInKelvin = Math.round(1000000/tempInMired)
|
||||
log.trace "temp in kelvin: $tempInKelvin"
|
||||
sendEvent(name: "colorTemperature", value: tempInKelvin, displayed:false)
|
||||
}
|
||||
else if(descMap.attrId == "0008"){ //Color mode attribute
|
||||
if(descMap.value == "00"){
|
||||
state.colorType = "rgb"
|
||||
}else if(descMap.value == "02"){
|
||||
state.colorType = "white"
|
||||
}
|
||||
}
|
||||
else if(descMap.attrId == "0000"){ //Hue Attribute
|
||||
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
|
||||
log.debug "Hue value returned is $hueValue"
|
||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||
}
|
||||
else if(descMap.attrId == "0001"){ //Saturation Attribute
|
||||
def saturationValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
|
||||
log.debug "Saturation from refresh is $saturationValue"
|
||||
sendEvent(name: "saturation", value: saturationValue, displayed:false)
|
||||
}
|
||||
}
|
||||
else if(descMap.cluster == "0008"){
|
||||
def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
|
||||
log.debug "dimmer value is $dimmerValue"
|
||||
sendEvent(name: "level", value: dimmerValue)
|
||||
}
|
||||
}
|
||||
else {
|
||||
def name = description?.startsWith("on/off: ") ? "switch" : null
|
||||
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
|
||||
def result = createEvent(name: name, value: value)
|
||||
log.debug "description is $description"
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.debug "on()"
|
||||
sendEvent(name: "switch", value: "on")
|
||||
setLevel(state?.levelValue)
|
||||
}
|
||||
|
||||
def zigbeeOff() {
|
||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "off()"
|
||||
sendEvent(name: "switch", value: "off")
|
||||
zigbeeOff()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
[
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 1", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 7", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 8"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
def configure() {
|
||||
state.levelValue = 100
|
||||
state.colorType = "white"
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
def configCmds = [
|
||||
|
||||
//Switch Reporting
|
||||
"zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
|
||||
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1000",
|
||||
|
||||
//Level Control Reporting
|
||||
"zcl global send-me-a-report 8 0 0x20 5 3600 {0010}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",
|
||||
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 1000",
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 500",
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
|
||||
]
|
||||
return configCmds + refresh() // send refresh cmds as part of config
|
||||
}
|
||||
|
||||
def setColorTemperature(value) {
|
||||
state?.colorType = "white"
|
||||
if(value<101){
|
||||
value = (value*38) + 2700 //Calculation of mapping 0-100 to 2700-6500
|
||||
}
|
||||
|
||||
def tempInMired = Math.round(1000000/value)
|
||||
def finalHex = swapEndianHex(hex(tempInMired, 4))
|
||||
def genericName = getGenericName(value)
|
||||
log.debug "generic name is : $genericName"
|
||||
|
||||
def cmds = []
|
||||
sendEvent(name: "colorTemperature", value: value, displayed:false)
|
||||
sendEvent(name: "colorName", value: genericName)
|
||||
|
||||
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x0300 0x0a {${finalHex} 2000}"
|
||||
|
||||
cmds
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
|
||||
def poll(){
|
||||
log.debug "Poll is calling refresh"
|
||||
refresh()
|
||||
}
|
||||
|
||||
def zigbeeSetLevel(level) {
|
||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}"
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
state.levelValue = (value==null) ? 100 : value
|
||||
log.trace "setLevel($value)"
|
||||
def cmds = []
|
||||
|
||||
if (value == 0) {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
cmds << zigbeeOff()
|
||||
}
|
||||
else if (device.latestValue("switch") == "off") {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
|
||||
sendEvent(name: "level", value: state.levelValue)
|
||||
def level = hex(state.levelValue * 255 / 100)
|
||||
cmds << zigbeeSetLevel(level)
|
||||
|
||||
//log.debug cmds
|
||||
cmds
|
||||
}
|
||||
|
||||
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
|
||||
private getGenericName(value){
|
||||
def genericName = "White"
|
||||
if(state?.colorType == "rgb"){
|
||||
genericName = "Color Mode"
|
||||
}
|
||||
else{
|
||||
if(value < 3300){
|
||||
genericName = "Soft White"
|
||||
} else if(value < 4150){
|
||||
genericName = "Moonlight"
|
||||
} else if(value < 5000){
|
||||
genericName = "Cool White"
|
||||
} else if(value <= 6500){
|
||||
genericName = "Daylight"
|
||||
}
|
||||
}
|
||||
|
||||
genericName
|
||||
}
|
||||
|
||||
//input Hue Integer values; returns color name for saturation 100%
|
||||
private getColorName(hueValue){
|
||||
if(hueValue>100 || hueValue<0)
|
||||
return
|
||||
|
||||
hueValue = Math.round(hueValue / 100 * 360)
|
||||
|
||||
log.debug "hue value is $hueValue"
|
||||
|
||||
def colorName = "Color Mode"
|
||||
if(hueValue>=0 && hueValue <= 4){
|
||||
colorName = "Red"
|
||||
}
|
||||
else if (hueValue>=5 && hueValue <=21 ){
|
||||
colorName = "Brick Red"
|
||||
}
|
||||
else if (hueValue>=22 && hueValue <=30 ){
|
||||
colorName = "Safety Orange"
|
||||
}
|
||||
else if (hueValue>=31 && hueValue <=40 ){
|
||||
colorName = "Dark Orange"
|
||||
}
|
||||
else if (hueValue>=41 && hueValue <=49 ){
|
||||
colorName = "Amber"
|
||||
}
|
||||
else if (hueValue>=50 && hueValue <=56 ){
|
||||
colorName = "Gold"
|
||||
}
|
||||
else if (hueValue>=57 && hueValue <=65 ){
|
||||
colorName = "Yellow"
|
||||
}
|
||||
else if (hueValue>=66 && hueValue <=83 ){
|
||||
colorName = "Electric Lime"
|
||||
}
|
||||
else if (hueValue>=84 && hueValue <=93 ){
|
||||
colorName = "Lawn Green"
|
||||
}
|
||||
else if (hueValue>=94 && hueValue <=112 ){
|
||||
colorName = "Bright Green"
|
||||
}
|
||||
else if (hueValue>=113 && hueValue <=135 ){
|
||||
colorName = "Lime"
|
||||
}
|
||||
else if (hueValue>=136 && hueValue <=166 ){
|
||||
colorName = "Spring Green"
|
||||
}
|
||||
else if (hueValue>=167 && hueValue <=171 ){
|
||||
colorName = "Turquoise"
|
||||
}
|
||||
else if (hueValue>=172 && hueValue <=187 ){
|
||||
colorName = "Aqua"
|
||||
}
|
||||
else if (hueValue>=188 && hueValue <=203 ){
|
||||
colorName = "Sky Blue"
|
||||
}
|
||||
else if (hueValue>=204 && hueValue <=217 ){
|
||||
colorName = "Dodger Blue"
|
||||
}
|
||||
else if (hueValue>=218 && hueValue <=223 ){
|
||||
colorName = "Navy Blue"
|
||||
}
|
||||
else if (hueValue>=224 && hueValue <=251 ){
|
||||
colorName = "Blue"
|
||||
}
|
||||
else if (hueValue>=252 && hueValue <=256 ){
|
||||
colorName = "Han Purple"
|
||||
}
|
||||
else if (hueValue>=257 && hueValue <=274 ){
|
||||
colorName = "Electric Indigo"
|
||||
}
|
||||
else if (hueValue>=275 && hueValue <=289 ){
|
||||
colorName = "Electric Purple"
|
||||
}
|
||||
else if (hueValue>=290 && hueValue <=300 ){
|
||||
colorName = "Orchid Purple"
|
||||
}
|
||||
else if (hueValue>=301 && hueValue <=315 ){
|
||||
colorName = "Magenta"
|
||||
}
|
||||
else if (hueValue>=316 && hueValue <=326 ){
|
||||
colorName = "Hot Pink"
|
||||
}
|
||||
else if (hueValue>=327 && hueValue <=335 ){
|
||||
colorName = "Deep Pink"
|
||||
}
|
||||
else if (hueValue>=336 && hueValue <=339 ){
|
||||
colorName = "Raspberry"
|
||||
}
|
||||
else if (hueValue>=340 && hueValue <=352 ){
|
||||
colorName = "Crimson"
|
||||
}
|
||||
else if (hueValue>=353 && hueValue <=360 ){
|
||||
colorName = "Red"
|
||||
}
|
||||
|
||||
colorName
|
||||
}
|
||||
|
||||
|
||||
private getEndpointId() {
|
||||
new BigInteger(device.endpointId, 16).toString()
|
||||
}
|
||||
|
||||
private hex(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
private evenHex(value){
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() % 2 != 0) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex,16)
|
||||
}
|
||||
|
||||
//Need to reverse array of size 2
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
byte tmp;
|
||||
tmp = array[1];
|
||||
array[1] = array[0];
|
||||
array[0] = tmp;
|
||||
return array
|
||||
}
|
||||
|
||||
def setAdjustedColor(value) {
|
||||
log.debug "setAdjustedColor: ${value}"
|
||||
def adjusted = value + [:]
|
||||
adjusted.level = null // needed because color picker always sends 100
|
||||
setColor(adjusted)
|
||||
}
|
||||
|
||||
def setColor(value){
|
||||
state?.colorType = "rgb"
|
||||
log.trace "setColor($value)"
|
||||
def max = 0xfe
|
||||
|
||||
if (value.hex) { sendEvent(name: "color", value: value.hex, displayed:false)}
|
||||
|
||||
def colorName = getColorName(value.hue)
|
||||
log.debug "color name is : $colorName"
|
||||
sendEvent(name: "colorName", value: colorName)
|
||||
sendEvent(name: "colorTemperature", value: "--", displayed:false)
|
||||
|
||||
|
||||
sendEvent(name: "hue", value: value.hue, displayed:false)
|
||||
sendEvent(name: "saturation", value: value.saturation, displayed:false)
|
||||
def scaledHueValue = evenHex(Math.round(value.hue * max / 100.0))
|
||||
def scaledSatValue = evenHex(Math.round(value.saturation * max / 100.0))
|
||||
|
||||
def cmd = []
|
||||
if (value.switch != "off" && device.latestValue("switch") == "off") {
|
||||
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 1 {}"
|
||||
cmd << "delay 150"
|
||||
}
|
||||
|
||||
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x00 {${scaledHueValue} 00 0000}"
|
||||
cmd << "delay 150"
|
||||
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x03 {${scaledSatValue} 0000}"
|
||||
|
||||
if (value.level) {
|
||||
state.levelValue = value.level
|
||||
sendEvent(name: "level", value: value.level)
|
||||
def level = hex(value.level * 255 / 100)
|
||||
cmd << zigbeeSetLevel(level)
|
||||
}
|
||||
|
||||
if (value.switch == "off") {
|
||||
cmd << "delay 150"
|
||||
cmd << off()
|
||||
}
|
||||
|
||||
cmd
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
Osram Tunable White 60 A19 bulb
|
||||
|
||||
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
|
||||
that issue by using state variables
|
||||
*/
|
||||
|
||||
//DEPRECATED - Using the generic DTH for this device. Users need to be moved before deleting this DTH
|
||||
|
||||
metadata {
|
||||
definition (name: "OSRAM LIGHTIFY LED Tunable White 60W", namespace: "smartthings", author: "SmartThings") {
|
||||
|
||||
capability "Color Temperature"
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Switch Level"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
attribute "colorName", "string"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
// status messages
|
||||
status "on": "on/off: 1"
|
||||
status "off": "on/off: 0"
|
||||
|
||||
// reply messages
|
||||
reply "zcl on-off on": "on/off: 1"
|
||||
reply "zcl on-off off": "on/off: 0"
|
||||
}
|
||||
|
||||
// UI tile definitions
|
||||
tiles {
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
|
||||
state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
|
||||
state "colorTemperature", action:"color temperature.setColorTemperature"
|
||||
}
|
||||
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat") {
|
||||
state "colorTemperature", label: '${currentValue} K'
|
||||
}
|
||||
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat") {
|
||||
state "colorName", label: '${currentValue}'
|
||||
}
|
||||
|
||||
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
|
||||
state "level", action:"switch level.setLevel"
|
||||
}
|
||||
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
|
||||
state "level", label: 'Level ${currentValue}%'
|
||||
}
|
||||
|
||||
|
||||
main(["switch"])
|
||||
details(["switch", "refresh", "colorName", "levelSliderControl", "level", "colorTempSliderControl", "colorTemp"])
|
||||
}
|
||||
}
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
//log.trace description
|
||||
|
||||
if (description?.startsWith("catchall:")) {
|
||||
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
|
||||
{
|
||||
def result = createEvent(name: "switch", value: "on")
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
|
||||
{
|
||||
def result = createEvent(name: "switch", value: "off")
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
else if (description?.startsWith("read attr -")) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
log.trace "descMap : $descMap"
|
||||
|
||||
if (descMap.cluster == "0300") {
|
||||
log.debug descMap.value
|
||||
def tempInMired = convertHexToInt(descMap.value)
|
||||
def tempInKelvin = Math.round(1000000/tempInMired)
|
||||
log.trace "temp in kelvin: $tempInKelvin"
|
||||
sendEvent(name: "colorTemperature", value: tempInKelvin, displayed:false)
|
||||
}
|
||||
else if(descMap.cluster == "0008"){
|
||||
def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
|
||||
log.debug "dimmer value is $dimmerValue"
|
||||
sendEvent(name: "level", value: dimmerValue)
|
||||
}
|
||||
}
|
||||
else {
|
||||
def name = description?.startsWith("on/off: ") ? "switch" : null
|
||||
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
|
||||
def result = createEvent(name: name, value: value)
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.debug "on()"
|
||||
sendEvent(name: "switch", value: "on")
|
||||
setLevel(state?.levelValue)
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "off()"
|
||||
sendEvent(name: "switch", value: "off")
|
||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
[
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 7"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
def configure() {
|
||||
state.levelValue = 100
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
def configCmds = [
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
|
||||
]
|
||||
return onOffConfig() + levelConfig() + configCmds + refresh() // send refresh cmds as part of config
|
||||
}
|
||||
|
||||
def onOffConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 6 0 0x10 0 300 {01}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
//level config for devices with min reporting interval as 5 seconds and reporting interval if no activity as 1hour (3600s)
|
||||
//min level change is 01
|
||||
def levelConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 8 0 0x20 5 3600 {01}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
def setColorTemperature(value) {
|
||||
if(value<101){
|
||||
value = (value*38) + 2700 //Calculation of mapping 0-100 to 2700-6500
|
||||
}
|
||||
|
||||
def tempInMired = Math.round(1000000/value)
|
||||
def finalHex = swapEndianHex(hex(tempInMired, 4))
|
||||
def genericName = getGenericName(value)
|
||||
log.debug "generic name is : $genericName"
|
||||
|
||||
def cmds = []
|
||||
sendEvent(name: "colorTemperature", value: value, displayed:false)
|
||||
sendEvent(name: "colorName", value: genericName)
|
||||
|
||||
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x0300 0x0a {${finalHex} 2000}"
|
||||
|
||||
cmds
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
state.levelValue = (value==null) ? 100 : value
|
||||
log.trace "setLevel($value)"
|
||||
def cmds = []
|
||||
|
||||
if (value == 0) {
|
||||
sendEvent(name: "switch", value: "off")
|
||||
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
|
||||
}
|
||||
else if (device.latestValue("switch") == "off") {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
|
||||
sendEvent(name: "level", value: state.levelValue)
|
||||
def level = hex(state.levelValue * 254 / 100)
|
||||
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}"
|
||||
|
||||
//log.debug cmds
|
||||
cmds
|
||||
}
|
||||
|
||||
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
|
||||
private getGenericName(value){
|
||||
def genericName = "White"
|
||||
if(value < 3300){
|
||||
genericName = "Soft White"
|
||||
} else if(value < 4150){
|
||||
genericName = "Moonlight"
|
||||
} else if(value < 5000){
|
||||
genericName = "Cool White"
|
||||
} else if(value <= 6500){
|
||||
genericName = "Daylight"
|
||||
}
|
||||
|
||||
genericName
|
||||
}
|
||||
|
||||
private getEndpointId() {
|
||||
new BigInteger(device.endpointId, 16).toString()
|
||||
}
|
||||
|
||||
private hex(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex,16)
|
||||
}
|
||||
|
||||
//Need to reverse array of size 2
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
byte tmp;
|
||||
tmp = array[1];
|
||||
array[1] = array[0];
|
||||
array[0] = tmp;
|
||||
return array
|
||||
}
|
||||
@@ -37,9 +37,9 @@ metadata {
|
||||
}
|
||||
|
||||
standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00a0dc", nextState:"turningOff"
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00a0dc", nextState:"turningOff"
|
||||
state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat") {
|
||||
|
||||
@@ -42,9 +42,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"
|
||||
}
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) {
|
||||
|
||||
@@ -24,6 +24,7 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
capability "Outlet"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-ZHAC"
|
||||
|
||||
@@ -69,283 +70,36 @@ metadata {
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
|
||||
def finalResult = isKnownDescription(description)
|
||||
if (finalResult != "false") {
|
||||
log.info finalResult
|
||||
if (finalResult.type == "update") {
|
||||
log.info "$device updates: ${finalResult.value}"
|
||||
}
|
||||
else if (finalResult.type == "power") {
|
||||
def powerValue = (finalResult.value as Integer)/10
|
||||
sendEvent(name: "power", value: powerValue)
|
||||
|
||||
/*
|
||||
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
|
||||
|
||||
power level is an integer. The exact power level with correct units needs to be handled in the device type
|
||||
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
|
||||
*/
|
||||
}
|
||||
else {
|
||||
sendEvent(name: finalResult.type, value: finalResult.value)
|
||||
}
|
||||
}
|
||||
else {
|
||||
def event = zigbee.getEvent(description)
|
||||
if (!event) {
|
||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||
log.debug parseDescriptionAsMap(description)
|
||||
log.debug zigbee.parseDescriptionAsMap(description)
|
||||
} else if (event.name == "power") {
|
||||
/*
|
||||
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. Simplifying to 10 power level is an integer.
|
||||
*/
|
||||
event.value = event.value / 10
|
||||
}
|
||||
}
|
||||
|
||||
// Commands to device
|
||||
def zigbeeCommand(cluster, attribute){
|
||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} ${cluster} ${attribute} {}"
|
||||
}
|
||||
|
||||
def off() {
|
||||
zigbeeCommand("6", "0")
|
||||
}
|
||||
|
||||
def on() {
|
||||
zigbeeCommand("6", "1")
|
||||
return event ? createEvent(event) : event
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
value = value as Integer
|
||||
if (value == 0) {
|
||||
off()
|
||||
}
|
||||
else {
|
||||
if (device.latestValue("switch") == "off") {
|
||||
sendEvent(name: "switch", value: "on")
|
||||
}
|
||||
sendEvent(name: "level", value: value)
|
||||
setLevelWithRate(value, "0000") //value is between 0 to 100
|
||||
}
|
||||
zigbee.setLevel(value)
|
||||
}
|
||||
|
||||
def off() {
|
||||
zigbee.off()
|
||||
}
|
||||
|
||||
def on() {
|
||||
zigbee.on()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
[
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
|
||||
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0B04 0x050B", "delay 500"
|
||||
]
|
||||
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.electricMeasurementPowerRefresh()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
refresh() + onOffConfig() + levelConfig() + powerConfig()
|
||||
}
|
||||
|
||||
|
||||
private getEndpointId() {
|
||||
new BigInteger(device.endpointId, 16).toString()
|
||||
}
|
||||
|
||||
private hex(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
private String swapEndianHex(String hex) {
|
||||
reverseArray(hex.decodeHex()).encodeHex()
|
||||
}
|
||||
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex,16)
|
||||
}
|
||||
|
||||
//Need to reverse array of size 2
|
||||
private byte[] reverseArray(byte[] array) {
|
||||
byte tmp;
|
||||
tmp = array[1];
|
||||
array[1] = array[0];
|
||||
array[0] = tmp;
|
||||
return array
|
||||
}
|
||||
|
||||
def parseDescriptionAsMap(description) {
|
||||
if (description?.startsWith("read attr -")) {
|
||||
(description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()): nameAndValue[1].trim()]
|
||||
}
|
||||
}
|
||||
else if (description?.startsWith("catchall: ")) {
|
||||
def seg = (description - "catchall: ").split(" ")
|
||||
def zigbeeMap = [:]
|
||||
zigbeeMap += [raw: (description - "catchall: ")]
|
||||
zigbeeMap += [profileId: seg[0]]
|
||||
zigbeeMap += [clusterId: seg[1]]
|
||||
zigbeeMap += [sourceEndpoint: seg[2]]
|
||||
zigbeeMap += [destinationEndpoint: seg[3]]
|
||||
zigbeeMap += [options: seg[4]]
|
||||
zigbeeMap += [messageType: seg[5]]
|
||||
zigbeeMap += [dni: seg[6]]
|
||||
zigbeeMap += [isClusterSpecific: Short.valueOf(seg[7], 16) != 0]
|
||||
zigbeeMap += [isManufacturerSpecific: Short.valueOf(seg[8], 16) != 0]
|
||||
zigbeeMap += [manufacturerId: seg[9]]
|
||||
zigbeeMap += [command: seg[10]]
|
||||
zigbeeMap += [direction: seg[11]]
|
||||
zigbeeMap += [data: seg.size() > 12 ? seg[12].split("").findAll { it }.collate(2).collect {
|
||||
it.join('')
|
||||
} : []]
|
||||
|
||||
zigbeeMap
|
||||
}
|
||||
}
|
||||
|
||||
def isKnownDescription(description) {
|
||||
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
|
||||
def descMap = parseDescriptionAsMap(description)
|
||||
if (descMap.cluster == "0006" || descMap.clusterId == "0006") {
|
||||
isDescriptionOnOff(descMap)
|
||||
}
|
||||
else if (descMap.cluster == "0008" || descMap.clusterId == "0008"){
|
||||
isDescriptionLevel(descMap)
|
||||
}
|
||||
else if (descMap.cluster == "0B04" || descMap.clusterId == "0B04"){
|
||||
isDescriptionPower(descMap)
|
||||
}
|
||||
}
|
||||
else if(description?.startsWith("on/off:")) {
|
||||
def switchValue = description?.endsWith("1") ? "on" : "off"
|
||||
return [type: "switch", value : switchValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
def isDescriptionOnOff(descMap) {
|
||||
def switchValue = "undefined"
|
||||
if (descMap.cluster == "0006") { //cluster info from read attr
|
||||
value = descMap.value
|
||||
if (value == "01"){
|
||||
switchValue = "on"
|
||||
}
|
||||
else if (value == "00"){
|
||||
switchValue = "off"
|
||||
}
|
||||
}
|
||||
else if (descMap.clusterId == "0006") {
|
||||
//cluster info from catch all
|
||||
//command 0B is Default response and the last two bytes are [on/off][success]. on/off=00, success=00
|
||||
//command 01 is Read attr response. the last two bytes are [datatype][value]. boolean datatype=10; on/off value = 01/00
|
||||
if ((descMap.command=="0B" && descMap.raw.endsWith("0100")) || (descMap.command=="01" && descMap.raw.endsWith("1001"))){
|
||||
switchValue = "on"
|
||||
}
|
||||
else if ((descMap.command=="0B" && descMap.raw.endsWith("0000")) || (descMap.command=="01" && descMap.raw.endsWith("1000"))){
|
||||
switchValue = "off"
|
||||
}
|
||||
else if(descMap.command=="07"){
|
||||
return [type: "update", value : "switch (0006) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (switchValue != "undefined"){
|
||||
return [type: "switch", value : switchValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//@return - false or "success" or level [0-100]
|
||||
def isDescriptionLevel(descMap) {
|
||||
def dimmerValue = -1
|
||||
if (descMap.cluster == "0008"){
|
||||
//TODO: the message returned with catchall is command 0B with clusterId 0008. That is just a confirmation message
|
||||
def value = convertHexToInt(descMap.value)
|
||||
dimmerValue = Math.round(value * 100 / 255)
|
||||
if(dimmerValue==0 && value > 0) {
|
||||
dimmerValue = 1 //handling for non-zero hex value less than 3
|
||||
}
|
||||
}
|
||||
else if(descMap.clusterId == "0008") {
|
||||
if(descMap.command=="0B"){
|
||||
return [type: "update", value : "level updated successfully"] //device updating the level change was successful. no value sent.
|
||||
}
|
||||
else if(descMap.command=="07"){
|
||||
return [type: "update", value : "level (0008) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (dimmerValue != -1){
|
||||
return [type: "level", value : dimmerValue]
|
||||
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
def isDescriptionPower(descMap) {
|
||||
def powerValue = "undefined"
|
||||
if (descMap.cluster == "0B04") {
|
||||
if (descMap.attrId == "050b") {
|
||||
if(descMap.value!="ffff")
|
||||
powerValue = convertHexToInt(descMap.value)
|
||||
}
|
||||
}
|
||||
else if (descMap.clusterId == "0B04") {
|
||||
if(descMap.command=="07"){
|
||||
return [type: "update", value : "power (0B04) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (powerValue != "undefined"){
|
||||
return [type: "power", value : powerValue]
|
||||
}
|
||||
else {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def onOffConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 6 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 6 0 0x10 0 600 {01}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
//level config for devices with min reporting interval as 5 seconds and reporting interval if no activity as 1hour (3600s)
|
||||
//min level change is 01
|
||||
def levelConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 8 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 8 0 0x20 5 3600 {01}",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
|
||||
]
|
||||
}
|
||||
|
||||
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
|
||||
//min change in value is 05
|
||||
def powerConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 0x0B04 0x050B 0x29 1 600 {05 00}", //The send-me-a-report is custom to the attribute type for CentraLite
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
|
||||
]
|
||||
}
|
||||
|
||||
def setLevelWithRate(level, rate) {
|
||||
if(rate == null){
|
||||
rate = "0000"
|
||||
}
|
||||
level = convertToHexString(level * 255 / 100) //Converting the 0-100 range to 0-FF range in hex
|
||||
"st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {$level $rate}"
|
||||
}
|
||||
|
||||
String convertToHexString(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
refresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.electricMeasurementPowerConfig()
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ metadata {
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Sensor"
|
||||
capability "Outlet"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0006, 0004, 0003, 0000, 0005", outClusters: "0019", manufacturer: "Compacta International, Ltd", model: "ZBMPlug15", deviceJoinName: "SmartPower Outlet V1"
|
||||
}
|
||||
@@ -23,7 +24,7 @@ metadata {
|
||||
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"
|
||||
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"
|
||||
}
|
||||
}
|
||||
main "switch"
|
||||
@@ -51,7 +52,7 @@ def on() {
|
||||
'zcl on-off on',
|
||||
'delay 200',
|
||||
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
|
||||
'delay 500'
|
||||
'delay 2000'
|
||||
|
||||
]
|
||||
|
||||
@@ -62,6 +63,6 @@ def off() {
|
||||
'zcl on-off off',
|
||||
'delay 200',
|
||||
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
|
||||
'delay 500'
|
||||
'delay 2000'
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SmartPower Outlet
|
||||
|
||||
|
||||
Local Execution on V2 Hubs
|
||||
|
||||
Works with:
|
||||
|
||||
@@ -23,10 +23,11 @@ Works with:
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C1 smart power outlet with maxReportTime of 5 mins.
|
||||
Check-in interval is double the value of maxReportTime.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 12 mins
|
||||
SmartPower outlet 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`
|
||||
|
||||
* V1, TV, HubV2 AppEngine < 1.5.1 - __21min__ checkInterval
|
||||
* HubV2 AppEngine 1.5.1 - __12min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
metadata {
|
||||
// Automatically generated. Make future change here.
|
||||
definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") {
|
||||
definition(name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Switch"
|
||||
capability "Power Meter"
|
||||
@@ -24,10 +23,12 @@ metadata {
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
capability "Outlet"
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "Outlet"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Outlet"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "Outlet"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Outlet"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019"
|
||||
}
|
||||
|
||||
@@ -45,32 +46,32 @@ metadata {
|
||||
preferences {
|
||||
section {
|
||||
image(name: 'educationalcontent', multiple: true, images: [
|
||||
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS1.jpg",
|
||||
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS2.jpg"
|
||||
])
|
||||
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS1.jpg",
|
||||
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS2.jpg"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// 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: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||
multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
|
||||
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState: "turningOff"
|
||||
attributeState "off", label: 'Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||
attributeState "turningOn", label: 'Turning On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
|
||||
attributeState "turningOn", label: 'Turning On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState: "turningOff"
|
||||
attributeState "turningOff", label: 'Turning Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
|
||||
}
|
||||
tileAttribute ("power", key: "SECONDARY_CONTROL") {
|
||||
attributeState "power", label:'${currentValue} W'
|
||||
tileAttribute("power", key: "SECONDARY_CONTROL") {
|
||||
attributeState "power", label: '${currentValue} W'
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
|
||||
}
|
||||
|
||||
main "switch"
|
||||
details(["switch","refresh"])
|
||||
details(["switch", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,48 +79,33 @@ metadata {
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
|
||||
def finalResult = zigbee.getKnownDescription(description)
|
||||
def event = zigbee.getEvent(description)
|
||||
|
||||
//TODO: Remove this after getKnownDescription can parse it automatically
|
||||
if (!finalResult && description!="updated")
|
||||
finalResult = getPowerDescription(zigbee.parseDescriptionAsMap(description))
|
||||
|
||||
if (finalResult) {
|
||||
log.info "final result = $finalResult"
|
||||
if (finalResult.type == "update") {
|
||||
log.info "$device updates: ${finalResult.value}"
|
||||
if (event) {
|
||||
if (event.name == "power") {
|
||||
def value = (event.value as Integer) / 10
|
||||
event = createEvent(name: event.name, value: value, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true)
|
||||
} else if (event.name == "switch") {
|
||||
def descriptionText = event.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
||||
event = createEvent(name: event.name, value: event.value, descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
else if (finalResult.type == "power") {
|
||||
def powerValue = (finalResult.value as Integer)/10
|
||||
sendEvent(name: "power", value: powerValue, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true )
|
||||
/*
|
||||
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
|
||||
power level is an integer. The exact power level with correct units needs to be handled in the device type
|
||||
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
|
||||
*/
|
||||
}
|
||||
else {
|
||||
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
|
||||
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
def cluster = zigbee.parse(description)
|
||||
|
||||
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07){
|
||||
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) {
|
||||
if (cluster.data[0] == 0x00) {
|
||||
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
else {
|
||||
event = createEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
} else {
|
||||
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
||||
event = null
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||
log.debug "${cluster}"
|
||||
}
|
||||
}
|
||||
return event ? createEvent(event) : event
|
||||
}
|
||||
|
||||
def off() {
|
||||
@@ -141,47 +127,11 @@ def refresh() {
|
||||
}
|
||||
|
||||
def configure() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// 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: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
refresh() + zigbee.onOffConfig(0, 300) + powerConfig()
|
||||
refresh() + zigbee.onOffConfig(0, 300) + zigbee.electricMeasurementPowerConfig()
|
||||
}
|
||||
|
||||
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
|
||||
//min change in value is 01
|
||||
def powerConfig() {
|
||||
[
|
||||
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200",
|
||||
"zcl global send-me-a-report 0x0B04 0x050B 0x29 1 600 {05 00}", //The send-me-a-report is custom to the attribute type for CentraLite
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
|
||||
]
|
||||
}
|
||||
|
||||
private getEndpointId() {
|
||||
new BigInteger(device.endpointId, 16).toString()
|
||||
}
|
||||
|
||||
//TODO: Remove this after getKnownDescription can parse it automatically
|
||||
def getPowerDescription(descMap) {
|
||||
def powerValue = "undefined"
|
||||
if (descMap.cluster == "0B04") {
|
||||
if (descMap.attrId == "050b") {
|
||||
if(descMap.value!="ffff")
|
||||
powerValue = zigbee.convertHexToInt(descMap.value)
|
||||
}
|
||||
}
|
||||
else if (descMap.clusterId == "0B04") {
|
||||
if(descMap.command=="07"){
|
||||
return [type: "update", value : "power (0B04) capability configured successfully"]
|
||||
}
|
||||
}
|
||||
|
||||
if (powerValue != "undefined"){
|
||||
return [type: "power", value : powerValue]
|
||||
}
|
||||
else {
|
||||
return [:]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,18 +50,18 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){
|
||||
tileAttribute ("device.status", key: "PRIMARY_CONTROL") {
|
||||
attributeState "closed", label:'${name}', icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821", nextState:"opening"
|
||||
attributeState "open", label:'${name}', icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e", nextState:"closing"
|
||||
attributeState "opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e"
|
||||
attributeState "closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e"
|
||||
attributeState "closed", label:'${name}', icon:"st.doors.garage.garage-closed", backgroundColor:"#00A0DC", nextState:"opening"
|
||||
attributeState "open", label:'${name}', icon:"st.doors.garage.garage-open", backgroundColor:"#e86d13", nextState:"closing"
|
||||
attributeState "opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#e86d13"
|
||||
attributeState "closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#00A0DC"
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
standardTile("acceleration", "device.acceleration", decoration: "flat", width: 2, height: 2) {
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#00A0DC")
|
||||
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
|
||||
}
|
||||
valueTile("temperature", "device.temperature", decoration: "flat", width: 2, height: 2) {
|
||||
@@ -86,9 +86,9 @@ metadata {
|
||||
|
||||
def parse(String description) {
|
||||
log.debug "parse($description)"
|
||||
def results = null
|
||||
def results = [:]
|
||||
|
||||
if (!isSupportedDescription(description) || zigbee.isZoneType19(description)) {
|
||||
if (!isSupportedDescription(description) || description.startsWith("zone")) {
|
||||
// Ignore this in favor of orientation-based state
|
||||
// results = parseSingleMessage(description)
|
||||
}
|
||||
|
||||
@@ -50,17 +50,17 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("status", "device.status", width: 2, height: 2) {
|
||||
state("closed", label:'${name}', icon:"st.doors.garage.garage-closed", action: "actuate", backgroundColor:"#79b821", nextState:"opening")
|
||||
state("open", label:'${name}', icon:"st.doors.garage.garage-open", action: "actuate", 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("closed", label:'${name}', icon:"st.doors.garage.garage-closed", action: "actuate", backgroundColor:"#00A0DC", nextState:"opening")
|
||||
state("open", label:'${name}', icon:"st.doors.garage.garage-open", action: "actuate", 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("contact", "device.contact") {
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
}
|
||||
standardTile("acceleration", "device.acceleration", decoration: "flat") {
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#00A0DC")
|
||||
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
|
||||
}
|
||||
valueTile("temperature", "device.temperature", decoration: "flat") {
|
||||
@@ -117,7 +117,7 @@ def parse(String description) {
|
||||
log.debug "parse($description)"
|
||||
def results = null
|
||||
|
||||
if (!isSupportedDescription(description) || zigbee.isZoneType19(description)) {
|
||||
if (!isSupportedDescription(description) || description.startsWith("zone")) {
|
||||
// Ignore this in favor of orientation-based state
|
||||
// results = parseSingleMessage(description)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Smartsense Moisture Sensor
|
||||
|
||||
|
||||
Local Execution on V2 Hubs
|
||||
|
||||
Works with:
|
||||
|
||||
@@ -23,10 +23,11 @@ Works with:
|
||||
|
||||
## Device Health
|
||||
|
||||
A Category C2 moisture sensor with maxReportTime of 5 mins.
|
||||
Check-in interval is double the value of maxReportTime.
|
||||
This gives the device twice the amount of time to respond before it is marked as offline.
|
||||
Check-in interval = 12 mins
|
||||
SmartSense Moisture sensor 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`
|
||||
|
||||
* V1, TV, HubV2 AppEngine < 1.5.1 - __121min__ checkInterval
|
||||
* HubV2 AppEngine 1.5.1 - __12min__ checkInterval
|
||||
|
||||
## Battery Specification
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
|
||||
|
||||
|
||||
metadata {
|
||||
definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings") {
|
||||
definition(name: "SmartSense Moisture Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Configuration"
|
||||
capability "Battery"
|
||||
capability "Refresh"
|
||||
@@ -29,9 +29,11 @@ metadata {
|
||||
command "enrollResponse"
|
||||
|
||||
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor"
|
||||
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,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor"
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -42,10 +44,10 @@ metadata {
|
||||
preferences {
|
||||
section {
|
||||
image(name: 'educationalcontent', multiple: true, images: [
|
||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture1.png",
|
||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture2.png",
|
||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture3.png"
|
||||
])
|
||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture1.png",
|
||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture2.png",
|
||||
"http://cdn.device-gse.smartthings.com/Moisture/Moisture3.png"
|
||||
])
|
||||
}
|
||||
section {
|
||||
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
||||
@@ -54,32 +56,32 @@ metadata {
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
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"
|
||||
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: "#00A0DC"
|
||||
}
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
backgroundColors:[
|
||||
[value: 31, color: "#153591"],
|
||||
[value: 44, color: "#1e9cbb"],
|
||||
[value: 59, color: "#90d2a7"],
|
||||
[value: 74, color: "#44b621"],
|
||||
[value: 84, color: "#f1d801"],
|
||||
[value: 95, color: "#d04e00"],
|
||||
[value: 96, color: "#bc2323"]
|
||||
]
|
||||
state "temperature", label: '${currentValue}°',
|
||||
backgroundColors: [
|
||||
[value: 31, color: "#153591"],
|
||||
[value: 44, color: "#1e9cbb"],
|
||||
[value: 59, color: "#90d2a7"],
|
||||
[value: 74, color: "#44b621"],
|
||||
[value: 84, color: "#f1d801"],
|
||||
[value: 95, color: "#d04e00"],
|
||||
[value: 96, color: "#bc2323"]
|
||||
]
|
||||
}
|
||||
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
state "battery", label: '${currentValue}% battery', unit: ""
|
||||
}
|
||||
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
state "default", action: "refresh.refresh", icon: "st.secondary.refresh"
|
||||
}
|
||||
|
||||
main (["water", "temperature"])
|
||||
main(["water", "temperature"])
|
||||
details(["water", "temperature", "battery", "refresh"])
|
||||
}
|
||||
}
|
||||
@@ -87,202 +89,101 @@ metadata {
|
||||
def parse(String description) {
|
||||
log.debug "description: $description"
|
||||
|
||||
Map map = [:]
|
||||
if (description?.startsWith('catchall:')) {
|
||||
map = parseCatchAllMessage(description)
|
||||
}
|
||||
else if (description?.startsWith('read attr -')) {
|
||||
map = parseReportAttributeMessage(description)
|
||||
}
|
||||
else if (description?.startsWith('temperature: ')) {
|
||||
map = parseCustomMessage(description)
|
||||
}
|
||||
else if (description?.startsWith('zone status')) {
|
||||
map = parseIasMessage(description)
|
||||
// getEvent will handle temperature and humidity
|
||||
Map map = zigbee.getEvent(description)
|
||||
if (!map) {
|
||||
if (description?.startsWith('zone status')) {
|
||||
map = parseIasMessage(description)
|
||||
} else {
|
||||
Map descMap = zigbee.parseDescriptionAsMap(description)
|
||||
if (descMap.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) {
|
||||
map = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||
} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
|
||||
if (descMap.data[0] == "00") {
|
||||
log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap"
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
} else {
|
||||
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"
|
||||
def result = map ? createEvent(map) : null
|
||||
def result = map ? createEvent(map) : [:]
|
||||
|
||||
if (description?.startsWith('enroll request')) {
|
||||
List cmds = enrollResponse()
|
||||
List cmds = zigbee.enrollResponse()
|
||||
log.debug "enroll response: ${cmds}"
|
||||
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private Map parseCatchAllMessage(String description) {
|
||||
Map resultMap = [:]
|
||||
def cluster = zigbee.parse(description)
|
||||
if (shouldProcessMessage(cluster)) {
|
||||
switch(cluster.clusterId) {
|
||||
case 0x0001:
|
||||
// 0x07 - configure reporting
|
||||
if (cluster.command != 0x07) {
|
||||
resultMap = getBatteryResult(cluster.data.last())
|
||||
}
|
||||
break
|
||||
|
||||
case 0x0402:
|
||||
if (cluster.command == 0x07) {
|
||||
if (cluster.data[0] == 0x00){
|
||||
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
else {
|
||||
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
||||
}
|
||||
}
|
||||
else {
|
||||
// temp is last 2 data values. reverse to swap endian
|
||||
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
|
||||
def value = getTemperature(temp)
|
||||
resultMap = getTemperatureResult(value)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return resultMap
|
||||
}
|
||||
|
||||
private boolean shouldProcessMessage(cluster) {
|
||||
// 0x0B is default response indicating message got through
|
||||
boolean ignoredMessage = cluster.profileId != 0x0104 ||
|
||||
cluster.command == 0x0B ||
|
||||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
|
||||
return !ignoredMessage
|
||||
}
|
||||
|
||||
private Map parseReportAttributeMessage(String description) {
|
||||
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
|
||||
def nameAndValue = param.split(":")
|
||||
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
|
||||
}
|
||||
log.debug "Desc Map: $descMap"
|
||||
|
||||
Map resultMap = [:]
|
||||
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
|
||||
def value = getTemperature(descMap.value)
|
||||
resultMap = getTemperatureResult(value)
|
||||
}
|
||||
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
|
||||
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
|
||||
}
|
||||
|
||||
return resultMap
|
||||
}
|
||||
|
||||
private Map parseCustomMessage(String description) {
|
||||
Map resultMap = [:]
|
||||
if (description?.startsWith('temperature: ')) {
|
||||
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
|
||||
resultMap = getTemperatureResult(value)
|
||||
}
|
||||
return resultMap
|
||||
}
|
||||
|
||||
private Map parseIasMessage(String description) {
|
||||
ZoneStatus zs = zigbee.parseZoneStatus(description)
|
||||
|
||||
return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry')
|
||||
}
|
||||
|
||||
def getTemperature(value) {
|
||||
def celsius = Integer.parseInt(value, 16).shortValue() / 100
|
||||
if(getTemperatureScale() == "C"){
|
||||
return Math.round(celsius)
|
||||
} else {
|
||||
return Math.round(celsiusToFahrenheit(celsius))
|
||||
}
|
||||
}
|
||||
|
||||
private Map getBatteryResult(rawValue) {
|
||||
log.debug "Battery rawValue = ${rawValue}"
|
||||
def linkText = getLinkText(device)
|
||||
|
||||
def result = [
|
||||
name: 'battery',
|
||||
value: '--',
|
||||
translatable: true
|
||||
]
|
||||
def result = [:]
|
||||
|
||||
def volts = rawValue / 10
|
||||
|
||||
if (rawValue == 0 || rawValue == 255) {}
|
||||
else {
|
||||
if (volts > 3.5) {
|
||||
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
|
||||
}
|
||||
else {
|
||||
if (device.getDataValue("manufacturer") == "SmartThings") {
|
||||
volts = rawValue // For the batteryMap to work the key needs to be an int
|
||||
def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70,
|
||||
22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0]
|
||||
def minVolts = 15
|
||||
def maxVolts = 28
|
||||
if (!(rawValue == 0 || rawValue == 255)) {
|
||||
result.name = 'battery'
|
||||
result.translatable = true
|
||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||
if (device.getDataValue("manufacturer") == "SmartThings") {
|
||||
volts = rawValue // For the batteryMap to work the key needs to be an int
|
||||
def batteryMap = [28: 100, 27: 100, 26: 100, 25: 90, 24: 90, 23: 70,
|
||||
22: 70, 21: 50, 20: 50, 19: 30, 18: 30, 17: 15, 16: 1, 15: 0]
|
||||
def minVolts = 15
|
||||
def maxVolts = 28
|
||||
|
||||
if (volts < minVolts)
|
||||
volts = minVolts
|
||||
else if (volts > maxVolts)
|
||||
volts = maxVolts
|
||||
def pct = batteryMap[volts]
|
||||
if (pct != null) {
|
||||
result.value = pct
|
||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||
}
|
||||
}
|
||||
else {
|
||||
def minVolts = 2.1
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
|
||||
}
|
||||
if (volts < minVolts)
|
||||
volts = minVolts
|
||||
else if (volts > maxVolts)
|
||||
volts = maxVolts
|
||||
def pct = batteryMap[volts]
|
||||
result.value = pct
|
||||
} else {
|
||||
def minVolts = 2.1
|
||||
def maxVolts = 3.0
|
||||
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||
def roundedPct = Math.round(pct * 100)
|
||||
if (roundedPct <= 0)
|
||||
roundedPct = 1
|
||||
result.value = Math.min(100, roundedPct)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private Map getTemperatureResult(value) {
|
||||
log.debug 'TEMP'
|
||||
if (tempOffset) {
|
||||
def offset = tempOffset as int
|
||||
def v = value as int
|
||||
value = v + offset
|
||||
}
|
||||
def descriptionText
|
||||
if ( temperatureScale == 'C' )
|
||||
descriptionText = '{{ device.displayName }} was {{ value }}°C'
|
||||
else
|
||||
descriptionText = '{{ device.displayName }} was {{ value }}°F'
|
||||
|
||||
return [
|
||||
name: 'temperature',
|
||||
value: value,
|
||||
descriptionText: descriptionText,
|
||||
translatable: true,
|
||||
unit: temperatureScale
|
||||
]
|
||||
}
|
||||
|
||||
private Map getMoistureResult(value) {
|
||||
log.debug "water"
|
||||
def descriptionText
|
||||
if ( value == "wet" )
|
||||
descriptionText = '{{ device.displayName }} is wet'
|
||||
else
|
||||
descriptionText = '{{ device.displayName }} is dry'
|
||||
def descriptionText
|
||||
if (value == "wet")
|
||||
descriptionText = '{{ device.displayName }} is wet'
|
||||
else
|
||||
descriptionText = '{{ device.displayName }} is dry'
|
||||
return [
|
||||
name: 'water',
|
||||
value: value,
|
||||
descriptionText: descriptionText,
|
||||
translatable: true
|
||||
name : 'water',
|
||||
value : value,
|
||||
descriptionText: descriptionText,
|
||||
translatable : true
|
||||
]
|
||||
}
|
||||
|
||||
@@ -295,59 +196,18 @@ def ping() {
|
||||
|
||||
def refresh() {
|
||||
log.debug "Refreshing Temperature and Battery"
|
||||
def refreshCmds = [
|
||||
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
|
||||
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
|
||||
]
|
||||
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) +
|
||||
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)
|
||||
|
||||
return refreshCmds + enrollResponse()
|
||||
return refreshCmds + zigbee.enrollResponse()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
|
||||
// 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: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
|
||||
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
|
||||
// battery minReport 30 seconds, maxReportTime 6 hrs by default
|
||||
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config
|
||||
}
|
||||
|
||||
def enrollResponse() {
|
||||
log.debug "Sending enroll response"
|
||||
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
|
||||
[
|
||||
//Resending the CIE in case the enroll request is sent before CIE is written
|
||||
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
|
||||
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
|
||||
//Enroll Response
|
||||
"raw 0x500 {01 23 00 00 00}",
|
||||
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
|
||||
]
|
||||
}
|
||||
|
||||
private getEndpointId() {
|
||||
new BigInteger(device.endpointId, 16).toString()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user