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

Как правильно создать defineVirtualDevice с типом integer или float?
Чтоб, например, передавать значения типа колограмм…

1 Симпатия

Люди добрый помогите пожалуйста непрограммисту составить шаблоны правил для следующих задач, которые будут востребованы в 100% случаях при использовании WB в домашней автоматизации:

  1. Управление несколькими реле по изменению счетчика dev["device/Input "x" counter с предварительной проверкой состояния реле (если хотя бы одно реле On, то выкл все. если все off то вкл все.
  2. Аналогичное, но с добавлением в правило каналов диммера.
  3. Обработка двойного нажатия для определенного ```dev["device/Input “x” counter``. Т.е. запуск таймера после n+1, далее ожидание n+2, далее сброс таймера, анализ n и выполнение разных функций для n+1 и для n+2.
  4. Правило для постановки на сигнализацию определенных dev["device/Input "x" при отработке определенной функции, а также оповещение при сработке входа.
  5. Различные правила для ```dev["device/Input “x” counter` в зависимости от времени суток.
  6. Создание многонаборных сценариев, для виртуальных устройств: напр. включение димера на 20%+включение реле1+ реле5+ температура пола 26.
  7. Настройка алиасов для входных каналов : например вместо ```dev["device/Input “x”] применять одну глобальную переменную “button1”.
  8. То же самое для каналов реле - тут сложности: как система поймет что алиас “rele1” в одном случае это dev[device/K1], а в другом dev[device][K1]
  9. Как правильно структурировать правила - вкладывать все обработки в одну функцию (проверка времени суток, проверка состояния, проверка количества нажатий" или лучше разносить обработки в разных функциях, тогда какие должны выполняться раньше?
  10. будут дополняться

p.s. простое изложение данных шаблонов увеличит количество желающих приобрести Ваше оборудование. Возможно стоит выделить отдельную тему и назвать ее типа “Умный дом на WB для чайников”)))))

Может кто возьмется за плату составить шаблоны на выше указанные задачи?

5 сообщений перенесены в новую тему: Правило на групповое управление светом

Добрый день! Немного доработал правило на определение одиночного/двойного/длинного нажатия.
Скрипт состоит из 2 частей: функция onButtonPress (её можно положить в отдельный .js файл) и пример её использования.

/**
 * 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} ruleName -  Name of the rule that will be defined.
 * @param  {string} trigger  -  Name of device and control in the following format: "<device>/<control>".
 * @param  {object} action   -  Object that defines relays that will be switched.
 *                              Keys: "singlePress", "doublePress", "longPress".
 *                              Each key contains an array of the following format: ["<device>", "control"].
 * @param  {number} timeToNextPress - Time (ms) after button up to wait for the next press before reseting the counter.
 * @param  {number} timeOfLongPress - Time (ms) after button down to be considered as as a long press.
 */
function onButtonPress(ruleName, trigger, action, timeToNextPress, timeOfLongPress) {
    var buttonPressedCounter = 0;
    var timerWaitNextShortPress = null;
    var timerLongPress = null;
    var isLongPress = false;

    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 () {
                    dev[action.longPress[0]][action.longPress[1]] = !dev[action.longPress[0]+"/"+action.longPress[1]];
                    // 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:
                                dev[action.singlePress[0]][action.singlePress[1]] = !dev[action.singlePress[0]+"/"+action.singlePress[1]];
                                // log(">>>>>> short press - single <<<<<<");
                            break;
                        // Counter equals 2 - it's a double short press
                        case 2:
                                dev[action.doublePress[0]][action.doublePress[1]] = !dev[action.doublePress[0]+"/"+action.doublePress[1]];
                                // log(">>>>>> short press - double <<<<<<");
                            break;
                        }
                        // Reset the counter
                        buttonPressedCounter = 0;
                    }, timeToNextPress);
                }
                isLongPress = false;
            }

        }
    });

}

// Usage example
onButtonPress(
    "on_button_press",
    "wb-gpio/EXT1_DR14",
    {
        singlePress: ["wb-mr6c_111", "K1"],
        doublePress: ["wb-mr6c_111", "K2"],
        longPress: ["wb-mr6c_111", "K3"]
    },
    200, 2000
);

4 Симпатий

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

Добрый день!
пытаюсь расположить функцию в отдельном 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() - простейшая функция переключения реле, она будет работать в большинстве случаев; остальные функции (про диммер) стоит проверить для конкретного случая.

3 Симпатий

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

Цитата
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.

1 Симпатия

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

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