openHAB 4.0 wishlist

I would like a persistence function for item.runtimeSince(ZonedDateTime) that gives the time a switch have been on.
Then i can get rid of some rules.

1 Like

This will have a lot of preconditions but you should be able to do this with the persistence actions that exist now. previousState(true) returns a HistoricItem that comes from the first record found in the database where the state of the Item was saved as a different state than the Item currently has. So if the Item is currently ON, MySwitch.previousState(false) could return the HistoricItem and the .getTimestamp method will tell you when the Item turned ON. And if the Item is currently OFF, MySwitch.previousState(false) could return the time when the Item changed to ON and MySwitch.previousState(true) the time when the Item changed to ON and you can see how long the Item was ON before it was turned OFF.

I use the word “could” because it depends on the strategy being used to persist the states of these Items. You’ll have to use an everyChange strategy only for those Items (which excludes the possibility to use rrd4j for this).

If all you care about is to know how long the Item has been ON if it’s currently ON, you can even use MapDB for this. I’m not sure what the default MapDB strategy is but suspect it’s everyChange so previousState(false) will give you the time when the Item changed to it’s current state.

Another approach is to take advantage of the fact that Switches are stored as 1/0 in some databases (e.g. rrd4j). In that case you can take advantage of the fact that rrd4j stores a record every minute so you can use a MySwitch.sumSince(now.minusHours(1)) and get the number of minutes the Switch was On over that time period.

FYI I am using InfluxDB with persistence strategies everyChange and everyMinute, I am using MySwitch.history.averageSince(now.minusHours(24)) * 24 to get the number of hours my warm-water pump was on in the last 24 hours. This works great, no matter how many points are persisted since the average is time-weighted.

EDIT (for clarification): average since is time-weighted in all persistence services, not only InfluxDB. Influx is just what I use, rrd4j and the others should work the same.

It’s time-weighted in all persistence DBs if I’m not mistaken.

It’s probably a longshot for the wishlist, especially for 4.0 (probably needs to go on a future version) but have been thinking more about ‘simple’ global timers for (and aligned to) items.
Anyway, here’s the basic idea

  • Extend the Item class to include a method to set/manipulate a timer associated with the item
  • The timer would be managed by something within the OpenHAB core, outside the rule
  • There can be ONE and only one timer associated with each item
  • This same timer could then be queried/manipulated from any rule/language
  • These item timers could almost be simple enough to present in the UI, as an action in the rules section
  • The existing timers would still remain available in the context of a scripts for more complex use-cases (triggering functions etc), so this should be a non-breaking change (I think!!).

As a quick example of what I am thinking as a starting point for the conversation(excuse the pigeon-code, I am no Java dev!!), see the following:

  • Item.timer
    • Item.timer.setTimer(time,itemCommand)
      • Potentially 3 formats for time:
        • now+x
        • schedule+x (if timer already scheduled, add x to the current schedule time, if not scheduled, treat as now+x). I had considered a ‘-x’ option, but I can see too many situations where it could try set it in the past
        • absolute datetime value
      • Any existing scheduled timer would be cancelled and replaced with above
    • Item.timer.setTimerDiff(time,command if different)
      • Same behaviour as previous, but with send command if different function
    • Item.timer.reschedule(time)
      • Potentially 3 formats for time (see Item.timet.setTimer for details)
      • Returns nextCommand if timer is already active, otherwise FALSE (?)
    • Item.timer.isActive()
      • Returns absolute datetime value if scheduled, otherwise FALSE (?)
    • Item.timer.nextCommand()
      • Returns the next command to be run, if the timer is active, otherwise FALSE (?)
    • Item.timer.cancel()
      • Cancels the existing scheduled timer
      • Returns absolute datetime value if it was scheduled, otherwise FALSE (?)

