Rules / functions / scripts: best approach

(untested…)

val Map<String, Timer> stopTimers = newHashMap

val Procedures$Procedure3<RollershutterItem, String, Number> rsCommand = [
  thisRollershutterItem,
  thisCommand,
  thisTime |

  stopTimers.get(thisRollershutterItem.name)?.cancel()

  thisRollershutterItem.sendCommand(thisCommand)

  stopTimers.put(thisRollershutterItem.name, createTimer(now.plusSeconds(thisTime)) [|
    stopTimers.put(thisRollershutterItem.name, null)

    thisRollershutterItem.sendCommand(STOP)
  ])
]

rule "morning"
when
  Item MorningScenario changed to ON
then
  rsCommand(rs1, UP, 5)
  rsCommand(rs2, UP, 10)
end

etc.
1 Like

Ok, so the approach is “function-oriented”.
…I guess I have to copy the function code in every scenario I want to implement (i.e. night / afternoon / …), am I right?

Functions are possible in OH: Reusable Functions: A simple lambda example with copious notes.

However I will warn you that there is almost always a better approach to achieve the same thing in OH. Functions like this are awkward and challenging to write and maintain and usually end up being brittle and excessively long.

So my recommendation is to:

  • if you insist on wanting to code Rules in a more functional way, use the JSR223 binding and code your Rules using Jython, JavaScript, or Groovy

  • trade having a few more Items and Groups for massively simpler Rules code.

Look at the Design Pattern postings for lots of approaches that work better in Rules DSL than trying to force functions and data structures on the Rules DSL.

Not really but you haven’t provided enough details to provide any additional advice. But, for example,

rule "time changed"
when
    Item MorningScenario changed to ON or
    Item AfternoonScenario changed to ON or
    ...
then
    val rs1Direction = UP
    val rs2Direction = UP
    val rs1Timer = 5
    val rs2Timer = 10

    switch triggeringItem.name {
        case "MorningScenario": {
            // defaults are already correct
        }
        case "AfternoonScenario": {
            rs1Direction = UP
            rs2Direction = UP
            rs1Timer = 10
            rs2Timer = 10
        }
        ...
    }

    rsCommand.apply(rs1, rs1Direction, rs1Timer)
    rsCommand.apply(rs2, rs2Direction, rs2Timer)
end

Of course it could be simpler if you apply the Time of Day DP and use a single String Item to represent the time of day.

There are other approcahes one can use such as Associated Items DP to implement the Timer itself.

1 Like

Mmmm…ok…At this point I think I’m missing somenthing…
First of all, thanks for you patience…
Now, I don’t really want to make anything complicated, just a bunch of scenarios where my roller shutters open or close partially based on a button I press i my UI.

It should be something like this:

SITEMAP:

Switch SelectedScenario mappings:[1="morning", 2="night"]

RULE:

rule "Scenarios"
when
    Item SelectedScenario received command
then
    switch SelectedScenario.state {
        case 1: {
            // morning scenario
            rs1.sendCommand(UP)
            Thread::sleep(5000)
            rs1.sendCommand(STOP)
            rs2.sendCommand(UP)
            Thread::sleep(10000)
            rs2.sendCommand(STOP)
        }
        case 2: {
           // night scenario
            rs1.sendCommand(DOWN)
            rs2.sendCommand(DOWN)
        }
    }
end

I was just wandering if there was a more efficient way to manage something like 10 roller shutters, each with its timer (since sun exposition differs).

And another point is that I want ALL the roller shutters open at the same time, and then each one has to stop when reached the right position…In the example up here, using sleep, I have to wait every single roller shutter to be in position.

Well, with something like this the answer is always in the details, and you didn’t provide any.

If it is based on the time of day, why require a manual button press?

For rules that look like this I recommend the Design Pattern: How to Structure a Rule.

  1. Determine whether the rule even needs to run. In this case

  2. Calculate what needs to be done

  3. Do it.

You will always want to run the rule so 1 isn’t in play. So we are left with 2 and 3.

We also have to deal with

Which is a whole new requirement you’ve not mentioned until now.

The first question is is the set of ten roller shutters the same set for each scene? Or do you have one scene with only three of them and another with all ten?

