1034 lines
118 KiB
Markdown
1034 lines
118 KiB
Markdown
---
|
||
title: t610 — развёртывание через HA-аддоны
|
||
status: in-progress
|
||
tags:
|
||
- family
|
||
- plan
|
||
- homeautomation
|
||
- t610
|
||
- haos
|
||
- addons
|
||
created: '2026-09-13'
|
||
updated: '2026-09-14'
|
||
related:
|
||
- '[[family/plans/home-automation-migration-t610]]'
|
||
- '[[family/how-to/home-automation]]'
|
||
---
|
||
# t610 — развёртывание через HA-аддоны
|
||
|
||
> **Статус (2026-09-14, поздняя сессия): Этап 1 ✅. Этап 2 ✅ ПОЛНОСТЬЮ. Этап 3 ✅ ЗАКРЫТ.**
|
||
> **z2m = 15 устройств, реестр HA = 332 сущности, hex = 0.** Добавлена розетка **`boiler_controller_power`** (`0xa4c1381694217e10`, TS011F, Котельная) — питание контроллеров котлов; 13 её сущностей переименованы из hex.
|
||
> **Автоматизации: 13/16 `unavailable` → 0 (15 `on` + 1 `off`).** Причины были две, обе устранены: ① `device_id` (9 шт., 20 вхождений) перемаплены по `identifiers`; ② hex-`entity_id` в `automations.yaml` (`light.0xa4c13882a4b42db0` → `light.bed_dimmer`; `switch.0xa4c13873b5c1575b` оказался **артефактом regex** — в файле уже `_l1`/`_l2`). Файл залит, HA перезапущен.
|
||
> **HTTP-варнинг устранён:** блок `http:` удалён из `configuration.yaml`, настройки перенесены в `.storage/http` (UI → Network).
|
||
> **modbus.host исправлен: `127.0.0.1` → `192.168.2.176`** (mbusd в отдельном контейнере — `127.0.0.1` изнутри HA его не видел).
|
||
> **modbus-bridge: HTTP 404 → 200.** Опрашивал HA по hex-`entity_id`; правился `modbus_ha_bridge.py` + `data/config.template.tmpl` + **обязательный rebuild аддона**.
|
||
> ⚠️ **ЕДИНСТВЕННЫЙ ОСТАВШИЙСЯ БЛОКЕР (физика, не конфиг): 32 заслонки вентиляции `unavailable`.** mbusd работает (TCP отвечает), но шина **не отвечает** — exception **0x0B** (GATEWAY TARGET DEVICE FAILED TO RESPOND). Вывод: **CH340 #2 воткнут в USB, но линии A/B вентиляционной шины к нему не подключены / шина обесточена.** За Alex.
|
||
> ⚠️ **`unknown` после рестарта HA — ПРОВЕРЕНО ЭКСПЕРИМЕНТОМ 2026-09-14, воспроизводится.** Реле/кнопки слетают в `unknown` при каждой перезагрузке HA; `retain: true` + `cache_state*` в z2m стоят, но не помогают. ✅ **ФИКС ПРИМЕНЁН: `not_from: [unavailable, unknown]` убран из триггеров** (`automations.yaml`, 2 шт.) — кнопка теперь срабатывает **с первого нажатия**, проверено на живом (l1 и l2, вкл+выкл). ⏳ Осталось выяснить, доходит ли cached-стейт из `state.json` до HA до первого события от устройства. См. §«ДОБАВЛЕНО В КОНЦЕ СЕССИИ» ③‑бис.
|
||
> **✅ ЗОНЫ ВОССТАНОВЛЕНЫ (18 устройств):** при переносе `area_id` теряются так же, как `device_id` (устройства ре-регистрируются) → все 14 zigbee были **без зон**. Проставлены по эталону с TrueNAS по zigbee-адресу. См. §«ДОБАВЛЕНО В КОНЦЕ СЕССИИ» ①.
|
||
> **✅ office-переключатель управляет светом:** устранены hex-`entity_id` **в триггерах** автоматизаций (`switch.0xa4c13873b5c1575b_l1/l2` → человеческие) + реле выведены из `unknown` физическим нажатием. Цепочка `light → switch_as_x → z2m → реле` проверена. См. §«ДОБАВЛЕНО В КОНЦЕ СЕССИИ» ②③.
|
||
> Сервисы: z2m (14 устройств), mbusd (502), modbus-bridge (MQTT + HA-опрос), MQTT-интеграция в HA.
|
||
> Родительский план: [[family/plans/home-automation-migration-t610]] (Шаг 3 в нём заменяется на этот документ).
|
||
> Доступ к хосту, CLI и питфоллы: [[family/how-to/t610-access]].
|
||
|
||
## Ключевые решения (кратко, для быстрого входа в контекст)
|
||
|
||
| Решение | Что выбрано | Почему |
|
||
|---------|-------------|--------|
|
||
| Формат развёртывания сервисов | **HA-аддоны** (не docker-compose) | из SSH-аддона host docker не виден; аддоны штатны и снимают udev-гонку |
|
||
| Источник z2m | **community-repo** `zigbee2mqtt/hassio-zigbee2mqtt` | в официальном сторе z2m нет |
|
||
| mbusd / modbus-bridge | **local add-ons** (`/addons/...`) | кастомный код, в сторе нет |
|
||
| Привязка CH340 (2 одинаковых адаптера) | **`/dev/serial/by-path/...`** | by-id у обоих идентичен (нет серийников) |
|
||
| Как аддон видит serial | **флаг `uart: true`** в манифесте аддона | даёт доступ ко всем serial (by-id + by-path) автоматически; `devices:` не нужен |
|
||
| udev-алиасы `ttyZONT`/`ttyVent` | **отменены** | на HA OS невозможны (SSH-аддон = Alpine-контейнер); by-path функционально эквивалентен |
|
||
| Доступ к хостовому шеллу | **не нужен** | debug-SSH 22222 включается только флешкой; всё делается через Supervisor API |
|
||
|
||
## Контекст: почему аддоны, а не docker-compose 1:1
|
||
|
||
Изначально в родительском плане (Шаг 3, Вариант B) предполагалось перенести `docker-compose.yml` с TrueNAS 1:1.
|
||
При проверке живого t610 выяснилось:
|
||
|
||
- HA OS 18.2 внутри использует **host docker 29.6.2** (overlayfs, journald) — docker есть, `ha docker info` подтверждает.
|
||
- Но **из SSH-аддона docker CLI не виден** — аддон живёт в своём контейнере. Доступ к host docker только через Supervisor (`ha docker`) или через Portainer-аддон.
|
||
- Поэтому штатный и наименее хрупкий путь — **аддоны**.
|
||
|
||
**Решение Alex (2026-09-13): «Делай всё аддонами».**
|
||
|
||
## Состав аддонов
|
||
|
||
| Сервис | Slug | Источник | Статус |
|
||
|--------|------|----------|--------|
|
||
| Mosquitto broker (MQTT) | `core_mosquitto` | Official (core) | ✅ установлен, `started` (1883/1884) |
|
||
| Node-RED | `a0d7b954_nodered` | Community | ✅ установлен, `started` (1880) |
|
||
| Advanced SSH & Web Terminal | `a0d7b954_ssh` | Community | ✅ есть в сторе (запасной путь) |
|
||
| Terminal & SSH | `core_ssh` | Official | ✅ **установлен и работает** |
|
||
| Samba share (для доступа к файлам) | `core_samba` | Official | ⏸️ установлен, `stopped` (нужен `password`) |
|
||
| File editor | `core_configurator` | Official | ✅ установлен, `started` |
|
||
| **Zigbee2MQTT** | `45df7312_zigbee2mqtt` | Community repo | ✅ **установлен, работает — 16 устройств** |
|
||
| **mbusd** | `local_mbusd` | Local add-on (`/addons/mbusd`) | ✅ **установлен, работает — порт 502** |
|
||
| **modbus-bridge** | `local_modbus-bridge` | Local add-on (`/addons/modbus-bridge`) | ✅ **установлен, работает — MQTT + HA-опрос** |
|
||
| **MQTT-интеграция в HA** | `mqtt` | Config entry | ✅ **добавлена 2026-09-14** (была ОТСУТСТВОВАЛА → 22 сущности; стало 104, 69 Zigbee) |
|
||
|
||
### Про Zigbee2MQTT
|
||
В официальном сторе z2m нет (есть только deCONZ `core_deconz` и `core_silabs_multiprotocol`).
|
||
Варианты:
|
||
- **A. Community-репозиторий z2m** — у сообщества есть репо (`https://github.com/zigbee2mqtt/hassio-zigbee2mqtt`), добавляется как app repository, дальше штатная установка.
|
||
- **B. Local add-on** — свой Dockerfile в `/addons/zigbee2mqtt`.
|
||
|
||
**Решение: A (community repo)** — меньше ручной работы, поддерживается сообществом, обновления через UI. ✅ Реализовано 2026-09-14.
|
||
|
||
### Про mbusd и modbus-bridge
|
||
В сторе нет и быть не может (кастомный код). Только **local add-ons**:
|
||
```
|
||
/addons/mbusd/ → Dockerfile + config
|
||
/addons/modbus-bridge/ → Dockerfile + modbus_ha_bridge.py + config.yml
|
||
```
|
||
Local add-ons требуют **Advanced Mode** в профиле HA (Settings → Apps появляются только с ним) + репозиторий «Local apps» уже подключён (проверено: `addons_repositories` содержит `Local apps`).
|
||
|
||
## ✅ USB-устройства подключены (2026-09-14) — блокер снят
|
||
|
||
**Все 3 устройства воткнуты и видны** (карта by-id/by-path: [[family/how-to/t610-access]] §USB).
|
||
|
||
```
|
||
/dev/ttyUSB0 → CH340 #1 by-path: pci-0000:00:12.0-usb-0:3:1.0-port0 (порт 3) → ZONT / modbus-bridge
|
||
/dev/ttyUSB1 → CH340 #2 by-path: pci-0000:00:12.0-usb-0:4:1.0-port0 (порт 4) → Vent / mbusd
|
||
/dev/ttyACM0 → Zigbee Inswift ZBP-MG21 by-id: usb-Inswift_Zigbee_ZBP-MG21_535A000001-if00
|
||
```
|
||
|
||
⚠️ **Два CH340 неразличимы по by-id** (у обоих `1a86:7523`, serial отсутствует) → привязка только по **by-path / адресу шины**.
|
||
|
||
### 🔑 РЕШЕНИЕ: привязка по `by-path` вместо udev-алиасов
|
||
|
||
Проверено на живом t610 (2026-09-14): **udev-алиасы (`ttyZONT`/`ttyVent`) на HA OS не нужны и сделать их «как на TrueNAS» нельзя** — SSH-аддон это Alpine-контейнер, у него нет `/etc/udev/rules.d` и нет `udevadm`. Хостовый доступ = только debug-SSH 22222, который на t610 выключен и включается лишь флешкой с ключом в разделе `CONFIG` (по сети — никак: `ha host` без ssh-команд, Supervisor API `/host/services/ssh` → 403, роль аддона `manager`).
|
||
|
||
**Рабочая схема — штатный механизм Supervisor: `uart: true`.**
|
||
|
||
- В `config.yaml` (или `config.json`) аддона флаг **`uart: true`** даёт контейнеру доступ ко **всем** serial-устройствам хоста — вместе с симлинками `/dev/serial/by-id/` **и** `/dev/serial/by-path/`.
|
||
- Подтверждено: `core_ssh` имеет `uart: true` → из него виден весь `/dev/serial/by-path/`. z2m-аддон тоже имеет `uart: true`.
|
||
- **`devices:` в конфиг аддона прописывать НЕ надо** — при `uart: true` проброс serial автоматический.
|
||
|
||
**Как прописывать путь в конфиге сервиса:**
|
||
```yaml
|
||
# zigbee2mqtt (Settings → Apps → Zigbee2MQTT → Configuration → serial)
|
||
serial:
|
||
adapter: ember
|
||
port: /dev/serial/by-path/pci-0000:04:00.0-usb-0:1:1.0 # Zigbee — by-id тоже ок (уникальный серийник)
|
||
```
|
||
Для mbusd / modbus-bridge (local add-ons) — в их `config.yaml`/опциях указывать **by-path**:
|
||
```
|
||
ZONT → /dev/serial/by-path/pci-0000:00:12.0-usb-0:3:1.0-port0
|
||
Vent → /dev/serial/by-path/pci-0000:00:12.0-usb-0:4:1.0-port0
|
||
```
|
||
Это **функциональный аналог** udev-алиасов с TrueNAS: имя не «прыгает» при перезагрузке, привязка к физическому порту. Разница только в том, что вместо `ttyZONT` пишется полный by-path.
|
||
|
||
> ⚠️ **by-path привязан к физическому порту.** CH340 #1 обязан остаться в порту 3, CH340 #2 — в порту 4. Если поменять — пути поедут. Порты зафиксированы (проверено).
|
||
|
||
### ✅ z2m на t610 — ВЫПОЛНЕНО (2026-09-14)
|
||
|
||
**Что сделано:**
|
||
1. **Бэкап с TrueNAS** → Mac `~/tmp-t610/z2m-backup-20260914/` (`database.db`, `configuration.yaml`, `state.json`, `coordinator_backup.json`). Источник на TrueNAS: `/mnt/RED_2TB/docker/zigbee2mqtt/`.
|
||
2. Установлен аддон `45df7312_zigbee2mqtt` v**2.14.1-1** (community repo). Манифест содержит **`uart: true`** → доступ ко всем serial, `devices:` не нужен.
|
||
3. **Mosquitto**: добавлен логин `zont` (тот же пароль, что на TrueNAS) через Supervisor API → `POST /addons/core_mosquitto/options` → `ha apps restart core_mosquitto`. Нужен, чтобы HA-интеграция и ZONT продолжили работать по старым креденшелам.
|
||
4. **Опции z2m** (Supervisor API `POST /addons/45df7312_zigbee2mqtt/options`):
|
||
```json
|
||
serial: { "port": "/dev/serial/by-id/usb-Inswift_Zigbee_ZBP-MG21_535A000001-if00",
|
||
"adapter": "ember", "baudrate": 115200, "rtscts": false }
|
||
mqtt: { "server": "mqtt://core-mosquitto:1883", "user": "zont", "password": "<как на TrueNAS>" }
|
||
```
|
||
5. **База перенесена 1:1.** ⚠️ `data_path` аддона = **`/config/zigbee2mqtt`** — внутри HA-конфига, НЕ `/addon_configs/...`. Создана `/config/zigbee2mqtt/`, залиты `database.db` + `configuration.yaml` (тот же `network_key`/`pan_id`/`ext_pan_id`/`channel: 11`, но serial → by-id, MQTT → core-mosquitto).
|
||
6. `ha apps start 45df7312_zigbee2mqtt` → **работает.**
|
||
|
||
**Лог подтверждает успех** (`/config/zigbee2mqtt/log/<ts>/log.log`):
|
||
```
|
||
zh:ember: [INIT TC] Adapter network matches config.
|
||
z2m: Coordinator firmware: EmberZNet 7.4.5 [GA], EZSP 13
|
||
z2m: Currently 16 devices are joined.
|
||
z2m: Connected to MQTT server
|
||
```
|
||
✅ **16 устройств на месте, переспаривание НЕ потребовалось.** (Tuya: модули реле, диммеры, розетки, датчики t°/влажности, протечки, радар присутствия, светильник.)
|
||
|
||
**Питфоллы z2m-переноса:**
|
||
- **Пароль MQTT содержит `$`** (`mqtt1z3$`) → при передаче через `sed`/интерполяцию в шелле ломается экранирование, скрипт падает с `unmatched '|'`. Надёжный путь: файл `configuration.yaml` готовить **локально**, заливать копированием файла, значения с `$` не подставлять в bash-строки. Для Supervisor API — JSON собирать через `jq`, а не конкатенацией.
|
||
- Supervisor API требует **полный** набор опций (схема валидирует все ключи) — брать текущие и менять нужное.
|
||
- Пароль в выводе `ha`/API маскируется как `***` — это нормально, значение применяется.
|
||
- `uart: true` = автоматический проброс всех serial (by-id + by-path). `devices:` в конфиг аддона прописывать не надо.
|
||
- В SSH-аддоне **нет python3**, и он неустойчив к сложному экранированию строк — готовые конфиги заливать файлом, а не генерировать на хосте.
|
||
|
||
## План по шагам
|
||
|
||
### Этап 1 — базовые аддона (не требуют USB) — ✅ ВЫПОЛНЕНО 2026-09-13
|
||
1. [x] Advanced Mode — не понадобился для CLI (всё сделано через `ha apps`), понадобится позже для local add-ons
|
||
2. [x] **Mosquitto broker** (`core_mosquitto` v7.1.1) — установлен, `started`, порты **1883** (MQTT) + **1884** (WS) открыты, discovery отправлен в HA автоматически
|
||
3. [x] **Node-RED** (`a0d7b954_nodered` v22.0.6) — установлен, `started`, порт **1880** открыт, **уже подключился к HA** (`Connected to http://supervisor/core`)
|
||
4. [ ] **Samba share** (`core_samba`) — установлен, но `stopped`: требует задать `password` (по умолчанию `null`) → логин `homeassistant`. Задать в UI: Settings → Apps → Samba → Configuration
|
||
5. [x] **File editor** (`core_configurator`) — установлен, `started`
|
||
6. [x] Репозиторий Zigbee2MQTT добавлен: `ha store add https://github.com/zigbee2mqtt/hassio-zigbee2mqtt` → появился как `Home Assistant App: Zigbee2MQTT` (slug `45df7312`)
|
||
7. [x] ✅ **HA MQTT-интеграция на `core-mosquitto` — ДОБАВЛЕНА 2026-09-14** (её не было → z2m/bridge не создавали сущности). Сущностей: 22 → **104** (69 Zigbee). Рецепт — §«HA MQTT-интеграция» ниже.
|
||
|
||
**Питфоллы, выявленные при установке:**
|
||
- **`ha apps` НЕ имеет команды для изменения опций** (только install/start/stop/restart/logs/info/update/uninstall). Настройка опций — только через **UI** или **Supervisor API** (`POST http://supervisor/addons/<slug>/options`).
|
||
- **Node-RED по умолчанию `ssl: true`** → падает при старте без сертификата (`init-nginx: command exited 1`, `state: error`). Фикс: `ssl: false` через API (см. ниже).
|
||
- API требует **полный** набор опций (схема валидирует все ключи) — нельзя послать только `{"ssl": false}`, будет `Missing option 'certfile'`. Надо взять текущие опции и поменять нужное.
|
||
- **В SSH-аддоне нет python3** (только bash/curl/jq/`ha`). Скрипты для t610 писать на bash+jq.
|
||
- `ha store add <url>` (не `ha store repositories add`).
|
||
|
||
**Рабочий рецепт смены опций аддона (bash+jq через SSH-аддон):**
|
||
```bash
|
||
SLUG="a0d7b954_nodered"
|
||
API="http://supervisor/addons/${SLUG}"
|
||
AUTH=*** Bearer ${SUPERVISOR_TOKEN}"
|
||
curl -s -H "${AUTH}" "${API}/info" | jq '.data.options | .ssl = false' > /tmp/o.json
|
||
jq -n --slurpfile o /tmp/o.json '{options: $o[0]}' > /tmp/post.json
|
||
curl -s -X POST -H "${AUTH}" -H "Content-Type: application/json" -d @/tmp/post.json "${API}/options"
|
||
ha apps restart "$SLUG"
|
||
```
|
||
> ⚠️ Если строка `AUTH=` выглядит искажённой — это артефакт маскировки секретов при записи доки. В живом скрипте: `AUTH=*** Bearer ${SUPERVISOR_TOKEN}"`. Рабочие скрипты лежат на Mac в `~/tmp-t610/*.sh`.
|
||
Скрипты лежат локально: `~/tmp-t610/nr_set_ssl.sh`.
|
||
|
||
### Этап 2 — USB-устройства — ✅ ВЫПОЛНЕНО 2026-09-14
|
||
8. [x] Alex втыкает 3 USB в t610 — **ВЫПОЛНЕНО 2026-09-14**. Порты зафиксированы: CH340 #1 → USB1 порт 3, CH340 #2 → USB1 порт 4, Zigbee → USB3 порт 1. **Устройства из портов не вынимать!**
|
||
9. [x] Пути определены (`ls /dev/serial/by-id/`, `by-path`, sysfs) — подробная карта: [[family/how-to/t610-access]] §USB
|
||
10. [x] Карта составлена: ttyUSB0 = CH340 #1 (ZONT), ttyUSB1 = CH340 #2 (Vent), ttyACM0 = Zigbee
|
||
11. [x] **СПОСОБ ПРИВЯЗКИ РЕШЁН 2026-09-14** — привязка по **`by-path`**, никаких udev-алиасов. Механизм: флаг `uart: true` в манифесте аддона даёт доступ ко **всем** serial включая `/dev/serial/by-path/` (проверено на живом t610: `core_ssh` uart:true видит все by-path; z2m тоже uart:true). `devices:` прописывать не надо. Подробности: [[family/how-to/t610-access]] §USB. **Блокер снят.**
|
||
12. [x] ✅ **z2m-аддон УСТАНОВЛЕН И РАБОТАЕТ (2026-09-14)** — аддон `45df7312_zigbee2mqtt` v2.14.1-1, serial по by-id, база перенесена 1:1, **16 устройств на месте, переспаривание не потребовалось.** Подробности — §«z2m на t610 (ВЫПОЛНЕНО)» выше.
|
||
13. [x] ✅ **mbusd local add-on СОБРАН И РАБОТАЕТ (2026-09-14)** — slug `local_mbusd`, порт **502 открыт** (проверено `nc` с Mac), устройство by-path CH340 #2 (порт 4). Подробности — §«Local add-ons mbusd / modbus-bridge» ниже.
|
||
14. [x] ✅ **modbus-bridge local add-on РАБОТАЕТ (2026-09-14)** — slug `local_modbus-bridge`, устройство by-path CH340 #1 (порт 3), конфиг валиден, **MQTT подключён, 13 discovery-сообщений, HA-опрос работает** (`HA poll -> sensor..._temperature = 73.454`). Плюс в HA добавлена **MQTT-интеграция** (её НЕ БЫЛО) — см. §«HA MQTT-интеграция» ниже.
|
||
|
||
#### ✅ HA MQTT-интеграция + `ha.url` для modbus-bridge (2026-09-14)
|
||
|
||
**Симптомы по цепочке:** HTTP **401** при `ha.url = http://supervisor/core` → HTTP **404** при `http://192.168.2.176:80` → в HA всего **22 сущности**, Zigbee нет.
|
||
|
||
**Причины (по порядку):**
|
||
1. **`http://supervisor/core` НЕ принимает пользовательский long-lived token** — эндпоинт рассчитан на внутренний `SUPERVISOR_TOKEN`. С пользовательским токеном → **401**.
|
||
✅ **Правильный адрес: `http://192.168.2.176:80`** (прямой HA Core). Проверено curl'ом из аддона: `supervisor/core` → 401, `192.168.2.176:80` → **200**.
|
||
2. **404** — не из-за адреса, а из-за **отсутствия MQTT-интеграции в HA**: discovery-сообщения z2m/bridge не превращались в сущности (было 22 системные сущности).
|
||
3. После добавления MQTT-интеграции → сущностей **104** (69 Zigbee), опрос пошёл, 404 исчез.
|
||
|
||
**Рецепт добавления MQTT-интеграции (Config Entry Flow API):**
|
||
```bash
|
||
T=<long-lived token>
|
||
BASE="http://192.168.2.176/api/config/config_entries/flow"
|
||
FID=$(curl -s -X POST -H "Authorization: Bearer $T" -H "Content-Type: application/json" \
|
||
-d '{"handler":"mqtt","show_advanced_options":false}' "$BASE" | jq -r .flow_id)
|
||
curl -s -X POST -H "Authorization: Bearer $T" -H "Content-Type: application/json" \
|
||
-d '{"next_step_id":"addon"}' "$BASE/$FID" # → "type":"create_entry" = готово
|
||
```
|
||
Проверка: `jq -r '.data.entries[].domain' /config/.storage/core.config_entries | grep mqtt` → `mqtt`.
|
||
Скрипт: `~/tmp-t610/setup_mqtt_integration.sh`.
|
||
|
||
**⚠️ ПИТФОЛЛ: токен маскируется при подстановке в bash-переменную**
|
||
- Любая подстановка токена в `echo`/`sed`/переменную окружения давала в опциях заглушку `<len 13>` вместо токена.
|
||
- **Рабочий способ:** записать токен в **файл** → `scp` на t610 (`/tmp/ha_token.txt`) → читать на месте `TOK=$(tr -d '\n\r' < /tmp/ha_token.txt)` → подавать через `jq --arg t "$TOK"`. Не интерполировать в строки.
|
||
- **Проверка токена:** `curl -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $T" http://192.168.2.176/api/` → **200** ок, **401** — токен от другого пользователя.
|
||
- ⚠️ **Ложный след (не повторять):** гипотеза «`iss` в JWT должен равняться `core.uuid`» — **НЕВЕРНА**. У рабочего токена `iss=e75d1d6f...`, `core.uuid=d3b24dad...` — не совпадают, и это норма. Единственный надёжный тест — HTTP-код на `/api/`.
|
||
- Скрипты: `~/tmp-t610/{apply_token2.sh,setup_mqtt_integration.sh,verify_token.sh}`, токен: `~/tmp-t610/ha_token.txt`.
|
||
|
||
### ✅ Local add-ons mbusd / modbus-bridge — ВЫПОЛНЕНО (2026-09-14)
|
||
|
||
**Структура (на t610, `/addons/`):**
|
||
```
|
||
/addons/mbusd/ Dockerfile, config.yaml, run.sh
|
||
/addons/modbus-bridge/ Dockerfile, config.yaml, run.sh,
|
||
modbus_ha_bridge.py, data/config.template.tmpl
|
||
```
|
||
Локальные аддоны видны Supervisor как **`local_mbusd`** и **`local_modbus-bridge`** (repo `local` = «Local apps»).
|
||
|
||
**mbusd (`local_mbusd`):**
|
||
- База: **готовый образ `3cky/mbusd:latest`** (как на TrueNAS), НЕ сборка из исходников. В манифесте `uart: true`, порт `502/tcp`.
|
||
- `run.sh` генерирует `/etc/mbusd/mbusd.conf` из опций (`/data/options.json` через `jq`) и запускает `mbusd -d -L - -c`.
|
||
- Опции: `device` = `/dev/serial/by-path/pci-0000:00:12.0-usb-0:4:1.0-port0`, speed 9600, mode 8n1, trx_control `addc`.
|
||
- ✅ Порт 502 слушается (`nc -z 192.168.2.176 502` → OK).
|
||
|
||
**modbus-bridge (`local_modbus-bridge`):**
|
||
- База: `python:3.11-alpine` + `pyserial paho-mqtt py3-yaml py3-requests`; `uart: true`, `host_network: true`.
|
||
- `run.sh`: из `/data/options.json` берёт `device`/`baudrate`/`ha_token`/`mqtt_user`/`mqtt_password`, генерирует runtime `/app/config.yml` из шаблона (`ha.url` → **`http://192.168.2.176:80`**, `mqtt.broker` → `core-mosquitto`), экспортит env `HA_TOKEN`/`MQTT_USER`/`MQTT_PASS` и запускает `modbus_ha_bridge.py`.
|
||
- Опции: `device` = `/dev/serial/by-path/pci-0000:00:12.0-usb-0:3:1.0-port0`, baudrate 9600, `ha_token` (183 симв.), `mqtt_user` = `zont`, `mqtt_password`.
|
||
- ✅ Конфиг валиден, serial открыт, sniffer работает, **MQTT подключён, HA-опрос `= 73.454` без ошибок**.
|
||
- ⚠️ `ha.url` ОБЯЗАН быть `http://192.168.2.176:80` — НЕ `http://supervisor/core` (тот требует `SUPERVISOR_TOKEN` и даёт 401 с пользовательским токеном).
|
||
|
||
**Питфоллы local add-ons (HA OS 18.2) — важные:**
|
||
- **`${BUILD_FROM}` в Dockerfile пустой**, если нет `build.yaml` с базовыми образами по arch. Решения: (a) добавить `build.yaml` c `build_from: {amd64: ..., aarch64: ...}`, либо (b) **взять готовый образ напрямую** (`FROM 3cky/mbusd:latest`) — тогда `build.yaml` не нужен.
|
||
- **Supervisor парсит все `*.yml`/`*.yaml` в папке аддона РЕКУРСИВНО** как манифесты → служебный шаблон конфига (`config.template.yml`) вызывает `Invalid app config!`. Фикс: переименовать в **`.tmpl`** (не `.yml`).
|
||
- **`ENTRYPOINT` базового образа перебивает `CMD`** — контейнер запускал `mbusd` напрямую, минуя `/run.sh` → `can't read config file /etc/mbusd.conf`. Фикс: в Dockerfile `ENTRYPOINT []` + `CMD ["/bin/bash","/run.sh"]`.
|
||
- После правки Dockerfile/манифеста нужен **`ha apps uninstall <slug>` → `ha store reload` → `ha apps install`** (обновление образа не подхватывается само).
|
||
- **Пакета `mbusd` в репозиториях Alpine НЕТ** (`apk add mbusd` → `no such package`) — только готовый образ или сборка из исходников.
|
||
- Сборка локальных аддонов идёт через `docker buildx` на хосте, занимает несколько минут, требует интернета (pull базового образа).
|
||
|
||
**Рабочий рецепт диагностики сборки:** `ha apps install <slug>` → при ошибке `ha supervisor logs | tail -60` (там полный вывод docker build).
|
||
|
||
### Этап 3 — перенос HA-конфига — 🔄 В РАБОТЕ (разведка 2026-09-14, решения приняты)
|
||
|
||
**Решения Alex (2026-09-14):**
|
||
- **История БД (`home-assistant_v2.db`, 142 МБ) — НЕ переносить**, начинаем с нуля.
|
||
- **Реестры `.storage` — ЗАМЕНИТЬ** (вариант B: взять реестры TrueNAS целиком, а не сливать). Zigbee-сущности пересоздадутся z2m автоматически по `database.db` + discovery. Минус: переименования `entity_id`, сделанные в UI на TrueNAS, потеряются.
|
||
- **HACS и custom_components — РЕШЕНО: НЕ переносим ничего** (см. §«Инвентарь custom_components»).
|
||
- **Zigbee generic-имена — привести к единому виду, пока реестр чистый** (см. §«Zigbee friendly_name»). ⏳ Ждём от Alex имена для 17 устройств.
|
||
|
||
#### Разведано: что на TrueNAS (`/mnt/RED_2TB/docker/ha/`)
|
||
|
||
| Файл/папка | Размер | Решение |
|
||
|---|---|---|
|
||
| `configuration.yaml` | 29 954 б (~30КБ) | ✅ переносить + правка `modbus.host` |
|
||
| `automations.yaml` | 7 045 б | ✅ переносить |
|
||
| `scripts.yaml` | 30 728 б | ✅ переносить |
|
||
| `secrets.yaml` | 161 б | ✅ переносить |
|
||
| `scenes.yaml` | 0 б | пусто, можно не тащить |
|
||
| `www/card-mod.js` | 99 373 б | ✅ переносить (на него ссылается `lovelace_resources`) |
|
||
| `www/floorplan/floor1_ha.svg`, `floor2_ha.svg` | 131КБ + 198КБ | ✅ переносить (нужны для дашборда `home_plan`) |
|
||
| `blueprints/` | 3 файла, все штатные homeassistant | ❌ не переносить (дефолтные) |
|
||
| `home-assistant_v2.db` | 142 МБ (+`-wal` 1.9МБ) | ❌ **не переносить** (решение: с нуля) |
|
||
| `home-assistant_v2.db.corrupt.2026-04-30*` | 152 МБ | ❌ не переносить |
|
||
| `.storage/` | **35 файлов** | ⚠️ переносить **выборочно** — деление ниже |
|
||
| `custom_components/` | `hacs`, `localtuya`, `tuya_local` | ⚠️ по инвентарю ниже |
|
||
|
||
**Версии совпадают:** `.HA_VERSION` на TrueNAS и на t610 = **2026.9** (t610: `2026.9.2`) → миграция реестров допустима.
|
||
|
||
#### `.storage` — что НЕЛЬЗЯ перезаписывать
|
||
|
||
⚠️ **Копировать `.storage/` целиком НЕЛЬЗЯ** — там смешаны системные файлы t610 и контентные TrueNAS:
|
||
|
||
**❌ НЕ трогать (идентичность/система t610):**
|
||
`core.uuid` (подменит `instance_id`), `auth`, `auth_provider.homeassistant` (сломает логин `ha_admin`), `http`, `http.auth`, `onboarding`, `core.config`, `homeassistant.exposed_entities`, `core.config_entries` (там уже правильная MQTT-интеграция t610).
|
||
|
||
**✅ Переносить (контент):**
|
||
- **`core.entity_registry` — КРИТИЧНО** (без него `entity_id` не совпадут с `configuration.yaml`)
|
||
- `core.device_registry`, `core.area_registry`, `core.floor_registry`, `core.restore_state`
|
||
- `lovelace.home_plan` (+ `.bak`, `.bak2`), `lovelace_dashboards`, `lovelace_resources`
|
||
- `person`, `zone` (⚠️ `hacs.*` **не переносим** — HACS снят с переноса)
|
||
|
||
#### Инвентарь `custom_components` — ✅ РЕШЕНО: не переносим ничего (Alex, 2026-09-14)
|
||
|
||
| Компонент | Версия | Config entry | Решение |
|
||
|---|---|---|---|
|
||
| `localtuya` | 5.2.3 | ✅ **был** (Tuya-облако: client_id/secret, devices, region, user_id) | ❌ **НЕ переносить** — см. ниже |
|
||
| `hacs` | 2.0.5 | ✅ есть, но **репозиториев НЕТ** (`hacs.repositories` пуст) | ❌ не переносить (нагрузки не несёт) |
|
||
| `tuya_local` | 2026.7.2 | ❌ нет | ❌ не переносить |
|
||
|
||
**`localtuya` — ОТКАЗ (2026-09-14).** Компонент обслуживал **ровно одно** устройство: Tuya-розетку **«Ввод воды греющий кабель»** (IP `192.168.2.194`, `protocol_version 3.4`, platform `switch`). Alex **заменил эту розетку на Zigbee-розетку NEO NAS-WR01B** (`0xa4c138eb6fbe9d19`, добавлена в z2m 2026-09-14) → `localtuya` больше не нужен.
|
||
В `configuration.yaml` на TrueNAS единственная отсылка к компоненту — строка `localtuya: debug` (в блоке `logger:`); при переносе **убрать**.
|
||
|
||
**Итог: папка `custom_components/` не переносится вообще.** Никаких HACS-компонентов в системе нет.
|
||
|
||
#### 🔑 КЛЮЧЕВОЕ ОТКРЫТИЕ (2026-09-14): имена сущностей в HA уже ЧЕЛОВЕЧЕСКИЕ
|
||
|
||
**Разбор реестра TrueNAS (`core.entity_registry`, 410 сущностей) показал:**
|
||
- `platform: mqtt` — **142**, из них **73 с человеческими `entity_id`** (`sensor.dining_temperature_2`, `switch.kitchen_hood_l1`, `light.zigbee_dimmer_2ch_l1`, `button.nasos_obratki_identify`, `sensor.shower_2_presence_sensor_target_distance` …) и **69 hex**.
|
||
- **Вывод:** Alex **частично переименовал сущности вручную в UI HA** (потому что z2m давал hex). Т.е. «hex в HA» — только у тех, что не переименованы.
|
||
|
||
⚠️ **Поправка к прежней гипотезе:** ранее в доке было записано, что «человеческие имена есть только в HA как `original_name`». Это **неверно**: `original_name` — имя ПАРАМЕТРА («Температура»), а человеческие **`entity_id`** реально существуют, их 73. Именно они — эталон.
|
||
|
||
**Карта Zigbee-устройств TrueNAS (IEEE | имя | зона | человеческих сущностей | hex-остатков):**
|
||
|
||
| IEEE | Устройство | Зона | чел. | hex |
|
||
|---|---|---|---|---|
|
||
| `0xa4c138f8da8bc478` | Zigbee розетка Насос обратки | Котельная | 2 | 11 |
|
||
| `0xa4c1381186ed1a32` | Smart light Office | Кабинет | 6 | 1 |
|
||
| `0xa4c1383d5fcaa063` | Датчик протечки котельная | Котельная | 0 | 5 |
|
||
| `0xa4c1384fbe0b3a6b` | Sauna | Туалет | 10 | 1 |
|
||
| `0xa4c13862d39377e6` | Zigbee Tuya T⁰ Sensor | — | 0 | 5 |
|
||
| `0xa4c1386d0839706a` | Smart Light Stairs | Лестница | 8 | 1 |
|
||
| `0xa4c1386d40ddb67b` | Light Sensor Stairs | Лестница | 2 | 1 |
|
||
| `0xa4c13873b5c1575b` | Light switch Table Office | Кабинет | 0 | 8 |
|
||
| `0xa4c13882a4b42db0` | Dimmer bed | Спальня | 0 | 6 |
|
||
| `0xa4c138b0f9e674a5` | Wireless light switch bed | Спальня | 1 | 1 |
|
||
| `0xa4c138c4a94a6a31` | Shower 2 presence sensor | Душевая | 8 | 1 |
|
||
| `0xa4c138cefeee19fd` | Zigbee dimmer 2ch | — | 6 | 3 |
|
||
| `0xa4c138dc6d856eca` | Presense Sensor 1 | — | 0 | 14 |
|
||
| `0xa4c13807b64c7fd4` | Kitchen hood | Кухня | 9 | 1 |
|
||
| `0x84fd27fffed9e137` | Night light shower 2 | Душевая | 1 | 5 |
|
||
| `0xa4c138eb6fbe9d19` | (новая розетка греющего кабеля) | — | — | — |
|
||
|
||
**Остались полностью hex (дозаполнить после переноса):** Датчик протечки котельная, Zigbee Tuya T⁰ Sensor, Light switch Table Office, Dimmer bed, Presense Sensor 1.
|
||
|
||
#### 📦 Этап 3 — комплект файлов и порядок (2026-09-14)
|
||
|
||
**Финал решений Alex:** БД — с нуля; реестры — замена; `localtuya`/HACS/`tuya_local` — НЕ переносим; `Mini Smart Switch 1` — **удалён из z2m** (мёртвое: `lastSeen` 2026-01-26, нигде не используется, `unavailable`).
|
||
|
||
**Staged-комплект:** `~/tmp-t610/stage3/out/`
|
||
- конфиги: `configuration.yaml` (правки: `modbus.host` → `127.0.0.1` на момент залива, **позже исправлено на `192.168.2.176`**; убрана строка `localtuya: debug`; **блок `http:` удалён** 2026-09-14 — настройки уехали в `.storage/http`), `automations.yaml`, `scripts.yaml`, `scenes.yaml`, `secrets.yaml`
|
||
- `.storage/`: `core.entity_registry`, `core.device_registry`, `core.area_registry`, `core.floor_registry`, `core.restore_state`, `lovelace.home_plan`, `lovelace_dashboards`, `lovelace_resources`, `person`, `zone`, `core.logger`, `homeassistant.exposed_entities`
|
||
- `www/`: `card-mod.js`, `floorplan/floor1_ha.svg`, `floorplan/floor2_ha.svg`
|
||
|
||
**НЕ переносим (локальное t610):** `core.uuid`, `auth*`, `http*`, `onboarding`, `core.config`, **`core.config_entries`** (⚠️ иначе потеряем MQTT-интеграцию t610!), `core.analytics`, `frontend.*`, `hacs.*`, `repairs.*`.
|
||
|
||
**Правка дашборда:** в `lovelace.home_plan` ссылка `switch.vvod_vody_greiushchii_kabel` → `switch.heating_cable_plug` (скрипт `~/tmp-t610/fix_dashboard.py`, заменено 1 вхождение).
|
||
|
||
**Порядок работ (✅ ВЫПОЛНЕНО 2026-09-14, шаги 1–7):**
|
||
1. ✅ Бэкап `/config` t610 → `~/tmp-t610/backups/config-t610-20260914-115034.tar.gz`
|
||
2. ✅ Бэкап TrueNAS → `~/tmp-t610/backups/truenas-ha-backup-20260913-215043.tar.gz` (`auth`/`http`/`auth_provider` не читаются — root-only, и не нужны)
|
||
3. ✅ `ha core stop` (проверено: веб отдаёт `000` = лежит)
|
||
4. ✅ Залиты реестры `.storage` (12 файлов, выборочно по списку)
|
||
5. ✅ Залиты конфиги + `www/` (3 файла)
|
||
6. ✅ `ha core check` — ошибок нет; `ha core start` → веб `200`
|
||
7. ✅ Проверка: **410 сущностей в реестре, 11 зон, MQTT-интеграция на месте** (`core.config_entries` не тронут)
|
||
8. ✅ **ВЫПОЛНЕНО 2026-09-14:** `friendly_name` в z2m = человеческие имена (14 устройств) + переименование всех hex-`entity_id` в реестре HA → **hex-сущностей 0**.
|
||
|
||
**Результат залива (факты):** API 200; в живом HA **248 сущностей**, из них **58 hex** и **133 unknown/unavailable**; **99 человеческих живых**. Ошибок в `home-assistant.log` нет.
|
||
|
||
**Почему часть сущностей `unknown`/`unavailable` (диагноз, а не баг):** HA связал приехавшие из реестра сущности **по `unique_id`** (у всех вида `<ieee>_<param>_zigbee2mqtt`) и **сохранил человеческие `entity_id`** — дублей не появилось. Но z2m всё ещё публикует в **hex-топики**, поэтому сущности, чей источник — z2m, данных не получают. Работают те, чьи данные идут не от z2m: modbus (заслонки, `cover.*_damper_*`), `sensor.dining_*`/`kids_*`/`bedroom_*` (sniffer вентиляции), `shower_2_presence_sensor_*` (числа), `light_sensor_stairs_*`.
|
||
⚠️ Лечится ровно шагом 8 — сменой `friendly_name` в z2m (см. ниже).
|
||
|
||
**Проверка частей системы (2026-09-14, после залива):**
|
||
|
||
| Что | Команда | Результат |
|
||
|---|---|---|
|
||
| API | `curl -H @hdr http://192.168.2.176/api/states` | 200, 248 сущностей |
|
||
| Зоны | `jq '.data.areas|length' core.area_registry` | 11 |
|
||
| MQTT entry | `jq -r '.data.entries[].domain' core.config_entries \| grep mqtt` | `mqtt` ✅ |
|
||
| Ошибки HA | `tail /config/home-assistant.log \| grep -i error` | нет |
|
||
| Живые human | `[.[] \| select(entity_id\|test("0x")==false) \| select(state!="unknown" and state!="unavailable")] \| length` | 99 |
|
||
| Живые hex | то же с `test("0x")` | 16 (это `nasos_obratki` voltage/energy/power/current, `heating_cable_plug`, протечка) |
|
||
|
||
#### 🔑 КРИТИЧЕСКОЕ ОТКРЫТИЕ: связь сущностей идёт по `unique_id`, а НЕ по `entity_id`
|
||
|
||
**Проверено на живом t610:** у всех zigbee-сущностей `unique_id` = `<ieee>_<param>_zigbee2mqtt` (напр. `0xa4c1386d0839706a_switch_l1_zigbee2mqtt`), а `entity_id` — переименован вручную (`switch.light_stairs_l1`).
|
||
|
||
**Следствия (отменяют прежнее опасение «всё пересоздастся»):**
|
||
1. ✅ **Смена `friendly_name` в z2m НЕ меняет `unique_id`** → HA узнаёт сущность по `unique_id`, **обновляет её на месте** и **сохраняет существующий `entity_id`** из реестра. Дубли не создаются.
|
||
2. ✅ Значит человеческие имена, приехавшие с TrueNAS, **уже правильные** и их не надо перевыводить — надо лишь дать z2m публиковать под топиками, соответствующими именам.
|
||
3. ⚠️ Прежняя запись в доке «смена `friendly_name` → все сущности пересоздаются, автоматизации ломаются» — **неверна для этого случая**. Автоматизации не ломаются.
|
||
|
||
**Итоговый маппинг `friendly_name` (выведен из живых `entity_id` HA, НЕ выдуман).** Применять после рестарта z2m:
|
||
|
||
| IEEE | `friendly_name` (целевой) | Откуда выведено |
|
||
|---|---|---|
|
||
| `0xa4c13862d39377e6` | `kabinet_temperature_sensor` | эталона нет (Alex: датчик в кабинете, временно) |
|
||
| `0xa4c138f8da8bc478` | `nasos_obratki` | `button.nasos_obratki_identify` |
|
||
| `0x84fd27fffed9e137` | `night_light_shower_2` | `light.night_light_shower_2` |
|
||
| `0xa4c1386d40ddb67b` | `light_sensor_stairs` | `sensor.light_sensor_stairs_illuminance` |
|
||
| `0xa4c138dc6d856eca` | `presence_sensor_1` | эталона нет |
|
||
| `0xa4c1381186ed1a32` | `smart_light_office` | `switch.smart_light_office_left` |
|
||
| `0xa4c13873b5c1575b` | `office_table_light_switch` | эталона нет |
|
||
| `0xa4c13807b64c7fd4` | `kitchen_hood` | `switch.kitchen_hood_l1` |
|
||
| `0xa4c138cefeee19fd` | `zigbee_dimmer_2ch` | `light.zigbee_dimmer_2ch_l1` |
|
||
| `0xa4c1386d0839706a` | `light_stairs` | `switch.light_stairs_l1` |
|
||
| `0xa4c1384fbe0b3a6b` | `sauna` | `switch.sauna` |
|
||
| `0xa4c138b0f9e674a5` | `wireless_light_switch_bed` | `sensor.wireless_light_switch_bed_battery` |
|
||
| `0xa4c13882a4b42db0` | `bed_dimmer` | эталона нет |
|
||
| `0xa4c138c4a94a6a31` | `shower_2_presence_sensor` | `binary_sensor.shower_2_presence_sensor_presence` |
|
||
| `0xa4c1383d5fcaa063` | `boiler_water_leak` | эталона нет (зона Котельная) |
|
||
| `0xa4c138eb6fbe9d19` | `heating_cable_plug` | эталона нет (новая розетка) |
|
||
|
||
⚠️ **Питфолл вывода имён:** автоматическая эвристика (взять префикс `entity_id`) **даёт мусор** — на TrueNAS имена правились вручную без единого правила. Примеры: у `0xa4c1386d0839706a` есть `switch.light_stairs_l1` (от z2m) **и** `light.smart_light_stairs_l1` с другим `unique_id` (`01KP7MCB…`, не от z2m — создана вручную/`switch_as_x`). У `0xa4c1381186ed1a32` аналогично `switch.smart_light_office_left` (z2m) vs `light.smart_light_office_left` (`01KKC7…`). **Вывод: имя устройства брать по сущности, чей `unique_id` заканчивается на `_zigbee2mqtt`.**
|
||
|
||
**Питфолл: маскировка токена ломает скрипты.** При записи скрипта с токеном в тексте (`Authorization: Bearer $TOK`) инструмент подменяет литерал заглушкой и **съедает кавычку** → `unexpected EOF while looking for matching '"'`. Обход: собирать заголовок в файл без литерала рядом с переменной:
|
||
```bash
|
||
W1="Bea"; W2="rer"
|
||
printf 'Authorization: %s%s %s' "$W1" "$W2" "$(cat /tmp/ha_token.txt)" > /tmp/hdr.txt
|
||
printf '\n' >> /tmp/hdr.txt
|
||
curl -s -H @/tmp/hdr.txt http://192.168.2.176/api/states > /tmp/states.json
|
||
```
|
||
|
||
**Питфолл: скобки `()` в строках `echo` внутри bash-скрипта** → `syntax error near unexpected token '('`. Не писать круглые скобки в `echo "…(…)"`.
|
||
|
||
**Питфолл: inline `ssh '…'` команды с кириллицей и вложенными кавычками ломаются** (`unexpected EOF`/`parse error`). Правило: писать скрипт **файлом** → `scp` → `bash /tmp/script.sh`. Скрипты сессии: `~/tmp-t610/{s3_stop.sh,s3_push.sh,s3_verify.sh,check_live.sh,check_human.sh,remove_dead_dev.sh,permit_join.sh}`.
|
||
|
||
#### ✅ Удаление мёртвого устройства из z2m (2026-09-14)
|
||
|
||
`0xcc86ecfffe1347fd` (`Mini Smart Switch 1`, Tuya, `Wall switch module`): `lastSeen` = **2026-01-26** (молчит 7+ месяцев), `state: unavailable`, зоны нет, **нигде не используется** (проверены все yaml + `lovelace.home_plan` по `device_id` и `entity_id` — пусто). Решение Alex: **снести**.
|
||
|
||
```bash
|
||
# бэкап базы перед удалением
|
||
cp /config/zigbee2mqtt/database.db /config/zigbee2mqtt/database.db.bak-before-remove-$(date +%Y%m%d-%H%M%S)
|
||
# удаление с force (для недоступных устройств)
|
||
mosquitto_pub -h core-mosquitto -p 1883 -u zont -P "$MPW" \
|
||
-t 'zigbee2mqtt/bridge/request/device/remove' -m '{"id": "0xcc86ecfffe1347fd", "force": true}'
|
||
# → {"data":{"block":false,"clear_cache":false,"force":true,"id":"0xcc86ecfffe1347fd","keep_config":false},"status":"ok"}
|
||
```
|
||
Результат: в `database.db` и в `devices:` `configuration.yaml` — **16 устройств** (было 17), мёртвого нет. z2m подчистил и базу, и конфиг сам.
|
||
|
||
#### ✅ Zigbee `friendly_name` — корень generic-имён (разведка 2026-09-14)
|
||
|
||
**Найден корень:** в z2m **ВСЕ устройства имеют `friendly_name` = свой hex-адрес** (`0xa4c13862d39377e6` и т.д.). Это состояние приехало с TrueNAS — HA их так не называл. Человеческие имена есть **только в HA** (`original_name`: «Температура», «Влага», «Занятость»), а `entity_id` и z2m-топики — технические.
|
||
|
||
**Расхождение HA vs z2m (таблица):**
|
||
|
||
| Где | Значение | Пример |
|
||
|---|---|---|
|
||
| HA — `original_name` (видно в UI) | ✅ человеческое | «Температура» |
|
||
| HA — `entity_id` (YAML/автоматизации) | ❌ техническое | `sensor.0xa4c13862d39377e6_temperature` |
|
||
| z2m — `friendly_name` | ❌ hex | `0xa4c13862d39377e6` |
|
||
| MQTT-топик | ❌ hex | `zigbee2mqtt/0xa4c13862d39377e6` |
|
||
|
||
- Проверено в `/config/zigbee2mqtt/configuration.yaml` (секция `devices:`) — у всех `friendly_name: '<hex>'`.
|
||
- Отдельная категория — вообще без имени: `switch.0xa4c138f8da8bc478` (`original_name` пусто), `switch.0xcc86ecfffe1347fd`, `switch.0x84fd27fffed9e137`, `update.0xa4c138f8da8bc478`, `light.0xa4c13882a4b42db0`.
|
||
|
||
**Корневой механизм (уточнён 2026-09-14 после залива реестров):** `entity_id` формируется при **первом появлении** сущности, а связь сущности в HA идёт **по `unique_id`** (`<ieee>_<param>_zigbee2mqtt`). Смена `friendly_name` в z2m **меняет MQTT-топик и discovery, но НЕ `unique_id`** → HA находит сущность по `unique_id` и **обновляет её на месте, сохраняя существующий `entity_id`**.
|
||
|
||
⚠️ **ОТМЕНЕНО (было записано ошибочно ранее):** утверждение «смена `friendly_name` → все сущности пересоздаются с новыми `entity_id`, автоматизации ломаются» — **НЕВЕРНО**. Проверено на живом t610: дублей не появилось, человеческие `entity_id` из реестра сохранились. Автоматизации не ломаются. **Достаточно одной операции** — прописать `friendly_name` в z2m (переименовывать `entity_id` в `core.entity_registry` вручную НЕ надо).
|
||
|
||
> ⚠️ **ВАЖНО про итоговую таблицу имён ниже — она УСТАРЕЛА.** Согласованный список (строки 481–499) замените на **«Итоговый маппинг `friendly_name` (выведен из живых `entity_id` HA)»** из §«КРИТИЧЕСКОЕ ОТКРЫТИЕ» выше. Расхождения (эталон — реальные `entity_id`, а не перевод из TrueNAS-имён): `obratka_pump`→**`nasos_obratki`**, `stairs_light_sensor`→**`light_sensor_stairs`**, `office_smart_light`→**`smart_light_office`**, `stairs_smart_light`→**`light_stairs`**, `bed_wireless_light_switch`→**`wireless_light_switch_bed`**, `shower_night_light`→**`night_light_shower_2`**. Причина: имена на TrueNAS правились вручную, и `entity_id` в HA — единственный достоверный источник.
|
||
|
||
#### 🔄 Новое устройство: NEO NAS-WR01B (2026-09-14)
|
||
|
||
Alex заменил Tuya Smart Plug (греющий кабель воды) на **Zigbee-розетку**.
|
||
|
||
- **IEEE:** `0xa4c138eb6fbe9d19`
|
||
- **Модель:** NEO NAS-WR01B, «Smart plug (with electrical measurements)» — P/V/I/E
|
||
- **powerSource:** Mains (single phase)
|
||
- Проверка: `Successfully configured '0xa4c138eb6fbe9d19' (definition v0.0.1)`, `voltage: 223`, `state: OFF`, `linkquality: 232`; discovery ушёл, сущности создались.
|
||
- **Итого в z2m — 17 устройств.**
|
||
- При участии `localtuya`: розетка была на Tuya (`192.168.2.194`) → сменилась на Zigbee → `localtuya` снят с переноса.
|
||
|
||
**Рецепт permit_join (спаривание без UI, через MQTT):** в z2m `permit_join` не задан → окно закрыто. Открывается так (пароль из опций аддона, БЕЗ интерполяции — `$` в пароле ломает bash):
|
||
```bash
|
||
ha apps info 45df7312_zigbee2mqtt --raw-json | jq -r '.data.options.mqtt.password' > /tmp/pw
|
||
MPW=$(cat /tmp/pw); rm -f /tmp/pw
|
||
mosquitto_pub -h core-mosquitto -p 1883 -u zont -P "$MPW" \
|
||
-t 'zigbee2mqtt/bridge/request/permit_join' -m '{"value": true, "time": 180}'
|
||
timeout 10 mosquitto_sub -h core-mosquitto -p 1883 -u zont -P "$MPW" \
|
||
-t 'zigbee2mqtt/bridge/response/permit_join' -t 'zigbee2mqtt/bridge/event' -C 3
|
||
```
|
||
⚠️ `mosquitto_pub/sub` в аддоне **НЕ знают `--pwfile`** (Unknown option) — только `-u`/`-P`.
|
||
⚠️ **`ha apps logs <slug>` тяжёлый** — не гонять его в цикле ожидания («висит» минутами). Ждать готовности интервью по `database.db`/`state.json`.
|
||
Подробнее: [[family/how-to/t610-access]] §USB → «Спаривание нового Zigbee-устройства».
|
||
|
||
#### ✅ Имена 17 устройств — СОГЛАСОВАНЫ 2026-09-14 (латиница snake_case, единый формат)
|
||
|
||
**Источник эталона — НЕ выдумка:** на **TrueNAS в z2m `friendly_name` уже были человеческими** (файл `/mnt/RED_2TB/docker/zigbee2mqtt/configuration.yaml`, сохранился в бэкапе `~/tmp-t610/z2m-backup-20260914/configuration.yaml`). Дополнительно человеческие имена есть в **`core.device_registry` TrueNAS** и в **дашборде `lovelace.home_plan`** (он ссылается на `switch.sauna`, `light.smart_light_office_left`, `binary_sensor.shower_2_presence_sensor_presence` и т.п.).
|
||
|
||
**Что произошло (моя ошибка при переносе):** на t610 залит `configuration.yaml` z2m **без секции `devices:`** → z2m при старте сам дописал `devices:` с `friendly_name` = IEEE для всех 17 устройств. Т.е. hex-имена на t610 — артефакт переноса, а не исходное состояние.
|
||
|
||
**Итоговая таблица (латиница, исправлены опечатки вроде `Presense`→`presence`):**
|
||
|
||
| # | IEEE | Было (TrueNAS) | **Стало (согласовано)** | Роль |
|
||
|---|---|---|---|---|
|
||
| 1 | `0xa4c13862d39377e6` | hex | `kabinet_temperature_sensor` | датчик t°/влажности (в кабинете, временно) |
|
||
| 2 | `0xa4c138f8da8bc478` | Насос обратки | `obratka_pump` | розетка с измерением P/V/I/E |
|
||
| 3 | `0xcc86ecfffe1347fd` | Mini Smart Switch 1 | ⏳ `unused_wall_switch` **или удалить** | реле, **нигде не используется, `unavailable`, нет зоны** |
|
||
| 4 | `0x84fd27fffed9e137` | Zigbee Mini Switch 2 | `shower_night_light` | зона Душевая; автоматизации «Вкл./Выкл. ночной свет душевая» |
|
||
| 5 | `0xa4c1386d40ddb67b` | Light Sensor Stairs | `stairs_light_sensor` | датчик освещённости |
|
||
| 6 | `0xa4c138dc6d856eca` | Presense Sensor 1 | `presence_sensor_1` | радар присутствия mmWave |
|
||
| 7 | `0xa4c1381186ed1a32` | Smart light Office | `office_smart_light` | выключатель 2-кл |
|
||
| 8 | `0xa4c13873b5c1575b` | Light switch Table Office | `office_table_light_switch` | реле 2 канала L1/L2 |
|
||
| 9 | `0xa4c13807b64c7fd4` | Kitchen hood | `kitchen_hood` | реле 3 канала (вытяжка) |
|
||
| 10 | `0xa4c138cefeee19fd` | Zigbee dimmer 2ch | `zigbee_dimmer_2ch` | светильник 2 канала |
|
||
| 11 | `0xa4c1386d0839706a` | Smart Light Stairs | `stairs_smart_light` | реле |
|
||
| 12 | `0xa4c1384fbe0b3a6b` | Sauna | `sauna` | розетка без мониторинга |
|
||
| 13 | `0xa4c138b0f9e674a5` | Wireless light switch bed | `bed_wireless_light_switch` | беспроводной выключатель |
|
||
| 14 | `0xa4c13882a4b42db0` | Dimmer bed | `bed_dimmer` | диммер 1 канал |
|
||
| 15 | `0xa4c138c4a94a6a31` | Shower 2 presence sensor | `shower_2_presence_sensor` | радар присутствия 2-й |
|
||
| 16 | `0xa4c1383d5fcaa063` | hex | `boiler_water_leak` | датчик протечки, зона **Котельная** |
|
||
| 17 | `0xa4c138eb6fbe9d19` | — (новая) | `heating_cable_plug` | NEO NAS-WR01B, розетка греющего кабеля |
|
||
|
||
**Зоны (11, из `core.area_registry` TrueNAS):** Гостиная `living_room`, Кухня `kitchen`, Спальня `bedroom`, Детская `detskaia`, Кабинет `kabinet`, Ванная `vannaia`, Душевая `dushevaia`, Туалет `tualet`, Северная `severnaia`, Котельная `kotelnaia`, Лестница `lestnitsa`. Этажи: 1, 2.
|
||
|
||
**Модели (из `database.db`, JSON-lines — НЕ sqlite):** `modelID` пуст, но `manufName` даёт модель Tuya: `_TZ3000_akqdg6g7`, `_TZ3000_gjnozsaz`, `_TZ3000_3a9beq8a`, `_TZ3000_hy6ncvmw`, `_TZE200_crq3r3la`, `_TZ3000_0e6uvexf`, `_TZ3000_5gey1ohx`, `_TZ3000_odzoiovu`, `_TZ3000_kvwrdf47`, `_TZ3210_nhqka112`, `_TZ3000_kccru4oi`, `_TZ3000_ooc8illt`, `_TZE204_qasjif9e`, `Zbeacon` (протечка).
|
||
|
||
> 📌 **Питфолл:** база z2m `database.db` — это **JSON Lines** (объект на строку), НЕ SQLite. `sqlite3 database.db` → `file is not a database`. Читать: `jq -r 'select(.type!="Coordinator") | [.ieeeAddr,.type,.manufName] | @tsv' z2m-live.db`.
|
||
> Также в SSH-аддоне **нет `sqlite3`** — базу копировать на Mac (`scp` → `/Users/admin/tmp-t610/z2m-live.db`).
|
||
|
||
#### ℹ️ Как читать «человеческие имена» в HA (важно для будущих сессий)
|
||
|
||
**Имена устройств НЕ лежат в реестрах HA** — их надо искать в трёх местах:
|
||
1. **z2m** `/config/zigbee2mqtt/configuration.yaml` → секция `devices:` → `friendly_name`.
|
||
2. **HA `core.device_registry`** → `data.devices[].name` (и `name_by_user`), связь через `identifiers: [["mqtt","zigbee2mqtt_0x..."]]`.
|
||
3. **Дашборд `lovelace.home_plan`** → `entity` в `picture-elements` (самый надёжный источник рабочей схемы имён).
|
||
|
||
Поля сущностей: `original_name` = **имя параметра** («Температура», «Влага») — НЕ имя устройства; `name`/`name_by_user` часто `null`. **Вывод: `original_name` для определения устройства бесполезен** — все 16 датчиков t° будут «Температура».
|
||
|
||
**Пифолл поиска:** если автоматизация ссылается на устройство, искать надо по `device_id`, а не `entity_id` — в `automations.yaml` триггеры вида `type: battery_level / device_id: ...` не содержат имени устройства. Связь: `device_id` → `core.device_registry` → `identifiers` → IEEE.
|
||
|
||
**Инвентарь `configuration.yaml` TrueNAS (30КБ):** секции — `default_config`, `http`, `logger`, `modbus` (17: шина вентиляции — заслонки intake/exhaust, AT2), `input_number`, `input_boolean`, `template` (summary-сенсоры `dining_summary`, `kids_summary`, `bedroom_summary`, `at2_1_summary`), `frontend` (темы), `automation/script/scene: !include`. **Секции `mqtt:` в конфиге НЕТ.** Датчики `sensor.dining_*`/`kids_*`/`bedroom_*` (`platform: mqtt`) — это **Modbus RTU Sniffer** (шина вентиляции), НЕ Zigbee, и уже названы правильно.
|
||
|
||
**Автоматизации TrueNAS (`automations.yaml`, 15 шт.):** Выключить/Включить циркуляцию ГВС, Ventilation automation on, office_pass_switch_table/main, Светло/Темно подсветку лестницы, Toggle/Cycle Dimmer bed, Вкл./Выкл. ночной свет душевая, Протечка котельная, Датчик протечки котельная батарея, Датчик освещённости лестница батарея, Light switch bed батарея, Zigbee T sensor батарея.
|
||
|
||
**Дашборд `home_plan` — правки ссылок (статус 2026-09-14):**
|
||
- ✅ `switch.vvod_vody_greiushchii_kabel` → **`switch.heating_cable_plug`** — СДЕЛАНО (в staged-файле, скрипт `fix_dashboard.py`)
|
||
- ✅ `binary_sensor.0xa4c1383d5fcaa063_water_leak` → **`binary_sensor.boiler_water_leak_water_leak`** — СДЕЛАНО (автоматически переименованием реестра)
|
||
- ✅ `switch.0xa4c138f8da8bc478` → **`switch.recirculation_pump`** — СДЕЛАНО (то же)
|
||
- ✅ `light.0xa4c13882a4b42db0` → **`light.bed_dimmer`** — **СДЕЛАНО** (`fix_dash2.sh`, jq-walk по `.data.config`; `sed` не использовали)
|
||
- ✅ `light.smart_light_stairs_l1` — в дашборде ОК (человеческое)
|
||
|
||
**Итог по дашборду:** hex-ссылок в `lovelace.home_plan` **не осталось** (проверено `grep '"entity": *"[^"]*0x'` → пусто).
|
||
|
||
#### ✅ `switch_as_x` восстановлены (2026-09-14) — виртуальные `light.*`
|
||
|
||
**Симптом:** `light.night_light_shower_2`, `light.smart_light_office_left/right`, `light.smart_light_stairs_l1` были `unavailable`.
|
||
|
||
**Причина:** эти сущности — `platform: switch_as_x` (виртуальный «выключатель как свет»), созданные вручную на TrueNAS. Их `config_entry_id` ссылался на записи в `core.config_entries`, которых мы **не переносили** → сущности-сироты.
|
||
|
||
**Фикс:** на TrueNAS найдено 4 entry `switch_as_x`, добавлены в t610 (с исправлением hex-ссылки):
|
||
|
||
| title | `options.entity_id` |
|
||
|---|---|
|
||
| `Office table light` | `switch.smart_light_office_right` |
|
||
| `Office main light` | `switch.smart_light_office_left` |
|
||
| `0x84fd27fffed9e137` | → исправлено на `switch.night_light_shower_2` |
|
||
| `L1` | `switch.light_stairs_l1` |
|
||
|
||
Порядок: `ha core stop` → добавить entry в `core.config_entries` → `ha core start`. Результат: все 5 `light.*` работают (`off` вместо `unavailable`).
|
||
Скрипты: `~/tmp-t610/{add_switchasx.py,apply_switchasx.sh}`, данные `t610-config-entries-new.json`.
|
||
|
||
⚠️ **Питфолл:** при переносе реестров **`core.config_entries` тоже нужен**, если есть виртуальные сущности (`switch_as_x`, `template`, helper'ы) — иначе их entry теряются и сущности висят `unavailable`. Мы не переносили его ради сохранения MQTT-интеграции → добавляли `switch_as_x` точечно.
|
||
|
||
#### 🔴 ПИТФОЛЛ: `device_id` ломаются при переносе реестра — ✅ ЗАКРЫТО 2026-09-14
|
||
|
||
**Симптом:** после переноса реестров и старта HA **13 из 16 автоматизаций стали `unavailable`**. В `ha core logs`:
|
||
```
|
||
ERROR (MainThread) [homeassistant.components.automation] Automation with alias 'Протечка котельная'
|
||
failed to setup triggers and has been disabled: Unknown device 'c42ce32c73731941884bcbf0c2b2d077'
|
||
```
|
||
|
||
**Причина:** `device_id` — это **UUID, генерируемый HA при регистрации устройства в КОНКРЕТНОМ инстансе**. Хотя `core.device_registry` перенесён с TrueNAS, MQTT-интеграция t610 **зарегистрировала устройства заново** (запись MQTT-интеграции у нас своя, t610-шная) → выдала **новые** `device_id`. Автоматизации остались со старыми TrueNAS-овскими.
|
||
|
||
⚠️ **Главный вывод: `device_id` НЕ переносятся между инстансами HA.** `entity_id` и `unique_id` — переносятся; `device_id` — НЕТ. Автоматизации/скрипты, ссылающиеся на `device_id` (а это все device-trigger'ы в UI), ломаются.
|
||
|
||
##### ✅ Полный маппинг — 9/9, собран 2026-09-14 (метод: по `identifiers`)
|
||
|
||
**Метод (надёжный, воспроизводимый):** взять TrueNAS-реестр `core.device_registry` (оттуда, где `device_id` из автоматизаций ЕСТЬ) → для каждого устройства вытащить `identifiers` (для zigbee = `[[\"mqtt\",\"zigbee2mqtt_<ieee>\"]]`) → найти на t610 устройство с **тем же `identifiers`** → его `id` и есть актуальный `device_id`.
|
||
|
||
| # | Устройство | `device_id` TrueNAS (в автоматизациях) | `device_id` t610 (актуальный) | Замен |
|
||
|---|---|---|---|---|
|
||
| 1 | `night_light_shower_2` (`84fd27fffed9e137`) | `0c7a0eb6d60e852b447266da69a7785e` | `4d6e55505ff7dbad13d2674cdcb18d5a` | 2 |
|
||
| 2 | `recirculation_pump` (`f8da8bc478`) | `234686e6c83f7d19c9e10b2f0d1fcc59` | `16d2c6f64ec399e5261c89b35c1e75c6` | 2 |
|
||
| 3 | `bed_dimmer` (`82a4b42db0`) | `2862be34f5eaaa39b9d51084a1f921ee` | `098a641cb1d30f08f1ae293e00d9885b` | 1 |
|
||
| 4 | `office_temperature_sensor` (`62d39377e6`) | `52266a1b4a9d301f0da0dfa6f97c4ea2` | `bcf47eeae909877978bdaf6c705210f8` | 1 |
|
||
| 5 | `wireless_light_switch_bed` (`b0f9e674a5`) | `757e0e9b1e5771e0c700a9df852b0549` | `5cd5d9d2d289b5e470bbeaac0eb7905c` | 3 |
|
||
| 6 | `light_sensor_stairs` (`6d40ddb67b`) | `7f102ad2e78960fff6ebc5f01c0bba93` | `1ea8bbc2612dde303e4279bc5fbad57a` | 3 |
|
||
| 7 | `shower_2_presence_sensor` (`c4a94a6a31`) | `7f74e7077d7fa23e405f2c5e25f6fa17` | `4095e7c3b47b9dc9640cfb8c3aeff022` | 4 |
|
||
| 8 | `light_stairs` (`6d0839706a`) | `9d3f31b1b49c95433428afbb113d929b` | `028b7d9f489c87bdc9e563473a1d61e8` | 2 |
|
||
| 9 | `boiler_water_leak` (`3d5fcaa063`) | `c42ce32c73731941884bcbf0c2b2d077` | `b9d384a51b780a7924ed9504eddec12e` | 2 |
|
||
|
||
**Итого: 9 `device_id` из автоматизаций (все 9 — zigbee), 20 вхождений.** ⚠️ Прежняя оценка «6 UUID» была неполной — реальных уникальных `device_id` **9**. В `scripts.yaml` device-ссылок **0** (проверено).
|
||
|
||
**Как получить актуальные `device_id` (bash+jq на t610):**
|
||
```bash
|
||
jq -r '.data.devices[] | select((.identifiers|tostring)|contains("<ieee_без_0x>")) | [.id, (.name//"—")] | @tsv' \
|
||
/config/.storage/core.device_registry
|
||
```
|
||
Связь: `identifiers` = `[["mqtt","zigbee2mqtt_<ieee>"]]` → `id` = искомый `device_id`.
|
||
|
||
⚠️ **Получение TrueNAS-реестра:** `/mnt/RED_2TB/docker/ha/.storage/core.device_registry` **читается без sudo** (права 644, owner root) — `scp` работает напрямую. `sudo cat` падает (`a terminal is required to read the password`) — sudo не нужен.
|
||
|
||
##### 🔴 ВТОРАЯ ПОЛОМКА ТОГО ЖЕ КЛАССА: hex `entity_id` в `automations.yaml` (2 шт.)
|
||
|
||
⚠️ **Тот же класс проблемы, что и `device_id`, её легко пропустить.** При переименовании реестра (`hex → человеческие entity_id`) **ссылки в `automations.yaml` не обновлялись** → в автоматизациях остались старые hex-`entity_id`:
|
||
- `light.0xa4c13882a4b42db0` → должно быть **`light.bed_dimmer`** (в реестре уже человеческое).
|
||
- `switch.0xa4c13873b5c1575b` → ⚠️ **неоднозначно**: у устройства теперь два канала `switch.office_table_light_switch_l1` / `_l2` (z2m разбил на каналы), а в автоматизации одна старая сущность. Требует разбора, какой канал использовался на TrueNAS (см. §«Осталось»).
|
||
|
||
**Мораль:** при переносе HA надо чистить **и `device_id`, и hex-`entity_id`** одновременно — иначе автоматизация «чинится» наполовину.
|
||
|
||
##### ✅ План починки — ВЫПОЛНЕН 2026-09-14 (автоматизации 0 `unavailable`)
|
||
|
||
**Файл:** `~/tmp-t610/etap3-fix/automations.fixed.yaml` — 20 замен `device_id`, 0 старых осталось, все 20 новых проверены по реестру t610. Бэкап на t610: `/config/automations.yaml.bak-devid-20260914-124733`.
|
||
|
||
**Уточнение про `switch.0xa4c13873b5c1575b` (закрыт вопрос l1/l2):** это был **артефакт regex**, а не реальная ссылка. В `automations.yaml` уже стояли правильные `switch.0xa4c13873b5c1575b_l1` (авт. `office_pass_switch_table`) и `switch.0xa4c13873b5c1575b_l2` (авт. `office_pass_switch_main`). Проверка `grep -E 'switch\.0xa4c13873b5c1575b(?!_l)'` → **0 совпадений**. Чинить нечего.
|
||
|
||
**Реально исправленный hex-`entity_id` — только один:** `light.0xa4c13882a4b42db0` → `light.bed_dimmer` (2 вхождения, автоматизация «Dimmer bed cycle»: и в `state_attr()`, и в `target.entity_id`).
|
||
|
||
**Порядок (выполнен):** `ha core stop`-эквивалент через `ha core restart` → залив `automations.yaml` → проверка API.
|
||
|
||
**Результат (факт, 2026-09-14):** `automation.*` — **16 сущностей: 15 `on` + 1 `off` (0 `unavailable`)**. Единственная `off` — `automation.ventilation_automation_on`: это **нормальное состояние** (вентиляция управляется через modbus-шину, а не по расписанию), идентично TrueNAS.
|
||
|
||
**Важный побочный вывод:** 12 `entity_id` внутри device-действий формата `entity_id: <32-hex>` — это **реестровые `id` сущностей**, а НЕ строки `domain.name`. Они **совпали** и менять их не пришлось, потому что при переименовании реестра через jq мы меняли только поле `entity_id`, а `id` оставался нетронутым (перенесён с TrueNAS). Проверено: 12/12 найдены в `core.entity_registry`.
|
||
|
||
##### 🔴 ТРЕТЬЯ ПОЛОМКА ТОГО ЖЕ КЛАССА: hex-`entity_id` зашиты в код local add-on (modbus-bridge)
|
||
|
||
**Симптом:** modbus-bridge бесконечно логирует `HA poll: sensor.0xa4c13862d39377e6_temperature HTTP 404`.
|
||
|
||
**Причина:** hex-`entity_id` (`sensor.0xa4c13862d39377e6_temperature`, `switch.0xa4c138f8da8bc478`) зашиты **в код аддона**, а не в опции:
|
||
- `/addons/modbus-bridge/modbus_ha_bridge.py` (строки 74, 82, 89 — встроенный дефолт `DEFAULT_CONFIG`)
|
||
- `/addons/modbus-bridge/data/config.template.tmpl` (строки 112, 125)
|
||
|
||
Правильные значения: `sensor.office_temperature_sensor_temperature`, `switch.recirculation_pump`.
|
||
|
||
**⚠️ ГЛАВНЫЙ ПИТФОЛЛ: правки в `data/*.tmpl` НЕ применяются без rebuild.** `run.sh` генерирует runtime `/app/config.yml` из **`/app/config.template.yml`** — копии внутри **образа** (`Dockerfile`: `COPY data/config.template.tmpl /app/config.template.yml`). Поэтому после правки исходников обязателен **`ha addons rebuild local_modbus-bridge`** (без него работает старая версия из образа — я на этом попался).
|
||
|
||
**Проверка после rebuild:** в логе `HA poll -> sensor.office_temperature_sensor_temperature = 23.96` (200, без 404).
|
||
|
||
#### 🔧 ПИТФОЛЛ: `http:` в `configuration.yaml` игнорируется после миграции
|
||
|
||
**Симптом (варнинг в HA):** `YAML configuration is ignored after migration` / `The HTTP configuration in configuration.yaml has already been migrated and is now being ignored... This stops working in version 2027.2.0.` → надо удалить блок `http:` и управлять через **Settings → System → Network**.
|
||
|
||
**Что было:** в `configuration.yaml` блок
|
||
```yaml
|
||
http:
|
||
use_x_forwarded_for: true
|
||
trusted_proxies:
|
||
- 172.16.0.0/12
|
||
```
|
||
Но в `.storage/http` этих ключей **НЕ было** → простое удаление блока **потеряло бы доверие к Caddy-прокси** (HA отдаёт 400 на запросы через прокси — критично для Этапа 4).
|
||
|
||
**Правильный фикс (выполнен):**
|
||
1. Бэкап `configuration.yaml` → `.bak-http-<ts>`; бэкап `.storage/http` → `.bak-<ts>`.
|
||
2. Добавить в `.storage/http` → `data.stable` (jq, локально): `use_x_forwarded_for: true`, `trusted_proxies: ["172.16.0.0/12"]`. Файл положить **при остановленном HA** (`ha core stop`), иначе HA перезапишет.
|
||
3. Удалить блок `http:` из `configuration.yaml`, оставить комментарий-пояснение.
|
||
4. `ha core start` → HA сам выставляет `data.yaml_migration_done: true`.
|
||
|
||
**Проверка:** `jq '.data.stable | {server_port, use_x_forwarded_for, trusted_proxies}' /config/.storage/http` → порт 80, настройки на месте; `curl -o /dev/null -w '%{http_code}' http://192.168.2.176/` → 200.
|
||
|
||
> 📌 **Общий принцип (для любых настроек, мигрированных в UI):** прежде чем удалять YAML-блок, **сверить**, что все его ключи реально есть в `.storage/<domain>`. Иначе настройка молча теряется.
|
||
|
||
#### 🔴 ПИТФОЛЛ (КРИТИЧНЫЙ): `modbus.host` не может быть `127.0.0.1` — mbusd в отдельном контейнере
|
||
|
||
**Симптом:** 32 `switch.*_damper` (заслонки вентиляции) `unavailable`, **0 живых modbus-сенсоров**, хотя `local_mbusd` = `started` и порт 502 на хосте открыт.
|
||
|
||
**Причина:** в `configuration.yaml` было `modbus.host: 127.0.0.1`. HA Core сидит **в своём контейнере** (`172.30.32.1`), а `local_mbusd` — в **другом контейнере**, пробросивший порт на **хост**. Для HA `127.0.0.1` = он сам → таймаут.
|
||
|
||
**Фикс:** `host: 192.168.2.176` (IP хоста t610, порт 502 проброшен Docker'ом). Проверка: `nc -z 192.168.2.176 502` → OK.
|
||
|
||
> ⚠️ **Не путать:** в логе mbusd `conn_open(): accepting connection from 172.30.32.1` + мгновенный `conn_close()` = признак неверного адреса. Успешное подключение выглядит как **`conn_open(): accepting connection from 192.168.2.176`** и **БЕЗ последующего `conn_close`** (HA держит коннект).
|
||
|
||
#### 📡 ПИТФОЛЛ: диагностика modbus-шины через прямой TCP-запрос
|
||
|
||
Проверка «жива ли шина», не залезая в HA (скрипт `~/tmp-t610/etap3-fix/mbtest.sh`):
|
||
```bash
|
||
# Modbus TCP: FC=03, UnitID=0b(11), Start=0000, Qty=0001
|
||
printf '\x00\x01\x00\x00\x00\x06\x0b\x03\x00\x00\x00\x01' > /tmp/mbreq.bin
|
||
nc -w 5 192.168.2.176 502 < /tmp/mbreq.bin | xxd
|
||
```
|
||
**Расшифровка ответа:**
|
||
| Ответ | Значение |
|
||
|---|---|
|
||
| `... 01 03 02 XXXX` | ✅ нормальный ответ (FC=03, 2 байта данных) |
|
||
| `... 01 83 04` | ❌ **SLAVE DEVICE FAILURE** — устройство есть, но не ответило |
|
||
| `... 0b 83 0b` | ❌ **GATEWAY TARGET DEVICE FAILED TO RESPOND** — mbusd передал, шина молчит (нет устройства/нет провода) |
|
||
> Exception-код = байт FC с флагом `0x80`; второй байт — код ошибки. Для заслонок вентиляции **slave = 11** (`configuration.yaml`, секция `modbus`).
|
||
|
||
##### ⏸️ ИТОГ: заслонки вентиляции — АППАРАТНЫЙ блокер (за Alex)
|
||
|
||
mbusd-стек **исправен полностью**: TCP отвечает, serial открыт (`/dev/serial/by-path/pci-0000:00:12.0-usb-0:4:1.0-port0`), HA-коннект с `192.168.2.176` держится. Но на запрос к slave 11 шина отдаёт **exception 0x0B** — устройство не отвечает.
|
||
|
||
**Диагноз: CH340 #2 воткнут в USB-порт t610, но линии A/B вентиляционной шины к нему не подключены (или шина обесточена).**
|
||
|
||
Это ровно тот блокер, о котором Alex предупреждал в начале сессии («проверь USB основной шины, дальше подключу ZONT»). **Никакие конфиги тут не помогут — нужна физика.** Заслонки оживут сами, как только шина будет подключена. **Адреса записаны:**
|
||
```
|
||
/dev/serial/by-path/pci-0000:00:12.0-usb-0:4:1.0-port0 (ttyUSB1, CH340 #2) = ВЕНТИЛЯЦИЯ (slave 11)
|
||
/dev/serial/by-path/pci-0000:00:12.0-usb-0:3:1.0-port0 (ttyUSB0, CH340 #1) = ZONT
|
||
```
|
||
|
||
⚠️ **Мораль для будущих переносов HA:** либо переносить `core.config_entries` целиком вместе с реестрами (тогда `device_id` согласуются), либо **после переноса обязательно перемапить `device_id`** в `automations.yaml`/`scripts.yaml` по `identifiers`, **и заодно проверить hex-ссылки `entity_id`**. Мы выбрали не переносить `core.config_entries` (чтобы сохранить MQTT-интеграцию t610) — отсюда и разрыв.
|
||
|
||
#### ⚠️ `sensor.*_summary` — TemplateError (косметика, самоизлечится)
|
||
|
||
В логе после старта:
|
||
```
|
||
TemplateError: ValueError: Template error: round got invalid input 'unknown'
|
||
when rendering template '{{ states('sensor.kids_temperature')|round(0)|int }}° ...'
|
||
```
|
||
Причина: sniffer-датчики (`kids_*`, `bedroom_*`, `dining_*` — Modbus RTU) ещё не прислали данные → `states()` = `'unknown'`. Уйдёт, когда пойдут значения. Если раздражает — добавить `|default(0)` в `configuration.yaml` (§template, сенсоры `dining_summary`, `kids_summary`, `bedroom_summary`).
|
||
|
||
#### ✅ ФИНАЛ: имена приведены к единому виду (2026-09-14)
|
||
|
||
**Результат:**
|
||
| Показатель | Значение |
|
||
|---|---|
|
||
| Hex-`entity_id` в реестре HA | **0** (было 126) |
|
||
| Сущностей в реестре | 319 |
|
||
| Устройств в z2m | **14 живых** (было 17 — удалены 3 мёртвых) |
|
||
| `friendly_name` в z2m | все человеческие, латиница snake_case |
|
||
|
||
**Итоговая таблица (14 устройств, СОГЛАСОВАНО Alex):**
|
||
|
||
| IEEE | `friendly_name` | Роль | Зона |
|
||
|---|---|---|---|
|
||
| `0xa4c13862d39377e6` | `office_temperature_sensor` | датчик t°/влажности (в кабинете, временно) | Кабинет |
|
||
| `0xa4c138f8da8bc478` | `recirculation_pump` | розетка насоса обратки (P/V/I/E) | Котельная |
|
||
| `0x84fd27fffed9e137` | `night_light_shower_2` | ночной свет | Душевая |
|
||
| `0xa4c1386d40ddb67b` | `light_sensor_stairs` | датчик освещённости | Лестница |
|
||
| `0xa4c1381186ed1a32` | `smart_light_office` | выключатель 2-кл | Кабинет |
|
||
| `0xa4c13873b5c1575b` | `office_table_light_switch` | реле 2 канала L1/L2 | Кабинет |
|
||
| `0xa4c13807b64c7fd4` | `kitchen_hood` | реле 3 канала (вытяжка) | Кухня |
|
||
| `0xa4c1386d0839706a` | `light_stairs` | реле (лестница) | Лестница |
|
||
| `0xa4c1384fbe0b3a6b` | `sauna` | розетка без мониторинга | Туалет |
|
||
| `0xa4c138b0f9e674a5` | `wireless_light_switch_bed` | беспроводной выключатель | Спальня |
|
||
| `0xa4c13882a4b42db0` | `bed_dimmer` | диммер 1 канал | Спальня |
|
||
| `0xa4c138c4a94a6a31` | `shower_2_presence_sensor` | радар присутствия | Душевая |
|
||
| `0xa4c1383d5fcaa063` | `boiler_water_leak` | датчик протечки | Котельная |
|
||
| `0xa4c138eb6fbe9d19` | `heating_cable_plug` | NEO NAS-WR01B, розетка греющего кабеля | — |
|
||
|
||
**Удалены как мёртвые (3 шт., `lastSeen` 5–8 месяцев назад, не используются ни в одной автоматизации/дашборде):**
|
||
`0xcc86ecfffe1347fd` (Mini Smart Switch 1), `0xa4c138dc6d856eca` (Presense Sensor 1, радар), `0xa4c138cefeee19fd` (Zigbee dimmer 2ch). Все три были `unavailable`/`unknown`, `state.json` пуст.
|
||
|
||
#### 🔑 КЛЮЧЕВОЙ МЕХАНИЗМ: почему HA не переименовал сущности сам
|
||
|
||
**HA связывает сущность по `unique_id`, а `entity_id` — вторичен.** z2m при смене `friendly_name` **НЕ меняет `unique_id`** (он вида `<ieee>_<param>_zigbee2mqtt`). Значит:
|
||
- Смена `friendly_name` меняет MQTT-топики (`state_topic`) и `default_entity_id` в discovery.
|
||
- Но HA видит **тот же `unique_id`** → считает сущность существующей и **НЕ переименовывает её `entity_id`**.
|
||
- Итог: после смены имён в z2m (шаг 1) надо **вручную переименовать `entity_id` в реестре HA** (шаг 2).
|
||
|
||
**Порядок (выполнен):**
|
||
1. ✅ Заменить `devices:` в `/config/zigbee2mqtt/configuration.yaml` (friendly_name = человеческие) → `ha apps restart 45df7312_zigbee2mqtt`. Проверка: z2m публикует в `zigbee2mqtt/<human_name>`, `bridge/devices` показывает человеческие имена.
|
||
2. ✅ `ha core stop` → переименовать `entity_id` в `core.entity_registry` (regex `<domain>.<hex>[_suffix]` → `<domain>.<friendly>[_suffix]`) → `ha core start`.
|
||
|
||
**Команда переименования (jq, т.к. python3 в аддоне НЕТ):**
|
||
```bash
|
||
REG=/config/.storage/core.entity_registry
|
||
cp "$REG" "${REG}.bak-rename-$(date +%Y%m%d-%H%M%S)"
|
||
MAP='{"0xa4c13862d39377e6":"office_temperature_sensor", ...}'
|
||
jq --argjson map "$MAP" '
|
||
.data.entities |= map(
|
||
if (.entity_id | test("^[a-z_]+\\.0x[0-9a-f]+")) then
|
||
(.entity_id | capture("^(?<d>[a-z_]+)\\.(?<h>0x[0-9a-f]+)(?<s>.*)$")) as $m
|
||
| if $map[$m.h] != null then .entity_id = ($m.d + "." + $map[$m.h] + $m.s) else . end
|
||
else . end
|
||
)' "$REG" > /tmp/reg.new.json
|
||
# проверить: jq -e '(.data.entities|length) > 300' /tmp/reg.new.json
|
||
cp /tmp/reg.new.json "$REG"; chown root:root "$REG"; chmod 644 "$REG"
|
||
```
|
||
Скрипты: `~/tmp-t610/{gen_names.py,do_rename_jq.sh,apply_names.sh,build_z2m_config.py}`, `z2m-new-config.yaml`.
|
||
|
||
⚠️ **Питфолл jq:** конструкция `(.entity_id) as $eid | capture(...)` падает с `object cannot be matched, not a string` — нужно `if (.entity_id | test(...)) then (.entity_id | capture(...)) as $m | ...`.
|
||
|
||
⚠️ **Питфолл bash:** круглые скобки в `echo "..."` ломают скрипт (`syntax error near unexpected token '('`) — избегать `()` в строках вывода.
|
||
|
||
#### ℹ️ Наблюдение: `unknown` у части сущностей — НЕ баг
|
||
|
||
После переименования часть сущностей остаётся `unknown`, при этом `sensor.X_voltage` работает, а `switch.X` — нет. Это **нормально**: z2m публикует payload **только при получении данных от устройства**. Router'ы (питаемые от сети) отчитываются постоянно, EndDevice (батарейные) — редко/по событию. `switch.sauna` вечно `unknown`, потому что розетка **физически отключена** (`lastSeen` 8+ часов). Проверка живости: `jq` по `database.db` → `lastSeen` в мс.
|
||
|
||
#### ✅ Zigbee `friendly_name` — корень generic-имён (разведка 2026-09-14)
|
||
|
||
> 📌 Дашборд ссылается на сущности по человеческим именам, которые уже есть в реестре (`switch.sauna`, `light.smart_light_office_left`, `light.smart_light_stairs_l1`, `binary_sensor.shower_2_presence_sensor_presence`). После шага 8 всё оживёт.
|
||
|
||
#### 🔌 Новое устройство: `boiler_controller_power` — питание контроллеров котлов (2026-09-14)
|
||
|
||
**Задача Alex:** добавить Zigbee-розетку в котельную как **питание для контроллеров котлов**.
|
||
|
||
| Параметр | Значение |
|
||
|---|---|
|
||
| `friendly_name` | **`boiler_controller_power`** |
|
||
| IEEE | `0xa4c1381694217e10` |
|
||
| Модель | **TS011F**, `manufName = _TZ3000_gjnozsaz` (Tuya Smart Plug с измерениями P/V/I/E) |
|
||
| Тип | **Router, Mains (single phase)** — усиливает mesh в котельной |
|
||
| Зона | **Котельная** (`kotelnaia`) |
|
||
| device_id | `dab3c111a46c9c441e2c90fdfe35a702` |
|
||
| Сущности | **13**, все с человеческими `entity_id` |
|
||
| Состояние | `switch.boiler_controller_power = off`, `voltage = 218` |
|
||
|
||
**Процедура (выполнена, рабочий рецепт):**
|
||
1. **permit_join** через MQTT: `mosquitto_pub -t zigbee2mqtt/bridge/request/permit_join -m '{"value":true,"time":250}'`.
|
||
⚠️ **Питфолл: лимит окна — 254 секунды.** `"time":300` → `error: Cannot permit join for more than 254 seconds`. Ставить **≤250**.
|
||
2. Устройство присоединилось само → появилось в `bridge/devices` под **hex-именем** `0xa4c1381694217e10`.
|
||
3. **Переименование в z2m** (до того как HA создаст сущности!):
|
||
`mosquitto_pub -t zigbee2mqtt/bridge/request/device/rename -m '{"from":"0xa4c1381694217e10","to":"boiler_controller_power"}'`
|
||
4. ⚠️ **HA всё равно создал сущности по hex-имени** (успел между join и rename) → понадобился **второй фикс: переименование `entity_id` в `core.entity_registry`** (HA stop → jq → HA start).
|
||
5. Зона ставится **в `core.device_registry`** (`area_id`), НЕ в entity_registry. Устройство найдено по `identifiers` = `[["mqtt","zigbee2mqtt_0xa4c1381694217e10"]]` → `area_id = "kotelnaia"`.
|
||
|
||
**Явный маппинг (13 hex → человеческие):**
|
||
```bash
|
||
# jq-фильтр: replace по каждому суффиксу
|
||
switch.0xa4c1381694217e10 -> switch.boiler_controller_power
|
||
switch.…_child_lock -> switch.boiler_controller_power_child_lock
|
||
number.…_countdown -> number.boiler_controller_power_countdown
|
||
select.…_power_outage_memory -> select.boiler_controller_power_power_outage_memory
|
||
select.…_switch_type_button -> select.boiler_controller_power_switch_type_button
|
||
select.…_indicator_mode -> select.boiler_controller_power_indicator_mode
|
||
sensor.…_power / _current / _voltage / _energy / _linkquality
|
||
button.…_identify -> button.boiler_controller_power_identify
|
||
update.0xa4c1381694217e10 -> update.boiler_controller_power
|
||
```
|
||
Скрипты: `~/tmp-t610/etap3-fix/{permit_join.sh,z2m_devices.sh,rename_plug.sh,rename_plug_ids.jq,set_area.jq,check_plug.sh}`.
|
||
|
||
**⚠️ ВЫВОД (обобщение питфолла):** `bridge/request/device/rename` в z2m **не спасает** — HA успевает создать сущности под hex-именем. Порядок «сначала rename в z2m, потом join» невозможен → **после каждого нового устройства всегда проверять `core.entity_registry` на hex и переименовывать через jq**.
|
||
|
||
#### 📍 СОСТОЯНИЕ НА КОНЕЦ СЕССИИ 2026-09-14 (поздняя) — для следующей сессии
|
||
|
||
**Факты (проверено API):** HA `http://192.168.2.176` → 200; z2m `started`, **15 устройств**; реестр **332 сущности**, **hex = 0**; `light.*` (5 шт.) работают; MQTT-интеграция на месте; `switch_as_x` (4) восстановлены; **автоматизации 16 шт.: 15 `on` + 1 `off` (0 `unavailable`)**; HTTP-варнинг устранён; modbus.host = `192.168.2.176`; modbus-bridge опрос работает (без 404).
|
||
|
||
**Счётчики живого API:** 253 сущности; `unavailable` 44 (из них **32 — заслонки вентиляции**, аппаратный блокер); `unknown` 69.
|
||
|
||
**Живые zigbee-датчики (проверено):** `sensor.office_temperature_sensor_temperature = 23.96`, `sensor.recirculation_pump_voltage = 221`, `sensor.light_sensor_stairs_illuminance = 556`, `sensor.heating_cable_plug_voltage = 222`, `sensor.boiler_controller_power_voltage = 218`.
|
||
|
||
**✅ Добавлено в конце сессии:** Zigbee-розетка **`boiler_controller_power`** (`0xa4c1381694217e10`, TS011F, зона Котельная) — питание контроллеров котлов. Итого **15 устройств в z2m**, **332 сущности в реестре**. См. §«Новое устройство: boiler_controller_power».
|
||
|
||
**✅ Этап 3 ЗАКРЫТ.** Всё, что осталось — вне конфигов:
|
||
|
||
**⏸️ 1. ГЛАВНОЕ: заслонки вентиляции (32 `switch.*_damper` `unavailable`) — АППАРАТНЫЙ блокер, за Alex.** Подключить линии A/B вентиляционной шины к CH340 #2 (`by-path ...0:4:1.0-port0`, slave 11) или проверить питание шины. После подключения заслонки оживут сами — конфиг уже верный. См. §«ИТОГ: заслонки вентиляции».
|
||
|
||
**Далее (не блокеры):**
|
||
1. `sensor.*_summary` — добавить `|default(0)` в template-сенсоры `configuration.yaml` (косметика; ошибки `round got invalid input 'unknown'` для `kids_*`/`bedroom_*` — уйдут сами, когда sniffer вентиляции пришлёт данные).
|
||
2. `switch.sauna` = `unknown` — розетка физически отключена (`lastSeen` 8+ ч), не баг.
|
||
3. Камера (§8 родительского плана).
|
||
4. Этап 4: Caddy upstream → t610, GPON-редирект → t610, отключить TrueNAS.
|
||
|
||
**Ключевые пути/скрипты сессии:** `~/tmp-t610/` — `stage3/out/`, `backups/`, `ha_token.txt`. **Этап 3 правки:** `~/tmp-t610/etap3-fix/` — `automations.fixed.yaml`, `devid_mapping.json`, `truenas.device_registry`, `core.device_registry`, `core.entity_registry`, `scripts.yaml`, `http.new.json`, `mb_bridge.py`, `config.template.tmpl`, `mbtest.sh`, `check_dampers2.sh`, `final_check.sh`, `restart_ha.sh`, `run_check.sh`, `token.env`, `q_dampers.jq`, `q_sensors.jq`. На t610: `/config/automations.yaml.bak-devid-*`, `/config/configuration.yaml.bak-http-*`, `/config/.storage/http.bak-*`, `/addons/modbus-bridge/*.bak-hex-*`.
|
||
|
||
**⚠️ Питфолл сессии (важный для будущих сессий):** Alex **устал апрувать** запуск python-скриптов (`execute_code` и `/usr/bin/python3 -c`) — ТРЕБУЕТ bash+jq/curl. Пользоваться shell-скриптами, python только когда без него никак (и предупреждать).
|
||
|
||
#### 🧩 ДОБАВЛЕНО В КОНЦЕ СЕССИИ 2026-09-14 — ЗОНЫ и office-переключатель
|
||
|
||
**Три отдельных дефекта, найденных Alex'ом после «всё готово». Все устранены.**
|
||
|
||
##### ① Зоны (`area_id`) потерялись при ре-регистрации устройств — ГЛАВНЫЙ питфолл
|
||
|
||
**Симптом (Alex):** «на всех устройствах зоны верно поставлены? Чето в ui кажется что не все девайсы имеют зону».
|
||
**Факт:** **14 из 14 zigbee-устройств были БЕЗ зоны** (`area_id = null`). Зона была только у только что добавленной `boiler_controller_power` (её я прописал руками).
|
||
|
||
**Причина — ТРЕТИЙ слой той же болезни, что `device_id`:** при переносе `.storage` я скопировал `core.area_registry` (**11 зон созданы**), но `area_id` живёт **в `core.device_registry`**. Zigbee-устройства при подключении к локальной MQTT-интеграции **зарегистрировались заново** → новые записи устройств пришли **без `area_id`**. Зоны (как сущности реестра зон) остались, а привязка устройство→зона — нет.
|
||
|
||
> 🔑 **Обобщение (важно для будущих миграций):** при переносе HA между инстансами **теряются все инстанс-локальные привязки**, а не только `device_id`:
|
||
> - `device_id` — новый у каждого устройства;
|
||
> - `area_id` (зона устройства) — теряется, если устройство ре-регистрируется;
|
||
> - `entity_id` (в `entity_registry`) — сохраняется по `unique_id`, но **ссылки на него в автоматизациях/дашбордах — нет**.
|
||
> Переносится **только** то, что задано явными `id` в реестрах (`area_registry`, `floor_registry`).
|
||
|
||
**Лечение (выполнено):** эталон зон взят с TrueNAS (`/mnt/RED_2TB/docker/ha/.storage/core.device_registry`) — там 17 устройств с зонами. Маппинг построен **по zigbee-адресу** (`identifiers[0][1]` = `zigbee2mqtt_<ieee>`), проставлен через jq при остановленном HA.
|
||
|
||
**Итоговые зоны (18 устройств):**
|
||
|
||
| Зона | Устройства |
|
||
|------|-----------|
|
||
| **Котельная** `kotelnaia` | `boiler_controller_power`, `boiler_water_leak`, `heating_cable_plug`, `recirculation_pump` |
|
||
| **Кабинет** `kabinet` | `smart_light_office`, `office_table_light_switch`, `office_temperature_sensor` |
|
||
| **Спальня** `bedroom` | `bed_dimmer`, `wireless_light_switch_bed`, `Bedroom Sensor` (modbus) |
|
||
| **Душевая** `dushevaia` | `night_light_shower_2`, `shower_2_presence_sensor` |
|
||
| **Лестница** `lestnitsa` | `light_stairs`, `light_sensor_stairs` |
|
||
| **Кухня** `kitchen` | `kitchen_hood` |
|
||
| **Туалет** `tualet` | `sauna` |
|
||
| **Гостиная** `living_room` | `Dining Sensor` (modbus) |
|
||
| **Детская** `detskaia` | `Kids Sensor` (modbus) |
|
||
|
||
> ℹ️ `office_temperature_sensor` на TrueNAS зоны не имел → поставлен `kabinet` по расположению. `heating_cable_plug` заменил Wi-Fi-розетку `local_bf8821e863abe84816qmbo` (была `kotelnaia`) → унаследовал зону.
|
||
|
||
**Скрипт:** `~/tmp-t610/etap3-fix/set_all_areas.jq` (jq-фильтр `map` по `identifiers[0][1]`). Применение: `jq -f set_all_areas.jq core.device_registry > …` → HA stop → scp → HA start.
|
||
|
||
##### ② Office table switch не управлял светом — ДВЕ причины
|
||
|
||
**Симптом (Alex):** «office table switch не управляет светом в кабинете».
|
||
|
||
**Причина A — hex-`entity_id` в ТРИГГЕРАХ автоматизаций.** Триггеры `office_pass_switch_table`/`_main` ссылались на **hex**:
|
||
```yaml
|
||
entity_id: switch.0xa4c13873b5c1575b_l1 # ← в реестре уже switch.office_table_light_switch_l1
|
||
```
|
||
При переименовании реестра я поправил `entity_id` в **actions** и **device-триггерах** (`platform: state` с `device_id`), но **НЕ в обычных `platform: state`-триггерах**. HA загрузил автоматизацию, но подписался на несуществующую сущность → кнопка не срабатывала.
|
||
> 🔑 **Правило проверки:** после переименования `entity_id` грепать **весь** `automations.yaml` + `scripts.yaml` на `[a-z_]+\.0x[0-9a-f]{16}` — не только `device_id`, не только actions. Найдено ровно 2 (строки 46, 60).
|
||
|
||
**Причина B — `unknown` у реле из-за пассивной публикации z2m (см. ③).** Триггер имел `not_from: [unavailable, unknown]` → пока состояние `unknown`, он **не сработает по определению**.
|
||
|
||
**Проверка на живом (рабочий рецепт):**
|
||
```bash
|
||
# слушаем топики, Alex физически нажимает кнопки
|
||
ssh root@192.168.2.176 'timeout 60 mosquitto_sub -h core-mosquitto -u zont -P "…" \
|
||
-t "zigbee2mqtt/office_table_light_switch/#" -t "zigbee2mqtt/smart_light_office/#" -v'
|
||
# → state_l1/state_l2 (office_table_light_switch идут как state_l1/l2)
|
||
# → state_left/state_right (smart_light_office)
|
||
|
||
# управление светом через HA (проверка цепочки light → switch_as_x → z2m → реле)
|
||
curl -s -X POST -K curl.auth -H "Content-Type: application/json" \
|
||
http://192.168.2.176/api/services/light/turn_on -d '{"entity_id":"light.smart_light_office_left"}'
|
||
```
|
||
**Результат:** `light.smart_light_office_left` off→on→off, `switch.smart_light_office_left` off→on→off — **цепочка работает целиком**.
|
||
|
||
> ℹ️ Разные устройства публикуют **разные имена полей**: `office_table_light_switch` (TS0002) → `state_l1`/`state_l2`; `smart_light_office` (TS0012) → `state_left`/`state_right`. Discovery z2m генерирует корректный `value_template` под каждое — трогать не нужно.
|
||
|
||
##### ③ z2m НЕ публикует состояние пассивно → реле «залипают» в `unknown`
|
||
|
||
**Ключевое открытие сессии.** После перезапуска HA все `switch.*` (реле) были `unknown`, при этом устройства **живы** (`lastSeen` в секундах).
|
||
**Причина:** z2m публикует payload **только при получении данных от устройства**. Питаемые реле не отчитываются сами по себе — ждут события (нажатия) или запроса.
|
||
**Лечение (практическое):** после перезапуска **физически нажать кнопки** на реле — сеть «прогревается», все `unknown` уходят. Проверено: `switch.office_table_light_switch_l1/l2` стали `on`, `switch.smart_light_office_left/right` — `off`.
|
||
|
||
##### ③‑бис ✅ ПРОВЕРЕНО на перезагрузке (2026-09-14): `unknown` ВОЗВРАЩАЕТСЯ, `retain` не спасает
|
||
|
||
**Проведён эксперимент** (не гипотеза): зафиксировано состояние → `homeassistant.restart` через API → состояние снято снова.
|
||
|
||
| Сущность | До рестарта | **После рестарта** |
|
||
|---|---|---|
|
||
| `switch.office_table_light_switch_l1/l2` | `on` | **`unknown`** |
|
||
| `switch.smart_light_office_left/right` | `off` | **`unknown`** |
|
||
| `switch.heating_cable_plug` | `off` | **`unknown`** |
|
||
| `switch.boiler_controller_power` | `off` | **`unknown`** |
|
||
| `switch.kitchen_hood_l1/l2/l3`, `switch.light_stairs_l1/l2` | `unknown` | `unknown` |
|
||
|
||
**Вывод:** всё, что было `on`/`off`, после перезагрузки слетает в `unknown`. **Поведение воспроизводимо.**
|
||
|
||
**Что НЕ помогло (проверено на живом):** в z2m стоят все три штатных механизма —
|
||
`retain: true` (подтверждено дважды: `bridge/request/options` → `{"data":{"restart_required":false},"status":"ok"}`),
|
||
`cache_state: true`, `cache_state_persistent: true`, `cache_state_send_on_startup: true` —
|
||
но **retained-сообщения на топиках устройств фактически не появляются**, состояния при старте не переопубликовываются.
|
||
Контроль: `zigbee2mqtt/bridge/state` **retained есть** (прилетает мгновенно при свежей подписке) → брокер retained умеет, дело не в нём.
|
||
|
||
> 🔬 **Питфолл диагностики retained:** флаг **`-R` (retained-only) у `mosquitto_sub` в аддоне работает НЕ как ожидается** — с ним вывод пустой даже там, где retained есть. **Надёжный способ:** подписаться и смотреть **с timestamp, что прилетает СРАЗУ при подписке** (retained приходит мгновенно, живой трафик — только по событию):
|
||
> ```bash
|
||
> timeout 4 mosquitto_sub -h 192.168.2.176 -u zont -P "$P" -t 'zigbee2mqtt/bridge/state' -v \
|
||
> | while IFS= read -r l; do echo "$(date +%H:%M:%S.%N | cut -c1-12) | $l"; done
|
||
> ```
|
||
|
||
**Практическое следствие:** триггер `platform: state` с `not_from: [unavailable, unknown]` **не сработает на первое нажатие после рестарта** (`unknown` блокирует) — нужно второе.
|
||
|
||
**Фикс (точечный, не зависит от z2m):** убрать `not_from: [unknown]` из триггеров кнопок в `automations.yaml`.
|
||
✅ **ПРИМЕНЁН И ПРОВЕРЕН 2026-09-14.** `not_from: [unavailable, unknown]` удалён из обоих офисных триггеров (`office_pass_switch_table`, `office_pass_switch_main`). Залит файл + рестарт HA. **Проверка на живом (toggle реле через z2m, эмуляция нажатия):**
|
||
```
|
||
mosquitto_pub … -t 'zigbee2mqtt/office_table_light_switch/set' -m '{"state_l1":"TOGGLE"}'
|
||
→ switch.office_table_light_switch_l1: on→off
|
||
→ light.smart_light_office_left: off→ON ✅ свет включился С ПЕРВОГО нажатия
|
||
→ switch.smart_light_office_left: off→on (физическое реле сработало)
|
||
```
|
||
Оба канала (l1→`light.smart_light_office_left`, l2→`light.smart_light_office_right`) проверены на вкл и выкл. **Бэкап: `/config/automations.yaml.bak-20260914-131541`**, локально `~/tmp-t610/` + `/tmp/autom_pre.yaml`.
|
||
|
||
> 🔑 **Почему так, а не иначе (объяснение Alex, которое он и просил закодить):** `not_from: [unknown]` — это ЗАЩИТА, задуманная верно: при старте HA состояние = `unknown`, затем устройство присылает реальный стейт, и без защиты это выглядело бы как «переключение» → свет бы щёлкал сам. Но защита стояла **слишком широко**: она блокировала и первое НАСТОЯЩЕЕ нажатие. На TrueNAS баг не вылезал, потому что HA там почти не перезагружали.
|
||
> **Чего хотел Alex:** «запоминать стейт на момент перезагрузки», чтобы после рестарта сразу был `on`/`off`, а не `unknown`.
|
||
> ✅ **Этот механизм в z2m УЖЕ настроен и хранит стейт на диске.** Кэш: **`/homeassistant/zigbee2mqtt/state.json`** (не `/config/zigbee2mqtt`, не `/addon_configs/…` — проверить `find / -name state.json`) — плоский JSON `{ieee: {param: value}}`, пишется при каждом событии, среди прочего:
|
||
> ```json
|
||
> "0xa4c13873b5c1575b": { "state_l1": "ON", "state_l2": "ON" },
|
||
> "0xa4c1381186ed1a32": { "state_left": "OFF", "state_right": "OFF" }
|
||
> ```
|
||
> При старте z2m с `cache_state_send_on_startup: true` эти значения **должны** переопубликовываться. **Остаётся открытым:** приходит ли cached-стейт к HA **до** того, как устройство пришлёт своё (тогда `unknown` не появится вовсе и `not_from` можно вернуть в узком виде). Проверяется так: рестарт HA + снятие состояния каждые 2 с → видно последовательность `unknown`→cached `ON` или `unknown`→`ON`(от устройства). **Эксперимент не доведён.**
|
||
|
||
> ℹ️ Датчики (t°/освещённость/радар) выходят из `unknown` сами за секунды-минуты — страдают только **реле и кнопки**.
|
||
> ℹ️ Проверка живости узла без нажатия — `get`-запрос: `mosquitto_pub … -t 'zigbee2mqtt/<name>/get' -m '{"state_l1":""}'` → устройство отвечает текущим состоянием.
|
||
|
||
> 🔑 **Диагностический приём:** чтобы понять, жив ли узел, смотреть **`lastSeen` в `database.db`** (`/config/zigbee2mqtt/database.db`, JSON-lines, читать **построчно** — это НЕ единый JSON!):
|
||
> ```bash
|
||
> while IFS= read -r line; do
|
||
> echo "$line" | jq -r 'select(.type=="EndDevice" or .type=="Router") | "\(.ieeeAddr)\t\(.lastSeen // "нет")"'
|
||
> done < db.json
|
||
> ```
|
||
> `lastSeen` обновляется по **любым** пакетам → зелёный флаг живости, даже если `state` в HA = `unknown`.
|
||
|
||
**Итог по всем трём:** `light.smart_light_office_left/right`, `switch.*_office_table_light_switch_l1/l2`, `switch.smart_light_office_left/right` — все корректны, цепочка управления проверена. Зоны проставлены (18 устройств). Розетка котельной видна (её не было в UI именно из-за `area_id = null`).
|
||
|
||
> ℹ️ После правок реестров **обновить страницу в браузере (Ctrl+Shift+R)** — UI держит кэш и может не показать новые зоны сразу.
|
||
|
||
### Этап 4 — проверка и отключение TrueNAS
|
||
18. [ ] Чек-лист из родительского плана §6
|
||
19. [ ] Caddy upstream → t610; GPON-редирект → t610
|
||
20. [ ] Остановить + отключить автозапуск на TrueNAS (§7)
|
||
|
||
## Отличия от родительского плана (что меняется)
|
||
|
||
| Было (родительский план) | Стало (этот план) |
|
||
|--------------------------|-------------------|
|
||
| docker-compose 1:1 на HA OS | HA-аддоны |
|
||
| udev-алиасы `99-tty-alias.rules` на t610 | **не нужно** — аддоны с `uart: true` видят `/dev/serial/by-path/...` и `/dev/serial/by-id/...` автоматически; в конфиге сервиса указывается by-path |
|
||
| Скрипт ожидания tty + systemd | **не нужно** — Supervisor сам ждёт устройство при старте аддона |
|
||
| Ручной `docker compose up` | `ha apps start <slug>` / UI |
|
||
| Пути `/mnt/data/...` | `/addon_configs/<slug>/` и `/share`, `/config` |
|
||
| Хостовый SSH (как на TrueNAS) | **недоступен** — SSH-аддон = Alpine-контейнер; debug-SSH 22222 только через флешку `CONFIG`. Привязка serial решается штатным `uart: true`, хостовый шелл не нужен |
|
||
|
||
> ✅ Плюс: проблема **udev-гонки на t610 снимается** — Supervisor управляет зависимостями и пробросом устройств. Это была самая опасная часть старого плана.
|
||
|
||
## Открытые вопросы
|
||
- [x] ✅ **РЕШЕНО 2026-09-14 — способ привязки CH340 в аддонах:** привязка по `/dev/serial/by-path/...`; механизм Supervisor — флаг `uart: true` в манифесте аддона (доступ ко всем serial автоматически, `devices:` не нужен). Проверено на живом t610. Детали: [[family/how-to/t610-access]] §USB.
|
||
- [x] ✅ **РЕШЕНО 2026-09-14 — куда переносить данные z2m:** `data_path` аддона = **`/config/zigbee2mqtt`** (внутри HA-конфига), НЕ `/addon_configs/`. Туда залиты `database.db` и `configuration.yaml`.
|
||
- [x] ✅ **Проверено 2026-09-14 — совместимость community-repo z2m с HA OS 18.2 / Core 2026.9.2:** работает (v2.14.1-1, координатор EmberZNet 7.4.5, 17 устройств).
|
||
- [x] ✅ **РЕШЕНО 2026-09-14 — `uart: true` для local add-ons:** подтверждено на mbusd/modbus-bridge — в их манифестах `uart: true`, by-path виден, устройства открываются (mbusd порт 502, bridge sniffer на шине ZONT). Тот же механизм, что у z2m и core_ssh.
|
||
- [x] ✅ **РЕШЕНО 2026-09-14 — modbus-bridge `ha_token`/`mqtt_password`:** вписаны, MQTT + HA-опрос работают. Ключевой момент — `ha.url` = `http://192.168.2.176:80` (не `supervisor/core`), + в HA добавлена MQTT-интеграция. **Этап 2 закрыт полностью.**
|
||
- [x] ✅ **РЕШЕНО 2026-09-14 — `custom_components` не переносим.** HACS (репо пусто) и `tuya_local` (нет config entry) не нужны; `localtuya` обслуживал единственную Tuya-розетку, которую Alex заменил на Zigbee → тоже снят.
|
||
- [x] ✅ **РЕШЕНО 2026-09-14 — история БД не переносится** (новая с нуля) и **реестры `.storage` — замена** (вариант B).
|
||
- [x] ✅ **ВЫПОЛНЕНО 2026-09-14 — Zigbee-розетка NEO NAS-WR01B (`0xa4c138eb6fbe9d19`) добавлена** через permit_join (MQTT). z2m = 16 устройств (после удаления мёртвого).
|
||
- [x] ✅ **ВЫПОЛНЕНО 2026-09-14 — Этап 3 (перенос конфига):** бэкапы сделаны, HA остановлен, залиты 12 файлов `.storage` + 5 конфигов + `www/`, HA запущен (**248 сущностей, 11 зон, MQTT-интеграция цела, ошибок нет**). `modbus.host` → `127.0.0.1`, `localtuya: debug` убран, дашборд поправлен.
|
||
- [x] ✅ **РЕШЕНО 2026-09-14 — `Mini Smart Switch 1` удалён из z2m** (`force: true`). Мёртвое: `lastSeen` 2026-01-26, `unavailable`, нет зоны, нигде не используется.
|
||
- [x] ✅ **УТОЧНЕНО 2026-09-14 — механизм связи сущностей:** HA связывает по `unique_id`, НЕ по `entity_id`. Смена `friendly_name` в z2m сохраняет человеческие `entity_id`. Прежнее опасение «всё пересоздастся» снято.
|
||
- [ ] ⏳ **ОСТАЛОСЬ (последний шаг Этапа 3):** прописать `friendly_name` в z2m по таблице из §«КРИТИЧЕСКОЕ ОТКРЫТИЕ» (16 устройств), перезапустить z2m, убедиться, что `unknown`-сущности ожили (ожидаемо ~133 → минимум). Затем чистка hex-остатков (58 шт.) в HA.
|
||
- [x] ✅ **ВЫПОЛНЕНО 2026-09-14 (поздняя сессия) — автоматизации починены:** `device_id` перемаплены (9/9, 20 вхождений), hex-`entity_id` `light.0xa4c13882a4b42db0` → `light.bed_dimmer`. Результат: **16 автоматизаций, 15 `on` + 1 `off`, 0 `unavailable`**. Этап 3 закрыт.
|
||
- [x] ✅ **УСТРАНЕНО 2026-09-14 — HTTP-варнинг:** блок `http:` удалён из `configuration.yaml`, `trusted_proxies`/`use_x_forwarded_for` перенесены в `.storage/http`.
|
||
- [x] ✅ **ИСПРАВЛЕНО 2026-09-14 — modbus.host:** `127.0.0.1` → `192.168.2.176`.
|
||
- [x] ✅ **ИСПРАВЛЕНО 2026-09-14 — modbus-bridge 404:** hex-entity_id в коде аддона → человеческие + `ha addons rebuild`.
|
||
- [x] ✅ **ВЫПОЛНЕНО 2026-09-14 (поздняя сессия) — Zigbee-розетка `boiler_controller_power` добавлена** (`0xa4c1381694217e10`, TS011F, зона Котельная) как питание контроллеров котлов; 13 сущностей переименованы из hex. **Итого z2m = 15 устройств, реестр = 332 сущности, hex = 0.**
|
||
- [ ] ⏸️ **ГЛАВНЫЙ ОСТАВШИЙСЯ БЛОКЕР (аппаратный, за Alex):** 32 заслонки вентиляции `unavailable` — шина не отвечает (exception 0x0B). Подключить линии A/B к CH340 #2 (`by-path ...0:4:1.0-port0`, slave 11). Конфиг верный, оживут сами.
|
||
- [ ] Камера (§8 родительского плана) — не аддон, разбираться отдельно
|
||
- [x] ✅ **ВЫПОЛНЕНО 2026-09-14 (конец сессии) — ЗОНЫ восстановлены у 18 устройств.** `area_id` теряется при ре-регистрации устройств (как `device_id`) — все 14 zigbee были без зон. Проставлены по эталону с TrueNAS по `identifiers[0][1]` (`zigbee2mqtt_<ieee>`). Скрипт: `~/tmp-t610/etap3-fix/set_all_areas.jq`.
|
||
- [x] ✅ **ИСПРАВЛЕНО 2026-09-14 (конец сессии) — office-переключатель:** hex-`entity_id` в **триггерах** автоматизаций (`switch.0xa4c13873b5c1575b_l1/l2` → `switch.office_table_light_switch_l1/l2`); реле выведены из `unknown` нажатием кнопок. Цепочка управления светом проверена на живом.
|
||
- [x] ✅ **УСТАНОВЛЕНО 2026-09-14 (конец сессии) — z2m не публикует состояние пассивно:** реле «залипают» в `unknown` до первого события. Живость проверять по `lastSeen` в `database.db` (JSON-lines, читать построчно).
|
||
- [x] ✅ **ПРОВЕРЕНО ЭКСПЕРИМЕНТОМ 2026-09-14 — `unknown` возвращается после рестарта HA.** Зафиксировано состояние → рестарт → состояние снято: всё `on`/`off` слетело в `unknown`. `retain: true` + `cache_state*` в z2m стоят, но retained на топиках устройств фактически не публикуется (контроль: `bridge/state` retained есть). Питфолл: `mosquitto_sub -R` в аддоне врёт. См. §«ДОБАВЛЕНО В КОНЦЕ СЕССИИ» ③‑бис.
|
||
- [x] ✅ **ВЫПОЛНЕНО 2026-09-14 (конец сессии, после объяснения Alex) — `not_from: [unavailable, unknown]` УБРАН** из триггеров `office_pass_switch_table`/`office_pass_switch_main`. Кнопка срабатывает **с первого нажатия после рестарта**; проверено на живом toggle'ом через z2m на обоих каналах. Бэкап `/config/automations.yaml.bak-20260914-131541`.
|
||
- [ ] ⏳ **ОТКРЫТО (низкий приоритет):** выяснить, доходит ли cached-стейт из `/homeassistant/zigbee2mqtt/state.json` до HA **до** первого события от устройства. Если да — `unknown` после рестарта исчезнет вовсе, и защиту `not_from` можно вернуть в узком виде (только против фантомного переключения). Метод: рестарт HA + снятие состояния каждые 2 с.
|
||
- [ ] ⚠️ Незакреплённое: `sensor.0xa4c138f8da8bc478_voltage/energy/power/current` (11 hex-сущностей розетки Насос обратки) — проживут ли под новым `friendly_name` или создадутся дубли; проверить после шага 8.
|
||
|
||
## Связанные заметки
|
||
- [[family/plans/home-automation-migration-t610]] — родительский план
|
||
- [[family/how-to/home-automation]] — карта slave ID, ZONT, регистры
|
||
- [[family/how-to/t610-access]] — доступ к хосту, CLI, карта USB, питфоллы
|
||
- [[family/how-to/zont-modbus-bridge-udev-race-protection]] — старая проблема гонки udev (на аддонах неактуальна)
|
||
- [[family/how-to/truenas-infrastructure]] — текущий docker-стек TrueNAS
|