Мониторинг доступности IP, портов и сайтов с помощью NodeJS и VizIoT

Ваш домашний сервер снова оффлайн? IP-камера перестала транслировать видео? Интернет пропал полностью или просто не открывается один сайт? Чтобы получать ответы на эти вопросы мгновенно, можно создать простую, но эффективную систему мониторинга.

В этой статье мы настроим скрипт, который поддерживает работу на Windows, macOS и Linux. Он будет регулярно проверять доступность любых интернет-ресурсов и отправлять данные на сервер VizIoT.

Как это работает: ICMP, TCP и HTTP

Для максимально точного мониторинга наш скрипт использует три разных метода проверки. Это важно, так как устройство может «пинговаться», но при этом его конкретный сервис (например, сайт или видеотрансляция) может не работать.

  1. ICMP (Ping): Классическая проверка доступности устройства по IP-адресу или домену. Подходит для роутеров, серверов и общих проверок связи.
  2. TCP (Port Check): Позволяет проверить доступность конкретного сервиса. Например, работает ли порт 554 на IP-камере (RTSP) или порт 3306 у базы данных. Если порт открыт — сервис жив.
  3. HTTP/HTTPS: Проверка веб-сайтов. Скрипт запрашивает содержимое страницы. Это полезно, если сервер работает, но сам сайт выдает ошибку.

Почему мы используем эти инструменты?

Для работы скрипта нам понадобятся две ключевые библиотеки:

  • Пакет ping: Это удобная обёртка, которая позволяет Node.js использовать системные утилиты проверки связи. Она избавляет нас от написания сложного кода для каждой ОС.
  • Пакет viziot-mqtt-client-nodejs: Официальный клиент для платформы VizIoT. Он берет на себя всю работу по протоколу MQTT: шифрование, поддержку соединения и надежную отправку ваших данных на графики.

Анализ данных: Как отличить проблему в сети от поломки сервиса

Скрипт не просто отправляет «сухие» цифры, он помогает вам стать «сетевым детективом». Посмотрев на график в VizIoT, вы сможете сразу определить источник проблемы:

  • 📈 Резкие скачки (лаги) по ВСЕМ целям сразу: Проблема на вашей стороне. Скорее всего, перегружен ваш роутер, кто-то в сети забил канал загрузкой файлов или возникли проблемы у вашего интернет-провайдера.
  • 📉 Скачок или обрыв только по ОДНОЙ цели: Ваша сеть в порядке. Проблема именно на целевом устройстве или сервере (например, зависла камера или упал конкретный сайт).

Для точности замеров: Скрипт делает три замера для каждой цели и высчитывает среднее арифметическое. Это исключает случайные погрешности (одиночные «вылеты» пакетов) и дает вам плавный, реалистичный график задержки.


**Конечный результат: Интерактивный виджет

Прежде чем мы перейдем к коду, посмотрите, как выглядит готовый виджет.

1. Живой виджет мониторинга

Что можно отслеживать в такой конфигурации:

  • 🌐 Google.com — эталонный ресурс для проверки, есть ли у вас вообще доступ в интернет.
  • 🏠 Локальный IP-адрес — контроль доступности устройств внутри вашей сети (например, NAS или роутера).
  • 📹 RTSP-порт IP-камеры — проверка, работает ли видеотрансляция (стандартный порт 554).
  • 🌍 Внешние IP-адреса — контроль доступности филиалов и сервисов.

Как интерпретировать график:

  • Положительные значения — время отклика в миллисекундах (чем ниже, тем лучше).
  • Значение -1 — ресурс недоступен.
  • 📊 Резкие скачки — возможные проблемы с сетью или перегрузка сервера.

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

2. Уведомления о сбоях в Telegram

Видеть проблему на графике — это хорошо, а получать мгновенное уведомление о ней — еще лучше. VizIoT позволяет настроить оповещения, которые придут вам в Telegram или на E-mail, как только один из ваших ресурсов станет недоступен, а также когда он вернется в онлайн.

pinger_test_notif

Заинтересовались? Давайте настроим такую систему шаг за шагом.


Инструкция по настройке

Шаг 1: Настройка устройства в VizIoT

  1. Создайте новое устройство с именем VizIoTPinger.
  2. В разделе Основные настройки скопируйте Ключ доступа и Пароль доступа. Они понадобятся скрипту для авторизации.

Шаг 2: Установка Node.js

Наш код кроссплатформенный и одинаково хорошо работает на Windows, Linux и macOS.

  • Windows: Скачайте установщик с официального сайта.
  • Linux/macOS: Рекомендуем использовать NVM:
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
    nvm install 24

Шаг 3: Создание проекта и установка библиотек

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

mkdir ./VizIoTPinger && cd ./VizIoTPinger
npm init -y
npm i ping viziot-mqtt-client-nodejs

Шаг 4: Скрипт мониторинга

Создайте файл main.js.

nano ./main.js

Обратите внимание на функцию runMeasurements — именно она отвечает за тройную проверку для точности данных.

const net = require('net')
const ping = require('ping')
const http = require('http')
const https = require('https')
const viziotMQTT = require('viziot-mqtt-client-nodejs')

// ================= SETTINGS =================
// Paste your VizIoT device key and password here
const DEVICE_KEY = '_____________'
const DEVICE_PASSWORD = '______________________'

const CHECK_INTERVAL_SEC = 3 * 60 // Interval between checks in seconds

