MQTT long press design pattern (dimmer)

Tags: #<Tag:0x00007f616db66ef0> #<Tag:0x00007f616db66e28> #<Tag:0x00007f616db66d38>

I have simple push butons that send out an ON message on a mqtt topic and an OFF message to the same topic when they are released.
This works great for turning lights on and off with a simple rule

rule "toggle"
when
    Item mqttSwitchIn2 received command ON
then
    if (modbusSwitchOut1.state != ON)
        modbusSwitchOut1.sendCommand(ON)
    else
        modbusSwitchOut1.sendCommand(OFF)
end

But now I would want to som somthing while I’m waiting for the OFF command when my rule is running.
Something like this

rule "toggle"
when
    Item mqttSwitchIn2 received command ON
then
    if (modbusSwitchOut1.state != ON)
        while(!mqttSwitchIn2 received command ON){
           dimmer.state -= 10;
           Thread::sleep(100);
        }
        modbusSwitchOut1.sendCommand(ON)
    else
        modbusSwitchOut1.sendCommand(OFF)
end

First of all I’m not sure what I’m doing here is even a good design, measuring the time between 2 mqtt commands and action on it.
I would need a listener that listens for the OFF command being received, but I cannot really find something in the forums

1 Like

I’d use a variable to hold the milliseconds. then make a rule for state changed from OFF to ON and safe new Date().getTime() to your variable. Then make a new rule for state changed from ON to OFF. Get the time again and substract your safed value. Now you know how many milliseconds passed between the on and off event and can act accordingly.

Sorry I’m not at home right now so i can’t provide you a proper code example but this should work.

So, I am home now and i gave it quick try this seems to work for me:


var Number millis


rule "timer on"

when 
	Item DummySwitch changed from OFF to ON

then
	millis = now.getMillis()
end
	
	
rule "timer off"

when 
	Item DummySwitch changed from ON to OFF

then
	var duration = now.getMillis() - millis
	DummyDuration.sendCommand(duration)
end

The second rule is where your actual logic would happen. a switch case for example.
I hope this what your looking for, not sure if i correctly understood the question.

I like @hrc’s solution but there is one error case not covered: what if the OFF is lost or never comes?

To manage that I would use the Expire binding on DummySwitch to automatically set it to OFF after a long enough period of time to know that the OFF never happened or was lost.

Hey cool, i wasn’t aware that rules could have state; How is this state managed? Is this like an object and what is the lifecycle of the oject?
My pattern is a bit different since I don’t have a switch that I control, but I can create a switch just to keep the state.
I do want to update my dimmer during.

The state is kept in global vars and vals which are declared before your first rule in the file. They will be initialized when the .rules file is first loaded and remain until the file is loaded again at which time the state is lost and they get reinitialized.

Global variables are only available to rules in the same file they are declared. You cannot access a global variable defined in another .rules file.

Personally, I recommend storing state in Items most of the time because it lets you do things like:

  • use persistence to restore the state on a reboot or reload of the .rules files
  • access the state from multiple .rules files
  • visualize the state on your sitemap

But there are often cases where one doesn’t need the above. This is one such case. Another common case is when using Timers and ReentrantLocks.

1 Like

so the milliseconds could be just a Number item that I’m keeping as an item.

Yes. And use a Switch or Contact for booleans and String for just about everything else.

Ok, clear
if I have the same idiom for all my switchs, is there a way i can make this generic? Or is this copy paste per switch?

You have to define each switch individually but in your rules you can leverage groups.

2 Likes

If this is something you built yourself with Arduino or similar, I would suggest to detect this on the MCU side and send appropriate MQTT command. The onebutton library can do that for you.
At least if you ever want to go for doubleclick detection, then everything is about milliseconds and you cannot afford the network latency.

Hmm, this is interesting. I’m using a PLC as my controller. I could read everything with modbus, but this is very inefficient from an engineering standpoint (polling), this is why I’m trying to build this with mqtt that only sends a command when a button is pushed. But I want immediate feedback. This is why I’m sending an ON command when the push button is pressed and a OFF command when the button is release. So I can react when someone touches the button and start dimming right away. You are the first now that tell me to do this on the MCU side.
It just seems to be causing much overhead.
Having a switch where I need to react on, having a button to store the state (this can be my output switch), having a Number to store the time the button is pressed and applying this to a dimmer. Binding this all together with a rule. Repeating this for 20 (or so) switches.
I’ll look up some real life openhab configs that drive an entire house, perhaps this would help me to understand this better

edit:
I’m now thinking of solving it another way.
Instead of sending an ON command when someone presses a button and sending an OFF command when they release it. Perhaps this would be better (500 is just a parameter, as an example).

press single < 500ms -> send PRESSED command
press twice < 500ms -> send DOUBLE  command
keep pressing >500ms -> send LONG command for each interval of 500ms

The pressed command would just be used as a toggle for a Switch
The double could be an extra command
The long press would reduce a dimmer each time

Perhaps have a look at the lib I mentioned. But AFAIR, this is basically what it does (in the sense of callback functions instead of MQTT commands, but in my case, the callbacks send the MQTT commands), with an additional LONGSTART and LONGSTOP.
I use this for a light switch with double push buttons. It is very responsive (in contrast to the single push button commercial dimmer thingy I have in another room, that is terrible.)

Left click - switch on/off
Left hold - increase brightness
Right hold - decrease brightness
Right click - toggle through lamp groups
Right doubleclick - reset to main lamp group

I got it working, the code is not very clean an can be improved at many points, but this is my base now

basically I have a timer when a button goes on and i count the edges for the time the timer is running.
Encapsulate this in a function on my plc, bind it to the different inputs and I can handle everything on openhab :slight_smile:

pub := FALSE;
subi := FALSE;
topic := 'dev/switch/2';
(*payload:= '';*)
tx(IN:=%IX0.2,PT:=t#1000ms);
(* Q == true -> pressed *)

IF %IX0.2 THEN
	IF NOT tx.Q THEN
		IF CNT = 0 THEN
			payload := 'long';
			pub:=TRUE;
			long:=TRUE;
			(* need to reint the counter, still going strong! *)
			tx(IN:=FALSE,PT:=t#1000ms);
			tx(IN:=%IX0.2,PT:=t#1000ms);
		END_IF
	END_IF
ELSIF tx.Q AND edge THEN
	(* found an edge!*)
	cnt := cnt +1;
END_IF
IF NOT tx.Q AND cnt>0 THEN
	pub:=TRUE;
	payload := INT_TO_STRING(cnt);
	cnt := 0;
END_IF
edge := %IX0.2;
1 Like

Hello, I am trying to have a push button with mqtt in openHAB2. The functionality should be like, when I click it show ON command in MQTT and as soon as I release the click it shows me the OFF command. Can you help me with items and sitemaps file.?

Where are you clicking? An actual switch or on the screen?
If it’s on the screen, you can’t do that with a sitemap.
There may be a way with HABpanel but I don’t know how…

Hi kvoit
Could you describe how you have implemented this?

As I said, the publicly available library OneButton does the detection for you. Then you can decide in the callback of the respective click type what to do. I as far as I remember, I decided to send an MQTT notification to OpenHAB, but actually trigger the light action from the MCU directly in addition. Originally, I only planned that as an offline fallback but I never changed it once it worked.

I githubed the script I used: https://github.com/kvoit/LE_FirstFloor_BedroomLight2
The imported libraries should be findable, some in my github account.

New verb in the English language…

1 Like

Yep, much better than that evil “googled” :stuck_out_tongue: