SmartThings button double tap


#1

Can you program double tap function for SmartThings button in webCoRE? If so how?


#2

That might be more of device handler feature. Some register double tap as push button 2. Watch your IDE live logging when doing single tap and double tap, that might give you some hint.


#3

I’ve jumped ship to Hubitat from ST, so I might be off here, but my understanding is that the double-tap of the button is supported only on the “new” ST app. To my knowledge, there was never a “native” way on classic ST to handle double-taps and whatnot aside from just assigning them a different button number. Mysteriously, the “classic” developer docs list the Button capability as “deprecated” (and HoldableButton, but I think it’s been that way for longer–and it, even more mysterious now that Button is the same, says “deprecated in favor of Button”).

If there is something like capabilities spelled out in the “new” developer docs somewhere, I am not able to find that (there doesn’t seem to be a lot of documentation, and examples are harder to find and harder to read because they’re much more complicated than “old” DTHs). However, I suspect they have “new” capabilities that include double-clicks/presses/taps or the equivalent baked into the “DTH” or whatever they are calling it now for this device, just perhaps ones that are not publicly documented (yet?).

I think the best bet would, indeed, be using a custom DTH on Classic that maps the double-tap to a different button. For hold, you could probably either keep using the deprecated “HoldableButton” or map it to a different number of “pushed” button, but for the double-tap you’d need to map it to an existing capability (say, double-tap = press of button “2”).

I spent some time looking at the messages this button sent on Hubitat so I could write a “driver” (Hubitat speak for “DTH”) there, which others then contributed some improvements to. Here is a hasty attempt of mine at “backporting” this to ST, but again I don’t really use ST anymore and don’t have this button paired to it, so I can’t test this, and someone may need to fix my syntax or button events. :slight_smile: (Hubitat makes this easier with a DoubleTapableButton [sic.] capability.)

