Словарь домена автоматизаций

Единый контролируемый словарь: канонический термин ↔ wire/код ↔ UI-лейбл ↔ определение. Снимает путаницу «сценарий/автоматизация/расписание» и «условие/сравнение/выражение».

Канон — «автоматизация». «Сценарий» — исторический алиас (код Scenario*, scenarios-worker-db.md — легаси-имена; переименование символов — отдельный follow-up).

Канон (RU)

wire / код

UI-лейбл

Определение

Автоматизация

automation

«Автоматизация»

Сущность: name, triggers, condition_blocks, is_active, schema_version.

Триггер

trigger.kind = time | device | manual

Что запускает вычисление.

Расписание

триггер kind:"time" (schedule_type, time_mode)

«Когда (по времени)»

Time-триггер (cron/one_time, fixed/solar). НЕ отдельная сущность.

Предварительный запуск (готовность к времени)

lead_seconds на АВТОМАТИЗАЦИИ

«Предварительный запуск», «Нужно время на подготовку»

Пользователь задаёт целевое время T (one_time_at расписания) и длительность подготовки у самой автоматизации; запуск = clamp(now, T lead_seconds) (менее чем за lead → немедленно). Наследуют все её разовые расписания с фиксированным временем, включая добавленные из окна автоматизации; cron/солнечные игнорируют — у повторяющегося расписания опережение задаётся его собственным временем. Для процессов с прогревом (баня, тёплый пол).

Событие устройства

триггер kind:"device"

«Когда (устройство)»

Триггер по MQTT-сообщению устройства.

Блок условия

элемент condition_blocks[]

«Если → Тогда»

Пара {condition, then_actions}. Исполняется первый истинный.

Условие

condition (JSON-AST, null = всегда истина)

«Если»

Булева формула. Ранее — строка if_expression.

Действие

then_actions[].type = pause | device_command | message | wait_until | set_group_expose | control

«Тогда»

Что выполнить.

Ожидание

действие type:"wait_until"

«Ожидание»

Блокирует run, пока condition по свойству целевого устройства (device_id) не станет истинным ЛИБО не истечёт таймаут (timeout_seconds — секунды, приоритетнее; либо timeout_minutes). Условие можно не задавать: тогда сценарий продолжится, как только устройство пришлёт любой новый статус — этот статус доступен следующим шагам ({{prop:<device_id>.*}}). send_get (best-effort) провоцирует свежую публикацию статуса. Секундный таймаут и ожидание «любого статуса» требуют schema_version=7. Целевой прибор выбирается каскадом Помещение → Устройство; группы в списке нет — у неё нет своего MQTT-адреса, и ожидание на ней досидело бы до таймаута. С v19 в обоих режимах засчитывается только статус, полученный после входа в шаг; опция «Принимать последний полученный статус» (last_status_max_age_seconds, стампит schema_version=19) явно допускает и уже полученный, если он не старше заданного окна, — редактор предлагает её устройствам без обратной связи. NB: не путать с location_local_runners.last_status — это хартбит хаба, к окну свежести отношения не имеет.

Регистр (exposes)

set_group_expose; device_expose_states (expose_kind / expose_name / value / origin / published / externally_writable)

«Регистр»

Именованное значение состояния прибора — аналог exposes в zigbee2mqtt (state, brightness…). Появляется присвоением, и создать его вправе только сам прибор: его шаги, обработчики и задачи; входящее сообщение регистров не заводит. Пишется действием set_group_expose (value; desired_value при этом гасится), читается как сигнал условия (value / state_gen). Требует schema_version=3.

Умение против виртуального регистра

device_expose_states.origin = declared | virtual

«свой» у виртуального

Умение объявлено через справочник: типизировано, валидируется по метаданным, видно в панели и Алисе. Виртуальный регистр умением не объявлен — тип берётся из значения. Здесь живут фаза сеанса, счётчик кругов, «сколько парить». Цена названа вслух: словарной валидации у него нет.

Границы регистра

published / externally_writable

«Виден» / «Принимает команды»

