Holdable and Releasable Button

I'm trying to modify a driver code for a simple relay switcher (iTach IP2CC) to simulate a holding/releasing button effect.
The device controls a projector screen. When only Port1 is grounded (relay closed), the screen goes up. When only Port2 is grounded, the screen goes down. When both ports are grounded, the screen stops.
NOTE: After the relay is closed and then reopened, the screen will continue to move until both ports are closed together.
What I am trying to simulate is hold release button effect for UP and DOWN.

When UP button is pressed: {execute code1} (screen moved up)
When UP button is released: {execute code2} (screen stops)

code1: def ScreenUP()
code2: def ScreenSTOP()

I have the basic controls working. Here is the driver code:

/*
*    iTach iP2CC Driver for Projector Screen Control
*    Screen uses Port 1 for UP trigger and Port 2 for DOWN Trigger
*    To stop the screen both ports must be triggered
 */


@SuppressWarnings('unused')
static String version() {return "0.0.1"}

metadata {
    definition (
        name: "iTach iP2CC", 
        namespace: "uw", 
        author: "U W",
        importUrl:"na"
    ) {
        
        capability "Telnet"
        capability "Initialize"
        capability "Switch"
        capability "HoldableButton"
        capability "ReleasableButton"
        
        attribute "Port1", "ENUM", ["on", "off"]
        attribute "Port2", "ENUM", ["on", "off"]
        attribute "lastMessage", "STRING"

        command "connectTelnet"
        command "disconnectTelnet"
        command "p1On"
        command "p1Off"
        command "p2On"
        command "p2Off"
        command "ScreenUP"
        command "ScreenDOWN"
        command "ScreenSTOP"

    }   
}

preferences {

    input(name: "ipAddr", type: "string", title:"IP Address", required: true)
    input(name: "portNum", type: "number", title: "Port Number", required: true)
}

@SuppressWarnings('unused')
def installed() {

}

def initialize(){
    connectTelnet()  
    log.debug "----- INITIALIZING TELNET -----"
}

void updateAttr(String aKey, aValue, String aUnit = ""){
    sendEvent(name:aKey, value:aValue, unit:aUnit)
}

def connectTelnet(){
    try{
        telnetConnect([termChars:[10]], ipAddr, (int)portNum, null, null)
    } catch (ex) {
        updateAttr("lastMessage", ex)
    }
    updateAttr("switch", "on")
}

def disconnectTelnet() {
    telnetClose()
    updateAttr("switch", "off")
}

def sendMsg(message) {
    sendHubCommand(new hubitat.device.HubAction("""$message\r\n""", hubitat.device.Protocol.TELNET))
}

def p1On(){
    sendMsg("setstate,1:1,1")
    updateAttr("Port1", "On")
}

def p1Off(){
    sendMsg("setstate,1:1,0")
    updateAttr("Port1", "Off")
}

def ScreenUP(){
    p1On()
    runInMillis(100,p1Off)
}

def p2On(){
    sendMsg("setstate,1:2,1")
    updateAttr("Port2", "On")
}
               
def p2Off(){
    sendMsg("setstate,1:2,0")
    updateAttr("Port2", "Off")
}

def ScreenDOWN(){
    p2On()
    runInMillis(100,p2Off)
}

def ScreenSTOP(){
    p1On()
    p2On()
    runInMillis(500,p1Off)
    runInMillis(500,p2Off)
}
               
def parse(message) {
    updateAttr("parsedMessage", message)
}

def telnetStatus(message){
        updateAttr("statusMessage", message)
}

However, I need some guidance on how to implement the holdable and releasable buttons so they can then be picked up from the Dashboard for simple UP/DOWN control.

After more exploration, I've added the following code:

def hold(button){
    if (${button} == 1){ScreenUP()}
    if (${button} == 2){ScreenDOWN()}
}

def release(button){
    if(${button} == 1){ScreenSTOP()}
    if(${button} == 2){ScreenSTOP()}
}

and under definitions:

        command "hold", ["NUMBER"]
        command "release", ["NUMBER"]

Any programmers out there who can let me know if I'm on the right track?

You don't need to define custom commands; they come as part of the capability you're using.

If your goal is to use those on Dashboard, you can use these capabilities but can also just use PushableButton, which is conventionally also used if one of these others also is (in light of the fact that it's the minimum capability most buttons of any kind support). You can't get a hold or release from a pushable dashboard button if that is your goal; you'll only get a single action on a tap of the tile, and you can specify which action that is. [EDIT: And to clarify, I mean you get one action per Dashboard tile--which can be push, hold, or release, all from a regular tap, not any other real-world action on the Dashboard tile.]

That's too bad... Not seeing why those options are available under the dashboard then, if you can't use hold/release as an actual action.

@bertabcd1234 I've updated the code to:

def push(button){
    if (button == 1){ScreenUP()}
    if (button == 2){ScreenDOWN()}
    if (button == 3){ScreenSTOP()}
}

Also added the capability "PushableButton" under definitions.

If I go to the device directly and enter the specific button number and click "Push," the respective command will fire.

However, after assigning a button push under a tile, there is no response.

Any suggestions?

You can; again, you just have to pick which one you want.

Can't see why this wouldn't work. I'd double check that the button number and events match (and that you're using the same device) or check Logs for errors.

Checked all of that, even restarted hub. I have 3 buttons, none of them are working.



So it's only working directly from the device.

Even tried recreating the device and adding a new tile. No go...

Can you control other devices from the dashboard, e.g. a switch, or even another virtual button?

Yes, everything else is working fine from the dashboard.

Not sure if this is required or not, but when you added the PushableButton capability and the push method, did you also add the command reference in the driver?

Ignore my other comments I have added and removed...

@bertabcd1234 mentioned:

I did go ahead and add the push command reference as well:

command "push", ["number"]

Does not make any difference.

Ah yes, good point...

And it wouldn't; as I said, you don't need to. :slight_smile: (It comes with the capability. That's why you already saw it; this is necessary only for custom commands, ones that don't.)

I'm not sure what is going that it's nothing working for you, but it has to be either the wrong device on the Dashboard, wrong button number or event, or some error you or other clue that is not being shared.

That was my fault, misunderstanding / not reading the earlier comments closely enough.

@vadimchp
Have you looked at the events part of the device page?

Button/Tile actions like HOLD and RELEASE are available on some third-party dasbboard apps, like Home Assistant and SharpTools.

I am not sure why the myth that Hubitat Dashboard cannot do hold or release keeps re-appearing, but it is just that. You can configure a button tile on Hubitat Dashboard to perform any button command -- one of push, hold, or release -- that you want. The OP is almost certainly dealing with some other problem, yet to be determined.

@bertabcd1234 please clarify.
I do see that you can choose between what action will be performed for "button" template (hold, push, release). But the point is that the actual dashboard tile does not have hold/release behavior (correct me if I'm wrong). I want to be able to click (tap) and hold a tile, and while the tile is held, the screen goes UP/DOWN, once the tile is released, the screen STOPS.
As to simply having the ability to trigger a hold command from a push of a tile, that is not as helpful/practical.

Here is the Event log:

The "command-push" events are me clicking the "Down" tile from the dashboard.
RESULT: no response.

The "Port1" and "Port2" events are me pressing the "Push" {2} command and then pressing the "Push" {3} command from the Device menu.
RESULT: as expected — the screen goes down and then stops on the second push.

I'm not sure if the "Value" column should show the "Button Number" for the "command-push" events, but the actual tile "Button Number" is specified.

Correct, as with other Dashboard tiles, you must assign single action to this one.