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

This commit is contained in:
Alexey Martemyanov
2026-09-14 15:44:04 +06:00
parent 865cac30c0
commit 823c2e76c4
+108 -14
View File
@@ -9,7 +9,7 @@
## 1. Состояние на 2026-09-14 (актуализировано 15:33 → дополнено вечерней сессией)
**Этап 1 ✅ · Этап 2 ✅ · Этап 3 ✅ ЗАКРЫТ.** Этап 4 — **ПОЧТИ ЗАКРЫТ (3 из 4):** ① Caddy → t610 (`mallexxx.duckdns.org` → HA на t610, HTTP 200); ② Node-RED flows перенесены, `Connected to HA`, наружу не выпущен (решение Alex «оставляем так», доступ через ingress); ③ **ZONT MQTT-редирект переключён на t610** (DNAT на роутере `.197``.176`, живой поток идёт). **ОСТАЛОСЬ:** погасить/отключить сервисы TrueNAS (заблокировано — Caddy на TrueNAS держит точку входа) + хвосты (static IP, бэкап).
**Последняя верификация: 2026-09-14 (вечер-3) — Caddyfile залит, `trusted_proxies` исправлен, `mallexxx.duckdns.org` → HTTP 200; Node-RED: 68 узлов перенесены, `Connected to http://supervisor/core`, лог без ошибок; разгадана причина `dining_*` — **маршрут MQTT** (ZONT пишет в брокер TrueNAS, а не t610), железо исправно.** Детали — §5-кватер-Б, -В, -Г, -Д.
**Последняя верификация: 2026-09-14 (вечер-3, финал) — Caddyfile залит, `trusted_proxies` исправлен, `mallexxx.duckdns.org` → HTTP 200; Node-RED: 68 узлов перенесены, `Connected to http://supervisor/core`, ошибок 0; DNAT MQTT переключён на t610 (ZONT → mosquitto t610, живой поток); по гостиной установлено фактом: **ответ на шине ЕСТЬ, bridge его не публикует** (см. §5-кватер-Д и новый раздел «Архитектура modbus-bridge»).** Детали — §5, §5-кватер-Б/-В/-Г/-Д.
| Что | Факт |
|---|---|
@@ -47,7 +47,7 @@
| ~~`verify` как причина~~ | **ОПРОВЕРГНУТО как причина:** при верной привязке шин заслонки ожили при том же `verify` (`state_on:1`/`state_off:0`). Причина была в перепутанных шинах, не в `verify` |
| **`sensor.fan_at2_*` / `switch.fan_3_*` — сироты** | В `configuration.yaml` **весь блок slave 10 (AT2 fans) закомментирован** → эти сущности физически не могут получать данные. Не задача — просто известный факт состояния конфига |
| `switch.sauna` = `unknown` | Розетка физически отключена (`lastSeen` 8+ ч) — не баг |
| ZONT не перенаправлен | **✅ ПЕРЕНАПРАВЛЕН 2026-09-14 (вечер-3, §5-кватер-Д):** DNAT на роутере `192.168.2.2` (`redirect[0]` MQTT + `rule[3]`) переключён `.197``.176`; ZONT пишет в mosquitto **t610** (живой поток kids/bedroom). ⚠️ Не решено: `dining/*` не публикуется ZONT'ом вообще — вопрос ZONT-стороны |
| ZONT не перенаправлен | **✅ ПЕРЕНАПРАВЛЕН 2026-09-14 (вечер-3, §5-кватер-Д):** DNAT на роутере `192.168.2.2` (`redirect[0]` MQTT + `rule[3]`) переключён `.197``.176`; ZONT пишет в mosquitto **t610** (живой поток kids/bedroom). ⚠️ Не решено: `dining/*` не публикуется **ответ на шине есть, bridge его не отдаёт** (§5-кватер-Д, задача №3) |
| Камера | Контейнер был на TrueNAS, остановлен при восстановлении пула. В Caddy остался мёртвый upstream `192.168.2.197:8090`. Разбираться отдельно |
---
@@ -587,6 +587,71 @@ ch341 1-4 → ttyUSB1 (гнездо 4 = вентиляция) ← ОШИБК
**Это опровергнуто физическим тестом Alex'а** (см. выше): ZONT = гнездо 4.
> 🔴 **УРОК ДИАГНОСТИКИ:** соответствие «гнездо ↔ устройство» **НЕЛЬЗЯ вывести из `dmesg`/`by-path`** — там видно только, что CH340 воткнут в порт 3 и порт 4, но **не видно, какой кабель к какому прибору идёт**. Единственный надёжный способ — **физический тест: выдернуть шнур и посмотреть, какое гнездо отвалилось в `dmesg`.** Это то, что Alex сделал за минуту там, где агент полчаса строил теории.
### 🔧 АРХИТЕКТУРА `modbus-bridge` (важно для диагностики любых датчиков 485)
**`modbus-bridge` — НЕ простой сниффер, а двусторонний ВИРТУАЛЬНЫЙ SLAVE-ПРОКСИ.**
| Направление | Что делает |
|---|---|
| **Чтение (снифф)** | Слушает шину ZONT 485, ловит обмены ZONT ↔ реальные датчики (`slave 2` детская, `slave 3` спальня, `slave 1` гостиная), распаковывает по `sniff:`-конфигу и **публикует в MQTT** `modbus/sensors/<room>/<param>` |
| **Запись (эмуляция)** | Сам **притворяется датчиками** под виртуальными адресами **100103** (100 = proxy на HA-сенсор, 101 = Гостиная, 102 = Детская, 103 = Спальня) и отдаёт ZONT'у значения, когда тот их спрашивает |
**Файлы:**
- `/addons/modbus-bridge/data/config.template.tmpl` — шаблон конфига (**в образ копируется при сборке**! см. питфолл ниже)
- `/addons/modbus-bridge/modbus_ha_bridge.py` — код
- `/addons/modbus-bridge/run.sh` — генерит `/app/config.yml` из шаблона + опций, переопределяя `serial.port`, `ha.url` (`http://192.168.2.176:80`), `mqtt.broker` (`core-mosquitto`)
- Эталон TrueNAS: `/mnt/RED_2TB/docker/modbus-bridge/config.yml` (читается без sudo)
> 🔴 **ПИТФОЛЛ СБОРКИ:** `Dockerfile` содержит `COPY data/config.template.tmpl /app/config.template.yml` — шаблон впекается в образ **при сборке**. **Правка `data/*.tmpl` НЕ применяется без `ha addons rebuild local_modbus-bridge`.** Даже если на диске шаблон правильный, работающий контейнер может использовать старую версию из образа. **Проверка:** сравнить `data/config.template.tmpl` с датой сборки образа; при сомнении — rebuild.
**Формат `sniff`-блока (эталон, гостиная):**
```yaml
sniff:
- slave_id: 1
function: 3
base_register: 2
quantity: 7
device_name: "Dining Sensor"
fields:
dining_co2: { offset: 0, type: uint16 }
dining_formaldehyde: { offset: 1, type: uint16, divider: 10 }
dining_tvoc: { offset: 2, type: uint16 }
dining_pm2_5: { offset: 3, type: uint16, divider: 10 }
dining_pm10: { offset: 4, type: uint16, divider: 10 }
dining_temperature: { offset: 5, type: int16, divider: 10, correction_offset: -1 }
dining_humidity: { offset: 6, type: uint16, divider: 10, precision: 1 }
```
Плюс `mappings:` — блоки, описывающие виртуальных slave 100103 (`source: sniff` / `source: ha`).
**Тайминги (в шаблоне):** `serial.timeout: 0.05` (50 мс), `rts_de: true`.
### 🔬 МЕТОД: «датчик молчит» vs «bridge не публикует»
Единственный надёжный способ различить — **прослушать шину сырьём при остановленном bridge**:
```bash
# 1. Остановить bridge (освобождает serial-порт)
ha apps stop local_modbus-bridge
# 2. Настроить порт и читать hex (by-path, гнездо ZONT = :4)
DEV=/dev/serial/by-path/pci-0000:00:12.0-usb-0:4:1.0-port0
stty -F $DEV 9600 cs8 -cstopb -parenb -echo raw
timeout 60 cat $DEV | xxd -p
# 3. В выводе искать пару «запрос → ответ».
# Пример ГОСТИНОЙ (работает):
# 01 03 00 02 00 07 a5 c8 ← запрос: slave 1, reg 2, 7 regs
# 01 03 0e 03 1a 00 0e 00 19 00 0b 00 0d 01 00 01 d2 4e c4 ← ответ: 0x0E=14 байт данных
# 4. Вернуть bridge
ha apps start local_modbus-bridge # подождать ~12 с, проверить state=started
```
**Как читать:** MBAP/RTU-ответ = `<slave> <func> <byte_count> <data…> <CRC2>`. Наличие ответа = **датчик жив**. Если bridge при этом публикует `0` — проблема **в bridge** (приём кадра/распаковка), а не в датчике.
> ⚠️ `cat /dev/ttyUSB*` при **работающем** bridge = 0 байт (bridge держит порт). Слушать только при остановленном bridge.
> ⚠️ Не мерить шину, пока HA/mbusd её опрашивают — иначе артефакт (см. выше про 76% `EXC 0x0B`).
---
## 5-тер. 🔬 Датчики столовой: `dining_summary` `unavailable` — причина НЕ в формуле (2026-09-14 15:39)
@@ -941,16 +1006,42 @@ Slave: 100 (виртуальный) → ha:sensor.office_temperature_sensor_temp
**🔑 Ключевое открытие: `modbus-bridge` работает как ВИРТУАЛЬНЫЙ SLAVE-ПРОКСИ.**
- Реальные датчики 485 опрашиваются ZONT'ом под **slave 2 (детская)**, **slave 3 (спальня)** — bridge **снифит** эти обмены и запоминает значения.
- Затем bridge **отдаёт ZONT'у те же значения под виртуальными адресами 101/102/103** (Гостиная=101, Детская=102, Спальня=103) — ZONT читает их как «внешние датчики».
- **Гостиная (101) отдаётся bridge'ем как `0`** — потому что bridge **ни разу не снифил** реальный датчик гостиной (`Sniff:` в логе содержит только `kids_*` и `bedroom_*`).
- У `slave 1` ZONT читает **7 регистров с адреса 2** — это **внутренние параметры** прибора, НЕ датчик. То есть **реальный датчик гостиной ZONT не опрашивает** (или опрашивает под адресом, который bridge не распознаёт).
- **Гостиная (101) отдаётся bridge'ем как `0`** — `→ Response: 1 register(s) from 100 (sniff:dining_temperature) = 0 [00 00]`.
- ⚠️ **Уточнение (финал сессии):** `slave 1 / 7 регистров с адреса 2` — это **НЕ «внутренние параметры ZONT»** (как было записано первым заходом). Это **реальный датчик гостиной**, и bridge **пытается** его снифить (`sniff:` секция с `slave_id: 1, base_register: 2, quantity: 7` присутствует в конфиге).
**Вывод:** «датчик гостиной отдаёт 0» (§5-тер) — **не ZONT, не маршрут MQTT, не t610 и не сам датчик**. Причина: **bridge не имеет значения по гостиной и отдаёт дефолт `0`** → `modbus/sensors/dining/*` не публикуется (0 отбрасывается как невалидный) → `sensor.dining_*` = `unknown` → `dining_summary` падает.
**✅ ФИНАЛЬНАЯ ПРИЧИНА (2026-09-14, конец сессии) — ОТВЕТ НА ШИНЕ ЕСТЬ, bridge его не публикует:**
**Что проверять дальше (НЕ СДЕЛАНО):** конфиг/mapping `modbus-bridge` — какой реальный slave/регистр он считает «гостиной» и почему не снифит его. Смотреть `/addons/modbus-bridge/` (mapping-файл, `modbus_ha_bridge.py`). Возможно, датчик гостиной в ZONT заведён под адресом, отличным от ожидаемого bridge'ем.
Alex дал решающую наводку: **«ответ есть или нет? bridge неправильно сконфигурен или данных реально нет?»** и **«он через bridge ходит. zont запрашивает — bridge должен снифать»**. Проверено двумя способами:
1. **Bridge остановлен, шина прослушана сырьём** (`cat /dev/serial/by-path/...usb-0:4...` → `xxd -p`). Поймано:
```
01 03 00 02 00 07 a5 c8 ← ЗАПРОС ZONT: slave 1, reg 2, 7 регистров
01 03 0e 03 1a 00 0e 00 19 00 0b 00 0d 01 00 01 d2 4e c4 ← ОТВЕТ: 0x0E = 14 байт данных
```
Значения из ответа: `794, 14, 25, 11, 13, 256, 1` → это CO2=794, формальдегид=1.4, TVOC=25, PM2.5=1.1, PM10=1.3, температура (int16, /10, −1), влажность.
**🔴 ВЫВОД: датчик гостиной ИСПРАВЕН и ОТВЕЧАЕТ на шине. Данные есть.**
2. **Конфиг bridge на t610 проверен — `dining` ТАМ ЕСТЬ.** `/addons/modbus-bridge/data/config.template.tmpl`, шаблон 3934 б: `slave_id: 1`, `base_register: 2`, `quantity: 7`, `device_name: "Dining Sensor"`, поля `dining_co2/formaldehyde/tvoc/pm2_5/pm10/temperature/humidity` с `offset` 0…6, `divider: 10`, `correction_offset: -1` у температуры. **Конфиг идентичен эталону TrueNAS** (`/mnt/RED_2TB/docker/modbus-bridge/config.yml` — тот же блок sniff с теми же offset/divider).
**🔴 ГДЕ ЛОМАЕТСЯ (гипотеза, требует проверки кодом):** bridge **знает** про `dining_temperature` (в логе есть `sniff:dining_temperature`), но получает **0**. При этом сырой ответ 14 байт на шине **есть**. Значит bridge **не доводит приём ответа slave 1 до конца**:
- ответ гостиной — **14 байт** (`0x0E`), у работающих датчиков — **12 байт** (`0x0C`). Разная длина.
- в `modbus_ha_bridge.py` приём: `buf += ser.read(ser.in_waiting or 1)` (стр. ~675) — читает «сколько успело лечь в буфер»; на длинном кадре может прочитать **часть** → CRC не сходится → ответ молча отбрасывается → значение = 0.
- усугубляет узкий `serial.timeout: 0.05` (50 мс) в шаблоне.
**Это особенность реализации bridge, а не железо, не конфиг, не ZONT и не маршрут.**
**⚠️ Отменяет прежние записи:** «ZONT не опрашивает гостиную» ❌, «ZONT сам перестал публиковать» ❌, «внутренние параметры ZONT на slave 1» ❌, «bridge ни разу не снифил гостиную» ❌ — **всё опровергнуто** прямым прослушиванием шины и чтением конфига.
**Что делать дальше (НЕ СДЕЛАНО):** смотреть `modbus_ha_bridge.py` — механизм чтения кадра (строки ~660–700, `ser.read(ser.in_waiting)`) и починку: читать ответ **до конца кадра по межбайтовой паузе**, а не по `in_waiting`. Плюс проверить, почему `dining_temperature` в логе = 0, а не «нет данных» (bridge подставляет дефолт вместо пропуска).
> ⚠️ **Питфолл диагностики (важный):** bridge — **виртуальный slave-прокси**, а не просто «сниффер, который публикует в MQTT». Он **двусторонний**: снифит реальные обмены ZONT↔датчики И притворяется датчиками под адресами 100–103 для ZONT. Поэтому «в MQTT нет dining» может означать не «ZONT не спрашивает», а «bridge не имеет значения и отдаёт 0». Смотреть лог bridge целиком (запросы **и** ответы), а не только MQTT-топики.
> ⚠️ **Прежний ошибочный вывод (опровергнут):** «ZONT сам перестал публиковать столовую, вопрос ZONT-стороны» — **НЕВЕРНО.** На TrueNAS `dining/*` был **retained** от *того же* bridge, который раньше стоял на TrueNAS и имел значение по гостиной. После переезда на t610 bridge потерял это значение (не снифит) и стал отдавать 0.
> ⚠️ **Цепочка опровергнутых версий (не повторять):**
> 1. «ZONT не опрашивает гостиную» — ❌ опровергнуто (запрос `01 03 00 02 00 07` идёт каждые ~5 с).
> 2. «Датчик отвечает нулём» (§5-тер) — ❌ опровергнуто (ответ `01 03 0E 03 1A …` с данными CO2=794 и т.д. **есть на шине**).
> 3. «ZONT сам перестал публиковать, вопрос ZONT-стороны» — ❌ опровергнуто (на TrueNAS `dining/*` — retained **от того же bridge**, который там работал и имел значение).
> 4. «slave 1 / reg 2 — внутренние параметры ZONT, не датчик» — ❌ опровергнуто (это и есть датчик гостиной, он в sniff-конфиге bridge).
> **Действующая версия:** ответ приходит с шины, bridge его не публикует (приём кадра не доводится до конца на 14 байтах) — см. выше.
**Откат:** `uci set firewall.@redirect[0].dest_ip='192.168.2.197'; uci set firewall.@rule[3].dest_ip='192.168.2.197'; uci commit firewall; /etc/init.d/firewall reload`
@@ -1294,7 +1385,7 @@ curl -s -X POST -H "$HDR" -H "Content-Type: application/json" \
| ~~1b~~ | ~~`verify` в заслонках~~ — **✅ ОПРОВЕРГНУТО:** заслонки ожили при том же `verify`. Не причина. | — |
| ~~1c~~ | ~~ZONT-шина на других гнёздах~~ — **✅ СДЕЛАНО:** физический тест доказал (ZONT = гнездо 4, вентиляция = гнездо 3), привязки аддонов обменяны, док приведён в соответствие. Осталась только проверка «① подтвердить у Alex» — **закрыто**: он сам это и тестировал. | — |
| ~~2~~ | ~~Раскомментировать slave 10 (AT2 fans)~~ — **СНЯТО с плана (Alex 2026-09-14): задачи по slave 10 НЕ БЫЛО.** | — |
| 3 | **`sensor.dining_summary` / `dining_air_summary`** — **🔬 ПРИЧИНА НАЙДЕНА (2026-09-14 финал, см. §5-кватер-Д): это BRIDGE, не ZONT, не маршрут и не датчик.** `modbus-bridge` — **виртуальный slave-прокси**: снифит реальные датчики ZONT (slave 2 детская, slave 3 спальня) и **отдаёт их ZONT'у под виртуальными адресами 101/102/103** (Гостиная=101, Детская=102, Спальня=103). По гостиной bridge **ни разу не снифил** реальный датчик (`Sniff:` только `kids_*`/`bedroom_*`) → отдаёт ZONT'у **`0`** → в MQTT `dining/*` не публикуется (0 невалиден) → `sensor.dining_*` = `unknown` → сводка падает. **Прежние версии** («`\|default(0)`», «ZONT отвечает 0», «ZONT не публикует») — **опровергнуты/уточнены**. **Что делать:** смотреть mapping/bridge (`/addons/modbus-bridge/`, `modbus_ha_bridge.py`) — какой реальный slave/регистр он считает «гостиной» и почему не снифит. **НЕ ДОДЕЛАНО** (Alex переключил на план миграции) | отдельно |
| 3 | **`sensor.dining_summary` / `dining_air_summary`** — ** ПРИЧИНА УСТАНОВЛЕНА ФАКТАМИ (2026-09-14, финал, §5-кватер-Д): ДАННЫЕ НА ШИНЕ ЕСТЬ, bridge их не публикует.** Прямое прослушивание шины (bridge остановлен): запрос `01 03 00 02 00 07` → **ответ `01 03 0E 03 1A …` (14 байт, CO2=794 и т.д.)**. Конфиг `modbus-bridge` **содержит** блок `slave_id: 1 / base_register: 2 / quantity: 7 / Dining Sensor` с полями `dining_*` (идентичен эталону TrueNAS). Но bridge в логе отдаёт `sniff:dining_temperature = 0` → в MQTT `dining/*` не публикуется → `sensor.dining_*` = `unknown`. **Гипотеза:** приём кадра не доводится до конца на ответе 14 байт (`buf += ser.read(ser.in_waiting or 1)`, `timeout: 0.05`). **Что делать:** смотреть `modbus_ha_bridge.py` (чтение кадра ~стр. 660–700) и чинить приём по межбайтовой паузе. **НЕ ДОДЕЛАНО** | отдельно |
| 4 | **Камера** — найти образ/папку, поднять на t610, поправить upstream в Caddy | отдельно |
| ~~5~~ | ~~**Этап 4:** Caddy upstream → t610~~ — **✅ ЧАСТЬ «CADDY» ЗАКРЫТА 2026-09-14 (см. §5-кватер-Б/В):** Caddyfile залит (Alex подменил файл + `restart caddy`), `mallexxx.duckdns.org` → **HTTP 200** (HA на t610, подтверждено Alex'ом), попутно исправлен `trusted_proxies` в `.storage/http` (§5-кватер-В-1). **ОСТАЛОСЬ из Этапа 4:** ① `nodered.*` — починить порт (см. задачу 5-нр ниже); ② GPON-редирект → t610; ③ ZONT MQTT → t610. **🔴 Архитектурный вывод: Caddy НЕ переносить** (17 из 20 доменов — сервисы TrueNAS) | — |
| 5-нр | **Node-RED: flows перенесены с TrueNAS → t610, РАБОТАЕТ.** ~~Alex выбрал «выставить порт наружу»~~ → через API не удалось (`host_network` снимается только в UI, маппинг при нём игнорируется). **Alex: «ок. оставляем так»** — наружу НЕ выпущен, доступ через ingress. 68 узлов, `[server:Home Assistant] Connected to http://supervisor/core`, ошибок 0. Ключевая правка: узел `server` `addon: false` → `true`. Подробно — **§5-кватер-Г**. Остаётся на будущее (если понадобится домен): снять `host_network` в UI + Caddy → `:11880` | ✅ сделано |
@@ -1549,15 +1640,18 @@ docker restart caddy
|---|---|
| ✅ DNAT MQTT → t610 | На роутере `192.168.2.2`: `firewall.@redirect[0].dest_ip` и `firewall.@rule[3].dest_ip` `.197` → **`.176`**, `uci commit` + `/etc/init.d/firewall reload`. Бэкап `/root/firewall.bak-20260914-092555`. ZONT **пошёл в mosquitto t610** (живой поток kids/bedroom, проверено подпиской) |
| 🔑 Адрес ZONT найден | **`192.168.0.50`** (MAC `f8:b3:b7:d8:46:f3`), Web UI «ZONT LOCAL» на :80, доступен **только с роутера**. `192.168.0.10` — это wan роутера, не ZONT |
| 🔑 Механика bridge | **`modbus-bridge` = виртуальный slave-прокси** (не просто сниффер): снифит реальные датчики (slave 2 детская, slave 3 спальня) и **отдаёт их ZONT'у под адресами 101/102/103**. По гостиной (101) сниффа нет → отдаёт **`0`** |
| 🔴 Причина гостиной | **Не ZONT, не маршрут, не датчик — BRIDGE.** Он не имеет значения по гостиной и отдаёт дефолт `0`; 0 не публикуется в MQTT → `sensor.dining_*` = `unknown` → сводка падает |
| ❌ Ошибочный промежуточный вывод | «ZONT сам перестал публиковать столовую» — **опровергнут**. Retained на TrueNAS шёл от *того же* bridge, пока он стоял там |
| 🔑 Механика bridge | **`modbus-bridge` = виртуальный slave-прокси** (не просто сниффер): снифит реальные датчики (slave 2 детская, slave 3 спальня) и **отдаёт их ZONT'у под адресами 101/102/103** (Гостиная=101, Детская=102, Спальня=103) |
| 🔴 Причина гостиной (ФАКТ) | **Ответ на шине ЕСТЬ.** Bridge остановлен → сырое прослушивание `...usb-0:4...` поймало: запрос `01 03 00 02 00 07` → **ответ `01 03 0E 03 1A …`** (14 байт: CO2=794 и т.д.). Конфиг bridge **содержит** Dining-блок (`slave_id: 1, base_register: 2, quantity: 7`, поля `dining_*`, идентичен TrueNAS). Но bridge в логе отдаёт `sniff:dining_temperature = 0` → MQTT `dining/*` пусто → `sensor.dining_*` = `unknown` |
| 🔴 Гипотеза поломки | Приём кадра не доводится до конца на ответе **14 байт** (`buf += ser.read(ser.in_waiting or 1)` в `modbus_ha_bridge.py` ~стр. 675, `serial.timeout: 0.05`) → CRC не сходится → ответ молча отбрасывается. **Требует проверки кодом** |
| ❌ Ошибочные промежуточные выводы | «ZONT не опрашивает гостиную», «датчик отвечает 0», «ZONT сам перестал публиковать», «slave 1 = внутренние параметры» — **все опровергнуты** прямым прослушиванием шины и чтением конфига |
**Что менялось в железе:** DNAT-правила на роутере `192.168.2.2` (2 правила). Больше ничего.
**Что менялось в железе:** DNAT-правила на роутере `192.168.2.2` (2 правила); bridge **останавливался на ~60 с** для сырого прослушивания шины и **возвращён в работу** (`started`, публикует kids/bedroom).
**Не сделано (задача №3 «отдельно»):** разбор mapping `modbus-bridge` — почему не снифит гостиную. **НЕ ДЕЛАЛОСЬ** — Alex переключил внимание на план миграции.
**Не сделано (задача №3 «отдельно»):** починка приёма кадра в `modbus_ha_bridge.py` (читать ответ до конца кадра по межбайтовой паузе, а не по `in_waiting`). **НЕ ДЕЛАЛОСЬ.**
**Процессный урок:** агент снова «расползся» — вместо выполнения прямой команды Alex («перенастрой роутер») начал исследовать MQTT-топики, ARP, веб-UI ZONT. **Правило: получил команду — выполняй, не исследуй попутно.** Отдельно: когда Alex говорит «ZONT запрашивает — bridge должен снифать» — это **готовая гипотеза от человека, знающего физику**, проверять её первой, а не строить свою.
**Процессный урок:** агент снова «расползся» — вместо выполнения прямой команды Alex («перенастрой роутер») начал исследовать MQTT-топики, ARP, веб-UI ZONT. **Правило: получил команду — выполняй, не исследуй попутно.** Отдельно: когда Alex говорит «ZONT запрашивает — bridge должен снифать» — это **готовая гипотеза от человека, знающего физику**, проверять её первой, а не строить свою. И ещё: **не объявлять причину, пока не проверен весь путь запрос→ответ на шине** — три версии подряд оказались ложными.
**Полезный метод диагностики Modbus-шины (запомнить):** остановить bridge → `cat /dev/serial/by-path/<by-path> | xxd -p` → искать в hex пару «запрос + ответ». Это **единственный способ** отличить «датчик молчит» от «bridge не публикует». `stty -F <dev> 9600 cs8 -cstopb -parenb -echo raw` перед чтением.
---