Две НЕЗАВИСИМЫЕ границы, обе ставятся руками (у умения подняты, у виртуального опущены). Регулируемая настройка должна приходить снаружи и быть видна, не становясь умением; служебное состояние не обязано быть видно, чтобы им пользовались собственные задачи прибора. Запрещена одна комбинация: принимать запись, не публикуя (422). Граница публикации действует на ПРОВОД (шина, панель, Алиса), а не на движок: свои обработчики и задачи видят все регистры — иначе условие по скрытому регистру читало бы «неизвестно», а у задач это ложь.

Обработчик записи

condition_blocks[].expose_name; schema_version=27

«Разложить по регистрам»

Блок логики группы, привязанный к РЕГИСТРУ: на каждый регистр сообщения свой обработчик, они складываются в цепочку в порядке объявления умений. Регистр без переопределения обслуживает встроенное умолчание — порегистрово. Обработчик видит ВСЁ сообщение, а не только свой ключ, поэтому вправе уступить соседу. Шаги — только команды (пауза и ожидание 422: обработчик отвечает, пока сообщение ждёт). Смешивать «с регистром» и «без» в одной ветке нельзя.

Применить умолчание

then_actions[].type = "apply_default"

«Применить умолчание»

Позвать встроенную запись своего регистра по правилам умения. Без него переопределение обработчика — всегда «вместо умолчания» и никогда «вдобавок». Только внутри обработчика.

Ответить

then_actions[].type = "reply"

«Ответить»

Точка, в которой прибор отвечает состоянием на /get. Только в ветке «Чтение» и обязателен в каждом её блоке, если хоть один его несёт: иначе обращение осталось бы без ответа. Не завёлся прогон — отвечает движок (запасной путь).

Исход ожидания

then_actions[].on_timeout у wait_until

«Ожидание → если не дождались»

Что делает шаг, если статуса так и не было: continue (дальше по цепочке — дефолт и поведение до v23) либо cancel (прогон кончается, detail=wait_timed_out). Различить «дождались» и «истёк» иначе нечем: шаг завершается одинаково в обоих случаях. Словарь общий с ждущим сообщением. У фоновой задачи cancel завершает сеанс, то есть уводит в on_stop. Требует schema_version=23.

Ветвление внутри цепочки

then_actions[].type = "branch"

«Ветвление» (шаг цепочки)

Развилка, которая проверяется в тот момент, когда цепочка до неё ДОШЛА, — этим она и отличается от блоков «Если» самой автоматизации: те выбраны один раз, при срабатывании. После получасовой паузы или ожидания обстановка уже другая. Тело — те же блоки: работает первый подошедший, блок без условия это «иначе». Не подошёл ни один — шаг ничего не делает. Пустая ветка законна и означает «в этом случае — ничего»: ветвление последним шагом с пустой веткой и есть обрыв цепочки по условию, который вернули вместо снятого шага «Завершение». Внутри нельзя ветвление (глубина одна), регулятор и вопрос; в условии ветки нельзя операторы перехода. В журнале прогона цепочка видна развёрнутой. Требует schema_version=23.

Фоновая задача группы

автоматизация с group_role = 'task' + automation_task_triggers

«Фоновые задачи» (карточка группы)

Цикл: блоки означают «Пока верно», и пока держится хоть одно условие, задача крутит круги. Перестали держаться все — сеанс кончается веткой остановки и гасит выключатель своей группы. Заменила снятый примитив «циклический переключатель»: тот умел «одно устройство, два состояния, круг», задача — произвольную циклограмму, регулятор и осмысленное окончание. Окно исполнения одно на все задачи — минута, от начала круга: настроек темпа у входа нет, более редкий цикл держит пауза внутри цепочки. Ветка остановки — on_stop (только команды). Требует schema_version=29.

Задача-взводчик

фоновая задача, пишущая выключатель ДРУГОЙ группы

обычные блоки «Если → Тогда» в окне фоновой задачи

