53 KiB
aliases, created, namespace, related, tags, title, type, updated
| aliases | created | namespace | related | tags | title | type | updated | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
2026-09-17 | family |
|
|
🔁 ZONT — сценарная логика и конструктор (11/46/47/48/49/50/59/45/9/5) | tech | 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, функциональной нагрузки не несёт.
Смысл логики
Это защита от повторного передёргивания:
- Виртуальное реле
virt. Запретить передергивание(id11190) ставится в 1 первым действием. - Условие сценария (
11823) требует== 0→ пока реле в 1, сценарий повторно не запустится. - Снимается реле только после 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:
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. 🔴🔴 ОТМЕНЕНО 2026-09-17: поле 5 типа 11 — НЕ тип запуска. Таблица ниже ОШИБОЧНА
🔴 ЭТА МОДЕЛЬ ОТВЕРГНУТА ALEX (2026-09-17, конец сессии). Читать §8.16.
Alex: сценарий
9628(«Автомат.: Рад. ванная 2эт ВКЛ») — по триггеру, а поле 5 у него1, не9. Сценарий8456(«Простой тестовый сценарий») — такой же ручной, как остальные, но disabled, а поле 5 у него8, не1. Оба противоречат таблице ниже.Что осталось верным: поле 5 — не просто
enabled(§1 устарела тоже). Что неверно: сопоставление8=расписание,9=триггер,0/1=ручной. Актуальный разбор — §8.16. Таблица сохранена как история ошибки.
| Значение | Что это (❌ ОШИБОЧНО) | Доказательство из тестовых сценариев |
|---|---|---|
0 / 1 |
❌ ручной запуск (кнопка), вкл/выкл | 64 старых сценария = 1; 11109 = 0 — но 9628 с 1 триггерный |
8 |
❌ по расписанию | #Z8597 — поле 4 = 61, поле 6 = 3354 ✅ расшифровано — но 8456 с 8 ручной+disabled |
9 |
❌ по триггеру (условию) | #Z8547 «по времени» и #Z8551 «по триггеру» — разные типы, а поле 5 у обоих 9 |
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 ✅ |
#Z8597 = 11, 'Тестовый сценарий по расписанию', [8598], 61, 3354, 8, 0, 0
^годнедели ^время ^тип
Формула времени:
поле6 = (час << 8) | минута. Начало недели — ПН (бит 0), не ВС. Проверка:61 >> 0 & 1 = 1(ПН есть),61 >> 1 & 1 = 0(ВТ нет — у Alex ВТ не отмечен) ✅.
#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) |
мин` |
| 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 полей = если/то/иначе):
#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 |
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 — что это? → ✅ пункт 7 ЗАКРЫТ: 145728 — id выхода (output_id = 145728 >> 4 = 9108), 512 — код. Alex: «145728 — это очевидно id». Выводить семантику 512 из подписи не нужно |
| 8 | #Z8457 = [9,'Установить целевую температуру 23.8...',8669,'2968'], #Z8470 = [9,...,8669,'8574'] |
→ ✅ пункт 8 ЗАКРЫТ: type 9 = [9, '<подпись>', <id цели>, '<значение>']. Цель — реле (14) / контур (16) / режим (20). Поле 4 — ссылка/код, не уставка («'2968'» при тексте «23.8»). Alex подтвердил: 8470 → цель 8669 = «Контур ГВС» |
8.12. 🔴 Type 9 и Type 5 — модель полей (подтверждено Alex 2026-09-17, вечер)
Type 9 — команда (реле / контур / режим отопления):
#Z<id> = [9, '<подпись команды>', <id цели>, '<значение>']
| Часть | Смысл | Пример |
|---|---|---|
| поле 2 | подпись, собранная UI | 'Установить целевую температуру 22 для контура Спальня' |
| поле 3 | id цели → тип 14 (реле) / 16 (контур) / 20 (режим) | 10034 = Спальня (16), 8574 = Режим отопления (20), 9263 = Реле 1: Конвектор кухня (14) |
| поле 4 | значение как строка — '1'/'0' (вкл/выкл) или ссылка/код |
9826→'1' (вкл реле), 8470→'8574', 8457→'2968' |
🔴 Ловушка: поле 4 ≠ значение уставки. У 8457 подпись «температуру 23.8», а поле 4 = '2968'
(ссылка на отсутствующий объект). Не выводить значение из текста подписи.
Type 5 — действие над выходом контроллера:
#Z<id> = [5, '<имя>', <output_ref>, <on/off>, <delay>, <impulse>, [], bmp, time, <code>]
output_id = output_ref >> 4. Пример 9500: output_ref=145728 → output_id=9108, value=1.
✅ Ответы Alex (пересозданный сценарий): «
8457— поменял температуру на 22, контур на Спальня» (→ новый#Z8641=9,'Установить целевую температуру 22 для контура Спальня',10034,'2950'); «8470:8669это очевидно Контур ГВС. больше там полей в UI нет. добавил второе такое же действие но с "для Всех контуров"» (→ новый#Z8640=9,'Включить режим «Режим отопления»',8574,'1').
Что уже прозрачно (не переспрашивать):
- 7×
set var1и их источники:8450(Температура подачи, тип 27),9864(Статус 13/9, тип 0),8560(Контур газ котла, тип 16),9263(Реле 1 конвектор, тип 14),8254(Погода из интернета, тип 1),4098(Адаптер газ. котла, тип 6),8382(Температура Zigbee, тип 1) ✅ #8466puts "Отладка",#8468storeev I "z",#8469storeev A "asdf"— вывод/события ✅#8489пауза 5 суток (432000000мс), дерево#8506→#8505(И/ИЛИ/НЕ + ИНАЧЕ) ✅#8195SMSCMC оповещение→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) |
Счётчики конфига config_local_2026-09-17_14-16-35.txt (для сверки): 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 |
Финальный конфиг config_local_2026-09-17_16-02-18.txt (Alex пересоздал сценарий, вечер):
686 строк, 34 962 байт. Сценарий 8456 полностью перегенерирован (новые id 8640–8777):
добавлены #Z8640=9,'Включить режим «Режим отопления»',8574,'1' и
#Z8641=9,'Установить целевую температуру 22 для контура Спальня',10034,'2950'.
YAML — zont_config/config_local_2026-09-17_16-02-18.yml, round-trip ✅.
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 сценария как есть:
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.
8.13. 🔴 УСТАВКА ТЕМПЕРАТУРЫ — формула код = (t °C + 273) × 10 (подтверждено 3 точками, 2026-09-17)
Главное открытие разбора type 9. Поле 4 команды «Установить целевую температуру» — не ссылка, а закодированная уставка в Кельвинах ×10 (fixed point):
код = (t_celsius + 273) * 10 обратно: t_celsius = (код - 2730) / 10
Три подтверждённые точки (Alex менял температуру в UI, я сверял с конфигом):
| Уставка в UI | Код в конфиге | Проверка |
|---|---|---|
22 |
2950 |
(22 + 273) * 10 = 2950 ✅ |
23.8 |
2968 |
(23.8 + 273) * 10 = 2968 ✅ |
5.2 |
2782 |
(5.2 + 273) * 10 = 2782 ✅ (предсказано заранее, совпало) |
Как это выглядит в конфиге: сам код уставки лежит в поле 4, а человекочитаемая температура — только в текстовой подписи поля 2, которую контроллер собирает сам:
#Z8817=9,'Установить целевую температуру 5.2 для контура Спальня',10034,'2782'. 🔴 Раньше я ошибочно считал'2950'/'2968'«ссылкой на отсутствующий объект» — это была уставка, просто закодированная.⚠️ Питфолл метода: на двух точках линейная формула подбирается бесконечно многими способами. Я нашёл
(t+273)*10на 22 и 23.8, но не вшивал её в парсер, пока Alex не дал третью точку (5.2). Правило: ≥3 точки, прежде чем кодировать формулу.
Что раскрыто в YAML (type 9) — ✅ финальные имена полей (2026-09-17): descr/target/value,
сырьё в raw_value (Alex: «ОЧЕВИДНО ИЛИ НЕТ ЧТО НАМ НУЖНЫ ПОЛЯ descr, target id и value который блядь 5,2»):
- id: 8817
descr: Установить целевую температуру 5.2 для контура Спальня
target: 10034
target_name: Спальня # развёрнуто из id (тип 16 = контур)
target_type: 16
value: 5.2 # ← Цельсии, то, что правит человек
raw_value: '2782' # сырой код из конфига (источник истины для сборки)
- id: 9826
descr: 'Включить реле «Реле 1: Конвектор кухня»'
target: 9263
target_name: 'Реле 1: Конвектор кухня'
target_type: 14 # 14 = реле
value: true # ← раскодировано (было '1')
raw_value: '1'
value — раскодированное значение (Цельсии / true / false), raw_value — сырьё.
Декодирование уставки — только при точном совпадении формулы (|round(t*10)-(код-2730)| < 0.5);
иначе value = сырая строка без raw_value. Энкодер собирает строку из raw_value, не из value.
8.14. objcmd <id> "1 %0" — запись значения в объект
Скрипт #Z<id> = [59, 'objcmd <target> "1 %0"', <значение>, 0, <флаг>] пишет <значение>
в объект <target>:
- id: 8818
script: objcmd 8700 "1 %0"
args: [14.5, 0, 1]
target: 8700
target_name: Температура Детская # развёрнуто из id
set_value: 14.5 # ← «= 14.5»
🔴 Питфолл имён: у типа 1 (виртуальный датчик) имя лежит в поле 2, а не в поле 1:
#Z8700=1,'0','Температура Детская',…. Наивный fields[1] даёт '0'.
Использовать хелпер _object_display_name(fields) — он знает про это исключение.
Проверено: target_name был '0', после фикса — «Температура Детская».
8.15. Финальное состояние конвертеров (2026-09-17, вечер)
| Артефакт | Путь | Состояние |
|---|---|---|
| Сырой конфиг | zont_config/config_local_2026-09-17_16-13-28.txt |
34 963 Б, снят с http://192.168.0.50/config.txt |
| YAML | zont_config/config_local_2026-09-17_16-13-28.yml |
в проекте, round-trip ✅ |
Round-trip чистый на 6/6 конфигах (боевой, 3 архива, 14-16-35, 16-13-28).
⚠️ УСТАРЕЛО после правок конца сессии (см. §8.16):
target_name/target_type/raw_valueудалены, секции-дубли убраны, YAML = 5452 строки. Актуальное состояние — §8.16.
8.16. 🔴 Разбор конца сессии 2026-09-17 — поле 5 и удаление подстановок
(а) Поле 5 типа 11 — модель §8.1 ОПРОВЕРГНУТА
Alex, разбирая YAML, наткнулся на сценарий 9628:
#Z9628=11,'Автомат.: Рад. ванная 2эт ВКЛ',[9756],0,0,1,0,0
«сценарий
Автомат.: Рад. ванная 2эт ВКЛ: там ахинея какая-то! это сценарий по триггеру. почему он manual?»
Парсер выводил trigger: {type: manual} — по таблице §8.1 (1 = manual). Alex: неверно.
Дальше — прямое опровержение всей таблицы:
| Сценарий | Что сказал Alex | Поле 5 | Поля 3/4 |
|---|---|---|---|
9628 … 64 шт Автомат.:* |
по триггеру | 1 |
0,0 |
8456 «Простой тестовый сценарий» |
«точно такой же manual как и другие два, но он disabled» | 8 |
0,0 |
8547 «тестовый сценарий по времени» |
по времени | 9 |
0,0 |
8551 «Тестовый сценарий по триггеру» |
по триггеру | 9 |
0,0 |
8597 «Тестовый сценарий по расписанию» |
расписание пн,ср,чт,пт,сб 13:26 | 8 |
61,3354 |
Вывод: 8 и 9 не различают «расписание/триггер». Два сценария с 9 — «по времени»
и «по триггеру» — Alex сделал специально как разные типы. 8456 с 8 — ручной.
9628 с 1 — триггерный.
Распределение поля 5 по 70 сценариям боевого конфига:
| Код | Кол-во | Что известно |
|---|---|---|
1 |
64 | Автомат.:* — по триггеру (условие внутри шага) |
0 |
2 | 11109 и ещё один |
9 |
2 | 8547 (по времени), 8551 (по триггеру) |
8 |
1 | 8597 (расписание 61,3354) + 8456 (disabled) |
10 |
1 | 8599 (интервал) |
🔴 ЧЕГО Я НЕ ЗНАЮ (не выдумывать):
- Что означает
1vs9— оба могут быть «включён и запускается по своему условию».- Как кодируется disabled (
8456=8+ нули,8597=8+ заполненные поля — возможно, disabled = «тип есть, параметры сброшены»).- Где вообще хранится «по триггеру / по времени / ручной», если не в поле 5. Кандидаты: поле 4, поле 6, поле 7 — у
9628и8547/8551все нули.Статус:
TRIGGER_KINDSвconfig-to-yml.py— ВЫДУМКА, подлежит удалению. Ждём ответ Alex, что показано в UI у9628/8547/8551/8456.
(б) Структура YAML — работы конца сессии
| # | Что | Итог |
|---|---|---|
| 1 | Секции-дубли delays/scenario_steps/scenario_conditions/scenario_scripts/scenario_raw_objects |
удалены из парсера, TYPE_ORDER и вывода. Осталась одна служебная — scenario_orphans (недостижимые объекты) |
| 2 | Переименование command→descr, output/output_id→target, temp_c→value |
✅ сделано, имена = поля строки конфига |
| 3 | target_name / target_type / raw_value |
✅ удалены (5 мест в парсере + энкодер) |
| 4 | Энкодер пересчитывает код из value |
✅ хелпер _encode_type9_value(): True→'1', False→'0', число → str(round((v+273)*10)) |
| 5 | object_display_name() вынесен на уровень модуля |
✅ (был вложенной функцией ниже места вызова → NameError, питфолл 28) |
Три питфолла, каждый ловил round-trip (Duplicate ID 8640/9826 → потеряно 45 → потеряно 189)
подробно в family/how-to/zont-config-compiler §5c.
(в) Открытый вопрос Alex: «получается script editing language?»
Alex сформулировал корневое сомнение по форме YAML:
«хуйня какая-то получается, не правда ли?» → «я говорю хуйня а не script editing language выходит какая-то»
Смысл: в YAML появились if/then/else/group: and/op: '>'/wait/args — структура,
которой в конфиге нет. Это не отображение данных, а интерпретация поверх них.
Отсюда же растут «две правды» ('2782' vs 5.2 в разных секциях) — симптом, не причина.
Рассмотренный, но НЕ принятый вариант: steps = те же плоские записи, что в #Z-строках,
плюс человеческие имена только для доказанных полей. Без if/then/group/op.
Решение не принято — ждёт Alex.
🔴 Урок для методологии: при правке одной секции проверять, не отображается ли тот же объект в другой. Раскодировал
valueвsteps[], но забыл проrelay_commands→ один объект, два разныхvalue. Alex нашёл это глазами, round-trip был зелёным. Round-trip не ловит смысловые расхождения между секциями.