motion sensors: tripped motion/occupancy/presence pulses a soft yellow ring
Validate / hacs (push) Failing after 42s
Validate / hassfest (push) Failing after 40s
Validate / frontend (push) Successful in 1m33s
Validate / backend (push) Failing after 6m16s
Validate / smoke (push) Successful in 5m8s

Owner's rule (2026-08-01): the yellow FILL is reserved for 'on' — a tripped
sensor keeps the neutral badge and gets a new 'sense' state class instead:
a gentle .dev.sense::after ring in the .dev.on yellow (--hp-on), 2.4s period,
max opacity 0.5 — the soft sibling of the red .dev.alarm siren. Declared
before .dev.alarm so red wins on the shared ::after if both ever apply;
ghosts don't pulse (hidden gate already strips the state class).
prefers-reduced-motion: static faint ring, same treatment as alarm.

Smoke smoke_motion_sense.mjs (fails on the pre-fix bundle): sense class on/off,
::after animation, neutral background (not --hp-on), occupancy/presence,
hidden ghost. shot_motion_sense.mjs captures the ring for review.
This commit is contained in:
Matysh
2026-08-01 11:54:23 +03:00
parent 88008961e3
commit 29dae45e2a
7 changed files with 196 additions and 3 deletions
+7
View File
@@ -1308,6 +1308,13 @@ class HouseplanCard extends LitElement {
const dc = p.attributes?.device_class;
if (['door', 'window', 'garage_door', 'opening', 'gas', 'smoke', 'moisture', 'problem'].includes(dc))
return p.state === 'on' ? 'open' : '';
// Owner's rule (2026-08-01): «жёлтая подложка = включено; срабатывание
// сенсора = лёгкая жёлтая пульсация». A tripped motion/occupancy/
// presence sensor must NOT take the yellow 'on' fill — it keeps the
// neutral badge and gets a soft yellow pulse ring instead (.dev.sense
// in styles.ts, the gentle sibling of the red .dev.alarm ring).
if (['motion', 'occupancy', 'presence'].includes(dc))
return p.state === 'on' ? 'sense' : '';
}
if (dom === 'media_player') return ['playing', 'on'].includes(p.state) ? 'on' : '';
if (dom === 'vacuum') return ['cleaning', 'returning'].includes(p.state) ? 'on' : '';
+25
View File
@@ -883,6 +883,29 @@ export const cardStyles = css`
/* v1.52.0: the RGB tint of the icon/border is gone — a lamp's colour
lives ONLY in its glow spot (owner's rule). The ripple-color fallback
keeps using the light colour; that is set inline via --ripple-color. */
/* motion/occupancy/presence tripped: a soft yellow pulse ring around
the icon. Owner's rule (2026-08-01): «жёлтая подложка = включено;
срабатывание сенсора = лёгкая жёлтая пульсация» — the yellow FILL
stays reserved for the 'on' state, so a tripped sensor keeps the
neutral badge and only breathes. Same ring mechanics as .dev.alarm
below, but gentler: the .dev.on yellow (--hp-on), half the opacity,
a slower period and a smaller amplitude. Declared BEFORE .dev.alarm
on purpose: equal specificity on the same ::after means the later
alarm rule wins if a device ever carries both — red beats yellow. */
.dev.sense::after {
content: '';
position: absolute;
inset: calc(var(--dev-size, var(--icon-size, 2.5cqw)) * -0.35);
border: 2px solid var(--hp-on);
border-radius: 50%;
animation: hp-sense 2.4s ease-in-out infinite;
pointer-events: none;
}
@keyframes hp-sense {
0% { transform: scale(0.9); opacity: 0.5; }
60% { transform: scale(1.12); opacity: 0.12; }
100% { transform: scale(1.12); opacity: 0; }
}
/* alarms pulse red over everything */
.dev.alarm::after {
content: '';
@@ -899,6 +922,8 @@ export const cardStyles = css`
}
@media (prefers-reduced-motion: reduce) {
.dev.alarm::after { animation: none; opacity: 0.9; }
/* static counterpart, same as alarm's: a faint steady yellow ring */
.dev.sense::after { animation: none; opacity: 0.4; }
}
.dev .newdot {
position: absolute;