Here i show you my implementation and concept of a timer and astrotimer (i call it timeclock / astrotimeclock) setup build with rules, groups and a naming-convention.
With this concept you are able to configure your time- and astroclocks as well as the mapping of your items to the specific clocks completely in your sitemap (or Habpanel) without writing code.
You can get the full example code here:
For openHAB 2: GitHub - ModuloFS/openhab_example_timeclock: This is an example of realizing a timeclock functionality in openhab.
For openHAB 3: GitHub - ModuloFS/OH3_example_timeclock
How to use this example
- Install the Astro-Binding and add your location to the Astro.things file.
- Optionally you can install a persistence service like influxDB to store your selections beyond a systemrestart.
- Copy all files from my github repository in your openHAB setup.
- Initialize all the items which are used by the timeclock and the astrotimeclock internally by switching the item “DEBUG_ResetTimeclock” and “DEBUG_ResetAstroclock” to ON once. (You can do that through the openHAB console or by creating a switch in your sitemap)
- Initialize each of the item-timeclock-mapping-items in the example sitemap under “Timeclock Mappings” and map each item either to a timeclock or to no timeclock.
A example of how to use this timeclock-setup you can find in the Timeclock_Test.sitemap and Timeclock_Test.items.
How to configure your Items
You have your normal item which you want to switch with a timeclock:
Switch GF_Livingroom_tablelamp (gTimeclock)
This item you have to add to the group “gTimeclock”.
Then you have to add an additional item which stores and indicates the timeclock mapping of the item above:
Number TIMER_GF_Livingroom_tablelamp (gTimeclockSelection, gTimeclockMemory)
For this item it is important that you name it exactly the same as the item you want to map, with the prefix “TIMER_”. Additionally you have to add this item, which is always of type Number, to the groups “gTimeclockSelection” and “gTimeclockMemory”. The group “gTimeclockMemory” is to store your selection of the mapping in a database.
At this example you have four different timeclock-types on which you can map your items.
-
Timeclock 1/2: A normal Timeclock with two different switchtimes within a week
-
Astrotimeclock 1/2: A Astrotimeclock which you can define switchphases at sunset as well as switchphases at sunrsie as well as switchphase between sunset and sunrise. This is good for decoration lamps for example.
-
Astrotimeclock Rollershutter 1/2: A standard astrotimeclock with a switchtime at sunset and a switchtime at sunrise. This is good for Rollershuttercontrol
- Calendar 1/2: Timetrigger via calendar events. This is realized with the items: “LOCAL_Kalender_1” and “LOCAL_Kalender_2” for the mappings Calendar 1 and Calendar 2. If you switch this items to ON, then all mapped items are switched ON or DOWN, depending if its a switch or a rollershutter. If they switched OFF the all mapped items are switched OFF or UP.
This is the rule where all the magic happens:
val Number SCHALTUHR_1 = 1.0
val Number SCHALTUHR_2 = 2.0
val Number ASTRO_1 = 10.0
val Number ASTRO_2 = 11.0
val Number KALENDER_1 = 20.0
val Number KALENDER_2 = 21.0
val Number ASTRO_ROLLO_1 = 30.0
val Number ASTRO_ROLLO_2 = 31.0
rule "Timeclock_evaluation"
when
Member of gTimeclockTrigger received command
then
// check if a rule is already running
while (LOCAL_RULELOCK.state == ON)
{
Thread::sleep(3000)
}
// set the Rulelock to block a second ruleexecution of "Timeclock_evaluation"
LOCAL_RULELOCK.sendCommand(ON)
// Loop through all items (member of group gTimeclock), witch can maped to a timeclock.
gTimeclock.members.forEach[timeclock_item|{
// Loop through all items (member of group gTimeclockselection), whitch contains the timeclockmapping to each item.
val selection_item = gTimeclockSelection.members.findFirst[i|i.name == "TIMER_"+timeclock_item.name] as NumberItem
// Check if a item was found at all with the findFirst method
if(selection_item !== null)
{
// Check if the actual item (temporary stored in timeclock_item) is maped to the matching timeclock
if( ((selection_item.state == SCHALTUHR_1) && (triggeringItem.name == "LOCAL_Timersignal_1")) ||
((selection_item.state == SCHALTUHR_2) && (triggeringItem.name == "LOCAL_Timersignal_2")) ||
((selection_item.state == ASTRO_1) && (triggeringItem.name == "LOCAL_Astrotrigger_1")) ||
((selection_item.state == ASTRO_2) && (triggeringItem.name == "LOCAL_Astrotrigger_2")) ||
((selection_item.state == KALENDER_1) && (triggeringItem.name == "LOCAL_Kalender_1")) ||
((selection_item.state == KALENDER_2) && (triggeringItem.name == "LOCAL_Kalender_2")) ||
((selection_item.state == ASTRO_ROLLO_1) && (triggeringItem.name == "LOCAL_Rollo_Astrotrigger_1")) ||
((selection_item.state == ASTRO_ROLLO_2) && (triggeringItem.name == "LOCAL_Rollo_Astrotrigger_2")) )
{
// The actual item matches to a timeclock, so the switch command has to be executed
// Here i use the implicit variable for the triggering item
if(receivedCommand == ON)
{
// You can control Switches, Dimmers and Color items, as well as Rollershutter
if(timeclock_item.type != "Rollershutter") timeclock_item.sendCommand(ON)
else timeclock_item.sendCommand(DOWN)
}
else
{
// You can control Switches, Dimmers and Color items, as well as Rollershutter
if(timeclock_item.type != "Rollershutter") timeclock_item.sendCommand(OFF)
else timeclock_item.sendCommand(UP)
}
// Waittime is optional and im my case its for my RF-Outlets whitch need some time between each switching cycle
Thread::sleep(1200)
}
}
}]
// reset the Rulelock to enable the ruleexecution of "Timeclock_evaluation"
LOCAL_RULELOCK.sendCommand(OFF)
end
In this Rule you have two loops. The first (outer-) loop loops through the group “gTimeclock”. This group contains all items that can be assigned to a timeclock (Timeclock_1/SCHALTUHR_1, Astrotimeclock_1/ASTRO_1, usw…).
The second (inner-) loop loops through the group “gTimeclockSelection”. This group contains all items that mapps all the items to the different timeclocks. So each of the item in the first loop has a related item in the second loop with the additional prefix “TIMER_”.
Now if a pair is found in this line:
val selection_item = gTimeclockSelection.members.findFirst[i|i.name == "TIMER_"+timeclock_item.name] as NumberItem
The mapping information is checked and if necessary a switching command is executed in the following lines of the rule.