# ТЗ #54 — контекстные связи Zigbee на плане - Issue: https://github.com/Matysh/houseplan-card/issues/54 - Приоритет: P3 - Тип: feature - Трек: полный — новый UX-контракт, провайдеры, настройки, privacy и performance - Stage 0: capability research завершён, решение **GO с условиями** - Контракты проверены: 2026-08-31 - UX скорректирован решением владельца: 2026-09-04 ## 1. Сценарий Персона — Home admin (HA enthusiast) из `docs/SCOPE.md`. Поверхность — полная карточка House Plan в режиме View на устройстве с мышью. Момент — пользователь ищет пространственную причину нестабильной работы конкретного Zigbee-устройства и хочет увидеть его наблюдаемых соседей относительно комнат и стен, не переходя в отдельный сетевой граф. ## 2. Что человек увидит до и после До: House Plan показывает LQI устройства и комнаты, но не показывает, с какими Zigbee-узлами связано выбранное устройство и где они находятся на плане. После: если пользователь заранее включил диагностику в общих настройках и навёл мышь на размещённое Zigbee-устройство, House Plan временно показывает только его непосредственные связи; без наведения план выглядит как раньше. ## 3. Проблема Полный mesh-граф поверх жилого плана быстро превращается в нечитаемую паутину: число линий растёт вместе с сетью, связи закрывают геометрию и устройства, а пользователь всё равно диагностирует один проблемный endpoint за раз. При этом ZHA и Zigbee2MQTT дают разные по свежести и стоимости snapshots, а их neighbor records нельзя честно называть фактическим маршрутом каждого пакета. Нужен opt-in диагностический слой с progressive disclosure: сеть не видна в обычном состоянии, а непосредственное окружение одного устройства появляется только на время hover. ## 4. Scope и non-scope ### 4.1 Входит в #54 - одна выключенная по умолчанию опция в «Общих настройках»; - provider-neutral topology model и точное сопоставление узлов с размещёнными маркерами; - ZHA cached snapshot через штатный admin-only WebSocket API; - Zigbee2MQTT raw network map через штатные HA MQTT surfaces и явно заданные base topics; - ручное получение/обновление provider data из «Общих настроек»; - только непосредственные связи наведённого устройства; - локальные линии внутри текущего пространства и временный счётчик связей в другие пространства; - состояния загрузки, устаревшего snapshot, partial data, unsupported и error; - lazy loading, общий memory cache, privacy guards, i18n, unit/backend/browser, golden и performance coverage. ### 4.2 Не входит в #54 - постоянный слой или кнопка «показать весь граф»; - вычисление либо изображение «маршрута до координатора»; - tap, long press или другой topology-жест на touch/pen; - интерактивная панель cross-space links и переход к удалённому marker; - линии между независимыми пространствами, этажами или экземплярами карточки; - принудительный ZHA radio scan без completion-aware upstream API; - background/periodic scan, scan по hover, HA state tick, смене пространства или созданию второй карточки; - pairing, reconfigure, bind/unbind, удаление устройств и изменение сети; - автоматическое создание маркеров, matching по имени/model/friendly name; - RF-прогноз по стенам, причинные утверждения, history и графики; - topology в kiosk, редакторах и `houseplan-space-card`; - доступ non-admin в обход штатных прав Home Assistant. ## 5. Контракт поведения ### 5.1 Общая настройка В «Общих настройках» появляется переключатель «Показывать связи Zigbee при наведении на устройство». - missing/invalid/`false` означает выключено; - значение сохраняется вместе с общими настройками и действует для всех пространств полной карточки; - выключенное состояние не загружает topology runtime, не вызывает provider API и не строит topology/mapping; - включение разрешает отображение, но само по себе не запускает radio scan; - выключение немедленно убирает hover-слой. Уже начатый явный Z2M scan не прерывается небезопасным unsubscribe: ответ может завершить shared cache, но UI остаётся скрыт; - non-admin не видит активный control и не инициирует provider requests, даже если настройку ранее сохранил admin. ### 5.2 Получение данных Под переключателем, только в раскрытом включённом состоянии, показываются provider controls и статус последнего snapshot. - ZHA: действие «Прочитать данные ZHA» читает cached `zha/devices`. Оно не обещает свежий radio scan и не показывает фиктивный момент сканирования; - Zigbee2MQTT: для каждой настроенной instance задаётся base topic и доступно действие «Обновить карту Zigbee2MQTT»; - перед Z2M scan показывается предупреждение: операция занимает от 10 секунд до 2 минут и временно может снизить отзывчивость сети; - provider actions явные и независимые. Ошибка одного provider не скрывает пригодный snapshot другого; - статус сообщает provider, время получения House Plan, stale/partial и человекопонятную ошибку. IEEE, raw payload и внутренние identifiers не показываются; - snapshot старше пяти минут помечается устаревшим, но не обновляется сам. ### 5.3 Hover одного устройства Связи разрешены только если одновременно выполняются условия: 1. настройка включена; 2. текущая поверхность — View полной карточки, не kiosk; 3. последняя pointer modality — реальная мышь с fine/hover capability; 4. существует пригодный snapshot; 5. курсор находится над drawable live marker, точно сопоставленным Zigbee node. Тогда House Plan берёт только incident links выбранного node — пары, в которых этот node является одним из концов. Весь остальной граф не строится и не рисуется. Это наблюдаемые непосредственные соседи, а не вычисленный путь до координатора. Если второй endpoint точно сопоставлен видимому marker текущего пространства, рисуется одна связь между маркерами, а marker-сосед получает лёгкое временное выделение. Если второй endpoint находится в другом пространстве, линия не рисуется; рядом с исходным marker появляется компактный неинтерактивный счётчик «ещё N в других пространствах». Unmatched, ambiguous, hidden, removed и HA-disabled endpoints не рисуются и в cross-space count не входят. Hover-слой очищается при pointerleave marker, переходе на touch/pen, смене пространства или режима, remount/disconnect и выключении настройки. Он не остаётся закреплённым после клика. Existing click, long-press, hover tooltip, device action и dispatched-action animation не меняются. ### 5.4 Touch, keyboard и другие режимы - Touch/pen topology не показывают и не получают нового жеста. Tap и long press продолжают действовать по существующему контракту устройства. - Keyboard focus не имитирует mouse hover и не вводит новую навигацию по плану; это соответствует текущей accessibility-границе `docs/SCOPE.md`. - Plan, Devices и Decor не показывают линии, соседнее выделение или cross-space count независимо от сохранённой настройки. - Kiosk и `houseplan-space-card` не загружают runtime и не делают запросы. - Несколько полных карточек могут переиспользовать один snapshot, но каждая самостоятельно владеет своим mouse-hover состоянием. ## 6. UX и визуальная семантика > **Заменено #464:** первоначальный контракт ниже оставлен как исторический > контекст. Актуальный порядок слоёв и оформление unknown-LQI пунктира задаёт > [ТЗ #464](464-zigbee-topology-layer-order.md): активная топология находится > над названиями комнат и посторонними маркерами, но под полными маркерами > устройств — концов реально показанных локальных связей. - В обычном View при включённой функции нет постоянной легенды, badges или линий. Визуальный шум равен выключенному состоянию. - Связи находятся в отдельном pointer-transparent layer: над архитектурой и decor, но под device markers и их tooltips. - Линия соединяет фактические центры двух уже спроецированных маркеров, не участвует в fit/bounds и не меняет геометрию плана. - Цвет использует каноническую непрерывную шкалу House Plan LQI. Unknown LQI — нейтральный пунктир. Provider error никогда не изображается как «плохая» красная связь. - Для двух directional observations визуал линии использует наблюдение от наведённого node к соседу; обратное значение не усредняется и остаётся в нормализованной модели для диагностики/тестов. - Толщина/opacity допускают ограниченное усиление по LQI, но сохраняются в читаемом screen-space диапазоне при любом zoom. - Coordinator/router/end не получают постоянных новых обозначений. Во время hover сосед может получить один нейтральный halo, не похожий на alert, selection или dispatched-action animation. - Cross-space count живёт только пока активен тот же hover, имеет `pointer-events:none`, не становится частью hit area и не предлагает tap. - При отсутствии incident links или drawable соседей план не показывает ложную линию или toast. Причина и summary доступны в provider status общих настроек. ## 7. Подтверждённые provider-контракты Stage 0 ### 7.1 ZHA Проверены Home Assistant Core `2026.8.3` и `dev@6ad726ba56d517e56536cdd6fa2ba9f358bbc0ef`, Home Assistant Frontend `dev@6b7d3871754dada4d599e4fecc0d5ca0442cf22b`, а также zigpy topology. 1. Admin-only `zha/devices` возвращает `device_reg_id`, IEEE, роль, `neighbors`, `routes` и directional LQI. 2. `device_reg_id` даёт exact HA Device Registry mapping. 3. Штатный HA frontend строит граф из `neighbors`/`routes`, сохраняет два directional LQI пары и использует route только вместе с реальным neighbor next hop. 4. zigpy обычно сканирует topology раз в четыре часа. `zha/devices` читает кешированный snapshot, который может быть старым или неполным. 5. `zha/topology/update` в проверенном контракте запускает background scan, но не возвращает completion/result. Поэтому #54 его не вызывает. ### 7.2 Zigbee2MQTT Проверены Zigbee2MQTT `2.13.0` и `master@fcbb7ff44bdc05a16e95d3472a81d110646cc17b`. 1. Документированный request `BASE/bridge/request/networkmap` с `{"type":"raw","routes":false,"transaction":"…"}` отвечает на `BASE/bridge/response/networkmap` и возвращает nodes/links. 2. Node содержит IEEE, friendly name, роль и scan failures. Link содержит source, target, directional LQI и relationship. 3. Scan занимает 10 секунд — 2 минуты и может временно снизить отзывчивость Zigbee-сети; только явное действие пользователя вправе его запускать. 4. `routes:false` достаточно для непосредственных neighbor edges и уменьшает нагрузку. 5. HA MQTT предоставляет admin-only WebSocket subscription, а `mqtt.publish` — штатное HA action. Браузер не подключается к broker напрямую. 6. Base topic конфигурируем и не выводится надёжно из HA registry. Для каждой instance пользователь задаёт его явно; default `zigbee2mqtt` проверяется по retained `bridge/info` до scan. ## 8. Модель данных и миграция ### 8.1 Сохраняемая настройка Концептуальная форма общих настроек: ```ts type ZigbeeTopologySettings = { enabled?: boolean; // missing/invalid => false z2mBaseTopics?: string[]; // normalized exact base topics }; ``` Точное внутреннее имя поля вправе изменить реализация без изменения UX. Настройка принадлежит плану целиком, а не пространству. Повторное сохранение несвязанных общих настроек не удаляет её. Дубликаты/пустые/невалидные base topic нормализуются fail-closed. Миграции существующих планов нет: отсутствие объекта означает выключенную функцию. Downgrade игнорирует неизвестное поле. Выключение функции не удаляет base topics, чтобы повторное включение не требовало настройки заново. ### 8.2 Runtime model Provider payload не проходит в render: ```ts type ZigbeeProvider = 'zha' | 'z2m'; type ZigbeeTopology = { provider: ZigbeeProvider; instanceId: string; obtainedAt: number; freshness: 'provider-cache' | 'fresh-scan'; nodes: ZigbeeTopologyNode[]; links: ZigbeeTopologyLink[]; warnings: ZigbeeTopologyWarning[]; }; type ZigbeeTopologyNode = { key: string; // provider instance + normalized IEEE ieee: string; // private runtime value deviceId?: string; role: 'coordinator' | 'router' | 'end' | 'unknown'; available?: boolean; }; type ZigbeeDirectionalObservation = { lqi?: number; // provider-native 0..255 relationship?: string; activeRoute?: boolean; }; type ZigbeeTopologyLink = { a: string; b: string; aToB?: ZigbeeDirectionalObservation; bToA?: ZigbeeDirectionalObservation; }; type ZigbeeTopologyWarning = { code: | 'invalid_payload' | 'duplicate_link' | 'self_link' | 'unmatched_device' | 'ambiguous_placement' | 'provider_scan_failure'; nodeKey?: string; }; ``` Snapshot остаётся memory-only и не входит в экспорт, backup, diagnostics, support report, localStorage или console. Shared cache key включает HA connection, provider и instance; concurrent requests дедуплицируются. ## 9. Нормализация и mapping - IEEE нормализуется до одного canonical representation; raw/friendly name не используется как identity. - Одинаковая unordered pair хранится один раз; directional observations не усредняются и не выдумываются из отсутствующей стороны. - Self links и exact duplicates отбрасываются с warning. - Route destination не создаёт прямую линию: route может только пометить существующий neighbor next hop. - Некорректный/неизвестный LQI становится `undefined`, а не `0`. - Payload ограничивается по bytes, nodes и links до дорогостоящей обработки. Для каждого node сначала определяется exact HA `deviceId`, затем marker: 1. единственный видимый live marker с binding `device:`; 2. если его нет — единственный видимый entity marker, чья entity принадлежит тому же HA device; 3. несколько равноправных markers дают `ambiguous_placement` и не рисуются; 4. hidden, removed и HA-disabled markers не являются drawable endpoints; 5. rebind, перемещение и смена пространства пересчитывают mapping по тому же snapshot без provider request. Для Z2M обычный identifier `zigbee2mqtt_`, а bridge/coordinator — `zigbee2mqtt_bridge_`. Exact Entity Registry owner допускается как fallback. User-overridden identifiers могут оставить node unmatched; matching по friendly name, entity name или model запрещён. ## 10. Fetch, cache, lifecycle и permissions - Runtime загружается динамически только после сохранённого `enabled:true` на доступной admin View/full-card поверхности. - Provider data загружается только явными controls в общих настройках; hover никогда не вызывает fetch или scan. - Shared cache — per HA connection + provider + instance, memory-only, с in-flight dedupe. Snapshot старше пяти минут stale, но пригоден до явного обновления или конца connection lifecycle. - Z2M transaction обязателен; timeout — 150 секунд. Retained ответ до request, ответ с чужой transaction и late response игнорируются; subscription всегда очищается после success/error/timeout. - Hide/disable, изменение binding, удаление marker, смена режима/пространства и disconnect синхронно инвалидируют draw mapping/hover, но не запускают scan. - Provider/API failure не ломает план, device actions, LQI badges/fill и остальные режимы. - Если HA отказывает в admin API, adapter fail-closed и не пытается обойти права через backend House Plan. ## 11. Ограничения достоверности - Neighbor tables — наблюдаемый snapshot, а не лог текущих packet routes. - Спящие end devices могут отсутствовать или иметь устаревшую связь. - Пара может наблюдаться только с одной стороны; это нормальный результат. - Coordinator/router failures могут оставить частичный граф. - Диапазон LQI 0..255 общий, но providers не гарантируют одинаковую методику и момент измерения. Межпровайдерный рейтинг качества не строится. - Геометрия стен используется только как визуальный контекст. House Plan не утверждает, что конкретная стена вызвала слабый сигнал. ## 12. i18n Новые пользовательские строки добавляются синхронно в `en`, `ru`, `de`, `fr`: - название и help переключателя; - названия ZHA/Z2M provider controls; - статус «данные не загружены / получены / устарели / частичные»; - предупреждение о длительности и нагрузке Z2M scan; - ошибки permission, unsupported, timeout, invalid base topic/payload; - cross-space count с pluralization; - нейтральное summary «связи не найдены / часть узлов не показана». Тексты используют «наблюдаемые связи» и «полученные данные», а не «текущий маршрут». Internal identifiers и raw provider errors локализованную границу не пересекают. ## 13. Критерии приёмки - **AC1 — default/off.** Новый и существующий план без валидного `enabled:true` визуально и по поведению совпадает с текущим View; topology runtime/API не вызываются. Доказательство: unit config tests + browser request spy, Codex. - **AC2 — persistence.** Admin может включить/выключить настройку в «Общих настройках»; она переживает reload, относится ко всем spaces и не теряется при сохранении соседнего поля. Доказательство: config/store unit + browser smoke, Codex; backend suite на Linux CI при изменении store schema. - **AC3 — permissions.** Non-admin не получает активного control, не вызывает ZHA/MQTT API и не видит слой при сохранённом `true`. Доказательство: unit permission matrix + browser smoke, Codex. - **AC4 — contextual only.** При включённой функции и пригодном snapshot без hover нет линий, постоянных badges или легенды; hover одного mapped Zigbee marker рисует только incident links этого node. Доказательство: resolver unit + browser smoke + golden light/dark, Codex. - **AC5 — no inferred route.** Реализация не строит путь до coordinator и не добавляет несуществующие edges из route destinations. Доказательство: provider fixtures + mutation witness, Codex. - **AC6 — same-space mapping.** Рисуются только links с двумя exact drawable endpoints текущего space; сосед получает transient neutral highlight. Доказательство: mapping unit + browser smoke + golden, Codex. - **AC7 — cross-space truth.** Remote endpoint не создаёт линию; рядом с hovered marker временно показан неинтерактивный count только drawable remote endpoints, без направления «выше/ниже». Доказательство: classification unit + browser smoke + golden, Codex. - **AC8 — ambiguous/hidden lifecycle.** Unmatched, ambiguous, hidden, removed и HA-disabled endpoints не рисуются и не входят в cross-space count; rebind и перемещение пересчитывают mapping без scan. Доказательство: unit + browser smoke, Codex. - **AC9 — modality and modes.** Touch/pen, keyboard focus, editors, kiosk и `houseplan-space-card` не показывают topology и не получают нового действия; существующие tap/long-press/click работают без изменений. Доказательство: browser mouse/touch/mode matrix + existing device-action smokes, Codex. - **AC10 — cleanup.** Pointerleave, mouse→touch/pen, mode/space change, disconnect/remount и setting off очищают линии, highlight и count в том же lifecycle turn. Доказательство: state-machine unit + browser smoke, Codex. - **AC11 — pointer ownership.** Линии, highlight и count pointer-transparent, не меняют device hit area, hover tooltip, click action, pan/zoom или fit. Доказательство: DOM/computed-style assertions + browser gestures smoke, Codex. - **AC12 — LQI visual.** Hover→neighbor observation использует каноническую continuous LQI colour; unknown — нейтральный пунктир; reverse observation не усредняется. Stroke остаётся читаемым в screen space на min/default/max zoom. Доказательство: pure resolver unit + golden zoom/theme matrix, Codex. Порядок слоёв в AC6 и внешний casing unknown-LQI линии в AC12 впоследствии уточнены #464; provider/mapping-семантика этих критериев не меняется. - **AC13 — ZHA contract.** Явное действие читает admin-only `zha/devices`, нормализует fixtures и не вызывает `zha/topology/update`; UI не заявляет неизвестную scan freshness. Доказательство: adapter unit/contract fixture + request spy, Codex. - **AC14 — Z2M contract.** Только подтверждённая instance публикует raw `routes:false` request после явного предупреждения; transaction, 150 s timeout, retained/foreign/late response и cleanup соблюдены. Доказательство: adapter unit with fake clock/MQTT + HA MQTT contract fixture на Linux CI (не House Plan backend), Codex/CI. - **AC15 — no implicit work.** HA state ticks, hover, space switch, rebind и второй экземпляр карточки не запускают provider fetch/scan; concurrent explicit requests дедуплицируются. Доказательство: call-count unit + browser smoke + mutation witnesses, Codex. - **AC16 — failures.** Unsupported API, permission denial, invalid payload, partial provider failure и timeout дают локализованный status и не ломают plan либо snapshot другого provider. Доказательство: unit/error fixtures + browser smoke, Codex. - **AC17 — privacy.** IEEE/raw topology не попадают в saved config, exports, backup, diagnostics, support report, localStorage или console. Доказательство: serialization/privacy tests + reviewer code audit, Codex/Claude. - **AC18 — bounds.** Payload limits, edge cap и incident-only render сохраняют установленный performance budget на fixtures 20/100/500 nodes и dense malformed graph. Доказательство: benchmark + performance smoke, Codex/CI. - **AC19 — lazy boundary.** Выключенный initial View не включает topology runtime в initial graph и сохраняет действующий bundle ceiling; включённый путь загружает отдельный chunk один раз. Доказательство: chunk/import unit + bundle budget + browser network spy, Codex. - **AC20 — i18n.** Все новые видимые состояния имеют parity en/ru/de/fr, корректные plurals и не показывают raw provider text. Доказательство: i18n parity/dead-key tests + golden, Codex. ## 14. План автотестов 1. Добавить pure-unit suites для settings normalization, normalized graph, directional pair merge, incident-edge selection, exact registry/marker mapping, cross-space classification, payload bounds и cache/in-flight dedupe. 2. Добавить обезличенные ZHA и Z2M fixtures: one-way/two-way/unknown LQI, sleeping end device, coordinator, multiple instances, duplicates/self links, partial router failure, wrong base topic и malformed/dense payload. 3. Adapter tests с fake clock/transport доказывают ZHA read-only path и полную Z2M transaction/timeout/retained/late/cleanup матрицу. Для защит implicit scan, matching и cleanup зарегистрировать mutation-gate entries. 4. Новый browser smoke `smoke_zigbee_topology_hover.mjs`: off/no requests; admin enable/persistence; explicit load; mouse hover incident-only; leave и lifecycle cleanup; same/cross-space; touch/pen/mode/kiosk/static absence; click/pan/zoom regression; errors и second-card dedupe. 5. Golden-сценарии light/dark: local strong/weak/unknown links, cross-space count, min/default/max zoom и partial-data status в общих настройках. Baselines принимаются только через штатный Linux reviewed workflow. 6. Performance fixture 20/100/500 nodes измеряет normalize/map, first hover, repeated hover и dense-invalid rejection; обычный HA tick сохраняет нулевую topology работу. Бюджеты фиксируются до S7 на измеренном dev-стенде. 7. Если реализация добавляет House Plan backend surface, выполнить полный HA harness на Linux CI; Windows native результат не считать backend-доказательством. 8. Перед S7 обязательны `npm run typecheck`, `npm test`, `npm run build`, `npm run bundle:sync`, `npm run bundle:budget`, `node scripts/check-docs.mjs`, `node scripts/smoke-select.mjs --base origin/dev --head HEAD`, выбранные smoke, `node scripts/no-new-any.mjs --base origin/dev --head HEAD` и относящиеся mutation runs. Golden/performance полным набором — перед бетой, targeted witnesses — до код-ревью. ## 15. Затронутые файлы и модули Ожидаемые продуктовые поверхности: - `src/types.ts` и нормализация общих настроек — optional topology settings; - новый pure topology module — provider-neutral types, validation, normalization, incident-link selection и exact marker mapping; - новый lazy topology runtime/provider adapters — shared cache, ZHA WebSocket, Z2M MQTT transaction/lifecycle и permission guards; - `src/houseplan-card.ts` и device render seams — lazy lifecycle, mouse-hover ownership, same-space edge projection, cross-space count и cleanup; - `src/houseplan-editor-runtime.ts` — переключатель, provider controls и status в «Общих настройках»; - device/dialog styles — pointer-transparent edge/highlight/count layer и responsive provider settings; - `src/i18n/*.json` и при необходимости тематический lazy i18n chunk — новые строки en/ru/de/fr. Ожидаемые доказательные и документальные поверхности: - `test/**` — settings/model/mapping/cache/provider/privacy unit suites и обезличенные fixtures; - `demo/smoke_zigbee_topology_hover.mjs`, golden matrix/baselines и topology performance fixture/budget; - `scripts/mutation-gate.mjs` и smoke-link registry — negative witnesses и связь нового smoke с изменёнными модулями; - `docs/UX-MODES.md`, `docs/SCOPE.md`, пользовательское руководство, оба changelog и при необходимости privacy/config compatibility docs; - manifest-driven generated bundle/chunks после `npm run bundle:sync`. `custom_components/houseplan/**/*.py` не планируется: штатных HA WebSocket/MQTT surfaces достаточно. Если реализация докажет обратное, появление House Plan backend считается изменением заявленного технического скоупа и требует явной фиксации до код-ревью. ## 16. Риски - ZHA snapshot может быть старым, а completion-aware refresh отсутствует. - Z2M scan дорогой и зависит от корректного base topic/MQTT permissions. - Один HA device может иметь несколько маркеров, а custom discovery identifiers могут разрушить exact mapping. - Пользователь может принять neighbor snapshot за реальный маршрут трафика. - Dense/ошибочный payload способен перегрузить normalization или DOM без ранних limits и incident-only resolver. - Hover-only UX недоступен на touch; это осознанное ограничение диагностической функции, а не скрытая попытка переопределить device gestures. ## 17. Откат Функция fail-closed и выключена по умолчанию. Операционный откат — отключить настройку: runtime перестаёт загружаться/рисоваться, provider requests не выполняются, текущий план и LQI остаются прежними. Каждый provider adapter можно отключить независимо без изменения данных плана. Snapshot memory-only, поэтому cleanup пользовательских данных и миграция назад не требуются; сохранённые base topics безопасно игнорируются старой версией. ## 18. Release-артефакты - запись со ссылкой на #54 в `docs/CHANGELOG.md` и `docs/CHANGELOG.ru.md` в том же user-visible implementation commit; - актуализация `docs/UX-MODES.md`, `docs/SCOPE.md`/J7 и пользовательского руководства: default-off, mouse-only, incident-only, manual provider refresh; - обновление описания общих настроек и privacy/support boundary; - reviewed golden screenshots для topology hover и настроек; - release note прямо сообщает ограничения: admin, mouse hover, snapshot, без ZHA forced scan и без touch interaction. ## 19. Предположения автора — можно менять на ревью без решения владельца - Внутреннее поле настроек группируется в `settings.zigbee_topology`; точное имя не является продуктовым контрактом. - Provider-neutral runtime и adapters живут в отдельном lazy chunk; House Plan backend не добавляется, пока штатных HA WebSocket/MQTT surfaces достаточно. - Default Z2M base topic — `zigbee2mqtt`; несколько instances представлены списком exact base topics. - Stale threshold — пять минут; он меняет только status, не запускает refresh. - Z2M hard timeout — 150 секунд, `routes:false`. - Line visual использует LQI направления hovered→neighbor; reverse observation хранится, но не требует второго параллельного stroke. - Cross-space detail ограничен неинтерактивным count; список и навигация могут стать отдельной задачей только после полевого запроса. - Точные числовые performance budgets устанавливаются до S7 по измеренному dev-стенду для уже перечисленных fixtures, а не угадываются на этапе ТЗ. ## 20. Источники Stage 0 - Home Assistant Core ZHA WebSocket API: https://github.com/home-assistant/core/blob/dev/homeassistant/components/zha/websocket_api.py - Home Assistant Frontend ZHA data contract: https://github.com/home-assistant/frontend/blob/dev/src/data/zha.ts - Home Assistant Frontend graph normalization: https://github.com/home-assistant/frontend/blob/dev/src/panels/config/integrations/integration-panels/zha/zha-network-data.ts - zigpy topology scanner: https://github.com/zigpy/zigpy/blob/dev/zigpy/topology.py - Home Assistant MQTT WebSocket subscription: https://github.com/home-assistant/core/blob/dev/homeassistant/components/mqtt/__init__.py - Home Assistant `mqtt.publish` action: https://www.home-assistant.io/actions/mqtt.publish/ - Zigbee2MQTT networkmap API: https://www.zigbee2mqtt.io/guide/usage/mqtt_topics_and_messages.html#zigbee2mqttbridgerequestnetworkmap - Zigbee2MQTT implementation/model: https://github.com/Koenkk/zigbee2mqtt/blob/master/lib/extension/networkMap.ts and https://github.com/Koenkk/zigbee2mqtt/blob/master/lib/types/api.ts