A Home Assistant Lovelace card for building an interactive floorplan — with a visual DRAG AND DROP editor. Draw walls, drop doors and windows, add gray furniture diagrams and text labels, and place your entities as icons, ripples or live state. Everything scales automatically to the card and screen size.
- Visual editor — draw walls (endpoints snap to nearby corners to close rooms), click to drop doors/windows that snap onto walls, drag everything around, nudge with arrow keys, undo/redo, zoom.
- Devices — bind any entity to an icon. Click to toggle lights/switches or open the more-info dialog. Optional live state label (incl. a paired temperature + humidity entity), custom icon (with autocomplete + preview), size and rotation.
- Presence ripples — render presence/movement sensors as animated concentric rings that pulse while active and fade to a faint dot when idle.
- Animated doors & windows — link a contact
binary_sensororcoverso doors swing and windows open on the plan as their real state changes, with an optional accent color while open. - Furniture — gray line-art diagrams: table, round table, desk, chair, sofa, bed, wardrobe, rug, plant, fridge, stove, sink, toilet, stairs, tv.
- Text labels and a configurable canvas background color.
- Multiple floors — group elements per floor and switch between them with a control in the top-right (in both the editor and the live card).
- Multi-select & copy/paste — shift/ctrl-click or drag a box to select many; move, duplicate (Ctrl/Cmd+D), copy/paste (Ctrl/Cmd+C/V) or delete them together.
- Free placement — elements aren't locked to the visible grid, so you can center them precisely; an optional Snap setting re-enables grid snapping at any granularity.
- Auto-scaling — a virtual coordinate space + SVG means the plan rescales to any card or screen size with no reflow.
This is currently distributed as a custom repository. Click the badge to add it to your own Home Assistant in one step:
…or add it manually:
- In Home Assistant, open HACS.
- Top-right ⋮ → Custom repositories.
- Add repository URL
https://github.com/nicosandller/easy-floorplanwith category Dashboard (a.k.a. Plugin). - Find Easy Floorplan in HACS and click Download.
- Hard-refresh your browser (Cmd/Ctrl-Shift-R).
HACS adds the dashboard resource automatically.
- Download
easy-floorplan-card.jsfrom the latest release. - Copy it to
<config>/www/easy-floorplan-card.js. - Add it as a dashboard resource (Settings → Dashboards → ⋮ → Resources → Add):
- URL
/local/easy-floorplan-card.js - Type JavaScript module
- URL
- Hard-refresh your browser.
Edit a dashboard → Add card → search Easy Floorplan. Use the toolbar:
- wall — drag to draw. Endpoints snap to nearby corners; start a new wall on an existing corner to continue the perimeter.
- door / window — click to drop; it snaps onto the nearest wall. Assign a sensor in the side panel to animate it open/closed (see Doors & windows).
- + device / + text / + furniture… — add elements, then edit them in the side panel.
- select — move, rotate, resize, recolor, delete. Shift/Ctrl-click or drag a box to select several at once; arrow keys nudge the selection (Shift+arrow jumps a full grid cell), and Ctrl/Cmd+C/V/D copy / paste / duplicate.
- floor — add, rename, switch and delete floors (top-right of the toolbar).
Undo/redo and a zoom slider are in the toolbar.
Everything you place on the plan is an element you can select, move (freely or snapped to a grid), nudge with arrow keys, copy/paste, duplicate and delete. The element types are devices, doors & windows, furniture and text — and each floor holds its own set of them.
A device binds a Home Assistant entity to a spot on the plan. Add one with + device, then pick the entity in the side panel. By default it shows an icon badge:
- Tap to act — lights, switches, covers, fans and
input_booleans toggle on tap; any other entity opens its more-info dialog. - Live look — the badge highlights when the entity is "on". Turn on Show state
to display the current value next to it. Add a 2nd entity to show two readings in
one element — e.g. a temperature and a humidity sensor render together as
21 °C · 45 %. - Make it yours — override the icon (with autocomplete + live preview), set a custom name, change the size, rotate it, or hide the icon entirely.
For motion/occupancy/presence sensors, switch a device's Display mode from Icon badge to Ripple or Icon + ripple. Instead of a static icon it draws animated concentric rings:
- Active (sensor on) → the rings continuously pulse outward and fade, drawing the eye to where motion is happening.
- Idle (sensor off) → the rings stop and only a faint dot remains, so the spot stays marked without being distracting.
You can set the ripple color and ripple size per device, so e.g. a calm blue ring in the living room and a warmer one by the entrance. It works with any entity that reports an on/off-like state, not just presence sensors.
Drop a door or window from the toolbar and it snaps onto the nearest wall. On its own a door is drawn open (the familiar swing arc) and a window closed — a static floor plan, just like before.
Bind a Sensor entity in the side panel — a contact binary_sensor or a cover — to
make the opening track its real state:
- Open / closed — the opening is drawn open when the entity is
on/open, closed otherwise. A door's leaf swings around its hinge; a window's two leaves swing outward from the middle. When closed the swing arc is hidden; as the opening moves, the arc draws on, tracing the path of the leaf edge — animated smoothly. - Active color — while actively open, the leaf/sash and arc take an accent color (the same idea as presence ripples) so an open door is easy to spot. Defaults to the primary color; pick your own per opening.
- Invert — flip the open/closed interpretation for sensors wired the other way.
Openings without a sensor keep the static look.
The editor writes this config for you; manual editing is optional.
| Option | Type | Default | Description |
|---|---|---|---|
type |
string | — | custom:easy-floorplan-card |
title |
string | — | Optional card header. |
width |
number | 1000 |
Virtual canvas width. |
height |
number | 600 |
Virtual canvas height. |
grid |
number | 20 |
Visible grid spacing (a visual guide). |
snap |
number | 0 |
Placement snap step; 0 = free placement. |
background |
string | card background | Canvas background color (CSS / hex). |
floors |
Floor[] | — | Per-floor element groups (see Floors). |
defaultFloor |
string | first floor | Id of the floor shown first. |
walls |
Wall[] | [] |
Wall segments (single-floor / floor 1). |
openings |
Opening[] | [] |
Doors and windows. |
items |
Item[] | [] |
Entity devices. |
texts |
Text[] | [] |
Free text labels. |
furniture |
Furniture[] | [] |
Gray furniture/fixture diagrams. |
When floors is present each floor carries its own walls, openings, items, texts
and furniture. The top-level arrays describe a single implicit floor and remain valid
for backward compatibility.
{ id, name, walls, openings, items, texts, furniture } — a named floor with its own
elements. Use the floor controls in the editor toolbar to add, rename, switch and
delete floors; the live card shows a floor switcher in the top-right when there is more
than one.
{ id, x1, y1, x2, y2 } — endpoints in virtual units.
| Field | Type | Description |
|---|---|---|
id |
string | Unique id. |
type |
door | window |
Symbol drawn. |
x, y |
number | Center position. |
length |
number | Length along the wall. |
angle |
number | Rotation in degrees. |
entity |
string | Optional contact binary_sensor / cover driving open/closed. |
invert |
boolean | Flip the open/closed interpretation. |
activeColor |
string | Leaf/arc color while actively open (default primary). |
| Field | Type | Default | Description |
|---|---|---|---|
id |
string | — | Unique id. |
entity |
string | — | Entity id to bind. |
secondaryEntity |
string | — | Optional 2nd entity shown alongside (e.g. humidity). |
x, y |
number | — | Position. |
kind |
light/switch/sensor/binary_sensor/climate/cover/generic | inferred | Used for the default icon. |
icon |
string | entity icon | Override mdi icon. |
name |
string | friendly name | Label / tooltip override. |
size |
number | 34 |
Icon badge diameter (px). |
angle |
number | 0 |
Icon rotation (deg). |
display |
badge | ripple | iconRipple |
badge |
How the device is drawn. |
rippleColor |
string | primary color | Ripple ring color (ripple modes). |
rippleSize |
number | 80 |
Max ripple diameter (px). |
showIcon |
boolean | true |
Show the icon badge. |
showState |
boolean | sensors only | Show the entity state label. |
Clicking a light, switch, cover, fan or input_boolean toggles it; other
domains open the more-info dialog.
{ id, x, y, text, size?, color?, angle? } — size px (default 16), color CSS/hex,
angle degrees.
{ id, type, x, y, w, h, angle?, color? } where type is one of table, roundTable,
desk, chair, sofa, bed, wardrobe, rug, plant, fridge, stove, sink,
toilet, stairs, tv. color defaults to gray so furniture reads differently from
walls.
type: custom:easy-floorplan-card
title: Living Room
width: 1000
height: 600
grid: 20
background: "#fafafa"
walls:
- { id: w1, x1: 100, y1: 100, x2: 900, y2: 100 }
- { id: w2, x1: 900, y1: 100, x2: 900, y2: 500 }
- { id: w3, x1: 900, y1: 500, x2: 100, y2: 500 }
- { id: w4, x1: 100, y1: 500, x2: 100, y2: 100 }
openings:
- id: d1
type: door
x: 300
y: 500
length: 80
angle: 0
entity: binary_sensor.front_door # swings open when the contact opens
activeColor: "#ef5350"
- { id: win1, type: window, x: 600, y: 100, length: 140, angle: 0 }
items:
- { id: i1, entity: light.living_room, x: 240, y: 200, kind: light }
- id: i2
entity: binary_sensor.presence
x: 380
y: 380
kind: binary_sensor
display: iconRipple
rippleColor: "#26c6da"
rippleSize: 120
- id: i3
entity: sensor.living_room_temperature
secondaryEntity: sensor.living_room_humidity
x: 700
y: 380
kind: sensor
showState: true
furniture:
- { id: f1, type: sofa, x: 250, y: 420, w: 170, h: 72, angle: 0 }
texts:
- { id: t1, x: 500, y: 60, text: Living Room, size: 22 }npm install
npm run build # bundles to dist/easy-floorplan-card.js
npm run watch # rebuild on changeReleases are built and attached automatically by GitHub Actions when a GitHub release is published.