- marker.controls[]: bound light.*/switch.* entities; explicit per-marker
tap_action=toggle flips them with HA-group semantics in one call
(controlsAction + isControllable pure helpers, +2 unit tests, 100)
- icon state and RGB tint mirror the targets (stateless remotes and
virtual dumb-switch markers finally show something); info card lists
targets with states; locks filtered everywhere
- chips+search UI in the marker dialog; backend schema; smoke_controls
(9 checks); TESTING/CHANGELOG same-commit
- fill_mode 'glow': uniform darkness over every room; lit lamps render
radial gradient pools (rgb_color -> color temp via kelvinToRgb ->
configurable default; brightness scales alpha)
- pools clipped by the source's room + doorway sectors (doorSector rays
to door edges; hasRoomBehind blocks entrance doors); windows and
islands don't participate (no shadow casting, documented)
- glow radius in HA units (m/ft) in general settings, stored in cm;
palette group glow_base/glow_light; backend schema updated
- +4 unit tests (98 total); smoke_glow.mjs (10 checks); docs same-commit
- roomsOverlap: full nesting is legal (islands), edge crossings and
partial overlaps still rejected; polyContainsPoly + islandsOf pure
helpers (+2 unit tests, 94 total)
- draw tool: per-click point-in-room rejection removed, validation at
contour closing; parent rooms with islands render as evenodd paths
(ring fills correctly, island stays clickable through the hole)
- smoke_island_rooms.mjs; smoke_merge_split stale room-count asserts
fixed (rg pushed a 5th room; cancelWhole silently false for ages)
- TESTING/CHANGELOG same-commit
- rebinding migrates the layout position to the new marker id instead of
recentering; room change within the same space keeps the position
(owner's decision); new icons and cross-space moves still center
- smoke_marker_stay.mjs; TESTING/CHANGELOG same-commit
- marker dialog stores autoIcon (DevItem.icon); ha-icon-picker gets it
as placeholder, fallback input too; 'Auto: mdi:...' hint with preview
under the picker, hidden once an explicit icon is chosen
- smoke_icon_placeholder.mjs; TESTING/CHANGELOG same-commit
- third mode tab: draw lines/rects/ovals/text with grid snap, drag
preview; select+move (Delete key), erase, color/width/fill controls;
Esc ladder; text dialog with S/M/L sizes and dblclick re-edit
- decor renders under rooms, visible in every mode, click-transparent
outside its editor; stored in space.decor with backend schema
(line/rect/ellipse/text variants), rev/optimistic locking as usual
- smoke_decor.mjs (19 checks); smoke_editor_tabs updated to 3 tabs;
TESTING/CHANGELOG/UX-MODES same-commit
- _openingPreview getter mirrors the click's snap (same eps, same
existing-opening hit test); dashed 90cm ghost + center dot on the wall
- smoke_opening_preview.mjs; TESTING/CHANGELOG same-commit
- splitRoomPath in logic.ts: wall-to-wall polyline cut with interior
vertices; validates wall crossings, self-intersection, degenerate
parts; splitRoom delegates (2-point path); +3 unit tests (93 total)
- split UI: interior clicks add snapped intermediate points, live
polyline + vertices + preview; new/updated toasts
- cursors: pointer for merge/delroom and split room-pick stage,
crosshair while cutting (stage tool-* classes)
- Esc: split — drop last point, then room pick, then back to draw;
merge — clear selection, then back to draw
- smoke_split_polyline.mjs (14 checks); TESTING/CHANGELOG same-commit
- .room.picked moved after .room.outlined: equal specificity, source
order silently killed the amber highlight in markup (gotcha x4,
documented inline)
- smoke_merge_highlight.mjs (waits out the 0.12s transition before
reading computed colors); TESTING/CHANGELOG same-commit
- _markupClick ignores clicks originating from .roomlabel/.rlhandle
(composedPath) and anything during an active drag/resize
- smoke_card_tool_conflict.mjs (draw/delroom/handle/during-resize +
normal stage click still works); TESTING/CHANGELOG same-commit
- room label becomes a card: name on top, optional metrics below
(temperature / humidity / avg zigbee / lights), four checkboxes in
space settings, all off by default; lights render On/Off or '1 of 3'
- areaHum + areaLightStats pure helpers (+3 unit tests, 90 total)
- resize via corner handles in the Plan editor (hover), uniform 0.5-3x,
scale stored as layout k next to the position; drag preserves it
- fix latent v1.25 regression: draggable HTML labels were never rendered
in the Plan editor (static SVG only) — real name-only cards now render
there, draggable and resizable
- repair merge/split smokes still calling removed _toggleMarkup
- validation.py: 4 label_* bools; smoke_room_cards.mjs; docs same-commit
- two tabs only: 'Plan editor' / 'Device editor'; View is the implicit
default state, no tab
- Device editor gets its own bottom toolbar (add/show-all/reset/rules
moved from the header), mirroring the Plan toolbar
- X button on both toolbars and inside the active tab returns to View;
re-click on active tab is a no-op; Plan<->Devices switches directly
- smoke_editor_tabs.mjs; TESTING/CHANGELOG/UX-MODES same-commit
- cog next to the space name shows in View/Plan/Devices (canEdit only);
'+' add-space tab stays Plan-only
- tabs flex-centered so the cog aligns with the tab text
- smoke_gear_tabs.mjs; TESTING.md + CHANGELOG same-commit
- View-mode door/window info card gets an Unlock (red) / Lock button when a
lock entity is bound and available; disabled while locking/unlocking,
hidden when unavailable
- plan-icon taps still never toggle locks (hard block untouched);
smoke_lock_action.mjs covers the full matrix incl. the block
- TESTING.md, CHANGELOG, UX-MODES same-commit
- auto devices/light groups appearing after install get a server-side 'new'
flag (settings.new_device_ids) and a red dot top-right of the icon; opening
the device's editor clears it for every client
- known_devices baseline seeded silently on first run — upgrades never flood
the plan with dots; hand-made markers never flagged
- pure diffNewDevices + unit tests; backend schema; smoke_new_device.mjs;
TESTING.md row
- device icons: pointer cursor in View (grab only in Devices mode); click
behavior untouched
- openings in View: pure drawings always — no cursor/hover/hit/click
- lock badge: the one exception — visible and clickable in View (info card),
inert in Plan; removed a duplicate pointer-events in .oplock
- smoke covers all cursor/badge cases
- op-hit: pointer-events none + default cursor by default; interactive (grab,
hover outline, drag) only inside Plan; lock badge is a pure indicator
- opening click edits in Plan with any tool; no interaction whatsoever in View
- smoke_inert_openings.mjs; TESTING.md updated
- area-less rooms appear in the marker room list ('no area, manual'); markers
store room_id and land at the room centre; dialog reopen restores the choice
- pure parseRoomRef (space#area / space#@roomId) + unit tests; backend schema
- ROADMAP phase 11 closed; TESTING.md row; smoke_subarea.mjs
- lightColorOf(): on+rgb_color tints icon/glow/ripple (explicit ripple color wins);
brightness ignored by design; off/white/unavailable unchanged
- isAlarmState(): leak/smoke/gas/CO/safety/tamper/problem sensors and sirens in
'on' pulse a red ring over any display mode; outages never alarm;
prefers-reduced-motion honoured
- +2 unit tests, smoke_rgb_alarm.mjs, TESTING.md rows
- General settings dialog: fill colors grouped by mode (light on/off, temp
cold/ok/hot, lqi weak/strong), each with its own opacity; lqi fill lerps
between the endpoints; stored in settings.fill_colors (defaults omitted);
space-card uses the same palette
- per-space show_lqi toggle (badges + room tooltip line), inherits the card's
show_signal when unset
- fillColorsOf/lerpColor/roomFillStyle helpers (+4 tests), backend schemas,
smoke_general_settings; TESTING.md updated in the same commit
- scale (cm/cell) input compact again (generic width:100% dialog rule stretched it)
- space with no background image gets a white paper canvas (.stage.noplan), so
hand-drawn rooms read like a plan on paper
- TESTING.md updated in the same commit
- 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.
- comfort bounds: parseFloat+isFinite guard (empty input collapsed a bound to 0
and the auto-swap made 'comfort from 25' behave as 0-25 → green at 24°)
- avg room temperature in the room tooltip (what the temp fill actually uses)
- hover darkens the current fill instead of recoloring blue; grey when unfilled
- fill mode as a radio group with short labels; compact inline bounds inputs
- space dialog widened to 500px; TESTING.md updated in the same commit
- fill_mode 'temp': below comfort → #4fc3f7, inside → #66d17a, above → #ffd45c;
bounds default 20-25°C, editable in the space dialog (visible only for this
mode), swapped bounds tolerated; rooms without a temperature reading unfilled
- areaTemp(): average of the area devices' temperatures
- tests: roomFillColor temp bands + bounds, spaceDisplayOf defaults, areaTemp,
backend schema (fill_mode temp + temp_min/temp_max); smoke_temp_fill.mjs
- TESTING.md updated in the same commit (policy)
- space dialog 'Display' section: room borders/names toggles, color+opacity,
fill by zigbee signal or lights (tri-state: on/off/no-lights)
- draggable room name labels persisted as layout rl_<roomId>
- 'no image, outline by hand' space source with canvas orientation; image
optional now; switching an existing space to draw detaches its plan
- demo/ synthetic-home harness lives in the repo (serve.mjs + smokes + icons gen)
- docs/TESTING.md checklist (same-commit update policy); self-run found and
fixed: plan_url not detached on image->draw, _stateClass crash on states
without entity_id; perf measured (162 devices ~14ms build)
- backend: SPACE_DISPLAY_SCHEMA validation (+test)