Why the set pushed_3x does not take effect?

Hi, All:
On the basis of the document here, I added the information of my own device. The revised document is as follows:

import groovy.json.JsonOutput

/**
 *  Z-Wave Multi Button
 *
 *  Copyright 2019 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: "Z-Wave Multi Button", namespace: "smartthings", author: "SmartThings", mcdSync: true, ocfDeviceType: "x.com.st.d.remotecontroller") {
		capability "Button"
		capability "Battery"
		capability "Sensor"
		capability "Health Check"
		capability "Configuration"

		// While adding new device to this DTH, remember to update method getProdNumberOfButtons()
		fingerprint mfr: "010F", prod: "1001", model: "1000", deviceJoinName: "Fibaro Remote Control", mnmn: "SmartThings", vid: "generic-6-button" //EU //Fibaro KeyFob
		fingerprint mfr: "010F", prod: "1001", model: "2000", deviceJoinName: "Fibaro Remote Control", mnmn: "SmartThings", vid: "generic-6-button"  //US //Fibaro KeyFob
		fingerprint mfr: "0371", prod: "0102", model: "0003", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //US //Aeotec NanoMote Quad
		fingerprint mfr: "0371", prod: "0002", model: "0003", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //EU //Aeotec NanoMote Quad
		fingerprint mfr: "0086", prod: "0101", model: "0058", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //US //Aeotec KeyFob
		fingerprint mfr: "0086", prod: "0001", model: "0058", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //EU //Aeotec KeyFob
		fingerprint mfr: "010F", prod: "1001", model: "3000", deviceJoinName: "Fibaro Remote Control", mnmn: "SmartThings", vid: "generic-6-button" //AU //Fibaro KeyFob
		fingerprint mfr: "0312", prod: "0924", model: "D001", deviceJoinName: "Minoston Remote Control", mnmn: "SmartThings", vid: "generic-4-button"//ZW924
	}

	tiles(scale: 2) {
		multiAttributeTile(name: "button", type: "generic", width: 6, height: 4, canChangeIcon: true) {
			tileAttribute("device.button", key: "PRIMARY_CONTROL") {
				attributeState "default", label: ' ', icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
			}
		}
		valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "battery", label:'${currentValue}% battery', unit:""
		}

		main "button"
		details(["button", "battery"])
	}
}

def installed() {
	sendEvent(name: "button", value: "pushed", isStateChange: true, displayed: false)
	sendEvent(name: "supportedButtonValues", value: supportedButtonValues.encodeAsJSON(), displayed: false)
	initialize()
}

def updated() {
	runIn(2, "initialize", [overwrite: true])
}


def initialize() {
	if(isUntrackedAeotec() || isUntrackedFibaro()) {
		sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false)
	} else {
		sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 10 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
	}

	response([
			secure(zwave.batteryV1.batteryGet()),
			"delay 2000",
			secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
	])
}

def configure() {
	def cmds = []
	if(isAeotecKeyFob()) {
		cmds += secure(zwave.configurationV1.configurationSet(parameterNumber: 250, scaledConfigurationValue: 1))
		//makes Aeotec KeyFob communicate with primary controller
	}
	if(isFibaro()) {
		for (def parameter : 21..26) {
			cmds += secure(zwave.configurationV1.configurationSet(parameterNumber: parameter, scaledConfigurationValue: 15))
			//Makes Fibaro KeyFob buttons send all kind of supported events
		}
	}
	setupChildDevices()
	cmds
}

def setupChildDevices(){
	def numberOfButtons = prodNumberOfButtons[zwaveInfo.prod]
	sendEvent(name: "numberOfButtons", value: numberOfButtons, displayed: false)
	if(!childDevices) {
		addChildButtons(numberOfButtons)

		for(def endpoint : 1..prodNumberOfButtons[zwaveInfo.prod]) {
			def event = createEvent(name: "button", value: "pushed", isStateChange: true)
			sendEventToChild(endpoint, event)
		}
	}
}

def parse(String description) {
	def result = []
	if (description.startsWith("Err")) {
		result = createEvent(descriptionText:description, isStateChange:true)
	} else {
		def cmd = zwave.parse(description, [0x84: 1])
		if (cmd) {
			result += zwaveEvent(cmd)
		}
	}
	log.debug "Parse returned: ${result}"
	result
}

def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
	def encapsulatedCommand = cmd.encapsulatedCommand([0x84: 1])
	if (encapsulatedCommand) {
		zwaveEvent(encapsulatedCommand)
	} else {
		log.warn "Unable to extract encapsulated cmd from $cmd"
		createEvent(descriptionText: cmd.toString())
	}
}

def zwaveEvent(physicalgraph.zwave.commands.sceneactivationv1.SceneActivationSet cmd) {
	// Below handler was tested with Aoetec KeyFob and probably will work only with it
	def value = cmd.sceneId % 2 ? "pushed" : "held"
	def childId = (int)(cmd.sceneId / 2) + (cmd.sceneId % 2)
	def description = "Button no. ${childId} was ${value}"
	def event = createEvent(name: "button", value: value, descriptionText: description, data: [buttonNumber: childId], isStateChange: true)
	sendEventToChild(childId, event)
	return event
}

def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) {
	def value = eventsMap[(int) cmd.keyAttributes]
	def description = "Button no. ${cmd.sceneNumber} was ${value}"
	def childEvent = createEvent(name: "button", value: value, descriptionText: description, data: [buttonNumber: cmd.sceneNumber], isStateChange: true)
	sendEventToChild(cmd.sceneNumber, childEvent)
	return createEvent(name: "button", value: value, descriptionText: description, data: [buttonNumber: cmd.sceneNumber], isStateChange: true, displayed: false)
}

def sendEventToChild(buttonNumber, event) {
	String childDni = "${device.deviceNetworkId}:$buttonNumber"
	def child = childDevices.find { it.deviceNetworkId == childDni }
	child?.sendEvent(event)
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
	def results = []
	results += createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
	results += response([
			secure(zwave.batteryV1.batteryGet()),
			"delay 2000",
			secure(zwave.wakeUpV1.wakeUpNoMoreInformation())
	])
	results
}

def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
	def map = [ name: "battery", unit: "%", isStateChange: true ]
	state.lastbatt = now()
	if (cmd.batteryLevel == 0xFF) {
		map.value = 1
		map.descriptionText = "$device.displayName battery is low!"
	} else {
		map.value = cmd.batteryLevel
	}
	createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.Command cmd) {
	log.warn "Unhandled command: ${cmd}"
}

private secure(cmd) {
	if(zwaveInfo.zw.contains("s")) {
		zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
	} else {
		cmd.format()
	}
}

private addChildButtons(numberOfButtons) {
	for(def endpoint : 1..numberOfButtons) {
		try {
			String childDni = "${device.deviceNetworkId}:$endpoint"
			def componentLabel = (device.displayName.endsWith(' 1') ? device.displayName[0..-2] : (device.displayName + " ")) + "${endpoint}"
			def child = addChildDevice("Child Button", childDni, device.getHub().getId(), [
					completedSetup: true,
					label         : componentLabel,
					isComponent   : true,
					componentName : "button$endpoint",
					componentLabel: "Button $endpoint"
			])
			child.sendEvent(name: "supportedButtonValues", value: supportedButtonValues.encodeAsJSON(), displayed: false)
		} catch(Exception e) {
			log.debug "Exception: ${e}"
		}
	}
}

private getEventsMap() {[
		0: "pushed",
		1: "held",
		2: "down_hold",
		3: "double",
		4: "pushed_3x"
]}

private getProdNumberOfButtons() {[
		"1001" : 6,
		"0102" : 4,
		"0002" : 4,
		"0101" : 4,
		"0001" : 4,
		"0924":  4
]}

private getSupportedButtonValues() {
	def values = ["pushed", "held"]
	if (isFibaro() || isMinoston()) values += ["double", "down_hold", "pushed_3x"]
	return values
}

private isFibaro() {
	zwaveInfo.mfr?.contains("010F")
}

private isUntrackedFibaro() {
	isFibaro() && zwaveInfo.prod?.contains("1001")
}

private isUntrackedAeotec() {
	zwaveInfo.mfr?.contains("0371") && zwaveInfo.model?.contains("0003")
}

private isAeotecKeyFob() {
	zwaveInfo.mfr?.contains("0086")
}

private isMinoston() {
	zwaveInfo.model == "D001"
}

I expected the device interface for pushed_3x to appear, but it did not actually appear. Can anyone help me find out where the problem is? Except for pushed_3x, other effects are normal.


Four buttons have the same effect.

Hey @chenjun

Do you mean that the automation option is not being enabled or that the state update is not populating the app i.e. the Pressed 3 times message is not popping out?

(ignore the Lock status Push Putton. It is a custom capability that I made to ease the button update on-demand :grin:).

 Thanks!
 I guess what you created should be a virtual device, right? Does it apply to the option vid: "generic-4-button"? If it is not used, the default Button effect can show the effect of pushed_3x. By the way, do you know how to set up a fingerprint for a virtual device?

Hmm… it is interesting @chenjun I just queried the generic-4-button presentation and noticed that it’s only supporting the presentation of pushed, held, double, and Ready states, and since you’re not specifying which vid to use, your DTH is requiring the metadata from it by default. Therefore, even if you’re adding more button values into the supportedButtonValues array, you’ll just get metadata support for the 4 above-mentioned states.

Now, regarding the presentation I used above, I had to create a new presentationId / vid to give support to my custom capability, and by default, the API gave it support to all the button values.

If you want to have a better reference of these presentations, you can query them through the SmartThings CLI (or Presentations API) and then create your own based on your dth:

  • Command to query the generic-4-button:

    st presentation generic-4-button SmartThings --json
    
  • For the custom presentation (referred at the screenshot):

    st presentation 96d480f7-d4b1-3707-9c47-692d3ab78257 --json
    

About your last question, I’m not sure if setting up fingerprints for virtual devices is a valid case, but let me check with our team about that.

Do you have a specific use case for the implementation of fingerprints over virtual devices?

2 Likes

Thanks!
I do not have a specific use case for the implementation of fingerprints over virtual devices. I test with the vid “96d480f7-d4b1-3707-9c47-692d3ab78257”, The interface shows the effect I want, but when I click the buttons from Button1 to Button4 on the interface, the application will prompt an error and exit abnormally.

Great!, I’m glad that we’ve figured that out.

Now, do you mean the Push button that supports the 96d480f7-d4b1-3707-9c47-692d3ab78257 presentation Id (the one illustrated in the screenshot I shared previously)?

In case you do, it is because your DTH isn’t handling the commands for the custom capability that is providing the Push Button display type (againschool57104.lockButton).

If you want to have a reference on how it works, check its definition and presentaiton with the following ST CLI commands:

  • Custon Capabilit Definition:

      st capabilities againschool57104.lockButton --json
    
  • Presentation:

      st capabilities:presentation againschool57104.lockButton --json
    

or add the following monkey patch your DTH:

def unlock() {
    log.trace "button command"
	sendEvent(name: "button", value: "pushed_3x") // or any other button value
    sendEvent(name: "lock", value: "")
}

I have learned to use SmartThings CLI to generate custom capabilities. The capabilities I made can also get the same display effect, but unfortunately, clicking any button will cause an error as below (not with generic-4-button), and Clicking the child button will cause the APP to restart abnormally.


Firstly, in the “main” area, the button “Pressed 3 times” reported a hint when pressed(APP can not restart abnormally), hint as picture show above.
Secondly, in the four child button’s area, APP will restart abnormally after any button is pressed.

Based on “generic-button-4”, I added the following statement after each “alternatives”:

          {
            "key": "pushed_3x",
            "value": "___PO_CODE_CAPABILITY.SMARTTHINGS.BUTTON_DEFAULT_ATTRIBUTES_BUTTON_PUSHED_3X",
            "type": "active"
          }

I don’t know why there are so many errors.