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



  • @alesle, @Alex_Jet
    Программисты сказали "Вполне законное требование" 😉



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



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

    @Erik а кусочек сценария можно? Такого раньше не делал)))

    Первый такой

    /** 
    * @name признак движения 
    * @desc  
    * @version 4  
    */
    
    
    const Actor = Device("ActorA", "Признак движения");
    const Motion = Device("SensorD", "Датчик движения"); 
    
    // Запустим сценарий при сработке датчика движения,
    startOnChange([Motion], Motion.isOn());
    
    script({
        start() {
             Actor.on();
             // взводим таймер, чтобы отключить 
             this.startTimer("T1", 3600, "turnOff");       
            
            // Добавляем слушатель - следим за датчиком движения
            this.addListener(Motion, "onMotion");
        },
        
        onMotion() {
          // Если движение возобновилось - сбрасываем таймер
          if (Motion.isOn() )  {
            this.stopTimer("T1");
          }  
           // Если движение прекратилось - взводим таймер (после повторных движений)
          if (Motion.isOff() && this.timer.T1 == "off")  {
            this.startTimer("T1",  3600, "turnOff");
          }  
        },
       
        // Функция, которая сработает, когда таймер досчитает - отключаем признак движения и выходим
        turnOff() {
          Actor.off();
          this.exit(); // Здесь exit нужен, так как есть активные слушатели и сценарий сам не завершится
        }
    });
    
    

    Второй такой (запускаем на закате)

    /** 
    * @name признак движения 
    * @desc  
    * @version 4  
    */
    
    
    const Actor = Device("ActorA", "Признак движения");
    const Rol = Device("ActorA", "Ролкассета"); 
    
    script({
        start() {
             
     if (Actor.isOff()) {
    //пишите тут то, что хотите сделать с Rol)
    Rol.on();
    this.exit();
    },
    
     if (Actor.isOn()) {
             // взводим таймер, чтобы отключить 
             this.startTimer("T1", 3600, "turnOff");       
            
            // Добавляем слушатель - следим за признаком движения
            this.addListener(Actor, "onActor");
        },
        
        onActor() {
           // Если признак движения отключился, закрываем Ролкассету
          if (Actor.isOff())  {
            Rol.on();
             this.exit();
         }  
        },
       
        // Функция, которая сработает, когда таймер досчитает - закрываем Ролкассету и выходим
        turnOff() {
          Rol.on();
          this.exit();
        }
    }
    });
    
    


  • Подскажите, пожалуйста.
    Есть датчик универсальный аналоговый. Привязан к датчику CO2 T6703. У датчика определены состояния: до 400, до 600, до 800, до 1000 и более 1000. Для каждого состояния меняется цвет иконки. На мнемосхеме все работает прекрасно- отображаются значения, меняется цвет иконки. Вопрос в другом. Решил сделать сценарий, отслеживающий изменение состояния. В системе команд есть функция x.isChanged (имя свойства). Но конструкция
    startOnChanged(sens, sens.isChanged('state'));
    не работает. В ошибках рабочего сценария пишет, что функция не определена. Если убрать условие отслеживания состояния, то сценарий отрабатывает по изменению значения каждую минуту. Хотел сократить число запусков. Подскажите, что не так



  • @int144 Вы все верно сделали, но в v4 сценариев мы по техническим причинам убрали device.isChanged(prop), заменив его на this.isChanged(device, prop,value)
    То есть эту возможность в startOnChange сейчас применить нельзя.
    Можно проверить уже внутри start:

    // Проверить, что сценарий запустился из-за изменения состояния
    if (this.isChanged(sens, 'state')) {
    ....
    } 
    // Можно проверить, что переключился в определенное состояние
    if (this.isChanged(sens, 'state', 1)) {
    ...
    }
    

    Извините за неточности в документации 😞



  • @intrapro , спасибо за быстрый ответ. Жаль, конечно, что нет возможности использовать в условии, но так тоже пойдет. Буду иметь в виду



  • Подскажите как можно во время работы сценария блокировать повторный запуск сценария? Есть физическая кнопка которая запускает сценарий, нужно что бы во время работы сценария не было возможности повторного запуска сценария.



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

    Подскажите как можно во время работы сценария блокировать повторный запуск сценария? Есть физическая кнопка которая запускает сценарий, нужно что бы во время работы сценария не было возможности повторного запуска сценария.

    Через доп актуатор. Это прекрасная замена глобальным переменным.

    На старте скрипта проверяете значение этого актуатора. Если 1 - сразу выходите из скрипта. Если 0 - присваиваете ему значение 1, и продолжаете дальше. Потом делаете все, что должен сделать скрипт, а последгним шагом возвращаете этому актуатору значение 0.



  • Не совсем то что надо, у меня ролокасеты, актуатор должен блокироватся на опредиленое время, чтобы ролета могла опустится или поднятся.



  • @amgstone
    Добавьте в конце перед возвращением актуатору значения 0 таймер на нужное время.



  • Команда 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 измерений и усреднять их. Наверное, это можно сделать с помощью "Функции обработки" в канале?


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