// ================= TARGETS =================
// key    — the parameter name that will appear in VizIoT
// target — IP address, domain, or URL
// port   — port (optional, for TCP ping)
const TARGETS = [
    { key: 'home_router_latency', target: '192.168.2.1' },
    { key: 'cloudflare_dns_latency', target: '1.1.1.1' },
    { key: 'google_dns_latency', target: '8.8.8.8' },
    { key: 'google_latency', target: 'google.com' },
    { key: 'ipCamera_latency', target: '192.168.0.158', port: 554 },
    // { key: 'my_website', target: 'https://my-site.com' },
]

// ================= MQTT =================
const client = new viziotMQTT(DEVICE_KEY, DEVICE_PASSWORD)

// ================= UTILITIES =================
// Auxiliary function for performing measurements
async function runMeasurements(measureFn, attempts = 3) {
    const results = []

    for (let i = 0; i < attempts; i++) {
        results.push(await measureFn())
    }

    const valid = results.filter((r) => r > -1)

    if (valid.length === 0) {
        return { latency: -1 }
    }

    const avg = valid.reduce((a, b) => a + b, 0) / valid.length
    return { latency: Math.round(avg) }
}

// Function for checking ICMP (ping)
function checkICMP(item) {
    const measure = () =>
        new Promise((resolveRequest) => {
            ping.promise
                .probe(item.target, {
                    timeout: 5,
                })
                .then(function (res) {
                    resolveRequest(res.alive ? res.time : -1)
                })
        })
    return runMeasurements(measure)
}

// Function for checking TCP
function checkTCP(item) {
    const measure = () =>
        new Promise((resolveRequest) => {
            const socket = new net.Socket()
            const start = Date.now()

            socket.setTimeout(5000)

            socket.connect(item.port, item.target, () => {
                socket.destroy()
                resolveRequest(Date.now() - start)
            })

            socket.on('error', (e) => {
                console.log('Socket error:', e)
                socket.destroy()
                resolveRequest(-1)
            })

            socket.on('timeout', () => {
                console.log('Socket timeout')
                socket.destroy()
                resolveRequest(-1)
            })
        })

    return runMeasurements(measure)
}

// Function for checking HTTP/HTTPS
function checkURL(item) {
    const measure = () =>
        new Promise((resolveRequest) => {
            const start = Date.now()
            const lib = item.target.startsWith('https') ? https : http

            const req = lib.get(item.target, { timeout: 5000 }, (res) => {
                res.resume()
                resolveRequest(Date.now() - start)
            })

            req.on('error', () => resolveRequest(-1))
            req.on('timeout', () => {
                req.destroy()
                resolveRequest(-1)
            })
        })

    return runMeasurements(measure)
}
// ================= CHECKS =================

let isWorking = false
async function runChecks() {
    // Preventing overlapping launches
    if (isWorking) return
    isWorking = true

    console.log('🔍 Starting checks...')

    const payload = {}

    for (const item of TARGETS) {
        let result
        if (item.target.startsWith('http')) {
            result = await checkURL(item)
        } else {
            if (item.port) {
                result = await checkTCP(item)
            } else {
                result = await checkICMP(item)
            }
        }

        payload[item.key] = result.latency

        const status = result.latency >= 0 ? '✅' : '❌'
        console.log(`${status} ${item.key}: latency=${result.latency} ms`)
    }
    isWorking = false
    console.log('📨 Sending data to VizIoT:', payload)
    // client.sendDataToVizIoT(payload, (err) => {
    //  if (err) console.error('Error sending data:', err)
    // })
}

// ================= START =================
function main() {
    console.log('🔗 Try Connected to VizIoT')

    // client.connect(() => {
    console.log('✅ Connected')
    console.log('🚀 Pinger starting…')

    runChecks()
    setInterval(runChecks, CHECK_INTERVAL_SEC * 1000)
    // })
}
main()

Шаг 5: Автозапуск через PM2

Чтобы ваш скрипт работал 24/7 и запускался сам после перезагрузки компьютера, используйте менеджер процессов PM2:

npm install pm2 -g
pm2 start main.js --name "VizIoTPinger"
pm2 save
pm2 startup

Шаг 6: Настройка виджетов и уведомлений в VizIoT

Теперь, когда данные поступают, осталось настроить интерфейс.

Добавление виджетов

  1. Создайте панель, например «Доступность интернет-сервисов».
  2. Добавьте виджет «Время пинга».
    • Тип: График с областями суммирующий
    • Устройство: VizIoTPinger
    • Параметры: home_router_latency, google_latency, ipCamera_latency

Настройка уведомлений

  1. Перейдите в раздел Уведомления и нажмите Добавить уведомление.
  2. Устройство: VizIoTPinger.
  3. Параметр: Выберите тот, который хотите отслеживать (например, ipCamera_latency).
  4. Условие: Тип меньше (<), значение 0.
  5. Включите опцию «Уведомлять при возвращении в диапазон».
  6. Выберите способ доставки (Telegram, E-mail) и сохраните.

Подробную инструкцию по настройке уведомлений можно найти в этой статье.

Настройка уведомлений в Telegram

Теперь, когда данные передаются, зайдите в панель VizIoT:

  1. Перейдите в Уведомления -> Добавить.
  2. Выберите ваше устройство и параметр (например, ipCamera_latency).
  3. Установите условие: Значение < 0 (это значит, что сервис недоступен).
  4. Включите уведомление в Telegram.

Теперь, как только ваша камера «отвалится» или сайт перестанет открываться, вы получите сообщение об этом в течение минуты!