--- 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-17d' --- # 🔁 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=11,'Имя',[<шаг>,<шаг>,…],0,0,,0,0 ← сценарий #Z=46,,,[<действие>,…],[] ← шаг #Z=49,,, ← условие #Z=… ← действие (тип 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`, поле 5 = `3354` | | `9` | по **триггеру** (условию) | `#Z8547`, `#Z8551` — тестовые | | `10` | по **интервалу** | `#Z8599` — поле 6 = `43200000` мс = **12 ч** | Распределение по живому конфигу: `{1: 64, 0: 1, 8: 2, 9: 2, 10: 1}`. **Поле 6** — параметр запуска (для `10` — интервал в мс; для `9`/`8` — иное). **Поле 4** — у `8597` = `61` (вероятно день недели / индекс расписания), у остальных `0`. ```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. Новые типы объектов | Тип | Что | Формат | Пример | |---|---|---|---| | **47** | **лист дерева условий** | `[47, <оператор>, <объект>, <значение>, <порог>]` | `[47, 3, 8552, 0, -5]` — «температура ниже −5» | | **48** | **группа условий (И/ИЛИ)** | `[48, <логика>, []]` | `[48, 0, [8493,8495]]`, `[48, 1, [...]]`, `[48, 2, [...]]` | | **50** | **запуск команды/сценария по id** | `[50, 1, 0, 0, ]` | `[50, 1, 0, 0, 123]`, `[50, 1, 0, 0, 109]`, `[50, 0, 1, 3351, 0]` | | **59** | **мини-скрипт ZONT** | `[59, '<код>', <арг1>, <арг2>, <флаг>]` | `[59, 'expr "%0 + %1"', 8459, 8460, 0]` | Наблюдённые значения поля 1 у **47**: `0, 1, 3, 4` (оператор — семантика **не подтверждена**). У **48**: `0, 1, 2` (логика группы — семантика **не подтверждена**). ### 8.3. Type 59 — встроенный скриптовый язык Найденные команды (в поле 1, в кавычках — аргументы): | Команда | Пример | Смысл (предположительно) | |---|---|---| | `objcmd ""` | `objcmd 8700 "1 %0"` | команда объекту с подстановкой `%0`, `%1` | | `objstate ` | `objstate 9838 0 0` | чтение состояния объекта | | `expr "<формула>"` | `expr "%0 + %1"`, `expr "%0 - %1"` | арифметика | | `set ` | `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, ответа ещё нет) | # | Вопрос | |---|---| | 1 | **Type 47, поле 1** — что за оператор? Наблюдены `0, 1, 3, 4`. Гипотеза: `0`=равно, `1`=не равно, `3`=меньше, `4`=больше — **не подтверждено** | | 2 | **Type 48, поле 1** — логика группы? Наблюдены `0, 1, 2`. Гипотеза: `0`=И, `1`=ИЛИ, `2`=НЕ — **не подтверждено** | | 3 | **Type 50** — что за объект? `[50, 1, 0, 0, ]` — запуск по расписанию/интервалу? | | 4 | **Type 11, поле 4** — у `8597` = `61`, у остальных `0`. Минуты? День недели? | | 5 | **Type 59** — есть ли документация на язык? Или это внутренний формат | > 🔴 **Не строить гипотезы вместо ответа.** Alex явно предложил: «если нужна будет подсказка что и > чем является по типу/значениям — спрашивай меня». Вопросы заданы, ждём ответа. > До ответа **правки конвертера не делать** — можно закодировать неверную семантику. ### 8.8. Скрипты разбора (в проекте) | Файл | Назначение | |---|---| | `dump_new_types.py` | дамп типов 11/45/46/47/48/49/50/59 целиком | | `probe_types.py` | распределение поля 5 у type 11; тело контейнера; блок 8450–8560 | | `trace_scenarios.py` | трассировка сценарных графов, поиск сценариев с новыми типами | ```bash cd /Users/admin/Automation/HA-ZONT-Modbus python3 probe_types.py # → семантика поля 5, тестовый блок python3 dump_new_types.py # → все сценарные объекты ``` > ⚠️ `trace_scenarios.py` падает на `TypeError: unhashable type: 'list'` (шаг `8457` — type 9, > у него `cond` = `[16, 'Контур ГВС', …]`, а скрипт ждёт скаляр). Не критично — `probe_types.py` > даёт те же данные. Чинить при следующем заходе. ### 8.9. Состояние: конвертер на новом конфиге ПАДАЕТ ```bash $ python3 config-to-yml.py zont_config/config_local_2026-09-17_14-16-35.txt > /tmp/new.yml Ошибка: Условие 8496: не type 49 EXIT=2 ``` **Причина:** `config-to-yml.py` в блоке сценариев требует `cond[0] == 49` и `step[4] == []`. Новые типы (47/48/50/59) и непустое поле 4 не поддерживаются. **Что нужно (после ответов Alex на §8.7):** 1. Парсер типов **47/48/50/59** + рекурсивная сборка дерева условий 2. Поле 5 типа 11 — хранить как **тип запуска** (0/1/8/9/10) + поле 6 (параметр), не как `enabled` 3. Поле 4 шага — принимать непустые списки 4. Ссылки в поле 2 — допускать не-46 типы (45, 11, 3, 5, 9) 5. Round-trip всех 5 конфигов (боевой + 3 архива + новый локальный) **Счётчики нового конфига (для сверки):** `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 |