Hibah Pembelajaran Berdampak UNS 2026 · Skema Membangun Desa
Desa Karanggeneng, Boyolali Fakultas Teknik UNS

Publikasi

Server STB & Arsitektur Sistem JARPAK

Arsitektur edge server STB: go2rtc streaming, record.py API, nginx, autentikasi, Cloudflare Tunnel, dan alur data.

# Arsitektur & Alur Sistem JARPAK

Lokasi: Pos Ronda Karanggeneng
Edge device: STB Amlogic S9xx (aml-s9xx-box, Armbian)
Domain publik: bagaswara.hibahkaranggeneng.site
Disusun: 28 Oktober 2026, 23.16 WIB
Sifat dokumen: deskripsi arsitektur + alur dalam teks, siap dipakai untuk menggambar flowchart.

Dokumen ini menjelaskan kondisi apa adanya (as-built), termasuk bagian yang masih bermasalah. Bagian yang perlu diverifikasi ditandai [VERIFIKASI].


1. Ringkasan Eksekutif

JARPAK adalah sistem monitoring lingkungan pos ronda yang terdiri dari:

  1. Sensor & aktuator lapangan — ESP32 membaca PLN, aki, inverter, UPS, suhu, dan mengendalikan sirene.
  2. Kamera — 2 kamera IP terhubung ke NVR, di-stream ulang oleh go2rtc di STB.
  3. STB sebagai edge server — menjalankan web server, API telemetri, gateway stream, autentikasi, Cloudflare Tunnel, Tailscale, dan kiosk HDMI.
  4. Tampilan — kiosk TV 1080p, dashboard web publik, dan aplikasi Android.

Inti arsitekturnya: STB adalah satu-satunya simpul yang menyatukan data lapangan, stream kamera, dan tampilan publik. Tidak ada cloud database; semua data operasional disimpan lokal di SQLite dan file di SD card.

Status ringkas saat dokumen disusun:

  • Layanan inti (go2rtc, record.py, nginx, auth, tunnel, Tailscale) aktif.
  • Kiosk HDMI mati karena HDMI terdeteksi disconnected; kiosk.service berstatus failed.
  • Overlay ticker ikut mati karena hanya dijalankan oleh kiosk.sh.
  • NVR 192.168.0.20 tidak terjangkau dari STB (beda subnet), sehingga live view kamera mati.
  • Link serial ESP32 tidak terdeteksi; nilai telemetri perangkat saat ini berasal dari simulator fallback di record.py, bukan sensor nyata.
  • Perekaman lokal dimatikan (ENABLE_RECORDING=0); rekaman sepenuhnya di NVR.
  • Backup harian dan health check berjalan.

2. Prinsip & Batasan Desain

  1. Output kiosk wajib 1080p ke HDMI, tidak boleh 4K, full screen tanpa scroll.
  2. Aplikasi Bagaswara read-only — perubahan aplikasi hanya boleh lewat D:\BRIEF-APLIKASI-BAGASWARA.md.
  3. STB menjadi edge tunggal — tidak ada server cloud terpisah; internet hanya untuk akses publik dan remote.
  4. Data operasional tetap lokal — SQLite + SD card; cloud hanya jalur akses.
  5. Kiosk harus tetap tampil walau internet mati — dashboard kiosk dilayani dari 127.0.0.1:1985.
  6. Keamanan akses publik — dashboard di balik login; API internal sebisa mungkin tidak terekspos langsung.

3. Arsitektur Berlapis

+------------------------------------------------------------------+
| LAPISAN 1 - FISIK / LAPANGAN                                     |
| PLN - Aki 12V - Inverter - UPS - ESP32 (sensor + sirene)         |
| NVR 192.168.0.20 - 2 kamera IP                                  |
| TV HDMI 1080p - Router/AP 192.168.1.1                            |
+----------------------------------+-------------------------------+
                                   | serial / RTSP / HDMI / Ethernet
+----------------------------------v-------------------------------+
| LAPISAN 2 - EDGE STB (Armbian, systemd)                          |
| go2rtc (stream) | record.py (API + DB) | nginx (web)             |
| jarpak-auth (login) | cloudflared (tunnel) | tailscaled           |
| NetworkManager (eth/WiFi failover) | kiosk + overlay ticker      |
| kiosk-hdmi-poll | stb-health | cctv-backup | chrony              |
+----------------------------------+-------------------------------+
                                   | baca/tulis
