Background
Living in the South East means we’re blessed with extremely hard water. A softener fixes that, but most rely on ion exchange and salt blocks, and remembering to check when it’s running low is a guessing game.
I didn’t want to replace a perfectly good system. I just wanted to give it a little awareness, a way for it to tell me when it needed attention.
After doing some research I had a few different options to explore:
Magnetic contact sensor (aka a door sensor).
This would involve placing a magnet on top of the salt block each time, and having the magnetic sensor trigger when it aligns with the magnet placed near an “empty” line.
Whilst this would work in theory by showing a door as closed, which I could then use to trigger a notification in Home Assistant, the salt dissolves in a way that isn’t perfectly linear, so the magnet would most likely fall off.
Load cells (aka smart scales).
Essentially, using weight sensors to calculate whether the salt had run out by measuring it when full and empty to figure out the difference. This would involve quite a lot of DIY effort, as I’d need to configure a board to work with the load cells, and there weren’t any plug and play kits that you could buy.
You could hack something together using smart scales like Withings, but it felt pretty overkill, as they’re battery operated and don’t continuously report.
Time of flight sensor (aka distance sensor).
This was the most interesting to me. A single sensor would be placed at the top of the salt housing, facing down, and calculate the distance from the sensor to the top of the salt block.
It required the least effort, and because it runs on an Arduino board, it’s simple to set up with ESPHome.
Selecting the parts
Putting this together was pretty simple. I needed:
I also bought some longer unbuckled Grove cables, as I wanted this to be continually powered and the ATOM to be far away from any water. I’ve also previously used the ATOM Lites as they work really well as Bluetooth relays for Home Assistant.
Setting it up
The actual assembly is dead simple. The sensor is made to work with ATOMs and simply plugs in with a Grove cable.
I needed to get the ATOM set up with ESPHome. You connect it via USB to your computer and, in Chrome, visit ESPHome where you select the device and choose Full install of ESPHome. As it reboots, you can add your Wi‑Fi network and get it working within a few minutes.
My Home Assistant hub is in my garage, and I wanted it to serve as the brain behind my ESPHome devices, so I installed the ESPHome Device Builder.
Here I can take control of the device I just set up and add the configuration to tell it how to interpret the data coming from the time of flight sensor.
esphome:
name: esphome-web-bd04f4
friendly_name: ESPHome Web bd04f4
min_version: 2025.9.0
name_add_mac_suffix: false
esp32:
variant: esp32
framework:
type: esp-idf
# Enable logging
logger:
# Enable Home Assistant API
api:
# Allow Over-The-Air updates
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Grove port on Atom Lite is I2C (GPIO26 = SDA, GPIO32 = SCL)
i2c:
sda: 26
scl: 32
scan: true
# Time-of-Flight sensor
sensor:
- platform: vl53l0x
name: "Salt Level Distance"
update_interval: 60s
unit_of_measurement: "cm"
filters:
- multiply: 100You then save and install, and Home Assistant should automatically detect it as a new ESPHome device to set up.
Installation
Luckily, my water softener is in a cupboard which has a power socket behind it. I plugged the ATOM in with a USB‑C cable behind the cupboard, then ran the Grove cable through the very top casing of the water softener and used a sticky pad to attach it to the top of the cover.
Over time, the sensor readings rise as the salt dissolves. I learned that 30 cm means low, so I set an automation to check each morning and send a single notification if it’s above that.
alias: Alerts — Salt Low
description: Check salt level each morning at 10:30
triggers:
- at: "10:30:00"
trigger: time
conditions:
- condition: numeric_state
entity_id: sensor.salt_level_distance
above: 30
actions:
- action: script.notify_will_and_fran
data:
title: 🧊 Water Softener
message: Salt is running low.
mode: singleLimiting it to once a day avoids a flood of alerts, and it’s interesting to see the daily regeneration patterns in the data.
Wrap up
It’s been running for a few months now, and it’s surprisingly reliable, even with humidity and the occasional nudge when refilling. The softener quietly looks after itself now. I don’t think about it much, and that’s sort of the point.