Универсальный виртуальный счетчик

Итак, сегодня в рубрике “сову на глобус” представляю свой таймер. Вдруг кому пригодится. Как минимум зажигать свет по движению и продлевать горение, если кто-то все еще бродит, упрощает.

var TmrCnt = 0;

function makeVirtualTimer(deviceName, seconds) {
   defineVirtualDevice(deviceName, {
      title: {en: "Virtuall Timer " + deviceName + " " + seconds + " sec", ru: "Виртуальный таймер " + deviceName + " " + seconds + " сек" } ,
      cells: {
          enabled: {
            title: {'en': 'Enabled', 'ru': 'Запущен'},
            type: "switch",
            value: false,
          },
          reset: {
            title: {'en': 'Reset', 'ru': 'Сброс'},
            type: "switch",
            value: false,
          },
          state: {
            title: {'en': 'Current count', 'ru': 'Текущее значение'},
            type: "value",
            value: seconds,
          },
          tmrValue: {
            title: {'en': 'Timer Value', 'ru': 'Размер таймера'},
            type: "value",
            value: seconds,
          },
      },
  });


defineRule(deviceName + "_timer_rule", 
           {
             when: cron('@every 1s'),
             then: function () 
             {
               if(dev[deviceName + "/enabled"] == true)
               {
                 dev[deviceName + "/state"] -= 1;
                 if(dev[deviceName + "/reset"] == true)
                 {
                   dev[deviceName + "/reset"] = false;
                   dev[deviceName + "/state"] = dev[deviceName + "/tmrValue"];
                 }
                 if(dev[deviceName + "/state"] < 0)
                 {
                   dev[deviceName + "/enabled"] = false;
                   dev[deviceName + "/state"] = dev[deviceName + "/tmrValue"];
                 }
               }
             }
           }
          );
}

makeVirtualTimer("timer1", 10);
makeVirtualTimer("timerGoodByeAmerica", 30);
makeVirtualTimer("timerKladovka", 30);

Практическое применение c MSW:

//имя датчика
var motionDevName = "klad-msw"; 
//чувствительность датчика
var motionThreshold = 60; 
//имя девайса, который надо зажигать
var LampVirtDevName1 = "bedroom_kladovka_light"; 

defineRule("motion_detector_kladovka_on", {
    whenChanged: motionDevName + "/Current Motion",
    then: function (newValue, devName, cellName) {
        if (newValue > motionThreshold) {
            dev["timerKladovka/enabled"] = true;
            dev["timerKladovka/reset"] = true;
        }
    },
});

defineRule("motion_detector_kladovka_light_ctl", {
    whenChanged: "timerKladovka/enabled",
    then: function (newValue, devName, cellName) {
      dev[LampVirtDevName1+"/state"] = dev["timerKladovka/enabled"];
    },
});

4 лайка

Спасибо, что делитесь своими наработками!

Спасибо большое, очень хороший и полезный пример, каких немного на форуме! Добавил в закладки.

Несколько вопросов: А насколько с точки зрения “философии” программирования “хорошо” запускать столько крон-задач, сколько у меня существует таймеров?..

Не эффективнее ли сделать тикер на 1с и внутрь этого тикера дописывать декремент на каждый счетчик, который нужен…

А так получается создается крон на каждый таймер и еще и “висит” в памяти, даже если эти счетчики не используются…

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

Добавил свою версию кода. Показывает отработавшее время. Сброс можно сделать из любого состояния. Более точно работает определение окончания таймера. Комменты в коде.

// таймер удобно использовать для тех случаев, где необходимо менять время работы для таймера и контролировать оставшееся время работы. 
//Например, удобно использовать для полуавтоматического полива - на HMI можно выбрать длительность полива и контролировать оставшееся время. при необходимости, скорректировать время полива

function makeVirtualTimer(deviceName, seconds) {
    defineVirtualDevice(deviceName, {
        title: {
            en: "Virtuall Timer " + deviceName + " " + seconds + " sec",
            ru: "Виртуальный таймер " + deviceName + " " + seconds + " сек"
        },
        cells: {
            enabled: {
                title: {
                    'en': 'Enabled',
                    'ru': 'Запущен'
                },
                type: "switch",
                value: false,
            },
            reset: {
                title: {
                    'en': 'Reset',
                    'ru': 'Сброс'
                },
                type: "switch",
                value: false,
            },
            state: {
                title: {
                    'en': 'Current count',
                    'ru': 'Оставшееся значение'
                },
                type: "value",
                value: seconds,
                readonly: false
            },

            countedState: { // подсчет "моточасов" работы
                title: {
                    'en': 'Current count',
                    'ru': 'Насчитанное значение'
                },
                type: "value",
                value: 0,
                // readonly: false
            },
            tmrValue: {
                title: {
                    'en': 'Timer Value',
                    'ru': 'Уставка таймера'
                },
                type: "value",
                value: seconds,
                readonly: false
            },
        },
    });


    defineRule(deviceName + "_timer_rule", {
        when: cron('@every 1s'),
        then: function() {
            if (dev[deviceName + "/enabled"] == true) {
                dev[deviceName + "/state"] -= 1;
                dev[deviceName + "/countedState"] += 1;

                if (dev[deviceName + "/state"] < 1) { // поправлена проверка, теперь считает ровное количество секунд
                    dev[deviceName + "/enabled"] = false; // выключаем, но не сбрасываем счетчик "моточасов"

                }
            }
        },



    });

    defineRule(deviceName + "_counterStart", {
        whenChanged: deviceName + "/enabled",
        then: function(newValue, devName, cellName) {
            if (newValue == 1) {

                if (dev[deviceName + "/state"] == 0) { 
                    dev[deviceName + "/countedState"] = 0; // при запуске сбрасывать счетчик "моточасов" только в случае, если таймер досчитал до конца
                    dev[deviceName + "/state"] = dev[deviceName + "/tmrValue"];
                }
            }
        }
    });

    defineRule(deviceName + "_counterReset", { // сброс счетчика возможен независимо от того, был ли активирован счетчик
        whenChanged: deviceName + "/reset",
        then: function(newValue, devName, cellName) {
            if (newValue == 1) {

                   dev[deviceName + "/reset"] = false;
                    dev[deviceName + "/state"] = dev[deviceName + "/tmrValue"];
                    dev[deviceName + "/countedState"] = 0;
            }
        }
    });
  


}

//makeVirtualTimer("timer1", 10);
//makeVirtualTimer("timerGoodByeAmerica", 30);
makeVirtualTimer("timerKladovka", 10);
1 лайк

На самом деле это уже не сильно актуально, так как в testing доступны теперь Сценарии. Там в управлении светом уже реализована такая логика. Так что не уверен, что эта тема актуальна с появлением wb-scenarios.

Вот так:

       ```

Либо кнопкой в панели
Screenshot_20250623_190159