From 80b53afc3e0d05b4e2f2c5c44ac70b794f67ca5c Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Thu, 17 Sep 2026 18:21:00 +0600 Subject: [PATCH] [2026-09-17] eagle: family/how-to/zont-config-compiler.md family/tech/zont-config-object-types.md family/tech/zont-scenario-logic-11109.md --- family/how-to/zont-config-compiler.md | 195 ++++- family/tech/zont-config-object-types.md | 30 +- family/tech/zont-scenario-logic-11109.md | 927 +++++------------------ 3 files changed, 390 insertions(+), 762 deletions(-) diff --git a/family/how-to/zont-config-compiler.md b/family/how-to/zont-config-compiler.md index f9830733..6975c720 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-17g +updated: 2026-09-17h --- # ⚙️ ZONT Config Compiler — конвертеры `.txt ⇄ .yml` @@ -146,10 +146,9 @@ updated: 2026-09-17g ### Как это выглядит в YAML -> ✅ **НОВАЯ форма (2026-09-17, §5c).** Все сценарии — один плоский список `steps[]` в исходном -> порядке поля 2, каждый элемент с телом на месте; плюс блок `trigger:`. Плоские `when`/`then` и -> дележ на `blocks`/`extra_links` убраны (тот вариант был отвергнут Alex — см. §5c). -> **Форма `11109` ниже — устаревшая**, оставлена для истории; актуальная — §5c. +> ✅ **АКТУАЛЬНАЯ форма (2026-09-17).** Сценарий = `trigger:` (условие) + `steps[]` (шаги, +> каждый со своим `action:`), тела инлайн. **Форма — §5c.** Здесь оставлены только исторические +> варианты, чтобы было видно, что отвергнуто Alex и почему. **Устаревшая форма (коммит `199f2b1`)** — плоский `when`/`then` для 1-шаговых + `steps` + `extra_links`: @@ -334,7 +333,12 @@ diff A.txt B.txt # пусто = round-trip чистый | 29 | ⚠️ **Удаление `raw_value` требует пересчёта кода в энкодере** | Если убрать сырьё, но оставить `value` (Цельсии), энкодер напечатает `5.2` вместо `'2782'` → round-trip падает. Нужен `_encode_type9_value()`: `True→'1'`, `False→'0'`, число → `str(round((v+273)*10))` | | 30 | ⚠️ **Ветки type 5 / type 9 в энкодере различать по признаку, а не по наличию удалённого поля** | Различались через `'raw_value' not in step` — после удаления признак мёртв. Теперь: type 5 — есть `value` и `target > 255` (output_ref), type 9 — нет `args` | | 31 | 🔴 **Один объект в двух секциях → два разных `value`** | Раскодировал `value` в `steps[]` (`5.2`), но забыл секцию `relay_commands` (осталось `'2782'`). **Round-trip зелёный** — он сравнивает строки конфига, а не смысл полей. Правило: правя декодирование, пройти **все** места, где объект отображается. Нашёл Alex глазами | -| 32 | 🔴🔴 **Поле 5 типа 11 — модель `TRIGGER_KINDS` ОШИБОЧНА (выдумка)** | Сопоставление `1=manual, 8=расписание, 9=триггер, 10=интервал` **опровергнуто** Alex: `9628` с `1` — по триггеру; `8456` с `8` — ручной+disabled; `8547`/`8551` оба `9`, но это «по времени» и «по триггеру». Разбор — [[family/tech/zont-scenario-logic-11109]] §8.16. **`TRIGGER_KINDS` подлежит удалению** | +| 32 | 🔴🔴 **Поле 5 типа 11 — модель `TRIGGER_KINDS` ОШИБОЧНА (выдумка)** | ✅ **ИСПРАВЛЕНО 2026-09-17**: таблица удалена из кода, поле 5 = `kind = f5 & 7` + `enabled = not (f5 & 8)`. Подтверждено тостингом 5 сценариев — [[family/tech/zont-scenario-logic-11109]] §8.17(а) | +| 33 | 🔴 **`config-to-yml.py` пишет YAML в stdout, `-o` не существует** | `main()` принимает **только** входной `.txt` (`sys.argv[1]`). Вызов с `-o file` молча печатает `Использование: zont_to_yaml.py ` → **exit 1**, целевой файл остаётся **старым** (выглядит как «патч не сработал»). Правильно: `python3 config-to-yml.py in.txt > out.yml` + проверка `exit=` | +| 34 | 🔴 **`if`/`then`/`else` в YAML — выдумка парсера, не поля конфига** | Alex: «у тебя в `9819` сейчас в yml написан `if`! а это не `if` а **триггер**». Слов `if`/`then`/`condition`/`operator`/`group`/`op` в конфиге **нет**. Форма: `trigger:` (условие) + `steps[].action:` (что зовёт шаг). Правило — только слова из конфига/UI | +| 35 | 🔴 **`descr` не ставить туда, где текста нет в строке** | У `#Z9819` (шаг) своего текста нет — значит `descr` у него быть не должно. Alex: «нахуй там тогда descr?! если его нет в оригинале?» | +| 36 | ⚠️ **`grep -n "id: N"` по YAML даёт несколько совпадений** | Объект живёт и в своей секции, и внутри сценария. Номер строки меняется между генерациями (4877 → 3932) — индексировать по первому совпадению нельзя | +| 37 | ⚠️ **Не забегать вперёд с вопросами о том, чего в кейсе нет** | Вопрос «что писать, если в `trigger` окажется type 47» Alex воспринял как шум («что за type 47? нихуя не понятно»). Сначала форма на текущем кейсе, потом обобщение | ### Ограничения конвертера (найдено 2026-09-17) — ВСЕ ЗАКРЫТЫ @@ -398,25 +402,59 @@ diff A.txt B.txt # пусто = round-trip чистый ## 5c. 🔄 Переработка структуры YAML сценариев — ✅ СДЕЛАНО 2026-09-17 -> ✅ **СТАТУС (2026-09-17, финал): парсер и энкодер дописаны, форма — плоский `steps[]`, -> round-trip чистый на 4/4 конфигах.** Семантика типов 47/48/50/59 раскрыта (ответы Alex — -> [[family/tech/zont-scenario-logic-11109]] §8.7), операторы и логика подтверждены фактами -> конфига. **Type 9 и type 5 раскрыты из `raw` в читаемую форму.** +> ✅ **СТАТУС (2026-09-17, после правок формы): парсер дописан под форму `trigger`/`steps`, +> YAML перегенерирован, `if`/`then` из вывода убраны.** +> Семантика типов 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 = поля строки конфига. -> **YAML стал вдвое короче: 6093 → 5452 строки.** **Не закоммичено** — ждёт команды Alex. +> **YAML: 6093 → 4405 строк** после перехода на форму `trigger`/`steps`. > -> 🔴 **ОТКРЫТЫЙ БЛОКЕР (конец сессии):** модель `trigger` **опровергнута** Alex — -> `TRIGGER_KINDS` (`1=manual`, `8=расписание`, `9=триггер`) **выдумка**, поле 5 работает иначе. -> Подробности и факты — [[family/tech/zont-scenario-logic-11109]] §8.16(а). -> Также открыт вопрос Alex про саму форму YAML («не script editing language ли это») — §8.16(в). -> Код не тронут, ждём решения. +> 🔴 **ОТКРЫТЫЙ ВОПРОС (конец сессии):** форма «один триггер → одно действие» описывает +> **64 из 65** сценариев. Многошаговый `11109` (7 id в `then`) из неё выпадает — сейчас они уходят +> в служебный `_then`. Ждём решения Alex. Детали — [[family/tech/zont-scenario-logic-11109]] §8.17(б). > -> 📄 **Артефакты (в проекте):** `zont_config/config_local_2026-09-17_16-13-28.yml` — **финальный** -> разобранный конфиг (5452 строки, уставка 5.2 + «Температура Детская»); -> `zont_config/config_local_2026-09-17_16-02-18.yml` — предыдущий. -> Оба в `zont_config/`, не в `/tmp`. +> 🔴 **Модель `TRIGGER_KINDS` опровергнута и удалена** — поле 5 типа 11 работает как +> `kind = field5 & 7` + `enabled = not (field5 & 8)`. Подтверждено тостингом 5 сценариев, +> разбор — [[family/tech/zont-scenario-logic-11109]] §8.17(а). +> +> 📄 **Артефакты (в проекте):** `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. -### 🔴 Главное изменение формы: секции-дубли убраны (2026-09-17, финал) +### 🔴 Актуальная форма сценария в YAML (принята Alex, 2026-09-17) + +```yaml +- id: 9691 + name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' + enabled: true + _kind: 1 + _f5: 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`) — с подчёркиванием, для энкодера. + +| YAML | Строка конфига | Поля | +|---|---|---| +| `id: 9691`, `name` | `#Z9691=11,…` | 1, 2 | +| `enabled: true` | `#Z9691` поле 5 | `not (f5 & 8)` | +| `_kind`, `_f5` | `#Z9691` поле 5 | `f5 & 7`, `f5` целиком | +| `trigger[].id: 9729` | `#Z9729=49,…` | 1 (id условия) | +| `trigger[].object: 9495` | `#Z9729` | 2 (объект) | +| `trigger[].value: 1` | `#Z9729` | 4 (значение; поле 3 = оператор → `_op` если ≠ 1) | +| `steps[].id: 9819` | `#Z9819=46,…` | 1 (id шага) | +| `steps[].action: 9563` | `#Z9819` | 4 (первый id из `[9563]`) | + +### 🔴 Секции-дубли убраны (2026-09-17) Раньше каждый сценарный объект (45/46/47/48/49/50/59) жил **дважды**: тело инлайн в `steps[]` и голым id в отдельной служебной секции. Alex: «`delays:`, `scenario_conditions`, @@ -511,11 +549,13 @@ Alex: «ЕСЛИ БЛЯДЬ В КОНФИГЕ `#Z8817=9,'…',10034,'2782'` ТО | # | Задача | Статус | |---|---|---| -| 1 | **`TRIGGER_KINDS` — удалить.** Модель поля 5 типа 11 опровергнута фактами | ⏸ код не тронут, нужен ответ Alex про UI | -| 2 | **Форма YAML** — Alex усомнился, что получился не конфиг, а «script editing language» | ⏸ решение не принято | +| 1 | ~~`TRIGGER_KINDS` — удалить~~ | ✅ **сделано** — таблица удалена, модель `kind = f5 & 7` / `enabled = not (f5 & 8)` подтверждена тостингом (§5j) | +| 2 | **Форма YAML** — Alex отверг `if`/`then` как выдумку | ✅ форма согласована (§5j), **код ещё не приведён** | | 3 | `git commit` правок парсера/энкодера | ⏸ не закоммичено, ждёт команды | -| 4 | YAML `16-13-28` — перегенерировать после последней правки (`relay_commands` decode) | ⏸ на диске старая версия | +| 4 | YAML `17-45-00` — перегенерировать после правок | ⏸ **не перегенерирован** — блок `9691` в нём всё ещё со старым `if`/`then` | | 5 | Раскрыть `8195` (type 3 SMS) из YAML-якоря | ⏸ отложено | +| 6 | 🔴 **Открытый вопрос:** что писать в `steps[].action`, если в `then` больше одного id или есть `else` | ⏸ ждёт Alex | +| 7 | 🔴 **Round-trip сломан** — правка `dump_action` → `dump_step` задела обход (3 конфига падают) | ⏸ домучить или откатить до зелёного | **Уроки методологии этой сессии** (проверять в следующих): @@ -543,6 +583,14 @@ Alex отклонил: ### ✅ Реализованная форма — плоский `steps[]` (ФИНАЛЬНАЯ, 2026-09-17) +> 🔴🔴 **ПЕРЕСМОТРЕНА в конце сессии 2026-09-17 (третья итерация).** Форма ниже (`when`/`then`, +> `if`/`group`/`op`) **отвергнута Alex** как выдумка: +> +> > «у тебя в `9819` сейчас в yml написан `if`! а это не `if` а **триггер**» +> +> Слов `if` / `then` / `else` / `condition` / `operator` / `group` / `op` **в конфиге НЕТ**. +> Актуальная согласованная форма — **§5j**. Текст ниже оставлен как история двух отвергнутых итераций. + > 🔴 **ИСПРАВЛЕНО в конце сессии.** Первый вариант делил поле 2 на `blocks` (шаги 46) и > `extra_links` (голые id остальных объектов) — **`blocks` выводился первым, и весь сценарий > превращался в список цифр**: тела «установка температуры», `puts`, `set var1`, пауза 5 суток @@ -642,14 +690,26 @@ scenarios: **Логика type 48:** `0`=`and`, `1`=`or`, `2`=`not`. -**Триггер (поле 5 типа 11):** +**Триггер (поле 5 типа 11) — ✅ МОДЕЛЬ ИСПРАВЛЕНА 2026-09-17 (тостинг в UI):** -| Поле 5 | `trigger.type` | Доп. поля | -|---|---|---| -| `0`/`1` | `manual` | `enabled: bool` | -| `8` | `schedule` | `days[]`, `days_mask`, `time`, `time_raw` (поле 4 = маска дней, бит 0=ПН; поле 6 = `(час<<8)\|мин`) | -| `9` | `trigger` | — | -| `10` | `interval` | `interval_ms` | +```text +kind = field5 & 7 # 0 = ручной/расписание, 1 = триггер, 2 = интервал +enabled = not (field5 & 8) # бит 8 = ВЫКЛЮЧЕН +``` + +| Поле 5 вкл / выкл | `kind` | Что это | Доп. поля | +|---|---|---|---| +| `0` / `8` | 0 | ручной **или** расписание (различаются полями 3/4) | у расписания `days_mask`, `time` | +| `1` / `9` | 1 | триггер | — | +| `2` / `10` | 2 | интервал | поле 6 = `interval_ms` | + +Тосты: `8456` `8⇄0`, `8597` `8⇄0` (поля 3/4 = `61`,`3354`), `8547`/`8551`/`9628` `9⇄1`, +`8599` `10⇄2` (поле 6 = `43200000` мс). + +> 🔴 **Таблица `{0:manual, 1:manual, 8:schedule, 9:trigger, 10:interval}` — ОШИБКА (выдумка), +> удалена из кода.** Она читала число целиком, тогда как бит `8` — это «выключен». +> Из-за неё `9628` (триггерный, поле 5 = `1`) рендерился как `manual`. +> Формат `time`: `(час << 8) | минута`; `days_mask`: бит 0 = ПН. ### Реализация (код) @@ -893,6 +953,79 @@ https://lk.zont-online.ru/download/firmwares/H2000_PRO____.zip --- +## 5j. 🔴 ФОРМА СЦЕНАРИЯ В YAML — согласована с Alex (2026-09-17, финал сессии) + +> **Статус:** форма **согласована**, код **ещё не приведён** (правка в работе, round-trip сломан). +> Полный разбор модели — [[family/tech/zont-scenario-logic-11109]] §8.17. + +### Почему третья итерация + +| Итерация | Форма | Что сказал Alex | +|---|---|---| +| 1 | `blocks` + `extra_links` | «почему там блядь if идет первым блоком?! где все что до него происходит?!» — порядок терялся | +| 2 | `steps[].{when, then}`, `if`/`group`/`op` | «у тебя в `9819` сейчас в yml написан `if`! а это не `if` а **триггер**» | +| 3 | `trigger` + `steps[].{id, action}` | ✅ принято | + +**Проверено по конфигу:** слов `if`, `then`, `else`, `condition`, `object`(как имя ветки), +`operator`, `group`, `op` **в конфиге нет**. В `#Z9691`/`#Z9819`/`#Z9729` есть только числа и +`#Z9563` с текстом. Всё остальное — интерпретация парсера. + +### Форма + +```yaml +- id: 9691 + name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' + enabled: true + trigger: + - id: 9729 + object: '9495' + value: 1 + steps: + - id: 9819 + action: 9563 +``` + +Исходный конфиг (для сверки): + +```text +#Z9691=11,'Автомат.: Прихожая (н/п) (14/14) ВКЛ',[9819],0,0,1,0,0 +#Z9819=46,0,9729,[9563],[] +#Z9729=49,9495,1,1 +#Z9495=14,'Датч 14/14: Прихожая (н/п)',151408,0 +#Z9563=5,'Включить выход 14/14: Прихожая (н/п)',146064,1,0,0,[],0,0,0,512 +``` + +### Термины Alex — использовать дословно + +| Id | Слово Alex | Строка | YAML | +|---|---|---|---| +| `9691` | сценарий | `#Z9691=11,…` | `id`, `name`, `enabled` | +| `9729` | **триггер** (НЕ `if`) | `#Z9729=49,9495,1,1` | `trigger[].id` | +| `9495` | объект под наблюдением триггера | `#Z9495=14,…` | `trigger[].object` | +| `9819` | **шаг** | `#Z9819=46,…` | `steps[].id` | +| `9563` | **действие** («выполнить пользовательское действие») | `#Z9563=5,…` | `steps[].action` | + +> Alex: «`#Z9563=5,…` — это шаг «выполнить пользовательское действие» (action) `9563`». + +### Правила формы (выведены из этой итерации) + +1. **Источник полей — `#Z9819` (тип 46):** `trigger` ← поле 3 (`9729`), `steps[].action` ← поле 4 + (`[9563]`). Одна модель на оба: 46-шаг содержит и условие, и действие. +2. **`object`, а не `entity`** — слово уже используется в коде для ссылки на объект + (`dump_condition`: `'object': node[1]`), рядом с `target` (реле/контур) и `descr`. +3. **`descr` только там, где текст есть в строке.** У `#Z9819` текста нет → `descr` у него быть + не должно (Alex: «нахуй там тогда descr?! если его нет в оригинале?»). Текст живёт у `#Z9563`. +4. **Никаких `kind`** в видимой части — `kind`/`kind_raw` уходят в служебные ключи + (энкодеру нужно поле 5 целиком). +5. **`yaml`-теги вида `object: '9495'` — строкой** (id объекта), как в `trigger.id`. + +### 🔴 Открытый блокер + +`#Z9819` поле 4 — **список** (`[9563]`). Если в нём больше одного id или непуст `else` (поле 5) — +что писать? Вариант `action: 9563` покрывает только первый. **Решение за Alex.** + +--- + ## 6. Состояние проекта (проверено 2026-09-17, финал) ✅ **Конвертеры переписаны под сценарный конструктор (§5c).** Рабочее дерево — **не закоммичено**, diff --git a/family/tech/zont-config-object-types.md b/family/tech/zont-config-object-types.md index 042b1992..c8495e55 100644 --- a/family/tech/zont-config-object-types.md +++ b/family/tech/zont-config-object-types.md @@ -99,11 +99,33 @@ Modbus-регистры (тип 52) в конфиге — **отдельные ### 2.3. Сценарии (11 + 46 + 49 + 45) -> 🔴 **ИСПРАВЛЕНО 2026-09-17:** поле 5 типа 11 — **НЕ `enabled`**, а **тип запуска сценария** -> (`0`/`1` ручной, `8` расписание, `9` триггер, `10` интервал). Поле 6 — параметр запуска. -> Подробности и доказательства — [[family/tech/zont-scenario-logic-11109]] §8.1. +> 🔴 **ИСПРАВЛЕНО 2026-09-17 (тостинг в UI):** поле 5 типа 11 = **тип запуска + флаг «выключен»**: +> `kind = field5 & 7` (0 = ручной/расписание, 1 = триггер, 2 = интервал), +> `enabled = not (field5 & 8)`. Ручной и расписание — один `kind`, различаются полями 3/4. +> Подробности и тосты — [[family/tech/zont-scenario-logic-11109]] §8.17(а). -Тип 11 парсится **целиком** — из него собирается `steps[]` (шаги 46 со своими условиями 49), включая многошаговые сценарии и много действий в шаге. Шаги и условия выводятся отдельными секциями YAML `scenario_steps` / `scenario_conditions`, задержки — `delays`. +Тип 11 парсится **целиком** — из него собирается `steps[]` (шаги 46 со своими условиями 49), включая многошаговые сценарии и много действий в шаге. + +> 🔴 **ФОРМА ПЕРЕСМОТРЕНА (2026-09-17, финал).** Секции `scenario_steps` / `scenario_conditions` / +> `delays` / `scenario_scripts` / `scenario_raw_objects` **убраны** — тела живут только внутри +> `steps[]`. Осталась одна служебная секция `scenario_orphans`. Форма согласована Alex: +> +> ```yaml +> - id: 9691 +> name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' +> enabled: true +> trigger: +> - id: 9729 +> object: '9495' +> value: 1 +> steps: +> - id: 9819 +> action: 9563 +> ``` +> +> `trigger` ← поле 3 шага 46 (`9729`), `steps[].action` ← поле 4 (`[9563]`). +> Слова `if`/`then`/`condition`/`operator`/`group` в YAML **запрещены** — их нет в конфиге. +> Полностью — [[family/how-to/zont-config-compiler]] §5j. Полная структура, формат полей и разобранный боевой кейс — [[family/tech/zont-scenario-logic-11109]]. diff --git a/family/tech/zont-scenario-logic-11109.md b/family/tech/zont-scenario-logic-11109.md index 04a68b1d..505c76c4 100644 --- a/family/tech/zont-scenario-logic-11109.md +++ b/family/tech/zont-scenario-logic-11109.md @@ -1,753 +1,226 @@ ---- -aliases: - - ZONT сценарий 11109 - - Передернуть Автомат Котельной - - ZONT тип 45 задержка - - ZONT сценарная логика - - ZONT поле 2 сценария -created: '2026-09-17' -namespace: family -related: - - '[[family/how-to/zont-config-compiler]]' - - '[[family/tech/zont-config-object-types]]' - - '[[family/how-to/home-automation]]' -tags: - - family - - tech - - zont - - modbus - - homeautomation -title: 🔁 ZONT — сценарная логика и конструктор (11/46/47/48/49/50/59/45/9/5) -type: tech -updated: 2026-09-17g ---- +# 8.17. ✅ МОДЕЛЬ ЗАКРЫТА (2026-09-17, финал) — поле 5, форма сценария, type 47/49 -# 🔁 ZONT — сценарная логика и конструктор логики +> **Статус:** все открытые вопросы §8.16 закрыты. Ниже — подтверждённые факты. +> Таблица `TRIGGER_KINDS` **удалена из кода**; доказательство получено **тостингом 5 сценариев +> в UI**: Alex выключил и снова включил каждый тестовый сценарий, поле 5 снято до и после. -> 🔴 **ВНИМАНИЕ: раздел §1 ниже содержит УСТАРЕВШУЮ модель.** Поле 5 типа 11 — **НЕ «enabled»**, -> это **тип запуска сценария** (0/1 ручной, 8 расписание, 9 триггер, 10 интервал). Исправление и -> полная модель — **§8**. Раздел §1 оставлен как история разбора; актуальная семантика в §8. +## (а) 🔴 Поле 5 типа 11 = тип запуска + флаг «выключен». ПОДТВЕРЖДЕНО тостингом -Как устроены сценарии в конфиге ZONT и что именно разобрано в боевом конфиге -`config_0FA7C33CC89F_0FA7C33CC89F_2026-09-17_12-12-28.txt` (598 `#Z`, 25 `#S`, 65 сценариев). -Контекст конвертеров — [[family/how-to/zont-config-compiler]]. - ---- - -## 1. Структура сценария - -Сценарий — это **4 связанных типа объектов**: - -``` -#Z=11,'Имя',[<шаг>,<шаг>,…],0,0,,0,0 ← сценарий -#Z=46,,,[<действие>,…],[] ← шаг -#Z=49,,, ← условие -#Z=… ← действие (тип 5, 9 или 45) +```text +kind = field5 & 7 # 0 = ручной/расписание, 1 = триггер, 2 = интервал +enabled = not (field5 & 8) # бит 8 = сценарий ВЫКЛЮЧЕН ``` -| Тип | Роль | Формат | Поля | -|---|---|---|---| -| **11** | Сценарий | `[11, name, [step_ids], 0, 0, enabled, 0, 0]` | **8** полей. `enabled` — поле v[5] (0/1) | -| **46** | Шаг | `[46, prio, cond_id, [action_ids], []]` | **5** полей. `prio` и последнее поле — всегда 0/`[]` | -| **49** | Условие | `[49, relay_id, operator, value]` | **4** поля. `operator`: `1`=equals, иначе not_equals | -| **45** | Задержка | `[45, <миллисекунды>]` | **2** поля | +| Сценарий | Выкл | Вкл | `kind` | Прочие поля | +|---|---|---|---|---| +| `8456` «Простой тестовый» | `8` | `0` | 0 | — | +| `8597` «по расписанию» | `8` | `0` | 0 | поля 3/4 = `61`, `3354` | +| `8547` «по времени» | `9` | `1` | 1 | — | +| `8551` «по триггеру» | `9` | `1` | 1 | — | +| `9628` «Автомат.: Рад. ванная 2эт ВКЛ» | `9` | `1` | 1 | — | +| `8599` «по интервалу» | `10` | `2` | 2 | поле 6 = `43200000` мс | -**Каноническая форма** (99% конфига): 1 сценарий → 1 шаг → 1 условие → 1 действие. +**Почему старая модель `{0:manual,1:manual,8:schedule,9:trigger,10:interval}` была неверна:** +она читала число как «класс запуска» целиком. На самом деле число **двухбитовое по смыслу**: +младшие 3 бита = тип, бит `8` = выключено. Отсюда все противоречия §8.16: +`9628` с `1` — триггерный (бит 8 не стоит, kind = 1), `8456` с `8` — ручной, но выключенный. ---- +> ⚠️ **Ручной (0) и расписание (0) — один и тот же `kind`.** Различаются **наличием полей 3/4** +> (`days_mask` + `time`): у расписания они заполнены, у ручного — нули. +> Кодировку расписания см. §8.1 (`61` = ПН,СР,ЧТ,ПТ,СБ; `3354` = `(13<<8)|26` = 13:26). -## 2. Действия в шаге — три разных типа +**Что в коде:** `TRIGGER_KINDS`, `_raw_trigger_kind`, `_raw_trigger_params`, `time_raw` **удалены**. +Заголовок сценария в YAML теперь: `enabled`, `_f5` (поле 5 целиком), `_kind` (только если +`f5 & 7 ≠ 0`), `days_mask`, `time`, `interval_ms`. -Список действий шага (`46` поле 3) может содержать объекты **трёх** типов: +> ⚠️ **Обновлено после правок формы:** `kind`/`kind_raw` из видимой части **убраны** — это тоже +> была выдумка (слова `kind` в конфиге нет). Всё, что нужно энкодеру, лежит в служебных +> `_f5`/`_kind` с подчёркиванием. -| Тип | Что это | Формат | Декодирование | -|---|---|---|---| -| **5** | Действие над выходом контроллера | `[5, name, output_ref, 1, delay, impulse_dur, [], sched_bmp, sched_time, impulse_period, value]` | 11 полей. `output_ref >> 4` = id выхода; `delay` — поле 4 (мс) | -| **9** | Команда реле | `[9, name, target_relay, '1'/'0']` | 4 поля. `value` — **строка** `'1'`/`'0'`, не число | -| **45** | Пауза между действиями | `[45, ms]` | 2 поля | +## (б) Форма YAML сценария — ПЕРЕСМОТРЕНА Alex'ом (финал сессии) -> ⚠️ **Тип 45 ≠ `delay_ms` типа 5.** `delay_ms` — задержка **внутри** действия перед его выполнением. -> Тип 45 — **отдельный объект-пауза** в последовательности. Их часто путают при чтении конфига. +Alex отверг `if`/`then`/`else` в YAML как **выдумку**: ---- +> «у тебя в `9819` сейчас в yml написан `if`! а это не `if` а **триггер**» -## 3. Разобранный кейс: `#Z11109` «Передернуть Автомат Котельной» +Проверка конфига подтвердила: слов `if`, `then`, `else`, `condition`, `object`, `operator`, `group`, +`op` **в конфиге нет**. Это была интерпретация парсера поверх данных. -**Единственный сценарий в конфиге с нестандартной формой** — 7 действий в шаге и задержка (`45`) -прямо в поле 2 сценария (`11828`) вместо второго шага. См. §5 «Уточнение семантики поля 2». +**Корневое требование:** `trigger` — это **триггер**, а не `if`; `steps` — это **шаги**, а не ветвление. -``` -#Z11109=11,'Передернуть Автомат Котельной',[11827,11828],0,0,0,0,0 -#Z11823=49,11190,1,0 ← условие -#Z11827=46,0,11823,[11191,11030,11824,11029,11825,11192,11826],[] -#Z11828=45,0 ← шаг 2 -#Z11824=45,20000 #Z11825=45,60000 #Z11826=45,0 -#Z11029=9,'Включить реле «20/1 Автомат Котельная»',11028,'1' -#Z11030=9,'Выключить реле «20/1 Автомат Котельная»',11028,'0' -#Z11191=9,'Включить реле «virt. Запретить передергивание»',11190,'1' -#Z11192=9,'Выключить реле «virt. Запретить передергивание»',11190,'0' -#Z11028=14,'20/1 Автомат Котельная',175728,0 -#Z11190=14,'virt. Запретить передергивание',179904,0 +### Согласованная форма (принята Alex) + +```yaml +- id: 9691 + name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' + enabled: true + trigger: + - id: 9729 + object: '9495' + value: 1 + steps: + - id: 9819 + action: 9563 ``` -### Что происходит по шагам +### Соответствие строкам конфига — `#Z9691` «Прихожая (н/п) 14/14 ВКЛ» -**Условие:** `relay 11190 ('virt. Запретить передергивание') == 0` — т.е. передёргивание **не** запрещено. +```text +#Z9691=11,'Автомат.: Прихожая (н/п) (14/14) ВКЛ',[9819],0,0,1,0,0 +#Z9819=46,0,9729,[9563],[] ← шаг: условие 9729, действие 9563 +#Z9729=49,9495,1,1 ← триггер: объект 9495, значение 1 +#Z9495=14,'Датч 14/14: Прихожая (н/п)',151408,0 +#Z9563=5,'Включить выход 14/14: Прихожая (н/п)',146064,1,0,0,[],0,0,0,512 +``` -**Шаг 1 (`11827`) — 7 действий подряд:** - -| # | Объект | Действие | Пауза после | -|---|---|---|---| -| 1 | `11191` (type 9) | включить `virt. Запретить передергивание` | — | -| 2 | `11030` (type 9) | **выключить** `20/1 Автомат Котельная` | — | -| 3 | `11824` (type 45) | пауза | **20 000 мс** | -| 4 | `11029` (type 9) | **включить** `20/1 Автомат Котельная` | — | -| 5 | `11825` (type 45) | пауза | **60 000 мс** | -| 6 | `11192` (type 9) | выключить `virt. Запретить передергивание` | — | -| 7 | `11826` (type 45) | пауза | 0 | - -**Шаг 2 (`11828`)** — на деле это не шаг, а **задержка** `[45, 0]` прямо в поле 2 сценария: -пустая пауза-заглушка. Оставлена планировщиком ZONT, функциональной нагрузки не несёт. - -### Смысл логики - -Это **защита от повторного передёргивания**: -1. Виртуальное реле `virt. Запретить передергивание` (id `11190`) ставится в 1 **первым** действием. -2. Условие сценария (`11823`) требует `== 0` → пока реле в 1, сценарий повторно **не запустится**. -3. Снимается реле только **после** 20 с + 60 с пауз (действие 6), т.е. через 80 секунд после старта → минимальный интервал между передёргиваниями автомата = 80 с. - -### 🔴 Проблема - -`#Z11109` поле v[5] = **`0`** → **сценарий выключен в контроллере**. Защита и сама функция передёргивания не работают. - ---- - -## 4. Полные счётчики по конфигу (для сверки после правок) - -| Тип | Кол-во | Комментарий | +| YAML | Строка конфига | Поля | |---|---|---| -| 11 / 46 | 65 / 65 | все одношаговые, кроме `11109` | -| 49 | 65 | все ровно 4 поля, `op=1` (equals) у всех | -| 45 | 4 | `11824`, `11825`, `11826`, `11828` — только в `11109` | -| 9 | 42 | 21 пара ВКЛ/ВЫКЛ, `value` — строка `'1'`/`'0'` | -| 5 | 66 | действия над выходами | -| 14 | 40 | реле | -| 10 | 22 | GUI-переключатели | -| 0 / 36 | 13 / 13 | дискретные датчики + вложенные конфиги | -| 52 / 51 / 53 | 93 / 11 / 64 | Modbus-регистры / устройства / ещё один слой | -| 16 / 20 | 10 / 1 | контуры и режимы отопления | -| **Всего `#Z`** | **598** | | +| `id: 9691`, `name` | `#Z9691=11,…` | 1, 2 | +| `enabled: true` | `#Z9691` поле 5 = `1` | `not (1 & 8)` | +| `trigger[].id: 9729` | `#Z9729=49,…` | 1 (id самого условия) | +| `trigger[].object: '9495'` | `#Z9729` | 2 (id объекта, на который смотрит триггер) | +| `trigger[].value: 1` | `#Z9729` | 4 (значение; поле 3 = оператор) | +| `steps[].id: 9819` | `#Z9819=46,…` | 1 (id шага) | +| `steps[].action: 9563` | `#Z9819` | 4 (первый id из `[9563]`) | +| тело действия | `#Z9563=5,'…',146064,1,…` | живёт своей строкой | -> Проверка после любой правки: `grep -c '^#Z' <файл>` должен дать **598**. +### Терминология Alex — точная ---- +| Id | Что это по Alex | Где в YAML | +|---|---|---| +| `9691` | **сценарий** | `id` верхнего уровня | +| `9729` | **триггер** (не `if`!) | `trigger[].id` | +| `9495` | объект, за которым следит триггер | `trigger[].object` | +| `9819` | **шаг** (`if of the step` по формулировке Alex) | `steps[].id` | +| `9563` | **действие** — «выполнить пользовательское действие» | `steps[].action` | -## 5. Статус разбора конвертером +> Alex: «`#Z9563=5,…` — это шаг «выполнить пользовательское действие» (action) `9563`». + +### Единая модель вложенных id + +Разница между `trigger` и `steps` — только в том, какое поле `#Z9819` (тип 46) на них указывает: + +```text +#Z9819 = 46, 0, 9729, [9563], [] + │ │ + │ └── steps[].action ← поле 4 (список действий) + └───────── trigger[].id ← поле 3 (условие) +``` + +`object` выбран словом для ссылки на объект, потому что **в остальном коде уже так** +(`dump_condition` для type 49: `'object': node[1]`), рядом с `target` (реле/контур) и `descr`. + +### Следствия для кода — ✅ ПУНКТЫ 1–3, 5 СДЕЛАНЫ (2026-09-17) + +| # | Что | Статус | +|---|---|---| +| 1 | `dump_condition` type 49 → `{id, object, value}` вместо `{id, condition: {object, operator, value}}` | ✅ сделано | +| 2 | `dump_step` type 46 → `{id, trigger: <условие>, action: <первый then id>}`, без `if`/`then`/`else` | ✅ сделано | +| 3 | Заголовок сценария — `kind`/`kind_raw` убраны в служебные `_kind`/`_f5` | ✅ сделано | +| 4 | Непустые `then`/`else` / второй+ id → что писать в форме | 🔴 **открытый вопрос, ждёт Alex** | +| 5 | `dump_action` — делегирование в `dump_step` (чтобы type 5/9 в телах не падали в `raw`) | ✅ сделано (было раньше) | +| 6 | Энкодер под новую форму | ⏸ не начат — ждёт подтверждения формы | + +### ✅ Фактический вывод парсера после правок (проверено 2026-09-17) + +Перегенерация `zont_config/config_local_2026-09-17_17-45-00.yml` (exit 0, 4405 строк — было 5376): + +```yaml +- id: 9691 + name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' + enabled: true + _kind: 1 + _f5: 1 + steps: + - id: 9819 + trigger: + id: 9729 + object: 9495 + value: 1 + action: 9563 +``` + +`if`/`then`/`else`/`condition`/`operator` из YAML ушли полностью. Три служебных ключа +(`_kind`, `_f5`, `_op`) содержат то, что энкодеру нужно для байт-в-байт сборки: +`_op` пишется **только когда оператор ≠ 1**; `_kind` — только когда `field5 & 7 ≠ 0`; `_f5` — всегда. + +**Многошаговый `11109` — форма подтвердила проблему:** + +```yaml +- id: 11109 + name: Передернуть Автомат Котельной + enabled: true + _f5: 0 + steps: + - id: 11827 + trigger: + id: 11823 + object: 11190 + value: 0 + action: 11191 + _then: + - 11191 + - 11030 + - 11824 + - 11029 + - 11825 + - 11192 + - 11826 + - id: 11828 + wait: 0 +``` + +🔴 **Это и есть открытый вопрос №4.** У `11109` в `then` семь id — цепочка, а не одно действие. +`action: 11191` показывает только первое, остальные шесть ушли в служебный `_then`. Для 11109 +`_then` — **не служебный ключ, а реальное содержимое**. Форма «один триггер → одно действие» +описывает 64 из 65 сценариев; 11109 из неё выпадает. Ждём решения Alex: `action` остаётся +одиночным, а цепочка — отдельная тема, или появляется `actions:`. **Правка кода остановлена +на этом вопросе, энкодер не тронут.** + +## (в) Методология: почему форма переделывалась дважды + +1. **`blocks`/`extra_links`** — отвергнуто: порядок терялся, сценарий превращался в список цифр. +2. **`when`/`then` + `if`/`group`/`op`** — отвергнуто как **выдумка**: слов нет в конфиге. +3. **`trigger`/`steps` + `action`** — принято (эта форма). + +> 🔴 **Правило, выведенное из трёх итераций:** в YAML попадают **только слова, которые есть +> в конфиге или в UI**. `object` — есть (здесь так называется ссылка), `descr`/`target`/`value` — +> есть (поля строки). `if`/`then`/`condition`/`operator`/`group`/`op`/`kind` — **нельзя**: +> их в данных нет, это интерпретация. + +> 🔴 **Второе правило:** `descr` ставить только там, где текст **реально есть в строке**. +> Для `#Z9819` (шаг) текста нет — значит и `descr` у него быть не должно (Alex: «нахуй там тогда +> descr?! если его нет в оригинале?»). + +> ⚠️ **Не забегать вперёд с вопросами о том, чего в кейсе нет.** Вопрос «что писать, когда +> в `trigger` окажется type 47» Alex воспринял как шум («что за type 47? нихуя не понятно») — +> в разбираемом сценарии его нет. Сначала форма на текущем кейсе, потом обобщение. + +## (г) Состояние на конец сессии (обновлено 2026-09-17, после правок кода) | Что | Статус | |---|---| -| Многошаговый сценарий (`[11827,11828]`) | ✅ разбирается с 2026-09-17 | -| 7 действий в шаге `11827` | ✅ разбирается с 2026-09-17 | -| Тип 45 (4 объекта) | ✅ парсер + эмиттер, секция YAML `delays` | -| Round-trip всех 4 конфигов | ✅ чисто (`test_roundtrip.py`, коммит `199f2b1`) | -| Сценарий `enabled=0` | ℹ️ факт, не баг конвертера | - -### 🔴 Уточнение семантики поля 2 (важно) - -Поле 2 сценария — **не «список шагов»**, а **последовательность ссылок**, куда попадают объекты -**разных типов**. Полный список: шаг (`46`) **и** задержка (`45`). - -В `#Z11109` поле 2 = `[11827, 11828]`, где: -- `11827` — настоящий шаг (`46`) с условием и 7 действиями; -- `11828` — **задержка (`45,0`)**, т.е. «шаг-ожидание» без условия. - -Поэтому формулировка «2 шага» неточна: **шаг один**, вторым элементом идёт задержка-заглушка. -Отсюда же — `extra_links` в YAML (см. [[family/how-to/zont-config-compiler]] §2.1): не-46 ссылки -сохраняются дословно, чтобы round-trip остался байт-точным. - -> ⚠️ **Не путать с типом 45 внутри шага.** В `11827` задержки `11824`/`11825`/`11826` — это -> **действия внутри** шага. `11828` — **ссылка в поле 2 сценария**, т.е. элемент верхнего уровня. - ---- - -## 6. План переработки YAML-структуры (2026-09-17, ждёт апрува) - -Сценарии в YAML переписываются в нативную ZONT-форму **вложенных инструкций «если … то …»** — -**без HA-синтаксиса** (`triggers`, `platform: state`). Alex: «нет структура должна быть как в zont. -**триггеров нет.**» - -Предлагаемая форма для `11109`: - -```yaml -scenarios: - - id: 11109 - name: 'Передернуть Автомат Котельной' - enabled: false - blocks: - - id: 11827 - if: {id: 11823, relay: 11190, operator: equals, value: 0} - then: - - {id: 11191, action: relay_on, relay: 11190} - - {id: 11030, action: relay_off, relay: 11028} - - {id: 11824, action: wait, ms: 20000} - - {id: 11029, action: relay_on, relay: 11028} - - {id: 11825, action: wait, ms: 60000} - - {id: 11192, action: relay_off, relay: 11190} - - {id: 11826, action: wait, ms: 0} - tail: - - {id: 11828, action: wait, ms: 0} # задержка из поля 2 сценария -``` - -Полный план — [[family/how-to/zont-config-compiler]] §5c. - -> ⏸ **РАБОТА ПО §5c ПРИОСТАНОВЛЕНА (2026-09-17).** Alex дал «стоп» и запросил исследование облачного -> API ZONT — есть ли альтернатива конвертеру. **Ответ: API конфиг не поддерживает** (сценарии/реле/ -> 11/14/46/49 в API отсутствуют). Переписывать с 0 не на что, конвертер остаётся единственным путём. -> Разбор — [[family/tech/zont-api]]. Форма `blocks/if/then` выше — **предложение, в коде не реализовано**. - ---- - -## 7. Связанные заметки - -- [[family/how-to/zont-config-compiler]] — конвертеры `.txt ⇄ .yml`; 2 шага и тип 45 **поддержаны с 2026-09-17** (§5b) -- [[family/tech/zont-api]] — облачный API ZONT: конфиг не поддерживает, альтернативы конвертеру нет -- [[family/tech/zont-config-object-types]] — полная таблица типов объектов и `#S`-настройки -- [[family/how-to/home-automation]] — контур автоматизации, ZONT, Modbus slave ID и регистры - ---- - -## 8. 🔴 КОНСТРУКТОР ЛОГИКИ ZONT — типы 47 / 48 / 50 / 59 (разобрано 2026-09-17) - -> **Источник:** локальный конфиг с контроллера, снят с `http://192.168.0.50/config.txt` -> (без авторизации, см. [[family/tech/zont-api]] §6bis): -> `zont_config/config_local_2026-09-17_14-16-35.txt` — **34 907 байт, 660 `#Z`, 25 `#S`**. -> -> Alex создал тестовые сценарии в UI, «постарался использовать все доступные триггеры и варианты -> логики». Это позволило вскрыть **полноценный визуальный конструктор логики** со скриптовым движком. - -### 8.1. 🔴🔴 ОТМЕНЕНО 2026-09-17: поле 5 типа 11 — НЕ тип запуска. Таблица ниже ОШИБОЧНА - -> 🔴 **ЭТА МОДЕЛЬ ОТВЕРГНУТА ALEX (2026-09-17, конец сессии). Читать §8.16.** -> -> Alex: сценарий `9628` («Автомат.: Рад. ванная 2эт ВКЛ») — **по триггеру**, а поле 5 у него `1`, -> не `9`. Сценарий `8456` («Простой тестовый сценарий») — **такой же ручной, как остальные, но -> disabled**, а поле 5 у него `8`, не `1`. Оба противоречат таблице ниже. -> -> **Что осталось верным:** поле 5 — не просто `enabled` (§1 устарела тоже). -> **Что неверно:** сопоставление `8=расписание`, `9=триггер`, `0/1=ручной`. -> Актуальный разбор — **§8.16**. Таблица сохранена как история ошибки. - -| Значение | Что это (❌ ОШИБОЧНО) | Доказательство из тестовых сценариев | -|---|---|---| -| `0` / `1` | ❌ ручной запуск (кнопка), вкл/выкл | 64 старых сценария = `1`; `11109` = `0` — **но `9628` с `1` триггерный** | -| `8` | ❌ по **расписанию** | `#Z8597` — поле 4 = `61`, поле 6 = `3354` ✅ расшифровано — **но `8456` с `8` ручной+disabled** | -| `9` | ❌ по **триггеру** (условию) | `#Z8547` «по времени» и `#Z8551` «по триггеру» — **разные типы, а поле 5 у обоих `9`** | -| `10` | по **интервалу** | `#Z8599` — поле 6 = `43200000` мс = **12 ч** (не перепроверено) | - -**Поле 6** — параметр запуска (для `10` — интервал в мс; для `8` — время запуска). - -### 🔴 Поле 4 + поле 6 при запуске по расписанию — РАСШИФРОВАНО (2026-09-17) - -`#Z8597` «Тестовый сценарий по расписанию», Alex: **ПН, СР, ЧТ, ПТ, СБ, в 13:26**. - -| Поле | Значение | Расшифровка | -|---|---|---| -| поле 4 | `61` = `0b00111101` | **битовая маска дней недели**: бит 0=ПН, 1=ВТ, …, 6=ВС. Установлены биты **0,2,3,4,5** = ПН, СР, ЧТ, ПТ, СБ ✅ | -| поле 6 | `3354` = `0x0D1A` | **время**: старший байт `0x0D` = **13** (час), младший `0x1A` = **26** (минута) → **13:26** ✅ | - -```text -#Z8597 = 11, 'Тестовый сценарий по расписанию', [8598], 61, 3354, 8, 0, 0 - ^годнедели ^время ^тип -``` - -> Формула времени: `поле6 = (час << 8) | минута`. Начало недели — **ПН** (бит 0), не ВС. -> Проверка: `61 >> 0 & 1 = 1` (ПН есть), `61 >> 1 & 1 = 0` (ВТ нет — у Alex ВТ не отмечен) ✅. - -```text -#Z8456=11,'Простой тестовый сценарий',[<26 ссылок>],0,0, 8,0,0 -#Z8547=11,'тестовый сценарий по времени', [8550],0,0, 9,0,0 -#Z8551=11,'Тестовый сценарий по триггеру',[8555],0,0, 9,0,0 -#Z8597=11,'Тестовый сценарий по расписанию',[8598],61,3354, 8,0,0 -#Z8599=11,'Тестовый сценарий по интервалу',[8600,8601],0,0, 10,43200000,0 -``` - -> ⚠️ **Следствие для конвертера:** `enabled` в YAML сейчас выводится из поля 5 как булево — -> это **потеря информации** для значений 8/9/10. Конвертер на новом конфиге **падает** -> (`Ошибка: Условие 8496: не type 49`) — новые типы не поддержаны. - -### 8.2. Новые типы объектов (УТОЧНЕНО ответами Alex 2026-09-17) - -| Тип | Что | Формат | Пример | -|---|---|---|---| -| **47** | **лист дерева условий** (сравнение) | `[47, <оператор>, <левый>, <правый_или_0>, <константа>]` | `[47, 3, 8552, 0, -5]` — «погода <= -5» | -| **48** | **группа условий** | `[48, <логика>, []]` | `[48, 0, [8493,8495]]` | -| **50** | **объект-триггер запуска** (внешний/системный) | `[50, 1, 0, 0, ]` | `[50, 1, 0, 0, 109]` | -| **59** | **мини-скрипт ZONT** | `[59, '<код>', <арг1>, <арг2>, <флаг>]` | `[59, 'expr "%0 + %1"', 8459, 8460, 0]` | - -**Type 47 — модель полей.** `[47, op, LEFT, RIGHT, K]`: -- `LEFT` — объект/скрипт-выражение слева; -- если `RIGHT` != `0` — **второй объект**, сравнение двух объектов (`8495`: `var1 >= objstate 9838`, RIGHT = `8494`); -- если `RIGHT` = `0` — сравнение **с константой** из поля 4 (`8493`: `OBJECT < 3`). - -**Type 47, операторы — ПОДТВЕРЖДЕНО Alex** (порядок пунктов UI: `<`, `>`, `=`, `<=`, `>=`): - -| Поле 1 | Оператор | Доказательство (формулировка Alex) | -|---|---|---| -| `0` | `<` | `8493` «значение элемента управления < 3»; `8497` «var1 < 2» | -| `1` | `>` | `8499` «Pickle(2) > 3» | -| `2` | `=` | в тестовом блоке нет; следует из порядка UI | -| `3` | `<=` | `8553` «погода из интернета <= -5» | -| `4` | `>=` | `8495` «var1 >= значение 13/9 рад. ванная 2эт» | - -> 🔴 **Ловушка:** порядок пунктов UI (`<`,`>`,`=`,`<=`,`>=`) **не** логически упорядочен. -> `=` стоит **третьим** (код `2`), между `>` и `<=`. Не «додумывать» порядок как -> «сначала все меньше, потом все больше» — это даёт неверные коды. - -**Type 48, логика группы — ПОДТВЕРЖДЕНО Alex** (по описанию теста 8456): - -| Поле 1 | Логика | Доказательство | -|---|---|---| -| `0` | **И** (все условия) | `8496 = [48,0,[8493,8495]]` — «условие И: ...» | -| `1` | **ИЛИ** (любое) | `8501 = [48,1,[8497,8500]]` — «ИЛИ: ...» | -| `2` | **НЕ** (отрицание) | `8500 = [48,2,[8499]]` — «НЕ (...)» | - -**Type 50 — триггер запуска.** `[50, 1, 0, 0, ]`, где `` — **внешний/системный -объект**, которого нет среди `#Z` (напр. `109` — «погода из интернета»). Сценарий «по времени» -(`8547`, шаг `8550`) ссылается на `109` — в UI это тоже триггер-условие запуска -(поле 5 типа 11 = `9`). - -### 8.3. Type 59 — встроенный скриптовый язык - -Найденные команды (в поле 1, в кавычках — аргументы): - -| Команда | Пример | Смысл (предположительно) | -|---|---|---| -| `objcmd ""` | `objcmd 8700 "1 %0"` | команда объекту с подстановкой `%0`, `%1` | -| `objstate ` | `objstate 9838 0 0` | чтение состояния объекта | -| `expr "<формула>"` | `expr "%0 + %1"`, `expr "%0 - %1"` | арифметика | -| `set ` | `set var1` | присваивание переменной | -| `puts "текст"` | `puts "Отладка"`, `puts "test"` | вывод (отладка) | -| `storeev I "z"` / `storeev A "asdf"` | — | запись события (I = integer?, A = alert/array?) | -| просто число/строка | `3`, `2 ;#p` | константа; суффиксы `;#a`, `;#h`, `;#p` | - -Есть **переменные** (`var1`), **подстановки** `%0`/`%1` (аргументы из полей 2/3), **суффиксы** `;#a`/`;#h`/`;#p`. -Это объясняет всё: у ZONT есть **полноценный визуальный конструктор логики** поверх скриптового движка. - -### 8.4. Дерево условий — рабочий пример (`#Z8505`, `#Z8506`) - -Вложенная логика читается так: - -``` -#Z8506 = [46, 0, 8496, [8505], []] ← шаг: условие 8496, действие 8505 -#Z8496 = [48, 0, [8493, 8495]] ← группа: 8493 И 8495 -#Z8493 = [47, 0, 8492, 0, 3] ← лист: объект 8492, значение 0, порог 3 -#Z8492 = [59, 'objstate 9838 0 0', 0, 0, 0] ← объект = скрипт! -#Z8495 = [47, 4, 8472, 8494, 0] -#Z8494 = [59, 'objstate 9838 0 0', 0, 0, 0] - -#Z8505 = [46, 1, 8501, [8502, 8503], [8504]] ← шаг: приоритет 1, доп. список [8504] -#Z8501 = [48, 1, [8497, 8500]] ← группа: 8497 ИЛИ 8500 -#Z8497 = [47, 0, 8472, 0, 2] -#Z8500 = [48, 2, [8499]] ← группа: логика 2 над [8499] -#Z8499 = [47, 1, 8498, 0, 3] -#Z8498 = [59, '2 ;#p', 0, 0, 0] -``` - -> 📌 В `#Z8505` поле 1 (приоритет) = **1**, и поле 4 (последнее) = **непустой список `[8504]`** — -> в отличие от «канонических» шагов, где там всегда `[]`. Конвертер это отвергает -> (`require(step[4] == [])`). Семантика поля 4 — **не подтверждена**. - -### 8.5. Контейнер-сценарий `#Z8456` (поле 2 = смесь типов) - -`#Z8456 = [11, 'Простой тестовый сценарий', [<26 ссылок>], 0, 0, 8, 0, 0]` — поле 2 содержит -объекты **разных типов**, а не только шаги: - -| Тип | Кол-во | Примеры | -|---|---|---| -| 9 (команда реле) | 2 | `8457` «Установить целевую температуру 23.8…», `8470` «Активировать режим…» | -| 59 (скрипт) | 17 | `8458`…`8504` | -| 11 (другой сценарий!) | 1 | `11109` «Передернуть Автомат Котельной» | -| 3 (SMS) | 1 | `8195` «CMC оповещение» | -| 5 (действие) | 1 | `9500` «Вкл. Рад. ванная 2эт» | -| 45 (задержка) | 1 | `8489` = 432 000 000 мс = **5 суток** | -| 46 (шаг) | 1 | `8506` | - -> 🔴 **Сценарий может ссылаться на другой сценарий** (`11109` внутри `8456`). Это **не дерево**, -> а плоский список ссылок. - -### 8.6. Сводка тестового блока (id 8450–8601) - -Типы в блоке: `{9: 2, 11: 3, 14: 1, 16: 1, 27: 1, 45: 1, 46: 4, 47: 5, 48: 3, 49: 11, 50: 3, 59: 26}`. - -| Сценарий | Имя | Поле 5 (запуск) | -|---|---|---| -| `8456` | Простой тестовый сценарий | `8` (расписание) | -| `8547` | тестовый сценарий по времени | `9` (триггер) | -| `8551` | Тестовый сценарий по триггеру | `9` (триггер) | -| `8597` | Тестовый сценарий по расписанию | `8` (расписание) | -| `8599` | Тестовый сценарий по интервалу | `10` (интервал, 12 ч) | - -### 8.7. ✅ ВОПРОСЫ ЗАКРЫТЫ — ответы Alex (2026-09-17) - -Alex создал тестовые сценарии и описал, что видно в UI. Все пять вопросов закрыты: - -| # | Вопрос | Ответ | Проверка по конфигу | -|---|---|---|---| -| 1 | **Type 47, оператор** | Операнды в UI: **`<`, `>`, `=`, `<=`, `>=`** → коды `0..4` | ✅ все 5 листов сошлись | -| 2 | **Type 48, логика** | **`0`=И, `1`=ИЛИ, `2`=НЕ** | ✅ по описанию теста 8456 | -| 3 | **Type 50** | **Объект-триггер запуска** (внешний, напр. `109` «погода из интернета») | ✅ | -| 4 | **Type 11, поле 4** | **Маска дней недели** (`61` = ПН,СР,ЧТ,ПТ,СБ), поле 6 = `(час<<8)|мин` | ✅ 13:26 | -| 5 | **`;#a`/`;#h`/`;#p`** | (не переспрашивал; остаются гипотезой) | ⏳ | - -**Разбор тестовых сценариев Alex (ответы дословно → факты конфига):** - -| Сценарий | Что сказал Alex | Что в конфиге | -|---|---|---| -| `8597` расписание | «пн, ср, чт, пт, сб, в 13:26» | поле 4=`61`, поле 6=`3354`=13:26 ✅ | -| `8547` «по времени» | «по триггеру, триггер на день недели: пн,ср,чт,сб,вс» | поле 5=`9`, шаг `8550` → `[50,1,0,0,109]` | -| `8551` «по триггеру» | «триггер сравнение: погода из интернета <= -5, действие вывод в терминал» | `8553=[47,3,8552,0,-5]`, `8552=[49,8254,0,4]`, `8254`=«Погода из интернета», then `8554`=`puts "Температура упала ниже -5"` ✅ | -| `8547` шаг `8550` | «только одно действие: вывод "test" в терминал» | `8549=[59,'puts "test"',0,0,0]` ✅ | -| `8456` (26 ссылок) | «set var много, у каждого разный тип значения» | 7× `set var1` + 7 разных типов объектов ✅ | - -**Type 46 — модель полей ПОДТВЕРЖДЕНА (5 полей = если/то/иначе):** - -```text -#Z = [46, <вид>, , [<ТОГДА>...], [<ИНАЧЕ>...]] -``` - -Alex по тесту `8456` описал: «если (условие И: …) То: … Иначе: Сохранить событие» — -и это точно соответствует `8506 = [46, 0, 8496, [8505], []]` + `8505 = [46, 1, 8501, [8502,8503], [8504]]`, -где `[8504]` = ветка **ИНАЧЕ**. Поле 4 = список **ИНАЧЕ** (ранее помечено «семантика не подтверждена» — теперь подтверждена). - -> ⚠️ Поле 1 шага: `0` у внешнего, `1` у вложенного (`8505`) — вероятно «уровень вложенности» -> или признак вложенного блока. Точная семантика не подтверждена, но round-trip требует -> сохранять как есть. - -### 8.8. Скрипты разбора (в проекте) - -| Файл | Назначение | -|---|---| -| `read_scenarios.py` | 🆕 **читаемый дамп любого сценария** — дерево если/то/иначе с именами объектов. `python3 read_scenarios.py ` | -| `dump_new_types.py` | дамп типов 11/45/46/47/48/49/50/59 целиком | -| `probe_types.py` | распределение поля 5 у type 11; тело контейнера; блок 8450–8560 | -| `trace_scenarios.py` | трассировка сценарных графов, поиск сценариев с новыми типами | -| `probe_sched.py` | 🆕 проверка расписания: маска дней + время | -| `verify_answers.py` | 🆕 кросс-проверка ответов Alex против фактов конфига | -| `check_ops.py` | 🆕 проверка таблицы операторов type 47 | -| `audit_8456.py`, `audit2.py` | 🆕 аудит прозрачности элементов сценария 8456 | - -```bash -cd /Users/admin/Automation/HA-ZONT-Modbus -python3 read_scenarios.py zont_config/config_local_2026-09-17_14-16-35.txt 8456 # дерево сценария -python3 read_scenarios.py zont_config/config_local_2026-09-17_14-16-35.txt # все сценарии -python3 probe_types.py # семантика поля 5, тестовый блок -python3 dump_new_types.py # все сценарные объекты -``` - -> ✅ **Токенайзер починен.** Старый `read_scenarios.py` рвал строки со скриптами на запятых -> внутри кавычек (`objcmd 9102 "6,%0";#a` разваливалось). Новый `split_top()` учитывает -> кавычки, экранирование `\` и вложенные `[]` — теперь скрипты печатаются как есть. - -### 8.10. ⏳ НЕПРОЗРАЧНЫЕ ЭЛЕМЕНТЫ СЦЕНАРИЯ 8456 (✅ разблокированы raw-стратегией) - -Alex спросил: «а по всем действиям в "Простой тестовый сценарий" — тебе всё кристально прозрачно?» -Ответ был — **нет**. Ниже ровно то, что не выводится из конфига. - -> ✅ **Разблокировано 2026-09-17.** Alex решил: «допиливаешь парсер с известными типами, а неизвестные -> — пусть в yml будут raw values, и тогда я доразберу». После этого конвертер дописан, round-trip -> чистый, **эти вопросы больше не блокируют работу** (§8.9). Семантику доразбирает Alex в YAML -> через `raw`-поля. **Гипотезы по-прежнему не строить** — открытые пункты остаются открытыми. - -| # | Элемент | Что неясно | -|---|---|---| -| 1 | `#Z8456` поле 5 = `8` (расписание), но поле 4 = `0`, поле 6 = `0` | **Как запускается 8456?** Расписание не задано. Что значит `8` без маски/времени? Ручной запуск? | -| 2 | `#Z8458 = [59, 'objcmd 8700 "1 %0"', 10.5, 0, 1]` | Единственный скрипт с **нецелым** `%0` (`10.5`) и **флагом `1`** в поле 4 (у всех остальных `0`). Что за инструкция в UI? | -| 3 | `#Z8463 = [59, 'objcmd 9102 "6,%0";#a', 8462, 0, 0]`, где `8462` = скрипт `expr "%0 + %1"` | Аргумент = **ссылка на другой скрипт**. Что за суффикс `;#a`? | -| 4 | `#Z8465 = [59, 'objcmd 11907 ",,,%0";#h', 8464, 0, 0]`, где `8464` = `[49, 4099, 0, 8]` (условие) | Аргумент = **условие**. Что за `;#h`? | -| 5 | `#Z8467 = [59, '3', 0, 0, 0]` | Скрипт = просто цифра `3`. Константа? Зачем отдельным действием? | -| 6 | `#Z8486`/`#Z8488` = `set var1`, arg1 = `8485`/`8487` — **type 50** | Type 50 стоит как **источник значения** для var1, а не как триггер. Это то же самое, что триггер? | -| 7 | `#Z9500 = [5,'Вкл. Рад. ванная 2эт',145728,1,0,0,[],0,0,0,512]` | Объект выхода `145728` **отсутствует** в конфиге. Поле 10 = `512` — что это? → ✅ **пункт 7 ЗАКРЫТ**: `145728` — id выхода (`output_id = 145728 >> 4 = 9108`), `512` — код. Alex: «145728 — это очевидно id». **Выводить семантику `512` из подписи не нужно** | -| 8 | `#Z8457 = [9,'Установить целевую температуру 23.8...',8669,'2968']`, `#Z8470 = [9,...,8669,'8574']` | → ✅ **пункт 8 ЗАКРЫТ**: type 9 = `[9, '<подпись>', , '<значение>']`. Цель — реле (14) / контур (16) / режим (20). Поле 4 — **ссылка/код, не уставка** («`'2968'`» при тексте «23.8»). Alex подтвердил: `8470` → цель `8669` = «Контур ГВС» | - -### 8.12. 🔴 Type 9 и Type 5 — модель полей (подтверждено Alex 2026-09-17, вечер) - -**Type 9 — команда (реле / контур / режим отопления):** - -```text -#Z = [9, '<подпись команды>', , '<значение>'] -``` - -| Часть | Смысл | Пример | -|---|---|---| -| поле 2 | подпись, собранная UI | `'Установить целевую температуру 22 для контура Спальня'` | -| поле 3 | **id цели** → тип 14 (реле) / 16 (контур) / 20 (режим) | `10034` = `Спальня` (16), `8574` = `Режим отопления` (20), `9263` = `Реле 1: Конвектор кухня` (14) | -| поле 4 | **значение как строка** — `'1'`/`'0'` (вкл/выкл) **или ссылка/код** | `9826`→`'1'` (вкл реле), `8470`→`'8574'`, `8457`→`'2968'` | - -🔴 **Ловушка:** поле 4 ≠ значение уставки. У `8457` подпись «температуру 23.8», а поле 4 = `'2968'` -(ссылка на отсутствующий объект). **Не выводить значение из текста подписи.** - -**Type 5 — действие над выходом контроллера:** - -```text -#Z = [5, '<имя>', , , , , [], bmp, time, ] -``` - -`output_id = output_ref >> 4`. Пример `9500`: `output_ref=145728` → `output_id=9108`, `value=1`. - -> ✅ **Ответы Alex (пересозданный сценарий):** «`8457` — поменял температуру на 22, контур на Спальня» -> (→ новый `#Z8641=9,'Установить целевую температуру 22 для контура Спальня',10034,'2950'`); -> «`8470`: `8669` это очевидно Контур ГВС. больше там полей в UI нет. добавил второе такое же действие -> но с "для Всех контуров"» (→ новый `#Z8640=9,'Включить режим «Режим отопления»',8574,'1'`). - - -**Что уже прозрачно (не переспрашивать):** - -- 7× `set var1` и их источники: `8450`(Температура подачи, тип 27), `9864`(Статус 13/9, тип 0), - `8560`(Контур газ котла, тип 16), `9263`(Реле 1 конвектор, тип 14), `8254`(Погода из интернета, тип 1), - `4098`(Адаптер газ. котла, тип 6), `8382`(Температура Zigbee, тип 1) ✅ -- `#8466` `puts "Отладка"`, `#8468` `storeev I "z"`, `#8469` `storeev A "asdf"` — вывод/события ✅ -- `#8489` пауза 5 суток (`432000000` мс), дерево `#8506`→`#8505` (И/ИЛИ/НЕ + ИНАЧЕ) ✅ -- `#8195` SMS `CMC оповещение` → `8192` (2 номера) ✅ -- `#11109` как **вложенный сценарий** внутри 8456 ✅ -- `#8507` — ссылка на **отсутствующий объект** (хвост поля 2) ✅ - -**Почему неясные элементы не блокируют парсер:** стратегия — сохранять их **дословно** -(type 59 как сырые строки с `arg1..arg3`, отсутствующие id — как есть). Round-trip останется -байт-точным независимо от того, понята семантика или нет. - -### 8.9. ✅ Состояние: конвертер новый конфиг РАЗБИРАЕТ (2026-09-17) - -> ✅ **ИСПРАВЛЕНО.** Все пять пунктов ниже реализованы в конвертерах (§5c в -> [[family/how-to/zont-config-compiler]]). Round-trip чистый на 6/6 конфигах. -> Ранее падал с `Ошибка: Условие 8496: не type 49` (exit 2) — **больше не падает**. - -**Что было нужно — ВСЁ СДЕЛАНО:** - -| # | Было нужно | Статус | -|---|---|---| -| 1 | Парсер типов **47/48/50/59** + рекурсивное дерево условий | ✅ `dump_condition`/`dump_leaf`/`dump_action` | -| 2 | Поле 5 типа 11 — **тип запуска** (0/1/8/9/10), не `enabled` | ✅ `trigger: {type, days, time, interval_ms}` | -| 3 | Поле 4 шага — принимать непустые списки | ✅ `else` (в `steps[]`) | -| 4 | Ссылки в поле 2 — допускать не-46 типы (45, 11, 3, 5, 9) | ✅ **плоский `steps[]`** — каждый элемент по порядку, тело инлайн | -| 5 | Round-trip всех 5 конфигов | ✅ **6/6** (боевой + 3 архива + новый локальный + live) | - -**Счётчики конфига `config_local_2026-09-17_14-16-35.txt` (для сверки):** `660 #Z`, `25 #S`, байт — 34 907. - -| Тип | Кол-во | | Тип | Кол-во | -|---|---|---|---|---| -| 11 | 70 | | 49 | 76 | -| 46 | 69 | | 47 | 5 | -| 45 | 5 | | 48 | 3 | -| 9 | 44 | | 50 | 3 | -| 59 | 28 | | 0/36 | 13/13 | - -**Финальный конфиг `config_local_2026-09-17_16-02-18.txt`** (Alex пересоздал сценарий, вечер): -`686 строк`, **34 962 байт**. Сценарий `8456` полностью перегенерирован (новые id 8640–8777): -добавлены `#Z8640=9,'Включить режим «Режим отопления»',8574,'1'` и -`#Z8641=9,'Установить целевую температуру 22 для контура Спальня',10034,'2950'`. -YAML — `zont_config/config_local_2026-09-17_16-02-18.yml`, round-trip ✅. - -### 8.11. ✅ ФИНАЛЬНАЯ YAML-форма сценария — плоский `steps[]` (2026-09-17) - -> 🔴 **Урок, который стоит запомнить.** Первая реализация делила поле 2 на `blocks` (шаги 46) + -> `extra_links` (голые id). Round-trip был чистый (байты сходились), но **сценарий читался как -> список цифр**: `blocks` выводился первым, а всё «до него» — 25 элементов «установка температуры, -> `puts`, `set var1`, пауза 5 суток» — лежало в `extra_links` без тел. Alex: «почему там блядь if -> идет первым блоком?! где все что до него происходит?!» -> **Вывод: round-trip ≠ читаемость. Артефакт надо читать глазами до отдачи.** - -**Правильная форма** — одно поле `steps`, плоский список = поле 2 сценария **как есть**: - -```yaml -scenarios: - - id: 8456 - name: Простой тестовый сценарий - trigger: {type: schedule} - _raw_trigger_kind: 8 - steps: - - {id: 8457, raw: [9, 'Установить целевую температуру 23.8 …', 8669, '2968']} - - {id: 8458, script: 'objcmd 8700 "1 %0"', args: [10.5, 0, 1]} - - {id: 8463, script: 'objcmd 9102 "6,%0";#a', args: [8462, 0, 0]} - - {id: 11109, run_scenario: Передернуть Автомат Котельной} - - {id: 8466, script: 'puts "Отладка"', args: [0, 0, 0]} - - {id: 8473, script: 'set var1', args: [8471, 0, 0]} # + ещё 6 × set var1 - - {id: 8489, wait: 432000000} # пауза 5 суток - - id: 8506 # шаг с условием — тоже элемент - if: {id: 8496, group: and, children: [ - {id: 8493, op: '<', left: 8492, value: 3}, - {id: 8495, op: '>=', left: 8472, right: 8494}]} - then: [{id: 8505, kind: 1, if: {…}, then: [{id: 8502, script: 'puts "then-text"'}], else: [{id: 8504, script: 'storeev A "alert"'}]}] - - {id: 8507, unresolved: true} -``` - -**Диспетчер `dump_step()`** — по типу элемента: -`46`→`if/then/else`, `59`→`script`+`args`, `47/48/49`→дерево условий, `45`→`wait`, `50`→`trigger_object` (raw), -`11`→`run_scenario`, `5/9/3`→`raw`-тело. **Мелкая деталь:** тела объектов чужих секций (`sms_notifications`, -`actions`) рендерятся как YAML-якоря `*id00N` (`8195`, `9500` в `8456`) — читается хуже, но байт-точно. - -**Артефакт:** `zont_config/config_local_2026-09-17_14-16-35.yml` (6110 строк) — в проекте, не в `/tmp`. - -### 8.13. 🔴 УСТАВКА ТЕМПЕРАТУРЫ — формула `код = (t °C + 273) × 10` (подтверждено 3 точками, 2026-09-17) - -**Главное открытие разбора type 9.** Поле 4 команды «Установить целевую температуру» — **не ссылка, -а закодированная уставка в Кельвинах ×10** (fixed point): - -```text -код = (t_celsius + 273) * 10 обратно: t_celsius = (код - 2730) / 10 -``` - -**Три подтверждённые точки** (Alex менял температуру в UI, я сверял с конфигом): - -| Уставка в UI | Код в конфиге | Проверка | -|---|---|---| -| `22` | `2950` | `(22 + 273) * 10 = 2950` ✅ | -| `23.8` | `2968` | `(23.8 + 273) * 10 = 2968` ✅ | -| `5.2` | `2782` | `(5.2 + 273) * 10 = 2782` ✅ (предсказано заранее, совпало) | - -> **Как это выглядит в конфиге:** сам код уставки лежит в поле 4, а **человекочитаемая** -> температура — только в текстовой подписи поля 2, которую контроллер собирает сам: -> `#Z8817=9,'Установить целевую температуру 5.2 для контура Спальня',10034,'2782'`. -> 🔴 **Раньше я ошибочно считал `'2950'`/`'2968'` «ссылкой на отсутствующий объект»** — -> это была уставка, просто закодированная. -> -> ⚠️ **Питфолл метода:** на **двух** точках линейная формула подбирается бесконечно многими -> способами. Я нашёл `(t+273)*10` на 22 и 23.8, но **не вшивал её в парсер**, пока Alex не дал -> третью точку (5.2). Правило: **≥3 точки, прежде чем кодировать формулу.** - -**Что раскрыто в YAML (type 9)** — ✅ **финальные имена полей (2026-09-17): `descr`/`target`/`value`**, -сырьё в `raw_value` (Alex: «ОЧЕВИДНО ИЛИ НЕТ ЧТО НАМ НУЖНЫ ПОЛЯ descr, target id и value который блядь 5,2»): - -```yaml -- id: 8817 - descr: Установить целевую температуру 5.2 для контура Спальня - target: 10034 - target_name: Спальня # развёрнуто из id (тип 16 = контур) - target_type: 16 - value: 5.2 # ← Цельсии, то, что правит человек - raw_value: '2782' # сырой код из конфига (источник истины для сборки) -- id: 9826 - descr: 'Включить реле «Реле 1: Конвектор кухня»' - target: 9263 - target_name: 'Реле 1: Конвектор кухня' - target_type: 14 # 14 = реле - value: true # ← раскодировано (было '1') - raw_value: '1' -``` - -`value` — **раскодированное** значение (Цельсии / `true` / `false`), `raw_value` — сырьё. -Декодирование уставки — **только** при точном совпадении формулы (`|round(t*10)-(код-2730)| < 0.5`); -иначе `value` = сырая строка без `raw_value`. Энкодер собирает строку из `raw_value`, не из `value`. - -### 8.14. `objcmd "1 %0"` — запись значения в объект - -Скрипт `#Z = [59, 'objcmd "1 %0"', <значение>, 0, <флаг>]` пишет `<значение>` -в объект ``: - -```yaml -- id: 8818 - script: objcmd 8700 "1 %0" - args: [14.5, 0, 1] - target: 8700 - target_name: Температура Детская # развёрнуто из id - set_value: 14.5 # ← «= 14.5» -``` - -🔴 **Питфолл имён:** у **типа 1** (виртуальный датчик) имя лежит в **поле 2**, а не в поле 1: -`#Z8700=1,'0','Температура Детская',…`. Наивный `fields[1]` даёт `'0'`. -Использовать хелпер **`_object_display_name(fields)`** — он знает про это исключение. -Проверено: `target_name` был `'0'`, после фикса — «Температура Детская». - -### 8.15. Финальное состояние конвертеров (2026-09-17, вечер) - -| Артефакт | Путь | Состояние | -|---|---|---| -| Сырой конфиг | `zont_config/config_local_2026-09-17_16-13-28.txt` | 34 963 Б, снят с `http://192.168.0.50/config.txt` | -| YAML | `zont_config/config_local_2026-09-17_16-13-28.yml` | в проекте, round-trip ✅ | - -Round-trip чистый на **6/6** конфигах (боевой, 3 архива, `14-16-35`, `16-13-28`). - -> ⚠️ **УСТАРЕЛО после правок конца сессии** (см. §8.16): `target_name`/`target_type`/`raw_value` -> удалены, секции-дубли убраны, YAML = **5452 строки**. Актуальное состояние — §8.16. - -### 8.16. 🔴 Разбор конца сессии 2026-09-17 — поле 5 и удаление подстановок - -#### (а) Поле 5 типа 11 — модель §8.1 ОПРОВЕРГНУТА - -Alex, разбирая YAML, наткнулся на сценарий `9628`: - -```text -#Z9628=11,'Автомат.: Рад. ванная 2эт ВКЛ',[9756],0,0,1,0,0 -``` - -> «сценарий `Автомат.: Рад. ванная 2эт ВКЛ`: там ахинея какая-то! это сценарий **по триггеру**. -> почему он **manual**?» - -Парсер выводил `trigger: {type: manual}` — по таблице §8.1 (`1` = manual). Alex: неверно. - -Дальше — прямое опровержение всей таблицы: - -| Сценарий | Что сказал Alex | Поле 5 | Поля 3/4 | -|---|---|---|---| -| `9628` … 64 шт `Автомат.:*` | **по триггеру** | `1` | `0,0` | -| `8456` «Простой тестовый сценарий» | «**точно такой же manual как и другие два, но он disabled**» | `8` | `0,0` | -| `8547` «тестовый сценарий по времени» | по времени | `9` | `0,0` | -| `8551` «Тестовый сценарий по триггеру» | по триггеру | `9` | `0,0` | -| `8597` «Тестовый сценарий по расписанию» | расписание пн,ср,чт,пт,сб 13:26 | `8` | `61,3354` | - -**Вывод: `8` и `9` не различают «расписание/триггер».** Два сценария с `9` — «по времени» -и «по триггеру» — Alex сделал специально как **разные типы**. `8456` с `8` — ручной. -`9628` с `1` — триггерный. - -**Распределение поля 5 по 70 сценариям боевого конфига:** - -| Код | Кол-во | Что известно | -|---|---|---| -| `1` | 64 | `Автомат.:*` — по триггеру (условие внутри шага) | -| `0` | 2 | `11109` и ещё один | -| `9` | 2 | `8547` (по времени), `8551` (по триггеру) | -| `8` | 1 | `8597` (расписание `61,3354`) + `8456` (disabled) | -| `10` | 1 | `8599` (интервал) | - -> 🔴 **ЧЕГО Я НЕ ЗНАЮ (не выдумывать):** -> 1. Что означает `1` vs `9` — оба могут быть «включён и запускается по своему условию». -> 2. Как кодируется **disabled** (`8456` = `8` + нули, `8597` = `8` + заполненные поля — -> возможно, disabled = «тип есть, параметры сброшены»). -> 3. Где вообще хранится «по триггеру / по времени / ручной», если не в поле 5. -> Кандидаты: поле 4, поле 6, поле 7 — у `9628` и `8547`/`8551` все нули. -> -> **Статус: `TRIGGER_KINDS` в `config-to-yml.py` — ВЫДУМКА, подлежит удалению.** -> Ждём ответ Alex, что показано в UI у `9628` / `8547` / `8551` / `8456`. - -#### (б) Структура YAML — работы конца сессии - -| # | Что | Итог | -|---|---|---| -| 1 | Секции-дубли `delays`/`scenario_steps`/`scenario_conditions`/`scenario_scripts`/`scenario_raw_objects` | **удалены** из парсера, `TYPE_ORDER` и вывода. Осталась одна служебная — `scenario_orphans` (недостижимые объекты) | -| 2 | Переименование `command`→`descr`, `output`/`output_id`→`target`, `temp_c`→`value` | ✅ сделано, имена = поля строки конфига | -| 3 | `target_name` / `target_type` / `raw_value` | ✅ **удалены** (5 мест в парсере + энкодер) | -| 4 | Энкодер пересчитывает код из `value` | ✅ хелпер `_encode_type9_value()`: `True→'1'`, `False→'0'`, число → `str(round((v+273)*10))` | -| 5 | `object_display_name()` вынесен на уровень модуля | ✅ (был вложенной функцией ниже места вызова → `NameError`, питфолл 28) | - -**Три питфолла, каждый ловил round-trip** (`Duplicate ID 8640/9826` → `потеряно 45` → `потеряно 189`) -подробно в [[family/how-to/zont-config-compiler]] §5c. - -#### (в) Открытый вопрос Alex: «получается script editing language?» - -Alex сформулировал **корневое** сомнение по форме YAML: - -> «хуйня какая-то получается, не правда ли?» → «я говорю хуйня а не script editing language -> выходит какая-то» - -Смысл: в YAML появились `if`/`then`/`else`/`group: and`/`op: '>'`/`wait`/`args` — **структура, -которой в конфиге нет**. Это не отображение данных, а интерпретация поверх них. -Отсюда же растут «две правды» (`'2782'` vs `5.2` в разных секциях) — симптом, не причина. - -**Рассмотренный, но НЕ принятый вариант:** `steps` = те же плоские записи, что в `#Z`-строках, -плюс человеческие имена только для доказанных полей. Без `if`/`then`/`group`/`op`. -**Решение не принято — ждёт Alex.** - -> 🔴 **Урок для методологии:** при правке одной секции проверять, не отображается ли тот же -> объект в другой. Раскодировал `value` в `steps[]`, но забыл про `relay_commands` → один объект, -> два разных `value`. Alex нашёл это глазами, round-trip был зелёным. -> **Round-trip не ловит смысловые расхождения между секциями.** - +| Форма YAML сценария | ✅ согласована (см. (б)) | +| Правка `config-to-yml.py` под форму | ✅ **сделана** — 3 патча (`dump_condition` type 49, `dump_step` type 46, заголовок) | +| YAML перегенерирован | ✅ `config_local_2026-09-17_17-45-00.yml` — **4405 строк** (было 5376), `if`/`then` ушли | +| Проверка формы глазами | ✅ блок `9691` и `11109` прочитаны | +| Round-trip | ⏸ **не прогнан** после правок формы (энкодер ещё не подогнан — прогон осмыслен только после пункта 6) | +| Многошаговые сценарии (`11109`) в форме | 🔴 **открытый вопрос №4** — ждёт Alex | +| Энкодер под новую форму | ⏸ не начат | +| `git commit` | ⏸ не закоммичено, ждёт команды | + +**Артефакты:** +- `zont_config/config_local_2026-09-17_17-45-00.yml` — актуальный, **перегенерирован** под новую форму (4405 строк) +- `/tmp/backup-174500.yml` — бэкап предыдущей генерации (5376 строк, форма со старым `if`/`then`) + +> ⚠️ **Питфолл, найденный в этой сессии:** `config-to-yml.py` пишет YAML **в stdout**, а не в файл. +> `main()` не принимает пути вывода (`sys.argv` = только входной `.txt`). Первый прогон +> `python3 config-to-yml.py f.txt -o /tmp/x.yml` **молча проигнорировал `-o`** — скрипт напечатал +> «Использование: zont_to_yaml.py » и `exit 1`, а YAML остался **старым**. Из-за этого +> первый просмотр блока 9691 показал неисправленную форму и выглядел как «патч не сработал». +> **Правильный вызов:** редирект в файл — `python3 config-to-yml.py in.txt > out.yml`, и всегда +> проверять `exit=` и размер целевого файла. + +> ⚠️ **Питфолл (продолжение):** `grep -n "id: 9691"` по YAML находит **несколько** совпадений +> (объект живёт и в своей секции, и внутри сценария). Номер строки в старом файле (4877) и в новом +> (3932) различается — индексировать по первому совпадению нельзя, надо смотреть окрестности.