[RELEASE] Alternative Handlers for Aeon Minimote UI (With Location Mode)

Preface

This is my first ‘release’ and it is heavily based on some other work and help from others. I want to be sure I am appropriately crediting folks. If I have not followed correct protocol anywhere please let me know. It is not my intention to take credit for work that is not mine.


I have made various changes to a handler that @kyse first developed (original release: Improved UI for Aeon Minimote). Much credit goes to @kyse for writing the original handler.

The main changes I made are:

  1. I had to make some changes for iOS due to more recent ST mobile app updates that caused some rendering issues.
  2. Changed from setting up the buttons as a list view to setting a tile indivually for each button. This will allow the adventurous to assign an icon to each button (in the code). Otherwise there’s no difference in what you see.
  3. I also got a request to have the ability to show location mode. This may have limited use cases but hopefully some will find it useful.

In order to show mode you will first need a helper SmartApp. An instance of this needs to be installed for each Minimote. A big thanks to @gkl_sf for help with creating a skinny version of his Mode Helper app (Mode Switch device/app).

I am not proficient in publishing in GitHub yet so I am posting the code directly here.

SmartApp Code

As mentioned above, once you have created this in your IDE you will need to install a new instance in your ST app for each Minimote you want to show the mode on.

/**
 *  mode_switch_helper v1 for mode_switch virtual device
 *
 *  Copyright 2017 Nezmo68
 *
 *  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.
 *
 */
 
