Сценарии - новая версия API



  • Команда this.exit() может применяться для прерывания сценария в любом его месте?

    столкнулся с тем, что

    if (device.isOff() )  this.exit() ;  // если выключено - выйти из сценария
    
    

    в основном теле сценария не работает - сценарий бодро шлепает по коду дальше.
    В то же время this.exit() помещенная , например, в функцию таймера прерывает сценарий.



  • @alesle Да, Вы правы, внутри функции start this.exit() не работает. Чтобы завершить функцию start, достаточно сделать return.

    this.exit() предназначен, чтобы завершить асинхронную деятельность сценария из функций - обработчиков (запущенных из start). Логика такая:

    • Метод start() вызывается, когда движок запускает сценарий (по триггерам, по расписанию, интерактивно) и тот становится активным.

    • Если внутри сценария просто последовательность действий, он отработает и сразу завершится (станет не активным). При следующем запуске он опять начнется со start(). Большая часть сценариев работает именно так и 99,9% времени находятся в неактивном состоянии. Напротив таких сценариев не бывает зеленых галочек в Рабочих сценариях.

    • Но если сценарий взводит таймеры и/или устанавливает слушателей событий, сценарий не завершается, а остается активным, следит за своими таймерами и устройствами "изнутри", запускает функции-обработчики как реакцию на отслеживаемые события. Пока сценарий активен (зеленая галочка), start по триггеру уже не работает.

    Чтобы завершить такой сценарий, из обработчика нужно выполнить команду this.exit().
    По этой команде все его таймеры и слушатели удаляются и сценарий станет не активным. При следующем запуске опять выполнится start()



  • @intrapro ,
    Огромное спасибо за разяснения!!!



  • @sergeyygr, думаю, что нужно делать так же как для счётчика газа. Писал об этом - тут
    Главное верно определить вес импульса чтобы получить значения сразу в м/с или км/ч.
    Хотя мне не до конца понятен принцип работы такого датчика. Он самостоятельно каждую секунду выдает сколько то импульсов, которые соответствуют скорости ветра в текущую единицу времени? Если так, то лучше "захватывать" эти показания сценарием, пересчитывать и присваивать датчику. Покажите что выдает отладчик на канале к которому подключен датчик?
    Но опять же если все так как я написал выше, то можно пересчитывать показания с помощью формулы или через вкладку "Обработка значений" канала.



  • @sergeyygr, так может быть просто сервером каждую секунду опрашивать канал 5 раз, вычисляя приращение счётчика канала. Далее сортировать значения, откидывать "боковые", усреднять оставшиеся - то есть будем получать "правильное" усреднённое значение в м/с каждые 5 секунд. Причем все это действие можно проводить раз в минуту/5минут.
    Если вам удобнее км/ч, то в формуле можно усреднённое значение в м/с умножать на 3.6

    А вообще нужно в iH либо написать скрипт подобный или сделать то же средствами канала плагина MegaD.
    Коллеги, @intrahouse, @intrapro, помогите с помощью iH разрешить вот такой скрипт:

    <?
    $last_cnt = 0;
    while(true)
    {
        $state = explode("/", file_get_contents("http://192.168.0.14/sec/?pt=7&cmd=get"));
        $cnt = $state[1];
        if ( $last_cnt != $cnt )
        $wind_speed = ($cnt - $last_cnt) * 0.0875 + 0.1;
        else
        $wind_speed = 0;
        echo $cnt." - $wind_speed m/s";
        $last_cnt = $cnt;
        if ( $cnt > 1000 )
        {
            $state = explode("/", file_get_contents("http://192.168.0.14/sec/?pt=7&cnt=0"));
            $last_cnt = 0;
        }
        sleep(1);
    }
    ?>
    


  • @sergeyygr Добрый день!
    Пока не совсем в теме по датчику. Но если воспроизвести скрипт от @Alex_Jet, то получится примерно так:

    1. Опрашиваем датчик с заданным интервалом из сценария (в каналах опрос не нужен, т е период =0)
    2. По магической формуле 🙂 считаем значение, используя значение счетчика
    3. Периодически сбрасываем счетчик
    const WindSensor = Device('MyWindSensor');
    
    script({
      last_cnt:0,  // Эту переменную нужно сохранять, поэтому ее выносим и далее исп как this.last_cnt
    
      boot() {
          return true; // Сценарий будет запускаться при старте сервера
      }, 
    
      start() {
          this.resetCount();
          // Взвести таймер - опрос раз в секунду, можно чаще (0.5 = 500 мсек)
          this.startTimer('T1', 1, 'onTimer');
      },
      
      resetCount() {
        // Передать запрос на сброс счетчика
        this.pluginCommand({unit:'megad1', command:{url:'/sec/?pt=7&cnt=0'}});
      }, 
    
      onTimer() {
        // Передать запрос на чтение, ответ придет в функцию  getResponse
        this.pluginCommand({unit:'megad1', command:{url:'/sec/?pt=7&cmd=get', onResponse:'raw'}}, 'getResponse');
        this.startTimer('T1', 1, 'onTimer'); // Следующий запрос 
      },
    
      getResponse(body) {
        // body = OFF/2
        this.log('getResponse body= '+body); // Для отладки
        if (!body) return;
    
        const arr = body.split('/');
        if (arr.length<2) {
          this.log('Ожидается значение счетчика после символа  "/"');
          return; // Ошибка, значение не получено
        }  
    
        let cnt = arr[1];
        if ( this.last_cnt != cnt ) {
          let wind_speed = (cnt - this.last_cnt) * 0.0875 + 0.1;
          this.last_cnt = cnt;
          this.assign(WindSensor, 'value', wind_speed);
        }
      
        if ( cnt > 1000 ) {
          this.resetCount();
          this.last_cnt = 0;
        }
      }
    });
    
    • Сценарий будет запускаться при старте сервера
    • будет работать постоянно, потому что таймер все время перевзводится
    • не имеет триггеров, то есть при необходимости его надо взводить/останавливать вручную (Запустить сценарий/ Остановить сценарий)

    Потестируйте, возможно есть косяки или не все учтено



  • @sergeyygr А у вас тип устройства какой? Счетчик? Нужен обычный аналоговый датчик.



  • @sergeyygr Хорошо, что заработало. А скорость то считает правильно? Датчик непростой ☹



  • Пользователь @sergeyygr написал в Сценарии - новая версия API:

    Пользователь @sergeyygr написал в Сценарии - новая версия API:

    Пользователь @intrapro написал в Сценарии - новая версия API:

    @sergeyygr Хорошо, что заработало. А скорость то считает правильно?

    Эталонного измерителя нет). Только формула в паспорте

    Считает - это сейчас КЛЮЧЕВОЙ результат!!!

    Если будет время, опишите задачу и решение в разделе Решения пользователей
    Будем пользоваться 😉



  • @intrapro, ИМХО нужно все же не каждую секунду опрашивать канал, а 1 раз в минуту/5 минут делать 5 измерений и усреднять их. Наверное, это можно сделать с помощью "Функции обработки" в канале?



  • @Alex_Jet, сценарий воспроизводит предложенный вами вариант скрипта php, там идет опрос каждую секунду (while(true) { file_get_contents ... sleep(1)} и периодически сброс счетчика на MegaD

    Если можно опрашивать реже и усреднять - не проблема, усреднятьможно и в сценарии. Без сценария непонятно как сбрасывать счетчик, поэтому в канале опроса нет



  • Коллеги, помогите решить задачу. Имеем - кнопку ( не фиксируемую) для включения/выключения света. Причем кнопка имеет 3 варианта нажатия (1 клик, 2 клика, удержание) и каждый вариант работает по своему. Так же имеем датчик движения. Хочется чтобы освещение работало по датчику движения (появилось движение - лампа загоралась, исчезло движение и через N-минут лампа потухла), но! чтобы его можно было принудительно и на неограниченное время включить с помощью вышеописанной кнопки! Пока идей нет. Подкиньте чтобы я сделал мультисценарий.



  • @Alex_Jet, мне тоже хотелось сделать что-то подобное, но так и не придумал как. В качестве идеи предлагаю следующую. Кнопка управляет бинарным актуатором, а в основном сценарии кроме отслеживания состояния motion и lamp добавить отслеживание этого актуатора. При его включении например останавливать таймер выключения лампы, при выключении наоборот. Как-то так



  • @int144, у меня тоже была такая идея. Хотя вот сейчас родилась мысль!!! В плагине megad можно сделать любой номер канала. Например, для порта 3 (висит кнопка) сделать каналы SW3_1, SW3_2, SW3_3. К ним привязать свои актуаторы (они будут виртуальными - на мнемосхеме не нужны). В расширениях пишем SW3_1=TOGGLE. А в сценарии, в котором участвует датчик движения, лампа и кнопка проверяем в каком состоянии находится SW3_1. Сейчас попробую сделать.



  • @Alex_Jet, не совсем понял. Канал SW3_1 в качестве актуатора? А еще можно попробовать, чтобы кнопка управляла свойством auto, но в примере сценария был оператор выключающий lamp, т.е. его тогда надо корректировать



  • @int144, да именно так. В принципе этот канал меняет свое состояние.
    Вообще вот этот сценарий работает как надо правда только в том случае если выключатель находится вне помещения (включил свет, потом зашел в помещение). А вот в случае если кнопка находится внутри помещения - нужен совсем другой сценарий....



  • @Alex_Jet, у меня используется именно он. Кстати я его на данный момент немного доработал. Я добавил в сценарий лампу над зеркалом. И когда она включена у меня таймер запускается на 20 мин, а когда выключена - на 3.



  • @Alex_Jet, я тут подумал на досуге и предлагаю все таки первый вариант идеи и сценарий, на который Вы указали постом выше. Для реализации предлагаю добавить виртуальный актуатор и доп. сценарий. Указанный Вами сценарий остается без изменений. Актуатор (например lamp_sw) переключается кнопкой, которая должна включить lamp в основном сценарии на неограниченное время. Далее используем доп.сценарий

    const lamp_sw = Device("ActorD", "Наш актуатор");
    const lamp = Device("LAMP","Лампа из основного сценария");
    startOnChange(lamp_sw);
    script({
        start() {
            if (lamp_sw.isOn()) {
               lamp.setAuto(false);
               if (lamp.isOff()) lamp.on();
        } else {
     
    


  • Прошу прощения, не дописал. Руки крюки☹

    const lamp_sw = Device("ActorD", "Наш актуатор");
    const lamp = Device("LAMP","Лампа из основного сценария");
    startOnChange(lamp_sw);
    script({
        start() {
            if (lamp_sw.isOn()) {
               lamp.setAuto(false);
               if (lamp.isOff()) lamp.on();
        } else {
               if (lamp.isOn()) lamp.off();
               lamp.setAuto(true);
        }
     }
    });
    

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



  • А еще можно убрать из доп. сценария lamp.off() и тогда при свет потухнет после прекращения движения в помещении по основному сценарию


Авторизуйтесь, чтобы ответить