48 January & February 2021 http://www.elektormagazine.com
built in, much like the potentiometer of the thermostat. Fortunately,
there is a way around this limitation, and that is by using so-called
‘lambda’ blocks.
Lambda Blocks
The concept of lambda blocks is great and terrible at the same time. They
are great because they let you do whatever you want, or almost, and
they are awful because they defy the whole concept of device config-
uration by means of a simple YAML file without having to know C++.
Simply put, a lambda block is C++ code that is injected into the
ESPHome project, verbatim. To make this possible, the develop-
ers had to jump through hoops and loops to come up with a usable
YAML-to-C++ interface. The result is that lambda code is more compli-
cated than it would have been, had it been normal C++.
Actually, as soon as you find yourself using lambda blocks, you should
start thinking about creating a custom component. ESPHome supports
custom components for nearly everything. I was on the brink of writing
a custom climate component to solve the issues I was having but, in
the end, I decided to keep that subject for another article.
Back to setting the target temperature of the climate component.
Its C++ interface has a function to set the target temperature. The
lambda block in the on_value automation section of the potentio-
meter sensor above (repeated below) shows how to use it. Every time
a new value is available, the set_target_temperature method of the
climate component t_controller is called, like this:
on_value:
then:
- lambda: |-
auto call = id(t_controller).make_call();
call.set_target_temperature(x);
call.perform();
In normal C/C++ this would have been something like:
t_controller.set_target_temperature(x);
If the climate component would have had a target temperature input,
things could have been even simpler. For example:
climate:
- platform: thermostat
name: “Thermostat”
id: t_controller
sensor: t_room
target: t_target
...
Anyway.
Unfortunately, this way of automating on_value is too simplistic as it
overrides remote control of the target temperature from e.g., Home
Assistant. To solve this, the automation should only be allowed to run
when t_target changes (i.e., when someone turns the potentiome-
ter). As ESPHome does not have a on_value_changed automation
for sensors, we can handle this ourselves in the lambda block, like so:
then:
- lambda: |-
auto call = id(t_controller).make_call();
call.set_target_temperature(x);
call.perform();
The part at the end after on_value: is an automation and will be
explained after the introduction of the ‘climate’ component below.
But first, we will look at the relay.
Switching the Heater
This is pretty simple as the relay is just a switch connected to GPIO pin 4
and it is therefore part of the gpio platform. Like the temperature sensor, it
needs an id (heater) to make it accessible to other parts of the YAML file.
switch:
- platform: gpio
pin: GPIO4
name: “Heater”
id: heater
Climate Control
So, now we have a sensor to measure the room temperature (t_room),
a potentiometer to set a target temperature (t_target) and a relay to
switch a heater on and off (heater).
ESPHome features a climate component to control heating and cooling
devices. A thermostat therefore is a climate component. The advan-
tage of using a built-in component is that it saves you work. It will
also ensure that it gets a nice graphical control widget in the Home
Assistant user interface.
climate:
- platform: thermostat
name: “Thermostat”
id: t_controller
sensor: t_room
default_target_temperature_low: 20 °C
heat_action:
- switch.turn_on: heater
idle_action:
- switch.turn_off: heater
hysteresis: 0.5
away_config:
default_target_temperature_low: 15 °C
The thermostat described in this article does not cool; it can only heat.
That makes it a member of the ESPHome thermostat platform. It needs
an id so it can be controlled by other things in the YAML file. I set the
ID to t_controller because the platform is already called thermostat
and we don’t want to mix things up.
The climate component has a temperature sensor input, which we
connect to t_room. It must also be connected to a heater, which we
can do thanks to the heater switch we defined earlier.
Like me, you might expect that a climate component also has a target
temperature input, but it doesn’t. (Maybe it will be added in a future
version of ESPHome?) Instead it has a set-target-temperature control