diff --git a/personal/projects/zont-config-compiler.md b/personal/projects/zont-config-compiler.md index 2a84fe35..8d28e322 100644 --- a/personal/projects/zont-config-compiler.md +++ b/personal/projects/zont-config-compiler.md @@ -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 круги 15–16). @@ -580,6 +583,10 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z=<тип>' | 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____.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, круги 12–21), `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 круги 19–21) | +| `field` / `mode` / `cmp` / `_f2` / `_f5` | ⛔ **удалены** — 0 вхождений в коде и файлах (§10.1 круги 19–21, круг 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) +#### 🔴 Круг 25–26: тостинг 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 `.