Also, taking in to account some of the other wishes on here, maybe it could also include a restart persist flag for the setTimer methods, e.g. Item.timer.setTimer(time,itemCommand,persist{NO|YES|FORCE}) with the flags meaning:

  • NO (Default if flag omitted) - timer will not persist during reboot
  • YES - Timer will be recreated to execute at previously scheduled time IF scheduledtime >= ‘OH timer engine’ restart time, otherwise will be ignored
  • FORCE - Timer will be recreated to execute at previously scheduled time IF scheduledtime >= ‘OH timer engine’ restart time, otherwise will be executed immediately upon ‘OH timer engine’ restart.

I would be happy to be corrected, if there was already an easy way to already do this in OH, but unless my search terms have missed the mark, I can’t find anything in the documentation or community pages. Big wish? Yes. But it is a wishlist after all :slight_smile:

2 Likes

Sounds like a relatively straight forward extension to Expire. The way Expire works now is you define a state the Item should become (update or command) after a certain amount of time after the Item no longer receives an event. For example, update a sensor Item to UNDEF when it doesn’t receive an update for 15 minutes, or command a Switch to OFF five minutes after it last received a command ON.

Using Expire Updater [4.0.0.0;4.9.9.9] you can dynamically change the Expire time at runtime.

A simple rule can handle the restarting of the Expire timers (I should publish this as a rule template Rule template can now be found at: Restart Expire [4.0.0.0;4.9.9.9]).

configuration: {}
triggers:
  - id: "1"
    configuration:
      startlevel: 50
    type: core.SystemStartlevelTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >
        var {helpers} = require('openhab_rules_tools');


        console.loggerName = 'org.openhab.automation.rules_tools.Restart Expire.'+ruleUID;

        //osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'DEBUG');


        helpers.validateLibraries('4.1.0', '2.0.1');


        console.info('Restarting Expire timers');


        var expireItems = items.getItems().filter(i => i.getMetadata('expire') !== null).forEach( i => {
          if(i.getMetadata('expire').configuration['ignoreStateUpdates'] == 'true') {
            console.debug('Commanding Item ' + i.name + ' Expire by command');
            i.sendCommand(i.state);
          }
          else {
            console.debug('Resetting Item ' + i.name + ' Expire by update');
            i.postUpdate(i.state);
          }
        });
    type: script.ScriptAction

You can get pretty close to your requirements today.

1 Like

Hi @rlkoshak - Cheers for the response, and the scripts - Read it a few times, and I think I am getting my head around what you are suggesting…

It would meet the basic criteria of a timer which can be shared between rules, but I guess its only really a countdown timer as such, which allows for some of the manipulation I described.

In hindsight probably should have put in my original post what some of the key drivers were:

  • I started thinking about this while looking at options for moving away from the ‘create timer with arguments’ before it gets deprecated. I’m not necessarily suggesting that it will solve all my problems there (but it may help!!), but while looking through all the rules I wrote over the years, I realised how bloated they were for completing relatively simple & repeatable tasks

  • Given that as such, timers are only ‘visible’ and capable of being manipulated within the rule from which they are instantiated:

    • This has forced me to largely align my rules to items, so actions can be taken with the currently running timer(s) for that item (if any)
    • As a result I need to have a larger number of triggering events (House alarm set, house alarm unset, gate button pressed and so on), which result in the script becoming unwieldy determining which event occurred, and then the associated actions that need to follow
    • For the current timers, I also have code in each script which determines if the timers are defined/running (or not) before running the desired action with the timer, to avoid throwing errors etc
  • My ideal approach would be to to migrate to a ‘rulebook’ (/‘playbook’) type based approach for writing scripts:

    • This would be where rules are aligned with each ‘event’, e.g. House Alarm Set, House Alarm Unset, Gate Button Pressed, etc, and manipulate all necessary items/timers accordingly
    • One can then logically view/edit all automation actions which occur as a result of that event, in a single rule (if they desire)
  • Combined with some of the other suggestions, I also can then drop the code for handling undefined/not-running timers.

