Need ideas: translate flashing gate light (ON/OFF) into three-way status (OPEN/MOVING/CLOSED)

Hi,

I have just integrated my entrance gates into openHAB via a Shelly Plus 1. For determining the status of the gates I have connected the device to a gate open indicator terminal, which is actually intended to drive a light, in openHAB I have a Point of type Switch for that, with ON indicating the gate is open and OFF indicating the gate is closed.

Now, the challenge: while the gate is moving, the light is flashing, so the Switch goes ON/OFF in circles until the gate is fully opened or closed.

I would like to translate this into a proxy Point, which has three statuses: OPEN, MOVING and CLOSED.

I have been mulling this a bit, and the only thing I could come up with is a rule that is triggered on a change of the gate indicator switch, then waits for a time that is longer than the flashing interval and then checks using the gate indicator switch’s status history if the state has changed in that time.

However, this does not seem to be very elegant, and also I could not really get it to work so far.

So my first question would be: does anyone have alternative ideas how to solve this?

And my second, maybe someone knows why my attempt is not working. I tried to test it with this code, but even if I flip the switch during the sleep time, the changedSince always returns false.

var sleepTime = 5000;
var openIndicatorName = "TestSwitch";

console.info( "TestSwitch: *** Start ***");

var openIndicator = items.getItem( openIndicatorName );
var checkTime = new Date();

console.info( "TestSwitch: State=" + openIndicator.state );

java.lang.Thread.sleep( sleepTime );

console.info( "TestSwitch: State=" + openIndicator.state );

console.info( "TestSwitch: ChangedSince=" + openIndicator.history.changedSince( checkTime ) );

console.info( "TestSwitch: *** End ***");

The output looks like this, the state changes, but the changedSince still returns false.

2023-01-22 21:34:48.401 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: *** Start ***
2023-01-22 21:34:48.402 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: State=OFF
2023-01-22 21:34:53.403 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: State=ON
2023-01-22 21:34:53.486 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: ChangedSince=false
2023-01-22 21:34:53.487 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: *** End ***

Hi Robert.

I would create a rule triggered by update and save the timestamp in a global to compare it when the rule is triggered again. Depending on this comparison you can set your second item to open, closed or moving.

In a DSL rule it would look something like this

var lastUpdate = null

rule "Gate"
when Item openIndicator received update
then
    if((now - lastUpdate) < 5000) {
        gate.sendCommand("MOVING")
    } else {
        gate.sendCommand(if(newState===ON) "OPEN" : "CLOSED")
    }
    lastUpdate = now
end

This was typed on my phone so please excuse any typo :slight_smile:

Not really. If you can’t implement this at the device at the gate itself, any solution is going to be inelegant and based on timing in some way.

All of the persistence calls, and frankly any interaction with OH is going to require a ZonedDateTime, never a Date. I’m not entirely certain why that didn’t throw syntax errors but it’s definitely the first thing to correct. This looks like JS Scripting so the most terse way to get now is time.toZDT().

Out of curiousity, what does MOVING gain you? It’s such a momentary and fleeting status that you’ll rarely be looking at a UI when its triggered. Unless you’re planning to trigger a rule on the MOVING status, I would be more concerned about ensuring that the gate is closed.

I say this as someone who’s gone down similar paths, adding items and rules only to realize that they aren’t really necessary.

Note that typically gates do have fixed states - when it’s closed flashlight is off, when it’s opened flashlight is on, so the script should base on these facts, and there will be four states actually - opened, closed and opening, closing. So when the state changed the rule should check first initial state of proxy gates status item, change it’s state according to initial state to closing/opening and start a timer longer than flashlight cycle. When timer finishes the rule must check flashlight status and set proxy item to corresponding state. But every flashlight blink must reset the timer again and again till flashlight stops.

The state MOVING could be used in widget reflecting the fact that gates did started to move. User could want to be sure that his command was accepted.