/**
 *  SmartThings Button 2018 (IM6001-BTP01)
 *
 *  Loosely based on SmartThingsPublic ZigBee Button DTH copyright 2015 Mitch Pond
 *  Modified for SmartThings Button and adapted for Hubitat by Robert Morris and jp0550
 *  "Backported" for SmartThings again by Robert Morris for use on "classic" app--double tap
 *  maps to button 2 push
 *
 *  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 : "Smartthings Button (2018)", namespace: "RMoRobert", author: "Robert Morris, Mitch Pond, jp0550" ) {
    	capability "Holdable Button"
        capability "Battery"
        capability "Refresh"
        //capability "Double Tapable Button"
        capability "Pushable Button"
        capability "Temperature Measurement"
        capability "Sensor"
        capability "Actuator"
        capability "Configuration"  
        
        fingerprint inClusters: "0000,0001,0003,0020,0402,0500", outClusters: "0003,0019", manufacturer: "Samjin", model: "button"
    }
}

def parse(description){
 	log.debug "Received message: $description"    
    def event = zigbee.getEvent(description)
    
    if( description?.startsWith("catchall:") || description?.startsWith("read attr -") ){
        def descMap = zigbee.parseDescriptionAsMap(description)
        log.debug "Desc map: $descMap"
        if(descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020){ //battery
            event = getBatteryResult(zigbee.convertHexToInt(descMap.value))
        }
    }
    else if( description?.startsWith("zone status") ){
        event = parseIasButtonMessage(description)
    }
    
    log.debug "Parsed event $event"
    def result = event ? createEvent(event) : []
    
    if (description?.startsWith('enroll request')) {
		List cmds = zigbee.enrollResponse()
		log.debug "enroll response: ${cmds}"
		result = cmds?.collect { new physicalgraph.device.HubAction(it) }
	}
    
    result
}

def parseIasButtonMessage(description){
 	def zs = zigbee.parseZoneStatus(description)
    
    def eventType
    
    if (zs.isAlarm1Set()) {
        return [ name: "button", value: "pushed", data: [buttonNumber: 1], isStateChange: true, descriptionText: "Button 1 was pushed" ]
    } 
    else if (zs.isAlarm2Set()) {
        return [ name: "button", value: "pushed", data: [buttonNumber: 2], isStateChange: true, descriptionText: "Button 2 was pushed (i.e., button 1 double-tapped)" ]
    }
    else if(zs.isAlarm1Set() && zs.isAlarm2Set()) {
        return [ name: "button", value: "pushed", data: [buttonNumber: 1], isStateChange: true, descriptionText: "Button 1 was held" ]
    }
}

def getBatteryResult(rawValue){
 	log.debug "Parse battery: $rawValue"
    def volts = rawValue / 10.0
    if(volts > 3.0 || volts == 0 || rawValue == 0xFF){
     	return [:]   
    }
    else {
        def result = [ name : 'battery' ]
        def minVolts = 2.1
        def maxVolts = 3.0
        def pct = (volts - minVolts) / (maxVolts - minVolts)
        result.value = Math.min(100, (int)(pct * 100))
        def linkText = getLinkText(device)
        result.descriptionText = "$linkText battery was ${result.value}%"
        return result
    }
}

def refresh(){
    zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x20) + zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0) + zigbee.enrollResponse()
}

def configure(){
    log.debug "Configure"
    
    sendEvent(name: 'numberOfButtons', value: 2)
    
    zigbee.onOffConfig() + 
        zigbee.levelConfig() + 
        zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x20, DataType.UINT8, 30, 21600, 0x01) + 
        zigbee.temperatureConfig(30, 3600) +
        zigbee.enrollResponse() + 
        zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x20) + 
        zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0) + []
}

EDIT: In case you don’t want to read the code to figure it out, this should make the ST button a 2-button device in ST, with a push or hold of the button registered as you would expect (a push or hold, respectively, on button 1) and a double-tap registered as a push of button 2.


#4

In webcore’s, evaluation console, I can see the button reports a “double” button state…

(value) {[MyButton:supportedButtonValues]} »»» (string) [“pushed”,“held”,“double”]

How can my piston get an event with one of those string values? If that is possible, then no custom DTH needed.

You also see it is aware of the double press in the button’s Activity tab in the ST classic app. It says the button “was pushed twice”


#5

Curious how you got the values in the console.


#6

It is one of the attributes of the device.
I typed this in the evaluation console as Value:

{[MyButton:supportedButtonValues]}

where “MyButton” is what I named it in ST.

I can’t try it right now, but the attribute “DeviceWatch-DeviceStatus” might be what we are looking for. It is an empty string, but I think it may change to one of those strings when the button is pressed. I’ll have to try it tomorrow.


#7

Ah, so you can use on event and watch supportedButtonValues and evaluate if push, held, or double


#8

No, that is just the list of supported values. It doesn’t change. It’s looking more like webCore needs to add support for a “double” button state. Here are screenshots from my IDE.


#9

I’ll try this tomorrow too… Maybe I can just compare the button attribute to an expression “double”. I’m not sure if webCore/groovy will convert the button value to a string and then perform the comparison.


#10

Ran a quick test this morning. Looks like this will work in webCore.


#11

Confirmed. You can get expression on pushed, held or double and initiate an action.


#12

Confirmed as in double tap works?


#13

Correct. I use this piston currently. You can also add a gets {"held"} expression if you want, that still works too.


#14

Ok you’ll have to forgive me for being dense. There were comments above saying you need to edit handler and the. I saw something about expressions. What exactly do I need to do to make the double tap work? Just create an expression like this with “double” in it? Thanks!


#15

Yes thats all you need to do. Exactly as in Jimmy S photo further up the post.
Edit the condition and in the box underneath the EXPRESSION drop down type in double.

Cant remember if I added the " or the system added them itself. Try it and see. They should be there either way.

Is it not working for You ?
Is “Button” a valid button in your ST ?


#16

Thanks for getting back to me. I was able to get the double tap feature to work using “double” in the expression field and works well. Thanks again!


#17

I tried the above but find when I select Expression a red triangle is displayed and “Double” cannot be entered. I tried a work-around by selecting Argument. It accepted “Double”. When run the piston triggered but evaluated the statement False so the Then clause is ignored.

Your thoughts would be appreciated.

Thanks,
Ray


#18

I’ve been using “double” in an expression field with success for a while now.


#19

I’m new to the whole WebCore thing, and was pleased to find that the solution my problem (detecting a double tap) had already been found and published. Thanks acd37!


#20

I haven’t seen one quite like this, but it what I came up with after consulting this thread. Implements a switch with three distinct behaviors for pushed, double, and held. Actually, it seems to have four behaviors because a single push works as on AND off. Anyone know why?