Движок правил: примеры кода

Добрый вечер. Подскажите, как использовать переменные с восходом и закатом. Как сделать так, чтобы при наступлении восхода или заката сработало реле, которое выключает/включает уличное освещение. У меня, на данный момент, скрипт срабатывает только при обновлении или сохранении файла правила

Добрый день!
пытаюсь расположить функцию в отдельном js-файле, а ее вызов в другом и получаю ошибку:

Script error: ReferenceError: identifier ‘onButtonPress’ undefined
duk_js_var.c:1232
anon /etc/wb-rules/light_switches.js:42 preventsyield

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

И еще вопрос: при вызове функции, например, с параметром longPress, как передать несколько устройств (нужно по долгому нажатию выключать несколько реле)? и как задать какое значение должно принять реле (true/false)?

Спасибо!

Рекомендую сразу перейти на Бета-тестирование новой версии движка правил и прочитать
https://wirenboard.com/wiki/index.php/Движок_правил_wb-rules_2.0#.D0.98.D0.B7.D0.BE.D0.BB.D1.8F.D1.86.D0.B8.D1.8F_.D1.81.D1.86.D0.B5.D0.BD.D0.B0.D1.80.D0.B8.D0.B5.D0.B2

как понять какая версия движка установлена?

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

@almo
и все-таки, как корректно указать что нужно выключить несколько устройств при долгом нажатии?

выполнить в консоли
dpkg -s wb-rules

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

Но за прошедшее время появилась обновлённая версия скрипта, поскольку кроме управления несколькими устройствами мне ещё нужно было управлять цветами RGB ленты по нажатию на кнопку…
Теперь в качестве параметра передаётся не имя управляемого устройства а функция и параметры этой функции. Простейшей функцией будет управление реле - пример будет ниже.
Кроме того уникальное имя правила теперь генерируется внутри функции.
Функция onButtonPress() уже вынесена в отдельный .js файл, пример её вызова тоже ниже.

Функция onButtonPress() - её можно положить в отдельный .js файл:

(function () {
'use strict';

var ActionButtons = {};


/**
 * Function that identifies what kind of press was performed: single, double or long press;
 * and assigns an action for each type of press.
 *
 * @param  {string} trigger         -  Name of device and control in the following format: "<device>/<control>".
 * @param  {object} action          -  Defines actions to be taken for each type of button press.
 *                                  Key: "singlePress" or "doublePress" or "longPress" or "longRelease".
 *                                  Value: Object of the following structure {func: <function name>, prop: <array of parameters to be passed>}
 *                                  Example:
 *                                  {
 *                                      singlePress: {func: myFunc1, prop: ["wb-mr6c_1", "K1"]},
 *                                      doublePress: {func: myFunc2, prop: ["wb-mrgbw-d_2", "RGB", "255;177;85"]},
 *                                      longPress: {func: myFunc3, prop: []},
 *                                      longRelease: {func: myFunc4, prop: []}
 *                                  }
 * @param  {number} timeToNextPress -  Time (ms) after button up to wait for the next press before reseting the counter. Default is 300 ms.
 * @param  {number} timeOfLongPress -  Time (ms) after button down to be considered as as a long press. Default is 1000 ms (1 sec).
 */
ActionButtons.onButtonPress = function (trigger, action, timeToNextPress, timeOfLongPress) {
    var buttonPressedCounter = 0;
    var timerWaitNextShortPress = null;
    var timerLongPress = null;
    var isLongPress = false;

    var ruleName = "on_button_press_" + trigger.replace("/", "_");

    defineRule(ruleName, {
        whenChanged: trigger,
        then: function (newValue, devName, cellName) {

            // If button is pressed, wait for a long press
            if (newValue) {

                if (timerWaitNextShortPress) {
                    clearTimeout(timerWaitNextShortPress);
                }
                timerLongPress = setTimeout(function () {
                    if (typeof action.longPress === "object") {
                        if (typeof action.longPress.func === "function") {
                            action.longPress.func.apply(this, action.longPress.prop);
                        }
                    }
                    // log(">>>>>>> long press <<<<<<");
                    isLongPress = true;  // Long press identified, we will skip short press
                    buttonPressedCounter = 0;
                }, timeOfLongPress);

            }

            // If button is released, then it is not a "long press", start to count clicks
            else {
                if (!isLongPress) {
                    clearTimeout(timerLongPress);
                    buttonPressedCounter += 1;
                    timerWaitNextShortPress = setTimeout(function () {
                        switch (buttonPressedCounter) {
                        // Counter equals 1 - it's a single short press
                        case 1:
                            if (typeof action.singlePress === "object") {
                                if (typeof action.singlePress.func === "function") {
                                    action.singlePress.func.apply(this, action.singlePress.prop);
                                }
                            }
                            // log(">>>>>> short press - single <<<<<<");
                            break;
                        // Counter equals 2 - it's a double short press
                        case 2:
                            if (typeof action.doublePress === "object") {
                                if (typeof action.doublePress.func === "function") {
                                    action.doublePress.func.apply(this, action.doublePress.prop);
                                }
                            }
                            // log(">>>>>> short press - double <<<<<<");
                            break;
                        }
                        // Reset the counter
                        buttonPressedCounter = 0;
                    }, timeToNextPress);
                }

                // Catch button released after long press
                else {
                    if (typeof action.longRelease === "object") {
                        if (typeof action.longRelease.func === "function") {
                            if (typeof action.longRelease.prop === "array") {
                                action.longRelease.func.apply(this, action.longRelease.prop);
                            } else {
                                action.longRelease.func.apply(this, []);
                            }
                        }
                    }
                    isLongPress = false;
                }
            }

        }
    });
};



// export as Node module / AMD module / browser variable
if (typeof exports === 'object' && typeof module !== 'undefined') {
    module.exports = ActionButtons;
} else if (typeof define === 'function' && define.amd) {
    define(ActionButtons);
} else {
    global.ActionButtons = ActionButtons;
}

}());

