545 lines
37 KiB
Markdown
545 lines
37 KiB
Markdown
---
|
||
aliases:
|
||
- ZONT сценарий 11109
|
||
- Передернуть Автомат Котельной
|
||
- ZONT тип 45 задержка
|
||
- ZONT сценарная логика
|
||
- ZONT поле 2 сценария
|
||
created: '2026-09-17'
|
||
namespace: family
|
||
related:
|
||
- '[[family/how-to/zont-config-compiler]]'
|
||
- '[[family/tech/zont-config-object-types]]'
|
||
- '[[family/how-to/home-automation]]'
|
||
tags:
|
||
- family
|
||
- tech
|
||
- zont
|
||
- modbus
|
||
- homeautomation
|
||
title: 🔁 ZONT — сценарная логика и конструктор (11/46/47/48/49/50/59/45)
|
||
type: tech
|
||
updated: '2026-09-17g'
|
||
---
|
||
|
||
# 🔁 ZONT — сценарная логика и конструктор логики
|
||
|
||
> 🔴 **ВНИМАНИЕ: раздел §1 ниже содержит УСТАРЕВШУЮ модель.** Поле 5 типа 11 — **НЕ «enabled»**,
|
||
> это **тип запуска сценария** (0/1 ручной, 8 расписание, 9 триггер, 10 интервал). Исправление и
|
||
> полная модель — **§8**. Раздел §1 оставлен как история разбора; актуальная семантика в §8.
|
||
|
||
Как устроены сценарии в конфиге ZONT и что именно разобрано в боевом конфиге
|
||
`config_0FA7C33CC89F_0FA7C33CC89F_2026-09-17_12-12-28.txt` (598 `#Z`, 25 `#S`, 65 сценариев).
|
||
Контекст конвертеров — [[family/how-to/zont-config-compiler]].
|
||
|
||
---
|
||
|
||
## 1. Структура сценария
|
||
|
||
Сценарий — это **4 связанных типа объектов**:
|
||
|
||
```
|
||
#Z<sid>=11,'Имя',[<шаг>,<шаг>,…],0,0,<enabled>,0,0 ← сценарий
|
||
#Z<step>=46,<prio>,<cond>,[<действие>,…],[] ← шаг
|
||
#Z<cond>=49,<relay_id>,<operator>,<value> ← условие
|
||
#Z<act>=… ← действие (тип 5, 9 или 45)
|
||
```
|
||
|
||
| Тип | Роль | Формат | Поля |
|
||
|---|---|---|---|
|
||
| **11** | Сценарий | `[11, name, [step_ids], 0, 0, enabled, 0, 0]` | **8** полей. `enabled` — поле v[5] (0/1) |
|
||
| **46** | Шаг | `[46, prio, cond_id, [action_ids], []]` | **5** полей. `prio` и последнее поле — всегда 0/`[]` |
|
||
| **49** | Условие | `[49, relay_id, operator, value]` | **4** поля. `operator`: `1`=equals, иначе not_equals |
|
||
| **45** | Задержка | `[45, <миллисекунды>]` | **2** поля |
|
||
|
||
**Каноническая форма** (99% конфига): 1 сценарий → 1 шаг → 1 условие → 1 действие.
|
||
|
||
---
|
||
|
||
## 2. Действия в шаге — три разных типа
|
||
|
||
Список действий шага (`46` поле 3) может содержать объекты **трёх** типов:
|
||
|
||
| Тип | Что это | Формат | Декодирование |
|
||
|---|---|---|---|
|
||
| **5** | Действие над выходом контроллера | `[5, name, output_ref, 1, delay, impulse_dur, [], sched_bmp, sched_time, impulse_period, value]` | 11 полей. `output_ref >> 4` = id выхода; `delay` — поле 4 (мс) |
|
||
| **9** | Команда реле | `[9, name, target_relay, '1'/'0']` | 4 поля. `value` — **строка** `'1'`/`'0'`, не число |
|
||
| **45** | Пауза между действиями | `[45, ms]` | 2 поля |
|
||
|
||
> ⚠️ **Тип 45 ≠ `delay_ms` типа 5.** `delay_ms` — задержка **внутри** действия перед его выполнением.
|
||
> Тип 45 — **отдельный объект-пауза** в последовательности. Их часто путают при чтении конфига.
|
||
|
||
---
|
||
|
||
## 3. Разобранный кейс: `#Z11109` «Передернуть Автомат Котельной»
|
||
|
||
**Единственный сценарий в конфиге с нестандартной формой** — 7 действий в шаге и задержка (`45`)
|
||
прямо в поле 2 сценария (`11828`) вместо второго шага. См. §5 «Уточнение семантики поля 2».
|
||
|
||
```
|
||
#Z11109=11,'Передернуть Автомат Котельной',[11827,11828],0,0,0,0,0
|
||
#Z11823=49,11190,1,0 ← условие
|
||
#Z11827=46,0,11823,[11191,11030,11824,11029,11825,11192,11826],[]
|
||
#Z11828=45,0 ← шаг 2
|
||
#Z11824=45,20000 #Z11825=45,60000 #Z11826=45,0
|
||
#Z11029=9,'Включить реле «20/1 Автомат Котельная»',11028,'1'
|
||
#Z11030=9,'Выключить реле «20/1 Автомат Котельная»',11028,'0'
|
||
#Z11191=9,'Включить реле «virt. Запретить передергивание»',11190,'1'
|
||
#Z11192=9,'Выключить реле «virt. Запретить передергивание»',11190,'0'
|
||
#Z11028=14,'20/1 Автомат Котельная',175728,0
|
||
#Z11190=14,'virt. Запретить передергивание',179904,0
|
||
```
|
||
|
||
### Что происходит по шагам
|
||
|
||
**Условие:** `relay 11190 ('virt. Запретить передергивание') == 0` — т.е. передёргивание **не** запрещено.
|
||
|
||
**Шаг 1 (`11827`) — 7 действий подряд:**
|
||
|
||
| # | Объект | Действие | Пауза после |
|
||
|---|---|---|---|
|
||
| 1 | `11191` (type 9) | включить `virt. Запретить передергивание` | — |
|
||
| 2 | `11030` (type 9) | **выключить** `20/1 Автомат Котельная` | — |
|
||
| 3 | `11824` (type 45) | пауза | **20 000 мс** |
|
||
| 4 | `11029` (type 9) | **включить** `20/1 Автомат Котельная` | — |
|
||
| 5 | `11825` (type 45) | пауза | **60 000 мс** |
|
||
| 6 | `11192` (type 9) | выключить `virt. Запретить передергивание` | — |
|
||
| 7 | `11826` (type 45) | пауза | 0 |
|
||
|
||
**Шаг 2 (`11828`)** — на деле это не шаг, а **задержка** `[45, 0]` прямо в поле 2 сценария:
|
||
пустая пауза-заглушка. Оставлена планировщиком ZONT, функциональной нагрузки не несёт.
|
||
|
||
### Смысл логики
|
||
|
||
Это **защита от повторного передёргивания**:
|
||
1. Виртуальное реле `virt. Запретить передергивание` (id `11190`) ставится в 1 **первым** действием.
|
||
2. Условие сценария (`11823`) требует `== 0` → пока реле в 1, сценарий повторно **не запустится**.
|
||
3. Снимается реле только **после** 20 с + 60 с пауз (действие 6), т.е. через 80 секунд после старта → минимальный интервал между передёргиваниями автомата = 80 с.
|
||
|
||
### 🔴 Проблема
|
||
|
||
`#Z11109` поле v[5] = **`0`** → **сценарий выключен в контроллере**. Защита и сама функция передёргивания не работают.
|
||
|
||
---
|
||
|
||
## 4. Полные счётчики по конфигу (для сверки после правок)
|
||
|
||
| Тип | Кол-во | Комментарий |
|
||
|---|---|---|
|
||
| 11 / 46 | 65 / 65 | все одношаговые, кроме `11109` |
|
||
| 49 | 65 | все ровно 4 поля, `op=1` (equals) у всех |
|
||
| 45 | 4 | `11824`, `11825`, `11826`, `11828` — только в `11109` |
|
||
| 9 | 42 | 21 пара ВКЛ/ВЫКЛ, `value` — строка `'1'`/`'0'` |
|
||
| 5 | 66 | действия над выходами |
|
||
| 14 | 40 | реле |
|
||
| 10 | 22 | GUI-переключатели |
|
||
| 0 / 36 | 13 / 13 | дискретные датчики + вложенные конфиги |
|
||
| 52 / 51 / 53 | 93 / 11 / 64 | Modbus-регистры / устройства / ещё один слой |
|
||
| 16 / 20 | 10 / 1 | контуры и режимы отопления |
|
||
| **Всего `#Z`** | **598** | |
|
||
|
||
> Проверка после любой правки: `grep -c '^#Z' <файл>` должен дать **598**.
|
||
|
||
---
|
||
|
||
## 5. Статус разбора конвертером
|
||
|
||
| Что | Статус |
|
||
|---|---|
|
||
| Многошаговый сценарий (`[11827,11828]`) | ✅ разбирается с 2026-09-17 |
|
||
| 7 действий в шаге `11827` | ✅ разбирается с 2026-09-17 |
|
||
| Тип 45 (4 объекта) | ✅ парсер + эмиттер, секция YAML `delays` |
|
||
| Round-trip всех 4 конфигов | ✅ чисто (`test_roundtrip.py`, коммит `199f2b1`) |
|
||
| Сценарий `enabled=0` | ℹ️ факт, не баг конвертера |
|
||
|
||
### 🔴 Уточнение семантики поля 2 (важно)
|
||
|
||
Поле 2 сценария — **не «список шагов»**, а **последовательность ссылок**, куда попадают объекты
|
||
**разных типов**. Полный список: шаг (`46`) **и** задержка (`45`).
|
||
|
||
В `#Z11109` поле 2 = `[11827, 11828]`, где:
|
||
- `11827` — настоящий шаг (`46`) с условием и 7 действиями;
|
||
- `11828` — **задержка (`45,0`)**, т.е. «шаг-ожидание» без условия.
|
||
|
||
Поэтому формулировка «2 шага» неточна: **шаг один**, вторым элементом идёт задержка-заглушка.
|
||
Отсюда же — `extra_links` в YAML (см. [[family/how-to/zont-config-compiler]] §2.1): не-46 ссылки
|
||
сохраняются дословно, чтобы round-trip остался байт-точным.
|
||
|
||
> ⚠️ **Не путать с типом 45 внутри шага.** В `11827` задержки `11824`/`11825`/`11826` — это
|
||
> **действия внутри** шага. `11828` — **ссылка в поле 2 сценария**, т.е. элемент верхнего уровня.
|
||
|
||
---
|
||
|
||
## 6. План переработки YAML-структуры (2026-09-17, ждёт апрува)
|
||
|
||
Сценарии в YAML переписываются в нативную ZONT-форму **вложенных инструкций «если … то …»** —
|
||
**без HA-синтаксиса** (`triggers`, `platform: state`). Alex: «нет структура должна быть как в zont.
|
||
**триггеров нет.**»
|
||
|
||
Предлагаемая форма для `11109`:
|
||
|
||
```yaml
|
||
scenarios:
|
||
- id: 11109
|
||
name: 'Передернуть Автомат Котельной'
|
||
enabled: false
|
||
blocks:
|
||
- id: 11827
|
||
if: {id: 11823, relay: 11190, operator: equals, value: 0}
|
||
then:
|
||
- {id: 11191, action: relay_on, relay: 11190}
|
||
- {id: 11030, action: relay_off, relay: 11028}
|
||
- {id: 11824, action: wait, ms: 20000}
|
||
- {id: 11029, action: relay_on, relay: 11028}
|
||
- {id: 11825, action: wait, ms: 60000}
|
||
- {id: 11192, action: relay_off, relay: 11190}
|
||
- {id: 11826, action: wait, ms: 0}
|
||
tail:
|
||
- {id: 11828, action: wait, ms: 0} # задержка из поля 2 сценария
|
||
```
|
||
|
||
Полный план — [[family/how-to/zont-config-compiler]] §5c.
|
||
|
||
> ⏸ **РАБОТА ПО §5c ПРИОСТАНОВЛЕНА (2026-09-17).** Alex дал «стоп» и запросил исследование облачного
|
||
> API ZONT — есть ли альтернатива конвертеру. **Ответ: API конфиг не поддерживает** (сценарии/реле/
|
||
> 11/14/46/49 в API отсутствуют). Переписывать с 0 не на что, конвертер остаётся единственным путём.
|
||
> Разбор — [[family/tech/zont-api]]. Форма `blocks/if/then` выше — **предложение, в коде не реализовано**.
|
||
|
||
---
|
||
|
||
## 7. Связанные заметки
|
||
|
||
- [[family/how-to/zont-config-compiler]] — конвертеры `.txt ⇄ .yml`; 2 шага и тип 45 **поддержаны с 2026-09-17** (§5b)
|
||
- [[family/tech/zont-api]] — облачный API ZONT: конфиг не поддерживает, альтернативы конвертеру нет
|
||
- [[family/tech/zont-config-object-types]] — полная таблица типов объектов и `#S`-настройки
|
||
- [[family/how-to/home-automation]] — контур автоматизации, ZONT, Modbus slave ID и регистры
|
||
|
||
---
|
||
|
||
## 8. 🔴 КОНСТРУКТОР ЛОГИКИ ZONT — типы 47 / 48 / 50 / 59 (разобрано 2026-09-17)
|
||
|
||
> **Источник:** локальный конфиг с контроллера, снят с `http://192.168.0.50/config.txt`
|
||
> (без авторизации, см. [[family/tech/zont-api]] §6bis):
|
||
> `zont_config/config_local_2026-09-17_14-16-35.txt` — **34 907 байт, 660 `#Z`, 25 `#S`**.
|
||
>
|
||
> Alex создал тестовые сценарии в UI, «постарался использовать все доступные триггеры и варианты
|
||
> логики». Это позволило вскрыть **полноценный визуальный конструктор логики** со скриптовым движком.
|
||
|
||
### 8.1. 🔴 ИСПРАВЛЕНИЕ: поле 5 типа 11 — ТИП ЗАПУСКА, а не `enabled`
|
||
|
||
Прежняя модель (§1, строка `[11, name, [step_ids], 0, 0, enabled, 0, 0]`) **неверна**.
|
||
Поле 5 (индекс 5) — **тип запуска сценария**:
|
||
|
||
| Значение | Что это | Доказательство из тестовых сценариев |
|
||
|---|---|---|
|
||
| `0` / `1` | ручной запуск (кнопка), вкл/выкл | 64 старых сценария = `1`; `11109` = `0` |
|
||
| `8` | по **расписанию** | `#Z8597` — поле 4 = `61`, поле 6 = `3354` ✅ расшифровано |
|
||
| `9` | по **триггеру** (условию) | `#Z8547`, `#Z8551` — оба триггерные (подтверждено Alex) |
|
||
| `10` | по **интервалу** | `#Z8599` — поле 6 = `43200000` мс = **12 ч** |
|
||
|
||
**Поле 6** — параметр запуска (для `10` — интервал в мс; для `8` — время запуска).
|
||
|
||
### 🔴 Поле 4 + поле 6 при запуске по расписанию — РАСШИФРОВАНО (2026-09-17)
|
||
|
||
`#Z8597` «Тестовый сценарий по расписанию», Alex: **ПН, СР, ЧТ, ПТ, СБ, в 13:26**.
|
||
|
||
| Поле | Значение | Расшифровка |
|
||
|---|---|---|
|
||
| поле 4 | `61` = `0b00111101` | **битовая маска дней недели**: бит 0=ПН, 1=ВТ, …, 6=ВС. Установлены биты **0,2,3,4,5** = ПН, СР, ЧТ, ПТ, СБ ✅ |
|
||
| поле 6 | `3354` = `0x0D1A` | **время**: старший байт `0x0D` = **13** (час), младший `0x1A` = **26** (минута) → **13:26** ✅ |
|
||
|
||
```text
|
||
#Z8597 = 11, 'Тестовый сценарий по расписанию', [8598], 61, 3354, 8, 0, 0
|
||
^годнедели ^время ^тип
|
||
```
|
||
|
||
> Формула времени: `поле6 = (час << 8) | минута`. Начало недели — **ПН** (бит 0), не ВС.
|
||
> Проверка: `61 >> 0 & 1 = 1` (ПН есть), `61 >> 1 & 1 = 0` (ВТ нет — у Alex ВТ не отмечен) ✅.
|
||
|
||
```text
|
||
#Z8456=11,'Простой тестовый сценарий',[<26 ссылок>],0,0, 8,0,0
|
||
#Z8547=11,'тестовый сценарий по времени', [8550],0,0, 9,0,0
|
||
#Z8551=11,'Тестовый сценарий по триггеру',[8555],0,0, 9,0,0
|
||
#Z8597=11,'Тестовый сценарий по расписанию',[8598],61,3354, 8,0,0
|
||
#Z8599=11,'Тестовый сценарий по интервалу',[8600,8601],0,0, 10,43200000,0
|
||
```
|
||
|
||
> ⚠️ **Следствие для конвертера:** `enabled` в YAML сейчас выводится из поля 5 как булево —
|
||
> это **потеря информации** для значений 8/9/10. Конвертер на новом конфиге **падает**
|
||
> (`Ошибка: Условие 8496: не type 49`) — новые типы не поддержаны.
|
||
|
||
### 8.2. Новые типы объектов (УТОЧНЕНО ответами Alex 2026-09-17)
|
||
|
||
| Тип | Что | Формат | Пример |
|
||
|---|---|---|---|
|
||
| **47** | **лист дерева условий** (сравнение) | `[47, <оператор>, <левый>, <правый_или_0>, <константа>]` | `[47, 3, 8552, 0, -5]` — «погода <= -5» |
|
||
| **48** | **группа условий** | `[48, <логика>, [<id условий>]]` | `[48, 0, [8493,8495]]` |
|
||
| **50** | **объект-триггер запуска** (внешний/системный) | `[50, 1, 0, 0, <ext_id>]` | `[50, 1, 0, 0, 109]` |
|
||
| **59** | **мини-скрипт ZONT** | `[59, '<код>', <арг1>, <арг2>, <флаг>]` | `[59, 'expr "%0 + %1"', 8459, 8460, 0]` |
|
||
|
||
**Type 47 — модель полей.** `[47, op, LEFT, RIGHT, K]`:
|
||
- `LEFT` — объект/скрипт-выражение слева;
|
||
- если `RIGHT` != `0` — **второй объект**, сравнение двух объектов (`8495`: `var1 >= objstate 9838`, RIGHT = `8494`);
|
||
- если `RIGHT` = `0` — сравнение **с константой** из поля 4 (`8493`: `OBJECT < 3`).
|
||
|
||
**Type 47, операторы — ПОДТВЕРЖДЕНО Alex** (порядок пунктов UI: `<`, `>`, `=`, `<=`, `>=`):
|
||
|
||
| Поле 1 | Оператор | Доказательство (формулировка Alex) |
|
||
|---|---|---|
|
||
| `0` | `<` | `8493` «значение элемента управления < 3»; `8497` «var1 < 2» |
|
||
| `1` | `>` | `8499` «Pickle(2) > 3» |
|
||
| `2` | `=` | в тестовом блоке нет; следует из порядка UI |
|
||
| `3` | `<=` | `8553` «погода из интернета <= -5» |
|
||
| `4` | `>=` | `8495` «var1 >= значение 13/9 рад. ванная 2эт» |
|
||
|
||
> 🔴 **Ловушка:** порядок пунктов UI (`<`,`>`,`=`,`<=`,`>=`) **не** логически упорядочен.
|
||
> `=` стоит **третьим** (код `2`), между `>` и `<=`. Не «додумывать» порядок как
|
||
> «сначала все меньше, потом все больше» — это даёт неверные коды.
|
||
|
||
**Type 48, логика группы — ПОДТВЕРЖДЕНО Alex** (по описанию теста 8456):
|
||
|
||
| Поле 1 | Логика | Доказательство |
|
||
|---|---|---|
|
||
| `0` | **И** (все условия) | `8496 = [48,0,[8493,8495]]` — «условие И: ...» |
|
||
| `1` | **ИЛИ** (любое) | `8501 = [48,1,[8497,8500]]` — «ИЛИ: ...» |
|
||
| `2` | **НЕ** (отрицание) | `8500 = [48,2,[8499]]` — «НЕ (...)» |
|
||
|
||
**Type 50 — триггер запуска.** `[50, 1, 0, 0, <ext_id>]`, где `<ext_id>` — **внешний/системный
|
||
объект**, которого нет среди `#Z` (напр. `109` — «погода из интернета»). Сценарий «по времени»
|
||
(`8547`, шаг `8550`) ссылается на `109` — в UI это тоже триггер-условие запуска
|
||
(поле 5 типа 11 = `9`).
|
||
|
||
### 8.3. Type 59 — встроенный скриптовый язык
|
||
|
||
Найденные команды (в поле 1, в кавычках — аргументы):
|
||
|
||
| Команда | Пример | Смысл (предположительно) |
|
||
|---|---|---|
|
||
| `objcmd <id> "<args>"` | `objcmd 8700 "1 %0"` | команда объекту с подстановкой `%0`, `%1` |
|
||
| `objstate <id> <n> <n>` | `objstate 9838 0 0` | чтение состояния объекта |
|
||
| `expr "<формула>"` | `expr "%0 + %1"`, `expr "%0 - %1"` | арифметика |
|
||
| `set <var>` | `set var1` | присваивание переменной |
|
||
| `puts "текст"` | `puts "Отладка"`, `puts "test"` | вывод (отладка) |
|
||
| `storeev I "z"` / `storeev A "asdf"` | — | запись события (I = integer?, A = alert/array?) |
|
||
| просто число/строка | `3`, `2 ;#p` | константа; суффиксы `;#a`, `;#h`, `;#p` |
|
||
|
||
Есть **переменные** (`var1`), **подстановки** `%0`/`%1` (аргументы из полей 2/3), **суффиксы** `;#a`/`;#h`/`;#p`.
|
||
Это объясняет всё: у ZONT есть **полноценный визуальный конструктор логики** поверх скриптового движка.
|
||
|
||
### 8.4. Дерево условий — рабочий пример (`#Z8505`, `#Z8506`)
|
||
|
||
Вложенная логика читается так:
|
||
|
||
```
|
||
#Z8506 = [46, 0, 8496, [8505], []] ← шаг: условие 8496, действие 8505
|
||
#Z8496 = [48, 0, [8493, 8495]] ← группа: 8493 И 8495
|
||
#Z8493 = [47, 0, 8492, 0, 3] ← лист: объект 8492, значение 0, порог 3
|
||
#Z8492 = [59, 'objstate 9838 0 0', 0, 0, 0] ← объект = скрипт!
|
||
#Z8495 = [47, 4, 8472, 8494, 0]
|
||
#Z8494 = [59, 'objstate 9838 0 0', 0, 0, 0]
|
||
|
||
#Z8505 = [46, 1, 8501, [8502, 8503], [8504]] ← шаг: приоритет 1, доп. список [8504]
|
||
#Z8501 = [48, 1, [8497, 8500]] ← группа: 8497 ИЛИ 8500
|
||
#Z8497 = [47, 0, 8472, 0, 2]
|
||
#Z8500 = [48, 2, [8499]] ← группа: логика 2 над [8499]
|
||
#Z8499 = [47, 1, 8498, 0, 3]
|
||
#Z8498 = [59, '2 ;#p', 0, 0, 0]
|
||
```
|
||
|
||
> 📌 В `#Z8505` поле 1 (приоритет) = **1**, и поле 4 (последнее) = **непустой список `[8504]`** —
|
||
> в отличие от «канонических» шагов, где там всегда `[]`. Конвертер это отвергает
|
||
> (`require(step[4] == [])`). Семантика поля 4 — **не подтверждена**.
|
||
|
||
### 8.5. Контейнер-сценарий `#Z8456` (поле 2 = смесь типов)
|
||
|
||
`#Z8456 = [11, 'Простой тестовый сценарий', [<26 ссылок>], 0, 0, 8, 0, 0]` — поле 2 содержит
|
||
объекты **разных типов**, а не только шаги:
|
||
|
||
| Тип | Кол-во | Примеры |
|
||
|---|---|---|
|
||
| 9 (команда реле) | 2 | `8457` «Установить целевую температуру 23.8…», `8470` «Активировать режим…» |
|
||
| 59 (скрипт) | 17 | `8458`…`8504` |
|
||
| 11 (другой сценарий!) | 1 | `11109` «Передернуть Автомат Котельной» |
|
||
| 3 (SMS) | 1 | `8195` «CMC оповещение» |
|
||
| 5 (действие) | 1 | `9500` «Вкл. Рад. ванная 2эт» |
|
||
| 45 (задержка) | 1 | `8489` = 432 000 000 мс = **5 суток** |
|
||
| 46 (шаг) | 1 | `8506` |
|
||
|
||
> 🔴 **Сценарий может ссылаться на другой сценарий** (`11109` внутри `8456`). Это **не дерево**,
|
||
> а плоский список ссылок.
|
||
|
||
### 8.6. Сводка тестового блока (id 8450–8601)
|
||
|
||
Типы в блоке: `{9: 2, 11: 3, 14: 1, 16: 1, 27: 1, 45: 1, 46: 4, 47: 5, 48: 3, 49: 11, 50: 3, 59: 26}`.
|
||
|
||
| Сценарий | Имя | Поле 5 (запуск) |
|
||
|---|---|---|
|
||
| `8456` | Простой тестовый сценарий | `8` (расписание) |
|
||
| `8547` | тестовый сценарий по времени | `9` (триггер) |
|
||
| `8551` | Тестовый сценарий по триггеру | `9` (триггер) |
|
||
| `8597` | Тестовый сценарий по расписанию | `8` (расписание) |
|
||
| `8599` | Тестовый сценарий по интервалу | `10` (интервал, 12 ч) |
|
||
|
||
### 8.7. ✅ ВОПРОСЫ ЗАКРЫТЫ — ответы Alex (2026-09-17)
|
||
|
||
Alex создал тестовые сценарии и описал, что видно в UI. Все пять вопросов закрыты:
|
||
|
||
| # | Вопрос | Ответ | Проверка по конфигу |
|
||
|---|---|---|---|
|
||
| 1 | **Type 47, оператор** | Операнды в UI: **`<`, `>`, `=`, `<=`, `>=`** → коды `0..4` | ✅ все 5 листов сошлись |
|
||
| 2 | **Type 48, логика** | **`0`=И, `1`=ИЛИ, `2`=НЕ** | ✅ по описанию теста 8456 |
|
||
| 3 | **Type 50** | **Объект-триггер запуска** (внешний, напр. `109` «погода из интернета») | ✅ |
|
||
| 4 | **Type 11, поле 4** | **Маска дней недели** (`61` = ПН,СР,ЧТ,ПТ,СБ), поле 6 = `(час<<8)|мин` | ✅ 13:26 |
|
||
| 5 | **`;#a`/`;#h`/`;#p`** | (не переспрашивал; остаются гипотезой) | ⏳ |
|
||
|
||
**Разбор тестовых сценариев Alex (ответы дословно → факты конфига):**
|
||
|
||
| Сценарий | Что сказал Alex | Что в конфиге |
|
||
|---|---|---|
|
||
| `8597` расписание | «пн, ср, чт, пт, сб, в 13:26» | поле 4=`61`, поле 6=`3354`=13:26 ✅ |
|
||
| `8547` «по времени» | «по триггеру, триггер на день недели: пн,ср,чт,сб,вс» | поле 5=`9`, шаг `8550` → `[50,1,0,0,109]` |
|
||
| `8551` «по триггеру» | «триггер сравнение: погода из интернета <= -5, действие вывод в терминал» | `8553=[47,3,8552,0,-5]`, `8552=[49,8254,0,4]`, `8254`=«Погода из интернета», then `8554`=`puts "Температура упала ниже -5"` ✅ |
|
||
| `8547` шаг `8550` | «только одно действие: вывод "test" в терминал» | `8549=[59,'puts "test"',0,0,0]` ✅ |
|
||
| `8456` (26 ссылок) | «set var много, у каждого разный тип значения» | 7× `set var1` + 7 разных типов объектов ✅ |
|
||
|
||
**Type 46 — модель полей ПОДТВЕРЖДЕНА (5 полей = если/то/иначе):**
|
||
|
||
```text
|
||
#Z<step> = [46, <вид>, <id_условия_или_0>, [<ТОГДА>...], [<ИНАЧЕ>...]]
|
||
```
|
||
|
||
Alex по тесту `8456` описал: «если (условие И: …) То: … Иначе: Сохранить событие» —
|
||
и это точно соответствует `8506 = [46, 0, 8496, [8505], []]` + `8505 = [46, 1, 8501, [8502,8503], [8504]]`,
|
||
где `[8504]` = ветка **ИНАЧЕ**. Поле 4 = список **ИНАЧЕ** (ранее помечено «семантика не подтверждена» — теперь подтверждена).
|
||
|
||
> ⚠️ Поле 1 шага: `0` у внешнего, `1` у вложенного (`8505`) — вероятно «уровень вложенности»
|
||
> или признак вложенного блока. Точная семантика не подтверждена, но round-trip требует
|
||
> сохранять как есть.
|
||
|
||
### 8.8. Скрипты разбора (в проекте)
|
||
|
||
| Файл | Назначение |
|
||
|---|---|
|
||
| `read_scenarios.py` | 🆕 **читаемый дамп любого сценария** — дерево если/то/иначе с именами объектов. `python3 read_scenarios.py <config> <id>` |
|
||
| `dump_new_types.py` | дамп типов 11/45/46/47/48/49/50/59 целиком |
|
||
| `probe_types.py` | распределение поля 5 у type 11; тело контейнера; блок 8450–8560 |
|
||
| `trace_scenarios.py` | трассировка сценарных графов, поиск сценариев с новыми типами |
|
||
| `probe_sched.py` | 🆕 проверка расписания: маска дней + время |
|
||
| `verify_answers.py` | 🆕 кросс-проверка ответов Alex против фактов конфига |
|
||
| `check_ops.py` | 🆕 проверка таблицы операторов type 47 |
|
||
| `audit_8456.py`, `audit2.py` | 🆕 аудит прозрачности элементов сценария 8456 |
|
||
|
||
```bash
|
||
cd /Users/admin/Automation/HA-ZONT-Modbus
|
||
python3 read_scenarios.py zont_config/config_local_2026-09-17_14-16-35.txt 8456 # дерево сценария
|
||
python3 read_scenarios.py zont_config/config_local_2026-09-17_14-16-35.txt # все сценарии
|
||
python3 probe_types.py # семантика поля 5, тестовый блок
|
||
python3 dump_new_types.py # все сценарные объекты
|
||
```
|
||
|
||
> ✅ **Токенайзер починен.** Старый `read_scenarios.py` рвал строки со скриптами на запятых
|
||
> внутри кавычек (`objcmd 9102 "6,%0";#a` разваливалось). Новый `split_top()` учитывает
|
||
> кавычки, экранирование `\` и вложенные `[]` — теперь скрипты печатаются как есть.
|
||
|
||
### 8.10. ⏳ НЕПРОЗРАЧНЫЕ ЭЛЕМЕНТЫ СЦЕНАРИЯ 8456 (✅ разблокированы raw-стратегией)
|
||
|
||
Alex спросил: «а по всем действиям в "Простой тестовый сценарий" — тебе всё кристально прозрачно?»
|
||
Ответ был — **нет**. Ниже ровно то, что не выводится из конфига.
|
||
|
||
> ✅ **Разблокировано 2026-09-17.** Alex решил: «допиливаешь парсер с известными типами, а неизвестные
|
||
> — пусть в yml будут raw values, и тогда я доразберу». После этого конвертер дописан, round-trip
|
||
> чистый, **эти вопросы больше не блокируют работу** (§8.9). Семантику доразбирает Alex в YAML
|
||
> через `raw`-поля. **Гипотезы по-прежнему не строить** — открытые пункты остаются открытыми.
|
||
|
||
| # | Элемент | Что неясно |
|
||
|---|---|---|
|
||
| 1 | `#Z8456` поле 5 = `8` (расписание), но поле 4 = `0`, поле 6 = `0` | **Как запускается 8456?** Расписание не задано. Что значит `8` без маски/времени? Ручной запуск? |
|
||
| 2 | `#Z8458 = [59, 'objcmd 8700 "1 %0"', 10.5, 0, 1]` | Единственный скрипт с **нецелым** `%0` (`10.5`) и **флагом `1`** в поле 4 (у всех остальных `0`). Что за инструкция в UI? |
|
||
| 3 | `#Z8463 = [59, 'objcmd 9102 "6,%0";#a', 8462, 0, 0]`, где `8462` = скрипт `expr "%0 + %1"` | Аргумент = **ссылка на другой скрипт**. Что за суффикс `;#a`? |
|
||
| 4 | `#Z8465 = [59, 'objcmd 11907 ",,,%0";#h', 8464, 0, 0]`, где `8464` = `[49, 4099, 0, 8]` (условие) | Аргумент = **условие**. Что за `;#h`? |
|
||
| 5 | `#Z8467 = [59, '3', 0, 0, 0]` | Скрипт = просто цифра `3`. Константа? Зачем отдельным действием? |
|
||
| 6 | `#Z8486`/`#Z8488` = `set var1`, arg1 = `8485`/`8487` — **type 50** | Type 50 стоит как **источник значения** для var1, а не как триггер. Это то же самое, что триггер? |
|
||
| 7 | `#Z9500 = [5,'Вкл. Рад. ванная 2эт',145728,1,0,0,[],0,0,0,512]` | Объект выхода `145728` **отсутствует** в конфиге. Поле 10 = `512` — что это? |
|
||
| 8 | `#Z8457 = [9,'Установить целевую температуру 23.8...',8669,'2968']`, `#Z8470 = [9,...,8669,'8574']` | Значение `'2968'` — **отсутствующий объект**. Это «установить температуру»/«активировать режим»? |
|
||
|
||
**Что уже прозрачно (не переспрашивать):**
|
||
|
||
- 7× `set var1` и их источники: `8450`(Температура подачи, тип 27), `9864`(Статус 13/9, тип 0),
|
||
`8560`(Контур газ котла, тип 16), `9263`(Реле 1 конвектор, тип 14), `8254`(Погода из интернета, тип 1),
|
||
`4098`(Адаптер газ. котла, тип 6), `8382`(Температура Zigbee, тип 1) ✅
|
||
- `#8466` `puts "Отладка"`, `#8468` `storeev I "z"`, `#8469` `storeev A "asdf"` — вывод/события ✅
|
||
- `#8489` пауза 5 суток (`432000000` мс), дерево `#8506`→`#8505` (И/ИЛИ/НЕ + ИНАЧЕ) ✅
|
||
- `#8195` SMS `CMC оповещение` → `8192` (2 номера) ✅
|
||
- `#11109` как **вложенный сценарий** внутри 8456 ✅
|
||
- `#8507` — ссылка на **отсутствующий объект** (хвост поля 2) ✅
|
||
|
||
**Почему неясные элементы не блокируют парсер:** стратегия — сохранять их **дословно**
|
||
(type 59 как сырые строки с `arg1..arg3`, отсутствующие id — как есть). Round-trip останется
|
||
байт-точным независимо от того, понята семантика или нет.
|
||
|
||
### 8.9. ✅ Состояние: конвертер новый конфиг РАЗБИРАЕТ (2026-09-17)
|
||
|
||
> ✅ **ИСПРАВЛЕНО.** Все пять пунктов ниже реализованы в конвертерах (§5c в
|
||
> [[family/how-to/zont-config-compiler]]). Round-trip чистый на 6/6 конфигах.
|
||
> Ранее падал с `Ошибка: Условие 8496: не type 49` (exit 2) — **больше не падает**.
|
||
|
||
**Что было нужно — ВСЁ СДЕЛАНО:**
|
||
|
||
| # | Было нужно | Статус |
|
||
|---|---|---|
|
||
| 1 | Парсер типов **47/48/50/59** + рекурсивное дерево условий | ✅ `dump_condition`/`dump_leaf`/`dump_action` |
|
||
| 2 | Поле 5 типа 11 — **тип запуска** (0/1/8/9/10), не `enabled` | ✅ `trigger: {type, days, time, interval_ms}` |
|
||
| 3 | Поле 4 шага — принимать непустые списки | ✅ `else` (в `steps[]`) |
|
||
| 4 | Ссылки в поле 2 — допускать не-46 типы (45, 11, 3, 5, 9) | ✅ **плоский `steps[]`** — каждый элемент по порядку, тело инлайн |
|
||
| 5 | Round-trip всех 5 конфигов | ✅ **6/6** (боевой + 3 архива + новый локальный + live) |
|
||
|
||
**Счётчики нового конфига (для сверки):** `660 #Z`, `25 #S`, байт — 34 907.
|
||
|
||
| Тип | Кол-во | | Тип | Кол-во |
|
||
|---|---|---|---|---|
|
||
| 11 | 70 | | 49 | 76 |
|
||
| 46 | 69 | | 47 | 5 |
|
||
| 45 | 5 | | 48 | 3 |
|
||
| 9 | 44 | | 50 | 3 |
|
||
| 59 | 28 | | 0/36 | 13/13 |
|
||
|
||
### 8.11. ✅ ФИНАЛЬНАЯ YAML-форма сценария — плоский `steps[]` (2026-09-17)
|
||
|
||
> 🔴 **Урок, который стоит запомнить.** Первая реализация делила поле 2 на `blocks` (шаги 46) +
|
||
> `extra_links` (голые id). Round-trip был чистый (байты сходились), но **сценарий читался как
|
||
> список цифр**: `blocks` выводился первым, а всё «до него» — 25 элементов «установка температуры,
|
||
> `puts`, `set var1`, пауза 5 суток» — лежало в `extra_links` без тел. Alex: «почему там блядь if
|
||
> идет первым блоком?! где все что до него происходит?!»
|
||
> **Вывод: round-trip ≠ читаемость. Артефакт надо читать глазами до отдачи.**
|
||
|
||
**Правильная форма** — одно поле `steps`, плоский список = поле 2 сценария **как есть**:
|
||
|
||
```yaml
|
||
scenarios:
|
||
- id: 8456
|
||
name: Простой тестовый сценарий
|
||
trigger: {type: schedule}
|
||
_raw_trigger_kind: 8
|
||
steps:
|
||
- {id: 8457, raw: [9, 'Установить целевую температуру 23.8 …', 8669, '2968']}
|
||
- {id: 8458, script: 'objcmd 8700 "1 %0"', args: [10.5, 0, 1]}
|
||
- {id: 8463, script: 'objcmd 9102 "6,%0";#a', args: [8462, 0, 0]}
|
||
- {id: 11109, run_scenario: Передернуть Автомат Котельной}
|
||
- {id: 8466, script: 'puts "Отладка"', args: [0, 0, 0]}
|
||
- {id: 8473, script: 'set var1', args: [8471, 0, 0]} # + ещё 6 × set var1
|
||
- {id: 8489, wait: 432000000} # пауза 5 суток
|
||
- id: 8506 # шаг с условием — тоже элемент
|
||
if: {id: 8496, group: and, children: [
|
||
{id: 8493, op: '<', left: 8492, value: 3},
|
||
{id: 8495, op: '>=', left: 8472, right: 8494}]}
|
||
then: [{id: 8505, kind: 1, if: {…}, then: [{id: 8502, script: 'puts "then-text"'}], else: [{id: 8504, script: 'storeev A "alert"'}]}]
|
||
- {id: 8507, unresolved: true}
|
||
```
|
||
|
||
**Диспетчер `dump_step()`** — по типу элемента:
|
||
`46`→`if/then/else`, `59`→`script`+`args`, `47/48/49`→дерево условий, `45`→`wait`, `50`→`trigger_object` (raw),
|
||
`11`→`run_scenario`, `5/9/3`→`raw`-тело. **Мелкая деталь:** тела объектов чужих секций (`sms_notifications`,
|
||
`actions`) рендерятся как YAML-якоря `*id00N` (`8195`, `9500` в `8456`) — читается хуже, но байт-точно.
|
||
|
||
**Артефакт:** `zont_config/config_local_2026-09-17_14-16-35.yml` (6110 строк) — в проекте, не в `/tmp`.
|