Commit Graph
9 Commits
Author SHA1 Message Date
Matysh 4dc3fdef36 Release v1.62.0-beta.4 candidate 2026-08-12 10:19:51 +03:00
Matysh c41231a7ba Release v1.62.0-beta.3 candidate 2026-08-12 02:33:54 +03:00
Matysh 5c5833d69a fix: stabilize prerelease signing checks 2026-08-11 03:14:56 +03:00
Matysh 6a9122f41f v1.59.2: make dialogs accessible 2026-08-07 07:47:37 +03:00
Matysh 25f43da1bd v1.59.1: unify device and light state
Validate / smoke (push) Failing after 19m18s
Validate / hacs (push) Failing after 1m11s
Validate / hassfest (push) Failing after 1m37s
Validate / frontend (push) Successful in 3m0s
Validate / backend (push) Failing after 15m20s
2026-08-06 21:45:18 +03:00
Matysh d2bec266ed v1.59.0-beta.10: unify device visuals and wall refinements
Validate / hacs (push) Failing after 6s
Validate / hassfest (push) Failing after 6s
Validate / frontend (push) Successful in 3m19s
Validate / smoke (push) Failing after 1m12s
Validate / backend (push) Failing after 7m45s
2026-08-05 23:38:01 +03:00
Matysh 1da1aba625 Curtains never wear a coloured plate
Validate / hacs (push) Failing after 1m8s
Validate / hassfest (push) Failing after 1m6s
Validate / frontend (push) Successful in 2m26s
Validate / backend (push) Failing after 7m59s
Validate / smoke (push) Failing after 9m32s
Owner's contract, 2026-08-04, verbatim: «у штор не должно быть жёлтой подложки
никогда, индикация открыто/закрыто за счёт морфинга иконки».

WHAT 'open' WAS. `.dev.open` is not a border — it is the badge FILLED with
--hp-open (#ff9f43), border and glyph colour included: a solid orange plate,
one step down from the yellow «включено» one. Covers shared a branch with
`valve` and took it in `open` AND `opening`, so a travelling curtain wore the
orange plate UNDER the breathing ring the owner approved a day earlier — the
plate he had just said should stay neutral while it moves, kept for the state
it stopped in. Since de53d53 an «Открыть/закрыть» marker reads its cover
wherever that entity sits, so the paint had just reached every curtain that
had the action set, his own included.

WHAT IT IS NOW. `_stateClass` returns no plate class for the `cover` domain in
any state: closed, open, ajar (HA reports a positioned cover as plain 'open'),
opening and closing all keep the neutral badge, and motion is the `.covermove`
ring alone. Open/closed is told by the ICON — which makes the morph the only
signal there is, so it had to stop having holes:

- `awning` mapped BOTH states to `mdi:awning-outline` — one glyph for open and
  closed, i.e. no indication at all for that class. Now outline (retracted) ->
  `mdi:awning` (extended).
- a cover with NO device_class (z2m ships plenty) only morphed if its icon
  happened to be in a device_class pair — and the icons the card itself hands
  out are not: the name rule «штор|curtain|blind|shade» gives `mdi:roller-shade`,
  «ворота|garage|gate» gives `mdi:garage-variant`. Those, plus
  `mdi:blinds-horizontal` and `mdi:door`, are now recognised as pairs on the
  base icon (COVER_ICON_ALIASES — base-icon matching only, never picked by
  device_class, so nothing is swapped for a guess).
- a hand-picked icon still wins outright everywhere, with ONE exception: a
  cover whose custom icon IS one of those pair members morphs inside THAT pair
  (`mdi:curtains` <-> `mdi:curtains-closed`) — never traded for another family.
  Without it, choosing an icon would silently switch the marker's only
  indicator off.

WHAT KEEPS THE FRAME, deliberately: door / window / garage_door / opening
binary sensors, an unlocked lock — and `valve`, which parts ways with `cover`
here. No icon pair morphs for a valve, so the frame is the only thing it has
to say «открыт» with; sweeping it along would have left those markers mute for
a rule that names the curtains. If the two domains should ever read alike, a
valve needs an icon pair first (docs/FILTERING.md).

smoke_cover_no_plate.mjs walks one curtain through closed / open / ajar /
opening / closing and reads the COMPUTED plate colour against probes of
--hp-bg, --hp-on and --hp-open: neutral every time, never yellow, never
orange, no 'on'/'open' class, the breathing ring in the two travelling states
and nowhere else. It also checks the morph for all ten classes both ways, the
no-device_class and custom-icon paths, and — the point of the whole bottom
half — that an unlocked lock and an open window sensor STILL come out orange
(and a locked lock neutral again, so the frame still means something). 13
checks are red on the parent commit. The unit suite gains a loop that fails
any class mapping both states to one glyph. smoke_cover_tap and
smoke_cover_not_primary flip their «open frame» assertions to the new
contract; docs/FILTERING.md gets the state table and the valve reasoning,
docs/TESTING.md the checklist item. shot_cover_states.mjs captures the four
states side by side.
2026-08-04 04:38:23 +03:00
Matysh de53d530fa A curtain marker shows the cover it opens
Owner, 2026-08-04, on his own curtains: «нет ни дышащего кольца во время хода,
ни рамки "открыто", ни морфинга иконки». Same device and the same cause as the
tap fix two commits before this branch: his Aqara «Roller shade driver E1»
ships the `cover.*` hidden by the integration and a visible
`switch.*_reverse_direction`, so `primaryEntity` picks the service switch —
and `_stateClass`, the state-morphed icon and the ripple all read `d.primary`.
The plan reported the state of the reverse-direction option: a yellow
«включено» plate whenever it was on, and nothing at all while the curtain
actually travelled.

`coverEntityOf` already knew where the cover was; the indication now asks it
through one helper, `_coverIndicator` — the device's cover when the marker's
tap action is explicitly «Открыть/закрыть», null otherwise — and `_actEntity`
(`_coverIndicator || primary`) is what the tap path and the marker
presentation now share. Same entity offered in the dialog, driven by the tap
and shown on the plan.

THE RULE, and why it is the least surprising one (docs/FILTERING.md «What a
marker SHOWS»): picking «Открыть/закрыть» is the only statement the card has
that means «this marker IS the curtain», and the dialog offers it exactly for
the devices that own a cover. Hanging the indication on «the device has a
cover somewhere» would have re-decided, silently, what a mixed marker is — a
lamp that also owns a blind would stop showing the lamp. The precedence in
`_stateClass` is unchanged above it: bound controls first, then a lit light
(the glow spot and the badge may never disagree), then the cover, then the
primary — so even with the action chosen a shining lamp keeps its yellow. The
price is that a curtain left on «Инфо-карточка» still speaks for its primary;
that is one click in the dialog, and it is the honest reading of what the
marker has been told it is.

smoke_cover_not_primary.mjs grows an indication section on the owner's device:
closed / open / opening / closing give no class, `open`, `covermove`,
`covermove`, the icon morphs `mdi:curtains-closed` <-> `mdi:curtains`, and
reverse-direction ON never lights the marker again. The rule's boundary is
asserted from both sides (take the action away — the primary speaks again;
give it back — the cover does), a lit lamp with a travelling cover keeps its
yellow and its own icon, and the auditor's own DEV-2C947-04 shape (both
entities VISIBLE) is pinned for the tap as well. Eight checks are red on the
parent commit.
2026-08-04 01:45:05 +03:00
houseplan-dev fd72330549 Drag the plan at any zoom, not only above 100%
Owner's report 2026-08-04: «добавь возможность таскать план при любом
масштабе, а не только при более 100%, как сейчас (и в редакторах, и в
просмотре)».

