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

163 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Единая индикация состояния и активности устройств
Статус: **утверждено владельцем и реализовано локально 2026-08-05**.
## Цель
Заменить четыре пересекающиеся механики — цвет подложки, пользовательскую
пульсацию, motion/presence-эффекты и движение штор — одной семантической
системой. Визуал должен отвечать на три разных вопроса:
1. Доступно ли устройство.
2. В каком устойчивом состоянии оно находится.
3. Что оно делает прямо сейчас или что только что произошло.
## Режимы отображения
В UI остаются:
1. **Значок** — иконка, морфинг и статусная подложка без обычных эффектов
активности.
2. **Значок + активность** — то же плюс семантический эффект активности.
3. **Значение вместо значка** — числовая плашка со статусным цветом; обычная
активность не рисуется.
Опция **«Только пульсация» удаляется**. Legacy `display: ripple` читается как
`icon_ripple`, а при следующем сохранении конфигурации переписывается в
`icon_ripple`. Backend временно продолжает принимать старое значение.
## Модель визуального состояния
Для маркера вычисляется один результат:
```ts
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 переведены на новые классы.