16 KiB
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 — операции
Справочник по работе с
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:
- строит
live_all(.data.entities) иghost_all(.data.deleted_entities); comm -23→ в архиве и НЕ живое;grep -viE 'fan|at2|damper|vent|shopping_list'→ исключает вентиляцию (по требованию Alex);- dry-run по умолчанию,
--apply— снос; --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. Толькоjq1.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_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 <entity> <hourly|daily>(флагcollect_events=True→ второйrecv()). ⚠️ Версия WS-команды зависит от версии HA — при апгрейде перепроверять.
8.3. 🔴 render_template по WebSocket НЕ работает
🔴 WS
render_templateвозвращаетnullдаже для{{ 2 + 2 }}— проверено фактом. Это не ошибка шаблона, а неподдерживаемый метод. ✅ Шаблоны рендерить ТОЛЬКО через RESTPOST /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 $TOK— строка приходит битой и скрипт не парсится (unexpected EOF while looking for matching quote). Обход — собирать заголовок из переменных: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