I think the problem with that approach is that after the last “flashing” change to the status, there won’t be any further change that would trigger the rule’s execution. So in the last change, the status would still be moving and it will stay that way because once the light stops flashing, the rule will not be triggered again. That is why I ended up doing the timer in the rule.

Thanks, but even with time.toZDT(), the result stays the same. If I go back for an hour, the changedSince does return true, but if I go back only the 5 seconds it does not. Maybe the issue is that the history object is simply not updated fast enough for this purpose?

Here is my updated code:

var sleepTime = 5000;
var openIndicatorName = "TestSwitch";

console.info( "TestSwitch: *** Start ***");

var openIndicator = items.getItem( openIndicatorName );
var checkTime = time.toZDT();

console.info( "TestSwitch: State=" + openIndicator.state );

console.info( "TestSwitch: Going to sleep ..." );

java.lang.Thread.sleep( sleepTime );

openIndicator = items.getItem( openIndicatorName );

console.info( "TestSwitch: ... and waking up again." );

console.info( "TestSwitch: State=" + openIndicator.state );

console.info( "TestSwitch: CheckTime=" + checkTime + " ChangedSince=" + openIndicator.history.changedSince( checkTime ) );

checkTime = time.toZDT().minusNanos( sleepTime * 1000 );

console.info( "TestSwitch: CheckTime=" + checkTime + " ChangedSince=" + openIndicator.history.changedSince( checkTime ) );

checkTime = time.toZDT().minusHours( 1 );

console.info( "TestSwitch: CheckTime=" + checkTime + " ChangedSince=" + openIndicator.history.changedSince( checkTime ) );

console.info( "TestSwitch: *** End ***");

Output:

2023-01-23 05:08:16.751 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: *** Start ***
2023-01-23 05:08:16.753 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: State=ON
2023-01-23 05:08:16.753 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: Going to sleep ...
2023-01-23 05:08:21.754 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: ... and waking up again.
2023-01-23 05:08:21.755 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: State=OFF
2023-01-23 05:08:21.811 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: CheckTime=2023-01-23T05:08:16.752+01:00[SYSTEM] ChangedSince=false
2023-01-23 05:08:21.814 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: CheckTime=2023-01-23T05:08:21.806+01:00[SYSTEM] ChangedSince=false
2023-01-23 05:08:21.841 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: CheckTime=2023-01-23T04:08:21.814+01:00[SYSTEM] ChangedSince=true
2023-01-23 05:08:21.842 [INFO ] [nhab.automation.script.ui.7556f69259] - TestSwitch: *** End ***

Basically I just wanted to have a nice visual presentation. I created a widget, which status badge is now flickering back and forth between open and closed while the gate is moving.

But you are right, it does not gain me that much to have a nicer visual presentation, so at this point this is more my desire to learn how to solve such an issue than actually having a strong need for getting it resolved. :wink:

That is a good point, if already going through all that trouble, using four states to have more precise information is for sure a good idea!

:+1:

1 Like

I think this is the case, after testing it a bit further. If I set the sleep time to 30 seconds, the history still does not show a change, but if I set it to 60 seconds it does. So I assume it just takes some time until the history object is updated.

So I might just have to use the cache to store the last update time myself. And since there is only one thread for subsequent executions of the rule, I’d need to change the sleep to an asynchronous timer, so that while the timer is running, the rule can continue to be executed and update the cache with the latest update time.

Does that make sense?

Nothing wrong with that. The reason I asked is to see if they were any other motivations that might be accomplished without needing this state.

If you’re only concerned about the UI, I would use a rule that changes a proxy item to moving/opening/closing when the gate item first change, then ignores subsequent changes until the gate item has settled. Only then would the proxy item be updated to open/closed.

I’d use a timer that resets with every state change, and then updates the proxy item only if the timer successfully completes.

That sounds like a good approach, cancelling previous timers for sure makes the whole thing more efficient. For that I‘d need to store the timer id in the cache, so that subsequent instances of the rule can cancel it, right?

Edit: just saw that when using actions.ScriptExecution.createTimer instead of setTimeout, I can name the timer and would not have to store an id.