+----------------------------------v-------------------------------+
| LAPISAN 3 - DATA                                                 |
| SQLite /opt/camera/data.db                                       |
| File rekaman /mnt/sdcard/cctv (nonaktif)                         |
| Backup /mnt/sdcard/backup/*.tar.gz                               |
| Health log /mnt/sdcard/health/YYYY-MM-DD.log                     |
+----------------------------------+-------------------------------+
                                   | HTTP / WebSocket / HLS
+----------------------------------v-------------------------------+
| LAPISAN 4 - PRESENTASI                                           |
| Kiosk Chromium 1080p + window overlay ticker 60fps                |
| Website publik + dashboard (login)                               |
| Aplikasi Android                                                 |
+----------------------------------+-------------------------------+
                                   | internet
+----------------------------------v-------------------------------+
| LAPISAN 5 - EKSTERNAL                                            |
| Cloudflare Tunnel + CDN | Tailscale | NTP | Internet              |
+------------------------------------------------------------------+

Penjelasan tiap lapisan

  • Fisik/lapangan: sumber data dan aktuator. ESP32 adalah satu-satunya jalur telemetri dan sirene. NVR adalah sumber stream kamera.
  • Edge STB: seluruh logika sistem. Tidak ada dependensi ke server lain selain NVR dan internet.
  • Data: SQLite menjadi sumber kebenaran konfigurasi kamera dan ticker; go2rtc.yaml adalah cermin dari tabel cameras.
  • Presentasi: kiosk memakai jalur lokal (127.0.0.1) agar tidak bergantung internet; web publik memakai tunnel.
  • Eksternal: Cloudflare untuk akses publik, Tailscale untuk remote SSH, NTP untuk waktu.

4. Topologi Jaringan

                         INTERNET
                            |
                  +---------+---------+
                  |  Cloudflare Tunnel |
                  |  (cloudflared)     |
                  +---------+---------+
                            | outbound QUIC
+---------------------------v-----------------------------+
| Router/AP 192.168.1.1                                   |
|  +- STB aml-s9xx-box  eth0 192.168.1.11/24              |
|  |    +- Tailscale 100.77.104.5                         |
|  +- TV HDMI (langsung ke STB)                           |
|  +- Perangkat lain di LAN 192.168.1.0/24                |
|                                                         |
|  NVR 192.168.0.20  -> BEDA SUBNET, TIDAK ADA ROUTE     |
|  +- CAM 1 (channel 1)                             |
|  +- CAM 2 (channel 2)                        |
+---------------------------------------------------------+

Fakta jaringan saat ini:

  • eth0 UP, 192.168.1.11/24, gateway 192.168.1.1.
  • wlan0/wlan1 DOWN (mode ethernet-first).
  • Tailscale 100.77.104.5 aktif; akses SSH remote lewat Tailscale.
  • NVR 192.168.0.20 tidak punya rute dari 192.168.1.11; ping/RTSP gagal.
  • Publik lewat Cloudflare Tunnel; tidak ada port forwarding di router.

Implikasi: live view kamera hanya bisa hidup jika NVR bisa dijangkau. Pilihan perbaikan: pindahkan NVR ke subnet 192.168.1.0/24, tambahkan rute statis/VLAN, atau pasang NVR di switch yang sama.


5. Komponen & Tanggung Jawab

Komponen Proses / Service User Bind Tanggung jawab Status
go2rtc camera.service cctv 127.0.0.1:1985, *:8555, *:8556 Stream RTSP NVR → WebRTC/HLS/MP4; serve static /opt/camera/www aktif
record.py camera-record.service cctv + dialout 127.0.0.1:8088 API telemetri, ticker, sirene, kamera, settings; SQLite; simulator; promoter aktif
nginx nginx.service www-data 0.0.0.0:1984 Web publik, reverse proxy /api/ ke go2rtc, auth_request aktif
jarpak-auth jarpak-auth.service www-data 127.0.0.1:8099 Login, cookie HMAC, rate limit aktif
cloudflared cloudflared.service root outbound Tunnel publik ke nginx/API aktif
tailscaled tailscaled.service root tailscale0 Remote SSH/akses privat aktif
NetworkManager NetworkManager + dispatcher root — Ethernet-first, WiFi failover aktif
kiosk kiosk.service root :0 Xorg + Chromium fullscreen 1080p failed (HDMI putus)
overlay ticker child dari kiosk.sh root 9223 Chromium window 1920×68, ticker 60fps mati (ikut kiosk)
kiosk-hdmi-poll kiosk-hdmi-poll.timer root — Start/stop kiosk sesuai status HDMI timer aktif
stb-health cron /etc/crontab tiap 10 menit root — Log kesehatan STB aktif
cctv-backup cron root 03.30 root — Backup tar.gz harian aktif
chrony chrony.service root 323/udp Sinkronisasi waktu aktif
ESP32 firmware eksternal — serial Baca sensor, kontrol sirene tidak terdeteksi
NVR perangkat eksternal — 192.168.0.20:554 Rekaman + sumber RTSP tidak terjangkau

6. Port & Endpoint

6.1 Port

Port Protokol Bind Fungsi Akses
22 TCP semua SSH LAN/Tailscale
1984 HTTP 0.0.0.0 Website + dashboard + proxy LAN & tunnel
1985 HTTP 127.0.0.1 go2rtc API + static + WebSocket lokal
8088 HTTP 127.0.0.1 API record.py lokal
8099 HTTP 127.0.0.1 Auth service lokal (via nginx)
8555 RTSP semua go2rtc RTSP LAN
8556 WebRTC semua go2rtc WebRTC LAN/tunnel
9222 CDP 127.0.0.1 Debug Chromium kiosk lokal
9223 CDP 127.0.0.1 Debug Chromium overlay lokal
41641 UDP — Tailscale internet
323 UDP — NTP/chrony internet

6.2 Endpoint record.py (:8088)

Method Path Fungsi Auth
GET /api/telemetry Nilai terbaru per source/metric —
GET /api/ticker Teks ticker aktif —
GET /api/ticker?action=list Daftar semua ticker —
GET /api/siren Status sirene —
GET /api/services Status layanan —
GET /api/settings Setting non-rahasia —
GET /api/cameras Daftar kamera (URL dimasking) —
GET /api/cputemp Suhu CPU STB —
GET /api/timeline Timeline rekaman per tanggal —
GET /api/events Event log —
GET /api/history Riwayat telemetri —
GET /api/recordings Daftar file rekaman —
POST `/api/siren?action=on off` Trigger sirene
POST /api/auth?pin=... Verifikasi PIN admin PIN
POST /api/pin Ganti PIN admin PIN lama
POST /api/ticker Tambah/ubah/hapus ticker PIN
POST /api/camera Tambah kamera PIN
POST /api/settings Ubah setting PIN
DELETE /api/camera Hapus kamera PIN
DELETE /api/recordings Hapus rekaman PIN

Catatan keamanan: record.py hanya listen di 127.0.0.1. API ini tidak memiliki autentikasi sesi; endpoint tulis memakai PIN 8 digit, kecuali sirene (sengaja tanpa PIN). Jika tunnel publik mengarahkan /api/* ke :8088, endpoint tulis ini terekspos ke internet. Perlu pembatasan ingress atau auth tambahan.

6.3 Endpoint go2rtc (:1985)

Path Fungsi
/api/streams Daftar stream + producer/consumer
/api/streams?name=...&src=... (PUT) Tambah/ubah stream runtime
/api/streams?src=... (DELETE) Hapus stream runtime
/api/ws?src=... WebSocket WebRTC signaling
/api/stream.mp4?src=... Stream MP4
/api/stream.m3u8?src=... HLS
/kiosk.html, /ticker.html, /dashboard/ Static file dari /opt/camera/www

6.4 Endpoint auth (:8099)

Path Fungsi
GET /health Health check
GET /auth/check Validasi cookie sesi (dipanggil nginx auth_request)
GET /auth/login Halaman login
POST /auth/login Proses login, set cookie jarpak_session
POST /auth/logout Hapus sesi

7. Model Data (SQLite /opt/camera/data.db)

Tabel Isi Catatan
settings Konfigurasi key/value Termasuk serial_port, serial_baud, admin_pin_hash, disk_min_gb
cameras Sumber kebenaran kamera key, display_name, urls_json, enabled; dicerminkan ke go2rtc.yaml
ticker_messages Teks berjalan text, is_active, sort_order
latest_telemetry Cache nilai terbaru 1 baris per source+metric; dibaca API
readings Riwayat telemetri mentah Dipangkas/promosi oleh promoter
hourly_avg Agregasi per jam avg_v, min_v, max_v, n
siren_state Status sirene saat ini state, source, updated_at
siren_events Audit trigger sirene ts, action, reason
service_status Status layanan terakhir service, status, message, checked_at
event_log Log kejadian aplikasi level, source, message
state_changes Perubahan state komponen component, old_state, new_state, detail
recordings Metadata rekaman lokal Kosong karena recording dimatikan
file_info Metadata file rekaman Kosong/tidak aktif

Alur data telemetri: readings (riwayat) → hourly_avg (agregasi) → latest_telemetry (cache untuk API). Promoter juga menjalankan retensi.


8. Alur Utama (Teks untuk Flowchart)

Format: setiap langkah diberi ID. Tanda [ ] = proses, < > = keputusan, ( ) = kondisi akhir. Gunakan ID ini sebagai node saat menggambar.

A. Boot & Startup Order

[A1] STB power on
  → [A2] systemd mulai service dasar (network, chrony, tailscaled)
  → [A3] NetworkManager: eth0优先; jika eth0 down → WiFi failover
  → [A4] camera.service (go2rtc) start
  → [A5] camera-record.service start (Requires=camera.service)
  → [A6] jarpak-auth.service start (Before=nginx)
  → [A7] nginx.service start (:1984)
  → [A8] cloudflared.service start (tunnel keluar)
  → [A9] kiosk-hdmi-poll.timer aktif (cek tiap 10 detik)
  → [A10] wifi-failover-watchdog.timer aktif (cek tiap 30 detik)
  → [A11] cron: stb-health tiap 10 menit, cctv-backup 03.30
  → <A12> HDMI connected?>
       ├─ ya  → [A13] kiosk.service start
       │         → [A14] kiosk.sh: set display 1080p, tunggu 1985
       │         → [A15] start overlay ticker (setsid)
       │         → [A16] exec Chromium kiosk 1920×1080
       │         → [A17] overlay Chromium 1920×68 di y=1012
       │         → (A18) kiosk tampil
       └─ tidak → [A19] kiosk tidak dijalankan / dihentikan
                 → (A20) STB tetap melayani web, API, stream

Ketergantungan penting:

  • camera-record butuh camera (go2rtc) hidup.
  • jarpak-auth harus siap sebelum nginx melayani dashboard.
  • Kiosk tidak menjadi syarat layanan web/API.
  • Kiosk hanya hidup jika HDMI terdeteksi; ini menghemat CPU/RAM saat TV mati.

B. Alur Telemetri (ESP32 → DB → Tampilan)

[B1] ESP32 membaca sensor (PLN, aki, inverter, UPS, suhu)
  → [B2] ESP32 kirim blok data lewat serial (115200 baud)
  → [B3] record.py serial_reader_loop membaca baris
  → <B4> port serial ditemukan?>
       ├─ tidak → [B5] tunggu 5 detik, coba lagi
       │            → [B6] simulator fallback menulis nilai dummy tiap 60 detik
       │            → (B7) API tetap mengembalikan nilai (dummy)
       └─ ya    → [B8] parse blok `tel` / `state` / `danger`
                    → [B9] tulis `readings` + update `latest_telemetry`
                    → [B10] update `siren_state` jika ada perubahan
                    → (B11) data siap dibaca API
  → [B12] dashboard/kiosk polling GET /api/telemetry tiap 15 detik
  → [B13] render kartu PLN, aki, inverter, suhu, beban
  → [B14] promoter agregasi `hourly_avg` + retensi `readings`

Kondisi saat ini: [B4] = tidak. Tidak ada /dev/ttyUSB*, /dev/ttyACM*, atau /dev/ttyESP32; serial_port kosong. Log [SERIAL] terhubung tidak ada. Jadi jalur yang aktif adalah B5 → B6 → B7 (simulasi).

Sumber nilai STB: stb.cpu_temp_c, stb.load_1, stb.mem_used_pct, stb.disk_free_gb dibaca dari sistem (real), bukan simulasi.

C. Alur Sirene

[C1] User tekan tombol sirene di dashboard/Android
  → [C2] POST /api/siren?action=on&reason=...
  → <C3> rate limit lolos?>
       ├─ tidak → [C4] HTTP 429, berhenti
       └─ ya    → [C5] apply_siren("on")
                    → <C6> link serial tersedia?>
                         ├─ tidak → [C7] state = Unknown / gagal
                         └─ ya    → [C8] kirim perintah serial ke ESP32
                                      → [C9] ESP32 menyalakan sirene
                                      → [C10] ESP32 kirim balik state
                                      → [C11] update `siren_state` + `siren_events`
  → [C12] dashboard polling GET /api/siren tiap 5 detik
  → [C13] tampilkan status (On/Off/Unknown)

Catatan: durasi sirene sepenuhnya diatur ESP32; server hanya mengirim on/off. Sirene sengaja tanpa PIN agar bisa dipicu darurat, hanya dibatasi rate limit.

D. Alur Live View Kamera

[D1] NVR 192.168.0.20 menyediakan RTSP per channel
  → [D2] go2rtc membaca producer RTSP (ffmpeg, transcode H.264)
  → [D3] go2rtc menyediakan WebRTC (:8556), HLS, MP4 (:1985)
  → <D4> klien mana?>
       ├─ kiosk      → [D5] http://127.0.0.1:1985/api/ws?src=...
       ├─ browser LAN→ [D6] http://192.168.1.11:1984/api/ws?src=...
       └─ publik     → [D7] https://jarpak.../api/ws?src=...
  → [D8] video tampil di kartu kamera
  → [D9] NVR menyimpan rekaman (bukan STB)

Kondisi saat ini: [D1] gagal (NVR beda subnet). go2rtc tetap punya producer, tetapi tidak ada consumer yang berhasil. record.py local recording dimatikan.

Catatan Android: aplikasi Android memakai stream H.264 dari NVR/go2rtc (bukan WebRTC). Alurnya: login → daftar kamera → buka stream → putar via ExoPlayer.

E. Alur Ticker (DB → API → Overlay Kiosk)

[E1] Admin menambah/ubah teks di dashboard
  → [E2] POST /api/ticker?action=add|set|delete&pin=...
  → [E3] record.py validasi PIN
  → [E4] tulis `ticker_messages`
  → [E5] overlay ticker polling GET /api/ticker tiap 60 detik
  → [E6] ticker.html membangun track teks
  → [E7] animasi CSS berjalan 60fps di window overlay
  → [E8] window overlay 1920×68 di posisi (0,1012), always on top
  → [E9] kartu ticker di halaman utama kiosk disembunyikan
       (`visibility:hidden`, `animation:none`)

Mengapa overlay terpisah: halaman utama kiosk hanya mencapai ~41–49fps karena kompositing software 1080p + backdrop-filter. Window overlay yang kecil (1920×68) dengan --use-gl=disabled mencapai 60fps. Karena itu ticker dipindah ke window kedua, bukan di dalam halaman utama.

Geometri overlay: card 1895×52 di 12,3; label 141.09px; viewport 1752px; track 4869px; durasi 54.1 detik (45px/detik). Identik dengan tampilan utama.

Auto-restart: loop di kiosk-ticker-overlay.sh membunuh Chromium overlay lama (pkill -f user-data-dir) lalu menjalankan ulang; xdotool windowraise menjaga overlay di atas.

F. Alur Kiosk HDMI

[F1] kiosk-hdmi-poll.timer tiap 10 detik
  → [F2] baca /sys/class/drm/card0-HDMI-A-1/status
  → <F3> status terbaca?>
       ├─ tidak → [F4] diam (fail-safe), selesai
       └─ ya    → <F5> connected?>
            ├─ ya  → <F6> kiosk active?>
            │        ├─ tidak → [F7] systemctl start kiosk
            │        │           → [F8] Xorg :0 vt1 + kiosk.sh
            │        │           → [F9] display 1080p, Chromium, overlay
            │        └─ ya    → [F10] tidak apa-apa
            └─ tidak → <F11> kiosk active?>
                     ├─ ya  → [F12] systemctl stop kiosk (hemat CPU/RAM)
                     └─ tidak → [F13] tidak apa-apa

Override manual: buat /etc/kiosk-hdmi.manual untuk menonaktifkan otomasi sementara; hapus untuk mengaktifkan lagi.

Kondisi saat ini: [F5] = tidak; kiosk.service sudah failed (bukan active), jadi poll tidak menghentikannya lagi. Saat HDMI dicolok, [F7] akan start.

G. Alur Website Publik & Autentikasi

[G1] User buka https://bagaswara.hibahkaranggeneng.site
  → [G2] Cloudflare (DNS + CDN + tunnel)
  → [G3] cloudflared → nginx :1984
  → <G4> path /dashboard/ ?>
       ├─ tidak → [G5] serve static (landing, profil, tim, publikasi)
       └─ ya    → [G6] nginx auth_request ke :8099/auth/check
                  → <G7> cookie `jarpak_session` valid?>
                       ├─ tidak → [G8] redirect 302 /login
                       │            → [G9] user isi username/password
                       │            → [G10] POST /auth/login
                       │            → [G11] verifikasi PBKDF2 + rate limit
                       │            → [G12] set cookie HMAC (12 jam, Secure)
                       │            → [G13] redirect /dashboard/
                       └─ ya    → [G14] serve dashboard/index.html
  → [G15] dashboard polling API (telemetri 15 dtk, siren 5 dtk,
            services 15 dtk, ticker 60 dtk, kamera 8 dtk)

Rute API publik [VERIFIKASI]: kode dashboard mengasumsikan /api/* diarahkan tunnel ke :8088 (record.py). Namun nginx lokal mengarahkan seluruh /api/ ke :1985 (go2rtc) dan hanya mengizinkan GET/HEAD. Artinya:

  • Di LAN, dashboard memanggil http://hostname:8088 langsung; tetapi record.py hanya listen 127.0.0.1, sehingga dari perangkat LAN lain panggilan ini gagal (nilai dummy).
  • Di publik, jika tunnel mengarahkan /api/* ke :8088, API data jalan, tetapi endpoint go2rtc (/api/streams, /api/ws) juga ikut ke :8088 dan gagal.
  • Diperlukan pemisahan path yang eksplisit (mis. /api/data/* → 8088, /api/stream/* → 1985) atau nginx yang mem-proxy endpoint record.py.

Cache Cloudflare: HTML cf-cache-status: DYNAMIC; CSS/gambar statis HIT dengan max-age=14400.

H. Alur Backup & Health

[H1] cron 03.30 → cctv-backup.sh
  → [H2] tar database + config + website + kiosk assets
  → [H3] simpan /mnt/sdcard/backup/cctv-backup-YYYY-MM-DD.tar.gz
  → [H4] retensi 7 hari

[H5] cron */10 → stb-health.sh
  → [H6] catat suhu, load, memori, disk, eMMC life, service, NTP,
           stream count, m3u8 latency, backup terakhir
  → [H7] tulis /mnt/sdcard/health/YYYY-MM-DD.log
  → [H8] tandai ANOMALY jika suhu >75°C, root >85%, ada service failed,
           zombie >5
  → [H9] retensi log 14 hari