Так выражается «работай, пока держится условие», когда нагрузка должна остаться переиспользуемой: выключатель самого взводчика значит «я разрешаю», а его блоки включают и выключают выключатель РАБОЧЕЙ группы. Мёртвая зона пишется блоком с условием и ПУСТЫМ списком действий («держись, ничего не делай»); показания нет — не держится ни одно условие, и сеанс кончается сам. Заменил снятое поле triggers[].while («Пока выполняется», ADR 0031 → ADR 0037), а с волной 29 условие продолжения несут и сами блоки задачи. Форма со взводчиком вдобавок делает рабочую группу переиспользуемой — она видна в панели своим выключателем и уезжает в Алису.

Исход отказа шага

then_actions[].on_failure

«Если не вышло» (у шага)

Что делать с СЕАНСОМ, когда этот шаг не удался: команда не ушла, регистр не записался, длина паузы не взялась из пустого источника. Закончить и погасить (умолчание) — ветка остановки отрабатывает, выключатель гаснет, прогон закрывается completed; сломанной автоматизация не помечается. Идти дальше — со следующего шага; круг при этом всё равно считается неудачным для предохранителя, иначе задача с навсегда сломанной публикацией крутилась бы вечно, выглядя здоровой. Цена отказа у шагов разная — «не включилась печь» и «не зажёгся свет» стоят разного, — и прежний общий на всю задачу исход (triggers[].on_failure, v28) такой разницы выразить не мог; он снят. Требует schema_version=29.

Момент сверки условия

condition_blocks[].check

«Проверять: перед кругом / после первого круга»

У блока задачи. После первого круга — блок держится, пока сам ни разу не отработал: свой круг он получит обязательно, даже если первый достался соседу. Так пишется «в конце обязательно продуть». Долг принадлежит блоку, а не сеансу. Дальше блок сверяется как обычный, поэтому без условия он держится вечно — такое сочетание отвергается (422); «однажды в начале» пишется условием, которое сам круг и опровергает. Требует schema_version=29.

Регулятор

then_actions[].type = "control"

«Регулятор»

Считает выход по измерению и уставке (П/ПИ/ПИД — по тому, какие коэффициенты ненулевые) и кладёт его в переменную прогона output.var. Только внутри фоновой задачи. Мёртвая зона обязательна, сглаживание производной обязательно при kd 0, а такт без нового измерения ничего не считает и не пишет. Устаревшее измерение → публикуется fail_safe.

Длительность из значения (ШИМ)

pause.duration_from

«Пауза → Переменная»

Длина паузы берётся из переменной прогона (выход регулятора) или регистра группы — так выход становится скважностью медленного ШИМ. Вырожденный сегмент (длина меньше секунды) пропускается целиком вместе с открывающей его командой; порог min_seconds, которым это задавалось раньше, снят. Пустой источник значит «сегмента нет» — «мощности нет, не включать», отказом шага это не считается.

Длина паузы прямо в секундах

duration_from с одинаковыми диапазонами

«Пауза → Регистр, как есть»

Когда в источнике лежат секунды, а не проценты, диапазоны делают одинаковыми: in=[0,43200] scale=[0,43200]. Потолок 43200 — потолок самой паузы, обрезать такому отображению нечего. Так задаётся длина, которую называет человек («сколько парить»). Отдельный режим mode = "seconds" для этого был и снят: у него были обратные запреты и обратное поведение на пустом источнике, а в редакторе его было не выбрать.

Коридор поддержания

device-триггер + два condition_blocks (пороги </>)

«Если датчик < низ → ВКЛ» / «> верх → ВЫКЛ»

Не отдельный примитив, а композиция: одна device-триггерная автоматизация с двумя блоками (датчик < low → актуатор ON, датчик > high → OFF; между — держит, гистерезис = зазор). Гейтится неявно по on_off-регистру owner-группы (фаерит, пока группа ON). Прежний start_reading_reconciler (schema_version=5) декоммиссирован. Fail-safe по пропаже датчика нет.

Канал сообщения

channel = email | push | tg | max

Транспорт действия message.

Узел (условия)

AST-узел

группа | сравнение | именованный оператор | exists.

Группа

op = and | or (+``not``)

И / ИЛИ

Логическое объединение узлов.

Оператор

op (символьный =,``< | именованный ``crosses_up,``changed_to``…)

код (display SCREAMING)

Сравнение/переход. Не переводится.

Функция