It makes a huge difference in the potential approaches.

Ultimately you will need to figure out the timing and then farm the work of issuing the UP command and waiting into Timers, not Thread::slkeeps.

Do one really need all these timers and Thread::sleep(xxx)?

My approach would be to define two separate items bound to the correct channels for both roller shutters and have the command expire after a given time:

Rollershutter rs1 "some text " { channel="...", expire="5s,command=STOP" }
Rollershutter rs2 "some text " { channel="...", expire="10s,command=STOP" }
1 Like

I think the challenge here is during the morning he may want rs1 to Up for 5 seconds in the morning but may want it to go UP for 15 seconds in the afternoon.

It IS indeed possible to do this using separate Items for each scene number, and frankly that is probably what I would do in this circumstance, but rebzone specifically said he doesn’t want extra Items.

Personally, I’d trade a dozen extra Items to eliminate a dozen lines of Rules code. Though in my experience the return is closer to 3 lines of Rule code to each extra Item created for this purpose.

Oh, yes, you are right rikoshak: in the first post I just wanted to know a best practice just to understand the logic of OpenHab and then make my own “brainstorming” to get everything work…
But then you fed me with examples, and the topic got a different direction.

Now I understand that there are really a lot of different solutions, and each one just applies well…

I’ll give a try to the last 2 posts (ptweety + rikoshak), and give some feedback.

Really thanks for your time and patience :slight_smile:

Update:
after some attempts, I created (as suggested) some more items with the “expire” property, then made a “scenario” rule that send command UP to all those items.

However the behaviour is not as expected: I used 5 seconds expire for most of my roller shutters, and 10 seconds for the others, but the STOP command doesn’t respect the exact timing…For the 5 seconds expire items, some rollershutters stop at 5, some others at 6 and others at 7 seconds…and so on (ok, you’d probably say that this shouldn’t be a great issue, but 2 more seconds in a 5 period is 40% more sun in the room).

To give some more informations, in the rule I simply give the UP command to ALL the roller shutters this way:

rs1.sendCommand(UP)
rs2.sendCommand(UP)
...
rs10.sendCommand(UP)

OpenHAB is installed in a linux server virtual machine with 768Mb RAM (40% always free) on a Late 2009 Mac Mini.

Show your Item definitions.

Here it is

Rollershutter   itRS_PT_Exp10CucinaFinestra             "Tapparella finestra cucina"            <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:11:shutter", expire="10s,command=STOP"}
Rollershutter   itRS_PT_Exp10CucinaPortaFinestra        "Tapparella porta finestra cucina"      <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:12:shutter", expire="10s,command=STOP"}
Rollershutter   itRS_PT_Exp10Soggiorno                  "Tapparella soggiorno"                  <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:13:shutter", expire="10s,command=STOP"}
Rollershutter   itRS_PT_Exp5Bagno                       "Tapparella bagno"                      <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:14:shutter", expire="5s,command=STOP"}
Rollershutter   itRS_PT_Exp10StudioCortile              "Tapparella studio cortile"             <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:16:shutter", expire="10s,command=STOP"}
Rollershutter   itRS_PT_Exp5StudioStrada                "Tapparella studio fronte strada"       <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:15:shutter", expire="5s,command=STOP"}
Rollershutter   itRS_PT_Exp5Lavanderia                  "Tapparella lavanderia"                 <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:17:shutter", expire="5s,command=STOP"}

Rollershutter   itRS_P1_Exp10CameraMatrimoniale         "Tapparella camera matrimoniale"        <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:21:shutter", expire="10s,command=STOP"}
Rollershutter   itRS_P1_Exp10BagnoCameraMatrimoniale    "Tapparella bagno camera matrimoniale"  <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:22:shutter", expire="10s,command=STOP"}
Rollershutter   itRS_P1_Exp10BagnoCamerette             "Tapparella bagno camerette"            <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:23:shutter", expire="10s,command=STOP"}
Rollershutter   itRS_P1_Exp10CameraCamilla              "Tapparella camera Camilla"             <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:24:shutter", expire="10s,command=STOP"}
Rollershutter   itRS_P1_Exp10Cameretta                  "Tapparella cameretta"                  <rollershutter>     {channel="openwebnet:bus_automation:fad1ce9c:25:shutter", expire="10s,command=STOP"}

