[2026-09-15] eagle: family/how-to/home-automation.md family/plans/t610-zigbee-ids-battery-freshsensors-modbus.md family/tech/ha-registry-operations.md family/tech/zigbee-t610-z2m-i-zha.md

This commit is contained in:
Alexey Martemyanov
2026-09-15 23:42:01 +06:00
parent ee86207496
commit 5252cc00fe
4 changed files with 311 additions and 14 deletions
+1
View File
@@ -48,6 +48,7 @@ updated: 2026-09-15 (✅ поздний вечер — Zigbee: +7 новых TS0
> 🔴 **2026-09-16 (ночь): вычищены 185 призраков Z2M** из архива реестра (`deleted_entities` 360 → 175). Призраки живут **именно в архиве**, а не в `entities` — UI «Обслуживание» их показывал, `/api/states` и WS-реестр нет. Живые (572) не тронуты. Снесены также остатки аддонов `go2rtc`/`file_editor`/`samba_share` и старые `switch_as_x`-обёртки.
> 🔴 **План этажей (`home-plan`) ссылался на снесённого призрака** `light.smart_light_stairs_l1` → ошибка на карте. Исправлено на `light.light_stairs_left`. Проверять ссылки плана после сноса: `~/tmp-t610/fix_plan_stairs.sh`.
> 🔴 **H2000_PRO отдавал °F** — ручной override `sensor.private.suggested_unit_of_measurement: "°F"` у трёх сущностей. Сброшено через WS с **`options_domain: "sensor.private"`** (через `"sensor"` update проходит, но НЕ меняет). Стало 12.9 / 28.2 / 30.2 °C.
> 📄 **Реестры HA (переименование, опции, снос призраков) — отдельный справочник:** [[family/tech/ha-registry-operations]].
>
> 🔴 **Ключевой факт:** переименование HA-сущности **молча ломает** маппинг bridge → slave отдаёт `0`. Проверено на живом случае: slave 100 (кабинет) отдавал `0`, после правки — `23.97`. Правило: **переименовал сущность → проверь `data/config.template.tmpl` + `rebuild` + ссылки в `automations.yaml`.**
>
@@ -1,10 +1,10 @@
---
title: "План и результат: читаемые ID Zigbee, алерты батарей, виртуальные Modbus-датчики"
created: '2026-09-15'
updated: '2026-09-15'
updated: '2026-09-16 (ночь: добавлены этапы 5–6 — снос призраков реестра, починка плана этажей и H2000_PRO)'
type: plan
namespace: family
status: 🟢 ВЫПОЛНЕНО 2026-09-15 (поздний вечер). Все 4 шага закрыты, проверено фактами.
status: 🟢 ВЫПОЛНЕНО 2026-09-16. Шаги 14 закрыты 2026-09-15; 5–6 (вычистка призраков + починка UI) закрыты 2026-09-16 ночью. Всё проверено фактами.
tags:
- t610
- zigbee
@@ -289,19 +289,38 @@ ssh root@192.168.2.176 'ha apps rebuild local_modbus-bridge && ha apps start loc
**Скрипты:** `~/tmp-t610/{gard_step1,gard_step2,gard_step2b,gard_fix,fix_aut_entid}.py`.
**Коммит vault:** `db20bb8`.
### ⚠️ НЕ ЗАКРЫТО (на момент записи)
### ЗАКРЫТО (2026-09-16, ночь): «Office temperature sensor battery недоступно»
Alex сообщил: «**Office temperature sensor battery** — HA ругается что у устройства нет уникального id и оно недоступно».
Alex: «Office temperature sensor battery — HA ругается что у устройства нет уникального id и оно недоступно», затем «light.smart_light_stairs_l1 — тоже недоступен».
**Проверенопризрака в бэкенде НЕТ:**
- `sensor.office_temperature_sensor*` в `/api/states` — отсутствует;
- в реестре сущностей (572 записи) — нет упоминаний;
- MQTT-сущности без `unique_id` — **0**;
- MQTT-устройства-призраки Z2M — 4, все легитимные (3 сниффера + H2000);
- config entries `mqtt`/`zha`/`generic` — 3, все `loaded`, призраков нет;
- лог HA Core на `unique_id`/`office` — пусто.
**Причина найденазаписи лежали в архиве реестра**, а не в runtime: `core.entity_registry → data.deleted_entities`. Поэтому `/api/states`, WS-реестр и `repairs/list_issues` их не видели, а UI «Обслуживание» — видел.
**Вывод:** источник сообщения — UI-кэш (браузер/приложение) либо дашборд-карточка. Требуется уточнение от Alex: **в каком именно экране ругается** (Настройки → Устройства → MQTT / дашборд / Developer Tools / лог автоматизаций). Следующая сессия: спросить экран и вычистить прицельно.
**Решение:** контролируемый снос скриптом `~/tmp-t610/ghost_purge.sh` (исключает живое + вентиляцию, dry-run по умолчанию).
- Снято **185** записей: 6 точечно (`office_temperature_sensor_*` ×5 + `smart_light_stairs_l1`) + 179 пачкой.
- Архив реестра 360 → **175**. Живых 572 — не тронуто.
- На dry-run поймано 3 живых `switch_as_x`-записи (`light.night_light_shower_2`, `light.smart_light_office_left/right`) — исключены.
**Сопутствующее (этап 6):**
- **План этажей** (`home-plan`) ругался «недоступно» — ссылался на снесённого `light.smart_light_stairs_l1`. Исправлено на `light.light_stairs_left`. Все 29 ссылок валидны.
- **H2000_PRO** отдавал °F — ручной override `sensor.private.suggested_unit_of_measurement: "°F"`. Сброшено через WS с `options_domain: "sensor.private"` (⚠️ через `"sensor"` — `success: true`, но НЕ меняет). Стало 12.9 / 28.2 / 30.2 °C.
**Бэкапы:** `core.entity_registry.bak-ghostpurge-20260916-002503`, `.bak-purge-20260916-000530`, `.bak-ghosts-20260916-000329`, `lovelace.home_plan.bak-fixplan-20260916-003231`.
**Детали:** [[family/tech/zigbee-t610-z2m-i-zha]] §13, [[family/tech/ha-registry-operations]].
---
## 4a. ✅ ЭТАП 6 — починка UI после сноса призраков (2026-09-16)
| Проблема | Причина | Фикс |
|---|---|---|
| План этажей: «недоступно» на карте | `state-icon` → снесённый `light.smart_light_stairs_l1` | → `light.light_stairs_left` |
| H2000_PRO: °F вместо °C | override `sensor.private.suggested_unit_of_measurement` | сброс через `options_domain: "sensor.private"` |
**Правило:** **после сноса призраков — проверять ссылки дашбордов.** План этажей жалуется «недоступно» из-за мёртвой ссылки, а не из-за поломки устройства.
**Скрипты:** `fix_plan_stairs.sh` (ссылки плана), `fix_h2000_all.py` (единицы).
**Коммит vault:** `ee86207`.
---
+198
View File
@@ -0,0 +1,198 @@
---
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]]'
---
# Реестры 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-<ts>``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 |
---
## Связанные заметки
- [[family/tech/zigbee-t610-z2m-i-zha]] — Zigbee на ZHA, §13 вычистка призраков
- [[family/how-to/home-automation]] — t610: топология, аддоны, доступ
+81 -2
View File
@@ -1,10 +1,10 @@
---
title: "Zigbee на t610 — ZHA (справочник)"
created: '2026-09-15'
updated: '2026-09-15 (поздний вечер: +7 новых TS0201 тёплых полов, ЕДИНООБРАЗИЕ ID у 23 устройств, 12 алертов батарей, виртуальные Modbus slave 105112)'
updated: '2026-09-16 (ночь: вычищены 185 призраков Z2M из архива реестра `deleted_entities` 360→175; план этажей починен — ссылка на снесённого `light.smart_light_stairs_l1` → `light.light_stairs_left`; H2000_PRO °F→°C сбросом `sensor.private` override)'
type: tech
namespace: family
status: 🟢 РАБОТАЕТ. ZHA: 24 записи (23 устройства + координатор), `unavailable` = 0. Все устройства с читаемыми ID в едином виде, все в зонах. 26 автоматизаций: 25 `on`, 1 `off` намеренно. Bridge: 9 Zigbee-температур отдаются ZONT'у (slave 100112).
status: 🟢 РАБОТАЕТ. ZHA: 24 записи (23 устройства + координатор), `unavailable` = 0. Все устройства с читаемыми ID в едином виде, все в зонах. 24 автоматизации: 23 `on`, 1 `off` намеренно. Bridge: 9 Zigbee-температур отдаются ZONT'у (slave 100112). Призраки Z2M в архиве реестра вычищены; план этажей и H2000_PRO исправлены. `unavailable` всего 8 — вентиляция AT2 + `todo.shopping_list` (намеренно).
tags:
- t610
- haos
@@ -400,9 +400,88 @@ curl -s -H "$HDR" http://supervisor/backups | jq
---
## 13. Вычистка призраков Z2M и починка UI (2026-09-16, ночь)
### 13.1. 🔴 Призраки живут в `deleted_entities`, а НЕ в `entities`
**Симптом:** во вкладке «Обслуживание» висят сущности с пометкой «недоступно» — `sensor.office_temperature_sensor_battery`, `light.smart_light_stairs_l1` и др. При этом `/api/states` их не отдаёт, WS-реестр (`config/entity_registry/list`) — тоже, `repairs/list_issues` пуст.
**Причина:** записи лежат в **архиве удалённых** `core.entity_registry → data.deleted_entities`. HA их не загружает в runtime, но UI их показывает.
```bash
# живое vs архив — РАЗНЫЕ секции одного файла
jq '.data.entities|length' /config/.storage/core.entity_registry # 572 — живое
jq '.data.deleted_entities|length' /config/.storage/core.entity_registry # 360 → 175 — архив
```
> ⚠️ **`config/entity_registry/get` по такому entity_id → `not_found`.** Это ожидаемо и НЕ значит, что записи нет.
> ⚠️ Правка `.storage` вступает в силу только после рестарта HA.
### 13.2. Контролируемый снос — скрипт `~/tmp-t610/ghost_purge.sh`
Сам исключает: (1) `entity_id`, который сейчас живой; (2) вентиляцию (`fan|at2|damper|vent|shopping_list`). Dry-run по умолчанию.
```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' # снос
```
**Результат 2026-09-16:** снято **185** записей (179 в пачке + 6 точечных ранее). Архив 360 → 175. Живых 572 — не тронуто.
**Что снесено:** старые Z2M-сущности (`_linkquality`, префиксы `0x…`), остатки удалённых аддонов (`go2rtc`, `go2rtc_hardware`, `file_editor`, `samba_share`), HACS/Tuya-флаги, старые `switch_as_x`-обёртки, 4 автоматизации-дубля.
**Что НЕ тронуто:** 175 записей, чей `entity_id` живой (ZHA-устройства, телефон `sm_s931b`, вентиляция, снифферы Modbus) — плюс отдельно `light.night_light_shower_2`, `light.smart_light_office_left/right` (тот же тип `switch_as_x`, но живые).
> 📌 Питфолл `set -e` в bash-скриптах: `comm`/`grep` возвращают 1 при пустом результате и валят скрипт. Использовать `set -uo pipefail` (без `-e`) + `|| true` на таких строках.
### 13.3. План этажей ссылался на снесённого призрака
**Дашборд:** `home_plan` (storage-mode, `url_path: home-plan`), конфиг — `/config/.storage/lovelace.home_plan`, карта — `/config/www/floorplan/floor1_ha.svg`, `floor2_ha.svg`.
**Симптом:** после сноса призраков на карте ошибка «недоступно».
**Причина:** элемент `state-icon` ссылался на снесённый `light.smart_light_stairs_l1` (был `switch_as_x`-обёрткой). Живой = `light.light_stairs_left/right`.
```bash
# 1) вытащить все ссылки плана
jq -r '.data.config.views[].sections[].cards[]? | select(.type=="picture-elements") | .elements[]? | select(.entity?!=null) | .entity' \
/config/.storage/lovelace.home_plan | sort -u
# 2) сверить со списком живых из /api/states
# 3) заменить мёртвые → скрипт
ssh root@192.168.2.176 'bash /tmp/fix_plan_stairs.sh /config/.storage/lovelace.home_plan --apply'
```
> 🔴 **Правило: после сноса призраков — проверять ссылки дашбордов.** План этажей жалуется «недоступно» именно из-за мёртвой ссылки, а не из-за поломки устройства. Все 29 ссылок плана после фикса валидны.
### 13.4. 🔴 H2000_PRO показывал °F
**Симптом:** три датчика `sensor.h2000_pro_temperatura_*` (улица/подача/тёплый пол) отдавали °F. Значения тоже были в °F (55.22 °F = 12.9 °C).
**Причина:** ручной override в опциях сущности — `sensor.private.suggested_unit_of_measurement: "°F"` (все три).
**Фикс (WS):**
```python
{"type":"config/entity_registry/update",
"entity_id":"sensor.h2000_pro_temperatura_ulitsa",
"options_domain":"sensor.private", # ⚠️ не "sensor"!
"options":{"suggested_unit_of_measurement": None}}
```
> 🔴 **ПИТФОЛЛ: `options_domain: "sensor"` даёт `success: true`, но единицу НЕ меняет.** Проверено фактом: первый заход не подействовал, после смены на `"sensor.private"` — сработало.
> ⚠️ `H2000_PRO` — контроллер отопления, **не Zigbee**. Приходит через MQTT-discovery (ZONT). **Не трогать интеграцию** — только опции сущности.
**Результат:** 12.9 / 28.2 / 30.2 °C.
**Скрипты сессии:** `~/tmp-t610/ghost_purge.sh` (снос призраков), `ghost_inventory.sh` (инвентаризация), `fix_plan_stairs.sh` (ссылки плана), `fix_h2000_all.py` (единицы °F→°C), `clean_ghosts.sh` (ранняя версия).
**Бэкапы:** `core.entity_registry.bak-ghostpurge-20260916-002503`, `.bak-purge-20260916-000530`, `.bak-ghosts-20260916-000329`, `lovelace.home_plan.bak-fixplan-20260916-003231`.
---
## Связанные заметки
- [[family/how-to/home-automation]] — топология, аддоны t610, первопричина RCU stall, Modbus
- [[family/tech/ha-registry-operations]] — реестры HA: переименование, опции, снос призраков
- [[family/how-to/ha-automations]] — автоматизации
- [[family/plans/t610-backup-to-truenas]] — автобэкап `/config/zigbee2mqtt/`
- [[family/tech/local-ustreamer-addon]] — камера на том же хосте