fn = round | bool | upper | lower

код (display SCREAMING)

Обёртка операнда-свойства.

Операнд

{var} | скаляр | {var, fn} | {arg}

Левая/правая часть сравнения.

Свойство

{"var":"temperature"}

имя свойства

Ссылка на сигнал устройства (dotted-path).

Константа

JSON 24 / "ON"

значение

Число (typed) или строка.

Параметр

определение automation.arguments[]; ссылка {"arg":"name"} / {{arg:name}}

«Параметр»

Именованное типизированное значение, которым пользователь влияет на автоматизацию, не правя её логику. Значения спрашиваются при ЗАПУСКЕ (automation.argument_values) и при добавлении запуска по расписанию (timeTrigger.arguments). Типы number/string/boolean/enum/datetime. В интерфейсе у параметра одно имя — название (label); ключ ссылки (name) редактор выдаёт сам и не показывает, поэтому переименование параметра ссылки не ломает.

Обязательный параметр

automation.arguments[].required

галка «Обязательный», звёздочка у поля

Значение обязано быть заполнено при запуске (иначе кнопка запуска заблокирована, а сервер ответит 422). Автоматизация, у обязательного параметра которой значения нет, выключается — значения спросят при включении.

Предустановленное значение

automation.arguments[].preset

«Предустановленное значение»

Чем предзаполняется диалог ввода и что подставится, если введённых значений нет. Необязательно: необязательный параметр можно оставить и без него — тогда значения при исполнении нет, и в местах вставки подставится пустое, а в сравнениях параметр считается пустой строкой; шаг, длительность которого он задаёт, пропускается. Прежнее default декоммишено (присланное → 422).

Разовый запуск

timeTrigger со schedule_type=one_time (+ свои arguments)

«Разовый запуск» (диалог «Расписания»)

Добавляется/взводится из списка автоматизаций, вне редактора; спрашивает время и значения параметров. Отработавший гаснет (is_active=false) и взводится заново новым временем.

Продолжительность

number + constraints.format=duration (max 43200)

«Продолжительность» (диалог часы/минуты)

Мета-формат параметра: целое число секунд, потолок 12 ч; ввод в UI через часы/минуты. Не отдельный тип — number с маркером формата.

Плейсхолдер свойства

{{prop:path}}

чип свойства

Текущее значение свойства устройства в строке действия (резолв при исполнении).

Порог / Гистерезис

threshold / hysteresis

Параметры crosses_* (детектор фронта).

wire / display

wire = хранимый JSON (lowercase); display = отображение (SCREAMING/инфикс).

Версия контракта

schema_version

Мажор доменного SemVer (см. Версионирование и расширяемость (SemVer)).

Действие

шаг device_commanddevice_id или с голым topic)

«Действие» + подтип

Один пункт списка с двумя подтипами. «Устройство» — команда прибору из справочника (адрес считает сервер). «Публикация в топик» — свой MQTT-адрес внутри локации и свой payload; адрес пишется как есть, префикс дома брокер добавляет сам, шаблоны + и # недопустимы — публикуют в конкретный топик, а не в маску.

Продление шага

поле шага retrigger = skip | extend_whilepause и wait_until)

«Продлевать при повторном срабатывании»

Что делает срабатывание триггера, пока прогон спит на ЭТОМ шаге: отбрасывается (по умолчанию) либо перевзводит его дедлайн на полную длительность. Продлевает только статус, проходящий «Если» того блока, из которого растёт прогон. Этим выражается «N минут после ПОСЛЕДНЕГО движения»: «движения нет» паузу не продлит. Настройка живёт в диалоге шага, а не у автоматизации: перевзвести можно только дедлайн, а он принадлежит конкретному ожиданию.

Отмена прогона

then_actions[].cancel_on = {device_id | topic, condition?, mode}, mode = finish | abort — поле ЖДУЩЕГО шага (пауза, ожидание, ждущее сообщение)

«Отменять по событию» в самом шаге

