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

Понятно. Спасибо огромное. Потихоньку начинают “осмысляться” непонятные “кракозяблы” на импортном языке… Интересно., захватывает … Но меня прямо " с корнем выдирают" из-за компа. (Занимаюсь на работе…) Придётся пока на этом остановиться. Продолжу попозже. Ещё раз огромное спаибо за ликбез…
До встречи !

1 лайк

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

// Включение / выключение правила выключателем на панели Devices
// Создаём виртуальное устройство - выключатель.
defineVirtualDevice("simple_test", {
  title: "Simple test",
  cells: {
    enabled: {
      type: "switch",
      value: false
    },
  }
});

defineRule("heater_control", { //название правила - "контроль обогревателя"
  whenChanged: "wb-adc/R1", //при изменении состояния датчика света
  then: function(newValue, devName, cellName) { //выполняй следующие действия
    if (dev.simple_test.enabled) { // если вируальн. устр-во simple_test  в положении enabled    
      if (dev["wb-adc"]["R1"] > 800) { //если сопротивление датчика больше 800,
        dev["wb-gpio"]["EXT1_R3A6"] = 0; //установи на выходе wb-gpio устройство EXT1_R3A5  
        //в состояние "выключено" 
      } else { // иначе
        if (dev["wb-adc"]["R1"] < 1000) { //если сопротивление датчика больше 1550,
          //(обеспечиваем гистерезис)
          dev["wb-gpio"]["EXT1_R3A6"] = 1; //установи EXT1_R3A1  в состояние "включено" 
        }
      }
    }
  }
});

Наверное криво, что-то может не правильно., но работает. Ещё раз спасибо за помощь!

Неожиданная особенность движка правил. Видимо, стоит добавить в вики.

Все глобальные переменные ОБЩИЕ между скриптами. Алиасы (defineAlias) также создают глобальные переменные. Т.е. два defineAlias или объявление глобальной переменной в разных скриптах могут конфликтовать. Нужно это учитывать при написании правил (например, делать префикс соответствующий скрипту). Долгое время думал, что это странные глюки движка, а все оказалось просто.

У меня в двух скриптах были конструкции типа defineAlias(“sensor”, “wb-mr11_1/Input 2”) (естественно входы были разные).

Не согласен, что НЕОЖИДАННОЕ, я наоборот ожидал и использую это. Но то, что это нужно явно подчеркнуть в вики - согласен

/Детектор присутстствия. В качестве детектора используется Bluetooth beacon с приёмником. Вкл. на D1_IN WB.
При приближении/удалении метки Bluetooth приёмник входит в неустойчивое состояние и появляется подобие дребезга контактов на его выходе.
И в зоне уверенного приёма наблюдаются кратковременные потери метки. (Логировать эти потери хочется, но как это сделать во время задерки не знаю,
полагаю надо использовать таймер с другой логикой, прерыванием, или сделать свой таймер.)
Следовательно надо организовать задерку включения/выключения. (В принципе достаточно и RC ценочки…)
Пример с задержкой http://contactless.ru/wiki/index.php/Движок_правил_wb-rules
см. “Детектор движения c таймаутом”.
/
var DelayIn = 5
1000; //Задержка включения реле при появлении метки.
var DelayOut = 10
1000;//Задержка выключения реле при пропадании метки.
var timer_3_id = null;//Организация таймера.
var FirstRelayStatus = 0;//… для экспериментов.
var RelayStatus = FirstRelayStatus;//При старте делаю реле выключенным, для этого этот статус присваиваем самому реле см. строку 26,50
//ввёл статус для присвоения его нашему реле после перезагрузки WB, т.е. метка в зоне - реле включится, метка вне зоны - реле выключено.

defineRule(“I’m home IN”, {
when: function(){
return dev[“wb-gpio”][“D1_IN”] == 1// Когда появилась метка (начался дребезг)…
},
then: function(){
if (timer_3_id) {clearTimeout(timer_3_id);} //Организация таймера.

	dev["wb-gpio"]["Relay_1"] = RelayStatus;//... статус RelayStatus = 0 присваиваем нашему реле...

	timer_3_id = setTimeout(function() { //...запукаем таймер с задеркой DelayIn по истечение которой выполняем:
		timer_3_id = null;// 1.Организация таймера.
		if(dev["wb-gpio"]["D1_IN"] == 1){//2.Проверяем осталась ли метка в зоне приёма
			if(RelayStatus == 0){//3. Лишний раз не хочу без толку включать уже включенное реле...
				RelayStatus = !RelayStatus;//... но когда реле выключено меняем статус на будущее и
				dev["wb-gpio"]["Relay_1"] = RelayStatus;//включаем реле.
			}
		} 
	},
	DelayIn);// Вот само значение задержки.
	
}

});