Number      itN_RS_Input      "Scenario tapparelle"

The last one is the “trigger” I use to activate the scenario

How are you measuring the amount of time it takes for the rollershutters to move? Are you just watching them or are you looking in the logs? The reason why I ask is the problem may be the binding or the technology cannot process a bunch of commands all at once and the binding may be queuing up the commands and working them off as fast as it can or dropping messages and having to resend them, both of which take some time.

Look in events.log at the time stamps for when each of the rsX Items receive the STOP commands. Are they all with a second of each other? If so the problem isn’t your Rule, it is your binding or the technology and there isn’t anything that we can do about it from the Rule.You will probably have to live with sending the commands in sequence using Design Pattern: Gate Keeper.

And as an aside, if you put all the rsX Items into a Group, let’s call it RS, the you can just use RS.sendCommand(UP) to send the command to all the members of that Group.

This is my log file:

2018-06-20 13:31:05.473 [vent.ItemStateChangedEvent] - itN_RS_Input changed from 0 to 2
2018-06-20 13:31:05.520 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10CucinaFinestra' received command UP
2018-06-20 13:31:05.529 [vent.ItemStateChangedEvent] - itRS_PT_Exp10CucinaFinestra changed from UNDEF to 0
2018-06-20 13:31:05.537 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10CucinaPortaFinestra' received command UP
2018-06-20 13:31:05.559 [vent.ItemStateChangedEvent] - itRS_PT_Exp10CucinaPortaFinestra changed from UNDEF to 0
2018-06-20 13:31:05.630 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10Soggiorno' received command UP
2018-06-20 13:31:05.640 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5Bagno' received command UP
2018-06-20 13:31:05.646 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10StudioCortile' received command UP
2018-06-20 13:31:05.649 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5StudioStrada' received command UP
2018-06-20 13:31:05.652 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5Lavanderia' received command UP
2018-06-20 13:31:05.656 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10CameraMatrimoniale' received command UP
2018-06-20 13:31:05.996 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10BagnoCameraMatrimoniale' received command UP
2018-06-20 13:31:06.902 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10BagnoCamerette' received command UP
2018-06-20 13:31:06.911 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10CameraCamilla' received command UP
2018-06-20 13:31:06.915 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10Cameretta' received command UP
2018-06-20 13:31:07.001 [vent.ItemStateChangedEvent] - itN_RS_Input changed from 2 to 0
2018-06-20 13:31:07.013 [vent.ItemStateChangedEvent] - itRS_PT_Exp10Soggiorno changed from UNDEF to 0
2018-06-20 13:31:07.017 [vent.ItemStateChangedEvent] - itRS_PT_CucinaFinestra changed from 100 to UNDEF
2018-06-20 13:31:07.024 [vent.ItemStateChangedEvent] - itRS_PT_Exp10CucinaFinestra changed from 0 to UNDEF
2018-06-20 13:31:07.026 [vent.ItemStateChangedEvent] - itRS_PT_Exp5Bagno changed from UNDEF to 0
2018-06-20 13:31:07.028 [vent.ItemStateChangedEvent] - itRS_PT_Exp10StudioCortile changed from UNDEF to 0
2018-06-20 13:31:07.032 [vent.ItemStateChangedEvent] - itRS_PT_Exp5StudioStrada changed from UNDEF to 0
2018-06-20 13:31:07.035 [vent.ItemStateChangedEvent] - itRS_PT_Exp5Lavanderia changed from UNDEF to 0
2018-06-20 13:31:07.042 [vent.ItemStateChangedEvent] - itRS_P1_Exp10CameraMatrimoniale changed from UNDEF to 0
2018-06-20 13:31:07.044 [vent.ItemStateChangedEvent] - itRS_P1_Exp10BagnoCameraMatrimoniale changed from UNDEF to 0
2018-06-20 13:31:07.048 [vent.ItemStateChangedEvent] - itRS_P1_Exp10BagnoCamerette changed from UNDEF to 0
2018-06-20 13:31:07.054 [vent.ItemStateChangedEvent] - itRS_P1_BagnoCamerette changed from 100 to UNDEF
2018-06-20 13:31:07.060 [vent.ItemStateChangedEvent] - itRS_P1_Exp10BagnoCamerette changed from 0 to UNDEF
2018-06-20 13:31:07.067 [vent.ItemStateChangedEvent] - itRS_P1_BagnoCameraMatrimoniale changed from 100 to UNDEF
2018-06-20 13:31:07.069 [vent.ItemStateChangedEvent] - itRS_P1_Exp10BagnoCameraMatrimoniale changed from 0 to UNDEF
2018-06-20 13:31:07.071 [vent.ItemStateChangedEvent] - itRS_P1_Exp10CameraCamilla changed from UNDEF to 0
2018-06-20 13:31:07.073 [vent.ItemStateChangedEvent] - itRS_P1_Exp10Cameretta changed from UNDEF to 0
2018-06-20 13:31:07.074 [vent.ItemStateChangedEvent] - itRS_P1_CameraMatrimoniale changed from 100 to UNDEF
2018-06-20 13:31:07.075 [vent.ItemStateChangedEvent] - itRS_P1_Exp10CameraMatrimoniale changed from 0 to UNDEF
2018-06-20 13:31:07.425 [vent.ItemStateChangedEvent] - itRS_PT_Lavanderia changed from 100 to UNDEF
2018-06-20 13:31:07.428 [vent.ItemStateChangedEvent] - itRS_PT_Exp5Lavanderia changed from 0 to UNDEF
2018-06-20 13:31:07.864 [vent.ItemStateChangedEvent] - itRS_PT_StudioStrada changed from 100 to UNDEF
2018-06-20 13:31:07.867 [vent.ItemStateChangedEvent] - itRS_PT_Exp5StudioStrada changed from 0 to UNDEF
2018-06-20 13:31:08.294 [vent.ItemStateChangedEvent] - itRS_PT_StudioCortile changed from 100 to UNDEF
2018-06-20 13:31:08.297 [vent.ItemStateChangedEvent] - itRS_PT_Exp10StudioCortile changed from 0 to UNDEF
2018-06-20 13:31:08.734 [vent.ItemStateChangedEvent] - itRS_PT_Bagno changed from 100 to UNDEF
2018-06-20 13:31:08.737 [vent.ItemStateChangedEvent] - itRS_PT_Exp5Bagno changed from 0 to UNDEF
2018-06-20 13:31:09.164 [vent.ItemStateChangedEvent] - itRS_PT_Soggiorno changed from 100 to UNDEF
2018-06-20 13:31:09.167 [vent.ItemStateChangedEvent] - itRS_PT_Exp10Soggiorno changed from 0 to UNDEF
2018-06-20 13:31:09.574 [vent.ItemStateChangedEvent] - itRS_PT_CucinaPortaFinestra changed from 100 to UNDEF
2018-06-20 13:31:09.577 [vent.ItemStateChangedEvent] - itRS_PT_Exp10CucinaPortaFinestra changed from 0 to UNDEF
2018-06-20 13:31:10.026 [vent.ItemStateChangedEvent] - itRS_P1_Cameretta changed from 100 to UNDEF
2018-06-20 13:31:10.030 [vent.ItemStateChangedEvent] - itRS_P1_Exp10Cameretta changed from 0 to UNDEF
2018-06-20 13:31:10.506 [vent.ItemStateChangedEvent] - itRS_P1_CameraCamilla changed from 100 to UNDEF
2018-06-20 13:31:10.528 [vent.ItemStateChangedEvent] - itRS_P1_Exp10CameraCamilla changed from 0 to UNDEF
2018-06-20 13:31:13.221 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5StudioStrada' received command STOP
2018-06-20 13:31:13.227 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5Lavanderia' received command STOP
2018-06-20 13:31:14.221 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5Bagno' received command STOP
2018-06-20 13:31:17.227 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10BagnoCamerette' received command STOP
2018-06-20 13:31:17.233 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10CucinaFinestra' received command STOP
2018-06-20 13:31:17.237 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10CameraMatrimoniale' received command STOP
2018-06-20 13:31:17.238 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10BagnoCameraMatrimoniale' received command STOP
2018-06-20 13:31:19.227 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5StudioStrada' received command STOP
2018-06-20 13:31:19.232 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10StudioCortile' received command STOP
2018-06-20 13:31:19.235 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5Lavanderia' received command STOP
2018-06-20 13:31:19.238 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10Soggiorno' received command STOP
2018-06-20 13:31:20.228 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10CucinaPortaFinestra' received command STOP
2018-06-20 13:31:20.233 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10Cameretta' received command STOP
2018-06-20 13:31:20.235 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp5Bagno' received command STOP
2018-06-20 13:31:21.229 [ome.event.ItemCommandEvent] - Item 'itRS_P1_Exp10CameraCamilla' received command STOP

