Сценарии

Сценарии одно из наиболее важных свойств данной прошивки. Наличие сценариев позволяет реализовать почти любые применения в автоматизации с учетом имеющегося на борту железа.
Возможна реализация сетевых сценариев, что еще больше расширяет применение прошивки.

Перед тем как перейти непосредственно к описанию сценариев еще раз подчеркнем, система IotManager событийная и все действия в ней кроме заложенных рутинных системой выполняются по событиям.

Общий принцип сценариев:

if «какое-то событие» then «что-то сделали»;
либо
if «какое-то событие» then «что-то сделали»; else if «какое-то событие» then «что-то сделали»;

В сценариях можно оперировать данными с сенсоров, переменными, состоянием Gpio (пинов) и функциями.

Пример сценария: if btn==1 then led=1;

  • В сравнении используется оператор ==, в присвоении =
  • Сценарий всегда заканчивается ;
  • Возможны сравнения: равно ==, не равно !=, больше или равно >= , меньше или равно <=, больше >, меньше <
  • Возможна простая математика */+. При использовании математики отделяйте пробелами символы.
  • Возможны двойные, тройные и более условия И (&)ИЛИ (|) например if btn1 == 1 & btn2 == 1 & var_bin == 7 then pump0_out = 1,
    if n100s1 | n101s1 then log1 = n100s1/2 + n101s1/2;
  • В исполнительной части возможно выполнение нескольких действий заключенных в {} Например if btn1 == 1 then {var_pump0 = 1; led = 1;}

(команды могут быть разделены пробелом, точкой с запятой и началом другого определения)

Еще пример:
if t < 10 then {
btn1 = 1 — btn1;
// инверсия кнопки var = var + 1;
if var == 112 then var = 109;
}

  • Возможно использование в сравнениях астериска(*) для исключения числового символа из сравнения, что означает на месте * любой символ.

Пример использования, Включать btn12 и индикатор led каждые 10 минут в интервале с 8 до 23 часов.:
if timer_l==0&getHours() > 07&getHours() <23&gethhmm() == «**:*1» then {btn12= 1; led=1;}
Выключать это через 2 минуты
if timer_l==0&getHours() > 07&getHours() <23&gethhmm() == «**:*3» then {btn12 = 0; led = 0;}


Следующие команды будут возвращать верное значение, если время получено из сети Интернет или RTC(в разработке) иначе пока нули


getHours(); — получить текущее число часов.
getMinutes(); — получить текущее число минут.
getSeconds(); — получить текущее число секунд.
getMonth(); — получить номер текущего месяца.
getDay(); — получить номер текущего дня месяца.
gethhmm(); — получить строку вида hh:mm.
gethhmmss(); — получить строку вида hh:mm:ss.
getTime(); — получить строку вида dd.mm.yy hh:mm:ss
getUptime(); — получить строку вида dd.mm.yy hh:mm:ss о времени работы системы


!!! Важно, на текущий момент мы можем использовать время в сценариях и на его основе делать гибкие конфигурации и расписания.

Однако !!! Само по себе наступление времени не является событием которое мы можем использовать с сравнении if. Наступление времени само по себе не генерирует событие.

Поэтому для сравнения времени мы используем таймер как генератор события. Например минутный зацикленный таймер.

Поэтому события времени всегда выглядят следующим образом if timer_l==0&getHours() > 07 then ...

Для удобства в сценариях можно использовать комментарии, все что будет после знака # будет игнорировано.

if timer0 == 0 then btn1 = 1 # Включить после того как таймер отработал.

Изменение состояние сенсора тоже является событием. Это позволяет реагировать на изменение показаний. Например Ваш датчик опрашивается каждые 5 секунд. Такая конструкция позволит отслеживать изменения и реагировать на них, и даже выполнять простейшую математику с показаниями:

If sensor then data = sensor*5;
if data > limit then out1 = 1;


Обработка ошибок

В модули (пока не все, старые надо обновить, заложен алгоритм генерирования ошибки в случае неверных действий. Например отсутствие данных датчика. При необходимости можно обрабатывать и использовать эту ошибку. Ошибка это элемент id_onError, где id это наш элемент.
Обработка ошибок отличается на локальном устройстве, где непосредственно подключен датчик и на удаленном, которое получает данные это датчика по сети (включен global и прием данных по сети)

Случай на локальном устройстве:

if temp — датчик температуры прислал данные отличные от нуля ( событие истинно). Опрос данных можно обработать как ошибку в случае отсутствия данных.

if temp_onError — внутри модуля при измерении произошла ошибка. Температуру мы не получили. Это может быть связанно с отсутствием модуля или его неисправности, частого опроса и др.

Случай на удаленном устройстве: (Более подробно об этом на странице о сетевых сценариях)

if temp — элемент существует значит и прислал данные отличные от нуля ( событие истинно)

if temp_onError — датчик не прислал событие вовремя

Работа со строками:

В сценарии есть возможность конкатенации строк, в том числе и с числами.
Можно использовать такие конструкции:
if btn == 1 then { var2 = «/pic» + var1 + «.jpg»; var1 = var1 + 1;}

В строках сценария можно использовать экранирование символов: \» — двойная кавычка и \n — перенос строки
strOUT = «Секунд=\n\»» + getSeconds() + «\»» # перенос строки можно увидеть в консоли

Паразитные глобальные циклы

Сценарий — это очень гибкий механизм создания логики реакции контроллера на события в системе. Но в то же время, сценарий может быть источником генерации новых событий, на которые придется реагировать контроллеру в следующий такт выполнения задач. И если реакцией на такие события будет повторная генерация таких же событий, то возникнет бесконечный цикл событий, который будет сильно мешать выполнению основной логики, забивать трафик MQTT и замедлять веб-интерфейс.

Пример такого сценария:

if timer1 == 0 then {
ip = getIP();
if ip != "192.168.4.1" then noWiFiCounter = noWiFiCounter +1;
}

В данном случае временная переменная ip получает сетевой адрес и сверяется со строкой. Проблема в том, что в этот момент формируется событие об изменении временной переменной и после выполнения текущего сценария он будет запущен снова, но уже для события для переменной IP, а т.к. timer1 будет все еще == 0 (секундный таймер, а контроллер оперирует миллисекундами), то событие для IP снова сгенерируется и так столько раз сколько успеет контроллер выполнить за одну секунду, но там уже и новое событие подоспеет для timer1.

Решить проблему можно логически — отказаться от временной переменной: if getIP() != «192.168.4.1» then noWiFiCounter = noWiFiCounter +1;

Или применить специальный вид присваивания — «тихое»: ip := getIP(); В таком случае значение ip изменится, но события не будет. Это так же позволяет разгрузить трафик MQTT, даже без проблемы с циклом.

Примеры сценариев можно посмотреть в специальном разделе нашего канала

Поддержал проект — спас молодого самодельщика! А мы принимаем подарки...

X