Today I want you present my clock timer that has different programs for workdays and weekends and also takes bank holidays and appointments from a google calendar into account for the weekend schedule.
This are my assumptions made in my solution:
- The scheduler runs every minute and therefore can send commands to items on a schedule with minute granularity.
- The scheduler uses the JavaScript automation in a file based rule.
- The items can have an optional lock item to suppress the schedule
- The Ephemeris Binding is used with a suitable configuration to calculate weekdays, workdays and bank holidays.
- Works on the opeHab 4.x plattform
This is the rule definition with the main process function:
const { rules, triggers, actions, time, items } = require('openhab');
rules.JSRule({
id: "Zeitschaltuhr",
name: "Zeitschaltuhr",
description: "Definiert ein Wochenzeitprogramm für Items",
triggers: [
triggers.GenericCronTrigger('0 * * * * ? *')
],
tags: [
"Zeitschaltuhr"
],
execute: (event) => {
onUpdate(this, event)
}
})
function onUpdate(context, event) {
const timeSchedules = createTimeSchedules()
const selectedSchedule = timeSchedules[selectSchedule()]
console.debug('Selected schedule is', selectedSchedule.scheduleType)
processSchedule(selectedSchedule)
}
The function createTimeSchedules()
creates an array of time schedules. It contains two scheduling programs. One for workdays and one for weekends. The function selectSchedule()
chooses one of the two schedule programs and select the one to be used from the scheduling array. After that the function processSchedule()
takes that schedule and sends commands to items according to the switch points defined in that schedule.
The scheduling contains SwitchPoint
objects wich has properties for the time span the switch point is active (from
and to
), the command
that should be send, the item
the command should be send to and an optional lockItem
. The lock item can be used to suppress the time schedule if the item is locked.
Here is the listing for the creation of the time schedules:
function SwitchPoint(from, to, command, item, lockItem = null) {
if ((from instanceof time.LocalTime) == false) {
throw new TypeError("From must be of type LocalTime")
}
this.from = from
if ((to instanceof time.LocalTime) == false) {
throw new TypeError("To must be of type LocalTime")
}
this.to = to
if (typeof command !== 'string') {
throw new TypeError("Command must be of type string")
}
this.command = command
this.item = assertItem('item', item)
this.lockItem = assertItem('lockItem', lockItem, true)
}
const ScheduleType = Object.freeze({
"workday": "weekday",
"weekend": "weekend"
})
function TimeSchedule(name, scheduleType, switchPoints) {
if (typeof name !== 'string') {
throw new TypeError("Name must be of type string")
}
this.name = name
this.scheduleType = assertScheduleType(scheduleType)
this.switchPoints = assertSwitchPoints(switchPoints)
}
function assertItem(parameterName, item, isOptional = false) {
if ((isOptional) && (item == null)) {
return item
}
if (typeof item !== 'string') {
throw new TypeError(`Parameter ${parameterName} must be of type string`)
}
if (items.getItem(item, true) == null) {
throw new TypeError(`Item ${item} of parameter ${parameterName} does not exist`)
}
return item
}
function assertScheduleType(parameter) {
if (parameter == null) {
throw new TypeError("Parameter must not be null")
}
switch (parameter) {
case "weekday":
case "weekend":
break
default:
throw new TypeError("Parameter scheduleType is not a ScheduleType")
}
return parameter
}
function assertSwitchPoints(parameter) {
if (parameter == null) {
throw new TypeError("Parameter switchPoints must not be null")
}
if (Array.isArray(parameter) == false) {
throw new TypeError("Parameter switchPoints must be an array")
}
parameter.forEach((switchPoint) => {
if ((switchPoint instanceof SwitchPoint) == false) {
throw new TypeError("Array elements of parameter switchPoints must be of type SwitchPoint")
}
})
return parameter
}
function createTimeSchedules() {
const weekdaySchedule = new TimeSchedule(
"Wochenprogramm",
ScheduleType.workday,
[
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("08:00"), "Economy", 'Lounge_Klima_HVAC_Betriebsart', 'Lounge_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("08:00"), time.LocalTime.parse("22:30"), "Comfort", 'Lounge_Klima_HVAC_Betriebsart', 'Lounge_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("22:30"), time.LocalTime.parse("23:59"), "Economy", 'Lounge_Klima_HVAC_Betriebsart', 'Lounge_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("07:00"), "Economy", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("07:00"), time.LocalTime.parse("10:30"), "Comfort", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("10:30"), time.LocalTime.parse("21:30"), "Standby", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("21:30"), time.LocalTime.parse("23:00"), "Comfort", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("23:00"), time.LocalTime.parse("23:59"), "Economy", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("08:00"), "Economy", 'Esszimmer_Klima_HVAC_Betriebsart', 'Esszimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("08:00"), time.LocalTime.parse("10:30"), "Comfort", 'Esszimmer_Klima_HVAC_Betriebsart', 'Esszimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("10:30"), time.LocalTime.parse("17:00"), "Standby", 'Esszimmer_Klima_HVAC_Betriebsart', 'Esszimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("17:00"), time.LocalTime.parse("22:30"), "Comfort", 'Esszimmer_Klima_HVAC_Betriebsart', 'Esszimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("22:30"), time.LocalTime.parse("23:59"), "Economy", 'Esszimmer_Klima_HVAC_Betriebsart', 'Esszimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("08:00"), "Economy", 'Wohnzimmer_Klima_HVAC_Betriebsart', 'Wohnzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("08:00"), time.LocalTime.parse("10:30"), "Comfort", 'Wohnzimmer_Klima_HVAC_Betriebsart', 'Wohnzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("10:30"), time.LocalTime.parse("17:00"), "Standby", 'Wohnzimmer_Klima_HVAC_Betriebsart', 'Wohnzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("17:00"), time.LocalTime.parse("22:30"), "Comfort", 'Wohnzimmer_Klima_HVAC_Betriebsart', 'Wohnzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("22:30"), time.LocalTime.parse("23:59"), "Economy", 'Wohnzimmer_Klima_HVAC_Betriebsart', 'Wohnzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("20:00"), "Economy", 'Schlafzimmer_Klima_HVAC_Betriebsart', 'Schlafzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("20:00"), time.LocalTime.parse("23:30"), "Comfort", 'Schlafzimmer_Klima_HVAC_Betriebsart', 'Schlafzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("23:30"), time.LocalTime.parse("23:59"), "Economy", 'Schlafzimmer_Klima_HVAC_Betriebsart', 'Schlafzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("23:59"), "Economy", 'Kinderzimmer1_Klima_HVAC_Betriebsart', 'Kinderzimmer1_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("23:59"), "Economy", 'Kinderzimmer2_Klima_HVAC_Betriebsart', 'Kinderzimmer2_Klima_HVAC_Sperren'),
]
)
const holidaySchedule = new TimeSchedule(
"Wochenendprogramm",
ScheduleType.weekend,
[
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("02:00"), "Comfort", 'Lounge_Klima_HVAC_Betriebsart', 'Lounge_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("02:00"), time.LocalTime.parse("10:00"), "Economy", 'Lounge_Klima_HVAC_Betriebsart', 'Lounge_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("10:00"), time.LocalTime.parse("23:59"), "Comfort", 'Lounge_Klima_HVAC_Betriebsart', 'Lounge_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("07:00"), "Economy", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("07:00"), time.LocalTime.parse("10:30"), "Comfort", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("10:30"), time.LocalTime.parse("21:30"), "Standby", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("21:30"), time.LocalTime.parse("23:00"), "Comfort", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("23:00"), time.LocalTime.parse("23:59"), "Economy", 'Badezimmer_Klima_HVAC_Betriebsart'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("08:30"), "Economy", 'Esszimmer_Klima_HVAC_Betriebsart', 'Esszimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("08:30"), time.LocalTime.parse("23:59"), "Comfort", 'Esszimmer_Klima_HVAC_Betriebsart', 'Esszimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("08:30"), "Economy", 'Wohnzimmer_Klima_HVAC_Betriebsart', 'Wohnzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("08:30"), time.LocalTime.parse("23:59"), "Comfort", 'Wohnzimmer_Klima_HVAC_Betriebsart', 'Wohnzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("20:00"), "Economy", 'Schlafzimmer_Klima_HVAC_Betriebsart', 'Schlafzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("20:00"), time.LocalTime.parse("23:30"), "Comfort", 'Schlafzimmer_Klima_HVAC_Betriebsart', 'Schlafzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("23:30"), time.LocalTime.parse("23:59"), "Economy", 'Schlafzimmer_Klima_HVAC_Betriebsart', 'Schlafzimmer_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("23:59"), "Economy", 'Kinderzimmer1_Klima_HVAC_Betriebsart', 'Kinderzimmer1_Klima_HVAC_Sperren'),
new SwitchPoint(time.LocalTime.parse("00:00"), time.LocalTime.parse("23:59"), "Economy", 'Kinderzimmer2_Klima_HVAC_Betriebsart', 'Kinderzimmer2_Klima_HVAC_Sperren'),
]
)
const schedules = new Object()
schedules[weekdaySchedule.scheduleType] = weekdaySchedule
schedules[holidaySchedule.scheduleType] = holidaySchedule
return schedules
}
The function isHolidayByCalendar()
checks an item that is filled by the iCalendar Binding connected to a google calendar.
function isHolidayByCalendar() {
var urlaubsKalenderEintragVorhandenItem = items.getItem("Zentralfunktionen_Zeitinformationen_Kalender_Urlaub_Eintrag_Vorhanden");
var urlaubsKalenderEintragStart = items.getItem("Zentralfunktionen_Zeitinformationen_Kalender_Urlaub_Eintrag_Start");
var urlaubsKalenderEintragEnde = items.getItem("Zentralfunktionen_Zeitinformationen_Kalender_Urlaub_Eintrag_Ende");
var aktuellesDatum = time.ZonedDateTime.now();
if (urlaubsKalenderEintragVorhandenItem.state === "ON") {
var start = time.toZDT(urlaubsKalenderEintragStart);
var ende = time.toZDT(urlaubsKalenderEintragEnde);
if (start.isBefore(aktuellesDatum) && (ende.isAfter(aktuellesDatum))) {
return true;
}
}
return false;
}
function selectSchedule() {
switch (true) {
case isHolidayByCalendar():
console.debug("Today is a scheduled holiday");
return ScheduleType.weekend
case actions.Ephemeris.isBankHoliday():
console.debug("Today is a bank holiday");
return ScheduleType.weekend;
case actions.Ephemeris.isWeekend():
console.debug("Today is a weekend");
return ScheduleType.weekend;
case actions.Ephemeris.isInDayset("workday"):
console.debug("Today is a workday");
return ScheduleType.workday
default:
console.warn("Could not determine the schedule type. Falling back to workday");
return ScheduleType.workday
}
}
Happy programming