Adding more official apps gotten from https://github.com/SmartThingsCommunity/Code
[smartapps.git] / official / ubi.groovy
1 /**
2  *  Copyright 2015 SmartThings
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  *  in compliance with the License. You may obtain a copy of the License at:
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
10  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
11  *  for the specific language governing permissions and limitations under the License.
12  *
13  *  Ubi
14  *
15  *  Author: SmartThings
16  */
17
18 definition(
19     name: "Ubi",
20     namespace: "smartthings",
21     author: "SmartThings",
22     description: "Add your Ubi device to your SmartThings Account",
23     iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/ubi-app-icn.png",
24     iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/ubi-app-icn@2x.png",
25     oauth: [displayName: "Ubi", displayLink: ""]
26 )
27
28 preferences {
29         section("Allow a web application to control these things...") {
30                 input name: "switches", type: "capability.switch", title: "Which Switches?", multiple: true, required: false
31                 input name: "motions", type: "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false
32                 input name: "locks", type: "capability.lock", title: "Which Locks?", multiple: true, required: false
33                 input name: "contactSensors", type: "capability.contactSensor", title: "Which Contact Sensors?", multiple: true, required: false
34                 input name: "presenceSensors", type: "capability.presenceSensor", title: "Which Presence Sensors?", multiple: true, required: false
35         }
36 }
37
38 mappings {
39         path("/list") {
40                 action: [
41                         GET: "listAll"
42                 ]
43         }
44
45         path("/events/:id") {
46                 action: [
47                         GET: "showEvents"
48                 ]
49         }
50
51         path("/switches") {
52                 action: [
53                         GET: "listSwitches",
54                         PUT: "updateSwitches",
55                         POST: "updateSwitches"
56                 ]
57         }
58         path("/switches/:id") {
59                 action: [
60                         GET: "showSwitch",
61                         PUT: "updateSwitch",
62                         POST: "updateSwitch"
63                 ]
64         }
65         path("/switches/subscriptions") {
66                 log.debug "switches added"
67                 action: [
68                         POST: "addSwitchSubscription"
69                 ]
70         }
71         path("/switches/subscriptions/:id") {
72                 action: [
73                         DELETE: "removeSwitchSubscription",
74                         GET: "removeSwitchSubscription"
75                 ]
76         }
77
78         path("/motionSensors") {
79                 action: [
80                         GET: "listMotions",
81                         PUT: "updateMotions",
82                         POST: "updateMotions"
83
84                 ]
85         }
86         path("/motionSensors/:id") {
87                 action: [
88                         GET: "showMotion",
89                         PUT: "updateMotion",
90                         POST: "updateMotion"
91                 ]
92         }
93         path("/motionSensors/subscriptions") {
94                 log.debug "motionSensors added"
95                 action: [
96                         POST: "addMotionSubscription"
97                 ]
98         }
99         path("/motionSensors/subscriptions/:id") {
100                 log.debug "motionSensors Deleted"
101                 action: [
102                         DELETE: "removeMotionSubscription",
103                         GET: "removeMotionSubscription"
104                 ]
105         }
106
107         path("/locks") {
108                 action: [
109                         GET: "listLocks",
110                         PUT: "updateLocks",
111                         POST: "updateLocks"
112                 ]
113         }
114         path("/locks/:id") {
115                 action: [
116                         GET: "showLock",
117                         PUT: "updateLock",
118                         POST: "updateLock"
119                 ]
120         }
121         path("/locks/subscriptions") {
122                 action: [
123                         POST: "addLockSubscription"
124                 ]
125         }
126         path("/locks/subscriptions/:id") {
127                 action: [
128                         DELETE: "removeLockSubscription",
129                         GET: "removeLockSubscription"
130                 ]
131         }
132
133         path("/contactSensors") {
134                 action: [
135                         GET: "listContactSensors",
136                         PUT: "updateContactSensor",
137                         POST: "updateContactSensor"
138                 ]
139         }
140         path("/contactSensors/:id") {
141                 action: [
142                         GET: "showContactSensor",
143                         PUT: "updateContactSensor",
144                         POST: "updateContactSensor"
145                 ]
146         }
147         path("/contactSensors/subscriptions") {
148                 log.debug "contactSensors/subscriptions"
149                 action: [
150                         POST: "addContactSubscription"
151                 ]
152         }
153         path("/contactSensors/subscriptions/:id") {
154                 action: [
155                         DELETE: "removeContactSensorSubscription",
156                         GET: "removeContactSensorSubscription"
157                 ]
158         }
159
160         path("/presenceSensors") {
161                 action: [
162                         GET: "listPresenceSensors",
163                         PUT: "updatePresenceSensor",
164                         POST: "updatePresenceSensor"
165                 ]
166         }
167         path("/presenceSensors/:id") {
168                 action: [
169                         GET: "showPresenceSensor",
170                         PUT: "updatePresenceSensor",
171                         POST: "updatePresenceSensor"
172                 ]
173         }
174         path("/presenceSensors/subscriptions") {
175                 log.debug "PresenceSensors/subscriptions"
176                 action: [
177                         POST: "addPresenceSubscription"
178                 ]
179         }
180         path("/presenceSensors/subscriptions/:id") {
181                 action: [
182                         DELETE: "removePresenceSensorSubscription",
183                         GET: "removePresenceSensorSubscription"
184                 ]
185         }
186
187         path("/state") {
188                 action: [
189                         GET: "currentState"
190                 ]
191         }
192
193         path("/phrases") {
194                 action: [
195                         GET: "listPhrases"
196                 ]
197         }
198         path("/phrases/:phraseName") {
199                 action: [
200                         GET: "executePhrase",
201                         POST: "executePhrase",
202                 ]
203         }
204
205 }
206
207 def installed() {
208 //      subscribe(motions, "motion.active", motionOpenHandler)
209
210 //      subscribe(contactSensors, "contact.open", contactOpenHandler)
211 //      log.trace "contactSensors Installed"
212
213 }
214
215 def updated() {
216
217 //      unsubscribe()
218 //    subscribe(motions, "motion.active", motionOpenHandler)
219 //      subscribe(contactSensors, "contact.open", contactOpenHandler)
220
221 //log.trace "contactSensors Updated"
222
223 }
224
225 def listAll() {
226         listSwitches() + listMotions() + listLocks() + listContactSensors() + listPresenceSensors() + listPhrasesWithType()
227 }
228
229 def listContactSensors() {
230         contactSensors.collect { device(it, "contactSensor") }
231 }
232
233
234 void updateContactSensors() {
235         updateAll(contactSensors)
236 }
237
238 def showContactSensor() {
239         show(contactSensors, "contact")
240 }
241
242 void updateContactSensor() {
243         update(contactSensors)
244 }
245
246 def addContactSubscription() {
247         log.debug "addContactSensorSubscription,  params: ${params}"
248         addSubscription(contactSensors, "contact")
249 }
250
251 def removeContactSensorSubscription() {
252         removeSubscription(contactSensors)
253 }
254
255
256 def listPresenceSensors() {
257         presenceSensors.collect { device(it, "presenceSensor") }
258 }
259
260
261 void updatePresenceSensors() {
262         updateAll(presenceSensors)
263 }
264
265 def showPresenceSensor() {
266         show(presenceSensors, "presence")
267 }
268
269 void updatePresenceSensor() {
270         update(presenceSensors)
271 }
272
273 def addPresenceSubscription() {
274         log.debug "addPresenceSensorSubscription,  params: ${params}"
275         addSubscription(presenceSensors, "presence")
276 }
277
278 def removePresenceSensorSubscription() {
279         removeSubscription(presenceSensors)
280 }
281
282
283 def listSwitches() {
284         switches.collect { device(it, "switch") }
285 }
286
287 void updateSwitches() {
288         updateAll(switches)
289 }
290
291 def showSwitch() {
292         show(switches, "switch")
293 }
294
295 void updateSwitch() {
296         update(switches)
297 }
298
299 def addSwitchSubscription() {
300         log.debug "addSwitchSubscription,  params: ${params}"
301         addSubscription(switches, "switch")
302 }
303
304 def removeSwitchSubscription() {
305         removeSubscription(switches)
306 }
307
308 def listMotions() {
309         motions.collect { device(it, "motionSensor") }
310 }
311
312 void updateMotions() {
313         updateAll(motions)
314 }
315
316 def showMotion() {
317         show(motions, "motion")
318 }
319
320 void updateMotion() {
321         update(motions)
322 }
323
324 def addMotionSubscription() {
325
326         addSubscription(motions, "motion")
327 }
328
329 def removeMotionSubscription() {
330         removeSubscription(motions)
331 }
332
333 def listLocks() {
334         locks.collect { device(it, "lock") }
335 }
336
337 void updateLocks() {
338         updateAll(locks)
339 }
340
341 def showLock() {
342         show(locks, "lock")
343 }
344
345 void updateLock() {
346         update(locks)
347 }
348
349 def addLockSubscription() {
350         addSubscription(locks, "lock")
351 }
352
353 def removeLockSubscription() {
354         removeSubscription(locks)
355 }
356
357 /*
358 def motionOpenHandler(evt) {
359 //log.trace "$evt.value: $evt, $settings"
360
361         log.debug "$motions was active, sending push message to user"
362         //sendPush("Your ${contact1.label ?: contact1.name} was opened")
363
364
365         httpPostJson(uri: "http://automatesolutions.ca/test.php", path: '', body: [evt: [value: "motionSensor Active"]]) {
366         log.debug "Event data successfully posted"
367     }
368
369 }
370 def contactOpenHandler(evt) {
371         //log.trace "$evt.value: $evt, $settings"
372
373         log.debug "$contactSensors was opened, sending push message to user"
374         //sendPush("Your ${contact1.label ?: contact1.name} was opened")
375
376
377         httpPostJson(uri: "http://automatesolutions.ca/test.php", path: '', body: [evt: [value: "ContactSensor Opened"]]) {
378         log.debug "Event data successfully posted"
379     }
380
381
382 }
383 */
384
385
386 def deviceHandler(evt) {
387         log.debug "~~~~~TEST~~~~~~"
388         def deviceInfo = state[evt.deviceId]
389         if (deviceInfo)
390         {
391                 httpPostJson(uri: deviceInfo.callbackUrl, path: '', body: [evt: [value: evt.value]]) {
392                         log.debug "Event data successfully posted"
393                 }
394         }
395         else
396         {
397                 log.debug "No subscribed device found"
398         }
399 }
400
401 def currentState() {
402         state
403 }
404
405 def showStates() {
406         def device = (switches + motions + locks).find { it.id == params.id }
407         if (!device)
408         {
409                 httpError(404, "Switch not found")
410         }
411         else
412         {
413                 device.events(params)
414         }
415 }
416
417 def listPhrasesWithType() {
418         location.helloHome.getPhrases().collect {
419                 [
420                         "id"   : it.id,
421                         "label": it.label,
422                         "type" : "phrase"
423                 ]
424         }
425 }
426
427 def listPhrases() {
428         location.helloHome.getPhrases().label
429 }
430
431 def executePhrase() {
432         def phraseName = params.phraseName
433         if (phraseName)
434         {
435                 location.helloHome.execute(phraseName)
436                 log.debug "executed phrase: $phraseName"
437         }
438         else
439         {
440                 httpError(404, "Phrase not found")
441         }
442 }
443
444 private void updateAll(devices) {
445         def type = params.param1
446         def command = request.JSON?.command
447         if (!devices) {
448                 httpError(404, "Devices not found")
449         }
450         if (command){
451                 devices.each { device ->
452                         executeCommand(device, type, command)
453                 }
454         }
455 }
456
457 private void update(devices) {
458         log.debug "update, request: ${request.JSON}, params: ${params}, devices: $devices.id"
459         def type = params.param1
460         def command = request.JSON?.command
461         def device = devices?.find { it.id == params.id }
462
463         if (!device) {
464                         httpError(404, "Device not found")
465         }
466
467         if (command) {
468                 executeCommand(device, type, command)
469         }
470 }
471
472 /**
473  * Validating the command passed by the user based on capability.
474  * @return boolean
475  */
476 def validateCommand(device, deviceType, command) {
477         def capabilityCommands = getDeviceCapabilityCommands(device.capabilities)
478         def currentDeviceCapability = getCapabilityName(deviceType)
479         if (capabilityCommands[currentDeviceCapability]) {
480                 return command in capabilityCommands[currentDeviceCapability] ? true : false
481         } else {
482                 // Handling other device types here, which don't accept commands
483                 httpError(400, "Bad request.")
484         }
485 }
486
487 /**
488  * Need to get the attribute name to do the lookup. Only
489  * doing it for the device types which accept commands
490  * @return attribute name of the device type
491  */
492 def getCapabilityName(type) {
493     switch(type) {
494                 case "switches":
495                         return "Switch"
496                 case "locks":
497                         return "Lock"
498                 default:
499                         return type
500         }
501 }
502
503 /**
504  * Constructing the map over here of
505  * supported commands by device capability
506  * @return a map of device capability -> supported commands
507  */
508 def getDeviceCapabilityCommands(deviceCapabilities) {
509         def map = [:]
510         deviceCapabilities.collect {
511                 map[it.name] = it.commands.collect{ it.name.toString() }
512         }
513         return map
514 }
515
516 /**
517  * Validates and executes the command
518  * on the device or devices
519  */
520 def executeCommand(device, type, command) {
521         if (validateCommand(device, type, command)) {
522                 device."$command"()
523         } else {
524                 httpError(403, "Access denied. This command is not supported by current capability.")
525         }       
526 }
527
528 private show(devices, type) {
529         def device = devices.find { it.id == params.id }
530         if (!device)
531         {
532                 httpError(404, "Device not found")
533         }
534         else
535         {
536                 def attributeName = type
537
538                 def s = device.currentState(attributeName)
539                 [id: device.id, label: device.displayName, value: s?.value, unitTime: s?.date?.time, type: type]
540         }
541 }
542
543 private addSubscription(devices, attribute) {
544         //def deviceId = request.JSON?.deviceId
545         //def callbackUrl = request.JSON?.callbackUrl
546
547         log.debug "addSubscription,  params: ${params}"
548
549         def deviceId = params.deviceId
550         def callbackUrl = params.callbackUrl
551
552         def myDevice = devices.find { it.id == deviceId }
553         if (myDevice)
554         {
555                 log.debug "Adding switch subscription" + callbackUrl
556                 state[deviceId] = [callbackUrl: callbackUrl]
557                 log.debug "Added state: $state"
558                 def subscription = subscribe(myDevice, attribute, deviceHandler)
559                 if (subscription && subscription.eventSubscription) {
560                         log.debug "Subscription is newly created"
561                 } else {
562                         log.debug "Subscription already exists, returning existing subscription"
563                         subscription = app.subscriptions?.find { it.deviceId == deviceId && it.data == attribute && it.handler == 'deviceHandler' }
564                 }
565                 [
566                         id: subscription.id,
567                         deviceId: subscription.deviceId,
568                         data: subscription.data,
569                         handler: subscription.handler,
570                         callbackUrl: callbackUrl
571                 ]
572         }
573 }
574
575 private removeSubscription(devices) {
576         def deviceId = params.id
577         def device = devices.find { it.id == deviceId }
578         if (device)
579         {
580                 log.debug "Removing $device.displayName subscription"
581                 state.remove(device.id)
582                 unsubscribe(device)
583         }
584 }
585
586 private device(it, type) {
587         it ? [id: it.id, label: it.displayName, type: type] : null
588 }