565 lines
54 KiB
Markdown
565 lines
54 KiB
Markdown
# HP t610 — хост домашней автоматизации (HA OS)
|
||
|
||
> Хост, на который переносится домашняя автоматизация с TrueNAS.
|
||
> План переноса: [[family/plans/home-automation-migration-t610]]
|
||
> Развёртывание сервисов: [[family/plans/t610-addons-deployment]]
|
||
|
||
> ✅ **Состояние на 2026-09-14 (Этап 3 ЗАКРЫТ, z2m = 15 устройств):** z2m, mbusd (порт 502), modbus-bridge (MQTT + HA-опрос) — развёрнуты аддонами и **работают**. В HA добавлена **MQTT-интеграция**. **HA-конфиг перенесён с TrueNAS** (`.storage` реестры + конфиги + `www/`): HA запущен, **11 зон, MQTT-интеграция цела, ошибок нет**. Zigbee-розетки добавлены: `heating_cable_plug` (NEO NAS-WR01B, греющий кабель) и **`boiler_controller_power`** (`0xa4c1381694217e10`, TS011F, Котельная — питание контроллеров котлов), мёртвые устройства (3 шт.) **удалены из z2m**. Реестр HA = **332 сущности, hex = 0**.
|
||
> ✅ **Автоматизации: 16 шт., 15 `on` + 1 `off`, 0 `unavailable`** (было 13 «мёртвых» — `device_id` перемаплены + hex-`entity_id` почищен).
|
||
> ✅ **HTTP-варнинг устранён** (блок `http:` → `.storage/http`), **`modbus.host` = `192.168.2.176`** (НЕ `127.0.0.1` — см. питфолл ниже), **modbus-bridge 404 исправлены** (+rebuild аддона).
|
||
> ⏸️ **ОСТАЛСЯ ОДИН БЛОКЕР — аппаратный:** 32 заслонки вентиляции `unavailable` (шина не отвечает, exception 0x0B) → CH340 #2 нужно физически подключить к линиям A/B шины. Конфиг верный.
|
||
>
|
||
> 🔑 **Ключевой вывод:** HA связывает сущности по **`unique_id`**, а не по `entity_id`. Смена `friendly_name` в z2m **сохраняет человеческие `entity_id`** — дублей нет, автоматизации не ломаются.
|
||
> 🔑 **`device_id` НЕ переносится между инстансами HA** — автоматизации, ссылающиеся на `device_id`, надо перемапить по `identifiers`. Подробно: [[family/plans/t610-addons-deployment]] §«ПИТФОЛЛ: device_id ломаются при переносе реестра».
|
||
|
||
## Основное
|
||
|
||
| Параметр | Значение |
|
||
|----------|----------|
|
||
| Железо | HP t610 (AMD T56N 2×@1.65 ГГц, 4 ГБ DDR3, HDD WD2500BEVT 250 ГБ) |
|
||
| ОС | Home Assistant OS 18.2 (generic-x86-64) |
|
||
| HA Core | 2026.9.2 |
|
||
| Supervisor | 2026.09.0 |
|
||
| **IP** | **192.168.2.176** (DHCP-имя `homeassistant`, MAC `9c:8e:99:ef:3f:c5`) |
|
||
| **Web UI** | **`http://192.168.2.176`** — ⚠️ **порт 80, не 8123!** |
|
||
| SSH | `ssh -i ~/.ssh/id_rsa root@192.168.2.176` (через аддон Terminal & SSH) |
|
||
| Сеть | 192.168.2.0/24, статический IP пока не закреплён на роутере |
|
||
|
||
> ⚠️ **HA слушает порт 80, а не 8123.** Порт 8123 на t610 **закрыт**. Веб-морда открывается по `http://192.168.2.176` без указания порта. Проверено 2026-09-13 (curl вернул HTTP 200 и страницу Home Assistant).
|
||
|
||
> 📌 **Первая установка HA OS ставит HA «с нуля»** — конфиги переносятся вручную (см. план миграции §5.6), НЕ через бэкап-снапшот.
|
||
|
||
## Подключение
|
||
|
||
```bash
|
||
# HA UI (порт 80!)
|
||
http://192.168.2.176
|
||
|
||
# SSH — только после установки аддона Terminal & SSH (core_ssh)
|
||
ssh -i ~/.ssh/id_rsa root@192.168.2.176
|
||
```
|
||
|
||
**Важно про SSH:** в HA OS SSH **выключен по умолчанию** — порты 22 и 22222 дают `Connection refused`. Включается **только через аддон** `core_ssh` (Terminal & SSH): Settings → Apps → Terminal & SSH → Install → положить свой публичный ключ в `authorized_keys` → Start. Пароля root для SSH не существует — вход только по ключу.
|
||
|
||
> ⚠️ **Порт 22222 (debug SSH) на t610 закрыт** — debug-доступ не включён. Рабочий путь — аддон.
|
||
|
||
### Хостовый SSH (debug-SSH 22222) — как и зачем
|
||
|
||
Порт **22222** даёт root-шелл **самого хоста HA OS** (не контейнера) — там есть `/etc/udev/rules.d`, `udevadm`, systemd. Это аналог того доступа, что был на TrueNAS.
|
||
|
||
**Но включить его по сети НЕЛЬЗЯ** (проверено 2026-09-14):
|
||
- `ha host` — ssh-команд нет (только reboot/shutdown/disks/options/logs)
|
||
- Supervisor API `/host/services/ssh` → **403 Forbidden** (роль аддона `core_ssh` = `manager`, нужен `admin`)
|
||
- `ha os import` — только импорт конфигов с флешки
|
||
|
||
**Единственный штатный способ:** флешка (FAT32, метка тома **`CONFIG`**) с файлом `authorized_keys` (публичный ключ) в корне → воткнуть в t610 → перезагрузка/ожидание → порт 22222 открывается.
|
||
|
||
> 📌 **На практике хостовый SSH на t610 не нужен:** задача алиасов serial решается штатным механизмом Supervisor — флагом `uart: true` (см. §USB → «РЕШЕНИЕ»). Хостовый шелл потребовался бы только для кастомных udev-правил, а они не требуются.
|
||
|
||
## Ограничения SSH-аддона (что доступно из шелла)
|
||
|
||
Внутри аддона `core_ssh` **НЕТ** `docker` CLI и **НЕТ** `python3`.
|
||
|
||
Есть: `bash`, `curl`, `jq`, `ha` (HA CLI), `ssh`, `ls`, `cat`.
|
||
|
||
**Как управлять docker:** только через Supervisor — `ha docker info` (сам docker на хосте есть, v29.6.2, overlayfs/journald), но из аддона он не виден, т.к. аддон живёт в своём контейнере. Полноценный docker-compose на HA OS — нештатный путь; сервисы ставим **аддонами** (см. [[family/plans/t610-addons-deployment]]).
|
||
|
||
**Скрипты для t610 писать на bash + jq**, не на python3.
|
||
|
||
## HA CLI (`ha`) — полезные команды
|
||
|
||
```bash
|
||
ha info # общая информация
|
||
ha core info # состояние HA Core
|
||
ha supervisor info # список аддонов и репозиториев
|
||
ha apps # список установленных аддонов + состояние
|
||
ha apps info <slug> # детали аддона (в т.ч. options, схема)
|
||
ha apps install <slug> # установить
|
||
ha apps start|stop|restart <slug>
|
||
ha apps logs <slug> # логи аддона
|
||
ha store add <url> # добавить репозиторий аддонов
|
||
ha hardware info # железо + USB-устройства (tty, serial)
|
||
ha host info # диск, версия OS, features
|
||
```
|
||
|
||
> ⚠️ `ha apps` **не умеет менять опции** (нет команды `options`). Настройка — через UI или Supervisor API (см. ниже).
|
||
|
||
### Диагностика USB/serial из аддона (udevadm НЕТ)
|
||
|
||
В SSH-аддоне **нет `udevadm`** (`udevadm info` вернёт пусто / command not found). Атрибуты USB-устройств читать напрямую из **sysfs**:
|
||
|
||
```bash
|
||
# все serial-симлинки (по id и по адресу шины)
|
||
ls -la /dev/serial/by-id/ /dev/serial/by-path/
|
||
|
||
# для конкретного tty — найти sysfs-путь и прочитать атрибуты
|
||
P=$(readlink -f /sys/class/tty/ttyUSB0/device) # базовый sysfs-путь
|
||
for f in idVendor idProduct serial product manufacturer; do
|
||
v=$(cat "${P%/*}/$f" 2>/dev/null); [ -n "$v" ] && echo "$f = $v"
|
||
done
|
||
|
||
# топология USB (какое устройство на каком контроллере/порту)
|
||
lsusb
|
||
lsusb -t
|
||
|
||
# быстрый срез всех tty + их sysfs
|
||
for d in ttyUSB0 ttyUSB1 ttyACM0; do echo "$d -> $(readlink -f /sys/class/tty/$d/device)"; done
|
||
```
|
||
|
||
> 📌 **Питфолл:** у двух CH340 (`1a86:7523`) поля `serial`/`manufacturer` **пустые** → их `by-id` совпадает. Различать только по `by-path` (адрес шины). Подробнее — §USB.
|
||
|
||
### Смена опций аддона через Supervisor API
|
||
|
||
API доступен из аддона (`SUPERVISOR_TOKEN` уже в окружении):
|
||
|
||
```bash
|
||
SLUG="a0d7b954_nodered"
|
||
API="http://supervisor/addons/${SLUG}"
|
||
AUTH="Authorization: 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"
|
||
```
|
||
|
||
> ⚠️ **ПРИМЕЧАНИЕ (2026-09-14):** в некоторых скриптах этой доки переменная токена при записи через инструменты выглядит как `AUTH="...***..."` — это артефакт маскировки секретов, а не рабочий код. В живом скрипте должно быть `AUTH="Authorization: Bearer ${SUPERVISOR_TOKEN}"`. Если строку с `AUTH` съело при редактировании — перезаписать файл целиком (см. `~/tmp-t610/*.sh` на Mac, они рабочие).
|
||
|
||
> ⚠️ API требует **полный** набор опций — схема валидирует все ключи. Посылать только изменённое нельзя: вернёт `Missing option '<key>'`. Берём текущие опции и меняем нужное.
|
||
|
||
### Long-lived token HA и обращение к API из аддонов (важно, 2026-09-14)
|
||
|
||
**Как создать токен:** `http://192.168.2.176` → профиль (аватар) → **Security** → **Long-lived access tokens** → **Create token**.
|
||
|
||
**Проверка токена:**
|
||
```bash
|
||
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer <TOKEN>" http://192.168.2.176/api/
|
||
# 200 = рабочий, 401 = негодный
|
||
```
|
||
|
||
**⚠️ ПИТФОЛЛ 1 — токен маскируется в bash.** Если подставлять токен через переменную окружения / `echo` / `sed`, в итоговый JSON/опции попадает **заглушка** (наблюдалось `<len 13>` вместо реальных 183 символов). **Рабочий способ:** записать токен в **файл** → скопировать файлом на t610 → читать на месте:
|
||
```bash
|
||
TOK=$(tr -d '\n\r' < /tmp/ha_token.txt)
|
||
jq --arg t "$TOK" '.ha_token = $t' input.json > out.json
|
||
```
|
||
|
||
**⚠️ ПИТФОЛЛ 1б — маскировка СЪЕДАЕТ КАВЫЧКУ в скрипте.** Если в тексте bash-скрипта стоит литерал `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
|
||
rm -f /tmp/hdr.txt
|
||
```
|
||
|
||
**⚠️ ПИТФОЛЛ 1в — круглые скобки `()` в строках `echo` внутри bash-скрипта** → `syntax error near unexpected token '('`. Не писать `(…)` в `echo "текст (пояснение)"`. То же для апострофов внутри одинарных кавычек.
|
||
|
||
**⚠️ ПИТФОЛЛ 1г — `jq '…\(…)'` инлайн в bash-скрипте ломается** → `syntax error near unexpected token ')'` / `unexpected EOF`. `jq`-выражения с интерполяцией (`"\(.state)\t\(.entity_id)"`) и вложенными кавычками писать **в отдельный файл** `q_*.jq` и вызывать `jq -rf q_x.jq`. Это самый устойчивый способ (проверено).
|
||
|
||
**⚠️ ПИТФОЛЛ 1д — сборка curl-заголовка с токеном.** Самый надёжный обход маскировки — файл-конфиг curl:
|
||
```bash
|
||
printf 'header = "Authorization: Bearer *** > /tmp/curl.auth # токен из файла, БЕЗ литерала в скрипте
|
||
curl -s -K /tmp/curl.auth http://192.168.2.176/api/states | jq -rf q_x.jq
|
||
```
|
||
Так в тексте скрипта токена нет → маскировщик не портит строку.
|
||
|
||
**⚠️ ПИТФОЛЛ 2 — адрес для аддона.** Внутри аддона `http://supervisor/core` требует **внутренний** `SUPERVISOR_TOKEN`, а пользовательский long-lived token там даёт **401**. Для обращения к HA Core из аддона использовать **прямой адрес**:
|
||
```
|
||
http://192.168.2.176:80 ✅ работает с пользовательским токеном (200)
|
||
http://supervisor/core ❌ 401 с пользовательским токеном
|
||
```
|
||
Это ровно то, что нужно modbus-bridge в его `ha.url` (см. [[family/plans/t610-addons-deployment]]).
|
||
|
||
`host_network: true` в манифесте аддона не обязателен для этого, но не мешает — `192.168.2.176` достижим и без него.
|
||
|
||
**⚠️ ЛОЖНЫЙ СЛЕД (не повторять):** гипотеза «`iss` в JWT должен совпадать с `core.uuid` HA» — **НЕВЕРНА**. У рабочего токена `iss` и `core.uuid` не совпадают (проверено: `iss=e75d1d6f…`, `core.uuid=d3b24dad…`), и это норма. Единственный надёжный критерий — HTTP-код на `/api/`.
|
||
|
||
### Добавление интеграции MQTT в HA (Config Entry Flow API)
|
||
|
||
Если в HA **нет MQTT-интеграции**, discovery-сообщения z2m/bridge уходят в mosquitto и **висят** — сущности не создаются (симптом: `404` при обращении к сущности; в HA только системные ~22 сущности).
|
||
|
||
```bash
|
||
T="<long-lived token>"
|
||
BASE="http://192.168.2.176/api/config/config_entries/flow"
|
||
# 1) создать 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)
|
||
# 2) выбрать вариант "addon" (Mosquitto Mqtt Broker) — креды Supervisor подставит сам
|
||
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`.
|
||
**Эффект:** сущностей стало 104 (69 Zigbee) вместо 22. Скрипт: `~/tmp-t610/setup_mqtt_integration.sh`.
|
||
|
||
## Доступ к роутерам (диагностика сети t610)
|
||
|
||
t610 в сети `192.168.2.0/24`. Роутеры для проверки:
|
||
|
||
| Роутер | Доступ | Особенность |
|
||
|--------|--------|-------------|
|
||
| `192.168.2.2` (OpenWrt, основной) | SSH root, пароль `1316261` | DHCP-аренды: `cat /tmp/dhcp.leases` |
|
||
| `192.168.6.1` («Rasputin», OpenWrt aarch64) | SSH root | имеет eth0 `192.168.2.157/24` → видит сеть 192.168.2.x |
|
||
|
||
> ⚠️ **ПИТФОЛЛ: `nc` на OpenWrt (busybox) НЕ поддерживает флаг `-z`.** `nc -z host port` молча печатает usage и возвращает неверный результат → ложный вывод «порт закрыт». Для проверки портов с OpenWrt использовать `curl -s -o /dev/null -w '%{http_code}'` или `wget`. Проверка портов с Mac через `nc -z` работает нормально.
|
||
|
||
## Диск и железо (проверено 2026-09-13, `ha host info`)
|
||
|
||
| Параметр | Значение |
|
||
|----------|----------|
|
||
| Диск | WD2500BEVT (WD-WX31A60P2258), 228.5 ГБ |
|
||
| Занято | 5 ГБ |
|
||
| Свободно | 214.2 ГБ |
|
||
| Docker (host) | 29.6.2, storage overlayfs, logging journald |
|
||
| OS | `haos:18.2`, generic-x86-64, production |
|
||
|
||
> 📌 Диск был взят из TrueNAS (бывший системный диск с Windows 7) — образ HA OS записан через `dd` с Mac. Подробности в [[family/plans/home-automation-migration-t610]] Шаг 1.
|
||
|
||
## USB-устройства (подключены 2026-09-14, карта зафиксирована)
|
||
|
||
**Все 3 устройства физически подключены к t610** (проверено 2026-09-14).
|
||
|
||
| Устройство | by-id | **by-path (фиксированная привязка)** | tty | Физический порт |
|
||
|-----------|-------|--------------------------------------|-----|-----------------|
|
||
| CH340 #1 | `usb-1a86_USB_Serial-if00-port0` | `pci-0000:00:12.0-usb-0:3:1.0-port0` | `/dev/ttyUSB0` | USB1 порт 3 (OHCI `pci-0000:00:12.0`) |
|
||
| CH340 #2 | `usb-1a86_USB_Serial-if00-port0` ⚠️ **тот же** | `pci-0000:00:12.0-usb-0:4:1.0-port0` | `/dev/ttyUSB1` | USB1 порт 4 |
|
||
| Zigbee Inswift ZBP-MG21 | `usb-Inswift_Zigbee_ZBP-MG21_535A000001-if00` | `pci-0000:04:00.0-usb-0:1:1.0` | `/dev/ttyACM0` | USB3 порт 1 (xhci `pci-0000:04:00.0`) |
|
||
|
||
Sysfs-пути:
|
||
```
|
||
ttyUSB0 → /sys/devices/pci0000:00/0000:00:12.0/usb1/1-3/1-3:1.0/ttyUSB0
|
||
ttyUSB1 → /sys/devices/pci0000:00/0000:00:12.0/usb1/1-4/1-4:1.0/ttyUSB1
|
||
ttyACM0 → /sys/devices/pci0000:00/0000:00:15.3/0000:04:00.0/usb3/3-1/3-1:1.0
|
||
```
|
||
|
||
### ⚠️ ГЛАВНЫЙ ПИТФОЛЛ: два CH340 неразличимы по by-id
|
||
Оба CH340 — `1a86:7523`, **`serial = <none>`, `manufacturer = <none>`, `product = "USB Serial"`** (одинаковые).
|
||
→ **by-id у обоих идентичен** (`usb-1a86_USB_Serial-if00-port0`). Проброс по by-id в аддонах **сломается** — оба аддона получат одно и то же устройство.
|
||
|
||
**Различать только по `by-path`** (адрес шины) — ровно та же проблема, что была на TrueNAS, где алиасы `ttyZONT`/`ttyVent` делались udev-правилами по адресу шины ([[family/how-to/zont-modbus-bridge-udev-race-protection]]).
|
||
|
||
**Zigbee-координатор** — единственный из трёх, у кого есть уникальный серийник (`535A000001`), поэтому его by-id стабилен и проброс по by-id безопасен.
|
||
|
||
### Различия by-path на t610 vs TrueNAS
|
||
- TrueNAS: `KERNELS=="?-1.5"` / `"?-1.6"` (другая топология USB).
|
||
- t610: путь `pci-0000:00:12.0-usb-0:3` и `...-0:4` — **порт 3 и порт 4** на одном OHCI-контроллере. Т.е. udev-правило на t610: `KERNELS=="1-3"` и `KERNELS=="1-4"`.
|
||
|
||
### 🔑 РЕШЕНИЕ: привязка по `by-path` (udev-алиасы на HA OS не нужны)
|
||
|
||
**Как на TrueNAS — нельзя.** Там был хостовый шелл TrueNAS → `/etc/udev/rules.d/99-tty-alias.rules`. На t610 SSH-аддон = **Alpine-контейнер**: нет `/etc/udev/rules.d`, нет `udevadm`. Хостовый доступ = только **debug-SSH 22222**, но он **выключен** и включается **только флешкой** (`authorized_keys` в разделе с меткой `CONFIG`); по сети — никак:
|
||
- `ha host` — ssh-команд нет
|
||
- Supervisor API `/host/services/ssh` → **403 Forbidden** (аддон `core_ssh` имеет роль `manager`, нужен `admin`)
|
||
- `ha os import` — только импорт конфигов с флешки
|
||
|
||
**Рабочая схема — штатный `uart: true`:**
|
||
- Флаг **`uart: true`** в манифесте аддона даёт контейнеру доступ ко **всем** serial-устройствам хоста, включая симлинки `/dev/serial/by-id/` **и** `/dev/serial/by-path/`.
|
||
- Проверено на живом t610: `core_ssh` имеет `uart: true` → его `/dev/serial/by-path/` содержит все пути (таблица выше). z2m-аддон тоже `uart: true`.
|
||
- **`devices:` прописывать не нужно** — проброс автоматический.
|
||
|
||
**Что писать в конфигах сервисов:**
|
||
```
|
||
Zigbee (z2m): serial.port = /dev/serial/by-id/usb-Inswift_Zigbee_ZBP-MG21_535A000001-if00 (или by-path pci-0000:04:00.0-usb-0:1:1.0)
|
||
ZONT (modbus-bridge): /dev/serial/by-path/pci-0000:00:12.0-usb-0:3:1.0-port0 (CH340 #1, порт 3)
|
||
Вентиляция (mbusd): /dev/serial/by-path/pci-0000:00:12.0-usb-0:4:1.0-port0 (CH340 #2, порт 4)
|
||
```
|
||
Функциональный аналог TrueNAS-алиасов: имя не «прыгает» при перезагрузке. Отличие — вместо `ttyZONT` пишется полный by-path.
|
||
|
||
> ⚠️ **by-path привязан к физическому порту** — CH340 #1 держать в порту 3, CH340 #2 в порту 4. Порты зафиксированы.
|
||
|
||
> ✅ **Плюс аддонов:** старая проблема гонки udev ([[family/how-to/zont-modbus-bridge-udev-race-protection]]) на t610 **неактуальна** — Supervisor сам ждёт устройство при старте аддона, скрипты ожидания tty не нужны.
|
||
|
||
## Установленные аддоны (обновлено 2026-09-14)
|
||
|
||
| Аддон | Slug | Версия | Состояние | Порты |
|
||
|-------|------|--------|-----------|-------|
|
||
| Terminal & SSH | `core_ssh` | 10.4.0 | ✅ started | 22 |
|
||
| Mosquitto broker | `core_mosquitto` | 7.1.1 | ✅ started | 1883 (MQTT), 1884 (WS) |
|
||
| Node-RED | `a0d7b954_nodered` | 22.0.6 | ✅ started | 1880 |
|
||
| File editor | `core_configurator` | — | ✅ started | web UI |
|
||
| **Zigbee2MQTT** | `45df7312_zigbee2mqtt` | 2.14.1-1 | ✅ **started (14 устройств)** | ingress 8099 |
|
||
| **mbusd** | `local_mbusd` | 1.0.0 | ✅ **started** | 502 (Modbus TCP) |
|
||
| **modbus-bridge** | `local_modbus-bridge` | 1.1.0 | ✅ **started — MQTT + HA-опрос** | sniffer шины ZONT |
|
||
| Samba share | `core_samba` | — | ⏸️ stopped (нужен пароль) | — |
|
||
|
||
Репозитории аддонов: официальный + **Zigbee2MQTT** (`https://github.com/zigbee2mqtt/hassio-zigbee2mqtt`) + Local apps.
|
||
|
||
## Как собрать local add-on на HA OS (рецепт + питфоллы, 2026-09-14)
|
||
|
||
Local add-ons (`/addons/<slug>/`) нужны, когда сервиса нет в сторе и нет community-репо.
|
||
Собраны так: **mbusd** (`local_mbusd`) и **modbus-bridge** (`local_modbus-bridge`).
|
||
Готовые исходники лежат и на t610 (`/addons/…`), и локально на Mac (`~/tmp-t610/addons/…`).
|
||
|
||
**Минимальная структура аддона:**
|
||
```
|
||
/addons/<slug>/
|
||
config.yaml ← манифест Supervisor (name, version, slug, arch, options, schema, uart, …)
|
||
Dockerfile
|
||
run.sh ← читает /data/options.json, готовит конфиг сервиса, exec сервиса
|
||
build.yaml ← ТОЛЬКО если базовый образ задаётся через ${BUILD_FROM} (см. питфолл 1)
|
||
```
|
||
|
||
**Жизненный цикл:**
|
||
```bash
|
||
ha store reload # Supervisor подхватывает /addons/* → local_<slug> в сторе
|
||
ha apps install local_<slug> # собирает образ (docker buildx) и ставит
|
||
ha apps start local_<slug>
|
||
ha apps logs local_<slug>
|
||
# при правке Dockerfile/манифеста:
|
||
ha apps uninstall local_<slug> && ha store reload && ha apps install local_<slug>
|
||
```
|
||
Настройка опций после установки — через UI или Supervisor API (`POST http://supervisor/addons/<slug>/options`, полный набор опций).
|
||
Диагностика провала сборки: **`ha supervisor logs | tail -60`** — там полный вывод docker build.
|
||
|
||
**⚠️ Питфоллы (все ловились на живом t610):**
|
||
1. **`${BUILD_FROM}` пустой** → `base name (${BUILD_FROM}) should not be blank`. Либо добавить `build.yaml` c `build_from: {amd64: <image>, aarch64: <image>}`, либо **взять готовый образ напрямую** (`FROM 3cky/mbusd:latest`) — тогда `build.yaml` не нужен.
|
||
2. **Supervisor рекурсивно парсит ВСЕ `*.yml`/`*.yaml` в папке аддона** как манифесты → служебный шаблон конфига даёт `Invalid app config!` и ломает загрузку. Фикс: держать шаблон с расширением **`.tmpl`** (например `data/config.template.tmpl`), переименовывать в `.yml` только внутри контейнера при сборке.
|
||
3. **`ENTRYPOINT` базового образа перебивает `CMD`** → контейнер запускал бинарь напрямую, минуя `run.sh` (`mbusd: can't read config file /etc/mbusd.conf`). Фикс: в Dockerfile явно **`ENTRYPOINT []`** + `CMD ["/bin/bash","/run.sh"]`.
|
||
4. **Пакета может не быть в репозиториях Alpine** (`apk add mbusd` → `no such package`) — тогда только готовый образ из Docker Hub или сборка из исходников.
|
||
5. **Сборка идёт через `docker buildx` на хосте**, тянет базовый образ из интернета, занимает несколько минут.
|
||
6. **`uart: true`** в манифесте обязателен для доступа к `/dev/serial/by-path/…` (иначе аддон не увидит CH340/Zigbee). Для modbus-bridge дополнительно `host_network: true`.
|
||
7. В HA UI local add-ons требуют **Advanced Mode** в профиле (Settings → Apps).
|
||
8. **⚠️ Правка файлов аддона (`data/*.tmpl`, `*.py`) НЕ применяется без rebuild.** `run.sh` генерирует runtime-конфиг из копии **внутри образа** (`Dockerfile`: `COPY data/config.template.tmpl /app/config.template.yml`). После правки исходников обязателен **`ha addons rebuild local_modbus-bridge`** — иначе работает старая версия из образа и правки «не видно». Симптом ошибки: `ha addons rebuild <slug>` (команда долгая, ~1 мин).
|
||
|
||
> 📌 Из SSH-аддона `/addons/` **виден** (это `/addons/modbus-bridge`, без префикса `local_`); изначально казалось, что нет — проверять именно `/addons/<имя-папки>`.
|
||
|
||
> 📌 `<slug>` в URL аддона = `local_<имя папки>`. Опции из UI кладутся в `/data/options.json` внутри контейнера — читать через `jq`.
|
||
|
||
> 📌 z2m: `data_path` = **`/config/zigbee2mqtt`** (внутри HA-конфига, НЕ `/addon_configs/`). Там `database.db`, `configuration.yaml`, `coordinator_backup.json`, `log/`. Данные перенесены 1:1 с TrueNAS — подробности и питфоллы: [[family/plans/t610-addons-deployment]] §«z2m на t610 (ВЫПОЛНЕНО)».
|
||
|
||
> 📌 В HA 2026.x аддоны в UI называются **Settings → Apps** (не «Add-ons»). Пункта «Add-ons» в меню больше нет.
|
||
|
||
## Работа с данными z2m на t610 (2026-09-14)
|
||
|
||
**Файлы в `/config/zigbee2mqtt/`:**
|
||
|
||
| Файл | Что это |
|
||
|---|---|
|
||
| `database.db` | база z2m (устройства, endpoints, keys) |
|
||
| `configuration.yaml` | конфиг z2m: `network_key`, `pan_id`, `ext_pan_id`, `channel`, `serial`, `mqtt`, **секция `devices:` с `friendly_name`** |
|
||
| `coordinator_backup.json` | бэкап координатора |
|
||
| `state.json` | runtime-состояние |
|
||
| `log/<ts>/log.log` | логи запуска |
|
||
|
||
**⚠️ ПИТФОЛЛ: `database.db` — это НЕ SQLite, а JSON Lines** (по одному JSON-объекту на строку, расширение `.db` обманчиво).
|
||
- `sqlite3 database.db "SELECT ..."` → **`Error: in prepare, file is not a database (26)`** — не тратить на это время.
|
||
- Читать через `jq` построчно (каждый объект = устройство):
|
||
```bash
|
||
scp root@192.168.2.176:/config/zigbee2mqtt/database.db /Users/admin/tmp-t610/z2m-live.db
|
||
jq -r 'select(.type!="Coordinator") | [.ieeeAddr, .type, (.manufName // "-")] | @tsv' /Users/admin/tmp-t610/z2m-live.db
|
||
# .ieeeAddr, .type (EndDevice/Router/Coordinator), .manufName (модель Tuya, напр. _TZ3000_gjnozsaz)
|
||
```
|
||
- В SSH-аддоне **нет `sqlite3`** — базу копировать на Mac (`scp`) и разбирать там.
|
||
|
||
> 📌 **`modelID` в `database.db` пустой** (не заполнился при переносе базы 1:1), но `manufName` даёт модель Tuya — по ней определяется тип устройства.
|
||
|
||
> ⚠️ **Расхождение имён: HA vs z2m (главный вывод 2026-09-14, УТОЧНЁН).** Человеческие имена в z2m на TrueNAS **были** (`Насос обратки`, `Kitchen hood`, `Sauna`…), но на t610 они превратились в hex — z2m при старте дописал секцию `devices:` с `friendly_name` = IEEE (файл заливался без этой секции). В HA при этом `entity_id` остались человеческими — они правились вручную.
|
||
>
|
||
> | Где | На TrueNAS | На t610 сейчас |
|
||
> |---|---|---|
|
||
> | z2m `friendly_name` | ✅ человеческое (`Sauna`) | ❌ hex |
|
||
> | MQTT-топик | ✅ человеческий | ❌ hex |
|
||
> | HA `entity_id` | ✅ человеческий (`switch.sauna`) | ✅ **сохранился из реестра** |
|
||
> | HA `original_name` | «Температура» (имя параметра) | то же |
|
||
>
|
||
> **Чтобы починить:** прописать `friendly_name` в z2m (= префикс существующих `entity_id`), перезапустить z2m. ⚠️ **`entity_id` при этом НЕ изменятся** — HA связывает сущности по `unique_id` (`<ieee>_<param>_zigbee2mqtt`), который не меняется. Дублей не возникает, автоматизации не ломаются. Полная таблица имён: [[family/plans/t610-addons-deployment]] §«КРИТИЧЕСКОЕ ОТКРЫТИЕ».
|
||
|
||
### Спаривание нового Zigbee-устройства (permit_join через MQTT, 2026-09-14)
|
||
|
||
В z2m-конфиге **`permit_join` не задан** → окно спаривания закрыто по умолчанию. Открывается штатно через MQTT-запрос (без UI):
|
||
|
||
```bash
|
||
# пароль MQTT берём из опций аддона БЕЗ интерполяции в строку ($ в пароле ломает bash)
|
||
ha apps info 45df7312_zigbee2mqtt --raw-json | jq -r '.data.options.mqtt.password' > /tmp/mqtt_pw.z2m
|
||
MPW=$(cat /tmp/mqtt_pw.z2m); rm -f /tmp/mqtt_pw.z2m
|
||
|
||
mosquitto_pub -h core-mosquitto -p 1883 -u zont -P "$MPW" \
|
||
-t 'zigbee2mqtt/bridge/request/permit_join' -m '{"value": true, "time": 250}'
|
||
# → {"data":{"time":250},"status":"ok"}
|
||
|
||
# результат: подписка на события
|
||
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
|
||
# → {"type":"device_joined"} → {"type":"device_interview","status":"started"}
|
||
```
|
||
|
||
> ⚠️ **ЛИМИТ ОКНА СПАРИВАНИЯ = 254 секунды.** `"time": 300` → `error: Cannot permit join for more than 254 seconds`. Ставить **≤250**.
|
||
|
||
> ⚠️ **Спаривание через MQTT-запрос требует запуска ИЗНУТРИ контейнера аддона** (там есть `mosquitto_pub` и доступ к `core-mosquitto`). Если запускать через `ssh root@t610 'mosquitto_pub …'`, переменные окружения не пробрасываются — подставлять значения **локально в строку команды** (`. mqtt.env` → `CMD="mosquitto_pub … '$MQTT_USER' -P '$MQTT_PASS' …"` → `ssh … "$CMD"`), иначе `Connection Refused: not authorised`.
|
||
|
||
**Переименование устройства после спаривания (обязательный шаг):**
|
||
```bash
|
||
mosquitto_pub -h core-mosquitto -u zont -P "$MPW" \
|
||
-t 'zigbee2mqtt/bridge/request/device/rename' \
|
||
-m '{"from":"0xa4c1381694217e10","to":"boiler_controller_power"}'
|
||
```
|
||
⚠️ **HA успевает создать сущности под hex-именем раньше, чем rename применяется** → после rename всё равно нужно чистить hex-`entity_id` в `core.entity_registry` (HA stop → jq → HA start). Подробно: [[family/plans/t610-addons-deployment]] §«Новое устройство: boiler_controller_power».
|
||
|
||
**Зона для нового устройства** ставится в **`core.device_registry`** (поле `area_id`), НЕ в `core.entity_registry`. Найти устройство по `identifiers`: `jq -r '.data.devices[] | select((.identifiers|tostring)|test("<ieee_без_0x>")) | [.id,.name,.area_id] | @tsv' /config/.storage/core.device_registry`.
|
||
|
||
Проверка результата:
|
||
```bash
|
||
jq -r 'select(.ieeeAddr=="0x…") | {ieeeAddr,type,manufName,powerSource}' /config/zigbee2mqtt/database.db
|
||
jq -r '.["0x…"]' /config/zigbee2mqtt/state.json
|
||
```
|
||
|
||
**⚠️ Питфоллы:**
|
||
- **`mosquitto_pub/sub` в аддоне НЕ поддерживают `--pwfile`** (`Error: Unknown option '--pwfile'`) — только `-u`/`-P`. Передавать пароль через переменную, прочитанную из файла (`$(cat)`), а не интерполировать в команду.
|
||
- **`ha apps logs <slug>` тяжёлый** — не ставить его в цикл ожидания (команда «висит» минутами). Ждать завершения интервью лучше через `database.db`/`state.json`, а не грепая логи в `while`.
|
||
|
||
> ✅ **Новое устройство 2026-09-14:** `0xa4c138eb6fbe9d19` — **NEO NAS-WR01B, Smart plug with electrical measurements** (розетка с измерением P/V/I/E), `powerSource: Mains (single phase)`. Заменяет прежний **Tuya Smart Plug** («Ввод воды греющий кабель») — Alex поменял его на Zigbee-розетку. `Successfully configured '0xa4c138eb6fbe9d19' (definition v0.0.1)`, discovery ушёл, сущности создались.
|
||
|
||
> ✅ **Новое устройство 2026-09-14 (поздняя сессия):** `0xa4c1381694217e10` → **`boiler_controller_power`** — **TS011F** (Tuya Smart Plug с измерениями), `manufName _TZ3000_gjnozsaz`, **Router / Mains**, зона **Котельная**. Питание контроллеров котлов. 13 сущностей переименованы из hex → `switch.boiler_controller_power` и т.д. **Итого в z2m 15 устройств.** Рецепт — §«Спаривание нового Zigbee-устройства» выше + [[family/plans/t610-addons-deployment]] §«Новое устройство: boiler_controller_power».
|
||
|
||
### Удаление мёртвого/ненужного устройств из z2m (2026-09-14)
|
||
|
||
Признак мёртвого: `state: unavailable`, в `state.json` записи нет, `lastSeen` в `database.db` — давно. Проверка:
|
||
```bash
|
||
jq -r --arg i "0xcc86ecfffe1347fd" 'select(.ieeeAddr==$i) | {ieeeAddr,lastSeen,interviewCompleted}' /config/zigbee2mqtt/database.db
|
||
# lastSeen — unix ms. 1769414207062 = 2026-01-26 (у мёртвого), живое = сейчас.
|
||
jq -r '.["0xcc86ecfffe1347fd"] // "НЕТ ДАННЫХ"' /config/zigbee2mqtt/state.json
|
||
```
|
||
Перед удалением убедиться, что устройство **нигде не используется** — искать по `device_id` И `entity_id` во всех yaml + `lovelace.home_plan`.
|
||
|
||
```bash
|
||
# бэкап базы
|
||
cp /config/zigbee2mqtt/database.db /config/zigbee2mqtt/database.db.bak-before-remove-$(date +%Y%m%d-%H%M%S)
|
||
# удаление (force=true обязателен для недоступных устройств)
|
||
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/device/remove' -m '{"id": "0xcc86ecfffe1347fd", "force": true}'
|
||
# → {"data":{...,"force":true,...},"status":"ok"}
|
||
```
|
||
**z2m сам** убирает устройство из `database.db` И из секции `devices:` `configuration.yaml`. Проверка: `jq -r 'select(.type!="Coordinator") | .ieeeAddr' database.db | wc -l`.
|
||
|
||
### Перенос HA-конфига с TrueNAS на t610 (Этап 3, 2026-09-14)
|
||
|
||
**Решения Alex:** БД `home-assistant_v2.db` — **с нуля** (не переносим); реестры `.storage` — **замена** (берём с TrueNAS целиком); `custom_components/` — **не переносим** (HACS репо пуст, `tuya_local` не используется, `localtuya` обслуживал заменённую Tuya-розетку).
|
||
|
||
**Комплект для переноса:** конфиги (`configuration.yaml` с правками `modbus.host`→`127.0.0.1` и удалённой строкой `localtuya: debug`, `automations.yaml`, `scripts.yaml`, `scenes.yaml`, `secrets.yaml`), `.storage/` (12 файлов: `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/*.svg`).
|
||
|
||
**⚠️ НЕ переносить** (локальное для t610): `core.uuid` (подменит `instance_id`), `auth`, `auth_provider.homeassistant`, `http`, `http.auth`, `onboarding`, `core.config`, **`core.config_entries`** (⚠️ иначе потеряется настроенная MQTT-интеграция!), `core.analytics`, `frontend.*`, `hacs.*`, `repairs.*`.
|
||
|
||
**Процедура:**
|
||
```bash
|
||
# 1) бэкапы (Mac): tar -czf на t610 в /tmp + scp; с TrueNAS аналогично
|
||
# 2) стоп HA
|
||
ha core stop # проверить: curl http://192.168.2.176/ → 000
|
||
# 3) залить .storage реестры (cp), затем конфиги, затем www/
|
||
# 4) проверить: ha core check # пусто = ошибок нет
|
||
# 5) старт
|
||
ha core start # curl http://192.168.2.176/ → 200
|
||
```
|
||
**Проверка результата:** `jq '.data.entities|length' core.entity_registry` (на TrueNAS было 410), `jq '.data.areas|length' core.area_registry` (11), `jq -r '.data.entries[].domain' core.config_entries | grep mqtt` (должен остаться `mqtt`).
|
||
|
||
> ⚠️ **`lovelace.home_plan` ссылается на сущности по именам** — если дашборд ссылается на старую Tuya-розетку (`switch.vvod_vody_greiushchii_kabel`), заменить в файле на новую (`switch.heating_cable_plug`) **до** залива.
|
||
|
||
**Питфолл: `tar` не читает `auth`/`http`/`auth_provider`** (права `0600`, владелец root) — это норма, и они не нужны. Не считать ошибкой.
|
||
|
||
### Итог миграции (факты после запуска, 2026-09-14)
|
||
- API `200`, **248 сущностей**, 11 зон, MQTT-интеграция цела, ошибок в `home-assistant.log` нет.
|
||
- **99 человеческих сущностей живых**, 16 hex живых, 58 hex всего, 133 `unknown`/`unavailable`.
|
||
- **`unknown` — норма середины работы:** z2m ещё публикует в hex-топики; сущности, питаемые от z2m, данных не получают. Работают modbus (заслонки), `sensor.dining_*`/`kids_*`/`bedroom_*` (sniffer), `shower_2_presence_sensor_*`, `light_sensor_stairs_*`. Лечится сменой `friendly_name` в z2m.
|
||
- **Сущности связаны по `unique_id`** (`<ieee>_<param>_zigbee2mqtt`) → смена `friendly_name` сохраняет `entity_id`, дублей нет.
|
||
|
||
## HA-конфиг: что где лежит (TrueNAS → t610, разведка 2026-09-14)
|
||
|
||
**На TrueNAS** конфиг HA: `/mnt/RED_2TB/docker/ha/` (доступ: `ssh truenas_admin@mallexxx.duckdns.org`).
|
||
|
||
**Состав (для переноса в Этапе 3):**
|
||
- `configuration.yaml` (~30 КБ), `automations.yaml`, `scripts.yaml` (~30 КБ), `secrets.yaml`
|
||
- `www/` — `card-mod.js` + `floorplan/floor1_ha.svg`, `floor2_ha.svg`
|
||
- `.storage/` — **35 файлов**, переносить **выборочно** ⚠️
|
||
- `custom_components/` — `hacs` (2.0.5, **репо пусто → НЕ переносим**), `localtuya` (5.2.3, ~~используется~~ → **ОТКАЗ 2026-09-14**: Tuya-розетка заменена на Zigbee, компонент не нужен), `tuya_local` (2026.7.2, не используется)
|
||
- `home-assistant_v2.db` — 142 МБ, **не переносится** (решение: история с нуля)
|
||
|
||
**Решение по custom_components (2026-09-14, подтверждено Alex):**
|
||
| Компонент | Решение | Почему |
|
||
|---|---|---|
|
||
| `hacs` | ❌ не переносить | репозиториев в HACS ноль, нагрузки не несёт |
|
||
| `localtuya` | ❌ не переносить | обслуживал 1 Tuya-розетку («Ввод воды греющий кабель», IP 192.168.2.194); розетка заменена на Zigbee-розетку NEO NAS-WR01B (`0xa4c138eb6fbe9d19`) |
|
||
| `tuya_local` | ❌ не переносить | config entry отсутствовал → не использовался |
|
||
|
||
Итог: **`custom_components/` вообще не переносим.** В `configuration.yaml` убрать строку `localtuya: debug` (была единственной отсылкой к компоненту).
|
||
|
||
> 📌 **Как узнать, используется ли интеграция:** `jq -r '.data.entries[].domain' core.config_entries` — если домена нет, компонент не подключён. Наличие папки в `custom_components/` ≠ использование.
|
||
|
||
**⚠️ `.storage/` НЕ копировать целиком** — смешаны системные файлы t610 и контентные TrueNAS:
|
||
- ❌ **НЕ трогать:** `core.uuid` (подменит `instance_id`), `auth`, `auth_provider.homeassistant` (сломает логин), `http`, `http.auth`, `onboarding`, `core.config`, `core.config_entries`
|
||
- ✅ **Переносить:** `core.entity_registry` (критично!), `core.device_registry`, `core.area_registry`, `core.floor_registry`, `core.restore_state`, `lovelace.home_plan`, `lovelace_dashboards`, `lovelace_resources`, `person`, `zone`
|
||
|
||
**Порядок критичен:** сначала реестры, **потом** (после старта HA) правка `friendly_name` в z2m и `entity_id` в HA. Переименование до переноса реестров пропадёт — HA перезапишет реестр.
|
||
|
||
### 🔴 Питфоллы переноса HA-конфига между инстансами (2026-09-14, дорого выучено)
|
||
|
||
**1. `device_id` НЕ переносятся.** `device_id` — UUID, генерируемый HA при регистрации устройства **в конкретном инстансе**. Даже с перенесённым `core.device_registry` MQTT-интеграция на новом хосте регистрирует устройства заново → выдаёт **новые** `device_id`.
|
||
**Следствие:** все автоматизации/скрипты с device-триггерами (в UI это почти все) падают с `Unknown device '<uuid>'` и получают `unavailable`.
|
||
**Лечение:** перемапить `device_id` в `automations.yaml`/`scripts.yaml` по `identifiers` (`[["mqtt","zigbee2mqtt_<ieee>"]]`):
|
||
```bash
|
||
jq -r '.data.devices[] | select((.identifiers|tostring)|contains("<ieee_без_0x>")) | [.id, (.name//"—")] | @tsv' \
|
||
/config/.storage/core.device_registry
|
||
```
|
||
Проверить битые ссылки: `ha core logs 2>&1 | grep "Unknown device"`.
|
||
⚠️ `entity_id` и `unique_id` — переносятся; `area_id`/`floor_id` — переносятся (задаются в реестре); `device_id` — **НЕТ**.
|
||
|
||
**2. `core.config_entries` нужен, если есть виртуальные сущности.** `switch_as_x`, `template`, helper'ы ссылаются на entry в `core.config_entries`. Не перенесёшь → сущности-сироты `unavailable`.
|
||
⚠️ **Конфликт:** на новом хосте `core.config_entries` свой (там свежая MQTT-интеграция). Если перенести целиком — потеряешь локальные интеграции; если не перенести — потеряешь `switch_as_x`/helpers. **Решение:** не переносить целиком, а **точечно добавить** нужные entry (мы так добавили 4 `switch_as_x`).
|
||
|
||
**3. HA не переименовывает `entity_id` при смене `friendly_name`.** Связь — по `unique_id` (у z2m `<ieee>_<param>_zigbee2mqtt`, не меняется). Смена `friendly_name` меняет MQTT-топики и `default_entity_id` в discovery, но `entity_id` в реестре остаётся старым → переименовывать вручную (`jq` по `core.entity_registry`).
|
||
|
||
**4. `switch_as_x` с hex-ссылкой.** Восстановленные entry могут ссылаться на старое hex-имя (`switch.0x84...`) → обязательно поправить `options.entity_id` на новое человеческое имя.
|
||
|
||
**Общий вывод для будущих переносов HA:** либо переносить конфиг+реестры **вместе с `core.config_entries`**, либо после переноса делать **два фикса**: (а) перемап `device_id`, (б) добор виртуальных entry. Иначе половина автоматизаций будет `unavailable`.
|
||
|
||
**5. hex-`entity_id` в `automations.yaml`/`scripts.yaml` — вторая поломка того же класса (открыта 2026-09-14).**
|
||
При переименовании реестра (`hex → человеческие entity_id`) **ссылки внутри `automations.yaml` НЕ обновляются**. После переименования реестра в автоматизациях остаются старые hex-имена (`light.0xa4c13882a4b42db0`, `switch.0xa4c13873b5c1575b`) — HA их не находит.
|
||
**Вывод: при переносе чистить ОБА вида ссылок — и `device_id`, и hex-`entity_id`.** Иначе автоматизация «чинится» наполовину.
|
||
**Систематическая проверка (перед заливкой):** собрать множество `device_id` из `automations.yaml`/`scripts.yaml` регексом `device_id:\s*([0-9a-f]{32})`, а hex-`entity_id` — `[a-z_]+\.0x[0-9a-f]{16}`; каждое сверить с реестром t610.
|
||
|
||
**6. `sudo` на TrueNAS не нужен для чтения реестров HA.** `/mnt/RED_2TB/docker/ha/.storage/*` — права 644, owner root → **читаются напрямую** (`scp truenas_admin@...:/mnt/.../core.device_registry .`). `sudo cat` падает с `a terminal is required to read the password` — sudo не требуется.
|
||
|
||
### 🔍 Где искать человеческие имена устройств (важный урок 2026-09-14)
|
||
|
||
**В реестрах HA имён устройств НЕТ.** Проверено на TrueNAS: `core.entity_registry` содержит `original_name` = имя **параметра** («Температура», «Влага», «Занятость»), а не устройства; `name`/`name_by_user` — `null`. Все 16 датчиков t° называются «Температура» → **по `original_name` устройство не определить.**
|
||
|
||
**Три места, где реально лежат имена:**
|
||
1. **z2m** → `/config/zigbee2mqtt/configuration.yaml` → секция `devices:` → `friendly_name`. ✅ На TrueNAS там были человеческие имена (`Насос обратки`, `Kitchen hood`, `Sauna`, `Dimmer bed`...).
|
||
2. **HA `core.device_registry`** → `data.devices[].name`, связь через `identifiers: [["mqtt","zigbee2mqtt_0x…"]]`:
|
||
```bash
|
||
jq -r '.data.devices[] | select((.identifiers|tostring)|test("zigbee2mqtt_0x")) | [.id, (.name//"—"), (.area_id//"—"), (.model//"—")] | @tsv' \
|
||
/mnt/RED_2TB/docker/ha/.storage/core.device_registry
|
||
```
|
||
3. **Дашборд `lovelace.home_plan`** → `entity` в `picture-elements` — **самый надёжный** источник рабочей схемы имён (`switch.sauna`, `light.smart_light_office_left`, `binary_sensor.shower_2_presence_sensor_presence`...).
|
||
|
||
**Питфолл поиска по автоматизациям:** триггеры часто ссылаются на **`device_id`**, а не `entity_id`:
|
||
```yaml
|
||
triggers:
|
||
- type: battery_level
|
||
device_id: 52266a1b4a9d301f0da0dfa6f97c4ea2 # ← имени нет
|
||
```
|
||
Поэтому `grep` по `entity_id` даёт пусто. Искать надо по `device_id` → `core.device_registry` → `identifiers` → IEEE.
|
||
|
||
**⚠️ Питфолл при переносе z2m:** если залить `configuration.yaml` **без секции `devices:`**, z2m при старте сам создаст её и пропишет `friendly_name` = IEEE для каждого устройства → hex-имена. Это и случилось на t610 2026-09-14 (человеческие имена с TrueNAS были потеряны). **Проверять секцию `devices:` сразу после переноса.**
|
||
|
||
**Зоны TrueNAS (`core.area_registry`):** Гостиная `living_room`, Кухня `kitchen`, Спальня `bedroom`, Детская `detskaia`, Кабинет `kabinet`, Ванная `vannaia`, Душевая `dushevaia`, Туалет `tualet`, Северная `severnaia`, Котельная `kotelnaia`, Лестница `lestnitsa`.
|
||
|
||
> 📌 Полезно: имя устройства в z2m ≠ `entity_id` в HA. `entity_id` формируется при **первом появлении** сущности в HA и **не меняется** при смене `friendly_name`. Чтобы переименовать — нужно менять `entity_id` в `core.entity_registry` (или удалить сущность и дать создать заново).
|
||
|
||
**Полезные команды разведки конфига (из SSH-аддона на t610):**
|
||
```bash
|
||
ls -la /config/ # состав HA-конфига
|
||
ls /config/.storage/ | wc -l # число файлов реестра
|
||
jq -r '.data.entries[].domain' /config/.storage/core.config_entries | sort | uniq -c # какие интеграции подключены
|
||
jq -r '.data.entities | length' /config/.storage/core.entity_registry # число сущностей
|
||
cat /config/.HA_VERSION # версия HA
|
||
```
|
||
На TrueNAS аналогично, путь `/mnt/RED_2TB/docker/ha/` (читать под `truenas_admin`).
|
||
|
||
> 📌 **Как узнать, используется ли интеграция:** `jq -r '.data.entries[].domain' core.config_entries` — если домена нет, компонент не подключён (напр. `tuya_local` стоял в файлах, но config entry отсутствовал → не использовался).
|
||
|
||
## Связанные заметки
|
||
- [[family/plans/home-automation-migration-t610]] — план миграции (родительский)
|
||
- [[family/plans/t610-addons-deployment]] — развёртывание аддонов
|
||
- [[family/how-to/home-automation]] — карта slave ID, ZONT, регистры
|
||
- [[family/how-to/truenas-access]] — доступ к TrueNAS
|
||
- [[family/how-to/zont-modbus-bridge-udev-race-protection]] — гонка udev (на t610 неактуальна)
|