Отслеживание скачков напряжения

У меня бывают скачки напряжения и я хочу сделать оповещения в телегу когда напряжение выходит за пределы и когда возвращается.

По-быстрому сделал вот так

var L1 = "wb-map3et_145/Urms L1";
var L2 = "wb-map3et_145/Urms L2";
var L3 = "wb-map3et_145/Urms L3";
VoltageTopics = [L1, L2, L3];
VoltageNames =  ["L1", "L2", "L3"];


defineRule("VoltageNotifications", { 
  whenChanged: VoltageTopics,
  then: function (newValue, devName, cellName) {

    var index = VoltageTopics.indexOf(devName + '/' + cellName);
    if (newValue > 240) {   SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном выше нормы. U = ' + dev[VoltageTopics[index]] + 'В');   }
    if (newValue < 200) {   SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном ниже нормы. U = ' + dev[VoltageTopics[index]] + 'В');   }
    if ( 201.999 < newValue && newValue < 239.999) {   SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном вернулось в норму. U = ' + dev[VoltageTopics[index]] + 'В');   }

  }
});

Но оно несколько раз в минуту шлёт сообщения, когда напряжение по каждому случаю IF, а я хотел только одно сообщение когда напряжение вышло за предел и потом одно сообщение когда напряжение вернулось в диапазон.

Как это сделать?

В третьем if каждое логическое условие поместите в свои скобки ((201.999 < newValue) && (newValue < 239.999))
В условиях предупреждения выхода из пределов добавьте условие, что старое значение (oldValue) находилось в пределах. В условии возвращения в пределы добавьте условие, что старое значение было вне пределов.
Поскольку в параметрах функции нет oldValue (и очень жаль!), предлагаю сделать virtualDevice, в которое записывать текущее напряжение после проверок всех условий. Тогда при следующей проверке там окажется прежнее напряжение, то, что нам надо.
Если у вас напряжение склонно “дребезжать” вокруг одного из пределов, вы будете получать сообщения очень часто. Можно разнести пределы выхода из границ и возврата в границы - гистерезис.

Добрый день!

Чтобы избежать отправки множества сообщений при каждом изменении напряжения, можно добавить флаги для каждой фазы, которые будут отслеживать, было ли уже отправлено сообщение о выходе напряжения за пределы или о его возвращении в норму. Таким образом, скрипт будет отправлять сообщения только при смене состояния.

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

Это мне не нужно. Если надо я могу графики в веб интерфейсе посмотреть. Мне скорее нужна выписка, что напряжение было выше нормы с хх:хх по уу:уу.
И потом все эти отрезки времени за месяц сложить.

вот так?
Сначала сравниваем значение текущего напряжения с предыдущим, а потом записываем текущее в ячейку и при следующей проверке оно уже будет предыдущим.
Получается только при первой проверке в ячейке VirtualDevice будет пусто, но это один раз не страшно. Всего одно дополнительное сообщение.

var L1 = "wb-map3et_145/Urms L1";
var L2 = "wb-map3et_145/Urms L2";
var L3 = "wb-map3et_145/Urms L3";
VoltageTopics = [L1, L2, L3];
VoltageNames =  ["L1", "L2", "L3"];
oldTopics = ['Voltage/L1', 'Voltage/L2', 'Voltage/L3'];


defineVirtualDevice("Voltage", { 
    title: "Напряжение",  
    cells: {
      L1: { 
        title: "L1",
        type: "value",
        value: "",
        units: "В",
        precision: 2,
      }, 
      L2: { 
        title: "L2",
        type: "value",
        value: "",
        units: "В",
        precision: 2,
      }, 
      L3: { 
        title: "L3",
        type: "value",
        value: "",
        units: "В",
        precision: 2,
      }
  }
});


defineRule("VoltageNotifications", { 
  whenChanged: VoltageTopics,
  then: function (newValue, devName, cellName) {

    var index = VoltageTopics.indexOf(devName + '/' + cellName);


    if ( (newValue >= 240) && (200 < oldTopics[index]) && (oldTopics[index] < 240) ) {  
     SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном выше нормы. U = ' + dev[VoltageTopics[index]] + 'В');   
    }


    if ( (newValue <= 200) && (200 < oldTopics[index]) && (oldTopics[index] < 240) ) {   
      SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном ниже нормы. U = ' + dev[VoltageTopics[index]] + 'В');   
    }


    if ( (200 < newValue) && (newValue < 240) && (oldTopics[index] <= 200) && (240 <= oldTopics[index])      ) {   
      SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном вернулось в норму. U = ' + dev[VoltageTopics[index]] + 'В');   
    }

    dev[ oldTopics[index] ] = newValue;   // запись напряжения в ячейку виртуального устройства

  }
});

Да, ваш код должен работать.
Остались ли еще вопросы?

нет. буду пробовать

Нет я заметил логическую ошибку в условии.

Вот это непонятно как будет работать, т.к. старое значение сначала должно быть меньше 200, а потом меньше 240

if ( (newValue <= 200) && (200 < oldTopics[index]) && (oldTopics[index] < 240) )

А мне надо чтобы оно было либо меньше 200, либо больше 240
Вот так?

if ( (200 < newValue) && (newValue < 240) && ( (oldTopics[index] <= 200) || (240 >= oldTopics[index]) ) )

Теперь вторая часть задачи: как зафиксировать промежуток времени между сообщением о выходе напряжения за пределы 200…240В и сообщением о возвращении его в норму?

Рекомендую ознакомится с данными примерами правил.

Тут ничего полезного нет.
Я сделал вот так

var L1 = "wb-map3et_145/Urms L1";
var L2 = "wb-map3et_145/Urms L2";
var L3 = "wb-map3et_145/Urms L3";
VoltageTopics = [L1, L2, L3];
VoltageNames =  ["L1", "L2", "L3"];
oldTopics = ['oldVoltage/L1', 'oldVoltage/L2', 'oldVoltage/L3'];


defineVirtualDevice("oldVoltage", { 
    title: "Напряжение OLD",  
    cells: {
      L1: { 
        title: "L1",
        type: "value",
        value: "",
        units: "В",
        precision: 2,
      }, 
      L2: { 
        title: "L2",
        type: "value",
        value: "",
        units: "В",
        precision: 2,
      }, 
      L3: { 
        title: "L3",
        type: "value",
        value: "",
        units: "В",
        precision: 2,
      },
      date: { 
        title: "date",
        type: "value",
        value: "",
        units: "ms",
        precision: 0,
      }
  }
});


defineRule("VoltageNotifications", { 
  whenChanged: VoltageTopics,
  then: function (newValue, devName, cellName) {

    var index = VoltageTopics.indexOf(devName + '/' + cellName);

    if ( (newValue >= 240) && (200 < oldTopics[index]) && (oldTopics[index] < 240) ) {  
     SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном выше нормы. U = ' + dev[VoltageTopics[index]] + 'В');   
     dev['oldVoltage/date'] = new Date();   // записываем время, когда напряжение стало выше 240
    }

    if ( (newValue <= 200) && (200 < oldTopics[index]) && (oldTopics[index] < 240) ) {   
      SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном ниже нормы. U = ' + dev[VoltageTopics[index]] + 'В');  
      dev['oldVoltage/date'] = new Date();   // записываем время, когда напряжение стало ниже 200
    }

    if ( (200 < newValue) && (newValue < 240) && ( (oldTopics[index] <= 200) || (240 >= oldTopics[index]) ) ) {   
      timeDifference = ( new Date() - dev['oldVoltage/date'] ) / (1000*60); // Время отсутствия должного напряжения в минутах
      SendTelegramMsg( 'Напряжение на фазе ' + VoltageNames[index] + ' в Южном вернулось в норму. U = ' + dev[VoltageTopics[index]] + 'В. Время отсутствия должного напряжения ' + timeDifference + ' минут.');   

    }

    dev[ oldTopics[index] ] = newValue;   // запись напряжения в ячейку виртуального устройства

  }
});

Ошибок вроде нет синтаксических, надеюсь, все будет работать корректно.

Могу ли я еще чем-то помочь?

Не работает. Прямо сейчас на одной фазе 245В, а в телеге ни одного сообщения

Добрый день, вот пример
Немного конечно больше пишет в логи, но достаточно наглядно отображает.

// Установим диапазон напряжения
var voltage_min = 215; // 220 - 5
var voltage_max = 225; // 220 + 5
var voltage_exceeded = false; // Флаг для контроля выхода за пределы

// Лог при запуске скрипта
log("Скрипт для мониторинга напряжения запущен.");

// Функция для отправки сообщений в Telegram
function sendTelegramMessage(message) {
    var token = "ВАШ_TG_BOT_TOKEN"; // Токен вашего Telegram бота
    var chat_id = "ВАШ_CHAT_ID"; // Ваш чат или ID пользователя
    var url = "https://api.telegram.org/bot" + token + "/sendMessage?chat_id=" + chat_id + "&text=" + encodeURIComponent(message);
    
    runShellCommand("curl -s -X POST " + url);
    log("Отправлено сообщение в Telegram: " + message); // Лог отправки сообщения
}

// Следим за изменением напряжения
defineRule("voltage_check", {
    whenChanged: "wb-map6s_134/Urms", // Топик с данными напряжения
    then: function (newValue, devName, cellName) {
        var voltage = parseFloat(newValue);
        
        // Логируем текущее значение напряжения
        log("Текущее напряжение: " + voltage + " В");
        
        // Если напряжение вышло за пределы диапазона и еще не было оповещения
        if ((voltage < voltage_min || voltage > voltage_max) && !voltage_exceeded) {
            var message = "Напряжение вышло за пределы: " + voltage + " В";
            sendTelegramMessage(message);
            log(message); // Логируем событие
            voltage_exceeded = true;
        }
        
        // Если напряжение вернулось в норму
        if (voltage >= voltage_min && voltage <= voltage_max && voltage_exceeded) {
            var message = " Напряжение вернулось в норму: " + voltage + " В";
            sendTelegramMessage(message);
            log(message); // Логируем событие
            voltage_exceeded = false;
        }
    }
});
1 лайк

Теперь работает, но это только для одной фазы пример. Я доработал его под 3 фазы.

var voltage_min = 200;
var voltage_max = 240;
var L1 = "wb-map3et_145/Urms L1";
var L2 = "wb-map3et_145/Urms L2";
var L3 = "wb-map3et_145/Urms L3";

log("Скрипт для мониторинга напряжения запущен.");

VoltageTopics = [L1, L2, L3];
VoltageNames =  ["L1", "L2", "L3"];
VoltageExceeded = [false, false, false]; // Флаг для контроля выхода за пределы. Становится true при отправке сообщения о выходе напряжение за пределы нормы.


// Следим за изменением напряжения
defineRule("VoltageNotifications", {
    whenChanged: VoltageTopics, // Топики с данными напряжения
    then: function (newValue, devName, cellName) {
        var voltage = parseFloat(newValue);  // на всякий случай, если в newValue передаётся строка 220В, то parseFloat прочитает из строки только число 220
        var index = VoltageTopics.indexOf(devName + '/' + cellName);
        
        // Логируем текущее значение напряжения
        log("Текущее напряжение: " + VoltageNames[index] + ' = ' + voltage + " В");
        
        // Если напряжение вышло за пределы диапазона и еще не было оповещения
        if ( (voltage < voltage_min || voltage > voltage_max) && !VoltageExceeded[index]) {
            var message = 'Напряжение в Южном вышло за пределы: ' + VoltageNames[index] + ' = ' + voltage + ' В';
            SendTelegramMsg(message);
            log(message); // Логируем событие
            VoltageExceeded[index] = true;
            log(VoltageExceeded[index]); // Логируем событие
        }

        // Если напряжение вернулось в норму
        if (voltage >= voltage_min && voltage <= voltage_max && VoltageExceeded[index]) {
            var message = 'Напряжение в Южном вернулось в норму: ' + VoltageNames[index] + ' = ' + voltage + ' В';
            SendTelegramMsg(message);
            log(message); // Логируем событие
            VoltageExceeded[index] = false;
            log(VoltageExceeded[index]); // Логируем событие
        }
    }
});

Дальше я хотел вычислять время отсутствия нормального напряжения. Внутри первого IF вставляю

var voltageExceededDate = new Date();

а внутрь второго

var voltageReturnDate = new Date();
var noVoltagePeriod = (voltageReturnDate - voltageExceededDate) / (1000*60);

Но почему-то в отличии от Javascript тут время выводится не в миллисекунах и поэтому над ним невозможно совершать математические действия.

voltageExceededDate: 2024-09-10 18:10:29.912+03:00
voltageReturnDate: 2024-09-10 18:10:35.908+03:00
noVoltagePeriod: NaN

Что делать?

Добрый день, ниже несколько обновил свой скрипт.

// Установим диапазон напряжения
var voltage_min = 210; 
var voltage_max = 230;
var voltage_exceeded = false; // Флаг для контроля выхода за пределы
var exceeded_start_time = null; // Время выхода напряжения за пределы

// Лог при запуске скрипта
log("Скрипт для мониторинга напряжения запущен.");

// Настройка токена и ID чата
var telegram_bot_token = "token"; // Токен вашего Telegram бота
var telegram_chat_id = chat_id; // ID чата или пользователя

// Функция для отправки сообщений в Telegram
function sendTelegramMessage(message) {
    var command = 'curl -s -X POST https://api.telegram.org/bot' + telegram_bot_token + '/sendMessage -d chat_id=' + telegram_chat_id + ' -d text="' + encodeURIComponent(message) + '"';
    
    runShellCommand(command, {
        captureOutput: true,
        exitCallback: function (exitCode, stdout, stderr) {
            log("Telegram сообщение отправлено: " + message + ". Код выхода: " + exitCode);
            if (stderr) {
                log("Ошибка отправки в Telegram: " + stderr);
            }
        }
    });
}

// Следим за изменением напряжения
defineRule("voltage_check", {
    whenChanged: "wb-map6s_134/Urms", // Топик с данными напряжения
    then: function (newValue, devName, cellName) {
        var voltage = parseFloat(newValue);
        
        // Логируем текущее значение напряжения
        log("Текущее напряжение: " + voltage + " В");
        
        // Если напряжение вышло за пределы диапазона и еще не было оповещения
        if ((voltage < voltage_min || voltage > voltage_max) && !voltage_exceeded) {
            var message = "⚠️ Напряжение вышло за пределы: " + voltage + " В";
            sendTelegramMessage(message);
            log(message); // Логируем событие
            voltage_exceeded = true;
            exceeded_start_time = new Date(); // Запоминаем время выхода за пределы
        }
        
        // Если напряжение вернулось в норму
        if (voltage >= voltage_min && voltage <= voltage_max && voltage_exceeded) {
            var exceeded_end_time = new Date(); // Время возвращения в норму
            var duration = (exceeded_end_time - exceeded_start_time) / 1000; // Продолжительность в секундах
            
            var message = "✅ Напряжение вернулось в норму: " + voltage + " В. " +
                          "Напряжение было вне диапазона в течение " + duration + " секунд.";
            sendTelegramMessage(message);
            log(message); // Логируем событие
            
            // Сбрасываем флаг и время
            voltage_exceeded = false;
            exceeded_start_time = null;
        }
    }
});

Блин работает. Вчера точно так же делал и new Date() и new Date(milliseconds) пробовал, но не получалось.

Рад, что смог помочь.