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

Я примерно это и понял, у меня последний вариант работает без ошибок.
Т.е. кнопкой контролируем активность правила, а по изменениям данный с датчика контролируем выход за рамки по температуре. Вы его проверяли?

не успел еще, обязательно проверю, отпишусь

Отрабатывает все четко, спасибо огромное!!

а вот тут

if (dev.control_heating.enabled) { // если вируальн. устр-во в положении enabled
log.info(“newValue ={}”,newValue);
if (newValue < temp_on) { //если температура датчика меньше xx градусов
if (!(relay_on_timer_id||relay_off_timer_id)){on_timer();}
log.info(“Stop timer and off relay, temp > {}”,temp_on);

в лог не Start timer and on relay, temp < {}",temp_on ?

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

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

(function () {
'use strict';

var ActionButtons = {};


/**
 * Version: 0.3.1
 * 
 * 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 "triplePress" 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"]},
 *                                      triplePress: {func: myFunc3, prop: []},
 *                                      longPress: {func: myFunc4, prop: []},
 *                                      longRelease: {func: myFunc5, 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).
 * @param  {number} intervalOfRepeat - Time (ms) before repeating action specified in LongPress action. Default is 50 ms.
 * 
 * Note: In case longRelease function defined, longPress function will repeate till button is released.
 *       In case longRelease function not defined, only one action will be executed for longPress.
 */
ActionButtons.onButtonPress = function (trigger, action, timeToNextPress, timeOfLongPress, intervalOfRepeat) {
    
    // Set default values if not passed into function
timeToNextPress = timeToNextPress || 300;
timeOfLongPress = timeOfLongPress || 1000;
intervalOfRepeat = intervalOfRepeat || 100;

var buttonPressedCounter = 0;
var actionRepeatCounter = 0;
var timerWaitNextShortPress = null;
var timerLongPress = null;
var timerWaitLongRelease = null;
var isLongPressed = false;
var isLongReleased = false;

var ruleName = "on_button_press_" + trigger.replace("/", "_").replace(/ /g, "_"); //Добавил условие для удаления пробелов
log("LOG::Define WB Rule: ", ruleName);

defineRule(ruleName, {
    whenChanged: trigger,
    then: function (newValue, devName, cellName) {
        //  Вставка для работы со счетчиками нажатий 	
        var isButtonPressed = newValue;
        if (typeof newValue === "number"){
        	isButtonPressed = newValue % 2 !== 0;
        }

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

            if (timerWaitNextShortPress) {
                clearTimeout(timerWaitNextShortPress);
            }
            timerLongPress = setTimeout(function () {
                isLongPressed = true;  // Long press identified, we will skip short press
                isLongReleased = false;
                buttonPressedCounter = 0;
                actionRepeatCounter = 1;
                if (typeof action.longPress === "object") {
                    if (typeof action.longPress.func === "function") {
                        action.longPress.func.apply(this, action.longPress.prop);
                        // If Long Release action define, we will repeat Long Press action till not released. Otherwise only 1 Long Press action is executed
                        if (typeof action.longRelease === "object") {
                            if (typeof action.longRelease.func === "function") {
                                timerWaitLongRelease = setInterval(function () {
                                    if(!isLongReleased) {
                                        if (typeof action.longPress === "object") {
                                            if (typeof action.longPress.func === "function") {
                                                action.longPress.func.apply(this, action.longPress.prop);
                                            }
                                        }
                                        // log(">>>>>> long press - press (" + actionRepeatCounter++ + ") <<<<<<");    
                                    }
                                    if(isLongReleased) {
                                        clearInterval(timerWaitLongRelease);
                                    }
                                }, intervalOfRepeat);        
                            }                                        
                        }

                    }
                }
                // log(">>>>>> long press - press (" + actionRepeatCounter++ + ") <<<<<<");
            }, timeOfLongPress);

        }

        // If button is released, then it is not a "long press", start to count clicks
        else {
            if (!isLongPressed) {
                if (timerLongPress) {
                    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;
                    // Counter equals 3 - it's a triple short press
                    case 3:
                        if (typeof action.triplePress === "object") {
                            if (typeof action.triplePress.func === "function") {
                                action.triplePress.func.apply(this, action.triplePress.prop);
                            }
                        }
                        // log(">>>>>> short press - triple <<<<<<");
                        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, []);
                        // }
                    }
                }
                // log(">>>>>> long press - release <<<<<<");
                isLongPressed = false;
                isLongReleased = true;
            }
        }

    }
});
};



// 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;
}

}());

