Skip to content

Commit 8b1a644

Browse files
squizzer73claude
andcommitted
Initial scaffold: Lit v3 house card with grid editor and runtime display
Phase 1 (grid editor) and Phase 2 (entity binding + runtime) complete. Phase 3 TODOs tracked in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
0 parents  commit 8b1a644

9 files changed

Lines changed: 1460 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Release
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
build-and-release:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
13+
- name: Setup Node
14+
uses: actions/setup-node@v4
15+
with:
16+
node-version: '20'
17+
cache: 'npm'
18+
19+
- name: Install dependencies
20+
run: npm ci
21+
22+
- name: Build
23+
run: npm run build
24+
25+
- name: Upload release asset
26+
uses: softprops/action-gh-release@v1
27+
with:
28+
files: dist/house-card.js
29+
env:
30+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
dist/
3+
mnt/
4+
.DS_Store

CLAUDE.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
npm install # install dependencies
9+
npm run build # production build → dist/house-card.js
10+
npm run dev # watch mode (rebuilds on save)
11+
npm run lint # lint src/**/*.js with eslint
12+
```
13+
14+
After building, copy `dist/house-card.js` to `/config/www/house-card.js` on the Home Assistant instance.
15+
16+
## Architecture
17+
18+
This is a HACS-compatible Lovelace custom card for Home Assistant, built with **Lit 3** and bundled by **Rollup**.
19+
20+
### Source files (`src/`)
21+
22+
- **`src/house-card.js`** — Main display card (`house-card` custom element). Extends `LitElement`. Receives `hass` (HA state object) and `_config` as properties. Renders a grid-based floorplan using percentage-positioned `div` elements; no canvas or SVG. Reads entity states via `this._hass.states[entityId]`. Registers itself in `window.customCards` for HA discovery.
23+
24+
- **`src/house-card-editor.js`** — Visual config editor (`house-card-editor` custom element). Returned by `HouseCard.getConfigElement()`. Manages a three-state internal UI (`floors``grid``room`) with mouse-drag room painting. Fires `config-changed` custom events (bubbling, composed) to communicate config updates back to HA.
25+
26+
### Build output
27+
28+
Rollup bundles both source files plus the Lit library into a single **`dist/house-card.js`** ES module. The root-level `house-card.js` is also a fully bundled artifact (minified).
29+
30+
### Config schema
31+
32+
```
33+
{
34+
type: 'custom:house-card',
35+
title: string,
36+
floors: [{ id, name, cols, rows, rooms: [{ id, name, col, row, width, height, color, entities: { light?, occupancy?, temperature? } }] }]
37+
}
38+
```
39+
40+
Rooms use percentage-based positioning derived from `col/cols` and `row/rows`. The grid canvas uses `padding-bottom: (rows/cols * 100)%` to maintain aspect ratio, so the card scales automatically to any card width with no configuration.
41+
42+
### HA integration points
43+
44+
- `set hass(value)` — called by HA on every state change; triggers re-render
45+
- `setConfig(config)` — called by HA when card config changes
46+
- `static getConfigElement()` — returns editor element
47+
- `static getStubConfig()` — returns a valid starting config with one empty ground floor (shown in card picker)
48+
- Entity states checked: `light.*` (on/off), `binary_sensor.*` (on/off for occupancy), `sensor.*` (numeric state + `unit_of_measurement` attribute for temperature)
49+
50+
### Key implementation patterns
51+
52+
**Config-changed pattern:** The editor fires `config-changed` on every mutation (add/delete floor, add/delete room, update entity). The config object is always deep-cloned before mutation to avoid reference issues. This is the standard HA editor contract — HA catches the event and updates dashboard YAML in real time.
53+
54+
**Overlap detection:** On drag complete, the new rectangle is checked against all existing rooms using AABB intersection before prompting for a name. Overlapping drags are silently discarded.
55+
56+
**Room colour assignment:** Auto-assigned from an 8-colour palette (`ROOM_COLORS` array in `house-card-editor.js`) using modulo on current room count. Stored in the room config object; used for editor overlays.
57+
58+
**HACS release:** `.github/workflows/release.yml` auto-builds and attaches `dist/house-card.js` to any published GitHub release. HACS downloads this single file.
59+
60+
## Build status & roadmap
61+
62+
**Phase 1 (grid editor) — complete.** Floor tabs, grid size picker, click-drag room drawing, overlap detection, room colour assignment, delete rooms.
63+
64+
**Phase 2 (entity binding + runtime display) — complete.** Per-room entity binding in editor; runtime light/occupancy/temperature state display.
65+
66+
**Phase 3 — TODO:**
67+
68+
| Task | Notes |
69+
|------|-------|
70+
| `ha-entity-picker` swap | Replace the three text inputs in the ROOM editor state with `ha-entity-picker`. Import from `../../components/ha-entity-picker.js` — check HA version for correct import path. |
71+
| Touch/pointer events | Grid painter uses `mousedown/mousemove/mouseup`. Add `pointerdown/pointermove/pointerup` equivalents for tablet/touchscreen. |
72+
| Room rename | Add name input to the ROOM editor state — currently the name is fixed at creation time. |
73+
| Side-by-side wallboard layout | Currently always shows tabs; add a mode where floors render side-by-side for large displays. |
74+
| Light group support | Currently expects a single light entity; extend to handle `group` on/off aggregate. |
75+
76+
## Environment
77+
78+
HA instance runs on an M2 Mac Mini via Docker. After building, symlink or copy `dist/house-card.js` to `/config/www/house-card.js`, then add the resource in HA:
79+
80+
```yaml
81+
resources:
82+
- url: /local/house-card.js
83+
type: module
84+
```

README.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# House Card
2+
3+
A visual house floorplan card for Home Assistant. Build a stylised representation of your home using a grid-based room editor, then display entity states — lights, occupancy, temperature — directly on the floorplan.
4+
5+
## Features
6+
7+
- **Grid-based room editor** — draw rectangular rooms on a configurable grid per floor
8+
- **Multi-floor support** — tab-based floor switching on mobile, side-by-side on wallboard
9+
- **Light state** — rooms glow when lights are on
10+
- **Occupancy indicator** — green dot when a motion/occupancy sensor is triggered
11+
- **Temperature overlay** — displays current temperature sensor value in the room
12+
- **Scales to card size** — works on mobile dashboards and large wallboard displays
13+
- **HACS compatible**
14+
15+
## Installation via HACS
16+
17+
1. Open HACS → Frontend
18+
2. Click the three-dot menu → Custom repositories
19+
3. Add `squizzer73/lovelace-house-card` with category `Lovelace`
20+
4. Install and reload
21+
22+
## Manual Installation
23+
24+
Copy `dist/house-card.js` to your `/config/www/` directory, then add to your Lovelace resources:
25+
26+
```yaml
27+
resources:
28+
- url: /local/house-card.js
29+
type: module
30+
```
31+
32+
## Configuration
33+
34+
Add via the Lovelace UI editor (recommended) or manually:
35+
36+
```yaml
37+
type: custom:house-card
38+
title: My House
39+
floors:
40+
- id: ground
41+
name: Ground Floor
42+
cols: 8
43+
rows: 6
44+
rooms:
45+
- id: lounge
46+
name: Lounge
47+
col: 0
48+
row: 0
49+
width: 3
50+
height: 2
51+
color: '#4a90d9'
52+
entities:
53+
light: light.lounge
54+
occupancy: binary_sensor.lounge_motion
55+
temperature: sensor.lounge_temperature
56+
- id: kitchen
57+
name: Kitchen
58+
col: 3
59+
row: 0
60+
width: 2
61+
height: 2
62+
color: '#50c878'
63+
entities:
64+
light: light.kitchen
65+
occupancy: binary_sensor.kitchen_motion
66+
```
67+
68+
## Room Configuration
69+
70+
| Key | Required | Description |
71+
|-----|----------|-------------|
72+
| `id` | Yes | Unique identifier |
73+
| `name` | Yes | Display name |
74+
| `col` | Yes | Starting column (0-indexed) |
75+
| `row` | Yes | Starting row (0-indexed) |
76+
| `width` | Yes | Width in grid cells |
77+
| `height` | Yes | Height in grid cells |
78+
| `color` | No | Room accent colour (hex) |
79+
| `entities.light` | No | Light entity ID |
80+
| `entities.occupancy` | No | Binary sensor for occupancy |
81+
| `entities.temperature` | No | Temperature sensor entity ID |
82+
83+
## Development
84+
85+
```bash
86+
npm install
87+
npm run dev # watch mode
88+
npm run build # production build
89+
```
90+
91+
Output is `dist/house-card.js` — copy to `/config/www/house-card.js`.

hacs.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "House Card",
3+
"description": "A visual house floorplan card for Home Assistant with grid-based room layout and entity state display.",
4+
"render_readme": true,
5+
"domains": [],
6+
"iot_class": "local_polling",
7+
"homeassistant": "2024.1.0"
8+
}

package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "lovelace-house-card",
3+
"type": "module",
4+
"version": "0.1.0",
5+
"description": "Visual house floorplan card for Home Assistant",
6+
"main": "dist/house-card.js",
7+
"scripts": {
8+
"build": "rollup -c rollup.config.js",
9+
"dev": "rollup -c rollup.config.js --watch",
10+
"lint": "eslint src/**/*.js"
11+
},
12+
"devDependencies": {
13+
"@rollup/plugin-node-resolve": "^15.0.0",
14+
"@rollup/plugin-terser": "^0.4.0",
15+
"rollup": "^4.0.0"
16+
},
17+
"dependencies": {
18+
"lit": "^3.0.0"
19+
}
20+
}

rollup.config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import resolve from '@rollup/plugin-node-resolve';
2+
import terser from '@rollup/plugin-terser';
3+
4+
export default {
5+
input: 'src/house-card.js',
6+
output: {
7+
file: 'dist/house-card.js',
8+
format: 'es',
9+
sourcemap: false,
10+
},
11+
plugins: [
12+
resolve(),
13+
terser(),
14+
],
15+
};

0 commit comments

Comments
 (0)