I have a water softener which regenerates every 9 days or so depending on use. I can see when this has happened when the power monitor for the softener goes above 4w.
I am wondering since Home Assistant tends to be trigger and threshold based what approaches I could hijack or adapt to evaluate the time between regenerations. Static coded to number of days elapsed between the last four or five regenerations recorded. The goal really is to know when to fill the salt bin.
I have tried a couple of things so far, but none of them really achieve what I want dynamically. Specifically:
An input number which increments - and then in grafana via influxdb calculate the timestamp differences between the number increments. Using a query and six transformations I can statically get the answer I am looking for, however it is a hard coded option due to the limitation of grafana and the need to convert to rows to do the timestamp calculations.
I used a input date time to record the time of the last regen, but of course this is only good for the last regen offset against this one which isnât what I want to achieve.
I am now thinking maybe a better way would be to template the data back from influxdb into the frontend and use time_delta in jija to do the delta calculations.
Ideas sought on how to natively do a calculation between five or six previous timestamps in home assistant.
If your goal is to know when to fill the salt bin then I solved the same problem with a golfball, a plastic stick and a magnetic door sensor.
You need some weight. I used a golfball because I had it and it doesnât corrode in a salty enviroment. I drilled a hole in the ball and glued a plastic rod on it. The stick is guide through a small hole in the lid of the salt bin. The golf ball will sit on top of the salt. The door sensor is attached on top of the lid. I attached a magnet to the stick at a hight such that when salt is low the magnet triggers the door sendor.
Just keeping it simple and it works for years already
It helps allot to know how quickly the unit regenerates, to understand our water usage against what the water company say we use. The unit regens at 3mÂł. These things will not be identical even in a perfect world since not all our water is softened. But I guess I can just do that by the input number increments and manually keeping a note. Anyway, you gave me food for thought. thank you.
I use a HC-SR04 Ultrasonic Module Distance Sensor connected to a Wemos D1 Mini.
I think, I originally built it based on this description (Water tank monitoring with ESPHome) but found out that the beam of the sensor thatâs used here (JSN SR04T) is too wide for the narrow salt container, so I switched to the HC-SR04.
Once the distance is above a predefined value for (I think) 30min) I receive a notification that I need to add more salt.
I would think a level monitor would be best (I built my own, but you can also get one already built like this.
However, if you want to do what you originally asked about, there are options for that as well. It really depends on how you want the data stored or displayed in HA. Hereâs some options:
A sensor with a state that is the last regen datetime. It would also have a âhistoryâ attribute with a list of the previous 6 regen datetimes. I use something like this to store the history of when I last changed my furnace filter. I donât have the data shown on any dashboard but I can go look at the entity whenever I want to see it. This is what I have for attribute data:
A sensor that simply increments by one every time there is a regen. You could look at a history graph of the sensor to see how many regens happened over any period. Assuming you set up the sensor properly so that statistics are created for it, the retention duration would be indefinite.
A sensor that reports the number of days since the last regen and resets to zero at every regen. It could also store a history attribute with a list of the number of days between each of the past 6 regens.
â
Whatever option you choose, the best method to make the sensor would be to use a trigger-based template sensor. The trigger would be power increasing above 4W, and then you can write whatever logic you want for the state and attributes.
If you want to go down this path and need help just ask.
I think your wisdom is more along the lines of the original ask and clearly you have some advanced templating knowledge. I would be grateful for an example of the trigger based template sensor with history that you use. My templating skills are very much developing as opposed to complete.
I had a database explosion some time back and this caused me to split mine between mariadb and influxdb. MariaDB purges all data > 9 days. InfluxDb only records entities I specify.
Given my DB configuration I guess that your solution would still work fine since the entity history is an attribute of the object which lives either in memory or in the core.restore_state file for reboots? [not to mention the other files in .storage where entities are located.]
Since the information is in an attribute for the current state of the sensor, it is never purged. The recorder database will never purge a âcurrentâ value.
I havenât tested this, but the sensor config would be something like this:
Looks good, about 10 times more complex than I can mange at the moment in jinja. I can write that in PowerShell no problem, but jinja syntax I just havenât had enough time with it. plus its all on one line which makes it hard to understand.
If I decode it I think it says this:
history: retrieves existing history from this sensorâs attributes.
last: adds a new entry with:
datetime: now (ISO format, rounded to seconds).
days_since_previous: calculated as days since the last recorded regeneration.
(last + history)[:20]: prepends new record and limits to the 20 most recent.
Sorted by most recent (reverse=true)
?
I found a couple of bracket equality issues which I have corrected as a best guess below?.
Sorry, I should have tested it out before dropping it here. You were right about bracket equality issues; this is now confirmed working (and I also changed some variable names just so it is easier for me to explain what is going on):
Most of what you had was correct. Iâll repeat what was correct and update with the new variables:
history is the name I chose for the name of the attribute that Iâm storing all the info in
prev_history is a temporary jinja variable that Iâm using just to make the code easier to read. It contains the data that was stored in the history attribute of this sensor the instant before executing this code. If the history attribute doesnât exist (which will happen the very first time the template is triggered), this variable will be an empty list [].
new_entry is also a temporary jinja variable. It will be a list containing one dictionary, with a datetime key and a days_since_previous key.
datetime will have a value of now, truncated to whole seconds, in isoformat string
days_since_previous will have the days since last recorded regen. It calculates this by taking now() and subtracting the most recent regen datetime from the prev_history variable. If there isnât a datetime key, it will default to now, and so the calculation will be now()-now() which is zero days.
(last + history)[:20]: prepends new record and limits to the 20 most recent.
Sorted by the datetime key, starting with most recent (reverse=true)
That would change the state of the sensor to be continuously-incrementing, and it would also create hourly statistics retained indefinitely. So 5 years from now, if you want to show a bar chart of how many regens per month (or per hour, day, week, or year), you could do that.
I am also using MariaDB (though I have 10 day retention) and InfluxDB, but that doesnât have any effect on the above behavior.
Well itâs all time Rick, so it means allot that you came back with such a comprehensive response. Thank you for your valuable reply. I feel sure allot of other people after me will be helped since the logic you have provided could be reused in so many areas of home automation.
I have a ton of stuff to complete this weekend outside so I wonât get to any time with HA until the weekday evenings.
Thank you also for all the other replies each and everyone really useful and appreciated. I am looking forward to upgrading my empty floating jam jar stainless tube guide and nut âfishing lineâ arrangement level indicator on my borehole water tank to a JSN SR04T based MQTT esp32 S3 ultrasonic version at some future point. After I do the HC-SR04 esp32 S3 water softener version (or the golf ball door sensor version) first which is higher priority. [ the task list grows again ].
I use around 1.5 times what the tank can hold each run, which is every 2 days in the heat (at the moment), so if the well pump stops for any reason, the watering cycle will not complete and if the tank float switch (empty cut off device) fails as it did last year I risk loosing the pressure pump which is a non-trivial cost and time replacement deal. Consequently being able to shut off hydrawise via an HA automation before the tank float switch ever needs to kick in is going to be a significant notch in the better nights sleep pole.
The difference is what I have implemented from what was discussed is that an automation watches for the water softener power consumption and increments a counter. This is because the softener valve rotates a few times and spikes power > 4 each time it does so. Clearly from my perspective these additional spikes all constitute one regeneration. So it is easer to have an automation trigger on the first spike and then delay for an hour to effectively ignore the additional spikes of valve rotation.
With this implementation I can manually increase the counter via the helpers interface which I have done âtwo times in short successionâ to test the code out. Once the first âreal regenerationâ happens, this wonât be an option as it will mess with the data.
I am not clear whether what I am seeing in the state under dev tools states is truthfully what is expected.
I was expecting to see some timestamps and a history attribute in there as well. Or does that not appear in this case do we know? Or have I totally goofed on how this works?
In the sprit of clarity here is an image of the statistics for the sensor entity.
Sorry, when renaming some of the variables I forgot to change one. A tip for next time: make sure to go to your logs and see if there are issues whenever something isnât working as expected. In this case, the log error should state
Error rendering attributes.history template for sensor.water_softener_last_regen: UndefinedError: 'history' is undefined
Seeing that error is how I realized what mistake I made.
"days_since_previous": ((now().timestamp() - (prev_history[0] | default({"datetime": now()}, true)).datetime | as_timestamp ) / 60/60/24) | round(0) }] %}
^^^^^
I forgot to rename "history" to "prev_history"
Once you change that line it should work as expected.
Also know that you can do lots of advanced stuff with trigger-based template sensors. You could add a condition to avoid the update if the last entry to the history is less than an hour old, for example:
In the condition template, it is taking the first item from the sensorâs history (or using 0 if the history isnât defined) and converting that to a datetime object, then adding one hour, and comparing it to now().
Ah I see, I did not know that errors based on things I had configured or messed up were placed into the log file. Like messed up jinja code in templates. So thatâs a really great tip to check that every time something isnât working. I do sense there isnât much feedback from the system compared to Windows which is what I am used to. Windows is a Goliath in terms of the power to run it due to all the error handling and logging !
Anyway, I did find that error in my log, exactly as stated, thank you, another light in the dark. Where did you, how did you become familiar with this stuff may I ask?
Yes thatâs perfect - I also started to move my config into packages which makes modifications so much less disruptive. This also means I can just assign a new guid to a sensor entity and trash the old one if I donât like the data.
I incremented and decremented the counter (in quick succession for testing) and found all the entries were recorded. As far as I can see the logic in the condition is right and should be returning false when the incoming trigger timestamp is less that one hour after the last recorded one in the history attribute. But it is allowing the entry to proceed.
state_class: total
history:
- datetime: "2025-06-23T14:36:54+01:00"
days_since_previous: 0
- datetime: "2025-06-23T14:35:31+01:00"
days_since_previous: 0
unit_of_measurement: count
friendly_name: Water Softener Regnerations
I would suggest you paste the template from the conditions into developer tools â templates and that will show you whether it evaluates to false or true. You can then dig into it further, for example remove the < now() from the template to see what the datetime is that is being calculated. Or just enter {{ state_attr('sensor.water_softener_regenerations', 'history') }} to see if it outputs what you expect.
I say all this already knowing that the problem is probably that you have the incorrect entity_id specified, and that last line will probably return null. Take note of what your entity id is. I see in an earlier post your code showed:
And that will get slugified as the entity id. Note the misspelling of âRegnerationsâ. Therefore I suspect your entity_id is sensor.water_softener_regnerations which is one letter different from what I had in my code.
I spent a good amount of time reading the documentation (HA docs and jinja docs) and these forums, and trying various things out in my own HA installation to see how it works. I now spend a decent amount of time helping others because I always learn something in the process, and I enjoy learning about this.
I just wanted to feedback that the salt distance sensor and the Softener Regenerations template are both working perfectly. The machine regenerated last night and co-incidentally I added salt yesterday evening. So I was a bit surprised to see the distance had changed in the morning. Then I saw the state of the template had incremented and the history and day calculation all had worked as expected. Now I can custom-buttoncard that into the dashboard. The board for the tank level sensor âJSN SR04Tâ should come today enabling that project to complete as well.