[2026-08-25] eagle: family/how-to/home-automation.md family/how-to/truenas-infrastructure.md family/plans/zont-modbus-bridge-udev-race-protection.md
This commit is contained in:
@@ -194,6 +194,10 @@ ZONT relays
|
||||
[8: Конвектор котельная - н.п.]
|
||||
```
|
||||
|
||||
> **ℹ️ Про «виртуальные sensor 101/102/103» и «недоступные датчики в ZONT»:**
|
||||
> 101/102/103 — это **виртуальные Modbus-slaves**, которые контейнер `modbus-bridge` на TrueNAS подставляет на шине `ttyZONT`: реальные 485-датчики **Гостиная=1, Детская=2, Спальня=3** сниффятся и их температуры выдаются ZONT'у как датчики 101/102/103 (регистр 100). ZONT (Modbus master) опрашивает их как внешние датчики.
|
||||
> Если `modbus-bridge` не запущен/не слушает `ttyZONT` → ZONT показывает эти датчики **«недоступные»**. Известная первопричина — гонка docker/udev после рестарта TrueNAS. Подробности и план защиты: `[[family/how-to/truenas-infrastructure.md#Проблема-modbus-bridge/mbusd-после-рестарта-TrueNAS-гонка-с-udev]]` и `[[family/plans/zont-modbus-bridge-udev-race-protection]]`. (Заметка обновлена 2026-08-25: добавлено пояснение про 101/102/103, ZONT relays не менялись.)
|
||||
|
||||
## Карта регистров контроллера вентиляторов
|
||||
|
||||
```
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
# TrueNAS — инфраструктура
|
||||
|
||||
> Обновлено: 2026-08-24 (пересоздание закончено, пул ONLINE, остался подъём docker)
|
||||
> Обновлено: 2026-08-25 (docker поднят; modbus-bridge/mbusd починены после гонки с udev)
|
||||
|
||||
> ## ✅ СТАТУС на 2026-08-24: пул ПЕРЕСОЗДАН и работает; docker НЕ поднят
|
||||
> **Пул RED_2TB пересоздан и УЖЕ зареган в TrueNAS** — перезагрузка/импорт НЕ нужны. `zpool status` ONLINE (`mirror-0{sdc,sdf}`+`mirror-1{sde,sdd}`), `midclt call pool.query` id=1 RED_2TB ONLINE, топология обновлена. rsync возврата данных ЗАВЕРШЁН (storage 3.29T, immich 353G, backup 287G, Edu 214G, Photos 146G, old-bu 65G, docker, docker.bak, system — все смонтированы, `/mnt/RED_2TB/`).
|
||||
> **⚠️ НЕ ГОТОВО — слой приложений (docker) НЕ восстановлен:**
|
||||
> - `docker.service` — `inactive (dead)`, `disabled`. Образы/тома/контейнеры НЕ запущены.
|
||||
> - **`.ix-apps` датасет (docker storage, был 24G) НЕ перенесён при выгрузке на IronWolf** и НЕ сохранён — rsync шёл из `/mnt/RED_2TB/*` и не включал монтируемый в `/mnt/.ix-apps` системный датасет. Проверено: `/mnt/.ix-apps` пуст, на IronWolf его нет, в `zfs list` нет. Образы = кеш → перекачаются из registry сами; конфиги целы в `/mnt/RED_2TB/docker/<app>`.
|
||||
> - **Контейнеры запускались через `docker-compose`, НЕ через TrueNAS Apps (TrueCharts/Custom App).** Управление в доке — `docker restart homeassistant`, `docker exec ...`; daemon.json `data-root:/mnt/.ix-apps/docker`. `.ix-apps` = просто data-root docker, не TrueNAS Apps.
|
||||
> **TODO подъёма docker:** (1) создать датасет/папку под `data-root` (куда смотрит `/mnt/.ix-apps/docker`), (2) Docker engine: `systemctl enable --now docker` + проверить `docker info`, (3) поднять контейнеры: большинство уже имеют `docker-compose.yml` в `/mnt/RED_2TB/docker/<app>/` → `docker compose up -d`; **4 контейнера без compose (ha, webdav, modbus-bridge, zigbee2mqtt) восстанавливать вручную** — параметры и полный инвентарь в разделе «Инвентарь compose-файлов по папкам» ниже. Конфиги целы в `/mnt/RED_2TB/docker/<app>`, образы перекачаются из registry (storage `.ix-apps` потерян = только кеш).
|
||||
> **⚠️ НЕ ЗАКРЫТО отдельно:** TimeMachine (~277G, бэкапы macOS) НЕ возвращён — на IronWolf `/mnt/IRONWOLF/TimeMachine/` root-only NFSv4 ACL → rsync не докопировал. Алекс решение по нему не подтвердил («какой нахуй возврат time machine» — вероятно не нужен, старые снепшоты не важны).
|
||||
> ## ✅ СТАТУС на 2026-08-25: пул ONLINE, docker поднят, почти все контейнеры Up
|
||||
> **Пул RED_2TB пересоздан (2026-08-21) и работает:** `zpool status` ONLINE, rsync возврата данных завершён, `/mnt/RED_2TB/` смонтирован.
|
||||
> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). Ещё не подняты/падают отдельные (library был Restarting; inpxer/inpx-web/arr/cups/ser2net не в списке — проверить при надобности).
|
||||
> **⚠️ Известная проблема modbus-bridge/mbusd — гонка с udev при рестарте TrueNAS:**
|
||||
> - После перезагрузки docker стартует раньше udev, поэтому `/dev/ttyZONT` и `/dev/ttyVent` на момент старта контейнеров ещё не существуют.
|
||||
> - Контейнеры с `devices: /dev/ttyZONT:...` падают на `start` с `error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
|
||||
> - `restart: unless-stopped` НЕ перезапускает такой контейнер (отказ до старта процесса).
|
||||
> - Следствие: modbus-bridge не отвечает на шине → 485-датчики в ZONT отображаются **«недоступные»**.
|
||||
> - Ручной фикс: `docker start modbus-bridge mbusd` (после готовности tty-симлинков).
|
||||
> - Защита от рецидива (план, НЕ внедрён — ждёт OK Alex): `family/plans/zont-modbus-bridge-udev-race-protection.md` — POSTINIT init script с ожиданием симлинков + `docker start`.
|
||||
> **⚠️ ДРУГОЕ (открытое):** пул `DEST` (IronWolf) на 2026-08-24 `DEGRADED` — разобраться, почему (отвалился mirror-член). TimeMachine (~277G) не возвращён (бэкапы macOS, вероятно не нужен — решение Alex не подтверждено). SSH с Mac по 192.168.2.197 нестабилен — использовать `mallexxx.duckdns.org`.
|
||||
|
||||
> ## 🔒 ИСТОРИЯ: статус до восстановления (2026-08-24)
|
||||
> **Пул RED_2TB пересоздан и УЖЕ зареган в TrueNAS** — `zpool status` ONLINE (`mirror-0{sdc,sdf}`+`mirror-1{sde,sdd}`), `midclt call pool.query` id=1 RED_2TB ONLINE. rsync возврата данных ЗАВЕРШЁН (storage 3.29T, immich 353G, backup 287G, Edu 214G, Photos 146G, old-bu 65G, docker, docker.bak, system — все смонтированы).
|
||||
> **`.ix-apps` датасет (docker storage, был 24G) НЕ перенесён** — rsync шёл из `/mnt/RED_2TB/*` и не включал монтируемый в `/mnt/.ix-apps` системный датасет. Образы = кеш → перекачаются из registry сами; конфиги целы в `/mnt/RED_2TB/docker/<app>`.
|
||||
> **Контейнеры запускались через `docker-compose`, НЕ через TrueNAS Apps (TrueCharts/Custom App).** daemon.json `data-root:/mnt/.ix-apps/docker`. `.ix-apps` = просто data-root docker, не TrueNAS Apps.
|
||||
> **⚠️ ДРУГОЕ (проверено 2026-08-24):** пул `DEST` (IronWolf, источник спасения) на момент проверки `DEGRADED` в `zpool list` — стоит разобраться, почему (вероятно отвалился mirror-член). Также SSH с Mac к TrueNAS по 192.168.2.197 нестабилен (`Connection reset/reset by peer`, `kex_exchange_identification read`), возможен конфликт сессий при активном входе на консоль — использовать `mallexxx.duckdns.org` и ретраи.
|
||||
|
||||
> ## ⚠️ СТАТУС на 2026-08-21: пул RED_2TB — данные СПАСЕНЫ, пул OFFLINE, пересоздаём
|
||||
@@ -182,24 +190,49 @@ ssh truenas_admin@mallexxx.duckdns.org "docker restart homeassistant"
|
||||
ssh truenas_admin@mallexxx.duckdns.org "docker exec homeassistant python -m homeassistant --script check_config --config /config"
|
||||
```
|
||||
|
||||
### mbusd — Modbus TCP gateway
|
||||
### mbusd — Modbus RTU → TCP gateway
|
||||
|
||||
Мост Modbus RTU → TCP: пробрасывает серийный порт инвертора AT2 вентиляции в TCP 502.
|
||||
Мост Modbus RTU → TCP: пробрасывает серийный порт в TCP 502. Используется и для вентиляции (AT2), и для ZONT-шины.
|
||||
|
||||
- **Image:** `3cky/mbusd`
|
||||
- **Порт:** `502:502`
|
||||
- **Config:** `/mnt/RED_2TB/docker/mbusd/mbusd.conf` → `/etc/mbusd.conf`
|
||||
- **Device:** `/dev/ttyVent` → `/dev/ttyUSB0` (внутри контейнера)
|
||||
- **Image:** `3cky/mbusd`, compose в `/mnt/RED_2TB/docker/mbusd/`
|
||||
- **Порт:** `502:502` (TCP)
|
||||
- **Config:** `/mnt/RED_2TB/docker/mbusd/mbusd.conf` → `/etc/mbusd.conf` (`device=/dev/ttyUSB0 speed=9600 mode=8n1`, `port=502`)
|
||||
- **Device:** `devices: - /dev/ttyVent:/dev/ttyUSB0`
|
||||
- **Entrypoint:** `["/usr/bin/mbusd","-d","-L","-","-c","/etc/mbusd.conf"]`
|
||||
- **Pitfall udev-race:** см. ниже «Проблема modbus-bridge/mbusd после рестарта».
|
||||
|
||||
### modbus-bridge — AT2 Modbus → MQTT
|
||||
### modbus-bridge — 485-датчики + виртуальные slaves для ZONT
|
||||
|
||||
Кастомный Python-мост: читает регистры инвертора AT2 через mbusd и публикует в MQTT → Home Assistant.
|
||||
Кастомный Python-мост (`modbus_ha_bridge.py`, репо `HA-ZONT-Modbus`), контейнер `/mnt/RED_2TB/docker/modbus-bridge/`.
|
||||
|
||||
- **Image:** `modbus-bridge` (локальная сборка)
|
||||
- **Volumes:**
|
||||
- `/mnt/RED_2TB/docker/modbus-bridge/config.yml` → `/app/config.yml` (ro)
|
||||
- `/mnt/RED_2TB/docker/modbus-bridge/modbus_ha_bridge.py` → `/app/modbus_ha_bridge.py` (ro)
|
||||
- **Source:** `modbus_ha_bridge.py` в репозитории `HA-ZONT-Modbus`
|
||||
- **Image:** `modbus-bridge` (локальная сборка, `--no-cache`). **НЕ имеет compose-файла** — создавался через `docker-compose`-подобный run; для восстановления использовать параметры ниже.
|
||||
- **Device:** `devices: - /dev/ttyZONT:/dev/ttyUSB0` (внутри контейнера слушает `/dev/ttyUSB0`)
|
||||
- **Volumes:** `config.yml` → `/app/config.yml` (ro), `modbus_ha_bridge.py` → `/app/modbus_ha_bridge.py` (ro)
|
||||
- **Env (из compose):** `HA_TOKEN`, `MQTT_USER=zont`, `MQTT_PASS=...`
|
||||
- **`network_mode: host`**; MQTT broker `localhost:1883` (mosquitto), HA `http://localhost:8123`.
|
||||
|
||||
**Роль (важно — два режима работы):**
|
||||
1. **Sniff (чтение)** — сниффит реальные 485-датчики на шине ZONT: **Dining=slave 1, Kids=slave 2, Bedroom=slave 3** (CO₂/температура/влажность, регистры 2 и 100) и публикует в MQTT (`modbus/sensors/<room>/...`), а также пишет в HA.
|
||||
2. **Mappings (виртуальные slaves для ZONT)** — подставляет на **slave 101/102/103** (регистр 100) значения temperature от 485-датчиков: Dining→101, Kids→102, Bedroom→103. **ZONT (Modbus master) опрашивает 101/102/103 как свои внешние датчики температуры.** Есть также wifi-термостат как slave 100 (`sensor.0xa4c13862d39377e6_temperature`) и Socket 1 = slave 101 через switch (обратим внимание: 101 занят и Dining temp — переиспользуется на одном slave с разными функциями; сверить в config.yml).
|
||||
3. Также через mbusd может работать с AT2 вентиляции.
|
||||
|
||||
**Если modbus-bridge не слушает `ttyZONT`** (упал/не запущен) → ZONT не получает ответ на запросы slave 101/102/103 → в интерфейсе ZONT 485-датчики отображаются **«недоступные»**.
|
||||
|
||||
### Проблема modbus-bridge/mbusd после рестарта TrueNAS (гонка с udev)
|
||||
|
||||
**Симптом:** после перезагрузки TrueNAS `/dev/ttyZONT` и `/dev/ttyVent` присутствуют (`ttyZONT`→ttyUSB1, `ttyVent`→ttyUSB0, создаются init script id=1), НО контейнеры `modbus-bridge`/`mbusd` упали на старте и не перезапустились; 485-датчики в ZONT «недоступные».
|
||||
|
||||
**Причина:** docker стартует раньше udev → при `start` контейнера устройство ещё не существует → падение ДО запуска процесса:
|
||||
```
|
||||
docker inspect <c> --format '{{.State.Error}}'
|
||||
error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory
|
||||
# ExitCode 128 / 255, RestartCount=0
|
||||
```
|
||||
`restart: unless-stopped` такой отказ **не подхватывает** (RestartCount=0), и задержка внутри entrypoint не поможет (device mount происходит до старта процесса).
|
||||
|
||||
**Ручной фикс:** `docker start modbus-bridge mbusd` (после того как симлинки созданы).
|
||||
|
||||
**Защита от рецидива (план, НЕ внедрён, ждёт OK Alex):** `family/plans/zont-modbus-bridge-udev-race-protection.md` — POSTINIT init script `/mnt/RED_2TB/system/start-modbus.sh`, который ждёт появления `ttyZONT`/`ttyVent` (≤25с) и делает `docker start modbus-bridge mbusd`.
|
||||
|
||||
### USB device aliases
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: 'План: защита modbus-bridge от гонки с udev (ZONT датчики)'
|
||||
tags:
|
||||
- plan
|
||||
- family
|
||||
- homeautomation
|
||||
- zont
|
||||
- modbus
|
||||
updated: '2026-08-25'
|
||||
status: proposed
|
||||
---
|
||||
# План: защита modbus-bridge/mbusd от гонки с udev (после рестарта TrueNAS)
|
||||
|
||||
> Статус: **план на согласование** (2026-08-25), ничего не применено.
|
||||
> Задача: исключить повторение «485-датчики недоступные в ZONT» после перезагрузки TrueNAS.
|
||||
|
||||
## Контекст / причины
|
||||
- ZONT (192.168.0.10) — Modbus master на RS-485 шине `ttyZONT`. 485-датчики: Dining=1, Kids=2, Bedroom=3.
|
||||
- Контейнер `modbus-bridge` сниффит их и подставляет виртуальные slaves 101/102/103 → ZONT опрашивает как внешние датчики.
|
||||
- Если modbus-bridge не слушает шину → ZONT показывает датчики «недоступные».
|
||||
- Первопричина 2026-08-25: **гонка загрузки** — docker стартовал раньше udev, `/dev/ttyZONT` и `/dev/ttyVent` ещё не существовали → контейнеры упали на старте:
|
||||
`error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
|
||||
- `restart: unless-stopped` **не перезапускает** контейнер, упавший на этапе `docker start` (binding device fail) — отказ до старта процесса.
|
||||
|
||||
## Ключевое ограничение (почему задержка в entrypoint НЕ поможет в чистом виде)
|
||||
У modbus-bridge и mbusd устройство проброшено через `devices: /dev/ttyZONT:/dev/ttyUSB0` / `devices: /dev/ttyVent:/dev/ttyUSB0`.
|
||||
**Docker при `start` монтирует устройство ДО запуска процесса** — если `<src>` не существует на _момент создания/старта контейнера_, контейнер вообще не стартует, и `sleep` внутри entrypoint/CMD никогда не выполнится.
|
||||
→ Простой «sleep N в entrypoint» проблему НЕ решает.
|
||||
|
||||
## Почему контейнеры сами НЕ поднялись (RestartCount=0) — разбор
|
||||
`restart: unless-stopped`/`always` ретраит в ДВУХ случаях:
|
||||
1. **Процесс контейнера стартовал и завершился** с ненулевым кодом → демон ретраит с backoff (RestartCount растёт).
|
||||
2. **Перезапуск самого docker daemon** → демон пробует поднять `unless-stopped`/`always` контейнеры.
|
||||
|
||||
Наш случай — ТРЕТИЙ, особый: контейнер **вообще не стартовал** — ошибка на этапе монтирования устройства:
|
||||
`error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory`.
|
||||
- Нет процесса, который «завершился бы» — docker не смог примонтировать устройство, процесс не запущен вовсе.
|
||||
- Раз нет завершённого процесса, **restart policy не на что применять** → `RestartCount=0`, демон не ретраит.
|
||||
- После падения (13:24) демон НЕ перезапускался (docker.pid с 07:14) → второго триггера не было.
|
||||
|
||||
Вывод: **отказ на этапе mount устройства НЕ перезапускается restart policy**. Авто-подъём при перезагрузке гарантирован только если устройства существуют на момент старта демона. Иначе — контейнеры молча остаются в exited до ручного `docker start`.
|
||||
|
||||
## Решение (рекомендуемое): POSTINIT init script с ожиданием + docker start
|
||||
Уже есть init script id=1 «Map ttyUSB» (POSTINIT), который копирует udev-правила и делает `udevadm trigger`. Он НЕ ждёт создания симлинков и не запускает докер-контейнеры.
|
||||
|
||||
Добавить новый POSTINIT init script (COMMAND, when=POSTINIT, enabled=true), который:
|
||||
1. Ждёт появления `/dev/ttyZONT` и `/dev/ttyVent` (poll до ~20 сек).
|
||||
2. Как только оба есть — `docker start modbus-bridge mbusd`.
|
||||
3. Если docker ещё не готов — также пробует переждать.
|
||||
|
||||
Пример скрипта (положить в `/mnt/RED_2TB/system/start-modbus.sh`):
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Ожидание tty-алиасов от udev (макс ~25 сек)
|
||||
for i in $(seq 1 25); do
|
||||
[ -e /dev/ttyZONT ] && [ -e /dev/ttyVent ] && break
|
||||
sleep 1
|
||||
done
|
||||
sleep 2 # небольшая пауза на устойчивость udev
|
||||
docker start modbus-bridge mbusd 2>/dev/null
|
||||
```
|
||||
Команда в TrueNAS init: `bash /mnt/RED_2TB/system/start-modbus.sh`
|
||||
Timeout скрипта в TrueNAS: `30`.
|
||||
|
||||
> Альтернатива (не рекомендуется): поменять `devices:` на прямой `/dev/ttyUSB1`/`/dev/ttyUSB0` — но номер ttyUSB может прыгать, алиасы для того и нужны.
|
||||
|
||||
> Примечание: менять Dockerfile/entrypoint и/или `restart: always` здесь **недостаточно** — отказ на этапе mount устройства restart policy не обрабатывает.
|
||||
|
||||
## Шаги внедрения (после согласования)
|
||||
1. Создать `/mnt/RED_2TB/system/start-modbus.sh` на TrueNAS (chmod +x).
|
||||
2. Добавить init script через midclt:
|
||||
```bash
|
||||
midclt call initshutdownscript.create '{"type":"COMMAND","command":"bash /mnt/RED_2TB/system/start-modbus.sh","when":"POSTINIT","enabled":true,"timeout":30,"comment":"Start modbus-bridge/mbusd after udev tty aliases"}'
|
||||
```
|
||||
3. Проверить: `midclt call initshutdownscript.query`
|
||||
4. Проверить после reboot: контейнеры Up, `ttyZONT`/`ttyVent` созданы, датчики в ZONT отвечают.
|
||||
|
||||
## Вердикт по исходному вопросу «добавить задержку в entrypoint»
|
||||
- Задержка внутри entrypoint/CMD для этих контейнеров **не выполняется** из-за device mount до старта процесса → этот путь отпадает.
|
||||
- Правильный эквивалент «задержки до готовности» — скрипт выше на уровне TrueNAS init (ожидание устройств, затем `docker start`).
|
||||
|
||||
## Связанные заметки
|
||||
- [[family/how-to/home-automation.md]] — карта slave ID, ZONT, датчики
|
||||
- [[family/how-to/truenas-infrastructure.md]] — docker, modbus-bridge, mbusd, udev rules, init scripts
|
||||
Reference in New Issue
Block a user