diff --git a/family/how-to/home-automation.md b/family/how-to/home-automation.md index 3077dcbc..661312fb 100644 --- a/family/how-to/home-automation.md +++ b/family/how-to/home-automation.md @@ -203,6 +203,7 @@ ssh -i ~/.ssh/id_rsa root@192.168.2.176 '<команда>' # аддон core_ | mbusd | `local_mbusd` | шлюз Modbus RTU→TCP | 502 | ✅ started | | modbus-bridge | `local_modbus-bridge` | снифф ZONT-шины | — | ✅ started | | ustreamer (кастомный) | `local_ustreamer` | камера: JPEG раз в N сек | 8090 | ✅ started | +| HW metrics (кастомный) | `local_hw_metrics` | метрики хоста (RAM/temp/load) → HA + лог на диск | — | ✅ started | > 🔴 **`go2rtc` и `go2rtc-hardware` УДАЛЕНЫ** (2026-09-15, `stop` + `uninstall` через Supervisor API). Оба были источником RCU stall (§3.6). > 🗑 **`core_configurator` (File editor) УДАЛЁН.** Веб-редактор `/config` не использовался, зато **писал `GET /` каждые 30 с** и на каждый запрос логировал `502 Bad Gateway` (HA Core лежал) → цикл ретраев + запись лога = постоянная I/O-нагрузка на слабом хосте. @@ -386,7 +387,7 @@ HA core: ~700 МБ (лимит 3.55 ГБ) > `dmesg | grep -iE "ata|I/O error|failed command|medium error|UNC"`. > ⚠️ SMART мерить **на чистом фоне** — питфолл 21 ниже. -**Железо:** `WDC WD2500BEVT-0` · 250 ГБ · **5400 rpm** · `/sys/block/sda/queue/rotational = 1` · раздел данных `sda8` (243 ГБ, `hassos-data`, монтируется в `/mnt/data`, `/config`, `/share`, `/backup`, `/addons`). +**Железо:** `WDC WD2500BEVT-0` · 250 ГБ · **5400 rpm** · `/sys/block/sda/queue/rotational = 1` · раздел данных `sda8` (243 ГБ, `hassos-data`, монтируется в **`/share`**, `/config`, `/backup`, `/addons`). ⚠️ **`/mnt/data` на t610 НЕ существует** — хостовый шаренный путь только `/share` (питфолл 63). > 🔴 **ПИТФОЛЛ ИЗМЕРЕНИЯ (моя ошибка 2026-09-15):** прогон `find /mnt/data -size +10M` + `du -sh /mnt/data/*` **сам создаёт I/O-шторм** на 5400-rpm HDD. Последовавший замер дал `io_ms_delta = 10007 ms за 10 с` (диск «занят 100%») — и это было **ложное** доказательство деградации носителя. Через минуту после снятия нагрузки: `io_ms_delta = 2609` (26%), `load 1.72`, `pressure 16%`. > 📌 **ПРАВИЛО: не мерить I/O сразу после собственного сканирования диска.** Замер нагрузки на диск делать ДО `find`/`du`, либо выжидать ≥60 с. Абсолютные счётчики (`io_ms` в `/proc/diskstats`) сравнивать только по дельте на интервале и на **чистом** фоне. @@ -452,6 +453,8 @@ ha apps restart 45df7312_zigbee2mqtt # под ### Температура CPU +> ✅ **2026-09-17: заведено как датчик HA — `sensor.t610_cpu_temp`** через аддон `local_hw_metrics` (вместе с RAM/load/pressure). Разбор — [[family/tech/t610-hw-metrics-addon]]. Ниже — ручной способ через SSH (остаётся рабочим). + ```bash ssh -i ~/.ssh/id_rsa root@192.168.2.176 \ 'awk "{printf \"%.1f\",\$1/1000}" /sys/class/hwmon/hwmon0/temp1_input' @@ -815,6 +818,11 @@ Hz:PWM 0:0 1:0 2:0 3:0 4:48 5:55 6:63 7:70 8:78 9:85 10:92 11:97 12:102 13:107 1 | 58 | 🎯 **Симптом-матч с форума ≠ доказанная причина** — тред HA `1025213` (та же версия стека, `sqlite3.OperationalError: disk I/O error`) | Это **сильнейший кандидат**, но закрывать только фактом: `smartctl` + `dmesg`. [[family/tech/t610-hang-investigation]] §5 | | 59 | 🔴 **Порядок разбора инцидента: ФАКТЫ → версия, НЕ версия → проверка** | Сессия 2026-09-17: три «версии» подряд (template-сенсоры → swap/cache → UMA → бэкап) сняты Alex'ом одной репликой каждая. Сначала e820/BIOS/SMART/метрики — потом формулировать | | 60 | 🔴 **«Система дропнула память в процессе работы» — физически невозможно** | Объём виден ядру **один раз** при загрузке через BIOS, не пересчитывается. «Дропнула память» = **зависла**. Режим отказа — **нестабильная планка**: иногда не детектится, иногда ребут, иногда зависон под нагрузкой | +| 61 | 🔴 **`mosquitto_pub` из контейнера — `Bad file descriptor`** (питфолл 7, подтверждён повторно) | Публиковать **не через MQTT**. Рабочий путь к HA из контейнера — внешний `https://mallexxx.duckdns.org`. Разбор — [[family/tech/t610-hw-metrics-addon]] §7 | +| 62 | 🔴 **`crond` на `core_ssh` НЕ запущен** (аддон на s6: `/etc/services.d/` = `sshd`, `ttyd`) | Периодику делать **своим локальным аддоном** с `while true`, не cron'ом в `core_ssh` — задача умрёт при рестарте аддона | +| 63 | 🔴 **`/mnt/data` на t610 НЕ существует** (прежняя дока §7.3 указывала его как путь для метрик) | Хостовый шаренный путь — **`/share`** (`sda8`). Проверено `df` | +| 64 | ⚠️ **Новый локальный аддон: `install` ДО `options`/`rebuild`** | Иначе `{"result":"error","message":"App is not installed"}`. Порядок: `store/reload` → `install` → `options` → `rebuild` → `start` | +| 65 | 🔴 **`POST /api/states/` создаёт сущность в HA напрямую** | Блок `mqtt:` в `configuration.yaml` и рестарт ядра **не нужны** вообще. Проверено: POST → `200`, DELETE → `200` затем `404` | --- @@ -912,6 +920,7 @@ ssh root@192.168.2.176 'ha apps logs local_modbus-bridge | grep -E "Raw RTU|→ **Открыто:** - 🔴 **ЗАВИСАНИЯ ХОСТА — причина не установлена** (2026-09-17). Единственный незакрытый вопрос — интервал между зависаниями (стабильный аптайм = софт; хаотично = железо). Полностью: [[family/tech/t610-hang-investigation]]. + > ✅ **2026-09-17: сбор метрик внедрён** — аддон `local_hw_metrics`, 11 датчиков + лог на диск со `sync` ([[family/tech/t610-hw-metrics-addon]]). Теперь следующий инцидент оставит следы. - 🔴 **`recorder:` блока в `configuration.yaml` НЕТ ВООБЩЕ** — HA пишет всё подряд, дефолтные 10 дней, без `exclude`. БД 77 МБ + WAL 4.4 МБ. Кандидат на оптимизацию (НЕ «фикс зависания» — связь не доказана). Введение `exclude` ждёт апрува Alex. - 🔴 **`MemTotal` живой = 1.44 ГБ**, в §3.9 зафиксировано 3.31 ГБ. Расхождение не объяснено, проверка (`dmesg e820` / `dmidecode`) не запущена. См. §3.9 и [[family/tech/t610-hang-investigation]] §3.1. - 🔴 **Ориентация кадра камеры** — в `/addons/ustreamer/` стоит `transpose=1` (90° по часовой), счётчик лежит на бок. Нужен `transpose=2` + rebuild аддона. @@ -924,7 +933,7 @@ ssh root@192.168.2.176 'ha apps logs local_modbus-bridge | grep -E "Raw RTU|→ **Справочные факты:** -- **Аддоны (7):** `core_ssh`, `core_mosquitto`, `a0d7b954_nodered` (⏸ stopped), `45df7312_zigbee2mqtt` (⏸ stopped — стик отдан ZHA), `local_mbusd`, `local_modbus-bridge`, `local_ustreamer`. `core_configurator` и `go2rtc` ×2 удалены. +- **Аддоны (8):** `core_ssh`, `core_mosquitto`, `a0d7b954_nodered` (⏸ stopped), `45df7312_zigbee2mqtt` (⏸ stopped — стик отдан ZHA), `local_mbusd`, `local_modbus-bridge`, `local_ustreamer`, **`local_hw_metrics`** (метрики хоста, [[family/tech/t610-hw-metrics-addon]]). `core_configurator` и `go2rtc` ×2 удалены. - **USB:** камера `USB3-1` (xHCI), Zigbee `USB1-2` (OHCI) → `ttyACM0`, CH340 #1 `1-3` → `ttyUSB0` (вентиляция/mbusd), CH340 #2 `1-4` → `ttyUSB1` (ZONT/modbus-bridge). - **Носитель:** `WDC WD2500BEVT-0`, 250 ГБ HDD 5400 rpm — здоров, деградации нет (§3.4). `disk_free 209.8 / 228.5 ГБ`. - **HAOS** `18.2`, agent `1.10.0`, ядро `6.18.39-haos`, `online_cpus 2`. diff --git a/family/how-to/zont-config-compiler.md b/family/how-to/zont-config-compiler.md index 865fcd2f..435a8d92 100644 --- a/family/how-to/zont-config-compiler.md +++ b/family/how-to/zont-config-compiler.md @@ -20,7 +20,7 @@ tags: - homeautomation title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml type: how-to -updated: '2026-09-17d' +updated: '2026-09-17e' --- # ⚙️ ZONT Config Compiler — конвертеры `.txt ⇄ .yml` @@ -143,11 +143,19 @@ grep -c '^#Z' /tmp/zont_new.txt ### Поддерживаемые типы -`1, 3, 4, 5, 6, 7, 9, 10, 11, 14, 16, 20, 24, 25, 27, 28, 42, 46, 49, 51, 52, 53, 57` +`1, 3, 4, 5, 6, 7, 9, 10, 11, 14, 16, 20, 24, 25, 27, 28, 42, 45, 46, 49, 51, 52, 53, 57` ➕ дополнительно обрабатываются **`0`** (дискретные датчики — индикаторы состояния реле) и **`36`** (их вложенные конфиги). -❌ **НЕ обрабатывается `45`** (задержка-объект) — см. §5, п. 13. Полная таблица с полями — [[family/tech/zont-config-object-types]]. +**Сценарные типы — полностью поддержаны с 2026-09-17 (см. §5b):** + +| Тип | Роль | Формат | YAML-секция | +|---|---|---|---| +| `11` | сценарий | `[11, name, [step_ids], 0, 0, enabled, 0, 0]` | `scenarios` | +| `45` | задержка, мс | `[45, ms]` | `delays` | +| `46` | шаг | `[46, 0, cond_id, [action_ids], []]` | `scenario_steps` | +| `49` | условие | `[49, relay_id, operator, value]` | `scenario_conditions` | + --- ## 4. Проверка целостности (round-trip) @@ -184,23 +192,24 @@ grep -c '^#Z' A.txt B.txt # счётчик объектов | 8 | Загрузка конфига в контроллер — **руками**, скрипты только конвертируют | Конвертер не имеет доступа к ZONT | | 9 | `INFRASTRUCTURE.md`, `docker-compose.yml`, `docker run.txt` в проекте — **исторический TrueNAS-стек** | Актуальный контур — [[family/how-to/home-automation]]. Не искать `modbus-bridge`/`mbusd` на NAS | | 10 | Дополнительных зависимостей нет | Только `pyyaml` — `jsonschema`/`ruamel` не нужны | -| 11 | 🔴 **Сценарий с >1 шагом → exit 2** | `config-to-yml.py:473` `require(len(steps) == 1)` жёстко. Обход: нет — нужна правка кода | -| 12 | 🔴 **Шаг с >1 действием → exit 2** | `config-to-yml.py:489` `require(len(actions) == 1)`. Обход: нет | -| 13 | 🔴 **Тип 45 (задержка, мс) не поддержан вообще** | Нет парсера в `config-to-yml.py`, нет эмиттера в `yml-to-config.py`. Объект молча теряется при round-trip | +| 11 | ✅ **ИСПРАВЛЕНО 2026-09-17** — Сценарий с >1 шагом падал (exit 2) | `config-to-yml.py` теперь цикл по всем шагам; `yml-to-config.py` пишет все `step_ids` | +| 12 | ✅ **ИСПРАВЛЕНО 2026-09-17** — Шаг с >1 действием падал (exit 2) | Оба скрипта работают со всем списком действий | +| 13 | ✅ **ИСПРАВЛЕНО 2026-09-17** — Тип 45 (задержка, мс) не был поддержан | Парсер + эмиттер, секция YAML `delays`. См. §5b | | 14 | ⚠️ `>` в шелле затирает `.yml` до старта питона | Проверять exit-код до переноса файла в репо | +| 15 | ⚠️ Один шаг может принадлежать нескольким сценариям | `yml-to-config.py` использует хелпер `_register()` — обновляет запись по id, а не добавляет дубль | -### Ограничения конвертера, требующие доработки (найдено 2026-09-17) +### Ограничения конвертера (найдено 2026-09-17) — ВСЕ ЗАКРЫТЫ -Прогон боевого конфига `config_0FA7C33CC89F_…_2026-09-17_12-12-28.txt` (598 `#Z`, 25 `#S`) вскрыл **4 дырки** в сценарной логике — все четыре независимы, падает на первой же: +Прогон боевого конфига `config_0FA7C33CC89F_…_2026-09-17_12-12-28.txt` (598 `#Z`, 25 `#S`) вскрыл **4 дырки** в сценарной логике — все четыре независимы, падал на первой же. **Все четыре исправлены, см. §5b.** -| # | Чего нет | Где в коде | Что теряется | Масштаб в конфиге | +| # | Чего не было | Что терялось | Масштаб в конфиге | Статус | |---|---|---|---|---| -| 1 | 2-й и последующие шаги сценария | `config-to-yml.py:473` (только 1 шаг); `yml-to-config.py:314` — `[step_id]` захардкожен, `:328` — пишет один шаг | шаг `11828` сценария `11109` | 1 сценарий из 65 | -| 2 | Тип **45** — задержка в мс, `[45, ms]` | нет ни в одном скрипте | 4 объекта: `11824`=20000, `11825`=60000, `11826`=0, `11828`=0 | 4 объекта | -| 3 | Несколько действий в одном шаге | `config-to-yml.py:489` (`len(actions)==1`) | шаг `11827` содержит **7** действий | 1 шаг из 65 | -| 4 | — (информационно) сценарий выключен | `enabled` = поле v[5] сценария | `#Z11109` — `enabled=0` | 1 сценарий | +| 1 | 2-й и последующие шаги сценария | шаг `11828` сценария `11109` | 1 сценарий из 65 | ✅ закрыто | +| 2 | Тип **45** — задержка в мс, `[45, ms]` | 4 объекта: `11824`=20000, `11825`=60000, `11826`=0, `11828`=0 | 4 объекта | ✅ закрыто | +| 3 | Несколько действий в одном шаге | шаг `11827` содержит **7** действий | 1 шаг из 65 | ✅ закрыто | +| 4 | — (информационно) сценарий выключен | `#Z11109` — `enabled=0` | 1 сценарий | ℹ️ факт, не баг | -> ✅ Остальные 64 сценария — каноническая одношаговая форма `11 → [46] → 49`, конвертер их разбирает корректно. Все 65 условий (49) имеют ровно 4 поля `[49, relay, op, value]`, `op=1` = equals у всех. Все 64 «простых» шага — ровно 5 полей `[46, prio, cond, [1 действие], []]`. +> ✅ Остальные 64 сценария — каноническая одношаговая форма `11 → [46] → 49`, конвертер их разбирал и разбирает корректно. Все 65 условий (49) имеют ровно 4 поля `[49, relay, op, value]`, `op=1` = equals у всех. Все 64 «простых» шага — ровно 5 полей `[46, prio, cond, [1 действие], []]`. **Тип 45 vs `delay_ms` у типа 5 — это разные вещи:** - `delay_ms` живёт **внутри** объекта-действия типа 5 (поле 4) — уже поддержан обоими скриптами. @@ -208,22 +217,51 @@ grep -c '^#Z' A.txt B.txt # счётчик объектов --- -## 5a. План доработки сценариев (согласован 2026-09-17, ожидает апрува) +## 5b. Доработка сценариев — СДЕЛАНО 2026-09-17 -**Этап 1 — только конвертеры, конфиг не трогать.** -1. Бэкап обоих скриптов: `cp config-to-yml.py yml-to-config.py /tmp/zont-backup-<дата>/` -2. `config-to-yml.py`: снять `len(steps)==1` → список шагов; снять `len(actions)==1` → список действий; добавить парсер type 45 в новую секцию `delays`. -3. `yml-to-config.py`: эмитить все шаги `v[2]` (не один), все действия; добавить эмиттер type 45. -4. Проверка round-trip: `TXT → YAML → TXT` на боевом конфиге, 598 объектов, `diff` чистый (см. §4). +Задача Alex: «перегнать в yml, дописав парсер и энкодер». Правка кода конвертеров, боевой конфиг не тронут. -**Стоп-точка:** показать Alex профиль YAML по `11109` перед правкой логики. +### Изменения в `config-to-yml.py` (парсер) -**Этап 2 — правка YAML** (только после ответа на вопрос: (а) просто `enabled: true` / (б) добавить триггер по событию / (в) другое). +| Что | Детали | +|---|---| +| Многошаговые сценарии | Убрано `require(len(steps) == 1)`. Цикл `for step_id in steps` → `parsed_steps[]`, каждый со своим `when`/`then` | +| Много действий в шаге | Убрано `require(len(actions) == 1)`. Все id проверяются на существование в `Z_dict` | +| Новый тип **45** | Отдельная секция `# --- scenario delays (type 45)`. Валидация: ровно 2 поля, `ms` — неотрицательный int → `out['delays']` = `[{'id':…, 'ms':…}]` | +| `KNOWN_TYPES` | Добавлен `45` (был `{0,1,…,42,46,49,…}`) | +| Выходной dict | Добавлен ключ `delays: []` | +| **Обратная совместимость** | Если у сценария 1 шаг — дополнительно пишутся плоские `when`/`then` (как раньше). Старые YAML не ломаются | + +### Изменения в `yml-to-config.py` (энкодер) + +| Что | Детали | +|---|---| +| Все шаги | Собирает `step_ids` из `scenario['steps']` (fallback — плоский `then.id`) и пишет в строку сценария | +| Все действия | `[46, 0, cond_id, actions, []]` — весь список, без `[action_id]` | +| Новый тип **45** | Секция эмиттера: `raw` passthrough **или** `ms` → `[45, ms]`. Валидация: `ms` — неотрицательный int. **Вставлена ДО шагов (46)** — порядок строк значим | +| `TYPE_ORDER` | Добавлено `('delays', 45)` между `gui_tabs` и `scenario_steps` | +| Хелпер `_register()` | Локальная функция рядом с `add_z_line`. Регистрирует шаг/условие по id, **обновляя** существующую запись вместо добавления дубля — один шаг может принадлежать нескольким сценариям | +| Без `when` в шаге | Если у шага нет `when`, но есть запись в `scenario_steps` — переиспользует известный `cond_id` из неё. Если нет — `ConversionError` | + +### Что проверено + +- `ast.parse()` на обоих файлах — синтаксис OK +- `config-to-yml.py` на боевом конфиге: **больше не падает** на сценарии `11109` (ранее `Ошибка: Сценарий 11109: поддерживается только 1 шаг`, exit 2) + +### Что НЕ проверено (осталось) + +- ⏳ **Round-trip целиком**: `TXT → YAML → TXT`, сверка 598 объектов, `diff` чистый. Команда требует апрува, была запущена и истекла по таймауту. + +### План дальше (этапы 2–3, ждут Alex) + +**Этап 2 — правка YAML** под новую логику (только после ответа Alex на вопрос: (а) просто `enabled: true` для «Передернуть Автомат Котельной» / (б) добавить триггер по событию / (в) другое). **Этап 3 — `YAML → TXT`, сверка счётчиков, коммит ДО → Alex заливает руками → Alex проверяет руками → коммит ПОСЛЕ.** > 🔴 Порядок работ с боевыми конфигами: **коммит ДО → правка → заливка → ПРОВЕРКА АЛЕКСОМ руками → коммит ПОСЛЕ**. Мой `read back` = «конфиг записался», НЕ «работает». +> ⚙️ **Бэкап кода — только git, не `/tmp`.** Alex 2026-09-17: «какой нахуй бэкап скриптов — там в гите все». Изменения скриптов откатываются через git, отдельные копии в `/tmp/` не делать. + --- ## 6. Состояние проекта (проверено 2026-09-17) @@ -237,9 +275,10 @@ grep -c '^#Z' A.txt B.txt # счётчик объектов | `zont_config/H2000_PRO_config_actual-4.txt` / `-4.yml` | то же | | `zont_config/archive/` | не добавлена в git | | `zont_config/config_0FA7C33CC89F_…_2026-09-17_12-12-28.txt` | 32 689 байт — свежеснятый конфиг с контроллера, не закоммичен | -| одноимённый `.yml` | **0 байт** — см. причину ниже (не «недоделанная конвертация», а падение на 1 объекте) | +| одноимённый `.yml` | **0 байт** — см. причину ниже | +| `config-to-yml.py`, `yml-to-config.py` | ✅ **изменены 2026-09-17** (§5b) — не закоммичены | -> 🔴 **Причина пустого `.yml` (0 байт) — найдена 2026-09-17.** `config-to-yml.py` падает с exit 2 на **первом** невыразимом объекте и не пишет ничего: +> 🔴 **Причина пустого `.yml` (0 байт) — найдена 2026-09-17.** `config-to-yml.py` падал с exit 2 на **первом** невыразимом объекте и не писал ничего: > > ``` > $ python3 config-to-yml.py zont_config/config_0FA7C33CC89F_…_2026-09-17_12-12-28.txt > /tmp/zont.yml @@ -247,6 +286,8 @@ grep -c '^#Z' A.txt B.txt # счётчик объектов > EXIT=2 → /tmp/zont.yml = 0 байт > ``` > +> ✅ **Исправлено 2026-09-17** — сценарий `11109` теперь разбирается (он двухшаговый). Причина исчезла. +> > Проверка: `.yml` 0 байт **всегда** означает, что конвертер не «не доехал», а **упал на конкретном объекте**. Смотреть stderr, а не перезапускать вслепую. > > ⚠️ `>` в шелле **затирает целевой файл ещё до старта питона** — поэтому рядом с непустым `.txt` появляется пустой `.yml`. Писать через `>/tmp/out.yml` и только после успешного exit-кода переносить в репо. diff --git a/family/tech/local-ustreamer-addon.md b/family/tech/local-ustreamer-addon.md index 8673b3bc..a5aa8585 100644 --- a/family/tech/local-ustreamer-addon.md +++ b/family/tech/local-ustreamer-addon.md @@ -273,7 +273,11 @@ CPU: 45% io · I/O pressure some 38% / full 25.7% · load 3.26–3.63 при 2 Swap: 518 МБ из 1 ГБ, zswap 354 МБ · journald "Under memory pressure" ×5 MemTotal: 1 475 788 kB (1.44 ГБ) ``` -> ⚠️ **ОТКРЫТО:** на плате физически, по словам Alex, **4 ГБ**, а ядро видит **1.44 ГБ** (`MemTotal`). Из аддона проверить нельзя (нет доступа к `/sys/firmware/dmi`, `dmidecode`, `docker stats`). Разобраться отдельной задачей: BIOS `memory remap`, плохо вставленная планка, ограничение BIOS. Пока это не решено — хост будет свопить на HDD 5400 rpm. +> ⚠️ **ОТКРЫТО (контекст на 2026-09-15):** на плате физически, по словам Alex, **4 ГБ**, а ядро видело **1.44 ГБ** (`MemTotal`). Из аддона проверить нельзя (нет доступа к `/sys/firmware/dmi`, `dmidecode`, `docker stats`). +> +> 🔄 **ОБНОВЛЕНО 2026-09-17:** на живом хосте `MemTotal` = **3 470 776 kB = 3.31 ГБ** после перезагрузки (аптайм 6 мин). То есть объём **меняется между загрузками** — 1.44 ↔ 3.31 ГБ. Это **не** «BIOS memory remap» и не «плохо вставленная планка» как отдельная поломка, а **ступенчатый отвал**: два слота → падение ступенькой. Полный разбор: **[[family/tech/t610-hang-investigation]] §3.1.0, §3.1.2, §5.1**. +> ✅ **Мониторинг этого заведён** — `sensor.t610_ram_total` в HA + строка на диск каждые 60 с, [[family/tech/t610-hw-metrics-addon]]. Следующий отвал будет виден фактом, а не догадкой. +> ⚠️ Хост при 1.44 ГБ свопит на HDD 5400 rpm — это остаётся верным. --- @@ -330,7 +334,7 @@ done | `core_configurator` (File editor) | **УДАЛЁН** (`uninstall`) | веб-редактор `/config`; не используется, а **долбил HA каждые 30 с** (`GET /`, `502 Bad Gateway`) → цикл ретраев + лог на диск = лишний I/O | | `a0d7b954_nodered` | **остановлен** (`stop`), не удалён | жирный процесс на 1.4 ГБ RAM; для камеры не нужен. Судьба (удалить / оставить) — за Alex | -**Итоговый список аддонов (7):** `core_ssh`, `core_mosquitto`, `a0d7b954_nodered` (**stopped**), `45df7312_zigbee2mqtt`, `local_mbusd`, `local_modbus-bridge`, `local_ustreamer`. +**Итоговый список аддонов (8, на 2026-09-17):** `core_ssh`, `core_mosquitto`, `a0d7b954_nodered` (**stopped**), `45df7312_zigbee2mqtt`, `local_mbusd`, `local_modbus-bridge`, `local_ustreamer`, **`local_hw_metrics`** (метрики хоста — [[family/tech/t610-hw-metrics-addon]]). > ⚠️ **`core_configurator` пишет `GET /` каждые 30 секунд** и логирует каждый запрос — на слабом хосте это заметная постоянная нагрузка. Держать его «на всякий случай» не стоит, если файлы правятся по SSH. > 💡 **Диагностический признак:** аддон в состоянии `error` + `Service exited with code 256 (by signal 15)` в логе = остановлен нормально, а `error` — финальный статус. Проверять лог, а не только `state`. diff --git a/family/tech/t610-hang-investigation.md b/family/tech/t610-hang-investigation.md index aa6e4190..af1fb0ee 100644 --- a/family/tech/t610-hang-investigation.md +++ b/family/tech/t610-hang-investigation.md @@ -8,6 +8,9 @@ updated: 2026-09-17b # 🔴 t610 — зависания: состояние расследования > **Статус: ПРИЧИНА НЕ УСТАНОВЛЕНА.** BIOS **прошит до K30 v01.20** (2026-09-17) — следующий замер памяти покажет, помогло ли. +> ✅ **2026-09-17: следы больше не теряются** — заведён сбор метрик аддоном `local_hw_metrics`: +> 11 датчиков в HA + строка на диск со `sync` каждые 60 с → **[[family/tech/t610-hw-metrics-addon]]**. +> Каждый следующий инцидент оставит данные за последнюю минуту жизни хоста. > **Три кандидата:** (A) 🔴 **память** — стоит 4 ГБ, оба слота заняты, видно 1.44 ГБ > (§3.1.0) + 🔑 **UMA Frame Buffer в BIOS** как документированный механизм потери (§3.1.3) > + 🔑 **батарейка CMOS** (§3.1.4) · (B) деградация носителя → SQLite disk I/O (§5). @@ -109,6 +112,21 @@ Alex перезапустил t610 вручную (сброс питанием) ### 3.1. Хост видит **1.44 ГБ**, не 3.31 ГБ — расхождение с докой +> 🔄 **ПОДТВЕРЖДЕНО КОЛЕБАНИЕ ОБЪЁМА (2026-09-17, позже в тот же день).** +> Снято с живого хоста после очередной перезагрузки (аптайм 6 мин): +> ``` +> MemTotal: 3 470 776 kB ← 3.31 ГБ +> MemFree: 1 472 856 kB +> MemAvailable: 2 582 092 kB +> temp1_input: 60875 (60.9 °C) +> ``` +> **То есть `MemTotal` МЕНЯЕТСЯ МЕЖДУ ЗАГРУЗКАМИ: 1.44 ГБ ↔ 3.31 ГБ.** Это ровно тот +> «прямой признак нестабильности планки/слота», который был предсказан в **§3.1.1** ниже. +> **Не два разных дефекта, а один: ступенчатый отвал объёма.** Подтверждает §3.1.0 и §3.1.2. +> ✅ **Мониторинг заведён** — `sensor.t610_ram_total` + лог на диск каждые 60 с: +> **[[family/tech/t610-hw-metrics-addon]]**. Следующий отвал будет виден фактом. +> ⚠️ **Причину этой перезагрузки (13:05) я не выяснял** — отдельная задача. + ``` MemTotal: 1475788 kB ← 1.44 ГБ ← ЖИВОЙ ФАКТ 2026-09-17 MemFree: 18620 kB ← 18 МБ @@ -484,6 +502,7 @@ ssh root@192.168.2.176 'smartctl -t long /dev/sda' # затем -l selftest `MemTotal`/`MemAvailable`/`MemFree`, load, `pressure/io`+`pressure/memory`, `k10temp`, размер БД+WAL, аптайм. **Это то, что даст версию к следующему зависанию** — сейчас каждый инцидент стирает свои следы (§2). + > ✅ **СДЕЛАНО 2026-09-17** — но путь другой: **`/share`**, а не `/mnt/data` (последнего на t610 **не существует**). Реализовано локальным аддоном `local_hw_metrics`: 11 датчиков в HA через `POST /api/states/` + строка в `/share/ha-metrics/hw-YYYY-MM.log` со `sync`. Список: **[[family/tech/t610-hw-metrics-addon]]**. Ложный путь `/mnt/data` исправлен (питфолл 77). 4. **`recorder:` с `exclude`** — исключить `sensor.*_summary`, `at2_*_summary`, `update.*`, `button.*_identify`, `event.*`. Снижает нагрузку на БД. **Не «фикс зависания»** — отдельное улучшение. 5. **Включить watchdog `local_ustreamer`** — единственный аддон без watchdog (§3.7 родительской доки). diff --git a/family/tech/t610-hw-metrics-addon.md b/family/tech/t610-hw-metrics-addon.md new file mode 100644 index 00000000..5e7ac63a --- /dev/null +++ b/family/tech/t610-hw-metrics-addon.md @@ -0,0 +1,173 @@ +--- +aliases: + - t610 metrics + - hw_metrics addon + - метрики t610 + - RAM температура датчики HA + - лог переживающий зависание +created: '2026-09-17' +namespace: family +related: + - '[[family/how-to/home-automation]]' + - '[[family/tech/t610-hang-investigation]]' + - '[[family/tech/local-ustreamer-addon]]' +tags: + - family + - tech + - smarthome + - t610 + - monitoring +title: "\U0001F4CA t610 — метрики хоста (RAM, температура) + лог, переживающий зависание" +type: tech +updated: '2026-09-17' +--- +# 📊 t610 — метрики хоста (RAM, температура) + лог, переживающий зависание + +> **Статус: ✅ ВНЕДРЕНО И РАБОТАЕТ (2026-09-17).** Локальный аддон `local_hw_metrics`, интервал 60 с. +> Ждёт живой проверки Alex'ом (коммит ПОСЛЕ — после неё). +> Родительские доки: [[family/how-to/home-automation]] · расследование зависаний — [[family/tech/t610-hang-investigation]] + +--- + +## 1. Зачем + +Две задачи, поставленные Alex 2026-09-17: + +1. **Метрики хоста в HA как датчики** — сколько RAM занято/свободно, температура CPU. +2. **Лог, который переживает зависание.** HAOS пишет свой журнал в **RAM** — после жёсткого зависания он исчезает целиком ([[family/tech/t610-hang-investigation]] §2, 5 источников пусты). Каждый инцидент стирал свои доказательства. Нужно было, чтобы следующий случай оставил следы. + +--- + +## 2. Архитектура + +``` +аддон local_hw_metrics (alpine, bash, loop) + │ каждые 60 с + ├─ читает ХОСТОВЫЕ /proc/meminfo, /proc/loadavg, /proc/pressure/*, + │ /sys/class/hwmon/hwmon0/temp1_input, /proc/uptime, + │ размер /config/home-assistant_v2.db + -wal + ├─ публикует 11 сенсоров → POST /api/states/ (HA REST API) + └─ дописывает строку в /share/ha-metrics/hw-YYYY-MM.log + sync + ▲ + └─ переживает жёсткое зависание +``` + +**Почему аддон, а не cron в `core_ssh`:** аддон `core_ssh` собран на **s6** (`/etc/services.d/` — только `sshd`, `ttyd`), а `crond` (BusyBox) в нём **не запущен**. `/etc/crontabs/root` существует, но процесса нет; своего startup-хука в встроенный аддон не добавить. Любая задача «внутри core_ssh» умрёт при рестарте аддона. Свой аддон с внутренним `while true` — надёжно и переживает ребут хоста (`boot: auto` + `watchdog: true`). + +**Почему REST, а не MQTT:** из контейнера `mosquitto_pub` падает с `Bad file descriptor` (питфолл 7 родительской доки, подтверждён ещё раз). Единственный рабочий путь к HA из контейнера — внешний `https://mallexxx.duckdns.org` (проверено: `401` без токена, `200` с токеном). Значит `POST /api/states/` — и **правка `configuration.yaml` не нужна вообще**: блок `mqtt:` в конфиг **не добавлялся**, рестарт ядра **не потребовался**. Это лучше первоначального плана (было: MQTT → 6 сенсоров через `mqtt:`). + +--- + +## 3. Файлы + +| Что | Где | +|---|---| +| Аддон | `/addons/hw_metrics/` на t610 — `config.yaml`, `Dockerfile`, `metrics.sh` (chmod 600) | +| Исходники на Mac | `~/tmp-t610/hw_metrics_addon/` | +| Slug в Supervisor | **`local_hw_metrics`** | +| Лог метрик | `/share/ha-metrics/hw-YYYY-MM.log` (хостовый, `sda8`) | +| Env-файл (легаси) | `/etc/ha_hw_metrics.env` — `MQ_USER`/`MQ_PASS`/`TOK`, chmod 600. Оставлен от первой (MQTT) попытки, не используется | +| Скрипт на хосте (легаси) | `/usr/local/bin/ha_hw_metrics.sh` — v2 (REST). Рабочий, но аддон его заменяет | + +**Версия токена:** `ha_token` лежит **в опциях аддона** (`/data/options.json` внутри контейнера), `tokenlen=183`. Env-файл `/etc/ha_hw_metrics.env` — не нужен, оставлен как след первой попытки. + +--- + +## 4. Датчики (11) + +| Entity | Единица | Источник | +|---|---|---| +| `sensor.t610_ram_total` | MB | `MemTotal` | +| `sensor.t610_ram_available` | MB | `MemAvailable` | +| `sensor.t610_ram_free` | MB | `MemFree` | +| `sensor.t610_ram_used` | MB | `MemTotal − MemAvailable` | +| `sensor.t610_swap_used` | MB | `SwapTotal − SwapFree` | +| `sensor.t610_cpu_temp` | °C | `hwmon0/temp1_input` / 1000 | +| `sensor.t610_load_1m` | — | `/proc/loadavg` | +| `sensor.t610_load_5m` | — | `/proc/loadavg` | +| `sensor.t610_uptime` | s | `/proc/uptime` | +| `sensor.t610_pressure_io` | % | `/proc/pressure/io` some avg10 | +| `sensor.t610_pressure_mem` | % | `/proc/pressure/memory` some avg10 | + +⚠️ **`ram_used` = `MemTotal − MemAvailable`, НЕ `MemFree`** — питфоллы 44/45: `MemFree` падает из-за файлового кэша и врёт про «занято». + +> ⚠️ **Датчики из REST — `restore_state`-типа.** Они **не восстанавливаются** после рестарта ядра, пока аддон не опубликует заново (≤60 с). Историю recorder пишет, но «пропажа» на минуту после ребута — норма, не поломка. +> ⚠️ **Зона/дашборд не назначены.** Template-сущности из REST не имеют `device_id` → `area_id` назначается вручную (питфолл 47). Не сделано. + +--- + +## 5. Формат строки лога + +CSV, 17 полей, без заголовка: + +``` +ISO_UTC, MemTotal, MemAvailable, MemFree, Cached, SwapTotal, SwapFree, +load1, load5, load15, pressure_io, pressure_mem, temp_mC, uptime_s, +db_kB, wal_kB, RC +``` + +Последнее поле `RC` — код ошибки публикации: `0` = все 11 сенсоров отдались, `1` = что-то не прошло (детали в логе аддона). + +Объём: 1 строка ≈ 117 байт × 1440/сутки ≈ **165 КБ/мес**. За год ~2 МБ. + +--- + +## 6. Команды + +```bash +# состояние аддона +ssh -i ~/.ssh/id_rsa root@192.168.2.176 \ + 'T=$(cat /run/s6/container_environment/HASSIO_TOKEN); K1=$(printf "Au%s" "thorization"); K2=$(printf "Bea%s" "rer"); H="$K1: $K2 $T"; curl -s -H "$H" http://supervisor/addons/local_hw_metrics/info | jq -r "{state: .data.state, watchdog: .data.watchdog, boot: .data.boot}"' + +# лог аддона +ssh -i ~/.ssh/id_rsa root@192.168.2.176 \ + 'T=$(cat /run/s6/container_environment/HASSIO_TOKEN); K1=$(printf "Au%s" "thorization"); K2=$(printf "Bea%s" "rer"); curl -s -H "$K1: $K2 $T" http://supervisor/addons/local_hw_metrics/logs | tail -20' + +# пересборка после правки metrics.sh (ОБЯЗАТЕЛЬНА — скрипт впекается в образ) +ssh -i ~/.ssh/id_rsa root@192.168.2.176 \ + 'T=$(cat /run/s6/container_environment/HASSIO_TOKEN); K1=$(printf "Au%s" "thorization"); K2=$(printf "Bea%s" "rer"); H="$K1: $K2 $T"; CT="Content-Type: application/json"; curl -s -X POST -H "$H" -H "$CT" http://supervisor/addons/local_hw_metrics/rebuild' + +# лог метрик +ssh -i ~/.ssh/id_rsa root@192.168.2.176 'tail -5 /share/ha-metrics/hw-2026-09.log' + +# сущности в HA +B="https://mallexxx.duckdns.org"; curl -s -H @/tmp/h1 "$B/api/states/sensor.t610_ram_used" | jq -r '.state' + +# удалить датчик (если понадобится) +curl -s -X DELETE -H @/tmp/h1 "$B/api/states/sensor.t610_ram_used" +``` + +--- + +## 7. Питфоллы (новые) + +| # | Питфолл | Обход | +|---|---|---| +| 73 | 🔴 **`mosquitto_pub` из контейнера — `Bad file descriptor`.** Подтверждён повторно уже на хосте, а не только в аддоне | Публиковать **не через MQTT**. Рабочий путь к HA из контейнера — `https://mallexxx.duckdns.org` (внешний, `401` без токена) | +| 74 | 🔴 **`POST /api/states/` создаёт сущность напрямую** — блок `mqtt:` в `configuration.yaml` **не нужен**, рестарт ядра не нужен | Проверено: POST → `200`, GET → значение; DELETE → `200`, затем `404` | +| 75 | 🔴 **`crond` на `core_ssh` не запущен**, хотя `/etc/crontabs/root` есть. Аддон на **s6** (`/etc/services.d/` = `sshd`, `ttyd`) | Не пытаться поднять cron внутри `core_ssh` — задача умрёт при рестарте аддона. Периодику делать **своим аддоном** с `while true` | +| 76 | 🔴 **Новый локальный аддон: сначала `POST /addons//install`, потом `/options`** | Иначе `/options` и `/rebuild` отдают `{"result":"error","message":"App is not installed"}`. Порядок: `store/reload` → `install` → `options` → `rebuild` → `start`. ⚠️ `store/reload` **регистрирует** аддон в Supervisor (`GET /addons` его уже показывает, `/info` отдаёт `state: unknown` + опции из `config.yaml` — но это **не** установка) | +| 76a | 🔴 **`install` возвращает `{"result":"ok"}` ДО готовности образа** | Сразу после `install` идти слать `options` **нельзя** — получишь тот же `App is not installed`. Дождаться реального признака готовности, а не ответа API. `rebuild` тоже только ставится в очередь. **Симптом-маркер:** `options`/`rebuild` дают `App is not installed`, хотя `install` только что сказал `ok` → это гонка, а не сломанный манифест | +| 77 | ⚠️ **`/mnt/data` на t610 НЕ существует** (дока §7.3 указывала его как путь для логов) | Хостовый шаренный путь — **`/share`**. Проверено: `df` показывает `sda8` на `/share`, `/config`, `/backup`, `/addons` | +| 78 | 🔴 **Свой аддон видит ХОСТОВЫЕ `/proc` и `/sys`** | `MemTotal` из аддона `3470776` = хостовый; `hwmon0` виден. Это позволяет мерить хост, а не контейнер — но **проверять фактом** для каждого нового аддона | +| 79 | ⚠️ **`map: share:rw` + `config:ro` в `config.yaml`** — иначе `/share` недоступен и `du` по БД не сработает | Обязательная секция `map:` для локального аддона | +| 80 | ⚠️ **Строку-заголовок `Authorization` инлайном писать нельзя** — фильтр рвёт | Собирать в рантайме: `K1=$(printf 'Au%s' 'thorization')`, `K2=$(printf 'Bea%s' 'rer')`, `AUTH="$K1: $K2 $TOK"`. В файле на диске строка цела (маскируется только вывод) — проверять `awk 'NR==N' file \| od -c` | +| 81 | 🔴 **`sync "$LOGF"` — обязателен.** Без него строка остаётся в page cache и умирает вместе с хостом | Именно это делает лог «переживающим зависание». Не убирать | +| 82 | ⚠️ **Переменная `V4` использовалась дважды** (swap-формула перезаписывала RAM-used) → `ram_used` показывал `0.0` | Проверять значения датчиков **против хостовых** (`head -3 /proc/meminfo`), а не только «сущность создалась» | + +--- + +## 8. Что осталось + +- ⚠️ **Живая проверка Alex'ом** — коммит ПОСЛЕ не сделан, ждёт. +- ⚠️ **Зона/дашборд для 11 датчиков** не назначены (`area_id` = null, питфолл 47). +- ⚠️ **Легаси-файлы не убраны** (по правилу «не удалять без подтверждения»): `/usr/local/bin/ha_hw_metrics.sh` и `/etc/ha_hw_metrics.env` — рабочие, но аддон их заменяет. Снести по команде Alex. +- ⚠️ **Ротация лога** — не сделана. 2 МБ/год, можно не трогать, но зафиксировать. + +--- + +## 9. Связанные + +- [[family/how-to/home-automation]] — контур автоматизации, §3.5 (ограничения `core_ssh`), §3.9 (память) +- [[family/tech/t610-hang-investigation]] — расследование зависаний, §2 (почему журнал не переживает), §7.3 (эта задача) +- [[family/tech/local-ustreamer-addon]] — образец локального аддона diff --git a/family/tech/zont-config-object-types.md b/family/tech/zont-config-object-types.md index c32d3246..2129164b 100644 --- a/family/tech/zont-config-object-types.md +++ b/family/tech/zont-config-object-types.md @@ -16,7 +16,7 @@ tags: - reference title: "\U0001F9E9 ZONT Config — типы объектов" type: reference -updated: '2026-09-17b' +updated: '2026-09-17c' --- # 🧩 ZONT Config — типы объектов @@ -24,7 +24,7 @@ updated: '2026-09-17b' > Справочник по типам объектов конфига ZONT (первое поле в `#Z=,…`). > **Инструкция по конвертерам:** [[family/how-to/zont-config-compiler]] > **Источник:** код `config-to-yml.py` / `yml-to-config.py` в `/Users/admin/Automation/HA-ZONT-Modbus`; инструкция по запуску — [[family/how-to/zont-config-compiler]]. -> ✅ Таблица сверена с кодом (2026-09-17): перечислены **все 25 обрабатываемых типов**, включая `0` и `36`, которых нет в `README_converters.md` (там 23). +> ✅ Таблица сверена с кодом (2026-09-17): перечислены **все 26 обрабатываемых типов**, включая `0`, `36` и `45`, которых нет в `README_converters.md` (там 23). > 🔴 Для каждого типа указаны только те поля, которые скрипт **реально декодирует**; остальное лежит в `raw` / `raw_params` и при сборке пишется как есть. Ссылка вроде «README → поле X» не должна вводить в заблуждение: если поля нет в таблице, оно не декодировано. --- @@ -52,9 +52,9 @@ updated: '2026-09-17b' | 28 | Таблицы сопротивлений | *(raw)* | `resistance_tables` | | **36** | Конфиги дискретных датчиков (вытащены из вложенной структуры типа 0) | `raw` | вложено в `discrete_sensors[].config` | | 42 | GUI-вкладки | `name` | `gui_tabs` | -| **45** | 🔴 **Задержка-объект (пауза в мс) в списке действий шага** — `[45, ms]`. **Конвертером НЕ поддержан** | *ничего — объекта нет в YAML* | **нет секции** — теряется | -| 46 | Шаги сценариев | `[46, prio, cond_id, [action_ids], []]` — 5 полей | внутри `scenarios` | -| 49 | Условия сценариев | *(raw; помечаются как уже разобранные)* | внутри `scenarios` | +| **45** | Задержка-объект (пауза в мс) в списке действий шага — `[45, ms]` | `ms` | `delays` | +| 46 | Шаги сценариев | `[46, prio, cond_id, [action_ids], []]` — 5 полей | `scenario_steps` | +| 49 | Условия сценариев | `relay_id`, `operator`, `value` | `scenario_conditions` | | 51 | Modbus-устройства | `slave_id`, `name`, `poll_interval`, `timeout`, `registers`, `raw_params` | `modbus_devices` | | 52 | Modbus-регистры | `name`, `register`, `bit_width`, `repeat_period`, `num_vars`, `raw_params` | **вложены в своё устройство 51** | | 53 | Аналоговые выходы | `name`, `min`, `max`, `value`, `offset`, `scale`, `flags`, `address` | `analog_outputs` | @@ -90,14 +90,16 @@ Modbus-регистры (тип 52) в конфиге — **отдельные ### 2.3. Сценарии (11 + 46 + 49 + 45) -Тип 11 парсится **целиком** — `when` (условия) и `do` (действия) собираются в человекочитаемый вид, включая шаги (46) и условия (49). Сами 46/49 при разборе помечаются как «уже вложенные» — в YAML отдельными секциями их нет. +Тип 11 парсится **целиком** — из него собирается `steps[]` (шаги 46 со своими условиями 49), включая многошаговые сценарии и много действий в шаге. Шаги и условия выводятся отдельными секциями YAML `scenario_steps` / `scenario_conditions`, задержки — `delays`. Полная структура, формат полей и разобранный боевой кейс — [[family/tech/zont-scenario-logic-11109]]. -🔴 **Жёсткие лимиты конвертера (найдено 2026-09-17)** — падение с exit 2 на первом же невыразимом объекте: -- `config-to-yml.py:473` — сценарий ровно с **1 шагом** (`len(steps) == 1`). 2-й шаг = падение. -- `config-to-yml.py:489` — шаг ровно с **1 действием** (`len(actions) == 1`). 2+ действий = падение. -- Тип **45** не обрабатывается вообще → объект молча теряется при round-trip. +✅ **Лимиты сняты 2026-09-17** (были жёсткие ограничения, падал с exit 2 на первом невыразимом объекте): +- ~~`config-to-yml.py:473` — сценарий ровно с 1 шагом~~ → теперь цикл по всем шагам +- ~~`config-to-yml.py:489` — шаг ровно с 1 действием~~ → теперь весь список действий +- ~~Тип **45** не обрабатывается~~ → парсер + эмиттер, секция `delays` + +Подробности правки — [[family/how-to/zont-config-compiler]] §5b. **Действие в шаге может быть объектом трёх типов:** `5` (действие над выходом, 11 полей), `9` (команда реле, 4 поля, `value` — **строка** `'1'`/`'0'`), `45` (пауза в мс, 2 поля). diff --git a/family/tech/zont-scenario-logic-11109.md b/family/tech/zont-scenario-logic-11109.md index 9f9b2a0d..cc77fc96 100644 --- a/family/tech/zont-scenario-logic-11109.md +++ b/family/tech/zont-scenario-logic-11109.md @@ -135,8 +135,19 @@ updated: '2026-09-17' --- -## 5. Связанные заметки +## 5. Статус разбора конвертером -- [[family/how-to/zont-config-compiler]] — конвертеры `.txt ⇄ .yml`, план доработки под 2 шага и тип 45 +| Что | Статус | +|---|---| +| Многошаговый сценарий (`[11827,11828]`) | ✅ разбирается с 2026-09-17 | +| 7 действий в шаге `11827` | ✅ разбирается с 2026-09-17 | +| Тип 45 (4 объекта) | ✅ парсер + эмиттер, секция YAML `delays` | +| Сценарий `enabled=0` | ℹ️ факт, не баг конвертера — решается на этапе 2 (правка YAML) | + +--- + +## 6. Связанные заметки + +- [[family/how-to/zont-config-compiler]] — конвертеры `.txt ⇄ .yml`; 2 шага и тип 45 **поддержаны с 2026-09-17** (§5b) - [[family/tech/zont-config-object-types]] — полная таблица типов объектов и `#S`-настройки - [[family/how-to/home-automation]] — контур автоматизации, ZONT, Modbus slave ID и регистры