[2026-09-17] eagle: personal/projects/zont-config-compiler.md

This commit is contained in:
Alexey Martemyanov
2026-09-17 21:08:53 +06:00
parent dafdcf9ec6
commit 272bc5dc6e
+152 -43
View File
@@ -381,11 +381,14 @@ enabled = not (f5 & 8)
| ⛔ **применять разобранную форму ко ВСЕМ объектам типа, а не только к подтверждённым** | временную форму 50 применил к `8844``op: <` / `time: 00:00`; Alex: «ты сломал его нахуй», §10.5 круг 24 |
| ⛔ **`days_mask: <число>` / `mask: <число>` / `_head` / `raw` в теле объекта 50** | «какой нахуй days», «какой mask», «откуда блядь опять raw выполз», «че за head блядь» — §10.5 |
| ⛔ **выдумывать знак сравнения, если поле не подтверждено** | `>` у `8842` получен случайным совпадением (`1``LEAF_OPS`); контрпримера нет — знак в YAML не пишется, §10.5 |
| ⛔ **`_f2` / `_f5` — служебные ключи «на всякий случай»** | `_f2: 1` доехало до YAML у `set_var` объекта `8844` (`type: days_mask`); Alex: **«это че бля»**. Поля 2/5 восстанавливаются из `type`+`days`/`op` — дублировать нельзя, §10.5 круг 26 |
| ⛔ **имя параметра объекта 49 при поле 3 = `0`** | поле 4 — код **параметра по типу владельца** (§10.6): `3` = модуляция у электрокотла и целевая температура у контура. Единого словаря нет, имена не выдумывать — ждать подтверждения |
**РАЗРЕШЕНО:** `trigger:` (выводится из тела — один шаг 46), `if`/`then`/`else`/`flag` — **реальные поля
записи 46**, `action` — имя списка у шага с поднятым условием, `set_var: {id, name, object/operator/value
| days/days_mask}`**разобранное значение** (§10.1), `log: <текст>` — плоский (§10.3),
`storeenv: {level, text}`**разобранный вызов** журнала событий (§10.2).
`storeenv: {level, text}`**разобранный вызов** журнала событий (§10.2),
**`op: '…'` + `time: 'HH:MM'`** у `type: time_condition` — знак подтверждён на 4/4 условиях (§10.5).
Служебные `_`-ключи — **только** на нестандартных случаях (`_op` при операторе ≠ 1, `_raw_level`, `_raw_op`).
Безымянные `_head`/`_body` заменены на `raw` (§10.1 круги 1516).
@@ -580,6 +583,10 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z<id>=<тип>'
| 18 | 🔴 **Alex называет ГОТОВОЕ имя, а не направление** | «`field` его обзови» / «`type: condition` блядь!» — это финальный ответ, а не подсказка для дальнейшего изобретения. Принимать буквально, не искать «улучшение». Он отвергает промежуточные формы (круги 19–21: `field`/`value``field``type: condition`). |
| 19 | 🔴 **Не «узнавать» смысл поля по аналогии с другим типом** | Объект 50 дважды выведен неверно: как расписание сценария (`days`) и как маска. Alex: «какой нахуй days! я тебе блядь недоступно написал?!» → истина «current time > 13:23». **Сначала СЛОВА из UI, потом модель** (питфолл 32). Проверять множество значений поля по ВСЕМУ конфигу (`Counter`), не строить аналогию. |
| 20 | 🔴 **Выдуманный термин = выдуманная модель** | Alex: «че такое `cmp`… гдето у нас еще есть термин "cmp"?» — термина в проекте не было. Имена ключей брать из **уже принятого словаря** (`op` у типа 47), не изобретать синонимы. |
| 21 | 🔴 **«Я добавил/поменял в UI» → СНАЧАЛА `curl`, потом любой `grep`** | Alex создал `Values test` в UI и сказал «скачивай». Я проверил прошлый снимок (`20-55-22`), не нашёл, и **трижды доложил «сценария нет»** — «ты дебил?», «да как нахуй нет то блядь?! ты блядь скачал конфиг новый?!». Перекачал → `21-07-03`, 760 строк, `#Z9324=11,'Values test'` на месте. **Прошлый файл — не доказательство отсутствия объекта.** |
| 22 | 🔴 **Не рапортовать «в данных этого нет», не перепроверив источник свежим запросом** | Тот же случай. Утверждение «нет в конфиге» проверяется только на **снимке, снятом ПОСЛЕ** изменения. Порядок: `curl``wc -l` (сравнить с прошлым) → `grep`. Если строк стало больше — данные новые. |
| 23 | ⚠️ **Служебный `_`-ключ «на всякий случай» = мусор в артефакте** | `_f2: 1` в `set_var` объекта 50 (`type: days_mask`) — я сохранял поля 2/5 «пока семантика не ясна». Alex: «это че бля». Поля, которые восстанавливаются из разобранных ключей, **не дублировать**. Ключ удалён. |
| 24 | ⚠️ **`sed`/`execute_code` падают на середине проверки — это НЕ «расхождение»** | Round-trip-диф упал на `sed: RE error: illegal byte sequence` (cp1251) и на `No module named 'psutil'` — оба раза вывод выглядел как «есть расхождения». **Сравнивать в Python по байтам** (`open(...,'rb')` + нормализация `\r\n`), не `sed`/`iconv`-пайплайном. |
### 7.2. Код — парсер/энкодер
@@ -702,13 +709,14 @@ https://lk.zont-online.ru/download/firmwares/H2000_PRO_<HW>__<FW>_<PROFILE>.zip
| Что | Состояние |
|---|---|
| Round-trip | 🟢 **ЗЕЛЁНЫЙ, байт-в-байт**`698 → 698`, `723 → 723` (снимок `20-40-48`); ранее `661 → 661` (`19-53-21`). Прогон на `20-55-22` **не сделан** (снимок только снят, конвертеры не менялись) |
| Round-trip | 🟢 **ЗЕЛЁНЫЙ**`698 → 698`, `723 → 723` (снимок `20-40-48`); `661 → 661` (`19-53-21`). На `20-55-22` и **`21-07-03` прогон НЕ сделан** — только регенерация `.yml` (80945 байт на `20-55-22`) |
| Форма сценария | ✅ закрыта (§5), оба конвертера переведены |
| Тела типа 59 | ✅ **`storeenv` (§10.2), `set_var` с вложенным телом цели (§10.1, круги 1221), `log` плоский (§10.3) — готовы, не закоммичены** · `objcmd`/`expr`/`objstate``descr`+`args` |
| Условия 49 (`set_var`) | ✅ **форма закрыта**`type: condition` + `object` + `event`\|`value`; поле 3 в YAML не пишется; 18 комбинаций из **Conditions Test** (§10.4) |
| Объект 50 (`set_var`) | ✅ **две формы по ПОЛЮ 2**: `0` = время → `type: time_condition` + `time`; `1` = дни → `type: days_mask` + `days` (§10.5). ⚠️ **знак сравнения лежит в ПОЛЕ 3** (найден круг 25: `9248`=`1`, `9250`=`0`, `9252`=`2`, `9254`=`4`) — коды совпадают с `LEAF_OPS` типа 47, но **`op` в YAML не пишется, пока Alex не назвал подписи этих 4 условий** |
| Условия 49 при поле 3 = `1` | ✅ **форма закрыта**`type: condition` + `object` + `event`; поле 3 в YAML не пишется; 18 комбинаций из **Conditions Test** (§10.4) |
| Условия 49 при поле 3 = `0` | ⚠️ **семантика раскрыта, ключ не заведён** — поле 4 = **код параметра по типу владельца** (§10.6, **Values test**, 15 значений). В YAML пока `value: <число>`. Имена параметров не выдумывать — ждать подтверждения словаря |
| Объект 50 (`set_var`) | ✅ **ЗАКРЫТО (§10.5)****поле 2** = форма (`0` время / `1` дни), **поле 3** = знак сравнения, поле 4 = время, поле 5 = маска. Знак пишется как **`op`** — коды `0 <` `1 >` `2 =` `3 <=` `4 >=` (совпали с типом 47 на 4/4 условиях) |
| Имя переменной | ✅ `set var1` **и** `set varname` — регексп `^set (var\w*\|\w+)$` (§10.4) |
| `field` / `mode` | ⛔ **удалены** — 0 вхождений в файле (§10.1 круги 1921) |
| `field` / `mode` / `cmp` / `_f2` / `_f5` | ⛔ **удалены** — 0 вхождений в коде и файлах (§10.1 круги 1921, круг 26) |
| Потери объектов | ✅ **0** — было 5 (`8472`, `8821`, `8849`, `8851`, `8855`) |
| `unresolved: true` | ⚠️ висит у `#Z8860`, `#Z8864`, `#Z8601` — объекта в конфиге нет (§10, §10.4) |
| Коммит | `b75c51f` — «Drop stale YAML snapshots in old scenario shape» ← **текущий HEAD** |
@@ -1248,30 +1256,31 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z8830=59'
> (вложенный шаг), а два — в `scenario_orphans`. Реальный счёт был 3 из 5, не 2. Считать без
> привязки к отступу и печатать **какие именно id** не попали, прежде чем делать вывод.
### 10.5. ✅ Объект 50 — ДВЕ формы: время и дни (круги 22–24, 2026-09-17)
### 10.5. ✅ Объект 50 — ДВЕ формы: время и дни (круги 22–26) — ЗАКРЫТО
**Отправная точка:** `#Z8842=50,0,1,3351,0` выводился как `type: days_mask` + `days: []` +
`days_mask: 0` + `raw: [0, 1, 3351]`. Alex: «откуда блядь опять raw выполз блядь», затем
«там: current time > 13:23», затем «какой нахуй days! я тебе блядь недоступно написал?!».
**Итог:** объект 50 имеет **ДВЕ формы**, различаются **полем 3**:
**Итог:** объект 50 имеет **ДВЕ формы**, различаются **полем 2** (не полем 3 — круг 26):
| Поле 3 | Форма | Поле 4 | Поле 5 | YAML |
|---|---|---|---|---|
| `1` | условие по времени | **время** `(час<<8)\|мин` | 0 | `type: time_condition` + `time: 'HH:MM'` |
| `0` | **выбор дней недели** | 0 | **маска дней** (бит 0 = ПН) | `type: days_mask` + `days: [mon, …]` |
| Поле 2 | Форма | Поле 3 | Поле 4 | Поле 5 | YAML |
|---|---|---|---|---|---|
| `0` | условие по времени | **знак сравнения** | **время** `(час<<8)\|мин` | 0 | `type: time_condition` + `op` + `time: 'HH:MM'` |
| `1` | **выбор дней недели** | 0 | 0 | **маска дней** (бит 0 = ПН) | `type: days_mask` + `days: [mon, …]` |
```yaml
- id: 8843 # #Z8843=59,'set var1',8842,0,0
- id: 9249 # #Z9249=59,'set var1',9248,0,0
set_var:
id: 8842 # #Z8842=50,0,1,3351,0 (поле 3 = 1 → время)
id: 9248 # #Z9248=50,0,1,3351,0 (поле 2 = 0 → время)
name: var1
type: time_condition
op: '>' # поле 3 = 1
time: '13:23' # поле 4 = 3351 = 13*256+23
- id: 8845 # #Z8845=59,'set var1',8844,0,0
- id: 9257 # #Z9257=59,'set var1',9256,0,0
set_var:
id: 8844 # #Z8844=50,1,0,0,123 (поле 3 = 0 → дни)
id: 9256 # #Z9256=50,1,0,0,123 (поле 2 = 1 → дни)
name: var1
type: days_mask
days: [mon, tue, thu, fri, sat, sun] # поле 5 = 123
@@ -1279,43 +1288,47 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z8830=59'
| Поле 50 | Что | В YAML |
|---|---|---|
| `2` | **признак формы**: `1` = **дни недели**, `0` = **время** (подтверждено, круг 25) | **не пишется** — восстанавливается из `type` |
| `3` | **знак сравнения** (`0` `<` · `1` `>` · `2` `=` · `4` `>=` …) или `0` при форме «дни» | `op: '…'` (нужна расшифровка всех кодов в UI) |
| `2` | **признак формы**: `1` = дни недели, `0` = время | **не пишется** — восстанавливается из `type` |
| `3` | **знак сравнения** (`0` `<` · `1` `>` · `2` `=` · `3` `<=` · `4` `>=`) | `op: '…'` |
| `4` | при форме «время» — **время** `(час<<8)\|мин`; при «дни» — `0` | `time: 'HH:MM'` |
| `5` | при форме «время» — `0`; при «дни» — **маска дней** (бит 0 = ПН) | `days: [mon, …]` |
#### 🔴 Круг 25: тостинг Alex — знак сравнения НАЙДЕН (поле 3)
#### 🔴 Круг 2526: тостинг Alex — знак сравнения НАЙДЕН и ПОДТВЕРЖДЁН (поле 3)
**Как добыто:** Alex в UI переключил `8842` на другие знаки, добавил 4 разных time-условия
и снял конфиг:
и снял конфиг. ⚠️ **Первый снимок был СТАРЫМ** — 729 строк, без новых объектов; Alex пришлось
дважды сказать «качай». **Урок: после «я добавил в UI» — перекачать, не искать в старом файле.**
```bash
cd /Users/admin/Automation/HA-ZONT-Modbus/zont_config
TS=$(date +%Y-%m-%d_%H-%M-%S)
curl -s --max-time 25 http://192.168.0.50/config.txt -o "config_local_${TS}.txt"
# → config_local_2026-09-17_20-55-22.txt, 729 строк
# → config_local_2026-09-17_20-55-22.txt, 729 строк ← СТАРЫЙ (был первым)
# → config_local_2026-09-17_21-07-03.txt, 760 строк ← ✅ содержит Values test
```
**Пять объектов 50 в новом снимке** (`#Z8548` + 4 новых тостинга + `#Z9256`):
**Четыре новых объекта 50 + два «дни»** (снимок `20-55-22`, затем то же в `21-07-03`):
| #Z | строка конфига | поле 2 | поле 3 | поле 4 | поле 5 | форма |
| #Z | строка конфига | поле 2 | поле 3 | поле 4 | описание Alex | знак |
|---|---|---|---|---|---|---|
| 9248 | `50,0,1,3351,0` | `0` | **`1`** | 3351 = **13:23** | 0 | время |
| 9250 | `50,0,0,2817,0` | `0` | **`0`** | 2817 = **11:01** | 0 | время |
| 9252 | `50,0,2,3842,0` | `0` | **`2`** | 3842 = **15:02** | 0 | время |
| 9254 | `50,0,4,3847,0` | `0` | **`4`** | 3847 = **15:07** | 0 | время |
| 8548 | `50,1,0,0,109` | **`1`** | `0` | 0 | 109 | дни |
| 9256 | `50,1,0,0,123` | **`1`** | `0` | 0 | 123 | дни |
| 9248 | `50,0,1,3351,0` | `0` | **`1`** | 13:23 | time_condition | `>` |
| 9250 | `50,0,0,2817,0` | `0` | **`0`** | 11:01 | time_condition | `<` |
| 9252 | `50,0,2,3842,0` | `0` | **`2`** | 15:02 | time_condition | `=` |
| 9254 | `50,0,4,3847,0` | `0` | **`4`** | 15:07 | time_condition | `>=` |
| 8548 | `50,1,0,0,109` | **`1`** | `0` | 0 | дни (пн,ср,чт,сб,вс) | — |
| 9256 | `50,1,0,0,123` | **`1`** | `0` | 0 | дни (пн,вт,чт,пт,сб,вс) | — |
**Вывод:** у объектов 50 поле 2 — **признак формы** (`0` = время, `1` = дни), поле 3 — **знак сравнения**
(у формы «время»; у формы «дни» всегда `0`), поле 4 — время, поле 5 — маска дней.
**Коды знаков совпадают с `LEAF_OPS` типа 47 ПО ПОЗИЦИИ — подтверждено.** Alex дал четыре
условия с четырьмя разными знаками, коды легли `1`/`0`/`2`/`4` — ровно позиции `>`/`<`/`=`/`>=`
из таблицы типа 47 (§5.2). Расхождений нет, это **не случайное совпадение** (круг 25 нёс
осторожность: «совпадение не подтверждено подписью»). **Теперь `op` пишется.**
🟢 **Коды знаков совпадают с `LEAF_OPS` типа 47 по позиции** (`1` = `>`, `0` = `=`, `2` = `<`,
`4` = `>=` или `<=`). ⚠️ **Но это по-прежнему гипотеза** — Alex тостинг сделал, а **подписи из UI
не назвал**. Пока подписи нет, `op` в YAML **не пишется** (питфолл: не выводить по совпадению без контроля).
**Что нужно, чтобы закрыть:** Alex называет подписи 4 условий (`9248`, `9250`, `9252`, `9254`) —
либо одно слово «прими по позиции `0=<, 1=>, 2===, 3=<=, 4=>=`».
```python
CMP_CODES = {0: '<', 1: '>', 2: '=', 3: '<=', 4: '>='} # модульный, в config-to-yml.py
_OP_TO_CODE = {'<': 0, '>': 1, '=': 2, '<=': 3, '>=': 4} # в yml-to-config.py
```
`LEAF_OPS` внутри `build_yaml` оставлен для типа 47 — **новый словарь `CMP_CODES` вынесен на
уровень модуля**, потому что `_set_var_target` вложена и до локального `LEAF_OPS` не достаёт.
**Как связывать шаги:** шаги-держатели `set var1` для новых условий:
`#Z9249=59,'set var1',9248,0,0` · `9251``9250` · `9253``9252` · `9255``9254` · `9257``9256`.
@@ -1326,9 +1339,6 @@ curl -s --max-time 25 http://192.168.0.50/config.txt -o "config_local_${TS}.txt"
> 🔴 **Резать поля от `type`:** `fields[0]=type`, `fields[1]=поле2`, … Проверять `grep -oE` со
> **скобочной группой до конца строки** `[...](?=\r|\n)`, иначе `[0-9,\-]*` съедает не то.
Все три объекта 50 живого конфига: `#Z8548=50,1,0,0,109` (дни), `#Z8842=50,0,1,3351,0` (время),
`#Z8844=50,1,0,0,123` (дни). `109` = пн,ср,чт,сб,вс; `123` = пн,вт,чт,пт,сб,вс.
> ⛔ **Запрещено (круг 22):** ключ `days_mask` как число, `mask`, `raw`, `_head` в теле объекта 50.
> Alex отверг подряд: «какой нахуй days», «какой mask», «откуда raw выполз», «че за head блядь».
> Поля 2/3 **не пишутся** — восстанавливаются из `type` (`time_condition`/`days_mask`) и `op`.
@@ -1360,21 +1370,120 @@ curl -s --max-time 25 http://192.168.0.50/config.txt -o "config_local_${TS}.txt"
---
### 10.6. 📊 Values test — 15 значений параметров объекта 49 (снимок `21-07-03`) — ЧАСТИЧНО
**Как добыто:** Alex в UI создал сценарий **«Values test»** и добавил 15 присваиваний
«Значение <параметр> → var1». Первый снимок (`20-55-22`, 729 строк) его **не содержал**
Alex дважды сказал «качай», перекачал → `21-07-03`, **760 строк**.
```bash
curl -s --max-time 30 http://192.168.0.50/config.txt -o /tmp/zfresh.txt
wc -l /tmp/zfresh.txt # 760
cat /tmp/zfresh.txt | iconv -f cp1251 -t utf-8 | grep -nE "=11,'" | grep -viE "Автомат|Передернуть"
# → #Z9324=11,'Values test',[9608,9610,…,9886],0,0,0,0,0
```
**Сценарий `#Z9324` — 15 шагов через один**, ровно как назвал Alex. **Порядок совпал один в один:**
| # | шаг | цель 49 | строка конфига | текст Alex (UI) |
|---|---|---|---|---|
| 1 | 9608 | 9607 | `49,9864,0,4` | Величина входа (V) · Статус 13/9: Рад. ванная 2эт |
| 2 | 9610 | 9609 | `49,8911,0,4` | Температура (°C) · Температура Гостиная |
| 3 | 9612 | 9611 | `49,4098,0,7` | Значение температура теплоносителя · Адаптер газового котла |
| 4 | 9614 | 9613 | `49,4099,0,8` | Значение температура ГВС · Адаптер электрокотла |
| 5 | 9616 | 9615 | `49,4099,0,9` | Значение температура обратки · Адаптер электрокотла |
| 6 | 9618 | 9617 | `49,4099,0,3` | Значение модуляция · Адаптер электрокотла |
| 7 | 9620 | 9619 | `49,4099,0,4` | Значение давление · Адаптер электрокотла |
| 8 | 9622 | 9621 | `49,4099,0,5` | Значение состояние · Адаптер электрокотла |
| 9 | 9624 | 9623 | `49,4099,0,6` | Значение код ошибки · Адаптер электрокотла |
| 10 | 9626 | 9625 | `49,8560,0,3` | Значение целевая температура (°C) · Контур газ котла |
| 11 | 9878 | 9627 | `49,8560,0,4` | Значение текущая температура (°C) · Контур газ котла |
| 12 | 9880 | 9879 | `49,8669,0,5` | Значение расчётная ТН (°C) · Контур ГВС |
| 13 | 9882 | 9881 | `49,8669,0,6` | Значение запрос тепла (°С) · Контур ГВС |
| 14 | 9884 | 9883 | `49,10152,0,2` | Значение ошибка · Тёплый пол |
| 15 | 9886 | 9885 | `59,'objstate 9838 0 0',0,0,0` | Значение элемента управления 13/9: Рад. ванная 2эт |
#### 🔴 Поле 3 = `0` — это НЕ «сравнение с числом», а «ЗНАЧЕНИЕ ПАРАМЕТРА»
Ранее (§10.4) поле 3 трактовалось как `0` = сравнение с числом, `1` = событие. **Values test
опровергает это:** все 15 объектов имеют поле 3 = `0`, и **ни один** из них не сравнение — все
читают **значение параметра**. Значит точная семантика: `0` = **значение** (число или параметр),
`1` = **событие**. Ключ `type: condition` + `value: <число>` верен, но семантика шире.
#### 🔴 Поле 4 — код ПАРАМЕТРА, ЗАВИСИТ ОТ ТИПА ОБЪЕКТА-ВЛАДЕЛЬЦА
| Объект-владелец | тип | код | параметр |
|---|---|---|---|
| Адаптер газового котла `4098` | 6 | `7` | температура теплоносителя |
| Адаптер электрокотла `4099` | 6 | `8` | температура ГВС |
| Адаптер электрокотла `4099` | 6 | `9` | температура обратки |
| Адаптер электрокотла `4099` | 6 | `3` | модуляция |
| Адаптер электрокотла `4099` | 6 | `4` | давление |
| Адаптер электрокотла `4099` | 6 | `5` | состояние |
| Адаптер электрокотла `4099` | 6 | `6` | код ошибки |
| Контур газ котла `8560` | 16 | `3` | целевая температура |
| Контур газ котла `8560` | 16 | `4` | текущая температура |
| Контур ГВС `8669` | 16 | `5` | расчётная ТН |
| Контур ГВС `8669` | 16 | `6` | запрос тепла |
| Тёплый пол `10152` | 16 | `2` | ошибка |
| Статус 13/9 `9864` | 0 | `4` | величина входа |
| Температура Гостиная `8911` | 1 | `4` | температура |
> 🔴 **Один код = разные параметры у разных типов.** `3` = «модуляция» у электрокотла и
> «целевая температура» у газового контура. `4` = «давление» у электрокотла, «текущая
> температура» у газового контура, «величина входа» у дискретного датчика.
> Единого словаря «код → имя» **не существует** — нужен словарь **по типу владельца**.
> ⚠️ **Старое толкование поля 4 как «кода события» (§10.4) верно ТОЛЬКО при поле 3 = `1`.**
> При поле 3 = `0` поле 4 — код параметра. Это **два разных словаря в одном поле**.
#### ❌ Что ещё НЕ закрыто в тип 49
- `#Z9229=49,4099,0,8` — по Values test `8` у электрокотла = «температура ГВС». Сходится.
- ⚠️ **Ключ `object` содержит id владельца, а `value` — код параметра. Пока в YAML пишется
`value: <число>` без имени параметра.** Имя параметра требует словаря по типу владельца —
Alex его ещё не подтверждал для **всех** типов. **Не выдумывать имена** (питфолл: «YAML = строка
конфига + вывод из тела»); словарь заводить только после явного подтверждения.
- Шаг 15 (`9885`) — не объект 49, а скрипт `objstate 9838 0 0` (`9838` = «13/9: Рад. ванная 2эт»,
тип 10). В UI показывает «Значение элемента управления» — то есть `objstate` тоже читает значение.
#### 🔴 Питфолл: «я добавил в UI» → ПЕРЕКАЧАТЬ, а не искать в старом снимке
Я сначала проверил `20-55-22` (729 строк), не нашёл там `Values test`, и **доложил Alex, что
сценария нет** — трижды. Alex: «ты дебил?», «да как нахуй нет то блядь?! ты блядь скачал конфиг новый?!».
Перекачал → `21-07-03`, 760 строк, **`#Z9324=11,'Values test'` на месте**.
**Правило:** после слов Alex «я добавил / я поменял в UI» — **первое действие `curl`**, а не
`grep` по последнему снимку. Прибор отдаёт `config.txt` из памяти; сохранение в UI тоже нужно
(`#S15=1`), но проверять надо **свежим запросом**, а не прошлым файлом.
> 📌 **Сравнение снимков:** `20-55-22` = 729 строк / 71 сценарий · `21-07-03` = 760 строк /
> **72 сценария** (+`Values test`). Разница 31 строка = 15 шагов + 15 целей 49 + 1 сценарий.
---
## 11. Файлы проекта
| Файл | Статус |
|---|---|
| `config-to-yml.py` | ✅ форма §5: `trigger:` подъём через `pop('if')`, шаг = `{id, [flag], action}` или `{id, [flag], if, then, [else]}` · ✅ тип 59: `set_var` (§10.1), `storeenv` (§10.2), `log` (§10.3), `type: condition` + `event`/`value` (§10.4), объект 50 двумя формами — `time_condition`/`days_mask` (§10.5) — **в рабочей копии, не закоммичено** |
| `yml-to-config.py` | ✅ `emit_step` читает `then`/`action`/`else`/`flag`; `f5` из `trigger:`/`interval_ms` + бит 8 · ✅ `set_var`/`storeenv` собраны в **`_script_body()`** — одна функция на обе точки входа (`emit_action` + инлайн `emit_step`) · ✅ `_body_type()` — карта `id → тип` по секциям + `raw`-склад (§10.4) · ✅ `days_mask`/`time_condition` (§10.5) — **не закоммичено** |
| `config-to-yml.py` | ✅ форма §5: `trigger:` подъём через `pop('if')`, шаг = `{id, [flag], action}` или `{id, [flag], if, then, [else]}` · ✅ тип 59: `set_var` (§10.1), `storeenv` (§10.2), `log` (§10.3), `type: condition` + `event`/`value` (§10.4) · ✅ объект 50 двумя формами — **поле 2** = форма, **поле 3** = знак → `op` из `CMP_CODES` (§10.5) — **в рабочей копии, не закоммичено** |
| `yml-to-config.py` | ✅ `emit_step` читает `then`/`action`/`else`/`flag`; `f5` из `trigger:`/`interval_ms` + бит 8 · ✅ `set_var`/`storeenv` собраны в **`_script_body()`** — одна функция на обе точки входа (`emit_action` + инлайн `emit_step`) · ✅ `_body_type()` — карта `id → тип` по секциям + `raw`-склад (§10.4) · ✅ `time_condition` пишет `[50, 0, _OP_TO_CODE[op], (hh<<8)\|mm, 0]`, `days_mask``[50, 1, 0, 0, mask]` (§10.5) — **не закоммичено** |
| `test_roundtrip.py` | ✅ без изменений (в коммите `199f2b1`) |
| `zont_config/config_local_2026-09-17_20-55-22.{txt,yml}` | 🆕 **АКТУАЛЬНЫЙ** снимок (729 строк, 36204 байта) — добавлены 4 time-условия Alex (`#Z9248/9250/9252/9254`), знак сравнения локализован в поле 3 (§10.5 круг 25). **Не закоммичен** |
| `zont_config/config_local_2026-09-17_20-40-48.{txt,yml}` | предыдущий (723 строки, 698 `#Z`) — сценарий **`9144` Conditions Test** (18 шагов, все комбинации условий 49). Круг ✅ `698 → 698`. **Не закоммичен** |
| `zont_config/config_local_2026-09-17_21-07-03.{txt,yml}` | 🆕 **АКТУАЛЬНЫЙ** снимок (760 строк, 72 сценария) — содержит **`#Z9324=11,'Values test'`** (§10.6, 15 значений параметров) и 4 time-условия Alex (§10.5). **Не закоммичен** |
| `zont_config/config_local_2026-09-17_20-55-22.{txt,yml}` | предыдущий (729 строк, 71 сценарий) — 4 time-условия Alex (`#Z9248/9250/9252/9254`), знак локализован в поле 3. **Values test в нём НЕТ** |
| `zont_config/config_local_2026-09-17_20-40-48.{txt,yml}` | сценарий **`9144` Conditions Test** (18 шагов) + ⚠️ **`_f2: 1` — мусорный ключ** (§10.5): `set_var` объекта `8844` (`type: days_mask`) донёс `_f2` из поля 2. Файл **устарел**, ключ вычищен из кода |
| `zont_config/config_local_2026-09-17_19-53-21.{txt,yml}` | ещё раньше (686 строк, 661 `#Z`) — форма §10.2/§10.3; **форма объекта 50 в нём УСТАРЕЛА** (§10.5) |
| `zont_config/config_local_2026-09-17_18-43-24.{txt,yml}` | ещё раньше, форма §5 |
| `zont_config/config_local_2026-09-17_{17-45-00,16-13-28,16-02-18,14-16-35}.*` | исторические снапшоты |
| `zont_config/config_0FA7C33CC89F_…_12-12-28.txt` | боевой конфиг (598 `#Z`, 25 `#S`) |
| `zont_config/archive/` | `-2`/`-3`/`-4` — закоммичены (`7ae0e32`) |
> ⛔ **`_f2` / `_f5` — МУСОРНЫЕ КЛЮЧИ, удалены из кода (круг 26).** В `config-to-yml.py` был блок
> «поля 2 и 5 сохраняем как есть — семантика не подтверждена», который донёс `_f2: 1` до YAML
> (`type: days_mask` + `_f2: 1`). Alex: «это че бля». Поля 2 и 5 **больше не пишутся** —
> они восстанавливаются из `type` и `days`/`op`. Питфолл: служебный ключ «на всякий случай»
> = выдуманная сущность в файле; лучше явная ошибка, чем мусор.
> 📌 **Бэкап кода — только git** (питфолл 8). Alex 2026-09-17: «какой нах бэкап. у нас гит».
> Копии файлов перед правкой **не делать**, рабочий откат = `git checkout <SHA>`.