My ESP8266 based device that is measuring water temperature, water level and liquid chlorine level is happily running for quite some time now.
However, the eco-system around Home Assistant and ESP has evolved, and I just wanted to quickly share how that has changed the setup of my smart swimming pool.
ESPHome
A great new project has emerged called ESPHome which vastly simplifies the setup and integration of ESP based devices into Home Assistant. The main selling points for me for migrating from ESPEasy to ESPHome are:
- File based configuration of the device which makes backing up or versioning the configuration so much easier. No more need for fiddling around in a web based user interface.
- Super simple OTA (over the air) updates of software and configuration.
- Wide range built-in support for devices and sensors for ESP32 and ESP8266.
- Great extension points with generic device support, template sensors, custom code, simple automations.
- Made for Home Assistant which means that the sensors just appear without any further configuration required.
- Its native API and UI support makes the integration of new devices a very seamless experience.
- Better device monitoring within Home Assistant – built-in uptime sensor, connection status sensor, version sensor, restart switch.
Device configuration
Without making any hardware modifications to the device I built and described in the previous posts, I developed the following ESPHome configuration to get the same (or better) outcome in Home Assistant.
As you can see, I stored all my passwords in a separate file and only reference the password by key in here. You will have to find out your Dallas sensor’s ID; it is automatically shown after uploading the firmware in the start-up log, and you can copy and paste the ID (format: 0x1234567890123456
) into your configuration and upload the firmware again.
esphome:
name: esp_01_pool
platform: ESP8266
board: d1_mini_pro
wifi:
ssid: <insert your Wifi SSID here>
password: !secret wifi_password
api:
password: !secret api_password
logger:
ota:
password: !secret ota_password
web_server:
port: 80
dallas:
- pin: D7
switch:
- platform: restart
name: "ESP 01 Pool Restart"
binary_sensor:
- platform: status
name: "ESP 01 Pool Status"
# Water Level
- platform: gpio
pin: D2
name: "ESP 01 Pool Water Level Low"
device_class: problem
- platform: gpio
pin: D1
name: "ESP 01 Pool Water Level Critical"
device_class: problem
sensor:
- platform: uptime
name: "ESP 01 Pool Uptime"
# Water Temperature
- platform: dallas
address: <insert your sensor's ID here>
name: "ESP 01 Pool Water Temperature"
# Liquid Chlorine Level
- platform: ultrasonic
trigger_pin: D5
echo_pin: D6
name: "ESP 01 Pool Liquid Chlorine Distance Level"
text_sensor:
- platform: version
name: "ESP 01 Pool Version"
Uploading firmware
As a prerequisite for turning the above configuration into a firmware binary you will have to set up a development environment – from the command line or from within Hass.io. If you are, like me, coming from a device currently running ESPEasy, please follow this simple guide for the initial upload.
Updated Home Assistant configuration
Entity IDs generated by the ESPHome integration are of course different to what my ESPEasy based entity IDs looked like, so all template sensors and customisations required some small changes. Also, ESPHome’s ultrasonic sensor integration measures the distance in metres by default, I had to tweak the way I calculate the liquid chlorine level in percent a bit.
Water Level
In the following configuration snippet I really only had to change the entity IDs in the value_template
s to make this work again.
binary_sensor:
- platform: template
sensors:
pool_water_level_low:
value_template: "{{ is_state('binary_sensor.esp_01_pool_water_level_low', 'on') }}"
delay_on:
minutes: 5
delay_off:
minutes: 5
friendly_name: "Water Level Low"
device_class: safety
pool_water_level_critical:
value_template: "{{ is_state('binary_sensor.esp_01_pool_water_level_critical', 'on') }}"
delay_on:
minutes: 5
delay_off:
minutes: 5
friendly_name: "Water Level Critical"
device_class: safety
Liquid Chlorine level
In the following I had to change the entity_id
of the statistics
sensor. The template
sensor is now working in metres (hence the change from 60cm to 0.6m – the height of my drum), and I am dealing with unknown values of the statistics
sensor which caused the chlorine level to spike to 100% each time Home Assistant was restarted.
sensor:
- platform: statistics
entity_id: sensor.esp_01_pool_liquid_chlorine_distance_level
name: Pool Liquid Chlorine Level
sampling_size: 180
- platform: template
sensors:
pool_liquid_chlorine_level:
value_template: >
{% if (states('sensor.pool_liquid_chlorine_level_mean') != 'unknown') %}
{% if (states.sensor.pool_liquid_chlorine_level_mean.state | float >= 0.6) %}
0.0
{% else %}
{{ ((0.6 - states.sensor.pool_liquid_chlorine_level_mean.state | float) | float / 0.6 * 100) | round(1) }}
{% endif %}
{% else %}
unknown
{% endif %}
unit_of_measurement: '%'
friendly_name: 'Liquid Chlorine Level'
In addition to the above, all my MQTT sensor configurations related to this device could now go, and I manually removed the retained MQTT messages from my broker.
ESPHome specific configuration
ESPHome comes with an uptime sensor that provides the uptime in seconds. I found it more useful to translate that into a more human-readable form, and added the following (adopted from the HA community forum):
sensor:
- platform: template
sensors:
esp_01_pool_uptime_readable:
friendly_name: "ESP 01 Pool Uptime"
value_template: >-
{% set uptime = states.sensor.esp_01_pool_uptime.state | int %}
{% set days = (uptime / 86400) | int %}
{%- if days > 0 -%}
{{ days }} days, {{ (uptime - (days * 86400)) | int | timestamp_custom('%H:%M:%S', false) }}
{%- else -%}
{{ uptime | int | timestamp_custom('%H:%M:%S', false) }}
{%- endif -%}
TileBoard
I had been using HADashboard for some time, but recently decided to switch to TileBoard. This solution does not require a server application , but instead is purely based on JavaScript and can be served by the web server built into Home Assistant. There is already a wide variety of components included out-of-the-box, and can easily be customised and extended through JavaScript and CSS.
Configuration
I decided to adapt a modular approach for configuring TileBoard where I split the components into manageable portions and which is based around reusable tiles in the form of JavaScript functions.
In the following I am showing you the relevant tiles and other configuration elements relevant to my pool setup.
Tiles
The advantage of defining each tile as a function is that you can reuse a tile on multiple screens. The x-y-coordinates are passed in as parameters, i.e. I can decide where to show each tile at the place where it is used.
// Pool Water Temperature Sensor
var tile_sensor_pool_water_temperature = function(x, y) {
return {
position: [x, y],
type: TYPES.SENSOR,
id: 'sensor.esp_01_pool_water_temperature',
title: 'Water Temperature',
state: false,
}
};
// Pool Pump Sensor
var tile_sensor_pool_pump = function(x, y) {
return {
position: [x, y],
type: TYPES.SENSOR_ICON,
id: 'binary_sensor.pool_pump_running',
title: 'Pool Pump',
states: { on: 'running', off: 'off' },
icons: { on: 'mdi-engine', off: 'mdi-engine' }
}
};
// Pool Pump Running Today Sensor
var tile_sensor_pool_pump_history = function(x, y) {
return {
position: [x, y],
type: TYPES.SENSOR,
id: 'sensor.pool_pump_running_today',
title: 'Today',
subtitle: 'Pool Pump',
state: false,
classes: ["-value-font-size-medium"],
}
};
// Pool Water Level Sensor
var tile_sensor_pool_water_level = function(x, y) {
return {
position: [x, y],
type: TYPES.SENSOR,
id: 'sensor.pool_water_level',
title: 'Water Level',
subtitle: 'Pool',
state: false,
classes: ["-value-font-size-medium"],
}
};
// Pool Liquid Chlorine Level Sensor
var tile_sensor_pool_liquid_chlorine_level = function(x, y) {
return {
position: [x, y],
type: TYPES.SENSOR,
id: 'sensor.pool_liquid_chlorine_level',
title: 'Liquid Chlorine',
subtitle: 'Pool',
state: false,
filter: function (value) { return valueToFixed(value, 0); }
}
};
// Pool Swimming Season
var tile_input_boolean_pool_swimming_season = function(x, y) {
return {
position: [x, y],
type: TYPES.INPUT_BOOLEAN,
id: 'input_boolean.swimming_season',
title: 'Swimming Season',
subtitle: 'Pool',
icons: { on: "mdi-swim", off: "mdi-swim" },
}
};
// Pool Pump Mode Input Select
var tile_input_select_pool_pump_mode = function(x, y) {
return {
position: [x, y],
type: TYPES.INPUT_SELECT,
id: 'input_select.pool_pump',
title: 'Mode',
subtitle: 'Pool Pump',
state: false,
}
};
Utils
The following JavaScript function is a reusable filter that I am using in the tiles to round numbers to save some space on the screen.
// Format provided value to the have number of decimal digits.
function valueToFixed(value, digits) {
var num = parseFloat(value);
return num && !isNaN(num) ? num.toFixed(digits) : value;
}
Custom CSS
For the sake of completeness, here the CSS styles I have used in the tiles above. Of course, depending on the style and layout you use in TileBoard, you may need to adapt the actual sizes and distances, etc.
.-value-font-size-medium .item-entity--value {
font-size: 24px;
white-space: normal;
padding-top: 5px;
}
.-value-font-size-medium .item-entity {
line-height: normal;
}
Page
The following shows an example page where all the tiles defined above are displayed in one row.
var page_outside = {
title: 'Outside',
icon: 'mdi-flower',
groups: [
{
width: 7,
height: 5,
items: [
<...other tiles...>
tile_sensor_pool_water_temperature(0, 3),
tile_sensor_pool_pump(1, 3),
tile_sensor_pool_pump_history(2, 3),
tile_input_select_pool_pump_mode(3, 3),
tile_sensor_pool_water_level(4, 3),
tile_sensor_pool_liquid_chlorine_level(5, 3),
tile_input_boolean_pool_swimming_season(6, 3),
]
}
]
};
Main configuration
The following just demonstrates how the page defined above is included in the main configuration.
var CONFIG = {
pages: [
<...other pages...>
page_outside,
],
}
Serving TileBoard from Home Assistant’s webserver
The easiest way to serve the TileBoard files is from Home Assistant’s own webserver. To do that create folders <configuration folder>/www/tileboard
and copy all files and folders including your customisations into that new folder. After a restart of Home Assistant you can access the TileBoard dashboard – depending on your own setup – under http://<server name>:8123/local/tileboard/index.html
.
How the above looks like in the UI
Development approach and how to keep in sync
TileBoard is not a packaged application, so I came up with the following configuration and customisation approach to keep my own code manageable while not losing the ability to update to the latest code base.
Initial setup
- Clone the TileBoard GitHub repository onto the local development machine.
- Configure this remote repository as
upstream
. - Create a private Git repository (because you may want to store a long-lived access token in the config file), and configure this as
origin
. - Push current state to
origin
. - Create a branch, e.g.
custom-home
that will contain all your customisations. Keep the original code on themaster
.
Configuration and customisations
- Keep changes to the original code to a minimum. I only had to add links to my own custom JavaScript and CSS into the
index.html
file. - Test any changes locally.
- Copy changed files to your Home Assistant server.
- Commit onto your
custom-home
branch and push to your own repository.
Catching up
- Fetch updates from
upstream
repository, and update yourmaster
branch:git fetch upstream
git checkout master
git reset --hard upstream/master
git push origin master --force
- Apply all your customisations
git checkout custom-home
git rebase origin/master
- The above may fail, requiring some manual merging.
git push origin custom-home --force
Outlook
Apart from the various small improvements to my existing setup like the ones described above, I am still looking at integrating a turbidity sensor. It turned out that my first turbidity sensor was broken and hence did not work as expected at all – waiting for a replacement now.
Compatibility
At the time of writing this post, I used:
- Home Assistant 0.89.1 with Python 3.6.3
- ESPHome v1.11.2
- Wemos D1 mini Pro
Leave a Reply