26 KiB
🍄 Backend IoT Kumbung Jamur
Backend Node.js untuk sistem monitoring dan kontrol otomatis kumbung jamur berbasis IoT. Sistem ini menghubungkan sensor SHT31 dan relay pada ESP32 ke aplikasi mobile Flutter melalui MQTT (HiveMQ Cloud), dengan penjadwalan penyiraman otomatis menggunakan BullMQ (Redis), sistem push notification anti-spam via OneSignal, serta database utama Supabase.
Aplikasi ini dirancang dengan arsitektur High Availability (Dual Server Failover) dan In-Memory Optimizations untuk memastikan keandalan sistem yang optimal dan efisiensi resource yang sangat tinggi.
🏗️ Arsitektur Sistem Terdistribusi
Sistem ini mendukung arsitektur Failover Otomatis menggunakan dua server (Primary & Backup) untuk menjamin layanan tetap berjalan meskipun server utama mengalami kendala (down).
┌──────────────────────────────────────┐
│ Aplikasi Flutter │
└──────┬────────────────────────┬──────┘
│
HTTP REST API │
(https://...railway.app)
▼
┌────────────────────────┐
│ PRIMARY SERVER ├ ────────────────────
│ (Railway) │ |
└───────────┬────────────┘ |
│ │
┌─────┴─────────────────────────────────┴─────┐
│ │
▼ ▼ ▼
Supabase (DB) Upstash (Redis) HiveMQ Cloud
┌────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ Tabel Utama & │ │ Antrian Kerja │ │ Broker MQTT │
│ RPC Functions │ │ (BullMQ) │ │ (TLS) │
└────────────────┘ └─────────────────┘ └────────┬─────────┘
│
▼ (mqtts://)
┌─────────────────┐
│ ESP32 + SHT31 │
└─────────────────┘
🔄 Alur & Fitur Utama Failover (Dynamic Failover Manager)
- Primary Server (Railway): Berjalan secara aktif. Menangani REST API, koneksi MQTT, pemrosesan antrian BullMQ, dan pendeteksian perangkat offline.
- Backup Server (VPS - Standby):
- Saat startup, background services (MQTT, Worker BullMQ, Scheduler Restore, Offline Detector) dinonaktifkan (standby).
- Secara berkala (setiap 10 detik /
FAILOVER_PING_INTERVAL_MS), server backup mengirimkan ping (HTTP GET) kePRIMARY_SERVER_URL. - Jika Primary Down (Ping Gagal): Backup server langsung mengaktifkan semua background services lokal, me-resume worker BullMQ, me-restore jadwal aktif dari database, dan mengambil alih kendali sistem.
- Jika Primary Kembali Online (Ping Sukses): Backup server secara otomatis mendegradasi diri kembali ke mode standby (disconnect MQTT, pause worker, matikan offline detector) untuk menghindari bentrokan eksekusi (race conditions).
⚡ Optimalisasi Kinerja & Efisiensi Database
Untuk mencegah pembengkakan biaya database (kuota request Supabase) dan beban broker MQTT akibat frekuensi transmisi data ESP32 yang tinggi, backend mengimplementasikan beberapa teknik optimalisasi tingkat tinggi:
1. In-Memory Cache (Threshold Cache)
- Masalah: ESP32 mengirim data sensor secara real-time. Jika backend harus menanyakan batas threshold ke Supabase setiap kali data masuk, database akan terbebani ribuan query per menit.
- Solusi: Nilai threshold disimpan dalam in-memory cache server dengan TTL 30 detik (
CACHE_TTL_MS). Query ke Supabase hanya dilakukan saat cache kedaluwarsa atau terjadi update threshold melalui API (cache langsung di-invalidate secara instan).
2. Smart Filtering & Deadband Logging (Sensor Log Cache)
Backend menyaring log sensor sebelum disimpan ke database Supabase melalui algoritma Deadband filtering:
- Data sensor hanya akan disimpan ke tabel
sensor_logsjika memenuhi salah satu kondisi berikut:- Suhu berubah lebih dari 0.5°C (
TEMP_DELTA). - Kelembapan berubah lebih dari 1.0% (
HUM_DELTA). - Status relay berubah (
ON↔OFF). - Mode operasi perangkat berubah (
auto,manual,offline). - Interval waktu detak jantung (heartbeat) telah mencapai 5 menit (
HEARTBEAT_INTERVAL_MS), bertujuan untuk menjaga kontinuitas grafik di UI.
- Suhu berubah lebih dari 0.5°C (
- Mengurangi penyimpanan database hingga 90% tanpa kehilangan data historis yang penting.
3. Online Status Throttling
- Status keaktifan perangkat (
last_seendanis_online) di database diperbarui secara berkala maksimal 1 menit sekali (LAST_SEEN_INTERVAL_MS), mencegah spam penulisan (write-heavy) ke Supabase.
🚨 Pendeteksi Perangkat Offline & Push Notification
1. Offline Detector Job
- Berjalan di latar belakang setiap 5 menit.
- Memindai perangkat di database yang berstatus
is_online = truenamunlast_seenberumur lebih dari 5 menit lalu. - Jika ditemukan, secara otomatis memperbarui status perangkat menjadi offline di database dan memicu push notification darurat ke pemilik perangkat.
2. Anti-Spam Push Notification (OneSignal Integration)
- Koneksi: Integrasi langsung ke OneSignal menggunakan API REST resmi.
- Anti-Spam Guard (Cooldown): Setiap notifikasi yang sama (misal peringatan sensor kering/panas atau status offline) memiliki cooldown time (seperti 30 menit atau 1 jam) yang dikelola di Redis / In-Memory Map agar pengguna tidak dibanjiri spam push notification.
- Notification Stacking & Threading: Menggunakan
android_group,thread_id, dancollapse_iddengan namajamur_monitoring_groupuntuk mengelompokkan notifikasi secara rapi di notification tray Android & iOS (seperti gaya chat WhatsApp).
📋 Prasyarat Layanan Cloud
Pastikan Anda memiliki akun dan konfigurasi untuk layanan-layanan berikut:
| Layanan | Keterangan |
|---|---|
| Supabase | Database PostgreSQL utama (gratis) |
| HiveMQ Cloud | MQTT Broker TLS aman port 8883 (gratis) |
| Upstash Redis | Redis Cloud untuk antrian BullMQ (gratis) |
| OneSignal | Platform Push Notification ke Aplikasi Mobile |
| Node.js >= 18 | Runtime JavaScript lokal |
⚙️ Instalasi & Konfigurasi
1. Clone & Install Dependencies
git clone <repository-url>
cd backend-jamur
npm install
2. Setup Environment Variables
Buat file .env di root folder aplikasi, lalu isi konfigurasi berikut:
# 💻 Server Configuration
PORT=3000
IS_BACKUP_SERVER=false # Set 'true' jika ini dideploy ke server VPS Backup
PRIMARY_SERVER_URL=https://nama-project-kamu.up.railway.app # URL Primary Server (diperlukan jika ini Backup Server)
FAILOVER_PING_INTERVAL_MS=10000 # Interval cek primary server (dalam milidetik, default 10 detik)
# ⚡ Supabase Configuration (Ambil dari: Project Settings > API)
SUPABASE_URL=https://xxxxxxxxxxxxxxxx.supabase.co
SUPABASE_SERVICE_KEY=sb_secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 📡 HiveMQ Cloud MQTT Broker (Ambil dari: Clusters > MQTT Credentials & Connection Settings)
MQTT_BROKER_URL=mqtts://xxxxxxxxxxxxxxxxxxxxxxxx.s1.eu.hivemq.cloud:8883
MQTT_PORT=8883
MQTT_USERNAME=username_hivemq_kamu
MQTT_PASSWORD=password_hivemq_kamu
# 🔴 Upstash Redis Connection (Ambil dari: Database > Details > Connection URL)
REDIS_URL=rediss://default:xxxxxxxx@xxxx.upstash.io:6379
# 🔔 OneSignal Push Notification (Ambil dari: Settings > Keys & IDs)
ONESIGNAL_APP_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ONESIGNAL_REST_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
⚠️ PENTING: Jangan pernah melakukan commit file
.envke Git! File ini sudah otomatis diabaikan di file.gitignore.
3. Setup Database Supabase (Skema Tabel & Stored Procedure)
Jalankan perintah SQL berikut di dashboard Supabase > SQL Editor:
-- 1. TABEL UTAMA: Perangkat ESP32
CREATE TABLE devices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
device_id TEXT UNIQUE NOT NULL, -- e.g. "esp32-01"
label TEXT, -- nama display e.g. "Kumbung Barat"
location TEXT, -- lokasi penempatan
claim_code TEXT UNIQUE, -- kode klaim huruf kapital, e.g. "JAMUR01"
claimed_by UUID REFERENCES auth.users(id),
claimed_at TIMESTAMPTZ,
is_online BOOLEAN DEFAULT false,
last_seen TIMESTAMPTZ,
current_mode TEXT DEFAULT 'auto' -- mode kerja aktif: auto, manual, offline
);
-- 2. TABEL LOG: Riwayat Sensor & Log Aksi
CREATE TABLE sensor_logs (
id BIGSERIAL PRIMARY KEY,
device_id TEXT NOT NULL,
temperature FLOAT,
humidity FLOAT,
relay_state BOOLEAN DEFAULT false, -- true = ON, false = OFF
mode TEXT DEFAULT 'auto', -- snapshot mode kerja saat log terekam
event TEXT, -- event penting (e.g. "manual_stop", "system_on")
note TEXT, -- catatan tambahan
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 3. TABEL CONFIG: Batas Sensor Otomatisasi
CREATE TABLE thresholds (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
device_id TEXT UNIQUE NOT NULL,
temp_max FLOAT NOT NULL DEFAULT 30.0, -- suhu maks sebelum pompa ON
hum_max FLOAT NOT NULL DEFAULT 80.0, -- kelembapan maks sebelum pompa ON
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 4. TABEL JADWAL: Jadwal Penyiraman BullMQ
CREATE TABLE schedules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
device_id TEXT NOT NULL,
label TEXT,
cron TEXT NOT NULL, -- cron format: "menit jam hari bulan hari-minggu"
duration_s INTEGER NOT NULL, -- durasi penyiraman dalam detik
bull_job_id TEXT, -- ID job BullMQ untuk kontrol sinkronisasi
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 5. FUNCTION: Rata-rata Harian (RPC Function)
CREATE OR REPLACE FUNCTION get_daily_average(p_device_id TEXT, p_days INT)
RETURNS TABLE(day DATE, avg_temp NUMERIC, avg_hum NUMERIC) AS $$
BEGIN
RETURN QUERY
SELECT
created_at::DATE AS day,
ROUND(AVG(temperature)::NUMERIC, 1) AS avg_temp,
ROUND(AVG(humidity)::NUMERIC, 1) AS avg_hum
FROM sensor_logs
WHERE device_id = p_device_id
AND created_at >= NOW() - (p_days || ' days')::INTERVAL
AND temperature IS NOT NULL
AND humidity IS NOT NULL
GROUP BY created_at::DATE
ORDER BY day DESC;
END;
$$ LANGUAGE plpgsql;
-- 6. FUNCTION: Rata-rata Per Jam (RPC Function)
CREATE OR REPLACE FUNCTION get_hourly_average(p_device_id TEXT, p_days INT)
RETURNS TABLE(hour TIMESTAMPTZ, avg_temp NUMERIC, avg_hum NUMERIC) AS $$
BEGIN
RETURN QUERY
SELECT
date_trunc('hour', created_at) AS hour,
ROUND(AVG(temperature)::NUMERIC, 1) AS avg_temp,
ROUND(AVG(humidity)::NUMERIC, 1) AS avg_hum
FROM sensor_logs
WHERE device_id = p_device_id
AND created_at >= NOW() - (p_days || ' days')::INTERVAL
AND temperature IS NOT NULL
AND humidity IS NOT NULL
GROUP BY date_trunc('hour', created_at)
ORDER BY hour DESC;
END;
$$ LANGUAGE plpgsql;
4. Jalankan Server Secara Lokal
# Jalankan mode Development (Auto-restart via nodemon)
npm run dev
# Jalankan mode Production
npm start
Server akan aktif pada port http://localhost:3000 (atau sesuai konfigurasi env PORT).
🚀 Panduan Deployment
A. Deploy ke Railway (Sebagai Primary Server)
- Hubungkan repository GitHub Anda ke Railway.app.
- Buat Project Baru dan pilih Deploy dari GitHub.
- Di tab Variables, masukkan semua Environment Variables seperti isi file
.envdi atas (kecualiPORTkarena dikelola otomatis oleh Railway). - Klik Generate Domain di tab Settings > Networking untuk mendapatkan URL server publik (contoh:
https://nama-project-kamu.up.railway.app).
B. Deploy ke VPS (Sebagai Backup Server)
- Siapkan server VPS (Ubuntu/Debian) dengan Node.js >= 18 dan PM2 terinstall.
- Clone repository, jalankan
npm install. - Set file
.envdengan variabelIS_BACKUP_SERVER=truedan isikanPRIMARY_SERVER_URLdengan URL Railway yang didapatkan dari langkah di atas. - Jalankan server menggunakan PM2 agar berjalan di latar belakang:
pm2 start src/index.js --name backend-jamur-backup pm2 save pm2 startup
🔌 API Reference
Base URL (Development): http://localhost:3000/api
Base URL (Production): https://nama-project-kamu.up.railway.app/api
🛡️ Rate Limiting:
- Global rate limit untuk semua API: 100 request/menit per IP.
- Rate limit ketat untuk trigger siram manual: 5 request/menit per IP (menghindari banjir air pada kumbung).
📋 Ringkasan Daftar API (API Cheat Sheet)
Untuk mempermudah pencarian, berikut adalah ringkasan seluruh endpoint API yang tersedia pada backend ini:
| Kategori | Fitur / Kegunaan | Method | Endpoint | Parameter | Deskripsi Singkat |
|---|---|---|---|---|---|
| 📱 Device | Klaim Device Baru (Pairing) | POST |
/api/device/claim |
claim_code (Body), user_id (Body) |
Menghubungkan perangkat fisik ke akun user via kode klaim. |
| Ambil Info Device User | GET |
/api/device/my-device/:userId |
userId (Path) |
Mengambil detail perangkat milik user (status online, last seen). | |
| Cek Status Online Device | GET |
/api/device/status/:deviceId |
deviceId (Path) |
Membaca status keaktifan perangkat secara real-time. | |
| 🌡️ Threshold | Ambil Threshold Aktif | GET |
/api/threshold/:deviceId |
deviceId (Path) |
Membaca batas suhu & kelembapan otomatisasi aktif. |
| Update Threshold | POST |
/api/threshold/:deviceId |
deviceId (Path), temp_max (Body), hum_max (Body), temp_min (Body, opsional) |
Mengubah batas threshold (langsung sinkron ke ESP32 via MQTT). | |
| 🗓️ Jadwal | Ambil Semua Jadwal | GET |
/api/schedule/:deviceId |
deviceId (Path) |
Membaca seluruh daftar jadwal penyiraman perangkat. |
| Buat Jadwal Baru | POST |
/api/schedule/:deviceId |
deviceId (Path), cron (Body), duration_s (Body), label (Body, opsional) |
Mendaftarkan jadwal berulang baru ke DB & BullMQ (cron). | |
| Hapus Jadwal | DELETE |
/api/schedule/:id |
id (Path) |
Menghapus jadwal permanen dari database dan antrian BullMQ. | |
| Toggle Status Jadwal | PATCH |
/api/schedule/:id/toggle |
id (Path) |
Mengaktifkan/menonaktifkan jadwal sementara tanpa menghapus. | |
| Trigger Siram Instan | POST |
/api/schedule/:deviceId/now |
deviceId (Path), duration_s (Body, opsional) |
Memicu penyiraman manual sekali jalan (default 30 detik). | |
| Hentikan Pompa Paksa | POST |
/api/schedule/:deviceId/stop |
deviceId (Path) |
Mengirim sinyal OFF langsung ke relay pompa via MQTT. |
|
| ⚙️ Mode | Ambil Mode Kerja Aktif | GET |
/api/mode/:deviceId |
deviceId (Path) |
Membaca mode kerja aktif perangkat (auto, manual, offline). |
| Ubah Mode Kerja | POST |
/api/mode/:deviceId |
deviceId (Path), mode (Body) |
Mengubah mode kerja ESP32 dan sinkronisasi perintah via MQTT. | |
| 📊 Riwayat | Ambil Log Sensor Terbaru | GET |
/api/history/:deviceId |
deviceId (Path), limit (Query, opsional) |
Mengambil data sensor real-time terbaru (terlimit maks 500). |
| Rata-rata Harian | GET |
/api/history/:deviceId/daily |
deviceId (Path), days (Query, opsional) |
Mengambil data rata-rata suhu & kelembapan harian (tren grafik). | |
| Rata-rata Per Jam | GET |
/api/history/:deviceId/hourly |
deviceId (Path), days (Query, opsional) |
Mengambil data rata-rata suhu & kelembapan per jam (grafik analitis). |
📱 Device Management
1. Klaim Device Baru (Pairing)
Menghubungkan kode unik perangkat fisik dengan ID akun pengguna.
- Endpoint:
POST /api/device/claim - Body Request:
{ "claim_code": "JAMUR01", "user_id": "uuid-user-dari-supabase-auth" } - Respons Sukses (200):
{ "message": "Device berhasil diklaim", "device": { "device_id": "esp32-01", "label": "Kumbung Barat", "location": "Sektor A" } }
2. Ambil Info Device Milik User
- Endpoint:
GET /api/device/my-device/:userId - Respons Sukses (200):
{ "device_id": "esp32-01", "label": "Kumbung Barat", "location": "Sektor A", "is_online": true, "last_seen": "2026-05-24T10:00:00Z" }
🌡️ Threshold Management
1. Ambil Threshold Aktif
- Endpoint:
GET /api/threshold/:deviceId - Respons Sukses (200):
{ "device_id": "esp32-01", "temp_max": 30.0, "hum_max": 80.0, "updated_at": "2026-05-24T09:00:00Z" }
2. Update Threshold
Memperbarui batas threshold di DB dan langsung mengirimkan pembaruan ke ESP32 secara instan via MQTT.
- Endpoint:
POST /api/threshold/:deviceId - Body Request:
{ "temp_max": 31.5, "hum_max": 85.0 } - Respons Sukses (200):
{ "message": "Threshold diupdate", "data": { "device_id": "esp32-01", "temp_max": 31.5, "hum_max": 85.0 } }
🗓️ Jadwal & Kontrol Penyiraman
1. Ambil Semua Jadwal Perangkat
- Endpoint:
GET /api/schedule/:deviceId - Respons Sukses (200):
[ { "id": "uuid-jadwal-1", "device_id": "esp32-01", "label": "Siram Pagi Hari", "cron": "0 6 * * *", "duration_s": 60, "is_active": true, "created_at": "2026-05-24T08:00:00Z" } ]
2. Buat Jadwal Penyiraman Baru
Menambahkan jadwal ke DB dan mendaftarkannya sebagai antrian repeatable job di BullMQ.
- Endpoint:
POST /api/schedule/:deviceId - Body Request:
{ "label": "Siram Sore", "cron": "0 17 * * *", "duration_s": 45 } - Respons Sukses (210/201):
{ "id": "uuid-jadwal-baru", "device_id": "esp32-01", "label": "Siram Sore", "cron": "0 17 * * *", "duration_s": 45, "is_active": true }
3. Hapus Jadwal
Menghapus permanen jadwal dari database dan membatalkan repeatable job dari BullMQ.
- Endpoint:
DELETE /api/schedule/:id - Respons Sukses (200):
{ "message": "Jadwal dihapus" }
4. Toggle Jadwal (Aktif / Nonaktif)
Menghentikan eksekusi jadwal sementara waktu di BullMQ tanpa menghapus data jadwal dari database.
- Endpoint:
PATCH /api/schedule/:id/toggle - Respons Sukses (200):
{ "message": "Jadwal dinonaktifkan", "data": { "id": "uuid-jadwal-1", "is_active": false } }
5. Trigger Siram Manual (Sekali Jalan)
Memicu penyiraman instan di luar jadwal reguler.
- Endpoint:
POST /api/schedule/:deviceId/now - Body Request (Opsional):
{ "duration_s": 30 } - Respons Sukses (200):
{ "message": "Siram manual 30s dijadwalkan" }
6. Hentikan Pompa Secara Paksa
Segera mengirimkan sinyal relay OFF via MQTT untuk mematikan pompa secara langsung.
- Endpoint:
POST /api/schedule/:deviceId/stop - Respons Sukses (200):
{ "message": "Pompa dimatikan" }
⚙️ Mode Operasi Perangkat
ESP32 mendukung 3 mode operasi utama:
auto: Pompa dikendalikan secara otomatis berdasarkan data sensor SHT31.manual: Logika otomatis dimatikan, kontrol pompa sepenuhnya diatur manual lewat API/Aplikasi.offline: Mode darurat jika terputus dari jaringan cloud (logika auto berjalan mandiri di hardware lokal tanpa mempublikasikan data MQTT).
1. Ambil Mode Kerja Aktif
- Endpoint:
GET /api/mode/:deviceId - Respons Sukses (200):
{ "device_id": "esp32-01", "current_mode": "auto", "is_online": true, "last_seen": "2026-05-24T10:00:00Z" }
2. Ubah Mode Kerja
Mengirim perintah ganti mode ke hardware via MQTT dan menyimpan perubahannya di database.
- Endpoint:
POST /api/mode/:deviceId - Body Request:
{ "mode": "manual" } - Respons Sukses (200):
{ "message": "Mode berhasil diubah ke manual", "mode": "manual", "changed": true }
📊 Riwayat Sensor & Tren Kondisi
1. Ambil Log Sensor Terbaru
Mengambil log real-time sensor terbaru (termasuk filter in-memory deadband).
- Endpoint:
GET /api/history/:deviceId?limit=100 - Respons Sukses (200):
[ { "temperature": 28.5, "humidity": 82.3, "relay_state": false, "mode": "auto", "created_at": "2026-05-24T10:15:00Z" } ]
2. Ambil Rata-rata Sensor Harian (RPC get_daily_average)
Untuk visualisasi grafik jangka panjang di aplikasi Flutter.
- Endpoint:
GET /api/history/:deviceId/daily?days=7 - Respons Sukses (200):
[ { "day": "2026-05-24", "avg_temp": 28.1, "avg_hum": 83.4 } ]
3. Ambil Rata-rata Sensor Per Jam (RPC get_hourly_average)
Untuk visualisasi grafik analitis jangka menengah/harian.
- Endpoint:
GET /api/history/:deviceId/hourly?days=7 - Respons Sukses (200):
[ { "hour": "2026-05-24T10:00:00.000Z", "avg_temp": 28.5, "avg_hum": 82.9 } ]
📡 MQTT Topics
Sistem komunikasi backend dan ESP32 menggunakan protokol MQTT over TLS (mqtts) di port 8883.
| Topic | Arah | Payload | Keterangan |
|---|---|---|---|
sensor/sht31 |
ESP32 → Backend | {"device_id":"esp32-01","temp":28.5,"hum":82.3,"mode":"auto","relay":false} |
Laporan status sensor & hardware berkala |
config/threshold/{deviceId} |
Backend → ESP32 | {"temp":31.5,"hum":85.0} |
Sinkronisasi perubahan threshold sensor |
cmd/relay/{deviceId} |
Backend → ESP32 | "ON" atau "OFF" |
Perintah langsung kontrol relay pompa |
cmd/mode/{deviceId} |
Backend → ESP32 | "auto", "manual", atau "offline" |
Perintah langsung untuk mengubah mode operasi |
🔁 Antrian Kerja (BullMQ) & Recovery Sistem
Sistem penjadwalan dikelola secara hybrid menggunakan BullMQ dan Supabase. Hal ini memecahkan masalah hilangnya repeatable job jika server mengalami restart/deployment ulang.
Alur Kerja BullMQ Worker
- Job repeatable terpicu sesuai jadwal cron atau instan dari trigger siram manual.
- Worker BullMQ mengambil job dari Redis:
- Mengirim perintah relay
ONke ESP32 via MQTT. - Mengirimkan push notification "Penyiraman Dimulai" via OneSignal ke pemilik alat.
- Menahan eksekusi (non-blocking sleep) selama durasi siram
duration_s. - Mengirim perintah relay
OFFke ESP32 via MQTT saat durasi berakhir.
- Mengirim perintah relay
Alur Recovery (Schedule Restore)
Saat server pertama kali menyala (atau setelah failover berpindah ke aktif):
- Menghapus semua job antrian lama di Redis demi mencegah tumpang tindih.
- Membaca semua data jadwal yang aktif (
is_active = true) di tabel Supabase. - Mendaftarkan ulang job ke Redis menggunakan pustaka terbaru BullMQ v5+ (menggunakan format parameter
cron).
📁 Struktur Folder
backend-jamur/
├── src/
│ ├── index.js # Entry point utama, Express & rate limit setup
│ ├── jobs/
│ │ └── offlineDetector.js# Background job pemeriksa status online perangkat
│ ├── mqtt/
│ │ └── mqttClient.js # Koneksi HiveMQ, subscriber sensor, & publisher command
│ ├── queues/
│ │ ├── irrigationQueue.js# Inisialisasi antrian BullMQ & koneksi Redis Upstash
│ │ ├── irrigationWorker.js# Worker pengeksekusi siklus pompa ON -> DELAY -> OFF
│ │ └── scheduleRestore.js# Pemulihan otomatis jadwal aktif dari database saat startup
│ ├── routes/
│ │ ├── device.js # API endpoint klaim & info perangkat
│ │ ├── history.js # API endpoint riwayat & agregasi sensor (daily/hourly)
│ │ ├── mode.js # API endpoint kontrol mode kerja ESP32
│ │ ├── schedule.js # API endpoint CRUD jadwal & trigger pompa manual
│ │ └── threshold.js # API endpoint pembacaan & modifikasi batas sensor
│ ├── supabase/
│ │ └── client.js # Setup Supabase JS client (Service Role authorization)
│ └── utils/
│ └── notification.js # Pengirim OneSignal Push Notification + Cooldown redis
├── .env # File konfigurasi privat (lokal)
├── .gitignore # Daftar file terabaikan dari Git
├── package.json # Daftar pustaka dependencies & scripts npm
└── package-lock.json # Lock file dependencies
📦 Dependensi Utama
Detail library penting yang digunakan pada proyek ini:
"dependencies": {
"@supabase/supabase-js": "^2.101.1",
"bullmq": "^5.73.0",
"dotenv": "^17.4.1",
"express": "^5.2.1",
"express-rate-limit": "^8.3.2",
"ioredis": "^5.10.1",
"mqtt": "^5.15.1"
}