3 import groovy.transform.Field
6 public class Switches {
11 //If we have only one device
13 private String label = "switch"
14 private String displayName = "switch"
15 private String switchState = "off"
16 private String currentSwitch = "off"
17 private int currentLevel = 50
18 private String switchLatestValue = "off"
20 Switches(Closure sendEvent, int deviceNumbers) {
21 this.sendEvent = sendEvent
22 this.deviceNumbers = deviceNumbers
24 for (int i = 0;i < deviceNumbers;i++) {
25 switches.add(new Switch(sendEvent, i+40, label+i.toString(), displayName+i.toString(), this.switchState, this.currentSwitch, this.currentLevel, this.switchLatestValue))
29 //Methods for closures
30 def count(Closure Input) {
36 def each(Closure Input) {
41 def setLevel(int level) {
42 switches*.setLevel(level)
49 def on(LinkedHashMap metaData) {
57 def off(LinkedHashMap metaData) {
62 def setValue(LinkedHashMap eventDataMap) {
63 switches[eventDataMap["deviceId"]].setValue(eventDataMap["value"])
64 if (deviceNumbers == 1)
65 this.switchState = switches[eventDataMap["deviceId"]].switchState
66 this.switchLatestValue = switches[eventDataMap["deviceId"]].switchLatestValue
67 sendEvent(eventDataMap)
71 def currentValue(String deviceFeature) {
72 if (deviceNumbers == 1)
73 switches[0].currentValue(deviceFeature)
75 switches*.currentValue(deviceFeature)
78 def latestValue(String deviceFeature) {
79 if (deviceNumbers == 1)
80 switches[0].latestValue(deviceFeature)
82 switches*.latestValue(deviceFeature)
92 private String displayName
93 private String switchState
94 private String currentSwitch
95 private int currentLevel
96 private String switchLatestValue
100 Switch(Closure sendEvent, int id, String label, String displayName, String switchState, String currentSwitch, int currentLevel, String switchLatestValue) {
101 this.sendEvent = sendEvent
102 this.currentSwitch = currentSwitch
103 this.currentLevel = currentLevel
106 this.displayName = displayName
107 this.switchState = switchState
108 this.switchLatestValue = switchLatestValue
112 def setLevel(int level) {
113 println("the switch with id:$id is setted to level $level!")
114 this.currentLevel = level
118 println("the switch with id:$id is on!")
119 this.switchLatestValue = this.switchState
120 this.switchState = "on"
121 this.currentSwitch = "on"
122 sendEvent([name: "switch", value: "on", deviceId: this.id, descriptionText: "",
123 displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
126 def on(LinkedHashMap metaData) {
127 println("the switch with id:$id is on!")
128 this.switchLatestValue = this.switchState
129 this.switchState = "on"
130 this.currentSwitch = "on"
131 sendEvent([name: "switch", value: "on", deviceId: this.id, descriptionText: "",
132 displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
136 println("the switch with id:$id is off!")
137 this.switchLatestValue = this.switchState
138 this.switchState = "off"
139 this.currentSwitch = "off"
140 sendEvent([name: "switch", value: "off", deviceId: this.id, descriptionText: "",
141 displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
144 def off(LinkedHashMap metaData) {
145 println("the switch with id:$id is off!")
146 this.switchLatestValue = this.switchState
147 this.switchState = "off"
148 this.currentSwitch = "off"
149 sendEvent([name: "switch", value: "off", deviceId: this.id, descriptionText: "",
150 displayed: true, linkText: "", isStateChange: false, unit: "", data: []])
154 def setValue(String value) {
155 println("the switch with id:$id is $value!")
156 this.switchLatestValue = this.switchState
157 this.switchState = value
158 this.currentSwitch = value
161 def currentValue(String deviceFeature) {
162 if (deviceFeature == "switch") {
167 def latestValue(String deviceFeature) {
168 if (deviceFeature == "switch") {
169 return switchLatestValue
174 @Field def switchObject
177 def input(String name, String type) {
179 case "capability.lock":
181 case "capability.alarm":
183 case "capability.battery":
185 case "capability.beacon":
187 case "capability.carbonMonoxideDetector":
189 case "capability.colorControl":
191 case "capability.contactSensor":
193 case "capability.doorControl":
195 case "capability.energyMeter":
197 case "capability.illuminanceMeasurement":
199 case "capability.accelerationSensor":
201 case "capability.motionSensor":
203 case "capability.musicPlayer":
205 case "capability.powerMeter":
207 case "capability.presenceSensor":
209 case "capability.relativeHumidityMeasurement":
211 case "capability.relaySwitch":
213 case "capability.sleepSensor":
215 case "capability.smokeDetector":
217 case "capability.stepSensor":
219 case "capability.switch":
221 case "capability.switchLevel":
223 case "capability.temperatureMeasurement":
225 case "capability.thermostat":
227 case "capability.valve":
229 case "capability.waterSensor":
231 case "capability.touchSensor":
233 case "capability.imageCapture":
235 case "device.mobilePresence":
237 case "device.aeonKeyFob":
262 //input "","",linkedHashMap
263 def input(LinkedHashMap metaData, String name, String type) {
265 case "capability.lock":
267 case "capability.alarm":
269 case "capability.battery":
271 case "capability.beacon":
273 case "capability.carbonMonoxideDetector":
275 case "capability.colorControl":
277 case "capability.contactSensor":
279 case "capability.doorControl":
281 case "capability.energyMeter":
283 case "capability.illuminanceMeasurement":
285 case "capability.accelerationSensor":
287 case "capability.motionSensor":
289 case "capability.musicPlayer":
291 case "capability.powerMeter":
293 case "capability.presenceSensor":
295 case "capability.relativeHumidityMeasurement":
297 case "capability.relaySwitch":
299 case "capability.sleepSensor":
301 case "capability.smokeDetector":
303 case "capability.stepSensor":
305 case "capability.switch":
307 case "capability.switchLevel":
309 case "capability.temperatureMeasurement":
311 case "capability.thermostat":
313 case "capability.valve":
315 case "capability.waterSensor":
317 case "capability.touchSensor":
319 case "capability.imageCapture":
321 case "device.mobilePresence":
323 case "device.aeonKeyFob":
347 //input linkedHashMap
348 def input(LinkedHashMap metaData) {
349 switch(metaData['type']) {
350 case "capability.lock":
352 case "capability.alarm":
354 case "capability.battery":
356 case "capability.beacon":
358 case "capability.carbonMonoxideDetector":
360 case "capability.colorControl":
362 case "capability.contactSensor":
364 case "capability.doorControl":
366 case "capability.energyMeter":
368 case "capability.illuminanceMeasurement":
370 case "capability.accelerationSensor":
372 case "capability.motionSensor":
374 case "capability.musicPlayer":
376 case "capability.powerMeter":
378 case "capability.presenceSensor":
380 case "capability.relativeHumidityMeasurement":
382 case "capability.relaySwitch":
384 case "capability.sleepSensor":
386 case "capability.smokeDetector":
388 case "capability.stepSensor":
390 case "capability.switch":
392 case "capability.switchLevel":
394 case "capability.temperatureMeasurement":
396 case "capability.thermostat":
398 case "capability.valve":
400 case "capability.waterSensor":
402 case "capability.touchSensor":
404 case "capability.imageCapture":
406 case "device.mobilePresence":
408 case "device.aeonKeyFob":
433 def definition(LinkedHashMap metaData) {
434 println("///Just some information///")
437 def preferences(Closure inputData) {
438 find(inputData) //Run the closure to extract pages/sections/inputMethods
441 def page(LinkedHashMap metaData, Closure inputData) {
442 if (metaData.containsKey('name'))
443 println(metaData['name'])
444 if (metaData.containsKey('title'))
445 println(metaData['title'])
447 find(inputData) //Run the closure to extract sections/inputMethods
450 def page(LinkedHashMap metaData) {
451 def nameOfFunction = metaData['name']
452 "$nameOfFunction"() //Call the page
456 def dynamicPage(LinkedHashMap metaData, Closure inputData) {
457 if (metaData.containsKey('name'))
458 println(metaData['name'])
459 if (metaData.containsKey('title'))
460 println(metaData['title'])
462 //find(inputData) //Run the closure to extract sections/inputMethods
465 def section(String title, Closure inputData) {
467 find(inputData) //Run the closure to extract inputMethods
468 switchObject = "switch1"
469 this[switchObject] = new Switches({}, 1)
476 name: "NFC Tag Toggle",
477 namespace: "smartthings",
478 author: "SmartThings",
479 description: "Allows toggling of a switch, lock, or garage door based on an NFC Tag touch event",
480 category: "SmartThings Internal",
481 iconUrl: "https://s3.amazonaws.com/smartapp-icons/Developers/nfc-tag-executor.png",
482 iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Developers/nfc-tag-executor@2x.png",
483 iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Developers/nfc-tag-executor@2x.png")
487 page(name: "pageOne", title: "Device selection", uninstall: true, nextPage: "pageTwo") {
488 section("Select an NFC tag") {
489 input "tag", "capability.touchSensor", title: "NFC Tag"
491 section("Select devices to control") {
492 input "switch1", "capability.switch", title: "Light or switch", required: false, multiple: true
493 input "lock", "capability.lock", title: "Lock", required: false, multiple: true
494 input "garageDoor", "capability.doorControl", title: "Garage door controller", required: false, multiple: true
498 page(name: "pageTwo", title: "Master devices", install: true, uninstall: true)
504 dynamicPage(name: "pageTwo") {
505 section("If set, the state of these devices will be toggled each time the tag is touched, " +
506 "e.g. a light that's on will be turned off and one that's off will be turned on, " +
507 "other devices of the same type will be set to the same state as their master device. " +
508 "If no master is designated then the majority of devices of the same type will be used " +
509 "to determine whether to turn on or off the devices.") {
511 if (switch1 || masterSwitch) {
512 input "masterSwitch", "enum", title: "Master switch", options: switch1.collect{[(it.id): it.displayName]}, required: false
514 if (lock || masterLock) {
515 input "masterLock", "enum", title: "Master lock", options: lock.collect{[(it.id): it.displayName]}, required: false
517 if (garageDoor || masterDoor) {
518 input "masterDoor", "enum", title: "Master door", options: garageDoor.collect{[(it.id): it.displayName]}, required: false
521 section([mobileOnly:true]) {
522 label title: "Assign a name", required: false
523 mode title: "Set for specific mode(s)", required: false
529 log.debug "Installed with settings: ${settings}"
534 log.debug "Updated with settings: ${settings}"
540 subscribe tag, "nfcTouch", touchHandler
541 subscribe app, touchHandler
544 private currentStatus(devices, master, attribute) {
545 log.trace "currentStatus($devices, $master, $attribute)"
548 result = devices.find{it.id == master}?.currentValue(attribute)
553 def value = it.currentValue(attribute)
554 map[value] = (map[value] ?: 0) + 1
555 log.trace "$it.displayName: $value"
558 result = map.collect{it}.sort{it.value}[-1].key
560 log.debug "$attribute = $result"
564 def touchHandler(evt) {
565 log.trace "touchHandler($evt.descriptionText)"
567 def status = currentStatus(switch1, masterSwitch, "switch")
569 if (status == "on") {
579 def status = currentStatus(lock, masterLock, "lock")
581 if (status == "locked") {
591 def status = currentStatus(garageDoor, masterDoor, "status")
593 if (status == "open") {