const flatIOs = [
    { name: 'DI0', io: ['Counter 1', 'Counter 2', 'Counter 3', 'Counter 4', 'Counter 5', 'Counter 6', 'Counter 7', 'Counter 8', 'Counter 9', 'Counter 10', 'Counter 11', 'Counter 12', 'Counter 13', 'Counter 14'] },
    { name: 'R1', io: ['K1', 'K2', 'K3', 'K4', 'K5', 'K6'] },
    { name: 'R2', io: ['K1', 'K2', 'K3', 'K4', 'K5', 'K6'] }
];


ActionButtons.onButtonPress(
flatIOs[0].name +"/"+ flatIOs[0].io[0],          //Вход, за которым следим.
{
singlePress: {
   func: switchRelay,
   prop: [flatIOs[1].name, flatIOs[1].io[0]]
},
doublePress: {
   func: switchRelay, prop: [flatIOs[1].name, flatIOs[1].io[1]]
    //func: switchDimmerRGB,
    //prop: ["wb-mr6c_10", "K2", "wb-mrgbw-d_24"]
},
longPress: {
   func: switchRelay, prop: [flatIOs[1].name, flatIOs[1].io[2]]
    //func: setRandomRGB,
    //prop: ["wb-mr6c_10", "K2", "wb-mrgbw-d_24"]
}
},
300, 1000
);


/**
* Helper Functions
*/
function switchRelay(device, control) { //Принимает в параметрах устройство и выход. Переключает состояние выхода на противоположное.
  log.info("LongPress switchRelay", device, control)//Это лог. Он попадает в /var/log/messages
  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"];
}

Добрый день!

Использовал ваш кусок кода для обработки 1-, 2-, Долгого- нажатий, все работает, но есть одна проблема, после запуска контроллера, автоматически включаются все выходы, где был использован этот код. Получается после подачи питания на щиток и загрузки контроллера, включается свет на тех реле где используется этот кусок кода (Version: 0.3.1), модули WB-MRPS6/S и WB-MDM3.

В лог выводится сообщение: LOG::Define WB Rule: on_button_press_wb-gpio_EXT5_IN10

Если закомментировать этот кусок кода: singlePress: { func: Switcher, prop: [“rele3”, “K5”] }, то после запуска контроллера свет автоматически не включается.

Значение регистра 6: 0: не восстанавливать состояние реле.

Код функции Switcher:
function Switcher(pDevace, pChanel){
dev[pDevace][pChanel] = !dev[pDevace][pChanel];
}

В чем проблема? Правила, свойствах, версии, настройки модуля?

Тут, думаю, дело в том что при апуске первым в mqtt топик приходит “null”, вызывая срабатывание. Можно либо сделать костыль, принудительно выключая все каналы реле либо исключить в скрипте обработку “null”.

Здравствуйте! Набросал код управления рулонными шторами.

defineRule(“roller cabinet down”, {
when: function(newValue, devName, cellName) {
return dev[“wb-gpio”][“EXT5_IN4”] = true;
},
then: function (newValue, devName, cellName) {
dev[“wb-gpio”][“EXT3_ON2”] = true;
dev[“wb-gpio”][“EXT3_DIR2”] = false;
}
});

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

defineRule(“roller cabinet down”, {
when: function(newValue, devName, cellName) {
return dev[“wb-gpio”][“EXT5_IN4”] = true;
},
then: function (newValue, devName, cellName) {
dev[“wb-gpio”][“EXT3_ON2”] = true;
dev[“wb-gpio”][“EXT3_DIR2”] = false;
setTimeout(function () {}, 30000);
dev[“wb-gpio”][“EXT3_ON2”] = false;
}
});

Подскажете, в чем может быть проблема?

И еще хотелось бы сделать так, чтобы при одном нажатии на кнопку штора опускалась до конца, а при повторном она останавливалась.

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

Заранее спасибо за помощь!

Здравствуйте!

Для создания кнопок в веб-интерфейсе нужно создать виртуальное устройство с помощью правил:

defineVirtualDevice("drapery_control", {
    title: "Drapery Control",
    cells: {
        open: {
            type: "switch",
            value: false,
            order: 1,
        },
        close: {
            type: "switch",
            value: false,
            order: 2,
        },
    },
});

Результатом будет такое окно на странице Devices:
image

Сделал несколько исправлений, должно заработать:

