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

This commit is contained in:
Alexey Martemyanov
2026-09-14 15:54:14 +06:00
parent 74c9995589
commit f801e469e8
2 changed files with 182 additions and 14 deletions
+54 -9
View File
@@ -1,27 +1,72 @@
---
title: Gitea Config
created: '2026-05-23'
updated: '2026-05-23'
updated: '2026-09-14'
type: tech
namespace: personal
namespace: family
tags: [infra, gitea, git, credentials]
related:
- "[[tech/kraken-network]]"
- "[[family/plans/t610-home-automation]]"
- "[[family/how-to/truenas-access]]"
---
# Gitea Config
**URL:** `http://git.mallexxx.duckdns.org` (external) / `http://172.16.3.4:3000` (internal LAN)
**User:** `git_admin`
**URL (внешний):** `https://git.mallexxx.duckdns.org` **HTTPS**, DNS `90.189.160.148`
**Версия:** 1.27.3 (проверено 2026-09-14: `GET /api/v1/version`)
**Внутренний LAN endpoint:** `http://172.16.3.4:3000`
**User:** `git_admin` (id 1, **is_admin: true**)
## API Token
Token name: `eagle-reflect`
Token value: `f2e679a571f4c599d64b384a711043f995fd8844`
Token value: `f2e6...8844` (полное значение — в remote репо `nolvu-landing` на Mac)
Used by Eagle for repo operations (push, PR creation, hook management).
**Использование (проверено):**
```bash
TOKEN=$(git -C ~/Developer/nolvu-landing remote get-url origin | sed -E 's|https://git_admin:([^@]+)@.*|\1|')
curl -sS -H "Authorization: token $TOKEN" https://git.mallexxx.duckdns.org/api/v1/user # → кто я
curl -sS -H "Authorization: token $TOKEN" 'https://git.mallexxx.duckdns.org/api/v1/user/repos?limit=50'
```
> ⚠️ **В inline-команде Hermes эта конструкция ломается** (см. «Питфоллы») — писать скрипт файлом.
## Создание приватного репо через API (эталон 2026-09-14)
```bash
curl -sS -X POST -H "Authorization: token $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"<REPO>","private":true,"auto_init":false,"default_branch":"main"}' \
'https://git.mallexxx.duckdns.org/api/v1/user/repos' | jq '{full_name,private,clone_url}'
```
## Рабочий паттерн remote + креды (НЕ токен в URL)
```bash
git remote add origin 'https://git.mallexxx.duckdns.org/git_admin/<REPO>.git' # чистый URL!
git config credential.helper store
printf 'https://git_admin:%s@git.mallexxx.duckdns.org\n' "$TOKEN" >> ~/.git-credentials
chmod 600 ~/.git-credentials
git push -u origin main
```
Проверка: `git ls-remote origin` — SHA должен совпасть с `git rev-parse HEAD`.
## Существующие репозитории (2026-09-14)
| Репо | Видимость |
|---|---|
| `git_admin/eagle-hermes` | private |
| `git_admin/kraken-docker-config` | private |
| `git_admin/nolvu-landing` | private |
| `git_admin/obsidian-vault` | **public** (единственный) |
| `git_admin/reflect-app` | private |
| `git_admin/HA-ZONT-Modbus` | private (создан 2026-09-14, см. `[[family/plans/t610-home-automation]]` §5-кватер-Е) |
## Питфоллы
1. 🔴 **Токен в URL = утечка.** В `.git/config` репо `nolvu-landing` remote записан как `https://git_admin:<token>@git.mallexxx.duckdns.org/...` → токен светится в каждом `git remote -v` и в выводах команд/логах. **Правильно:** чистый URL + токен в `~/.git-credentials` (chmod 600) + `credential.helper=store`. Кандидат на **ротацию токена**.
2. ⚠️ **Inline shell в Hermes ломается** на `$( … | sed -E 's|…|…|' )` — приём спотыкается на `|`. **Решение: писать скрипт файлом** (`write_file``bash file.sh`), не инлайн. Пример: `~/tmp-t610/setup_gitea_creds.sh`.
3. 📌 Старый адрес `http://git.mallexxx.duckdns.org` (без `s`) — **устарел**, рабочая схема только `https://`.
## Notes
- External URL uses DuckDNS DDNS pointing to home IP
- Internal API endpoint preferred when on home network
- External URL использует DuckDNS DDNS на домашний IP.
- Внутренний API endpoint предпочтительнее, когда ты в домашней сети.
+128 -5
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`, ошибок 0; DNAT MQTT переключён на t610 (ZONT → mosquitto t610, живой поток); по гостиной установлено фактом: **ответ на шине ЕСТЬ, bridge его не публикует** (см. §5-кватер-Д и новый раздел «Архитектура modbus-bridge»).** Детали — §5, §5-кватер-Б/-В/-Г/-Д.
**Последняя верификация: 2026-09-14 (вечер-4, разбор кода + git/Gitea) — регресса нет, в железе ничего не менялось. Код `modbus_ha_bridge.py` разобран построчно: причина потери ответа гостиной локализована в **приёме кадра** (стр. 675 `ser.read(ser.in_waiting or 1)`), а лог bridge **структурно слеп** к этой ошибке (печатает только валидные кадры, стр. 692–694; битые уходят молча — стр. 687). Гипотеза «поднять таймаут» снята фактом (12-байтные ответы kids/bedroom ловятся тем же чтением). План фикса — задача A0/№3 (§9), **ждёт ОК Alex**. Также: проект-репозиторий — `~/Automation/HA-ZONT-Modbus` (не `/addons/`!), **Gitea-remote СОЗДАН (`git_admin/HA-ZONT-Modbus`, private, push `7e0b281`) — задача 3-гт ✅ ЗАКРЫТА (§5-кватер-Е).** Детали — §5-кватер-Д («РАЗБОР КОДА»), §5-кватер-Е («GITEA»), §9, §11 (журнал вечер-4).
| Что | Факт |
|---|---|
@@ -1032,7 +1032,50 @@ Alex дал решающую наводку: **«ответ есть или не
**⚠️ Отменяет прежние записи:** «ZONT не опрашивает гостиную» ❌, «ZONT сам перестал публиковать» ❌, «внутренние параметры ZONT на slave 1» ❌, «bridge ни разу не снифил гостиную» ❌ — **всё опровергнуто** прямым прослушиванием шины и чтением конфига.
**Что делать дальше (НЕ СДЕЛАНО):** смотреть `modbus_ha_bridge.py` — механизм чтения кадра (строки ~660–700, `ser.read(ser.in_waiting)`) и починку: читать ответ **до конца кадра по межбайтовой паузе**, а не по `in_waiting`. Плюс проверить, почему `dining_temperature` в логе = 0, а не «нет данных» (bridge подставляет дефолт вместо пропуска).
**⚙️ РАЗБОР КОДА `modbus_ha_bridge.py` (2026-09-14, вечер-4) — почему bridge теряет ответ и почему ЛОГ НЕ ПОКАЖЕТ ЭТУ ОШИБКУ:**
Проверено чтением актуального кода с t610 (`/addons/modbus-bridge/modbus_ha_bridge.py`, 987 строк, 40 016 б; копия на Mac: `~/tmp-mbbridge/`). **Это не git-репозиторий** — деплой-копия аддона.
Главный цикл (стр. ~669–963):
```python
buf = bytearray()
while not shutdown_flag.is_set():
buf += ser.read(ser.in_waiting or 1) # стр. 675
i = 0
while i <= len(buf) - 5: # стр. 679 — сканер кадров
found = False
for j in range(i + 4, len(buf)): # стр. 681 — перебор конца кадра
frame = bytes(buf[i:j+1])
if crc16_int(frame[:-2]) != (frame[-2] | frame[-1] << 8):
continue # стр. 686–687 — ❌ БИТЫЙ КАДР ОТБРАСЫВАЕТСЯ МОЛЧА
print(ts, "Slave:", slave, "Func:", hex(func), "CRC OK: True") # стр. 692–694 — логируется ТОЛЬКО валидный кадр
...
del buf[i:j+1]; i = 0; found = True; break # стр. 955958
if not found:
i += 1 # стр. 960–961 — нераспознанные байты ОСТАЮТСЯ в буфере
time.sleep(0.01) # стр. 963
```
**🔴 ТРИ СЛЕДСТВИЯ (почему лог слеп к багу):**
1. **Логируется только ЦЕЛИКОМ собранный валидный кадр** (стр. 692–694, печать идёт ПОСЛЕ проверки CRC). Всё, что не сошлось по CRC, уходит через `continue` на стр. 687 — **ни следа в логе**. Значит «в логе нет ответа гостиной» НЕ равно «ответ не пришёл»: он мог прийти и быть отброшен.
2. **Нет лога сырых байт.** Код нигде не печатает, **сколько байт пришло за итерацию** и что это за байты. А именно это и отличает «пришло 19 байт целиком» от «пришло 7 + 12 кусками» от «не пришло вовсе».
3. **Остаток буфера не чистится при неудаче.** Стр. 960961: `if not found: i += 1` — нераспознанные байты остаются в `buf` и **склеиваются со следующим кадром** → мусор накапливается, последующие кадры тоже могут не сойтись. (Оправдывает «замерзание» лога на 7 ч — §1.)
**Что нужно для точного диагноза (правка логирования, НЕ конфига).** Добавить ДО сканера (после стр. 675):
```python
raw = ser.read(ser.in_waiting or 1)
buf += raw
if raw:
print(f"[RAW {len(raw)}] {raw.hex(' ')}") # ← этого сейчас НЕТ
```
Тогда в логе станет видно буквально: `[RAW 19] 01 03 0e 03 1a ...` (ответ целиком) / `[RAW 7] …` + `[RAW 12] …` (кусками → баг в чтении) / ничего (ответа нет → другая причина). Плюс — печатать остаток `buf`, когда `found == False`.
**Три кандидата-причины (различить только замером сырых байт):** ① узкий `serial.timeout: 0.05` (50 мс) — **но ПРОТИВ него факт: 12-байтные ответы kids/bedroom ловятся тем же чтением**, значит таймаут сам по себе не блокер; ② `in_waiting or 1` забирает куски (читает «сколько легло», а не «сколько ждём»); ③ потеря первых байт из-за `rts_de: true` (RTS-переключение направления на длинном кадре). **Не угадывать — замерять.**
> 📌 **ПРАВКА ЛОГИРОВАНИЯ (Шаг 1 плана) — TO DO, ждёт ОК Alex.** Правка продакшена → нужен бэкап `modbus_ha_bridge.py` + `ha addons rebuild local_modbus-bridge` (питфолл: код/шаблон впекается в образ, без rebuild не применится, см. §4).
**Что делать дальше (НЕ СДЕЛАНО):** починка приёма кадра — читать ответ **до конца кадра по межбайтовой паузе** (≥3.5 символа = Modbus RTU inter-frame), а не по `in_waiting`. Плюс проверить, почему `dining_temperature` в логе = 0, а не «нет данных» (bridge подставляет дефолт вместо пропуска).
> ⚠️ **Питфолл диагностики (важный):** bridge — **виртуальный slave-прокси**, а не просто «сниффер, который публикует в MQTT». Он **двусторонний**: снифит реальные обмены ZONT↔датчики И притворяется датчиками под адресами 100–103 для ZONT. Поэтому «в MQTT нет dining» может означать не «ZONT не спрашивает», а «bridge не имеет значения и отдаёт 0». Смотреть лог bridge целиком (запросы **и** ответы), а не только MQTT-топики.
@@ -1050,6 +1093,43 @@ Alex дал решающую наводку: **«ответ есть или не
> ⚠️ **Питфолл SSH-аддона:** `mosquitto_sub` изнутри аддона даёт `Error: Bad file descriptor` — это ограничение песочницы аддона, **не** отказ авторизации. Проверять с Mac/через `docker run eclipse-mosquitto`.
> 🙋 **Вклад Alex:** маршрут MQTT (ZONT → роутер `192.168.0.10` → DNAT) он подсказал вопросом «gpon все редиректит на openwrt же?» — **правильная гипотеза с первой попытки**.
### 5-кватер-Е. ✅ Gitea: репозиторий проекта `HA-ZONT-Modbus` (2026-09-14, вечер-4)
**Триггер:** Alex — *«Добавь для этих проектов remote на gitea»*, затем *«Flows да, pdf — нет»* (что класть в git).
**Проект-репозиторий:** `~/Automation/HA-ZONT-Modbus` (git, ветка `main`). ⚠️ **НЕ** `/addons/modbus-bridge/` — там деплой-копия аддона без `.git`. В `~/Automation/` это **единственный** git-репо (остальное — скрипты/скриптлеты).
**Gitea:** `https://git.mallexxx.duckdns.org` — v1.27.3, DNS `90.189.160.148`. Юзер `git_admin` (**admin**, id 1). Токен (40 симв.) — в remote репо `nolvu-landing`. Существующие репо: `eagle-hermes`, `kraken-docker-config`, `nolvu-landing`, `obsidian-vault` (единственный public), `reflect-app`.
**Что сделано:**
| # | Шаг | Факт |
|---|---|---|
| 1 | `.gitignore` | Добавлен `project_home.pdf` (по указанию Alex: flows — в git, pdf — нет) |
| 2 | Коммит | `7e0b281` — 6 файлов, 4366 вставок, 127 удалений. Внутри: `homeassistant/configuration.yaml` (блок slave 10 закомментирован — правки миграции), `homeassistant/scripts.yaml`, `floorplan/lovelace.home_plan.json`, `nodered-flows-backup.json`, `nodered-flows-updated.json`, `.gitignore` |
| 3 | Репо в Gitea | `git_admin/HA-ZONT-Modbus`, **private**, через `POST /api/v1/user/repos` (не веб-морда) |
| 4 | Remote | `origin` = `https://git.mallexxx.duckdns.org/git_admin/HA-ZONT-Modbus.git` — **чистый URL, токена в нём НЕТ** |
| 5 | Креды | токен в `~/.git-credentials` (**chmod 600**) + `git config credential.helper store` → push без токена в URL |
| 6 | Push | `git push -u origin main` ✅ → `git ls-remote origin` = `7e0b281…` = локальный HEAD, `main` трекается, дерево чистое |
**Команды (эталон):**
```bash
# создать репо (private) через API
curl -sS -X POST -H "Authorization: token $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"HA-ZONT-Modbus","private":true,"auto_init":false,"default_branch":"main"}' \
'https://git.mallexxx.duckdns.org/api/v1/user/repos' | jq '{full_name,private,clone_url}'
# remote + креды (токен в файл, НЕ в URL)
git remote add origin 'https://git.mallexxx.duckdns.org/git_admin/HA-ZONT-Modbus.git'
git config credential.helper store
printf 'https://git_admin:%s@git.mallexxx.duckdns.org\n' "$TOKEN" >> ~/.git-credentials # chmod 600!
git push -u origin main
```
> 🔴 **ПИТФОЛЛ (безопасность):** токен лежит **открытым текстом** в `.git/config` у `nolvu-landing` (`https://git_admin:<token>@…`) → светится в каждом `git remote -v` и в выводах команд. **Настроить так же** (креды-файл + чистый URL). Кандидат на ротацию токена. **Не сделано по указанию — править только с подтверждения Alex.**
> ⚠️ **ПИТФОЛЛ shell (Hermes):** inline-команда вида `TOKEN=$(git remote get-url origin | sed -E 's|…|…|')` **не работает** — приём shell в Hermes спотыкается на `|` внутри `$( )`. Решение: **писать скрипт файлом** (`write_file` → `bash file.sh`). Проявилось дважды в этой сессии → сохранён `~/tmp-t610/setup_gitea_creds.sh`.
> 📌 **Что кладём/не кладём в git (решение Alex):** `nodered-flows-*.json` — **да** (это артефакты потоков); `project_home.pdf` (3 МБ бинарь) — **нет**, в `.gitignore`.
### 5-кватер-Г. ✅ Node-RED: перенос flows с TrueNAS на t610 (2026-09-14, вечер-2)
**Триггер:** Alex открыл `nodered.mallexxx.duckdns.org` → увидел basic auth вместо страницы Node-RED. В ходе разбора выяснилось, что **на t610 `flows.json` = 124 байта (пусто)** — агент при миграции **не перенёс flows Node-RED с TrueNAS**. Alex: *«в смысле чистый лист?? ты не перенёс все с truenas значит»* → команда **«переносим как есть»**.
@@ -1385,7 +1465,8 @@ 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 их не публикует.** Прямое прослушивание шины (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) и чинить приём по межбайтовой паузе. **НЕ ДОДЕЛАНО** | отдельно |
| 3 | **`sensor.dining_summary` / `dining_air_summary`** — **✅ ПРИЧИНА ЛОКАЛИЗОВАНА КОДОМ (2026-09-14, вечер-4; §5-кватер-Д «РАЗБОР КОДА»): ответ на шине ЕСТЬ, bridge его не публикует — баг ПРИЁМА кадра.** Сырьё: запрос `01 03 00 02 00 07` → ответ `01 03 0E 03 1A …` (19 байт: 794/14/25/11/13/256/1). Конфиг bridge содержит Dining-блок (идентичен эталону TrueNAS). **Код (`modbus_ha_bridge.py`, 987 стр.):** `buf += ser.read(ser.in_waiting or 1)` (стр. 675), сканер перебирает кадры, **битый по CRC отбрасывается молча** (`continue`, стр. 687), логируется **только валидный кадр** (стр. 692–694) → **лог слеп к багу**. Остаток буфера не чистится (стр. 960–961) → мусор копится (объясняет «замерзание» лога, §1). **3 кандидата:** ① `timeout 0.05` (против него факт: 12-байтные kids/bedroom ловятся), ② `in_waiting or 1` берёт куски, ③ `rts_de: true` теряет первые байты. **ПЛАН: Шаг 1 — правка логирования (`[RAW n] <hex>` до сканера) → rebuild → прогон; Шаг 2 — фикс чтения по межбайтовой паузе.** Ждёт ОК Alex | отдельно |
| ~~3-гт~~ | ~~**Gitea remote для `~/Automation/HA-ZONT-Modbus`**~~ — **✅ СДЕЛАНО 2026-09-14 (см. §5-кватер-Е «GITEA»).** Репо `git_admin/HA-ZONT-Modbus` создан через API (**private**), remote добавлен (чистый URL без токена), токен вынесен в `~/.git-credentials` (chmod 600) + `credential.helper=store`, первый push прошёл (`7e0b281`, ветка `main`). **Осталось:** ⚠️ ротировать/вынести токен из НАМЕРТВО открытого remote у `nolvu-landing` (`https://git_admin:<token>@…` — светился в выводах команд). Детали — §5-кватер-Е | ✅ сделано |
| 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` | ✅ сделано |
@@ -1428,7 +1509,8 @@ curl -s -X POST -H "$HDR" -H "Content-Type: application/json" \
| Шаг | Задача | Риск | Действие |
|---|---|---|---|
| ~~A1~~ | ~~№3: `\|default(0)`~~ — **❌ СНЯТ ОКОНЧАТЕЛЬНО** | — | Причина: датчик отвечает `0` (§5-кватер-А). Формула не чинится |
| ~~A1~~ | ~~№3: `\|default(0)`~~ — **❌ СНЯТ ОКОНЧАТЕЛЬНО** | — | Причина: данные на шине есть, bridge их теряет (§5-кватер-Д). Формула не чинится |
| **A0** | **№3: починка приёма кадра bridge** (приоритет сессии) | 🔶 средний | **Шаг 1 — правка ЛОГИРОВАНИЯ:** добавить `[RAW n] <hex>` до сканера + лог остатка буфера (§5-кватер-Д «РАЗБОР КОДА»). Бэкап `modbus_ha_bridge.py` → правка → `ha addons rebuild local_modbus-bridge` → прогон 2–3 мин → смотреть, как приходит ответ гостиной. **Шаг 2 — фикс чтения:** читать ответ по межбайтовой паузе (≥3.5 симв.), а не по `in_waiting`. **Ждёт ОК Alex** |
| ~~B1~~ | ~~**№5 (Этап 4): ДОЛИТЬ Caddyfile**~~ — **✅ СДЕЛАН + `trusted_proxies` фикс** | — | Alex подменил файл + `docker restart caddy`. `mallexxx.duckdns.org` → 200. Затем `.storage/http` → `trusted_proxies += 192.168.2.197/32` (§5-кватер-В) |
| **B2** | **Node-RED наружу** — выставить порт аддона и поправить Caddy | 🔶 средний | Опции `a0d7b954_nodered`: `{ "1880/tcp": 11880 }`, `host_network` убрать → рестарт → Caddy `nodered.*` → `192.168.2.176:11880` + reload. **Сначала ответить: нужен ли пустой t610-Node-RED?** (§5-кватер-В-2) |
| **B3** | **Этап 4, остаток:** GPON-редирект → t610, ZONT MQTT → t610 | 🔴 высокий | Трогает живое → окно + согласование |
@@ -1477,7 +1559,9 @@ docker restart caddy
**Диагностика modbus (2026-09-14 позднейшая, второй заход):** `~/tmp-t610/` — `mb_backup.sh` (бэкап опций+конфига), `mb_fix_opts.sh` (правка опций — СЛОМАЛ mbusd), `mb_rollback.sh` (откат), `mb_dump_t610.sh`, `mb_stress.sh` (30 запросов — артефакт), `mb_master_test.sh` (`ha core stop` — повис), `mb_bus_check.sh`, `mb_bus_listen.sh`, `mb_whotraffic.sh`, `mb_bus2.sh`, `mb_bridge_check.sh`, `mb_zont.sh`, `mb_zont2.sh`, `mb_zont3.sh`, `mb_zont_now.sh`, `mb_after_zont.sh`, `mb_who.sh`.
> 🔴 **Урок по скриптам:** сырой замер шины (`cat /dev/ttyUSB*` + `nc`) **пока HA/mbusd опрашивают ту же шину — портит замер и мешает работе**. Плюс `cat /dev/ttyUSB0` при работающем `modbus-bridge` **всегда пусто** (bridge держит порт). Единственный чистый метод привязки — **физический: выдернуть шнур + `dmesg`**.
> ⚠️ Секреты в скриптах — только через обходных путей из §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-*`.
**Диагностика modbus (2026-09-14 вечер-4, разбор кода):** `~/tmp-mbbridge/` — `modbus_ha_bridge.py` (987 стр., 40 016 б, копия с t610) и `config.template.tmpl` (156 стр.). Копирование: `scp root@192.168.2.176:/addons/modbus-bridge/modbus_ha_bridge.py ~/tmp-mbbridge/`. **Проект-репозиторий (git):** `~/Automation/HA-ZONT-Modbus` — `modbus_ha_bridge.py` (40 009 б), `config.yml`, `INFRASTRUCTURE.md`; remote **отсутствует**, untracked `nodered-flows-{backup,updated}.json` + `project_home.pdf`.
**Бэкапы на 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`.
@@ -1655,6 +1739,45 @@ docker restart caddy
---
### 2026-09-14 (вечер-4: разбор кода bridge + подготовка Gitea remote)
**Контекст:** Alex спросил «что делаем дальше по плану» → обсудили варианты (A1/A2/A3 vs Этап 4) → Alex потребовал разобраться с датчиком гостиной: *«то есть как я понял ты нихуя не нашел?»*, *«то есть таймаут надо поднимать?»*, *«логирование правильно сделано? оно позволит идентифицировать проблему?»*. Затем: «Да. Не забудь сначала закоммитить текущий код если в нем есть незакоммиченые измениния. Ты же в папке проекта это делаешь?» → «Добавь для этих проектов remote на gitea».
**Что сделано (только чтение + анализ; в железе НИЧЕГО не менялось):**
| Тема | Результат |
|---|---|
| 🔬 Разбор кода bridge | Скопирован `/addons/modbus-bridge/modbus_ha_bridge.py` (987 стр., 40 016 б) на Mac (`~/tmp-mbbridge/`). Найдены точные строки: `ser.read(ser.in_waiting or 1)` — **стр. 675**; молчаливый отброс битого кадра — **стр. 687**; логируется только валидный кадр — **стр. 692–694**; остаток буфера не чистится — **стр. 960–961**. Полный разбор — §5-кватер-Д «РАЗБОР КОДА» |
| 🔴 Ответ на вопрос Alex | **Текущее логирование проблему НЕ идентифицирует** — оно печатает только целиком собранные валидные кадры; битые/неполные исчезают без следа. Нужен лог сырых байт (`[RAW n] <hex>`) ДО сканера |
| ❌ Снята гипотеза «поднять таймаут» | **Против неё факт:** 12-байтные ответы kids/bedroom ловятся тем же чтением при том же `timeout: 0.05` → таймаут не блокер сам по себе. Три кандидата (timeout / `in_waiting` куски / `rts_de`), различить — только замером |
| 🔴 `modbus-bridge` — НЕ git-репозиторий | `/addons/modbus-bridge/` = деплой-копия аддона. Проект-репозиторий на Mac: **`~/Automation/HA-ZONT-Modbus`** (git, `main`, файлы `modbus_ha_bridge.py` + `config.yml` трекаются) |
| 📌 Расхождение версий | Локальный `modbus_ha_bridge.py` (23 янв, 40 009 б) ≠ t610 (14 сен, 40 016 б). **Различия только в `entity_id`** (`0xa4c138…` → `office_temperature_sensor_temperature`, `switch.recirculation_pump`) — **код идентичен** |
| 🔴 Gitea | Живой: DNS `90.189.160.148`, HTTP 200, **v1.27.3**, API работает. Юзер `git_admin` (**admin**), токен 40 симв. найден в remote `nolvu-landing`. Репо: `eagle-hermes`, `kraken-docker-config`, `nolvu-landing`, `obsidian-vault`, `reflect-app`. **`HA-ZONT-Modbus` — НЕТ** |
| ⚠️ Утечка токена | Токен лежит **открытым текстом** в `.git/config` у `nolvu-landing` (`https://git_admin:<token>@git.mallexxx…`) → вынести в `~/.git-credentials` + `credential.helper=store` |
| 📌 Незакоммичено в проекте | staged: `floorplan/lovelace.home_plan.json`, `homeassistant/configuration.yaml`, `homeassistant/scripts.yaml`; unstaged: `homeassistant/configuration.yaml`; untracked: `nodered-flows-{backup,updated}.json`, `project_home.pdf` (3 МБ). **У репо НЕТ remote** |
**Что менялось в железе:** **НИЧЕГО.** Только чтение кода и опрос Gitea API.
**Что изменено в git/Gitea (вечер-4, продолжение — задача 3-гт ЗАКРЫТА):**
| Шаг | Факт |
|---|---|
| `.gitignore` | Добавлен `project_home.pdf` (Alex: «Flows да, pdf — нет» → flows в git, pdf игнор) |
| Коммит | `7e0b281` — 6 файлов, 4366 вставок: `configuration.yaml` (закомментирован slave 10), `scripts.yaml`, `floorplan/lovelace.home_plan.json`, `nodered-flows-backup.json`, `nodered-flows-updated.json`, `.gitignore` |
| Репо в Gitea | `git_admin/HA-ZONT-Modbus`, **private**, создан `POST /api/v1/user/repos` (токен из `nolvu-landing`) |
| Remote | `origin` = `https://git.mallexxx.duckdns.org/git_admin/HA-ZONT-Modbus.git` — **чистый URL, без токена** |
| Креды | токен в `~/.git-credentials` (chmod 600) + `git config credential.helper store` |
| Push | ✅ `git ls-remote origin` = `7e0b281…` = локальный HEAD, ветка `main` трекается, дерево чистое |
| Скрипт | `~/tmp-t610/setup_gitea_creds.sh` — выносит токен из remote `nolvu-landing` в файл кредов |
**Не сделано (ждёт ОК Alex):** правка логирования bridge (Шаг 1 плана, задача A0/№3). **Gitea-репо + remote (3-гт) — ✅ ЗАКРЫТО.**
> ⚠️ **ПИТФОЛЛ shell (Hermes):** `TOKEN=$(git … | sed -E 's|…|…|')` в inline-команде **ломается** — shell-приём спотыкается на `|` внутри `$( )`. Решение: писать скрипт **файлом** (`write_file` → `bash file.sh`), не инлайн. Проявилось дважды в этой сессии.
> 📌 **Процессный урок:** Alex трижды переспросил «нашёл ли ты причину», потому что предыдущие ответы давали **обоснованные гипотезы** вместо **проверенных фактов**. Правильно: либо «нашёл, вот факт», либо «не нашёл, вот что нужно замерить» — без «я думаю, это таймаут». На вопрос «позволит ли логирование найти проблему» — **проверять код логирования, а не рассуждать о нём**.
---
## Связанные заметки
- [[family/how-to/home-automation]] — карта Modbus slave ID, ZONT, регистры вентиляции