Событие прерывает идущий прогон: finish снимает ожидание и доигрывает остальные действия («движение прекратилось — выключить сразу»), abort прерывает прогон целиком («протечку устранили — перестать звонить»). Действует, только пока прогон ждёт. Источник события выбирается вкладкой в диалоге: «Устройство» — прибор локации, выбирается каскадом Помещение → Устройство (смена помещения — фильтр списка, уже выбранный прибор и собранное условие она не сбрасывает); «Сообщение в топик» — адрес, который вы пишете сами (прибор чужого моста, свой контроллер), хвостом без префикса локации. У адреса-топика условие необязательно: без него шаг отменит любое сообщение в этот адрес, включая вложенные уровни. Такой адрес — ваш: переименование прибора его не поправит, а автоматизация с адресом вне сети Zigbee этого хаба на хаб не переносится (сообщение видит только облако). Правило проверяется на каждом сообщении устройства, ещё до того как прогон найден, — поэтому сравнивать не с чем: ни с предыдущим значением (CHANGED_TO, CROSSES_*), ни со значениями параметров запуска.

Режим сообщения

mode = alert | confirm | prompt

«Что сделать»: «Сообщить» / «Запросить подтверждение» / «Запросить значения»

Что шаг «Сообщение» делает с человеком. «Сообщить» — односторонняя телеграмма: кнопок нет, автоматизация не ждёт. Два других режима ЖДУТ ответа: подтверждение спрашивает «да/нет», запрос значений добавляет к нему поля — например, на сколько минут включить. Пока ответа нет, автоматизация занята и повторно не срабатывает, поэтому ждать можно не дольше часа.

Подписи кнопок

confirm_label / decline_label

«Кнопка согласия» / «Кнопка отказа»

Что будет написано на кнопках у жильца. Пустые — подставится текст по умолчанию, причём у кнопки отказа он зависит от того, прерывает отказ прогон или нет: «Прервать автоматизацию» на кнопке, которая ничего не прерывает, вводила бы в заблуждение.

Таймаут вопроса

timeout_seconds + on_timeout = continue | cancel

«Ждать ответа, минут» + «Если ответа не будет»

Сколько ждать и что считать молчанием: продолжить прогон (параметры сохраняют прежние значения) или прервать его. Значения по умолчанию нет намеренно — «не дозвонились, значит не открываем ворота» и «не дозвонились, значит греем как обычно» одинаково законны.

Отказ на вопросе

on_decline = cancel | continue

«Если ответят отказом»

Что делает вторая кнопка: прерывает прогон целиком или лишь пропускает этот шаг, оставляя остальные действия. Значения по умолчанию нет намеренно — «спросил и всё равно сделал» и «спросил и не сделал» одинаково законны.

Ответ на вопрос

ссылка /p/{token}

подписи кнопок задаёт автор

Страница из текста вопроса. Токен в ссылке и есть право ответить — авторизации страница не требует, поэтому ссылку не пересылают: тот, у кого она есть, может подтвердить действие или прервать прогон. Отвечает первый: повторный переход ничего не переигрывает.

Место исполнения

execution_place

«Исполнять локально»

Где исполняется автоматизация: в облаке или на локальном сервере (хабе). Ровно одно место в любой момент. Локальная работает без интернета; расписания на хабе не навёрстывают пропущенное время простоя (выключенный хаб = пропуск срабатывания, облако не подстраховывает). См. Локальное исполнение автоматизаций (Zigbee2MQTT).

Адрес на шине

friendly_nameautomations/{kind}/{name}

«Адрес автоматизации»

Имя, по которому к автоматизации можно обратиться из MQTT. Выдаётся сервером как hex идентификатора и не выводится из названия: переименование автоматизации не рвёт чужую подписку. {kind}tasks | logic | rules; отдельный сегмент существует ради подписки «только фоновые задачи» (automations/tasks/#).

Уровень телеметрии

telemetry_level = off | state | events | steps

«Что рассказывать на шине»

Сколько автоматизация сообщает о себе внешним подписчикам, накопительно. Уровень у КАЖДОЙ автоматизации, а не у дома: шумит конкретная задача, глушить нужно её. Ответ на прямой запрос состояния (/get) уровню не подчиняется — иначе отказ был бы неотличим от потери.

См. также: спецификация языка.