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



  • @Erik:

    > const ActRz = Device("ActorA", "Режим отопления");
    > const ActComf = Device("ActorA", "Уставка Комфорт");
    > const ActDsp = Device("ActorA", "Отображение уставки текущего режима");
    > 
    > startOnChange(ActDsp);
    > 
    > script({
    >     start() {
    >       this.addTimer(T1);
    >       if (ActRz.value === 0) {
    >         let newvalue = ActDsp.value;
    >         this.stopTimer('T1');
    >         this.startTimer('T1', 2.0, 'next');
    >         return;
    >       }  
    >     },
    >  next() {
    >     ActComf.setValue(newvalue);
    >   }     
    > });
    > 
    

    не работает.

    Добрый день!

    Здесь есть синтаксическая ошибка (объявление newvalue) и структурная

    Попробуйте написать так, чуть позже прокомментирую:

    const ActRz = Device("ActorA", "Режим отопления");
    const ActComf = Device("ActorA", "Уставка Комфорт");
    const ActDsp = Device("ActorA", "Отображение уставки текущего режима");
    
    startOnChange(ActDsp);
    
    script({
        newvalue:0,
    
        start() {
           if (ActRz.value === 0) {  // Не совсем понятно, что это. Но надо, так надо
            	this.newvalue = ActDsp.value;
            	this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next');
            	this.addListener(ActDsp, 'resetTimer');
           }
        },
    
       next() {
       	  this.log('SET '+this.newvalue)
        	  ActComf.setValue(this.newvalue);
              this.exit();
       },
    
      resetTimer() {
            if (this.newvalue != ActDsp.value) {      
             	 this.newvalue = ActDsp.value;
             	 this.log('new value '+this.newvalue)
             	 this.stopTimer('T1');
            	 this.startTimer('T1', 2, 'next');
            }  
      }
    });
    
    
    

    this.log вставлен, чтобы отследить работу алгоритма в отладчике или журнале



  • "Не совсем понятно …" - эта часть скрипта должна выполняться, если режим отопления комфортный (ActRz.value = 0)

    Полная версия для 3-х разных режимов присваивает значения разным актуаторам.

    А в 4-и режиме ничего не присваивает.

    const ActRz = Device("ActorA", "Режим отопления");
    const ActComf = Device("ActorA", "Уставка Комфорт");
    const ActEco = Device("ActorA", "Уставка Эконом");
    const ActOff = Device("ActorA", "Уставка Выключено");
    const ActDsp = Device("ActorA", "Отображение уставки текущего режима");
    
    startOnChange(ActDsp);
    
    script({
        newvalue:0,
    
        start() {
           if (ActRz.value === 0) {  // Не совсем понятно, что это. Но надо, так надо
            	this.newvalue = ActDsp.value;
            	this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next1');
            	this.addListener(ActDsp, 'resetTimer');
           }
            if (ActRz.value == 1) {  // Не совсем понятно, что это. Но надо, так надо
            	this.newvalue = ActDsp.value;
            	this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next2');
            	this.addListener(ActDsp, 'resetTimer');
           }
            if (ActRz.value == 3) {  // Не совсем понятно, что это. Но надо, так надо
            	this.newvalue = ActDsp.value;
            	this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next3');
            	this.addListener(ActDsp, 'resetTimer');
           }
        },
    
       next1() {
       	  this.log('SET '+this.newvalue)
        	  ActComf.setValue(this.newvalue);
              this.exit();
       },
        next2() {
       	  this.log('SET '+this.newvalue)
        	  ActEco.setValue(this.newvalue);
              this.exit();
        },       
      next3() {
       	  this.log('SET '+this.newvalue)
        	  ActOff.setValue(this.newvalue);
              this.exit();
        },        
    
      resetTimer() {
            if (this.newvalue != ActDsp.value) {      
             	 this.newvalue = ActDsp.value;
             	 this.log('new value '+this.newvalue)
             	 this.stopTimer('T1');
            	 this.startTimer('T1', 2, 'next');
            }  
      }
    });
    
    

    А что будет, если второе нажатие случится ДО истечения таймера от первого нажатия? Таймер сам перезапускается, ему команды старт/стоп давать не нужно?



  • Этот скрипт (без таймеров) у меня работает

    const ActRz = Device("ActorA", "Режим отопления");
    const ActComf = Device("ActorA", "Уставка Комфорт");
    const ActEco = Device("ActorA", "Уставка Эконом");
    const ActOff = Device("ActorA", "Уставка Выключено");
    const ActDsp = Device("ActorA", "Отображение уставки текущего режима");
    
    startOnChange(ActDsp);
    
    script({
        start() {
          if (ActRz.value === 0) {
            let newvalue = ActDsp.value;
            ActComf.setValue(newvalue);
            return;
          }  
    
          if (ActRz.value == 1) {
            let newvalue = ActDsp.value;
            ActEco.setValue(newvalue);
            return;
          }
          if (ActRz.value == 3) {
            let newvalue = ActDsp.value;
            ActOff.setValue(newvalue);
            return;
          }
        }
    });
    
    

    Если добавляю таймер только в экономичный режим, все работает (в экономичном режиме переключает тоже)

    const ActRz = Device("ActorA", "Режим отопления");
    const ActComf = Device("ActorA", "Уставка Комфорт");
    const ActEco = Device("ActorA", "Уставка Эконом");
    const ActOff = Device("ActorA", "Уставка Выключено");
    const ActDsp = Device("ActorA", "Отображение уставки текущего режима");
    
    startOnChange(ActDsp);
    
    script({
       newvalue:0,
        start() {
          if (ActRz.value === 0) {
            let newvalue = ActDsp.value;
            ActComf.setValue(newvalue);
            return;
          }  
    
          if (ActRz.value == 1) {
            this.newvalue = ActDsp.value;
            	this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next');
            	this.addListener(ActDsp, 'resetTimer');
           }
    
          if (ActRz.value == 3) {
            let newvalue = ActDsp.value;
            ActOff.setValue(newvalue);
            return;
          }
        },
    
       next() {
       	  this.log('SET '+this.newvalue)
        	  ActEco.setValue(this.newvalue);
              this.exit();
       },
    
      resetTimer() {
            if (this.newvalue != ActDsp.value) {      
             	 this.newvalue = ActDsp.value;
             	 this.log('new value '+this.newvalue)
             	 this.stopTimer('T1');
            	 this.startTimer('T1', 2, 'next');
            }  
      }
    });
    
    

    А если по аналогии делаю все режимы - ничего не работает

    const ActRz = Device("ActorA", "Режим отопления");
    const ActComf = Device("ActorA", "Уставка Комфорт");
    const ActEco = Device("ActorA", "Уставка Эконом");
    const ActOff = Device("ActorA", "Уставка Выключено");
    const ActDsp = Device("ActorA", "Отображение уставки текущего режима");
    
    startOnChange(ActDsp);
    
    script({
       newvalue:0,
        start() {
          if (ActRz.value === 0) {
            this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next1');
            	this.addListener(ActDsp, 'resetTimer');
          }  
    
          if (ActRz.value == 1) {
            this.newvalue = ActDsp.value;
            	this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next2');
            	this.addListener(ActDsp, 'resetTimer');
           }
    
          if (ActRz.value == 3) {
            this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next3');
            	this.addListener(ActDsp, 'resetTimer');
          }
        },
       next1() {
       	  this.log('SET '+this.newvalue)
        	  ActComf.setValue(this.newvalue);
              this.exit();
       },
       next2() {
       	  this.log('SET '+this.newvalue)
        	  ActEco.setValue(this.newvalue);
              this.exit();
       },
       next3() {
       	  this.log('SET '+this.newvalue)
        	  ActOff.setValue(this.newvalue);
              this.exit();
       },
    
      resetTimer() {
            if (this.newvalue != ActDsp.value) {      
             	 this.newvalue = ActDsp.value;
             	 this.log('new value '+this.newvalue)
             	 this.stopTimer('T1');
            	 this.startTimer('T1', 2, 'next');
            }  
      }
    });
    
    


  • @Erik:

    А что будет, если второе нажатие случится ДО истечения таймера от первого нажатия? Таймер сам перезапускается, ему команды старт/стоп давать не нужно?

    Для этого добавлен слушатель : this.addListener(ActDsp, 'resetTimer');

    В целом, механизм такой:

    Если сценарий запустился и взвел таймеры, он второй раз по триггерам не запустится. Он ведь уже работает!

    И если нужно следить за событиями устройств изнутри сценария, нужно добавить слушателя

     this.addListener(ActDsp, 'resetTimer');
    
    

    Слушатель срабатывает по событиям устройства ActDsp, то есть при втором … n-ном нажатии. По событию выполнится resetTimer

    В нем смотрим - изменилось значение - тогда перевзводим таймер

    Если же таймер досчитал и обновил значение - нужно завершить сценарий

    this.exit();. 
    
    

    Тогда следующее нажатие уже пройдет через старт.

    Если сценарий со слушателем не завершить - он останется активным



  • @Erik:

    Этот скрипт (без таймеров) у меня работает

    А если по аналогии делаю все режимы - ничего не работает

    Вы значение this.newvalue присваиваете только в одном из IF.

     if (ActRz.value === 0) {
          // ЗДЕСЬ НЕТ
            this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next1');
            	this.addListener(ActDsp, 'resetTimer');
          }  
    
          if (ActRz.value == 1) {
            this.newvalue = ActDsp.value;
            	this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next2');
            	this.addListener(ActDsp, 'resetTimer');
           }
    
          if (ActRz.value == 3) {
          // ЗДЕСЬ НЕТ
            this.log('start value ' + this.newvalue)
            	this.startTimer('T1', 2, 'next3');
            	this.addListener(ActDsp, 'resetTimer');
          }
        }
    
    

    Можно вынести в начало

    start() {
          this.newvalue = ActDsp.value;
          if (ActRz.value === 0) {
         ...
    
    
    


  • Заработало так

    const ActRz = Device("ActorA", "Режим отопления");
    const ActComf = Device("ActorA", "Уставка Комфорт");
    const ActEco = Device("ActorA", "Уставка Эконом");
    const ActOff = Device("ActorA", "Уставка Выключено");
    const ActDsp = Device("ActorA", "Отображение уставки текущего режима");
    
    startOnChange(ActDsp);
    
    script({
       newvalue:0,
        start() {
          if (ActRz.value === 0) {
            this.newvalue = ActDsp.value;
            	this.startTimer('T1', 2, 'next1');
            	this.addListener(ActDsp, 'resetTimer1');
          }  
    
          if (ActRz.value == 1) {
            this.newvalue = ActDsp.value;
            	this.startTimer('T2', 2, 'next2');
            	this.addListener(ActDsp, 'resetTimer2');
           }
    
          if (ActRz.value == 3) {
            	this.newvalue = ActDsp.value;
            	this.startTimer('T3', 2, 'next3');
            	this.addListener(ActDsp, 'resetTimer3');
          }
        },
       next1() {
       	  this.log('SET '+this.newvalue)
        	  ActComf.setValue(this.newvalue);
              this.exit();
       },
       next2() {
       	  this.log('SET '+this.newvalue)
        	  ActEco.setValue(this.newvalue);
              this.exit();
       },
       next3() {
       	  this.log('SET '+this.newvalue)
        	  ActOff.setValue(this.newvalue);
              this.exit();
       },
    
      resetTimer1() {
            if (this.newvalue != ActDsp.value) {      
             	 this.newvalue = ActDsp.value;
             	 this.log('new value '+this.newvalue)
             	 this.stopTimer('T1');
            	 this.startTimer('T1', 2, 'next1');
            }
      },
      resetTimer2() {
            if (this.newvalue != ActDsp.value) {      
             	 this.newvalue = ActDsp.value;
             	 this.log('new value '+this.newvalue)
             	 this.stopTimer('T2');
            	 this.startTimer('T2', 2, 'next2');
            }
      },
      resetTimer3() {
            if (this.newvalue != ActDsp.value) {      
             	 this.newvalue = ActDsp.value;
             	 this.log('new value '+this.newvalue)
             	 this.stopTimer('T3');
            	 this.startTimer('T3', 2, 'next3');
            }  
      }
    });
    
    

    Посоветуйте учебник.

    Хаотические манипуляции с непонятным текстом интересны только первые 20 минут 🙂



  • @Erik:

    Заработало так

    Посоветуйте учебник.

    Хаотические манипуляции с непонятным текстом интересны только первые 20 минут 🙂

    Вы оказывается и таймеры разные взводили 🙂 Но получилось же :!:

    Можно значительно короче - один и тот же таймер и один resetTimer - за один раз сработает только один вариант

    По учебнику - любой по JavaScript, основы, можно в комбинации: JavaScript+Node.js, серверный JavaScript

    Нужно еще привыкнуть, что сценарии выполняются не напрямую, а движком сценариев IH

    И все происходит не линейно, а асинхронно

    Здесь описан механизм сценариев с примерами: https://ih-systems.com/ru/about-scenes/

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

    В нем используются все основные концепции

    И советую применять отладчик в рабочих сценариях, там виден ход выполнения

    Вот результат выполнения одного из вариантов вашего скрипта

    06.02 16:50:53.228 S9(ACTORA1,,ACTORA2,ACTORA3) Trigger ACTORA3
    06.02 16:50:53.229 S9(ACTORA1,,ACTORA2,ACTORA3) Started
    06.02 16:50:53.230 S9(ACTORA1,,ACTORA2,ACTORA3) log: start value 28
    06.02 16:50:53.357 S9(ACTORA1,,ACTORA2,ACTORA3) start timer T1 for 2 sek
    06.02 16:50:55.795 S9(ACTORA1,,ACTORA2,ACTORA3) listener on event ACTORA3: resetTimer
    06.02 16:50:55.796 S9(ACTORA1,,ACTORA2,ACTORA3) exec function resetTimer
    06.02 16:50:55.797 S9(ACTORA1,,ACTORA2,ACTORA3) log: new value 35
    06.02 16:50:55.818 S9(ACTORA1,,ACTORA2,ACTORA3) stop timer T1
    06.02 16:50:55.819 S9(ACTORA1,,ACTORA2,ACTORA3) start timer T1 for 2 sek
    06.02 16:51:00.872 S9(ACTORA1,,ACTORA2,ACTORA3) Done timer T1
    06.02 16:51:00.873 S9(ACTORA1,,ACTORA2,ACTORA3) exec function next
    06.02 16:51:00.874 S9(ACTORA1,,ACTORA2,ACTORA3) log: SET 35
    06.02 16:51:00.895 S9(ACTORA1,,ACTORA2,ACTORA3) do ACTORA2 set 35
    06.02 16:51:00.897 S9(ACTORA1,,ACTORA2,ACTORA3) exit
    
    


  • Пока все по углам не развел - не работало. 🙂



  • Подскажите, а сейчас есть возможность в сценариях сделать что-то такое:

    act.on(onResponse:[this.flag = 1])
    
    

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

    Конечно можно взвести таймер и проверить включился ли актюатор (act.isOn()), но это будет плохим решением когда одна железка отрабатывает за несколько мс (виртуальный актюатор, не подключенный к каналам), другой за 1-2 секунды (MegaD с дисплеями особенно), третий за 30-180 секунд (приводы заслонок с обратной связью).



  • @Alex_Jet:

    Подскажите, а сейчас есть возможность в сценариях сделать что-то такое:

    > act.on(onResponse:[this.flag = 1])
    > 
    

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

    Конечно можно взвести таймер и проверить включился ли актюатор (act.isOn()), но это будет плохим решением когда одна железка отрабатывает за несколько мс (виртуальный актюатор, не подключенный к каналам), другой за 1-2 секунды (MegaD с дисплеями особенно), третий за 30-180 секунд (приводы заслонок с обратной связью).

    Можно, конечно

    Нужно установить слушателя событий this.addListener(device, 'funname')

    Пример 4 из документации https://ih-systems.com/ru/about-scenes/

    /**
    * @name Hello World and Blinking and Manual
    * @desc Тестовый сценарий
    * @version 4 
    */
    
    const lamp = Device("LAMP1");
    
    script ({
      lampState:0, // Просто добавляем объекту свойства - переменные             
      count:0,  // Внутри методов обращаться к ним нужно через this
    
      start() {
        this.count = 0; // В таких переменных данные сохраняются и между запусками.
                        // Иногда это нужно, в данном случае считаем с нуля 
        this.lampState = lamp.value; // фиксируем, в каком состоянии находится лампа
                                    // при запуске
        this.log('Hello, World!');
        this.addListener(lamp, 'onLamp'); // Слушаем переключения состояния лампы
        this.next(); // Запускаем функцию, которая также будет работать по таймеру
      },
    
      next() { 
        if (this.lampState) {
           this.lampState = 0; // Здесь ставим ожидаемое состояние     
           lamp.off(); // Даем команду на выключение.
                       // В следующей строке лампа возможно еще не выключится,
                       // если мы работаем с физическим устр-вом
                       // Когда  выключится и пришлет новое состояние - зависит от железа 
                       // и от плагина. При переключении сработает onLamp  
    
           if (this.count <= 10) {
             this.startTimer('T1', 0.4, 'next'); 
           } else { 
             // Выходим после выключения, 10 раз уже мигнули
            this.exit();
           }    
         } else {
            this.lampState = 1;     
            lamp.on();
            this.count += 1; // считаем включения
            this.startTimer('T1', 3, 'next'); 
         }
      },
    
      onLamp() {
        if (this.lampState != lamp.value) {
          // Было внешнее переключение - завершим сценарий
          this.exit();
        }
      }
    });
    
    
    


  • А можно без слушателя и таймеров.

    Запускать сценарий по факту любого изменения актуатора ( startOnChange(Act); ).

    А внутри сценария проверять какое значение от принял, перепроверять (получать подтверждения), и присваивать/изменять значение глобальной переменной.



  • @intrapro:

    Можно значительно короче - один и тот же таймер и один resetTimer - за один раз сработает только один вариант

    Там каждый "nextХ" работает с конкретным актуатором, а каждый "resetX" отсылает к конкретному "nextX".

    Чтобы их оставить по одному, необходжтмо "нексту" строковой переменной имя нужного актуатора передавать.

    Это как сделать?



  • @Erik:

    @intrapro:

    Можно значительно короче - один и тот же таймер и один resetTimer - за один раз сработает только один вариант

    Там каждый "nextХ" работает с конкретным актуатором, а каждый "resetX" отсылает к конкретному "nextX".

    Чтобы их оставить по одному, необходжтмо "нексту" строковой переменной имя нужного актуатора передавать.

    Это как сделать?

    Имя строкой не передается. Возможно, добавим этот функционал

    Но думаю вам это и не надо. Вы к разным актуаторам только в next обращаетесь. Нужно запомнить на входе значение ActRz.

    Хотя можно и не запоминать. Просто проверятьActRz в next

    Вариант с запоминанием:

    startOnChange(ActDsp);
    
    script({
       newvalue:0,
       regim:0,
    
        start() {
          if (ActRz.value < 4) {
                   this.newvalue = ActDsp.value;
                   this.regim = ActRz.value;
                   this.startTimer('T1', 2, 'next');
                   this.addListener(ActDsp, 'resetTimer');
          }  
        },
    
       next() {
       	  this.log('SET '+this.newvalue+' regim='+ this.regim);
              if (this.regim == 0)  ActComf.setValue(this.newvalue);
              if (this.regim == 1)  ActEco.setValue(this.newvalue);
              if (this.regim == 2)  ActOff.setValue(this.newvalue);
    
              this.exit();
       },
    
      resetTimer() {
            if (this.newvalue != ActDsp.value) {      
             	 this.newvalue = ActDsp.value;
             	 this.log('new value '+this.newvalue)
             	 this.stopTimer('T1');
            	 this.startTimer('T1', 2, 'next');
            }
      }
    });
    
    


  • @intrapro:

    Вариант с запоминанием:

    Да, красиво!

    Спасибо.



  • Доброго времени суток Всем !

    Помогите разобраться с информированием telegramm.

    Создал простейшую блок-схему включения отопителя по условию достижения определённой температуры DHT22_t с последующей отправкой сообщения в telegramm. Все отрабатывает как нужно, но но сообщения в бот валятся при каждом опросе датчика DHT22_t.

    Что я делаю не так ?
    1_3.png
    1_2.png
    1_1.png



  • перед "Action" нужна проверка состояния.

    Если и так все как надо присвоено - ничего не делать.

    Если нет - тогда присвоить и отправить в телеграмм.

    Иначе он каждый раз присваивает и отправляет.



  • @intrapro:

    @homa:

    Добрый день!

    Есть простейшее расписание - включить на закате две лампочки, но включается только одна. Причем включается та, которая будет первой в списке)) Пересоздать задачу не помогает, но в журнале отображается запись "Команда on Расписание"

    Спасибо. Проверим, пофиксим

    Добрый день!

    В журнале для каждой лампочки отображается "Команда on Расписание"?

    А состояние - "Включено" - только для одной?

    С какими каналами связаны светильники? С MegaD?



  • @MAMOHT:

    Все отрабатывает как нужно, но но сообщения в бот валятся при каждом опросе датчика DHT22_t.

    @Erik:

    перед "Action" нужна проверка состояния.

    Если и так все как надо присвоено - ничего не делать.

    Если нет - тогда присвоить и отправить в телеграмм.

    Все верно.

    Надо добавить условие AND:
    scen101.jpg
    Если показания датчика меньше уставки и радиатор выключен, включить радиатор и отправить сообщение.

    Кроме этого я бы добавил проверку "Авто":
    scen102.jpg
    Это даст возможность отключить управление радиатором в автоматическом режиме из пользовательского интерфейса. Достаточно будет просто убрать там галку "Авто". Это первое.

    И второе. Иногда хочется включить/выключить радиатор на некоторое время, и чтобы он не срабатывал по датчику. Это можно будет делать благодаря проверки Авто
    scen103.jpg
    То есть при нажатии на иконку радиатора, автоматический режим отключится, появятся часики на иконке радиатора.

    Можно установить время восстановления автоматического режима для радиатора 2 часа после включения.

    Приходите домой, замерзли, нажимаете на иконку радиатора и он будет греть два часа не отключаясь. А потом (через 2 часа) сам перейдет в автоматический режим. Это для примера 😉



  • @sergeyygr:

    А все таки, есть ли способ запуска звукового файла из сценария? 😉

    Есть. В версии 4.4.18 добавлена команда this.execOS(команда ОС) https://ih-systems.com/ru/command_list/

    Можно попробовать



  • @intrahouse:

    Есть. В версии 4.4.18 добавлена команда this.execOS(команда ОС) https://ih-systems.com/ru/command_list/

    Можно попробовать

    про нее написано
    <quote>> Выполнение команд операционной системы

    А операционной системы сервера? Или клиента?

    Где выполняться будет?


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