Як я в еру вайбкодингу перевіряю, чи є світло вдома
Вітаю! Мене звати Олександр Корнієнко, я працюю Senior+ React.JS Software Developer у компанії Yalantis. У цій статті хочу поділитися рішенням простої, але болючої проблеми — як зрозуміти, чи є світло вдома, коли тебе там немає.

Проблема
Офіційні графіки відключень електроенергії іноді не відповідають дійсності, а живуть своїм життям. Тому потрібно щось надійніше.
Теорія вирішення проблеми
Нам потрібний девайс, який:
- Має доступ до інтернету.
- Працює тільки коли є світло і перестає працювати, коли світла немає
(або напряму може показувати його наявність). - З яким можна налагодити зв’язок через застосунок або сайт.
Логіка максимально проста:
Девайс онлайн → світло є.
Девайс офлайн → світла немає.
Практичне вирішення проблеми
Розумного будинку в мене немає, тому особливо розгулятися ніде. Але є банальна і дуже корисна річ: Wi-Fi роутер, і не один.
Коли є світло, то і Wi-Fi роутер працює. Коли світла немає, то немає нічого 🙂
Мій провайдер надав статичну IPv4-адресу, до якої можна достукатися за межами локальної мережі. Це і був ключовий момент.
Я зробив наступне:
- Знайшов локальну IP адресу роутера.
- Зробив її статичною.
- Прив’язав її до глобального IPv4, який надав мені провайдер за допомогою Port Mapping.
Схематично це виглядало так:
Name: Main Router Protocol: TCP External IP Address: 1.2.3.4 (глобальний IPv4) External PORT: 12345 Internal Server IP: 192.168.0.2 Internal Port: 80
Також у налаштуваннях обмежив, хто саме може звертатися до цього IP:Port. У моєму випадку — це IP-сервера на хмарі.
Перевірка доступності
Тепер, щоб перевірити, чи працює роутер, достатньо виконати команду:
nc -zv <IP> <port> (MacOS)
Безперебійний інтернет (GPON) з підтримкою PoE та резервного живлення (UPS / інвертор)
У 2026 році провайдери та користувачі вже підготувалися до відключень електроенергії, і тепер є можливість підключення безперебійного інтернету. Цією опцією користується все більше людей: світла немає, а інтернет працює. У такому випадку перевіряти наявність світла через роутер немає сенсу. Потрібно дивитися на інші пристрої:
- Розумні вимикачі чи пилососи.
- Комп’ютери з живленням 24/7.
- Телевізори.
- Додаткові роутери.
- Інші мережеві девайси.
Особисто у мене також підключений безперебійний інтернет, тому я перевіряю наявність світла за допомогою роутера, який знаходиться зовні будинку. Він відключається разом із електрикою.
Підключення пристрою до глобальної мережі
Нехай я вирішив робити перевірку через телевізор, а не роутер. Алгоритм простий:
- Знаходимо локальний IPv4 телевізора. Наприклад, 192.168.0.3.
- Присвоюємо йому статичну адресу, щоб після перезавантаження роутера DHCP не змінював локальний IP.
- Прив’язуємо локальний IPv4 до глобального IPv4. І відповідно прокидаємо порт для зовнішнього доступу.
Name: Samsung TV Protocol: TCP External IP Address: 1.2.3.4 (глобальний IPv4) External PORT: 12333 Internal Server IP: 192.168.0.3 Internal Port: ?
Залишається зрозуміти, який Internal Port потрібно обрати. З роутером було зрозуміло, для HTTP використовується за замовчуванням
sudo masscan 192.168.0.3 p1-65535 --rate 20000 Discovered open port 8187/tcp on 192.168.0.3 Discovered open port 7678/tcp on 192.168.0.3 Discovered open port 8009/tcp on 192.168.0.3 Discovered open port 7443/tcp on 192.168.0.3 Discovered open port 8008/tcp on 192.168.0.3 Discovered open port 8001/tcp on 192.168.0.3
Маючи порти, перевіряємо, який нам найбільше підходить. Я обрав 8001.
Name: Samsung TV Protocol: TCP External IP Address: 1.2.3.4 (глобальний IPv4) External PORT: 12333 Internal Server IP: 192.168.0.3 Internal Port: 8001 Тепер отримати доступ до 192.168.0.3:8001 можна через 1.2.3.4:12333.
Вебінтерфейс
Я зробив мінімалістичний фронтенд на HTML + CSS + JS. Концепція наступна:
index.html відправляє GET-запит на http://IPv4:Port:
- якщо відповідь є — перенаправлення на online.html (світло є ✅);
- якщо відповіді немає — перенаправлення на offline.html (світла немає ❌).