1 Like

OK, got it to work now.

I decided to stick with one status MOVING for the movement, since the direction of the movement can actually be reversed while the gate is moving, and the script would have no way to detect this and would show the wrong status.

Apart from the topic of the history taking some time to be updated, I also noticed that this applies to the state itself, albeit the time there is shorter. If I use item.postUpdate and immediately afterwards check item.state, then item.state still has the previous state. If I put a short sleep in between, then the new state will show up. So it seems behind postUpdate there is some asynchronous logic updating the actual state of the item.

My code looks like this now. The actual logic is actually more concise than it looks like, I just put a lot of debug/info messages in the code.

/* This script translates the ON/OFF signals of the gate open indicator light terminal of 
 * a Ditec VIVAH entrance gate controller to a proxy item with states OPEN/MOVING/CLOSED.
 *
 * In the configuration of the Shelly Plus 1 that integrates the VIVA we have inverted the
 * gate open indicator, so we consider it now a gate closed indicator, with ON indicating the
 * gate is closed and OFF it is open. 
 *
 * While the gate is moving, the indicator flashes, i.e. moves between ON and OFF in quick 
 * succession. This script uses a timer to detect that and translate it into the MOVING state
 * of the proxy item. 
 * 
 * The script works as following:
 * - It is triggered by every status change to the gate closed indicator.
 * - The first status change after a period of inactivity will set the proxy item to MOVING.
 * - A timer is then set to a time longer than the flashing cycle.
 * - Every new status change will reset the timer. 
 * - Therefore the timer will only run through once the flashing has stopped and can then set the 
 *   final state of the gate.
 */

var sleepTime = 2000; // sleep time needs to be set to a value higher than the flashing cycle of the gate open indicator
var timerName = "CACHE_GARAGE_GATE_TIMER";
var gateClosedIndicatorName = "Door_GA_Einfahrtstore_InputButton"; // the Point connected to the Input/Button channel of the Shelly connected to the gate open indicator
var gateStatusName = "Door_GA_Einfahrtstore_Status"; // the proxy item of which the status should be set to OPEN/MOVING/CLOSED
var statusOpen = "OPEN";
var statusMoving = "MOVING";
var statusClosed = "CLOSED";

var gateClosedIndicator = items.getItem( gateClosedIndicatorName );
var gateStatus = items.getItem( gateStatusName );

/* This function is called by the timer and sets the final state of the proxy item */
function updateGateStatus()
{
  var newState = ( gateClosedIndicator.state == "ON") ? statusClosed : statusOpen;
  gateStatus.postUpdate( newState );
  console.info( "Gate Movement: gate movement has stopped, status is now " + newState );
}

console.debug( "Gate Movement: gate closed indicator \"" + gateClosedIndicatorName.name + "\" has changed to state=" + gateClosedIndicator.state );
console.debug( "Gate Movement: gate status \"" + gateStatus.name + "\" has state=" + gateStatus.state );

// We get the timer from the cache and calculate the time for the schedule
var timer = cache.private.get( timerName );
var timerSchedule = time.ZonedDateTime.now().plusNanos( sleepTime * 1000000 );

if( ! timer )
  {
    console.debug( "Gate Movement: creating timer ..." );
    cache.private.put( timerName, timer = actions.ScriptExecution.createTimer( timerSchedule, updateGateStatus ) );
  }
else
  {
    if( timer.isActive() )
      {
        console.debug( "Gate Movement: cancelling timer ..." );
        timer.cancel();
      }
    
    console.debug( "Gate Movement: rescheduling timer ..." );
    timer.reschedule( timerSchedule );
  }

if( gateStatus.state != statusMoving )
  {
    gateStatus.postUpdate( statusMoving );
    console.info( "Gate Movement: gate started moving, status is now " + statusMoving );
  }
   

console.debug( "Gate Movement: Processing complete");

It should take milliseconds, not a minute for persistence to save the value. If it’s taking that long, something else is wrong.

