Rule DSL - best way to schedule an action based on condition

Hi all,
Would like to get some suggestion on what’s the best approach to schedule an action. I implemented a rule that schedule the action to start charging of my car during cheap hours, it looks like below:

 timer = createTimer(startTimeToCharge.atZone(ZoneId.systemDefault())) [|
                            logInfo("SmartCharging", "Scheduled charge starts now")
                            EaseeBox_charging.sendCommand(ON)
                        ]  

While this works well I also notice a few shortcomings:

  1. The schedule is not persisted and doesn’t survive a restart (looks like)
  2. Editing the rule file also cause the schedule disappear for unknown reason

I’d like to ask is there a better way to schedule an action in the future which still works after restart/rule editing?

Many thanks

How are you setting the start time?

If it’s determined manually, you could store that start time in a DateTimeItem, and use a DateTime trigger.

rule "XXX"
when
  Time is My_DateTimeItem_StartTimeToCharge
then
  logInfo("SmartCharging", "Scheduled charge starts now")
  EaseeBox_charging.sendCommand(ON)
end

Then you can persist that DateTimeItem into a MapDB persistence so it survives through openhab restarts.

It also works of course if the DateTime item automatically changes value based on some dynamic rules.

Way back in the depths of time this general use case is why I created the Time of Day design pattern. Design Pattern: Simple State Machine (e.g. Time Of Day)

Over all what you do is separate the calculation on when to do this from what to do. You’d have one rule or rules which keep track of the time and update/command an item (e.g. at sunrise command an Item with DAY). Changes to that stare Item can be used to trigger rules, used in if statements in rules, etc.

To handle restarts and such, the rule or rules that calculate the time of day usually have an “at midnight” trigger and a “restart” trigger to calculate the current state and set the timers for future states at the start of a new day and on OH restart.

So, for example, if OH is down when the charging time has passed, when OH comes back up it runs, sees it’s currently between the charging start and stop times and commands/updates the state Item. The state Item changed to CHARGE (or some such) triggering the rule that starts the charging, even though OH missed the exact start time.

Time Based State Machine [4.0.0.0;4.9.9.9] provides a relatively complete time if day tracking which might be more than you need for this.

Thanks I wasn’t aware of the new trigger “Time is item” seems something introduced recently. It’s an elegant way of solving my issue. A couple of questions though:

  1. In case I don’t want the rule to be triggered, should I set the My_DateTimeItem_StartTimeToCharge to NULL or UNDEF?
  2. I saw some discussion of using MapDB with restoreOnStartup. I have never tried to configure persistency service (so it defaults to RRD4J). As I read restoreOnStartup also works for RRD4J, is there any particular reason to use MapDB for this purpose? If so can I use MapDB only for specified items and leave the rest with RRD4J?

Many thanks!

Hi, thank you for your reply! I have read through but don’t think I understand how to use the solution. In my setup I’d like to trigger something to happen in the future at calculated time - it’s not exactly a state machine, maybe I missed something in your idea?

Correct!

rrd4j only stores numeric values. It cannot store datetime values. So you’d have to use mapdb for this.

Correct. Simply configure that one datetime item to be persisted with mapdb, and the others can be left as is.

Ok maybe answer my own question, looks like it’s not allowed to set UNDEF on a DateTime item, I received the following error:

 Script execution of rule with UID '_Test-2' failed: An error occurred during the script execution: Could not invoke method: org.openhab.core.model.script.actions.BusEvent.sendCommand(org.openhab.core.items.Item,java.lang.String) on instance: null in _Test

Don’t use sendCommand. Use postUpdate instead

You do have two states.

  1. It’s a time where it’s ok to charge
  2. It’s a time where it’s not ok to charge

Each state has a start time. The end time is the start of the next state.

To keep track of these two states you’d have a rule and an item. If you’ve only two states a Switch will work. If more you’d use a String Item.

When this rule runs it just needs to test if now is after the start of charging time and before not charging time. If so set the switch to ON. If not set the switch to OFF.

The only thing left now is to make sure the rule triggers at the right times. The triggers you need are:

  • start time of charging
  • start time of not charging
  • system started

The switch will be ON when it’s ok to charge and OFF when it isn’t. So now all you need is a very simple rule that triggers when the switch Item changes.

Or, you could just command the switch that contains the charging directly.

The main point though is there’s no timer so there’s nothing to keep track of. And yet if OH were offline when charging time started and comes back while it’s still charging time, charging will start, even though it’s a little late.

Hi @rlkoshak :

Thank you for the explanation! I think I understand how it works now, regarding the rule to test if “now” is in charging period or not, my understanding is to use cron to trigger to check every x minutes, right? In my setup the check would be something like the following:

  1. If cable is disconnected, then no charging and set switch to OFF
  2. If cable is connected, check the cheap charging period, and check if now is inside the period, if so set switch to ON
    In my setup I calculate cheapest to charge period when the cable is connected, and this calculate will give a different result when I run it later or during the charging period. For example if I plug cable in during morning, I want charging to be done in afternoon. Or if I plug cable in afternoon, I want charging to be done before next morning. I think what I could do is to store the start/end charging time based on the calculation in 2 DateTime items when the cable is connected.
    I can see the benefits of this solution without additional items and can also take care of stop charging outside the period (in my setup I don’t need it as I always calculate hours based on full charge). I actually quite like “state based” control in automations, so basically the automation maps from condition → correct state under the condition. Today most home automation systems only focus on condition → action(s).

Also based on the other discussion on trigger “Time is item” (thanks to @jimtng ), another possibility is to:

  1. Create a DateTime item indicate when the charging should start
  2. When cable is connected it calculates when should charge start and set the trigger DateTime item
  3. Implement a rule that is triggered by the DateTime item to start charging
  4. Cancel the timer if cable is unplugged before charging starts

Very happy I posted the discussion here as I now learned two more ways of schedule actions which are both better than what I’m doing now.

No. You trigger the rule using:

  • system startup
  • at the charging start time (use Time is Item trigger if it’s in an Item, otherwise use cron)
  • at the charging end time (use Time is Item trigger if it’s an Item, otherwise use cron)

You do not poll. If OH happened to be offline when the start time or end time occurred, it will calculated the correct ON/OFF state for the Switch when it comes back online through the system startup trigger.

Since the times can vary, I recommend using Items to store the start and end charging times.

You can add a condition to the rule to check if the cable is disconnected, though if the cable is disconnected does it cost anything to allow charging even though nothing is plugged in? Maybe you can skip that complication entirely as moot.

Understood, thank you for the explanation!