mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
- fix: Split snaps the click to the room's nearest wall instead of the grid, so it works on non-grid-aligned rooms (imported/legacy polygons); the pull is capped (~6 cells) so accidental mid-room clicks stay a miss; splitRoom() still rejects a bad cut. closestPointOnBoundary() + unit test. - docs: README (en+ru) documents Merge/Split/ruler/per-space scale (were undocumented); TESTING.md merge/split rows + fresh self-run; new smokes smoke_merge_split, smoke_split_nonsnap (incl. the capped-pull case). - 73 frontend + 12 backend tests green; module eval + both cards register.
656 lines
47 KiB
Markdown
656 lines
47 KiB
Markdown
# Changelog
|
||
|
||
## v1.21.1 — 2026-07-16 (audit: split snaps to the wall, docs for merge/split)
|
||
- **Fix (found in audit):** Split required each click to land on a grid node, so it
|
||
silently refused rooms whose walls are not grid-aligned (imported or older polygons)
|
||
— the "pick a wall" toast fired no matter where you clicked. The click now snaps to
|
||
the room's nearest wall (`closestPointOnBoundary`) instead of the grid, with the pull
|
||
capped at ~6 grid cells so an accidental click in the middle of a room stays a miss
|
||
rather than becoming a wall point the user never meant; `splitRoom()` still rejects
|
||
a cut that is not a clean wall-to-wall chord. Also makes aiming easier on the fine
|
||
(240-cell) grid.
|
||
- **Docs:** README (en + ru) now documents room Merge, Split, the drawing ruler and the
|
||
per-space scale — these v1.18–v1.21 features were shipped without user-facing docs.
|
||
`docs/TESTING.md` gained merge/split rows and a fresh self-run record.
|
||
- New smokes `demo/smoke_merge_split.mjs` and `smoke_split_nonsnap.mjs`;
|
||
`closestPointOnBoundary` unit-tested. (+1 test: 72 → 73.)
|
||
|
||
## v1.21.0 — 2026-07-16 (merge and split rooms)
|
||
- **Merge** (toolbar "Merge"): click a room, then a neighbour. Only rooms that **share a wall**
|
||
can merge — and that is decided by the result rather than a heuristic: `mergeRooms` unions the
|
||
outlines and accepts the pair only when they collapse into ONE hole-free outline. A corner
|
||
touch, rooms apart, or a union enclosing a hole are refused. A dialog picks which name and
|
||
area survive; the kept room keeps its id, so its label position and devices stay put. The
|
||
dialog warns that the other area is released.
|
||
- **Split** (toolbar "Split"): click the room, then two points on its walls — the chord cuts it
|
||
in two, with the live ruler on the cut. **The bigger part stays the room it was** (name, area,
|
||
devices); the smaller becomes a new room and its dialog asks for name/area. Cancelling the
|
||
dialog leaves the room whole — the cut is applied only on confirm. Cuts that do not run
|
||
wall-to-wall inside the room (ends off the wall, chord leaving a concave room, a chord along a
|
||
wall) are refused.
|
||
- Boolean geometry via **polyclip-ts** (proper ESM + native types; `polygon-clipping` ships named
|
||
types but a default-only ESM build, which breaks either tsc or the runtime). Verified against
|
||
the real plan, where neighbouring walls overlap collinearly instead of matching exactly —
|
||
the case a hand-rolled union gets wrong. Bundle: 151 KB → 202 KB.
|
||
- New pure helpers: `polygonArea`, `mergeRooms`, `splitRoom`. (+5 tests: 67 → 72.)
|
||
|
||
## v1.20.0 — 2026-07-16 (rooms may not overlap)
|
||
- **A click strictly inside an existing room is refused** while drawing (toast names the room).
|
||
Being *on* a wall stays legal — neighbouring rooms share walls, and real walls overlap
|
||
collinearly rather than match exactly, so new vertices land on existing outlines mid-span
|
||
all the time. `pointStrictlyInside` excludes the boundary explicitly (ray casting alone is
|
||
unreliable exactly on an edge).
|
||
- **Closing an outline that overlaps an existing room is refused** — vertex checks alone are not
|
||
enough: an outline drawn *around* a room has every vertex outside it. The outline stays open
|
||
so it can be corrected. Nesting one room inside another counts as an overlap.
|
||
- New pure geometry in logic.ts: `roomPoly`, `pointOnBoundary`, `pointStrictlyInside`,
|
||
`segmentsProperlyCross` (touching/collinear is deliberately not a crossing), `roomsOverlap`
|
||
(edge crossings + containment probe, which also catches duplicate outlines). (+4 tests: 63 → 67.)
|
||
|
||
## v1.19.0 — 2026-07-16 (a line is never a thing of its own)
|
||
**Model change.** A wall can only exist as an edge of a closed room. Consequences:
|
||
- **Walls are derived from room outlines** (`roomEdges` in logic.ts), not stored. A wall
|
||
shared by two rooms is emitted once, so **deleting a room keeps the borders its neighbours
|
||
still contribute** — and drops the walls nobody else uses. This falls out of the model
|
||
instead of needing bookkeeping.
|
||
- **An abandoned outline leaves nothing behind.** Previously every click pair was written to
|
||
`space.segments` immediately, so a contour you never closed left orphan lines on the plan.
|
||
Now nothing is persisted until the room is saved.
|
||
- **The "Erase line" tool is gone** — there is no standalone line to erase. Mis-clicks are
|
||
undone with Esc / Ctrl+Z as before.
|
||
- **`space.segments` is dropped on every save** (legacy configs shed it on first write).
|
||
Validation still tolerates the field so a stale browser tab cannot fail a save; diagnostics
|
||
no longer reports it. Lines that belonged to no room disappear on upgrade — by design.
|
||
- Dead code removed: `_addSegment`, `_removeSegmentByKey`, `_distToSeg`, `_pathSegs`, `_segKey`.
|
||
`segKey(a, b, prec)` gained a precision argument (normalized coords need more than render
|
||
units). (+2 tests: 61 → 63.)
|
||
|
||
## v1.18.1 — 2026-07-16 (fix: the drawing ruler was invisible)
|
||
- Fix on top of v1.18.0: the length badge never showed up while drawing. It was rendered
|
||
inside `.devlayer`, and `.stage.markup .devlayer { display: none }` hides that whole layer
|
||
in markup mode (so icons do not get in the way) — the badge was in the DOM but invisible.
|
||
It now lives in its own `.measurelayer` (absolute, `pointer-events: none`), which markup
|
||
mode does not hide. Verified visually on a real drawn segment ("3.60 m" on screen).
|
||
- Testing lesson (see docs/TESTING.md): asserting on `textContent` is not enough — a DOM
|
||
query passes on elements hidden by an ancestor. Check `offsetParent`/rect or look at a
|
||
screenshot.
|
||
|
||
## v1.18.0 — 2026-07-14 (live measurements while drawing rooms + per-space scale)
|
||
- **Ruler while drawing.** In room-markup "draw" mode, a badge follows the cursor showing the
|
||
length of the current segment (last placed vertex → cursor). Units come from the HA unit
|
||
system: metric → metres ("1.25 m"), imperial → feet+inches ("4′ 1″").
|
||
- **Per-space scale.** New "Scale (grid cell size)" field in the space dialog — cm represented
|
||
by one grid cell (default **5 cm**, i.e. 240 cells ≈ 12 m). Stored as `space.cell_cm`; each
|
||
plan can have its own real-world size.
|
||
- Pure helpers `segmentCm` / `formatLength` in logic.ts (unit-tested); `_fmtLen` +
|
||
`_renderMeasureLabel` in the card; `.measurelabel` style; i18n `space.scale_label`/`scale_unit`.
|
||
(+3 tests: 58 → 61 frontend.)
|
||
|
||
## v1.17.2 — 2026-07-11 (humidity badge: gate on the sensor, not the icon)
|
||
- Fix on top of v1.17.1: the humidity `%` badge is now shown whenever the marker's primary
|
||
entity is a humidity sensor (`device_class: humidity`), regardless of the resolved icon.
|
||
Previously it required the `mdi:water-percent` icon, so a humidity sensor whose name matched
|
||
another icon rule (e.g. a "Myheat Влажность …" sensor → boiler icon) showed no value.
|
||
Verified live (45.2 → 45%). (+1 test: 57 → 58.)
|
||
|
||
## v1.17.1 — 2026-07-11 (humidity value next to the icon, like temperature)
|
||
- **Humidity sensors now show their value (%) next to the icon**, mirroring the temperature
|
||
badge. Any marker resolved to the humidity icon (`mdi:water-percent`) — a humidity device or a
|
||
humidity entity placed on its own (v1.17.0) — gets an integer `%` badge and the value in its
|
||
tooltip. New `isHumEntity`/`humFor` (diagnostic entities excluded), `DevItem.hum`, `.hval`
|
||
badge, gated by the same "sensor values" option as temperature. (+2 tests: 55 → 57.)
|
||
|
||
## v1.17.0 — 2026-07-11 (place individual entities, not just whole devices — issue #1)
|
||
- **You can now put a single entity on the plan as its own icon** — e.g. a climate sensor
|
||
exposes temperature AND humidity; add the device (shows temperature) and separately add the
|
||
humidity entity as a second icon. In the "add device" dialog, start typing in the binding
|
||
search and individual entities now appear alongside devices/helpers (surfaced only while
|
||
searching, so the default list stays clean); the sub-label shows the domain and parent device.
|
||
- Entity markers now get a **sensible auto icon** (name rules → `device_class` → chip) instead of
|
||
the generic shape, and a **temperature value** when the entity is a thermometer/air-monitor.
|
||
The `entity:<eid>` binding already existed (used by helpers/light groups); this exposes it for
|
||
any entity. (+1 test: 54 → 55.)
|
||
|
||
## v1.16.2 — 2026-07-11 (docs+log: correct card resource URL — fixes "Custom element doesn't exist")
|
||
- **Support issue #2**: users adding a Lovelace resource that points at the on-disk path
|
||
`/custom_components/houseplan/frontend/houseplan-card.js` get a `text/plain` MIME error and
|
||
"Custom element doesn't exist: houseplan-card" — HA does not serve `custom_components/` over
|
||
HTTP. The integration serves the card at **`/houseplan_files/houseplan-card.js`** (verified:
|
||
`200 text/javascript`) and auto-registers it as a Lovelace resource in storage mode.
|
||
- README (en+ru) now documents the correct URL and the common mistake, incl. a YAML-mode
|
||
`resources:` snippet.
|
||
- On setup the integration logs (INFO) the exact served URL and, when Lovelace resources are
|
||
YAML-managed, how to add it manually — so the fix is discoverable from the logs.
|
||
|
||
## v1.16.1 — 2026-07-08 (space-card shows room fills as configured)
|
||
- **The static `houseplan-space-card` now renders room fills exactly as configured on the
|
||
full card** (temperature / signal / lights coloring), as a snapshot of the states passed
|
||
in via `hass` — reverting the v1.16.0 omission. The card still does not subscribe to state
|
||
changes itself and stays non-interactive (`pointer-events:none`); fills refresh when HA
|
||
hands the card an updated `hass`. Added shared `areaLqi()` in devices.ts.
|
||
|
||
## v1.16.0 — 2026-07-08 (new: houseplan-space-card + deep-link)
|
||
- **New second card `custom:houseplan-space-card`** — a READ-ONLY, static schematic of a
|
||
single space, embeddable on any dashboard. Draws the configured plan + room borders/names +
|
||
device markers at their saved positions, with **no interactivity** (the schematic layer is
|
||
`pointer-events:none` — no clicks/hover/tooltips/drag/more-info) and **no live states**
|
||
(no state subscription, no status/temperature fills). A footer button opens the space in the
|
||
full component via a **deep-link** (`#space=<id>`). Config: `space` (required), `title`,
|
||
`show_button`, `button_label`, `button_target`, `aspect_ratio`, `icon_size`; unknown space → a
|
||
tidy error card. GUI editor with a space dropdown from the integration config.
|
||
- **Deep-link in the full card**: on load it reads `#space=<id>` (valid id wins over
|
||
`default_floor`) and listens to `hashchange`, without blocking manual space switching.
|
||
- **Shared rendering**: pure geometry in `space-geometry.ts` (unit-tested), the static drawer in
|
||
`space-render.ts`, and a **module-level config cache** (`config-store.ts`, rev-keyed, seeded from
|
||
the full card's localStorage snapshot, invalidated on `houseplan_config_updated`) so N embedded
|
||
cards on one board share a single WS request. Both cards ship in the one bundle.
|
||
- Tests: 48 → 54 frontend (space geometry) + demo smokes `smoke_space_card` and `smoke_deeplink`.
|
||
- Note: status/temperature fills are intentionally omitted from the static card (they are live);
|
||
it shows configured room borders/names + neutral icons. Marker/position edits reflect after the
|
||
config event or a reload.
|
||
|
||
## v1.15.6 — 2026-07-08 (room hover also reveals the border)
|
||
- Hovering a room now **shows its outline** even when borders are turned off. The stroke
|
||
colour (`--room-stroke`) is now always set to the room colour and only hidden via
|
||
`--room-stroke-op` — so the existing `stroke-opacity: 1` on hover reveals a crisp border
|
||
(previously the stroke was `transparent` when borders were off, so hover showed nothing).
|
||
|
||
## v1.15.5 — 2026-07-08 (fix: room hover was always grey even when filled)
|
||
- Room hover now **darkens the current fill** for filled rooms and only greys **unfilled**
|
||
ones (as intended since v1.15.1). The legacy `.room.overlay:hover` / `.room.yard:hover`
|
||
grey rules were still matching styled rooms and, being applied to `fill` directly, beat
|
||
the `--room-fill` variable — so a temperature/light/zigbee-filled room turned grey on
|
||
hover. Scoped those legacy rules with `:not(.styled)`; styled rooms are now governed only
|
||
by `.styled.filled:hover` (brightness 0.78) and `.styled:not(.filled):hover` (grey).
|
||
|
||
## v1.15.4 — 2026-07-08 (fix: device icon vertical centering — proper root cause)
|
||
- **Root cause of the off-centre icon, found in the live app** (the demo stub hid it):
|
||
HA's real `<ha-icon>` host is `display:block` with a large inherited `line-height`
|
||
(~22 px for a ~12 px glyph), so the SVG sat ~1.8 px **below** the badge centre.
|
||
Fix: `.dev ha-icon` is now a zero-line-height flex box — the glyph centres exactly.
|
||
- Reverted the v1.15.3 `box-sizing: border-box` (it shrank the badge by 2 px and made the
|
||
vertical offset *more* visible — "worse"). The 1 px anchor drift is instead corrected by
|
||
the centering margin (`-(size/2 + 1px)`), keeping the original badge size.
|
||
- Verified in the real dashboard: anchor offset 0, glyph offset 0, badge size unchanged.
|
||
- Demo `ha-icon` stub made faithful to HA (block + line-height) so `smoke_icon_center`
|
||
now actually reproduces and guards this; the smoke also asserts glyph-in-badge centering.
|
||
|
||
## v1.15.3 — 2026-07-08 (fix: device icon 1px off its anchor point)
|
||
- Device icon badges were sitting **1 px down-and-right of their true point**: `.dev`
|
||
used the default `content-box`, so the 1 px border made the rendered square 2 px
|
||
wider than the width the centering margin assumed. Added `box-sizing: border-box` —
|
||
the badge centre now lands exactly on the device coordinate (verified in the demo:
|
||
anchor offset 1 px → 0). The glyph itself was already centred within the square.
|
||
|
||
## v1.15.2 — 2026-07-08 (fix: room average temperature counted non-thermometers)
|
||
- **Average room temperature now uses only devices the card treats as thermometers**
|
||
(`mdi:thermometer` / `mdi:air-filter`). Previously `areaTemp` swept every device in the
|
||
area through `tempFor`, so fridges, TRV heads and smart plugs leaked their readings into
|
||
the average (e.g. a 8.3 °C valve/appliance temperature dragging a room down).
|
||
- **`isTempEntity` now excludes chip/diagnostic temperatures**: `*_device_temperature`
|
||
sensors and any entity in the `config`/`diagnostic` category are no longer treated as a
|
||
room temperature — so even a genuine thermometer no longer reports its chip temperature.
|
||
- Affects the temperature room fill and the room tooltip average. (+3 frontend tests: 46 → 48.)
|
||
|
||
## v1.15.1 — 2026-07-06 (display-settings UX round from live usage)
|
||
- **Comfort-bounds input hardening**: clearing a temperature field can no longer
|
||
collapse a bound to 0 (`Number('') === 0` — this silently turned "comfort from
|
||
25" into a 0–25 range after the auto-swap, showing green at 24°). Inputs now
|
||
parse with `parseFloat` + `isFinite` guard and the save path falls back to the
|
||
defaults for non-finite values.
|
||
- Room tooltip now shows the **average room temperature** (what the temperature
|
||
fill actually uses — averages every thermometer in the area, including TRVs).
|
||
- **Hover no longer recolors rooms blue**: filled rooms darken their current fill
|
||
(`brightness(0.78)`), unfilled rooms get a light grey tint.
|
||
- Fill mode selector is a **radio group** with short labels (no color legend);
|
||
the comfort bounds sit compactly inline on the temperature row (56 px inputs).
|
||
- The space dialog is wider (500 px) — the settings no longer feel cramped.
|
||
|
||
## v1.15.0 — 2026-07-06 (temperature room fill)
|
||
- New room fill mode **"Temperature"**: light blue below the comfort range, green
|
||
inside it, warm yellow above. The comfort bounds (default 20–25 °C) are editable
|
||
right in the space dialog and appear only when the mode is selected; bounds
|
||
entered in the wrong order are swapped automatically. A room's temperature is
|
||
the average of its devices' temperature readings; rooms without a reading stay
|
||
unfilled. (+`areaTemp` helper, 3 new frontend tests, backend schema fields.)
|
||
|
||
## v1.14.0 — 2026-07-06 (per-space display settings, hand-drawn spaces, testing checklist)
|
||
- **Per-space "Display" settings** (space dialog): always-visible room borders,
|
||
room name labels, a border/name color picker with an opacity slider, and a room
|
||
fill mode — none / by zigbee signal (red→green) / by lights (yellow = something
|
||
is on, grey = all lights off; rooms without lights stay unfilled).
|
||
- **Room name labels are draggable** like device icons; positions persist server-side
|
||
(layout keys `rl_<roomId>`), defaults to the room centre; hidden in markup mode.
|
||
- **Hand-drawn spaces**: the space dialog got a "No image — I'll outline rooms by
|
||
hand" option with a canvas orientation choice (landscape/portrait/square). Such
|
||
spaces default to visible borders and names; switching an existing space to this
|
||
mode detaches its background image. The plan image is no longer mandatory.
|
||
- Backend: explicit validation schema for the new per-space settings (+test).
|
||
- **`demo/` harness moved into the repository** (synthetic home, host page, capture
|
||
and smoke scripts, icon-map generator) — public materials and smoke tests no
|
||
longer depend on a perishable sandbox.
|
||
- **`docs/TESTING.md`**: a comprehensive manual-testing checklist (environments
|
||
matrix, every feature, edge cases); policy — updated in the same commit as any
|
||
functional change. The first self-run found and fixed two bugs:
|
||
`plan_url` not detached on image→draw switch, and a `_stateClass` crash on
|
||
state objects without `entity_id`.
|
||
- Tests: 43 frontend + 11 pure backend; new smokes `smoke_space_settings` and
|
||
`smoke_edge_cases` (empty install, XSS names, legacy layout entries, 150-device perf).
|
||
|
||
## v1.13.3 — 2026-07-06 (privacy: drop legacy real-house plan sources)
|
||
- Removed the legacy `assets/` directory (real floor-plan sources from the pre-v1.3
|
||
bundled-data era). Nothing in the build referenced it; instance data lives in
|
||
server-side config. Note: the files remain in old git history and release archives.
|
||
|
||
## v1.13.2 — 2026-07-05 (audit round 3: fixes + buildDevices test suite)
|
||
- **buildDevices finally has a direct unit-test suite** (12 tests on a fake hass):
|
||
area filtering, curation incl. show-all, duplicate numbering, light-group folding
|
||
and its `group_lights=false` inverse, marker claim/metadata/hidden/virtual/entity
|
||
paths, custom icon rules + the deliberate lock override, device_class fallback,
|
||
primary-entity priority, LQI/temperature extraction. Frontend tests: 28 → 41.
|
||
- Fix: `t()` now substitutes **every** occurrence of a placeholder (extracted as the
|
||
pure `subst()` helper with a regression test).
|
||
- Fix: `_saveConfigNow` refreshes the local config on a rev conflict before
|
||
rethrowing — a retry no longer hits the same conflict (the debounced path already
|
||
did this; the immediate path did not).
|
||
- Fix: `pointercancel` on a device icon clears the long-press timer — no phantom
|
||
info card after an aborted touch gesture.
|
||
- Repairs check moved to `repairs.py` and now **re-runs after every config save**,
|
||
so a missing/restored plan file is reflected in the Repairs UI without a restart.
|
||
- Documented the deliberate `mdi:lock` override in `devices.ts` (wins over custom
|
||
rules — a mislabeled lock icon is safety-relevant confusion).
|
||
- Test infra: `tsconfig.test.json` also compiles `devices.ts`/`types.ts`;
|
||
`scripts/fix-test-build.mjs` appends `.js` to tsc's extensionless ESM imports.
|
||
|
||
## v1.13.1 — 2026-07-05 (distribution materials)
|
||
- **Demo GIF** in the README — recorded on a fully synthetic demo home (no real
|
||
floor plans): live states, tap-to-toggle, drag, zoom, info card, space tabs.
|
||
- GitHub issue templates (bug report with diagnostics hint, feature request),
|
||
`CONTRIBUTING.md` (5-minute setup, ground rules, architecture pointers),
|
||
Discussions enabled.
|
||
|
||
## v1.13.0 — 2026-07-05 (universality: floors import, icon rules, tap actions)
|
||
- **Floors import wizard**: on first run, if the HA registry has floors, the card
|
||
offers to create a space per floor — names prefilled, the (mandatory) plan image is
|
||
requested step by step, any floor can be skipped; after the last one the room-markup
|
||
mode opens automatically. No floors / old HA → the old single-dialog onboarding.
|
||
- **Editable icon rules**: the built-in "name pattern → MDI icon" rules are now data
|
||
(`settings.icon_rules`) with an in-card editor (⬡ in the header): reorder, delete,
|
||
add, live test field, invalid-regex highlighting, one-click reset to the bilingual
|
||
(EN/RU) defaults. Fallback chain: rules → entity `device_class` → generic chip.
|
||
Invalid user regexes are skipped safely.
|
||
- **Tap actions**: `tap_action` card option (`info` default | `more-info` | `toggle`)
|
||
plus a per-device override in the device dialog. Safety model: a card-wide toggle
|
||
only affects lights/switches/fans/humidifiers; covers/valves need a conscious
|
||
per-device toggle; locks and alarm panels never toggle from the plan. A long press
|
||
(600 ms) always opens the info card.
|
||
- **i18n dictionaries moved to JSON** (`src/i18n/{en,ru}.json`) — new languages can be
|
||
contributed without touching TypeScript; tests enforce key and placeholder parity.
|
||
- Light-theme pass: hardcoded dark badge backgrounds replaced with theme variables.
|
||
- Tests: 28 frontend (was 15) — tap-action resolver incl. security cases, icon-rule
|
||
compilation/overrides/device-class fallback, floors sorting, i18n parity.
|
||
|
||
## v1.12.0 — 2026-07-05 (Quality Scale: Bronze + selected Silver/Gold)
|
||
Backend brought to Integration Quality Scale patterns (custom integrations are not
|
||
formally graded; progress is tracked in `custom_components/houseplan/quality_scale.yaml`):
|
||
|
||
- **`entry.runtime_data`** (typed `HouseplanData`: both stores + the write lock) replaces
|
||
`hass.data[DOMAIN]` for entry data; WS handlers resolve it per call and answer
|
||
`not_ready` while no entry is loaded. New `store.py` common module.
|
||
- **test-before-setup**: storage readability is verified in `async_setup_entry`
|
||
(`ConfigEntryNotReady` on failure). **Unloading** is supported; WS commands and
|
||
static paths are global by design (documented).
|
||
- **`single_config_entry: true`** in the manifest replaces the manual flow check.
|
||
- **Store versioning**: stores now carry `minor_version` and a migration hook
|
||
(`HouseplanStore._async_migrate_func`) — schema changes get a single upgrade path.
|
||
- **Diagnostics** (`diagnostics.py`): redacted dump (options, rev, per-space stats,
|
||
markers with personal fields redacted, layout size).
|
||
- **Repairs**: a missing plan file raises a repair issue (`broken_plan`) with
|
||
en/ru translations; issues clear automatically when resolved.
|
||
- **System health** (`system_health.py`): rev, spaces/rooms/markers/layout counters.
|
||
- **Uninstall cleanup**: `async_remove_entry` deletes our Lovelace resource entry.
|
||
- **Tests**: config flow, WS API (layout ops, rev conflict, not_ready, plan upload
|
||
validation), HTTP upload (ok/bad ext/traversal) on `pytest-homeassistant-custom-component`
|
||
— run in CI on Python 3.13; pure validation tests still run anywhere.
|
||
- `strings.json` added; translations updated.
|
||
|
||
## v1.11.2 — 2026-07-05 (device dialog: usable Description height)
|
||
- The Description textarea in the device edit dialog was squeezed to ~2 lines by
|
||
the dialog body's flex column. Now `min-height: 92px`, `flex-shrink: 0`, `rows=4`;
|
||
still resizable vertically.
|
||
|
||
## v1.11.1 — 2026-07-05 (brand images shipped inside the integration)
|
||
- Brand icon and logo (256/512, transparent background) now live in
|
||
`custom_components/houseplan/brand/` — the native mechanism for custom
|
||
integrations since Home Assistant 2026.3 (served via `/api/brands/...`,
|
||
local images take priority over the brands CDN). The former root `brand/`
|
||
directory is gone, and no home-assistant/brands PR is needed (theirs bot
|
||
closes such PRs as obsolete).
|
||
|
||
## v1.11.0 — 2026-07-05 (full English translation + UI localization)
|
||
- **UI localization (en/ru)**: every card string moved to `src/i18n.ts` dictionaries.
|
||
The language follows the HA user profile (`hass.locale`) automatically; a new
|
||
`language: en|ru` card option forces it. The GUI editor got the option and its own
|
||
localized labels. Generated device names (light group, unnamed, virtual device)
|
||
are localized via a `loc` callback in `BuildCtx`.
|
||
- **English-only codebase**: all comments, docstrings, section banners, JSDoc, test
|
||
names, backend WS/HTTP error messages and log lines translated to English.
|
||
Russian remains only where it is functional or content: the `ru` i18n dictionary,
|
||
`translations/ru.json` (config flow), `iconFor` regex patterns matching Russian
|
||
device names (with their test fixtures), and the Russian documentation copy.
|
||
- **Docs**: README is now English-first with a full Russian copy in `README.ru.md`;
|
||
`docs/ARCHITECTURE.md`, `DEVELOPMENT.md`, `ROADMAP.md` and the entire CHANGELOG
|
||
history translated to English. `translations/en.json` had Russian strings — fixed.
|
||
- Removed obsolete `RELEASE_NOTES_v1.9.3.md` and `scripts_publish.sh` (publication
|
||
is done; the repo lives at github.com/Matysh/houseplan-card).
|
||
|
||
## v1.10.0 — 2026-07-05 (audit: write races, XSS, modularity)
|
||
**Backend**
|
||
- **Write race eliminated**: all load→modify→save cycles (`layout/set|update|delete`,
|
||
`config/set`) are serialized by a shared `asyncio.Lock` — concurrent WS calls no longer lose
|
||
changes, and the `expected_rev` check became atomic.
|
||
- New WS command `houseplan/layout/delete` — cleans up a position when a marker is deleted
|
||
(previously orphans accumulated in `.storage/houseplan.layout`).
|
||
- Removed the dead WS command `houseplan/file/set` — files are uploaded over HTTP only
|
||
(`/api/houseplan/upload`), as the card already did.
|
||
- HTTP upload: the 25 MB limit is enforced WHILE reading the multipart stream (abort at the limit),
|
||
not after reading the whole file into memory; file `stat()` moved into the executor.
|
||
- `request.app[KEY_HASS]` instead of the deprecated `request.app["hass"]` (with a fallback for older HA).
|
||
|
||
**Frontend**
|
||
- **The god-component was broken up**: `houseplan-card.ts` 3023 → ~1990 lines.
|
||
Styles → `styles.ts`; types → `types.ts`; building devices from the HA registries
|
||
(curation, light groups, markers, LQI/temperature) → `devices.ts` (pure functions).
|
||
- **Last-writer-wins in the layout eliminated**: `_saveMarker` writes the position pointwise
|
||
(`layout/update`), not the whole layout (`layout/set`) — it no longer wipes positions from other
|
||
windows (the regression class of the v1.4.4 incident). Rebinding/deletion clean up the old position.
|
||
- **XSS closed**: marker `link` and `pdfs[].url` go through `safeUrl()` (only http(s)
|
||
and relative paths; `javascript:`/`data:` are rejected). +12 tests.
|
||
- File uploads via `hass.fetchWithAuth` (auto-refresh of an expired token),
|
||
fallback to the raw token.
|
||
- **The dacha hardcode removed from the GUI editor**: the "default space" list is built
|
||
from the server config (WS `config/get`), not the baked-in f1/f2/yard.
|
||
- `rules.ts`: removed the dead export `GROUP_TITLES`; the `iconFor` regexes are precompiled.
|
||
- A single `_errText()` in all error handlers (never "[object Object]").
|
||
|
||
## v1.9.3 — 2026-07-05 (audit + refactoring + tests)
|
||
- The pure functions `fitView` (viewport contain rectangle) and `declump` (icon push-apart)
|
||
were extracted from the card into `logic.ts` — covered by unit tests (was 9 → now 14 tests).
|
||
- `_lqiFor` and `_roomLqi` now use the shared `averageLqi` (duplicate averaging removed).
|
||
- Removed dead code left after dropping the separate edit mode: the `_edit` field (written but
|
||
never read), the methods `_renderEditbar` and `_applyXY` (never called).
|
||
- No behavior change — only structure and test coverage. Verified: tsc, build,
|
||
14 frontend tests + 10 backend tests green, headless smoke test (render/zoom/devices/declump) clean.
|
||
|
||
## v1.9.2 — 2026-07-05 (grid twice as fine)
|
||
- The markup/snapping grid step was halved: `GRID_N` 120 → 240. Device positions and room
|
||
outlines are NOT recomputed and do not shift: coordinates are stored as normalized fractions and snap
|
||
to grid nodes, and the old nodes (multiples of 1/120) are an exact subset of the new ones (multiples of 1/240).
|
||
Verified on the live layout (65 positions — all already on the new grid). Dragging now
|
||
allows more precise positioning.
|
||
|
||
## v1.9.1 — 2026-07-05 (Zigbee signal: reading from the attribute + ZHA)
|
||
- The signal level is now taken not only from the dedicated `*_linkquality` sensor (Z2M), but also:
|
||
from `*_lqi` sensors (ZHA), and from the `linkquality`/`lqi` ATTRIBUTE on any entity of the device.
|
||
This covers devices whose dedicated signal sensor is disabled but the value is available in an attribute.
|
||
- NOTE: if a device's `*_linkquality` sensor is disabled (disabled_by=integration) AND the value
|
||
is not published anywhere as an attribute — the signal cannot be shown; enable the sensor in the HA registry.
|
||
|
||
## v1.9.0 — 2026-07-05 (UX: always-on dragging, declumping, show all, responsive)
|
||
- **Icon dragging is available at all times** — the separate "edit mode" is gone (the ✥ button removed).
|
||
Clicking an icon opens the device card (metadata editing — via the "Edit" button
|
||
inside it), dragging works at any moment. `_pointerDown` no longer requires `_edit`.
|
||
- **Icons no longer clump together** (`_declump`): after the automatic grid layout a push-apart pass
|
||
runs within the room so that icons do not overlap each other.
|
||
- **Positions are pinned when a room is saved**: the auto-positions of the area's devices are written
|
||
into the layout once, so icons do not get reshuffled when the order in the HA registry changes.
|
||
- **Duplicates are numbered instead of hidden**: devices with the same "name|area" get a suffix
|
||
(" 2", " 3"…) instead of silently disappearing.
|
||
- **The 👁 "Show all devices" button** in the header: temporarily disables curation and shows
|
||
all devices of the area (bridges, service records, duplicates) — in case a needed one is hidden. The `show_all`
|
||
flag in the config settings.
|
||
- **Markup continuation hint**: after saving a room a toast shows the room counter and
|
||
suggests outlining the next one or leaving markup mode.
|
||
- **Responsive header**: the toolbar wraps and shrinks on narrow screens (media query ≤620px).
|
||
|
||
## v1.8.4 — 2026-07-04 (onboarding: required fields + user guidance)
|
||
- Initial setup brought to the target scenario: install → space dialog →
|
||
rooms → auto device icons.
|
||
- **The background is mandatory** when creating a space: "Save" stays disabled without an uploaded
|
||
plan (previously only the name was required — an empty space could be created).
|
||
- **The room area is mandatory**, with an explicit escape hatch: the main "Save" requires binding to an HA area;
|
||
for decorative rooms without devices (hall, sauna) there is a separate "No area" button
|
||
(only a name is needed). Previously saving was possible with "name OR area".
|
||
- **User guidance:** with an empty server config the space dialog opens
|
||
automatically; after the first space is added the card enters room markup mode
|
||
by itself with an "outline the rooms" hint.
|
||
- When a room with an area is saved, the icons of that area's devices are auto-placed inside the outline
|
||
(the curated set: without bridges/service records/duplicates/individual lamps from groups), and a toast shows
|
||
how many devices were added.
|
||
|
||
## v1.8.3 — 2026-07-04 (instant start: config cache, data in the background)
|
||
- Icons and the plan appeared with a delay — the card waited for the server response (WS `config/get`
|
||
+ connection warm-up retries), and until then the space model was empty.
|
||
- A "stale-while-revalidate" cache was introduced: a snapshot of the server config + rev + layout
|
||
is saved in `localStorage['houseplan_card_cfg_v1']`. In `setConfig` (synchronously, before
|
||
hass arrives) it is restored — the plan and icons are drawn immediately from the cache (verified: setConfig
|
||
populates the model in 0.2 ms without the server), while fresh data loads in the background and updates the
|
||
card. Live states/temperatures/signal come in as hass warms up.
|
||
- The cache is refreshed after every successful load, live resync and icon position save,
|
||
so on the next open the current snapshot is visible. When the server is unavailable the
|
||
last known plan is shown instead of the empty onboarding.
|
||
|
||
## v1.8.2 — 2026-07-04 (vector-crisp zoom via viewBox + full-width stage)
|
||
- Zoom moved from CSS `transform: scale()` to manipulating the SVG `viewBox`. Previously the layer
|
||
was rasterized and then scaled — everything "blurred" when zooming in. Now the browser
|
||
redraws the vector at the target resolution: icons, room labels, lines, hatching and the
|
||
vector background remain crisp at any scale (verified at 274%).
|
||
- The icon layer (HTML) is positioned and scaled by the same `view`, not a global
|
||
transform — the icons are crisp and grow together with the plan.
|
||
- The stage now occupies the full viewport width (a "contain" model by the stage aspect): when
|
||
zoomed in the content fills the width without black bars on the sides. Fully zoomed out the
|
||
whole plan is visible (both width and height), the margins around it are filled with the card
|
||
background, not black.
|
||
- All coordinate math (markup click `_svgPoint`, icon drag, pan, pinch) was rewritten
|
||
in unified `view` coordinates — editing is correct at any zoom. A ResizeObserver on the stage
|
||
recomputes the `view` when dimensions change (sidebar, rotation). Pan/zoom are constrained to `fit`.
|
||
|
||
## v1.8.1 — 2026-07-04 (fit-to-viewport + per-space zoom persistence)
|
||
- Zooming out to the full view: the lower zoom limit = 100% coincides with the fully
|
||
visible plan. `.stage` is now constrained by the viewport height
|
||
(`width:min(100%, calc((100dvh − 118px) × aspect))`, `max-width:100%`, centering),
|
||
so on reset/zoom-out the whole layout is visible both in width and height without clipping.
|
||
- The zoom level is remembered per space and locally per device
|
||
(`localStorage['houseplan_card_zoom_v1']`, `_zoomBySpace`): switching floors
|
||
restores each floor's zoom; on return the pan is centered and constrained.
|
||
- Verified on live HA: f1→196%, f2→140%, the yard untouched — after switching around the
|
||
values are restored; reset gives 100% and the full plan (874≤992 px in height).
|
||
|
||
|
||
## v1.8.0 — 2026-07-04 (plan zooming)
|
||
- Zoom and pan of the layout: mouse wheel (towards the cursor), two-finger pinch on a touch screen,
|
||
one-finger background drag when zoomed in, −/reset/+ buttons in the toolbar, a zoom badge.
|
||
- Implemented via a CSS transform on `.zoomwrap` (translate+scale); the coordinate math
|
||
(markup clicks, icon drag, tooltips) was recomputed with zoom/pan in mind — editing
|
||
works when zoomed in too. Pan is constrained so the content always covers the stage. Zoom 100–800%.
|
||
- `.stage`: overflow hidden + touch-action none (custom gestures instead of the browser's).
|
||
|
||
## v1.7.4 — 2026-07-04 (PDF over HTTP instead of WebSocket)
|
||
- THE ROOT CAUSE of being unable to upload a PDF: the file was sent as base64 in a single WebSocket
|
||
message, and WS has a message-size limit — a real manual (>2–3 MB) exceeded it, the connection dropped
|
||
("Connection lost"), which both failed the upload and closed the dialog (WS drop → reconnect
|
||
→ re-render). Confirmed by test: 1 MB over WS is fine, 3 MB → Connection lost.
|
||
- Manual files are now uploaded through the HTTP endpoint `POST /api/houseplan/upload` (multipart,
|
||
HomeAssistantView, requires_auth, admin_only check) — like media in HA itself. Verified:
|
||
3 MB (182 ms) and 10 MB (446 ms) upload without a hitch.
|
||
- Readable errors everywhere (`_errText`): no more "[object Object]"; clear texts for
|
||
too_large / bad_ext / unauthorized.
|
||
- The WS command houseplan/file/set is kept for compatibility, but the frontend uses HTTP.
|
||
|
||
|
||
## v1.7.3 — 2026-07-04 (device dialog fixes)
|
||
- PDF manuals failed to upload: the manual base64 (`btoa(String.fromCharCode(...subarray))`) crashed
|
||
on real files (RangeError when spreading large arrays), and when the dialog was closed during an
|
||
await it threw a null access. Replaced with `FileReader.readAsDataURL` (reliable for any size);
|
||
the results are accumulated and added only if the dialog is still open (no exceptions).
|
||
- The device/space/room edit dialog closed on an outside click and "by itself"
|
||
(an accidental click on the background overlay, including after closing the system file picker). Removed
|
||
background-click closing for editor dialogs — only explicit Cancel/Save/Esc.
|
||
The info card (read-only) still closes on an outside click.
|
||
- Verified on live HA: PDFs attach; a background click and dialog data refresh do not close the dialog.
|
||
|
||
## v1.7.2 — 2026-07-04 (fix for the mobile "configuration error")
|
||
- THE CAUSE: the card was hooked up only via `add_extra_js_url` (extra_module_url), whose loading the
|
||
frontend does NOT wait for — on a cold start of the mobile app the dashboard was drawn
|
||
before the element was registered → "configuration error". (All working HACS cards are Lovelace resources.)
|
||
- FIX 1: the integration registers the card as a **Lovelace resource** (module) — the frontend waits for
|
||
those before rendering. Idempotent (updates the URL on a version change, no duplicates); a fallback to
|
||
extra_module_url for Lovelace YAML mode. `_register_lovelace_resource` in __init__.py.
|
||
- FIX 2: `_loadFromServer` retries (up to 8 attempts on hass updates) — if hass arrived before
|
||
WS readiness, the card does not get stuck on the onboarding but waits for the config.
|
||
- Verified: the houseplan-card.js resource is registered, extra_module_url is empty (a single clean
|
||
module), the card renders the plan on a fresh tab without the error.
|
||
|
||
## v1.7.0 — 2026-07-04 (audit + refactoring + tests)
|
||
- Removed the sample house baked into the bundle (the dacha, ~245 KB of base64 plans + ROOMS/FLOOR_*): the bundle
|
||
went 293 KB → 83 KB. A fresh install shows the "Add a space" onboarding, not someone else's house.
|
||
- Removed the legacy→server migration code (the dacha is already on the server config) and the dead
|
||
device_overrides/virtual_devices paths (replaced by markers[]).
|
||
- Pure logic extracted into src/logic.ts (lqiColor, snapToGrid, segKey, pointInPolygon,
|
||
markerIdForBinding, averageLqi) — covered by unit tests (node:test, 9 tests + iconFor).
|
||
- Backend: validation/sanitizers extracted into validation.py (no HA imports) — pytest, 10 tests.
|
||
- SECURITY: a test revealed that the file/marker name sanitizer preserved leading dots
|
||
(a directory traversal risk via ".."); leading-dot stripping added, empty → misc/file.
|
||
- Fixed real bugs previously masked by the truncated rules.ts: DOMAIN_PRIORITY was
|
||
cut short (breaking the more-info entity selection); _defaultPositions crashed on polygon rooms
|
||
(now bbox). tsc --noEmit added to the build and CI — strict typing passes.
|
||
- Removed the junk duplicates src/data/{editor,rules}.ts; versions synchronized (the manifest was stuck at 1.4.0).
|
||
- CI: added typecheck, frontend and backend unit tests.
|
||
|
||
## v1.6.2 — 2026-07-04
|
||
- The "Room" field in the device dialog is now for ALL icons (not only virtual ones):
|
||
for bound ones it "overrides the placement" (by default, the device's area); changing the
|
||
room moves the icon to its center. Stored in marker.space/area and wins over the HA area.
|
||
- An "Edit" button was added to the info card — it opens the device edit dialog.
|
||
|
||
## v1.6.0 — 2026-07-04 (Phase 3: device editor)
|
||
- The "markers" model (config.markers[]): a hybrid — auto-discovered HA devices appear by themselves, markers
|
||
edit/rebind/augment them and describe manual/virtual icons. The marker id
|
||
preserves the position (device→device_id, entity→lg_<eid>, virtual→v_<rand>).
|
||
- Clicking an icon: normal mode → the info card (name, model, state, link, description,
|
||
PDF manuals, an "Open in HA" button); edit mode → the edit dialog.
|
||
- The device dialog: name, binding (a picker of all HA+Z2M devices/groups+helpers with a text
|
||
filter, minus already-placed ones and duplicates by name|area, plus "Virtual device"), MDI icon
|
||
(ha-icon-picker), model, link, description, manual file attachment (PDF to the server),
|
||
room (for virtual ones), Remove/Cancel/Save.
|
||
- Toolbar: a "+" button (tooltip "Add device") — an empty dialog.
|
||
- Backend: MARKER_SCHEMA in the config; WS houseplan/file/set (files in /config/houseplan/files/,
|
||
≤25 MB, served from /houseplan_files/files/); the files static path registered.
|
||
- Virtual icons are now draggable and positioned like normal ones (layout by id).
|
||
|
||
## v1.6.1 — 2026-07-04
|
||
- The binding picker hides device duplicates by "name|area" (Tuya/LocalTuya), same as the dedup on the plan.
|
||
|
||
## v1.5.1 — 2026-07-04 (space management)
|
||
- A pencil icon to the right of each space name in the top toolbar + a "+" button
|
||
(only with a server config).
|
||
- The space dialog: a "Name" field, a preview of the current background + "Upload/Replace…"
|
||
(SVG/PNG/JPG/WebP, base64 → houseplan/plan/set, the aspect ratio is detected from the file),
|
||
Delete / Cancel / Save buttons. Creating an empty space and deletion (together with all its
|
||
rooms and markup, with a confirmation). Saved via config/set with a revision.
|
||
|
||
## v1.5.0 — 2026-07-04 (light groups = entities)
|
||
- Lamps replaced by group entities: HA light groups (platform=group, area from the entity registry)
|
||
and Z2M groups (device model=Group) are rendered with the mdi:lightbulb-group icon; a click → the group's
|
||
more-info (controlling the whole group and its members natively in HA).
|
||
- Individual lamps are hidden in rooms that have a light group (settings.group_lights=false
|
||
brings back the per-lamp mode).
|
||
- The old visual grouping and the custom member menu were removed (code simplification).
|
||
- The positions of the old groups (grp_<area>) were migrated to the new ids (lg_<entity_id>) in the dacha layout.
|
||
|
||
## v1.4.4 — 2026-07-04 (CRITICAL fix: configuration race)
|
||
- INCIDENT: the config was saved wholesale on a last-writer-wins basis — an open client with a
|
||
stale copy wiped other clients' edits (the first iteration of the user's markup was lost).
|
||
- Fix: optimistic locking — the config carries a `rev`; `config/set` accepts `expected_rev`
|
||
and returns a `conflict` error on mismatch; the client re-reads the config.
|
||
- Live synchronization: `config/set` broadcasts a `houseplan_config_updated` event; all open
|
||
cards are subscribed and re-read the config automatically (windows no longer diverge).
|
||
- Positions during drag are saved pointwise via `layout/update` (dirty set), not a full
|
||
`layout/set` — the layout is also no longer wiped between windows.
|
||
- A config backup before deployment: .storage/houseplan.config.bak-*.
|
||
|
||
## v1.4.3 — 2026-07-04
|
||
- Device icons snap to the same grid as the markup (the icon center = a node, snapping
|
||
on drag and on X/Y input). The existing 57 positions in the dacha DB were snapped to the nodes.
|
||
|
||
## v1.4.2 — 2026-07-04 (markup grid)
|
||
- The grid step was halved: GRID_N 60 → 120 (step 8.33 render units ≈ 0.83% of the plan width).
|
||
- Grid points are rendered ON TOP of the plan and rooms (the grid-rect was moved from below the plan
|
||
into the top markup layer); the points are higher-contrast (r=0.14g, outline).
|
||
- Dacha data: the boundaries of all 13 rooms were snapped to grid nodes (edge snapping, shifts ≤ half a step;
|
||
only the instance config was edited via houseplan/config/set, the code was untouched).
|
||
- Card cache: the module URL ?v= is taken from const.VERSION — when updating the frontend, bump
|
||
const.py and restart HA, otherwise browsers keep the old module in memory cache.
|
||
|
||
## v1.4.1 — 2026-07-04 (markup UX)
|
||
- Esc / Ctrl+Z while drawing remove the last point (and its line, if it was added
|
||
by that step; reused walls belonging to others are untouched).
|
||
- The editing panel (markup and layout editing) was moved to the top under the card header
|
||
and pinned together with it (a shared sticky container .hdr).
|
||
- Closing the outline automatically opens the "New room" modal dialog: the display
|
||
name, a dropdown of ONLY the free HA areas (not assigned to any room of the config),
|
||
Save/Cancel. Cancel (or Esc) removes the closing point — the outline can be continued.
|
||
Choosing an area with an empty name fills in the area's name.
|
||
|
||
## v1.4.0 — 2026-07-04 (Phase 2: room markup editor)
|
||
- The "Markup" mode in the card (the mdi:vector-square-edit button, only with a server config):
|
||
a grid of points (60 nodes across the width), lines drawn by pairs of clicks with node snapping, a polyline preview.
|
||
- A closed outline (a click on the first point) activates "Save": choosing an HA area from a
|
||
dropdown + a name; the room is saved as a polygon (poly, normalized vertices).
|
||
- Tools: Add (drawing), Erase line (a click on a line), Delete room
|
||
(a click inside + confirm). The "wall" lines are stored in space.segments and reused by
|
||
neighboring rooms (shared walls — via clicks on existing nodes).
|
||
- Room rendering: polygons on par with rectangles (hover, click into the area, LQI tooltip,
|
||
the label at the centroid); in markup mode the outlines and names of all rooms are visible.
|
||
- Backend: ROOM_SCHEMA accepts poly (≥3 vertices) or x/y/w/h; SPACE_SCHEMA — segments.
|
||
|
||
## v1.3.0 — 2026-07-04 (Phase 1: server-side configuration)
|
||
- The `houseplan.config` Store: spaces (plan, aspect, view_box, rooms), device_overrides
|
||
(hidden/icon/name), virtual_devices, settings (exclude_integrations, group_lights).
|
||
- WS: `houseplan/config/get|set`, `houseplan/plan/set` (plan file upload as base64 →
|
||
`<config>/houseplan/plans/`, served through /houseplan_files/plans/).
|
||
- The card: a resolve model — the server config (coordinates NORMALIZED 0..1, render 1000×1000/aspect)
|
||
or the legacy bundle (canvas 1489×1053). Layout v2: {device_id: {s, x, y}} normalized.
|
||
- The "To server" button in edit mode — a one-click migration of the legacy config (plans, rooms,
|
||
view_box, layout). Performed at the dacha: .storage/houseplan.config + plans/f1.svg,f2.svg.
|
||
- Read-side support for device overrides and virtual devices (the management UI — phases 3–4).
|
||
|
||
## v1.2.2 — 2026-07-04
|
||
- The card toolbar (floor tabs) pins under the HA header while scrolling
|
||
(`position: sticky; top: var(--header-height)`; ha-card overflow: visible).
|
||
- Incident: an intermediate build on the unstable mount produced a broken bundle that crashed the
|
||
dashboard rendering; the rule "build only in /tmp + md5 control" was locked into DEVELOPMENT.md.
|
||
|
||
## v1.2.1 — 2026-07-04
|
||
- Icons no longer enlarge on hover/drag (transform: scale removed).
|
||
|
||
## v1.2.0 — 2026-07-03
|
||
- Room boundaries snapped to the walls of the vector plan.
|
||
- Zigbee LQI: the value under the icon, the room average in the tooltip, a red→green gradient,
|
||
the show_signal option.
|
||
- `mdi:lock` for any devices with a lock.* entity (TTLock).
|
||
- The hallway switch: the device's area in the HA registry was fixed (it was in detskaia_elina).
|
||
|
||
## v1.1.0 — 2026-07-03
|
||
- Vector backgrounds (SVG from REMPLANNER), automatic scale/offset alignment via raster correlation.
|
||
- Icon size as a % of the plan width (container queries, default 2.5%).
|
||
|
||
## v1.0.1 — 2026-07-03
|
||
- fix: nested SVG fragments via lit svg`` (the background/rooms were not rendered).
|
||
- fix: the version taken from const instead of a blocking manifest.json read in the event loop.
|
||
|
||
## v1.0.0 — 2026-07-03
|
||
- First release: the Lovelace card (TS+Lit, no token, driven by hass) + the houseplan integration
|
||
(WS layout storage, JS serving). The prototype's model carried over: curation, lamp groups,
|
||
iconFor, temperature, more-info, area navigation, drag layout.
|