Expire Updater [4.0.0.0;4.9.9.9]

image

There are circumstances where one might want to modify the Expire timeout duration from a rule. For example, maybe a light should stay on longer on weekends or when no one is home.

This rule template will create a Script type rule. It does not have any triggers of it’s own. It is intended to be called from another rule, passing the Item and the duration string as arguments.

parameter expected value
item The Item Object or name of the Item to update the metadata
newDuration A duration string supported by the expire binding. Note, Expire will accept just defining the parts of the duration you are using (e.g. 5s) but that confuses MainUI so always include all three fields (e.g.0h0m5s). Using capital letters also confuses MainUI. In both cases they are treated as an error by the rule.

If newDuration is null or not passed, the rule will remove the expire metadata from the Item . Only the duration time is changed. All other parts of the expire metadata remains unchanged.

If the Item does not have expire metadata, it will be added.

An example in JS Scripting 11 for calling the rule.

rules.runRule('dynamic_expire', 
              {item:'TestSwitch', newDuration:'0h2m3s'}, 
              true);

Watch the logs for errors.

Language: JS Scripting 11

Dependencies:

  • openhab-js 4.1.0+
  • openhab_rules_tools 2.0.1+

Changelog

Version 0.2

  • throws an exception if the openhab-js or openhab_rules_tools are not the right verisons

Version 0.1

  • initial release

Resources

https://raw.githubusercontent.com/rkoshak/openhab-rules-tools/main/rule-templates/expire_updater/expire_updater.yaml

1 Like

