Iām running exactly the same code and I cannot reproduce the error. I once saw something similar in another rule but it went away without my needing to do anything.
Based on nothing at all, try to replace those lines 82-85 with the following:
Aha! I didnāt pick that up when I looked at the Items. Iāll add a check for that in the validation code so it produces a better error message.
Itās still a little weird though because I would have been expected that to have been caught by the call to checkGrpAndMetadata. It should have complained that the time of day Item doesnāt have the metadata. So I might need to add a new check and figure out why it wasnāt caught there in the first place.
But now at least I have something to go on and can improve the template.
Thanks!
Edit: I found the bug (two bugs actually) but itās in the OHRT library. A fix will be included in the next release but this is low impact so I want to get a few more changes into OHRT before I cut another release.
Hello,
me again.
The template has gone through wonderfully for the last n days and has worked flawlessly. Today I wanted to adjust that so that Friday_night has a different time than weekdays.
I have added the following entry to ephemeris.cfg (and restart openhab):
This time the door is occurring in a different place. Iāll have to run some tests and I might need to have you add another logging statement if I canāt reproduce it.
Hello Rich,
first of all thank you very much for the rule-template. It does what itās supposed to.
But unfortunately it doesnāt work the way I would like it to. It would be nice if a little more āintelligenceā found its way into it. You focus on exactly one day and then use a few criteria to decide which time of day should start at what time.
My focus is a little different. I would look what day is today, what day is tomorrow.
If tomorrow is a holiday or weekend, then today is to be taken as night time on the weekend rate. If it is a weekend or holiday and tomorrow is a work day, then the night time of the weekday should be used. Also, Iām missing a check to see if the time for the morning is after the time for the day. Then of course the time of day should not change from day to morning like today.
On weekdays, my morning starts at 5:30 am and turns on appropriate lights. After sunrise, the lighting is switched off again. On the weekend my morning is at 7:30am, sunrise was 6:15am. So 6:15 was switched to day and 7:30 to morning and the lights stayed on.
In my production system, this works with modified scripts from you, but unfortunately not with the TimeStateMachine.
I will now have to learn JS scripting in order to be able to implement my requirements. A home automation system should be able to do something like this by itself.
I am grateful for ideas and am available for tests and brainstorming.
I went down that path for a bit once. This rule template is already super complicated all things considered. To implement all of these additional requirements would require an overall completely different approach and the complexity in both the template code and the configuration on the end user will explode.
Some considerations:
how is the rule supposed to know that itās supposed to pay attention to tomorrow or not? Which ātomorrowāsā are important? How does the end user tell this to the rule?
how is the rule supposed to know the order you intend the events to occur and skip one? All it knows now is the date time. It doesnāt know the order of the events outside the date times.
It probably wouldnāt be too complex to start the state machine at noon instead of midnight. But that would potentially be a breaking change. Iād have to think about that.
Hi,
I would integrate an āadvanced modeā switch into the template. If this switch is not activated, the template works as before.
If the switch is activated, the times would have to be preprocessed. All times are copied accordingly into the default items and the template now only works with the times of the default items.
Copying and sorting from weekday and weekend in default will be the challenge.
Iāll check if the template works reliably when only the default items are present. If this works, Iāll build a script that copies the times into the items according to my wishes. Then you wouldnāt have to change anything.
If all this doesnāt work properly, Iāll try to port my current script to JS scripting.
Many thanks for your effort.
PS
Can you please post me an example of items.replace related to TSM? Iād like to try something, but Iām failing because of the meta data for the TSM items.
Or how can I change the value tsm=āDAYā to tsm=āMORNINGā and vice versa using JS script? That would help me a lot.
Thank you very much.
One challenge that occurred to me is you canāt really rely on any datetimes from Astro for tomorrow. Astro only calculates the times for the current day around midnight. So that has to be taken into account too. Especially as one gets closer to the poles, the difference in sunrise/sunset from day to day can be significant.
However based on what you describe you donāt need that. Just have the one set of default Items and in your rule update their states. Leave the metadata alone.
Thank you. I would like to adjust the metadata if the morning time is after the daytime.
Iām currently building the script, which inserts the times into the default items according to my requirements. It is then checked whether the morning time is later than the day time. If so, tsm should be set to day, otherwise to morning. Then your TSM will be executed with the default values. Letās see if that works.
With the metadata it would/could work.
Sunrise sets the time of day to day for me. The morning time sets the time of day to morning (week 05:30 and weekend 07:30). How can I prevent the morning status of the time of day from being/can be set after the day status?
Idea 1:
If the time for morning is later than the time for day, then set the metadata for morning to Day_Late and include that value in the evaluation script that starts the actions by time of day, or
Idea 2:
Set the time for morning to 2s before the time for the day and build a timer of 5s into the evaluation rule, which prevents unnecessary actions.
What would be the better approach for you?
This is my Rule for idea 2 for the weekend-test
var RuleName = 'Zeiten';
var debugLevel = 2;
console.loggerName = 'org.openhab.automation.rules.' + RuleName;
if (debugLevel == 1 )
{
osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'DEBUG');
}
else if ( debugLevel == 2)
{
osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'TRACE');
}
else
{
osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'INFO');
}
console.debug("***** Rule '" + RuleName + "' gestartet *****");
if (actions.Ephemeris.isBankHoliday() == true) {
console.debug('Heute ist Feiertag');
items.TZ_Default_Morgen.postUpdate(items.TZ_Wochenende_Morgen.state.toString());
}
else if (actions.Ephemeris.isWeekend() == true) {
console.debug('Heute ist Wochenende');
items.TZ_Default_Morgen.postUpdate(items.TZ_Wochenende_Morgen.state.toString());
}
else {
console.debug('Heute ist ein Wochentag');
items.TZ_Default_Morgen.postUpdate(items.TZ_Woche_Morgen.state.toString());
}
if (actions.Ephemeris.isBankHoliday(1) == true) {
console.debug('Morgen ist Feiertag');
items.TZ_Default_Nacht.postUpdate(items.TZ_Wochenende_Nacht.state.toString());
}
else if (actions.Ephemeris.isWeekend(1) == true) {
console.debug('Morgen ist Wochenende');
items.TZ_Default_Nacht.postUpdate(items.TZ_Wochenende_Nacht.state.toString());
}
else {
console.debug('Morgen ist ein Wochentag');
items.TZ_Default_Nacht.postUpdate(items.TZ_Woche_Nacht.state.toString());
}
var Morgen = items.TZ_Default_Morgen.state;
var Tag = items.TZ_Default_Tag.state;
if (time.toZDT(Morgen).isAfterTime(time.toZDT(Tag))) {
console.debug('Morgen-Zeit ist nach Sonnenaufgang');
var NewTime=time.toZDT(Tag).MinusSeconds(3);
items.TZ_Default_Morgen.postUpdate(NewTime.toString);
console.debug('Setze neue Morgen-Zeit auf : ' + NewTime.toString());
}
else {
console.debug('Morgen-Zeit ist vor Sonnenaufgang')
}
console.debug("***** Rule '" + RuleName + "' beendet *****");
set the config of the Astro Channel so that Channel thatās linked to the DAY Items with an āearliestā and ālatestā properties to prevent it from occurring before the MORNING event (probably doesnāt work in this specific use case but it can work in others)
Handle the MORNING state in some other rule based on some other event. For example, I donāt have MORNING in my Time State Machine Items. I have a separate rule that is triggered by my alarm time and it will only set the state to MORNING if itās appropriate to do so (i.e. current state is BED or NIGHT). No alarm or if itās after DAY, there will be no MORNING state in the first place.
I might suggest that handling MORNING outside the state machine rule might be the easiest approach over all.
One of my overall philosophies for rule templates is that these are merely building blocks. Many of them donāt even do anything useful on their own and depend on some custom script that gets called to do the actual work.
Maybe this could be a similar case. Let this template handle most of the time states but because MORNING is complicated, just move that into a separate rule. Writing a rule that handles MORNING independently is easier than trying to change metadata or adjust the DateTime Itemās states on the fly.
Here is my MORNING state rule:
configuration: {}
triggers:
- id: "2"
configuration:
thingUID: mqtt:topic:broker:sleepasandroid
event: alarm_alert_start
channelUID: mqtt:topic:broker:sleepasandroid:event
type: core.ChannelEventTrigger
conditions:
- inputs: {}
id: "3"
configuration:
type: application/javascript
script: |-
var curr = items.TimeOfDay.state;
curr == 'NIGHT' || curr == 'BED'
type: script.ScriptCondition
actions:
- inputs: {}
id: "1"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var { Deferred } = require('openhab_rules_tools');
var hour = time.toZDT().hour();
var currToD = items.TimeOfDay.state;
console.debug('Current ToD = {} Current Hour = {}', currToD, hour);
// Puppy lights
if(currToD == 'NIGHT' || time.toZDT().isBefore(time.toZDT('04:45'))) {
console.info("The alarm went off and it's night time, turning on the lights for five minutes");
items.TOD_Lights_ON_MORNING.sendCommand('ON');
Deferred().defer('TOD_Lights_ON_MORNING', 'OFF', 'PT5M', true);
}
// MORNING TIME
else if(currToD == 'BED'){
console.info('Good morning!');
items.TimeOfDay.sendCommand('MORNING');
}
// Should never reach this becuase of the rule template
else {
console.warn("The alarm went off but it's not BED or NIGHT")
}
type: script.ScriptAction
Itās a little more complex than it needs to be because when I wrote I we had a puppy who needed to go out to potty in the middle of the night so Iād only keep the lights on for five minutes instead of transitioning to MORNING in those cases. I donāt need that anymore and probably should remove it.
Without that logic itās a simple one-liner to send MORNING to TimeOfDay. The Condition handles making sure it only sends MORNING when itās appropriate (i.e. not when itās already DAY.
The rule template already has a timer built in so that it waits for itās Items to stop updating before actually running. This ensures, for example, that when Astro updates all its Items around midnight the rule doesnāt run more than once all the way through.
Be careful setting the log level every time a rule runs. If another rule happens to be setting itās level at the same time you can end up with an empty log4j2.xml file.
This seems awfully convoluted. Why create a separate ādebugLevelā with an if/else if and not just set the logging level directly?
There is an implicit variable ruleUID which is the unique identifier for the rule. Itās often more useful to use that because you can actually search for that on the Rules and Scripts pages in MainUI.
You donāt need to have == true. If the function returns true the == true is understood.
You donāt need to call toString() on .state, itās already a String.
I prefer a 1-2-3 structure for code like this.
// 1. determine if there is anything to do, in this case always do the steps
// 2. calculate what to do
var dayType = 'Woche';
if(actions.Ephemeris.isBankHolidaty() || actions.Ephemeris.isWeekend()) {
dayType = = 'Wochenende';
}
// 3. do it
items.TZ_Default_Morgen.postUpdate(items['TZ_'+dayType+'_Morgen'].state);
items.TZ_Default_Nacht.postUpdate(items['TZ_'+dayType+'_Nacht'].state);
You just potentially updated this Item. It might not yet have become the updated state yet.
The whole script as I would write itā¦
// By default the logger name will be `org.openhab.automation.rules.ruleUID`
// Uncomment and change the level to change the logging level. Comment back out after the rule runs.
// osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'DEBUG');
console.debug("***** Rule '" + ruleUID + "' gestartet *****");
// 1. Determine if there is anything to do
// Often it's appropriate to add this part as a script condition to the rule
// 2. Calculate what to do
var dayType = 'Woche';
if(actions.Ephemeris.isBankHolidaty() || actions.Ephemeris.isWeekend()) {
dayType = = 'Wochenende';
}
var morningTime = time.toZDT(items['TZ_'+dayType+'_Morgen]); // converts Item to ZDT
var dayTime = time.toZDT(items['TZ_Default_Tag']);
if(morningTime.isAfter(dayTime)) {
console.debug('Morgen-Zeit ist nach Sonnenaufgang');
morningTime = dayTime.minusSeconds(3);
console.debug('Setze neue Morgen-Zeit auf : ' + morningTime.toString());
}
else {
console.debug('Morgen-Zeit ist vor Sonnenaufgang');
}
// 3. Do it
items.TZ_Default_Nacht.postUpdate(items['TZ_'+dayType+'_Nacht].state);
items.TZ_Default_Morgen.postUpdate(morningTime.toString());
console.debug("***** Rule '" + ruleUID + "' beendet *****");
Hi, thank you very much for your very interesting input. I will take your suggestions and modify the rules accordingly. Especially when I see how much shorter and clearer your code is. I will remove the morning time from the TSM and set the timer for it according to my requirements using a separate rule.
Thank you again for your patience.