├── DeviceType_ms_w_vts.groovy
├── DigitalLoggersWebPowerSwitch7
├── LICENSE
├── README.md
├── RaspberryPi2
├── SmartApp_DLI_Web_Power_Switch_with_Virtual_Tiles
├── SmartApp_Powerstrip.groovy
├── SmartApp_SuperState.groovy
├── devicetypes
├── ashishagrawal
│ └── foscam-universal-device-v3.src
│ │ └── foscam-universal-device-v3.groovy
├── cmonroe
│ └── everspring-st814.src
│ │ └── everspring-st814.groovy
├── ledridge
│ ├── foscam-hd.src
│ │ └── foscam-hd.groovy
│ ├── my-garage-door-opener.src
│ │ └── my-garage-door-opener.groovy
│ ├── raspberry-pi.src
│ │ └── raspberry-pi.groovy
│ ├── virtual-motion-detector.src
│ │ └── virtual-motion-detector.groovy
│ ├── vtile-dli.src
│ │ └── vtile-dli.groovy
│ ├── web-power-switch.src
│ │ └── web-power-switch.groovy
│ └── wifi-370-led-strip-controller.src
│ │ └── wifi-370-led-strip-controller.groovy
├── mmaxwell
│ └── remoteczfm80.src
│ │ └── remoteczfm80.groovy
├── ms-w-vts
│ └── vtile-ms.src
│ │ └── vtile-ms.groovy
├── mujica
│ └── dlna-player.src
│ │ └── dlna-player.groovy
├── skp19
│ └── foscam-universal-device.src
│ │ └── foscam-universal-device.groovy
└── superuser
│ ├── aeon-smart-strip-1.src
│ └── aeon-smart-strip-1.groovy
│ ├── aeon-smart-strip-2.src
│ └── aeon-smart-strip-2.groovy
│ └── poolswitch.src
│ └── poolswitch.groovy
└── vTile_DLI
/DeviceType_ms_w_vts.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * V Tile device-type for ms_w_vts
3 | *
4 | * Needed for Multi Switch with Virtual Tiles to create virtual switch tiles in ST for devices that have multiple "switch[x]"
5 | * attributes within them and have on[x] and off[x] commands for each (fairly common device-types)
6 | * Also has support for device-label inside the name when on or off and polling occurs
7 | * Copyright 2014 Cooper Lee
8 | *
9 | */
10 | metadata {
11 | definition (name: "vTile_ms", namespace: "ms_w_vts", author: "Cooper Lee") {
12 | capability "Switch"
13 | capability "relaySwitch"
14 | capability "Polling"
15 | capability "Refresh"
16 |
17 | attribute "lastOn", "string"
18 | attribute "lastOff", "string"
19 | }
20 | }
21 |
22 | preferences {
23 | tiles {
24 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
25 | state "name", label: '${currentValue}', action: "switch.on", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#DDDDff", nextState: "turningOn"
26 | state "off", label: 'off', action: "switch.on", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#DDDDff", nextState: "turningOn"
27 | state "on", label: 'on', action: "switch.off", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff", nextState: "turningOff"
28 | state "turningOff", iconLabel:"http://cdn.flaticon.com/png/256/56413.png" , icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#FA5882", nextState: "on"
29 | state "turningOn", iconLabel:"http://cdn.flaticon.com/png/256/56498.png" , icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "off"
30 | }
31 |
32 | valueTile("lastOn", "device.lastOn", inactiveLabel: false, width: 3, height: 1, canChangeIcon: false, decoration:"flat") {
33 | state "default", label: 'Last On: ${currentValue}'}
34 |
35 | valueTile("lastOff", "device.lastOff", inactiveLabel: false, width: 3, height: 1, canChangeIcon: false, decoration:"flat") {
36 | state "default", label: 'Last Off: ${currentValue}'}
37 |
38 |
39 | main "switch"
40 | details(["switch", "lastOn", "lastOff"])
41 | }
42 | }
43 |
44 | def parse(desc) {
45 | def results = []
46 | if(desc=="updated") { log.debug "Device $device.label has been UPDATED"; poll() }
47 | }
48 |
49 | def on() {
50 | sendEvent([name: "switch", value: "on"])
51 | parent.on(this)
52 | sendEvent([name: "lastOn", value: "${df(now())}"])
53 | log.debug "$device.label is On"
54 | }
55 |
56 | def off() {
57 | sendEvent([name: "switch", value: "off"])
58 | parent.off(this)
59 | sendEvent([name: "switch", value: "$device.label"])
60 | sendEvent([name: "lastOff", value: "${df(now())}"])
61 | log.debug "$device.label is Off"
62 | }
63 |
64 | def poll() {
65 | def current = device.currentValue("switch")
66 | log.debug "Polling - $device.label is $current"
67 | if(!current || current=="off") { sendEvent(name:"switch", value:"$device.label", isStateChange:true, displayed:false) }
68 | }
69 |
70 | def df(e) {
71 | // * df(e) - Date Format "E"
72 | // * Takes epoch time format and returns Date formatted in current timezone
73 | // * Copyright 2014 Cooper Lee
74 | def locale = getWeatherFeature("geolookup", zip); def tz = TimeZone.getTimeZone(locale.location.tz_long); def formatted
75 | if(e) { formatted = new Date(e).format("EEE, MMM d, 'at' hh:mm aaa", tz); return formatted }
76 | }
77 |
--------------------------------------------------------------------------------
/DigitalLoggersWebPowerSwitch7:
--------------------------------------------------------------------------------
1 | /**
2 | * Web Power Switch 7
3 | */
4 |
5 | import com.google.common.base.Splitter;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.Map.Entry;
9 |
10 |
11 | preferences {
12 | input("ip", "string", title:"IP Address", description: "192.168.1.3", defaultValue: "192.168.1.3" ,required: true, displayDuringSetup: true)
13 | input("port", "string", title:"Port", description: "80", defaultValue: "80" , required: true, displayDuringSetup: true)
14 | input("username", "string", title:"Username", description: "admin", defaultValue: "admin" , required: true, displayDuringSetup: true)
15 | input("password", "password", title:"Password", description: "password", defaultValue: "password" , required: true, displayDuringSetup: true)
16 |
17 | input("port1name", "string", title:"Port1 Name", description: "Name for port", defaultValue: "Port 1" , required: true, displayDuringSetup: true)
18 | input("port2name", "string", title:"Port2 Name", description: "Name for port", defaultValue: "Port 2" , required: true, displayDuringSetup: true)
19 | input("port3name", "string", title:"Port3 Name", description: "Name for port", defaultValue: "Port 3" , required: true, displayDuringSetup: true)
20 | input("port4name", "string", title:"Port4 Name", description: "Name for port", defaultValue: "Port 4" , required: true, displayDuringSetup: true)
21 | input("port5name", "string", title:"Port5 Name", description: "Name for port", defaultValue: "Port 5" , required: true, displayDuringSetup: true)
22 | input("port6name", "string", title:"Port6 Name", description: "Name for port", defaultValue: "Port 6" , required: true, displayDuringSetup: true)
23 | input("port7name", "string", title:"Port7 Name", description: "Name for port", defaultValue: "Port 7" , required: true, displayDuringSetup: true)
24 | input("port8name", "string", title:"Port8 Name", description: "Name for port", defaultValue: "Port 8" , required: true, displayDuringSetup: true)
25 | }
26 |
27 | metadata {
28 | definition (name: "Web Power Switch", namespace: "Ledridge", author: "Ledridge") {
29 |
30 | capability "Polling"
31 | capability "Refresh"
32 | capability "Switch"
33 |
34 | command "OA1ON"
35 | command "OA1OFF"
36 | command "OA1CCL"
37 |
38 | command "OA2ON"
39 | command "OA2OFF"
40 | command "OA2CCL"
41 |
42 | command "OA3ON"
43 | command "OA3OFF"
44 | command "OA3CCL"
45 |
46 | command "OA4ON"
47 | command "OA4OFF"
48 | command "OA4CCL"
49 |
50 | command "OA5ON"
51 | command "OA5OFF"
52 | command "OA5CCL"
53 |
54 | command "OA6ON"
55 | command "OA6OFF"
56 | command "OA6CCL"
57 |
58 | command "OA7ON"
59 | command "OA7OFF"
60 | command "OA7CCL"
61 |
62 | command "OA8ON"
63 | command "OA8OFF"
64 | command "OA8CCL"
65 |
66 | command "OutletAction", ["number", "string"]
67 | command "OutletStatus", ["number"]
68 | command "OutletName", ["number"]
69 | }
70 |
71 | simulator {
72 | // TODO: define status and reply messages here
73 | }
74 |
75 | tiles {
76 |
77 | valueTile("Label1", "device.Label1", decoration: "flat") {state "default", label:'${currentValue}'}
78 | standardTile("Outlet1", "device.Outlet1", width: 1, height: 1) {
79 | state "off", action: "OA1ON", label: 'Off', backgroundColor: "#ffffff", nextState: "on"
80 | state "on" , action: "OA1OFF", label: 'On', backgroundColor: "#79b821", nextState: "off"
81 | }
82 | standardTile("Cycle1", "device.Cycle1", width: 1, height: 1) {
83 | state "off", action: "OA1CCL", label: 'Off', backgroundColor: "#ffffff", nextState: "on", icon: "st.secondary.refresh"
84 | state "on" , label: 'On', backgroundColor: "#79b821", nextState: "off", icon: "st.secondary.refresh"
85 | }
86 |
87 | valueTile("Label2", "device.Label2", decoration: "flat") {state "default", label:'${currentValue}'}
88 | standardTile("Outlet2", "device.Outlet2", width: 1, height: 1) {
89 | state "off", action: "OA2ON", label: 'Off', backgroundColor: "#ffffff", ne2tState: "on"
90 | state "on" , action: "OA2OFF", label: 'On', backgroundColor: "#79b821", ne2tState: "off"
91 | }
92 | standardTile("Cycle2", "device.Cycle2", width: 1, height: 1) {
93 | state "off", action: "OA2CCL", label: 'Off', backgroundColor: "#ffffff", ne2tState: "on", icon: "st.secondary.refresh"
94 | state "on" , label: 'On', backgroundColor: "#79b821", ne2tState: "off", icon: "st.secondary.refresh"
95 | }
96 |
97 | valueTile("Label3", "device.Label3", decoration: "flat") {state "default", label:'${currentValue}'}
98 | standardTile("Outlet3", "device.Outlet3", width: 1, height: 1) {
99 | state "off", action: "OA3ON", label: 'Off', backgroundColor: "#ffffff", ne3tState: "on"
100 | state "on" , action: "OA3OFF", label: 'On', backgroundColor: "#79b821", ne3tState: "off"
101 | }
102 | standardTile("Cycle3", "device.Cycle3", width: 1, height: 1) {
103 | state "off", action: "OA3CCL", label: 'Off', backgroundColor: "#ffffff", ne3tState: "on", icon: "st.secondary.refresh"
104 | state "on" , label: 'On', backgroundColor: "#79b821", ne3tState: "off", icon: "st.secondary.refresh"
105 | }
106 |
107 | valueTile("Label4", "device.Label4", decoration: "flat") {state "default", label:'${currentValue}'}
108 | standardTile("Outlet4", "device.Outlet4", width: 1, height: 1) {
109 | state "off", action: "OA4ON", label: 'Off', backgroundColor: "#ffffff", ne4tState: "on"
110 | state "on" , action: "OA4OFF", label: 'On', backgroundColor: "#79b821", ne4tState: "off"
111 | }
112 | standardTile("Cycle4", "device.Cycle4", width: 1, height: 1) {
113 | state "off", action: "OA4CCL", label: 'Off', backgroundColor: "#ffffff", ne4tState: "on", icon: "st.secondary.refresh"
114 | state "on" , label: 'On', backgroundColor: "#79b821", ne4tState: "off", icon: "st.secondary.refresh"
115 | }
116 |
117 | valueTile("Label5", "device.Label5", decoration: "flat") {state "default", label:'${currentValue}'}
118 | standardTile("Outlet5", "device.Outlet5", width: 1, height: 1) {
119 | state "off", action: "OA5ON", label: 'Off', backgroundColor: "#ffffff", ne5tState: "on"
120 | state "on" , action: "OA5OFF", label: 'On', backgroundColor: "#79b821", ne5tState: "off"
121 | }
122 | standardTile("Cycle5", "device.Cycle5", width: 1, height: 1) {
123 | state "off", action: "OA5CCL", label: 'Off', backgroundColor: "#ffffff", ne5tState: "on", icon: "st.secondary.refresh"
124 | state "on" , label: 'On', backgroundColor: "#79b821", ne5tState: "off", icon: "st.secondary.refresh"
125 | }
126 |
127 | valueTile("Label6", "device.Label6", decoration: "flat") {state "default", label:'${currentValue}'}
128 | standardTile("Outlet6", "device.Outlet6", width: 1, height: 1) {
129 | state "off", action: "OA6ON", label: 'Off', backgroundColor: "#ffffff", ne6tState: "on"
130 | state "on" , action: "OA6OFF", label: 'On', backgroundColor: "#79b821", ne6tState: "off"
131 | }
132 | standardTile("Cycle6", "device.Cycle6", width: 1, height: 1) {
133 | state "off", action: "OA6CCL", label: 'Off', backgroundColor: "#ffffff", ne6tState: "on", icon: "st.secondary.refresh"
134 | state "on" , label: 'On', backgroundColor: "#79b821", ne6tState: "off", icon: "st.secondary.refresh"
135 | }
136 |
137 | valueTile("Label7", "device.Label7", decoration: "flat") {state "default", label:'${currentValue}'}
138 | standardTile("Outlet7", "device.Outlet7", width: 1, height: 1) {
139 | state "off", action: "OA7ON", label: 'Off', backgroundColor: "#ffffff", ne7tState: "on"
140 | state "on" , action: "OA7OFF", label: 'On', backgroundColor: "#79b821", ne7tState: "off"
141 | }
142 | standardTile("Cycle7", "device.Cycle7", width: 1, height: 1) {
143 | state "off", action: "OA7CCL", label: 'Off', backgroundColor: "#ffffff", ne7tState: "on", icon: "st.secondary.refresh"
144 | state "on" , label: 'On', backgroundColor: "#79b821", ne7tState: "off", icon: "st.secondary.refresh"
145 | }
146 |
147 | valueTile("Label8", "device.Label8", decoration: "flat") {state "default", label:'${currentValue}'}
148 | standardTile("Outlet8", "device.Outlet8", width: 1, height: 1) {
149 | state "off", action: "OA8ON", label: 'Off', backgroundColor: "#ffffff", ne8tState: "on"
150 | state "on" , action: "OA8OFF", label: 'On', backgroundColor: "#79b821", ne8tState: "off"
151 | }
152 | standardTile("Cycle8", "device.Cycle8", width: 1, height: 1) {
153 | state "off", action: "OA8CCL", label: 'Off', backgroundColor: "#ffffff", ne8tState: "on", icon: "st.secondary.refresh"
154 | state "on" , label: 'On', backgroundColor: "#79b821", ne8tState: "off", icon: "st.secondary.refresh"
155 | }
156 |
157 | standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
158 | state "default", action:"refresh.refresh", icon: "st.secondary.refresh"
159 | }
160 |
161 | main "Outlet1"
162 | details([ "Label1", "Outlet1", "Cycle1"
163 | ,"Label2", "Outlet2", "Cycle2"
164 | ,"Label3", "Outlet3", "Cycle3"
165 | ,"Label4", "Outlet4", "Cycle4"
166 | ,"Label5", "Outlet5", "Cycle5"
167 | ,"Label6", "Outlet6", "Cycle6"
168 | ,"Label7", "Outlet7", "Cycle7"
169 | ,"Label8", "Outlet8", "Cycle8", "refresh"])
170 | }
171 | }
172 |
173 | // ------------------------------------------------------------------
174 |
175 | // parse events into attributes
176 | def parse(String description) {
177 | def map = [:]
178 | def descMap = parseDescriptionAsMap(description)
179 | //log.debug "descMap: ${descMap}"
180 |
181 | //log.debug description.decodeBase64().substring(50,10)
182 |
183 | def body = new String(descMap["body"].decodeBase64())
184 | //log.debug "body: ${body}"
185 |
186 | def hexString = body.tokenize()
187 | //log.debug "0: ${hexString[0]}"
188 | //log.debug "1: ${hexString[1]}"
189 | //log.debug "2: ${hexString[2]}"
190 | //log.debug "3: ${hexString[3]}"
191 | //log.debug "4: ${hexString[4]}"
192 | //log.debug "5: ${hexString[5]}"
193 | //log.debug "6: ${hexString[6]}"
194 | //log.debug "7: ${hexString[7]}"
195 | //log.debug "8: ${hexString[8]}"
196 |
197 | if (hexString[1].contains("status"))
198 | {
199 |
200 | def a = hexString[3].toString().replace('id="state">','');
201 | //log.debug "a: ${a}"
202 |
203 | char[] charArray = a.toCharArray();
204 |
205 | //log.debug charArray[0];
206 | //log.debug charArray[1];
207 |
208 |
209 | def b = "${charArray[0]}${charArray[1]}"
210 | //log.debug "b: ${b}"
211 |
212 | def binaryString = hexToBin(b)
213 | //log.debug "Binary String: ${binaryString}"
214 |
215 | char[] StatusArray = binaryString.toString().toCharArray();
216 |
217 | //log.debug StatusArray[0];
218 | //log.debug StatusArray[1];
219 | //log.debug StatusArray[2];
220 | //log.debug StatusArray[3];
221 | //log.debug StatusArray[4];
222 | //log.debug StatusArray[5];
223 | //log.debug StatusArray[6];
224 | //log.debug StatusArray[7];
225 |
226 | def o5 = device.latestValue('o5')
227 |
228 | if (StatusArray[7] == "0")
229 | sendEvent(name: "Outlet1", value: "off")
230 | else
231 | sendEvent(name: "Outlet1", value: "on")
232 |
233 | if (StatusArray[6] == "0")
234 | sendEvent(name: "Outlet2", value: "off")
235 | else
236 | sendEvent(name: "Outlet2", value: "on")
237 |
238 | if (StatusArray[5] == "0")
239 | sendEvent(name: "Outlet3", value: "off")
240 | else
241 | sendEvent(name: "Outlet3", value: "on")
242 |
243 | if (StatusArray[4] == "0")
244 | sendEvent(name: "Outlet4", value: "off")
245 | else
246 | sendEvent(name: "Outlet4", value: "on")
247 |
248 | if (StatusArray[3] == "0")
249 | sendEvent(name: "Outlet5", value: "off")
250 | else
251 | sendEvent(name: "Outlet5", value: "on")
252 |
253 | if (StatusArray[2] == "0")
254 | sendEvent(name: "Outlet6", value: "off")
255 | else
256 | sendEvent(name: "Outlet6", value: "on")
257 |
258 | if (StatusArray[1] == "0")
259 | sendEvent(name: "Outlet7", value: "off")
260 | else
261 | sendEvent(name: "Outlet7", value: "on")
262 |
263 | if (StatusArray[0] == "0")
264 | sendEvent(name: "Outlet8", value: "off")
265 | else
266 | sendEvent(name: "Outlet8", value: "on")
267 |
268 | }
269 | else
270 | log.debug "not status"
271 |
272 |
273 | //log.debug "Label1: ${port1name}"
274 | sendEvent(name: "Label1", value: port1name)
275 |
276 | //log.debug "Label2: ${port2name}"
277 | sendEvent(name: "Label2", value: port2name)
278 |
279 | //log.debug "Label3: ${port3name}"
280 | sendEvent(name: "Label3", value: port3name)
281 |
282 | //log.debug "Label4: ${port4name}"
283 | sendEvent(name: "Label4", value: port4name)
284 |
285 | //log.debug "Label5: ${port5name}"
286 | sendEvent(name: "Label5", value: port5name)
287 |
288 | //log.debug "Label6: ${port6name}"
289 | sendEvent(name: "Label6", value: port6name)
290 |
291 | //log.debug "Label7: ${port7name}"
292 | sendEvent(name: "Label7", value: port7name)
293 |
294 | //log.debug "Label8: ${port8name}"
295 | sendEvent(name: "Label8", value: port8name)
296 |
297 | }
298 |
299 | // handle commands
300 | def poll() {
301 | log.debug "Executing 'poll'"
302 | getRemoteData()
303 | }
304 |
305 | def refresh() {
306 | sendEvent(name: "switch", value: "off")
307 | log.debug "Executing 'refresh'"
308 | getRemoteData()
309 | }
310 |
311 |
312 | def OA1ON() {OutletAction(1,"ON")}
313 | def OA1OFF(){OutletAction(1,"OFF")}
314 | def OA1CCL(){OutletAction(1,"CCL")}
315 |
316 | def OA2ON() {OutletAction(2,"ON")}
317 | def OA2OFF(){OutletAction(2,"OFF")}
318 | def OA2CCL(){OutletAction(2,"CCL")}
319 |
320 | def OA3ON() {OutletAction(3,"ON")}
321 | def OA3OFF(){OutletAction(3,"OFF")}
322 | def OA3CCL(){OutletAction(3,"CCL")}
323 |
324 | def OA4ON() {OutletAction(4,"ON")}
325 | def OA4OFF(){OutletAction(4,"OFF")}
326 | def OA4CCL(){OutletAction(4,"CCL")}
327 |
328 | def OA5ON() {OutletAction(5,"ON")}
329 | def OA5OFF(){OutletAction(5,"OFF")}
330 | def OA5CCL(){OutletAction(5,"CCL")}
331 |
332 | def OA6ON() {OutletAction(6,"ON")}
333 | def OA6OFF(){OutletAction(6,"OFF")}
334 | def OA6CCL(){OutletAction(6,"CCL")}
335 |
336 | def OA7ON() {OutletAction(7,"ON")}
337 | def OA7OFF(){OutletAction(7,"OFF")}
338 | def OA7CCL(){OutletAction(7,"CCL")}
339 |
340 | def OA8ON() {OutletAction(8,"ON")}
341 | def OA8OFF(){OutletAction(8,"OFF")}
342 | def OA8CCL(){OutletAction(8,"CCL")}
343 |
344 |
345 | def OutletStatus(outlet){
346 | //getRemoteData()
347 |
348 | def int o = outlet.toInteger()
349 |
350 | log.debug "Get Outlet ${o} Status"
351 | def outletStatus
352 | switch(o) {
353 | case 1:
354 | outletStatus = device.latestValue('Outlet1');
355 | break;
356 | case 2:
357 | outletStatus = device.latestValue('Outlet2');
358 | break;
359 | case 3:
360 | outletStatus = device.latestValue('Outlet3');
361 | break;
362 | case 4:
363 | outletStatus = device.latestValue('Outlet4');
364 | break;
365 | case 5:
366 | outletStatus = device.latestValue('Outlet5');
367 | break;
368 | case 6:
369 | outletStatus = device.latestValue('Outlet6');
370 | break;
371 | case 7:
372 | outletStatus = device.latestValue('Outlet7');
373 | break;
374 | case 8:
375 | outletStatus = device.latestValue('Outlet8');
376 | break;
377 | default:
378 | outletStatus = "Invalid Outlet Number: ${o}"
379 | }
380 |
381 | log.debug "Outlet Status: ${outletStatus}"
382 | return outletStatus;
383 | }
384 |
385 | def OutletName(outlet){
386 | def int o = outlet.toInteger()
387 |
388 | log.debug "Get Outlet ${o} Name"
389 | def outletName
390 | switch(o) {
391 | case 1:
392 | outletName = port1name
393 | break;
394 | case 2:
395 | outletName = port2name
396 | break;
397 | case 3:
398 | outletName = port3name
399 | break;
400 | case 4:
401 | outletName = port4name
402 | break;
403 | case 5:
404 | outletName = port5name
405 | break;
406 | case 6:
407 | outletName = port6name
408 | break;
409 | case 7:
410 | outletName = port7name
411 | break;
412 | case 8:
413 | outletName = port8name
414 | break;
415 | default:
416 | outletName = "Invalid Outlet Number: ${o}"
417 | }
418 |
419 | log.debug "Outlet Name: ${outletName}"
420 | return outletName;
421 | }
422 |
423 | def OutletAction(outlet,action){
424 | log.debug "Send Outlet ${outlet.toInteger().toString()} ${action}"
425 | def uri = "/outlet?${outlet.toInteger().toString()}=${action}"
426 | log.debug "URI: ${uri}"
427 | delayBetween([
428 | postAction(uri),
429 | getRemoteData()
430 | ], 5000);
431 | }
432 |
433 | private getRemoteData() {
434 | def uri = "/status"
435 | postAction(uri)
436 | }
437 |
438 | // ------------------------------------------------------------------
439 |
440 | private postAction(uri){
441 | setDeviceNetworkId(ip,port)
442 |
443 | def userpass = encodeCredentials(username, password)
444 | //log.debug("userpass: " + userpass)
445 |
446 | def headers = getHeader(userpass)
447 | //log.debug("headders: " + headers)
448 |
449 | def hubAction = new physicalgraph.device.HubAction(
450 | method: "GET",
451 | path: uri,
452 | headers: headers
453 | )
454 | log.debug("Executing hubAction on " + getHostAddress())
455 | //log.debug hubAction
456 | hubAction
457 | }
458 |
459 | // ------------------------------------------------------------------
460 | // Helper methods
461 | // ------------------------------------------------------------------
462 |
463 | def parseDescriptionAsMap(description) {
464 | description.split(",").inject([:]) { map, param ->
465 | def nameAndValue = param.split(":")
466 | map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
467 | }
468 | }
469 |
470 |
471 | def toAscii(s){
472 | StringBuilder sb = new StringBuilder();
473 | String ascString = null;
474 | long asciiInt;
475 | for (int i = 0; i < s.length(); i++){
476 | sb.append((int)s.charAt(i));
477 | sb.append("|");
478 | char c = s.charAt(i);
479 | }
480 | ascString = sb.toString();
481 | asciiInt = Long.parseLong(ascString);
482 | return asciiInt;
483 | }
484 |
485 | private encodeCredentials(username, password){
486 | log.debug "Encoding credentials"
487 | def userpassascii = "${username}:${password}"
488 | def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
489 | //log.debug "ASCII credentials are ${userpassascii}"
490 | //log.debug "Credentials are ${userpass}"
491 | return userpass
492 | }
493 |
494 | private getHeader(userpass){
495 | log.debug "Getting headers"
496 | def headers = [:]
497 | headers.put("HOST", getHostAddress())
498 | headers.put("Authorization", userpass)
499 | //log.debug "Headers are ${headers}"
500 | return headers
501 | }
502 |
503 | private delayAction(long time) {
504 | new physicalgraph.device.HubAction("delay $time")
505 | }
506 |
507 | private setDeviceNetworkId(ip,port){
508 | def iphex = convertIPtoHex(ip)
509 | def porthex = convertPortToHex(port)
510 | device.deviceNetworkId = "$iphex:$porthex"
511 | log.debug "Device Network Id set to ${iphex}:${porthex}"
512 | }
513 |
514 | private getHostAddress() {
515 | return "${ip}:${port}"
516 | }
517 |
518 | private String convertIPtoHex(ipAddress) {
519 | String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
520 | return hex
521 |
522 | }
523 |
524 | private String convertPortToHex(port) {
525 | String hexport = port.toString().format( '%04x', port.toInteger() )
526 | return hexport
527 | }
528 |
529 | private String hexToBin(String hex){
530 | String bin = "";
531 | String binFragment = "";
532 | int iHex;
533 | hex = hex.trim();
534 | hex = hex.replaceFirst("0x", "");
535 |
536 | for(int i = 0; i < hex.length(); i++){
537 | iHex = Integer.parseInt(""+hex.charAt(i),16);
538 | binFragment = Integer.toBinaryString(iHex);
539 |
540 | while(binFragment.length() < 4){
541 | binFragment = "0" + binFragment;
542 | }
543 | bin += binFragment;
544 | }
545 | return bin;
546 | }
547 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SmartThings
2 | SmartThings Code
3 |
--------------------------------------------------------------------------------
/RaspberryPi2:
--------------------------------------------------------------------------------
1 | /**
2 | * Raspberry Pi
3 | *
4 | * Copyright 2014 Nicholas Wilde
5 | *
6 | * Monitor your Raspberry Pi using SmartThings and WebIOPi
7 | *
8 | * Companion WebIOPi python script can be found here:
9 | *
10 | *
11 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
12 | * in compliance with the License. You may obtain a copy of the License at:
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
17 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
18 | * for the specific language governing permissions and limitations under the License.
19 | *
20 | */
21 |
22 | import groovy.json.JsonSlurper
23 | import com.google.common.base.Splitter;
24 | import java.util.List;
25 | import java.util.Map;
26 | import java.util.Map.Entry;
27 |
28 |
29 | preferences {
30 | input("ip", "string", title:"IP Address", description: "192.168.1.50", defaultValue: "192.168.1.50" ,required: true, displayDuringSetup: true)
31 | input("port", "string", title:"Port", description: "80", defaultValue: "80" , required: true, displayDuringSetup: true)
32 | input("username", "string", title:"Username", description: "pi", defaultValue: "pi" , required: true, displayDuringSetup: true)
33 | input("password", "password", title:"Password", description: "raspberry", defaultValue: "raspberry" , required: true, displayDuringSetup: true)
34 | }
35 |
36 | metadata {
37 | definition (name: "Raspberry Pi", namespace: "Ledridge", author: "Ledridge") {
38 | capability "Polling"
39 | capability "Refresh"
40 | capability "Temperature Measurement"
41 | capability "Switch"
42 | capability "Sensor"
43 | capability "Actuator"
44 |
45 | attribute "cpuPercentage", "string"
46 | attribute "memory", "string"
47 | attribute "diskUsage", "string"
48 |
49 | command "restart"
50 | }
51 |
52 | simulator {
53 | // TODO: define status and reply messages here
54 | }
55 |
56 | tiles {
57 | valueTile("temperature", "device.temperature", width: 1, height: 1) {
58 | state "temperature", label:'${currentValue}° CPU', unit: "F",
59 | backgroundColors:[
60 | [value: 25, color: "#153591"],
61 | [value: 35, color: "#1e9cbb"],
62 | [value: 47, color: "#90d2a7"],
63 | [value: 59, color: "#44b621"],
64 | [value: 67, color: "#f1d801"],
65 | [value: 76, color: "#d04e00"],
66 | [value: 77, color: "#bc2323"]
67 | ]
68 | }
69 | standardTile("button", "device.switch", width: 1, height: 1, canChangeIcon: true) {
70 | state "off", label: 'Off', icon: "st.Electronics.electronics18", backgroundColor: "#ffffff", nextState: "on"
71 | state "on", label: 'On', icon: "st.Electronics.electronics18", backgroundColor: "#79b821", nextState: "off"
72 | }
73 | valueTile("cpuPercentage", "device.cpuPercentage", inactiveLabel: false) {
74 | state "default", label:'${currentValue}% CPU', unit:"Percentage",
75 | backgroundColors:[
76 | [value: 31, color: "#153591"],
77 | [value: 44, color: "#1e9cbb"],
78 | [value: 59, color: "#90d2a7"],
79 | [value: 74, color: "#44b621"],
80 | [value: 84, color: "#f1d801"],
81 | [value: 95, color: "#d04e00"],
82 | [value: 96, color: "#bc2323"]
83 | ]
84 | }
85 | valueTile("memory", "device.memory", width: 1, height: 1) {
86 | state "default", label:'${currentValue} MB', unit:"MB",
87 | backgroundColors:[
88 | [value: 353, color: "#153591"],
89 | [value: 287, color: "#1e9cbb"],
90 | [value: 210, color: "#90d2a7"],
91 | [value: 133, color: "#44b621"],
92 | [value: 82, color: "#f1d801"],
93 | [value: 26, color: "#d04e00"],
94 | [value: 20, color: "#bc2323"]
95 | ]
96 | }
97 | valueTile("diskUsage", "device.diskUsage", width: 1, height: 1) {
98 | state "default", label:'${currentValue}% Disk', unit:"Percent",
99 | backgroundColors:[
100 | [value: 31, color: "#153591"],
101 | [value: 44, color: "#1e9cbb"],
102 | [value: 59, color: "#90d2a7"],
103 | [value: 74, color: "#44b621"],
104 | [value: 84, color: "#f1d801"],
105 | [value: 95, color: "#d04e00"],
106 | [value: 96, color: "#bc2323"]
107 | ]
108 | }
109 | standardTile("restart", "device.restart", inactiveLabel: false, decoration: "flat") {
110 | state "default", action:"restart", label: "Restart", displayName: "Restart"
111 | }
112 | standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
113 | state "default", action:"refresh.refresh", icon: "st.secondary.refresh"
114 | }
115 | main "button"
116 | details(["button", "temperature", "cpuPercentage", "memory" , "diskUsage", "restart", "refresh"])
117 | }
118 | }
119 |
120 | // ------------------------------------------------------------------
121 |
122 | // parse events into attributes
123 | def parse(String description) {
124 | def map = [:]
125 | def descMap = parseDescriptionAsMap(description)
126 | log.debug "descMap: ${descMap}"
127 |
128 | def body = new String(descMap["body"].decodeBase64())
129 | log.debug "body: ${body}"
130 |
131 | def slurper = new JsonSlurper()
132 | def result = slurper.parseText(body)
133 |
134 | log.debug "result: ${result}"
135 |
136 | if (result){
137 | log.debug "Computer is up"
138 | sendEvent(name: "switch", value: "on")
139 | }
140 |
141 | log.debug "check temp..."
142 | if (result.containsKey("cpu_temp")) {
143 | log.debug "temp: ${result.cpu_temp}"
144 | log.debug "temp: ${celsiusToFahrenheit(result.cpu_temp.toDouble())} F"
145 | sendEvent(name: "temperature", value: celsiusToFahrenheit(result.cpu_temp.toDouble()))
146 | }
147 |
148 | if (result.containsKey("cpu_perc")) {
149 | sendEvent(name: "cpuPercentage", value: result.cpu_perc)
150 | }
151 |
152 | if (result.containsKey("mem_avail")) {
153 | log.debug "mem_avail: ${result.mem_avail}"
154 | sendEvent(name: "memory", value: result.mem_avail)
155 | }
156 | if (result.containsKey("disk_usage")) {
157 | log.debug "disk_usage: ${result.disk_usage}"
158 | sendEvent(name: "diskUsage", value: result.disk_usage)
159 | }
160 |
161 | }
162 |
163 | // handle commands
164 | def poll() {
165 | log.debug "Executing 'poll'"
166 | sendEvent(name: "switch", value: "off")
167 | getRPiData()
168 | }
169 |
170 | def refresh() {
171 | sendEvent(name: "switch", value: "off")
172 | log.debug "Executing 'refresh'"
173 | getRPiData()
174 | }
175 |
176 | def restart(){
177 | log.debug "Restart was pressed"
178 | sendEvent(name: "switch", value: "off")
179 | def uri = "/api_command/reboot"
180 | postAction(uri)
181 | }
182 |
183 | // Get CPU percentage reading
184 | private getRPiData() {
185 | def uri = "/api_command/smartthings"
186 | postAction(uri)
187 | }
188 |
189 | // ------------------------------------------------------------------
190 |
191 | private postAction(uri){
192 | setDeviceNetworkId(ip,port)
193 |
194 | def userpass = encodeCredentials(username, password)
195 | //log.debug("userpass: " + userpass)
196 |
197 | def headers = getHeader(userpass)
198 | //log.debug("headders: " + headers)
199 |
200 | def hubAction = new physicalgraph.device.HubAction(
201 | method: "POST",
202 | path: uri,
203 | headers: headers
204 | )//,delayAction(1000), refresh()]
205 | log.debug("Executing hubAction on " + getHostAddress())
206 | //log.debug hubAction
207 | hubAction
208 | }
209 |
210 | // ------------------------------------------------------------------
211 | // Helper methods
212 | // ------------------------------------------------------------------
213 |
214 | def parseDescriptionAsMap(description) {
215 | description.split(",").inject([:]) { map, param ->
216 | def nameAndValue = param.split(":")
217 | map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
218 | }
219 | }
220 |
221 |
222 | def toAscii(s){
223 | StringBuilder sb = new StringBuilder();
224 | String ascString = null;
225 | long asciiInt;
226 | for (int i = 0; i < s.length(); i++){
227 | sb.append((int)s.charAt(i));
228 | sb.append("|");
229 | char c = s.charAt(i);
230 | }
231 | ascString = sb.toString();
232 | asciiInt = Long.parseLong(ascString);
233 | return asciiInt;
234 | }
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 | private encodeCredentials(username, password){
246 | log.debug "Encoding credentials"
247 | def userpassascii = "${username}:${password}"
248 | def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
249 | //log.debug "ASCII credentials are ${userpassascii}"
250 | //log.debug "Credentials are ${userpass}"
251 | return userpass
252 | }
253 |
254 | private getHeader(userpass){
255 | log.debug "Getting headers"
256 | def headers = [:]
257 | headers.put("HOST", getHostAddress())
258 | headers.put("Authorization", userpass)
259 | //log.debug "Headers are ${headers}"
260 | return headers
261 | }
262 |
263 | private delayAction(long time) {
264 | new physicalgraph.device.HubAction("delay $time")
265 | }
266 |
267 | private setDeviceNetworkId(ip,port){
268 | def iphex = convertIPtoHex(ip)
269 | def porthex = convertPortToHex(port)
270 | device.deviceNetworkId = "$iphex:$porthex"
271 | log.debug "Device Network Id set to ${iphex}:${porthex}"
272 | }
273 |
274 | private getHostAddress() {
275 | return "${ip}:${port}"
276 | }
277 |
278 | private String convertIPtoHex(ipAddress) {
279 | String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
280 | return hex
281 |
282 | }
283 |
284 | private String convertPortToHex(port) {
285 | String hexport = port.toString().format( '%04x', port.toInteger() )
286 | return hexport
287 | }
288 |
--------------------------------------------------------------------------------
/SmartApp_DLI_Web_Power_Switch_with_Virtual_Tiles:
--------------------------------------------------------------------------------
1 | /**
2 | * DLI Web Power Switch with Virtual Tiles
3 | *
4 | */
5 | definition(
6 | name: "DLI Web Power Switch with Virtual Tiles.",
7 | namespace: "Ledridge",
8 | author: "Ledridge",
9 | description: "Use this app with a Digital Logger Inc. Web Power Switch and it will create virtual switches for each of the embedded switches.",
10 | category: "Convenience",
11 | iconUrl: "http://cdn.flaticon.com/png/256/25823.png",
12 | iconX2Url: "http://cdn.flaticon.com/png/256/25823.png",
13 | iconX3Url: "http://cdn.flaticon.com/png/256/25823.png")
14 |
15 |
16 | preferences {
17 | page(name: "mainDevice", uninstall: true, install:false)
18 | page(name: "virtualDetails", uninstall: true, install:true)
19 | page(name: "virtualSwitches", uninstall: true, install:true)
20 | }
21 |
22 | def mainDevice() {
23 | dynamicPage(name: "mainDevice", title: "Setup virtual app and multi-switch device", nextPage: "virtualDetails", uninstall: true, install:false) {
24 | section {
25 | input "master", "capability.switch", multiple: false, required: true, title: "Choose the device with multiple switches", image: "http://cdn.flaticon.com/png/256/61163.png"
26 | label title: "Assign a name for this virtual tile handler", required: false
27 | paragraph: "Assign switches to virtual tiles or real switches on next page"
28 | }
29 | }
30 | }
31 |
32 | def virtualDetails() {
33 | unsubscribe()
34 | syncChildDevices()
35 | dynamicPage(name: "virtualDetails", title: "Which virtual switches to create for $master.label?", uninstall: true, install:true) {
36 |
37 | section {
38 | input "switches", "enum", multiple: true, required: false, refreshAfterSelection:true, options:templates(), title:"Select Switches", description: "Choose which switches of $master.label you would like to have as separate switches", image: "http://cdn.flaticon.com/png/256/25823.png"
39 | }
40 |
41 | section("Current Virtual Switches of $master.label:") {
42 | def kids = getChildDevices()
43 | kids.each { paragraph "$it.label" }
44 | }
45 | }
46 | }
47 |
48 |
49 | def templates() {
50 | return [ "switch":"Web Power Switch 7", "Outlet1":"Outlet 1", "Outlet2":"Outlet 2", "Outlet3":"Outlet 3", "Outlet4":"Outlet 4", "Outlet5":"Outlet 5", "Outlet6":"Outlet6", "Outlet7":"Outlet 7", "Outlet8":"Outlet 8", "OutletA":"All Outlets"]
51 | }
52 |
53 |
54 | def installed() {
55 | syncChildDevices()
56 | def kids = getChildDevices()
57 | }
58 |
59 | def updated() {
60 | syncChildDevices()
61 | def kids = getChildDevices()
62 | unsubscribe()
63 | kids.each { subscribe(master, "$it.name", vswitch) }
64 | }
65 |
66 | def uninstalled() {
67 | removeChildDevices()
68 | }
69 |
70 | def vswitch(evt) {
71 | log.debug evt.descriptionText
72 | def vswitch = evt.descriptionText.find(/outlet./).replaceAll(" ","")
73 | log.debug "child switch to change: $vswitch event: $evt.value DDNI:${ddni(vswitch)}"
74 | def vkid = getChildDevice(ddni(vswitch))
75 | log.debug "$evt.value $evt.deviceId $evt.description $evt.descriptionText"
76 | log.debug "vkid: $vkid"
77 | vkid."${evt.value}"()
78 | }
79 |
80 | def mswitch(evt) { log.debug "$evt.value $evt.deviceId $evt.description $evt.descriptionText" }
81 |
82 | def OutletStatus(childDevice) {
83 | log.debug "Parent OUTLET_STATUS: ${childDevice}"
84 | def num = childDevice.device.name.replaceAll("Outlet","")
85 | log.debug "Parent OUTLET_STATUS: ${num}"
86 | if(num) {return master.OutletStatus(num)} else { return "Unknown" ;log.debug "No switch number provided for off" }
87 | }
88 |
89 | def OutletName(childDevice) {
90 | log.debug "Parent OUTLET_NAME: ${childDevice}"
91 | def num = childDevice.device.name.replaceAll("Outlet","")
92 | log.debug "Parent OUTLET_NAME: ${num}"
93 | if(num) {return master.OutletName(num)} else { return "Unknown" ;log.debug "No switch number provided for off" }
94 | }
95 |
96 | def OutletAction(childDevice,action) {
97 | log.debug "Parent OUTLET_ACTION: ${childDevice} ${action}"
98 | def num = childDevice.device.name.replaceAll("Outlet","")
99 | log.debug "Parent OUTLET_ACTION: ${num} ${action}"
100 | if(num) { master.OutletAction(num,action)} else { master.off();log.debug "No switch number provided for off" }
101 | }
102 |
103 |
104 | private syncChildDevices() {
105 | switches.each { def e = getChildDevice(ddni(it)); if(!e) { createChild(it) } }
106 | def switchKids = getChildDevices()
107 | def removeKids = switchKids.name - switches
108 | log.debug "SWs: $switches del: $removeKids kids: $removeKids"
109 | removeKids.each { rem ->
110 | def delKid = getChildDevice(ddni(rem))
111 | log.debug delKid.deviceNetworkId
112 | removeChildDevices(delKid)
113 | }
114 | }
115 |
116 | private createChild(vt) {
117 | def label = templates().getAt(vt)
118 | log.debug "Label $label"
119 | def addedvt = addChildDevice("Ledridge", "vTile_DLI", ddni(vt), null, [name:vt, label:label, completedSetup: true])
120 | log.info "created Virtual Switch ${addedvt.displayName} with DNI ${addedvt.deviceNetworkId}"
121 | }
122 |
123 | private removeChildDevices(delete) {
124 | unsubscribe()
125 | if(!delete) { delete = getChildDevices() }
126 | delete.each {
127 | deleteChildDevice(it.deviceNetworkId)
128 | log.debug "deleted ${delete.displayName}"
129 | }
130 | }
131 |
132 | private ddni(id){
133 | if(!state.appId) { state.appId = app.id }
134 | def ddni = state.appId + "/" + id
135 | return ddni
136 | }
137 |
--------------------------------------------------------------------------------
/SmartApp_Powerstrip.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Multi Switch with Virtual Tiles for Master Switch
3 | *
4 | * Copyright 2014 Cooper Lee
5 | *
6 | * NOTE - THIS ONLY WORKS WITH ATTRIBUTE TYPE switch and switch[x] (1..9)
7 | *
8 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
9 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
10 | * for the specific language governing permissions and limitations under the License.
11 | *
12 | */
13 | definition(
14 | name: "Multi Switch with Virtual Tiles for Master Switch",
15 | namespace: "ms_w_vts",
16 | author: "Cooper Lee",
17 | description: "Use this app with a device that has multiple switches embedded in the device type and it will create virtual switches for each of the various embedded switches.",
18 | category: "Convenience",
19 | iconUrl: "http://cdn.flaticon.com/png/256/25823.png",
20 | iconX2Url: "http://cdn.flaticon.com/png/256/25823.png",
21 | iconX3Url: "http://cdn.flaticon.com/png/256/25823.png")
22 |
23 |
24 | preferences {
25 | page(name: "mainDevice", uninstall: true, install:false)
26 | page(name: "virtualSwitches", uninstall: true, install:true)
27 | page(name: "virtualDetails", uninstall: true, install:true)
28 | }
29 |
30 | def mainDevice() {
31 | dynamicPage(name: "mainDevice", title: "Setup virtual app and multi-switch device", nextPage: "virtualDetails", uninstall: true, install:false) {
32 | section {
33 | input "master", "capability.switch", multiple: false, required: true, title: "Choose the device with multiple switches", image: "http://cdn.flaticon.com/png/256/61163.png"
34 | label title: "Assign a name for this virtual tile handler", required: false
35 | paragraph: "Assign switches to virtual tiles or real switches on next page"
36 | }
37 | }
38 | }
39 |
40 | def devTemplates(devType) {
41 | def templates = [
42 | "base2" :"Device with 2 switches",
43 | "base9" :"Device with up to 9 switches",
44 |
45 | "aeon4" :"Aeon Power Strip",
46 |
47 | "iPool1PSV" :"PE653 - Pool & Spa w/o Booster (Variable speed pump)", // (blower on sw3 lights on sw2
48 | "iPool1PSVB":"PE653 - Pool & Spa w/ Booster (Variable speed pump)", // (booster on sw3 lights/blower on sw2
49 |
50 | "iPool1" :"PE653 - Pool and 4 Acc (1 speed pump)",
51 | "iPool1H" :"PE653 - Pool w/Heater and 3 Acc (1 speed pump)",
52 | "iPool1PS" :"PE653 - Pool & Spa w/o Booster (1 speed pump)", // blower on sw3 lights on sw2
53 | "iPool1PSB" :"PE653 - Pool & Spa w/ Booster (1 speed pump)", // (booster on sw3 lights/blower on sw2
54 |
55 | "iPool1S" :"PE653 - Spa Only (1 speed pump)", // blower on sw3 lights on sw2
56 | "iPool2S" :"PE653 - Spa Only (2 speed pump)", // blower on sw4 lights/booster on sw3
57 |
58 | "iPool2" :"PE653 - Pool and 3 Acc (2 speed pump)",
59 | "iPool2H" :"PE653 - Pool w/Heater and 2 Acc (2 speed pump)",
60 | "iPool2PS" :"PE653 - Pool & Spa w/o Booster (2 speed pump)", // blower on sw3
61 | "iPool2PB" :"PE653 - Pool w/ Booster and Lights (2 speed pump)", // (booster on sw3 lights on sw4
62 | ]
63 | if(devType) { return devTemplates.getAt(dev_type) } else { return templates }
64 | }
65 |
66 |
67 | def templates() {
68 | def templates = [
69 | 'aeon4': [ "switch":"Power Strip", "switch1":"SW Outlet 1", "switch2":"SW Outlet 2", "switch3":"SW Outlet 3", "switch4":"SW Outlet 4", "switch5":"Outlet 1", "switch6":"Outlet 2", "switch7":"All SW Outlets"],
70 | 'base': [ "switch":"switch", "switch1":"Switch 1"],
71 | 'base2': [ "switch":"switch", "switch1":"Switch 1", "switch2":"Switch 2"],
72 | 'base9': [ "switch":"switch", "switch1":"Switch 1", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Switch 5", "switch6":"Switch 6", "switch7":"Switch 7", "switch8":"Switch 8", "switch9":"Switch 9"],
73 | 'iPool1': [ "switch1":"Pool Pump", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Switch 5"],
74 | 'iPool1H': [ "switch1":"Pool Pump", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Pool Heater"],
75 | 'iPool1PS': [ "switch1":"Filter Pump", "switch2":"Water Lights", "switch3":"Spa Blower", "switch4":"Pool/Spa", "switch5":"Water Heater"],
76 | 'iPool1PSB':[ "switch1":"Filter Pump", "switch2":"Spa Blower", "switch3":"Booster Pump", "switch4":"Pool/Spa", "switch5":"Water Heater"],
77 | 'iPool1PSV':[ "switch1":"Filter Pump", "switch2":"Spa Blower", "switch3":"Booster Pump", "switch4":"Pool/Spa", "switch5":"Water Heater"],
78 | 'iPool1PSVB':[ "switch1":"Filter Pump", "switch2":"Pool Lights", "switch3":"Spa Blower", "switch4":"Pool/Spa", "switch5":"Water Heater"],
79 | 'iPool1S': [ "switch1":"Spa Pump", "switch2":"Spa Lights", "switch3":"Spa Blower", "switch4":"Spa Accessory", "switch5":"Spa Heater"],
80 |
81 | 'iPool2': [ "switch1":"Pool Pump High", "switch2":"Pool Pump Low", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Switch 5"],
82 | 'iPool2H': [ "switch1":"Pool Pump High", "switch2":"Pool Pump Low", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Pool Heater"],
83 | 'iPool2S': [ "switch1":"Spa Pump High", "switch2":"Spa Pump Low", "switch3":"Spa Lights", "switch4":"Spa Blower", "switch5":"Spa Heater"],
84 | 'iPool2PS': [ "switch1":"Filter Pump High", "switch2":"Filter Pump Low", "switch3":"Spa Blower", "switch4":"Pool/Spa", "switch5":"Water Heater"],
85 | 'iPool2PB': [ "switch1":"Pool Pump High", "switch2":"Pool Pump Low", "switch3":"Booster Pump", "switch4":"Pool Lights", "switch5":"Water Heater"],
86 |
87 |
88 | 'iPoolSpa': [ "switch1":"Pump", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Pool Heater"],
89 | ]
90 | if(switch_template) { return templates.getAt(switch_template) } else { return [ "switch":"switch", "switch1":"Switch 1", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4"] }
91 | }
92 |
93 |
94 | def virtualDetails() {
95 | unsubscribe()
96 | syncChildDevices()
97 | dynamicPage(name: "virtualDetails", title: "Which virtual switches to create for $master.label?", uninstall: true, install:true) {
98 |
99 | section {
100 | input "switch_template", "enum", multiple: false, required: false, refreshAfterSelection:true, options:devTemplates(), title:"Template to use", description: "What device template should to use for $master.label?", image: "http://cdn.flaticon.com/png/256/25823.png"
101 | }
102 |
103 | section {
104 | input "switches", "enum", multiple: true, required: false, refreshAfterSelection:true, options:templates(), title:"Select Switches", description: "Choose which switches of $master.label you would like to have as separate switches", image: "http://cdn.flaticon.com/png/256/25823.png"
105 | }
106 |
107 | section("Current Virtual Switches of $master.label:") {
108 | def kids = getChildDevices()
109 | kids.each { paragraph "$it.label" }
110 | }
111 | }
112 | }
113 |
114 |
115 |
116 | def installed() {
117 | syncChildDevices()
118 | def kids = getChildDevices()
119 | }
120 |
121 | def updated() {
122 | syncChildDevices()
123 | def kids = getChildDevices()
124 | unsubscribe()
125 | kids.each { subscribe(master, "$it.name", vswitch) }
126 | }
127 |
128 | def uninstalled() {
129 | removeChildDevices()
130 | }
131 |
132 | def vswitch(evt) {
133 | def vswitch = evt.descriptionText.find(/switch./).replaceAll(" ","")
134 | // log.debug "child switch to change: $vswitch event: $evt.value DDNI:${ddni(vswitch)}"
135 | def vkid = getChildDevice(ddni(vswitch))
136 | // log.debug "$evt.value $evt.deviceId $evt.description $evt.descriptionText"
137 | // log.debug "vkid: $vkid"
138 | vkid."${evt.value}"()
139 | }
140 |
141 | //def mswitch(evt) { log.debug "$evt.value $evt.deviceId $evt.description $evt.descriptionText" }
142 |
143 | def on(childDevice) {
144 | def num = childDevice.device.name.replaceAll("switch","")
145 | if(num) { master."on${num}"() } else { master.on();log.debug "No switch number provided for on" }
146 | }
147 |
148 | def off(childDevice) {
149 | def num = childDevice.device.name.replaceAll("switch","")
150 | if(num) { master."off${num}"() } else { master.off();log.debug "No switch number provided for off" }
151 | }
152 |
153 |
154 | private syncChildDevices() {
155 | switches.each { def e = getChildDevice(ddni(it)); if(!e) { createChild(it) } }
156 | def switchKids = getChildDevices()
157 | def removeKids = switchKids.name - switches
158 | // log.debug "SWs: $switches del: $removeKids kids: $removeKids"
159 | removeKids.each { rem ->
160 | def delKid = getChildDevice(ddni(rem))
161 | log.debug delKid.deviceNetworkId
162 | removeChildDevices(delKid)
163 | }
164 | }
165 |
166 | private createChild(vt) {
167 | def label = templates().getAt(vt)
168 | log.debug "Label $label"
169 | def addedvt = addChildDevice("ms_w_vts", "vTile_ms", ddni(vt), null, [name:vt, label:label, completedSetup: true])
170 | log.info "created Virtual Switch ${addedvt.displayName} with DNI ${addedvt.deviceNetworkId}"
171 | }
172 |
173 | private removeChildDevices(delete) {
174 | unsubscribe()
175 | if(!delete) { delete = getChildDevices() }
176 | delete.each {
177 | deleteChildDevice(it.deviceNetworkId)
178 | log.debug "deleted ${delete.displayName}"
179 | }
180 | }
181 |
182 | private ddni(id){
183 | if(!state.appId) { state.appId = app.id }
184 | def ddni = state.appId + "/" + id
185 | return ddni
186 | }/**
187 | * Multi Switch with Virtual Tiles for Master Switch
188 | *
189 | * Copyright 2014 Cooper Lee
190 | *
191 | * NOTE - THIS ONLY WORKS WITH ATTRIBUTE TYPE switch and switch[x] (1..9)
192 | *
193 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
194 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
195 | * for the specific language governing permissions and limitations under the License.
196 | *
197 | */
198 | definition(
199 | name: "Multi Switch with Virtual Tiles for Master Switch",
200 | namespace: "ms_w_vts",
201 | author: "Cooper Lee",
202 | description: "Use this app with a device that has multiple switches embedded in the device type and it will create virtual switches for each of the various embedded switches.",
203 | category: "Convenience",
204 | iconUrl: "http://cdn.flaticon.com/png/256/25823.png",
205 | iconX2Url: "http://cdn.flaticon.com/png/256/25823.png",
206 | iconX3Url: "http://cdn.flaticon.com/png/256/25823.png")
207 |
208 |
209 | preferences {
210 | page(name: "mainDevice", uninstall: true, install:false)
211 | page(name: "virtualSwitches", uninstall: true, install:true)
212 | page(name: "virtualDetails", uninstall: true, install:true)
213 | }
214 |
215 | def mainDevice() {
216 | dynamicPage(name: "mainDevice", title: "Setup virtual app and multi-switch device", nextPage: "virtualDetails", uninstall: true, install:false) {
217 | section {
218 | input "master", "capability.switch", multiple: false, required: true, title: "Choose the device with multiple switches", image: "http://cdn.flaticon.com/png/256/61163.png"
219 | label title: "Assign a name for this virtual tile handler", required: false
220 | paragraph: "Assign switches to virtual tiles or real switches on next page"
221 | }
222 | }
223 | }
224 |
225 | def devTemplates(devType) {
226 | def templates = [
227 | "base2" :"Device with 2 switches",
228 | "base9" :"Device with up to 9 switches",
229 |
230 | "aeon4" :"Aeon Power Strip",
231 |
232 | "iPool1PSV" :"PE653 - Pool & Spa w/o Booster (Variable speed pump)", // (blower on sw3 lights on sw2
233 | "iPool1PSVB":"PE653 - Pool & Spa w/ Booster (Variable speed pump)", // (booster on sw3 lights/blower on sw2
234 |
235 | "iPool1" :"PE653 - Pool and 4 Acc (1 speed pump)",
236 | "iPool1H" :"PE653 - Pool w/Heater and 3 Acc (1 speed pump)",
237 | "iPool1PS" :"PE653 - Pool & Spa w/o Booster (1 speed pump)", // blower on sw3 lights on sw2
238 | "iPool1PSB" :"PE653 - Pool & Spa w/ Booster (1 speed pump)", // (booster on sw3 lights/blower on sw2
239 |
240 | "iPool1S" :"PE653 - Spa Only (1 speed pump)", // blower on sw3 lights on sw2
241 | "iPool2S" :"PE653 - Spa Only (2 speed pump)", // blower on sw4 lights/booster on sw3
242 |
243 | "iPool2" :"PE653 - Pool and 3 Acc (2 speed pump)",
244 | "iPool2H" :"PE653 - Pool w/Heater and 2 Acc (2 speed pump)",
245 | "iPool2PS" :"PE653 - Pool & Spa w/o Booster (2 speed pump)", // blower on sw3
246 | "iPool2PB" :"PE653 - Pool w/ Booster and Lights (2 speed pump)", // (booster on sw3 lights on sw4
247 | ]
248 | if(devType) { return devTemplates.getAt(dev_type) } else { return templates }
249 | }
250 |
251 |
252 | def templates() {
253 | def templates = [
254 | 'aeon4': [ "switch":"Power Strip", "switch1":"SW Outlet 1", "switch2":"SW Outlet 2", "switch3":"SW Outlet 3", "switch4":"SW Outlet 4", "switch5":"Outlet 1", "switch6":"Outlet 2", "switch7":"All SW Outlets"],
255 | 'base': [ "switch":"switch", "switch1":"Switch 1"],
256 | 'base2': [ "switch":"switch", "switch1":"Switch 1", "switch2":"Switch 2"],
257 | 'base9': [ "switch":"switch", "switch1":"Switch 1", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Switch 5", "switch6":"Switch 6", "switch7":"Switch 7", "switch8":"Switch 8", "switch9":"Switch 9"],
258 | 'iPool1': [ "switch1":"Pool Pump", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Switch 5"],
259 | 'iPool1H': [ "switch1":"Pool Pump", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Pool Heater"],
260 | 'iPool1PS': [ "switch1":"Filter Pump", "switch2":"Water Lights", "switch3":"Spa Blower", "switch4":"Pool/Spa", "switch5":"Water Heater"],
261 | 'iPool1PSB':[ "switch1":"Filter Pump", "switch2":"Spa Blower", "switch3":"Booster Pump", "switch4":"Pool/Spa", "switch5":"Water Heater"],
262 | 'iPool1PSV':[ "switch1":"Filter Pump", "switch2":"Spa Blower", "switch3":"Booster Pump", "switch4":"Pool/Spa", "switch5":"Water Heater"],
263 | 'iPool1PSVB':[ "switch1":"Filter Pump", "switch2":"Pool Lights", "switch3":"Spa Blower", "switch4":"Pool/Spa", "switch5":"Water Heater"],
264 | 'iPool1S': [ "switch1":"Spa Pump", "switch2":"Spa Lights", "switch3":"Spa Blower", "switch4":"Spa Accessory", "switch5":"Spa Heater"],
265 |
266 | 'iPool2': [ "switch1":"Pool Pump High", "switch2":"Pool Pump Low", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Switch 5"],
267 | 'iPool2H': [ "switch1":"Pool Pump High", "switch2":"Pool Pump Low", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Pool Heater"],
268 | 'iPool2S': [ "switch1":"Spa Pump High", "switch2":"Spa Pump Low", "switch3":"Spa Lights", "switch4":"Spa Blower", "switch5":"Spa Heater"],
269 | 'iPool2PS': [ "switch1":"Filter Pump High", "switch2":"Filter Pump Low", "switch3":"Spa Blower", "switch4":"Pool/Spa", "switch5":"Water Heater"],
270 | 'iPool2PB': [ "switch1":"Pool Pump High", "switch2":"Pool Pump Low", "switch3":"Booster Pump", "switch4":"Pool Lights", "switch5":"Water Heater"],
271 |
272 |
273 | 'iPoolSpa': [ "switch1":"Pump", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4", "switch5":"Pool Heater"],
274 | ]
275 | if(switch_template) { return templates.getAt(switch_template) } else { return [ "switch":"switch", "switch1":"Switch 1", "switch2":"Switch 2", "switch3":"Switch 3", "switch4":"Switch 4"] }
276 | }
277 |
278 |
279 | def virtualDetails() {
280 | unsubscribe()
281 | syncChildDevices()
282 | dynamicPage(name: "virtualDetails", title: "Which virtual switches to create for $master.label?", uninstall: true, install:true) {
283 |
284 | section {
285 | input "switch_template", "enum", multiple: false, required: false, refreshAfterSelection:true, options:devTemplates(), title:"Template to use", description: "What device template should to use for $master.label?", image: "http://cdn.flaticon.com/png/256/25823.png"
286 | }
287 |
288 | section {
289 | input "switches", "enum", multiple: true, required: false, refreshAfterSelection:true, options:templates(), title:"Select Switches", description: "Choose which switches of $master.label you would like to have as separate switches", image: "http://cdn.flaticon.com/png/256/25823.png"
290 | }
291 |
292 | section("Current Virtual Switches of $master.label:") {
293 | def kids = getChildDevices()
294 | kids.each { paragraph "$it.label" }
295 | }
296 | }
297 | }
298 |
299 |
300 |
301 | def installed() {
302 | syncChildDevices()
303 | def kids = getChildDevices()
304 | }
305 |
306 | def updated() {
307 | syncChildDevices()
308 | def kids = getChildDevices()
309 | unsubscribe()
310 | kids.each { subscribe(master, "$it.name", vswitch) }
311 | }
312 |
313 | def uninstalled() {
314 | removeChildDevices()
315 | }
316 |
317 | def vswitch(evt) {
318 | def vswitch = evt.descriptionText.find(/switch./).replaceAll(" ","")
319 | // log.debug "child switch to change: $vswitch event: $evt.value DDNI:${ddni(vswitch)}"
320 | def vkid = getChildDevice(ddni(vswitch))
321 | // log.debug "$evt.value $evt.deviceId $evt.description $evt.descriptionText"
322 | // log.debug "vkid: $vkid"
323 | vkid."${evt.value}"()
324 | }
325 |
326 | //def mswitch(evt) { log.debug "$evt.value $evt.deviceId $evt.description $evt.descriptionText" }
327 |
328 | def on(childDevice) {
329 | def num = childDevice.device.name.replaceAll("switch","")
330 | if(num) { master."on${num}"() } else { master.on();log.debug "No switch number provided for on" }
331 | }
332 |
333 | def off(childDevice) {
334 | def num = childDevice.device.name.replaceAll("switch","")
335 | if(num) { master."off${num}"() } else { master.off();log.debug "No switch number provided for off" }
336 | }
337 |
338 |
339 | private syncChildDevices() {
340 | switches.each { def e = getChildDevice(ddni(it)); if(!e) { createChild(it) } }
341 | def switchKids = getChildDevices()
342 | def removeKids = switchKids.name - switches
343 | // log.debug "SWs: $switches del: $removeKids kids: $removeKids"
344 | removeKids.each { rem ->
345 | def delKid = getChildDevice(ddni(rem))
346 | log.debug delKid.deviceNetworkId
347 | removeChildDevices(delKid)
348 | }
349 | }
350 |
351 | private createChild(vt) {
352 | def label = templates().getAt(vt)
353 | log.debug "Label $label"
354 | def addedvt = addChildDevice("ms_w_vts", "vTile_ms", ddni(vt), null, [name:vt, label:label, completedSetup: true])
355 | log.info "created Virtual Switch ${addedvt.displayName} with DNI ${addedvt.deviceNetworkId}"
356 | }
357 |
358 | private removeChildDevices(delete) {
359 | unsubscribe()
360 | if(!delete) { delete = getChildDevices() }
361 | delete.each {
362 | deleteChildDevice(it.deviceNetworkId)
363 | log.debug "deleted ${delete.displayName}"
364 | }
365 | }
366 |
367 | private ddni(id){
368 | if(!state.appId) { state.appId = app.id }
369 | def ddni = state.appId + "/" + id
370 | return ddni
371 | }
--------------------------------------------------------------------------------
/devicetypes/cmonroe/everspring-st814.src/everspring-st814.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * EverSpring ST814
3 | *
4 | * Copyright 2014 Ben (SmartThings) and Chad Monroe
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | */
16 | metadata
17 | {
18 | definition (name: "EverSpring ST814", namespace: "cmonroe", author: "@Ben chad@monroe.io")
19 | {
20 | capability "Battery"
21 | capability "Temperature Measurement"
22 | capability "Relative Humidity Measurement"
23 | capability "Configuration"
24 | capability "Alarm"
25 | capability "Sensor"
26 |
27 | fingerprint deviceId: "0x2101", inClusters: "0x31,0x60,0x86,0x72,0x85,0x84,0x80,0x70,0x20,0x71"
28 |
29 | /**
30 | * 0x31: COMMAND_CLASS_SENSOR_MULTILEVEL_V2
31 | * 0x60: COMMAND_CLASS_MULTI_CHANNEL_V2
32 | * 0x86: COMMAND_CLASS_VERSION
33 | * 0x72: COMMAND_CLASS_MANUFACTURER_SPECIFIC
34 | * 0x85: COMMAND_CLASS_ASSOCIATION_V2
35 | * 0x84: COMMAND_CLASS_WAKE_UP_V2
36 | * 0x80: COMMAND_CLASS_BATTERY
37 | * 0x70: COMMAND_CLASS_CONFIGURATION_V2
38 | * 0x20: COMMAND_CLASS_BASIC
39 | * 0x71: COMMAND_CLASS_ALARM
40 | **/
41 | }
42 |
43 | simulator
44 | {
45 | /* messages the device returns in response to commands it receives */
46 | for( int i = 0; i <= 100; i += 20 )
47 | {
48 | status "temperature ${i}F": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
49 | scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1).incomingMessage()
50 | }
51 |
52 | for( int i = 0; i <= 100; i += 20 )
53 | {
54 | status "humidity ${i}%": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
55 | scaledSensorValue: i, precision: 0, sensorType: 5).incomingMessage()
56 | }
57 |
58 | for( int i = 0; i <= 100; i += 20 )
59 | {
60 | status "battery ${i}%": new physicalgraph.zwave.Zwave().batteryV1.batteryReport(
61 | batteryLevel: i).incomingMessage()
62 | }
63 | }
64 |
65 | tiles
66 | {
67 | valueTile( "temperature", "device.temperature", inactiveLabel: false )
68 | {
69 | state( "temperature", label:'${currentValue}°',
70 | backgroundColors:[
71 | [value: 31, color: "#153591"],
72 | [value: 44, color: "#1e9cbb"],
73 | [value: 59, color: "#90d2a7"],
74 | [value: 74, color: "#44b621"],
75 | [value: 84, color: "#f1d801"],
76 | [value: 95, color: "#d04e00"],
77 | [value: 96, color: "#bc2323"]
78 | ]
79 | )
80 | }
81 |
82 | valueTile( "humidity", "device.humidity", inactiveLabel: false )
83 | {
84 | state( "humidity", label:'${currentValue}% humidity', unit:"" )
85 | }
86 |
87 | standardTile( "alarm", "device.alarm", inactiveLabel: false )
88 | {
89 | state( "ok", label:'BAT OK', action:'alarm.on', icon:"st.alarm.alarm.alarm", backgroundColor:"#ffffff" )
90 | state( "low", label:'BAT LOW', action:'alarm.off', icon:"st.alarm.alarm.alarm", backgroundColor:"#e86d13" )
91 | }
92 |
93 | valueTile( "battery", "device.battery", inactiveLabel: false, decoration: "flat" )
94 | {
95 | state( "battery", label:'${currentValue}% battery', unit:"" )
96 | }
97 |
98 | standardTile( "configure", "device.configure", inactiveLabel: false, decoration: "flat" )
99 | {
100 | state( "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure" )
101 | }
102 |
103 | main( ["temperature", "humidity"] )
104 | details( ["temperature", "humidity", "alarm", "battery", "configure"] )
105 | }
106 | }
107 |
108 | /**
109 | * parse incoming device messages and generate events
110 | **/
111 | def parse(String description)
112 | {
113 | def result = []
114 | def get_battery = false
115 | def cmd = null
116 |
117 |
118 | if ( description == "updated" )
119 | {
120 | log.debug "event description: ${description} - updating battery status"
121 | get_battery = true
122 | }
123 | else
124 | {
125 | cmd = zwave.parse( description, [0x20: 1, 0x31: 2, 0x70: 1, 0x71: 1, 0x80: 1, 0x84: 2, 0x85: 2] )
126 | }
127 |
128 | if ( cmd != null )
129 | {
130 | if ( cmd.CMD == "8407" )
131 | {
132 | result << zwaveEvent( cmd )
133 |
134 | log.debug "cmd.CMD=8407; result=${result} - updating battery status"
135 | get_battery = true
136 | }
137 | else
138 | {
139 | result << createEvent( zwaveEvent( cmd ) )
140 | }
141 | }
142 |
143 | if( get_battery == true )
144 | {
145 | def last = device.currentState( "battery" )
146 |
147 | /* device wakes up roughly every hour */
148 | def age = last ? (new Date().time - last.date.time)/60000 : 10
149 |
150 | log.debug "Battery status was last checked ${age} minute(s) ago"
151 |
152 | /* don't check too often if woken up more frequently */
153 | if( age >= 10 )
154 | {
155 | log.debug "Battery status is outdated, requesting battery report"
156 | result << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format())
157 | }
158 |
159 | result << new physicalgraph.device.HubAction(zwave.wakeUpV1.wakeUpNoMoreInformation().format())
160 | }
161 |
162 | log.debug "Parse returned: ${result} for cmd=${cmd} description=${description}"
163 | return result
164 | }
165 |
166 | /**
167 | * event generation
168 | **/
169 | def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd)
170 | {
171 | [descriptionText: "${device.displayName} woke up", isStateChange: false]
172 | }
173 |
174 | def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
175 | {
176 | [descriptionText: "${device.displayName} woke up", isStateChange: false]
177 | }
178 |
179 | def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd)
180 | {
181 | def map = [:]
182 |
183 | log.debug "AlarmReport cmd: ${cmd.toString()}}"
184 |
185 | if(( cmd.alarmType == 2 ) && ( cmd.alarmLevel == 1 ))
186 | {
187 | log.info "${device.displayName} powered up!"
188 | return map
189 | }
190 | else
191 | {
192 | /* alarmType == 1 && alarmLevel == 255 means low battery, else ok */
193 | map.value = cmd.alarmLevel == 255 ? "low" : "ok"
194 | map.name = "alarm"
195 | }
196 |
197 | map
198 | }
199 |
200 | def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd)
201 | {
202 | log.debug "SensorMultilevelReport cmd: ${cmd.toString()}}"
203 |
204 | def map = [:]
205 | switch( cmd.sensorType )
206 | {
207 | case 1:
208 | /* temperature */
209 | def cmdScale = cmd.scale == 1 ? "F" : "C"
210 | map.value = convertTemperatureIfNeeded( cmd.scaledSensorValue, cmdScale, cmd.precision )
211 | map.unit = getTemperatureScale()
212 | map.name = "temperature"
213 | break;
214 | case 5:
215 | /* humidity */
216 | map.value = cmd.scaledSensorValue.toInteger().toString()
217 | map.unit = "%"
218 | map.name = "humidity"
219 | break;
220 | }
221 |
222 | map
223 | }
224 |
225 | def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd)
226 | {
227 | def map = [:]
228 |
229 | map.name = "battery"
230 | map.value = cmd.batteryLevel > 0 ? cmd.batteryLevel.toString() : 1
231 | map.unit = "%"
232 | map.displayed = true
233 |
234 | map
235 | }
236 |
237 | def zwaveEvent(physicalgraph.zwave.Command cmd)
238 | {
239 | log.debug "Catchall reached for cmd: ${cmd.toString()}"
240 | [:]
241 | }
242 |
243 | def configure()
244 | {
245 | log.debug "sending config values..."
246 |
247 | delayBetween([
248 | /* report in every 5 minute(s) */
249 | zwave.configurationV1.configurationSet(parameterNumber: 6, size: 2, scaledConfigurationValue: 5).format(),
250 |
251 | /* report a temperature change of 1 degree C */
252 | zwave.configurationV1.configurationSet(parameterNumber: 7, size: 1, scaledConfigurationValue: 1).format(),
253 |
254 | /* report a humidity change of 1 percent */
255 | zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: 1).format()
256 |
257 | ])
258 | }
--------------------------------------------------------------------------------
/devicetypes/ledridge/foscam-hd.src/foscam-hd.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Foscam HD
3 | *
4 | * Author: skp19
5 | * Date: 6/18/14
6 | *
7 | * Example device type for HD Foscam cameras.
8 | * Code based off of Foscam device type by brian@bevey.org.
9 | * Heavily modified to work with the Foscam HD cameras.
10 | *
11 | * This device has the following functions:
12 | * - Take a snapshot
13 | * - Toggle the infrared lights
14 | * - Enable/Disable motion alarm
15 | * - Go to and set preset locations
16 | * - Enable cruise maps
17 | * - Control PTZ
18 | * - Reboot
19 | *
20 | * Capability: Image Capture, Polling
21 | * Custom Attributes: setStatus, alarmStatus, ledStatus
22 | * Custom Commands: alarmOn, alarmOff, toggleAlarm, left, right, up, down,
23 | * stop, set, preset, preset1, preset2, preset3, cruisemap1,
24 | * cruisemap2, cruise, toggleLED, ledOn, ledOff, ledAuto
25 | */
26 |
27 | preferences {
28 | input("username", "text", title: "Username", description: "Your Foscam camera username")
29 | input("password", "password", title: "Password", description: "Your Foscam camera password")
30 | input("ip", "text", title: "IP address/Hostname", description: "The IP address or hostname of your Foscam camera")
31 | input("port", "text", title: "Port", description: "The port of your Foscam camera")
32 | input("preset1", "text", title: "Preset 1", description: "Name of your first preset position")
33 | input("preset2", "text", title: "Preset 2", description: "Name of your second preset position")
34 | input("preset3", "text", title: "Preset 3", description: "Name of your third preset position")
35 | input("cruisemap1", "text", title: "Cruise Map 1", description: "Name of your first cruise map")
36 | input("cruisemap2", "text", title: "Cruise Map 2", description: "Name of your second cruise map")
37 | }
38 |
39 | metadata {
40 | definition (name: "Foscam HD", namespace: "Ledridge", author: "Ledridge" ) {
41 | capability "Polling"
42 | capability "Image Capture"
43 |
44 | attribute "setStatus", "string"
45 | attribute "alarmStatus", "string"
46 | attribute "ledStatus", "string"
47 |
48 | command "alarmOn"
49 | command "alarmOff"
50 | command "toggleAlarm"
51 | command "left"
52 | command "right"
53 | command "up"
54 | command "down"
55 | command "stop"
56 | command "set"
57 | command "preset"
58 | command "preset1"
59 | command "preset2"
60 | command "preset3"
61 | command "cruisemap1"
62 | command "cruisemap2"
63 | command "cruise"
64 | command "toggleLED"
65 | command "ledOn"
66 | command "ledOff"
67 | command "ledAuto"
68 | command "reboot"
69 | }
70 |
71 | tiles {
72 | carouselTile("cameraDetails", "device.image", width: 3, height: 2) { }
73 |
74 | standardTile("foscam", "device.alarmStatus", width: 1, height: 1, canChangeIcon: true, inactiveLabel: true, canChangeBackground: false) {
75 | state "off", label: "off", action: "toggleAlarm", icon: "st.camera.dropcam-centered", backgroundColor: "#FFFFFF"
76 | state "on", label: "on", action: "toggleAlarm", icon: "st.camera.dropcam-centered", backgroundColor: "#53A7C0"
77 | }
78 |
79 | standardTile("camera", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
80 | state "default", label: "", action: "Image Capture.take", icon: "st.camera.dropcam-centered", backgroundColor: "#FFFFFF"
81 | }
82 |
83 | standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false, decoration: "flat") {
84 | state "take", label: "", action: "Image Capture.take", icon: "st.secondary.take", nextState:"taking"
85 | }
86 |
87 | standardTile("up", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
88 | state "up", label: "up", action: "up", icon: "st.thermostat.thermostat-up"
89 | }
90 |
91 | standardTile("alarmStatus", "device.alarmStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
92 | state "off", label: "off", action: "toggleAlarm", icon: "st.quirky.spotter.quirky-spotter-sound-off", backgroundColor: "#FFFFFF"
93 | state "on", label: "on", action: "toggleAlarm", icon: "st.quirky.spotter.quirky-spotter-sound-on", backgroundColor: "#53A7C0"
94 | }
95 |
96 | standardTile("preset1", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
97 | state "preset1", label: "preset 1", action: "preset1", icon: ""
98 | }
99 |
100 | standardTile("preset2", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
101 | state "preset2", label: "preset 2", action: "preset2", icon: ""
102 | }
103 |
104 | standardTile("preset3", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
105 | state "preset3", label: "preset 3", action: "preset3", icon: ""
106 | }
107 |
108 | standardTile("left", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
109 | state "left", label: "left", action: "left", icon: ""
110 | }
111 |
112 | standardTile("stop", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
113 | state "stop", label: "", action: "stop", icon: "st.sonos.stop-btn"
114 | }
115 |
116 | standardTile("right", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
117 | state "right", label: "right", action: "right", icon: ""
118 | }
119 |
120 | standardTile("blank", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
121 | state "blank", label: "", action: "stop", icon: "", backgroundColor: "#53A7C0"
122 | }
123 |
124 | standardTile("down", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
125 | state "down", label: "down", action: "down", icon: "st.thermostat.thermostat-down"
126 | }
127 |
128 | standardTile("cruisemap1", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
129 | state "cruisemap1", label: "Cruise Map 1", action: "cruisemap1", icon: ""
130 | }
131 |
132 | standardTile("cruisemap2", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
133 | state "cruisemap2", label: "Cruise Map 2", action: "cruisemap2", icon: ""
134 | }
135 |
136 | standardTile("set", "device.setStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
137 | state "set", label: "preset", action: "set", icon: "", backgroundColor: "#FFFFFF"
138 | state "add", label: "set mode", action: "set", icon: "", backgroundColor: "#53A7C0"
139 | }
140 |
141 | standardTile("refresh", "device.alarmStatus", inactiveLabel: false, decoration: "flat") {
142 | state "refresh", action:"polling.poll", icon:"st.secondary.refresh"
143 | }
144 |
145 | standardTile("ledStatus", "device.ledStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
146 | state "auto", label: "auto", action: "toggleLED", icon: "st.Lighting.light13", backgroundColor: "#53A7C0"
147 | state "off", label: "off", action: "toggleLED", icon: "st.Lighting.light13", backgroundColor: "#FFFFFF"
148 | state "on", label: "on", action: "toggleLED", icon: "st.Lighting.light11", backgroundColor: "#FFFF00"
149 | state "manual", label: "manual", action: "toggleLED", icon: "st.Lighting.light13", backgroundColor: "#FFFF00"
150 | }
151 |
152 | standardTile("reboot", "device.image", inactiveLabel: false, decoration: "flat") {
153 | state "reboot", label: "reboot", action: "reboot", icon: "st.Health & Wellness.health8"
154 | }
155 |
156 | main "foscam"
157 | details(["cameraDetails", "take", "ledStatus", "alarmStatus", "preset1", "preset2", "preset3", "cruisemap1", "up", "cruisemap2", "left", "stop", "right", "blank", "down", ,"blank", "refresh", "set", "reboot"])
158 | }
159 | }
160 |
161 | private getPictureName() {
162 | def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')
163 | "image" + "_$pictureUuid" + ".jpg"
164 | }
165 |
166 | def take() {
167 | log.debug("Take a photo")
168 |
169 | api("snapshot", "") {
170 | log.debug("Image captured")
171 |
172 | if(it.headers.'Content-Type'.contains("image/jpeg")) {
173 | if(it.data) {
174 | storeImage(getPictureName(), it.data)
175 | }
176 | }
177 | }
178 | }
179 |
180 | def toggleAlarm() {
181 | if(device.currentValue("alarmStatus") == "on") {
182 | alarmOff()
183 | }
184 |
185 | else {
186 | alarmOn()
187 | }
188 | }
189 |
190 | def alarmOn() {
191 | api("set_alarm", "isEnable=1") {
192 | log.debug("Alarm changed to: on")
193 | sendEvent(name: "alarmStatus", value: "on");
194 | }
195 | }
196 |
197 | def alarmOff() {
198 | api("set_alarm", "isEnable=0") {
199 | log.debug("Alarm changed to: off")
200 | sendEvent(name: "alarmStatus", value: "off");
201 | }
202 | }
203 |
204 | def left() {
205 | api("decoder_control", "cmd=ptzMoveLeft") {
206 | log.debug("Executing 'LEFT'")
207 | }
208 | stop()
209 | }
210 |
211 | def right() {
212 | api("decoder_control", "cmd=ptzMoveRight") {
213 | log.debug("Executing 'RIGHT'")
214 | }
215 | stop()
216 | }
217 |
218 | def up() {
219 | api("decoder_control", "cmd=ptzMoveUp") {
220 | log.debug("Executing 'UP'")
221 | }
222 | stop()
223 | }
224 |
225 | def down() {
226 | api("decoder_control", "cmd=ptzMoveDown") {
227 | log.debug("Executing 'DOWN'")
228 | }
229 | stop()
230 | }
231 |
232 | def stop() {
233 | api("decoder_control", "cmd=ptzStopRun") {
234 | log.debug("Executing 'STOP'")
235 | }
236 | }
237 |
238 | def preset1() {
239 | log.debug("Preset 1 Selected - ${preset1}")
240 | preset("${preset1}")
241 | }
242 |
243 | def preset2() {
244 | log.debug("Preset 2 Selected - ${preset2}")
245 | preset("${preset2}")
246 | }
247 |
248 | def preset3() {
249 | log.debug("Preset 3 Selected - ${preset3}")
250 | preset("${preset3}")
251 | }
252 |
253 | //Go to a preset location
254 | def preset(def presetname) {
255 | if(presetname == null) return
256 |
257 | if(device.currentValue("setStatus") == "add") {
258 | setPreset(presetname)
259 | }
260 |
261 | else {
262 | log.debug("Go To Preset Location - " + presetname)
263 | def cmd = "cmd=ptzGotoPresetPoint&name=" + presetname
264 |
265 | api("decoder_control", "${cmd}") {}
266 | }
267 | }
268 |
269 | //Set the selected preset to the current location
270 | def setPreset(def presetname) {
271 | log.debug("Set Preset - " + presetname)
272 | delPreset(presetname)
273 | addPreset(presetname)
274 |
275 | log.debug("Exit Add Preset Mode")
276 | sendEvent(name: "setStatus", value: "set");
277 | }
278 |
279 | //Delete currently selected preset
280 | def delPreset(def presetname) {
281 | log.debug("Delete Preset Location - " + presetname)
282 | def cmd = "cmd=ptzDeletePresetPoint&name=" + presetname
283 | api("decoder_control", "${cmd}") {}
284 | }
285 |
286 | //Add currently selected preset
287 | def addPreset(def presetname) {
288 | log.debug("Add Preset Location - " + presetname)
289 | def cmd = "cmd=ptzAddPresetPoint&name=" + presetname
290 | api("decoder_control", "${cmd}") {}
291 | }
292 |
293 | //Toggle the the mode to set the preset
294 | def set() {
295 | if((device.currentValue("setStatus") == "set").or(device.currentValue("setStatus") == "")) {
296 | log.debug("Entering Add Preset Mode")
297 | sendEvent(name: "setStatus", value: "add");
298 | }
299 |
300 | else {
301 | log.debug("Exit Add Preset Mode")
302 | sendEvent(name: "setStatus", value: "set");
303 | }
304 | }
305 |
306 | def cruisemap1() {
307 | log.debug("Cruise Map 1 Selected - ${cruisemap1}")
308 | cruise("${cruisemap1}")
309 | }
310 |
311 | def cruisemap2() {
312 | log.debug("Cruise Map 2 Selected - ${cruisemap2}")
313 | cruise("${cruisemap2}")
314 | }
315 |
316 | //Start cruise
317 | def cruise(def cruisename) {
318 |
319 | log.debug("Start Cruise Map - " + cruisename)
320 | def cmd = "cmd=ptzStartCruise&mapName=" + cruisename
321 |
322 | api("decoder_control", "${cmd}") {}
323 |
324 | }
325 |
326 | //Toggle LED's
327 | def toggleLED() {
328 | log.debug("Toggle LED")
329 |
330 | if(device.currentValue("ledStatus") == "auto") {
331 | ledOn()
332 | }
333 |
334 | else if(device.currentValue("ledStatus") == "on") {
335 | ledOff()
336 | }
337 |
338 | else {
339 | ledAuto()
340 | }
341 | }
342 |
343 | def ledOn() {
344 | api("decoder_control", "cmd=setInfraLedConfig&mode=1") {}
345 | api("decoder_control", "cmd=openInfraLed") {
346 | log.debug("LED changed to: on")
347 | sendEvent(name: "ledStatus", value: "on");
348 | }
349 | }
350 |
351 | def ledOff() {
352 | api("decoder_control", "cmd=setInfraLedConfig&mode=1") {}
353 | api("decoder_control", "cmd=closeInfraLed") {
354 | log.debug("LED changed to: off")
355 | sendEvent(name: "ledStatus", value: "off");
356 | }
357 | }
358 |
359 | def ledAuto() {
360 | api("decoder_control", "cmd=setInfraLedConfig&mode=0") {
361 | log.debug("LED changed to: auto")
362 | sendEvent(name: "ledStatus", value: "auto");
363 | }
364 | }
365 |
366 | def reboot() {
367 | api("reboot", "") {
368 | log.debug("Rebooting")
369 | }
370 | }
371 |
372 | def api(method, args = [], success = {}) {
373 | def methods = [
374 | "decoder_control": [uri: "http://${ip}:${port}/cgi-bin/CGIProxy.fcgi${login()}&${args}", type: "get"],
375 | "snapshot": [uri: "http://${ip}:${port}/cgi-bin/CGIProxy.fcgi${login()}&cmd=snapPicture2", type: "get"],
376 | "set_alarm": [uri: "http://${ip}:${port}/cgi-bin/CGIProxy.fcgi${login()}&cmd=setMotionDetectConfig&${args}", type: "get"],
377 | "reboot": [uri: "http://${ip}:${port}/cgi-bin/CGIProxy.fcgi${login()}&cmd=rebootSystem", type: "get"],
378 | "camera_control": [uri: "http://${ip}:${port}/camera_control.cgi${login()}&${args}", type: "get"],
379 | "get_params": [uri: "http://${ip}:${port}/cgi-bin/CGIProxy.fcgi${login()}&${args}", type: "get"]
380 | ]
381 |
382 | def request = methods.getAt(method)
383 |
384 | doRequest(request.uri, request.type, success)
385 | }
386 |
387 | private doRequest(uri, type, success) {
388 | log.debug(uri)
389 | httpGet(uri, success)
390 | }
391 |
392 | private login() {
393 | return "?usr=${username}&pwd=${password}"
394 | }
395 |
396 | def poll() {
397 | //Poll Motion Alarm Status
398 | api("get_params", "cmd=getMotionDetectConfig") {
399 | def CGI_Result = new XmlParser().parse(it.data)
400 | def motionAlarm = CGI_Result.isEnable.text()
401 |
402 | if(motionAlarm == "0") {
403 | log.info("Polled: Alarm Off")
404 | sendEvent(name: "alarmStatus", value: "off");
405 | }
406 |
407 | if(motionAlarm == "1") {
408 | log.info("Polled: Alarm On")
409 | sendEvent(name: "alarmStatus", value: "on");
410 | }
411 | }
412 |
413 | //Poll IR LED Mode
414 | api("get_params", "cmd=getInfraLedConfig") {
415 | def CGI_Result = new XmlParser().parse(it.data)
416 | def ledMode = CGI_Result.mode.text()
417 |
418 | if(ledMode == "0") {
419 | log.info("Polled: LED Mode Auto")
420 | sendEvent(name: "ledStatus", value: "auto");
421 | }
422 |
423 | if(ledMode == "1") {
424 | log.info("Polled: LED Mode Manual")
425 | sendEvent(name: "ledStatus", value: "manual");
426 | }
427 | }
428 |
429 | //Reset
430 | }
--------------------------------------------------------------------------------
/devicetypes/ledridge/my-garage-door-opener.src/my-garage-door-opener.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Z-Wave Garage Door Opener
3 | *
4 | * Copyright 2014 SmartThings
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | */
16 | metadata {
17 | definition (name: "My Garage Door Opener", namespace: "ledridge", author: "ledridge") {
18 | capability "Actuator"
19 | capability "Door Control"
20 | capability "Contact Sensor"
21 | capability "Refresh"
22 | capability "Sensor"
23 | capability "Polling"
24 | capability "Switch"
25 | capability "Momentary"
26 | capability "Relay Switch"
27 | capability "Garage Door Control"
28 |
29 |
30 | fingerprint deviceId: "0x4007", inClusters: "0x98"
31 | fingerprint deviceId: "0x4006", inClusters: "0x98"
32 | }
33 |
34 | simulator {
35 | status "closed": "command: 9881, payload: 00 66 03 00"
36 | status "opening": "command: 9881, payload: 00 66 03 FE"
37 | status "open": "command: 9881, payload: 00 66 03 FF"
38 | status "closing": "command: 9881, payload: 00 66 03 FC"
39 | status "unknown": "command: 9881, payload: 00 66 03 FD"
40 |
41 | reply "988100660100": "command: 9881, payload: 00 66 03 FC"
42 | reply "9881006601FF": "command: 9881, payload: 00 66 03 FE"
43 | }
44 |
45 | tiles {
46 | standardTile("toggle", "device.door", width: 2, height: 2) {
47 | state("unknown", label:'${name}', action:"refresh.refresh", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e")
48 | state("closed", label:'${name}', action:"door control.open", icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821", nextState:"opening")
49 | state("open", label:'${name}', action:"door control.close", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e", nextState:"closing")
50 | state("opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
51 | state("closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
52 |
53 | }
54 | standardTile("open", "device.door", inactiveLabel: false, decoration: "flat") {
55 | state "default", label:'open', action:"door control.open", icon:"st.doors.garage.garage-opening"
56 | }
57 | standardTile("close", "device.door", inactiveLabel: false, decoration: "flat") {
58 | state "default", label:'close', action:"door control.close", icon:"st.doors.garage.garage-closing"
59 | }
60 | standardTile("refresh", "device.door", inactiveLabel: false, decoration: "flat") {
61 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
62 | }
63 | main "toggle"
64 | details(["toggle", "open", "close", "refresh"])
65 | }
66 | }
67 |
68 |
69 | import physicalgraph.zwave.commands.barrieroperatorv1.*
70 |
71 | def parse(String description) {
72 | def result = null
73 | if (description.startsWith("Err")) {
74 | if (state.sec) {
75 | result = createEvent(descriptionText:description, displayed:false)
76 | } else {
77 | result = createEvent(
78 | descriptionText: "This device failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.",
79 | eventType: "ALERT",
80 | name: "secureInclusion",
81 | value: "failed",
82 | displayed: true,
83 | )
84 | }
85 | } else {
86 | def cmd = zwave.parse(description, [ 0x98: 1, 0x72: 2 ])
87 | if (cmd) {
88 | result = zwaveEvent(cmd)
89 | }
90 | }
91 | log.debug "\"$description\" parsed to ${result.inspect()}"
92 | result
93 | }
94 |
95 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
96 | def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x80: 1, 0x85: 2, 0x63: 1, 0x98: 1])
97 | log.debug "encapsulated: $encapsulatedCommand"
98 | if (encapsulatedCommand) {
99 | zwaveEvent(encapsulatedCommand)
100 | }
101 | }
102 |
103 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.NetworkKeyVerify cmd) {
104 | createEvent(name:"secureInclusion", value:"success", descriptionText:"Secure inclusion was successful")
105 | }
106 |
107 | def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
108 | state.sec = cmd.commandClassSupport.collect { String.format("%02X ", it) }.join()
109 | if (cmd.commandClassControl) {
110 | state.secCon = cmd.commandClassControl.collect { String.format("%02X ", it) }.join()
111 | }
112 | log.debug "Security command classes: $state.sec"
113 | createEvent(name:"secureInclusion", value:"success", descriptionText:"$device.displayText is securely included")
114 | }
115 |
116 | def zwaveEvent(BarrierOperatorReport cmd) {
117 | def result = []
118 | def map = [ name: "door" ]
119 | def switchMap = [ name: "switch" ]
120 |
121 | switch (cmd.barrierState) {
122 | case BarrierOperatorReport.BARRIER_STATE_CLOSED:
123 | map.value = "closed"
124 | result << createEvent(name: "contact", value: "closed", displayed: false)
125 | result << createEvent(name: "switch", value: "off", displayed: false)
126 | break
127 | case BarrierOperatorReport.BARRIER_STATE_UNKNOWN_POSITION_MOVING_TO_CLOSE:
128 | map.value = "closing"
129 | break
130 | case BarrierOperatorReport.BARRIER_STATE_UNKNOWN_POSITION_STOPPED:
131 | map.descriptionText = "$device.displayName door state is unknown"
132 | map.value = "unknown"
133 | break
134 | case BarrierOperatorReport.BARRIER_STATE_UNKNOWN_POSITION_MOVING_TO_OPEN:
135 | map.value = "opening"
136 | result << createEvent(name: "contact", value: "open", displayed: false)
137 | break
138 | case BarrierOperatorReport.BARRIER_STATE_OPEN:
139 | map.value = "open"
140 | result << createEvent(name: "contact", value: "open", displayed: false)
141 | result << createEvent(name: "switch", value: "on", displayed: false)
142 | break
143 | }
144 | result + createEvent(map)
145 | }
146 |
147 | def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
148 | def result = []
149 | def map = [:]
150 | if (cmd.notificationType == 6) {
151 | map.displayed = true
152 | switch(cmd.event) {
153 | case 0x40:
154 | if (cmd.eventParameter[0]) {
155 | map.descriptionText = "$device.displayName performing initialization process"
156 | } else {
157 | map.descriptionText = "$device.displayName initialization process complete"
158 | }
159 | break
160 | case 0x41:
161 | map.descriptionText = "$device.displayName door operation force has been exceeded"
162 | break
163 | case 0x42:
164 | map.descriptionText = "$device.displayName motor has exceeded operational time limit"
165 | break
166 | case 0x43:
167 | map.descriptionText = "$device.displayName has exceeded physical mechanical limits"
168 | break
169 | case 0x44:
170 | map.descriptionText = "$device.displayName unable to perform requested operation (UL requirement)"
171 | break
172 | case 0x45:
173 | map.descriptionText = "$device.displayName remote operation disabled (UL requirement)"
174 | break
175 | case 0x46:
176 | map.descriptionText = "$device.displayName failed to perform operation due to device malfunction"
177 | break
178 | case 0x47:
179 | if (cmd.eventParameter[0]) {
180 | map.descriptionText = "$device.displayName vacation mode enabled"
181 | } else {
182 | map.descriptionText = "$device.displayName vacation mode disabled"
183 | }
184 | break
185 | case 0x48:
186 | if (cmd.eventParameter[0]) {
187 | map.descriptionText = "$device.displayName safety beam obstructed"
188 | } else {
189 | map.descriptionText = "$device.displayName safety beam obstruction cleared"
190 | }
191 | break
192 | case 0x49:
193 | if (cmd.eventParameter[0]) {
194 | map.descriptionText = "$device.displayName door sensor ${cmd.eventParameter[0]} not detected"
195 | } else {
196 | map.descriptionText = "$device.displayName door sensor not detected"
197 | }
198 | break
199 | case 0x4A:
200 | if (cmd.eventParameter[0]) {
201 | map.descriptionText = "$device.displayName door sensor ${cmd.eventParameter[0]} has a low battery"
202 | } else {
203 | map.descriptionText = "$device.displayName door sensor has a low battery"
204 | }
205 | result << createEvent(name: "battery", value: 1, unit: "%", descriptionText: map.descriptionText)
206 | break
207 | case 0x4B:
208 | map.descriptionText = "$device.displayName detected a short in wall station wires"
209 | break
210 | case 0x4C:
211 | map.descriptionText = "$device.displayName is associated with non-Z-Wave remote control"
212 | break
213 | default:
214 | map.descriptionText = "$device.displayName: access control alarm $cmd.event"
215 | map.displayed = false
216 | break
217 | }
218 | } else if (cmd.notificationType == 7) {
219 | switch (cmd.event) {
220 | case 1:
221 | case 2:
222 | map.descriptionText = "$device.displayName detected intrusion"
223 | break
224 | case 3:
225 | map.descriptionText = "$device.displayName tampering detected: product cover removed"
226 | break
227 | case 4:
228 | map.descriptionText = "$device.displayName tampering detected: incorrect code"
229 | break
230 | case 7:
231 | case 8:
232 | map.descriptionText = "$device.displayName detected motion"
233 | break
234 | default:
235 | map.descriptionText = "$device.displayName: security alarm $cmd.event"
236 | map.displayed = false
237 | }
238 | } else if (cmd.notificationType){
239 | map.descriptionText = "$device.displayName: alarm type $cmd.notificationType event $cmd.event"
240 | } else {
241 | map.descriptionText = "$device.displayName: alarm $cmd.v1AlarmType is ${cmd.v1AlarmLevel == 255 ? 'active' : cmd.v1AlarmLevel ?: 'inactive'}"
242 | }
243 | result ? [createEvent(map), *result] : createEvent(map)
244 | }
245 |
246 | def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
247 | def map = [ name: "battery", unit: "%" ]
248 | if (cmd.batteryLevel == 0xFF) {
249 | map.value = 1
250 | map.descriptionText = "$device.displayName has a low battery"
251 | } else {
252 | map.value = cmd.batteryLevel
253 | }
254 | state.lastbatt = new Date().time
255 | createEvent(map)
256 | }
257 |
258 | def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
259 | def result = []
260 |
261 | def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
262 | log.debug "msr: $msr"
263 | updateDataValue("MSR", msr)
264 |
265 | result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)
266 | result
267 | }
268 |
269 | def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
270 | def fw = "${cmd.applicationVersion}.${cmd.applicationSubVersion}"
271 | updateDataValue("fw", fw)
272 | def text = "$device.displayName: firmware version: $fw, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
273 | createEvent(descriptionText: text, isStateChange: false)
274 | }
275 |
276 | def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationBusy cmd) {
277 | def msg = cmd.status == 0 ? "try again later" :
278 | cmd.status == 1 ? "try again in $cmd.waitTime seconds" :
279 | cmd.status == 2 ? "request queued" : "sorry"
280 | createEvent(displayed: true, descriptionText: "$device.displayName is busy, $msg")
281 | }
282 |
283 | def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationRejectedRequest cmd) {
284 | createEvent(displayed: true, descriptionText: "$device.displayName rejected the last request")
285 | }
286 |
287 | def zwaveEvent(physicalgraph.zwave.Command cmd) {
288 | createEvent(displayed: false, descriptionText: "$device.displayName: $cmd")
289 | }
290 |
291 | def open() {
292 | secure(zwave.barrierOperatorV1.barrierOperatorSet(requestedBarrierState: BarrierOperatorSet.REQUESTED_BARRIER_STATE_OPEN))
293 | }
294 |
295 | def close() {
296 | secure(zwave.barrierOperatorV1.barrierOperatorSet(requestedBarrierState: BarrierOperatorSet.REQUESTED_BARRIER_STATE_CLOSE))
297 | }
298 |
299 |
300 | def refresh() {
301 | secure(zwave.barrierOperatorV1.barrierOperatorGet())
302 | }
303 |
304 | def poll() {
305 | secure(zwave.barrierOperatorV1.barrierOperatorGet())
306 | }
307 |
308 |
309 | def on() {
310 | log.debug "on() was called and ignored"
311 | }
312 |
313 | def off() {
314 | log.debug "off() was called and ignored"
315 | }
316 |
317 | def push() {
318 |
319 | // get the current "door" attribute value
320 | //
321 | // For some reason, I can't use "device.doorState" or just "doorState". Not sure why not.
322 |
323 | def lastValue = device.latestValue("door");
324 |
325 | // if its open, then close the door
326 | if (lastValue == "open") {
327 | return close()
328 |
329 | // if its closed, then open the door
330 | } else if (lastValue == "closed") {
331 | return open()
332 |
333 | } else {
334 | log.debug "push() called when door state is $lastValue - there's nothing push() can do"
335 | }
336 |
337 | }
338 |
339 |
340 |
341 | private secure(physicalgraph.zwave.Command cmd) {
342 | zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
343 | }
344 |
345 | private secureSequence(commands, delay=200) {
346 | delayBetween(commands.collect{ secure(it) }, delay)
347 | }
--------------------------------------------------------------------------------
/devicetypes/ledridge/raspberry-pi.src/raspberry-pi.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Raspberry Pi
3 | *
4 | * Copyright 2014 Nicholas Wilde
5 | *
6 | * Monitor your Raspberry Pi using SmartThings and WebIOPi
7 | *
8 | * Companion WebIOPi python script can be found here:
9 | *
10 | *
11 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
12 | * in compliance with the License. You may obtain a copy of the License at:
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
17 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
18 | * for the specific language governing permissions and limitations under the License.
19 | *
20 | */
21 |
22 | import groovy.json.JsonSlurper
23 | import com.google.common.base.Splitter;
24 | import java.util.List;
25 | import java.util.Map;
26 | import java.util.Map.Entry;
27 |
28 |
29 | preferences {
30 | input("ip", "string", title:"IP Address", description: "192.168.1.50", defaultValue: "192.168.1.50" ,required: true, displayDuringSetup: true)
31 | input("port", "string", title:"Port", description: "80", defaultValue: "80" , required: true, displayDuringSetup: true)
32 | input("username", "string", title:"Username", description: "pi", defaultValue: "pi" , required: true, displayDuringSetup: true)
33 | input("password", "password", title:"Password", description: "raspberry", defaultValue: "raspberry" , required: true, displayDuringSetup: true)
34 | }
35 |
36 | metadata {
37 | definition (name: "Raspberry Pi", namespace: "Ledridge", author: "Ledridge") {
38 | capability "Polling"
39 | capability "Refresh"
40 | capability "Temperature Measurement"
41 | capability "Switch"
42 | capability "Sensor"
43 | capability "Actuator"
44 |
45 | attribute "cpuPercentage", "string"
46 | attribute "memory", "string"
47 | attribute "diskUsage", "string"
48 |
49 | command "restart"
50 | }
51 |
52 | simulator {
53 | // TODO: define status and reply messages here
54 | }
55 |
56 | tiles {
57 | valueTile("temperature", "device.temperature", width: 1, height: 1) {
58 | state "temperature", label:'${currentValue}° CPU', unit: "F",
59 | backgroundColors:[
60 | [value: 25, color: "#153591"],
61 | [value: 35, color: "#1e9cbb"],
62 | [value: 47, color: "#90d2a7"],
63 | [value: 59, color: "#44b621"],
64 | [value: 67, color: "#f1d801"],
65 | [value: 76, color: "#d04e00"],
66 | [value: 77, color: "#bc2323"]
67 | ]
68 | }
69 | standardTile("button", "device.switch", width: 1, height: 1, canChangeIcon: true) {
70 | state "off", label: 'Off', icon: "st.Electronics.electronics18", backgroundColor: "#ffffff", nextState: "on"
71 | state "on", label: 'On', icon: "st.Electronics.electronics18", backgroundColor: "#79b821", nextState: "off"
72 | }
73 | valueTile("cpuPercentage", "device.cpuPercentage", inactiveLabel: false, decoration: "flat") {
74 | state "default", label:'${currentValue}% CPU', unit:"Percentage", icon: "http://chart.apis.google.com/chart?cht=p&chs=120x120&chd=t:80,20&chco=ff000d&chf=bg,s,FFFFFF|c,s,FFFFFF",
75 | backgroundColors:[
76 | [value: 31, color: "#153591"],
77 | [value: 44, color: "#1e9cbb"],
78 | [value: 59, color: "#90d2a7"],
79 | [value: 74, color: "#44b621"],
80 | [value: 84, color: "#f1d801"],
81 | [value: 95, color: "#d04e00"],
82 | [value: 96, color: "#bc2323"]
83 | ]
84 | }
85 | valueTile("memory", "device.memory", width: 1, height: 1) {
86 | state "default", label:'${currentValue} MB', unit:"MB",
87 | backgroundColors:[
88 | [value: 353, color: "#153591"],
89 | [value: 287, color: "#1e9cbb"],
90 | [value: 210, color: "#90d2a7"],
91 | [value: 133, color: "#44b621"],
92 | [value: 82, color: "#f1d801"],
93 | [value: 26, color: "#d04e00"],
94 | [value: 20, color: "#bc2323"]
95 | ]
96 | }
97 | valueTile("diskUsage", "device.diskUsage", width: 1, height: 1) {
98 | state "default", label:'${currentValue}% Disk', unit:"Percent",
99 | backgroundColors:[
100 | [value: 31, color: "#153591"],
101 | [value: 44, color: "#1e9cbb"],
102 | [value: 59, color: "#90d2a7"],
103 | [value: 74, color: "#44b621"],
104 | [value: 84, color: "#f1d801"],
105 | [value: 95, color: "#d04e00"],
106 | [value: 96, color: "#bc2323"]
107 | ]
108 | }
109 | standardTile("restart", "device.restart", inactiveLabel: false, decoration: "flat") {
110 | state "default", action:"restart", label: "Restart", displayName: "Restart"
111 | }
112 | standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
113 | state "default", action:"refresh.refresh", icon: "st.secondary.refresh"
114 | }
115 | main "button"
116 | details(["button", "temperature", "cpuPercentage", "memory" , "diskUsage", "restart", "refresh"])
117 | }
118 | }
119 |
120 | // ------------------------------------------------------------------
121 |
122 | // parse events into attributes
123 | def parse(String description) {
124 | def map = [:]
125 | def descMap = parseDescriptionAsMap(description)
126 | log.debug "descMap: ${descMap}"
127 |
128 | def body = new String(descMap["body"].decodeBase64())
129 | log.debug "body: ${body}"
130 |
131 | def slurper = new JsonSlurper()
132 | def result = slurper.parseText(body)
133 |
134 | log.debug "result: ${result}"
135 |
136 | if (result){
137 | log.debug "Computer is up"
138 | sendEvent(name: "switch", value: "on")
139 | }
140 |
141 | log.debug "check temp..."
142 | if (result.containsKey("cpu_temp")) {
143 | log.debug "temp: ${result.cpu_temp}"
144 | log.debug "temp: ${celsiusToFahrenheit(result.cpu_temp.toDouble())} F"
145 | sendEvent(name: "temperature", value: celsiusToFahrenheit(result.cpu_temp.toDouble()))
146 | }
147 |
148 | if (result.containsKey("cpu_perc")) {
149 | sendEvent(name: "cpuPercentage", value: result.cpu_perc)
150 | }
151 |
152 | if (result.containsKey("mem_avail")) {
153 | log.debug "mem_avail: ${result.mem_avail}"
154 | sendEvent(name: "memory", value: result.mem_avail)
155 | }
156 | if (result.containsKey("disk_usage")) {
157 | log.debug "disk_usage: ${result.disk_usage}"
158 | sendEvent(name: "diskUsage", value: result.disk_usage)
159 | }
160 |
161 | }
162 |
163 | // handle commands
164 | def poll() {
165 | log.debug "Executing 'poll'"
166 | sendEvent(name: "switch", value: "off")
167 | getRPiData()
168 | }
169 |
170 | def refresh() {
171 | sendEvent(name: "switch", value: "off")
172 | log.debug "Executing 'refresh'"
173 | getRPiData()
174 | }
175 |
176 | def restart(){
177 | log.debug "Restart was pressed"
178 | sendEvent(name: "switch", value: "off")
179 | def uri = "/api_command/reboot"
180 | postAction(uri)
181 | }
182 |
183 | // Get CPU percentage reading
184 | private getRPiData() {
185 | def uri = "/api_command/smartthings"
186 | postAction(uri)
187 | }
188 |
189 | // ------------------------------------------------------------------
190 |
191 | private postAction(uri){
192 | setDeviceNetworkId(ip,port)
193 |
194 | def userpass = encodeCredentials(username, password)
195 | //log.debug("userpass: " + userpass)
196 |
197 | def headers = getHeader(userpass)
198 | //log.debug("headders: " + headers)
199 |
200 | def hubAction = new physicalgraph.device.HubAction(
201 | method: "POST",
202 | path: uri,
203 | headers: headers
204 | )//,delayAction(1000), refresh()]
205 | log.debug("Executing hubAction on " + getHostAddress())
206 | //log.debug hubAction
207 | hubAction
208 | }
209 |
210 | // ------------------------------------------------------------------
211 | // Helper methods
212 | // ------------------------------------------------------------------
213 |
214 | def parseDescriptionAsMap(description) {
215 | description.split(",").inject([:]) { map, param ->
216 | def nameAndValue = param.split(":")
217 | map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
218 | }
219 | }
220 |
221 |
222 | def toAscii(s){
223 | StringBuilder sb = new StringBuilder();
224 | String ascString = null;
225 | long asciiInt;
226 | for (int i = 0; i < s.length(); i++){
227 | sb.append((int)s.charAt(i));
228 | sb.append("|");
229 | char c = s.charAt(i);
230 | }
231 | ascString = sb.toString();
232 | asciiInt = Long.parseLong(ascString);
233 | return asciiInt;
234 | }
235 |
236 |
237 | private encodeCredentials(username, password){
238 | log.debug "Encoding credentials"
239 | def userpassascii = "${username}:${password}"
240 | def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
241 | //log.debug "ASCII credentials are ${userpassascii}"
242 | //log.debug "Credentials are ${userpass}"
243 | return userpass
244 | }
245 |
246 | private getHeader(userpass){
247 | log.debug "Getting headers"
248 | def headers = [:]
249 | headers.put("HOST", getHostAddress())
250 | headers.put("Authorization", userpass)
251 | //log.debug "Headers are ${headers}"
252 | return headers
253 | }
254 |
255 | private delayAction(long time) {
256 | new physicalgraph.device.HubAction("delay $time")
257 | }
258 |
259 | private setDeviceNetworkId(ip,port){
260 | def iphex = convertIPtoHex(ip)
261 | def porthex = convertPortToHex(port)
262 | device.deviceNetworkId = "$iphex:$porthex"
263 | log.debug "Device Network Id set to ${iphex}:${porthex}"
264 | }
265 |
266 | private getHostAddress() {
267 | return "${ip}:${port}"
268 | }
269 |
270 | private String convertIPtoHex(ipAddress) {
271 | String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
272 | return hex
273 |
274 | }
275 |
276 | private String convertPortToHex(port) {
277 | String hexport = port.toString().format( '%04x', port.toInteger() )
278 | return hexport
279 | }
--------------------------------------------------------------------------------
/devicetypes/ledridge/virtual-motion-detector.src/virtual-motion-detector.groovy:
--------------------------------------------------------------------------------
1 | metadata {
2 | // Automatically generated. Make future change here.
3 | definition (name: "Virtual Motion Detector", namespace: "Ledridge", author: "Ledridge") {
4 | capability "Motion Sensor"
5 | capability "Sensor"
6 |
7 | fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0009,0500"
8 | }
9 |
10 | // simulator metadata
11 | simulator {
12 | status "active": "zone report :: type: 19 value: 0031"
13 | status "inactive": "zone report :: type: 19 value: 0030"
14 | }
15 |
16 | // UI tile definitions
17 | tiles {
18 | standardTile("motion", "device.motion", width: 2, height: 2) {
19 | state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
20 | state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
21 | }
22 |
23 | main "motion"
24 | details "motion"
25 | }
26 | }
27 |
28 | // Parse incoming device messages to generate events
29 | def parse(String description) {
30 | def name = null
31 | def value = description
32 | def descriptionText = null
33 | if (zigbee.isZoneType19(description)) {
34 | name = "motion"
35 | def isActive = zigbee.translateStatusZoneType19(description)
36 | value = isActive ? "active" : "inactive"
37 | descriptionText = isActive ? "${device.displayName} detected motion" : "${device.displayName} motion has stopped"
38 | }
39 |
40 | def result = createEvent(
41 | name: name,
42 | value: value,
43 | descriptionText: descriptionText
44 | )
45 |
46 | log.debug "Parse returned ${result?.descriptionText}"
47 | return result
48 | }
49 |
--------------------------------------------------------------------------------
/devicetypes/ledridge/vtile-dli.src/vtile-dli.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * V Tile device-type for Digital Loggers Inc.
3 | *
4 | * Needed for Multi Switch with Virtual Tiles to create virtual switch tiles in ST for devices that have multiple "switch[x]"
5 | * attributes within them and have on[x], off[x], and cycle[x] commands for each.
6 | * Also has support for device-label inside the name when on or off and polling occurs
7 | *
8 | */
9 | metadata {
10 | definition (name: "vTile_DLI", namespace: "Ledridge", author: "Ledridge") {
11 | capability "Switch"
12 | capability "relaySwitch"
13 | capability "Polling"
14 | capability "Refresh"
15 |
16 | attribute "lastEvent", "string"
17 |
18 | command "cycle"
19 | }
20 | }
21 |
22 | preferences {
23 | tiles {
24 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
25 | state "off", label:'${name}', action: "switch.on", icon:"st.switches.switch.off", backgroundColor: "#DDDDff", nextState: "turningOn"
26 | state "on", label:'${name}', action: "switch.off", icon:"st.switches.switch.on", backgroundColor: "#0088ff", nextState: "turningOff"
27 | state "turningOff", label:'${name}', action: "switch.on", icon:"st.switches.switch.off", backgroundColor: "#FA5882", nextState: "off"
28 | state "turningOn", label:'${name}', action: "switch.on", icon:"st.switches.switch.on", backgroundColor: "#F3F781", nextState: "on"
29 | state "cyclingOff", label:"Turning Off", icon:"st.switches.switch.off", backgroundColor: "#FA5882", nextState: "cyclingOn"
30 | state "cyclingOn", label:"Turning On", icon:"st.switches.switch.on", backgroundColor: "#F3F781", nextState: "on"
31 | }
32 |
33 | standardTile("Cycle", "device.switch", width: 1, height: 2, canChangeIcon: true) {
34 | state "default", action: "cycle", icon: "st.secondary.refresh-icon", backgroundColor: "#0088ff"
35 | }
36 |
37 | valueTile("lastEvent", "device.lastEvent", inactiveLabel: false, width: 3, height: 1, canChangeIcon: false, decoration:"flat") {
38 | state "default", label: 'Last Event: ${currentValue}'}
39 |
40 | main "switch"
41 | details(["switch", "Cycle", "lastEvent"])
42 | }
43 | }
44 |
45 | def parse(desc) {
46 | def results = []
47 | log.debug desc
48 | if(desc=="updated") { log.debug "Device $device.label has been UPDATED"; poll() }
49 | }
50 |
51 | def on() {
52 | sendEvent([name: "switch", value: "on"])
53 | parent.OutletAction(this,"ON")
54 | sendEvent([name: "lastEvent", value: "${df(now())}"])
55 | log.debug "$device.label is On"
56 | }
57 |
58 | def off() {
59 | sendEvent([name: "switch", value: "off"])
60 | parent.OutletAction(this,"OFF")
61 | sendEvent([name: "switch", value: "$device.label"])
62 | sendEvent([name: "lastEvent", value: "${df(now())}"])
63 | log.debug "$device.label is Off"
64 | }
65 |
66 | def cycle() {
67 | log.debug "$device.label is Cycling"
68 | parent.OutletAction(this,"CCL")
69 |
70 | sendEvent([name: "switch", value: "cyclingOff"])
71 | pause(6000)
72 |
73 | sendEvent([name: "switch", value: "cyclingOn"])
74 | pause(5000)
75 |
76 | sendEvent([name: "switch", value: "on"])
77 |
78 | sendEvent([name: "lastEvent", value: "${df(now())}"])
79 | //log.debug "$device.label is Off"
80 | }
81 |
82 | def poll() {
83 | def current = device.currentValue("switch")
84 | log.debug "Polling - $device.label is $current"
85 |
86 | log.debug "This - $this"
87 |
88 | def outletStatus = parent.OutletStatus(this)
89 | log.debug "Polling - Status is $outletStatus"
90 |
91 | def OutletName = parent.OutletName(this)
92 | log.debug "Polling - Name is $OutletName"
93 |
94 | if(!current || current=="off") { sendEvent(name:"switch", value:"$device.label", isStateChange:true, displayed:false) }
95 | }
96 |
97 | def pause(millis) {
98 | def passed = 0
99 | def now = new Date().time
100 | log.debug "pausing... at Now: $now"
101 | /* This loop is an impolite busywait. We need to be given a true sleep() method, please. */
102 | while ( passed < millis ) {
103 | passed = new Date().time - now
104 | }
105 | log.debug "... DONE pausing."
106 | }
107 |
108 | def df(e) {
109 | // * df(e) - Date Format "E"
110 | // * Takes epoch time format and returns Date formatted in current timezone
111 | def locale = getWeatherFeature("geolookup", zip);
112 | def tz = TimeZone.getTimeZone(locale.location.tz_long);
113 | def formatted
114 | if(e) { formatted = new Date(e).format("EEE, MMM d, 'at' hh:mm aaa", tz); return formatted }
115 | }
--------------------------------------------------------------------------------
/devicetypes/ledridge/wifi-370-led-strip-controller.src/wifi-370-led-strip-controller.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Foscam Universal Device
3 | *
4 | * Copyright 2014 skp19
5 | *
6 | */
7 | metadata {
8 | definition (name: "WiFi 370 LED Strip Controller", namespace: "Ledridge", author: "Ledridge") {
9 | capability "Switch Level"
10 | capability "Actuator"
11 | capability "Color Control"
12 | capability "Switch"
13 | capability "Refresh"
14 | capability "Sensor"
15 |
16 | command "setAdjustedColor"
17 | command "refresh"
18 | }
19 |
20 | preferences {
21 | input("ip", "string", title:"Controller IP Address", description: "Controller IP Address", defaultValue: "192.168.1.69", required: true, displayDuringSetup: true)
22 | input("port", "string", title:"Controller Port", description: "Controller Port", defaultValue: 5577 , required: true, displayDuringSetup: true)
23 | input("username", "string", title:"Controller Username", description: "Controller Username", defaultValue: admin, required: true, displayDuringSetup: true)
24 | input("password", "password", title:"Controller Password", description: "Controller Password", defaultValue: nimda, required: true, displayDuringSetup: true)
25 | }
26 |
27 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
28 | state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821"
29 | state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff"
30 | }
31 | standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
32 | state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
33 | }
34 | controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {
35 | state "color", action:"setAdjustedColor"
36 | }
37 | controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
38 | state "level", action:"switch level.setLevel"
39 | }
40 | valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
41 | state "level", label: 'Level ${currentValue}%'
42 | }
43 | controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) {
44 | state "saturation", action:"color control.setSaturation"
45 | }
46 | valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") {
47 | state "saturation", label: 'Sat ${currentValue} '
48 | }
49 | controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) {
50 | state "hue", action:"color control.setHue"
51 | }
52 | valueTile("hue", "device.hue", inactiveLabel: false, decoration: "flat") {
53 | state "hue", label: 'Hue ${currentValue} '
54 | }
55 |
56 | main(["switch"])
57 | details(["switch", "levelSliderControl", "rgbSelector", "refresh"])
58 |
59 | }
60 |
61 | // parse events into attributes
62 | def parse(description) {
63 | log.debug "parse() - $description"
64 | def results = []
65 | def map = description
66 | if (description instanceof String) {
67 | log.debug "Hue Bulb stringToMap - ${map}"
68 | map = stringToMap(description)
69 | }
70 | if (map?.name && map?.value) {
71 | results << createEvent(name: "${map?.name}", value: "${map?.value}")
72 | }
73 | results
74 | }
75 |
76 | // handle commands
77 | def on() {
78 | parent.on(this)
79 | sendEvent(name: "switch", value: "on")
80 | }
81 |
82 | def off() {
83 | parent.off(this)
84 | sendEvent(name: "switch", value: "off")
85 | }
86 |
87 | def nextLevel() {
88 | def level = device.latestValue("level") as Integer ?: 0
89 | if (level <= 100) {
90 | level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
91 | }
92 | else {
93 | level = 25
94 | }
95 | setLevel(level)
96 | }
97 |
98 | def setLevel(percent) {
99 | log.debug "Executing 'setLevel'"
100 | parent.setLevel(this, percent)
101 | sendEvent(name: "level", value: percent)
102 | }
103 |
104 | def setSaturation(percent) {
105 | log.debug "Executing 'setSaturation'"
106 | parent.setSaturation(this, percent)
107 | sendEvent(name: "saturation", value: percent)
108 | }
109 |
110 | def setHue(percent) {
111 | log.debug "Executing 'setHue'"
112 | parent.setHue(this, percent)
113 | sendEvent(name: "hue", value: percent)
114 | }
115 |
116 | def setColor(value) {
117 | log.debug "setColor: ${value}, $this"
118 | parent.setColor(this, value)
119 | if (value.hue) { sendEvent(name: "hue", value: value.hue)}
120 | if (value.saturation) { sendEvent(name: "saturation", value: value.saturation)}
121 | if (value.hex) { sendEvent(name: "color", value: value.hex)}
122 | if (value.level) { sendEvent(name: "level", value: value.level)}
123 | if (value.switch) { sendEvent(name: "switch", value: value.switch)}
124 | }
125 |
126 | def setAdjustedColor(value) {
127 | if (value) {
128 | log.debug "setAdjustedColor: ${value}"
129 | def adjusted = value + [:]
130 | adjusted.hue = adjustOutgoingHue(value.hue)
131 | // Needed because color picker always sends 100
132 | adjusted.level = null
133 | setColor(adjusted)
134 | }
135 | }
136 |
137 | def refresh() {
138 | log.debug "Executing 'refresh'"
139 | parent.poll()
140 | }
141 |
142 | def adjustOutgoingHue(percent) {
143 | def adjusted = percent
144 | if (percent > 31) {
145 | if (percent < 63.0) {
146 | adjusted = percent + (7 * (percent -30 ) / 32)
147 | }
148 | else if (percent < 73.0) {
149 | adjusted = 69 + (5 * (percent - 62) / 10)
150 | }
151 | else {
152 | adjusted = percent + (2 * (100 - percent) / 28)
153 | }
154 | }
155 | log.info "percent: $percent, adjusted: $adjusted"
156 | adjusted
157 | }
158 |
159 |
160 |
161 | //TAKE PICTURE
162 | def take() {
163 | log.debug("Taking Photo")
164 | sendEvent(name: "hubactionMode", value: "s3");
165 | if(hdcamera == "true") {
166 | hubGet("cmd=snapPicture2")
167 | }
168 | else {
169 | hubGet("/snapshot.cgi?")
170 | }
171 | }
172 | //END TAKE PICTURE
173 |
174 | //ALARM ACTIONS
175 | def toggleAlarm() {
176 | log.debug "Toggling Alarm"
177 | if(device.currentValue("alarmStatus") == "on") {
178 | alarmOff()
179 | }
180 | else {
181 | alarmOn()
182 | }
183 | }
184 |
185 | def alarmOn() {
186 | log.debug "Enabling Alarm"
187 | sendEvent(name: "alarmStatus", value: "on");
188 | if(hdcamera == "true") {
189 | hubGet("cmd=setMotionDetectConfig&isEnable=1")
190 | }
191 | else {
192 | hubGet("/set_alarm.cgi?motion_armed=1&")
193 | }
194 | }
195 |
196 | def alarmOff() {
197 | log.debug "Disabling Alarm"
198 | sendEvent(name: "alarmStatus", value: "off");
199 | if(hdcamera == "true") {
200 | hubGet("cmd=setMotionDetectConfig&isEnable=0")
201 | }
202 | else {
203 | hubGet("/set_alarm.cgi?motion_armed=0&")
204 | }
205 | }
206 | //END ALARM ACTIONS
207 |
208 | //LED ACTIONS
209 | //Toggle LED's
210 | def toggleLED() {
211 | log.debug("Toggle LED")
212 |
213 | if(device.currentValue("ledStatus") == "auto") {
214 | ledOn()
215 | }
216 |
217 | else if(device.currentValue("ledStatus") == "on") {
218 | ledOff()
219 | }
220 |
221 | else {
222 | ledAuto()
223 | }
224 | }
225 |
226 | def ledOn() {
227 | log.debug("LED changed to: on")
228 | sendEvent(name: "ledStatus", value: "on");
229 | if(hdcamera == "true") {
230 | delayBetween([hubGet("cmd=setInfraLedConfig&mode=1"), hubGet("cmd=openInfraLed")])
231 | }
232 | else {
233 | hubGet("/decoder_control.cgi?command=95&")
234 | }
235 | }
236 |
237 | def ledOff() {
238 | log.debug("LED changed to: off")
239 | sendEvent(name: "ledStatus", value: "off");
240 | if(hdcamera == "true") {
241 | delayBetween([hubGet("cmd=setInfraLedConfig&mode=1"), hubGet("cmd=closeInfraLed")])
242 | }
243 | else {
244 | hubGet("/decoder_control.cgi?command=94&")
245 | }
246 | }
247 |
248 | def ledAuto() {
249 | log.debug("LED changed to: auto")
250 | sendEvent(name: "ledStatus", value: "auto");
251 | if(hdcamera == "true") {
252 | hubGet("cmd=setInfraLedConfig&mode=0")
253 | }
254 | else {
255 | hubGet("/decoder_control.cgi?command=95&")
256 | }
257 | }
258 | //END LED ACTIONS
259 |
260 | //PRESET ACTIONS
261 | def preset1() {
262 | log.debug("Preset 1 Selected - ${preset1}")
263 | if(hdcamera == "true") {
264 | hubGet("cmd=ptzGotoPresetPoint&name=${preset1}")
265 | }
266 | else {
267 | hubGet("/decoder_control.cgi?command=31&")
268 | }
269 | }
270 |
271 | def preset2() {
272 | log.debug("Preset 2 Selected - ${preset2}")
273 | if(hdcamera == "true") {
274 | hubGet("cmd=ptzGotoPresetPoint&name=${preset2}")
275 | }
276 | else {
277 | hubGet("/decoder_control.cgi?command=33&")
278 | }
279 | }
280 |
281 | def preset3() {
282 | log.debug("Preset 3 Selected - ${preset3}")
283 | if(hdcamera == "true") {
284 | hubGet("cmd=ptzGotoPresetPoint&name=${preset3}")
285 | }
286 | else {
287 | hubGet("/decoder_control.cgi?command=35&")
288 | }
289 | }
290 | //END PRESET ACTIONS
291 |
292 | //CRUISE ACTIONS
293 | def cruisemap1() {
294 | log.debug("Cruise Map 1 Selected - ${cruisemap1}")
295 | if(hdcamera == "true") {
296 | hubGet("cmd=ptzStartCruise&mapName=${cruisemap1}")
297 | }
298 | else {
299 | hubGet("/decoder_control.cgi?command=28&")
300 | }
301 | }
302 |
303 | def cruisemap2() {
304 | log.debug("Cruise Map 2 Selected - ${cruisemap2}")
305 | if(hdcamera == "true") {
306 | hubGet("cmd=ptzStartCruise&mapName=${cruisemap2}")
307 | }
308 | else {
309 | hubGet("/decoder_control.cgi?command=26&")
310 | }
311 | }
312 |
313 | def stopCruise() {
314 | log.debug("Stop Cruise")
315 | if(hdcamera == "true") {
316 | hubGet("cmd=ptzStopRun")
317 | }
318 | else {
319 | delayBetween([hubGet("/decoder_control.cgi?command=29&"), hubGet("/decoder_control.cgi?command=27&")])
320 | }
321 | }
322 | //END CRUISE ACTIONS
323 |
324 | //PTZ CONTROLS
325 | def left() {
326 | if(hdcamera == "true") {
327 | delayBetween([hubGet("cmd=ptzMoveLeft"), hubGet("cmd=ptzStopRun")])
328 | }
329 | else {
330 | if(mirror == "true") {
331 | hubGet("/decoder_control.cgi?command=4&onestep=1&")
332 | }
333 | else {
334 | hubGet("/decoder_control.cgi?command=6&onestep=1&")
335 | }
336 | }
337 | }
338 |
339 | def right() {
340 | if(hdcamera == "true") {
341 | delayBetween([hubGet("cmd=ptzMoveRight"), hubGet("cmd=ptzStopRun")])
342 | }
343 | else {
344 | if(mirror == "true") {
345 | hubGet("/decoder_control.cgi?command=6&onestep=1&")
346 | }
347 | else {
348 | hubGet("/decoder_control.cgi?command=4&onestep=1&")
349 | }
350 | }
351 | }
352 |
353 | def up() {
354 | if(hdcamera == "true") {
355 | delayBetween([hubGet("cmd=ptzMoveUp"), hubGet("cmd=ptzStopRun")])
356 | }
357 | else {
358 | if(flip == "true") {
359 | hubGet("/decoder_control.cgi?command=2&onestep=1&")
360 | }
361 | else {
362 | hubGet("/decoder_control.cgi?command=0&onestep=1&")
363 | }
364 | }
365 | }
366 |
367 | def down() {
368 | if(hdcamera == "true") {
369 | delayBetween([hubGet("cmd=ptzMoveDown"), hubGet("cmd=ptzStopRun")])
370 | }
371 | else {
372 | if(flip == "true") {
373 | hubGet("/decoder_control.cgi?command=0&onestep=1&")
374 | }
375 | else {
376 | hubGet("/decoder_control.cgi?command=2&onestep=1&")
377 | }
378 | }
379 | }
380 | //END PTZ CONTROLS
381 |
382 | def poll() {
383 |
384 | sendEvent(name: "hubactionMode", value: "local");
385 | //Poll Motion Alarm Status and IR LED Mode
386 | if(hdcamera == "true") {
387 | delayBetween([hubGet("cmd=getMotionDetectConfig"), hubGet("cmd=getInfraLedConfig")])
388 | }
389 | else {
390 | hubGet("/get_params.cgi?")
391 | }
392 | }
393 |
394 | private getLogin() {
395 | if(hdcamera == "true") {
396 | return "usr=${username}&pwd=${password}&"
397 | }
398 | else {
399 | return "user=${username}&pwd=${password}"
400 | }
401 | }
402 |
403 | private hubGet(def apiCommand) {
404 | //Setting Network Device Id
405 | def iphex = convertIPtoHex(ip)
406 | def porthex = convertPortToHex(port)
407 | device.deviceNetworkId = "$iphex:$porthex"
408 | log.debug "Device Network Id set to ${iphex}:${porthex}"
409 |
410 | log.debug("Executing hubaction on " + getHostAddress())
411 | def uri = ""
412 | if(hdcamera == "true") {
413 | uri = "/cgi-bin/CGIProxy.fcgi?" + getLogin() + apiCommand
414 | }
415 | else {
416 | uri = apiCommand + getLogin()
417 | }
418 | log.debug uri
419 | def hubAction = new physicalgraph.device.HubAction(
420 | method: "GET",
421 | path: uri,
422 | headers: [HOST:getHostAddress()]
423 | )
424 | if(device.currentValue("hubactionMode") == "s3") {
425 | hubAction.options = [outputMsgToS3:true]
426 | sendEvent(name: "hubactionMode", value: "local");
427 | }
428 | hubAction
429 | }
430 |
431 | //Parse events into attributes
432 | def parse(String description) {
433 | log.debug "Parsing '${description}'"
434 |
435 | def map = [:]
436 | def retResult = []
437 | def descMap = parseDescriptionAsMap(description)
438 |
439 | //Image
440 | if (descMap["bucket"] && descMap["key"]) {
441 | putImageInS3(descMap)
442 | }
443 |
444 | //Status Polling
445 | else if (descMap["headers"] && descMap["body"]) {
446 | def body = new String(descMap["body"].decodeBase64())
447 | if(hdcamera == "true") {
448 | def langs = new XmlSlurper().parseText(body)
449 |
450 | def motionAlarm = "$langs.isEnable"
451 | def ledMode = "$langs.mode"
452 |
453 | //Get Motion Alarm Status
454 | if(motionAlarm == "0") {
455 | log.info("Polled: Alarm Off")
456 | sendEvent(name: "alarmStatus", value: "off");
457 | }
458 | else if(motionAlarm == "1") {
459 | log.info("Polled: Alarm On")
460 | sendEvent(name: "alarmStatus", value: "on");
461 | }
462 |
463 | //Get IR LED Mode
464 | if(ledMode == "0") {
465 | log.info("Polled: LED Mode Auto")
466 | sendEvent(name: "ledStatus", value: "auto")
467 | }
468 | else if(ledMode == "1") {
469 | log.info("Polled: LED Mode Manual")
470 | sendEvent(name: "ledStatus", value: "manual")
471 | }
472 | }
473 | else {
474 | if(body.find("alarm_motion_armed=0")) {
475 | log.info("Polled: Alarm Off")
476 | sendEvent(name: "alarmStatus", value: "off")
477 | }
478 | else if(body.find("alarm_motion_armed=1")) {
479 | log.info("Polled: Alarm On")
480 | sendEvent(name: "alarmStatus", value: "on")
481 | }
482 | //The API does not provide a way to poll for LED status on 8xxx series at the moment
483 | }
484 | }
485 | }
486 |
487 | def parseDescriptionAsMap(description) {
488 | description.split(",").inject([:]) { map, param ->
489 | def nameAndValue = param.split(":")
490 | map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
491 | }
492 | }
493 |
494 | def putImageInS3(map) {
495 |
496 | def s3ObjectContent
497 |
498 | try {
499 | def imageBytes = getS3Object(map.bucket, map.key + ".jpg")
500 |
501 | if(imageBytes)
502 | {
503 | s3ObjectContent = imageBytes.getObjectContent()
504 | def bytes = new ByteArrayInputStream(s3ObjectContent.bytes)
505 | storeImage(getPictureName(), bytes)
506 | }
507 | }
508 | catch(Exception e) {
509 | log.error e
510 | }
511 | finally {
512 | //Explicitly close the stream
513 | if (s3ObjectContent) { s3ObjectContent.close() }
514 | }
515 | }
516 |
517 | private getPictureName() {
518 | def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')
519 | "image" + "_$pictureUuid" + ".jpg"
520 | }
521 |
522 | private getHostAddress() {
523 | return "${ip}:${port}"
524 | }
525 |
526 | private String convertIPtoHex(ipAddress) {
527 | String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
528 | return hex
529 |
530 | }
531 |
532 | private String convertPortToHex(port) {
533 | String hexport = port.toString().format( '%04x', port.toInteger() )
534 | return hexport
535 | }
--------------------------------------------------------------------------------
/devicetypes/mmaxwell/remoteczfm80.src/remoteczfm80.groovy:
--------------------------------------------------------------------------------
1 | /* Remotec ZFM-80 specific device V1.2
2 | *
3 | * Variation of the stock SmartThings Relay Switch
4 | * --auto re-configure after setting preferences
5 | * --preference settings for switch type and automatic shutoff features.
6 | *
7 | *
8 | * Mike Maxwell
9 | * madmax98087@yahoo.com
10 | * 2015-02-16
11 | *
12 | change log
13 | 2015-02-16 added delay between configuration changes, helps with devices further away from the hub.
14 | 2015-02-21 fixed null error on initial install
15 | */
16 |
17 | metadata {
18 |
19 | definition (name: "remotecZFM80", namespace: "mmaxwell", author: "mike maxwell") {
20 | capability "Actuator"
21 | capability "Door Control"
22 | capability "Switch"
23 | capability "Momentary"
24 | capability "Polling"
25 | capability "Refresh"
26 | capability "Sensor"
27 | capability "Contact Sensor"
28 | capability "Relay Switch"
29 |
30 | fingerprint deviceId: "0x1003", inClusters: "0x20, 0x25, 0x27, 0x72, 0x86, 0x70, 0x85"
31 | }
32 | preferences {
33 | input name: "param1", type: "enum", title: "Set external switch mode:", description: "Switch type", required: true, options:["Disabled","Momentary NO","Momentary NC","Toggle NO","Toggle NC"]
34 | input name: "param2", type: "enum", title: "Auto shutoff minutes:", description: "Minutes?", required: false, options:["Never","1","5","30","60","90","120","240"]
35 | }
36 |
37 | // simulator metadata
38 | simulator {
39 | status "on": "command: 2003, payload: FF"
40 | status "off": "command: 2003, payload: 00"
41 |
42 | // reply messages
43 | reply "2001FF,delay 100,2502": "command: 2503, payload: FF"
44 | reply "200100,delay 100,2502": "command: 2503, payload: 00"
45 | }
46 |
47 | // tile definitions
48 | tiles {
49 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
50 | state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
51 | state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
52 | }
53 | standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
54 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
55 | }
56 |
57 | main "switch"
58 | details(["switch","refresh"])
59 | }
60 | }
61 |
62 | def installed() {
63 | zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
64 | }
65 |
66 | def parse(String description) {
67 | def result = null
68 | def cmd = zwave.parse(description, [0x20: 1, 0x70: 1])
69 | if (cmd) {
70 | result = createEvent(zwaveEvent(cmd))
71 | }
72 | if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) {
73 | result = [result, response(zwave.basicV1.basicGet())]
74 | log.debug "Was hailed: requesting state update"
75 | } else {
76 | log.debug "Parse returned ${result?.descriptionText}"
77 | }
78 | return result
79 | }
80 |
81 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
82 | [name: "switch", value: cmd.value ? "on" : "off", type: "physical"]
83 | }
84 |
85 | def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
86 | [name: "switch", value: cmd.value ? "on" : "off", type: "digital"]
87 | }
88 |
89 | //def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
90 | // def value = "when off"
91 | // if (cmd.configurationValue[0] == 1) {value = "when on"}
92 | // if (cmd.configurationValue[0] == 2) {value = "never"}
93 | // [name: "indicatorStatus", value: value, display: false]
94 | //}
95 |
96 | def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
97 | [name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false]
98 | }
99 |
100 | def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
101 | if (state.manufacturer != cmd.manufacturerName) {
102 | updateDataValue("manufacturer", cmd.manufacturerName)
103 | }
104 |
105 | final relays = [
106 | //[manufacturerId:0x0113, productTypeId: 0x5246, productId: 0x3133, productName: "Evolve LFM-20"],
107 | //[manufacturerId:0x0113, productTypeId: 0x5246, productId: 0x3133, productName: "Linear FS20Z-1"],
108 | [manufacturerId:0x5254, productTypeId: 0x8000, productId: 0x0002, productName: "Remotec ZFM-80"]
109 | ]
110 |
111 | def productName = null
112 | for (it in relays) {
113 | if (it.manufacturerId == cmd.manufacturerId && it.productTypeId == cmd.productTypeId && it.productId == cmd.productId) {
114 | productName = it.productName
115 | break
116 | }
117 | }
118 |
119 | if (productName) {
120 | //log.debug "Relay found: $productName"
121 | updateDataValue("productName", productName)
122 | }
123 | else {
124 | //log.debug "Not a relay, retyping to Z-Wave Switch"
125 | setDeviceType("Z-Wave Switch")
126 | }
127 | [name: "manufacturer", value: cmd.manufacturerName]
128 | }
129 |
130 | def zwaveEvent(physicalgraph.zwave.Command cmd) {
131 | // Handles all Z-Wave commands we aren't interested in
132 | [:]
133 | }
134 |
135 | def on() {
136 | delayBetween([
137 | zwave.basicV1.basicSet(value: 0xFF).format(),
138 | zwave.switchBinaryV1.switchBinaryGet().format()
139 | ])
140 | }
141 |
142 | def off() {
143 | delayBetween([
144 | zwave.basicV1.basicSet(value: 0x00).format(),
145 | zwave.switchBinaryV1.switchBinaryGet().format()
146 | ])
147 | }
148 |
149 | def poll() {
150 | zwave.switchBinaryV1.switchBinaryGet().format()
151 | }
152 |
153 | def refresh() {
154 | delayBetween([
155 | zwave.switchBinaryV1.switchBinaryGet().format(),
156 | zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
157 | ])
158 | }
159 | //capture preference changes
160 | def updated() {
161 | //log.debug "before settings: ${settings.inspect()}, state: ${state.inspect()}"
162 | //"Disabled","Momentary NO","Momentary NC","Toggle NO","Toggle NC"
163 |
164 | //external switch function settings
165 | def Short p1 = 0
166 | switch (settings.param1) {
167 | case "Disabled":
168 | p1 = 0
169 | break
170 | case "Momentary NO":
171 | p1 = 1
172 | break
173 | case "Momentary NC":
174 | p1 = 2
175 | break
176 | case "Toggle NO":
177 | p1 = 3
178 | break
179 | case "Toggle NC":
180 | p1 = 4
181 | break
182 | }
183 |
184 |
185 | //auto off
186 | def Short p2 = 0
187 | if ("${settings.param2}" == "Never") {
188 | p2 = 0
189 | } else {
190 | p2 = (settings.param2 ?: 0).toInteger()
191 | }
192 |
193 | if (p1 != state.param1) {
194 | state.param1 = p1
195 | return response(zwave.configurationV1.configurationSet(parameterNumber: 1, size: 1, configurationValue: [p1]).format())
196 | }
197 |
198 | if (p2 != state.param2) {
199 | state.param2 = p2
200 | if (p2 == 0) {
201 | return response (delayBetween([
202 | zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, configurationValue: [0]).format(),
203 | zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, configurationValue: [0]).format(),
204 | zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, configurationValue: [0]).format()
205 | ]))
206 | } else {
207 | return response (delayBetween([
208 | zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, configurationValue: [p2]).format(),
209 | zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, configurationValue: [232]).format(),
210 | zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, configurationValue: [0]).format()
211 | ]))
212 | }
213 | }
214 |
215 | //log.debug "after settings: ${settings.inspect()}, state: ${state.inspect()}"
216 | }
217 |
218 | def configure() {
219 | delayBetween([
220 | zwave.configurationV1.configurationSet(parameterNumber: 1, size: 1, configurationValue: [3]).format(),
221 | zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, configurationValue: [0]).format(),
222 | zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, configurationValue: [0]).format(),
223 | zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, configurationValue: [0]).format()
224 | ])
225 | }
--------------------------------------------------------------------------------
/devicetypes/ms-w-vts/vtile-ms.src/vtile-ms.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * V Tile device-type for ms_w_vts
3 | *
4 | * Needed for Multi Switch with Virtual Tiles to create virtual switch tiles in ST for devices that have multiple "switch[x]"
5 | * attributes within them and have on[x] and off[x] commands for each (fairly common device-types)
6 | * Also has support for device-label inside the name when on or off and polling occurs
7 | * Copyright 2014 Cooper Lee
8 | *
9 | */
10 | metadata {
11 | definition (name: "vTile_ms", namespace: "ms_w_vts", author: "Cooper Lee") {
12 | capability "Switch"
13 | capability "relaySwitch"
14 | capability "Polling"
15 | capability "Refresh"
16 |
17 | attribute "lastOn", "string"
18 | attribute "lastOff", "string"
19 | }
20 | }
21 |
22 | preferences {
23 | tiles {
24 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
25 | //state "name", label: '${currentValue}', action: "switch.on", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#DDDDff", nextState: "turningOn"
26 | state "off", action: "switch.on", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#DDDDff", nextState: "turningOn"
27 | state "on", action: "switch.off", icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#0088ff", nextState: "turningOff"
28 | state "turningOff", iconLabel:"http://cdn.flaticon.com/png/256/56413.png" , icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#FA5882", nextState: "on"
29 | state "turningOn", iconLabel:"http://cdn.flaticon.com/png/256/56498.png" , icon: "http://cdn.flaticon.com/png/256/56724.png", backgroundColor: "#F3F781", nextState: "off"
30 | }
31 |
32 | valueTile("lastOn", "device.lastOn", inactiveLabel: false, width: 3, height: 1, canChangeIcon: false, decoration:"flat") {
33 | state "default", label: 'Last On: ${currentValue}'}
34 |
35 | valueTile("lastOff", "device.lastOff", inactiveLabel: false, width: 3, height: 1, canChangeIcon: false, decoration:"flat") {
36 | state "default", label: 'Last Off: ${currentValue}'}
37 |
38 | main "switch"
39 | details(["switch", "lastOn", "lastOff"])
40 | }
41 | }
42 |
43 | def parse(desc) {
44 | def results = []
45 | if(desc=="updated") { log.debug "Device $device.label has been UPDATED"; poll() }
46 | }
47 |
48 | def on() {
49 | sendEvent([name: "switch", value: "on"])
50 | parent.on(this)
51 | sendEvent([name: "lastOn", value: "${df(now())}"])
52 | log.debug "$device.label is On"
53 | }
54 |
55 | def off() {
56 | sendEvent([name: "switch", value: "off"])
57 | parent.off(this)
58 | sendEvent([name: "switch", value: "$device.label"])
59 | sendEvent([name: "lastOff", value: "${df(now())}"])
60 | log.debug "$device.label is Off"
61 | }
62 |
63 | def poll() {
64 | def current = device.currentValue("switch")
65 | log.debug "Polling - $device.label is $current"
66 | if(!current || current=="off") { sendEvent(name:"switch", value:"$device.label", isStateChange:true, displayed:false) }
67 | }
68 |
69 | def df(e) {
70 | // * df(e) - Date Format "E"
71 | // * Takes epoch time format and returns Date formatted in current timezone
72 | // * Copyright 2014 Cooper Lee
73 | def locale = getWeatherFeature("geolookup", zip); def tz = TimeZone.getTimeZone(locale.location.tz_long); def formatted
74 | if(e) { formatted = new Date(e).format("EEE, MMM d, 'at' hh:mm aaa", tz); return formatted }
75 | }
--------------------------------------------------------------------------------
/devicetypes/skp19/foscam-universal-device.src/foscam-universal-device.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Foscam Universal Device
3 | *
4 | * Copyright 2014 skp19
5 | *
6 | */
7 | metadata {
8 | definition (name: "Foscam Universal Device", namespace: "skp19", author: "skp19") {
9 | capability "Polling"
10 | capability "Image Capture"
11 |
12 | attribute "alarmStatus", "string"
13 | attribute "ledStatus", "string"
14 | attribute "hubactionMode", "string"
15 |
16 | command "alarmOn"
17 | command "alarmOff"
18 | command "toggleAlarm"
19 | command "toggleLED"
20 |
21 | command "ledOn"
22 | command "ledOff"
23 | command "ledAuto"
24 |
25 | command "left"
26 | command "right"
27 | command "up"
28 | command "down"
29 |
30 | command "cruisemap1"
31 | command "cruisemap2"
32 | command "stopCruise"
33 |
34 | command "preset1"
35 | command "preset2"
36 | command "preset3"
37 | }
38 |
39 | preferences {
40 | input("ip", "string", title:"Camera IP Address", description: "Camera IP Address", required: true, displayDuringSetup: true)
41 | input("port", "string", title:"Camera Port", description: "Camera Port", defaultValue: 80 , required: true, displayDuringSetup: true)
42 | input("username", "string", title:"Camera Username", description: "Camera Username", required: true, displayDuringSetup: true)
43 | input("password", "password", title:"Camera Password", description: "Camera Password", required: true, displayDuringSetup: true)
44 | input("hdcamera", "bool", title:"HD Foscam Camera? (9xxx Series)", description: "Type of Foscam Camera", required: true, displayDuringSetup: true)
45 | input("mirror", "bool", title:"Mirror? (Not required for HD cameras)", description: "Camera Mirrored?")
46 | input("flip", "bool", title:"Flip? (Not required for HD cameras)", description: "Camera Flipped?")
47 | input("preset1", "text", title: "Preset 1 (For HD cameras only)", description: "Name of your first preset position")
48 | input("preset2", "text", title: "Preset 2 (For HD cameras only)", description: "Name of your second preset position")
49 | input("preset3", "text", title: "Preset 3 (For HD cameras only)", description: "Name of your third preset position")
50 | input("cruisemap1", "text", title: "Cruise Map 1 (For HD cameras only. Non-HD cameras will default to Horizontal.)", description: "Name of your first cruise map", defaultValue: "Horizontal")
51 | input("cruisemap2", "text", title: "Cruise Map 2 (For HD cameras only. Non-HD cameras will default to Vertical.)", description: "Name of your second cruise map", defaultValue: "Vertical")
52 | }
53 |
54 | tiles {
55 | carouselTile("cameraDetails", "device.image", width: 3, height: 2) { }
56 |
57 | standardTile("camera", "device.alarmStatus", width: 1, height: 1, canChangeIcon: true, inactiveLabel: true, canChangeBackground: true) {
58 | state "off", label: "off", action: "toggleAlarm", icon: "st.camera.dropcam-centered", backgroundColor: "#FFFFFF"
59 | state "on", label: "on", action: "toggleAlarm", icon: "st.camera.dropcam-centered", backgroundColor: "#53A7C0"
60 | }
61 |
62 | standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
63 | state "take", label: "Take", action: "Image Capture.take", icon: "st.camera.camera", backgroundColor: "#FFFFFF", nextState:"taking"
64 | state "taking", label:'Taking', action: "", icon: "st.camera.take-photo", backgroundColor: "#53a7c0"
65 | state "image", label: "Take", action: "Image Capture.take", icon: "st.camera.camera", backgroundColor: "#FFFFFF", nextState:"taking"
66 | }
67 |
68 | standardTile("alarmStatus", "device.alarmStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
69 | state "off", label: "off", action: "toggleAlarm", icon: "st.quirky.spotter.quirky-spotter-sound-off", backgroundColor: "#FFFFFF"
70 | state "on", label: "on", action: "toggleAlarm", icon: "st.quirky.spotter.quirky-spotter-sound-on", backgroundColor: "#53A7C0"
71 | }
72 |
73 | standardTile("ledStatus", "device.ledStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
74 | state "auto", label: "auto", action: "toggleLED", icon: "st.Lighting.light13", backgroundColor: "#53A7C0"
75 | state "off", label: "off", action: "toggleLED", icon: "st.Lighting.light13", backgroundColor: "#FFFFFF"
76 | state "on", label: "on", action: "toggleLED", icon: "st.Lighting.light11", backgroundColor: "#FFFF00"
77 | state "manual", label: "manual", action: "toggleLED", icon: "st.Lighting.light13", backgroundColor: "#FFFF00"
78 | }
79 |
80 | standardTile("ledAuto", "device.ledStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
81 | state "auto", label: "auto", action: "ledAuto", icon: "st.Lighting.light11", backgroundColor: "#53A7C0"
82 | state "off", label: "auto", action: "ledAuto", icon: "st.Lighting.light13", backgroundColor: "#FFFFFF"
83 | state "on", label: "auto", action: "ledAuto", icon: "st.Lighting.light13", backgroundColor: "#FFFFFF"
84 | state "manual", label: "auto", action: "ledAuto", icon: "st.Lighting.light13", backgroundColor: "#FFFFFF"
85 | }
86 |
87 | standardTile("ledOn", "device.ledStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
88 | state "auto", label: "on", action: "ledOn", icon: "st.Lighting.light11", backgroundColor: "#FFFFFF"
89 | state "off", label: "on", action: "ledOn", icon: "st.Lighting.light11", backgroundColor: "#FFFFFF"
90 | state "on", label: "on", action: "ledOn", icon: "st.Lighting.light11", backgroundColor: "#FFFF00"
91 | state "manual", label: "on", action: "ledOn", icon: "st.Lighting.light11", backgroundColor: "#00FF00"
92 | }
93 |
94 | standardTile("ledOff", "device.ledStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
95 | state "auto", label: "off", action: "ledOff", icon: "st.Lighting.light13", backgroundColor: "#FFFFFF"
96 | state "off", label: "off", action: "ledOff", icon: "st.Lighting.light13", backgroundColor: "#53A7C0"
97 | state "on", label: "off", action: "ledOff", icon: "st.Lighting.light13", backgroundColor: "#FFFFFF"
98 | state "manual", label: "off", action: "ledOff", icon: "st.Lighting.light13", backgroundColor: "#00FF00"
99 | }
100 |
101 | standardTile("preset1", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
102 | state "preset1", label: "preset 1", action: "preset1", icon: ""
103 | }
104 |
105 | standardTile("preset2", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
106 | state "preset2", label: "preset 2", action: "preset2", icon: ""
107 | }
108 |
109 | standardTile("preset3", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
110 | state "preset3", label: "preset 3", action: "preset3", icon: ""
111 | }
112 |
113 | standardTile("cruisemap1", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
114 | state "cruisemap1", label: "Cruise Map 1", action: "cruisemap1", icon: ""
115 | }
116 |
117 | standardTile("cruisemap2", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
118 | state "cruisemap2", label: "Cruise Map 2", action: "cruisemap2", icon: ""
119 | }
120 |
121 | standardTile("stopcruise", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
122 | state "stopcruise", label: "Stop Cruise", action: "stopCruise", icon: ""
123 | }
124 |
125 | standardTile("left", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
126 | state "left", label: "left", action: "left", icon: ""
127 | }
128 |
129 | standardTile("right", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
130 | state "right", label: "right", action: "right", icon: ""
131 | }
132 |
133 | standardTile("up", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
134 | state "up", label: "up", action: "up", icon: "st.thermostat.thermostat-up"
135 | }
136 |
137 | standardTile("down", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
138 | state "down", label: "down", action: "down", icon: "st.thermostat.thermostat-down"
139 | }
140 |
141 | standardTile("stop", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
142 | state "stop", label: "", action: "stopCruise", icon: "st.sonos.stop-btn"
143 | }
144 |
145 | standardTile("refresh", "device.alarmStatus", inactiveLabel: false, decoration: "flat") {
146 | state "refresh", action:"polling.poll", icon:"st.secondary.refresh"
147 | }
148 |
149 | standardTile("blank", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
150 | state "blank", label: "", action: "", icon: "", backgroundColor: "#FFFFFF"
151 | }
152 |
153 | main "camera"
154 | //details(["cameraDetails", "take", "blank", "alarmStatus", "ledAuto", "ledOn", "ledOff", "refresh"]) //**Uncomment this line and comment out the next line to hide the PTZ controls
155 | details(["cameraDetails", "take", "blank", "alarmStatus", "ledAuto", "ledOn", "ledOff", "preset1", "preset2", "preset3", "cruisemap1", "cruisemap2", "stopcruise", "blank", "up", "blank", "left", "stop", "right", "blank", "down", "blank", "refresh"])
156 | }
157 | }
158 |
159 | //TAKE PICTURE
160 | def take() {
161 | log.debug("Taking Photo")
162 | sendEvent(name: "hubactionMode", value: "s3");
163 | if(hdcamera == "true") {
164 | hubGet("cmd=snapPicture2")
165 | }
166 | else {
167 | hubGet("/snapshot.cgi?")
168 | }
169 | }
170 | //END TAKE PICTURE
171 |
172 | //ALARM ACTIONS
173 | def toggleAlarm() {
174 | log.debug "Toggling Alarm"
175 | if(device.currentValue("alarmStatus") == "on") {
176 | alarmOff()
177 | }
178 | else {
179 | alarmOn()
180 | }
181 | }
182 |
183 | def alarmOn() {
184 | log.debug "Enabling Alarm"
185 | sendEvent(name: "alarmStatus", value: "on");
186 | if(hdcamera == "true") {
187 | hubGet("cmd=setMotionDetectConfig&isEnable=1")
188 | }
189 | else {
190 | hubGet("/set_alarm.cgi?motion_armed=1&")
191 | }
192 | }
193 |
194 | def alarmOff() {
195 | log.debug "Disabling Alarm"
196 | sendEvent(name: "alarmStatus", value: "off");
197 | if(hdcamera == "true") {
198 | hubGet("cmd=setMotionDetectConfig&isEnable=0")
199 | }
200 | else {
201 | hubGet("/set_alarm.cgi?motion_armed=0&")
202 | }
203 | }
204 | //END ALARM ACTIONS
205 |
206 | //LED ACTIONS
207 | //Toggle LED's
208 | def toggleLED() {
209 | log.debug("Toggle LED")
210 |
211 | if(device.currentValue("ledStatus") == "auto") {
212 | ledOn()
213 | }
214 |
215 | else if(device.currentValue("ledStatus") == "on") {
216 | ledOff()
217 | }
218 |
219 | else {
220 | ledAuto()
221 | }
222 | }
223 |
224 | def ledOn() {
225 | log.debug("LED changed to: on")
226 | sendEvent(name: "ledStatus", value: "on");
227 | if(hdcamera == "true") {
228 | delayBetween([hubGet("cmd=setInfraLedConfig&mode=1"), hubGet("cmd=openInfraLed")])
229 | }
230 | else {
231 | hubGet("/decoder_control.cgi?command=95&")
232 | }
233 | }
234 |
235 | def ledOff() {
236 | log.debug("LED changed to: off")
237 | sendEvent(name: "ledStatus", value: "off");
238 | if(hdcamera == "true") {
239 | delayBetween([hubGet("cmd=setInfraLedConfig&mode=1"), hubGet("cmd=closeInfraLed")])
240 | }
241 | else {
242 | hubGet("/decoder_control.cgi?command=94&")
243 | }
244 | }
245 |
246 | def ledAuto() {
247 | log.debug("LED changed to: auto")
248 | sendEvent(name: "ledStatus", value: "auto");
249 | if(hdcamera == "true") {
250 | hubGet("cmd=setInfraLedConfig&mode=0")
251 | }
252 | else {
253 | hubGet("/decoder_control.cgi?command=95&")
254 | }
255 | }
256 | //END LED ACTIONS
257 |
258 | //PRESET ACTIONS
259 | def preset1() {
260 | log.debug("Preset 1 Selected - ${preset1}")
261 | if(hdcamera == "true") {
262 | hubGet("cmd=ptzGotoPresetPoint&name=${preset1}")
263 | }
264 | else {
265 | hubGet("/decoder_control.cgi?command=31&")
266 | }
267 | }
268 |
269 | def preset2() {
270 | log.debug("Preset 2 Selected - ${preset2}")
271 | if(hdcamera == "true") {
272 | hubGet("cmd=ptzGotoPresetPoint&name=${preset2}")
273 | }
274 | else {
275 | hubGet("/decoder_control.cgi?command=33&")
276 | }
277 | }
278 |
279 | def preset3() {
280 | log.debug("Preset 3 Selected - ${preset3}")
281 | if(hdcamera == "true") {
282 | hubGet("cmd=ptzGotoPresetPoint&name=${preset3}")
283 | }
284 | else {
285 | hubGet("/decoder_control.cgi?command=35&")
286 | }
287 | }
288 | //END PRESET ACTIONS
289 |
290 | //CRUISE ACTIONS
291 | def cruisemap1() {
292 | log.debug("Cruise Map 1 Selected - ${cruisemap1}")
293 | if(hdcamera == "true") {
294 | hubGet("cmd=ptzStartCruise&mapName=${cruisemap1}")
295 | }
296 | else {
297 | hubGet("/decoder_control.cgi?command=28&")
298 | }
299 | }
300 |
301 | def cruisemap2() {
302 | log.debug("Cruise Map 2 Selected - ${cruisemap2}")
303 | if(hdcamera == "true") {
304 | hubGet("cmd=ptzStartCruise&mapName=${cruisemap2}")
305 | }
306 | else {
307 | hubGet("/decoder_control.cgi?command=26&")
308 | }
309 | }
310 |
311 | def stopCruise() {
312 | log.debug("Stop Cruise")
313 | if(hdcamera == "true") {
314 | hubGet("cmd=ptzStopRun")
315 | }
316 | else {
317 | delayBetween([hubGet("/decoder_control.cgi?command=29&"), hubGet("/decoder_control.cgi?command=27&")])
318 | }
319 | }
320 | //END CRUISE ACTIONS
321 |
322 | //PTZ CONTROLS
323 | def left() {
324 | if(hdcamera == "true") {
325 | delayBetween([hubGet("cmd=ptzMoveLeft"), hubGet("cmd=ptzStopRun")])
326 | }
327 | else {
328 | if(mirror == "true") {
329 | hubGet("/decoder_control.cgi?command=4&onestep=1&")
330 | }
331 | else {
332 | hubGet("/decoder_control.cgi?command=6&onestep=1&")
333 | }
334 | }
335 | }
336 |
337 | def right() {
338 | if(hdcamera == "true") {
339 | delayBetween([hubGet("cmd=ptzMoveRight"), hubGet("cmd=ptzStopRun")])
340 | }
341 | else {
342 | if(mirror == "true") {
343 | hubGet("/decoder_control.cgi?command=6&onestep=1&")
344 | }
345 | else {
346 | hubGet("/decoder_control.cgi?command=4&onestep=1&")
347 | }
348 | }
349 | }
350 |
351 | def up() {
352 | if(hdcamera == "true") {
353 | delayBetween([hubGet("cmd=ptzMoveUp"), hubGet("cmd=ptzStopRun")])
354 | }
355 | else {
356 | if(flip == "true") {
357 | hubGet("/decoder_control.cgi?command=2&onestep=1&")
358 | }
359 | else {
360 | hubGet("/decoder_control.cgi?command=0&onestep=1&")
361 | }
362 | }
363 | }
364 |
365 | def down() {
366 | if(hdcamera == "true") {
367 | delayBetween([hubGet("cmd=ptzMoveDown"), hubGet("cmd=ptzStopRun")])
368 | }
369 | else {
370 | if(flip == "true") {
371 | hubGet("/decoder_control.cgi?command=0&onestep=1&")
372 | }
373 | else {
374 | hubGet("/decoder_control.cgi?command=2&onestep=1&")
375 | }
376 | }
377 | }
378 | //END PTZ CONTROLS
379 |
380 | def poll() {
381 |
382 | sendEvent(name: "hubactionMode", value: "local");
383 | //Poll Motion Alarm Status and IR LED Mode
384 | if(hdcamera == "true") {
385 | delayBetween([hubGet("cmd=getMotionDetectConfig"), hubGet("cmd=getInfraLedConfig")])
386 | }
387 | else {
388 | hubGet("/get_params.cgi?")
389 | }
390 | }
391 |
392 | private getLogin() {
393 | if(hdcamera == "true") {
394 | return "usr=${username}&pwd=${password}&"
395 | }
396 | else {
397 | return "user=${username}&pwd=${password}"
398 | }
399 | }
400 |
401 | private hubGet(def apiCommand) {
402 | //Setting Network Device Id
403 | def iphex = convertIPtoHex(ip)
404 | def porthex = convertPortToHex(port)
405 | device.deviceNetworkId = "$iphex:$porthex"
406 | log.debug "Device Network Id set to ${iphex}:${porthex}"
407 |
408 | log.debug("Executing hubaction on " + getHostAddress())
409 | def uri = ""
410 | if(hdcamera == "true") {
411 | uri = "/cgi-bin/CGIProxy.fcgi?" + getLogin() + apiCommand
412 | }
413 | else {
414 | uri = apiCommand + getLogin()
415 | }
416 | log.debug uri
417 | def hubAction = new physicalgraph.device.HubAction(
418 | method: "GET",
419 | path: uri,
420 | headers: [HOST:getHostAddress()]
421 | )
422 | if(device.currentValue("hubactionMode") == "s3") {
423 | hubAction.options = [outputMsgToS3:true]
424 | sendEvent(name: "hubactionMode", value: "local");
425 | }
426 | hubAction
427 | }
428 |
429 | //Parse events into attributes
430 | def parse(String description) {
431 | log.debug "Parsing '${description}'"
432 |
433 | def map = [:]
434 | def retResult = []
435 | def descMap = parseDescriptionAsMap(description)
436 |
437 | //Image
438 | if (descMap["bucket"] && descMap["key"]) {
439 | putImageInS3(descMap)
440 | }
441 |
442 | //Status Polling
443 | else if (descMap["headers"] && descMap["body"]) {
444 | def body = new String(descMap["body"].decodeBase64())
445 | if(hdcamera == "true") {
446 | def langs = new XmlSlurper().parseText(body)
447 |
448 | def motionAlarm = "$langs.isEnable"
449 | def ledMode = "$langs.mode"
450 |
451 | //Get Motion Alarm Status
452 | if(motionAlarm == "0") {
453 | log.info("Polled: Alarm Off")
454 | sendEvent(name: "alarmStatus", value: "off");
455 | }
456 | else if(motionAlarm == "1") {
457 | log.info("Polled: Alarm On")
458 | sendEvent(name: "alarmStatus", value: "on");
459 | }
460 |
461 | //Get IR LED Mode
462 | if(ledMode == "0") {
463 | log.info("Polled: LED Mode Auto")
464 | sendEvent(name: "ledStatus", value: "auto")
465 | }
466 | else if(ledMode == "1") {
467 | log.info("Polled: LED Mode Manual")
468 | sendEvent(name: "ledStatus", value: "manual")
469 | }
470 | }
471 | else {
472 | if(body.find("alarm_motion_armed=0")) {
473 | log.info("Polled: Alarm Off")
474 | sendEvent(name: "alarmStatus", value: "off")
475 | }
476 | else if(body.find("alarm_motion_armed=1")) {
477 | log.info("Polled: Alarm On")
478 | sendEvent(name: "alarmStatus", value: "on")
479 | }
480 | //The API does not provide a way to poll for LED status on 8xxx series at the moment
481 | }
482 | }
483 | }
484 |
485 | def parseDescriptionAsMap(description) {
486 | description.split(",").inject([:]) { map, param ->
487 | def nameAndValue = param.split(":")
488 | map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
489 | }
490 | }
491 |
492 | def putImageInS3(map) {
493 |
494 | def s3ObjectContent
495 |
496 | try {
497 | def imageBytes = getS3Object(map.bucket, map.key + ".jpg")
498 |
499 | if(imageBytes)
500 | {
501 | s3ObjectContent = imageBytes.getObjectContent()
502 | def bytes = new ByteArrayInputStream(s3ObjectContent.bytes)
503 | storeImage(getPictureName(), bytes)
504 | }
505 | }
506 | catch(Exception e) {
507 | log.error e
508 | }
509 | finally {
510 | //Explicitly close the stream
511 | if (s3ObjectContent) { s3ObjectContent.close() }
512 | }
513 | }
514 |
515 | private getPictureName() {
516 | def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')
517 | "image" + "_$pictureUuid" + ".jpg"
518 | }
519 |
520 | private getHostAddress() {
521 | return "${ip}:${port}"
522 | }
523 |
524 | private String convertIPtoHex(ipAddress) {
525 | String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
526 | return hex
527 |
528 | }
529 |
530 | private String convertPortToHex(port) {
531 | String hexport = port.toString().format( '%04x', port.toInteger() )
532 | return hexport
533 | }
--------------------------------------------------------------------------------
/devicetypes/superuser/aeon-smart-strip-2.src/aeon-smart-strip-2.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Based on Aeon Smartstrip with inputs from @jwu
3 | * Devive handler for TKB TZ04 dual Relay insert
4 | *
5 | * By using BINARY_SWITCH_SET Command of Multi Channel Command Class Encapsulateion Command,
6 | * you can switch both Relay1 and Relay2 ON/OFF by setting endpoint to 1 or switch Relay1 ON/OFF
7 | * by setting endpoint to 2 or switch Relay1 ON/OFF by setting endpoint to 3
8 | * Status of Endpoint 1 returns ON when either relay 1 or 2 is ON or both or ON
9 | *
10 | * Device is capable of metering (combined when endpoint is set to 1) and individually when set to 2 or 3
11 | *
12 |
13 | */
14 | // for the UI
15 | metadata {
16 | // Automatically generated. Make future change here.
17 | definition (name: "Aeon Smart Strip 2", author: "jjhamb@yahoo.com") {
18 | capability "Energy Meter"
19 | capability "Refresh"
20 | capability "Power Meter"
21 | capability "Switch"
22 | capability "Configuration"
23 | capability "Polling"
24 |
25 | attribute "power1", "string"
26 | attribute "power2", "string"
27 | attribute "power3", "string"
28 | attribute "energy1", "string"
29 | attribute "energy2", "string"
30 | attribute "energy3", "string"
31 | attribute "switch1", "string"
32 | attribute "switch2", "string"
33 | attribute "switch3", "string"
34 |
35 | command "on1"
36 | command "off1"
37 | command "on2"
38 | command "off2"
39 | command "on3"
40 | command "off3"
41 | command "testA"
42 | command "testB"
43 | }
44 |
45 | simulator {
46 | // TODO: define status and reply messages here
47 | }
48 |
49 | tiles {
50 | valueTile("power", "device.power", decoration: "flat") {
51 | state "default", label:'${currentValue} W'
52 | }
53 | valueTile("energy", "device.energy", decoration: "flat") {
54 | state "default", label:'${currentValue} kWh'
55 | }
56 | standardTile("switch1", "device.switch1",canChangeIcon: true) {
57 | state "on", label: "switch1", action: "off1", icon: "st.switches.switch.on", backgroundColor: "#79b821"
58 | state "off", label: "switch1", action: "on1", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
59 | }
60 | standardTile("switch2", "device.switch2",canChangeIcon: true) {
61 | state "on", label: "switch2", action: "off2", icon: "st.switches.switch.on", backgroundColor: "#79b821"
62 | state "off", label: "switch2", action: "on2", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
63 | }
64 | standardTile("switch3", "device.switch3",canChangeIcon: true) {
65 | state "on", label: "switch3", action: "off3", icon: "st.switches.switch.on", backgroundColor: "#79b821"
66 | state "off", label: "switch3", action:"on3", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
67 | }
68 | standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
69 | state "default", label:"", action:"refresh", icon:"st.secondary.refresh"
70 | }
71 | standardTile("reset", "device.switch", inactiveLabel: false, decoration: "flat") {
72 | state "default", label:"reset kWh", action:"reset"
73 | }
74 | standardTile("configure", "device.switch", inactiveLabel: false, decoration: "flat") {
75 | state "default", label:"", action:"configure", icon:"st.secondary.configure"
76 | }
77 |
78 | valueTile("power2", "device.power2", decoration: "flat") {
79 | state "default", label:'${currentValue} W'
80 | }
81 | valueTile("energy2", "device.energy2", decoration: "flat") {
82 | state "default", label:'${currentValue} kWh'
83 | }
84 | valueTile("power3", "device.power3", decoration: "flat") {
85 | state "default", label:'${currentValue} W'
86 | }
87 | valueTile("energy3", "device.energy3", decoration: "flat") {
88 | state "default", label:'${currentValue} kWh'
89 | }
90 |
91 |
92 | main(["switch1", "power", "energy"])
93 | details(["switch1", "power", "energy", "switch2", "power2", "energy2",
94 | "switch3", "power3", "energy3", "refresh", "configure", "reset"])
95 | }
96 | }
97 |
98 | // 0x25 0x32 0x27 0x70 0x85 0x72 0x86 0x60 0xEF 0x82
99 |
100 | // 0x25: switch binary
101 | // 0x32: meter
102 | // 0x27: switch all
103 | // 0x70: configuration
104 | // 0x85: association
105 | // 0x86: version
106 | // 0x60: multi-channel
107 | // 0xEF: mark
108 | // 0x82: hail
109 |
110 | // parse events into attributes
111 | def parse(String description) {
112 | log.debug "Parsing desc => '${description}'"
113 |
114 | def result = null
115 | def cmd = zwave.parse(description, [0x60:3, 0x25:1, 0x32:1, 0x70:1])
116 | if (cmd) {
117 | result = createEvent(zwaveEvent(cmd))
118 | }
119 | log.debug "Parsing result => '${result}'"
120 | return result
121 | }
122 |
123 | //Reports
124 |
125 | def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
126 | [name: "switch", value: cmd.value ? "on" : "off", type: "physical"]
127 | }
128 |
129 | def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
130 | [name: "switch", value: cmd.value ? "on" : "off", type: "digital"]
131 | }
132 |
133 | def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
134 | def map = []
135 |
136 | if (cmd.scale == 0) {
137 | map = [ name: "energy", value: cmd.scaledMeterValue, unit: "kWh" ]
138 | }
139 | else if (cmd.scale == 2) {
140 | map = [ name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W" ]
141 | }
142 |
143 | map
144 | }
145 |
146 | def zwaveEvent(int endPoint, physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
147 | // MeterReport(deltaTime: 1368, meterType: 1, meterValue: [0, 3, 29, 17], precision: 3, previousMeterValue: [0, 3, 29, 17], rateType: 1, reserved02: false, scale: 0, scaledMeterValue: 204.049, scaledPreviousMeterValue: 204.049, size: 4)
148 | log.debug "EndPoint $endPoint, MeterReport $cmd"
149 | def map = []
150 |
151 | if (cmd.scale == 0) {
152 | map = [ name: "energy" + endPoint, value: cmd.scaledMeterValue, unit: "kWh" ]
153 | }
154 | else if (cmd.scale == 2) {
155 | map = [ name: "power" + endPoint, value: Math.round(cmd.scaledMeterValue), unit: "W" ]
156 | }
157 |
158 | map
159 | }
160 |
161 | /*
162 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
163 | log.debug "MultiChannelCmdEncap $cmd"
164 |
165 | def map = [ name: "switch$cmd.sourceEndPoint" ]
166 | if (cmd.commandClass == 37){
167 | if (cmd.parameter == [0]) {
168 | map.value = "off"
169 | }
170 | if (cmd.parameter == [255]) {
171 | map.value = "on"
172 | }
173 | map
174 | }
175 | else if (cmd.commandClass == 50) {
176 | // bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 100, 0, 0, 0, 0, 0, 94, 0, 0, 0, 0], res01: false, sourceEndPoint: 1
177 | def hex1 = { n -> String.format("%02X", n) }
178 | def desc = "command: ${hex1(cmd.commandClass)}${hex1(cmd.command)}, payload: " + cmd.parameter.collect{hex1(it)}.join(" ")
179 | // Re-assign source end point 3-6 to 1-4 and 1-2 to 5-6 to sync up with the switch end points.
180 | // Source end point in the message refers to always-on sockets.
181 | zwaveEvent((cmd.sourceEndPoint > 3) ? (cmd.sourceEndPoint-3) : (cmd.sourceEndPoint+3), zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))
182 | }
183 | }
184 | */
185 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
186 | log.debug "MultiChannelCmdEncap $cmd"
187 |
188 | def map = [ name: "switch$cmd.sourceEndPoint" ]
189 | if (cmd.commandClass == 37){
190 | if (cmd.parameter == [0]) {
191 | map.value = "off"
192 | }
193 | if (cmd.parameter == [255]) {
194 | map.value = "on"
195 | }
196 | map
197 | }
198 | else if (cmd.commandClass == 50) {
199 | def hex1 = { n -> String.format("%02X", n) }
200 | def desc = "command: ${hex1(cmd.commandClass)}${hex1(cmd.command)}, payload: " + cmd.parameter.collect{hex1(it)}.join(" ")
201 | zwaveEvent(cmd.sourceEndPoint, zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))
202 | }
203 | }
204 |
205 | def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) {
206 | // [50, 37, 32], dynamic: false, endPoint: 1, genericDeviceClass: 16, specificDeviceClass: 1)
207 | log.debug "multichannelv3.MultiChannelCapabilityReport $cmd"
208 | }
209 |
210 | def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
211 | log.debug "Configuration Report for parameter ${cmd.parameterNumber}: Value is ${cmd.configurationValue}, Size is ${cmd.size}"
212 | }
213 |
214 | def zwaveEvent(physicalgraph.zwave.Command cmd) {
215 | // Handles all Z-Wave commands we aren't interested in
216 | [:]
217 | log.debug "Capture All $cmd"
218 | }
219 |
220 | // handle commands
221 | def refresh() {
222 | def cmds = []
223 |
224 | for ( i in 1..3 )
225 | cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:37, command:2).format()
226 |
227 | for ( i in 1..3 ) {
228 | cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[0]).format()
229 | cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[16]).format()
230 | }
231 |
232 | cmds << zwave.meterV2.meterGet(scale:0).format()
233 | cmds << zwave.meterV2.meterGet(scale:2).format()
234 |
235 | delayBetween(cmds)
236 | }
237 |
238 | def on(value) {
239 | log.debug "value $value"
240 | delayBetween([
241 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:1, parameter:[255]).format(),
242 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:2).format()
243 | ])
244 | }
245 |
246 | def off(value) {
247 | log.debug "value $value"
248 | delayBetween([
249 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:1, parameter:[0]).format(),
250 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:2).format()
251 | ])
252 | }
253 |
254 | def poll() {
255 | log.debug "Poll - Refreshing"
256 | refresh()
257 | }
258 |
259 | def configure() {
260 | log.debug "Executing 'configure'"
261 | delayBetween([
262 | zwave.configurationV1.configurationSet(parameterNumber:101, size:4, configurationValue: [ 0, 0, 127, 127 ]).format(), // Report meter on all channels (6 sockets + master)
263 | zwave.configurationV1.configurationSet(parameterNumber:111, size:4, scaledConfigurationValue: 120).format(), // 120s interval for meter reports
264 | zwave.configurationV1.configurationSet(parameterNumber:4, configurationValue: [0]).format() // Report reguarly
265 | ])
266 | }
267 |
268 | def on1() {
269 | delayBetween([
270 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[255]).format(),
271 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
272 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format(),
273 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:2).format(),
274 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[0]).format(),
275 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[16]).format(),
276 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format(),
277 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format(),
278 | zwave.meterV2.meterGet(scale:0).format(),
279 | zwave.meterV2.meterGet(scale:2).format()
280 | ])
281 | }
282 |
283 | def off1() {
284 | delayBetween([
285 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[0]).format(),
286 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
287 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format(),
288 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:2).format(),
289 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[0]).format(),
290 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[16]).format(),
291 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format(),
292 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format(),
293 | zwave.meterV2.meterGet(scale:0).format(),
294 | zwave.meterV2.meterGet(scale:2).format()
295 | ])
296 | }
297 |
298 | def on2() {
299 | delayBetween([
300 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:1, parameter:[255]).format(),
301 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format(),
302 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[0]).format(),
303 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[16]).format(),
304 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
305 | zwave.meterV2.meterGet(scale:0).format(),
306 | zwave.meterV2.meterGet(scale:2).format()
307 | ])
308 | }
309 |
310 | def off2() {
311 | delayBetween([
312 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:1, parameter:[0]).format(),
313 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format(),
314 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[0]).format(),
315 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[16]).format(),
316 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
317 | zwave.meterV2.meterGet(scale:0).format(),
318 | zwave.meterV2.meterGet(scale:2).format()
319 | ])
320 | }
321 |
322 | def on3() {
323 | delayBetween([
324 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:1, parameter:[255]).format(),
325 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:2).format(),
326 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format(),
327 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format(),
328 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
329 | zwave.meterV2.meterGet(scale:0).format(),
330 | zwave.meterV2.meterGet(scale:2).format()
331 | ])
332 | }
333 |
334 | def off3() {
335 | delayBetween([
336 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:1, parameter:[0]).format(),
337 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:2).format(),
338 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format(),
339 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format(),
340 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
341 | zwave.meterV2.meterGet(scale:0).format(),
342 | zwave.meterV2.meterGet(scale:2).format()
343 | ])
344 | }
345 |
346 | /**
347 | //test
348 | def testB() {
349 | def cmds = []
350 | cmds << zwave.multiChannelV3.multiChannelCapabilityGet(endPoint:1).format()
351 | cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format()
352 | cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format()
353 | cmds << zwave.multiChannelV3.multiChannelEndPointFind(genericDeviceClass:32).format()
354 | cmds << zwave.multiChannelV3.multiChannelEndPointGet().format()
355 | cmds << zwave.meterV2.meterGet(scale:0).format()
356 | cmds << zwave.meterV2.meterGet(scale:2).format()
357 | cmds << zwave.configurationV1.configurationGet(parameterNumber:101).format()
358 | cmds << zwave.configurationV1.configurationGet(parameterNumber:4).format()
359 | cmds << zwave.configurationV1.configurationGet(parameterNumber:90).format()
360 | log.debug "Sending ${cmds.inspect()}"
361 | delayBetween(cmds, 2300)
362 | }
363 |
364 | def testA() {
365 | log.debug "testA"
366 | def cmds = []
367 | // cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:50, command:1, parameter:[0]).format()
368 | // cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:50, command:1, parameter:[16]).format()
369 | // cmds << zwave.switchBinaryV1.switchBinaryGet().format()
370 | // cmds << zwave.meterV2.meterGet(scale: 0).format()
371 | cmds << zwave.meterV3.meterGet(scale: 0).format()
372 | log.debug "$cmds"
373 | delayBetween(cmds, 1000)
374 | }
375 | **/
376 | //reset
377 | def reset() {
378 | def cmds = []
379 | cmds << zwave.meterV2.meterReset().format()
380 | cmds << zwave.meterV2.meterGet(scale:0).format()
381 | for ( i in 1..6 )
382 | cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[0]).format()
383 |
384 | delayBetween(cmds)
385 | }
--------------------------------------------------------------------------------
/devicetypes/superuser/poolswitch.src/poolswitch.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * PoolSwitch
3 | *
4 | * Copyright 2014 bigpunk6
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | */
16 | metadata {
17 | definition (name: "PowerStrip", author: "Ledridge") {
18 | capability "Actuator"
19 | capability "Switch"
20 | capability "Polling"
21 | capability "Configuration"
22 | capability "Refresh"
23 | capability "Temperature Measurement"
24 | capability "Sensor"
25 |
26 | attribute "switch1", "string"
27 | attribute "switch2", "string"
28 | attribute "switch3", "string"
29 | attribute "switch4", "string"
30 |
31 | command "onMulti"
32 | command "offMulti"
33 | command "on1"
34 | command "off1"
35 | command "on2"
36 | command "off2"
37 | command "on3"
38 | command "off3"
39 | command "on4"
40 | command "off4"
41 | }
42 |
43 | simulator {
44 | // TODO: define status and reply messages here
45 | }
46 |
47 | // tile definitions
48 | tiles {
49 | standardTile("switch1", "device.switch1",canChangeIcon: true) {
50 | state "on", label: "switch1", action: "off1", icon: "st.switches.switch.on", backgroundColor: "#79b821"
51 | state "off", label: "switch1", action: "on1", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
52 | }
53 | standardTile("switch2", "device.switch2",canChangeIcon: true) {
54 | state "on", label: "switch2", action: "off2", icon: "st.switches.switch.on", backgroundColor: "#79b821"
55 | state "off", label: "switch2", action: "on2", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
56 | }
57 | standardTile("switch3", "device.switch3",canChangeIcon: true) {
58 | state "on", label: "switch3", action: "off3", icon: "st.switches.switch.on", backgroundColor: "#79b821"
59 | state "off", label: "switch3", action:"on3", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
60 | }
61 | standardTile("switch4", "device.switch4",canChangeIcon: true) {
62 | state "on", label: "switch4", action: "off4", icon: "st.switches.switch.on", backgroundColor: "#79b821"
63 | state "off", label: "switch4", action:"on4", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
64 | }
65 |
66 | standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
67 | state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
68 | }
69 |
70 | standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
71 | state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
72 | }
73 |
74 | main(["switch1","switch2","switch3","switch4","temperature"])
75 | details(["switch1","switch2","switch3","switch4","temperature","refresh"])
76 | }
77 | }
78 |
79 | import physicalgraph.zwave.commands.*
80 |
81 | //Parse
82 | def parse(String description) {
83 | def result = null
84 | def cmd = zwave.parse(description, [0x20: 1, 0x70: 2, 0x86: 1, 0x60:3, 0x31:1, 0x25:1, 0x81:1])
85 | if (cmd) {
86 | if( cmd.CMD == "6006" ) {
87 | def map = [ name: "switch$cmd.instance" ]
88 | if (cmd.commandClass == 37){
89 | if (cmd.parameter == [0]) {
90 | map.value = "off"
91 | }
92 | if (cmd.parameter == [255]) {
93 | map.value = "on"
94 | }
95 | }
96 | result = createEvent(map)
97 | } else {
98 | result = createEvent(zwaveEvent(cmd))
99 | }
100 | }
101 | log.debug "Parse returned ${result?.descriptionText}"
102 | return result
103 | }
104 |
105 | //Reports
106 | def zwaveEvent(sensormultilevelv1.SensorMultilevelReport cmd)
107 | {
108 | def map = [:]
109 | map.value = cmd.scaledSensorValue.toString()
110 | map.unit = cmd.scale == 1 ? "F" : "C"
111 | map.name = "temperature"
112 | map
113 | }
114 |
115 | def zwaveEvent(thermostatsetpointv2.ThermostatSetpointReport cmd)
116 | {
117 | def map = [:]
118 | map.value = cmd.scaledValue.toString()
119 | map.unit = cmd.scale == 1 ? "F" : "C"
120 | map.displayed = false
121 | switch (cmd.setpointType) {
122 | case 1:
123 | map.name = "poolSetpoint"
124 | break;
125 | case 7:
126 | map.name = "spaSetpoint"
127 | break;
128 | default:
129 | return [:]
130 | }
131 | // So we can respond with same format
132 | state.size = cmd.size
133 | state.scale = cmd.scale
134 | state.precision = cmd.precision
135 | map
136 | }
137 |
138 | def zwaveEvent(multichannelv3.MultiInstanceReport cmd) {
139 | log.debug "$cmd"
140 | }
141 |
142 | def zwaveEvent(multichannelv3.MultiChannelCapabilityReport cmd) {
143 | log.debug "$cmd"
144 | }
145 |
146 | def zwaveEvent(multichannelv3.MultiChannelEndPointReport cmd) {
147 | log.debug "$cmd"
148 | }
149 |
150 | def zwaveEvent(multichannelv3.MultiInstanceCmdEncap cmd) {
151 | log.debug "$cmd"
152 | def map = [ name: "switch$cmd.instance" ]
153 | if (cmd.commandClass == 37){
154 | if (cmd.parameter == [0]) {
155 | map.value = "off"
156 | }
157 | if (cmd.parameter == [255]) {
158 | map.value = "on"
159 | }
160 | }
161 | createEvent(map)
162 | }
163 |
164 | def zwaveEvent(multichannelv3.MultiChannelCmdEncap cmd) {
165 | log.debug "$cmd"
166 | def map = [ name: "switch$cmd.destinationEndPoint" ]
167 | if (cmd.commandClass == 37){
168 | if (cmd.parameter == [0]) {
169 | map.value = "off"
170 | }
171 | if (cmd.parameter == [255]) {
172 | map.value = "on"
173 | }
174 | }
175 | createEvent(map)
176 | }
177 |
178 | def zwaveEvent(cmd) {
179 | log.warn "Captured zwave command $cmd"
180 | }
181 |
182 | //Commands
183 |
184 | def setPoolSetpoint(degreesF) {
185 | setHeatingSetpoint(degreesF.toDouble())
186 | }
187 |
188 | def setPoolSetpoint(Double degreesF) {
189 | def p = (state.precision == null) ? 1 : state.precision
190 | delayBetween([
191 | zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: 1, precision: p, scaledValue: degreesF).format(),
192 | zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
193 | ])
194 | }
195 |
196 | def setSpaSetpoint(degreesF) {
197 | setSpaSetpoint(degreesF.toDouble())
198 | }
199 |
200 | def setSpaSetpoint(Double degreesF) {
201 | def p = (state.precision == null) ? 1 : state.precision
202 | delayBetween([
203 | zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 7, scale: 1, precision: p, scaledValue: degreesF).format(),
204 | zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 7).format()
205 | ])
206 | }
207 |
208 | def on() {
209 | delayBetween([
210 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: 1, destinationEndPoint: 1, commandClass:37, command:1, parameter:[255]).format(),
211 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: 1, destinationEndPoint: 1, commandClass:37, command:2).format()
212 | ], 2300)
213 | }
214 |
215 | def off() {
216 | delayBetween([
217 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: 1, destinationEndPoint: 1, commandClass:37, command:1, parameter:[0]).format(),
218 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: 1, destinationEndPoint: 1, commandClass:37, command:2).format()
219 | ], 2300)
220 | }
221 |
222 | //switch instance
223 | def onMulti(value) {
224 | delayBetween([
225 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: value, destinationEndPoint: value, commandClass:37, command:1, parameter:[255]).format(),
226 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: value, destinationEndPoint: value, commandClass:37, command:2).format()
227 | ], 2300)
228 | }
229 |
230 | def offMulti(value) {
231 | delayBetween([
232 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: value, destinationEndPoint: value, commandClass:37, command:1, parameter:[0]).format(),
233 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: value, destinationEndPoint: value, commandClass:37, command:2).format()
234 | ], 2300)
235 | }
236 |
237 | //switch1
238 | def on1() {
239 | onMulti(1)
240 | }
241 |
242 | def off1() {
243 | offMulti(1)
244 | }
245 |
246 | //switch2
247 | def on2() {
248 | onMulti(2)
249 | }
250 |
251 | def off2() {
252 | offMulti(2)
253 | }
254 |
255 | //switch3
256 | def on3() {
257 | onMulti(3)
258 | }
259 |
260 | def off3() {
261 | offMulti(3)
262 | }
263 |
264 | //switch4
265 | def on4() {
266 | onMulti(4)
267 | }
268 |
269 | def off4() {
270 | offMulti(4)
271 | }
272 |
273 |
274 | def poll() {
275 | zwave.sensorMultilevelV1.sensorMultilevelGet().format()
276 | }
277 |
278 | def refresh() {
279 | delayBetween([
280 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format(),
281 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:2, destinationEndPoint:2, commandClass:37, command:2).format(),
282 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:3, destinationEndPoint:3, commandClass:37, command:2).format(),
283 | zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:4, destinationEndPoint:4, commandClass:37, command:2).format(),
284 | zwave.sensorMultilevelV1.sensorMultilevelGet().format(),
285 | zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
286 | zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 7).format()
287 | ], 2500)
288 | }
--------------------------------------------------------------------------------
/vTile_DLI:
--------------------------------------------------------------------------------
1 | /**
2 | * V Tile device-type for Digital Loggers Inc.
3 | *
4 | * Needed for Multi Switch with Virtual Tiles to create virtual switch tiles in ST for devices that have multiple "switch[x]"
5 | * attributes within them and have on[x], off[x], and cycle[x] commands for each.
6 | * Also has support for device-label inside the name when on or off and polling occurs
7 | *
8 | */
9 | metadata {
10 | definition (name: "vTile_DLI", namespace: "Ledridge", author: "Ledridge") {
11 | capability "Switch"
12 | capability "relaySwitch"
13 | capability "Polling"
14 | capability "Refresh"
15 |
16 | attribute "lastEvent", "string"
17 |
18 | command "cycle"
19 | }
20 | }
21 |
22 | preferences {
23 | tiles {
24 | standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
25 | state "off", label:'${name}', action: "switch.on", icon:"st.switches.switch.off", backgroundColor: "#DDDDff", nextState: "turningOn"
26 | state "on", label:'${name}', action: "switch.off", icon:"st.switches.switch.on", backgroundColor: "#0088ff", nextState: "turningOff"
27 | state "turningOff", label:'${name}', action: "switch.on", icon:"st.switches.switch.off", backgroundColor: "#FA5882", nextState: "off"
28 | state "turningOn", label:'${name}', action: "switch.on", icon:"st.switches.switch.on", backgroundColor: "#F3F781", nextState: "on"
29 | state "cyclingOff", label:"Turning Off", icon:"st.switches.switch.off", backgroundColor: "#FA5882", nextState: "cyclingOn"
30 | state "cyclingOn", label:"Turning On", icon:"st.switches.switch.on", backgroundColor: "#F3F781", nextState: "on"
31 | }
32 |
33 | standardTile("Cycle", "device.switch", width: 1, height: 2, canChangeIcon: true) {
34 | state "default", action: "cycle", icon: "st.secondary.refresh-icon", backgroundColor: "#0088ff"
35 | }
36 |
37 | valueTile("lastEvent", "device.lastEvent", inactiveLabel: false, width: 3, height: 1, canChangeIcon: false, decoration:"flat") {
38 | state "default", label: 'Last Event: ${currentValue}'}
39 |
40 | main "switch"
41 | details(["switch", "Cycle", "lastEvent"])
42 | }
43 | }
44 |
45 | def parse(desc) {
46 | def results = []
47 | log.debug desc
48 | if(desc=="updated") { log.debug "Device $device.label has been UPDATED"; poll() }
49 | }
50 |
51 | def on() {
52 | sendEvent([name: "switch", value: "on"])
53 | parent.OutletAction(this,"ON")
54 | sendEvent([name: "lastEvent", value: "${df(now())}"])
55 | log.debug "$device.label is On"
56 | }
57 |
58 | def off() {
59 | sendEvent([name: "switch", value: "off"])
60 | parent.OutletAction(this,"OFF")
61 | sendEvent([name: "switch", value: "$device.label"])
62 | sendEvent([name: "lastEvent", value: "${df(now())}"])
63 | log.debug "$device.label is Off"
64 | }
65 |
66 | def cycle() {
67 | log.debug "$device.label is Cycling"
68 | parent.OutletAction(this,"CCL")
69 |
70 | sendEvent([name: "switch", value: "cyclingOff"])
71 | pause(6000)
72 |
73 | sendEvent([name: "switch", value: "cyclingOn"])
74 | pause(5000)
75 |
76 | sendEvent([name: "switch", value: "on"])
77 |
78 | sendEvent([name: "lastEvent", value: "${df(now())}"])
79 | //log.debug "$device.label is Off"
80 | }
81 |
82 | def poll() {
83 | def current = device.currentValue("switch")
84 | log.debug "Polling - $device.label is $current"
85 |
86 | log.debug "This - $this"
87 |
88 | def outletStatus = parent.OutletStatus(this)
89 | log.debug "Polling - Status is $outletStatus"
90 |
91 | def OutletName = parent.OutletName(this)
92 | log.debug "Polling - Name is $OutletName"
93 |
94 | if(!current || current=="off") { sendEvent(name:"switch", value:"$device.label", isStateChange:true, displayed:false) }
95 | }
96 |
97 | def pause(millis) {
98 | def passed = 0
99 | def now = new Date().time
100 | log.debug "pausing... at Now: $now"
101 | /* This loop is an impolite busywait. We need to be given a true sleep() method, please. */
102 | while ( passed < millis ) {
103 | passed = new Date().time - now
104 | }
105 | log.debug "... DONE pausing."
106 | }
107 |
108 | def df(e) {
109 | // * df(e) - Date Format "E"
110 | // * Takes epoch time format and returns Date formatted in current timezone
111 | def locale = getWeatherFeature("geolookup", zip);
112 | def tz = TimeZone.getTimeZone(locale.location.tz_long);
113 | def formatted
114 | if(e) { formatted = new Date(e).format("EEE, MMM d, 'at' hh:mm aaa", tz); return formatted }
115 | }
116 |
--------------------------------------------------------------------------------