In summary, simplification is the key…All my rules currently run well at present, but I reckon with the proposed idea, I would shed 75% of my code overnight, and it would become a whole lot more readable/supportable…Cheers

Are you using JavaScript rules?

You can use the shared script to share timers across all rules and all available rule engines, e.g. for JavaScript see JavaScript Scripting - Automation | openHAB.

I am already using something comparable in my file-based JavaScript rules, where I have functions that basically generate rules based on passed in parameters.
For example, I have several Yamaha amplifiers/speakers and I want to enable scenes (e.g. tuner/radio, bluetooth) on them using my KNX switches. I have a single function where I implement the logic that the rule should have, and then I call this function with the amplifier Item name, the Switch Item name and the input name as arguments. This creates a rule for each of my amplifiers, and I can easily adjust the logic for all these rules by modifying the script.

filter by item value in the gui, like when a item value has the number 210, that I can search for that 210, and not only the item name. that would be great.

1 Like

Hi @florian-h05 - Cheers for the feedback. I am using Javascript based rules (ECMA2021 from within the UI).

Had a quick look at the shared cache - this could be a winner perhaps - I didn’t realise I could store the timer handle in there, and manipulate (cancel, reschedule etc) from multiple rules…

I think I will need to carefully re-read it after drinking a lot of coffee, and if it still doesn’t make sense, try reading after wine instead :slight_smile: . If that all fails, I will come back here looking for examples !! Thanks.

1 Like

:+1:

createTimerWithArgument is deprecated there in favor of the thread-safe createTimer. From JavaScript perspective, there is no loss in functionality when using createTimer instead of createTimerWithArgument. To pass variables, either reference them (then they can be mutated after the timer was scheduled) or use a function generator (to avoid having vars mutaded), the docs are detailed about this: JavaScript Scripting - Automation | openHAB.

I think this use case as one of the main thoughts behing having the shared cache …

You should be able to find those in the Scripts & Rules - openHAB Community section of the community or get help there.

There is now a shared cache where you can put timers that are shared between rules. @florian-h05 has covered that.

Management of timers can be greatly simplified with openhab-rules-tools TimerMgr. All that book keeping is done for you, all you need to do is call one function. For example:

var {timerMgr} = require('openhab-rules-tools');

cache.shared.get('tm', () => new timerMgr.TimerMgr()); 