…as you can see there are some timings not being respected.
I don’t even think it’s a hardware problem, because I was first using Home Assistant, and everything was working perfectly (I’m migrating from there since I can’t make my Tado AC work, ad It’s a core piece in my setup)…it has to be something binding/platform related.

Anyway first I’m going to try the “sleep-trick” you suggest…It seems a good starting point…

Then, if it’s not going to work, I’ll try using another binding…I was thinking about the exec binding: in HASS I managed everything via netcat commands…I’ll try to reproduce the same scenario in OpenHab to check if it’s something binding related.

Ohh … that’s clever. I never thought of select-an-Item as a means of selecting expire duration.

I see the problem.

First, what is changing the Items back to UNDEF? That is the cause of the problem. When ever the Item changes the Expire binding restarts its countdown so it will only send the STOP command 10 seconds after the last change. When we look just at itRS_PT_Exp10CucinaFinestra we see:

  1. receives UP command at 5 seconds after the minute
  2. changes from UNDEF to 0 a few milliseconds later
  3. changes from 0 to UNDEF at 7 seconds after the minute
  4. receives STOP command at 17 seconds after the minute
2018-06-20 13:31:05.520 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10CucinaFinestra' received command UP
2018-06-20 13:31:05.529 [vent.ItemStateChangedEvent] - itRS_PT_Exp10CucinaFinestra changed from UNDEF to 0
2018-06-20 13:31:07.024 [vent.ItemStateChangedEvent] - itRS_PT_Exp10CucinaFinestra changed from 0 to UNDEF
2018-06-20 13:31:17.233 [ome.event.ItemCommandEvent] - Item 'itRS_PT_Exp10CucinaFinestra' received command STOP

