This is a screen recording of the card in action (playing at 4x speed). There were 3 people at home, mostly together but I also walked around a bit to trigger some other sensors. Sensors are mainly Phillips Hue purchased used on ebay (~$10 each). They seem to be the most reliable. The ones that say “Motion” are motion alerts from Scrypted cameras via MQTT.
Here is a project I’ve been working on (with my buddy ChatGPT / aider). I was looking for a better way of getting a more ‘glanceable’ view of my presence sensors than the list of entity cards I had. There wasn’t any way to automatically order entities on an entity card by ‘last_changed’ so I went exploring other options. I was inspired by another post on here, and after some work, I think I’ve come up with something that will hopefully be useful to others.
As this is based on custom-button-card, you will need to have that installed. This isn’t packaged in any way, as I wrote the code directly inline in the card. But what it lacks in packaging it makes up for in simplicity: just create a new dashboard and paste the yaml in. You can also copy the yaml for the card directly into an existing dashboard if you prefer.
You also don’t have to worry that I’m going to abandon the project - there’s not much to abandon! We depend on 3 things from custom:button-card: the ‘label’ property (and JS templating of this property, this is basically everything), the ‘triggers_update’ property (this is how we rerender when state changes), and the extra_style property (adds a bit of flair but most styles are inline). If ‘label’ and ‘triggers_update’ continue to function, this card will work.
**Note: you will almost certainly want to customize this a bit to fit your situation. If you don’t understand how to modify these things, I would recommend using ChatGPT.
**
This is where you control which entities are shown:
const motion = Object.values(states)
.filter(e =>
(e.entity_id.match(/^binary_sensor/) && e.attributes.friendly_name?.match(/Occupancy|Presence/)) ||
(e.entity_id.match(/^binary_sensor/) && e.entity_id.match(/_motion/))
)
.sort((a, b) => new Date(b.last_changed) - new Date(a.last_changed));
&& means AND
|| means OR
This code means I am getting all entities where the entity_id starts with ‘binary_sensor’ and the friendly name contains ‘Occupancy’ or ‘Presence’, or where the entity_id ends with ‘_motion’. This is specific to how I have named my sensors, either rename your sensors or update the code as needed
This strips some extraneous text from entity names to better fit the card (e.g., ‘Side Door E Occupancy Sensor’ becomes ‘Side Door E’):
const name = friendlyName
.replace(/Sensor|PIR|Temp|Occupancy/gi, '')
.trim();
Depending on your sensor names you may want to add more here. It’s purely a visual thing but prevents text from overflowing
A couple addl things:
- If you want to change the colors they should be pretty easy to find. There are 6 colors and we use a log based function to determine a gradient between adjacent colors. All of this stuff is in constants towards the top of the JS code. I personally wouldn’t mess with any of the mathy stuff but you do you
- Adding a header should be trivial if you want, just put whatever HTML you want onto line 172-173 (the final return statement)
- I have a problem where sometimes sensors will get stuck ‘on’ (usually poor zigbee signal) so I automatically sort those to the bottom and label them as ‘err’ (those are the red ones). Any sensor that’s been ‘on’ for more than an hour straight is determined to be in an error state (these are mostly PIR sensors so ~5 minutes is more typical)
- Any sensors that are currently “on” are sorted to the top, and have a light bright outline. The text says ‘now’. Once they go to ‘off’, they dim a bit and are sorted to the middle (below the ‘on’ sensors). the gray text is the amount of time since they were last triggered (last_changed)
- Opacity decreases the longer they’ve been off
- ‘last_changed’ is updated when you restart HA so unfortunately if you’ve reset within the last 12 hours, that will be the time shown for your sensors. This is an HA problem and apparently working as designed. it is what it is
- Within each section (on, off, err), everything is sorted by ‘last_changed’ so the most recently triggered sensors are at the top (but ‘on’ sensors will always be first)
- All of it updates in realtime and seems performant! This is a big one. It’s very snappy and takes almost no resources to render since it’s just s with text. It seems to always rerender whenever any of the sensors is updated, with no perceptible lag. This took a lot more time than you might expect, and I think this line is what made it happen: ‘triggers_update: all’
What could be improved?
- Icons would be cool
- I’ve experimented with adding Contact (door) sensors to this but they need to be treated a little differently to make sense (obviously you just want the ‘on’->‘off’ or ‘off->on’ transition, and don’t want to sort all closed doors to the top). I don’t know where that code went and it’s not in this version.
- I’m working on getting the more-info popup to trigger for each entity. custom:button-card only supports opening the more-info popup for a single entity without a massive change to how all of this works, so it will need to be an onclick handler or custom javascript
- some of these are Scrypted cameras, it would be cool if clicking one popped up a live view of the camera, or maybe even customizable so you could pop up a view of a camera with a view of the sensor area
- I’m hoping to add a text input field that allows configuring the entities used without editing the code directly. might require custom JS which adds a lot of complexity
- I might try using last_updated instead of last_changed, but this already works great
Comments, suggestions, issues, or just general “hey that’s cool!” messages are welcome!
If there are other ideas people have for similar visualizations (i.e., plain text) of sensor data, let me know!
Here’s the code: