--- title: "Реестры Home Assistant — операции (справочник)" created: '2026-09-16' updated: '2026-09-16' type: tech namespace: family status: 🟢 Проверено фактами на t610 (HA 2026.9.2). Переиспользуемый справочник — не привязан к Zigbee. tags: - home-assistant - haos - t610 - registry - websocket related: - '[[family/tech/zigbee-t610-z2m-i-zha]]' - '[[family/how-to/home-automation]]' - '[[family/how-to/ha-automations]]' --- # Реестры Home Assistant — операции > **Справочник по работе с `core.entity_registry` / `core.device_registry` / `core.area_registry`.** Все факты проверены на t610. > ⚠️ Записи реестра — **только WebSocket**. REST `/api/config/*_registry/list` отдаёт **404**. --- ## 1. Структура `core.entity_registry` ```json { "version": 1, "minor_version": 23, "key": "core.entity_registry", "data": { "entities": [...], // ЖИВЫЕ сущности "deleted_entities": [...], // АРХИВ удалённых (их HA не грузит, но UI показывает!) "settings": {...} } } ``` > 🔴 **Ключевой факт: `entities` и `deleted_entities` — разные секции одного файла.** > Призраки живут в `deleted_entities` — поэтому их НЕ видно в `/api/states` и WS-реестре, но они вылезают в UI «Обслуживание» и в ссылках дашбордов. ```bash jq '.data.entities|length' /config/.storage/core.entity_registry # живое jq '.data.deleted_entities|length' /config/.storage/core.entity_registry # архив jq -r '.data.entities[].entity_id' ... # список живых jq -r '.data.deleted_entities[].entity_id' ... # список архивных ``` --- ## 2. WS API — рабочие команды | Задача | Команда | |---|---| | Список сущностей (живых) | `config/entity_registry/list` | | Список устройств | `config/device_registry/list` | | Список зон | `config/area_registry/list` | | Получить сущность | `config/entity_registry/get {entity_id}` | | Переименовать сущность | `config/entity_registry/update {entity_id, new_entity_id}` | | Переименовать устройство | `config/device_registry/update {device_id, name_by_user}` | | Сменить зону устройства | `config/device_registry/update {device_id, area_id}` | | Удалить запись | `config/entity_registry/remove {entity_id}` | | Проблемы (Обслуживание) | `repairs/list_issues` | | Записи интеграций | `config_entries/get` | > ⚠️ `/api/config/*_registry/list` (REST) → **404**. Только WS. > ⚠️ `repairs/list_issues` может быть **пуст**, даже когда UI «Обслуживание» что-то показывает — если источник в `deleted_entities`. --- ## 3. 🔴 Переименование сущности ### 3.1. `name_by_user` НЕ переименовывает `entity_id` Смена имени устройства (`device_registry/update`) меняет только `name_by_user`. HA **не перегенерирует** `entity_id` существующих сущностей — только у новых. ```python # Шаг 1 — имя устройства {"type":"config/device_registry/update","device_id": D, "name_by_user": "new_name"} # Шаг 2 — КАЖДАЯ сущность отдельно {"type":"config/entity_registry/update","entity_id":"sensor.old","new_entity_id":"sensor.new"} ``` ### 3.2. Обход `id_reuse: Identifier values have to increase` Внутренний счётчик реестра. **Штатный случай, не поломка.** Обход — через промежуточное имя: ``` sensor.old → sensor.tmp_xxx → sensor.new ``` ### 3.3. Сироты-автоматизации Тело удалено из `automations.yaml`, но запись в реестре осталась → сущность `unavailable`. `config/entity_registry/remove` → если `id_reuse` → **сначала** `update` c `disabled_by: "user"`, **затем** `remove`. > 🔴 **После любого переименования — проверять ссылки:** > - в `automations.yaml` (все `entity_id:` сверить со `/api/states`); > - в дашбордах (`lovelace.*`), см. §5; > - в маппингах `modbus-bridge`, см. [[family/tech/zigbee-t610-z2m-i-zha]] §5. --- ## 4. 🔴 Опции сущности: `options_domain` — важно какой Опции хранятся по доменам. Пример структуры: ```json "options": { "conversation": {"should_expose": false}, "sensor": {"suggested_display_precision": 1}, "sensor.private": {"suggested_unit_of_measurement": "°F"} } ``` Обновление: ```python {"type":"config/entity_registry/update", "entity_id":"sensor.x", "options_domain":"sensor.private", # ⚠️ ТОЧНЫЙ домен, не общий "options":{"suggested_unit_of_measurement": None}} ``` > 🔴 **ПИТФОЛЛ: `options_domain: "sensor"` даёт `success: true`, но единицу НЕ меняет.** Проверено фактом на H2000_PRO: заход через `"sensor"` не подействовал, после смены на `"sensor.private"` — сработало. > **Всегда читать обратно** (`config/entity_registry/get`) и сверять результат в `/api/states`. ### Кейс: датчик показывает °F вместо °C Причина — ручной override `sensor.private.suggested_unit_of_measurement: "°F"`. Значения тоже конвертируются (55.22 °F = 12.9 °C), поэтому цифры выглядят правдоподобно и ошибку легко не заметить. **Фикс:** сброс в `null` через `options_domain: "sensor.private"`. --- ## 5. Проверка ссылок дашбордов ```bash # все ссылки плана этажей (picture-elements) jq -r '.data.config.views[].sections[].cards[]? | select(.type=="picture-elements") | .elements[]? | select(.entity?!=null) | .entity' \ /config/.storage/lovelace.home_plan | sort -u # затем сверить список со живыми из /api/states ``` > 🔴 **Правило: после сноса призраков — проверять ссылки дашбордов.** План этажей показывает «недоступно» из-за мёртвой ссылки, а не из-за поломки устройства. Дашборды t610: `home_plan` (storage-mode, `/config/.storage/lovelace.home_plan`, карта `/config/www/floorplan/*.svg`), реестр дашбордов — `lovelace_dashboards`. --- ## 6. Контролируемый снос призраков **Принцип: только то, что в архиве И не живое. Никогда по grep — только по точному `entity_id`.** Скрипт `~/tmp-t610/ghost_purge.sh`: 1. строит `live_all` (`.data.entities`) и `ghost_all` (`.data.deleted_entities`); 2. `comm -23` → в архиве и НЕ живое; 3. `grep -viE 'fan|at2|damper|vent|shopping_list'` → исключает вентиляцию (по требованию Alex); 4. dry-run по умолчанию, `--apply` — снос; 5. `--apply` → бэкап `*.bak-ghostpurge-` → `jq`-фильтр → отчёт. **Обязательно после сноса:** рестарт HA (`POST /api/services/homeassistant/restart`), иначе правка `.storage` не подхватится. Проверить: `entities` не изменилось, `deleted_entities` уменьшилось. ```bash scp ~/tmp-t610/ghost_purge.sh root@192.168.2.176:/tmp/ ssh root@192.168.2.176 'bash /tmp/ghost_purge.sh /config/.storage/core.entity_registry' # dry-run ssh root@192.168.2.176 'bash /tmp/ghost_purge.sh /config/.storage/core.entity_registry --apply' ``` > ⚠️ **Питфолл `set -e` в bash:** `comm`/`grep` возвращают 1 при пустом результате и валят скрипт молча. Использовать `set -uo pipefail` (без `-e`) + `|| true` на таких строках. > ⚠️ **На t610 нет `python3`.** Только `jq` 1.8.1 (`/usr/bin/jq`). Скрипты для t610 писать на jq/bash, не на Python. --- ## 7. Питфоллы (сводка) | # | Питфолл | Обход | |---|---|---| | 1 | REST `/api/config/*_registry/list` → 404 | Только WebSocket | | 2 | `name_by_user` не меняет `entity_id` | Переименовывать устройство И сущности отдельно | | 3 | `id_reuse: Identifier values have to increase` | Промежуточное имя: `old → tmp → new` | | 4 | Сирота-автоматизация `unavailable` | `remove`, при `id_reuse` — сначала `disabled_by: user` | | 5 | `options_domain: "sensor"` не меняет единицу | Точный домен: `"sensor.private"` | | 6 | Призраки не видны в API, но видны в UI | Они в `deleted_entities`, а не в `entities` | | 7 | Правка `.storage` без рестарта не действует | `POST /api/services/homeassistant/restart` | | 8 | После сноса призраков дашборд ругается | Проверить ссылки дашбордов (§5) | | 9 | Удаление `device_id` сбрасывает `area_id` | Восстановить через `device_registry/update` | | 10 | На t610 нет `python3` | Скрипты на `jq`/bash | | 11 | `weather/get_forecast` (ед. ч.) и `weather/forecast` не существуют | Мн. ч.: сервис `weather.get_forecasts` либо WS `weather/subscribe_forecast` (ответ — 2 сообщения) | | 12 | `weather.get_forecasts()` в Jinja → `'weather' is undefined` | Вызывать как **ДЕЙСТВИЕ** `action: weather.get_forecasts` + `response_variable`; `variables:` — внутри `actions:` ПОСЛЕ вызова. Helper'ы НЕ нужны | | 13 | WS `render_template` возвращает `null` даже для `{{ 2 + 2 }}` | Шаблоны рендерить через REST `POST /api/template` | | 14 | `write_file` режет строку `Authorization: Bearer *** | Собирать заголовок из переменных: `P1=Authorization; P2=Bearer; AUTH=*** ${P2} ${TOK}"` | | 15 | 🔴 **Fail-safe на `min([99, fc_min])` не работает:** `min([99, 11.1])` = 11.1, а не 99 → проверка отвала датчика через `float(99)` + `min` **не срабатывает никогда** | Отдельный флаг по строковому состоянию: `zont_ok: "{{ states('sensor.x') not in ['unknown','unavailable','none',''] }}"`, затем `eff: "{{ zont if zont_ok else fc_min }}"` | | 16 | `automation.trigger` через WS **не обновляет `last_triggered`** | Прогон подтверждать иначе: значения в `variables`, `logbook.log`, лог HA Core | --- ## 8. 🔴 Прогноз погоды и шаблоны (HA 2026.9.2) ### 8.1. Прогноз НЕДОСТУПЕН ИЗ ШАБЛОНА — главное Проверено четырьмя способами 2026-09-16: | Способ | Результат | |---|---| | атрибут `forecast` у сущности `weather.*` | **`None`** — прогноза в атрибутах нет | | `weather.get_forecasts(...)` **в Jinja-шаблоне** | **`UndefinedError: 'weather' is undefined`** | | `weather.get_forecasts` **как ДЕЙСТВИЕ** (`action:` + `response_variable`) | ✅ **работает** — 48 точек | | сервис `weather.get_forecasts` (мн. число), REST/WS | ✅ работает — 48 ч hourly + 6 дн daily | | WS `weather/subscribe_forecast` | ✅ работает | > ✅ **Как брать прогноз в автоматизации (рабочий путь, 0 helper'ов):** > вызов **действием** с `response_variable`, а вычисления — в `variables:` > ВНУТРИ `actions:` ПОСЛЕ вызова (верхнеуровневые `variables:` вычисляются до > действий и `forecast` там ещё нет). > > ```yaml > actions: > - action: weather.get_forecasts > target: {entity_id: weather.forecast_laki_dom} > data: {type: hourly} > response_variable: wx > - variables: > fc_min: >- > {{ wx['weather.forecast_laki_dom']['forecast'][:24] > | map(attribute='temperature') | min | default(99) | float(99) }} > ``` > > 🔴 **Питфолл (стоил лишней итерации):** вызов сервисов из Jinja-шаблона > (`{{ weather.get_forecasts(...) }}`) действительно убран → `'weather' is undefined`. > Но это **не значит**, что прогноз недоступен: как **действие** он работает. Не > строить обходной слой (`input_number` + обновлятор), не проверив `action:` + `response_variable`. ### 8.2. WS `weather/subscribe_forecast` — стрим прогноза ```python {"type": "weather/subscribe_forecast", "entity_id": "weather.forecast_laki_dom", "forecast_type": "daily"} # ⚠️ именно forecast_type, НЕ type; или "hourly" ``` | Способ | Результат | |---|---| | `POST /api/services/weather/get_forecast` (ед. ч., REST) | **400 Bad Request** | | WS `call_service` → `weather.get_forecast` (ед. ч.) | `not_found` | | WS `weather/forecast` | `unknown_command` | | сервис **`weather.get_forecasts`** (мн. ч.) | ✅ работает | | WS **`weather/subscribe_forecast`** | ✅ работает | > 🔴 **ПИТФОЛЛ: ответ подписки приходит ДВУМЯ сообщениями** — сначала `result` (с `result: null`), затем отдельное `event` с `forecast`. Клиент, читающий только `result`, вернёт **0 точек**. > Реализация: `~/tmp-t610/ha_ws.py forecast ` (флаг `collect_events=True` → второй `recv()`). > ⚠️ Версия WS-команды зависит от версии HA — при апгрейде перепроверять. ### 8.3. 🔴 `render_template` по WebSocket НЕ работает > 🔴 **WS `render_template` возвращает `null` даже для `{{ 2 + 2 }}`** — проверено фактом. Это не ошибка шаблона, а неподдерживаемый метод. > ✅ **Шаблоны рендерить ТОЛЬКО через REST** `POST /api/template`, тело `{"template": "..."}`. Через REST: `{{ 2 + 2 }}` → `4`, `{{ states('sensor.x') }}` → значение. > > ```bash > jq -n --arg t '{{ 2 + 2 }}' '{template: $t}' > /tmp/tpl.json > # затем POST /api/template (с t610 изнутри: http://172.30.32.1/api/template) > ``` ### 8.4. ⚠️ Питфолл окружения: `write_file` портит строку с `Bearer` > ⚠️ При генерации скриптов **`write_file` режет литерал `Authorization: Bearer $TOK`** — строка приходит битой и скрипт не парсится (`unexpected EOF while looking for matching quote`). Обход — собирать заголовок из переменных: > > ```bash > TOK=$(cat /tmp/.hatok) > P1=Authorization > P2=Bearer > AUTH_HDR=*** ${P2} ${TOK}" > ``` --- ## Связанные заметки - [[family/tech/zigbee-t610-z2m-i-zha]] — Zigbee на ZHA, §13 вычистка призраков - [[family/how-to/home-automation]] — t610: топология, аддоны, доступ - [[family/how-to/ha-automations]] §7 — греющий кабель ввода водыс §8 (прогноз для греющего кабеля) - [[family/plans/t610-heating-cable-automation]] — план автоматизации на базе §8.1