So, either we need to stop those changes to and from UNDEF or we can’t use the Expire binding in quite this way and may need a different approach, perhaps using a proxy Item. But I do have to say that it is unusual for an Item to go to UNDEF in the normal course of operation. UNDEF is intended to be a special state indicating undefined.

The UNDEF thing is binding related. I’m using a Bticino binding which should “map” the roller shutter timing and then manage a % based aperture (i.e. 60%) instead of using UP/DOWN/STOP commands, so (since it seems I can’t make it work properly), every time I give a command it first sets to 0 or 100 if I give UP or DOWN command (0=totally open, 100= totally closed) starting and ending from an UNDEF position (since the binding doesn’t know the roller shutter actual position).

I’ve also noticed a weird behavior with the expire binding: it seems to store in my gateway to rollershutters the expire time so, when I use physical commands (without passing from OpenHAB), the roller shutter still opens for the time I gave in the item.
Given that, I can’t use this method.

I’m now looking deeper into this: Design Pattern: Separation of Behaviors
I think I’ll try creating a “rules chain” where the rule “scenario” switch ON some (proxy) items (one for each roller shutter) that fires other rules with UP/sleep/STOP commands inside.

That is the Expire binding behaving as it should. Expire is very simple. If the Item is not the “default” state, set a timer and at the end of the timer change it back to that “default” state. In this case, when you press the physical button it sends an update to the Item which triggers the Expire binding to start its timer.

Did you try this binding for OH2?

just one question from an “advanced beginner”:grinning: in your post from June '18 there is a declaration
“val rs1Timer=5”.
Later it follows a line
“rs1Timer=10”.
I have learned from this forum that val means a constant, where var is a variable. How can this re-assignment work? Or do I have a wrong understanding from val and var?

It can’t. It’s an error. It should be val.