A classic motion sensor only knows two things: motion right now, or none right now. What it doesn't answer out of the box is the far more useful question — when was someone last in this room? That single piece of information is the foundation for any presence heuristic: "house empty for two hours", "nobody in the hallway since breakfast", "has anyone even been in the garage today".
My first instinct was to buy expensive mmWave presence sensors for this. But the plain PIR detectors already on the wall are enough — you just have to store their last on-moment somewhere. In Home Assistant that's an input_datetime helper per room plus a trivial automation. This isn't a light-switching tutorial; it's a pure data pattern that I've since rolled out across the whole house.
Why input_datetime instead of last_changed?
Fair objection: every sensor already has a last_changed attribute. The problem is that last_changed flips on every state change — including the switch from on back to off. So you get the time of the last change, not the last motion. And it doesn't survive a Home Assistant restart cleanly: after a reboot the sensor sits freshly at off, last_changed is the boot time, and your history is gone.
An input_datetime helper fixes both. We write the timestamp only on the transition to on, the value is persistent (it survives restarts), and it's a real, queryable entity you can use in templates, conditions and on the dashboard just like any other sensor.
Step 1 — create one helper per room
Under Settings → Devices & Services → Helpers, create an input_datetime for each room with both date and time enabled. I keep the naming strictly consistent — letzte_bewegung_ (last_motion_) — because that makes later scaling trivial: new entity, same schema. If you prefer YAML, the same thing goes into configuration.yaml.
input_datetime:
letzte_bewegung_gang:
name: Letzte Bewegung Gang
has_date: true
has_time: true
letzte_bewegung_garage:
name: Letzte Bewegung Garage
has_date: true
has_time: true
The only thing that matters is that both flags are set — without has_time you lose the time of day and the whole pattern goes blunt.
Step 2 — write the timestamp on every motion
Now the actual logic, and it's pleasantly short: an automation that, on the motion sensor's transition to on, writes the current time into the matching helper via input_datetime.set_datetime. I deliberately trigger only on to: 'on' — the off event doesn't interest us, we want to capture the start of the last motion.
- id: '1711200000001'
alias: Letzte Bewegung Gang
triggers:
- trigger: state
entity_id: binary_sensor.bewegungsmelder_gang
to: 'on'
actions:
- action: input_datetime.set_datetime
target:
entity_id: input_datetime.letzte_bewegung_gang
data:
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
mode: single
The now().strftime(...) formats the local time into exactly the format set_datetime expects. mode: single is plenty: the action takes milliseconds, parallel runs are irrelevant.
Step 3 — the same pattern across rooms
The real charm is repeatability. For every additional room you copy the automation and swap only two things: the entity_id of the motion sensor and the entity_id of the helper. Same shape, different room.
- id: '1711200000007'
alias: Letzte Bewegung Garage
triggers:
- trigger: state
entity_id: binary_sensor.bewegungsmelder_garage
to: 'on'
actions:
- action: input_datetime.set_datetime
target:
entity_id: input_datetime.letzte_bewegung_garage
data:
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
mode: single
I run about a dozen of these automations in parallel — one per PIR. If you prefer something more compact you can build a single automation with multiple triggers and a template over trigger.to_state; I prefer the one-per-room variant because it stays instantly readable in the UI and lets you disable individual rooms on purpose.
Step 4 — relative time on the dashboard
The raw timestamp "2026-07-17 14:32:10" is awkward on a dashboard — what's interesting is "12 minutes ago". For that Home Assistant has the template function relative_time(), which turns a datetime into a human-readable span. In a Markdown card it looks like this:
type: markdown
content: >
## Last motion
- **Hallway:** {{ relative_time(states('input_datetime.letzte_bewegung_gang') | as_datetime) }} ago
- **Garage:** {{ relative_time(states('input_datetime.letzte_bewegung_garage') | as_datetime) }} ago
- **Living room:** {{ relative_time(states('input_datetime.letzte_bewegung_wohnzimmer') | as_datetime) }} ago
The as_datetime converts the string state into a real datetime object that relative_time can process. The result is a compact overview — "Hallway: 3 minutes ago, Garage: 6 hours ago" — that tells you at a glance where there was life most recently.
What the data is good for
Once the timestamps exist, the interesting automations open up. A "house empty" heuristic: if the most recent of all letzte_bewegung_* times is older than, say, 30 minutes, the house counts as unoccupied — cheap presence detection without an mmWave sensor. Or room-scoped triggers: only ramp up the heating in a room if there's been motion there in the last hour. If you like the Modbus/sensor-data way of thinking, you'll find the same "data first, logic second" stance in my post on PV string fault detection.
Frequently asked questions
Why not just use the sensor's last_changed attribute?
Because last_changed flips on every state change — including the switch from on to off — and doesn't survive a restart: after a reboot the sensor sits freshly at off and last_changed is the boot time. The input_datetime helper, by contrast, stores only the last on-moment, is persistent, and survives restarts.
Does the value survive a Home Assistant restart?
Yes. input_datetime helpers are persistent — their last set value is stored and restored after a reboot. That's one of the main advantages over raw sensor attributes, which start from scratch after a restart.
Do I need a separate automation per room?
No, but it's the most readable option. You can also build a single automation with multiple triggers and pick the matching helper from a mapping via trigger.to_state. I deliberately use one automation per room because it stays instantly understandable in the UI and can be disabled individually.
Does this work with any motion sensor?
With any that shows up in Home Assistant as a binary_sensor with on/off states — Zigbee PIR, KNX presence detector or Wi-Fi sensor alike. The pattern doesn't know the hardware, it only reacts to the on transition. On KNX detectors that I also use for light switching, the same timestamp automation runs alongside without issue.
Originally published on cloudapp.dev, where I write about Home Assistant, self-hosting and data infrastructure.
Top comments (0)