_stagePointerMove moved the view only while `_zoom > 1`. That gate is
older than the infinite canvas and made sense under the old rule — the
content had to cover the scene, so at 100% or below there was literally
nowhere to go and a drag could only jitter. The infinite canvas removed
the edge and gave panning a slack of one screen past the content in
every direction (CANVAS.md §5), and from that moment the gate was not a
guard but a missing feature: at 100% you could see the arrow «home is
that way» light up from a wheel-zoom, and still not drag the plan an
inch. The zoom no longer takes part in the decision — `_clampView`
alone says how far you may walk, at 400% and at 33% alike.

The drag also stopped depending on `_view` being materialised: it reads
`_viewOr(baseVb)`, so the very first drag on a freshly opened space
pans instead of doing nothing.

Gesture ownership is unchanged, and that is the point of most of the
new smoke: `_stagePointerDown` still bails out on the room-resize
handles, device badges, openings, room labels and decor shapes, and on
a decor drawing tool that consumes the press; two fingers are still a
pinch. The one place where a drag had a rival is the kiosk, where a
horizontal swipe changes floors. It is now classified once per gesture,
on the first movement past 8px (`_panLock`): horizontal in the swipe
zone (kiosk, zoom <= 1, more than one space) = swipe and no pan,
everything else = pan. So the plan never slides out from under a swipe,
a vertical drag on a wall tablet pans as it does everywhere else, and
zoomed in — where swipeTarget already refuses — a horizontal drag pans.

demo/smoke_pan_any_zoom.mjs: a drag on empty scene moves the view at
100%, 50% and 1/3 in View and in every editor (all seven plan tools,
Devices, Background), and at 400% as before; the walk stops at the
PAN_SLACK limit and the home arrow appears; a resize handle resizes, a
device badge moves the device and an opening slides along its wall,
none of them panning a pixel; two fingers still zoom; the kiosk still
swipes floors through a gesture that has real pointermove events in it
(smoke_kiosk only ever sent down+up), a vertical drag there pans, and a
zoomed-in horizontal drag pans without changing the floor.

Before the fix 25 of its checks are red, including every editor at
every zoom.
2026-08-04 00:43:05 +03:00