Create double/tripple pushes?

I´ve got a KNX input for dimming or 1 push function (gira 212800).
But seems this is missing multiple pushes.

Is it possible to have a rule so i can use 2 push, 3 push scenario´s?

Thanks!

I use openHAB exactly for this purpose (and other of course). It is in fact the only openHAB function that everybody uses in the house on a regular basis.

In my house I have rocker switches with ON(Brighter) on the top button and OFF(Darker) on the bottom button of KNX rockers . There are usually multiple light zones per room so a rocker addresses only one light zone. When I want to switch on or off all the lights of a room I perform a triple click on the top or bottom of any rocker in the room. It is very convenient because you don’t have to think about which switch to use to power off some room when leaving for example. Note that it relies on the use of rockers, it won’t work with single button concept (toggle light on the same button).

This is managed by an openHAB rule. All the things and items linked to the KNX light switching GA are duplicated with an AUTOSW suffix and associated to LocationName_SmartSwitch and gSmartSwitch groups. The location group gathers all the lamps that should be controlled by the triple switch, the gSmartSwitch group is used to trigger the rule.

Things file:

Type switch	: F1_Chambre_4_Liseuse_Droite_L	"Light"	[ ga="4/0/0+<4/0/3" ]
Type switch	: F1_Chambre_4_Liseuse_Droite_L_AUTOSW	"Auto switch input"	[ ga="<4/0/0" ]
Type switch	: F1_Chambre_4_Liseuse_Gauche_L	"Light"	[ ga="4/0/10+<4/0/13" ]
Type switch	: F1_Chambre_4_Liseuse_Gauche_L_AUTOSW	"Auto switch input"	[ ga="<4/0/10" ]

Items file:

Switch F1_Chambre_4_Liseuse_Droite_L "Liseuse Droite" <lightbulb> (gF1_Chambre_4, gLighting, gPersistChanges)  {  channel="knx:device:bridge:generated:F1_Chambre_4_Liseuse_Droite_L" }
Switch F1_Chambre_4_Liseuse_Droite_L_AUTOSW "Liseuse Droite" <lightbulb> (gF1_Chambre_4_Smartswitch, g_Smartswitch)  {  channel="knx:device:bridge:generated:F1_Chambre_4_Liseuse_Droite_L_AUTOSW" }
Switch F1_Chambre_4_Liseuse_Gauche_L "Liseuse Gauche" <lightbulb> (gF1_Chambre_4, gLighting, gPersistChanges)  {  channel="knx:device:bridge:generated:F1_Chambre_4_Liseuse_Gauche_L" }
Switch F1_Chambre_4_Liseuse_Gauche_L_AUTOSW "Liseuse Gauche" <lightbulb> (gF1_Chambre_4_Smartswitch, g_Smartswitch)  {  channel="knx:device:bridge:generated:F1_Chambre_4_Liseuse_Gauche_L_AUTOSW" }

Of course this is some additional work on the definition but I have a script that does this automatically based on the ETS5 project.

Finally the rule file that performs all the work. If you respect the suffixes above for items and group names then I don’t thinks you need to modify it.

import java.util.HashMap
import javafx.util.Pair

// Structure containing for each light switch auto item the start time of the multiple click sequence
// the click value (ON or OFF) and the number of clicks. For an OFF sequence, the click value is 
// negative, for an ON sequence, it is positive.
var HashMap<String, Pair<DateTime, Number>> switchClicks = new HashMap()

// Structure containing for each light switch auto group the time of action associated to the group name so that
// updates following automatic action are not processed
var HashMap<String, DateTime> groupSwitchTime = new HashMap()

val CLICKS_REQUIRED = 3     // Number of clicks required for action
val CLICKS_TIME = 1000      // Maximum time allowed to perform all the clicks in ms
val BLANK_TIME = 500        // Blank time after automatic action during which avents are ignored

rule "Multiple click switch"
when
    Member of g_Smartswitch received update 
