Maker, meme-r, and unabashed geek, Joe has been writing about technology since starting his career in 2018 at KnowTechie. He's covered everything from Apple to apps and crowdfunding and loves getting to the bottom of complicated topics. In that time, he's also written for SlashGear and numerous corporate clients before finding his home at XDA in the spring of 2023.
He was the kid who took apart every toy to see how it worked, even if it didn't exactly go back together afterward. That's given him a solid background for explaining how complex systems work together, and he promises he's gotten better at the putting things back together stage since then.
Let's face it, presence detection is the backbone of most smart homes. Mine is no different, and ever since setting up Home Assistant, I've assumed that if my phone stopped reporting, something somewhere would turn red. So before researching automations that act on stale data, I audited my own install, expecting a clean bill of health.
Home Assistant had me marked as home for five days straight. My iPhone was the source, and it hadn't sent a single update since my Proxmox server rebooted at 3 AM on October 8. No error, no warning, no "unavailable" badge on my Home Assistant dashboard. Just a confident "home" built on data that was older than most of my leftovers. And it turns out that's not a bug. That's how Home Assistant works, and it's why even the most useful automations need a freshness check.
My phone stopped talking to Home Assistant, and nothing noticed
Five days of "home" from a phone that never checked back in after a reboot
The audit itself is a single template, run on the Template tab under Settings -> Tools, that lists every entity alongside how long ago it was last reported. My Brother laser printer's sensors hadn't changed value in over 80 hours, but they'd checked in within the last few minutes, because a printer in sleep mode genuinely has nothing new to say.
My phones were a different story. Every sensor in the Home Assistant Companion app on my iPhone, plus every sensor in my DuRoBo Krono, an Android E Ink reader, last reported at the exact same minute: 3:02 AM on October 8, right as Home Assistant came back up after the reboot. Home Assistant restored its last known values and stamped them with the boot time, and neither device reported again. Here's what that looked like at 4:28 AM on October 13:
|
Entity |
What Home Assistant showed |
Last report |
|---|---|---|
|
Person (me) |
home |
121.4 hours ago |
|
iPhone device tracker |
home |
121.4 hours ago |
|
iPhone battery |
100% |
121.4 hours ago |
|
Krono battery |
72% |
121.4 hours ago |
|
Brother HL-L2350DW status |
sleep |
minutes ago |
Not every device handles failure gracefully
Something else on my network failed at that same minute and handled it perfectly. My Eve Energy smart plug, which runs over Matter and Thread, went straight to "unavailable" and stayed that way for days. It eventually recovered on its own around midnight on October 13, and at no point did it pretend everything was fine.
That plug has history, too. In July it dropped off for eight hours, and a script I was running treated any answer other than "unavailable" as a recovery, so an empty error fooled it. That's the same mistake in miniature: missing data isn't good data, and nothing will tell you the difference unless you check.
Why Home Assistant can't tell a quiet sensor from a dead one
An entity only has two ways to admit it's broken
Every entity in Home Assistant has a single state, and only two values carry special meaning: unavailable and unknown. If an integration doesn't explicitly set one of those, the entity keeps its last value indefinitely. My phone never sent a "goodbye," so Home Assistant thought I never left the house.
Automations make this worse. According to the Home Assistant documentation, a condition checks the current state once, right after a trigger fires, and never re-checks it. So time-based automation with a "person is home" condition will happily read a five-day-old value and carry on.
|
Timestamp |
Updates when |
|---|---|
|
last_changed |
The state's value changes |
|
last_updated |
The state or any attribute changes |
|
last_reported |
The integration writes the state, even if nothing changed |
Home Assistant does track when an entity last heard from its source. It just doesn't show it anywhere on the dashboard, and each state object carries three timestamps.
last_reported is the timestamp you want, with one big catch
Since last_changed only moves when the value moves, it can't tell a steady sensor from a dead one. last_reported, which arrived in Home Assistant 2024.4, can. That's what my audit used, and it's what exposed my printer as healthy and my phones as frozen.
The catch is MQTT, and by extension a lot of Zigbee. Per the MQTT integration docs, MQTT entities don't update last_reported unless force_update is turned on. That covers Zigbee2MQTT, so the obvious freshness check can quietly never move for exactly the hardware that dies most often, and Zigbee2MQTT waits 25 hours by default before calling a battery device offline.
The freshness check that stopped my automation acting on dead data
Make every automation prove data is recent, not just present
By the time I sat down to build the fix, my iPhone had reconnected, but Krono hadn't. It was still reporting a 72% battery and "discharging", exactly as it had since October 8. So I wrote a deliberately naive automation that checks Krono's battery every 15 minutes and posts a notification if it's above 20%. It doesn't control anything physical, so I can let Home Assistant trust it blindly.
It ran exactly as you'd fear. At 6:57 AM, it posted "Krono battery is 72%, value unchanged since 2026-10-13 08:51:11.445230+00:00, last reported 2.1 h ago", acting on a reading that was at least five days old. Even the "unchanged since" time was really this morning's restart. Now picture that automation unlocking a door, disarming an alarm, or deciding nobody's home and turning off the heat.
The fix is one extra condition that checks the age of the data, not just its value. It sits alongside the automation's existing conditions.
- condition: template
value_template: >
{% set s = states.sensor.krono_battery_level %}
{{ s is not none
and s.state not in ['unknown', 'unavailable']
and (now() - s.last_reported)
It refuses to act if the sensor is missing, unknown, unavailable, or hasn't reported in six hours. Six hours is a starting point, not a rule, and a sensible window is two or three times a device's normal reporting interval. On MQTT or Zigbee2MQTT entities without force_update, swap last_reported for last_updated and widen the window.
A restart can make five-day-old data look brand new
Except it didn't stop the automation. Home Assistant had restarted for a Core update at around 4:51 that morning, and the restart re-stamped every restored entity with a fresh time. Krono's 72%, unchanged since October 8, now claimed it had last reported just over an hour earlier. At 7:15, the guarded automation fired right alongside the naive one.
The History page still told the truth, with a single entry for Krono since October 8, because the restart didn't record a real change. But last_reported really means "last written", and a reboot counts. The fix is the Uptime integration, which records when it loads, and after any restart, that's the boot time. The sensor then has to have reported since boot:
- condition: template
value_template: >
{% set s = states.sensor.krono_battery_level %}
{% set boot = as_datetime(states('sensor.uptime')) %}
{{ s.last_reported > boot + timedelta(minutes=5)
and (now() - s.last_reported)
After one more restart, the guarded version fired again at 7:45, while the restart-proof version still hasn't run once. The trade-off is that right after a restart, even my phone failed the check until it reported in, then passed seconds after I opened the app. Treating a live sensor as stale for a few minutes is the safe direction to be wrong in.
Catching the next frozen sensor before an automation does
Guarding individual automations fixes the symptom, but I'd rather know a sensor is dead before anything depends on it. So I added an automation that runs hourly, compares each watched entity against its own window and the last boot, and posts a notification listing anything that's gone stale. When I dry-ran its template against my install, it flagged both phones and correctly ignored the printer.
The community is tackling this too, but the most promising option I found, the Device Sentinel HACS integration, is still pre-release and its freeze detection hasn't shipped. It's one to watch rather than one to install today.
Home Assistant fails loudly, but its sensors fail silently
I've always said Home Assistant is one of the few things in my home lab I'd notice breaking within minutes, because my wall panel goes dark and the whole house tells on it. That's still true of Home Assistant itself. It's just not true of the sensors feeding it, which can die in complete silence while everything looks healthy.
The template that caught my phones takes about a minute to run on the Template tab under Settings -> Tools, and it's worth doing tonight. Odds are, something in your install has been quietly lying to you too, and the automations relying on it will keep firing until you give them a reason not to. If you're building out a presence-detecting smart home, start with a freshness check, not after.





