Thought I would share a modification I made to the SmartThings button controller app.
I have added a “Fan” section that allows the user to cycle through fan modes by pushing the button. Using this in conjunction with the “hold” feature and I have programmed my Aeon Minimote to do the following:
- Push Button: Turn on Fan to Low
- Push Button Again: Change Fan Speed to Medium
- Push Button Again: Change Fan Speed to High
- Push Button Again: Turn off Fan
- Any time I Hold the Button, Fan turns Off/On
/** * Copyright 2015 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * * Button Controller * * Author: Thomas Howard based on SmartThings Original Button Controller * Date: 2015-11-09 */ definition( name: "Smarter Button Controller", namespace: "Button Controller", author: "Thomas Howard", description: "Control devices with buttons like the Aeon Labs Minimote; Based on SmartThings button controller.", category: "Convenience", iconUrl: "https://s3.amazonaws.com/smartapp-icons/MyApps/Cat-MyApps.png", iconX2Url: "https://s3.amazonaws.com/smartapp-icons/MyApps/Cat-MyApps@2x.png" )
preferences { page(name: "selectButton") page(name: "configureButton1") page(name: "configureButton2") page(name: "configureButton3") page(name: "configureButton4")
page(name: "timeIntervalInput", title: "Only during a certain time") { section { input "starting", "time", title: "Starting", required: false input "ending", "time", title: "Ending", required: false } } }
def selectButton() { dynamicPage(name: "selectButton", title: "First, select your button device", nextPage: "configureButton1", uninstall: configured()) { section { input "buttonDevice", "capability.button", title: "Button", multiple: false, required: true }
section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
def timeLabel = timeIntervalLabel()
href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false, options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
input "modes", "mode", title: "Only when mode is", multiple: true, required: false } } }
def configureButton1() { dynamicPage(name: "configureButton1", title: "Now let's decide how to use the first button", nextPage: "configureButton2", uninstall: configured(), getButtonSections(1)) } def configureButton2() { dynamicPage(name: "configureButton2", title: "If you have a second button, set it up here", nextPage: "configureButton3", uninstall: configured(), getButtonSections(2)) }
def configureButton3() { dynamicPage(name: "configureButton3", title: "If you have a third button, you can do even more here", nextPage: "configureButton4", uninstall: configured(), getButtonSections(3)) } def configureButton4() { dynamicPage(name: "configureButton4", title: "If you have a fourth button, you rule, and can set it up here", install: true, uninstall: true, getButtonSections(4)) }
def getButtonSections(buttonNumber) { return { section("Lights") { input "lights_${buttonNumber}_pushed", "capability.switch", title: "Pushed", multiple: true, required: false input "lights_${buttonNumber}_held", "capability.switch", title: "Held", multiple: true, required: false } section("Fans") { input "fans_${buttonNumber}_pushed", "capability.switchLevel", title: "Pushed", multiple: true, required: false input "fans_${buttonNumber}_held", "capability.switchLevel", title: "Held", multiple: true, required: false } section("Locks") { input "locks_${buttonNumber}_pushed", "capability.lock", title: "Pushed", multiple: true, required: false input "locks_${buttonNumber}_held", "capability.lock", title: "Held", multiple: true, required: false } section("Sonos") { input "sonos_${buttonNumber}_pushed", "capability.musicPlayer", title: "Pushed", multiple: true, required: false input "sonos_${buttonNumber}_held", "capability.musicPlayer", title: "Held", multiple: true, required: false } section("Modes") { input "mode_${buttonNumber}_pushed", "mode", title: "Pushed", required: false input "mode_${buttonNumber}_held", "mode", title: "Held", required: false } def phrases = location.helloHome?.getPhrases()*.label if (phrases) { section("Hello Home Actions") { log.trace phrases input "phrase_${buttonNumber}_pushed", "enum", title: "Pushed", required: false, options: phrases input "phrase_${buttonNumber}_held", "enum", title: "Held", required: false, options: phrases } } section("Sirens") { input "sirens_${buttonNumber}_pushed","capability.alarm" ,title: "Pushed", multiple: true, required: false input "sirens_${buttonNumber}_held", "capability.alarm", title: "Held", multiple: true, required: false }
section("Custom Message") { input "textMessage_${buttonNumber}", "text", title: "Message", required: false }
section("Push Notifications") { input "notifications_${buttonNumber}_pushed","bool" ,title: "Pushed", required: false, defaultValue: false input "notifications_${buttonNumber}_held", "bool", title: "Held", required: false, defaultValue: false }
section("Sms Notifications") { input "phone_${buttonNumber}_pushed","phone" ,title: "Pushed", required: false input "phone_${buttonNumber}_held", "phone", title: "Held", required: false } } }
def installed() { initialize() }
def updated() { unsubscribe() initialize() }
def initialize() { subscribe(buttonDevice, "button", buttonEvent) }
def configured() { return buttonDevice || buttonConfigured(1) || buttonConfigured(2) || buttonConfigured(3) || buttonConfigured(4) }
def buttonConfigured(idx) { return settings["lights_$idx_pushed"] || settings["fans_$idx_pushed"] || settings["locks_$idx_pushed"] || settings["sonos_$idx_pushed"] || settings["mode_$idx_pushed"] || settings["notifications_$idx_pushed"] || settings["sirens_$idx_pushed"] || settings["notifications_$idx_pushed"] || settings["phone_$idx_pushed"] }
def buttonEvent(evt){ if(allOk) { def buttonNumber = evt.data // why doesn't jsonData work? always returning [:] def value = evt.value log.debug "buttonEvent: $evt.name = $evt.value ($evt.data)" log.debug "button: $buttonNumber, value: $value"
def recentEvents = buttonDevice.eventsSince(new Date(now() - 3000)).findAll{it.value == evt.value && it.data == evt.data} log.debug "Found ${recentEvents.size()?:0} events in past 3 seconds"
if(recentEvents.size <= 1){ switch(buttonNumber) { case ~/.*1.*/: executeHandlers(1, value) break case ~/.*2.*/: executeHandlers(2, value) break case ~/.*3.*/: executeHandlers(3, value) break case ~/.*4.*/: executeHandlers(4, value) break } } else { log.debug "Found recent button press events for $buttonNumber with value $value" } } }
def executeHandlers(buttonNumber, value) { log.debug "executeHandlers: $buttonNumber - $value"
def lights = find('lights', buttonNumber, value) if (lights != null) toggle(lights)
def fans=find('fans', buttonNumber, value) if (fans != null) changeFanSpeed(fans)
def locks = find('locks', buttonNumber, value) if (locks != null) toggle(locks)
def sonos = find('sonos', buttonNumber, value) if (sonos != null) toggle(sonos)
def mode = find('mode', buttonNumber, value) if (mode != null) changeMode(mode)
def phrase = find('phrase', buttonNumber, value) if (phrase != null) location.helloHome.execute(phrase)
def textMessage = findMsg('textMessage', buttonNumber)
def notifications = find('notifications', buttonNumber, value) if (notifications?.toBoolean()) sendPush(textMessage ?: "Button $buttonNumber was pressed" )
def phone = find('phone', buttonNumber, value) if (phone != null) sendSms(phone, textMessage ?:"Button $buttonNumber was pressed")
def sirens = find('sirens', buttonNumber, value) if (sirens != null) toggle(sirens) }
def changeFanSpeed(fans){ def i; def fan; def level; for (i=0; i<fans.size(); i++){ fan = fans.get(i); if (fan.currentValue("switch").contains("off")) level = 0; else level = fan.currentValue("level").toInteger(); log.debug "CURRENT FAN LEVEL $level" if (level == 0) level = 30; else if (level <= 32) level = 60; else if (level <=65) level = 100; else level = 0; log.debug "SETTING LEVEL $level" fan.setLevel(level); } }
def find(type, buttonNumber, value) { def preferenceName = type + "_" + buttonNumber + "_" + value def pref = settings[preferenceName] if(pref != null) { log.debug "Found: $pref for $preferenceName" }
return pref }
def findMsg(type, buttonNumber) { def preferenceName = type + "_" + buttonNumber def pref = settings[preferenceName] if(pref != null) { log.debug "Found: $pref for $preferenceName" }
return pref }
def toggle(devices) { log.debug "toggle: $devices = ${devices*.currentValue('switch')}"
if (devices*.currentValue('switch').contains('on')) { devices.off() } else if (devices*.currentValue('switch').contains('off')) { devices.on() } else if (devices*.currentValue('lock').contains('locked')) { devices.unlock() } else if (devices*.currentValue('alarm').contains('off')) { devices.siren() } else { devices.on() } }
def changeMode(mode) { log.debug "changeMode: $mode, location.mode = $location.mode, location.modes = $location.modes"
if (location.mode != mode && location.modes?.find { it.name == mode }) { setLocationMode(mode) } }
// execution filter methods private getAllOk() { modeOk && daysOk && timeOk }
private getModeOk() { def result = !modes || modes.contains(location.mode) log.trace "modeOk = $result" result }
private getDaysOk() { def result = true if (days) { def df = new java.text.SimpleDateFormat("EEEE") if (location.timeZone) { df.setTimeZone(location.timeZone) } else { df.setTimeZone(TimeZone.getTimeZone("America/New_York")) } def day = df.format(new Date()) result = days.contains(day) } log.trace "daysOk = $result" result }
private getTimeOk() { def result = true if (starting && ending) { def currTime = now() def start = timeToday(starting).time def stop = timeToday(ending).time result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start } log.trace "timeOk = $result" result }
private hhmm(time, fmt = "h:mm a") { def t = timeToday(time, location.timeZone) def f = new java.text.SimpleDateFormat(fmt) f.setTimeZone(location.timeZone ?: timeZone(time)) f.format(t) }
private hideOptionsSection() { (starting || ending || days || modes) ? false : true }
private timeIntervalLabel() { (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : "" }