[2026-09-14] eagle: family/plans/t610-home-automation.md

This commit is contained in:
Alexey Martemyanov
2026-09-14 13:57:09 +06:00
parent 94953ba836
commit 9c78c2924e
+88 -2
View File
@@ -24,7 +24,8 @@
| Что | Состояние | | Что | Состояние |
|---|---| |---|---|
| **44 сущности `unavailable`** | ~32 заслонки (`switch.intake_damper_*`, `exhaust_damper_*`) + вентиляторы (`switch.fan_3_*`, `sensor.fan_at2_*`). **Шина при этом живая** — см. §5. Причина не в железе, разбираться в логе HA (`homeassistant.components.modbus`). Рабочая гипотеза: короткий `timeout 1000 мс` в mbusd для медленных реле-модулей | | **44 сущности `unavailable`** | ~32 заслонки (`switch.intake_damper_*`, `exhaust_damper_*`) + вентиляторы (`switch.fan_3_*`, `sensor.fan_at2_*`). **Причина НАЙДЕНА 2026-09-14 (поздняя), три слоя — см. §5 «ТРИ реальные причины»:** ① шторм ~28 параллельных TCP-коннектов HA при `mbusd maxconn: 8`; ② регистры отвечают нестабильно (`EXC 0x0B` через раз); ③ `verify` с `state_on: 1`/`state_off: 0` не может сойтись с живым `0x640001`. План фикса согласован, **не применён**. |
| **`sensor.fan_at2_*` — сироты** | В `configuration.yaml` **все `sensors:` закомментированы** → эти сущности физически не могут получать данные. Разобраться с происхождением (2026-09-14 поздняя) |
| `switch.sauna` = `unknown` | Розетка физически отключена (`lastSeen` 8+ ч) — не баг | | `switch.sauna` = `unknown` | Розетка физически отключена (`lastSeen` 8+ ч) — не баг |
| ZONT не перенаправлен | MQTT-редирект GPON-роутера ещё смотрит на TrueNAS | | ZONT не перенаправлен | MQTT-редирект GPON-роутера ещё смотрит на TrueNAS |
| Камера | Контейнер был на TrueNAS, остановлен при восстановлении пула. В Caddy остался мёртвый upstream `192.168.2.197:8090`. Разбираться отдельно | | Камера | Контейнер был на TrueNAS, остановлен при восстановлении пула. В Caddy остался мёртвый upstream `192.168.2.197:8090`. Разбираться отдельно |
@@ -289,6 +290,76 @@ nc -w 5 192.168.2.176 502 < /tmp/mbreq.bin | xxd
**Что осталось:** выяснить, почему HA держит 44 `unavailable`, хотя mbusd отдаёт данные. **Смотреть лог HA** (`homeassistant.components.modbus`, `pymodbus`), не лог mbusd. **Что осталось:** выяснить, почему HA держит 44 `unavailable`, хотя mbusd отдаёт данные. **Смотреть лог HA** (`homeassistant.components.modbus`, `pymodbus`), не лог mbusd.
### 🔴 ДИАГНОСТИКА 2026-09-14 (поздняя): ТРИ реальные причины `unavailable`
Гипотеза «короткий `timeout 1000 мс`» подтвердилась лишь частично. Лог HA вскрыл **шторм параллельных коннектов**, а прямой опрос — **нестабильность регистров** и **неверный `verify`**.
**Причина 1 — 🔴 ШТОРМ параллельных TCP-коннектов HA (главная).**
В логе HA при старте: `Something is blocking Home Assistant from wrapping up the start up phase` + **~28 одновременных задач** `ModbusBaseEntity.async_local_update()` (файл `/usr/src/homeassistant/homeassistant/components/modbus/entity.py:114`, `call_later 15.0`).
Следствие: HA открывает **новое TCP-соединение на каждый опрос сущности**, все параллельно, и сразу бросает.
Подтверждение — `netstat -an` на t610: **5 соединений в `TIME_WAIT`** с `172.30.33.0:*` (адрес контейнера HA Core) → `192.168.2.176:502`.
При `mbusd maxconn: 8` и ~28 опросах параллельно — **соединения упираются в лимит и отваливаются по таймауту**.
Признак в логе mbusd — **пачки рваных сессий**: `conn_open` → мгновенный `conn_close`, по 20+ подряд с интервалом 1–4 с.
> ⚠️ **ВАЖНО:** источник коннектов в логе mbusd = `192.168.2.157` (роутер Rasputin, NAT — см. §2), НЕ `192.168.2.176`. Это **нормально**, не «посторонний клиент». Но `netstat` внутри t610 показывает реального клиента — `172.30.33.0` = контейнер HA Core.
> 📌 **Различие коннектов:** успешный коннект HA держит соединение (без `conn_close`); при шторме — мгновенные `close`. Но и при здоровой работе HA может открывать/закрывать по опросу — судить по НАЛИЧИЮ `Time_WAIT`-пачек и загрузке `maxconn`, не по одному `conn_close`.
**Причина 2 — 🔴 Регистры отвечают НЕСТАБИЛЬНО (не таймаут).**
Прямой опрос 2026-09-14 (поздняя), `nc` + `printf`-запрос:
```
slave 11 reg 5 -> … 0b 83 0b ← ❌ EXC 0x0B (GATEWAY TARGET DEVICE FAILED TO RESPOND)
slave 11 reg 7 -> … 03 02 640001 ← ✅ OK
slave 11 reg 8 -> … 0b 83 0b ← ❌ EXC 0x0B
slave 12 reg 1 -> … 0c 83 0b ← ❌ EXC 0x0B
slave 12 reg 5 -> … 03 02 640001 ← ✅ OK
```
**Каждый раз отвечают РАЗНЫЕ регистры** (в прошлый замер 2026-09-14 было наоборот: reg 5 ✅, reg 8 ❌). Устройство на шине отвечает через раз → это **аппаратная/шинная нестабильность реле-модулей**, а не конфиг.
> ⚠️ Формат сырого запроса (`printf '\x…'` + `nc`): MBAP `00 01 00 00 00 06 <slave> 03 <addr_hi> <addr_lo> 00 01`.
> ⚠️ Питфолл bash: `printf '\\x…'` внутри скрипта, отправляемого через `scp` + `bash` — экранирование `\x` **удваивается** при передаче в одинарных кавычках. Проверять вывод `xxd -p`, а не доверять «красивой» команде из доки.
**Причина 3 — 🔴 `verify` в `configuration.yaml` физически не может сойтись.**
Конфиг (строки 96–…, `configuration.yaml`):
```yaml
switches:
- name: intake_damper_dining_right_0
unique_id: intake_damper_dining_right_0
slave: 11
address: 5
write_type: holding
command_on: 256
command_off: 512
verify:
input_type: holding
address: 5
state_on: 1 # ⚠️ HA ждёт ровно 1
state_off: 0 # ⚠️ HA ждёт ровно 0
```
HA **пишет** 256/512 в reg 5, а **читает** из того же reg 5 и ждёт `1`/`0`. Но в живом регистре лежит `0x640001` (не `1`), либо приходит `EXC 0x0B`.
**Следствие: `verify` не сходится НИКОГДА → сущность `unavailable` навсегда, даже при живом регистре.**
> 📌 Untested fix-варианты: ① убрать `verify` вовсе (доверять команде, не перечитывать); ② выставить `state_on`/`state_off` под реальное значение регистра; ③ `verify` только по «изменилось/нет» без сверки с 1/0. Выбирать после стабилизации шины (причина 1+2).
**Состояние блока `configuration.yaml` (строки 16+):**
- `modbus:``rtu_bus`, `type: tcp`, `host: 192.168.2.176`, `port: 502`.
- **`sensors:` — ВСЁ закомментировано** (slave 10 AT2 fans ×4 + `temp_3`/slave 102).
- **`switches:` — активны только заслонки slave 11** (адреса 5,7,8,9,11,12,13,14,…); **весь блок slave 10 (Fan 3 High/Medium/Low) закомментирован** строкой `# slave 10 (AT2 fans) not responding on vent bus — commented out to unblock damper polling`.
> ⚠️ **Активных `sensors:` в `modbus:` НЕТ ВООБЩЕ.** Значит сущности `sensor.fan_at2_*` в реестре — «сироты» от старого конфига/другого источника, они не могут получить данные по определению. Проверить их происхождение (возможно, template-сенсоры или остатки `modbus.sensor`).
**Опции `local_mbusd` (на момент диагностики):**
```json
{"device":"/dev/serial/by-path/pci-0000:00:12.0-usb-0:4:1.0-port0","speed":9600,"mode":"8n1",
"trx_control":"addc","maxconn":8,"wait":500,"pause":100,"retries":3,"timeout":1000}
```
**ПЛАН ФИКСА (согласован, НЕ применён — ждём ОК Alex):**
| # | Правка | Зачем | Риск |
|---|---|---|---|
| 1 | `timeout 1000 → 3000`, `retries 3 → 1` | Дать медленным реле ответить (причина 2) | обратимый |
| 2 | `maxconn 8 → 16` | Снять лимит при ~28 параллельных опросах (причина 1) | обратимый |
| 3 | Разобраться с `verify` (убрать/смягчить) | Иначе `unavailable` не уйдёт даже при живом регистре (причина 3) | требует проверки на живом |
**Порядок:** ① бэкап текущих опций аддона → ② правка `timeout`/`retries`/`maxconn` через Supervisor API (§8) → ③ `ha apps restart local_mbusd` → ④ проверка ухода `unavailable` → ⑤ только потом трогать `verify`.
> ⚠️ Правки в `/config/configuration.yaml` — через **HA stop → правка → HA start** (или `ha core check` перед стартом). Бэкап обязателен.
### ZONT (шина на CH340 #1) ### ZONT (шина на CH340 #1)
ZONT (`192.168.0.10`) — Modbus master на RS-485 `ttyZONT`. 485-датчики: Гостиная=1, Детская=2, Спальня=3. `modbus-bridge` сниффит их и подставляет виртуальные slaves 101/102/103 → ZONT опрашивает как внешние датчики. **Если modbus-bridge не слушает шину → ZONT показывает датчики «недоступными».** ZONT (`192.168.0.10`) — Modbus master на RS-485 `ttyZONT`. 485-датчики: Гостиная=1, Детская=2, Спальня=3. `modbus-bridge` сниффит их и подставляет виртуальные slaves 101/102/103 → ZONT опрашивает как внешние датчики. **Если modbus-bridge не слушает шину → ZONT показывает датчики «недоступными».**
@@ -569,7 +640,7 @@ curl -s -X POST -H "$HDR" -H "Content-Type: application/json" \
| # | Задача | Кто | | # | Задача | Кто |
|---|---|---| |---|---|---|
| 1 | **Разобраться с 44 `unavailable`** (заслонки + вентиляторы). Шина живая → смотреть лог HA `homeassistant.components.modbus`. Попробовать поднять `timeout` mbusd (1000 → 3000 мс), `retries` 3 → 1 | я | | 1 | **Фикс 44 `unavailable`** — диагноз готов (§5 «ТРИ реальные причины»). План: ① `mbusd timeout 1000→3000`, `retries 3→1`; ② `maxconn 8→16`; ③ разобраться с `verify` (не сходится с `0x640001`). Правки опций — через Supervisor API, бэкап до. **Ждёт ОК Alex** | я |
| 2 | Раскомментировать slave 10 (AT2 fans) в `configuration.yaml` — был закомментирован «not responding on bus» | я | | 2 | Раскомментировать slave 10 (AT2 fans) в `configuration.yaml` — был закомментирован «not responding on bus» | я |
| 3 | `sensor.*_summary` — добавить `\|default(0)` в template-сенсоры (косметика, самоизлечится) | — | | 3 | `sensor.*_summary` — добавить `\|default(0)` в template-сенсоры (косметика, самоизлечится) | — |
| 4 | **Камера** — найти образ/папку, поднять на t610, поправить upstream в Caddy | отдельно | | 4 | **Камера** — найти образ/папку, поднять на t610, поправить upstream в Caddy | отдельно |
@@ -599,6 +670,8 @@ docker restart caddy
## 10. Рабочие файлы и скрипты ## 10. Рабочие файлы и скрипты
**На Mac:** `~/tmp-t610/` — `stage3/out/`, `backups/`, `ha_token.txt`, `addons/`, `etap3-fix/` (`automations.fixed.yaml`, `set_all_areas.jq`, `devid_mapping.json`, `mbtest.sh`), скрипты `*.sh`/`*.py`. **На Mac:** `~/tmp-t610/` — `stage3/out/`, `backups/`, `ha_token.txt`, `addons/`, `etap3-fix/` (`automations.fixed.yaml`, `set_all_areas.jq`, `devid_mapping.json`, `mbtest.sh`), скрипты `*.sh`/`*.py`.
**Диагностика modbus (2026-09-14 поздняя, только чтение):** `~/tmp-t610/mbdiag1.sh` … `mbdiag4.sh` — снятие опций аддонов, блока `modbus:` из `configuration.yaml`, лога mbusd, лога HA по modbus, прямого опроса регистров. Запуск: `scp -i ~/.ssh/id_rsa <script> root@192.168.2.176:/tmp/ && ssh -i ~/.ssh/id_rsa root@192.168.2.176 'bash /tmp/<script>'`.
> ⚠️ Секреты в скриптах — только через обходных путей из §8 (маскировщик ломает литералы). Здесь секретов нет, использовался готовый `/tmp/curl.auth`.
**На t610:** `/config/automations.yaml.bak-*` (последний: `.bak-20260914-131541` — перед снятием `not_from`), `/config/configuration.yaml.bak-http-*`, `/config/.storage/http.bak-*`, `/addons/modbus-bridge/*.bak-hex-*`. **На t610:** `/config/automations.yaml.bak-*` (последний: `.bak-20260914-131541` — перед снятием `not_from`), `/config/configuration.yaml.bak-http-*`, `/config/.storage/http.bak-*`, `/addons/modbus-bridge/*.bak-hex-*`.
**Бэкапы (Mac):** `~/tmp-t610/backups/config-t610-20260914-115034.tar.gz`, `truenas-ha-backup-20260913-215043.tar.gz`. **Бэкапы (Mac):** `~/tmp-t610/backups/config-t610-20260914-115034.tar.gz`, `truenas-ha-backup-20260913-215043.tar.gz`.
@@ -619,6 +692,19 @@ docker restart caddy
> 📌 **Причина такой свалки (вывод на будущее):** каждая сессия дописывала блок «добавлено в конце» **сверху**, не убирая устаревшее и не сводя дубли. Итог — противоречивые утверждения в одном файле. **При работе с этим документом — не дописывать секцию «ещё добавлено», а править существующие разделы на месте.** > 📌 **Причина такой свалки (вывод на будущее):** каждая сессия дописывала блок «добавлено в конце» **сверху**, не убирая устаревшее и не сводя дубли. Итог — противоречивые утверждения в одном файле. **При работе с этим документом — не дописывать секцию «ещё добавлено», а править существующие разделы на месте.**
### 2026-09-14 (сессия диагностики Modbus, только чтение)
**Найдены три реальные причины 44 `unavailable`** (см. §5). Прежняя формулировка «шина РАБОТАЕТ → дело в таймауте» **уточнена**: шина живая, но нестабильная, плюс два конфигурационных слоя.
1. **Шторм ~28 параллельных TCP-коннектов** HA (`ModbusBaseEntity.async_local_update`) при `mbusd maxconn: 8` → отвал по таймауту. Доказательство: `netstat` → `TIME_WAIT` c `172.30.33.0` (контейнер HA Core).
2. **Регистры отвечают через раз** (`EXC 0x0B`) — в этот замер reg 7/12:5 ✅, а reg 11:5/11:8/12:1 ❌ (в прошлый замер наоборот). Не таймаут — шинная нестабильность.
3. **`verify` не может сойтись**: HA пишет 256/512, читает тот же регистр и ждёт `1`/`0`, а в живом лежит `0x640001`.
**Ключевое открытие про конфиг:** в `configuration.yaml` **все `sensors:` закомментированы**, `switches:` активны только для slave 11. `sensor.fan_at2_*` — сироты.
**Ничего не менялось** (только чтение: опции аддонов, конфиг, логи, прямой опрос). План фикса ждёт ОК Alex.
> ⚠️ **Питфолл записи в этот документ:** правка через `mcp_obsidian_patch_note` с большим `newString` **портит документ** (вставляла контент в середину строки, тело размножилось 4×, 21KB→50KB). Для крупных вставок — читать целиком и `write_file`/локальный `patch`. Мелкие точечные правки с уникальным контекстом — можно patch.
--- ---
## Связанные заметки ## Связанные заметки