An umlaut killed my frost warning
Three faults ran unnoticed for months, one of them in a protection function. All three were found in one evening — and two earlier findings had to be retracted.
Contents
The flat in Merano stands empty in winter. The most important alarm there is the cold warning: if the rooms cool too far, I need to hear about it before anything freezes.
That warning had been dead since it was built in August. For two and a half months.
The reason is an umlaut.
What happened
Going through the entities after an update, a sensor turned out to be missing. The template renders perfectly, every other entity in the package is there.
But in the registry it read:
sensor.kalteste_raumtemperatur unique_id: kaelteste_raumtemperatur
Home Assistant strips umlauts when forming the entity_id. It does not transliterate them.
"Kälteste Raumtemperatur" -> sensor.kalteste_raumtemperatur
"Kältewarnung Schwelle Entwarnung" -> sensor.kaltewarnung_schwelle_entwarnung
ä becomes a, not ae. My package used the ae form throughout. So the
triggers pointed at entities that had never existed.
And it was in the log every single time
This is the part that stings. Home Assistant wrote, on every single start:
Error initializing 'Kältewarnung: Wohnung kühlt zu stark aus' trigger:
In 'numeric_state' condition: unknown entity sensor.kaelteste_raumtemperatur
Since August. On every restart. Nobody read it.
An error in the startup log that nobody reads is as silent as no error at all. And a protection function that fails at startup behaves in operation exactly like one that never fires because nothing is happening.
That is the difference between “it works” and “it has never reported anything”. With an alarm, both states look identical for months.
The fix was one line. After deployment and a restart the sensor reads 20.5 °C, the all-clear threshold sits at 13.0, and both automations are active.
The trap is now a comment in the package. And one detail: the unique_id may
keep the ae form — it does not determine the entity_id.
Second find: a forgotten copy
The package that attaches weather to every speed measurement keeps its own copy of the weather station query. Deliberately built that way, so the weather package stays untouched.
When the source was migrated, that copy was overlooked. It had since been producing an error every five minutes.
That is the flip side of a clean separation: two copies mean two places to change, and you forget the second.
Migrating it brought two quirks of the new source, both inconspicuous and both error-prone:
- The timestamp has a space instead of the separator and a time zone without a colon. The timestamp validation rejects both.
- The response contains several records per quantity, including ancient ones from 2021 and 2022. You have to sort and take the last — take the first and you get weather from five years ago.
In passing, the new sensor answers a question I had not asked: the station data lags by about two hours. That matters when you write weather next to a measurement — you are then not writing the current weather next to it.
Third find: a command without a shell
A freshly deployed package failed immediately. The same command ran cleanly in the device’s terminal — so it was neither permissions nor paths.
If a command contains a template, Home Assistant starts it without a shell.
Pipes and redirections then land as literal strings at the first program. A > is
no longer a redirection but an argument.
The fix is to call the shell explicitly and pass the variable part in a way that never enters command-line splitting.
In doing so a second shortcoming turned up that would have been worse: the file’s header row did not know about two new sensors. The device would therefore have written a different column scheme than the analysis script — and the two series would not have been mergeable later.
A fault that would only have surfaced at merge time, weeks later, with a data loss nobody can repair.
And two findings I had to retract
This belongs to the same evening, and it belongs here.
“Duplicate YAML keys overwrite each other silently.” That is what my documentation said about one file. Too alarmist. The file uses a regular mechanism to override two fields on purpose — that is standard and works cleanly. Verified live: every query delivers its own parameters and the numbers are right. The warning in the log is cosmetic, not a defect.
“The last backup is six days old.” Also wrong. I had caught the last element of an unsorted list. There is an automatic backup from the previous day.
Both retractions matter more to me than the three finds. A finding that does not hold has to be retracted as visibly as it was written down — otherwise in six months it stands there as a fact and somebody acts on it.
What I would advise
- Read your startup log. Once after every deployment. An error initialising an automation is invisible while nobody looks.
- Check how your system handles umlauts. Do not guess. Create an entity and look at what it is actually called.
- Test alarms by triggering them. An alarm that has never reported is not the same as an alarm that works. For a cold warning that means: raise the threshold, wait, put it back.
- Count your copies. Deliberately duplicated configuration is fine — but note in both places that the other exists.
- Retract what does not hold. And do it where it was written.
So what?
Three faults, all three silent. None produced a message that would have reached anybody. Two would only have cost data — the third would have meant a frozen pipe in January.
The pattern behind it is always the same: the system did say so, just not loudly enough. The umlaut was in the log, the dead endpoint was in the log, the shell-less command returned a non-zero code.
A system that writes its own faults only into a log is a system that stays silent.
If you want to follow this: use RSS
No newsletter. You give me no address, I keep no list, and nobody has to agree to anything to learn that an article is finished. RSS is the older and, for both sides, the more honest solution.
How it works, and why no newsletter
- 1Get a feed reader. No account needed with an app on your phone or a browser extension; use a hosted service if you prefer.
- 2Add the address above to it.
- 3Done. New articles turn up in your reader by themselves — unread stays unread, and nothing lands between invoices and ads in your inbox.
Why that is better: your reader fetches the file. I get no address and cannot see who reads or whether anyone opened it. Unsubscribing means deleting the entry — no link, no confirmation mail, no me.
