Сценарии в intraHouse Cherry



  • @Alex_Jet:

    1. Какой синтаксис чтобы в таймере исполнять не функцию, а сделать действие по окончании счета?

    Действие все равно нужно оборачивать в анонимную callback-функцию.

    Для удобства трассировки и контроля сценариев анонимные функции в сценариях не используются.

    @Alex_Jet:

    2. Как в функции в таймер передать значение времени из переменной?

    Нужно объявить переменные на уровне объекта - сценария.

    Дальше к ним обращение через this.

    Эти переменные сохраняют значение пока сервер не перезагружен (т е запуск-останов сценария не приводит к сбросу значений).

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

    Для отладки (и записи в журнал) можно использовать this.log():

    const script = {
        curTime:[], 
        timeMon:[9,10],
        timeEve:[21,30],
    
        start() {
           let date = new Date();
           curTime[0] = date.getHours();
           curTime[1] = date.getMinutes());
           this.startTimer("T1", 20, "turnOffVent");
        },
    
        turnOffVent() {
             this.log('Current time='+this.curTime.join(':'));
        	 if(this.getStatusTime()) {
        	 ....
        	 }
        },
    
        getStatusTime() {
          return ((this.curTime[0]*60 + this.curTime[1] >= this.timeMon[0]*60 +this.timeMon[1]) 
             && (this.curTime[0]*60 + this.curTime[1] <= this.timeEve[0]*60 + this.timeEve[1])) ? 1 : 0;
        },
        ....
     }     
    
    
    


  • @intrapro:

    Для отладки (и записи в журнал) можно использовать this.log():

    Пока не разобрался как же использовать log. Что он будет логировать? - работу функций или в его тело нужно вставлять какие-то комментарии?

    В общем сделал автономный сценарий управления ПВУ. По задумке он должен работать когда мой SWITCH1 имеет режим AUTO (в другой теме отписался об этом) - это состояние 1 переключателя. Но я не до конца понимаю как работает check - он должен проверять какие-то свойства датчиков/актюаторов и если они true, то выполняется start??? В общем, в моем сценарии пока не работает то что надо.

    Идея сценария такова - если включен хоть один приточный клапан в доме (их 3 штуки), то если не включены - включаем входной и выходной клапаны, а в зависимости от времени суток и количества включенных приточных клапанов выбираем скорость работы вентиляторов ПВУ.

    Автоматическое управление приточными клапанами по датчикам влажности/СО2 скорее всего надо вынести в отдельные сценарии, а пока условия управления ими находится в start.

    Подскажите что я делаю не так? Кодю немного не по правилам…но иначе теряю скобки.

    Да, кстати, сценарий надо как-то принудительно вводить в эксплуатацию (Запустить сценарий)? В каком случае он останавливается (отладчик пишет "mRecuperatorBySensors Now is NOT active.")?

    /**
    * @name Работа рекуператора по датчикам температуры/влажности/СО2
    * @desc  
    */
    
    const sw = Device("SWITCH1");
    const vent_s1 = Device("VENT_PVU1");    //Скорость 1 вентиляторов
    const vent_s2 = Device("VENT_PVU2");    //Скорость 2 вентиляторов
    const vent_s3 = Device("VENT_PVU3");    //Скорость 3 вентиляторов
    const valveIN = Device("VALVE_AIR1");   //Клапан на входе
    const valveOUT = Device("VALVE_AIR2");  //Клапан на выходе
    const valve1F = Device("VALVE_AIR3");   //Клапан первого этажа
    const valve2F = Device("VALVE_AIR4");   //Клапан второго этажа
    const valveLD = Device("VALVE_AIR5");   //Клапан прачечной
    const hum_guest         = Device("SHUMIDITY1_01");
    const hum_laundry       = Device("SHUMIDITY1_07");
    const hum_dressing_room = Device("SHUMIDITY1_08");
    const hum_bedroom_SE    = Device("SHUMIDITY2_01");
    const hum_bedroom_NE    = Device("SHUMIDITY2_02");
    const hum_bedroom_SW    = Device("SHUMIDITY2_03");
    const co2_guest         = Device("SENSORA1_01");
    const co2_bedroom_SE    = Device("SENSORA2_01");
    const co2_bedroom_NE    = Device("SENSORA2_02");
    const co2_bedroom_SW    = Device("SENSORA2_03");
    
    const script = {
        //curTime:[], 
        //timeMon:[9,10],
        //timeEve:[21,30],
    
        check() {
          return this.isAuto() && (valve1F.dval || valve2F.dval || valveLD.dval);
        },
    
        start() {
          const humHST  = 5;
          const CO2HST  = 100;
          //let date = new Date();
          //curTime[0] = date.getHours();
          //curTime[1] = date.getMinutes();
          //this.log('Current time='+this.curTime.join(':'));
    
          //Если ночное время суток, то работа на скорости 1
          if(!this.getStatusTime())
          { //Если включен хотя бы один клапан
            if(valve1F.dval || valve2F.dval || valveLD.dval)
            { //Включаем клапаны на входе и выходе
              if(!valveIN.dval || !valveOUT.dval)
              { this.valveOn();
                //Задержка для открытия клапанов и включение скорости 1
                this.startTimer("OnNight", 20, "speed1");
              }
              else this.speed1();
            }
            else
            { this.turnOff();
            }
          }
          //Если дневное время, то работаем на скорости в зависимости от включенных клапанов
          else
          { //Если включен хотя бы один клапан
            if(valve1F.dval || valve2F.dval || valveLD.dval)
            { //Включаем клапаны на входе и выходе
              if(!valveIN.dval || !valveOUT.dval)
              { this.valveOn();
                //Задержка для открытия клапанов и включение нужной скорости
                this.startTimer("OnDay", 20, "speedChange");
              }
              else this.speedChange();
            }
            else
            { this.turnOff();
            }
          }
    
          //Условия открытия/закрытия клапана первого этажа
          if(hum_guest > 60+humHST || co2_guest > 800+CO2HST)
          { if(!valve1F.dval) this.do(valve1F, "on");
          } 
          else
          { if(hum_guest < 60-humHST || co2_guest < 800-CO2HST) 
            { if(valve1F.dval) this.do(valve1F, "off");
            }
          }
    
          //Условия открытия/закрытия клапана второго этажа
          if(hum_bedroom_SE > 60+humHST || hum_bedroom_NE > 60+humHST || hum_bedroom_SW > 60+humHST ||
             co2_bedroom_SE > 800+CO2HST || co2_bedroom_NE > 800+CO2HST || co2_bedroom_SW > 800+CO2HST)
          { if(!valve2F.dval) this.do(valve2F, "on");
            if(!this.getStatusTime()) this.startTimer("T1", 3600, "???");
          } 
          else
          { if(hum_bedroom_SE < 60-humHST || hum_bedroom_NE < 60-humHST || hum_bedroom_SW < 60-humHST ||
               co2_bedroom_SE < 800-CO2HST || co2_bedroom_NE < 800-CO2HST || co2_bedroom_SW < 800-CO2HST)
            { if(valve2F.dval) this.do(valve2F, "off");
            }
          }
    
          //Условия открытия/закрытия клапана прачечной и гардеробной
          if(hum_laundry > 60+humHST || hum_dressing_room > 60+humHST)
          { if(!valveLD.dval) this.do(valveLD, "on");
          }
          else
          { if(hum_laundry < 60-humHST || hum_dressing_room < 60-humHST)
            { if(valveLD.dval) this.do(valveLD, "off");
            }
          }
    
        },
    
        isAuto() {
          return (sw.hasAuto&&sw.auto || !sw.hasAuto);
        },
    
        onSw() {
          //Выключили другим способом или сбросили авто - просто выходим
          if (!sw.dval || !this.isAuto()) this.exit();
        },
    
        getStatusTime() {
          let date = new Date();
          let time = new Array(date.getHours(), date.getMinutes());
          let timeMon = new Array(07,30); //Время начала работы
          let timeEve = new Array(21,30); //Время окончания работы
          if(time[0]*60 + time[1] >= timeMon[0]*60 + timeMon[1] && time[0]*60 + time[1] <= timeEve[0]*60 + timeEve[1])
            return 1; //Текущее время совпадает с рабочим диапазоном времени
          else
            return 0; //Текущее время НЕ совпадает с рабочим диапазоном времени
        },
    
        valveOn() {
          if(!valveIN.dval)
          this.do(valveIN, "on");
          if(!valveOUT.dval)
          this.do(valveOUT, "on");
        },
    
        valveOff() {
          if(valveIN.dval)
          this.do(valveIN, "off");
          if(valveOUT.dval)
          this.do(valveOUT, "off");
        },
    
        speedChange() {
          //Переключение скоростей
          if(valve1F.dval && valve2F.dval || valve1F.dval && valveLD.dval || valve2F.dval && valveLD.dval) this.speed2();  //Включено два из всех клапанов
          else if(valve1F.dval && valve2F.dval && valveLD.dval) this.speed3();                                            //Включено три из всех клапанов
          else this.speed1();                                                                                           //Включен один из всех клапанов
        },
    
        speed1() {
          if(vent_s2.dval)
          this.do(vent_s2, "off");
          if(vent_s3.dval)
          this.do(vent_s3, "off");
          if(!vent_s1.dval)
          this.do(vent_s1, "on");
        },
    
        speed2() {
          if(vent_s1.dval)
          this.do(vent_s1, "off");
          if(vent_s3.dval)
          this.do(vent_s3, "off");
          if(!vent_s2.dval)
          this.do(vent_s2, "on");
        },
    
        speed3() {
          if(vent_s1.dval)
          this.do(vent_s1, "off");
          if(vent_s2.dval)
          this.do(vent_s2, "off");
          if(!vent_s3.dval)
          this.do(vent_s3, "on");
        },
    
        turnOffVent() {
          if(vent_s1.dval)
          this.do(vent_s1, "off");
          if(vent_s2.dval)
          this.do(vent_s2, "off");
          if(vent_s3.dval)
          this.do(vent_s3, "off");
        },
    
        turnOff() {
          this.valveOff();
          this.startTimer("Off", 20, "turnOffVent");
        }
    };
    
    

    Еще компилятор ругается вот так:
    Сценарии_Ошибка_в_скрипте_ПВУ.png

    UPD1: как я понял после сохранения скрипта его надо остановить и запустить. Тогда он работает по моим 3-м триггерам. Пока не понимаю почему:

    1. Не включает 3-ю скорость когда включены все 3 приточных клапана.

    2. Не выключает вентиляторы и входной/выходной клапаны, когда выключаю все приточные.



  • Вопросов много, начнем с конца
    @Alex_Jet:

    Пока не понимаю почему:

    1. Не включает 3-ю скорость когда включены все 3 приточных клапана.

    Потому что если включены 3 клапана, первое условие тоже выполняется!!! И запускается this.speed2();

    speedChange() {
          //Переключение скоростей
          if(valve1F.dval && valve2F.dval || valve1F.dval && valveLD.dval || valve2F.dval && valveLD.dval) this.speed2();  //Включено два из всех клапанов
          else if(valve1F.dval && valve2F.dval && valveLD.dval) this.speed3();   //Включено три из всех клапанов
          else this.speed1();    //Включен один из всех клапанов
        },
    
    
    

    Могу предложить как вариант

    speedChange() {
        let sumOpenValues = valve1F.dval + valve2F.dval +valveLD.dval;
        //Переключение скоростей
        switch (sumOpenValues) {
          case 3: this.speed3();  break;
          case 2: this.speed2();  break;
          case 1: this.speed1();  break;
      }	
    }
    
    
    

    @Alex_Jet:

    Но я не до конца понимаю как работает check - он должен проверять какие-то свойства датчиков/актюаторов и если они true, то выполняется start???

    Да, примерно так.

    Когда запускается сценарий, сразу выполняется start.

    Но КОГДА запускается сценарий?

    Вариант 1. Если в сценарии задать функцию check, в которой проверяются свойства устройств (аналог start:if в Berry), то

    сценарий будет запущен, если условие стало истинным (check возвращает true)

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

    Например, ваш сценарий управления клапанами по датчикам влажности

    const valveLD = Device("VALVE_AIR5");   //Клапан прачечной
    const hum_laundry       = Device("SHUMIDITY1_07");
    const hum_dressing_room = Device("SHUMIDITY1_08");
    
    const script = {
    
       check() {
          const humHST  = 5;
          const CO2HST  = 100;
          return ((hum_laundry > 60+humHST || hum_dressing_room > 60+humHST)&&(valveLD.dval == 0)
                 || (hum_laundry < 60-humHST && hum_dressing_room < 60-humHST)&&(valveLD.dval == 1));
        },
        start() {
           if(valveLD.dval == 0) this.do(valveLD, "on") else this.do(valveLD, "off");
       }
    }  
    
    
    

    В check проверяем, что влажность высокая хотя бы в одном из помещений и при этом клапан закрыт,

    или влажность низкая в обоих помещениях и при этом клапан открыт.

    Только в этом случае сценарий будет запущен, и в start остается только выполнить само переключение.

    Вариант 2. Можно объявить устройства-триггеры напрямую как DeviceT.

    Тогда сценарий будет запущен по любому событию устройств-триггеров.

    А условие проверять уже внутри сценария

    const valveLD = DeviceT("VALVE_AIR5");   
    const hum_laundry       = DeviceT("SHUMIDITY1_07");
    const hum_dressing_room = DeviceT("SHUMIDITY1_08");
    
    const script = {
        start() {
          const humHST  = 5;
          const CO2HST  = 100;
         if ((hum_laundry > 60+humHST || hum_dressing_room > 60+humHST)&&(valveLD.dval == 0)) this.do(valveLD, "on");
         if (hum_laundry < 60-humHST && hum_dressing_room < 60-humHST)&&(valveLD.dval == 1)) this.do(valveLD, "off");
       }
    }  
    
    
    

    Датчики, очевидно, являются триггерами.

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

    Такой сценарий покажет очень большое количество запусков, а фактические переключения будут происходить, конечно, значительно реже.

    Вариант 3. Сценарий не имеет устройств-триггеров, не объявлена функция check - такой сценарий считается "интерактивным" (также как в Berry) . Его можно запустить с кнопки, по расписанию и т д



  • Продолжение про ПВУ и долгоиграющие сценарии 🙂

    Допустим, сценарий запустился, выполнилась функция start. Что дальше? А дальше обычно сценарий завершается и находится в неактивном состоянии, пока не будет опять запущен при выполнении условия check (или по событиям устройств-триггеров DeviceT).

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

    Но если внутри функции start взвести таймеры и/или определить слушателя событий устройства, то сценарий остается активным.

    В это время он не будет запускаться при выполнении условия check. Он должен сам слушать нужные события "изнутри" и реагировать должным образом.

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

    @Alex_Jet:

    Да, кстати, сценарий надо как-то принудительно вводить в эксплуатацию (Запустить сценарий)? В каком случае он останавливается (отладчик пишет "mRecuperatorBySensors Now is NOT active.")?

    Нет, вводить в эксплуатацию не нужно. "Запустить сценарий" в разделе Рабочие сценарии - это техническая операция, позволяющая запустить сценарий без триггеров и без проверки check в целях отладки при разработке сценария. Как теперь должно быть ясно, сценарий останавливается, если у него нет активных процедур. Если таймер взведен и отработал, то сценарий завершится.

    Если же сценарий активен, в отладчике будет видно, какие таймеры и слушатели в работе.

    @Alex_Jet:

    В общем сделал автономный сценарий управления ПВУ. По задумке он должен работать когда мой SWITCH1 имеет режим AUTO (в другой теме отписался об этом) - это состояние 1 переключателя.

    Идея сценария такова - если включен хоть один приточный клапан в доме (их 3 штуки), то если не включены - включаем входной и выходной клапаны, а в зависимости от времени суток и количества включенных приточных клапанов выбираем скорость работы вентиляторов ПВУ….

    Попробуем составить план такого сценария:

    1. Сценарий будет запускаться, когда нажата кнопка АВТО на устройстве SWITCH1 (реализация автоматического алгоритма) и завершаться при нажатии на другие кнопки (ручное управление) И пусть здесь АВТО не означает ничего кроме нажатия на кнопку АВТО 🙂 Уберите "Имеет автоматический режим" для переключателя. Он же сам по себе не должен переключаться

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

    И главное - определить слушателей для приточных клапанов и для SWITCH1:

    Слушатель "слушает" события устройства изнутри сценария и запускает обработчик, если событие произошло.

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

    this.addListener(sw1, "onSw");

    this.addListener(valve1F, "valveControl");

    this.addListener(valve2F, "valveControl");

    this.addListener(valveLD, "valveControl");

    3. В обработчике для SWITCH1 - выполнить выход из сценария, если нажата не кнопка АВТО

    onSw() {
       if (sw1.dval > 0) this.exit();
    }
    
    
    

    4. В обработчиках для приточных клапанов - переключать скорости как задумано.

    А сами приточные клапаны будут переключаться другими сценариями или может быть даже вручную - в этом сценарии это не важно.

    @Alex_Jet:

    Автоматическое управление приточными клапанами по датчикам влажности/СО2 скорее всего надо вынести в отдельные сценарии

    Да, совершенно верно.

    В целом, как-то так:

    /**
    * @name Автоматическое управление рекуператором
    * @desc  
    */
    
    const sw = Device("SWITCH1");
    const vent_s1 = Device("VENT_PVU1");    //Скорость 1 вентиляторов
    const vent_s2 = Device("VENT_PVU2");    //Скорость 2 вентиляторов
    const vent_s3 = Device("VENT_PVU3");    //Скорость 3 вентиляторов
    const valveIN = Device("VALVE_AIR1");   //Клапан на входе
    const valveOUT = Device("VALVE_AIR2");  //Клапан на выходе
    const valve1F = Device("VALVE_AIR3");   //Клапан первого этажа
    const valve2F = Device("VALVE_AIR4");   //Клапан второго этажа
    const valveLD = Device("VALVE_AIR5");   //Клапан прачечной
    
    const script = {
    
        check() {
          return (sw.dval == 0); // выбрана верхняя кнопка переключателя АВТО 
        },
    
        start() {
        	this.valveControl();
        	this.addListener(sw1, "onSw");
    	this.addListener(valve1F, "valveControl");
    	this.addListener(valve2F, "valveControl");
    	this.addListener(valveLD, "valveControl");
        },
    
        valveControl() {
       	if (this.sumOpenValves() > 0) {
             //Включаем клапаны на входе и выходе
              if(valveIN.dval==0 || valveOUT.dval==0) { 
                 this.valveOn();
                //Задержка для открытия клапанов 
                this.startTimer("T1", 20, "speedControl");
              } else this.speedControl();
       	} else {
         	  this.turnOff();
      	}
         },
    
         sumOpenValves()  {
          return  valve1F.dval + valve2F.dval +valveLD.dval;
         }, 
    
        speedControl() {
            if(!this.getStatusTime()) {
                 this.speed1(); 
           } else  this.speedChange();
        },
    
        getStatusTime() {
          let date = new Date();
          let time = new Array(date.getHours(), date.getMinutes());
          let timeMon = new Array(07,30); //Время начала работы
          let timeEve = new Array(21,30); //Время окончания работы
          if(time[0]*60 + time[1] >= timeMon[0]*60 + timeMon[1] && time[0]*60 + time[1] <= timeEve[0]*60 + timeEve[1])
            return 1; //Текущее время совпадает с рабочим диапазоном времени
          else
            return 0; //Текущее время НЕ совпадает с рабочим диапазоном времени
        },
    
        valveOn() {
          this.do(valveIN, "on");
          this.do(valveOUT, "on");
        },
    
        valveOff() {
          this.do(valveIN, "off");
          this.do(valveOUT, "off");
        },
    
        speedChange() {
        	switch (this.sumOpenValves() ) {
          		case 3: this.speed3();  break;
          		case 2: this.speed2();  break;
          		case 1: this.speed1();  break;
      	}	
        },
    
        speed1() {
          this.do(vent_s2, "off");
          this.do(vent_s3, "off");
          this.do(vent_s1, "on");
        },
    
        speed2() {
          this.do(vent_s1, "off");
          this.do(vent_s3, "off");
          this.do(vent_s2, "on");
        },
    
        speed3() {
          this.do(vent_s1, "off");
          this.do(vent_s2, "off");
          this.do(vent_s3, "on");
        },
    
        turnOffVent() {
          this.do(vent_s1, "off");
          this.do(vent_s2, "off");
          this.do(vent_s3, "off");
        },
    
        turnOff() {
          this.valveOff();
          this.startTimer("Off", 20, "turnOffVent");
        },
    
        onSw() {
       	if (sw1.dval > 0) this.exit();
       }
    };
    
    
    

    P.S. Как видите, можно не проверять состояние реле перед выполнением команды. Если состояние уже целевое, команда на плагин выдаваться не будет.

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



  • @intrapro:

    P.S. Как видите, можно не проверять состояние реле перед выполнением команды. Если состояние уже целевое, команда на плагин выдаваться не будет.

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

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

    PHP я явно знаю лучше чем JS…ну а Вы еще отлично знаете структуру вашей системы.

    В функции выбора скоростей затупил:( Мою задумку Вы поняли почти на 100% (единственное, что я на переключателе перенес кнопку АВТО с 0 на 1, на 0 теперь "Выключено"). По проверке состояний реле - опять же сделал выводы зная особенности Berry, но еще не подключал к Cherry реальные MegaD и не смотрел что там происходит через отладчик...

    Еще раз спасибо! Сделаю нужные сценарии и потестирую их на визуализации. Сервер в процессе подготовки для перехода с эксплуатирующейся Berry на Cherry.



  • @Alex_Jet:

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

    Вам спасибо за желание докопаться до сути 🙂

    Действительно, документации у нас не хватает. Надеюсь, скоро соберем все и выложим в Wiki.

    Успешного внедрения!



  • Дальше настраиваю сервер. Надо сделать сценарии для отопления "Эконом", "День", "Ночь".

    По сути это изменение уставок температуры у датчиков, у которых есть уставки.

    Как их менять "на лету"? Делаем сценарий без каких-либо триггеров и привязываем их к нужным кнопкам?

    А в сценарии прописываем типа:

    const script = {
        start() {
          const temp = 25;
          this.do(temp_guest.defval, temp);
        } 
    };
    
    

    В Berry было удобно, что значение для каждого режима сохранялось, когда пользователь сам настраивал температуру. В Cherry так получится сделать?

    PS: если делаешь шаг шкалы 0,5, то это выполняется только если менять уставку + и -. Если двигать ползунок, то шаг = 1.



  • Не дождался ответа - разобрался сам. Надо сделать так:

    this.assign(temp_guest, "defval", temp);
    
    

    Остается вопрос №2:
    @Alex_Jet:

    В Berry было удобно, что значение для каждого режима сохранялось, когда пользователь сам настраивал температуру. В Cherry так получится сделать?

    И еще вопрос - можно как-то в цикле перебрать сами устройства (ST_HEATING1_0x)???



  • @intrapro:

    Вам спасибо за желание докопаться до сути 🙂

    В предыдущих постах снова задал кучу вопросов - чтобы они не потерялись процитировал Вас.

    Сделал скрипт для имитации изменения значений аналоговых сенсоров:

    /** 
    * @name Эмуляция изменения температуры  
    * @desc  
    */
    
    const actor = Device("ActorD", "Актуатор", [
      {"name":"porog_0", "note":"Установить по on", "type":"number", "val":25},
      {"name":"porog_1", "note":"Установить по off", "type":"number", "val":0}
      ]);
    
    const sensor = Device("SensorA", "Сенсор");
    
    const script = {
        check() {
            return (actor.dval || !actor.dval);
        },
    
        start() {
          //const temp = 0;
          this.assign(sensor, "aval", (actor.dval) ?  actor.porog_1 : actor.porog_0);
        } 
    };
    
    

    Отвязал сенсор от канала контроллера, но изменения значения в вебе не происходит. В журнале сенсора вот такое:
    Сценарии_Изменение_значения_аналогового_сенсора_Ошибка.png



  • @Alex_Jet:

    Сделал скрипт для имитации изменения значений аналоговых сенсоров:

    Отвязал сенсор от канала контроллера, но изменения значения в вебе не происходит. В журнале сенсора вот такое:

    Скрипт рабочий. Скорее всего, значение не принимается, так как для датчика температуры установлен диапазон Min-Max и флаги:

    -Не принимать значения вне диапазона

    -Устанавливать ошибку при выходе из диапазона.

    И диапазон не включает 0-10
    Настройка_аналогового_датчика.png

    Другие вопросы не такие простые, отвечу позже. Встроенного решения, как в Berry, нет.

    А журнал в последнем релизе более симпатичный 😉
    assign_aval.png



  • @intrapro:

    Скрипт рабочий. Скорее всего, значение не принимается, так как для датчика температуры установлен диапазон Min-Max и флаги:

    -Не принимать значения вне диапазона

    -Устанавливать ошибку при выходе из диапазона.

    И диапазон не включает 0-10

    Да, Вы были правы! Я снова с просьбами о помощи:)

    1. Бьюсь со сценарием включения/отключения актюатора по аналоговому датчику температуры (эмулирую значения 24,8 и 25,2; уставка температуры 25, гистерезис 0,1). Не могу побороть проблему - при понижении температуры актюатор включается, а при повышении - не выключается! Скрипт:

    /** 
    * @name Актюатор ТП по датчику температуры  
    * @desc Сценарий работает при включеном режиме АВТО у актюатора
    *   При увеличении температуры выше целевого значения на величину гистерезиса актюатор включается, 
    *   при уменьшении температуры ниже целевого значения на величину гистерезиса - выключается.
    *   В качестве целевого значения берется уставка с датчика температуры (dt.defval)
    *   В качестве гистерезиса берется пользовательская настройка актюатора (act.hist)
    */
    
    const act = Device("ActorD", "Актюатор ТП", [
      {"name":"hist", "note":"Гистерезис включения/отключения актюатора, °C", "type":"number", "val":0.1}
      ]);
    
    const dt = Device("SensorA", "Датчик температуры");
    
    const script = {
        check() {
          return act.auto && ( !act.dval&&(dt.aval <= dt.defval-act.hist) || act.dval&&(dt.aval >= dt.defval+act.hist) );
        },
    
        start() {
          if(!act.dval) {
            this.do(act, "on");
          }
          else {
            this.do(act, "off");
          }
        }
    };
    
    

    Лог отладчика - почему во втором случае Check выдает false - я не понимаю!

    24.09 11:51:37.416 S1(ACTOR_HEATING1_01,ST_HEATING1_01) Check(ST_HEATING1_01) => true
    24.09 11:51:37.416 S1(ACTOR_HEATING1_01,ST_HEATING1_01) Started
    24.09 11:51:37.417 S1(ACTOR_HEATING1_01,ST_HEATING1_01) do ACTOR_HEATING1_01 on 
    24.09 11:51:37.418 S1(ACTOR_HEATING1_01,ST_HEATING1_01) Stopped
    24.09 11:51:47.394 S1(ACTOR_HEATING1_01,ST_HEATING1_01) Check(ST_HEATING1_01) => false
    
    

    2. У меня актюаторы НО. Чтобы когнитивный диссонанс не устраивать в скриптах, я так понимаю, в соответствующих каналах плагина MegaD нужно активировать чек-бокс "Ивертировать выходное значение"?

    3. Еще что заметил - этот скрипт отключает режим АВТО у актюатора! Как сделать чтобы режим АВТО не отключался скриптом (кроме установки в свойствах актюатора 00:00:00 для включения/отключения режима АВТО)?

    4. И научите как же все-таки пользоваться логированием?
    @Alex_Jet:

    @intrapro:

    Для отладки (и записи в журнал) можно использовать this.log():

    Пока не разобрался как же использовать log. Что он будет логировать? - работу функций или в его тело нужно вставлять какие-то комментарии?



  • @Alex_Jet:

    Лог отладчика - почему во втором случае Check выдает false - я не понимаю!

    Еще что заметил - этот скрипт отключает режим АВТО у актюатора!

    Check выдает false именно потому, что режим АВТО при операции on/off отключается.

    @Alex_Jet:

    Как сделать чтобы режим АВТО не отключался скриптом (кроме установки в свойствах актюатора 00:00:00 для включения/отключения режима АВТО)?

    Нужно использовать команды aon/aoff (как в Berry)

    this.do(act, "aon");
    
    

    @Alex_Jet:

    И научите как же все-таки пользоваться логированием?

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

    this.log( "Температура "+dt.aval+". Уставка "+dt.defval);
    
    


  • @intrapro:

    Check выдает false именно потому, что режим АВТО при операции on/off отключается.

    Я тоже так думал и поэтому вручную в настройках актюатора установил 00:00:00 чтобы АВТО режим не отключался!

    В итоге с помощью логирования сейчас вот такую ерунду выяснил:

    24.09 13:34:47.890 S1(ACTOR_HEATING1_01,ST_HEATING1_01) log: Act.auto - 1; Act.dval - 0; Dt.aval = 24.8; Porog = 24.9
    24.09 13:34:47.895 S1(ACTOR_HEATING1_01,ST_HEATING1_01) Check(ST_HEATING1_01) => true
    24.09 13:34:47.895 S1(ACTOR_HEATING1_01,ST_HEATING1_01) Started
    24.09 13:34:47.896 S1(ACTOR_HEATING1_01,ST_HEATING1_01) do ACTOR_HEATING1_01 aon 
    24.09 13:34:47.897 S1(ACTOR_HEATING1_01,ST_HEATING1_01) Stopped
    24.09 13:35:05.407 S1(ACTOR_HEATING1_01,ST_HEATING1_01) log: Act.auto - 1; Act.dval - 1; Dt.aval = 25.2; Porog = 25.000.1
    24.09 13:35:05.412 S1(ACTOR_HEATING1_01,ST_HEATING1_01) Check(ST_HEATING1_01) => false
    
    

    Почему во втором случае Porog (dt.defval + act.hist) = 25.000.1? Почему не суммируется?



  • @Alex_Jet:

    Почему во втором случае Porog (dt.defval + act.hist) = 25.000.1? Почему не суммируется?

    JavaScript, он такой. Исправим у себя преобразование типов параметров сценария, недоглядели 😞

    Вам же достаточно сделать Number(dt.defval)

    Ну, вы наверное уже и сами догадались 🙂



  • @intrapro:

    @Alex_Jet:

    Почему во втором случае Porog (dt.defval + act.hist) = 25.000.1? Почему не суммируется?

    JavaScript, он такой. Исправим у себя преобразование типов параметров сценария, недоглядели 😞

    Вам же достаточно сделать Number(dt.defval)

    Ну, вы наверное уже и сами догадались 🙂

    У меня не такой огромный опыт в JS как у Вас. Поэтому для меня это было загадкой.

    Да, теперь работает. Единственный вопрос еще: как правильнее и чем это грозит:

    1. Проверка в check "короткого" условия return act.auto && dt.aval с последующей проверкой условий в start

    2. Проверка в check "длинного" условия return act.auto && dt.aval ( !act.dval&&(dt.aval <= dt.defval - act.hist) || act.dval&&(dt.aval >= dt.defval + act.hist) )

    PS: расскажите в теме про CCTV про видеоалерты - есть ли уже такая возможность? и как настраивать.



  • @Alex_Jet:

    Единственный вопрос еще: как правильнее и чем это грозит:

    1. Проверка в check "короткого" условия return act.auto && dt.aval с последующей проверкой условий в start

    2. Проверка в check "длинного" условия return act.auto && dt.aval ( !act.dval&&(dt.aval <= dt.defval - act.hist) || act.dval&&(dt.aval >= dt.defval + act.hist) )

    Да ничем не грозит, дело вкуса.

    Второй вариант предпочтительнее, т к :

    1. Меньше количество операций сравнения.

    Условные выражения в JS работают до первого ложного условия - т е если act.auto=0 - больше ничего проверяться не будет в обоих вариантах, хотя на вид условие по 2 варианту длинное.

    А если act.auto =1, то по 1 варианту сначала будет проверка dt.aval в check (Вам пришлось вставить, чтобы датчик был триггером), а потом будет проверка в start

    2. Меньше будет зафиксировано запусков сценария.

    По 1 варианту запуск будет фиксироваться при каждом изменении тем-ры, а если она у вас с десятыми (или даже с сотыми), то количество запусков быстро перевалит за 1000. А на фиксацию запуска сценария также тратятся ресурсы.

    По 2 варианту запуск сценария означает, что выполняется регулирование.

    Но работать будут оба варианта.

    @Alex_Jet:

    PS: расскажите в теме про CCTV про видеоалерты - есть ли уже такая возможность? и как настраивать.

    Запланировано, но пока не реализовано



  • @intrapro:

    Да ничем не грозит, дело вкуса.

    Ок, понятно. По актюаторам еще можно комментарий?
    @Alex_Jet:

    2. У меня актюаторы НО. Чтобы когнитивный диссонанс не устраивать в скриптах, я так понимаю, в соответствующих каналах плагина MegaD нужно активировать чек-бокс "Ивертировать выходное значение"?



  • @Alex_Jet:

    У меня актюаторы НО. Чтобы когнитивный диссонанс не устраивать в скриптах, я так понимаю, в соответствующих каналах плагина MegaD нужно активировать чек-бокс "Ивертировать выходное значение"?

    Да, должно работать (примерно как было в Berry)



  • @Alex_Jet:

    @intrapro:

    Можно еще проще - привязать бинарный актуатор, а интерактивные операции для него отключить

    В принципе эта идея сработала. При поднесении нужного ключа к считывателю актуатор меняет свое состояние. Отталкиваясь от его состояния можно написать сценарий установки/снятия с охраны. Единственный неопределенный момент в том, что когда MegaD опрашиваем командой cmd=all, то канал, соответствующий считывателю находится в неопределенном состоянии (ранее писал, что не верно под неопределенным состоянием считать 0). Возникает вопрос - после перезагрузки MegaD в каком состоянии окажется привязанный к этому каналу актуатор (ну или сенсор, если добавите ему обработку toggle). Или все же это правильно - считать за 0/OFF неопределенное состояние порта?

    Написал сценарий постановки дома на охрану. Button - это актюатор, который привязан ко входу MegaD, к которому подключен считыватель. Кроме этого, из этого устройства сделан индикатор постановки на охрану в строке состояния и две кнопки "Постановка/Снятие с охраны" (для одной в свойствах выбрано on, для другой - off). Lamp - это сигнальная лампа (загорается при постановке), Ring - это сирена.

    const Button = DeviceT("ACTOR_SECURITY_SYS01");
    const Lamp = Device("ACTOR_SECURITY_SYS02");
    const Ring = Device("ACTOR_SECURITY_SYS03");
    
    const script = {
        start() {
          this.log(Button.name+ "=" +Button.dval);
    
          if(Button.dval) {
            this.do(Lamp, "on");  //Включаем лампу
            this.do(Ring, "on");  //На секунду срабатывает сирена
            this.startTimer("T1", 0.1, "ringOff");
          }
          else {
            this.do(Lamp, "off");  //Выключаем лампу
            this.do(Ring, "on");  //На секунду срабатывает сирена
            this.startTimer("T1", 0.1, "ringOff");
          }
    
        },
    
        // Функция, которая сработает, когда таймер досчитает - отключаем и выходим
        ringOff() {
          this.do(Ring, "off");
          this.exit();
        }
    };
    
    

    Суть в том, что при сработке считывателя все работает хорошо (актюатор меняет свое состояние и скрипт все верно отрабатывает). А вот при нажатии кнопок работает не верно - нажимаю кнопку "Поставить на охрану" (on) индикатор охраны становится активным, но реле сигнальном лампы не срабатывает, а в отладчике скрипта видно, что актюатор почему-то не "встал":

    27.10 23:52:44.905 Trigger ACTOR_SECURITY_SYS01
    27.10 23:52:44.905 Started
    27.10 23:52:44.906 log: Актуатор постановки на охрану=0
    27.10 23:52:44.912 do ACTOR_SECURITY_SYS02 off 
    27.10 23:52:44.916 do ACTOR_SECURITY_SYS03 on 
    27.10 23:52:44.918 start timer T1 for 0.1 sek
    27.10 23:52:45.026 Done timer T1
    27.10 23:52:45.026 exec function ringOff
    27.10 23:52:45.027 do ACTOR_SECURITY_SYS03 off 
    27.10 23:52:45.029 exit
    27.10 23:52:45.029 Stopped
    
    

    Не могу понять почему так происходит? Надо таймер взводить перед проверкой состояния актюатора?

    Up1: методом итераций (сначало сделал таймер на 0,1 секунду с переходом в main), потом чтобы при повторном нажатии той же кнопки (когда взято под охрану, но еще раз нажимают кнопку "Взять под охрану") не срабатывала сирена решил проверять совпадает текущее значение актуатора с предыдущем, потом судя по логам работы решил убрать таймер. И получилось вот что:

    const Button = DeviceT("ACTOR_SECURITY_SYS01"); //Актуатор постановки на охрану
    const Lamp = Device("ACTOR_SECURITY_SYS02");    //Сигнальная лампа
    const Ring = Device("ACTOR_SECURITY_SYS03");    //Сирена
    var Status;
    
    const script = {
        start() {
          //this.log(Button.name+ " = " +Button.dval);
    
          this.main();
          Status = Button.dval;
        },
    
        // Основная функция постановки на охрану
        main() {
          //this.log(Button.name+ " = " +Button.dval);
    
          //Если предыдущее состояние актуатора совпадает с текущим, то выходим.
          if(Status != Button.dval) {
            if(Button.dval) this.do(Lamp, "on");    //Включаем лампу
            else            this.do(Lamp, "off");   //Выключаем лампу
            this.do(Ring, "on");  //Срабатывает сирена
            this.startTimer("T1", 0.1, "ringOff");
          }
        },
    
        // Функция отключения сирены с выходом из скрипта
        ringOff() {
          this.do(Ring, "off");
          this.exit();
        }
    };
    
    

    Парадокс в том, что если исключить Status = Button.dval; или перенести до this.main(); то ничего не работает. А именно в такой последовательности - работает. Однако моя логика не понимает в чем суть…



  • @Alex_Jet:

    Написал сценарий постановки дома на охрану. Button - это актюатор, который привязан ко входу MegaD, к которому подключен считыватель. Кроме этого, из этого устройства сделан индикатор постановки на охрану в строке состояния и две кнопки "Постановка/Снятие с охраны" (для одной в свойствах выбрано on, для другой - off)…..

    А канал считывателя MegaD отрабатывается как toggle?

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

    Можете посмотреть журнал устройства (при длинном нажатии).

    Если отключить Button от канала, то виртуальное устройство будет правильно отрабатывать кнопки по первому варианту.

    Выход - создать отдельный виртуальный актуатор Постановка/Снятие (Guard, например).

    Он выводится на индикацию, он переключается кнопками. По его событиям включается сигнальная лампа и вообще все, что нужно сделать при постановке (снятии). Его состояние вы можете учитывать в других сценариях (датчики, герконы на охране и т д, ну вы знаете 🙂

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


Log in to reply