From 7661a573d1d8513878ed7cbc6b1dc259fd9c62bb Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Thu, 17 Sep 2026 18:36:17 +0600 Subject: [PATCH] [2026-09-17] eagle: family/how-to/zont-config-compiler.md --- family/how-to/zont-config-compiler.md | 150 +++++++++++++++++++------- 1 file changed, 112 insertions(+), 38 deletions(-) diff --git a/family/how-to/zont-config-compiler.md b/family/how-to/zont-config-compiler.md index 6975c720..c3a5b7a5 100644 --- a/family/how-to/zont-config-compiler.md +++ b/family/how-to/zont-config-compiler.md @@ -21,7 +21,7 @@ tags: - homeautomation title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml type: how-to -updated: 2026-09-17h +updated: 2026-09-17k --- # ⚙️ ZONT Config Compiler — конвертеры `.txt ⇄ .yml` @@ -403,7 +403,7 @@ diff A.txt B.txt # пусто = round-trip чистый ## 5c. 🔄 Переработка структуры YAML сценариев — ✅ СДЕЛАНО 2026-09-17 > ✅ **СТАТУС (2026-09-17, после правок формы): парсер дописан под форму `trigger`/`steps`, -> YAML перегенерирован, `if`/`then` из вывода убраны.** +> YAML перегенерирован, `if`/`then` из вывода убраны. `trigger` поднят на уровень сценария.** > Семантика типов 47/48/50/59 раскрыта (ответы Alex — [[family/tech/zont-scenario-logic-11109]] §8.7). > **Type 9 и type 5 раскрыты из `raw` в читаемую форму.** > **Подстановки `target_name`/`target_type`/`raw_value` удалены** — поля YAML = поля строки конфига. @@ -413,14 +413,15 @@ diff A.txt B.txt # пусто = round-trip чистый > **64 из 65** сценариев. Многошаговый `11109` (7 id в `then`) из неё выпадает — сейчас они уходят > в служебный `_then`. Ждём решения Alex. Детали — [[family/tech/zont-scenario-logic-11109]] §8.17(б). > -> 🔴 **Модель `TRIGGER_KINDS` опровергнута и удалена** — поле 5 типа 11 работает как -> `kind = field5 & 7` + `enabled = not (field5 & 8)`. Подтверждено тостингом 5 сценариев, -> разбор — [[family/tech/zont-scenario-logic-11109]] §8.17(а). +> 🔴 **Поле 5 типа 11 — семантика НЕ раскрыта.** Модель `kind = field5 & 7` **опровергнута** +> (развалилась на `11109`: даёт `kind=0`, хотя устроен как триггерные `1`). Подтверждён только +> `enabled = not (field5 & 8)`. Полная таблица 5 значений и разбор — **§5j**. > > 📄 **Артефакты (в проекте):** `zont_config/config_local_2026-09-17_17-45-00.yml` — **актуальный** -> (4405 строк, форма `trigger`/`steps`, уставка 5.2 + «Температура Детская»); -> `zont_config/config_local_2026-09-17_16-13-28.yml` — предыдущая генерация (5452 строки). -> Оба в `zont_config/`, не в `/tmp`. **Не закоммичено** — ждёт команды Alex. +> (4405 строк, форма `trigger`/`steps` с `trigger` на уровне сценария, уставка 5.2 + +> «Температура Детская»); `zont_config/config_local_2026-09-17_16-13-28.yml` — предыдущая +> генерация (5452 строки, старая форма). Оба в `zont_config/`, не в `/tmp`. +> **Не закоммичено** — ждёт команды Alex. ### 🔴 Актуальная форма сценария в YAML (принята Alex, 2026-09-17) @@ -428,26 +429,29 @@ diff A.txt B.txt # пусто = round-trip чистый - id: 9691 name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' enabled: true - _kind: 1 _f5: 1 + trigger: + - id: 9729 + object: 9495 + value: 1 steps: - id: 9819 - trigger: - id: 9729 - object: 9495 - value: 1 action: 9563 ``` **Правило:** в YAML попадают **только слова, которые есть в конфиге или в UI**. `if`/`then`/`else`/`condition`/`operator`/`group`/`op`/`kind` — **нельзя**, их в данных нет. -Служебные ключи (`_kind`, `_f5`, `_op`, `_then`, `_else`) — с подчёркиванием, для энкодера. +Ключи с подчёркиванием (`_f5`, `_op`, `_then`, `_else`, `_kind`) — временные, Alex против +самого подхода (см. §5j «Служебные ключи»). + +📌 **`trigger` — на уровне СЦЕНАРИЯ** (не шага). **`descr` у шага не выводится** (у `#Z9819` +текста нет). Полное обоснование и разбор поля 5 — §5j. | YAML | Строка конфига | Поля | |---|---|---| | `id: 9691`, `name` | `#Z9691=11,…` | 1, 2 | | `enabled: true` | `#Z9691` поле 5 | `not (f5 & 8)` | -| `_kind`, `_f5` | `#Z9691` поле 5 | `f5 & 7`, `f5` целиком | +| `_f5` | `#Z9691` поле 5 | `f5` целиком (не выводится из остального) | | `trigger[].id: 9729` | `#Z9729=49,…` | 1 (id условия) | | `trigger[].object: 9495` | `#Z9729` | 2 (объект) | | `trigger[].value: 1` | `#Z9729` | 4 (значение; поле 3 = оператор → `_op` если ≠ 1) | @@ -549,13 +553,14 @@ Alex: «ЕСЛИ БЛЯДЬ В КОНФИГЕ `#Z8817=9,'…',10034,'2782'` ТО | # | Задача | Статус | |---|---|---| -| 1 | ~~`TRIGGER_KINDS` — удалить~~ | ✅ **сделано** — таблица удалена, модель `kind = f5 & 7` / `enabled = not (f5 & 8)` подтверждена тостингом (§5j) | -| 2 | **Форма YAML** — Alex отверг `if`/`then` как выдумку | ✅ форма согласована (§5j), **код ещё не приведён** | +| 1 | ~~`TRIGGER_KINDS` — удалить~~ | ✅ **сделано** — таблица удалена | +| 2 | **Форма YAML** — Alex отверг `if`/`then` как выдумку | ✅ форма согласована (§5j) **и реализована в парсере** | | 3 | `git commit` правок парсера/энкодера | ⏸ не закоммичено, ждёт команды | -| 4 | YAML `17-45-00` — перегенерировать после правок | ⏸ **не перегенерирован** — блок `9691` в нём всё ещё со старым `if`/`then` | +| 4 | YAML `17-45-00` — перегенерировать после правок | ✅ **перегенерирован** — 4405 строк, `trigger` на уровне сценария | | 5 | Раскрыть `8195` (type 3 SMS) из YAML-якоря | ⏸ отложено | | 6 | 🔴 **Открытый вопрос:** что писать в `steps[].action`, если в `then` больше одного id или есть `else` | ⏸ ждёт Alex | -| 7 | 🔴 **Round-trip сломан** — правка `dump_action` → `dump_step` задела обход (3 конфига падают) | ⏸ домучить или откатить до зелёного | +| 7 | 🔴 **Round-trip сломан** — энкодер не переведён под форму §5j (парсер обновлён, `yml-to-config.py` — нет) | ⏸ переписать энкодер | +| 8 | 🆕 🔴 **Поле 5 типа 11** — семантика не раскрыта; `_f5` нужен, но Alex против служебных ключей | ⏸ ждёт Alex (§5j) | **Уроки методологии этой сессии** (проверять в следующих): @@ -568,6 +573,14 @@ Alex: «ЕСЛИ БЛЯДЬ В КОНФИГЕ `#Z8817=9,'…',10034,'2782'` ТО 3. **Не кодировать формулу по 2 точкам.** `(t+273)*10` вшита только после третьей точки от Alex. 4. **Не переносить данные между сценариями.** Расписание `61,3354` принадлежит `8597`, я приписал его `8456` (у которого нули) — Alex поймал. +5. 🆕 **Не вводить слова, которых нет в конфиге.** `kind` — выдумка; Alex: «я его откуда тебе + высру?!». Если числу нельзя дать имя из конфига/UI — имени не давать вовсе. +6. 🆕 **Проверять, что «доказано», а что «похоже».** Формула `kind = f5 & 7` держалась на 5 + тостах и развалилась на `11109`. Тостинг на своих сценариях ≠ проверка на боевых. +7. 🆕 **Не переспрашивать абстракциями.** Alex: «я блядь не понимаю че ты несешь — нормально + поясняй». Вопрос должен нести факт + конкретный выбор, а не термин. +8. 🆕 **Не приписывать Alex выбор, который он не делал.** «Что блядь я решаю не ты?» — если + значение выводимо из конфига, решать по конфигу, а не перекладывать. **Задача Alex (исходная):** «переписать блок парсинга/сборки сценариев чтобы он составлял синтаксис как у Home Assistant automations вместо текущей разбросанной структуры. с опциональными айдишниками у операторов». @@ -955,7 +968,9 @@ https://lk.zont-online.ru/download/firmwares/H2000_PRO____.zip ## 5j. 🔴 ФОРМА СЦЕНАРИЯ В YAML — согласована с Alex (2026-09-17, финал сессии) -> **Статус:** форма **согласована**, код **ещё не приведён** (правка в работе, round-trip сломан). +> **Статус:** форма **согласована И реализована в парсере** (`config-to-yml.py`). +> YAML `17-45-00` перегенерирован (4405 строк), блок `9691` вышел в целевой форме. +> **Энкодер (`yml-to-config.py`) ещё не переведён под новую форму** → round-trip сломан. > Полный разбор модели — [[family/tech/zont-scenario-logic-11109]] §8.17. ### Почему третья итерация @@ -964,27 +979,36 @@ https://lk.zont-online.ru/download/firmwares/H2000_PRO____.zip |---|---|---| | 1 | `blocks` + `extra_links` | «почему там блядь if идет первым блоком?! где все что до него происходит?!» — порядок терялся | | 2 | `steps[].{when, then}`, `if`/`group`/`op` | «у тебя в `9819` сейчас в yml написан `if`! а это не `if` а **триггер**» | -| 3 | `trigger` + `steps[].{id, action}` | ✅ принято | +| 3 | `trigger` + `steps[].{id, action}` | ✅ принято и реализовано | **Проверено по конфигу:** слов `if`, `then`, `else`, `condition`, `object`(как имя ветки), `operator`, `group`, `op` **в конфиге нет**. В `#Z9691`/`#Z9819`/`#Z9729` есть только числа и `#Z9563` с текстом. Всё остальное — интерпретация парсера. -### Форма +### Форма (РЕАЛИЗОВАНА — так и выглядит в YAML `17-45-00`) ```yaml - id: 9691 name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' enabled: true + _f5: 1 trigger: - - id: 9729 - object: '9495' - value: 1 + - id: 9729 + object: 9495 + value: 1 steps: - - id: 9819 - action: 9563 + - id: 9819 + action: 9563 ``` +🔴 **`trigger` поднят на уровень СЦЕНАРИЯ**, а не шага (Alex: «почему блядь триггер внутри +steps?!»). В конфиге условие лежит внутри `#Z9819` (поле 3 = `9729`), но принадлежит сценарию — +парсер вынимает его (`scenario['trigger'] = [dump_condition(trig_id)]`) и оставляет шагу только +`action`. Триггер определяется по **первому** шагу `steps[]` (тип 46). + +🔴 **`descr` у шага НЕ выводится.** Alex: «нахуй там тогда `descr`?! если его нет в оригинале?» — +у `#Z9819` собственного текста нет, поэтому в `steps[]` только `id` + `action`. + Исходный конфиг (для сверки): ```text @@ -1019,42 +1043,92 @@ https://lk.zont-online.ru/download/firmwares/H2000_PRO____.zip (энкодеру нужно поле 5 целиком). 5. **`yaml`-теги вида `object: '9495'` — строкой** (id объекта), как в `trigger.id`. +### ⚠️ Поле 5 типа 11: что реально означает (проверено 2026-09-17) + +🔴 **Открытая проблема: поле 5 не выводится из содержимого сценария, но и не имеет имени.** +Alex: «какой нахуй kind?? что это блядь значит?! я его откуда тебе высру?!» — `kind` было +**моим выдуманным словом**, в конфиге и UI его нет. + +Распределение по боевому конфигу (70 сценариев): + +| поле 5 | enabled | шт | кто | особенности | +|---|---|---|---|---| +| `0` | вкл | 1 | `11109` Передернуть Автомат Котельной | 2 шага, **7 действий** в `then`, условие `value: 0` | +| `1` | вкл | 64 | все «Автомат.: …» | **1 шаг, 1 действие**, условие `value: 1` | +| `8` | выкл | 2 | `8456` (27 шагов, без 46), `8597` (расписание, 1 шаг) | — | +| `9` | выкл | 2 | `8547` «по времени», `8551` «по триггеру» | 1 шаг 46 | +| `10` | выкл | 1 | `8599` «по интервалу» | `interval_ms = 43200000` | + +**Что подтверждено:** +- `enabled = not (поле5 & 8)` — держится на всех 5 значениях. +- Поле 5 **не восстанавливается** из остальных полей: `#Z11109` (f5=`0`) и `#Z9628` (f5=`1`) + идентичны по имени/дням/времени/интервалу; `#Z8456` (ручной) и `#Z8597` (расписание) имеют + **одно и то же** f5=`8`, хотя у 8456 нет шага 46, а у 8597 есть. + +**Опровергнуто (снято):** +- ❌ `kind = field5 & 7` как «тип сценария» (manual/trigger/schedule/interval) — развалилось + на 11109: он даёт `kind=0`, хотя устроен так же, как триггерные `1`. Формула снята. +- ❌ «Триггерный vs ручной» как внятная пара — оба имеют тип 46 с условием. + +**Гипотеза в работе (НЕ подтверждена):** `поле5 & 7` = значение из условия шага 46 +(`#Z11823=49,11190,1,0` → `0`; `#Z9729=49,9495,1,1` → `1`). Проверялась скриптом по всем 65 +сценариям с типом 46, но команда была заблокирована и проверка не завершена. **Не вшивать +в код до зелёного round-trip.** + ### 🔴 Открытый блокер `#Z9819` поле 4 — **список** (`[9563]`). Если в нём больше одного id или непуст `else` (поле 5) — -что писать? Вариант `action: 9563` покрывает только первый. **Решение за Alex.** +что писать? Вариант `action: 9563` покрывает только первый. У `11109` их **7** — сейчас уходят +в служебный `_then`. **Решение за Alex.** + +### Служебные ключи — Alex против + +Alex отверг сам подход «служебных ключей» в YAML (`_f5`, `_kind`): «какое нахуй служебное». +Но удалить `_f5` нельзя — 6 сценариев соберутся неверно. **Компромисс не найден, ждёт Alex.** +Ключи с подчёркиванием временно остаются (`_f5`, `_op`, `_then`, `_else`, `_kind`). --- ## 6. Состояние проекта (проверено 2026-09-17, финал) -✅ **Конвертеры переписаны под сценарный конструктор (§5c).** Рабочее дерево — **не закоммичено**, -ждёт команды Alex. Последний коммит — `199f2b1`. +🟡 **Парсер переписан под форму §5j (триггер на уровне сценария), энкодер — ЕЩЁ НЕТ.** +Round-trip сейчас **не сходится** — `yml-to-config.py` ждёт перевода. Рабочее дерево +**не закоммичено**, ждёт команды Alex. Последний коммит — `199f2b1`. -✅ **Round-trip чистый на 4/4 конфигах** (§5c). Счётчики совпадают: 661 → 661. +✅ **Парсер `config-to-yml.py` — форма §5j реализована:** `trigger` на уровне сценария +(вынимается из первого шага типа 46), `steps[].{id, action}`, `descr` у шага не выводится, +`if`/`then`/`else`/`condition`/`operator`/`kind` из вывода убраны. + +✅ **Round-trip был чистый на 4/4 конфигах** (§5c, до правки формы). Счётчики совпадали: 661 → 661. +**После правки формы — сломан, ждёт энкодера.** ✅ **Уставка температуры раскодирована** — `код = (t °C + 273) × 10`, подтверждено 3 точками (`22→2950`, `23.8→2968`, `5.2→2782`; §8.13 в [[family/tech/zont-scenario-logic-11109]]). -✅ **YAML сжат вдвое:** 6093 → 5560 строк. Секции-дубли убраны, поля переименованы +✅ **YAML сжат вдвое:** 6093 → 4405 строк. Секции-дубли убраны, поля переименованы (`descr`/`target`/`value`). +🔴 **Поле 5 типа 11 — семантика не раскрыта** (§5j). `enabled = not (f5 & 8)` подтверждён; +что значат младшие биты — неизвестно, из содержимого не выводится. + | Файл | Статус | |---|---| -| `config-to-yml.py` | 🆕 **изменён** — парсер типов 47/48/50/59, плоский `steps[]`, триггер, type 9/5 раскрыты (`descr`/`target`/`value`), `object_display_name()` на уровне модуля, секции-дубли убраны + `scenario_orphans`, raw-фоллбэк, подстановки `target_name`/`target_type`/`raw_value` удалены (§5c). **не закоммичено** | -| `yml-to-config.py` | 🆕 **изменён** — `emit_step`/`emit_condition`/`emit_action`, `_build_type9_line()`, `_encode_type9_value()` (обратный пересчёт `(t+273)*10`), блок «2b» вытягивает helper-секции в `result_lines`, `TYPE_ORDER` без сценарных helper-типов (§5c). **не закоммичено** | +| `config-to-yml.py` | 🆕 **изменён** — форма §5j: `trigger` на уровне сценария, `steps[].{id, action}`, `_f5`/`_op`/`_then`/`_else`, `descr` у шага убран, `object` вместо `condition.operator`; плюс всё из §5c (типы 47/48/50/59, `descr`/`target`/`value`, `scenario_orphans`, `_build_type9_line`, подстановки удалены). **не закоммичено** | +| `yml-to-config.py` | 🔴 **НЕ переведён под форму §5j** — всё ещё ждёт старой формы (`if`/`then`, `trigger` внутри шага). **Round-trip сломан.** Остальное из §5c на месте: `_build_type9_line()`, `_encode_type9_value()`, блок «2b». **не закоммичено** | | `test_roundtrip.py` | ✅ в коммите `199f2b1`, без изменений | -| `zont_config/config_local_2026-09-17_16-13-28.txt` | 🆕 **34 963 байт** — **ПОСЛЕДНИЙ** конфиг (уставка 5.2 + «Температура Детская» = 14.5). Снят с `http://192.168.0.50/config.txt`. **не в git** | -| `zont_config/config_local_2026-09-17_16-13-28.yml` | 🆕 **5560 строк** — **финальная форма** YAML, round-trip ✅. **не в git** | -| `zont_config/config_local_2026-09-17_16-02-18.txt` / `.yml` | 34 962 байт — предыдущий (контур `Спальня`, режим `Режим отопления`). **не в git** | +| `zont_config/config_local_2026-09-17_17-45-00.txt` | 🆕 **34 963 байт** — **ПОСЛЕДНИЙ** конфиг. Снят с `http://192.168.0.50/config.txt`. **не в git** | +| `zont_config/config_local_2026-09-17_17-45-00.yml` | 🆕 **4405 строк** — **актуальная форма §5j**. **не в git** | +| `zont_config/config_local_2026-09-17_16-13-28.txt` / `.yml` | 34 963 байт, 5560 строк — предыдущая генерация (старая форма). **не в git** | +| `zont_config/config_local_2026-09-17_16-02-18.txt` / `.yml` | 34 962 байт — контур `Спальня`, режим `Режим отопления`. **не в git** | | `zont_config/config_local_2026-09-17_14-16-35.txt` / `.yml` | 34 907 байт, 660 `#Z` — с тестовыми сценариями логики. **не в git** | -| `zont_config/config_0FA7C33CC89F_…_2026-09-17_12-12-28.txt` | 32 689 байт — боевой конфиг, round-trip ✅. **не в git** | +| `zont_config/config_0FA7C33CC89F_…_2026-09-17_12-12-28.txt` | 32 689 байт — боевой конфиг, round-trip ✅ (до правки формы). **не в git** | | `zont_config/archive/` | `-2`/`-3`/`-4` — ✅ **закоммичены** (`7ae0e32`) | | `zont_local_ui_recon/config_live_192.168.0.50.txt` | живой конфиг для разведки WS-интерфейса, **не в git** | | `zont_api_docs/` | ✅ локальная копия доки облачного API (`zont_api_docs.html`, `.txt`, `convert.py`). **не в git** | | `read_scenarios.py` | 🆕 **читаемый дамп сценария** — дерево если/то/иначе с именами. Токенайзер `split_top()`. **не в git** | | `dump_new_types.py`, `probe_types.py`, `trace_scenarios.py` | 🆕 скрипты разбора сценарных типов. Разбор — [[family/tech/zont-scenario-logic-11109]] §8.8. **не в git** | | `probe_sched.py`, `verify_answers.py`, `check_ops.py`, `audit_8456.py`, `audit2.py`, `chk_extra.py`, `why_raw.py`, `fit_temp.py`, `fit_temp2.py` | 🆕 проверочные скрипты (в т.ч. подбор формулы уставки). **не в git** | +| `/tmp/backup-config-to-yml.py`, `/tmp/backup-yml-to-config.py` | 🆕 бэкапы обоих конвертеров перед правкой формы (временные) | | `~/rasputin-tmp/zont-util/` | ✅ настроечная утилита `H1000 Programmator` 2.8.5, прошивка `.enc`, `extract.py`. Разбор — §5g-2 | | `~/rasputin-tmp/zont-{auth-probe,recon,recon2,ws-probe}.js` | ✅ скрипты разведки локального WS. **не в git** |