now.withTime(6, 30, 0, 0)
Itâs always helpful to log out what morning_start is being set to and compare that to the other times to see if they make sense.
NOTE: the plus day minus hours is to deal with daylight savings. Scottâs approach is a better one for getting a specific time. I just havenât updated the DP with it yet.
Thanks, Iâve got all the times right for each val but the syntax above just didnât seem to run so I assume OH just canât process it so as it nulls then it will wait until the next time Val.
I like the simplicity of the withtime above, Iâll be changing to that when I get home
Please update your description under
Simple Example
In this example we will be tracking the time of day. There will be five states: MORNING, DAY, EVENING, NIGHT and BED.
It should say six states: MORNING, DAY, AFTERNOON, EVENING, NIGHT, and BED
alright well you donât have to yell, corrections are welcome
It wasnât meant as yelling. The code had the states in upper case so Iâm just repeating it the way it was.
@rlkoshak noticed that you are using sunrise as daystart, isnât it more likely daylight than day then?
because during winter season DAY definitely starts even when itâs still dark outside eg. sunrise not yet started.
just curious what DAY actually means in this DP
Iâm Thinking if time of the day should be tighted to time of the day rather than astro events. hm?
Itâs an example. The user tailors it to their needs; different times, different scheduled periods.
Astro binding offers more than one interpretation of sunrise, choose the one you like and add a fixed offset if you wish.
As rossko57 states, itâs all customizable for a given situation. And there is some subtle yet desirable behavior that the Rule handles here.
I only want the MORNING state to occur when the sun rises after 06:00. When the sun rises before 06:00, the MORNING state is skipped. That is a deliberate choice. Itâs also documented in the OP:
If you want MORNING to occur no matter what, then indeed, you want to tie it to sunrise with some sort of offset. But one of the things I wanted to show in the DP was as many different concepts as possible. Thatâs why we have some Astro events, some Astro events with offsets, static times, and at least one time period that getâs skipped because the sun rises too early.
But, as rossko57 points out, it doesnât matter what DAY represents in this DP. What do you want DAY to represent? Do you even want a DAY state? Maybe you want to call it something else. Maybe you want to divide it up into more time periods. This DP, as is the case with all DPs, presents an example. Itâs intended to be customized to your specific needs. Itâs also general, meaning this approach can be used to solve other similar types of problems, not just time of day.
In my particular case, MORNING controls a couple of lamps that I need to come on if the sun rises after 06:00 but do not if the sun rises before.
Any time period means âThat time of day between when it is defined to start up to the point that the next time of day starts.â For DAY above, that means that DAY is between 06:00 and Sunset minus 90 minutes.
Make sense, I was just curious what âDAYâ is used for in your environment. I get MORNING usecase . Anyway thanks to your DPâs Iâve made couple of nice complex rules for my setup so thank you for your great effort.
Hi all, I have an issue with this desgin pattern. I tried the first one with the map service installed. I have the following items:
Default.tems
String vTimeOfDay "Current Time of Day [%s]" <tod>
DateTime vMorning_Time "Morning [%1$tH:%1$tM]" <sunrise>
DateTime vSunrise_Time "Day [%1$tH:%1$tM]" <sun> { channel="astro:sun:home:rise#start" }
DateTime vSunset_Time "Evening [%1$tH:%1$tM]" <sunset> { channel="astro:sun:home:set#start" }
DateTime vNight_Time "Night [%1$tH:%1$tM]" <moon>
DateTime vBed_Time "Bed [%1$tH:%1$tM]" <bedroom_blue>
DateTime vEvening_Time "Afternoon [ %1$tH:%1$tM]" <sunset> { channel="astro:sun:minus90:set#start" }
Lighting.rules
rule "Calculate time of day state"
when
System started or // run at system start in case the time changed when OH was offline
Channel 'astro:sun:home:rise#event' triggered START or
Channel 'astro:sun:home:set#event' triggered START or
Channel 'astro:sun:minus90:set#event' triggered START or
Time cron "0 1 0 * * ? *" or // one minute after midnight so give Astro time to calculate the new day's times
Time cron "0 0 6 * * ? *" or
Time cron "0 0 23 * * ? *"
then
logInfo(logName, "Calculating time of day...")
// Calculate the times for the static tods and populate the associated Items
// Update when changing static times
// Jump to tomorrow and subtract to avoid problems at the change over to/from DST
val morning_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(18)
vMorning_Time.postUpdate(morning_start.toString)
val night_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(1)
vNight_Time.postUpdate(night_start.toString)
val bed_start = now.withTimeAtStartOfDay
vBed_Time.postUpdate(bed_start.toString)
// Convert the Astro Items to Joda DateTime
val day_start = new DateTime(vSunrise_Time.state.toString)
val evening_start = new DateTime(vSunset_Time.state.toString)
val afternoon_start = new DateTime(vEvening_Time.state.toString)
// Calculate the current time of day
var curr = "UNKNOWN"
switch now {
case now.isAfter(morning_start) && now.isBefore(day_start): curr = "MORNING"
case now.isAfter(day_start) && now.isBefore(afternoon_start): curr = "DAY"
case now.isAfter(afternoon_start) && now.isBefore(evening_start): curr = "AFTERNOON"
case now.isAfter(evening_start) && now.isBefore(night_start): curr = "EVENING"
case now.isAfter(night_start): curr = "NIGHT"
case now.isAfter(bed_start) && now.isBefore(morning_start): curr = "BED"
}
// Publish the current state
logInfo(logName, "Calculated time of day is " + curr)
vTimeOfDay.sendCommand(curr)
end
// Examples for use of vTimeOfDay
rule "Evening sesor GangBoven"
when
Item SW_SensorGangBoven changed to ON
then
if(vTimeOfDay.state != "EVENING") {
Milight_LampGangBoven_G1_Level.sendCommand(60)
}
end
rule "Night sesor GangBoven"
when
Item SW_SensorGangBoven changed to ON
then
if(vTimeOfDay.state != "NIGHT") {
Milight_LampGangBoven_G1_Level.sendCommand(5)
}
end
rule "Bed sesor GangBoven"
when
Item SW_SensorGangBoven changed to ON
then
if(vTimeOfDay.state != "BED") {
Milight_LampGangBoven_G1_Level.sendCommand(5)
}
end
rule "Morning sesor GangBoven"
when
Item SW_SensorGangBoven changed to ON
then
if(vTimeOfDay.state != "MORNING") {
Milight_LampGangBoven_G1_Level.sendCommand(20)
}
end
rule "Day sesor GangBoven"
when
Item SW_SensorGangBoven changed to ON
then
if(vTimeOfDay.state != "DAY") {
Milight_LampGangBoven_G1_Level.sendCommand(0)
}
end
rule "Afternoon sesor GangBoven"
when
Item SW_SensorGangBoven changed to ON
then
if(vTimeOfDay.state != "AFTERNOON") {
Milight_LampGangBoven_G1_Level.sendCommand(60)
}
end
Now I am not sure if the rules that determines the dimming are the issue here or the time of the day is not calculated.
When I activate the motion detection sensor this is what the log looks like:
2019-10-05 21:31:34.570 [vent.ItemStateChangedEvent] - SW_SensorGangBoven changed from NULL to ON
2019-10-05 21:31:34.645 [ome.event.ItemCommandEvent] - Item 'Milight_LampGangBoven_G1_Level' received command 60
2019-10-05 21:31:34.679 [ome.event.ItemCommandEvent] - Item 'Milight_LampGangBoven_G1_Level' received command 5
2019-10-05 21:31:34.682 [ome.event.ItemCommandEvent] - Item 'Milight_LampGangBoven_G1_Level' received command 60
2019-10-05 21:31:34.703 [ome.event.ItemCommandEvent] - Item 'Milight_LampGangBoven_G1_Level' received command 5
2019-10-05 21:31:34.713 [nt.ItemStatePredictedEvent] - Milight_LampGangBoven_G1_Level predicted to become 60
2019-10-05 21:31:34.731 [ome.event.ItemCommandEvent] - Item 'Milight_LampGangBoven_G1_Level' received command 0
2019-10-05 21:31:34.741 [vent.ItemStateChangedEvent] - Milight_LampGangBoven_G1_Level changed from NULL to 60
2019-10-05 21:31:34.743 [ome.event.ItemCommandEvent] - Item 'Milight_LampGangBoven_G1_Level' received command 20
2019-10-05 21:31:34.745 [nt.ItemStatePredictedEvent] - Milight_LampGangBoven_G1_Level predicted to become 5
2019-10-05 21:31:34.750 [nt.ItemStatePredictedEvent] - Milight_LampGangBoven_G1_Level predicted to become 60
2019-10-05 21:31:34.754 [nt.ItemStatePredictedEvent] - Milight_LampGangBoven_G1_Level predicted to become 5
2019-10-05 21:31:34.762 [nt.ItemStatePredictedEvent] - Milight_LampGangBoven_G1_Level predicted to become 0
2019-10-05 21:31:34.799 [vent.ItemStateChangedEvent] - Milight_LampGangBoven_G1_Level changed from 60 to 5
2019-10-05 21:31:34.806 [nt.ItemStatePredictedEvent] - Milight_LampGangBoven_G1_Level predicted to become 20
2019-10-05 21:31:34.811 [vent.ItemStateChangedEvent] - Milight_LampGangBoven_G1_Level changed from 5 to 60
2019-10-05 21:31:34.813 [vent.ItemStateChangedEvent] - Milight_LampGangBoven_G1_Level changed from 60 to 5
2019-10-05 21:31:34.816 [vent.ItemStateChangedEvent] - Milight_LampGangBoven_G1_Level changed from 5 to 0
2019-10-05 21:31:34.818 [vent.ItemStateChangedEvent] - Milight_LampGangBoven_G1_Level changed from 0 to 20
2019-10-05 21:31:35.337 [vent.ItemStateChangedEvent] - Milight_LampGangBoven_G1_Level changed from 20 to 60
2019-10-05 21:31:36.285 [vent.ItemStateChangedEvent] - Milight_LampGangBoven_G1_Level changed from 60 to 20
2019-10-05 21:32:05.035 [vent.ItemStateChangedEvent] - SW_SensorGangBoven changed from ON to OFF
2019-10-05 21:32:05.069 [ome.event.ItemCommandEvent] - Item 'Milight_LampGangBoven_G1_Level' received command 0
After a few seconds the sensor will switch off and the MiLight gets the command to switch off.
Now I have this issue that the MiLight will turn on but will get dimmed with different values and so it changes a few time before it stays on for a few seconds.
I want to achieve different dimming levels based on the time of the day. Any1 can point me to the right direction? Thanks allready for all the examples etc, I learn a lot from it.
Letâs say TimeOfDayb is AFTERNOON andSW_SensorGangBoven changes to ON. All of you rules will run. And the if states for all but the hat role will run because
- vTimeOfDay.state != âEVENINGâ is true so lamp is commanded to 60
- vTimeOfDay.state != âNIGHTâ is true so lamp is commanded to 5
- vTimeOfDay.state != âBEDâ is true so lamp is commanded to 5
- vTimeOfDay.state != âMORNINGâ is true so lamp is commanded to 20
- vTimeOfDay.state != âDAYâ is true so lamp is commanded to 0
- vTimeOfDay.state != âAFTERNOONâ is false so the role does nothing (vTimeOfDay.state == âAFTERNOONâ is true)
So either I donât username what you are trying to accomplish or you donât know the difference between equals (==) and not equals (!=).
Hey Rich, I really didnât know about the difference in != and ==. I copied the code from the pattern example and tried to achieve my own goals by tweaking it here and there.
I canât see the solution. I thought this codeâs purpose is to define which part of the day the system is in. And from there further on we can add rules to these parts. Correct?
Design Patterns show you how to solve common coding problems in OH but they still require you to know and understand the basics of OH ok and how to code Rules.
The Time of Day Rule appears to be working given the logs you provided. But your Rules are not right. For example, your âEvening sesor GangBovenâ code literally means " when SW_SensorGangBoven changes to ON, command Milight_LampGangBoven_G1_Level to 60 every time of day except EVENING."
if(vTimeOfDay.state != "EVENING")
means if vTimeOfDayâs State **is not*z EVENING. You probably want "=="which means if vTimeOfDay is EVENING.
This is super basic stuff you must understand to be successful with OH rules. You might benefit from going though one of the many beginning programming courses there are online.
Thanks for the explanation. I went through the basics and I see the difference now. I also found out that I was not using the astro.sun.home items but mine were called astro.sun.local. So I changed these entries. But before I proceed with investigating my rules I want to make sure that time of the day is working. Now when I look on my sitemap, Time Of The Day states â-â:
It appears to me that it is still not calculating the parts of the day right. I also tried to reboot my OH system because the rule will be triggered at system startup.
Are you sure, your rule is running ? I didnât compare it with Richâs original, but mine runs
and shows the correct value (transformed)
String vTimeOfDay "Current Time of Day [MAP(astroDE.map):%s]" <time> (gRichTime)
//String vTimeOfDay "Current Time of Day [%s]" <time> (gRichTime) // Testversion without transformation
But what Iâm missing is the declaration of
val logName = "Time Of Day"
at the top of your rule.
I took it out because I get an error when I have the val logName on top of the rule. My rule is as follows for now:
val logName = "Time Of Day"
rule "Calculate time of day state"
when
System started or // run at system start in case the time changed when OH was offline
Channel 'astro:sun:local:rise#event' triggered START or
Channel 'astro:sun:local:set#event' triggered START or
Channel 'astro:sun:local:set#start' triggered START or
Time cron "0 1 0 * * ? *" or // one minute after midnight so give Astro time to calculate the new day's times
Time cron "0 0 6 * * ? *" or
Time cron "0 0 23 * * ? *"
then
logInfo(logName, "Calculating Time Of The Day")
// Calculate the times for the static tods and populate the associated Items
// Update when changing static times
// Jump to tomorrow and subtract to avoid problems at the change over to/from DST
val morning_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(18)
vMorning_Time.postUpdate(morning_start.toString)
val night_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(1)
vNight_Time.postUpdate(night_start.toString)
val bed_start = now.withTimeAtStartOfDay
vBed_Time.postUpdate(bed_start.toString)
// Convert the Astro Items to Joda DateTime
val day_start = new DateTime(vSunrise_Time.state.toString)
val evening_start = new DateTime(vSunset_Time.state.toString)
val afternoon_start = new DateTime(vEvening_Time.state.toString)
// Calculate the current time of day
var curr = "UNKNOWN"
switch now {
case now.isAfter(morning_start) && now.isBefore(day_start): curr = "MORNING"
case now.isAfter(day_start) && now.isBefore(afternoon_start): curr = "DAY"
case now.isAfter(afternoon_start) && now.isBefore(evening_start): curr = "AFTERNOON"
case now.isAfter(evening_start) && now.isBefore(night_start): curr = "EVENING"
case now.isAfter(night_start): curr = "NIGHT"
case now.isAfter(bed_start) && now.isBefore(morning_start): curr = "BED"
}
// Publish the current state
logInfo(logName, "Calculated time of day is" + curr)
vTimeOfDay.sendCommand(curr)
end
Then I see this error in the log:
2019-10-07 19:13:50.457 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model âlighting.rulesâ has errors, therefore ignoring it: [29,1]: missing EOF at âvalâ
And interesting enough I see this error when the evening started:
2019-10-07 19:02:00.044 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule âCalculate time of day stateâ: The name âlogNameâ cannot be resolved to an item or type; line 42, column 11, length 7
Line 42 is:
logInfo(logName, âCalculating Time Of The Dayâ)
And I am curious how your astroDE.map file looks like. Iâm now using en.map and it looks like this:
CLOSED=closed
OPEN=open
NULL=unknown
-=unknown
My UI looks like this now:
I think your âlogInfoâ will not work correctly. So if your âplaceholderâ logName does not work, use it in this way:
logInfo("myRuleName in double quotes", "Calculating Time Of the Day")
I also saw that you are not using âastro:sun:minus90:set#eventâ -Thing which has normally has to be declared in this way:
Thing astro:sun:minus90 "Offset -90" [geolocation="12.34578,1.23456,123", interval=300]{
Channels:
Type rangeEvent : set#event [
offset=-90
]
Type start : set#start [
offset=-90
]
Type end : set#end [
offset=-90
]
}
and which corresponding (linked) to this item:
DateTime vEvening_Time "Late Afternoon minus90_sun [ %1$tH:%1$tM]" <sunset> (gRichTime) {channel="astro:sun:minus90:set#start"}
Please check this all.
As I donât know what exactly is your line 29 (and of course 28 and 30) I canât say whatâs wrong exactly. Maybe you have declared a variable twice or not declared.
The second error comes up, as you have not declared the variable val logName and want to use in the logInfo .
EDIT:
astroDE.map (look at the bottom of the Map)
// Zodiac - Sternzeichen
ARIES=â Widder
TAURUS=â Stier
GEMINI=â Zwilling
CANCER=â Krebs
LEO=â Löwe
VIRGO=â Jungfrau
LIBRA=â Waage
SCORPIO=â Skorpion
SAGITTARIUS=â SchĂŒtze
CAPRICORN=â Steinbock
AQUARIUS=â Wassermann
PISCES=â Fische
//seasons
Season=Jahreszeit
SPRING=FrĂŒhling
SUMMER=Sommer
AUTUMN=Herbst
WINTER=Winter
// day - night phases
SUN_RISE=Sonnenaufgang
ASTRO_DAWN=astronomische MorgendÀmmerung
NAUTIC_DAWN=nautische MorgendÀmmerung
CIVIL_DAWN=zivile MorgendÀmmerung
CIVIL_DUSK=zivile AbenddÀmmerung
NAUTIC_DUSK=nautische AbenddÀmmerung
ASTRO_DUSK=astronomische AbenddÀmmerung
SUN_SET=Sonnenuntergang
DAYLIGHT=Tag
NOON=Abend
NIGHT=Nacht
Night=Nacht
// moon phases
NEW=Neumond
WAXING_CRESCENT=zunehmender Halbmond
FIRST_QUARTER=erstes Viertel
WAXING_GIBBOUS=zunehmender Mond
FULL=Vollmond
WANING_GIBBOUS=abnehmender Mond
THIRD_QUARTER=letztes Viertel
WANING_CRESCENT=abnehmender Halbmond
//error-codes
NULL=unbekannt
-=-nicht verfĂŒgbar
UNDEF=nicht definiert
// Rich's Declarations for Time of the Day
MORNING=Morgen
DAY=Tag
AFTERNOON=Nachmittag
EVENING=Abend
NIGHT=Nacht
BED=Schlafenszeit
State | Start | End |
---|---|---|
MORNING | 06:00 | Sunrise |
DAY | Sunrise | Sunset - 90 minutes |
AFTERNOON | Sunset - 90 minutes | Sunset |
EVENING | Sunset | 11:00 |
NIGHT | 11:00 | 06:00 |
This works fine for most of the year
But during the summer sunrise is before 6, and sunset can be 02.00 so the table should also cover those cases:
State | Start | End |
---|---|---|
MORNING | 06:00(mon-fri), 08.00(sat-sun | Sunrise or 08.00(mon-friday), 10.00(sat-sun) |
DAY | Sunrise or 08.00(mon-fri),10.00 (sat-sun) and 16.00(mon-fri) | Sunset - 90 minutes or 20.00 |
WORK(mon-friday) | 09.00 or voice command | 16.00 or presence detection |
EVENING | Sunset - 90 minutes or 20.00 | Sunset or 23.00 |
NIGHT | 23:00(mon-fri),24:00 (sat-sun) or voice command | 06:00 |
Would be nice to include this for the one leaving above ±50deg latitude. I will work on an example that include this and share it here
Here is an example, which Iâm using sometimes for testing purposes:
Thing astro:sun:stowing1 "Offset 20" [geolocation="48.887211,9.8709123,502", interval=300]{
Channels:
Type rangeEvent : noon#event [
offset=230
]
Type start : noon#start [
offset=230
]
Type rangeEvent : set#event [
offset=20,
earliest="20:10"
]
Type rangeEvent : night#event [
offset=20
]
Type start : rise#start [
offset=20,
earliest="09:00"
]
Type end : rise#end [
offset=20
]
Type start : set#start [
offset=20,
earliest="21:10"
]
Type end : set#end [
offset=10,
latest="21:50"
]
Type rangeEvent : civilDusk#event [
offset=-180
]
Type start : civilDusk#start [
offset=-180
]
}
As you can see there are some more parameters in the channel descriptions (earliest, latest). You can add this too, for calculating time (#start, #end) or triggering a rule (#event).