Сценарии в intraHouse Cherry



  • Добрый день!

    @sergeyygr:

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

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

    Но в данном случае решение есть - можно в текст включить данные устройства в любой комбинации

    sensor.name - название датчика

    sensor.nameOf("place") - название уровня

    sensor.nameOf("room") - название зоны

    sensor.nameOf("subs") - название подсистемы

    sensor.getPlaceDesc() - уровень и зона вместе, разделяются символом /: "1 этаж/Котельная"

    Если уровня или зоны нет - выдается то что есть, без разделителя

    Например:

      this.info("email", "OWNER", "Тревога! "+ sensor.nameOf("place")+" "+sensor.nameOf("room")+". Сработал "+sensor.name)
    
    
    

    Сформирует сообщение: "Тревога! 1 этаж Холл. Сработал Датчик движения"

    Или

      this.info("email", "OWNER", actor2.nameOf("place") + ": Включена сирена! Движение "+ sensor.getPlaceDesc())
    
    
    

    Сформирует сообщение: "Гараж: Включена сирена! Движение 1 этаж/Коридор"



  • @intrapro:

    Добрый день!

    @sergeyygr:

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

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

    Но в данном случае решение есть - можно в текст включить данные устройства в любой комбинации

    sensor.name - название датчика

    sensor.nameOf("place") - название уровня

    sensor.nameOf("room") - название зоны

    sensor.nameOf("subs") - название подсистемы

    sensor.getPlaceDesc() - уровень и зона вместе, разделяются символом /: "1 этаж/Котельная"

    Если уровня или зоны нет - выдается то что есть, без разделителя

    Например:

    >   this.info("email", "OWNER", "Тревога! "+ sensor.nameOf("place")+" "+sensor.nameOf("room")+". Сработал "+sensor.name)
    > 
    > 
    

    Сформирует сообщение: "Тревога! 1 этаж Холл. Сработал Датчик движения"

    Или

    >   this.info("email", "OWNER", actor2.nameOf("place") + ": Включена сирена! Движение "+ sensor.getPlaceDesc())
    > 
    > 
    

    Сформирует сообщение: "Гараж: Включена сирена! Движение 1 этаж/Коридор"

    А нет никакой инструкции вцелом по работе с плагином email? Задумка отправлять в заданный день месяца показания счётчиков на емэйл в УК и себе, но не представляю как это реализовать.



  • Спасибо!



  • Недоступен для скачивания сценарий "Управление батареями отопления по датчикам температуры"…

    Расскажите про добавление в сценарии пользовательских настроек. Например, делаю так:

    const heater = Device("ActorD", "Нагреватель", [
      {"name":"hist", "note":"Гистерезис включения/отключения актюатора, °C", "type":"number", "val":0.2}
      ]);
    
    

    Но какое значение может быть в type чтобы hist можно было менять с точностью 0,05 градуса, например.



  • @Alex_Jet:

    Недоступен для скачивания сценарий "Управление батареями отопления по датчикам температуры"…

    Проверим

    @Alex_Jet:

    Расскажите про добавление в сценарии пользовательских настроек. Например, делаю так:

    > const heater = Device("ActorD", "Нагреватель", [
    >   {"name":"hist", "note":"Гистерезис включения/отключения актюатора, °C", "type":"number", "val":0.2}
    >   ]);
    > 
    

    Но какое значение может быть в type чтобы hist можно было менять с точностью 0,05 градуса, например.

    Для "type":"number" в пользовательском интерфейсе можно ввести вручную любое число, в том числе с любым числом знаков после запятой.

    • и - использовать не обязательно. Планируем шаг для +- также сделать настаиваемыми из сценария, но пока этого нет 😞

    Другие варианты:

    "type":"cb" - checkbox,

    "type":"time" - настройка времени - часы, минуты, секунды



  • Добрый день всем!!!

    Реализовал в своем сценарии передачу сообщения на e-mail при работке датчика движения (this.info("email", "OWNER", "Тревога! "+ sensor.nameOf("place")+" "+sensor.nameOf("room")+". Сработал "+sensor.name)). Усложняем задачу!

    В контроллере MegaD-2561 реализована возможность передачи и приема SMS сообщений https://ab-log.ru/page.php?Cat=110&ID=195&q=sim800.

    Из темы:

    Входящее SMS-сообщение передается на сервер следующим образом.

    /script.php?sms_phone=+79000001234&sms_text=hello

    Сервер также может через контроллер отправлять произвольные сообщения. Для этого необходимо отправить запрос следующего вида.

    http://192.168.0.14/sec/?sms=Alarm!&phone=+79000000000

    Подскажите пожалуйста как должна выглядеть строка в сценарии, что бы сообщение было продублировано на контроллер, а тот в свою очередь выслал бы это сообщение по SMS?



  • @sergeyygr:

    Добрый день всем!!!

    Реализовал в своем сценарии передачу сообщения на e-mail при работке датчика движения (this.info("email", "OWNER", "Тревога! "+ sensor.nameOf("place")+" "+sensor.nameOf("room")+". Сработал "+sensor.name)). Усложняем задачу!

    В контроллере MegaD-2561 реализована возможность передачи и приема SMS сообщений https://ab-log.ru/page.php?Cat=110&ID=195&q=sim800.

    Из темы:

    Входящее SMS-сообщение передается на сервер следующим образом.

    /script.php?sms_phone=+79000001234&sms_text=hello

    Сервер также может через контроллер отправлять произвольные сообщения. Для этого необходимо отправить запрос следующего вида.

    http://192.168.0.14/sec/?sms=Alarm!&phone=+79000000000

    Подскажите пожалуйста как должна выглядеть строка в сценарии, что бы сообщение было продублировано на контроллер, а тот в свою очередь выслал бы это сообщение по SMS?

    Добрый день!

    Для этого есть функция сценария plugincommand(pluginID, obj), но пока плагин MegaD такую функцию не поддерживает 😞

    Добавим в ближайшее время.

    Также, конечно, очень интересная тема по приему входящих sms через MegaD.

    Планируем сделать модуль для обработки входящих сообщений (sms, telegram и др)



  • В скрипте требуется для работы оборудования только в ночное время сравнить текущее время с заданным пользователем. Как это записать? Я конечно понимаю что есть объект Date() c атрибутами getHours, getMinutes и можно условие записать как if( new Array(date.getHours(), date.getMinutes()) > new Array(21,00) ). Но проверка синтаксиса ругается на эту строчку в скрипте. Поэтому как это сделать в iH?



  • @Alex_Jet:

    В скрипте требуется для работы оборудования только в ночное время сравнить текущее время с заданным пользователем. Как это записать? Я конечно понимаю что есть объект Date() c атрибутами getHours, getMinutes и можно условие записать как if( new Array(date.getHours(), date.getMinutes()) > new Array(21,00) ). Но проверка синтаксиса ругается на эту строчку в скрипте. Поэтому как это сделать в iH?

    А для чего Array? Например, ночное время с 21 до 6.

    if ((new Date().getHours() > 21) || (new Date().getHours() < 6)) ....
    
    
    

    А лучше один раз считать, потом проверять

    let dt = new Date();
    if ((dt.getHours()>21) || (dt.getHours()<6))
    
    
    


  • @intrapro:

    @Alex_Jet:

    В скрипте требуется для работы оборудования только в ночное время сравнить текущее время с заданным пользователем. Как это записать? Я конечно понимаю что есть объект Date() c атрибутами getHours, getMinutes и можно условие записать как if( new Array(date.getHours(), date.getMinutes()) > new Array(21,00) ). Но проверка синтаксиса ругается на эту строчку в скрипте. Поэтому как это сделать в iH?

    А для чего Array? Например, ночное время с 21 до 6.

    > if ((new Date().getHours() > 21) || (new Date().getHours() < 6)) ....
    > 
    > 
    

    А лучше один раз считать, потом проверять

    > let dt = new Date();
    > if ((dt.getHours()>21) || (dt.getHours()<6))
    > 
    > 
    

    Array для того чтобы проверять часы и минуты. Иногда недостаточно только часов. У меня вот утром свет в 6.45 включается, а выключается в 7.20 когда уезжаю. Неужели проверка синтаксиса на Array ругается?



  • @Alex_Jet:

    if( new Array(date.getHours(), date.getMinutes()) > new Array(21,00) ). Но проверка синтаксиса ругается на эту строчку в скрипте. ….

    Иногда недостаточно только часов. У меня вот утром свет в 6.45 включается, а выключается в 7.20 когда уезжаю. Неужели проверка синтаксиса на Array ругается?

    Скрипт не понимает, что такое date.getHours()

    И в целом так не получится. В JS массивы так не сравнивают.

    Могу предложить такой однострочный вариант (некрасивый, но рабочий)

    if (new Date().getHours()*100+new Date().getMinutes() > 2100)
    
    
    


  • @intrapro:

    И в целом так не получится. В JS массивы так не сравнивают.

    Я не стал особо заморачиваться минимизацией кода и написал, надеюсь, понятную пользователям функцию. "Настройка" простая, использование проще некуда.

        getStatusTime() {
          let date = new Date();
          let time = new Array(date.getHours(), date.getMinutes());
          let timeMon = new Array(09,10); //Время начала работы
          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; //Текущее время НЕ совпадает с рабочим диапазоном времени
        },
    
    

    Применение:

                if(this.getStatusTime())
                this.speed1();
                else
                this.speed2();
    
    

    Вопросы:

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

    this.startTimer("T1", 20, "turnOffVent");
    
    

    2. Как в функции в таймер передать значение времени из переменной? Чего-то догадаться не могу:( отладки такой как в Chrome нет…



  • @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)???


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