Tutorial¶
In this tutorial, you’ll learn how to set up a basic heating schedule with some cool features using Schedy.
Note
You are highly recommended to read the chapter about Schedy’s conception before proceeding.
This tutorial’s purpose is to get up and running quickly, which is why explanations aren’t very detailed here, but the individual sections tell you where to read more about particular features.
Contents
- Tutorial
- Objective
- Configuration Skeleton
- Reading the Log
- Configuring Some Heating Times
- Adding Window Sensors
- Automatic Re-Scheduling After Manual Adjustments
- Stopping the Kids From Playing With the Thermostats
- Switching Schedules as Needed
- Using
expression_environment
to Make Rules More Concise - Final Configuration
- Ok, And Now?
Objective¶
The goal is to have Schedy control thermostats in a flat with four rooms, a living room, a bedroom and two kids rooms. In the living room, there are two radiators, hence we’ve got two thermostats there.
In each room, there’s a window with window sensor attached. We want the heatings in the particular room to be turned off when a window is opened and the previous setting be restored when it’s closed again.
Furthermore, we make some enhancements to our schedules, allowing for dynamic schedule switching and more. Stay tuned!
Configuration Skeleton¶
Our first step is to create a basic configuration which defines our rooms and actors
and save it as schedy_heating.yaml
in AppDaemon’s apps
directory:
schedy_heating: # This is our app instance name.
module: hass_apps_loader
class: SchedyApp
actor_type: thermostat
rooms:
living:
actors:
climate.living_1:
climate.living_2:
schedule:
bed:
actors:
climate.bed_1:
schedule:
kids1:
actors:
climate.kids1_1:
schedule:
kids2:
actors:
climate.kids2_1:
schedule:
During the following steps, only configuration changes and additions are shown. A full sample configuration like it looks after all steps have been applied can be found at the end of this tutorial.
You may also want to consult the full reference of all available settings.
Reading the Log¶
Schedy uses AppDaemon’s regular logging functionality to inform you about what’s going on. How to access these logs depends on the way you set up AppDaemon, but by default they’re just printed to stdout. Consult AppDaemon’s documentation for details.
You’ll need to watch the log often as you proceed with this tutorial, so make sure you know how to do it.
Configuring Some Heating Times¶
Obviously, schedules are the most powerful part of Schedy. Unfortunately, that means they can get a little complex when advanced features are used heavily. This tutorial just configures simple heating times, but you may need to have a comprehensive look at the chapter about schedules at some point.
We want to keep it simple for now. During nights or when no other temperature has been configured, the heating should be turned off in all rooms.
As schedules are evaluated rule by rule from top to bottom until a matching rule was
found, we create a new rule as fallback at the end of each room’s schedule. But wait,
that would be redundant! Fortunately, there is the schedule_append
section we
can use to append something to the schedules of all rooms at once. This goes into
our config:
schedule_append:
- v: "OFF"
Now, each room gets its own heating times.
Living room:
schedule: # We set different heating times for weekdays and weekends. - { v: 20, start: "06:00", end: "07:30", weekdays: 1-5 } - { v: 20, start: "15:00", end: "22:30", weekdays: 1-5 } - { v: 20, start: "08:00", end: "23:30", weekdays: 6-7 }
Bedroom:
schedule: # The bedroom should always have 14 degrees to sleep well in there. - v: 14
Kids rooms:
# We use the exact same schedule for both kids1 and kids2. schedule: - { v: 20, start: "06:00", end: "07:30", weekdays: 1-5 } - { v: 20, start: "15:00", end: "19:00", weekdays: 1-5 } - { v: 20, start: "07:30", end: "20:00", weekdays: 6-7 }
Now save the configuration and watch your new schedules in action. You can play with the times of some rules and change them back and forth to verify Schedy applies everything correctly.
Grouping Similar Rules Into Sub-Schedules¶
The schedules we created so far work fine, but they are quite verbose and contain some redundancy. Let’s utilize a cool feature of Schedy to get rid of that redundancy and make our rules more concise: sub-schedules.
The only rooms this really makes sense for are the living room and the kids rooms,
as they contain multiple rules with common properties (like v
and weekdays
).
Living room:
schedule: - v: 20 rules: - weekdays: 1-5 rules: - { start: "06:00", end: "07:30" } - { start: "15:00", end: "22:30" } - weekdays: 6-7 rules: - { start: "08:00", end: "23:30" }
Kids rooms:
schedule: - v: 20 rules: - weekdays: 1-5 rules: - { start: "06:00", end: "07:30" } - { start: "15:00", end: "19:00" } - weekdays: 6-7 rules: - { start: "07:30", end: "20:00" }
You see that the schedules didn’t get shorter, but we now have a clear
hirarchy of rules and don’t need to repeat v
and weekdays
over and over
anymore. Structuring your schedules this way is by no means required, but it does
increase readability and maintainability as your schedules get more complex. Some
sophisticated features can take even more advantage of sub-schedules, as you’ll
see later.
Consolidating the Kids Rooms’ Schedules¶
The schedules for both kids rooms are identical. It would be nice to
have the schedule only once. We use the schedule snippets feature and create a schedule
snippet named "kids"
:
schedule_snippets:
kids:
- v: 20
rules:
- weekdays: 1-5
rules:
- { start: "06:00", end: "07:30" }
- { start: "15:00", end: "19:00" }
- weekdays: 6-7
rules:
- { start: "07:30", end: "20:00" }
Now, we include that snippet in the schedules of the kids rooms:
schedule:
- x: "IncludeSchedule(schedule_snippets['kids'])"
Done!
Adding Window Sensors¶
We’re just following the official guide for open window detection here.
The rule which turns the heatings off when a window is open is placed in the
schedule_prepend
section:
schedule_prepend:
- x: "Mark(OFF, Mark.OVERLAY) if not is_empty(filter_entities('binary_sensor', state='on', window_room=room_name)) else Next()"
Why that rule works as it does is explained in more detail in the guide linked above.
We now map our sensors to the rooms they belong to with help of customize.yaml
:
binary_sensor.living_window_1:
window_room: living
binary_sensor.bed_window_1:
window_room: bed
binary_sensor.kids1_window_1:
window_room: kids1
binary_sensor.kids2_window_1:
window_room: kids2
Adding more than one sensor per room would be very simple, as you can see.
Finally, we tell Schedy to re-evaluate the room’s schedule when a sensor changes its
state. For that, we just add them to the watched_entities
lists of the particular
rooms. Here is an example for living
, the others are analogous:
watched_entities:
- binary_sensor.living_window_1
Automatic Re-Scheduling After Manual Adjustments¶
It would be cool to be able to change the temperature in a room unplanned and have
Schedy apply the regular schedule again after some period of time. For this purpose,
there is the rescheduling_delay
setting that can be set per room.
Let’s enable it in living room and bedroom and set it to two hours (120 minutes):
living:
rescheduling_delay: 120
# ...
bed:
rescheduling_delay: 120
# ...
Stopping the Kids From Playing With the Thermostats¶
Our kids are still young and hit every button they can reach. Why not fix the
temperature in the kids rooms to what is dictated by the schedule? We disable
allow_manual_changes
and Schedy will revert any manual change as soon as it’s
performed:
kids1:
allow_manual_changes: false
# ...
kids2:
allow_manual_changes: false
# ...
Switching Schedules as Needed¶
Wouldnt it be nice to be able to switch the schedules when, for instance, we have holidays and are home over the day? Nothing simpler than that with Schedy.
We add an input_select
in Home Assistant:
input_select:
heating_mode:
name: Heating Mode
options:
- Normal
- Parents Home
- All Home
Then, we adapt the schedules accordingly. The pattern we follow is this one, should you need help understanding what’s going on here.
Living room:
schedule: - v: 20 rules: - weekdays: 1-5 rules: - rules: - x: "Next() if state('input_select.heating_mode') == 'Normal' else Break()" - { start: "06:00", end: "07:30" } - { start: "15:00", end: "22:30" } - rules: - x: "Next() if state('input_select.heating_mode') != 'Normal' else Break()" - { start: "08:00", end: "23:30" } - weekdays: 6-7 rules: - { start: "08:00", end: "23:30" }
Kids rooms:
schedule_snippets: kids: - v: 20 rules: - weekdays: 1-5 rules: - rules: - x: "Next() if state('input_select.heating_mode') != 'All Home' else Break()" - { start: "06:00", end: "07:30" } - { start: "15:00", end: "19:00" } - rules: - x: "Next() if state('input_select.heating_mode') == 'All Home' else Break()" - { start: "07:30", end: "20:00" } - weekdays: 6-7 rules: - { start: "07:30", end: "20:00" }
Don’t forget to add input_select.heating_mode
to the list of entities watched
for state changes. Instead of adding it to all three concerned rooms, we simply add
it to the global list and have it count for all rooms:
watched_entities:
- input_select.heating_mode
Using expression_environment
to Make Rules More Concise¶
We’ve got four schedule rules with expressions that all use
state('input_select.heating_mode')
to query the heating mode currently selected
from Home Assistant. This is quite repetitive and makes the rules long and unwieldy.
There is the expression_environment
setting, which allows us to built custom Python
objects we can then use in all our rule expressions. We utilize this functionality
and create a new function, heating_mode()
:
expression_environment: |
def heating_mode():
return state("input_select.heating_mode")
The individual rules then change to something like:
- x: "Next() if heating_mode() == 'All Home' else Break()"
The remaining ones are left to do for you.
Final Configuration¶
Here is the final outcome of our work as a full Schedy configuration.
schedy_heating: # This is our app instance name.
module: hass_apps_loader
class: SchedyApp
actor_type: thermostat
expression_environment: |
def heating_mode():
return state("input_select.heating_mode")
schedule_snippets:
kids:
- v: 20
rules:
- weekdays: 1-5
rules:
- rules:
- x: "Next() if heating_mode() != 'All Home' else Break()"
- { start: "06:00", end: "07:30" }
- { start: "15:00", end: "19:00" }
- rules:
- x: "Next() if heating_mode() == 'All Home' else Break()"
- { start: "07:30", end: "20:00" }
- weekdays: 6-7
rules:
- { start: "07:30", end: "20:00" }
watched_entities:
- input_select.heating_mode
schedule_prepend:
- x: "Mark(OFF, Mark.OVERLAY) if not is_empty(filter_entities('binary_sensor', state='on', window_room=room_name)) else Next()"
schedule_append:
- v: "OFF"
rooms:
living:
rescheduling_delay: 120
actors:
climate.living_1:
climate.living_2:
watched_entities:
- binary_sensor.living_window_1
schedule:
- v: 20
rules:
- weekdays: 1-5
rules:
- rules:
- x: "Next() if heating_mode() == 'Normal' else Break()"
- { start: "06:00", end: "07:30" }
- { start: "15:00", end: "22:30" }
- rules:
- x: "Next() if heating_mode() != 'Normal' else Break()"
- { start: "08:00", end: "23:30" }
- weekdays: 6-7
rules:
- { start: "08:00", end: "23:30" }
bed:
rescheduling_delay: 120
actors:
climate.bed_1:
watched_entities:
- binary_sensor.bed_window_1
schedule:
kids1:
allow_manual_changes: false
actors:
climate.kids1_1:
watched_entities:
- binary_sensor.kids1_window_1
schedule:
- x: "IncludeSchedule(schedule_snippets['kids'])"
kids2:
allow_manual_changes: false
actors:
climate.kids2_1:
watched_entities:
- binary_sensor.kids2_window_1
schedule:
- x: "IncludeSchedule(schedule_snippets['kids'])"
And the Home Assistant part:
customize:
binary_sensor.living_window_1:
window_room: living
binary_sensor.bed_window_1:
window_room: bed
binary_sensor.kids1_window_1:
window_room: kids1
binary_sensor.kids2_window_1:
window_room: kids2
input_select:
heating_mode:
name: Heating Mode
options:
- Normal
- Parents Home
- All Home
Ok, And Now?¶
Enjoy your new, powerful schedules! Consult the following chapters for more detailed information on creating advanced rules, supported actor types, events and statistics collection. The Tips & Tricks chapter may give you some more inspiration after all.