tm.check(event.itemName, 'PT5M', () => { console.info('Timer went off') }, true, () => { console.info('Timer already exists'), event.itemName+'MyTimer');

In that one function call it:

  1. checks to see if there is a timer for event.itemName
    a. if not create a timer to go off in five minutes (anything supported by time.toZDT() can be passed for the timer time, PT5M is I SO8601 duration string meaning five minutes) and to call the passed in function.
    b. If a timer exists, reschedule it for five more minutes (that’s what the true means, if the fourth argument is false or left out the timer is not rescheduled) and call the function passed in for the fifth argument.
  2. If there is an error, the String in the last argument will be included in the error message so it’s easier to track which timer generated the error. The rule UID is used when that argument isn’t provided.

Only the first and second arguments are required.

Also, don’t ignore Scenes. This lets you create a special purpose rule where you list a bunch of Items and what state they should be in. Then you can create another rule that gets triggered by events that call the desired Scene when it’s warranted (the advanced users among you can open the “Code” tab and add triggers manually :smiling_imp:).

This is good for controlling lighting and stuff like that.

Unfortunately, I can’t take screen shots right now or I’d show you. On Friday my UPS died and took down my ESXi machine with it. Once I replaced the batteries in the UPS and brought everything back up my VMs were toast (no storage found) and had to be recreated. I decided to move to Proxmox since my machine is old an no longer supported by VMWare, which is neither here nor there. But in my process to rebuild my environment the restore of my Gitlab backup isn’t working. It says it’s restoring everything but when I come back there are no users, no projects, and no repos. Luckily I have my Ansible checked out and safe as well as my fish configs. But not my OH config which may have to be recreated from scratch if I can’t solve this. :sob:

If anyone has experience restoring a GitLab instance using a backup taken with gitlab-backup I could use help.

Thanks for the extensive advice @florian-h05 and @rlkoshak That certainly opens up a world of possibilities for me, once I get my head completely around the concepts presented above. If I get stuck in the process, I will probably move any further questions to a new post, to avoid cluttering up the Wishlist…

To set my priority for refactoring my existing rules, and leveraging the approaches above, is there any indication within which OH release the createTimerWithArgument will actually be deprecated within? I’ve got a few new integrations I would rather play with first…

@rlkoshak - Sorry to hear about your rig failing - Hope you get it sorted soon

The scenes (coming with openHAB 4) are already in the docs of the latest branch: Rules - Scenes | openHAB.

That’s really sad to hear. As a last option before having to rebuild everything, you can untar the GitLab Backup (AFAIK it is tared) and you should be able to find your repos inside the tar.

It is marked as deprecated in JS, however I (as the JavaScript library maintainer) don’t plan to remove it anytime soon. It is not recommended to use it because it is not thread-safe (createTimer, setTimeout and setInterval are thread-safe since openHAB 3.4.0), which can lead to timers not executing. So if you want a reliable automation with JavaScript Scripting, you should switch to createTimer or setTimeout ASAP.

Or just use @rlkoshak 's GitHub - rkoshak/openhab-rules-tools: Library functions, classes, and examples to reuse in the development of new Rules. instead, I usually take care that this is in sync with the development of the openHAB JavaScript library and I myself use the TimerMgr really much, it’s really helpful.

Thanks! I tried that but the repos are in some weird hashed format. I can’t even tell which one is which. But that is my next avenue of attack. But my son really wants Plex back so he can watch his Studio Ghibli so I gotta work on that first and that’s not in Gitlab. Priorities. :wink:

3 Likes

Afaik the backup restore is a bit finicky.
You need to be on the same version to the minor number and you need the secrets from your previous install.
Then first get the new install running and then restore, not the other way around.
At work we refer to docs which are best read with a cup of coffee nearby: Restore GitLab | GitLab

Very familiar with those docs unfortunately. The versions should be the same (still can’t figure out how to tell though from the backup tar file) and the docs claim if the version from the backup is different that it’ll throw an error. And I have backups of secrets and my gitlab.rb files (the whole config directory actually I backup separately).

I’ll keep pounding on it and get it to work, or give up can count my losses. At least I got Plex back (and learned the way I was taking backups of that service were less than optimal so corrected that).

Thank goodness PostgreSQL’s restore worked like it was supposed to.

EDIT: I was one number off between the third point on the versions so the restore was taking a long time to essentially do nothing. Now that the container and back versions are exactly the same, the restore seems to be working now. I’ve recovered all my repos. Now to get OH back up and running (and figure out why Librephotos isn’t letting my log in).

I need to follow my own advice. Don’t just take backups, verify they are working! Luckily I haven’t lost anything but time.

1 Like

Brilliant - Thanks for this - Knowing that the createtimerwithargument is not deprecated with OH4.0, and that OH4.0 brings the ‘scenes’ functionality, I think my plan will be to upgrade to OH4.0 stable first, then do a complete rewrite of my rules to leverage the features described above, including scenes, shared cache, and the timermgr (and getting rid of the createtimerwithargument in the process).

Will spin up a dev container with OH4.0M2 soon, so I can try prototype and get my head around the concepts before OH4.0 stable comes out, and I look to ‘attack’ my prod system.

Do the scenes do a equivalent of ‘send command’ or ‘send command if different’? - Couldn’t spot that in the above mentioned page. Cheers

Also look at the rule templates in the marketplace. You might be able to use what’s there without needing to rewrite thing yourself.

I thought createtimerwithargument was deprecated? Has that decision changed? I last read about it early this year or late last year I think.