Пример использования (отдельный .js файл):

ActionButtons.onButtonPress(
"wb-gpio/EXT1_IN11",
{
    singlePress: {
        func: switchRelay,
        prop: ["wb-mr6c_10", "K1"]
    },
    doublePress: {
        func: switchDimmerRGB,
        prop: ["wb-mr6c_10", "K2", "wb-mrgbw-d_24"]
    },
    longPress: {
        func: setRandomRGB,
        prop: ["wb-mr6c_10", "K2", "wb-mrgbw-d_24"]
    }
},
300, 1000
);

В примере использованы функции переключения реле, переключения led ленты через диммер и функция установки случайного цвета диммера (они должны находиться в том же .js файле, что и пример использования):

/**
* Helper Functions
*/
function switchRelay(device, control) {
  dev[device][control] = !dev[device + "/" + control];
}

function switchDimmerRGB(relayDevice, relayControl, dimmerDevice) {
   dev[relayDevice][relayControl] = true;
   if (dev[dimmerDevice + "/RGB"] !== "0;0;0") {
     dev[dimmerDevice]["RGB"] = "0;0;0";
   }
   else {
     dev[dimmerDevice]["RGB"] = dev[relayDevice + "/RGB"];
   }
}

function setRandomRGB(relayDevice, relayControl, dimmerDevice) {
  dev[relayDevice][relayControl] = true;
  dev[relayDevice + "/RGB"] = "" + Math.floor(Math.random() * 255) + ";" + Math.floor(Math.random() * 255) + ";" + Math.floor(Math.random() * 255);
  dev[dimmerDevice]["RGB"] = dev[relayDevice + "/RGB"];
}

Как я писал выше, switchRelay() - простейшая функция переключения реле, она будет работать в большинстве случаев; остальные функции (про диммер) стоит проверить для конкретного случая.

UPD: в версияз wb-rules 2.x в отдельном файле функция НЕ работает.

4 симпатии

Спасибо большое, разобрался!
Но возникает одна странная ошибкаЖ

Цитата
Script error: ReferenceError: identifier ‘ActionButtons’ undefined
duk_js_var.c:1232
anon /etc/wb-rules/light_switches.js:30 preventsyield

если перезагрузить WB или сервис движка правил.
Соответственно и правила не работают, пока не зайдешь в правило и не пересохранишь его.

С чем такое может быть связано и как это исправить?

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

А что у вас оказалось на 30-й строчке файла light_switches.js, и около него?
Можете просто весь итоговый файл выложить.

light_switches.js.txt (4.4 КБ)
Вот весь файл, там много всего лишнего…
ругается на строку:

