Управление группами Dali в EcoDim

Добрый день.
Пытаюсь настроить правила для управления группами освещения сконфигурированными на EcoDim.

Скомпилировал правило, но в логах выдаёт ошибку:

ERROR: [rule error] ECMAScript error: TypeError: invalid base value
duk_hobject_props.c:2000
getDevValue /usr/share/wb-rules-system/scripts/lib.js:70 preventsyield
switchDimmer_Hall /etc/wb-rules/Выключатели.js:153 preventsyield
apply native strict preventsyield
anon /etc/wb-rules/Выключатели.js:69 strict preventsyield
30-06-2024 22:00:42.146 [wb-rules] ERROR: control ecodim_dali_gw2_21/Group 3 Brightness SetValue() error: can’t convert control value ‘true’ (type bool) to datatype ‘2’

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

С wb-rules и ECMA только разбираюсь.

Само правило:

(function () {
'use strict';

var ActionButtons = {};

ActionButtons.onButtonPress = function (trigger, action, timeToNextPress, timeOfLongPress) {
    log.info("LongPress ActionButtons.onButtonPress")//Это лог. Он попадает в /var/log/messages
    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) {
          log.info("LongPress defineRule")//Лог

            // 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(
"wb-gpio/EXT1_IN2",          //Вход, за которым следим.
{
    singlePress: {
       func: switchDimmer_Hall,
       prop: ["ecodim_dali_gw2_21", "Group 3 Brightness"]
    },
    doublePress: {
       func: switchDimmer_Hall, 
      prop: ["ecodim_dali_gw2_21", "Group 3 Brightness"]
    },
    longPress: {
       func: switchDimmer_Hall, 
       prop: ["ecodim_dali_gw2_21", "Group 3 Brightness"]

    }
},
300, 1000
);


/**
* Helper Functions
*/
function switchRelay(device, control) { //Принимает в параметрах устройство и выход. Переключает состояние выхода на противоположное.
  log.info("LongPress switchRelay" ,device, control)//Лог
  dev[device][control] = !dev[device + "/" + control];
}

function switchDimmer_Hall(relayDevice, relayControl, dimmerDevice, range) {
   dev[relayDevice][relayControl] = true;
   if (dev[dimmerDevice + "/Brightness"+range] !== "0") {
     dev[dimmerDevice]["Brightness"+range] == "254";
   }
   else {
     dev[dimmerDevice]["Brightness"] = dev[relayDevice + "/Brightness"];
   }
}

Добрый день.
Простите, но не понимаю вашего кода…

Дайте какой-либо минимальный пример для воспроизведения, например вызов этой функции с параметрами.

Я поправил код вспомогательной функции - не много изначально не то добавил :slight_smile:

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

Сама функция вызывается при нажатии клавиши в этой части правила:

ActionButtons.onButtonPress(
"wb-gpio/EXT1_IN2",          //Вход, за которым следим.
{
    singlePress: {
       func: switchDimmerDali,
       prop: ["ecodim_dali_gw2_21", "Group 3 Brightness"]
    },
    doublePress: {
       func: switchDimmerDali, 
      prop: ["ecodim_dali_gw2_21", "Group 3 Brightness"]
    },
    longPress: {
       func: switchDimmerDali, 
       prop: ["ecodim_dali_gw2_21", "Group 3 Brightness"]

    }

Логика: при нажатии, я вызываю функцию и идёт проверка - если параметр “Brightness” не равен нулю, то я ему выставляю значение “0”
Это как раз в этой части кода происходит:

   if (dev[dimmerDevice + "/Brightness"] !== "0") {
     dev[dimmerDevice]["Brightness"] == "0";
   }

В противном случае я инвертирую это значение в этой строчке:

   else {
     dev[dimmerDevice]["Brightness"] = dev[relayDevice + "/Brightness"];
   }

И вот тут начинает ругаться как раз, что не может инвертировать.
Возможно имеет смысл жестко объявить переменную “Brigtness” с диапазоном от “0” до “254”?

И прописать место логических значений простыми числами?

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

Т.к. у меня такое же правило сделано на мастер выключатель и не ругается, правда там вместо ecodim реле wb-mrps6…

Логическое NotEq - записывается как “!=”

Строчка

dev[dimmerDevice]["Brightness"] == "0";

это сравнение значения ‘dev[dimmerDevice][“Brightness”]’ со строкой “0”
То есть результатом ее выполнения будет true/false. Значение, соответственно, не изменится.

Тут про это: Операторы сравнения

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

Ок, понял логику в if сначала идёт булевое значение и всегда будет True/False в зависимости от чего запускается действие в блоке IF или Else.

Но ни как не могу понять, как в правиле присвоить числовое значение уровню яркости группе светильников :frowning:

Точнее сформулирую, вопрос с правильным написанием в хелпер функции.

Предполагаю, что это должно как-то так выглядеть:


var a= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; // диапазон номеров групп



if (dev[dimmerDevice][Group {a) brightness] !=0) {
    dev[dimmerDevice][Group {a) brightness]=0;
}
   else {

dev[dimmerDevice][Group {a) brightness]=255;
}

Массив надо проитеировать, например так:

var a= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; // диапазон номеров групп


for (i in a) {
  log.info(i)
}

Тут синтаксическая ошибка, надо

    dev[dimmerDevice]["Group "+a+" brightness"]=0;

Добрый день.
Спасибо! С Dali получилось, код такой:

function switchDimmDali(dimmerDali, groupControl) {

  dev[dimmerDali][groupControl] = true;    
  
var a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; // диапазон номеров групп  


for (i in a) {
          log.info(i) // лог итерации
      } // итерация массива

      if (dev["ecodim_dali_gw2_21" + "/Group "+i+" Brightness"] == dev[dimmerDali][groupControl]) {
          dev[dimmerDali][groupControl] = 0;
           }
           else {
              dev[dimmerDali][groupControl] = 254;
           }

}

Но возник вопрос, почему такая же конструкция с wb-led не работает?

Делаю такую функцию:

function switchLedNew(ledDevice, ledControl) {
 dev[ledDevice][ledControl] = true; 
  
var numbers = []; // массив для номеров устройств
var numbersChannel = [0, 1, 2, 3, 4]; // массив номеров каналов

  for (var i = 1; i <= 256; ++i) {
    numbers.push(i);
    log.info(numbers);
  } ; // заполнение массива номеров устройств

for (n in numbersChannel) {
          log.info(n)
        } // заполнение массива номеров каналов

      if (dev["wb-led_"+i+"/Channel "+n+" Brightness"] == dev[ledDevice][ledControl]) {
          dev[ledDevice][ledControl] = 0;
      }
          else {
              dev[ledDevice][ledControl] = 100; 
              }
}

И получаю ошибку:

2024-07-07 20:31:11failed to SetValue for unexisting control wb-led_29//Channel 1 Brightness: 0

При этом, если задать реальные значения, а не в виде аргументов функции, всё нормально происходит.

Хотя, кажется я понял в чём ошибка - только сейчас увидел в тексте, что в ошибке перед контролом две косые черты - поищу ошибку в скрипте

Да, про это и хотел написать.

Столкнулся с проблемой - действие в блоке if не выполняется. Сначала было для LED, после того как поправил ошибку.

Думал может глюк какой-то, обновил систему - мало ли, теперь блок кода перестал выполнятся и для dali, срабатывает только блок else.

Куда копать?

Код первой halper функции:



function switchDimmDali(dimmerDali, groupControl) {

  dev[dimmerDali][groupControl] = true;    
  
var a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; // диапазон номеров групп  


    for (i in a) {
          log.info(i) // лог итерации
      } // итерация масива

      if (dev["ecodim_dali_gw2_21" + "/Group "+i+" Brightness"] == dev[dimmerDali][groupControl]) {
          dev[dimmerDali][groupControl] = 0;
           }
           else {
              dev[dimmerDali][groupControl] = 254;
           }

}

Вызывается она, соответственно такой конструкцией - таких функций несколько в скрипте:

ActionButtons.onButtonPress(
"wb-gpio/EXT1_IN10",          //Вход, за которым следим, туалет, верхний свет
{
    singlePress: {
       func: switchDimmDali,
       prop: ["ecodim_dali_gw2_21", "Group 2 Brightness"]
    },
    doublePress: {
       func: switchDimmDali, 
       prop: ["ecodim_dali_gw2_21", "Group 2 Brightness"]
    },
    longPress: {
       func: switchDimmDali, 
       prop: ["ecodim_dali_gw2_21", "Group 2 Brightness"]
    }
},
 
300, 1000
);

Код второй хелпер функции:


function switchLed(ledDevice, ledControl) {
dev[ledDevice][ledControl] = true; 
  
var numbers = []; // массив для номеров устройств
var numbersChannel = [0, 1, 2, 3, 4]; // массив номеров каналов

  for (var i = 1; i <= 256; ++i) {
    numbers.push(i);
    log.info(numbers);
  } ; // заполнение массива номеров устройств

  for (n in numbersChannel) {
          log.info(n)
        } // заполнение массива номеров каналов

      if (dev["wb-led_"+i+"/Channel "+n+" Brightness"] == dev[ledDevice][ledControl]) {
            dev[ledDevice][ledControl] = 0;
          }
          else {
              dev[ledDevice][ledControl] = 50; 
              }
}

Вызывается она соответственно:

ActionButtons.onButtonPress(
"wb-gpio/EXT1_IN1",          //Вход, за которым следим, парящий потолок в коридоре
{
    singlePress: {
       func: switchLed,
       prop: ["wb-led_29", "Channel 1 Brightness"]
    },
    doublePress: {
       func: switchLed, 
       prop: ["wb-led_29", "Channel 1 Brightness"]
    },
    longPress: {
       func: switchLed, 
       prop: ["wb-led_29", "Channel 1 Brightness"]
    }
},
 
300, 1000
);

Может попробовать заново правило создать или разбить на несколько отдельных правил?

Кажется я слишком много правил старое правило :slight_smile:

  • пересоздал с новым именем и заменил операцию сравнения “==” в блоках
    dev["wb-led_"+i+"/Channel "+n+" Brightness"] == dev[ledDevice][ledControl]

и

dev["ecodim_dali_gw2_21" + "/Group "+i+" Brightness"] == dev[dimmerDali][groupControl]

на операцию присвоения “=”

с чем была связана бага, не знаю - но оно заработало.

P.S. До этого так не работало

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

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

Почему так - не понял.

Вот тут описано что происходит.
То есть по факту в условии все-таки присваивается значение.

Да, спасибо.

В итоге я разрешил все хелпер функции через “!==0” и получилось две такие хелпер функции:

unction switchLed(ledDevice, ledControl) {
  
  dev[ledDevice][ledControl] = true; 
  

      if (dev[ledDevice][ledControl] !==0) {
            dev[ledDevice][ledControl] = 0;
          }
          else {
              dev[ledDevice][ledControl] = 100; 
              }
}

function switchDimmDali(dimmerDali, groupControl) {

  dev[dimmerDali][groupControl] = true;    
  

      if (dev[dimmerDali][groupControl] !==0) {
          dev[dimmerDali][groupControl] = 0;
           }
           else {
              dev[dimmerDali][groupControl] = 254;
           }
}

Но потом столкнулся с багой - в некоторых случаях игнорируется запуск функции при срабатывании триггера.

Например у меня функция на этом входе:
"wb-gpio/EXT2_IN12"

и не важно, копирую я название топика или вношу его руками.

При этом в интерфейсе видно, что дергается переключатель в realy & etf

Эта бага полечилась перезагрузкой WB - видимо, что-то залипло …