Files
houseplan-card/docs/superpowers/specs/2026-08-05-device-visual-state-design.md

9.3 KiB
Raw Permalink Blame History

Единая индикация состояния и активности устройств

Статус: утверждено владельцем и реализовано локально 2026-08-05.

Цель

Заменить четыре пересекающиеся механики — цвет подложки, пользовательскую пульсацию, motion/presence-эффекты и движение штор — одной семантической системой. Визуал должен отвечать на три разных вопроса:

  1. Доступно ли устройство.
  2. В каком устойчивом состоянии оно находится.
  3. Что оно делает прямо сейчас или что только что произошло.

Режимы отображения

В UI остаются:

  1. Значок — иконка, морфинг и статусная подложка без обычных эффектов активности.
  2. Значок + активность — то же плюс семантический эффект активности.
  3. Значение вместо значка — числовая плашка со статусным цветом; обычная активность не рисуется.

Опция «Только пульсация» удаляется. Legacy display: ripple читается как icon_ripple, а при следующем сохранении конфигурации переписывается в icon_ripple. Backend временно продолжает принимать старое значение.

Модель визуального состояния

Для маркера вычисляется один результат:

availability: 'available' | 'unavailable'
status: 'neutral' | 'open' | 'working' | 'alarm'
activity: 'none' | 'event' | 'presence' | 'transition' | 'running'
  • availability отвечает за приглушение недоступного устройства;
  • status управляет подложкой и морфингом;
  • activity управляет кольцом или волнами;
  • тревога имеет высший приоритет;
  • недоступность подавляет обычную активность.

Визуальный язык

Смысл Подложка Эффект в «Значок + активность»
Доступно, простаивает нейтральная нет
Выполняет основную функцию жёлтая медленное дыхание
Открыто / разблокировано оранжевая только событие/переход, не постоянный эффект
Разовое срабатывание текущая статусная три расходящиеся волны, 3,3 с
Присутствие нейтральная спокойное статичное кольцо
Механическое движение текущая статусная дыхание до конца движения
Недоступно приглушённая нет
Тревога (протечка, дым, газ, CO, safety/tamper/problem, сирена, сработавшая охрана) красная быстрая красная пульсация при любом display

prefers-reduced-motion заменяет анимацию статичным кольцом.

Значение жёлтой подложки

Жёлтый означает только фактическое выполнение основной функции:

  • light/switch/fan/humidifier = on;
  • climate: hvac_action = heating/cooling/drying/fan, но не просто выбранный режим heat/cool/auto;
  • vacuum: cleaning (и работающий механизм при returning);
  • бытовая техника: running/washing/rinsing/spinning/drying/heating/cooking;
  • маркер с controls: работает хотя бы одна управляемая цель.

Не являются работой: открытая дверь, разблокированный замок, открытые шторы, presence/motion, enabled, home, unknown, unavailable, climate idle.

media_player — пассивный медиатранспорт, а не исполнительный механизм: on/idle/playing/paused/standby остаются нейтральными и не получают running- активность, а явный off использует то же приглушённое отображение, что unknown/unavailable. Правило действует на весь домен (телевизоры, ресиверы, колонки, саундбары), не на конкретные модели, и не вводит отдельного статуса. Если у маркера несколько media_player, он бледнеет только когда среди них нет доступной и включённой сущности.

В glow-режиме у реального источника света жёлтая подложка может быть скрыта: его устойчивым индикатором служит световое пятно. Эффект активности остаётся.

Семантика активности

Event, 3,3 секунды

  • motion: только засвидетельствованный off -> on, не весь cooldown;
  • vibration/shock/sound;
  • открытие двери/окна (off -> on), но не всё время открытого состояния;
  • button/event;
  • успешный запуск script/scene/automation без устойчивого состояния.

Первое состояние после загрузки и восстановление из unknown/unavailable не создают ложного события. Повторный детект перезапускает окно и анимацию.

Presence

occupancy/presence = on даёт статичное кольцо до исчезновения присутствия.

Transition

  • cover: opening/closing;
  • lock: locking/unlocking;
  • valve: opening/closing;
  • vacuum: returning.

Если промежуточного состояния нет, прямой конечный переход closed <-> open / locked <-> unlocked показывает transition 3,3 секунды.

Running

Пока устройство фактически работает, его подложка жёлтая, а в режиме «Значок + активность» вокруг неё медленно дышит кольцо.

Источник состояния

Приоритет:

  1. Явный специализированный источник (в будущей расширенной настройке).
  2. Cover, когда tap action явно задан как «Открыть/закрыть».
  3. controls — агрегированное состояние всех управляемых целей.
  4. Функциональная сущность устройства / включённый light.
  5. Primary entity.

Критические сущности устройства проверяются независимо и не могут быть скрыты менее важным primary state.

Приоритет вывода

  1. alarm;
  2. unavailable;
  3. event;
  4. transition;
  5. presence;
  6. running;
  7. open;
  8. neutral.

Инварианты

  • Одна функция вычисляет подложку и activity; они не смотрят на разные сущности.
  • Обычная активность появляется только в icon_ripple.
  • Alarm отображается при любом display и не зависит от пользовательского цвета.
  • Hidden/ghost не получает live-чисел, морфинга, подложки или активности.
  • Static houseplan-space-card остаётся статической и не рисует live effects.
  • Пользовательские цвет и размер применяются к обычной активности, но не к красной тревоге.

Реализация

  • Чистая классификация сущностей, агрегация и распознавание переходов: src/device-visual.ts.
  • Выбор эффективных источников маркера, runtime-окно 3,3 с, миграция legacy display и рендер: src/houseplan-card.ts.
  • Общий слой колец и reduced-motion варианты: src/styles.ts.
  • UI оставляет три режима; backend сохраняет ripple только как входное legacy-значение до завершения миграционного периода.
  • Unit-спецификация классификатора подготовлена в test/device-visual.test.mjs; браузерные smokes переведены на новые классы.