Цитата
ActionButtons.onButtonPress(

при этом если пересохранить, то перестает ругаться

А ещё файл с описанием объекта ActionButtons сбросьте, пожалуйста

вот он onButtonPress.js.txt (5.2 КБ)

Здравствуйте! Помогите пожалуйста написать правильно скрипт. Вообщем ситуация такая,есть кнопка для управления вентилятором (вентилятор с регулировкой оборотов-работает через аналоговый выход 0-10v) она его включает при первом нажатии на 100% при втором на 50% и при третьем выключает это реализовать удалось. Также вентилятор должен работать от датчика влажности при превышении порога включается на 50% и по истечении какого то времени включается на 100% .Выключается при снижении порога и сбрасывает таймер .Если произошло включение по кнопке сценарий с датчиком работать не должен.Сейчас работает по другому(от датчика влажности вкл на 50% потом по времени на 100% сбрасывает сброс таймера и все по новой) помогите подкорректировать.

(function() { 
  var button_counter = 0;


  defineRule( "on_fan_button", {
    whenChanged: "wb-gpio/A1_IN",
    then: function (newValue, devName, cellName) {

      // если поймали нажатие, увеличиваем счётчик
      if (newValue) {
        button_counter += 1;
      }
       // счётчик равен одному, значит было одинарное нажатие
      if (button_counter == 1) {
        log("fan 100%");dev["wb-dac/EXT2_O1"] = 10000;
      }
      
      // счётчик равен двум, значит было двойное нажатие
      if (button_counter == 2) {
        log("fan 50%");dev["wb-dac/EXT2_O1"] = 5000;
      }
       if (button_counter == 3) {
        log("fan off");dev["wb-dac/EXT2_O1"] = 0;
      }
      // сброс счетчика
     if (button_counter == 3) {
        log("clear counter!",button_counter);button_counter = 0;
      }
    }
      });
  })();
          var Hum = 10;
       var motion_timer_1_timeout_ms = 10 * 1000;
        var motion_timer_1_id = null;
  
   defineRule("Hum_fan_on", { 
   whenChanged: "T2 Bathroom/Humidity", 

   then: function (newValue, devName, cellName) { 
 if ( newValue > Hum) {
  dev["wb-dac/EXT2_O1"] = 5000;  
  
if (motion_timer_1_id) {
      clearTimeout(motion_timer_1_id);
   }
  
    motion_timer_1_id = setTimeout(function () {
	   dev["wb-dac"]["EXT2_O1"] = 10000;         
               motion_timer_1_id = null;   
   	}, motion_timer_1_timeout_ms);           	
}
}
});

Судя по ошибке объект ActionButtons не существует на момент запуска скрипта light_switches. В качестве эксперимента: попробуйте переименовать onButtonPress.js в _onButtonPress.js или что-то подобное, чтобы по порядку он был выше light_switches.

2 симпатии

Спасибо!
Это помогло!

2 сообщения было перемещено в эту тему: Новая версия движка правил

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

  defineVirtualDevice("Tp1", {
  title:"Регулятор температуры Пола в прихожей",
  cells: {
  "temperature": {
  type:"range",
  value: 25,
  max: 55
 },"Zima1": {
  type:"switch",
  value: 0,
 }
                }
                });
defineRule("Tpf1", {
whenChanged: "wb-w1/28-00000b785146",
then: function (newValue, devName, cellName) {
if (newValue>dev["Tp1/temperature"]) {
dev["wb-gpio/EXT1_HS5"] = 0;log("Выключение обогревателя 
 ТП1");
} else  
  dev["wb-gpio"]["EXT1_HS5"] = 1;log("Включение 
обогревателя ТП1");    
}
 });

Восход Заход
curl -sA Mozilla http://www.google.com/search?q=Восход+Заход+$1 | html2text -width 80 | grep "Заход\ "

Доброго дня!
Разбираюсь с движком правил, взял пример для кнопки (нажатие, долгое и двойное нажатие) отсюда выше. Сделал виртуальное устройство pushbutton, но с ним не получается оттестировать – всегда срабатывает долгое нажатие. Не получится на виртуальном девайсе без физической кнопки эмулировать даблклик и лонгпресс?

Давайте ваш скрипт целиком - посмотрю. Должно работать.

Кнопка в одном скрипте:

defineVirtualDevice("test_button1", {
  title: "Test dev 1",
  cells: {
    "switch_on": {
    type: "pushbutton",
    value: false,
    },
  }
});

Это скрипт в файле _OnButtonPress.js, его взял где-то здесь. Как бы я не нажимал, всегда срабатывает:

longPress: {
func: switchRelay,
prop: [“wb-mr6c_185”, “K2”]
}

Дабл клик просто выкл и вкл быстро реле.

(function () {
'use strict';

var ActionButtons = {};


/**
 * Function that identifies what kind of press was performed: single, double or long press;
 * and assigns an action for each type of press.
 *
 * @param  {string} trigger         -  Name of device and control in the following format: "<device>/<control>".
 * @param  {object} action          -  Defines actions to be taken for each type of button press.
 *                                  Key: "singlePress" or "doublePress" or "longPress" or "longRelease".
 *                                  Value: Object of the following structure {func: <function name>, prop: <array of parameters to be passed>}
 *                                  Example:
 *                                  {
 *                                      singlePress: {func: myFunc1, prop: ["wb-mr6c_1", "K1"]},
 *                                      doublePress: {func: myFunc2, prop: ["wb-mrgbw-d_2", "RGB", "255;177;85"]},
 *                                      longPress: {func: myFunc3, prop: []},
 *                                      longRelease: {func: myFunc4, prop: []}
 *                                  }
 * @param  {number} timeToNextPress -  Time (ms) after button up to wait for the next press before reseting the counter. Default is 300 ms.
 * @param  {number} timeOfLongPress -  Time (ms) after button down to be considered as as a long press. Default is 1000 ms (1 sec).
 */
ActionButtons.onButtonPress = function (trigger, action, timeToNextPress, timeOfLongPress) {
    var buttonPressedCounter = 0;
    var timerWaitNextShortPress = null;
    var timerLongPress = null;
    var isLongPress = false;

    var ruleName = "on_button_press_" + trigger.replace("/", "_");

    defineRule(ruleName, {
        whenChanged: trigger,
        then: function (newValue, devName, cellName) {

            // If button is pressed, wait for a long press
            if (newValue) {

                if (timerWaitNextShortPress) {
                    clearTimeout(timerWaitNextShortPress);
                }
                timerLongPress = setTimeout(function () {
                    if (typeof action.longPress === "object") {
                        if (typeof action.longPress.func === "function") {
                            action.longPress.func.apply(this, action.longPress.prop);
                        }
                    }
                    // log(">>>>>>> long press <<<<<<");
                    isLongPress = true;  // Long press identified, we will skip short press
                    buttonPressedCounter = 0;
                }, timeOfLongPress);

            }

            // If button is released, then it is not a "long press", start to count clicks
            else {
                if (!isLongPress) {
                    clearTimeout(timerLongPress);
                    buttonPressedCounter += 1;
                    timerWaitNextShortPress = setTimeout(function () {
                        switch (buttonPressedCounter) {
                        // Counter equals 1 - it's a single short press
                        case 1:
                            if (typeof action.singlePress === "object") {
                                if (typeof action.singlePress.func === "function") {
                                    action.singlePress.func.apply(this, action.singlePress.prop);
                                }
                            }
                            // log(">>>>>> short press - single <<<<<<");
                            break;
                        // Counter equals 2 - it's a double short press
                        case 2:
                            if (typeof action.doublePress === "object") {
                                if (typeof action.doublePress.func === "function") {
                                    action.doublePress.func.apply(this, action.doublePress.prop);
                                }
                            }
                            // log(">>>>>> short press - double <<<<<<");
                            break;
                        }
                        // Reset the counter
                        buttonPressedCounter = 0;
                    }, timeToNextPress);
                }

                // Catch button released after long press
                else {
                    if (typeof action.longRelease === "object") {
                        if (typeof action.longRelease.func === "function") {
                            if (typeof action.longRelease.prop === "array") {
                                action.longRelease.func.apply(this, action.longRelease.prop);
                            } else {
                                action.longRelease.func.apply(this, []);
                            }
                        }
                    }
                    isLongPress = false;
                }
            }

        }
    });
};



// export as Node module / AMD module / browser variable
if (typeof exports === 'object' && typeof module !== 'undefined') {
    module.exports = ActionButtons;
} else if (typeof define === 'function' && define.amd) {
    define(ActionButtons);
} else {
    global.ActionButtons = ActionButtons;
}

}());

ActionButtons.onButtonPress(
"test_button1/switch_on",
{
    singlePress: {
        func: switchRelay,
        prop: ["wb-mr6c_157", "K1"]
    },
    doublePress: {
        func: switchRelay,
        prop: ["wb-mr6c_185", "K1"]
    },
    longPress: {
        func: switchRelay,
        prop: ["wb-mr6c_185", "K2"]
    }
},
300, 1000
);

/**
* Helper Functions
*/
function switchRelay(device, control) {
  dev[device][control] = !dev[device + "/" + control];
}