defineRule(“I’m home OUT”, {
when: function(){
return dev[“wb-gpio”][“D1_IN”] == 0//Когда пропала метка (начался дребезг): ждём подольше, проверяем статус реле и выключаем реле.
},
then: function(){
if (timer_3_id) {clearTimeout(timer_3_id);}

	dev["wb-gpio"]["Relay_1"] = RelayStatus;

	timer_3_id = setTimeout(function() {
		timer_3_id = null;
		if(dev["wb-gpio"]["D1_IN"] == 0){
			if(RelayStatus == 1){
				RelayStatus = !RelayStatus;
				dev["wb-gpio"]["Relay_1"] = RelayStatus;
			}
		} 
	},
	DelayOut);
}

});

Добрый день! может кто знает? как привязать включение и выключение света к восходу и заходу солнца?

А у вас есть идеи, откуда получать время восхода и заката?

Тут можно посмотреть как рассчитывать позицию солнца.

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

При старте движка правил и раз в сутки (скажем, в полночь: when: cron("0 0 * * *")) определяем время восхода и заката на сегодня - как угодно, хоть стащив curl’ом страничку одного из многочисленных сайтов с этой информацией и выдрав из нее время sed’ом, хоть по предварительно забитой таблице.
Пересчитываем в миллисекунды, вычитаем текущее время, и соответственно выставляем два таймера через startTimer. В обработчиках таймеров делаем нужные действия.

Спасибо за информацию, но я не совсем понимаю, что значит curl’ом и sed’ом. можно по подробнее как это и что это такое?

Например так (координаты в запросе для Москвы, выдает время в миллисекундах с 00:00 GMT+3). Получаем данные curl’ом с http://sunrise-sunset.org/api, с помощью jq достаем из результата в формате json время восхода и заката и приводим в полезный для нас вид.
$ curl -s 'http://api.sunrise-sunset.org/json?lat=37.61778&lng=55.75583&formatted=0' | jq -r '.results | (.sunrise, .sunset) | (.[11:13] | tonumber) as $h | (.[14:16] | tonumber) as $m | (($h+3)*60 + $m) * 60 * 1000' 16500000 65160000
(WB под рукой нет, там довольно старая версия jq и синтаксис может несколько отличаться).
Можно эту команду вызвать из движка правил с помощью spawn с опцией captureOutput и что-то с этими числами сделать полезное.

1 лайк

Может есть такая возможность вычислить и сохранить локально массив с данными о восходе/закате и брать данные из него, т.к. не всегда есть интернет?

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

Помогите, пожалуйста, сделать правило для мигалки! Я написал, чтобы через 10 секунд включалась лампочка и мне нужно, чтобы она отключилась на 3 секунды, а затем снова включилась на 5 секунд. и так 15 раз. после чего обнулить таймер и
выключить лампочку.

var motion_timer_5_timeout_ms = 10 * 1000;
var motion_timer_5_id = null;
 
defineRule("svet_miganie_1", 
{
when: function ()
{
return dev["wb-gpio/EXT6_DR1"] ==1; 
},
then: function() 
{
if (motion_timer_5_id)
{
clearTimeout(motion_timer_5_id);
}
motion_timer_5_id = setTimeout(function () 
{
    dev["wb-gpio"]["EXT1_R3A2"] = 1;
motion_timer_5_id = null;   
},
motion_timer_5_timeout_ms);
}
});

Прочитайте пожалйуста первое сообщение темы и пропустите код через http://jsbeautifier.org/ , прежде чем постить, если он у вас отформатирован по левому краю.

Подскажите пожалуйста,
есть ли какой-то способ ловить двойные или тройные нажатия на нефиксирующуюся кнопку?
И зафиксировать “долгое” нажатие?

Спасибо

А кнопка куда подключена?

Пока на сухой контакт wb5, но это временное решение - кнопок планируется штук 20 и я пока не понимаю куда их все подключить

Можно расширять, через WBIO-DI-DR-16 например.

Про двойные и долгие нажатия: можно поэкспериментировать с правилами. Сделайте правило с whenChanged на канал кнопки, добавьте отладочный вывод на срабатывание (log). Он должен выводиться на каждое нажатие кнопки, хоть и с небольшой (<~100ms) задержкой. Дальше можете в правиле завести глобальную переменную-счётчик и сбрасывать её значение по таймеру.
Как-то так (код не проверял):

    (function() { //don't touch this line
      var button_counter = 0;
      var counter_clear_timer_id = null;

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

          // если поймали нажатие, увеличиваем счётчик
          if (newValue) {
            button_counter += 1;
          }
          
          // счётчик равен двум, значит было двойное нажатие
          if (button_counter == 2) {
            log("double press!");
          }
 
          // после каждого нажатия сбрасываем таймер (удяляем старый)
          if (counter_clear_timer_id) {
            clearTimeout(counter_clear_timer_id);
          }
         
          // и взводим новый таймер, который сбросит счётчик через 2 секунды
          counter_clear_timer_id = setTimeout(function() {
            button_counter = 0;
          }, 2000);
        }
      });
    })();
1 лайк