then
    // Check that we are not right after an automatic update has been performed
    val itemLightGroup = triggeringItem.getGroupNames().findFirst[ g | g != "g_Smartswitch"]
    val lastGroupAction = groupSwitchTime.get(itemLightGroup)
    if (lastGroupAction !== null && lastGroupAction.plusMillis(BLANK_TIME).isAfterNow) {
        logInfo("Multi-click switch", "ignoring updates on group " + itemLightGroup + " following automatic action")
        return
    }

    val Pair<DateTime, Number> clicks = switchClicks.get(triggeringItem.name)
    var increment = 1
    if (triggeringItem.state == "OFF") {
        increment = -1
    }
	
	if (clicks !== null     // Check for existing switch entry ...
        && clicks.getValue().intValue() * increment > 0 // ... in the same direction (ON/OFF) ...
        && clicks.getKey().plusMillis(CLICKS_TIME).isAfterNow) // ... within the right time
        {
        val clicksNumber = Math.abs(clicks.getValue().intValue() + increment)
        if (clicksNumber == CLICKS_REQUIRED) {
            // Perform action on group where item belongs
            logInfo("Multi-click switch", "Changing group " + itemLightGroup + " to " + triggeringItem.state)
            val group = new GroupItem(itemLightGroup)
            //group.sendCommand(triggeringItem.state as OnOffType)

            // Add automatic rule execution protection entry
            groupSwitchTime.put(itemLightGroup, now)

            // Send command to all group
            sendCommand(itemLightGroup, triggeringItem.state.toString)

            // Erase entry
            switchClicks.remove(triggeringItem.name)
        } else {
            // Update entry with new clicks value
            val entry = new Pair<DateTime, Number> (clicks.getKey(), clicksNumber)
            switchClicks.put(triggeringItem.name, entry)
        }
    } else {
        // Create new entry
        logInfo("Multi-click switch", "Starting to monitor clicks on " + triggeringItem.name)
        val entry = new Pair<DateTime, Number> (now, new Integer(increment))
        switchClicks.put(triggeringItem.name, entry)
    }
end

Unfortunately there are some errors with this rule under OH3 and I have not had time to fix them. I have migrated a few months ago and clearly I miss this function. I should take a bit of time to work on it.

If you want to do something different than switch on/off all lights, you could add a third item with a specific suffix and perform a slight modification to the rule so that this item would be toggled on/off and do whatever you want, or even trigger another rule.

1 Like

Not much to fix in fact. Here is an OH3 compatible version:

import java.util.HashMap

// Structure containing for each light switch auto item the start time of the multiple click sequence
// the click value (ON or OFF) and the number of clicks. For an OFF sequence, the click value is 
// negative, for an ON sequence, it is positive.
var HashMap<String, Pair<ZonedDateTime, Number>> switchClicks = new HashMap()

// Structure containing for each light switch auto group the time of action associated to the group name so that
// updates following automatic action are not processed
var HashMap<String, ZonedDateTime> groupSwitchTime = new HashMap()

val CLICKS_REQUIRED = 3     // Number of clicks required for action
val CLICKS_TIME = 1000      // Maximum time allowed to perform all the clicks in ms
val BLANK_TIME = 500        // Blank time after automatic action during which avents are ignored
val MS_TO_NS = 1000000

rule "Multiple click switch"
when
    Member of g_Smartswitch received update 
then
    // Check that we are not right after an automatic update has been performed
    val itemLightGroup = triggeringItem.getGroupNames().findFirst[ g | g != "g_Smartswitch"]
    val lastGroupAction = groupSwitchTime.get(itemLightGroup)
    if (lastGroupAction !== null && lastGroupAction.plusNanos(BLANK_TIME*MS_TO_NS).isAfter(ZonedDateTime.now())) {
        logDebug("SmartSwitch", "ignoring updates on group " + itemLightGroup + " following automatic action")
        return
    }

    val Pair<ZonedDateTime, Number> clicks = switchClicks.get(triggeringItem.name)
    var increment = 1
    if (triggeringItem.state == "OFF") {
        increment = -1
    }
	
	if (clicks !== null     // Check for existing switch entry ...
        && clicks.getValue().intValue() * increment > 0 // ... in the same direction (ON/OFF) ...
        && clicks.getKey().plusNanos(CLICKS_TIME*MS_TO_NS).isAfter(ZonedDateTime.now())) // ... within the right time
        {
        val clicksNumber = Math.abs(clicks.getValue().intValue() + increment)
        if (clicksNumber == CLICKS_REQUIRED) {
            // Perform action on group where item belongs
            logDebug("SmartSwitch", "Changing group " + itemLightGroup + " to " + triggeringItem.state)

            // Add automatic rule execution protection entry
            groupSwitchTime.put(itemLightGroup, now)

            // Send command to all group
            sendCommand(itemLightGroup, triggeringItem.state.toString)

            // Erase entry
            switchClicks.remove(triggeringItem.name)
        } else {
            // Update entry with new clicks value
            val entry = new Pair<ZonedDateTime, Number> (clicks.getKey(), clicksNumber)
            switchClicks.put(triggeringItem.name, entry)
        }
    } else {
        // Create new entry
        logDebug("SmartSwitch", "Starting to monitor clicks on " + triggeringItem.name)
        val entry = new Pair<ZonedDateTime, Number> (now, Integer.valueOf(increment))
        switchClicks.put(triggeringItem.name, entry)
    }
end
1 Like