Але є нюанс — CORS
Концептуально все працює, але на практиці браузер одразу викидає CORS error.
Причина очевидна: IPv4:Port не налаштований на CORS, і браузер блокує запити з іншого домену. Тому довелося скористатися класичним рішенням — proxy server. Проксі приймає запит з фронтенду, звертається до роутера вже зі свого IP, а потім повертає відповідь назад у браузер без CORS-болю і зайвих танців з бубном.
Трохи концептуального коду для ясності
Щоб ідея була максимально прозорою, покажу спрощений варіант коду, який ілюструє весь потік — від відкриття сторінки до отримання відповіді «є світло / немає світла».
Початковий HTML
Потрібен стартовий HTML-файл, який запускає перевірку одразу після відкриття сторінки.
<body> <div class="loader-container"> <div class="spinner"></div> <h1>Перевіряємо доступ до роутера</h1> </div> <script src="check.js"></script> </body>
Це сторінка з лоадером, яка чекає на результат перевірки з файлу check.js.
Check.js
Check.js відповідає лише за одне: запитати бекенд і вирішити, куди перенаправити користувача.
const CONFIG = {
HOME_ROUTER_API: "/api/check-router-status",
};
async function checkRouterAccess() {
try {
const { isOnline } = await fetch(CONFIG.HOME_ROUTER_API, {
method: "GET",
redirect: "manual",
signal: AbortSignal.timeout(3000),
}).then((res) => res.json());
if (isOnline) {
window.location.href = "online.html";
return;
}
window.location.href = "offline.html";
} catch (error) {
window.location.href = "offline.html";
}
}
checkRouterAccess();
Логіка максимально примітивна:
- Бекенд відповів 200 OK → світло є.
- Будь-яка помилка / таймаут → світла немає.
Бекенд: перевірка доступності роутера
Тут живе вся «магія» з CORS і зовнішнім доступом. Бекенд отримує запит від фронтенду і сам звертається до роутера за його глобальним IPv4:port.
@Get("/api/check-router-status")
async checkRouter() {
try {
const res = await fetch(process.env.ROUTER_API);
return { isOnline: res.ok };
} catch {
return { isOnline: false };
}
}
Важливі моменти:
- Браузер не напряму стукає в роутер → немає помилки CORS.
- Бекенд робить простий HTTP-запит і повертає результат.
- Якщо роутер недоступний — запит просто впаде по таймауту.
Деплой
Після налаштування всього коду його потрібно задеплоїти.
У моєму випадку фронтенд (статичні html/css/js) віддається з того ж сервера, що і бекенд. По суті, це один компактний сервер, який виконує три завдання:
- Віддає статичні файли.
- Обслуговує один API-ендпоінт для перевірки доступності пристрою.
- Проксіює запити до роутера чи телевізора, обходячи проблеми з CORS.
Безпека — критично важливий момент
Є дуже важлива складова, про яку не можна забувати. Якщо залишити все як є, завжди існує шанс, що хтось знайде вашу глобальну IPv4-адресу. Далі справа техніки: однією командою можна отримати інформацію про всі відкриті порти, прив’язані до цієї адреси.
Саме так зазвичай працюють боти. Вони автоматично знаходять такі IP-адреси й починають перебір портів та ендпоінтів у пошуках чогось «цікавого». Уявімо, що бот натрапив на Samsung TV, доступний із глобальної мережі. Далі він почне надсилати типові запити на кшталт:
1.2.3.4:12333/index.html 1.2.3.4:12333/wordpress-admin.php 1.2.3.4:12333/robots.txt 1.2.3.4:12333/api 1.2.3.4:12333/api/v2
І рано чи пізно може «вгадати» реальний ендпоінт телевізора:
1.2.3.4:12333/api/v2
Який у відповідь поверне:
{
"device": {
"type": "Samsung SmartTV",
"name": "65" Neo QLED",
"networkType": "wireless",
"resolution": "3840x2160"
...
},
...
}
На цьому етапі конфіденційність уже порушена.
У кращому випадку зловмисник просто отримає інформацію про пристрій. У гіршому — почне експериментувати: надсилати команди, змінювати налаштування або шукати спосіб отримати ширший доступ до мережі. Саме тому відкривати домашні пристрої напряму в інтернет без додаткового захисту це погана ідея, навіть якщо здається, що там нічого цікавого.
Як забезпечити безпеку
Мінімізувати витік IP-адреси
Навіть якщо у нас немає проблем з CORS і ми хочемо з публічного сайту відправити запит на наш роутер, то будь-хто в браузері може глянути, куди був відправлений запит і таким чином вони знайдуть IPv4 та порт. Щоб уникнути витоку, потрібно чутливу інформацію передавати не через браузер, а через сервер.
Клієнт -> Сервер -> Роутер -> Сервер -> Клієнт
У цьому випадку браузер бачить лише Клієнт -> Сервер -> Клієнт.
Код серверу тримати в приватному репозиторії та IPv4:port винести у .env файл.
Використовувати нестандартний порт
Боти зазвичай сканують стандартні порти: 80, 443, 3000, 5000, 8000 і т.д.
Щоб уникнути випадкових атак, краще обрати нестандартний порт у діапазоні
Firewall
Найефективніший спосіб захисту — це фільтрувати доступ по IP: додати у whitelist IP сервера, який перевіряє наявність світла. Усі інші запити блокувати.
Таким чином, навіть якщо бот дізнається http://IPv4:port, він не зможе отримати доступ.
Обмеження: на рівні телевізора чи роутера firewall часто неможливо налаштувати.
Можна використовувати фізичний firewall, підключений по LAN, але для гігабітного інтернету це дорого.
Remote management
Якщо роутер підтримує Remote Management, можна вказати конкретну адресу, з якої дозволено доступ із глобальної мережі. Всі інші запити будуть отримувати 403 Forbidden.
VPN
Багато сучасних роутерів підтримують VPN-доступ (зазвичай через OpenVPN).
Ідея проста: після налаштування VPN доступ до http://IPv4:port можливий тільки для підключених через VPN пристроїв. При такому підході сервер, який не підключений до VPN, не зможе напряму звертатися до роутера і отримає помилку. Тому, щоб перевіряти статус пристрою через сервер, сам сервер також повинен бути підключений до VPN.
Захистити клієнт
Якщо хтось інший тримає посилання на клієнт, він зможе переглянути, чи є у вас світло, чи немає, а також навантажувати сервер. Щоб уникнути цього, можна скористатись підходом з VPN. Доступ на сайт буде лише після сходу на відповідний VPN. Інший варіант — можна зробити Basic Auth (логін, пароль) і лише після того дозволяти взаємодію.
Оптимізація
Щоб перевірка наявності світла працювала швидко та ефективно, варто звернути увагу на два ключові моменти:
Використовувати TCP замість HTTP
Відправляти HTTP-запит, щоб перевірити доступність пристрою, не дуже оптимально.
Причина проста: HTTP спочатку встановлює TCP-з’єднання, потім робить handshake, обмінюється заголовками. Щоб просто дізнатися, чи пристрій онлайн, достатньо TCP-запиту. Він набагато швидший та менше навантажує мережу та сервер.
@Get("/api/check-router-status")
async pingRouter() {
const host = process.env.ROUTER_HOST;
const port = Number(process.env.ROUTER_PORT);
return await new Promise<{ isOnline: boolean }>((resolve) => {
const socket = new net.Socket();
socket.setTimeout(1500);
socket.once("connect", () => {
socket.destroy();
resolve({ isOnline: true });
});
socket.once("timeout", () => {
socket.destroy();
resolve({ isOnline: false });
});
socket.once("error", () => {
resolve({ isOnline: false });
});
socket.connect(port, host);
});
}
Використовувати кешування результатів
Перевіряти стан світла кожну секунду або робити запит до роутера щоразу, коли кілька людей дивляться сайт одночасно — не оптимальна витрата ресурсів. Щоб уникнути цього, можна застосувати примітивний кеш на сервері.
const lastCheck: { isOnline: boolean; timestamp: number } | null = null;
const LASTCHECK_THRESHOLD = 60*1000; // 1 хвилина
@Get("/api/check-router-status")
async pingRouter() {
const now = Date.now();
if (lastCheck && now - lastCheck.timestamp < LASTCHECK_THRESHOLD) {
return lastCheck;
}
...
const result = await new Promise<{ isOnline: boolean }>((resolve) => {
....
});
lastCheck = { isOnline: result.isOnline, timestamp: now };
return result;
}
Покращення
У рамках MVP ми вже маємо працюючий сайт, який відкривається одним кліком і показує реальний стан світла вдома, а не теоретичний графік. Але необхідність постійно заходити на сайт і перевіряти стан може швидко втомлювати. Тому наступний крок — автоматизувати процес за допомогою Telegram-бота, інтегрованого із сайтом.
Ідея така: моніторити стан світла з 8 ранку до 10 вечора, перевіряючи його кожну годину. Додатково передбачені перевірки ранком: якщо о 9:01 світло ще не з’явилося, також перевіряємо повторно о 9:05 та 9:10. Якщо ж світло з’явилося о 9:01, повторні перевірки не потрібні, і наступна планова перевірка буде о 10:01.
Telegram-бот відправляє повідомлення у канал тільки при зміні стану світла, якщо перед цим його не було, а зараз є, або навпаки. Таким чином, навіть якщо з 8 до 11 ранку світла не було, користувач отримає лише два повідомлення: о 8:00 світла немає і о 11:00 світло з’явилося.

Бонус
Сайт можна встановити як PWA на телефон і перевіряти доступність світла. (Share -> Add to Home Screen -> Add).
А як ви перевіряєте, чи є світло вдома? ⚡📱

Сподобалась стаття? Підписуйтесь на автора, щоб отримувати сповіщення про нові публікації на пошту.
81 коментар
Додати коментар Підписатись на коментаріВідписатись від коментарів