Files
houseplan-card/docs/DEVICE-LIGHT-SETTINGS-MATRIX.ru.md
T

121 lines
14 KiB
Markdown

# Матрица настроек света устройства
Актуально для локальной реализации issues [#84](https://github.com/Matysh/houseplan-card/issues/84)
и [#88](https://github.com/Matysh/houseplan-card/issues/88). Каноническая модель
геометрии и распространения света остаётся в [`LIGHT.md`](LIGHT.md).
## Обозначения
- **A** — режим «Авто» нашёл у устройства собственный пространственный источник.
- **S** — у устройства есть собственная управляемая `light.*`/`switch.*`, способная
дать состояние и принять service call.
- **Источник** — устройство участвует в Glow, заливке «Свет», карточке и статистике комнаты.
- **Live** — доступен режим цвета «Из источника».
- **Ручн.** — доступны ручной цвет и ручная яркость.
- **R** — доступен локальный радиус Glow.
- `Авто → фикс.` — сохранённый режим не переписывается, но в UI и runtime пассивный
источник получает безопасный fallback: общий цвет, яркость 100%.
Комбинация `A=да, S=нет` приведена для полноты контракта и мутационных тестов, но
недостижима штатным resolver: автоматически найденный источник всегда имеет реальную
`light.*`. Остальные 27 строк достижимы.
## Полная матрица role × A × S × режим Glow
| # | Роль | A | S | Сохранённый режим | Источник | Пассивный | Live | Ручн. | R | Эффективный режим |
|---:|---|:---:|:---:|---|:---:|:---:|:---:|:---:|:---:|---|
| 1 | Авто | нет | нет | Из источника | нет | нет | нет | нет | нет | Из источника, disabled |
| 2 | Авто | нет | нет | Задать цвет | нет | нет | нет | нет | нет | Задать цвет, disabled |
| 3 | Авто | нет | нет | Цвет + яркость | нет | нет | нет | нет | нет | Цвет + яркость, disabled |
| 4 | Авто | нет | да | Из источника | нет | нет | нет | нет | нет | Из источника, disabled |
| 5 | Авто | нет | да | Задать цвет | нет | нет | нет | нет | нет | Задать цвет, disabled |
| 6 | Авто | нет | да | Цвет + яркость | нет | нет | нет | нет | нет | Цвет + яркость, disabled |
| 7 | Авто | да | нет | Из источника | да | да | нет | да | да | Авто → фикс. (теоретическая) |
| 8 | Авто | да | нет | Задать цвет | да | да | нет | да | да | Задать цвет (теоретическая) |
| 9 | Авто | да | нет | Цвет + яркость | да | да | нет | да | да | Цвет + яркость (теоретическая) |
| 10 | Авто | да | да | Из источника | да | нет | да | да | да | Из источника |
| 11 | Авто | да | да | Задать цвет | да | нет | да | да | да | Задать цвет |
| 12 | Авто | да | да | Цвет + яркость | да | нет | да | да | да | Цвет + яркость |
| 13 | Всегда | нет | нет | Из источника | да | да | нет | да | да | Авто → фикс. |
| 14 | Всегда | нет | нет | Задать цвет | да | да | нет | да | да | Задать цвет |
| 15 | Всегда | нет | нет | Цвет + яркость | да | да | нет | да | да | Цвет + яркость |
| 16 | Всегда | нет | да | Из источника | да | нет | да | да | да | Из источника |
| 17 | Всегда | нет | да | Задать цвет | да | нет | да | да | да | Задать цвет |
| 18 | Всегда | нет | да | Цвет + яркость | да | нет | да | да | да | Цвет + яркость |
| 19 | Всегда | да | нет | Из источника | да | да | нет | да | да | Авто → фикс. (теоретическая) |
| 20 | Всегда | да | нет | Задать цвет | да | да | нет | да | да | Задать цвет (теоретическая) |
| 21 | Всегда | да | нет | Цвет + яркость | да | да | нет | да | да | Цвет + яркость (теоретическая) |
| 22 | Всегда | да | да | Из источника | да | нет | да | да | да | Из источника |
| 23 | Всегда | да | да | Задать цвет | да | нет | да | да | да | Задать цвет |
| 24 | Всегда | да | да | Цвет + яркость | да | нет | да | да | да | Цвет + яркость |
| 25 | Никогда | нет | нет | Из источника | нет | нет | нет | нет | нет | Из источника, disabled |
| 26 | Никогда | нет | нет | Задать цвет | нет | нет | нет | нет | нет | Задать цвет, disabled |
| 27 | Никогда | нет | нет | Цвет + яркость | нет | нет | нет | нет | нет | Цвет + яркость, disabled |
| 28 | Никогда | нет | да | Из источника | нет | нет | нет | нет | нет | Из источника, disabled |
| 29 | Никогда | нет | да | Задать цвет | нет | нет | нет | нет | нет | Задать цвет, disabled |
| 30 | Никогда | нет | да | Цвет + яркость | нет | нет | нет | нет | нет | Цвет + яркость, disabled |
| 31 | Никогда | да | нет | Из источника | нет | нет | нет | нет | нет | Из источника, disabled (теоретическая) |
| 32 | Никогда | да | нет | Задать цвет | нет | нет | нет | нет | нет | Задать цвет, disabled (теоретическая) |
| 33 | Никогда | да | нет | Цвет + яркость | нет | нет | нет | нет | нет | Цвет + яркость, disabled (теоретическая) |
| 34 | Никогда | да | да | Из источника | нет | нет | нет | нет | нет | Из источника, disabled |
| 35 | Никогда | да | да | Задать цвет | нет | нет | нет | нет | нет | Задать цвет, disabled |
| 36 | Никогда | да | да | Цвет + яркость | нет | нет | нет | нет | нет | Цвет + яркость, disabled |
Ручной цвет при stateful-источнике оставляет живую яркость. Режим «Цвет + яркость»
фиксирует оба значения. Для пассивного источника живой яркости нет, поэтому «Задать
цвет» означает яркость 100%, а «Цвет + яркость» использует сохранённое значение.
## Ведущая сущность (#88)
| Роль | Управляемых собственных сущностей | Сохранённый `light_entity` | UI и runtime |
|---|---:|---|---|
| Авто / Никогда | любое число | любое значение | Селектор скрыт; поле сохраняется без изменения, но на текущую роль не влияет |
| Всегда | 0 | отсутствует | Пассивный источник, селектор скрыт |
| Всегда | 1 | отсутствует | Единственная сущность выбирается автоматически, селектор скрыт |
| Всегда | 2+ | отсутствует | Селектор показан; fallback `binding → primary → первая управляемая` |
| Всегда | 1+ | валидное значение | Выбранная сущность даёт state и service target |
| Всегда | любое число | сущность исчезла | Предупреждение; временный fallback, сохранённая ссылка не стирается |
Выбор не зависит от текущего `on/off/unavailable`: capability берётся из binding и
реестра HA, а live state обрабатывается отдельно.
## Связи «Управляет другими источниками света» (#84)
| Цель в `controls` | Состояние цели | Service call контроллера | Glow и статистика |
|---|---|---|---|
| `light.*` / `switch.*` | Фактическое состояние entity; группа — `any(on)` | Только реальные entity IDs | Реальный отдельный marker владеет позицией; иначе цель участвует без отдельного пятна у контроллера |
| `marker:<id>` stateful | Состояние ведущей сущности цели | Ведущая сущность цели, с дедупликацией | Позиция, комната, цвет и радиус принадлежат marker-цели |
| `marker:<id>` passive, один controller | Его реальные targets, иначе собственная ведущая entity | Сам `marker:*` никогда не отправляется в HA | Пассивная лампа следует controller и светит в своей позиции |
| Passive, несколько controllers | OR всех активных driver entities | По каждому действию — только его реальные targets | Один источник и один голос комнаты, без дублей |
| Passive без сохранённых links | Всегда `on` | Нет собственного вызова | Постоянный Glow и `1 из 1` |
| Links есть, но все drivers скрыты/disabled/удалены | `off` / dormant | Нет вызова по битой цели | Ссылка сохраняется, пятна нет |
| Прямая entity + `marker:` на тот же stateful source | Одно effective состояние | Один service target | Один источник и один голос |
| Target переведён из «Всегда» в «Авто без источника»/«Никогда» | Dormant | Нет marker-service | Link сохраняется и оживает при возврате «Всегда» |
| Target скрыт или disabled в HA | Dormant | Нет marker-service | Не рисуется и не влияет на комнату |
| Target удалён с плана | Ссылка удаляется атомарно | Нет | Устройство снова можно добавить заново |
| Broken legacy ref | Игнорируется с диагностикой | Нет | Open → Save не уничтожает ссылку |
| Self-link / новый цикл | Запрещено | — | UI не предлагает, backend отклоняет запись/import |
Связи разрешены между комнатами и пространствами одного плана. При экспорте одного
пространства внутренние `marker:`-ссылки ремапятся вместе с marker ID, а внешние
отбрасываются с предупреждением в preview.
## Независимые переключатели отображения
| Настройка | Влияние на световую модель |
|---|---|
| «Значок + состояние» | Подложка устройства показывает его resolved working state; Glow определяется матрицей выше |
| «Значок + состояние и активность» | Дополнительно показывает короткую или постоянную пульсацию; источник света не меняется |
| «Значение + состояние» | Меняет только содержимое маркера; источник света не меняется |
| «Всегда статичный значок» | Блокирует динамику самого значка/подложки, но не отменяет отдельно настроенные Glow, заливку «Свет» и статистику комнаты |
| Glow выключен у пространства | Пятна не рисуются, но источник, состояние комнаты и заливка «Свет» продолжают вычисляться |
| Маркер скрыт / HA-disabled | Маркер и его собственный источник не участвуют в плане независимо от остальных настроек |
## Проверяемый контракт
- Все 36 строк первой таблицы проверяются pure unit-тестом.
- Выбор ведущей сущности, stale fallback, passive OR, dormant links, alias-dedupe,
lifecycle и запрет передачи `marker:*` в HA покрыты отдельными unit-тестами.
- Контекстный селектор, passive-гейтинг и plan-source picker покрыты browser smoke.
- Backend отдельно проверяет новые ссылки, циклы и перенос между пространствами.