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



  • @Alex_Jet, сейчас пришла мысль. А если в основной сценарий ввести слушателя нашего актуатора (или предложенного Вами канала MegaD), который будет не режим авто отключать, а блокировать датчик движения, пока этот актуатор в состоянии вкл



  • @Alex_Jet

    У меня работает такой скрипт

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

    Выключатель правда не кнопка, а переключатель. Он выведен на канал меги и подключен к объекту универсальный бинарный датчик.

    Если у вас именно кнопка, то на какое бы нажатие не была присоединена лампа (короткое, двойное, или длинное), лампа же управляется командой toggle, т.е. если включена - выключается, если выключена - включается.
    Поэтому, если лампа включилась датчиком движения, кнопкой ее можно только выключить.
    Т.е. следить за кнопкой в скрипте не нужно.

    Но если есть свободный вариант нажатия, то в "расширениях" в плагине на нужный вариант сделайте запуск скрипта, присваивающий какому нибудь актуатору значение 1 на 5 сек.
    А в основном скрипте контролируйте не изменение выключателя, как у меня, а изменение этого актуатора. Если он изменился (стал 1), выключить все таймеры, выйти из скрипта, оставив свет включенным.
    А через 5 сек этот актуатор сбросится в 0 сам, подготовившись к следующему использованию.

    Правда почему просто не делать выкл/вкл, это же не сложно? Зато есть визуальное подтверждение. В других случаях лампа как горела так и горит, и можно только верить, что таймер отключился. А это стресс. А он иммунитет подрывает.



  • В общем, мне кажется что эта идея не состоятельна:
    При управлении датчиком движения той же кнопкой отключать/включать авто режим. Скорее все нужно настраивать так - одинарное нажатие делает toggle лампы, и в скрипте отслеживается состояние датчика движения и самой лампы (то есть это стандартный сценарий, который привели в соответствующем разделе коллеги из iH). А вот двойным нажатием или удержанием кнопки можно как раз включать/выключать авто режим лампы.

    Таким образом, можем решить задачу с обоими вариантами монтажа выключателей - снаружи помещения (обычно санузел/ванная) и внутри.
    В первом случае, на входе делаем двойной клик кнопки - включаем лампу и выключаем авто-режим. Датчик движения больше ни на что не влияет. После выхода из помещения делаем также двойной клик - лампа выключается и включается у нее авто-режим.
    Во втором случае, входим в помещение, срабатывает датчик движения и включает лампу. Делаем двойной клик и выключается авто-режим у лампы, соответственно, датчик движения больше не влияет на работу лампы. При выходе также делаем двойной клик кнопки - отключается лампа и включается ее авто режим.

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

    Либо как вариант - вообще ничего не городить))))



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

    Сценарии простые. Для каждой кнопки создайте аналоговый актуатор с 4 значениями. 0, 1, 2, 3.
    А сценариями присваивайте этому актуатору значения на 1 сек.
    сценарий одинарного клика - 1, двойного - 2, длинного - 3.
    Сразу запускайте таймер на 1 сек, и после истечения возвращайте значение 0.

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

    Мне манипуляции с режимом "авто" кажутся не надежными.
    У меня у большинства устройств галочка на "есть автоматический режим" не установлена, но вся автоматика работает. С этой галочкой есть только вентиляторы. Если я включаю/выключаю их руками, автоматический режим отключается (на иконке видно таймер вместо буквы А), что не мешает тут же включать этот вентилятор из скрипта.



  • Добрый день. Вопрос, в папке проекта projects\scenes, если я правильно понял, находятся сценарии. Чем отличаются папки req и папка script ? И там и там сценарии на js, названия сценариев одинаковые



  • @fanagor Добрый день.
    Исходные сценарии находятся в папке scenes/script. Это код, который виден в редакторе.
    В папке scenes/req сценарии формируются системой в виде, готовом для запуска - обернуты в module.exports, подставлены входные параметры. Если нужно перенести сценарии, достаточно папки  script для обычных/мультисценариев. Папка diagram содержит блок-схемы. Папка req и все содержимое будет создано автоматически.



  • @intrapro, спасибо. Еще вопрос, как можно, вернее какой командой поменять активный проект ?



  • @fanagor Имя папки с активным проектом сервер считывает при старте из файла /opt/intrahouse-c/config.json.

    Поэтому есть два варианта

    1. Интерактивно через PM. Нажать кнопку Настройка (инструменты), выбрать пункт Проекты. Будут показаны проекты из папки projects.
      Щелкнуть по строчке с проектом, нажать кнопку вызова меню команд (полосатая кнопочка).
      В меню выбрать Сделать проект активным.
      Эта операция прописывает выбранный проект в config.json как активный и перезагружает сервер.
      changeProject.png

    2. Можно вручную изменить файл /opt/intrahouse-c/config.json, прописать в атрибут "project" имя проекта ( просто имя папки в projects, без пути):

    {"port":8088,"project":"your_new_project","name_service":"intrahouse-c","lang":"ru"}
    

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



  • @intrapro, супер, про вариант 2 как раз то, что спрашивал. Спасибо !



  • Добрый день. Пара вопросов.

    1. Команда this.execOS('export IAM_TOKEN=yc iam create-token') из сценария возвращает ошибку:
      18.05 15:39:07.725 Trigger LAMP_23
      18.05 15:39:07.725 Started
      18.05 15:39:07.725 execOS: yc iam create-token
      18.05 15:39:07.730 Stopped
      18.05 15:39:07.744 execOS error: Error: Command failed: yc iam create-token
      /bin/sh: 1: yc: not found

    18.05 15:39:07.744 stdout:
    18.05 15:39:07.744 stderr: /bin/sh: 1: yc: not found

    При этом из терминала все хорошо выполняется.

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


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

    Добрый день. Пара вопросов.

    1. Команда this.execOS('export IAM_TOKEN=yc iam create-token') из сценария возвращает ошибку ...
      При этом из терминала все хорошо выполняется.

    Добрый день. Здесь команда оболочки, напрямую не запустится.
    Попробуйте поместить строку в файл c расширением .sh и вызвать этот файл на выполнение:
    this.execOS ('/home/pi/myscript.sh')
    Для файла скрипта после сохранения нужно установить права на выполнение:

    chmod 777 /home/pi/myscript.sh
    
    1. Как передать значения из переменой в глобальную переменную используемую в проекте ?

    Если переменную сценария в глобальную переменную проекта, то присвоить через global.set. Если переменной нет - она будет создана

    global.set('myglobalvar', xx);  
    

    В другом сценарии можно считать:

    let myxx = global.get('myglobalvar');  
    

    Или Вы хотите результат выполнения команды execOS присвоить?



  • @intrapro да, результат команды execOS



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

    Добрый день. Здесь команда оболочки, напрямую не запустится.
    Попробуйте поместить строку в файл c расширением .sh и вызвать этот файл на выполнение:
    this.execOS ('/home/pi/myscript.sh

    тот же самый результат.

    18.05 21:35:09.144 Trigger LAMP_23
    18.05 21:35:09.144 Started
    18.05 21:35:09.145 execOS: /myscript8.sh
    18.05 21:35:09.272 Stopped
    18.05 21:35:09.305 execOS error: Error: Command failed: /myscript8.sh
    /myscript8.sh: 1: /myscript8.sh: yc: not found

    не хочет.



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

    /myscript8.sh: 1: /myscript8.sh: yc: not found

    не хочет.

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

    which  yc
    

    И посмотрите текущее значение PATH, он должен включать путь до yc

    echo $PATH
    


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

    @intrapro да, результат команды execOS

    Тогда можно без экспорта, напрямую считать результат в сценарии

    script({
        start() {
           this.execOS(`yc iam create-token`, 'saveToken');
        },
    
        saveToken(stdout) {
          this.log('stdout='+stdout);  
          global.set('myToken', stdout);
        }
    });
    


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

    this.execOS(yc iam create-token, 'saveToken');

    не понял в чем было дело, путь в $PATH был, но получилось сделать только прописав полный путь this.execOS('/root/yandex-cloud/bin/yc iam create-token','saveToken');

    здесь, ок, спасибо.

    Другая проблема при выполнении команды OS
    this.execOS(curl -X POST -H "Authorization: Bearer ${global.get('myToken')}" --data-urlencode "text=Здравствуй мир" -d "lang=ru-RU&folderId=b1g39r55fbahkvak21ha" "https://tts.api.cloud.yandex.net/speech/v1/tts:synthesize" > aaa92.ogg)

    или curl -X POST -H "Authorization: Bearer ${stdout}" --data-urlencode "text=Здравствуй мир" -d "lang=ru-RU&folderId=b1g39r55fbahkvak21ha" "https://tts.api.cloud.yandex.net/speech/v1/tts:synthesize" > aaa9.ogg
    если из того же сценария где и считывается переменная в сценарий

    лог пишет:

    20.05 21:24:17.379 Trigger LAMP_24
    20.05 21:24:17.380 Started
    20.05 21:24:17.386 execOS: curl -X POST -H "Authorization: Bearer Cgg..........
    " --data-urlencode "text=Здравствуй мир" -d "lang=ru-RU&folderId=b1g39r55fbahkvak21ha" "https://tts.api.cloud.yandex.net/speech/v1/tts:synthesize" > aaa92.ogg
    20.05 21:24:17.396 Stopped
    20.05 21:24:17.481 execOS error: Error: Command failed: curl -X POST -H "Authorization: Bearer Cgg... --data-urlencode "text=Здравствуй мир" -d "lang=ru-RU&folderId=b1g39r55fbahkvak21ha" "https://tts.api.cloud.yandex.net/speech/v1/tts:synthesize" > aaa92.ogg
    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed

    0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
    100 127 0 0 100 127 0 1788 --:--:-- --:--:-- --:--:-- 1788
    curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)

    20.05 21:24:17.481 stdout:
    20.05 21:24:17.481 stderr: % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed

    0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
    100 127 0 0 100 127 0 1788 --:--:-- --:--:-- --:--:-- 1788
    curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)

    при этом если в команду прописать не переменную а сам токен, все проходит. Из терминала и так и так все гуд.



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

    Другая проблема при выполнении команды OS
    this.execOS(curl -X POST -H "Authorization: Bearer ${global.get('myToken')}" --data-urlencode "text=Здравствуй мир" -d "lang=ru-RU&folderId=b1g39r55fbahkvak21ha" "https://tts.api.cloud.yandex.net/speech/v1/tts:synthesize" > aaa92.ogg)
    лог пишет: .....
    curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)

    при этом если в команду прописать не переменную а сам токен, все проходит. Из терминала и так и так все гуд.

    Есть две версии:

    1. Токен содержит лишние символы в начале и в конце (перевод строки, пробелы), это все убрать перед сохранением
    saveToken(stdout) {
         let myToken = stdout.replace(/^\s+/, '').replace(/\s+$/,'');
         global.set('myToken', myToken);
         this.log('myToken='+ global.get('myToken')+'!'); // Посмотреть, что здесь выводится  
    }
    
    1. Менее вероятно - неверно берется из global или подстановка внутри строки неверно отрабатывает
    let token = global.get('myToken');
    this.log('token='+ token+'!');  // Посмотреть, что здесь 
    this.execOS(`curl -X POST -H "Authorization: Bearer ${token}" --data-urlencode...   `);
    


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

    saveToken(stdout) {
    let myToken = stdout.replace(/^\s+/, '').replace(/\s+$/,'');
    global.set('myToken', myToken);
    this.log('myToken='+ global.get('myToken')+'!'); // Посмотреть, что здесь выводится
    }

    Отлично, помогло, действительно заползали лишние символы.
    Спасибо !!



  • Не в эту ветку, но спрошу, по умолчанию IH действует от имени пользователя ROOT ?
    root@ih-server-big:/opt/intrahouse-c#
    Можно ли под другим пользователем "по умолчанию" ?



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

    Не в эту ветку, но спрошу, по умолчанию IH действует от имени пользователя ROOT ?
    root@ih-server-big:/opt/intrahouse-c#
    Можно ли под другим пользователем "по умолчанию" ?

    Да, конечно, Вы можете создать своего пользователя и делать установку под ним. Но нужно дать ему достаточно прав для доступа:

    1. К папкам внутри /opt и /var/lib
    2. К периферии и портам, с которыми работает система
    3. Этот пользователь должен иметь права на sudo.
      Запуск из-под sudo используется при установке пакетов через npm, так как IH ставит пакеты при установке плагинов и их обновлении прозрачно для пользователя. Используемая версия npm требует такой доступ. Возможно, в 5 версии использование sudo получится избежать.

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