[2026-09-17] eagle: family/how-to/zont-config-compiler.md family/tech/zont-scenario-logic-11109.md

This commit is contained in:
Alexey Martemyanov
2026-09-17 18:56:36 +06:00
parent b7e945e787
commit 72ba9f774c
2 changed files with 171 additions and 55 deletions
+125 -29
View File
@@ -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** |
+46 -26
View File
@@ -1,41 +1,60 @@
# 8.17. ✅ МОДЕЛЬ ЗАКРЫТА (2026-09-17, финал) — поле 5, форма сценария, type 47/49
> **Статус:** все открытые вопросы §8.16 закрыты. Ниже — подтверждённые факты.
> Таблица `TRIGGER_KINDS` **удалена из кода**; доказательство получено **тостингом 5 сценариев
> в UI**: Alex выключил и снова включил каждый тестовый сценарий, поле 5 снято до и после.
> Таблица `TRIGGER_KINDS` **удалена из кода**; доказательство получено **тостингом сценариев
> в UI**: Alex выключил/включил тестовые сценарии, поле 5 снято до и после, плюс повторный
> тостинг `8597`/`8599` дал **включённые** значения `schedule`/`interval` (`0` и `2`).
## (а) 🔴 Поле 5 типа 11 = тип запуска + флаг «выключен». ПОДТВЕРЖДЕНО тостингом
## (а) 🔴 Поле 5 типа 11 = **тип сценария** + флаг «выключен». ПОДТВЕРЖДЕНО прибора + Alex
```text
kind = field5 & 7 # 0 = ручной/расписание, 1 = триггер, 2 = интервал
type = field5 & 7 # 0 = manual | schedule, 1 = trigger, 2 = interval
enabled = not (field5 & 8) # бит 8 = сценарий ВЫКЛЮЧЕН
```
| Сценарий | Выкл | Вкл | `kind` | Прочие поля |
> 🔴 **Слово — `type`, не `kind`.** Слова `kind` в конфиге и UI нет; я его выдумал. Alex:
> «какой нахуй kind?? что это блядь значит?! я его откуда тебе высру?!» Типы назвал Alex:
> **`manual`, `trigger`, `interval`, `schedule`**.
| Сценарий | Выкл | Вкл | `type` | Прочие поля |
|---|---|---|---|---|
| `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` мс |
| `8456` «Простой тестовый» | `8` | — | 0 = `manual` | — |
| `8597` «по расписанию» | `8` | **`0`** | 0 = `schedule` | поля 3/4 = `61`, `3354` |
| `11109` «Передернуть Котельной» | — | `0` | 0 = `manual` | 2 элемента в поле 2 |
| `8547` «по времени» | `9` | — | 1 = `trigger` | — |
| `8551` «по триггеру» | `9` | — | 1 = `trigger` | — |
| `9628` «Автомат.: Рад. ванная 2эт ВКЛ» | `9` | `1` | 1 = `trigger` | — |
| `8599` «по интервалу» | `10` | **`2`** | 2 = `interval` | поле 6 = `43200000` мс |
**🔴 Включённые `0` (8597) и `2` (8599) получены тостингом на приборе:** Alex включил оба
сценария в UI, конфиг снят заново (`curl -s http://192.168.0.50/config.txt`). До этого момента
включённые значения `schedule`/`interval` были неизвестны — были только выключенные `8`/`10`.
**Итог: у каждого типа есть вкл/выкл, и всегда `вкл + 8 = выкл`:**
| тип | вкл | выкл |
|---|---|---|
| `manual` | `0` (11109) | `8` (8456) |
| `schedule` | `0` (8597) | `8` (был) |
| `trigger` | `1` (64 шт) | `9` (8547, 8551) |
| `interval` | `2` (8599) | `10` (был) |
> ⚠️ **`manual` (0) и `schedule` (0) — один и тот же `type`.** Различаются **наличием полей 3/4**
> (`days_mask` + `time`): у расписания они заполнены, у ручного — нули. Alex: «manual от schedule
> очевидно отличаются наличием блядь schedule!»
> Кодировку расписания см. §8.1 (`61` = ПН,СР,ЧТ,ПТ,СБ; `3354` = `(13<<8)|26` = 13:26).
**Почему старая модель `{0:manual,1:manual,8:schedule,9:trigger,10:interval}` была неверна:**
она читала число как «класс запуска» целиком. На самом деле число **двухбитовое по смыслу**:
младшие 3 бита = тип, бит `8` = выключено. Отсюда все противоречия §8.16:
`9628` с `1` — триггерный (бит 8 не стоит, kind = 1), `8456` с `8` — ручной, но выключенный.
младшие 3 бита = тип, бит `8` = выключено. Отсюда все противоречия §8.16.
> ⚠️ **Ручной (0) и расписание (0) — один и тот же `kind`.** Различаются **наличием полей 3/4**
> (`days_mask` + `time`): у расписания они заполнены, у ручного — нули.
> Кодировку расписания см. §8.1 (`61` = ПН,СР,ЧТ,ПТ,СБ; `3354` = `(13<<8)|26` = 13:26).
> **Снятые гипотезы (не возвращаться):** «`field5 & 7` = значение из условия шага 46»
> (32 расхождения из 70 — пары ВКЛ/ВЫКЛ `9628`/`9629` имеют одинаковое поле `1` при
> противоположных условиях); «поле 5 не выводится из содержимого» (выводится: тип + бит 8).
**Что в коде:** `TRIGGER_KINDS`, `_raw_trigger_kind`, `_raw_trigger_params`, `time_raw` **удалены**.
Заголовок сценария в YAML теперь: `enabled`, `_f5` (поле 5 целиком), `_kind` (только если
`f5 & 7 ≠ 0`), `days_mask`, `time`, `interval_ms`.
> ⚠️ **Обновлено после правок формы:** `kind`/`kind_raw` из видимой части **убраны** — это тоже
> была выдумка (слова `kind` в конфиге нет). Всё, что нужно энкодеру, лежит в служебных
> `_f5`/`_kind` с подчёркиванием.
Заголовок сценария в YAML теперь: `enabled`, **`type`**, `days`/`days_mask`, `time`, `interval_ms`.
Служебные `_f5`/`_kind` **тоже убраны** — поле 5 полностью восстанавливается из `enabled` + `type`.
## (б) Форма YAML сценария — ПЕРЕСМОТРЕНА Alex'ом (финал сессии)
@@ -115,11 +134,12 @@ Alex отверг `if`/`then`/`else` в YAML как **выдумку**:
| # | Что | Статус |
|---|---|---|
| 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` | ✅ сделано |
| 2 | `dump_step` type 46 → `{id, action: <первый then id>}`; `trigger` **вынимается на уровень сценария** | ✅ сделано |
| 3 | Заголовок сценария — `kind`/`kind_raw`/`_kind`/`_f5` **убраны**, вместо них видимое поле **`type`** | ✅ сделано |
| 4 | Непустые `then`/`else` / второй+ id → что писать в форме | 🔴 **открытый вопрос, ждёт Alex** |
| 5 | `dump_action` — делегирование в `dump_step` (чтобы type 5/9 в телах не падали в `raw`) | ✅ сделано (было раньше) |
| 6 | Энкодер под новую форму | ⏸ не начат — ждёт подтверждения формы |
| 5 | `dump_action` — делегирование в `dump_step` (чтобы type 5/9 в телах не падали в `raw`) | ✅ сделано |
| 6 | Энкодер под новую форму | ⏸ **не начат** — ждёт подтверждения формы. Round-trip сломан |
| 7 | `type` для `11109` выходит `trigger` вместо `manual` | ⚠️ **баг**, гипотеза правки: `trigger` = ровно один элемент в `steps` И он типа 46. Прогон не выполнен |
### ✅ Фактический вывод парсера после правок (проверено 2026-09-17)