FYI - Looks like you have an extra [ in the version range in the title.

I’m curious about this line. If the metadata doesn’t exist, the rule just puts the duration into the new value. But, is expire metadata valid without the command or state update portion? What happens when the expiration is triggered and there’s no command or state update to send?

1 Like

I did have an error on the line above that one that I just fixed (a ternary operation doesn’t start with “if”).

OK, the version above works, at least in the main case. There’s lots of error case testing that needs to be done but it should be mostly functional now.

Yes, it will default to updating the Item to UNDEF when those are not present in the metadata.

I did not know that. Interesting.

Yeah, the if disappeared as I was highlighting it to add that to my comment. It was a neat trick. :hushed:

Sorry, I meant the version range in the topic title. Extra [ in [[4.0.0.0;4.1.0.0).

First off this looks amazing! Unfortunately I can’t quite seem to wrap my head around how to implement it. Part of the problem may be that I am trying to utilize this in the ui instead of via text. Is that possible to utilize it from the UI? and if so could you give an example? For example could you have an input item in a sitemap to insert the new meta data into? How does one trigger it? Do I have to put the item name into the rule somewhere for the duration? What exactly is the duration string? Is that just a normal string? I can’t seem to find a number:duration item. Thanks in advance for any assistance from myself and other novices!

Yes, using the rule action. I don’t have any experience using that action from a widget but the data that needs to be passed is the name of the Item to update the metadata and the new Expire metadata. That gets encoded in the actionRuleContext property.

I can’t, I don’t use this template in that way. From the docs I would expect the actionRuleContext to look something like

{ "item": "NameOfItemToChange", "newDuration": "0H2M" }

Of course, “NameOfItemToChange” and “0H2M” can come from an Item or maybe fields of the widget. I don’t create complicated widgets so I can’t really provide more detail than that.

I don’t see why not.

By doing what ever activates the widget (e.g. clicking on it, clicking on a check mark icon, etc.).

That needs to be passed in to the rule (see above). There is no configuration of rules created from these templates. They run based on what gets passed into it.

It’s what you would put into the expire metadata to configure expire on an Item manually. It takes the format of XHYMZS where X, Y, and Z are integers and X is hours, y is minutes and Z is seconds. For example 1 hour, 2 minutes, and 3 seconds would be 1H2M3S.

There isn’t one and even if there were one it is irrelevant to this rule template. The type of the Item passed to the rule does not matter. The String needs to be a standard Expire configuration string.

Rich is correct. The best way to run a rule from the UI is with a component that has the rule action set. Then you can use the actionRuleContext property to inject whatever information you’d like from the widget into the rule as named variables. Here’s an example using an oh-link

- component: oh-link
  config:
    text: Run A Rule
    action: rule
    actionRule: myWidgetRuleUID
    actionRuleContext:
      myRuleVariable: "Variable value"
      myRuleObject:
        myObjName: =items.someItem.state
        myObjValue: =vars.widgetVar

Whenever you click on the Run A Rule link on the widget, the rule with the UID myWidgetRuleUID gets run. That rule has the additional variables, myRuleVariable and myRuleObject added to it’s context. myRuleVariable just has a value of the string "Variable value", but because myRuleObject has defined properties, you can use myRuleObject.myObjName to access the state of the item someItem and myRuleObject.myObjValue to access whatever the widget variable widgetVar was set to when the link was activated.

If you really mean ‘sitemap’ (that is, the old user interface pages) then you would need a slightly different system. You will have to use the sitemap to change the state of an item, and trigger the rule on the item change and process the information from the item’s state.

If you are actually using the MainUI, then this is fairly simple. oh-input components can be configured to populate a widget variable with their input and, as you see above, a widget variable’s value can easily be sent to the rule where the metadata update can be performed.

1 Like

Ok, thanks for the input! I think it’s starting to click into place in my head! I’ll give it another shot this evening and see how it goes!

I finally got back to this, and I did get it functioning! Having one issue with trying to update it from the sitemap, and I use primarily the sitemaps because they work well for the phone app, which is primarily what I use for monitoring and doing any manual controlling that I need to do. I am using a text input in the sitemap which updates to ExpireTest string item, using the following code(which may well be wrong, I’m used to dsl rules not Javascript).

var dur = items.ExpireTest.state;

rules.runRule('ExpireUpdaterTest', 
              {item:'TestSwitch', newDuration:'dur'}, 
              true);

And I get this error in the logs.

2024-01-03 21:52:49.832 [ERROR] [penhab.automation.rules_tools.Expire] - Passed in expire string cannot be parsed. Examples of valid strings: "8h0m0s", "8h0m5s", "0h7m0s".
DateTimeParseException: Text cannot be parsed to a Duration: PTdur, at index: 0

I am using the correct format, this is what shows up in the item “5h5m5s” minus the quotes. What am I missing here to make it happy?

The string you are sending to your rule is 'dur', which is why it cannot be parsed.

By putting dur in quotes there you are sending the string 'dur' and not the value of the variable named dur.

I don’t know what else is going on in your rule, but if the variable dur is not used for anything else, then your best bet is to just skip the middle-man and send the state directly without worrying about the variable:

{item: 'TestSwitch', newDuration: items.ExpireTest.state}
1 Like

:man_facepalming: the one thing I didn’t do was get rid of the quotes, otherwise I tried that… Works perfectly now, thank you!

Very usefull!

I recommend to use an additional:

oldState = items.getItem(item.name).state;
console.warn(('Refresh State:' + String(oldState)));
item.sendCommand(oldState);

Otherwise an old and new Expire will never activated and State will stay ON.

It is not always desirable to refresh the Item to reset the Expire which is why I didn’t add that. But it’s very much something that is reasonable to do in a lot of cases.

However, I do not recommend using sendCommand. First, a command does not always result in a change in the Item’s state (e.g. Autoupdate is disabled, the command doesn’t result in a change in the Item), and the Expire might be configured to ignore commands.

However, postUpdate has problems too as the Expire might be configured to ignore state updates that do not result in a change.

Ultimately, whether and how the Item is updated to reschedule the Expire has to be handled on a case-by-case basis. This is a case where one size does not fit all.

Do I need to have an expire template rule and a rule to pass the item and duration for each item I want to change the meta data for. Or do I just need one template and then a separate rule for each item that calls the the one template?

The rule is really designed around being called from a MainUI Widget.

But whether you are using MainUI widgets or separate rules, you only need one instance of the template rule. The name of the Item and the new duration are passed as arguments when calling the template rule.

Given that, you don’t need a separate rule for each Item in the calling rule either, necessarily. That’s certainly an option but not a requirement.

Ok, that’s how I thought I understood it. I’m planning on using rules to automate changing my greenhouse fan runtime based on temp is how I want to use it.

Interesting, how would you go about using the same rule for multiple items?

Depends on your requirements. Lets say you have three separate fans to control each driven by individual thermometers.

  1. Build your semantic model so the two Items are members of the same Equipment.
  2. Add the thermometer Items to a Group.
  3. Create a rule that triggers based on changes to the members of the Group created in 2.
  4. The script action would be something like the following (in JS Scripting, using the semantic model approach):
var thermometer = items[event.itemName];
var equipment = thermometer.semantics.equipment;
var fan = equipment.members.filter( i => i.name.includes('Fan')); // assumes only the Fan Item has "Fan" in it's name

// calculate the new duration

rules.runRule('dynamic_expire', {item:fan.name, newDuration: duration), true);

Those four lines of code (could be less) will work for any number of thermometer/fan pairs so long as all the thermometers are in the same Group, each thermometer and fan Item are in the same equipment, and the fan Item has “Fan” somewhere in it’s name.

Oh nice! I definitely need to start taking more advantage of the semantic model for situations like that. Thanks for the example!