[2026-09-15] eagle: family/tech/zigbee-t610-z2m-i-zha.md
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: "Zigbee на t610 — переезд Z2M → ZHA (выполнен, 2026-09-15)"
|
||||
created: '2026-09-15'
|
||||
updated: '2026-09-15 (ночь-14: ✅ МИГРАЦИЯ ВЫПОЛНЕНА — мусор удалён, сущности + устройства переименованы, зоны восстановлены, 12/16 автоматизаций живых)'
|
||||
updated: '2026-09-15 (ночь-15: найдена причина «кнопка не свитчит диммер» — `select.bed_dimmer_*` висят на чужом устройстве `sauna`; `light.bed_dimmer` не существует; `database.db` подтверждён как первоисточник)'
|
||||
type: tech
|
||||
namespace: family
|
||||
status: 🟢 ПЕРЕЕЗД ВЫПОЛНЕН — ZHA работает, 13 устройств приняты автоматически, мусор Z2M удалён, сущности + устройства + зоны переименованы/восстановлены, 12/16 автоматизаций починены. Осталось: разбудить light_sensor_stairs + wireless_light_switch_bed (4 автоматизации ждут).
|
||||
status: 🟡 ZHA работает, 15/16 устройств. Найден и разобран корень «кнопка спальни не свитчит диммер» (имена ушли не тем устройствам). Осталось: фикс имён (`light.bed_dimmer`) + 4 автоматизации + `light_sensor_stairs`.
|
||||
tags:
|
||||
- t610
|
||||
- haos
|
||||
@@ -23,14 +23,16 @@ related:
|
||||
|
||||
# Zigbee на t610 — переезд Z2M → ZHA (история + рабочий рецепт)
|
||||
|
||||
> 🚦 **СОСТОЯНИЕ НА 2026-09-15 (ночь-14) — ПЕРЕЕЗД ВЫПОЛНЕН:**
|
||||
> 🚦 **СОСТОЯНИЕ НА 2026-09-15 (ночь-15) — ZHA РАБОТАЕТ, ИДЁТ ДОЧИСТКА ИМЁН:**
|
||||
> - ✅ **Три слоя мусора удалены:** 127 сущностей `platform=mqtt` + 18 устройств-призраков Z2M. Реестр: 624 → 493 сущности, 52 → 34 устройства.
|
||||
> - ✅ **13 modbus-датчиков СОХРАНЕНЫ** (проверено фактом: пишут данные) — они тоже `platform=mqtt`, но живые.
|
||||
> - ✅ **51 операция переименования сущностей:** 42 выполнены, 8 отбиты (`light`→`switch` запрещён HA), 1 пропущена.
|
||||
> - ✅ **12/13 устройств переименованы** — в UI больше нет `_TZ3000_5gey1ohx TS0002`. Это то, что Alex видел руками. (13-е, `office_temperature_sensor`, проснулось и переименовано отдельно.)
|
||||
> - ✅ **13 устройств получили ЗОНЫ обратно** (11 зон целы, все ZHA-устройства были без зоны).
|
||||
> - ✅ **Автоматизации: 12 из 16 живых** — пересобраны на ZHA `device_id` + `entity_id`.
|
||||
> - ⏳ **Осталось:** 4 автоматизации ждут 2 спящих батарейных (`light_sensor_stairs`, `wireless_light_switch_bed`).
|
||||
> - 🔴 **НОЧЬ-15 — найдена причина «кнопка в спальне не свитчит диммер»:** `select.bed_dimmer_*` привязаны к **чужому** устройству (`sauna`, `700e14b7…`), а `light.bed_dimmer` **не существует** — ZHA дала `light.tz3000_ooc8illt_ts0052`. См. §НОЧЬ-15.
|
||||
> - 🔴 **НОЧЬ-15 — `light_sensor_stairs` ОТОЗВАЛ вывод «не подхватился»:** устройство **в сети и привязано к координатору ZHA** (`iasCieAddr 0x0ceff6fffe9339f2`, `lastSeen` после миграции). Сущностей нет — EndDevice спит, значение освещённости не менялось.
|
||||
> - ⏳ **Осталось:** фикс имён кнопки спальни; 4 автоматизации на 2 батарейных.
|
||||
> - ⚠️ Z2M **остановлен**, не удалён. Данные целы в `/config/zigbee2mqtt/`.
|
||||
|
||||
> 🔴 **ЧИТАТЬ ДАЛЬШЕ И КАК РЕЦЕПТ, И КАК РАЗБОР ОШИБОК.** Ниже: техника `reuse_settings` (проверена дважды), рабочий способ увидеть устройства ZHA (WebSocket), карта переименования по IEEE, разбор организационных провалов.
|
||||
@@ -300,7 +302,7 @@ ZHA их обнаруживает (уже в сети) — заново спар
|
||||
5. ✅ **ВЫПОЛНЕНО ФАКТОМ — устройства приняты ZHA САМИ.** 12 сразу, `office_temperature_sensor` — позже, когда проснулся. **Итого 13 из 16.** Без «Add device» и без `zha.permit`.
|
||||
6. ⬜ **ОСТАЛОСЬ — разбудить 2 не отозвавшихся** (`light_sensor_stairs`, `wireless_light_switch_bed`). Кнопкой на устройстве, **НЕ перепаривание**.
|
||||
> 📌 `office_temperature_sensor` — ✅ РАЗБУЖЕН И ПОДХВАЧЕН (ночь-14), переименован, зона `kabinet`.
|
||||
> 📌 `sauna` (`0xa4c13882a4b42db0`) — в ZHA **так и не появилась**. Числилась Router'ом, но не вернулась; отдельная задача.
|
||||
> ✅ **УТОЧНЕНО ночью-15:** `sauna` в ZHA **ЕСТЬ** — `device_id 700e14b7d1710526b00f098b8506826d`, `zha:a4:c1:38:4f:be:0b:3a:6b`, `name_by_user=sauna`, зона `kotelnaia`, сущность `switch.tz3210_nhqka112_ts011f`. Ранее записанное «не вернулась» — **неверно**. ⚠️ Но именно на неё ошибочно повешены `select.bed_dimmer_*` (см. §НОЧЬ-15).
|
||||
7. ✅ **ВЫПОЛНЕНО (ночь-14) — переименование и починка ссылок.** Мусор удалён (127 сущностей + 18 устройств), 42 сущности + 13 устройств переименованы, 13 зон восстановлены, 12/16 автоматизаций пересобраны. См. §РЕЗУЛЬТАТ МИГРАЦИИ выше.
|
||||
|
||||
> 🔴 **ИСПРАВЛЕНО ночью-13:** прежде здесь стояло «шаги 5-7 — только в UI, за клавиатурой, агентом нельзя». **Отменено.** Агент снял реестры через WebSocket, построил карту по IEEE и может выполнить переименование (`rename.py --apply`). Руками нужны **только батарейные** — физически нажать кнопку.
|
||||
@@ -787,14 +789,14 @@ unavailable: 8 — и НИ ОДНА не Zigbee:
|
||||
| `0xa4c13873b5c1575b` | office_table_light_switch | TS0002 | Router |
|
||||
| `0xa4c13807b64c7fd4` | kitchen_hood | TS0003 | Router |
|
||||
| `0xa4c1386d0839706a` | light_stairs | TS0002 | Router |
|
||||
| `0xa4c138eb6fbe9d19` | sauna | TS011F | Router |
|
||||
| `0xa4c138eb6fbe9d19` | **sauna** (в ZHA ✅ есть, `switch.tz3210_nhqka112_ts011f`) | TS011F | Router |
|
||||
| `0xa4c138b0f9e674a5` | wireless_light_switch_bed | TS0041 | EndDevice (батарея) |
|
||||
| `0xa4c13882a4b42db0` | bed_dimmer | TS0052 | Router |
|
||||
| `0xa4c138c4a94a6a31` | shower_2_presence_sensor | TS0601 | Router (mmWave) |
|
||||
| `0xa4c1381694217e10` | boiler_water_leak | TS011F | Router |
|
||||
| `0xa4c1383d5fcaa063` | (heating_cable_plug) | TS0207 | EndDevice |
|
||||
| `0xa4c138c650636cf6` | (boiler_controller_power) | TS0201 | EndDevice |
|
||||
| `0xa4c1384fbe0b3a6b` | heating_cable_plug | TS011F | Router |
|
||||
| `0xa4c13882a4b42db0` | **bed_dimmer** (в ZHA ✅ есть, `light.tz3000_ooc8illt_ts0052`) | TS0052 | Router |
|
||||
| `0xa4c138c4a94a6a31` | shower_2_presence_sensor (TS0601 = mmWave присутствие ✅) | TS0601 | Router (mmWave) |
|
||||
| `0xa4c1381694217e10` | boiler_controller_power | TS011F | Router |
|
||||
| `0xa4c1383d5fcaa063` | boiler_water_leak | TS0207 | EndDevice |
|
||||
| `0xa4c138c650636cf6` | toilet_1_floor_temperature | TS0201 | EndDevice |
|
||||
| `0xa4c1386d40ddb67b` | light_sensor_stairs (`_TZ3000_hy6ncvmw`, в сети ZHA ✅, сущностей нет) | TS0222 | EndDevice (батарея) |
|
||||
|
||||
> Координатор: `0x0ceff6fffe9339f2`, `coordinator_ieee` `f23993fefff6ef0c`, imanufId 4169, ember/EZSP v13 (firmware 7.4.5 GA).
|
||||
|
||||
@@ -881,6 +883,115 @@ state.json MD5 20bfb775cbab002e59d1be31a15db9e6
|
||||
31. 🔴 **Автоматизации ссылаются на `device_id` + `entity_id`-UUID, а НЕ на `entity_id`.** Удаление устройств Z2M **убивает все 16 автоматизаций**, даже если сущности переименованы обратно. Проверять `config/device_registry/list` — если `device_id` из автоматизации в списке нет, триггер мёртв. Правка — только пересборка привязок по новым `device_id`.
|
||||
32. 🔴 **Зоны (Areas) НЕ удаляются вместе с устройствами, но и НЕ восстанавливаются автоматически.** Отдельный реестр `area_registry` (11 зон уцелели). Пересозданные ZHA-устройства получают `area_id: null`. **После любой миграции зоны проставлять заново:** `config/device_registry/update` + поле **`area_id`** (= `slug` зоны, не отображаемое имя).
|
||||
33. ⚠️ **Два устройства могут иметь ОДИНАКОВОЕ техническое имя.** `office_table_light_switch` и `light_stairs` — оба `_TZ3000_5gey1ohx TS0002`, различаются только по IEEE. В UI выглядят идентично; при массовых правках ключ — `device_id`, не имя.
|
||||
34. 🔴 **`database.db` (Z2M) — ПОСТРОЧНЫЙ JSON LINES, не цельный JSON.** `jq '.devices[]'` даёт пустоту. Читать построчно: `json.loads` на каждую строку (см. §НОЧЬ-15).
|
||||
35. 🔴 **ПЕРВОИСТОЧНИК — `database.db` / `configuration.yaml`, НЕ производные карты.** `rename-map.json`, `autofix-map.json`, `device-rename.json` — снимки, сделанные агентом; в них **мои же ошибки** (так перепутались `sauna` ↔ `bed_dimmer`). Сверять КАЖДЫЙ IEEE по `database.db` (`ieeeAddr` + `modelId` + `manufName` в одной строке).
|
||||
36. 🔴 **Сущность может висеть на ЧУЖОМ `device_id`.** `select.bed_dimmer_power_on_behavior` / `select.bed_dimmer_switch_type` оказались на устройстве `sauna` (`700e14b7…`), а не на `bed_dimmer` (`e230c12e…`). **Проверка: если `entity_id` одного устройства ссылается на другой `device_id` — имена разошлись.** Тот же корень, что перепутанные IEEE: массовое переименование вслепую.
|
||||
37. 🔴 **`select.*` / `number.*` (настройки) ZHA создаёт отдельно от `light.*`/`switch.*`** — при переименовании главной сущности настройки **остаются с техническим или чужим именем**. Проверять их отдельно.
|
||||
38. 🔴 **Устройство может БЫТЬ в сети ZHA и не иметь ни одной сущности.** `light_sensor_stairs`: `iasCieAddr` = IEEE координатора ZHA, `lastSeen` свежий — значит **привязалось**, но сущностей нет (EndDevice спит, значение не менялось). **Проверять реестр по WebSocket, а не состояние сущностей** — иначе ложный вывод «не подхватился».
|
||||
39. 🔴 **`ha_ws.py` падает с `FileNotFoundError: /tmp/.hatok`** — токен там не переживает очистку `/tmp`. Восстановление: `grep -o 'eyJ[A-Za-z0-9._-]*' ha_token.txt | head -1 > /tmp/.hatok`.
|
||||
40. ⚠️ **`ha_ws.py find <подстрока>`** — рабочий способ найти `device_id` + список сущностей устройства по IEEE без двоеточий. Быстрее, чем гонять полные реестры.
|
||||
41. 🔴 **`config/entity_registry/update` может ответить успехом, не применив значение** — после каждой правки **читать реестр обратно**. (Общий питфолл HA REST/WS — тот же, что у automation config.)
|
||||
|
||||
---
|
||||
|
||||
## 🔴 НОЧЬ-15: разбор «кнопка не свитчит диммер» + два факта из бэкапа
|
||||
|
||||
> **Триггер:** Alex — «Кнопка в спальне не свитчит диммер в спальне», плюс сомнение в модели датчика присутствия: «ты уверен что это `_TZE204_qasjif9e` — датчик присутствия??» и требование «проверяй ВСЁ из бэкапа».
|
||||
|
||||
### ✅ Факт 1 — датчик присутствия определён верно
|
||||
|
||||
Из Z2M-бэкапа (`~/tmp-t610/z2m-backup-20260915/database.db`), запись по строке:
|
||||
|
||||
```
|
||||
0xa4c138c4a94a6a31 modelId: TS0601 manufName: _TZE204_qasjif9e
|
||||
```
|
||||
|
||||
**`TS0601` / `_TZE204_qasjif9e` — это действительно mmWave-датчик присутствия** (Tuya-модуль, присутствие + illuminance + target distance + radar sensitivity). В ZHA устройство: `name_by_user=shower_2_presence_sensor`, `area=dushevaia`, `device_id=c9d62c9d04a231c4642c705088633121`. Сверено по `modelId`, а не по имени. **Ошибки нет.**
|
||||
|
||||
### ✅ Факт 2 — источник истины: `database.db`, а не промежуточные карты
|
||||
|
||||
> 🔴 **ПОЧЕМУ Я ПЕРЕПУТАЛ `sauna` ↔ `bed_dimmer` (ночь-14) — точная причина, найденная сейчас:**
|
||||
>
|
||||
> Я взял `~/tmp-t610/rename-map.json` — **производный файл, собранный мной же по кускам**. А `database.db` содержит **`ieeeAddr` + `modelId` + `manufName` + `nwkAddr` + `powerSource` одной строкой**, свериться можно было за одну команду.
|
||||
>
|
||||
> **УРОК: `database.db` — первоисточник. `rename-map.json`/`autofix-map.json`/`device-rename.json` — производные, они стареют и содержат мои же ошибки. Сверять ВСЕГДА по `database.db` или `/config/zigbee2mqtt/configuration.yaml`.**
|
||||
|
||||
Рабочий разбор `database.db` (это **JSON-строки по строке на устройство**, не единый JSON):
|
||||
|
||||
```bash
|
||||
jq -r '.devices[]?...' database.db # ❌ НЕ работает — это не цельный JSON
|
||||
```
|
||||
|
||||
```python
|
||||
# ✅ правильно: файл — построчный JSON Lines
|
||||
import json
|
||||
recs = []
|
||||
for line in open("database.db", encoding="utf-8", errors="replace").read().splitlines():
|
||||
line = line.strip()
|
||||
if line:
|
||||
try: recs.append(json.loads(line))
|
||||
except Exception: pass
|
||||
```
|
||||
|
||||
### 🔴 Факт 3 — почему кнопка спальни не свитчит диммер (корень найден)
|
||||
|
||||
**Проверено фактом** (`config/device_registry/list` + `config/entity_registry/list` по WebSocket):
|
||||
|
||||
```
|
||||
bed_dimmer device_id e230c12e6cb45492408ddba6456b6444 zha:a4:c1:38:82:a4:b4:2d:b0
|
||||
light.tz3000_ooc8illt_ts0052 ← ЕСТЬ, живой
|
||||
button.tz3000_ooc8illt_ts0052_identifikatsiia
|
||||
number.tz3000_ooc8illt_ts0052_vremia_perekhoda_mezhdu_vkliucheniem_i_vykliucheniem
|
||||
sensor.*_rssi, sensor.*_lqi, update.*
|
||||
|
||||
sauna device_id 700e14b7d1710526b00f098b8506826d zha:a4:c1:38:4f:be:0b:3a:6b
|
||||
switch.tz3210_nhqka112_ts011f ← ЕСТЬ, живой
|
||||
select.bed_dimmer_power_on_behavior ← 🔴 ИМЯ ОТ ДРУГОГО УСТРОЙСТВА
|
||||
select.bed_dimmer_switch_type ← 🔴 ИМЯ ОТ ДРУГОГО УСТРОЙСТВА
|
||||
```
|
||||
|
||||
> 🔴 **Новый класс ошибки, тот же корень:** `select.bed_dimmer_*` — это настройки реле **`sauna`**, но они названы «bed_dimmer». При ночном переименовании имена ушли **не тем устройствам**. **Ровно тот же класс, что перепутанные IEEE: правки по производной карте вслепую.**
|
||||
>
|
||||
> **Причина, почему кнопка не работает:** кнопка не рулит светом напрямую — она шлёт событие, автоматизация ловит его и зовёт сущность. Автоматизация ищет `light.bed_dimmer`, которого **не существует** (ZHA-имя с русской транслитерацией — `light.tz3000_ooc8illt_ts0052`). Событие кнопки уходит в пустоту.
|
||||
|
||||
**TS0052 — это НЕ лампа, а настенный диммер-контроллер.** ZHA отдаёт его как `light.*` (диммер), Z2M отдавал `light.bed_dimmer` через обёртку `switch_as_x`. Переименование `switch.tz3210_nhqka112_ts011f` → `light.bed_dimmer` **отбито HA** (`New entity ID should be same domain`).
|
||||
|
||||
### План фикса кнопки спальни (согласуется с Alex, не выполнен)
|
||||
|
||||
1. Бэкап `core.entity_registry` на Mac (обязательно первым шагом).
|
||||
2. Вернуть `select.bed_dimmer_power_on_behavior` / `_switch_type` на **`bed_dimmer`** (`e230c12e…`), а не на `sauna` (`700e14b7…`).
|
||||
3. Переименовать `light.tz3000_ooc8illt_ts0052` → `light.bed_dimmer` (домен совпадает — пройдёт).
|
||||
4. Пересобрать автоматизации кнопки спальни на реальный `light.bed_dimmer`, проверить `last_triggered` фактом.
|
||||
|
||||
> ⚠️ **Проверять после каждого шага чтением реестра обратно** — HA на `config/entity_registry/update` может ответить успехом, не применив значение.
|
||||
|
||||
### 🔴 Факт 4 — `light_sensor_stairs` В СЕТИ, но сущностей нет
|
||||
|
||||
Из `database.db`:
|
||||
|
||||
```
|
||||
ieeeAddr: 0xa4c1386d40ddb67b modelId: TS0222 manufName: _TZ3000_hy6ncvmw
|
||||
type: EndDevice powerSource: Battery
|
||||
interviewCompleted: true interviewState: SUCCESSFUL
|
||||
lastSeen: 1789478661136 ← 2026-09-15 ~21:24, ПОСЛЕ миграции
|
||||
iasCieAddr: 0x0ceff6fffe9339f2 ← IEEE координатора ZHA (не Z2M!)
|
||||
```
|
||||
|
||||
> ✅ **Вывод отменён:** раньше было записано «не дошёл / не подхватился». **Неверно.** Устройство **вышло на связь уже при ZHA** (`iasCieAddr` координатора ZHA, свежий `lastSeen`), т.е. **привязалось к ZHA**. Сущностей нет, потому что: `zoneState: 1` (зона в норме) + `measuredValue: 0` — ZHA создаёт сенсор освещённости только по первому **изменённому** значению, а EndDevice спит.
|
||||
>
|
||||
> ⚠️ **`manufName` у него `_TZ3000_hy6ncvmw`** — ранее в переписке я называл `_TZ3000_4ufaeuwv`, это была ошибка.
|
||||
> **Рекомендация:** длинное нажатие паринг-кнопки 3–5 с (не короткий тык) + поднести к свету, затем **проверить реестр фактом через WebSocket**, а не по состоянию сущностей.
|
||||
|
||||
### 🔴 ПИТФОЛЛ: `ha_ws.py` требует `/tmp/.hatok`
|
||||
|
||||
Скрипт `~/tmp-t610/ha_ws.py` читает токен из `/tmp/.hatok` — файл **не переживает перезагрузку/очистку `/tmp`** (`FileNotFoundError`). Восстановление:
|
||||
|
||||
```bash
|
||||
cd ~/tmp-t610 && grep -o 'eyJ[A-Za-z0-9._-]*' ha_token.txt | head -1 > /tmp/.hatok
|
||||
```
|
||||
|
||||
> 📌 `ha_ws.py <find|area|areas>` — рабочий инструмент поиска `device_id`/`entity_id` по подстроке через WebSocket. Найти, что висит на устройстве: `python3 ha_ws.py find <ieee-без-двоеточий>`.
|
||||
> 📌 **Один `device_id` — одно устройство.** Если сущности одного устройства ссылаются на два разных `device_id` — это верный признак, что имена разошлись по чужим устройствам (как `select.bed_dimmer_*`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user