Yes, a command or update takes place in parallel to the rule. It is almost never the case that if you update and Item and then check it’s state one or two lines of code later that the Item will have reached the updated state. But you know what you updated it to so if you need to, use what you know instead of the Item’s state.

Commands are even worse because:

  1. the Item’s state may not match the command sent (e.g. sending INCREASE to a Dimmer)
  2. it make require a round trip communication with the device itself before the Item updates in response to the command
  3. the Item may never update at all in response to the command.
1 Like

Maybe there is some layer between persistence and the history object that causes the delay? Not sure what else could be wrong there. I am using the default persistence, this is an area that in my short time with openHAB I have not looked at yet.

Not really. It’s a synchronous call to request data from the database. There shouldn’t be much between. However, depending on which database there might be networking issues getting in the way. The persistence strategy could also come into play (e.g. if you only have everyMinute, it can be up to a minute before a change in an Item to be saved compared to everyChange which gets saved immediately when the Item changes state).

If you’ve not modified the persistence strategies, rrd4j saves every change and every minute. Note that this can cause a different problem when using the persistence functions because the value gets saved every minute regardless of whether the Item was updated or changed which can impact certain results (e.g. lastUpdate will return the time of the most recent entry in the database so it will never be more than one minute ago even if the Item hasn’t been updated in days).

1 Like

OK, thanks for the explanation. I’ll put persistence on my list of openHAB things I still need to learn about. :wink:

I have now finished the entrance gates integration that this script is used for and written on my blog about it. While I guess garage door and gates integration has been done a zillion times, I hope the article may still be useful for someone using the same hardware as I do, especially the wiring between our Ditec VIVAH gate controller and the Shelly Plus 1:

i done something similar, does your gate not have magnetic limit sensors for opened & closed.
i just tapped into both of those and have full notification of, open, opening, closed & closing.
i also use the status to turn on/off a drive light for night time when the gate opens
also sends a cctv gif to my phone for added security when im not home

rule "22Gate Opening"
when
      Item Gate_Close changed from ON to OFF
then
        say ("The Drive Gate is opening")
val actions = getActions("pushover", "pushover:pushover-account:Pushover")
    actions.sendHtmlMessage(" <font color='red'>The drive gate is opening</font>!", "openHAB")
 getActions("ipcamera", "ipcamera:onvif:002").recordGIF("ipcamera",10)
        Gate_State.postUpdate("Opening")
        Drive_Light.sendCommand(ON)
end

rule "23Gate Opened"
when
        Item Gate_Open changed from ON to OFF
then
        say ("The Drive Gate is opened")
val actions = getActions("pushover", "pushover:pushover-account:Pushover")
    actions.sendHtmlMessage(" <font color='red'>The drive gate is opened</font>!", "openHAB")
        Gate_State.postUpdate("Opened")
        Drive_Light.sendCommand(ON)
end

rule "24Gate Closing"
when
        Item Gate_Open changed from OFF to ON
then
        say ("The Drive Gate is closing")
val actions = getActions("pushover", "pushover:pushover-account:Pushover")
    actions.sendHtmlMessage(" <font color='red'>The drive gate is closing</font>!", "openHAB")
 getActions("ipcamera", "ipcamera:onvif:002").recordGIF("ipcamera",10)
         Gate_State.postUpdate("Closing")
end

rule "25Gate Closed"
when
        Item Gate_Close changed from OFF to ON
then
        say ("The Drive Gate is closed")
val actions = getActions("pushover", "pushover:pushover-account:Pushover")
    actions.sendHtmlMessage(" <font color='red'>The drive gate is closed</font>!", "openHAB")
 getActions("ipcamera", "ipcamera:onvif:002").recordGIF("ipcamera",10)
         Gate_State.postUpdate("Closed")
         Drive_Light.sendCommand(OFF)
end

No, it does not have such sensors, the gate open indicator light is the only output I have for this purpose. But with the script and proxy item it works well enough. :slight_smile: