Not Dropping out of While Loop using time

  • Platform information:
    • PI4
    • Openhabian
      version_
    • openHAB 4.0
      Summary: Executing while loop based on time window derived from now +. Seems to blow right through the time.

2023-11-17 14:13:05.489 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - 83
2023-11-17 14:13:05.492 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - 80
2023-11-17 14:13:05.493 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - 3
2023-11-17 14:13:05.495 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - in the big temp window
2023-11-17 14:13:05.500 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - lower than 3 dark gold
2023-11-17 14:13:05.501 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - start of Blinky light
2023-11-17 14:13:05.510 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - 2023-11-17T14:14:05.502-05:00[SYSTEM]
2023-11-17 14:13:05.522 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - in the loop
2023-11-17 14:13:05.526 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - 2023-11-17T14:13:05.523-05:00[SYSTEM]

Should be out of loop

2023-11-17 14:14:06.321 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - 25
2023-11-17 14:14:06.347 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - in the loop
2023-11-17 14:14:06.356 [INFO ] [nhab.automation.script.ui.2fc1e9c00d] - 2023-11-17T14:14:06.349-05:00[SYSTEM]
==> /var/log/openhab/events.log <==
2023-11-17 14:14:06.041 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'LEDVANCE_A19_RGBW_cds_ofc_Color' changed from 48.188976,100.0,100 to 48.188976,100.0,72
2023-11-17 14:14:06.044 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'LEDVANCE_A19_RGBW_Color' changed from 48.188976,100.0,100 to 48.188976,100.0,72

Safety exit is a loop count that breaks out.

Not sure I’m using the resolution correctly?
The code

loopcount = 1;
console.info('start of Blinky light');
console.info((time.ZonedDateTime.now()).plusMinutes(1));
while (zdtCompare((time.ZonedDateTime.now()).plusMinutes(1), (time.ZonedDateTime.now()), 'after', 'seconds', 'time')) {
  console.info('in the loop');
  console.info((time.ZonedDateTime.now()));
  items.getItem('LEDVANCE_A19_RGBW_cds_ofc_Color').sendCommand('OFF');
  items.getItem('Hue_color_Weather_Color').sendCommand('OFF');
  thread.sleep(1500);
  items.getItem('LEDVANCE_A19_RGBW_cds_ofc_Color').sendCommand('ON');
  items.getItem('Hue_color_Weather_Color').sendCommand('ON');
  thread.sleep(1000);
  loopcount = (typeof loopcount === 'number' ? loopcount : 0) + 1;
  console.info(loopcount);
  if (loopcount == 40) {
    break;
  }

Anyone see an issue with this, tried seconds and min for test but always blows past time, can change to time test and exit.
Cliff

In your while loop condition you get now anew every time the while loop checks to see if it’s done. So every time the loop executes now moves forward and you never catch up to that one minute from now because one minute from now is always in the future.

You need to save now plus minutes 1 to a variable and then use the variable in the while loop condition.

If I understand this correctly you trying to blink a light for up to a minute or 40 times, which ever comes first.

If I were to implement this I would do it with a Timer so you have to option to cancel it. But using a while loop…

I should have named the variable “endTime” but you get the idea. now is reevaluated every time the while loop iterates but “startTime” remains the same. After a minute the condition will become false.

Thanks, looking at it as if it was set once and tested each pass, not updated each pass, but it’s so obvious after you mentioned. I was thinking the block sort of sets up a timer and then checks time outside the do. When I looked at the code I see it now. Sort of like do while a <20 but you set a to 0 each time. Duh?

The times are do for hours blinking light every few seconds. The loop is a drag and so is the delay. In a similar misguided thought, I was thinking the while would get aborted when the time was hit like a system fork, not so.

I’ll look into a timers driven by core events.

Thanks

Cliff

I second this. Using a repeat loop isn’t a good idea because it keeps the loop busy the whole time while a rule is intended to run quickly. Rather use a time with a reschedule that stops rescheduling after 40min

Thanks

I’ll look into it. Is there any examples on your help page?

Cds

This one is similar:

only that in this ^ example the “scheduled” loop until a count is reached. In your case you need to save “now” in a variable (e.g. startTime) in the beginning and before the rescheduling you need to compute the passed time and if that exceeds the expected duration you don’t reschedule.

Calculating the difference can be done via

Thanks so much for the reply. I really enjoy the ease of programing with Blockly, thanks for the work.

What is the intent?
I have a colored light that change with tomorrow’s weather. (see Milwaukee gas building Wisconsin Gas Building - Wikipedia.
The flame flickers (on and off) when it’s going to rain, or snow)
Two parts:
Color Changing part red blue gold, and if rain tomorrow, light goes on and off from sunset until 11PM

The on and off is the blinking part. I don’t like the delay and Rich has suggested I use a timer. In my day it was a for and join, but now I think it’s part of the OS and it’s a thread with a core trigger.

I think you are suggesting I have an outer loop as part of a timer and check to see timer is still active, then the inner loop is the “blinkie”. I think 1 sec is an eternity in cpu cycles so may the overhead of a cron is not so bad.

I’ve got time today to explore more.

Cliff

You may not need a while loop nor a timer for this at all. If it’s always the same a cron triggered rule might be the easiest to implement.

If you relax the on/off times to be just one second instead of 1.5 seconds the rule becomes very simple.

configuration: {}
triggers:
  - id: "1"
    configuration:
      cronExpression: "* * * * * ? *"
    type: timer.GenericCronTrigger
conditions:
  - inputs: {}
    id: "2"
    configuration:
      blockSource: <xml xmlns="https://developers.google.com/blockly/xml"><block
        type="oh_zdt_between" id="PHU!!N)WV2]uo2LH@4_b" x="14" y="29"><field
        name="dateComparison">time</field><value name="zdtOne"><shadow
        type="oh_zdt" id=")K452c,b^!b=tbUCx;=o"><field
        name="day">2023-11-21</field></shadow><block type="oh_zdt_now"
        id="8[,apY14USqj/lqOH%ON"></block></value><value name="zdtTwo"><shadow
        type="oh_zdt" id="/M%!MCCi:u:^bfqioyK~"><field
        name="day">2023-11-21</field></shadow><block type="oh_zdt_fromItem"
        id="3,rxp4*)cLLb}sB7)C-^"><value name="itemName"><shadow type="oh_item"
        id="%~n1xA84JGAeFgGE[2pG"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="%~n1xA84JGAeFgGE[2pG"><mutation itemName="Default_Day"
        itemLabel="Default Day"></mutation><field
        name="itemName">Default_Day</field></block></value></block></value><value
        name="zdtThree"><shadow type="oh_zdt" id="SCE81J(9-s#xRDL}p8k)"><field
        name="day">2023-11-21</field></shadow><block type="oh_zdt_create"
        id="G,DrwHR?-`+J|v9u@K5V"><value name="year"><shadow type="math_number"
        id="R#cJKC@)d|-BGQx*wi%1"><field
        name="NUM">2005</field></shadow></value><value name="month"><shadow
        type="math_number" id="zNhg.H[*Z%+_qL}^$B%:"><field
        name="NUM">4</field></shadow></value><value name="day"><shadow
        type="math_number" id="ms(?|}iyqRQ`*7u?xSt?"><field
        name="NUM">12</field></shadow></value><value name="hour"><shadow
        type="math_number" id="QREb_kY_K{(a5#g)(tsY"><field
        name="NUM">23</field></shadow></value><value name="minute"><shadow
        type="math_number" id="TQ!i9ycS;4A1:A~I$x8f"><field
        name="NUM">10</field></shadow><block type="math_number"
        id="TQ!i9ycS;4A1:A~I$x8f"><field
        name="NUM">0</field></block></value><value name="second"><shadow
        type="math_number" id="4iy#_N,i,vzVTGK`#%DI"><field
        name="NUM">58</field></shadow><block type="math_number"
        id="4iy#_N,i,vzVTGK`#%DI"><field
        name="NUM">0</field></block></value></block></value></block></xml>
      type: application/javascript
      script: >
        // graalVM

        function zdtCompare(zdt1, zdt2, compareOp, precision, compDate) {
          switch (precision) {
            case 'years':
             zdt2 = zdt2.withMonth(zdt1.monthValue());
            case 'months':
             zdt2 = zdt2.withDayOfMonth(zdt1.dayOfMonth());
            case 'days':
             zdt2 = zdt2.withHour(zdt1.hour());
            case 'hours':
             zdt2 = zdt2.withMinute(zdt1.minute());
            case 'minutes':
             zdt2 = zdt2.withSecond(zdt1.second());
            case 'seconds':
             zdt2 = zdt2.withNano(zdt1.nano());
          }
          if (compDate === 'date') {
            zdt1 = zdt1.toLocalDate();
            zdt2 = zdt2.toLocalDate();
          } else if (compDate === 'time') {
            zdt1 = zdt1.toLocalTime();
            zdt2 = zdt2.toLocalTime();
          }
          switch (compareOp) {
            case 'before':
              return zdt1.isBefore(zdt2);
            case 'equal':
              return zdt1.equals(zdt2);
            case 'after':
              return zdt1.isAfter(zdt2);
            case 'beforeEqual':
              return zdt1.isBefore(zdt2) || zdt1.equals(zdt2);
            case 'afterEqual':
              return zdt1.isAfter(zdt2) || zdt1.equals(zdt2);
          }
        }



        (time.ZonedDateTime.now()).isBetweenTimes((time.toZDT(items.getItem('Default_Day'))), time.ZonedDateTime.now().withYear(2005).withMonth(4).withDayOfMonth(12).withHour(23).withMinute(0).withSecond(0).withNano(0));
    type: script.ScriptCondition
actions:
  - inputs: {}
    id: "3"
    configuration:
      blockSource: <xml
        xmlns="https://developers.google.com/blockly/xml"><variables><variable
        id="1HSiEMy:fL^_2~;Svj~F">newCommand</variable></variables><block
        type="variables_set" id="#RN1dgP^c{T$tbK%BhI`" x="37" y="25"><field
        name="VAR" id="1HSiEMy:fL^_2~;Svj~F">newCommand</field><value
        name="VALUE"><block type="text" id="iG9-:i]jr6A5[!-emfLS"><field
        name="TEXT">OFF</field></block></value><next><block type="controls_if"
        id="O.%2G2P3ZN/=#vqEm*{|"><value name="IF0"><block type="logic_compare"
        id="RE_Ck%2oG~3f-;mj!u-m"><field name="OP">EQ</field><value
        name="A"><block type="oh_getitem_attribute"
        id="?)Q(iMNG[Ui3rdW![YJ,"><mutation
        attributeName="State"></mutation><field
        name="attributeName">State</field><value name="item"><shadow
        type="oh_getitem" id="iZ@.SUf=sP}ub{mj|xXv"><value
        name="itemName"><shadow type="oh_item"
        id="omk.nh-?FTaOIJSNDKQ@"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow></value></shadow><block
        type="oh_getitem" id="iZ@.SUf=sP}ub{mj|xXv"><value
        name="itemName"><shadow type="oh_item"
        id="omk.nh-?FTaOIJSNDKQ@"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="omk.nh-?FTaOIJSNDKQ@"><mutation itemName="FamilyRoomOutlet"
        itemLabel="Family Room Lamp 1"></mutation><field
        name="itemName">FamilyRoomOutlet</field></block></value></block></value></block></value><value
        name="B"><block type="text" id="$72gM9~5{YuWLv`oPZ*W"><field
        name="TEXT">OFF</field></block></value></block></value><statement
        name="DO0"><block type="variables_set" id="CUb,1oLkwQZ8Nds8L!+h"><field
        name="VAR" id="1HSiEMy:fL^_2~;Svj~F">newCommand</field><value
        name="VALUE"><block type="text" id="R5V}NI:t[FxfLyS!6!xZ"><field
        name="TEXT">ON</field></block></value></block></statement><next><block
        type="oh_event" id="Kn;+(xN:@KA?Flr_JTa$"><field
        name="eventType">sendCommand</field><value name="value"><shadow
        type="text" id="4W7Rfux)nlpB7IzV^6:j"><field
        name="TEXT">value</field></shadow><block type="variables_get"
        id="9H#Hh@|WH;W[MrAShPha"><field name="VAR"
        id="1HSiEMy:fL^_2~;Svj~F">newCommand</field></block></value><value
        name="itemName"><shadow type="oh_item"
        id="-.+cj*C-.%)j3Cz{/8hD"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="-.+cj*C-.%)j3Cz{/8hD"><mutation itemName="FamilyRoomOutlet"
        itemLabel="Family Room Lamp 1"></mutation><field
        name="itemName">FamilyRoomOutlet</field></block></value><next><block
        type="oh_event" id="=f7,|@Zk~9XHpH;*Z.M0"><field
        name="eventType">sendCommand</field><value name="value"><shadow
        type="text" id="=n0hvFrkFTGsIF]5xlY5"><field
        name="TEXT">value</field></shadow><block type="variables_get"
        id="sN.=S)FuD6N(hMt=JY}F"><field name="VAR"
        id="1HSiEMy:fL^_2~;Svj~F">newCommand</field></block></value><value
        name="itemName"><shadow type="oh_item"
        id="w=!,IrP3P-;*ARK~S~.@"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="K,v^y3ns$AHklt4@h}w-"><mutation itemName="FamilyRoomOutlet"
        itemLabel="Family Room Lamp 1"></mutation><field
        name="itemName">FamilyRoomOutlet</field></block></value></block></next></block></next></block></next></block></xml>
      type: application/javascript
      script: |
        var newCommand;


        newCommand = 'OFF';
        if (items.getItem('FamilyRoomOutlet').state == 'OFF') {
          newCommand = 'ON';
        }
        items.getItem('FamilyRoomOutlet').sendCommand(newCommand);
        items.getItem('FamilyRoomOutlet').sendCommand(newCommand);
    type: script.ScriptAction

Note I used one of my DateTime Items that gets set to Astro Dawn and two of my Iight Items. You’d use yours.

The condition looks like:

The Action looks like:

The trigger runs the rule once a second.

The condition skips the rule except between dawn and 23:00.

The action toggles the two lights.

That makes the rule run every second between dawn and 23:00 and toggles the light so it remains on for a second, then off for a second, and so on.

2 Likes

I’ll need to study this. It’s a combo of Blockly and JavaScript I guess

No, 100% Blockly.

The YAML shows the full Rule. It’s what you’d see if you clicked on the “Code” tab of the rule. You see all the triggers, conditions and actions in context this way.

Create a rule, copy that YAML and paste it into the code tab and you’ll have a copy of the whole rule.

The only “code” are the two blockly scripts I pasted screen shots into above. All that “blockSource” stuff is what those graphics turn into for storage and the script stuff is what the Blockly compiles into. But all you need to worry about is the graphical Blockly scripts.

Create a Blockly rule and click on the code tab and you’ll see something similar for any of your rules.

If I were to write this as JS Scripting it would look something like this:

configuration: {}
triggers:
  - id: "1"
    configuration:
      cronExpression: "* * * * * ? *"
    type: timer.GenericCronTrigger
conditions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >
        time.toZDT().isBetween(time.toZDT(items.Default_Day), time.toZDT('23:00');
    type: script.ScriptCondition
actions:
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript
      script: |
        newCommand = (items.FamilyRoomOutlet.state == 'OFF') ? 'ON' : 'OFF';
        items.getItem('FamilyRoomOutlet').sendCommand(newCommand);
        items.getItem('FamilyRoomOutlet').sendCommand(newCommand);
    type: script.ScriptAction

It’s the rule trigger.

`* * * * * ? *

In the YAML above it’s this part:

triggers:
  - id: "1"
    configuration:
      cronExpression: "* * * * * ? *"
    type: timer.GenericCronTrigger

There is a cron builder in MainUI.

In MainUI it would be:

As a DSL Rule as a file based rule trigger:

rule "Weather lights blinking"
when
    Time cron "* * * * * ? *"
then
    ...
end

The rule triggers every second do there is no reference to it in the Blockly. It’s handled by the rule trigger.

Blockly doesn’t use cron. That’s a rule trigger thing.

If I break it down:

configuration: {}

This is usually empty unless the rule was created from a rule template.

triggers:
  - id: "1"
    configuration:
      cronExpression: "* * * * * ? *"
    type: timer.GenericCronTrigger

These are the rule triggers. This is a cron trigger that runs this rule every second. This is your cron expression. No Blockly involved.

conditions:
  - inputs: {}
    id: "2"
    configuration:
      blockSource: <xml xmlns="https://developers.google.com/blockly/xml"><block
        type="oh_zdt_between" id="PHU!!N)WV2]uo2LH@4_b" x="14" y="29"><field
        name="dateComparison">time</field><value name="zdtOne"><shadow
        type="oh_zdt" id=")K452c,b^!b=tbUCx;=o"><field
        name="day">2023-11-21</field></shadow><block type="oh_zdt_now"
        id="8[,apY14USqj/lqOH%ON"></block></value><value name="zdtTwo"><shadow
        type="oh_zdt" id="/M%!MCCi:u:^bfqioyK~"><field
        name="day">2023-11-21</field></shadow><block type="oh_zdt_fromItem"
        id="3,rxp4*)cLLb}sB7)C-^"><value name="itemName"><shadow type="oh_item"
        id="%~n1xA84JGAeFgGE[2pG"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="%~n1xA84JGAeFgGE[2pG"><mutation itemName="Default_Day"
        itemLabel="Default Day"></mutation><field
        name="itemName">Default_Day</field></block></value></block></value><value
        name="zdtThree"><shadow type="oh_zdt" id="SCE81J(9-s#xRDL}p8k)"><field
        name="day">2023-11-21</field></shadow><block type="oh_zdt_create"
        id="G,DrwHR?-`+J|v9u@K5V"><value name="year"><shadow type="math_number"
        id="R#cJKC@)d|-BGQx*wi%1"><field
        name="NUM">2005</field></shadow></value><value name="month"><shadow
        type="math_number" id="zNhg.H[*Z%+_qL}^$B%:"><field
        name="NUM">4</field></shadow></value><value name="day"><shadow
        type="math_number" id="ms(?|}iyqRQ`*7u?xSt?"><field
        name="NUM">12</field></shadow></value><value name="hour"><shadow
        type="math_number" id="QREb_kY_K{(a5#g)(tsY"><field
        name="NUM">23</field></shadow></value><value name="minute"><shadow
        type="math_number" id="TQ!i9ycS;4A1:A~I$x8f"><field
        name="NUM">10</field></shadow><block type="math_number"
        id="TQ!i9ycS;4A1:A~I$x8f"><field
        name="NUM">0</field></block></value><value name="second"><shadow
        type="math_number" id="4iy#_N,i,vzVTGK`#%DI"><field
        name="NUM">58</field></shadow><block type="math_number"
        id="4iy#_N,i,vzVTGK`#%DI"><field
        name="NUM">0</field></block></value></block></value></block></xml>
      type: application/javascript
      script: >
        // graalVM

        function zdtCompare(zdt1, zdt2, compareOp, precision, compDate) {
          switch (precision) {
            case 'years':
             zdt2 = zdt2.withMonth(zdt1.monthValue());
            case 'months':
             zdt2 = zdt2.withDayOfMonth(zdt1.dayOfMonth());
            case 'days':
             zdt2 = zdt2.withHour(zdt1.hour());
            case 'hours':
             zdt2 = zdt2.withMinute(zdt1.minute());
            case 'minutes':
             zdt2 = zdt2.withSecond(zdt1.second());
            case 'seconds':
             zdt2 = zdt2.withNano(zdt1.nano());
          }
          if (compDate === 'date') {
            zdt1 = zdt1.toLocalDate();
            zdt2 = zdt2.toLocalDate();
          } else if (compDate === 'time') {
            zdt1 = zdt1.toLocalTime();
            zdt2 = zdt2.toLocalTime();
          }
          switch (compareOp) {
            case 'before':
              return zdt1.isBefore(zdt2);
            case 'equal':
              return zdt1.equals(zdt2);
            case 'after':
              return zdt1.isAfter(zdt2);
            case 'beforeEqual':
              return zdt1.isBefore(zdt2) || zdt1.equals(zdt2);
            case 'afterEqual':
              return zdt1.isAfter(zdt2) || zdt1.equals(zdt2);
          }
        }



        (time.ZonedDateTime.now()).isBetweenTimes((time.toZDT(items.getItem('Default_Day'))), time.ZonedDateTime.now().withYear(2005).withMonth(4).withDayOfMonth(12).withHour(23).withMinute(0).withSecond(0).withNano(0));
    type: script.ScriptCondition

This is the Script Condition. It’s a whole lot of text in the YAML but you don’t have to worry about that. In the UI it’s just

Even though the rule triggers every second all day long, this only runs the script actions if now is between the time stored in the Item Default_Day and 23:00. Only the time is compared so you can ignore the date part.

The XML at the top defines the “blocks” that make up the script condition. The JavaScript at the bottom is what the Blocks turn into to run.

actions:
  - inputs: {}
    id: "3"
    configuration:
      blockSource: <xml
        xmlns="https://developers.google.com/blockly/xml"><variables><variable
        id="1HSiEMy:fL^_2~;Svj~F">newCommand</variable></variables><block
        type="variables_set" id="#RN1dgP^c{T$tbK%BhI`" x="37" y="25"><field
        name="VAR" id="1HSiEMy:fL^_2~;Svj~F">newCommand</field><value
        name="VALUE"><block type="text" id="iG9-:i]jr6A5[!-emfLS"><field
        name="TEXT">OFF</field></block></value><next><block type="controls_if"
        id="O.%2G2P3ZN/=#vqEm*{|"><value name="IF0"><block type="logic_compare"
        id="RE_Ck%2oG~3f-;mj!u-m"><field name="OP">EQ</field><value
        name="A"><block type="oh_getitem_attribute"
        id="?)Q(iMNG[Ui3rdW![YJ,"><mutation
        attributeName="State"></mutation><field
        name="attributeName">State</field><value name="item"><shadow
        type="oh_getitem" id="iZ@.SUf=sP}ub{mj|xXv"><value
        name="itemName"><shadow type="oh_item"
        id="omk.nh-?FTaOIJSNDKQ@"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow></value></shadow><block
        type="oh_getitem" id="iZ@.SUf=sP}ub{mj|xXv"><value
        name="itemName"><shadow type="oh_item"
        id="omk.nh-?FTaOIJSNDKQ@"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="omk.nh-?FTaOIJSNDKQ@"><mutation itemName="FamilyRoomOutlet"
        itemLabel="Family Room Lamp 1"></mutation><field
        name="itemName">FamilyRoomOutlet</field></block></value></block></value></block></value><value
        name="B"><block type="text" id="$72gM9~5{YuWLv`oPZ*W"><field
        name="TEXT">OFF</field></block></value></block></value><statement
        name="DO0"><block type="variables_set" id="CUb,1oLkwQZ8Nds8L!+h"><field
        name="VAR" id="1HSiEMy:fL^_2~;Svj~F">newCommand</field><value
        name="VALUE"><block type="text" id="R5V}NI:t[FxfLyS!6!xZ"><field
        name="TEXT">ON</field></block></value></block></statement><next><block
        type="oh_event" id="Kn;+(xN:@KA?Flr_JTa$"><field
        name="eventType">sendCommand</field><value name="value"><shadow
        type="text" id="4W7Rfux)nlpB7IzV^6:j"><field
        name="TEXT">value</field></shadow><block type="variables_get"
        id="9H#Hh@|WH;W[MrAShPha"><field name="VAR"
        id="1HSiEMy:fL^_2~;Svj~F">newCommand</field></block></value><value
        name="itemName"><shadow type="oh_item"
        id="-.+cj*C-.%)j3Cz{/8hD"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="-.+cj*C-.%)j3Cz{/8hD"><mutation itemName="FamilyRoomOutlet"
        itemLabel="Family Room Lamp 1"></mutation><field
        name="itemName">FamilyRoomOutlet</field></block></value><next><block
        type="oh_event" id="=f7,|@Zk~9XHpH;*Z.M0"><field
        name="eventType">sendCommand</field><value name="value"><shadow
        type="text" id="=n0hvFrkFTGsIF]5xlY5"><field
        name="TEXT">value</field></shadow><block type="variables_get"
        id="sN.=S)FuD6N(hMt=JY}F"><field name="VAR"
        id="1HSiEMy:fL^_2~;Svj~F">newCommand</field></block></value><value
        name="itemName"><shadow type="oh_item"
        id="w=!,IrP3P-;*ARK~S~.@"><mutation itemName="MyItem"
        itemLabel="MyItem"></mutation><field
        name="itemName">MyItem</field></shadow><block type="oh_item"
        id="K,v^y3ns$AHklt4@h}w-"><mutation itemName="FamilyRoomOutlet"
        itemLabel="Family Room Lamp 1"></mutation><field
        name="itemName">FamilyRoomOutlet</field></block></value></block></next></block></next></block></next></block></xml>
      type: application/javascript
      script: |
        var newCommand;


        newCommand = 'OFF';
        if (items.getItem('FamilyRoomOutlet').state == 'OFF') {
          newCommand = 'ON';
        }
        items.getItem('FamilyRoomOutlet').sendCommand(newCommand);
        items.getItem('FamilyRoomOutlet').sendCommand(newCommand);
    type: script.ScriptAction

This is the script action. Again it’s a whole lot of text but in the UI you’d only see

When the rule is triggered and the condition allows the action to be run, this script will look to see if the light is already ON and turn it OFF, or if it’s already OFF it will turn it ON.

Therefore:

  • Trigger: run once per second
  • Condition: but only between Default_Day and 23:00
  • Do: Toggle the lights ON or OFF

Screen Shots showing how you would copy/paste and how the rule looks in the UI.

Click the Code Tab and paste the YAML from above.

Return to the Design Tab.

The Trigger:

The Condition (But only if…):

The Action:

2 Likes

where does the 1sec com? I don’t see the cron ref in Blockly?

I have a sunset rule that is turning everything on. Then I just shut it down at 11:00pm. (may make random down the road for security)
cds

Where does the 1 sec come from?? I don’t see a cron. I see the is now…That looks like the stop at end of day part?
I was going to look into blockly’s use of cron today. I’m jumping in and out of house working on a project but checking mail.

I’ll dig in more after 2pm.
Thanks
Cliff

Rich, you are amazing, like always. I just had to mention this here :heavy_heart_exclamation:

1 Like

Ok, I sort of get it, Trigger → Script → Conditional run.

Question: What is the overhead of a rule set to run every second? Does the rule get initiated once and stays in effect until effectively broken out of?
Is this running (5hr x 60min x 60 sec or 18,000 cron jobs)? I don’t know a way to instrument the processor, but I’d be curious to see if 18,000 cron invocations is better or worse than one loop? Don’t know the underlying core engine. I think I saw PS. I don’t know if a rule process will show up on the running process list. Do you know if this approach is significantly less burdensome than the loop. Probably, but I don’t know the cron overhead cost? Don’t get me wrong, I’m not challenging you in anyway, just technical curiosity. It’s got to be faster because the loop was dragging down log entries. Coun’t get the image to go up in code fence. Do running rules show up anywhere. This is from top?

Anyway, you have gone above and beyond in your explanation.

Unless you are running on an underpowered machine (e.g. RPi 0W) there is probably little you could do to make a measurable impact on the system resources.

Running a simple rule like this once a second would be almost unmeasureable.

Being concerned about resource usage like this is premature. CPU is almost never a problem in the home automation context. And since your time is much more valuable than the CPU’s. When the consumption of CPU resources starts to consume your time is when it’s appropriate to be concerned about resources like these. In the mean time, you need to be concerned with what does consume your time and resources. And that means concentrating on clear, simple, and maintainable rules.

Don’t solve problems you don’t even have.

No, it triggers once a second and exits probably in milliseconds. This is a really simple rule.

No, it’s running 86400 cron jobs. Of those the condition causes 68400 of them exit the rule early thanks to the condition.

But in all 86400 of those cases the CPU spends maybe 100 msec on the outside actually consuming CPU. So in the worse case I can think of, 90% of your CPU will be idle doing nothing.

OH is designed to be event driven. An event happens, a rule triggers, does something in response and exits. OH runs best when rules run and exit fast (milliseconds). You are kind of working against that when you create one rule that just sits there and runs forever or hours or even seconds. Using a timer is a little bit better because then when the rule gets triggered again it can see that there is already a timer and take some action to clean up after itself (e.g. cancel the timer and create a new one). But even better is to take advantage of the event driven nature of OH to simplify the code.

CPU consumption wise the difference will be unmeasureable. However, there are disadvantages to running in a while loop.

  • Only one instance of a rule can run at a time. If the rule is running and gets triggered again, that trigger doesn’t just go away. It gets queued up and will be processed when the running rule exits. You could end up with quite a backlog of triggers that could take hours to work off in some circumstances. So you have to account for that in your rule and how you design it to run.

  • It requires more complex code to cancel a running loop or modify it. As a case in point, to use a while loop or even a Timer above, you have to keep state (i.e. now) outside of the loop and have to set up logic to compare to it. In the cron triggered example you don’t need to keep track of state in the same way resulting in simpler to write, understand, and maintain code.

You can pass a flag to htop to show running threads but you would be hard pressed to identity which thread goes with which rule. And you wouldn’t see it consuming any resources either way since it does so little. It will pretty much show CPU usage as 0% all the time.

It’s less burdensome in the only way that matters. It’s simpler and easier to understand and maintain in the long run. Let the CPU take care of itself unless and until you start to actually see performance problems. And when/if you do, it’s not going to be this rule that’s the cause.

You could add logging statements to the cron triggered rule too if you want. There really isn’t a need to unless it’s not working the way you want.

You can use debug level logging and set up the logger for the rule in log4j2.xml to log at the debug level while you need them and they set it back to info level when you don’t. The code can keep the log statements but they won’t be added to the log.

1 Like

I can agree with that :+1:

1 Like

good points. The PI is much more powerful than I understood. I come from the world of cycle shaving on baby cpu’s. Guess it’s not a problem on today’s modern microP.

BTW what do you use your system for. I recall some remarks you made about I don’t do this or this? Is your house automated? Security? Light, heat? Music distribution? integrated with Amazon or Google? Where is the fun for you?
cds

Story… When I was in my 20’s I owned my first house, was poor and was heating rooms that didn’t need it. I decided to add duct dampeners to each room and then if I was in that room the heat would be allowed into that room. I tuned an openloop opamp for gain of ~1m and mounted the guts of a speaker cone to the floor joist below the room driving the opamp. My footsteps would rail the opamp which drove a timer to open the dampener. Worked great until the day the garbage truck showed up! Airplanes, firesirens would open all vents in all rooms… I sure love openHab!!!

On an RPi 3 or below, RAM is the limiting resource, not CPU. Home automation simply isn’t usually all that compute intensive over all. You can afford to be “wasteful” with the CPU because you’ll probably run out of RAM before you consume enough CPU to be a problem.

As with all things, there are exceptions.

My set up is relatively modest compared to some. I don’t “Automate all the Things!”. Instead I try to solve practical problems as they arise and keep my system humming along.

  • All my lighting is controlled by OH, but that doesn’t mean all the lights. There are a couple in the main rooms where it matters (family room, front room, bedrooms) that get controlled by various logic (e.g. come on when it’s cloudy, driven by presence, driven by weather and astro, etc).

  • My thermostat is connected and I can control it from OH. But mostly why I have this is so that OH can change between heating mode and cooling mode depending on the season and weather. My thermostat doesn’t support changing the mode on it’s own.

  • I’ve sensors through out the house to measure temp and humidity. These are used to help drive the thermostat mode and drive some dumb made smart humidifiers. I also use them to generate reminders when it appears a humidifier has run out of water (i.e. it’s on but the humidity remains low).

  • I get alerts when any of the exterior doors are open for too long.

  • I can open/close the garage door openers remotely.

  • All the TVs shut off automatically at 11:30 every night.

  • I get alerts when certain batteries are low.

  • I get alerts when a sensor stops reporting for too long of a time.

  • I get alerts when a service or machine that is important to the home automation goes offline.

  • I have a camera feed of the garage. I don’t like operating something like a garage door opener without having eyes on the doors.

  • I can turn on/off AdGuard (for those times when that link in the email contains trackers but I really want to click on it anyway).

  • I’ve a couple of water leak sensors where I have some questionable plumbing. I think I’ve finally fixed it. :crossed_fingers: These send an alert.

  • My deadbolts are connected but that’s mainly to monitor the battery. I don’t actually think locking/unlocking works right now and don’t really care.

  • I’ve a whole house power usage sensor I use to estimate the month’s power bill.

  • I track presence but don’t really do anything with it any more.

  • I get alerts if the radon levels are too high.

  • I get alerts if the power is lost and my machines are running off of UPS and track how long they can run on UPS.

  • I get alerts when one of the Google Home Hubs and Minis start playing something so I can monitor the kiddo’s usage. I can control them through OH but never do.

  • I get an alert if a motion sensor at my dad’s house doesn’t trigger for too long.

  • Lights and HVAC, are integrated with Google.

  • Christmas lights are controlled through OH as well.

Outside of OH I have:

  • other cameras
  • video doorbell
  • CO and smoke alarms
  • media control and streaming

I used to track locations but don’t any more. I used to do audio TTS announcements but my wife found them too creepy. I have a bunch of stuff I could integrate but I won’t just because. I’ll wait until I have a need.

I was talking with my son and I might be creating a weather cloud. It probably won’t be integrated with OH but it might be enough to get him some practical programming exposure.

I like to solve problems, be they my own or other people’s. I like to code. I like clean and elegant code.

My first foray into home automation involved a really old garage door opener that was too old to get a replacement remote to control. An RPi 1, some relays and a crappy web interface and I was off to the races.

That’s a lot more than me. I just had a goal of some lights and the weather. Sort of lazy but here in FL there isn’t much weather most of the year. Hurricanes in the fall, some predicable rain in the winter. In summer the convective activity is not so predictable.
I like the video stuff but turns out no support for Ring. I also like the audio because I would like to announce, it’s 10:00 your shop door is still open, later.

With this info you just sent me I’ll be able to run “blinky” to flash the rain indicator light. I did a small mode on the “Milwaukee weather light”.
less than 2 degrees the light is pale red or gold
over 2 degrees dark red or dark gold for cold. That’s been nice. Sort of like “little change”
Thanks for the help and the insight. I really like the Dad monitor. Could have used that to check the caregiver and him. He’s gone now. Good luck with your weather. If I recall you are colorado or arizona. I was checking out a place near phoenix today. An airpark aka a home with a hangar. To hot in summer!

BTW, I put a lot of none OH stuff in these message, tried to reply via email directly but is gets kicked back. Thought I set email correct, checking again.
cds