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



  • есть сценарий, при включении света по датчику движения, дополнил: если включаю дополнительный свет чтобы свет тот что загорелся по датчику движения погас, но что то не работает, где исправить ошибку?

    /** 
    * @name Світло коридор 1 поверх 
    * @desc Включает светильник по датчику движения, отключает при отсутствии движения в течение заданного времени.  
    * @version 4  
    */
    
    
    const lamp = Device("ActorD", "Светильник", [
      {"name":"timeOff", "note":"Светильник горит без движения, сек", "type":"number", "val":5},
      {"name":"light_threshold", "note":"Порог освещенности для включения", "type":"number", "val":20}
      ]); 
    
    const lamp2 = Device("ActorD", "Освітлення Led коридор 1 поверх Коридор");  
      
    const motion = Device("SensorD", "Датчик движения");  
    const lightSensor = Device("SensorA", "Датчик освещенности (аналоговый)"); 
    
    // Запустим сценарий при сработке датчика движения, если светильник не горит и освещенность ниже порога
    // ИЛИ если движение прекратилось, а светильник горит 
    startOnChange([motion,lamp2], motion.isOn() && lamp.isOff() && lightSensor.value <= lamp.getParam("light_threshold") || motion.isOff() && lamp.isOn());
    
    script ({    
        start() {
          if(lamp2.isOff()){
              this.addTimer("T1");  
               
              if (motion.isOn()) {
                lamp.on();
              } else {
                // движения нет - взводим таймер, чтобы отключить 
                this.startTimer("T1", lamp.getParam("timeOff"), "turnOff");       
              }
              // В любом случае следим за датчиком движения и светильником
              this.addListener(motion, "onMotion");
              this.addListener(lamp, "onLamp");
          }else{
            lamp.off();
            this.exit();
          }
        },
        
        onMotion() {
           // Если движение прекратилось - взводим таймер
          if (motion.isOff() && this.timer.T1 == "off")  {
            this.startTimer("T1", lamp.getParam("timeOff"), "turnOff");
          }  
            
           // Если движение возобновилось - сбрасываем таймер
          if (motion.isOn())  {
            this.stopTimer('T1');
          }  
        },
        
        onLamp() {
          // Светильник выключили другим способом - выходим
           if (lamp.isOff()) this.exit();
         },
         
        // Функция, которая сработает, когда таймер досчитает (нет движения в течение заданного 
        // времени) - отключаем и выходим
        turnOff() {
          lamp.off();
          this.exit();
        }
    });
    


  • @homa, не согласен - по мне физика отмирает. Даже в современных авто уже нет физических кнопок. Кстати, у моего рекуператора есть свой контроллер с дисплеем и т.д., но я его просто выкинул, подключив все к MegaD.
    Тренд в управлении - автоматика, мобильные приложения и голосовые ассистенты. Тот же рекуператор у меня всегда в режиме авто, "ручные" режимы сделаны скорее для того чтобы были.



  • Участник @homa написал

    И следом вопрос корректно ли объявлять одно и тоже свойство для одного и того же устройства в разных сценариях, или есть какие-то другие варианты обращения к ранее объявленным свойствам

    идея в том, чтобы изменять из разных сценариев одно свойство... выходит не выходит)

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



  • Добрый день! А можно добавить возможность писать значение устройства из плагина не в value, а в созданный в скрипте дополнительный параметр?
    76bb8199-889e-4bc4-8445-31ebbfce4727-image.png
    чтобы в value писать уже значение после обработки



  • подскажите пожалуйста решение задачи. требуется присвоить значение виртуальному счетчику от физического
    по простому продублировать значение одного другому



  • @m_n61rus Сделайте сценарий

    /** 
    * @name xx 
    * @desc  
    * @version 4 
    */
    const meter6 = Device("METER6"); //оригинал
    const meter7 = Device("METER7"); //копия
    
    startOnChange(meter6); 
    
    script({
        start() {
           this.assign(meter7, 'aval', meter6.value);
        } 
    });
    


  • @homa спасибо попробую



  • @intrapro, расскажите как сделать скрипт, который можно запустить кнопкой и остановить. При этом запущенный скрипт должен работать в бесконечном цикле. Задача - работа гирлянды на базе WS2818 с включением вручную, по расписанию, по датчикам.
    Пример скрипта на php для бесконечной работы гирлянды:

    $cmd_list = array("FF0000", "00FF00", "0000FF", "FFFFFF", "000000");
    $color = 0;
    
    while(true)
    {
        $cmd = "";
        for ( $i = 0; $i < 9; $i++ )
        {
            $color = dechex(rand(0,100));
            if ( strlen($color) == 1 )
            $color = "0".$color;
            $cmd .= $color;
        }
        file_get_contents("http://192.168.0.14/sec/?pt=35&ws=$cmd");
        sleep(1);
    }
    


  • @Alex_Jet
    Примерно так.

    Если нужно 4 состояния ленты менять по кругу бесконечно, то скрипт такой.

    По нажатию кнопки включаете ленту в состояние 1, запускаете таймер с переходом на "stage2", запускаете слушателя кнопки c переходом на "turnOff".
    stage2 - переключаете ленту в состояние 2, запускаете таймер с переходом на "stage3"
    stage3 - переключаете ленту в состояние 3, запускаете таймер с переходом на "stage4"
    stage4 - переключаете ленту в состояние 4, запускаете таймер с переходом на "stage5"
    stage5 - переключаете ленту в состояние 1, запускаете таймер с переходом на "stage2"
    turnOff - останавливаете таймер, выходите из скрипта.

    Если нужно не по кнопке, а из другого скрипта, сделайте актуатор, состояние которого меняете из другого скрипта или той же кнопкой, а скрипт ленты запускайте по изменению этого актуатора, и слушатель в нем на изменения этого актуатора, а не кнопки.



  • @Alex_Jet, Добрый день! @Erik прав - нужен актуатор, который можно переключать как угодно - интерактивно, сценарием, по расписанию...

    Сценарий гирлянды запускается при включении этого актуатора.

    Состояния в php скрипте меняются случайным образом, поэтому логика еще проще:

    1. Добавить слушателя для актуатора-триггера
    2. Генерировать строку цветов случайным образом
    3. Передать команду на MegaD
    4. Взвести таймер 1 сек

    По таймеру - повторить пункты 1-3
    При выключении триггера - завершить сценарий



  • Участник @intrapro написал в Сценарии - новая версия API:

    @Alex_Jet, Добрый день! @Erik прав - нужен актуатор, который можно переключать как угодно - интерактивно, сценарием, по расписанию...

    Сценарий гирлянды запускается при включении этого актуатора.

    Состояния в php скрипте меняются случайным образом, поэтому логика еще проще:

    1. Добавить слушателя для актуатора-триггера
    2. Генерировать строку цветов случайным образом
    3. Передать команду на MegaD
    4. Взвести таймер 1 сек

    По таймеру - повторить пункты 1-3
    При выключении триггера - завершить сценарий

    Спасибо за помощь. В принципе получилось примерно что хотел. Надо еще подумать как сделать более плавное и быстрое изменение уровня одного и того же цвета (главное не зациклить цикл... иначе iH перестанет откликаться), но в принципе все работоспособно! Для тех кто еще не юзал RGB ленты, подключенные к MegaD, через iH:

    /** 
    * @name Освещение - гирлянда из WS2818 
    * @desc Имитация работы гирлянды 
    * @version 4 
    */
    const sw = Device("ACTORA_GARLAND1_01"); 
    
    startOnChange(sw); 
    
    script({
        plugin: "megad1", //Переменная названия плагина
        channel: 34,      //Переменная номера канала плагина
        cmd: '000000',    //Переменная команды
        chip: 151,        //Количество чипов в ленте WS2818
        num: 3,
        i: 0,
        
        start() {
          this.ChangeStateSw();
        },
        
        ChangeStateSw() {
    
          //Остановка таймера для выхода из "цикла"
          this.stopTimer("T1");
          
          switch(sw.value) {
            case 0: this.cmd = '000000';
                    break;
            case 1: this.cmd = '00FF00';
                    break;
            case 2: this.cmd = 'FF0000';
                    break;
            case 3: this.cmd = '0000FF';
                    break;
            case 4: this.ChangeRed();
                    break;
            case 5: this.ChangeGreen();
                    break;
            case 6: this.ChangeBlue();
                    break;
            case 7: this.RandomColor();
                    break;
            case 8: this.num = 3; this.RandomPlay();
                    break;
            case 9: this.num = 6; this.RandomPlay();
                    break;
            case 10: this.num = 9; this.RandomPlay();
                    break;
          }
          
          if(sw.value < 4) {
            this.SendDataToWS2818(this.plugin, this.channel, this.cmd, this.chip);
            this.cmd = '';
            this.exit();
          }
        },
    
        ChangeRed() {
          this.addListener(sw, "ChangeStateSw");
          
          let color = this.i.toString(16);
          if(color.length == 1) color = '0' +color;
          this.cmd = '00' +color+ '00';
          
          this.SendDataToWS2818(this.plugin, this.channel, this.cmd, this.chip);
          this.cmd = '';
          
          if(this.i >= 0 && this.i < 16) this.i += 1;
          else if(this.i >= 16 && this.i < 96) this.i += 8;
          else if(this.i >= 96 && this.i < 240) this.i += 16;
          else if(this.i >= 240) this.i = 0;
    
          this.startTimer("T1", 1, "ChangeRed");
        },
    
        ChangeGreen() {
          this.addListener(sw, "ChangeStateSw");
          
          let color = this.i.toString(16);
          if(color.length == 1) color = '0' +color;
          this.cmd = color+ '0000';
          
          this.SendDataToWS2818(this.plugin, this.channel, this.cmd, this.chip);
          this.cmd = '';
          
          if(this.i >= 0 && this.i < 16) this.i += 1;
          else if(this.i >= 16 && this.i < 96) this.i += 8;
          else if(this.i >= 96 && this.i < 240) this.i += 16;
          else if(this.i >= 240) this.i = 0;
    
          this.startTimer("T1", 1, "ChangeGreen");
        },
        
        ChangeBlue() {
          this.addListener(sw, "ChangeStateSw");
          
          let color = this.i.toString(16);
          if(color.length == 1) color = '0' +color;
          this.cmd = '0000' +color;
          
          this.SendDataToWS2818(this.plugin, this.channel, this.cmd, this.chip);
          this.cmd = '';
          
          if(this.i >= 0 && this.i < 16) this.i += 1;
          else if(this.i >= 16 && this.i < 96) this.i += 8;
          else if(this.i >= 96 && this.i < 240) this.i += 16;
          else if(this.i >= 240) this.i = 0;
    
          this.startTimer("T1", 1, "ChangeBlue");
        },
    
        
        RandomColor() {
          this.addListener(sw, "ChangeStateSw");
          
          let color = ["FF0000", "00FF00", "0000FF", "FFFFFF", "000000"];
          this.cmd = color[this.GetRandomInt(0, 5)] + color[this.GetRandomInt(0, 5)] + color[this.GetRandomInt(0, 5)];
          
          this.SendDataToWS2818(this.plugin, this.channel, this.cmd, this.chip);
          this.cmd = '';
          this.startTimer("T1", 1, "RandomColor");
        },
        
        RandomPlay() {
          this.addListener(sw, "ChangeStateSw");
          
          let color;
          
          for (let i = 0; i < this.num; i++ ) {
            color = this.GetRandomInt(0,256).toString(16);
            if(color.length == 1) color = '0' +color;
            this.cmd += color;
          }
          
          this.SendDataToWS2818(this.plugin, this.channel, this.cmd, this.chip);
          this.cmd = '';
          this.startTimer("T1", 1, "RandomPlay");
        },
        
        //Функция возвращает случайное целое число между min (включительно) и max (не включая max)
        GetRandomInt(min, max) {
          return Math.floor( Math.random() * (max - min)) + min;
        },
        
        //Функция формирования данных для ленты WS2818
        SendDataToWS2818(plugin, channel, cmd, chip) {
          this.pluginCommand({unit: plugin, command: '/sec/?pt=' +channel+ '&ws=' +cmd+ '&chip=' +chip});
        }
    });
    

    Управление лентой - с помощью аналогового актюатора, который имеет слайдер с диапазоном 1-10. Чтобы управлять по расписанию нужен сценарий, в который из расписания будем передавать параметр, а этот параметр будет присваиваться значению актюатора.



  • Добрый день! Можно ли использовать в мультисценариях помимо переменный устройств, постоянные? Делаю так:
    609cea60-e90a-48e9-baec-96a4e3bef2a8-image.png
    В ошибке вижу:
    8e2499a3-ea80-40cc-81ba-aa6013db3d8e-image.png
    Или все таки нужно добавлять это устройство ко всем в списке?



  • Участник @homa написал в Сценарии - новая версия API:

    Добрый день! Можно ли использовать в мультисценариях помимо переменный устройств, постоянные? Делаю так:

    Добрый день, нет, поле все равно будет доступно для выбора, даже если вы пропишите id, но ошибки не должно быть



  • Добрый день!
    Сейчас движок сценариев распознает тип - обычный сценарий или мульти, анализируя именно описание устройств.
    За счет этого легко можно превратить обычный сценарий в мультисценарий и обратно. Поэтому смешивать параметрические и реальные устройства в текущей версии не получится



  • Добрый день, есть проблема, передаю на одну megad одновременно два запроса, как сделать между запросами паузу. если я на одну мегу одновременно даю 2 запроса this.pluginCommand не обрабатывает 2 запрос.

    /** 
    * @name Новый мультисценарий 
    * @desc  
    * @version 4 
    */
    const switch_1 = Device("ActorE","Switch");
    const jalusi = Device("ActorD","Actor",[
        {"name":"point_open", "note":"Пін відкриття", "type":"number", "val":15},
        {"name":"point_close", "note":"Пін закриття", "type":"number", "val":16}
      ]);
    
    startOnChange([switch_1,jalusi]); 
    
    script({
        start() {
          if(this.isChanged(switch_1)){
            this.onSwitch();
          }
          if(this.isChanged(jalusi)){
            this.onJalusi();
          }
        },
        onSwitch(){
          switch(switch_1.value){
              case 0:
                jalusi.on();
                if(jalusi.id === "ACTOR7"){
                  this.pluginCommand({unit:'megad3', command:'/258/?cmd='+jalusi.getParam('point_open')+':1;p200;'+jalusi.getParam('point_open')+':0'});
                }
                if(jalusi.id === "ACTOR8"){
                  //this.pluginCommand({unit:'megad2', command:'/258/?cmd='+jalusi.getParam('point_open')+':1;p210;'+jalusi.getParam('point_open')+':0'});
                }
                if(jalusi.id === "ACTOR9"){
                  //this.pluginCommand({unit:'megad2', command:'/258/?cmd='+jalusi.getParam('point_open')+':1;p230;'+jalusi.getParam('point_open')+':0'});
                }
              break;
              case 1:
                //this.pluginCommand({unit:'megad3', command:'/258/?cmd='+jalusi.getParam('point_open')+':0;'+jalusi.getParam('point_close')+':0'});
              break;
              case 2:
                jalusi.off();
                if(jalusi.id === "ACTOR7"){
                  this.pluginCommand({unit:'megad3', command:'/258/?cmd='+jalusi.getParam('point_close')+':1;p200;'+jalusi.getParam('point_close')+':0'});
                }
                if(jalusi.id === "ACTOR8"){
                  this.pluginCommand({unit:'megad2', command:'/258/?cmd='+jalusi.getParam('point_close')+':1;p210;'+jalusi.getParam('point_close')+':0'});
                }
                if(jalusi.id === "ACTOR9"){
                  this.pluginCommand({unit:'megad2', command:'/258/?cmd='+jalusi.getParam('point_close')+':1;p230;'+jalusi.getParam('point_close')+':0'});
                }
              break;
            }
            this.log('Жалюзі статус : '+switch_1.value+' '+jalusi.id);
        },
        onJalusi(){
          //this.log(this.isChanged(jalusi)+' 38 line');
          if(jalusi.isOn()){
            //this.log('jalusi On');
            if(jalusi.id === "ACTOR7"){
              this.pluginCommand({unit:'megad3', command:'/258/?cmd='+jalusi.getParam('point_open')+':1;p200;'+jalusi.getParam('point_open')+':0'});
            }
            if(jalusi.id === "ACTOR8"){
              this.pluginCommand({unit:'megad2', command:'/258/?cmd='+jalusi.getParam('point_open')+':1;p210;'+jalusi.getParam('point_open')+':0'});
            }
            if(jalusi.id === "ACTOR9"){
              this.pluginCommand({unit:'megad2', command:'/258/?cmd='+jalusi.getParam('point_open')+':1;p230;'+jalusi.getParam('point_open')+':0'});
            }
          }else{
            //this.log('jalusi Off');
            if(jalusi.id === "ACTOR7"){
              this.pluginCommand({unit:'megad3', command:'/258/?cmd='+jalusi.getParam('point_close')+':1;p200;'+jalusi.getParam('point_close')+':0'});
            }
            if(jalusi.id === "ACTOR8"){
              this.pluginCommand({unit:'megad2', command:'/258/?cmd='+jalusi.getParam('point_close')+':1;p210;'+jalusi.getParam('point_close')+':0'});
            }
            if(jalusi.id === "ACTOR9"){
              this.pluginCommand({unit:'megad2', command:'/258/?cmd='+jalusi.getParam('point_close')+':1;p230;'+jalusi.getParam('point_close')+':0'});
            }
          }
          this.exit();
        }
    });
    
    


  • А как у мультисценария отладчик запустить?

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

    Как сапортить в таком случае?

    И пробный запуск мультисценария для выбранного комплекта устройств возможен ли?



  • @Erik Добрый день, передите в рабочие сценарии, выберите необходимый сценарий, откройте нижнюю панель, и запустите отладчик



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



  • @Erik, немного кода. Я правильно понял задачу?

    const dev1 = Device("_UNIT_voiceterminal1", [
      {"name":"status", "note":"Состояние", "type":"string", "val":""}
      ]);
    
    script({
      start(param) {
        //Скрипт запускается плагином с параметрами
        if(param !== undefined) {
          const obj = this.ParseJSON(param);
          //Вывод status на вкладку "Параметры" dev1
          if(obj.status !== undefined)
          dev1.setParam("status", obj.status);
        }
      }
    })
    

    Есть только 2 неудобства:

    1. Когда справа всплывает панель устройства, то всегда отображается вкладка с кнопкой Вкл./Выкл...
    2. Данные на вкладке "Параметры" не обновляются "он-лайн" - свежие данные можно посмотреть только повторно открыв всплывающую панель устройства.


  • @Alex_Jet
    Мне время нужно на осознание. Я не "читаю с листа" скрипты 🙂

    Задача простая - вывести в интерфейсе информацию о последнем открытии (какое окно/дверь и дата время) и о последнем движении (в каком помещении и дата/время).

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

    Или окно пуш-уведомлений для графического интерфейса сделать.


Log in to reply