57 KiB
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. Зоны проставлены у 18 устройств. ✅ Автоматизации: 16 шт., 15on+ 1off, 0unavailable(было 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— дублей нет. 🔑 При переносе HA между инстансами теряются ВСЕ инстанс-локальные привязки (устройства ре-регистрируются):
device_id— новый у каждого устройства → перемапить вautomations.yaml/scripts.yamlпоidentifiers(zigbee2mqtt_<ieee>);area_id(зона устройства) — теряется → проставить заново по эталону;- ссылки на
entity_idв автоматизациях/дашбордах — не обновляются автоматически, даже если реестр переименован (грепать[a-z_]+\.0x[0-9a-f]{16}везде, включаяplatform: state-триггеры). Подробно: family/plans/t610-addons-deployment §«ДОБАВЛЕНО В КОНЦЕ СЕССИИ». 🔑 z2m не публикует состояние пассивно — реле остаютсяunknownдо первого события. Живость проверять поlastSeenвdatabase.db. После рестарта HA — физически нажать кнопки. 🔑 ⚠️unknownпосле рестарта HA ВОЗВРАЩАЕТСЯ — проверено экспериментом 2026-09-14. Зафиксировано →homeassistant.restart→ снято: всё, что былоon/off, сталоunknown(office_table_light_switch,smart_light_office,heating_cable_plug,boiler_controller_power).retain: true,cache_state,cache_state_persistent,cache_state_send_on_startupв z2m стоят — но retained на топиках устройств не публикуется (контроль:zigbee2mqtt/bridge/stateretained есть → брокер умеет). ✅ РЕШЕНИЕ (применено 2026-09-14):not_from: [unavailable, unknown]убран из триггеров кнопок вautomations.yaml. Первое нажатие срабатывает, проверено на живом. Бэкап/config/automations.yaml.bak-20260914-131541. 🔑 Кэш состояний z2m лежит в/homeassistant/zigbee2mqtt/state.json(НЕ в/config/zigbee2mqttи не в/addon_configs— искатьfind / -name state.json). Плоский JSON{ieee: {param: value}}, пишется при каждом событии. Полное описание и таблица «до/после»: family/plans/t610-addons-deployment §«ДОБАВЛЕНО В КОНЦЕ СЕССИИ» ③‑бис.
Основное
| Параметр | Значение |
|---|---|
| Железо | 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), НЕ через бэкап-снапшот.
Подключение
# 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) — полезные команды
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:
# все 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 уже в окружении):
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.
Проверка токена:
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 → читать на месте:
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 '"'. Обход — собирать заголовок без литерала рядом с переменной:
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:
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 сущности).
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)
Жизненный цикл:
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):
${BUILD_FROM}пустой →base name (${BUILD_FROM}) should not be blank. Либо добавитьbuild.yamlcbuild_from: {amd64: <image>, aarch64: <image>}, либо взять готовый образ напрямую (FROM 3cky/mbusd:latest) — тогдаbuild.yamlне нужен.- Supervisor рекурсивно парсит ВСЕ
*.yml/*.yamlв папке аддона как манифесты → служебный шаблон конфига даётInvalid app config!и ломает загрузку. Фикс: держать шаблон с расширением.tmpl(напримерdata/config.template.tmpl), переименовывать в.ymlтолько внутри контейнера при сборке. ENTRYPOINTбазового образа перебиваетCMD→ контейнер запускал бинарь напрямую, минуяrun.sh(mbusd: can't read config file /etc/mbusd.conf). Фикс: в Dockerfile явноENTRYPOINT []+CMD ["/bin/bash","/run.sh"].- Пакета может не быть в репозиториях Alpine (
apk add mbusd→no such package) — тогда только готовый образ из Docker Hub или сборка из исходников. - Сборка идёт через
docker buildxна хосте, тянет базовый образ из интернета, занимает несколько минут. uart: trueв манифесте обязателен для доступа к/dev/serial/by-path/…(иначе аддон не увидит CH340/Zigbee). Для modbus-bridge дополнительноhost_network: true.- В HA UI local add-ons требуют Advanced Mode в профиле (Settings → Apps).
- ⚠️ Правка файлов аддона (
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 |
⚠️ в этой папке его НЕТ. Живой кэш состояний: /homeassistant/zigbee2mqtt/state.json (искать find / -name state.json) — плоский JSON {ieee: {param: value}}, пишется при каждом событии; z2m переопубликовывает его при старте (cache_state_send_on_startup: true). Содержит state_l1/state_l2 (TS0002) и state_left/state_right (TS0012) — именно из него видно «запомненный на момент перезагрузки» стейт реле. |
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построчно (каждый объект = устройство):
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):
# пароль 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.
Переименование устройства после спаривания (обязательный шаг):
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.
Проверка результата:
jq -r 'select(.ieeeAddr=="0x…") | {ieeeAddr,type,manufName,powerSource}' /config/zigbee2mqtt/database.db
jq -r '.["0x…"]' /homeassistant/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 — давно. Проверка:
state.json читать по пути /homeassistant/zigbee2mqtt/state.json (не /config/zigbee2mqtt/).
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.
# бэкап базы
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.*.
Процедура:
# 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.yamlwww/—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>"]]):
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 устройство не определить.
Три места, где реально лежат имена:
- z2m →
/config/zigbee2mqtt/configuration.yaml→ секцияdevices:→friendly_name. ✅ На TrueNAS там были человеческие имена (Насос обратки,Kitchen hood,Sauna,Dimmer bed...). - HA
core.device_registry→data.devices[].name, связь черезidentifiers: [["mqtt","zigbee2mqtt_0x…"]]:
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
- Дашборд
lovelace.home_plan→entityвpicture-elements— самый надёжный источник рабочей схемы имён (switch.sauna,light.smart_light_office_left,binary_sensor.shower_2_presence_sensor_presence...).
Питфолл поиска по автоматизациям: триггеры часто ссылаются на device_id, а не entity_id:
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):
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 неактуальна)