definition(
    name: "Mode Helper",
    namespace: "Nezmo68",
    author: "Nezmo",
    description: "Mode helper",
    category: "Mode Magic",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")

preferences {
	section {
		input "theDTH", "capability.actuator"
	}
}

def installed() {
	log.debug "Installed with settings: ${settings}"

	initialize()
}

def updated() {
	log.debug "Updated with settings: ${settings}"

	unsubscribe()
	initialize()
}
def initialize() {
    subscribe(location, "mode", locationModeChanged)
}

def locationModeChanged(evt) { //send mode change to device
    theDTH.locationModeChanged(evt.value)
    log.debug "current device mode is ${currentDeviceMode}, system mode changed to ${evt.value}"
}

For the new Handler I have actually created two versions. Version 1 sets up a main tile and shows the mode there, Version 2 shows the mode below the button tiles.

Device Handler code for Version 1

/*
 *
 *  Modified by: Nezmo
 *  Modified and extended from SmartThings Kyse Aeon Minimote Device Handler Template.
 *  Changed to move away from using a list to display buttons.
 *
 *  Copyright 2015 SmartThings
 *
 *  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.
 *
 */

metadata {
	definition (name: "Nezmo's Aeon Minimote 4", namespace: "Nezmo68", author: "Nezmo") {
		capability "Actuator"
		capability "Button"
		capability "Configuration"
		capability "Sensor"
        
		attribute "numButtons", "STRING"
        
        // Virtual Button Attributes for defining button labels.
        attribute "lblPush1", "STRING"
        attribute "lblHold1", "STRING"
        attribute "lblPush2", "STRING"
        attribute "lblHold2", "STRING"
        attribute "lblPush3", "STRING"
        attribute "lblHold3", "STRING"
        attribute "lblPush4", "STRING"
        attribute "lblHold4", "STRING"
        
		command "pushed"
        command "held"
        command "pushed", [int]
        command "held", [int]
        command "push1"
        command "hold1"
        command "push2"
        command "hold2"
        command "push3"
        command "hold3"
        command "push4"
        command "hold4"
        command "locationModeChanged", ["string"]

		fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B", outClusters: "0x26,0x2B"
		fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B,0x85,0x84", outClusters: "0x26" // old style with numbered buttons
	}

	simulator {
		status "pushed 1":  "command: 2001, payload: 01"
		status "held 1":  "command: 2001, payload: 15"
		status "pushed 2":  "command: 2001, payload: 29"
		status "held 2":  "command: 2001, payload: 3D"
		status "pushed 3":  "command: 2001, payload: 51"
		status "held 3":  "command: 2001, payload: 65"
		status "pushed 4":  "command: 2001, payload: 79"
		status "held 4":  "command: 2001, payload: 8D"
		status "wakeup":  "command: 8407, payload: "
	}
    
    preferences {
    	section ("Labels") {
            input("lblPush1", "text", title: "Label for Button 1 Push?", required: true, displayDuringSetup: true, defaultValue: "Push 1", description: "Label for the first push button.")  
            input("lblHold1", "text", title: "Label for Button 1 Hold?", required: true, displayDuringSetup: true, defaultValue: "Hold 1", description: "Label for the first hold button.")  
            input("lblPush2", "text", title: "Label for Button 2 Push?", required: true, displayDuringSetup: true, defaultValue: "Push 2", description: "Label for the second push button.")  
            input("lblHold2", "text", title: "Label for Button 2 Hold?", required: true, displayDuringSetup: true, defaultValue: "Hold 2", description: "Label for the second hold button.")  
            input("lblPush3", "text", title: "Label for Button 3 Push?", required: true, displayDuringSetup: true, defaultValue: "Push 3", description: "Label for the third push button.")  
            input("lblHold3", "text", title: "Label for Button 3 Hold?", required: true, displayDuringSetup: true, defaultValue: "Hold 3", description: "Label for the third hold button.")  
            input("lblPush4", "text", title: "Label for Button 4 Push?", required: true, displayDuringSetup: true, defaultValue: "Push 4", description: "Label for the fourth push button.")  
            input("lblHold4", "text", title: "Label for Button 4 Hold?", required: true, displayDuringSetup: true, defaultValue: "Hold 4", description: "Label for the fourth hold button.")  
        }
    }

	tiles (scale: 2) {
            multiAttributeTile(name:"modeCurrent", type: "generic", width: 6, height: 4){
			tileAttribute ("device", key: "PRIMARY_CONTROL") {
				attributeState "default", label: '', icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote-transparent.png", backgroundColor: "#00a0dc", decoration: "flat"
				}
                tileAttribute ("device.modeCurrent", key: "SECONDARY_CONTROL") {
				attributeState "default", label:'Current Mode: ${currentValue}'
				}
            }
            standardTile("Button 1", "device.button", width: 2, height: 2) {
            	state("default", label: "Button 1", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
            }
            valueTile("Push 1", "device.lblPush${1}", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push${1}", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
            }
            valueTile("Hold 1", "device.lblHold${1}", width: 2, height: 2, decoration: "flat") {
            	state("default", label: '${currentValue}', action: "hold${1}", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true) 
            }
            standardTile("Button 2", "device.button", width: 2, height: 2) {
            	state("default", label: "Button 2", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
            }
            valueTile("Push 2", "device.lblPush${2}", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push${2}", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
            }
            valueTile("Held 2", "device.lblHold${2}", width: 2, height: 2, decoration: "flat") {
            	state("default", label: '${currentValue}', action: "hold${2}", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true) 
            }
            standardTile("Button 3" ,"device.button", width: 2, height: 2) {
            	state("default", label: "Button 3", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
            }
            valueTile("Push 3", "device.lblPush${3}", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push${3}", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
            }
            valueTile("Held 3", "device.lblHold${3}", width: 2, height: 2, decoration: "flat") {
            	state("default", label: '${currentValue}', action: "hold${3}", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true) 
            }
            standardTile("Button 4", "device.button", width: 2, height: 2) {
            	state("default", label: "Button 4", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
            }
            valueTile("Push 4", "device.lblPush${4}", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push${4}", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
            }
            valueTile("Held 4", "device.lblHold${4}", width: 2, height: 2, decoration: "flat") {
            	state("default", label: '${currentValue}', action: "hold${4}", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true) 
       		}
    		standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
      			state "configure", label: '', action:"configuration.configure", icon:"st.secondary.configure"
    		}
	}
}
def locationModeChanged(newMode) {
sendEvent(name: "modeCurrent", value: newMode)
}

def installed() {
initLabels()
}

def updated() {
	initLabels()
}

def initLabels() {
    (1..4).each { button ->
    	["Push","Hold"].each { action ->
           	def descriptionText = "Updating button ${button} ${action}"
            def settingName = "lbl${action}${button}"
            log.debug descriptionText + ": ${settings[settingName]}"
			sendEvent(name: "lbl${action}${button}", value: "${settings[settingName]}", descriptionText: descriptionText, isStateChange: true, displayed: false)
		}
    }
}

def parse(String description) {
	def results = []
	if (description.startsWith("Err")) {
	    results = createEvent(descriptionText:description, displayed:true)
	} else {
		def cmd = zwave.parse(description, [0x2B: 1, 0x80: 1, 0x84: 1])
    	if(cmd) results += zwaveEvent(cmd)
		if(!results) results = [ descriptionText: cmd, displayed: false ]
	}
	//log.debug("Parsed '$description' to $results")
	return results
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
	def results = [createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)]
    
    results += configurationCmds().collect{ response(it) }
	results << response(zwave.wakeUpV1.wakeUpNoMoreInformation().format())

	return results
}

def buttonEvent(button, held) {
	// Leaving value as pushed or held to stay compatible with Button Controller Smart App for now.
	button = button as Integer
	if (held) {
		createEvent(name: "button", value: "held", data: [buttonNumber: button, action: (held ? "held" : "pushed")], source: "DEVICE", descriptionText: "$device.displayName button $button was held", isStateChange: true)
	} else {
		createEvent(name: "button", value: "pushed", data: [buttonNumber: button, action: (held ? "held" : "pushed")], source: "DEVICE", descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
	}
}

def zwaveEvent(physicalgraph.zwave.commands.sceneactivationv1.SceneActivationSet cmd) {
	Integer button = ((cmd.sceneId + 1) / 2) as Integer
	Boolean held = !(cmd.sceneId % 2)
	buttonEvent(button, held)
}

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
	Integer button = (cmd.value / 40 + 1) as Integer
	Boolean held = (button * 40 - cmd.value) <= 20
	buttonEvent(button, held)
}

def zwaveEvent(physicalgraph.zwave.Command cmd) {
	[ descriptionText: "$device.displayName: $cmd", linkText:device.displayName, displayed: false ]
}

def configurationCmds() {
	def cmds = []
	def hubId = zwaveHubNodeId
	(1..4).each { button ->
		cmds << zwave.configurationV1.configurationSet(parameterNumber: 240+button, scaledConfigurationValue: 1).format()
	}
	(1..4).each { button ->
		cmds << zwave.configurationV1.configurationSet(parameterNumber: (button-1)*40, configurationValue: [hubId, (button-1)*40 + 1, 0, 0]).format()
		cmds << zwave.configurationV1.configurationSet(parameterNumber: (button-1)*40 + 20, configurationValue: [hubId, (button-1)*40 + 21, 0, 0]).format()
	}
	cmds
}

def configure() {
	// Set the number of buttons to 4
	sendEvent(name: "numButtons", value: "4", displayed: false)

	def cmds = configurationCmds()
	//log.debug("Sending configuration: $cmds")
	return cmds
}

def push1() {
	pushed(1)
}

def push2() {
	pushed(2)
}

def push3() {
	pushed(3)
}

def push4() {
	pushed(4)
}

def pushed(button) {
	sendEvent(name: "button", value: "pushed", data: [buttonNumber: button, action: "pushed"], source: "COMMAND", descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
}

def hold1() {
	held(1)
}

def hold2() {
	held(2)
}

def hold3() {
	held(3)
}

def hold4() {
	held(4)
}

def held(button) {
    sendEvent(name: "button", value: "held", data: [buttonNumber: button, action: "held"], source: "COMMAND", descriptionText: "$device.displayName button $button was held", isStateChange: true)
}

Device Handler code for Version 2

/*
 *
 *  Modified by: Nezmo
 *  Modified and extended from SmartThings Kyse Aeon Minimote Device Handler Template.
 *  Changed to move away from using a list to display buttons.
 *
 *  Copyright 2015 SmartThings
 *
 *  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.
 *
 */

metadata {
	definition (name: "Nezmo's Aeon Minimote 3", namespace: "Nezmo68", author: "Nezmo") {
		capability "Actuator"
		capability "Button"
		capability "Configuration"
		capability "Sensor"
        
		attribute "numButtons", "STRING"
        
        // Virtual Button Attributes for defining button labels.
        attribute "lblPush1", "STRING"
        attribute "lblHold1", "STRING"
        attribute "lblPush2", "STRING"
        attribute "lblHold2", "STRING"
        attribute "lblPush3", "STRING"
        attribute "lblHold3", "STRING"
        attribute "lblPush4", "STRING"
        attribute "lblHold4", "STRING"
        
		command "pushed"
        command "held"
        command "pushed", [int]
        command "held", [int]
        command "push1"
        command "hold1"
        command "push2"
        command "hold2"
        command "push3"
        command "hold3"
        command "push4"
        command "hold4"
        command "locationModeChanged", ["string"]

		fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B", outClusters: "0x26,0x2B"
		fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B,0x85,0x84", outClusters: "0x26" // old style with numbered buttons
	}

	simulator {
		status "pushed 1":  "command: 2001, payload: 01"
		status "held 1":  "command: 2001, payload: 15"
		status "pushed 2":  "command: 2001, payload: 29"
		status "held 2":  "command: 2001, payload: 3D"
		status "pushed 3":  "command: 2001, payload: 51"
		status "held 3":  "command: 2001, payload: 65"
		status "pushed 4":  "command: 2001, payload: 79"
		status "held 4":  "command: 2001, payload: 8D"
		status "wakeup":  "command: 8407, payload: "
	}
    
    preferences {
    	section ("Labels") {
            input("lblPush1", "text", title: "Label for Button 1 Push?", required: true, displayDuringSetup: true, defaultValue: "Push 1", description: "Label for the first push button.")  
            input("lblHold1", "text", title: "Label for Button 1 Hold?", required: true, displayDuringSetup: true, defaultValue: "Hold 1", description: "Label for the first hold button.")  
            input("lblPush2", "text", title: "Label for Button 2 Push?", required: true, displayDuringSetup: true, defaultValue: "Push 2", description: "Label for the second push button.")  
            input("lblHold2", "text", title: "Label for Button 2 Hold?", required: true, displayDuringSetup: true, defaultValue: "Hold 2", description: "Label for the second hold button.")  
            input("lblPush3", "text", title: "Label for Button 3 Push?", required: true, displayDuringSetup: true, defaultValue: "Push 3", description: "Label for the third push button.")  
            input("lblHold3", "text", title: "Label for Button 3 Hold?", required: true, displayDuringSetup: true, defaultValue: "Hold 3", description: "Label for the third hold button.")  
            input("lblPush4", "text", title: "Label for Button 4 Push?", required: true, displayDuringSetup: true, defaultValue: "Push 4", description: "Label for the fourth push button.")  
            input("lblHold4", "text", title: "Label for Button 4 Hold?", required: true, displayDuringSetup: true, defaultValue: "Hold 4", description: "Label for the fourth hold button.")  
        }
    }

	tiles (scale: 2) {
            standardTile("Button 1", "device.button", width: 2, height: 2) {
            	state("default", label: "Button 1", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
            }
            valueTile("Push 1", "device.lblPush${1}", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push${1}", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
            }
            valueTile("Hold 1", "device.lblHold${1}", width: 2, height: 2, decoration: "flat") {
            	state("default", label: '${currentValue}', action: "hold${1}", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true) 
            }
            standardTile("Button 2", "device.button", width: 2, height: 2) {
            	state("default", label: "Button 2", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
            }
            valueTile("Push 2", "device.lblPush${2}", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push${2}", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
            }
            valueTile("Held 2", "device.lblHold${2}", width: 2, height: 2, decoration: "flat") {
            	state("default", label: '${currentValue}', action: "hold${2}", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true) 
            }
            standardTile("Button 3" ,"device.button", width: 2, height: 2) {
            	state("default", label: "Button 3", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
            }
            valueTile("Push 3", "device.lblPush${3}", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push${3}", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
            }
            valueTile("Held 3", "device.lblHold${3}", width: 2, height: 2, decoration: "flat") {
            	state("default", label: '${currentValue}', action: "hold${3}", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true) 
            }
            standardTile("Button 4", "device.button", width: 2, height: 2) {
            	state("default", label: "Button 4", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
            }
            valueTile("Push 4", "device.lblPush${4}", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push${4}", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
            }
            valueTile("Held 4", "device.lblHold${4}", width: 2, height: 2, decoration: "flat") {
            	state("default", label: '${currentValue}', action: "hold${4}", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true) 
            }
            valueTile("deafult", "device", width: 4, height: 1, decoration: "flat") {
       			state("default", label: 'Current Mode:', backgroundColor:"#ffffff")
       		}
            valueTile("modeCurrent", "device.modeCurrent", width: 2, height: 1, decoration: "flat") {
       			state("default", label: '${currentValue}', backgroundColor:"#ffffff")
       		}
            standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
      			state "configure", label: '', action:"configuration.configure", icon:"st.secondary.configure"
    		}
	}
}
def locationModeChanged(newMode) {
sendEvent(name: "modeCurrent", value: newMode)
}

def installed() {
initLabels()
}

def updated() {
	initLabels()
}

def initLabels() {
    (1..4).each { button ->
    	["Push","Hold"].each { action ->
           	def descriptionText = "Updating button ${button} ${action}"
            def settingName = "lbl${action}${button}"
            log.debug descriptionText + ": ${settings[settingName]}"
			sendEvent(name: "lbl${action}${button}", value: "${settings[settingName]}", descriptionText: descriptionText, isStateChange: true, displayed: false)
		}
    }
}

def parse(String description) {
	def results = []
	if (description.startsWith("Err")) {
	    results = createEvent(descriptionText:description, displayed:true)
	} else {
		def cmd = zwave.parse(description, [0x2B: 1, 0x80: 1, 0x84: 1])
    	if(cmd) results += zwaveEvent(cmd)
		if(!results) results = [ descriptionText: cmd, displayed: false ]
	}
	//log.debug("Parsed '$description' to $results")
	return results
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
	def results = [createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)]
    
    results += configurationCmds().collect{ response(it) }
	results << response(zwave.wakeUpV1.wakeUpNoMoreInformation().format())

	return results
}

def buttonEvent(button, held) {
	// Leaving value as pushed or held to stay compatible with Button Controller Smart App for now.
	button = button as Integer
	if (held) {
		createEvent(name: "button", value: "held", data: [buttonNumber: button, action: (held ? "held" : "pushed")], source: "DEVICE", descriptionText: "$device.displayName button $button was held", isStateChange: true)
	} else {
		createEvent(name: "button", value: "pushed", data: [buttonNumber: button, action: (held ? "held" : "pushed")], source: "DEVICE", descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
	}
}

def zwaveEvent(physicalgraph.zwave.commands.sceneactivationv1.SceneActivationSet cmd) {
	Integer button = ((cmd.sceneId + 1) / 2) as Integer
	Boolean held = !(cmd.sceneId % 2)
	buttonEvent(button, held)
}

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
	Integer button = (cmd.value / 40 + 1) as Integer
	Boolean held = (button * 40 - cmd.value) <= 20
	buttonEvent(button, held)
}

def zwaveEvent(physicalgraph.zwave.Command cmd) {
	[ descriptionText: "$device.displayName: $cmd", linkText:device.displayName, displayed: false ]
}

def configurationCmds() {
	def cmds = []
	def hubId = zwaveHubNodeId
	(1..4).each { button ->
		cmds << zwave.configurationV1.configurationSet(parameterNumber: 240+button, scaledConfigurationValue: 1).format()
	}
	(1..4).each { button ->
		cmds << zwave.configurationV1.configurationSet(parameterNumber: (button-1)*40, configurationValue: [hubId, (button-1)*40 + 1, 0, 0]).format()
		cmds << zwave.configurationV1.configurationSet(parameterNumber: (button-1)*40 + 20, configurationValue: [hubId, (button-1)*40 + 21, 0, 0]).format()
	}
	cmds
}

def configure() {
	// Set the number of buttons to 4
	sendEvent(name: "numButtons", value: "4", displayed: false)

	def cmds = configurationCmds()
	//log.debug("Sending configuration: $cmds")
	return cmds
}

def push1() {
	pushed(1)
}

def push2() {
	pushed(2)
}

def push3() {
	pushed(3)
}

def push4() {
	pushed(4)
}

def pushed(button) {
	sendEvent(name: "button", value: "pushed", data: [buttonNumber: button, action: "pushed"], source: "COMMAND", descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
}

def hold1() {
	held(1)
}

def hold2() {
	held(2)
}

def hold3() {
	held(3)
}

def hold4() {
	held(4)
}

def held(button) {
    sendEvent(name: "button", value: "held", data: [buttonNumber: button, action: "held"], source: "COMMAND", descriptionText: "$device.displayName button $button was held", isStateChange: true)
}

Version 1 UI Example

Version 2 UI Example

Any issues please let me know.

UPDATE: Some have reported rendering issues with these on Android. I apologize but I do not have an Android device to test and correct any issues.

4 Likes

i’ve made some changes.

  1. on install/update get current mode
  2. button default values were null when you don’t run setup. so made a check there.
  3. cleaned up some code.

this works for me on my S8 note.

/*
 *
 *  Modified by: Nezmo
 *  Modified and extended from SmartThings Kyse Aeon Minimote Device Handler Template.
 *  Changed to move away from using a list to display buttons.
 *
 *  Copyright 2015 SmartThings
 *
 *  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.
 *
 */

metadata {
	definition (name: "Nezmo's Aeon Minimote 3", namespace: "gn0st1c", author: "Nezmo") {
		capability "Actuator"
		capability "Button"
		capability "Configuration"
		capability "Sensor"

		attribute "numButtons", "STRING"

		// Virtual Button Attributes for defining button labels.
		attribute "lblPush1", "STRING"
		attribute "lblHold1", "STRING"
		attribute "lblPush2", "STRING"
		attribute "lblHold2", "STRING"
		attribute "lblPush3", "STRING"
		attribute "lblHold3", "STRING"
		attribute "lblPush4", "STRING"
		attribute "lblHold4", "STRING"

		command "pushed"
		command "held"
		command "pushed", [int]
		command "held", [int]
		command "push1"
		command "hold1"
		command "push2"
		command "hold2"
		command "push3"
		command "hold3"
		command "push4"
		command "hold4"
		command "locationModeChanged", ["string"]

		fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B", outClusters: "0x26,0x2B"
		fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B,0x85,0x84", outClusters: "0x26" // old style with numbered buttons
	}

	simulator {
		status "pushed 1":  "command: 2001, payload: 01"
		status "held 1":  "command: 2001, payload: 15"
		status "pushed 2":  "command: 2001, payload: 29"
		status "held 2":  "command: 2001, payload: 3D"
		status "pushed 3":  "command: 2001, payload: 51"
		status "held 3":  "command: 2001, payload: 65"
		status "pushed 4":  "command: 2001, payload: 79"
		status "held 4":  "command: 2001, payload: 8D"
		status "wakeup":  "command: 8407, payload: "
	}

	preferences {
		section ("Labels") {
			input("lblPush1", "text", title: "Label for Button 1 Push?", required: true, displayDuringSetup: true, description: "Label for the first push button.")
			input("lblHold1", "text", title: "Label for Button 1 Hold?", required: true, displayDuringSetup: true, description: "Label for the first hold button.")
			input("lblPush2", "text", title: "Label for Button 2 Push?", required: true, displayDuringSetup: true, description: "Label for the second push button.")
			input("lblHold2", "text", title: "Label for Button 2 Hold?", required: true, displayDuringSetup: true, description: "Label for the second hold button.")
			input("lblPush3", "text", title: "Label for Button 3 Push?", required: true, displayDuringSetup: true, description: "Label for the third push button.")
			input("lblHold3", "text", title: "Label for Button 3 Hold?", required: true, displayDuringSetup: true, description: "Label for the third hold button.")
			input("lblPush4", "text", title: "Label for Button 4 Push?", required: true, displayDuringSetup: true, description: "Label for the fourth push button.")
			input("lblHold4", "text", title: "Label for Button 4 Hold?", required: true, displayDuringSetup: true, description: "Label for the fourth hold button.")
		}
	}

	tiles (scale: 2) {
			standardTile("Button 1", "device.button", width: 2, height: 2) {
				state("default", label: "Button 1", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
			}
			valueTile("Push 1", "device.lblPush1", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push1", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
			}
			valueTile("Hold 1", "device.lblHold1", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "hold1", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true)
			}
			standardTile("Button 2", "device.button", width: 2, height: 2) {
				state("default", label: "Button 2", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
			}
			valueTile("Push 2", "device.lblPush2", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push2", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
			}
			valueTile("Held 2", "device.lblHold2", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "hold2", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true)
			}
			standardTile("Button 3" ,"device.button", width: 2, height: 2) {
				state("default", label: "Button 3", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
			}
			valueTile("Push 3", "device.lblPush3", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push3", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
			}
			valueTile("Held 3", "device.lblHold3", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "hold3", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true)
			}
			standardTile("Button 4", "device.button", width: 2, height: 2) {
				state("default", label: "Button 4", defaultState: true, backgroundColor: "#ffffff", icon: "https://raw.githubusercontent.com/Nezmo68/SmartThingsPublic/master/Minimote.png", canChangeIcon: true, canChangeBackground: true)
			}
			valueTile("Push 4", "device.lblPush4", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "push4", defaultState: true, backgroundColor: "#33cc33", canChangeBackground: true)
			}
			valueTile("Held 4", "device.lblHold4", width: 2, height: 2, decoration: "flat") {
				state("default", label: '${currentValue}', action: "hold4", defaultState: true, backgroundColor: "#00a0dc", canChangeBackground: true)
			}
			standardTile("mode", "device", inactiveLabel: false, decoration: "flat", width: 4, height: 1) {
				state "default", label: 'Current Mode:', backgroundColor:"#ffffff"
			}
			valueTile("modeCurrent", "device.modeCurrent", width: 2, height: 1, decoration: "flat") {
				state("default", label: '${currentValue}', backgroundColor:"#ffffff")
			}
			standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
				state "configure", label: '', action:"configuration.configure", icon:"st.secondary.configure"
			}
	}
}
def locationModeChanged(newMode) {
	sendEvent(name: "modeCurrent", value: newMode)
}

def installed() {
	initLabels()
	locationModeChanged(location.mode)
}

def updated() {
	initLabels()
	locationModeChanged(location.mode)
}

def initLabels() {
	(1..4).each { button ->
		["Push","Hold"].each { action ->
			def settingName = "lbl${action}${button}"
			def lbl = settings."${settingName}"
			if (!lbl) lbl = "${action} ${button}"

			def descriptionText = "Updating button ${button} ${action} as ${lbl}"
			log.debug descriptionText
			sendEvent(name: "lbl${action}${button}", value: "${lbl}", descriptionText: descriptionText, isStateChange: true, displayed: false)
		}
	}
}

def parse(String description) {
	def results = []
	if (description.startsWith("Err")) {
		results = createEvent(descriptionText:description, displayed:true)
	} else {
		def cmd = zwave.parse(description, [0x2B: 1, 0x80: 1, 0x84: 1])
		if (cmd) results += zwaveEvent(cmd)
		if (!results) results = [ descriptionText: cmd, displayed: false ]
	}
	//log.debug("Parsed '$description' to $results")
	return results
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
	def results = [createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)]

	results += configurationCmds().collect{ response(it) }
	results << response(zwave.wakeUpV1.wakeUpNoMoreInformation().format())

	return results
}

def buttonEvent(button, held) {
	// Leaving value as pushed or held to stay compatible with Button Controller Smart App for now.
	button = button as Integer
	if (held) {
		createEvent(name: "button", value: "held", data: [buttonNumber: button, action: (held ? "held" : "pushed")], source: "DEVICE", descriptionText: "$device.displayName button $button was held", isStateChange: true)
	} else {
		createEvent(name: "button", value: "pushed", data: [buttonNumber: button, action: (held ? "held" : "pushed")], source: "DEVICE", descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
	}
}

def zwaveEvent(physicalgraph.zwave.commands.sceneactivationv1.SceneActivationSet cmd) {
	Integer button = ((cmd.sceneId + 1) / 2) as Integer
	Boolean held = !(cmd.sceneId % 2)
	buttonEvent(button, held)
}

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
	Integer button = (cmd.value / 40 + 1) as Integer
	Boolean held = (button * 40 - cmd.value) <= 20
	buttonEvent(button, held)
}

def zwaveEvent(physicalgraph.zwave.Command cmd) {
	[ descriptionText: "$device.displayName: $cmd", linkText:device.displayName, displayed: false ]
}

def configurationCmds() {
	def cmds = []
	def hubId = zwaveHubNodeId
	(1..4).each { button ->
		cmds << zwave.configurationV1.configurationSet(parameterNumber: 240+button, scaledConfigurationValue: 1).format()
	}
	(1..4).each { button ->
		cmds << zwave.configurationV1.configurationSet(parameterNumber: (button-1)*40, configurationValue: [hubId, (button-1)*40 + 1, 0, 0]).format()
		cmds << zwave.configurationV1.configurationSet(parameterNumber: (button-1)*40 + 20, configurationValue: [hubId, (button-1)*40 + 21, 0, 0]).format()
	}
	cmds
}

def configure() {
	// Set the number of buttons to 4
	sendEvent(name: "numButtons", value: "4", displayed: false)

	def cmds = configurationCmds()
	//log.debug("Sending configuration: $cmds")
	return cmds
}

def push1() {
	pushed(1)
}

def push2() {
	pushed(2)
}

def push3() {
	pushed(3)
}

def push4() {
	pushed(4)
}

def pushed(button) {
	sendEvent(name: "button", value: "pushed", data: [buttonNumber: button, action: "pushed"], source: "COMMAND", descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
}

def hold1() {
	held(1)
}

def hold2() {
	held(2)
}

def hold3() {
	held(3)
}

def hold4() {
	held(4)
}

def held(button) {
	sendEvent(name: "button", value: "held", data: [buttonNumber: button, action: "held"], source: "COMMAND", descriptionText: "$device.displayName button $button was held", isStateChange: true)
}
2 Likes