private def textHelp() {
def text =
section("User's Guide - Advanced Button Controller") {
paragraph "This smartapp allows you to use a device with buttons including, but not limited to:\n\n Aeon Labs Minimotes\n"+
" HomeSeer HS-WD100+ switches\n HomeSeer HS-WS100+ switches\n Lutron Picos**\n\n"+
"It is a heavily modified version of @dalec's 'Button Controller Plus' which is in turn"+
" a version of @bravenel's 'Button Controller+'."
}
section("Some of the included changes are:"){
paragraph "A complete revamp of the configuration flow. You can now tell at a glance, what has been configured for each button."+
"The button configuration page has been collapsed by default for easier navigation."
paragraph "The original apps were hardcoded to allow configuring 4 or 6 button devices."+
" This app will automatically detect the number of buttons on your device or allow you to manually"+
" specify (only needed if device does not report on its own)."
paragraph "Allows you to give your buton device full speaker control including: Play/Pause, NextTrack, Mute, VolumeUp/Down."+
"(Standard Pico remotes can be converted to Audio Picos)\n\nThe additional control options have been highlighted below."
}
section("Available Control Options are:"){
paragraph " Switches - Toggle \n"+
" Switches - Turn On \n"+
" Switches - Turn Off \n"+
" Dimmers - Toggle \n"+
" Dimmers - Set Level (Group 1) \n"+
" Dimmers - Set Level (Group 2) \n"+
" Dimmers - Inc Level \n"+
" Dimmers - Dec Level \n"+
" Fans - Low, Medium, High, Off \n"+
" Shades - Up, Down, or Stop \n"+
" Locks - Unlock Only \n"+
" Speaker - Play/Pause \n"+
" Speaker - Next Track \n"+
" Speaker - Mute/Unmute \n"+
" Speaker - Volume Up \n"+
" Speaker - Volume Down \n"+
" Set Modes \n"+
" Run Routines \n"+
" Sirens - Toggle \n"+
" Push Notifications \n"+
" SMS Notifications"
}
section ("** Quirk for HS-WD100+ on Button 5 & 6:"){
paragraph "Because a dimmer switch already uses Press&Hold to manually set the dimming level"+
" please be aware of this operational behavior. If you only want to manually change"+
" the dim level to the lights that are wired to the switch, you will automatically"+
" trigger the 5/6 button event as well. And the same is true in reverse. If you"+
" only want to trigger a 5/6 button event action with Press&Hold, you will be manually"+
" changing the dim level of the switch simultaneously as well.\n"+
"This quirk doesn't exist of course with the HS-HS100+ since it is not a dimmer."
}
section("*** Lutron Pico Requirements:"){
paragraph "Lutron Picos are not natively supported by SmartThings. A Lutron SmartBridge Pro, a device running @njschwartz's python script (or node.js) and the Lutron Caseta Service Manager"+
" SmartApp are also required for this functionality!\nSearch the forums for details."
}
}
Child SmartApp
/ DO NOT PUBLISH !!!!
Child Creator - Advanced Button Controller
Author: SmartThings, modified by Bruce Ravenel, Dale Coffing, Stephan Hackett
6/20/17 - fixed missing subs for notifications
1/14/18 - updated Version check code
1/15/18 - added icon support for Inovelli Switches (NZW30S and NZW31S)
small adjustments to "Configure Button" page layout
DO NOT PUBLISH !!!!
*/
def version(){"v0.2.180115"}
private def textHelp() {
def text =
section("User's Guide - Advanced Button Controller") {
paragraph "This smartapp allows you to use a device with buttons including, but not limited to:\n\n Aeon Labs Minimotes\n"+
" HomeSeer HS-WD100+ switches\n HomeSeer HS-WS100+ switches\n Lutron Picos**\n\n"+
"It is a heavily modified version of @dalec's 'Button Controller Plus' which is in turn"+
" a version of @bravenel's 'Button Controller+'."
}
section("Some of the included changes are:"){
paragraph "A complete revamp of the configuration flow. You can now tell at a glance, what has been configured for each button."+
"The button configuration page has been collapsed by default for easier navigation."
paragraph "The original apps were hardcoded to allow configuring 4 or 6 button devices."+
" This app will automatically detect the number of buttons on your device or allow you to manually"+
" specify (only needed if device does not report on its own)."
paragraph "Allows you to give your buton device full speaker control including: Play/Pause, NextTrack, Mute, VolumeUp/Down."+
"(Standard Pico remotes can be converted to Audio Picos)\n\nThe additional control options have been highlighted below."
}
section("Available Control Options are:"){
paragraph " Switches - Toggle \n"+
" Switches - Turn On \n"+
" Switches - Turn Off \n"+
" Dimmers - Toggle \n"+
" Dimmers - Set Level (Group 1) \n"+
" Dimmers - Set Level (Group 2) \n"+
" Dimmers - Inc Level \n"+
" Dimmers - Dec Level \n"+
" Fans - Low, Medium, High, Off \n"+
" Shades - Up, Down, or Stop \n"+
" Locks - Unlock Only \n"+
" Speaker - Play/Pause \n"+
" Speaker - Next Track \n"+
" Speaker - Mute/Unmute \n"+
" Speaker - Volume Up \n"+
" Speaker - Volume Down \n"+
" Set Modes \n"+
" Run Routines \n"+
" Sirens - Toggle \n"+
" Push Notifications \n"+
" SMS Notifications"
}
section ("** Quirk for HS-WD100+ on Button 5 & 6:"){
paragraph "Because a dimmer switch already uses Press&Hold to manually set the dimming level"+
" please be aware of this operational behavior. If you only want to manually change"+
" the dim level to the lights that are wired to the switch, you will automatically"+
" trigger the 5/6 button event as well. And the same is true in reverse. If you"+
" only want to trigger a 5/6 button event action with Press&Hold, you will be manually"+
" changing the dim level of the switch simultaneously as well.\n"+
"This quirk doesn't exist of course with the HS-HS100+ since it is not a dimmer."
}
section("*** Lutron Pico Requirements:"){
paragraph "Lutron Picos are not natively supported by SmartThings. A Lutron SmartBridge Pro, a device running @njschwartz's python script (or node.js) and the Lutron Caseta Service Manager"+
" SmartApp are also required for this functionality!\nSearch the forums for details."
}
}
def getSpecText(){
if(state.buttonType == "Lutron Pico") {
switch (state.currentButton){
case 1: return "Top Button"; break
case 2: return "Bottom Button"; break
case 3: return "Middle Button";break
case 4: return "Up Button"; break
case 5: return "Down Button"; break
}
}
if(state.buttonType.contains("Aeon Minimote")) {
switch (state.currentButton){
case 1: return "Top Left Button"; break
case 2: return "Top Right Button"; break
case 3: return "Lower Left Button";break
case 4: return "Lower Right"; break
}
}
if(state.buttonType.contains("WD100+ Dimmer")) {
switch (state.currentButton){
case 1: return "Double-Tap Upper Paddle"; break
case 2: return "Double-Tap Lower Paddle"; break
case 3: return "Triple-Tap Upper Paddle";break
case 4: return "Triple-Tap Lower Paddle"; break
case 5: return "Press & Hold Upper Paddle\n(See user guide for quirks)"; break
case 6: return "Press & Hold Lower Paddle\n(See user guide for quirks)"; break
case 7: return "Single Tap Upper Paddle\n(See user guide for quirks)"; break
case 8: return "Single Tap Lower Paddle\n(See user guide for quirks)"; break
}
}
if(state.buttonType.contains("WS100+ Switch")) {
switch (state.currentButton){
case 1: return "Double-Tap Upper Paddle"; break
case 2: return "Double-Tap Lower Paddle"; break
case 3: return "Triple-Tap Upper Paddle";break
case 4: return "Triple-Tap Lower Paddle"; break
case 5: return "Press & Hold Upper Paddle"; break
case 6: return "Press & Hold Lower Paddle"; break
case 7: return "Single Tap Upper Paddle";break
case 8: return "Single Tap Lower Paddle"; break
}
}
if(state.buttonType.contains("Inovelli")) {
switch (state.currentButton){
case 1: return "NOT OPERATIONAL - DO NOT USE"; break
case 2: return "2X Tap Upper Paddle = Pushed\n2X Tap Lower Paddle = Held"; break
case 3: return "3X Tap Upper Paddle = Pushed\n3X Tap Lower Paddle = Held"; break
case 4: return "4X Tap Upper Paddle = Pushed\n4X Tap Lower Paddle = Held";break
case 5: return "5X Tap Upper Paddle = Pushed\n5X Tap Lower Paddle = Held"; break
case 6: return "Hold Upper Paddle = Pushed\nHold Lower Paddle = Held"; break
}
}
return "Not Specified By Device"
}
/*FOR NEW INPUTS//////////////
add input to config
add info to detailMappings including subvalue if needed
ensure correct type is used in map..or create a new one with its own formattedPage
FOR NEW BUTTON DEVICE TYPES///////////////
ensure device reports buttonNumber
if not, add sendEvent to DTH as needed OR just enter manually
add any special instructions to getSpecText() using dth name
After creating /Publishing the Parent SmartApp (My Device Handler) , we attempt to create the Child SmartApp (My SmartApp) and observe this error -?-
Is there any obvious issues, or is there a method of troubleshot and verify that the syntax is correctly written, that can be recommended /thz -?-
Parent SmartApp /*
definition( name: "ABC Manager", namespace: "stephack", singleInstance: true, author: "Stephan Hackett", description: "Configure devices with buttons like the Aeon Labs Minimote and Lutron Pico Remotes.", category: "My Apps", iconUrl: "https://cdn.rawgit.com/stephack/ABC/master/resources/images/abcNew.png", iconX2Url: "https://cdn.rawgit.com/stephack/ABC/master/resources/images/abcNew.png", iconX3Url: "https://cdn.rawgit.com/stephack/ABC/master/resources/images/abcNew.png", )
preferences { page(name: "mainPage") page(name: "aboutPage")
}
def mainPage() { return dynamicPage(name: "mainPage", title: "", install: true, uninstall: true) { def childApps = getAllChildApps() def childVer = "InitialSetup" if(childApps.size() > 0) { childVer = childApps.first().version() } section("Create a new button device mapping.") { app(name: "childApps", appName: "ABC Child Creator", namespace: "stephack", title: "New Button Device Mapping", multiple: true) } section("Version Info, User's Guide") { href (name: "aboutPage", title: "Advanced Button Controller \n"+childVer, description: "Tap to get Smartapp Info and User's Guide.", image: verImgCheck(childVer), required: false, // check repo for image that matches current version. Displays update icon if missing page: "aboutPage" )
} remove("Uninstall ABC App","WARNING!!","This will remove the ENTIRE SmartApp, including all configs listed above.") } }
def verImgCheck(childVer){ def params = [ uri: "https://cdn.rawgit.com/stephack/ABC/master/resources/images/abc_${childVer}.png", ] try { httpGet(params) { resp -> resp.headers.each { //log.debug "${it.name} : ${it.value}" } log.debug "ABC appears to be running the latest Version" return params.uri } } catch (e) { log.error "ABC does not appear to be the latest version: Please update from IDE" return "https://cdn.rawgit.com/stephack/ABC/master/resources/images/update.png" } }
def installed() { initialize() }
def updated() { unsubscribe() initialize() }
def initialize() {
}
private def textHelp() { def text = section("User's Guide - Advanced Button Controller") { paragraph "This smartapp allows you to use a device with buttons including, but not limited to:\n\n Aeon Labs Minimotes\n"+ " HomeSeer HS-WD100+ switches\n HomeSeer HS-WS100+ switches\n Lutron Picos**\n\n"+ "It is a heavily modified version of @dalec's 'Button Controller Plus' which is in turn"+ " a version of @bravenel's 'Button Controller+'." } section("Some of the included changes are:"){ paragraph "A complete revamp of the configuration flow. You can now tell at a glance, what has been configured for each button."+ "The button configuration page has been collapsed by default for easier navigation." paragraph "The original apps were hardcoded to allow configuring 4 or 6 button devices."+ " This app will automatically detect the number of buttons on your device or allow you to manually"+ " specify (only needed if device does not report on its own)." paragraph "Allows you to give your buton device full speaker control including: Play/Pause, NextTrack, Mute, VolumeUp/Down."+ "(Standard Pico remotes can be converted to Audio Picos)\n\nThe additional control options have been highlighted below." } section("Available Control Options are:"){ paragraph " Switches - Toggle \n"+ " Switches - Turn On \n"+ " Switches - Turn Off \n"+ " Dimmers - Toggle \n"+ " Dimmers - Set Level (Group 1) \n"+ " Dimmers - Set Level (Group 2) \n"+ " Dimmers - Inc Level \n"+ " Dimmers - Dec Level \n"+ " Fans - Low, Medium, High, Off \n"+ " Shades - Up, Down, or Stop \n"+ " Locks - Unlock Only \n"+ " Speaker - Play/Pause \n"+ " Speaker - Next Track \n"+ " Speaker - Mute/Unmute \n"+ " Speaker - Volume Up \n"+ " Speaker - Volume Down \n"+ " Set Modes \n"+ " Run Routines \n"+ " Sirens - Toggle \n"+ " Push Notifications \n"+ " SMS Notifications" } section ("** Quirk for HS-WD100+ on Button 5 & 6:"){ paragraph "Because a dimmer switch already uses Press&Hold to manually set the dimming level"+ " please be aware of this operational behavior. If you only want to manually change"+ " the dim level to the lights that are wired to the switch, you will automatically"+ " trigger the 5/6 button event as well. And the same is true in reverse. If you"+ " only want to trigger a 5/6 button event action with Press&Hold, you will be manually"+ " changing the dim level of the switch simultaneously as well.\n"+ "This quirk doesn't exist of course with the HS-HS100+ since it is not a dimmer." } section("*** Lutron Pico Requirements:"){ paragraph "Lutron Picos are not natively supported by SmartThings. A Lutron SmartBridge Pro, a device running @njschwartz's python script (or node.js) and the Lutron Caseta Service Manager"+ " SmartApp are also required for this functionality!\nSearch the forums for details." } }
Child SmartApp / DO NOT PUBLISH !!!!
definition( name: "ABC Child Creator", namespace: "stephack", author: "Stephan Hackett", description: "SHOULD NOT BE PUBLISHED", category: "My Apps", parent: "stephack:ABC Manager", iconUrl: "https://cdn.rawgit.com/stephack/ABC/master/resources/images/abcNew.png", iconX2Url: "https://cdn.rawgit.com/stephack/ABC/master/resources/images/abcNew.png", iconX3Url: "https://cdn.rawgit.com/stephack/ABC/master/resources/images/abcNew.png", )
preferences { page(name: "chooseButton") page(name: "configButtonsPage") 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 chooseButton() { dynamicPage(name: "chooseButton", install: true, uninstall: true) { section("Step 1: Select Your Button Device") { input "buttonDevice", "capability.button", title: "Button Device", multiple: false, required: true, submitOnChange: true } if(buttonDevice){ state.buttonType = buttonDevice.typeName if(state.buttonType.contains("Aeon Minimote")) state.buttonType = "Aeon Minimote" log.debug "Device Type is now set to: "+state.buttonType state.buttonCount = manualCount?: buttonDevice.currentValue('numberOfButtons') //if(state.buttonCount==null) state.buttonCount = buttonDevice.currentValue('numButtons') //added for kyse minimote(hopefully will be updated to correct attribute name) section("Step 2: Configure Your Buttons"){ if(state.buttonCount<1) { paragraph "The selected button device did not report the number of buttons it has. Please specify in the Advanced Config section below." } else { for(i in 1..state.buttonCount){ href "configButtonsPage", title: "Button ${i} - (Tap to Edit)", description: getDescription(i), params: [pbutton: i] } } } } section("Set Custom Name (Optional)") { label title: "Assign a name:", required: false } section("Advanced Config:", hideable: true, hidden: hideOptionsSection()) { input "manualCount", "number", title: "Set/Override # of Buttons?", required: false, description: "Only set if DTH does not report", submitOnChange: true input "collapseAll", "bool", title: "Collapse Unconfigured Sections?", defaultValue: true input "hwSpecifics", "bool", title: "Hide H/W Specific Details?", defaultValue: false } section(title: "Only Execute When:", hideable: true, hidden: hideOptionsSection()) { 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 configButtonsPage(params) { if (params.pbutton != null) state.currentButton = params.pbutton.toInteger() dynamicPage(name: "configButtonsPage", title: "CONFIGURE BUTTON ${state.currentButton}:\n${state.buttonType}", getButtonSections(state.currentButton)) }
def getButtonSections(buttonNumber) { return { def picNameNoSpace = "${state.buttonType}${state.currentButton}.png"-" "-" "-" "-"/" //log.debug picNameNoSpace section(){ //"Hardware specific info on button selection:") { if(hwSpecifics== false) paragraph image: "https://cdn.rawgit.com/stephack/ABC/master/resources/images/${picNameNoSpace}", "${getSpecText()}" } section("Switches (Turn On)", hideable: true, hidden: !shallHide("lightOn${buttonNumber}")) { input "lightOn${buttonNumber}pushed", "capability.switch", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "lightOn${buttonNumber}held", "capability.switch", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section("Switches (Turn Off)", hideable: true, hidden: !shallHide("lightOff${buttonNumber}")) { input "lightOff_${buttonNumber}pushed", "capability.switch", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "lightOff${buttonNumber}held", "capability.switch", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section("Switches (Toggle On/Off)", hideable: true, hidden: !shallHide("lights${buttonNumber}")) { input "lights_${buttonNumber}pushed", "capability.switch", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "lights${buttonNumber}held", "capability.switch", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section(" "){} section("Dimmers (On to Level - Group 1)", hideable: true, hidden: !(shallHide("lightDim${buttonNumber}") || shallHide("valLight${buttonNumber}"))) { input "lightDim_${buttonNumber}_pushed", "capability.switchLevel", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll input "valLight${buttonNumber}pushed", "number", title: "Bright Level", multiple: false, required: false, description: "0 to 100%" if(showHeld()) input "lightDim${buttonNumber}_held", "capability.switchLevel", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "valLight${buttonNumber}held", "number", title: "Bright Level", multiple: false, required: false, description: "0 to 100%" } section("Dimmers (On to Level - Group 2)", hideable: true, hidden: !(shallHide("lightD2m${buttonNumber}") || shallHide("valLight2${buttonNumber}"))) { input "lightD2m_${buttonNumber}_pushed", "capability.switchLevel", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll input "valLight2${buttonNumber}pushed", "number", title: "Bright Level", multiple: false, required: false, description: "0 to 100%" if(showHeld()) input "lightD2m${buttonNumber}_held", "capability.switchLevel", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "valLight2${buttonNumber}held", "number", title: "Bright Level", multiple: false, required: false, description: "0 to 100%" } section("Dimmers (Increase Level By)", hideable: true, hidden: !(shallHide("dimPlus${buttonNumber}") || shallHide("valDimP${buttonNumber}"))) { input "dimPlus_${buttonNumber}_pushed", "capability.switchLevel", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll input "valDimP${buttonNumber}pushed", "number", title: "When Pushed Increase by", multiple: false, required: false, description: "0 to 15" if(showHeld()) input "dimPlus${buttonNumber}_held", "capability.switchLevel", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "valDimP${buttonNumber}held", "number", title: "When Held Increase by", multiple: false, required: false, description: "0 to 15" } section("Dimmers (Decrease Level By)", hideable: true, hidden: !(shallHide("dimMinus${buttonNumber}") || shallHide("valDimM${buttonNumber}"))) { input "dimMinus_${buttonNumber}_pushed", "capability.switchLevel", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll input "valDimM${buttonNumber}pushed", "number", title: "When Pushed Decrease by", multiple: false, required: false, description: "0 to 15" if(showHeld()) input "dimMinus${buttonNumber}_held", "capability.switchLevel", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "valDimM${buttonNumber}held", "number", title: "When Held Decrease by", multiple: false, required: false, description: "0 to 15" } section("Dimmers (Toggle OnToLevel/Off)", hideable: true, hidden: !(shallHide("lightsDT${buttonNumber}") || shallHide("valDT${buttonNumber}"))) { input "lightsDT_${buttonNumber}_pushed", "capability.switchLevel", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll input "valDT${buttonNumber}pushed", "number", title: "Bright Level", required: false, description: "0 to 100%" if(showHeld()) input "lightsDT${buttonNumber}_held", "capability.switchLevel", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "valDT${buttonNumber}held", "number", title: "Bright Level", required: false, description: "0 to 100%" } section(" "){} section("Speakers (Toggle Play/Pause)", hideable: true, hidden: !shallHide("speakerpp${buttonNumber}")) { input "speakerpp_${buttonNumber}pushed", "capability.musicPlayer", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "speakerpp${buttonNumber}held", "capability.musicPlayer", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section("Speakers (Increase Vol By)", hideable: true, hidden: !(shallHide("speakervu${buttonNumber}") || shallHide("valSpeakU${buttonNumber}"))) { input "speakervu_${buttonNumber}_pushed", "capability.musicPlayer", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll input "valSpeakU${buttonNumber}pushed", "number", title: "When Pushed Increase by", multiple: false, required: false, description: "0 to 15" if(showHeld()) input "speakervu${buttonNumber}_held", "capability.musicPlayer", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "valSpeakU${buttonNumber}held", "number", title: "When Held Increase by", multiple: false, required: false, description: "0 to 15" } section("Speakers (Decrease Vol By)", hideable: true, hidden: !(shallHide("speakervd${buttonNumber}") || shallHide("valSpeakD${buttonNumber}"))) { input "speakervd_${buttonNumber}_pushed", "capability.musicPlayer", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll input "valSpeakD${buttonNumber}pushed", "number", title: "When Pushed Decrease by", multiple: false, required: false, description: "0 to 15" if(showHeld())input "speakervd${buttonNumber}_held", "capability.musicPlayer", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll if(showHeld())input "valSpeakD${buttonNumber}held", "number", title: "When Held Decrease by", multiple: false, required: false, description: "0 to 15" } section("Speakers (Go to Next Track)", hideable: true, hidden: !shallHide("speakernt${buttonNumber}")) { input "speakernt_${buttonNumber}pushed", "capability.musicPlayer", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "speakernt${buttonNumber}held", "capability.musicPlayer", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section("Speakers (Toggle Mute/Unmute)", hideable: true, hidden: !shallHide("speakermu${buttonNumber}")) { input "speakermu_${buttonNumber}pushed", "capability.musicPlayer", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "speakermu${buttonNumber}held", "capability.musicPlayer", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section(" "){} section("Sirens (Toggle)", hideable: true, hidden: !shallHide("sirens${buttonNumber}")) { input "sirens_${buttonNumber}pushed","capability.alarm" ,title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "sirens${buttonNumber}held", "capability.alarm", title: "When Held", multiple: true, required: false, submitOnChange: true } section("Locks (Lock Only)", hideable: true, hidden: !shallHide("locks${buttonNumber}")) { input "locks_${buttonNumber}pushed", "capability.lock", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "locks${buttonNumber}held", "capability.lock", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section("Fans (Adjust - Low, Medium, High, Off)", hideable: true, hidden: !shallHide("fanAdjust${buttonNumber}")) { input "fanAdjust_${buttonNumber}pushed", "capability.switchLevel", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "fanAdjust${buttonNumber}held", "capability.switchLevel", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section("Shades (Adjust - Up, Down, or Stop)", hideable: true, hidden: !shallHide("shadeAdjust${buttonNumber}")) { input "shadeAdjust_${buttonNumber}pushed", "capability.doorControl", title: "When Pushed", multiple: true, required: false, submitOnChange: collapseAll if(showHeld()) input "shadeAdjust${buttonNumber}held", "capability.doorControl", title: "When Held", multiple: true, required: false, submitOnChange: collapseAll } section(" "){} section("Set Mode", hideable: true, hidden: !shallHide("mode${buttonNumber}")) { input "mode_${buttonNumber}pushed", "mode", title: "When Pushed", required: false, submitOnChange: collapseAll if(showHeld())input "mode${buttonNumber}held", "mode", title: "When Held", required: false, submitOnChange: collapseAll } def phrases = location.helloHome?.getPhrases()*.label if (phrases) { section("Run Routine", hideable: true, hidden: !shallHide("phrase${buttonNumber}")) { //log.trace phrases input "phrase_${buttonNumber}pushed", "enum", title: "When Pushed", required: false, options: phrases, submitOnChange: collapseAll if(showHeld()) input "phrase${buttonNumber}held", "enum", title: "When Held", required: false, options: phrases, submitOnChange: collapseAll } } section("Notifications:\nSMS, In App or Both", hideable: true, hidden: !shallHide("notifications${buttonNumber}")) { paragraph "****\nWHEN PUSHED\n****" input "notifications_${buttonNumber}pushed", "text", title: "Message", description: "Enter message to send", required: false, submitOnChange: collapseAll input "phone${buttonNumber}_pushed","phone" ,title: "Send Text To", description: "Enter phone number", required: false, submitOnChange: collapseAll input "valNotify${buttonNumber}pushed","bool" ,title: "Notify In App?", required: false, defaultValue: false, submitOnChange: collapseAll if(showHeld()) paragraph "\nWHEN HELD\n" if(showHeld()) input "notifications${buttonNumber}held", "text", title: "Message", description: "Enter message to send", required: false, submitOnChange: collapseAll if(showHeld()) input "phone${buttonNumber}_held", "phone", title: "Send Text To", description: "Enter phone number", required: false, submitOnChange: collapseAll if(showHeld()) input "valNotify${buttonNumber}held", "bool", title: "Notify In App?", required: false, defaultValue: false, submitOnChange: collapseAll
} if(enableSpec()){ section(" "){} section("Special", hideable: true, hidden: !shallHide("container${buttonNumber}")) { input "container_${buttonNumber}pushed", "device.VirtualContainer", title: "When Pushed", required: false, submitOnChange: collapseAll if(showHeld())input "container${buttonNumber}_held", "device.VirtualContainer", title: "When Held", required: false, submitOnChange: collapseAll } } } }
def enableSpec() { return false }
def showHeld() { if(state.buttonType.contains("100+ ")) return false else return true }
def shallHide(myFeature) { if(collapseAll) return (settings["${myFeature}_pushed"]||settings["${myFeature}_held"]||settings["${myFeature}"]) return true }
def getDescription(dNumber) { def descript = "Nothing Configured" def anySettings = settings.find{it.key.contains("${dNumber}")} if(anySettings) descript = "PUSHED:"+getDescDetails(dNumber,"_pushed")+"\n\nHELD:"+getDescDetails(dNumber,"_held")//"CONFIGURED : Tap to edit" return descript }
def getDescDetails(bNum, type){ def numType=bNum+type def preferenceNames = settings.findAll{it.key.contains("_${numType}")}.sort() //get all configured settings that: match button# and type, AND are not false if(!preferenceNames){ return " Not Configured" } else { def formattedPage ="" preferenceNames.each {eachPref-> def prefDetail = getPreferenceDetails().find{eachPref.key.contains(it.id)} //gets decription of action being performed(eg Turn On) def prefDevice = " : ${eachPref.value}" - "[" - "]" //name of device the action is being performed on (eg Bedroom Fan) def prefSubValue = settings[prefDetail.sub + numType]?:"(!Missing!)" if(prefDetail.type=="normal") formattedPage += "\n- ${prefDetail.desc}${prefDevice}" if(prefDetail.type=="hasSub") formattedPage += "\n- ${prefDetail.desc}${prefSubValue}${prefDevice}" if(prefDetail.type=="bool") formattedPage += "\n- ${prefDetail.desc}" } return formattedPage } }
def installed() { initialize() }
def updated() { unsubscribe() initialize() }
def initialize() { log.debug "INITIALIZED with settings: ${settings}" app.label==app.name?app.updateLabel(defaultLabel()):app.updateLabel(app.label) subscribe(buttonDevice, "button", buttonEvent) state.lastshadesUp = true }
def defaultLabel() { return "${buttonDevice} Mapping" }
def getPreferenceDetails(){ def detailMappings = [[id:'lights',desc:'Toggle On/Off',comm:toggle, type:"normal"], [id:'lightsDT', desc:'Toggle Off/Dim to ', comm:dimToggle, sub:"valDT", type:"hasSub"], [id:'lightOn',desc:'Turn On ',comm:turnOn, type:"normal"], [id:"lightOff",desc:'Turn Off',comm:turnOff, type:"normal"], [id:"lightDim",desc:'Dim to ',comm:turnDim, sub:"valLight", type:"hasSub"], [id:"lightD2m",desc:'Dim to ',comm:turnDim, sub:"valLight2", type:"hasSub"], [id:'dimPlus',desc:'Brightness +',comm:levelUp, sub:"valDimP", type:"hasSub"], [id:'dimMinus',desc:'Brightness -',comm:levelDown, sub:"valDimM", type:"hasSub"], [id:"fanAdjust",desc:'Adjust',comm:adjustFan, type:"normal"], [id:"shadeAdjust",desc:'Adjust',comm:adjustShade, type:"normal"], [id:"locks",desc:'Lock',comm:setUnlock, type:"normal"], [id:"speakerpp",desc:'Toggle Play/Pause',comm:speakerplaystate, type:"normal"], [id:'speakernt',desc:'Next Track',comm:speakernexttrack, type:"normal"], [id:'speakermu',desc:'Mute',comm:speakermute, type:"normal"], [id:'speakervu',desc:'Volume +',comm:levelUp, sub:"valSpeakU", type:"hasSub"], [id:"speakervd",desc:'Volume -',comm:levelDown, sub:"valSpeakD", type:"hasSub"], [id:"mode",desc:'Set Mode',comm:changeMode, type:"normal"], [id:"notifications",desc:'Send Push Notification',comm:messageHandle, sub:"valNotify", type:"bool"], [id:'sirens',desc:'Toggle',comm:toggle, type:"normal"], [id:"phone",desc:'Send SMS',comm:smsHandle, sub:"notifications", type:"normal"], [id:"phrase",desc:'Run Routine',comm:runRout, type:"normal"], [id:"container_",desc:'Cycle Playlist',comm:cyclePL, type:"normal"],
] return detailMappings }
def buttonEvent(evt) { if(allOk) { def buttonNumber = evt.jsonData.buttonNumber def pressType = evt.value log.debug "$buttonDevice: Button $buttonNumber was $pressType" def preferenceNames = settings.findAll{it.key.contains("${buttonNumber}${pressType}")} preferenceNames.each{eachPref-> def prefDetail = getPreferenceDetails()?.find{eachPref.key.contains(it.id)} //returns the detail map of id,desc,comm,sub def PrefSubValue = settings["${prefDetail.sub}${buttonNumber}_${pressType}"] //value of subsetting (eg 100) if(prefDetail.sub) "$prefDetail.comm"(eachPref.value,PrefSubValue) else "$prefDetail.comm"(eachPref.value) } } }
def turnOn(devices) { log.debug "Turning On: $devices" devices.on() }
def turnOff(devices) { log.debug "Turning Off: $devices" devices.off() }
def turnDim(devices, level) { log.debug "Dimming (to $level): $devices" devices.setLevel(level) }
def adjustFan(device) { log.debug "Adjusting: $device" def currentLevel = device.currentLevel if(device.currentSwitch == 'off') device.setLevel(15) else if (currentLevel < 34) device.setLevel(50) else if (currentLevel < 67) device.setLevel(90) else device.off() }
def adjustShade(device) { log.debug "Shades: $device = ${device.currentMotor} state.lastUP = $state.lastshadesUp" if(device.currentMotor in ["up","down"]) { state.lastshadesUp = device.currentMotor == "up" device.stop() } else { state.lastshadesUp ? device.down() : device.up() // if(state.lastshadesUp) device.down() // else device.up() state.lastshadesUp = !state.lastshadesUp } }
def speakerplaystate(device) { log.debug "Toggling Play/Pause: $device" device.currentValue('status').contains('playing')? device.pause() : device.play() }
def speakernexttrack(device) { log.debug "Next Track Sent to: $device" device.nextTrack() }
def speakermute(device) { log.debug "Toggling Mute/Unmute: $device" device.currentValue('mute').contains('unmuted')? device.mute() : device.unmute() }
def levelUp(device, inclevel) { log.debug "Incrementing Level (by +$inclevel: $device" def currentVol = device.currentValue('level')[0] //currentlevel return a list...[0] is first item in list ie volume level def newVol = currentVol + inclevel device.setLevel(newVol) log.debug "Level increased by $inclevel to $newVol" }
def levelDown(device, declevel) { log.debug "Decrementing Level (by -declevel: $device" def currentVol = device.currentValue('level')[0] def newVol = currentVol.toInteger()-declevel device.setLevel(newVol) log.debug "Level decreased by $declevel to $newVol" }
def setUnlock(devices) { log.debug "Locking: $devices" devices.lock() }
def toggle(devices) { log.debug "Toggling: $devices" if (devices.currentValue('switch').contains('on')) { devices.off() } else if (devices.currentValue('switch').contains('off')) { devices.on() } else if (devices*.currentValue('alarm').contains('off')) { devices.siren() } else { devices.on() } }
def dimToggle(devices, dimLevel) { log.debug "Toggling On/Off | Dimming (to $dimLevel): $devices" if (devices*.currentValue('switch').contains('on')) devices.off() else devices.setLevel(dimLevel) }
def runRout(rout){ log.debug "Running: $rout" location.helloHome.execute(rout) }
def messageHandle(msg, inApp) { if(inApp==true) { log.debug "Push notification sent" sendPush(msg) } }
def smsHandle(phone, msg){ log.debug "SMS sent" sendSms(phone, msg ?:"No custom text entered on: $app.label") }
def changeMode(mode) { log.debug "Changing Mode to: $mode" if (location.mode != mode && location.modes?.find { it.name == mode }) setLocationMode(mode) }
def cyclePL(device) { //int currPL = device.currentValue('lastRun') // int nextPL = currPL+1 device.cycleChild() //device.on(nextPL)
}
// 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 || manualCount) ? false : true }
private timeIntervalLabel() { (starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : "" }
private def textHelp() { def text = section("User's Guide - Advanced Button Controller") { paragraph "This smartapp allows you to use a device with buttons including, but not limited to:\n\n Aeon Labs Minimotes\n"+ " HomeSeer HS-WD100+ switches\n HomeSeer HS-WS100+ switches\n Lutron Picos**\n\n"+ "It is a heavily modified version of @dalec's 'Button Controller Plus' which is in turn"+ " a version of @bravenel's 'Button Controller+'." } section("Some of the included changes are:"){ paragraph "A complete revamp of the configuration flow. You can now tell at a glance, what has been configured for each button."+ "The button configuration page has been collapsed by default for easier navigation." paragraph "The original apps were hardcoded to allow configuring 4 or 6 button devices."+ " This app will automatically detect the number of buttons on your device or allow you to manually"+ " specify (only needed if device does not report on its own)." paragraph "Allows you to give your buton device full speaker control including: Play/Pause, NextTrack, Mute, VolumeUp/Down."+ "(Standard Pico remotes can be converted to Audio Picos)\n\nThe additional control options have been highlighted below." } section("Available Control Options are:"){ paragraph " Switches - Toggle \n"+ " Switches - Turn On \n"+ " Switches - Turn Off \n"+ " Dimmers - Toggle \n"+ " Dimmers - Set Level (Group 1) \n"+ " Dimmers - Set Level (Group 2) \n"+ " Dimmers - Inc Level \n"+ " Dimmers - Dec Level \n"+ " Fans - Low, Medium, High, Off \n"+ " Shades - Up, Down, or Stop \n"+ " Locks - Unlock Only \n"+ " Speaker - Play/Pause \n"+ " Speaker - Next Track \n"+ " Speaker - Mute/Unmute \n"+ " Speaker - Volume Up \n"+ " Speaker - Volume Down \n"+ " Set Modes \n"+ " Run Routines \n"+ " Sirens - Toggle \n"+ " Push Notifications \n"+ " SMS Notifications" } section ("** Quirk for HS-WD100+ on Button 5 & 6:"){ paragraph "Because a dimmer switch already uses Press&Hold to manually set the dimming level"+ " please be aware of this operational behavior. If you only want to manually change"+ " the dim level to the lights that are wired to the switch, you will automatically"+ " trigger the 5/6 button event as well. And the same is true in reverse. If you"+ " only want to trigger a 5/6 button event action with Press&Hold, you will be manually"+ " changing the dim level of the switch simultaneously as well.\n"+ "This quirk doesn't exist of course with the HS-HS100+ since it is not a dimmer." } section("*** Lutron Pico Requirements:"){ paragraph "Lutron Picos are not natively supported by SmartThings. A Lutron SmartBridge Pro, a device running @njschwartz's python script (or node.js) and the Lutron Caseta Service Manager"+ " SmartApp are also required for this functionality!\nSearch the forums for details." } }
def getSpecText(){ if(state.buttonType == "Lutron Pico") { switch (state.currentButton){ case 1: return "Top Button"; break case 2: return "Bottom Button"; break case 3: return "Middle Button";break case 4: return "Up Button"; break case 5: return "Down Button"; break
} } if(state.buttonType.contains("Aeon Minimote")) { switch (state.currentButton){ case 1: return "Top Left Button"; break case 2: return "Top Right Button"; break case 3: return "Lower Left Button";break case 4: return "Lower Right"; break } } if(state.buttonType.contains("WD100+ Dimmer")) { switch (state.currentButton){ case 1: return "Double-Tap Upper Paddle"; break case 2: return "Double-Tap Lower Paddle"; break case 3: return "Triple-Tap Upper Paddle";break case 4: return "Triple-Tap Lower Paddle"; break case 5: return "Press & Hold Upper Paddle\n(See user guide for quirks)"; break case 6: return "Press & Hold Lower Paddle\n(See user guide for quirks)"; break case 7: return "Single Tap Upper Paddle\n(See user guide for quirks)"; break case 8: return "Single Tap Lower Paddle\n(See user guide for quirks)"; break } } if(state.buttonType.contains("WS100+ Switch")) { switch (state.currentButton){ case 1: return "Double-Tap Upper Paddle"; break case 2: return "Double-Tap Lower Paddle"; break case 3: return "Triple-Tap Upper Paddle";break case 4: return "Triple-Tap Lower Paddle"; break case 5: return "Press & Hold Upper Paddle"; break case 6: return "Press & Hold Lower Paddle"; break case 7: return "Single Tap Upper Paddle";break case 8: return "Single Tap Lower Paddle"; break } } if(state.buttonType.contains("Inovelli")) { switch (state.currentButton){ case 1: return "NOT OPERATIONAL - DO NOT USE"; break case 2: return "2X Tap Upper Paddle = Pushed\n2X Tap Lower Paddle = Held"; break case 3: return "3X Tap Upper Paddle = Pushed\n3X Tap Lower Paddle = Held"; break case 4: return "4X Tap Upper Paddle = Pushed\n4X Tap Lower Paddle = Held";break case 5: return "5X Tap Upper Paddle = Pushed\n5X Tap Lower Paddle = Held"; break case 6: return "Hold Upper Paddle = Pushed\nHold Lower Paddle = Held"; break } } return "Not Specified By Device" }
/*FOR NEW INPUTS//////////////
FOR NEW BUTTON DEVICE TYPES///////////////
*/