Kondisi saat ini: backup terakhir cctv-backup-2026-10-28.tar.gz ada. Health log mencatat ANOMALY: SVC_FAILED karena kiosk.service failed.

I. Alur WiFi Failover

[I1] NetworkManager memantau eth0
  → <I2> eth0 punya koneksi internet?>
       ├─ ya  → [I3] putuskan WiFi, gunakan eth0
       └─ tidak → [I4] aktifkan profil WiFi (UNS-SOLO / bil)
  → [I5] wifi-failover-watchdog.timer tiap 30 detik memeriksa ulang
  → [I6] cloudflared & tailscaled reconnect otomatis

Kondisi saat ini: eth0 aktif, WiFi down.

J. Alur Kegagalan (Failure Modes)

Gejala Penyebab Deteksi Perilaku sistem Pemulihan
Kiosk mati HDMI putus kiosk-hdmi-poll baca status DRM kiosk tidak dijalankan; web/API tetap hidup colok HDMI; poll start
Live view hitam NVR beda subnet ping/RTSP gagal; go2rtc tanpa consumer kartu kamera error perbaiki rute/NVR
Telemetri dummy serial ESP32 tidak ada log [SERIAL] kosong; serial_port kosong simulator menulis nilai pasang/identifikasi port, set serial_port
Sirene tidak jalan link serial putus siren_state=Unknown perintah gagal perbaiki serial
Dashboard publik tanpa data rute /api/* salah nilai dummy / 404 dashboard fallback dummy pisahkan path API
Tunnel putus QUIC idle/timeout log cloudflared reconnect otomatis tunggu; cek jaringan
WebSocket putus ~100 dtk batas idle Cloudflare stream berhenti reconnect klien tidak bisa diubah dari STB
Kiosk patah-patah software compositing fps ~41–49 ticker overlay 60fps sudah dimitigasi overlay
Disk penuh rekaman/lokal health ROOT_FULL promoter retensi hapus/arsipkan
STB reboot listrik/watchdog uptime reset service auto-start otomatis

K. Alur Aplikasi Android (ringkas)

[K1] Buka aplikasi → [K2] login (PIN/akun)
  → [K3] muat daftar kamera dari API
  → [K4] pilih kamera → [K5] putar H.264 (ExoPlayer)
  → [K6] lihat telemetri + status
  → [K7] tombol sirene → POST /api/siren
  → [K8] ticker/teks berjalan
  → [K9] jika offline → tampilkan data terakhir / error

Detail endpoint Android ada di D:\BRIEF-APLIKASI-BAGASWARA.md. Aplikasi hanya boleh diubah lewat brief tersebut; source D:\Workspace\Apps\Bagaswara read-only.

L. Narasi End-to-End (untuk presentasi)

“Sensor ESP32 mengirim data ke STB lewat serial. STB menyimpannya di SQLite dan menyajikan API. Dashboard dan kiosk mengambil API itu setiap 15 detik. Kamera IP mengalir ke NVR, lalu di-stream ulang oleh go2rtc di STB agar bisa ditonton di kiosk, web, dan Android. Ticker dikelola dari database dan ditampilkan di window overlay terpisah agar tetap 60fps. Semua akses publik lewat Cloudflare Tunnel dan login. Jika HDMI mati, kiosk otomatis berhenti; jika internet mati, kiosk tetap jalan dari server lokal.—


9. Status Saat Ini (Bukti Lapangan)

Diambil 28 Oktober 2026, 23.16 WIB:

Item Status Bukti
camera.service active go2rtc listen 1985/8555/8556
camera-record.service active record.py listen 8088
nginx active listen 1984
jarpak-auth active listen 8099
cloudflared active QUIC registered sin19/sin14
tailscaled active 100.77.104.5
kiosk.service failed HDMI disconnected; failed sejak 18:08:46
Overlay ticker mati child kiosk tidak jalan
HDMI disconnected /sys/class/drm/card0-HDMI-A-1/status
NVR tidak terjangkau 192.168.0.20 beda subnet
Serial ESP32 tidak terdeteksi tidak ada ttyUSB/ttyACM/ttyESP32; log [SERIAL] kosong
Telemetri perangkat simulasi latest_telemetry update tiap 60 dtk dari simulator
Telemetri STB real CPU 38°C, load 0.41, mem 17.8%
Recording lokal nonaktif ENABLE_RECORDING=0
Backup OK cctv-backup-2026-10-28.tar.gz
Health anomali ANOMALY: SVC_FAILED (kiosk)
Uptime 1 jam 10 menit reboot terakhir

Nilai telemetri terakhir (simulasi): PLN 223.56 V (Hidup), aki 14.22 V (Discharging), inverter 225.0 V / 49.87 Hz / 4.53 A / 1019 W / 67.6%, ESP32 42.26°C, UPS 38.24°C.


10. Known Issues & Risiko

  1. NVR beda subnet — live view mati; perlu perbaikan jaringan.
  2. Serial ESP32 tidak teridentifikasi — telemetri simulasi; perlu cek kabel/port dan set serial_port.
  3. Rute API publik ambigu — potensi data dummy di LAN dan konflik /api/ go2rtc vs record.py di publik.
  4. API record.py tanpa auth sesi — endpoint tulis mengandalkan PIN; sirene tanpa PIN (by design) dan rate limit.
  5. Cloudflare WebSocket idle ~100 detik — live view publik bisa terputus; tidak bisa diubah dari STB.
  6. Tidak ada hardware acceleration — Chromium software rendering; halaman utama 41–49fps; sudah dimitigasi overlay.
  7. Kiosk bergantung HDMI — jika TV mati, kiosk berhenti (fitur hemat daya, bukan bug).
  8. SD card wear — backup + health log + rekaman (nonaktif) menulis ke SD; pantau eMMC/SD health.
  9. Single point of failure — STB adalah satu-satunya edge; jika mati, semua tampilan lokal dan publik mati.
  10. Simulasi menyamarkan status sensor — dashboard tidak membedakan nilai nyata vs dummy; perlu indikator “sensor offline—.

11. Keamanan

  • SSH: key id_ed25519; akses via LAN/Tailscale. Password root sementara olshop123 — segera ganti.
  • Dashboard: login username/password; PBKDF2; cookie jarpak_session HMAC 12 jam; Secure di host publik; rate limit 10 gagal/10 menit/IP.
  • Nginx: auth_request untuk /dashboard/; security headers; hidden files deny; /api/config 403; /api/ hanya GET/HEAD.
  • record.py: bind 127.0.0.1; CORS *; PIN 8 digit untuk endpoint tulis; audit log; masking URL/kredensial di /api/cameras.
  • Cloudflare Tunnel: tidak ada port forwarding; akses publik lewat tunnel; token di /etc/cloudflared/token (rahasia).
  • Tailscale: akses privat; relay “blr— latency 100–570ms.
  • Risiko: jika ingress meneruskan /api/* ke :8088, endpoint tulis (camera/ticker/settings) terekspos; sirene bisa dipicu siapa pun yang tahu URL (rate-limited). Perlu auth/ingress rule.

12. Lokasi File Penting

Path Isi
/opt/camera/record.py API + DB + serial + simulator + promoter
/opt/camera/data.db SQLite
/opt/camera/go2rtc.yaml Konfigurasi stream (cermin tabel cameras)
/opt/camera/www/ Static go2rtc: kiosk.html, ticker.html, dashboard/, assets
/etc/opt/kiosk/kiosk.sh Start Xorg/Chromium + overlay
/usr/local/bin/kiosk-ticker-overlay.sh Window overlay ticker
/usr/local/bin/kiosk-display.sh Set HDMI 1080p
/usr/local/bin/kiosk-hdmi-poll.sh Start/stop kiosk sesuai HDMI
/usr/local/bin/sync-kiosk-ui.sh Sinkronisasi UI kiosk dari dashboard
/usr/local/bin/jarpak-auth.py Auth service
/etc/nginx/conf.d/jarpak.conf Konfigurasi nginx
/etc/jarpak-auth/ Kredensial + secret (rahasia)
/etc/cloudflared/token Token tunnel (rahasia)
/etc/systemd/system/*.service Unit service
/usr/local/bin/stb-health.sh Health check
/usr/local/bin/cctv-backup.sh Backup harian
/mnt/sdcard/backup/ Backup tar.gz
/mnt/sdcard/health/ Log health
/mnt/sdcard/cctv/ Rekaman lokal (nonaktif)
/root/website-bagaswara-v2/ Sumber website (bind mount /var/www/jarpak)
/root/.ssh/github_deploy Deploy key GitHub STB

13. Checklist Verifikasi & Langkah Berikutnya

  1. HDMI/kiosk: colok HDMI → tunggu ≤10 detik → cek systemctl is-active kiosk dan window overlay. Pastikan resolusi 1920×1080, tidak 4K.
  2. Serial ESP32: cek kabel/port; coba ls -l /dev/ttyUSB* /dev/ttyACM* /dev/ttyESP32; set serial_port di settings; restart camera-record; cari log [SERIAL] terhubung; pastikan nilai berubah bukan simulasi.
  3. NVR: perbaiki rute/subnet; tes curl RTSP/ffprobe; cek /api/streams ada consumer; tes live view di kiosk, LAN, publik.
  4. Rute API publik: verifikasi ingress Cloudflare; putuskan skema path (/api/data/* vs /api/stream/*); sesuaikan API8088()/nginx; tes dari LAN dan publik.
  5. Auth API: pastikan endpoint tulis tidak terekspos tanpa PIN; pertimbangkan auth untuk sirene atau pembatasan IP.
  6. Backup restore: uji ekstrak satu arsip ke direktori sementara; pastikan DB + config + website lengkap.
  7. GitHub: setelah repo dibuat dan deploy key ditambahkan, push dari STB dengan GIT_SSH_COMMAND="ssh -i /root/.ssh/github_deploy"; jangan taruh token di URL.
  8. Dokumentasi: perbarui dokumen ini setelah perbaikan NVR/serial/rute API.

14. Peta Node untuk Menggambar Flowchart

Gunakan tabel ini sebagai daftar node; tanda → adalah edge.

Flow Node Edge
Boot A1..A20 A1→A2→A3→A4→A5→A6→A7→A8→A9→A10→A11→A12; A12-ya→A13→A14→A15→A16→A17→A18; A12-tidak→A19→A20
Telemetri B1..B14 B1→B2→B3→B4; B4-tidak→B5→B6→B7; B4-ya→B8→B9→B10→B11; B11→B12→B13→B14
Sirene C1..C13 C1→C2→C3; C3-tidak→C4; C3-ya→C5→C6; C6-tidak→C7; C6-ya→C8→C9→C10→C11; C11→C12→C13
Kamera D1..D9 D1→D2→D3→D4; D4-kiosk→D5; D4-LAN→D6; D4-publik→D7; D5/D6/D7→D8; D1→D9
Ticker E1..E9 E1→E2→E3→E4→E5→E6→E7→E8→E9
Kiosk HDMI F1..F13 F1→F2→F3; F3-tidak→F4; F3-ya→F5; F5-ya→F6; F6-tidak→F7→F8→F9; F6-ya→F10; F5-tidak→F11; F11-ya→F12; F11-tidak→F13
Web/Auth G1..G15 G1→G2→G3→G4; G4-tidak→G5; G4-ya→G6→G7; G7-tidak→G8→G9→G10→G11→G12→G13; G7-ya→G14; G14→G15
Backup/Health H1..H9 H1→H2→H3→H4; H5→H6→H7→H8→H9
WiFi I1..I6 I1→I2; I2-ya→I3; I2-tidak→I4; I3/I4→I5→I6
Android K1..K9 K1→K2→K3→K4→K5→K6→K7→K8→K9

Saran tata letak gambar:

  • Buat satu diagram besar dengan 5 kolom vertikal sesuai lapisan (Fisik → Edge → Data → Presentasi → Eksternal).
  • Letakkan STB di tengah; tarik panah dari ESP32/NVR ke STB, lalu dari STB ke kiosk/web/Android.
  • Buat sub-flow terpisah untuk Telemetri, Kamera, Ticker, Kiosk HDMI, dan Auth agar tidak terlalu padat.
  • Beri warna berbeda: hijau = alur normal, merah = kegagalan, kuning = kondisi saat ini (kiosk mati, NVR unreachable, serial simulasi).

Dokumen ini disusun dari inspeksi langsung STB pada 28 Oktober 2026. Rahasia (password, token, kredensial RTSP) sengaja tidak dicantumkan.