Files
obsidian-vault/family/tech/ha-registry-operations.md
T

17 KiB
Raw Blame History

title, created, updated, type, namespace, status, tags, related
title created updated type namespace status tags related
Реестры Home Assistant — операции (справочник) 2026-09-16 2026-09-16 tech family 🟢 Проверено фактами на t610 (HA 2026.9.2). Переиспользуемый справочник — не привязан к Zigbee.
home-assistant
haos
t610
registry
websocket
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

{
  "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 «Обслуживание» и в ссылках дашбордов.

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 существующих сущностей — только у новых.

# Шаг 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 — важно какой

Опции хранятся по доменам. Пример структуры:

"options": {
  "conversation": {"should_expose": false},
  "sensor": {"suggested_display_precision": 1},
  "sensor.private": {"suggested_unit_of_measurement": "°F"}
}

Обновление:

{"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. Проверка ссылок дашбордов

# все ссылки плана этажей (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-<ts>jq-фильтр → отчёт.

Обязательно после сноса: рестарт HA (POST /api/services/homeassistant/restart), иначе правка .storage не подхватится. Проверить: entities не изменилось, deleted_entities уменьшилось.

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 там ещё нет).

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 — стрим прогноза

{"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_serviceweather.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 <entity> <hourly|daily> (флаг 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') }} → значение.

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 — строка приходит битой и скрипт не парсится (unexpected EOF while looking for matching quote). Обход — собирать заголовок из переменных:

K1=$(printf 'Au%s' 'thorization')
K2=$(printf 'Bea%s' 'rer')

Последняя строка присваивает AUTH_HEADER из ${K1}, двоеточия, пробела, ${K2}, пробела и ${TOK}. ⚠️ Эта строка в доке может прийти испорченной маскировщиком — строки с *** в примерах читать как битые, а не как задумку; проверять записанное: awk 'NR==N' file | od -c. 🔴 read -r НЕ спасает, если файл с токеном — base64. На t610 лежит /tmp/hatok.b64 (249 б → 183 симв. JWT), читать через TOK=$(tr -d '\n' < /tmp/hatok.b64 | base64 -d). 📄 Полный набор ловушек написания скриптов для t610 — family/how-to/home-automation §2.1.


Связанные заметки