defineRule("roller cabinet down", {
    whenChanged: ["drapery_control/close", "wb-gpio/EXT5_IN4"],  //правило срабатывает при активировании входа или включении кнопки в веб-интерфейсе
    then: function (newValue, devName, cellName) {
        if (newValue) {
            dev["wb-gpio"]["EXT3_ON2"] = true;
            dev["wb-gpio"]["EXT3_DIR2"] = false;

            setTimeout(function () {
                dev["wb-gpio"]["EXT3_ON2"] = false;   //прописываем действия по истечению времени
                dev["drapery_control"]["close"] = false;
            }, 3000);
        }
    },
});
2 лайка

Спасибо за ответ! Буду пробовать!

Подскажите, как еще сделать так, чтобы при повторном нажатии на кнопку (хотя бы реальную) штора останавливалась в текущем положении? То есть нужно переключить “wb-gpio”][“EXT3_ON2”] в false

Доработал правило отображения ресурсов контроллера: отображается загрузка процессора, сведения об оперативной памяти, сведения о разделе rootfs и сведения об /mnt/data. В правиле используются фрагменты кода из тем на портале техподдержки, которыми поделились пользователи. Возможно, кому-нибудь будет полезным.

image.

Правило во вложении.
controller_utilization.zip (1.0 KB)

Здравствуйте! Виртуальные кнопки создаются, но скрипт на них не реагирует…

defineVirtualDevice(“Roller blinds”, {
title: “Шторы”,
cells: {
Спальня_О: {
type: “switch”,
value: false,
order: 1,
},
Спальня_З: {
type: “switch”,
value: false,
order: 2,
},
},
})
;

defineRule(“roller sleeping room down”, {
whenChanged: [“Шторы/Спальня_З”, “wb-gpio/EXT5_IN8”],
then: function (newValue, devName, cellName) {
if (newValue) {
dev[“wb-gpio”][“EXT4_ON3”] = true;
dev[“wb-gpio”][“EXT4_DIR3”] = false;

        setTimeout(function () {
            dev["wb-gpio"]["EXT4_ON3"] = false;  
            dev["Шторы"]["Спальня_З"] = false;
        }, 30000);
    }
},

});

defineRule(“roller sleeping room up”, {
whenChanged: [“Шторы/Спальня_О”, “wb-gpio/EXT5_IN9”],
then: function (newValue, devName, cellName) {
if (newValue) {
dev[“wb-gpio”][“EXT4_ON3”] = true;
dev[“wb-gpio”][“EXT4_DIR3”] = true;

        setTimeout(function () {
            dev["wb-gpio"]["EXT4_ON3"] = false;  
            dev["Шторы"]["Спальня_О"] = false;
        }, 30000);
    }
},

});

Кнопки сейчас в отдельном правиле, помещал и в одно со скриптами - все равно ничего. Пробовал писать отдельное правило на виртуальные кнопки, тоже нет изменений

В условии нужно использовать имя виртуального устройcтва, а не его заголовок окна. Попробуйте так:

whenChanged: ["Roller blinds/Спальня_З", "wb-gpio/EXT5_IN8"],
1 лайк

Скрипт у меня такой:

defineRule(“roller sleeping room down”, {
whenChanged: [“Roller blinds/Спальня_З”, “wb-gpio/EXT5_IN8”],
then: function (newValue, devName, cellName) {
if (newValue) {
dev[“wb-gpio”][“EXT4_ON3”] = true;
dev[“wb-gpio”][“EXT4_DIR3”] = false;

setTimeout(function () {
dev[“wb-gpio”][“EXT4_ON3”] = false;
dev[“Roller blinds”][“Спальня_З”] = false;
}, 30000);
}
},
});

Получается нужно сделать так?

defineRule(“roller sleeping room down”, {
whenChanged: [“Roller blinds/Спальня_З”, “wb-gpio/EXT5_IN8”],
then: function (newValue, devName, cellName) {
if (newValue) {
dev[“wb-gpio”][“EXT4_ON3”] = false;
dev[“wb-gpio”][“EXT4_DIR3”] = false;
dev[“wb-gpio”][“EXT4_ON3”] = true;

setTimeout(function () {
dev[“wb-gpio”][“EXT4_ON3”] = false;
dev[“Roller blinds”][“Спальня_З”] = false;
}, 30000);
}
},
});

пт, 14 янв. 2022 г. в 08:40, Explorerol через Wiren Board Support <info@wirenboard.ru>:

