Heating weekly schedule - more efficient way to implement this

Hello,

i recently switched to openhab and I’m very happy with it. But i looking help for some questions. i create some items and rules for my weekly heating schedule. And everything works without problems.

My question now: Does anyone know an elegant way to implement this? I’m not quite fit yet with all the language operations openhab has to offer.

// wochenprogramm
Group gMaxWeekSettings


// office
// SWD = Start Weekday
// EWD = End Weekday
Number maxNonEcoSWDOffice        "Komfortbeginn [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoEWDOffice        "Komfortende [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
// SWE = Start Weekend
// EWE = Stop Weekend
Number maxNonEcoSWEOffice        "Komfortbeginn [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoEWEOffice        "Komfortende [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
// WD = Weekday
// WE = Weekend
Number maxNonEcoTempWDOffice     "Komforttemperatur [%.1f °C]"   <temperature> (gMaxWeekSettings)
Number maxNonEcoTempWEOffice     "Komforttemperatur [%.1f °C]"   <temperature> (gMaxWeekSettings)
// WD = Weekday
// WE = Weekend
Number maxEcoTempWDOffice        "Ecotemperatur [%.1f °C]"               <temperature> (gMaxWeekSettings)
Number maxEcoTempWEOffice        "Ecotemperatur [%.1f °C]"               <temperature> (gMaxWeekSettings)


// Bedroom
Number maxNonEcoSWDBedroom        "Komfortbeginn [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoEWDBedroom        "Komfortende [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoSWEBedroom        "Komfortbeginn [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoEWEBedroom        "Komfortende [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoTempWDBedroom     "Komforttemperatur [%.1f °C]"   <temperature> (gMaxWeekSettings)
Number maxNonEcoTempWEBedroom     "Komforttemperatur [%.1f °C]"   <temperature> (gMaxWeekSettings)
Number maxEcoTempWDBedroom        "Ecotemperatur [%.1f °C]"               <temperature> (gMaxWeekSettings)
Number maxEcoTempWEBedroom        "Ecotemperatur [%.1f °C]"               <temperature> (gMaxWeekSettings)
// Livingroom
Number maxNonEcoSWDLivingroom        "Komfortbeginn [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoEWDLivingroom        "Komfortende [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoSWELivingroom        "Komfortbeginn [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoEWELivingroom        "Komfortende [JS(numberToClock.js):%s ]" (gMaxWeekSettings)
Number maxNonEcoTempWDLivingroom     "Komforttemperatur [%.1f °C]"   <temperature> (gMaxWeekSettings)
Number maxNonEcoTempWELivingroom     "Komforttemperatur [%.1f °C]"   <temperature> (gMaxWeekSettings)
Number maxEcoTempWDLivingroom        "Ecotemperatur [%.1f °C]"               <temperature> (gMaxWeekSettings)
Number maxEcoTempWELivingroom        "Ecotemperatur [%.1f °C]"               <temperature> (gMaxWeekSettings)
rule    "max Wochenprogramm"
when
        Time cron "0 0/5 * 1/1 * ? *"
then
        var Number hour = now.getHourOfDay
        var Number minutes = now.getMinuteOfHour
        var Number day = now.getDayOfWeek

        var Number tminutes = hour * 60 + minutes
        if ((day == 1) || (day == 2) || (day == 3) || (day == 4)) {
                // Weekday 

                // Office Komfortbeginn & End - Weekday
                if (tminutes.toString == maxNonEcoSWDOffice.state.toString) {
                        maxSetTempOfficer.sendCommand(maxNonEcoTempWDOffice.state)
                } else if (tminutes.toString == maxNonEcoEWDOffice.state.toString) {
                        maxSetTempOfficer.sendCommand(maxEcoTempWDOffice.state)
                }

                // Bedroom Komfortbeginn & End - Weekday
                if (tminutes.toString == maxNonEcoSWDBedroom.state.toString) {
                        if (PhonePresence.state == ON) {
                                maxSetTempBedroom.sendCommand(maxNonEcoTempWDBedroom.state)
                        } else {
                                logInfo("max", "Überspringe Komfortbeginn Bedroom - Keiner Zuhause")
                        }
                } else if (tminutes.toString == maxNonEcoEWDBedroom.state.toString) {
                        maxSetTempBedroom.sendCommand(maxEcoTempWDBedroom.state)
                }

                // Livingroom Komfortbeginn & End - Weekday
                if (tminutes.toString == maxNonEcoSWDLivingroom.state.toString) {
                        maxSetTempLivingroom.sendCommand(maxNonEcoTempWDLivingroom.state)
                } else if (tminutes.toString == maxNonEcoEWDLivingroom.state.toString) {
                        maxSetTempLivingroom.sendCommand(maxEcoTempWDLivingroom.state)
                }
        } else if (day == 5) {
                // friday

                // Office Komfortbeginn Weekday - KomformtEnd Weekend
                if (tminutes.toString == maxNonEcoSWDOffice.state.toString) {
                        maxSetTempOfficer.sendCommand(maxNonEcoTempWDOffice.state)
                } else if (tminutes.toString == maxNonEcoEWEOffice.state.toString) {
                        maxSetTempOfficer.sendCommand(maxEcoTempWEOffice.state)
                }
                // Bedroom Komfortbeginn Weekday - KomfortEnd Weekend
                if (tminutes.toString == maxNonEcoSWDBedroom.state.toString) {
                        if (PhonePresence.state == ON) {
                                maxSetTempBedroom.sendCommand(maxNonEcoTempWDBedroom.state)
                        } else {
                                logInfo("max", "Skipping - nobody at home")
                        }
                } else if (tminutes.toString == maxNonEcoEWEBedroom.state.toString) {
                        maxSetTempBedroom.sendCommand(maxEcoTempWEBedroom.state)
                }
                // Livingroom Komfortbeginn Weekday - KomfortEnd Weekend
                if (tminutes.toString == maxNonEcoSWDLivingroom.state.toString) {
                        maxSetTempLivingroom.sendCommand(maxNonEcoTempWDLivingroom.state)
                } else if (tminutes.toString == maxNonEcoEWELivingroom.state.toString) {
                        maxSetTempLivingroom.sendCommand(maxEcoTempWELivingroom.state)
                }
        } else if (day == 6) {
                // saturday

                // Office Komfortbeginn & End Weekend
                if (tminutes.toString == maxNonEcoSWEOffice.state.toString) {
                        maxSetTempOfficer.sendCommand(maxNonEcoTempWEOffice.state)
                } else if (tminutes.toString == maxNonEcoEWEOffice.state.toString) {
                        maxSetTempOfficer.sendCommand(maxEcoTempWEOffice.state)
                }
                // Bedroom Komfortbeginn & End Weekend
                if (tminutes.toString == maxNonEcoSWEBedroom.state.toString) {
                        if (PhonePresence.state == ON) {
                                maxSetTempBedroom.sendCommand(maxNonEcoTempWEBedroom.state)
                        } else {
                                logInfo("max", "Skipping - nobody at home")
                        }
                } else if (tminutes.toString == maxNonEcoEWEBedroom.state.toString) {
                        maxSetTempBedroom.sendCommand(maxEcoTempWEBedroom.state)
                }
                // Livingroom Komfortbeginn & End Weekend
                if (tminutes.toString == maxNonEcoSWELivingroom.state.toString) {
                        maxSetTempLivingroom.sendCommand(maxNonEcoTempWELivingroom.state)
                } else if (tminutes.toString == maxNonEcoEWELivingroom.state.toString) {
                        maxSetTempLivingroom.sendCommand(maxEcoTempWELivingroom.state)
                }
        } else {
                // sunday

                // Office Komfortbeginn Weekend - End Weekday
                if (tminutes.toString == maxNonEcoSWEOffice.state.toString) {
                        maxSetTempOfficer.sendCommand(maxNonEcoTempWEOffice.state)
                } else if (tminutes.toString == maxNonEcoEWDOffice.state.toString) {
                        maxSetTempOfficer.sendCommand(maxEcoTempWDOffice.state)
                }
                // Bedroom Komfortbeginn Weekend - End Weekday
                if (tminutes.toString == maxNonEcoSWEBedroom.state.toString) {
                        if (PhonePresence.state == ON) {
                                maxSetTempBedroom.sendCommand(maxNonEcoTempWEBedroom.state)
                        } else {
                                logInfo("max", "Skipping - nobody at home")
                        }
                } else if (tminutes.toString == maxNonEcoEWDBedroom.state.toString) {
                        maxSetTempBedroom.sendCommand(maxEcoTempWDBedroom.state)
                }
                // Livingroom Komfortbeginn Weekend - End Weekday
                if (tminutes.toString == maxNonEcoSWELivingroom.state.toString) {
                        maxSetTempLivingroom.sendCommand(maxNonEcoTempWELivingroom.state)
                } else if (tminutes.toString == maxNonEcoEWDLivingroom.state.toString) {
                        maxSetTempLivingroom.sendCommand(maxEcoTempWDLivingroom.state)
                }
        }
end
Strategies {
        default = everyUpdate
}

Items {
        gMaxWeekSettings* : strategy = everyChange, restoreOnStartup
}

Group                         gMaxSetTemp     "Wunschtemperatur"                  <temperature>
Number  maxSetTempOfficer       "Wunschtemperatur [%.1f °C]"                             <max_temp>  (gMaxOffice, gMaxSetTemp, gMaxAutoTemp) ["TargetTemperature"]  {channel="max:thermostatplus:NEQ1447237:NEQ1586718:set_temp"}
Number  maxSetTempBedroom       "Wunschtemperatur [%.1f °C]"                             <max_temp>  (gMaxBedroom, gMaxSetTemp, gMaxAutoTemp) ["TargetTemperature"]  {channel="max:thermostatplus:NEQ1447237:NEQ1585063:set_temp"}
Number  maxSetTempLivingroom          "Wunschtemperatur [%.1f °C]"                             <max_temp>  (gMaxLivingroom, gMaxSetTemp, gMaxAutoTemp)    ["TargetTemperature"]  {channel="max:wallthermostat:NEQ1447237:OEQ1456821:set_temp"}

Sitemap Heating

for suggestions, I would be grateful

There’s people to use the caldav binding so they create appointments in G**gle calendar for their flat.

Scheduling heating is a very complex thing however. At least long term, most home automation users will want to control everything to the greatest extent possible: have different temperatures at different times of days and week, orchestrate heater types to have different characteristics, switch based on presence detection or weather forecast and more, and have it all individually for each room.

And there simply is no UI or other tool to cover all of these potential inputs.

So long story short, the only reasonable thing to do is what you already do, i.e. to use rules DSL to program it yourself.
Now of course there’s more elegant ways of writing code, but the most important part is that YOU understand it and can maintain it because YOU are the only person who can and has to do it.
Here’s a pointer to a framework you might want to look at if you’re looking to improve your code.


But if your code works and does what you want, my advice would be to keep it.

It works with any CalDAV compliant server. For the “no cloud services” types, OwnCloud provides a self hosted alternative. I think NextCloud supports CalDAV too.

I’ll second Markus’s statement. It needs to make sense to you. But since you posted, lets look at the code as written and see if there are some techniques we can use to improve it. As with lots of things, “improvement” is in the eye of the beholder.

The first thing that comes to mind is to move the time calculations (i.e. what day is it) to another Rule. See Design Pattern: Time Of Day for an example. Your version of the Rule will be a bit different as you are only calculating the day of week, not a time of day. So you just need to run a smidgen after midnight.

Item:

String DayType

Rule:

rule "Calculate the type of day"
when
    Time Midnight
then
    Thread::sleep(500) // normally I don't recommend sleeps, but it is probably OK here.
    var dayType = "WEEKDAY"
    switch(now.getDayOfWeek) {
        case 5:  dayType = "FRIDAY"
        case 6: dayType = "SATURDAY"
        case 7: dayType = "SUNDAY"
    } 

    if(DayType.state.toString != dayType) DayType.postUpdate(dayType)
end

This lets your heating Rule a bit more self documenting and it lets you use DayType in other Rules rather than needing to repeat the same code.

Next, this Rule looks like it has the same code repeated over and over for the most part so apply Design Pattern: How to Structure a Rule to avoid some of this repeated code.

We can also take advantage of Design Pattern: Associated Items to avoid some of the duplicated code. What this will have you do is put your Items into a Group and change the names of your Items so they can be calculated in the Rule. So instead of having a separate if/else statement for each room you can have one loop that handles all three.

Finally, rather than having the Rule trigger once every five minutes it would be better to trigger the rule based on changes or other events that might change the heating state (time of day, presence, changing temperatures).

I don’t have time to do this all my self right now but if you run into trouble feel free to ask.

1 Like

The max devices are able to store heating profiles. Use them. Adjust details manually or with other sensors, but don’t adjust everything from openhab.