[2026-09-17] eagle: family/how-to/zont-config-compiler.md family/tech/zont-config-object-types.md
This commit is contained in:
@@ -21,7 +21,7 @@ tags:
|
||||
- homeautomation
|
||||
title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml
|
||||
type: how-to
|
||||
updated: 2026-09-17f
|
||||
updated: 2026-09-17g
|
||||
---
|
||||
|
||||
# ⚙️ ZONT Config Compiler — конвертеры `.txt ⇄ .yml`
|
||||
@@ -1470,16 +1470,53 @@ Alex, последнее сообщение сессии:
|
||||
переведены на форму §6a. Проверялось **с файла на диске**, не из `/tmp`.
|
||||
|
||||
✅ **Форма закрыта (2026-09-17, финал) — см. §6a.** Сценарий = `id`/`name`/`enabled` +
|
||||
опциональный `trigger:` + `steps[]`. Шаг 46 = `{id, [flag], if, then[], [else[]]}`.
|
||||
`type`, `field5`, `kind`, `_f5`, `_kind`, `_else`, `action` — **всё вырезано** (round-trip
|
||||
подтверждает: `grep -c 'type:\|field5\|kind'` = 0).
|
||||
опциональный `trigger:` + `steps[]`. Шаг 46 = `{id, [flag], if, then[], [else[]]}` либо
|
||||
`{id, [flag], action[], [else[]]}`.
|
||||
|
||||
✅ **Открытый вопрос ЗАКРЫТ — `trigger:` на уровне сценария.** Alex: «наличием поля trigger:
|
||||
блядь если ты еблан тупоголовый не можешь догадаться!». Правило: **если `steps` состоит ровно
|
||||
из одного шага-46 — его условие поднимается в `trigger:`**, сам шаг остаётся в `steps` с `id`
|
||||
и `then` (запись `#Z9819` существует отдельно). Подтверждено на `9691/9819`, `9628/9756`,
|
||||
`8547/8550`. Проверено и обратное: `11109` (2 шага) и `8456` (27 шагов) с 46 внутри — **не**
|
||||
trigger, поле 5 = `0`/`8`.
|
||||
🔴 **ГЛАВНОЕ ПРАВИЛО ФОРМЫ ШАГА (2026-09-17, самый последний фикс):**
|
||||
|
||||
| У шага есть свой `if`? | Список действий называется | Почему |
|
||||
|---|---|---|
|
||||
| **да** — условие внутри шага | **`then`** (+ `else` парой) | условие и его ветки — единая конструкция if/then/else |
|
||||
| **нет** — условие поднято в `trigger:` | **`action`** | от шага остался только список действий |
|
||||
|
||||
```yaml
|
||||
# trigger: условие наверху → шаг = action
|
||||
- id: 9688
|
||||
name: 'Автомат.: (н/п) (14/12) ВЫКЛ'
|
||||
enabled: true
|
||||
trigger:
|
||||
id: 9726
|
||||
object: 9494
|
||||
value: 0
|
||||
steps:
|
||||
- id: 9816
|
||||
action:
|
||||
- id: 9560
|
||||
descr: 'Выключить выход 14/12: (н/п)'
|
||||
target: 146032
|
||||
value: 1
|
||||
|
||||
# if внутри шага → then/else парой
|
||||
- id: 8863
|
||||
if: {id: 8853, group: and, children: [...]}
|
||||
then:
|
||||
- id: 8862
|
||||
flag: 1
|
||||
if: {id: 8858, group: or, children: [...]}
|
||||
then: [- id: 8859, ...]
|
||||
else: [- id: 8861, ...]
|
||||
```
|
||||
|
||||
Проверка на снимке `18-43-24`: `trigger:` 66 · `action:` 66 · `if:` 2 · `then:` 2 · анкоров 3
|
||||
(законные, не сценарии). `type`, `field5`, `kind`, `_f5`, `_kind`, `_else` — **вырезаны**
|
||||
(`grep -c` = 0).
|
||||
|
||||
✅ **Открытый вопрос ЗАКРЫТ — `trigger:` на уровне сценария.** Alex: «наличием поля trigger:».
|
||||
Правило: **если `steps` состоит ровно из одного шага-46 — его условие поднимается в
|
||||
`trigger:`**, а шаг остаётся в `steps` с `id` и `action` (запись `#Z9819` существует
|
||||
отдельно). Подтверждено на `9691/9819`, `9628/9756`, `8547/8550`. Проверено и обратное:
|
||||
`11109` (2 шага) и `8456` (27 шагов) с 46 внутри — **не** trigger, поле 5 = `0`/`8`.
|
||||
|
||||
✅ **Закоммичено — `1cc010a` на `main`** (2026-09-17, финал). 12 файлов: оба конвертера +
|
||||
5 снапшотов `zont_config/*.{txt,yml}`. **Не вошли** (остались untracked): 14 скриптов разбора,
|
||||
@@ -1522,7 +1559,118 @@ if not scenario.get('enabled', True):
|
||||
> `18-43-24.yml`, который увидел Alex. Файл перегенерирован на диск; бэкап старого —
|
||||
> `/tmp/backup_18-43-24.yml`. **Гонять конвертер надо в целевой файл, а не в `/tmp`.**
|
||||
|
||||
### Осталось не разобрано (остаётся `raw`/`unresolved`)
|
||||
### Объём коммита — РЕШЁН (2026-09-17)
|
||||
|
||||
Alex: «комит.» → закоммичено `1cc010a` на `main`, 12 файлов: оба конвертера +
|
||||
5 снапшотов `zont_config/*.{txt,yml}`. **Не вошли** (untracked): 14 скриптов разбора,
|
||||
`zont_api_docs/`, `zont_local_ui_recon/`. Push не сделан.
|
||||
|
||||
> 🔴 Коммит `1cc010a` сделан **до** финальных правок формы шага (`then`/`action`) — в нём
|
||||
> сломанные версии конвертеров и снапшот с `type:`. Требует `--amend` либо нового коммита,
|
||||
> см. §6f.
|
||||
|
||||
---
|
||||
|
||||
## 6d. 🔴 Питфоллы последнего круга (2026-09-17, после коммита `1cc010a`)
|
||||
|
||||
### 1. Дампер писал в `/tmp` — снапшот на диске остался старым 🔴
|
||||
|
||||
Все проверки шли как `python3 config-to-yml.py in.txt > /tmp/new.yml`. Файл
|
||||
`zont_config/config_local_2026-09-17_18-43-24.yml` не перегенерировался — Alex видел там
|
||||
`type: trigger` (70 шт), хотя из кода `type` уже был вырезан. **Правило: гонять конвертер
|
||||
в целевой файл, а не в `/tmp`; проверять `exit=` и размер файла.**
|
||||
|
||||
### 2. PyYAML-анкоры `&id061` / `*id061` 🔴
|
||||
|
||||
Подъём `trigger:` был сделан **той же dict-ссылкой**, что осталась в шаге:
|
||||
|
||||
```python
|
||||
scenario['trigger'] = flat_steps[0]['if'] # одна и та же ссылка в двух местах!
|
||||
```
|
||||
|
||||
PyYAML на один объект в двух местах пишет анкор и алиас — в файле появлялось 69 пар
|
||||
`&idNNN`/`*idNNN`. Alex: «че это блядь за хуяня?! нормально же блядь все было!».
|
||||
|
||||
**Две последовательные ошибки, обе исправлены:**
|
||||
1. `deepcopy` — убирает анкор, но оставляет **дубль** условия в файле.
|
||||
2. `pop('if')` — условие **переносится** в `trigger:` и из шага уходит совсем. ✅ финал.
|
||||
|
||||
Остаются 3 законных анкора — не сценарии: пустой список `[]`, одинаковый `raw`, `sensors`.
|
||||
|
||||
### 3. `if` нельзя вынимать из `dump_step` — ломается подъём `trigger` 🔴
|
||||
|
||||
Удаление `node['if'] = ...` в `dump_step` сломало **всё**: подъём ищет `'if' in flat_steps[0]`,
|
||||
не находил — условие терялось и у trigger (`9691`), и у manual (`11827`), и у `8555`.
|
||||
Снаружи выглядело как «шаг без условия, голый action».
|
||||
|
||||
**Правильно:** `if` **пишется** в `dump_step`, а у trigger-сценария **вынимается** через `pop`
|
||||
уже после (`scenario['trigger'] = first.pop('if')`).
|
||||
|
||||
### 4. `then` vs `action` — по наличию своего `if` у шага 🔴
|
||||
|
||||
- шаг **со своим** `if` → список действий = **`then`** (+ `else` парой);
|
||||
- шаг **без** своего `if` (условие поднято в `trigger:`) → список = **`action`**.
|
||||
|
||||
Alex: «какого хуя там then блядь?!» → затем «ДА БЛЯДЬ! КАК ТЫ БЛЯДЬ ДУМАЕШЬ?!
|
||||
Then конечно!!!» — это **не противоречие**: он говорил про разные шаги. Реализация в
|
||||
`dump_step` — ветка по `cond_id`; при подъёме `trigger:` ключ `then` переименовывается
|
||||
в `action` с сохранением порядка (`id`, `flag`, `action`, `else`).
|
||||
|
||||
### 5. Энкодер: обе ветки `emit_step` должны принимать И `then`, И `action` 🔴
|
||||
|
||||
После переименования в дампере энкодер терял списки (`#Z8550=46,0,8548,[],[]`). Исправлено
|
||||
во **всех четырёх** местах: `'then' in X or 'action' in X`, список берётся как
|
||||
`X.get('then') or X.get('action') or []`.
|
||||
|
||||
### 6. Две ветки `emit_step` для типа 46 — мёртвый код 🪤
|
||||
|
||||
В `yml-to-config.py` жили **две** ветки для 46: старая (стр. ~490, `if 'if' in step:`) и новая
|
||||
(стр. ~528). Старая стояла **выше**, перехватывала вызов и читала удалённые
|
||||
`action`/`_else`/`_kind`. Правка нижней ветки **не давала эффекта**, а round-trip был зелёным,
|
||||
пока не всплывали 12 потерянных объектов из `then`.
|
||||
**Правило: после правки проверить, что нужная ветка достижима (порядок `if`-ов сверху вниз),
|
||||
а не только что она написана.**
|
||||
|
||||
> 📌 **Итог проверки на снимке `18-43-24` (файл с диска):** `661 → 661` объектов,
|
||||
> `686 → 686` строк, `diff` по отсортированным `#Z` = **0**. `trigger:` 66 · `action:` 66 ·
|
||||
> `if:` 2 · `then:` 2 · анкоров 3.
|
||||
|
||||
---
|
||||
|
||||
## 6e. Круглые итерации — счёт вырос до 7
|
||||
|
||||
К §6c добавляются круги этой сессии. Диагноз: **вырезал поле → затащил его обратно под новым
|
||||
именем**, повторено 7 раз; в двух случаях источником были мои же комментарии, которые я правил
|
||||
вместо кода.
|
||||
|
||||
| Круг | Что затащил | Реплика Alex |
|
||||
|---|---|---|
|
||||
| 6 | `action` вместо `then` **у шага с `if`** | «ты блядь теперь if-then констркукции запорол» |
|
||||
| 7 | `then` вместо `action` **у шага без `if`** | «какого хуя там then блядь?!» |
|
||||
|
||||
**Урок круга 6–7 (главный):** у одного и того же поля (поле 3 записи 46) **имя зависит от
|
||||
контекста шага**. Я искал одно «правильное» имя, а их два, и выбор диктуется наличием `if`.
|
||||
Когда Alex сказал «then конечно!» — это не отменяло предыдущее «какого хуя там then»:
|
||||
он говорил **про разные шаги**. Слушать условие, а не только имя.
|
||||
|
||||
---
|
||||
|
||||
## 6f. Состояние репозитория (2026-09-17, самый финал)
|
||||
|
||||
| Что | Состояние |
|
||||
|---|---|
|
||||
| Коммит | `1cc010a` на `main` — **содержит сломанные версии** конвертеров + снапшот с `type`/анкорами |
|
||||
| Рабочее дерево | ✅ исправлено: правило `then`/`action`, `pop('if')` при подъёме, энкодер принимает оба ключа (4 ветки) |
|
||||
| Снапшот на диске | ✅ перегенерирован, round-trip `diff` = 0 |
|
||||
| Push | ❌ не сделан |
|
||||
| Не в коммите | 14 скриптов разбора, `zont_api_docs/`, `zont_local_ui_recon/` |
|
||||
|
||||
🔴 **Коммит требует дописывания:** `git commit --amend` либо отдельный коммит с правкой формы
|
||||
шага (`then`/`action`) + перегенерированным снапшотом.
|
||||
|
||||
---
|
||||
|
||||
## 6g. Осталось не разобрано (после этой сессии)
|
||||
|
||||
⚠️ **Не разобрано (остаётся `raw`/`unresolved`):**
|
||||
1. **Тип 50** — маска дней недели: `#Z8548=50,1,0,0,109`, где `109 = 0b1101101` = пн,ср,чт,сб,вс
|
||||
@@ -1532,22 +1680,24 @@ if not scenario.get('enabled', True):
|
||||
пн, вт, чт, пт, сб, вс»).
|
||||
3. **`unresolved: true`** у `#Z8860`, `#Z8864`, `#Z8601` — этих объектов нет в конфиге.
|
||||
4. **Старый конфиг `14-16-35`** — судьба не решена (с тестовыми сценариями логики).
|
||||
5. ~~Старые снапшоты содержат `type:`~~ — `18-43-24.yml` перегенерирован (§6). Старые
|
||||
`14-16-35.yml` / `16-02-18.yml` всё ещё содержат по 2 вхождения `type:` (сгенерированы до
|
||||
правки формы, закоммичены как есть). Перегенерировать по команде.
|
||||
5. **Старые снапшоты `14-16-35.yml` / `16-02-18.yml`** — по 2 вхождения `type:`
|
||||
(сгенерированы до правки формы, закоммичены как есть). Перегенерировать по команде.
|
||||
|
||||
### Объём коммита (ждёт ответа Alex)
|
||||
---
|
||||
|
||||
| Включить | Не включать (если не скажет иначе) |
|
||||
|---|---|
|
||||
| `config-to-yml.py`, `yml-to-config.py` | 14 скриптов разбора (`probe_types.py`, `audit2.py`, `read_scenarios.py`, `dump_new_types.py`, `trace_scenarios.py`, `probe_sched.py`, `verify_answers.py`, `check_ops.py`, `audit_8456.py`, `chk_extra.py`, `why_raw.py`, `fit_temp.py`, `fit_temp2.py` и т.п.) |
|
||||
| снапшот `zont_config/config_local_2026-09-17_18-43-24.{txt,yml}` | `zont_api_docs/`, старые снапшоты, `zont_local_ui_recon/` |
|
||||
## 7. Связанные заметки
|
||||
|
||||
- [[family/tech/zont-config-object-types]] — таблица типов объектов
|
||||
- [[family/tech/zont-scenario-logic-11109]] — разбор сценария 11109
|
||||
- [[family/tech/zont-api]] — API ZONT: конфиг через него недоступен
|
||||
- [[family/how-to/home-automation]] §6 — ZONT в общем контуре
|
||||
|
||||
---
|
||||
|
||||
## 6a. ✅ АКТУАЛЬНАЯ форма сценария в YAML (ФИНАЛ, 2026-09-17)
|
||||
|
||||
**Trigger-сценарий** (`steps` = ровно один шаг 46) — условие поднимается в `trigger:`:
|
||||
**Trigger-сценарий** (`steps` = ровно один шаг 46) — условие поднимается в `trigger:`,
|
||||
в шаге остаётся `action` (условия в шаге больше нет):
|
||||
|
||||
```yaml
|
||||
- id: 9691
|
||||
@@ -1559,7 +1709,7 @@ if not scenario.get('enabled', True):
|
||||
value: 1
|
||||
steps:
|
||||
- id: 9819
|
||||
then:
|
||||
action: # ← 'action', а не 'then': своего 'if' у шага нет
|
||||
- id: 9563
|
||||
descr: 'Включить выход 14/14: Прихожая (н/п)'
|
||||
target: 146064
|
||||
@@ -1575,7 +1725,7 @@ if not scenario.get('enabled', True):
|
||||
```
|
||||
|
||||
**Manual-сценарий** (несколько шагов, 46 — обычный шаг внутри) — `trigger:` отсутствует,
|
||||
условие живёт в шаге как `if:`:
|
||||
условие живёт в шаге как `if:`, а список действий — как `then:` (парой с `else:`):
|
||||
|
||||
```yaml
|
||||
- id: 11109
|
||||
@@ -1641,7 +1791,8 @@ if not scenario.get('enabled', True):
|
||||
| `steps[].id` | `#Z<sid>=46,…` поле 0 (id) | |
|
||||
| `steps[].flag` | `#Z<sid>` **поле 1** | пишется только если ≠ 0 (иначе `0`) |
|
||||
| `steps[].if` | `#Z<sid>` поле 2 (id условия) → `#Z<cond>=49,…` | поле 3 = оператор → `_op` если ≠ 1 |
|
||||
| `steps[].then[]` | `#Z<sid>` поле 3 (список id) | тела инлайн |
|
||||
| `steps[].then[]` | `#Z<sid>` поле 3 (список id) | тела инлайн; **только когда у шага есть свой `if`** |
|
||||
| `steps[].action[]` | `#Z<sid>` поле 3 (список id) | то же поле, но у шага **нет** своего `if` (условие ушло в `trigger:`) |
|
||||
| `steps[].else[]` | `#Z<sid>` поле 4 (список id) | пишется только если непусто |
|
||||
|
||||
### Сборка поля 5 при обратной конвертации (`yml-to-config.py`)
|
||||
@@ -1669,16 +1820,19 @@ if not scenario.get('enabled', True):
|
||||
| `field5` | «служебный ключ» вместо `_f5` — тот же грех; Alex: «какой нахуй field5» |
|
||||
| `kind`, `_f5`, `_kind`, `_else` | выдуманные/служебные ключи |
|
||||
| `base5` / словарь `{manual:0, schedule:0, trigger:1, interval:2}` | тот же выдуманный маппинг имён типов |
|
||||
| `action` вместо `then` | `then`/`else` — имена полей 3/4 самой записи 46, это факт, а не выдумка |
|
||||
| `run_scenario: <имя>` | подстановка имени из чужого объекта — у ссылки только `id` |
|
||||
| `target_name`, `target_type`, `raw_value` | дорисовка парсера, в строке конфига их нет |
|
||||
|
||||
> 📌 **Общий принцип (питфолл 45):** YAML-ключ обязан соответствовать полю строки конфига
|
||||
> либо выводиться из тела. Подстановка из **другого** объекта запрещена. `then`/`else`/`if`/`flag`
|
||||
> либо выводиться из тела. Подстановка из **другого** объекта запрещена. `if`/`then`/`else`/`flag`
|
||||
> — это **поля записи 46**, поэтому они разрешены; `type`/`field5` — не поля, поэтому запрещены.
|
||||
>
|
||||
> ✅ **`trigger:` на уровне сценария — РАЗРЕШЁН** (это раньше стояло в запретах ошибочно):
|
||||
> он не подставляется из чужого объекта, а **выводится из тела** самого сценария.
|
||||
>
|
||||
> ✅ **`action:` — РАЗРЕШЁН и обязателен** для шага с поднятым `trigger:` (см. §6, правило формы
|
||||
> шага). Ошибочная запись «`action` запрещён» относилась к другой итерации, где `action` был
|
||||
> **единственным** id вместо списка и подменял `then` там, где шаг имел свой `if`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -156,11 +156,16 @@ Modbus-регистры (тип 52) в конфиге — **отдельные
|
||||
> ...
|
||||
> ```
|
||||
>
|
||||
> `if` ← поле 2 (`cond_id`), `then` ← поле 3 (`[9563]`), `else` ← поле 4, `flag` ← поле 1.
|
||||
> `then`/`else`/`if`/`flag` — **имена полей записи 46**, поэтому разрешены; `then`/`else` — **пара**.
|
||||
> `if` ← поле 2 (`cond_id`), поле 3 — список действий, `else` ← поле 4, `flag` ← поле 1.
|
||||
>
|
||||
> 🔴 **Имя списка действий зависит от того, есть ли у шага свой `if`:**
|
||||
> - **есть `if`** → `then:` (+ `else:` парой) — вся конструкция if/then/else в шаге;
|
||||
> - **`if` поднят в `trigger:`** (у сценария ровно один шаг-46) → `action:`, шаг — чистый список.
|
||||
>
|
||||
> `if`/`then`/`else`/`action`/`flag` — **имена полей записи 46** (по контексту), поэтому разрешены.
|
||||
> **`trigger:` на уровне сценария РАЗРЕШЁН** — выводится из тела (ровно один шаг 46), не подставляется.
|
||||
> `type` / `field5` / `kind` / `_f5` / `action` — **запрещены**.
|
||||
> Полностью — [[family/how-to/zont-config-compiler]] §6a, правки после коммита — §6d.
|
||||
> `type` / `field5` / `kind` / `_f5` — **запрещены**.
|
||||
> Полностью — [[family/how-to/zont-config-compiler]] §6 и §6a.
|
||||
>
|
||||
> ⚠️ **Питфолл (§6d):** условие для `trigger:` вынимается из шага **переносом (`pop`)**, не копией.
|
||||
> `deepcopy` даёт PyYAML-анкоры `&idNNN`/`*idNNN`; убирание `if` из `dump_step` теряет условие у ВСЕХ
|
||||
|
||||
Reference in New Issue
Block a user