Не совсем так. Если вы отключаете реле и сразу его снова включаете, то контакты реле ON3 не успеют до конца разомкнуться, как придет команда снова на включение. Для отключения реле обычно 10-50 миллисекунд. Поэтому лучше отключить реле ON3, затем сделать паузу для ожидания отключения реле, например 100 мс, затем уже переключить реле DIR3, опять сделать паузу, а затем снова включить ON3.
То есть было так:

dev[“wb-gpio”][“EXT4_ON3”] = false;
dev[“wb-gpio”][“EXT4_DIR3”] = false;
dev[“wb-gpio”][“EXT4_ON3”] = true;

А станет, например, так:

var delay_ms = 100        

dev["wb-gpio"]["EXT4_ON3"] = false;

setTimeout(function () {
dev["wb-gpio"]["EXT4_DIR3"] = false;	
}, delay_ms);

setTimeout(function () {
dev["wb-gpio"]["EXT4_ON3"] = true;	
}, delay_ms * 2);

1 лайк

Подскажите еще: хочу сделать, чтобы при нажатии на кнопку один раз, штора доезжала до конца, при нажатии, второй раз - останавливалась. Идея такая: ввести переменную n, которая будет считать количество нажатий, то есть нажал один раз n=1, нажал второй раз n=2 и обнулилась после этого. При n=1 будет одна функция, при n=2 будет другая. Но как задать n? Если я задаю n после define rule, то выдает ошибку, если после whenchanged - тоже, если после then function, то норм, но тогда n всегда будет обнуляться при наступлении события нажатия на кнопку. У меня сейчас вот такой код:

defineRule(“roller cabinet room down”, {
whenChanged: [“Roller blinds/Кабинет_З”, “wb-gpio/EXT5_IN4”],
then: function (newValue, devName, cellName) {
if (newValue) {
dev[“Roller blinds”][“Кабинет_О”] = false;
dev[“wb-gpio”][“EXT3_ON2”] = false;
setTimeout(function () {
dev[“wb-gpio”][“EXT3_DIR2”] = false;
}, 100);
setTimeout(function () {
dev[“wb-gpio”][“EXT3_ON2”] = true;
}, 100);
setTimeout(function () {
if (“wb-gpio/EXT3_DIR2”==true) {
dev[“wb-gpio”][“EXT3_ON2”] = false;
dev[“Roller blinds”][“Кабинет_З”] = false; }
}, 30000);
}
},
});

пт, 14 янв. 2022 г. в 15:56, Explorerol через Wiren Board Support <info@wirenboard.ru>:

И еще понял, что не работает последний if. В коде не правильно написал, нужно написать if (“wb-gpio/EXT3_DIR2”==false). Это для того, чтобы, если я когда штора едет вниз, нажав на кнопку вверх, она бы не отключались по таймеру в 30 секунд с правила движения вниз. Как это правильнее реализовать?

Предлагаю вот это все прописать нормально и выложить куда-нибудь на вики. Большому количеству пользователей, типа меня, нужен стандартный набор функций устройств и чтобы они нормально работали. А то мало того, что приходятся разбираться в семантике языка, так еще и дофига тонкостей логики, которые по факту элементарные, но без опыта о них даже не думаешь.

вс, 16 янв. 2022 г. в 22:10, Андрей К <andrey.kopylov.mail@gmail.com>:

Конструкция

if (dev[“wb-gpio/EXT3_DIR2]”==true)

записывается как

if (dev[“wb-gpio/EXT3_DIR2”])

А такая:

if (dev[“wb-gpio/EXT3_DIR2”]==false)

как

if (!dev[“wb-gpio/EXT3_DIR2”])

Примененная запись вида if (“wb-gpio/EXT3_DIR2”==false) не выполнится корректно, потому что “wb-gpio/EXT3_DIR2” - это не значение топика а просто контатнта строковая. Верно: dev[“wb-gpio/EXT3_DIR2”]

И, кстати, для вставки кода советую пользоваться ``` (три символа на русской букве Ё)

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

пн, 17 янв. 2022 г. в 10:07, Андрей Радионов через Wiren Board Support <info@wirenboard.ru>:

Да, исправил.
“!” - отрицание.
Подробнее - тут:
https://wirenboard.com/wiki/Wb-jscript#Условия

Здесь будет лежать последняя версия модуля для программной обработки разных нажатий (короткое, двойное, тройное, долгое нажатие, удержание):
/wb-rules-modules/module_ActionButtons.js

А здесь пример использования вместе со вспомогательными функциями:
/wb-rules/rules_Buttons.js

Предполагается, что вы обновили движок правил до версии 2.0

4 лайка