[2026-09-17] eagle: family/how-to/zont-config-compiler.md family/tech/zont-scenario-logic-11109.md
This commit is contained in:
@@ -21,7 +21,7 @@ tags:
|
||||
- homeautomation
|
||||
title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml
|
||||
type: how-to
|
||||
updated: 2026-09-17m
|
||||
updated: 2026-09-17
|
||||
---
|
||||
|
||||
# ⚙️ ZONT Config Compiler — конвертеры `.txt ⇄ .yml`
|
||||
@@ -146,9 +146,9 @@ updated: 2026-09-17m
|
||||
|
||||
### Как это выглядит в YAML
|
||||
|
||||
> ✅ **АКТУАЛЬНАЯ форма (2026-09-17).** Сценарий = `trigger:` (условие) + `steps[]` (шаги,
|
||||
> каждый со своим `action:`), тела инлайн. **Форма — §5c.** Здесь оставлены только исторические
|
||||
> варианты, чтобы было видно, что отвергнуто Alex и почему.
|
||||
> ✅ **АКТУАЛЬНАЯ форма (2026-09-17).** Сценарий = `enabled` + `type` + `trigger:` (условие) +
|
||||
> `steps[]` (шаги, каждый со своим `action:`), тела инлайн. **Форма — §5j.** Здесь оставлены
|
||||
> только исторические варианты, чтобы было видно, что отвергнуто Alex и почему.
|
||||
|
||||
**Устаревшая форма (коммит `199f2b1`)** — плоский `when`/`then` для 1-шаговых + `steps` + `extra_links`:
|
||||
|
||||
@@ -339,6 +339,12 @@ diff A.txt B.txt # пусто = round-trip чистый
|
||||
| 35 | 🔴 **`descr` не ставить туда, где текста нет в строке** | У `#Z9819` (шаг) своего текста нет — значит `descr` у него быть не должно. Alex: «нахуй там тогда descr?! если его нет в оригинале?» |
|
||||
| 36 | ⚠️ **`grep -n "id: N"` по YAML даёт несколько совпадений** | Объект живёт и в своей секции, и внутри сценария. Номер строки меняется между генерациями (4877 → 3932) — индексировать по первому совпадению нельзя |
|
||||
| 37 | ⚠️ **Не забегать вперёд с вопросами о том, чего в кейсе нет** | Вопрос «что писать, если в `trigger` окажется type 47» Alex воспринял как шум («что за type 47? нихуя не понятно»). Сначала форма на текущем кейсе, потом обобщение |
|
||||
| 38 | 🔴 **`_f5` (служебный ключ в YAML) — отвергнут Alex, и он был прав** | Alex: «какое нахуй служебное». Поле 5 **полностью выводится** из типа + `enabled`. Упорство в служебном ключе = 20+ сообщений про `kind`. Сначала искать вывод из данных, служебный ключ — последнее средство |
|
||||
| 39 | 🔴 **«Тип сценария» брать из имён сценариев — выдумка** | Я строил тип по словам в `name` («по расписанию», «по триггеру») и назвал это типами. Alex: «из какого блядь имени?!». Типы назвал Alex: `manual`/`trigger`/`interval`/`schedule`. Имя сценария — подпись, не тип |
|
||||
| 40 | 🔴 **Гипотезу проверять ПОЛНЫМ прогоном до того, как о ней говорить** | Разбор поля 5 занял ~20 итераций: гипотезы («значение условия», «один шаг → 1») падали по 32 расхождения. Дважды скрипт проверки был с ошибкой индекса и выдавал «всё сходится». **Прогнать по всем 70 и печатать расхождения списком**, прежде чем строить вывод |
|
||||
| 41 | 🔴 **Тостинг в UI + свежий `curl` — единственный способ добыть включённые значения флаговых полей** | Пока `8597`/`8599` были выключены, значения `0`/`2` для `schedule`/`interval` вкл были неизвестны. Alex включил их в UI → новый снимок `18-43-24` → правило замкнулось. Для флаговых полей конфига: спросить Alex включить/выключить и снять конфиг заново |
|
||||
| 42 | ⚠️ **`#Z<id>=11` может лежать в `steps` другого сценария как ссылка** | `#Z8456` в поле 2 держит `11109` — это **ссылка на сценарий**, а не шаг. Отдельного объекта-шага нет, `id:` в YAML вводит в заблуждение (Alex: «нихуя не понял а id step-а?»). Открыто — §5j `run_scenario` |
|
||||
| 43 | ⚠️ **Проверочные скрипты на кириллице падают на `cut`/`awk`** | `cut: stdin: Illegal byte sequence` на cp1251-строках. Читать файл в Python (`open(...,'rb').read().decode('cp1251')`), не резать шеллом |
|
||||
|
||||
### Ограничения конвертера (найдено 2026-09-17) — ВСЕ ЗАКРЫТЫ
|
||||
|
||||
@@ -413,15 +419,15 @@ diff A.txt B.txt # пусто = round-trip чистый
|
||||
> **64 из 65** сценариев. Многошаговый `11109` (7 id в `then`) из неё выпадает — сейчас они уходят
|
||||
> в служебный `_then`. Ждём решения Alex. Детали — [[family/tech/zont-scenario-logic-11109]] §8.17(б).
|
||||
>
|
||||
> 🔴 **Поле 5 типа 11 — семантика НЕ раскрыта.** Модель `kind = field5 & 7` **опровергнута**
|
||||
> (развалилась на `11109`: даёт `kind=0`, хотя устроен как триггерные `1`). Подтверждён только
|
||||
> `enabled = not (field5 & 8)`. Полная таблица 5 значений и разбор — **§5j**.
|
||||
> 🔴 **Поле 5 типа 11 — семантика РАСКРЫТА И ПОДТВЕРЖДЕНА ПРИБОРОМ** (см. §5j):
|
||||
> `поле5 = тип + 8 при выключенном`, типы `manual`/`trigger`/`interval`/`schedule` (названы Alex),
|
||||
> `enabled = not (field5 & 8)`. Модель `kind = field5 & 7` опровергнута и снята.
|
||||
> Включённые значения `schedule`/`interval` добыты тостингом в UI (`8597` → `0`, `8599` → `2`).
|
||||
>
|
||||
> 📄 **Артефакты (в проекте):** `zont_config/config_local_2026-09-17_17-45-00.yml` — **актуальный**
|
||||
> (4405 строк, форма `trigger`/`steps` с `trigger` на уровне сценария, уставка 5.2 +
|
||||
> «Температура Детская»); `zont_config/config_local_2026-09-17_16-13-28.yml` — предыдущая
|
||||
> генерация (5452 строки, старая форма). Оба в `zont_config/`, не в `/tmp`.
|
||||
> **Не закоммичено** — ждёт команды Alex.
|
||||
> 📄 **Артефакты (в проекте):** `zont_config/config_local_2026-09-17_18-43-24.yml` — **актуальный**
|
||||
> (форма §5j **+ поле `type`**); `zont_config/config_local_2026-09-17_17-45-00.yml` — форма §5j
|
||||
> **без** `type` (4405 строк); `zont_config/config_local_2026-09-17_16-13-28.yml` — старая форма
|
||||
> (5452 строки). Всё в `zont_config/`, не в `/tmp`. **Не закоммичено** — ждёт команды Alex.
|
||||
|
||||
### 🔴 Актуальная форма сценария в YAML (принята Alex, 2026-09-17)
|
||||
|
||||
@@ -450,13 +456,18 @@ diff A.txt B.txt # пусто = round-trip чистый
|
||||
|---|---|---|
|
||||
| `id: 9691`, `name` | `#Z9691=11,…` | 1, 2 |
|
||||
| `enabled: true` | `#Z9691` поле 5 | `not (f5 & 8)` |
|
||||
| `_f5` | `#Z9691` поле 5 | `f5` целиком (не выводится из остального) |
|
||||
| `type: trigger` | `#Z9691` поле 5 | `f5 & 7` — выводится из тела (см. §5j «Поле `type`») |
|
||||
| `days` / `days_mask` / `time` | `#Z9691` поля 3, 4 | у `schedule`; у остальных пусто |
|
||||
| `interval_ms` | `#Z9691` поле 6 | у `interval` |
|
||||
| `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]`) |
|
||||
|
||||
> 🔴 `_f5` в таблице **больше нет** — поле 5 восстанавливается из `enabled` + `type`. Строка
|
||||
> оставлена только как явное «снято», чтобы не вернуться к служебному ключу.
|
||||
|
||||
### 🔴 Секции-дубли убраны (2026-09-17)
|
||||
|
||||
Раньше каждый сценарный объект (45/46/47/48/49/50/59) жил **дважды**: тело инлайн в `steps[]`
|
||||
@@ -1057,33 +1068,81 @@ steps?!»). В конфиге условие лежит внутри `#Z9819` (
|
||||
(энкодеру нужно поле 5 целиком).
|
||||
5. **`yaml`-теги вида `object: '9495'` — строкой** (id объекта), как в `trigger.id`.
|
||||
|
||||
### ✅ Поле 5 типа 11 — ЧЕТЫРЕ ТИПА + `enabled` (подтверждено Alex, 2026-09-17)
|
||||
### ✅ Поле 5 типа 11 — ЧЕТЫРЕ ТИПА + `enabled` (подтверждено Alex + прибором, 2026-09-17)
|
||||
|
||||
Alex назвал типы прямо: **`manual`, `trigger`, `interval`, `schedule`**.
|
||||
Сводка по боевому конфигу (70 сценариев):
|
||||
Сводка по боевому конфигу (70 сценариев). 🔴 **Включённые значения `schedule`/`interval`
|
||||
получены тостингом на приборе** (Alex включил `8597` и `8599` в UI, конфиг снят заново → `0` и `2`):
|
||||
|
||||
| ТИП | enabled | поле 5 | шт | кто |
|
||||
|---|---|---|---|---|
|
||||
| `trigger` | вкл | `1` | 64 | все «Автомат.: …» (ВКЛ и ВЫКЛ, по 32) |
|
||||
| `manual` | вкл | `0` | 1 | `11109` Передернуть Автомат Котельной |
|
||||
| `trigger` | выкл | `9` | 2 | `8547` «по времени», `8551` «по триггеру» |
|
||||
| `schedule` | выкл | `8` | 1 | `8597` «по расписанию» |
|
||||
| `interval` | выкл | `10` | 1 | `8599` «по интервалу» |
|
||||
| `schedule` | вкл | `0` | 1 | `8597` «по расписанию» |
|
||||
| `interval` | вкл | `2` | 1 | `8599` «по интервалу» |
|
||||
| `manual` | выкл | `8` | 1 | `8456` «Простой тестовый» |
|
||||
| `schedule` | выкл | `8` | — | (до включения `8597` было `8`) |
|
||||
| `trigger` | выкл | `9` | 2 | `8547` «по времени», `8551` «по триггеру» |
|
||||
| `interval` | выкл | `10` | — | (до включения `8599` было `10`) |
|
||||
|
||||
**Правило (полное, покрывает все 70):**
|
||||
|
||||
```text
|
||||
поле5 = тип + 8, если сценарий ВЫКЛЮЧЕН
|
||||
тип: 0 = manual | schedule 1 = trigger 2 = interval
|
||||
enabled = not (поле5 & 8) ← подтверждено на всех 5 значениях
|
||||
enabled = not (поле5 & 8) ← подтверждено на всех 7 значениях
|
||||
```
|
||||
|
||||
Проверка выключенных: `manual 0+8=8` (8456 ✓), `schedule 0+8=8` (8597 ✓),
|
||||
`trigger 1+8=9` (8547, 8551 ✓), `interval 2+8=10` (8599 ✓).
|
||||
Проверка обоими направлениями — у **каждого** типа есть вкл/выкл:
|
||||
|
||||
| тип | вкл | выкл | вкл − 8 = выкл ✓ |
|
||||
|---|---|---|---|
|
||||
| `manual` | `0` (11109) | `8` (8456) | 0+8=8 ✓ |
|
||||
| `schedule` | `0` (8597 включён) | `8` (8597 был) | 0+8=8 ✓ |
|
||||
| `trigger` | `1` (64 шт) | `9` (8547, 8551) | 1+8=9 ✓ |
|
||||
| `interval` | `2` (8599 включён) | `10` (8599 был) | 2+8=10 ✓ |
|
||||
|
||||
⚠️ **`manual` и `schedule` дают одно число `0`** — различаются только полями 3/4
|
||||
(`days_mask`, `time`): у `8597` = `61`/`3354`, у `8456` = `0`/`0`.
|
||||
(`days_mask`, `time`): у `8597` = `61`/`3354`, у `8456` = `0`/`0`. Alex: «manual от schedule
|
||||
очевидно отличаются наличием блядь schedule!»
|
||||
|
||||
### ✅ Поле `type` в YAML — реализовано в парсере (2026-09-17)
|
||||
|
||||
```yaml
|
||||
- id: 8597
|
||||
name: Тестовый сценарий по расписанию
|
||||
enabled: true
|
||||
type: schedule
|
||||
days: [mon, wed, thu, fri, sat]
|
||||
days_mask: 61
|
||||
time: 13:26
|
||||
steps:
|
||||
- id: 8598
|
||||
descr: 'storeev I "инфо событие в пн, ср, чт, пт, сб"'
|
||||
args: [0, 0, 0]
|
||||
```
|
||||
|
||||
Порядок ключей: `id`, `name`, `enabled`, `type`, `days`, `days_mask`, `time`, `interval_ms`,
|
||||
`_raw_field_count`, далее `trigger`, `steps`. Реализовано переупорядочиванием словаря
|
||||
после обхода шагов (`config-to-yml.py`, блок `# Reorder so the type sits with the other header fields`).
|
||||
|
||||
🔴 **Правило вывода `type` (текущее, порядок веток важен):**
|
||||
|
||||
```text
|
||||
trigger: есть поднятый `trigger` (первый шаг — тип 46)
|
||||
interval: есть `interval_ms` (поле 6)
|
||||
schedule: есть `days` или `time`
|
||||
manual: иначе
|
||||
```
|
||||
|
||||
⚠️ **ИЗВЕСТНАЯ ОШИБКА в этом правиле:** `#Z11109` получает `type: trigger`, хотя по полю 5
|
||||
он **`manual`** (`f5=0`). Причина: у него тоже есть шаг типа 46. Отличие от настоящих trigger —
|
||||
у `11109` **два** элемента в поле 2 (`11827` шаг + `11828` пауза), у trigger-сценариев — **ровно
|
||||
один**. Гипотеза правки: `trigger` = **ровно один** элемент в `steps` **и** он типа 46.
|
||||
**Не проверена** (команда прогона не выполнена — approval timeout).
|
||||
|
||||
Проверено на тот момент: `schedule` (8597) ✓, `interval` (8599) ✓, `trigger` (9628, 9691, 8547, 8551) ✓,
|
||||
`manual` (8456) ✓, **`manual` (11109) ✗**.
|
||||
|
||||
**Что опровергнуто и снято (не возвращаться):**
|
||||
|
||||
@@ -1092,8 +1151,12 @@ enabled = not (поле5 & 8) ← подтвержд
|
||||
- ❌ **Гипотеза «поле5 & 7 = значение из условия шага 46»** — проверена скриптом по всем 70:
|
||||
**32 расхождения**. Пары ВКЛ/ВЫКЛ одной линии (`9628` ВКЛ и `9629` ВЫКЛ) имеют **одинаковое**
|
||||
поле 5 = `1`, но противоположные условия (`1` и `0`). Гипотеза развалилась — **снята**.
|
||||
- ❌ **Гипотеза «поле5 = 1 ⇔ ровно один шаг 46 с одним действием»** — проверена, дала
|
||||
«предсказано 0» на всех 70 (индекс поля в скрипте был сдвинут, результат мусорный). **Снята.**
|
||||
- ❌ «Поле 5 не выводится из содержимого» — **неверно**: выводится по правилу выше
|
||||
(`тип` + бит 8). Ранее записанный вывод был получен до того, как Alex назвал типы.
|
||||
- ❌ Слово «триггерный» как тип сценария — тоже выдумка. Alex: «в смысле блядь триггерные».
|
||||
Типов ровно четыре, названы Alex: `manual`/`trigger`/`interval`/`schedule`.
|
||||
|
||||
### ✅ Служебные ключи — БОЛЬШЕ НЕ НУЖНЫ
|
||||
|
||||
@@ -1111,17 +1174,47 @@ enabled = not (поле5 & 8) ← подтвержд
|
||||
что писать? Вариант `action: 9563` покрывает только первый. У `11109` их **7** — сейчас уходят
|
||||
в служебный `_then`. **Решение за Alex.** Всё остальное по форме закрыто.
|
||||
|
||||
### ⚠️ `run_scenario` — имя вместо id (открыто, 2026-09-17)
|
||||
|
||||
Сценарий может вызывать другой сценарий: `#Z8456` в поле 2 держит **id сценария** `11109`
|
||||
прямой ссылкой (не отдельным объектом-шагом):
|
||||
|
||||
```text
|
||||
#Z8456 =11,'Простой тестовый сценарий',[8817,8818,8822,8824,11109,8195,9500,…]
|
||||
#Z11109=11,'Передернуть Автомат Котельной',[11827,11828],0,0,0,0,0
|
||||
```
|
||||
|
||||
Парсер рендерит это так:
|
||||
|
||||
```yaml
|
||||
- id: 11109
|
||||
run_scenario: Передернуть Автомат Котельной
|
||||
```
|
||||
|
||||
🔴 **Проблема:** `id:` здесь вводит в заблуждение — это id **сценария**, а не шага;
|
||||
отдельного объекта-шага у такой ссылки нет. И `run_scenario` хранит **имя**, а не id —
|
||||
при переименовании сценария энкодер не найдёт цель. Alex: «нихуя не понял а id step-а?»,
|
||||
«`11109` это id сценария же».
|
||||
|
||||
**Решение за Alex:** `run_scenario` по id (надёжнее) или по имени (читаемее, как сейчас).
|
||||
Один объект `11109` играет две роли — он самостоятельный сценарий **и** элемент в `steps` у `8456`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Состояние проекта (проверено 2026-09-17, финал)
|
||||
|
||||
🟡 **Парсер переписан под форму §5j (триггер на уровне сценария), энкодер — ЕЩЁ НЕТ.**
|
||||
🟡 **Парсер переписан под форму §5j (триггер на уровне сценария + поле `type`), энкодер — ЕЩЁ НЕТ.**
|
||||
Round-trip сейчас **не сходится** — `yml-to-config.py` ждёт перевода. Рабочее дерево
|
||||
**не закоммичено**, ждёт команды Alex. Последний коммит — `199f2b1`.
|
||||
|
||||
✅ **Парсер `config-to-yml.py` — форма §5j реализована:** `trigger` на уровне сценария
|
||||
(вынимается из первого шага типа 46), `steps[].{id, action}`, `descr` у шага не выводится,
|
||||
`if`/`then`/`else`/`condition`/`operator`/`kind` из вывода убраны.
|
||||
`type` (`manual`/`trigger`/`interval`/`schedule`) выводится из тела, `if`/`then`/`else`/
|
||||
`condition`/`operator`/`kind`/`_f5` из вывода убраны.
|
||||
|
||||
🔴 **Известный баг парсера (единственный):** `#Z11109` получает `type: trigger`, хотя он `manual`
|
||||
(поле 5 = `0`). Гипотеза правки: `trigger` = ровно один элемент в `steps` И он типа 46.
|
||||
Прогон не выполнен. Детали — §5j.
|
||||
|
||||
✅ **Round-trip был чистый на 4/4 конфигах** (§5c, до правки формы). Счётчики совпадали: 661 → 661.
|
||||
**После правки формы — сломан, ждёт энкодера.**
|
||||
@@ -1132,19 +1225,22 @@ Round-trip сейчас **не сходится** — `yml-to-config.py` ждё
|
||||
✅ **YAML сжат вдвое:** 6093 → 4405 строк. Секции-дубли убраны, поля переименованы
|
||||
(`descr`/`target`/`value`).
|
||||
|
||||
✅ **Поле 5 типа 11 — РАСКРЫТО** (§5j): `тип + 8 при выключенном`;
|
||||
✅ **Поле 5 типа 11 — РАСКРЫТО И ПОДТВЕРЖДЕНО ПРИБОРОМ** (§5j): `тип + 8 при выключенном`;
|
||||
типы `manual`/`trigger`/`interval`/`schedule` (Alex), `enabled = not (f5 & 8)`.
|
||||
Включённые `schedule`/`interval` добыты тостингом в UI (`8597` → `0`, `8599` → `2`).
|
||||
`_f5` из YAML убран — поле восстанавливается из `enabled` + типа.
|
||||
|
||||
⚠️ **Открыт один вопрос:** `steps[].action` при >1 id в поле 4 шага 46 (у `11109` их 7) — §5j.
|
||||
⚠️ **Открыто два вопроса:** (1) `steps[].action` при >1 id в поле 4 шага 46 (у `11109` их 7);
|
||||
(2) `run_scenario` — имя или id. Оба — §5j.
|
||||
|
||||
| Файл | Статус |
|
||||
|---|---|
|
||||
| `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`, подстановки удалены). **не закоммичено** |
|
||||
| `config-to-yml.py` | 🆕 **изменён** — форма §5j: `trigger` на уровне сценария, `steps[].{id, action}`, поле `type` из тела, `_f5`/`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_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_18-43-24.txt` | 🆕 **34 962 байт** — **САМЫЙ ПОСЛЕДНИЙ** конфиг. Снят `curl -s http://192.168.0.50/config.txt` после включения `8597`/`8599` в UI. **не в git** |
|
||||
| `zont_config/config_local_2026-09-17_18-43-24.yml` | 🆕 форма §5j + поле `type`. **не в git** |
|
||||
| `zont_config/config_local_2026-09-17_17-45-00.txt` / `.yml` | 34 963 байт, 4405 строк — предыдущий (форма §5j, **без** поля `type`). **не в 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** |
|
||||
|
||||
Reference in New Issue
Block a user