Tau.Acuvim/firmware/data/index.html
Renier Forster 84a0668c54 Initial commit: Tau Acuvim IoT monitoring system
Complete IoT monitoring platform for Acuvim II power meters via ESP32.

Firmware (Phases 1-7):
- ESP32-WROVER-B (TTGO T-Call v1.4) with RS485 Modbus RTU
- WiFi STA+AP concurrent mode with GSM/GPRS failover
- Transport abstraction layer with 4 priority modes
- MQTT protocol with 20 commands, LWT, QoS, exponential backoff
- SD card offline buffering with JSONL rotation and non-blocking drain
- OTA firmware updates with dual partition rollback protection
- Watchdog timer, crash loop detection, Acuvim health monitoring
- Captive portal provisioning with AP mode

Console backend (Phase 8):
- .NET 10 minimal API with PostgreSQL + EF Core
- JWT authentication, SignalR real-time updates
- MQTTnet 5.x bridge service with health monitoring
- Device, telemetry, firmware, alert, group management
- Rate limiting, security headers, Swagger/OpenAPI

Frontend (Phase 9):
- React 18 + TypeScript + Vite with Ant Design 5
- ECharts telemetry visualization, TanStack Query
- SignalR live updates, device management UI
- Dashboard, fleet management, firmware deployment

Testing & Production (Phase 10):
- 28 firmware unit tests (Modbus, JSON, config, version)
- 23 xUnit backend tests (device, telemetry, command, alert)
- Docker Compose with nginx, TLS MQTT, PostgreSQL
- Production deployment, commissioning, and troubleshooting docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-16 19:05:32 +02:00

281 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Acuvim Monitor</title>
<link rel="stylesheet" href="/app.css">
</head>
<body>
<header>
<h1>Acuvim Monitor</h1>
<span id="hdr-status" class="dot off"></span>
</header>
<nav id="tabs">
<button class="tab active" data-tab="status">Status</button>
<button class="tab" data-tab="wifi">WiFi</button>
<button class="tab" data-tab="mqtt">MQTT</button>
<button class="tab" data-tab="sleep">Sleep</button>
<button class="tab" data-tab="gsm">GSM</button>
<button class="tab" data-tab="device">Device</button>
</nav>
<main>
<!-- STATUS TAB -->
<section id="tab-status" class="panel active">
<div class="card">
<h2>Device</h2>
<div class="kv"><span>ID</span><span id="s-devid">--</span></div>
<div class="kv"><span>Firmware</span><span id="s-fw">--</span></div>
<div class="kv"><span>Uptime</span><span id="s-uptime">--</span></div>
</div>
<div class="card">
<h2>Connections</h2>
<div class="kv"><span id="s-wifi-dot" class="dot off"></span><span id="s-wifi">WiFi: --</span></div>
<div class="kv"><span id="s-mqtt-dot" class="dot off"></span><span id="s-mqtt">MQTT: --</span></div>
<div class="kv"><span id="s-gsm-dot" class="dot off"></span><span id="s-gsm">GSM: --</span></div>
<div class="kv"><span id="s-mb-dot" class="dot off"></span><span id="s-modbus">Modbus: --</span></div>
</div>
<div class="card">
<h2>Latest Readings</h2>
<div id="s-readings"><p class="muted">Waiting for data...</p></div>
</div>
<div class="card">
<h2>Storage</h2>
<div class="kv"><span id="s-sd-dot" class="dot off"></span><span id="s-sd">SD: --</span></div>
<div class="kv"><span>Queued</span><span id="s-sd-queued">--</span></div>
<div class="kv"><span>Files</span><span id="s-sd-files">--</span></div>
<div class="kv"><span>Retention</span><span id="s-sd-retention">--</span></div>
<div style="margin-top:8px">
<button class="btn-sm" onclick="drainSd()">Drain Now</button>
<button class="btn-sm" onclick="cleanupSd()" style="margin-left:4px">Cleanup</button>
</div>
</div>
</section>
<!-- WIFI TAB -->
<section id="tab-wifi" class="panel">
<div class="card">
<h2>WiFi Configuration</h2>
<label class="cb"><input type="checkbox" id="w-enabled" checked> WiFi Enabled</label>
<div class="kv"><span>Current</span><span id="w-current">--</span></div>
<h3>Available Networks <button class="btn-sm" onclick="scanWifi()">Scan</button></h3>
<div id="w-networks" class="net-list"><p class="muted">Click Scan to search</p></div>
<div class="form-group">
<label>SSID</label>
<input type="text" id="w-ssid" placeholder="Network name">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" id="w-pass" placeholder="Password">
</div>
<button class="btn" onclick="saveWifi()">Save &amp; Connect</button>
</div>
</section>
<!-- MQTT TAB -->
<section id="tab-mqtt" class="panel">
<div class="card">
<h2>MQTT Configuration</h2>
<div class="kv"><span>Status</span><span id="m-status">--</span></div>
<div class="form-group">
<label>Broker</label>
<input type="text" id="m-broker" placeholder="broker.example.com">
</div>
<div class="form-group">
<label>Port</label>
<input type="number" id="m-port" value="1883">
</div>
<div class="form-group">
<label>Username</label>
<input type="text" id="m-user" placeholder="Username">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" id="m-pass" placeholder="Password">
</div>
<div class="form-group">
<label>Topic Prefix</label>
<input type="text" id="m-prefix" value="acuvim">
</div>
<label class="cb"><input type="checkbox" id="m-tls"> Use TLS</label>
<button class="btn" onclick="saveMqtt()">Save</button>
</div>
<div class="card">
<h2>Console Application</h2>
<div class="form-group">
<label>URL</label>
<input type="text" id="c-url" placeholder="https://console.example.com">
</div>
<button class="btn" onclick="saveConsole()">Save</button>
</div>
</section>
<!-- SLEEP TAB -->
<section id="tab-sleep" class="panel">
<div class="card">
<h2>Sleep Configuration</h2>
<div class="form-group">
<label>Sleep Duration (minutes, 0 = disabled)</label>
<input type="number" id="sl-dur" min="0" value="0">
</div>
<div class="form-group">
<label>Wake Duration (seconds)</label>
<input type="number" id="sl-wake" min="10" value="300">
</div>
<button class="btn" onclick="saveSleep()">Save</button>
</div>
<div class="card">
<h2>Polling</h2>
<div class="form-group">
<label>Poll Interval (seconds)</label>
<input type="number" id="sl-poll" min="1" max="3600" value="5">
</div>
<div class="form-group">
<label>Heartbeat Interval (seconds)</label>
<input type="number" id="sl-hb" min="10" max="3600" value="60">
</div>
<button class="btn" onclick="savePolling()">Save</button>
</div>
</section>
<!-- GSM TAB -->
<section id="tab-gsm" class="panel">
<div class="card">
<h2>GSM Status</h2>
<div class="kv"><span>Modem</span><span id="g-modem">--</span></div>
<div class="kv"><span>SIM</span><span id="g-sim">--</span></div>
<div class="kv"><span id="g-status-dot" class="dot off"></span><span id="g-status">--</span></div>
<div class="kv"><span>Operator</span><span id="g-operator">--</span></div>
<div class="kv"><span>Signal</span><span id="g-signal">--</span></div>
<div class="kv"><span>IMEI</span><span id="g-imei">--</span></div>
<div class="kv"><span>ICCID</span><span id="g-iccid">--</span></div>
</div>
<div class="card">
<h2>GSM Configuration</h2>
<label class="cb"><input type="checkbox" id="g-enabled"> GSM Enabled</label>
<div class="form-group">
<label>APN</label>
<input type="text" id="g-apn" placeholder="internet">
</div>
<div class="form-group">
<label>Username</label>
<input type="text" id="g-user">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" id="g-pass">
</div>
<button class="btn" onclick="saveGsm()">Save APN</button>
</div>
<div class="card">
<h2>Transport Priority</h2>
<label class="cb"><input type="radio" name="t-prio" value="0" checked> WiFi preferred (GSM fallback)</label>
<label class="cb"><input type="radio" name="t-prio" value="1"> GSM preferred (WiFi fallback)</label>
<label class="cb"><input type="radio" name="t-prio" value="2"> WiFi only</label>
<label class="cb"><input type="radio" name="t-prio" value="3"> GSM only</label>
<h3>GSM Data Saving</h3>
<div class="form-group">
<label>GSM Poll Interval (seconds)</label>
<input type="number" id="g-poll" min="5" max="3600" value="30">
</div>
<label class="cb"><input type="checkbox" id="g-batch"> Batch readings</label>
<div class="form-group">
<label>Batch Size</label>
<input type="number" id="g-batchsize" min="2" max="60" value="6">
</div>
<button class="btn" onclick="saveTransport()">Save</button>
</div>
</section>
<!-- DEVICE TAB -->
<section id="tab-device" class="panel">
<div class="card">
<h2>Device Information</h2>
<div class="kv"><span>Device ID</span><span id="d-id">--</span></div>
<div class="kv"><span>Firmware</span><span id="d-fw">--</span></div>
<div class="kv"><span>Hardware</span><span id="d-hw">--</span></div>
<div class="kv"><span>MAC</span><span id="d-mac">--</span></div>
<div class="kv"><span>Free Heap</span><span id="d-heap">--</span></div>
<div class="kv"><span>Flash</span><span id="d-flash">--</span></div>
<div class="kv"><span>PSRAM</span><span id="d-psram">--</span></div>
</div>
<div class="card">
<h2>Firmware Update</h2>
<div class="kv"><span>Current Version</span><span id="ota-ver">--</span></div>
<div class="kv"><span>Partition</span><span id="ota-part">--</span></div>
<div class="kv"><span>Status</span><span id="ota-status">Idle</span></div>
<div id="ota-validation" style="display:none;margin:8px 0;padding:8px;background:var(--warn-bg,#fff3cd);border-radius:4px">
New firmware pending validation.
<button class="btn-sm" onclick="markOtaValid()">Mark Valid</button>
<button class="btn-sm warn" onclick="rollbackOta()">Rollback</button>
</div>
<div class="form-group">
<label>Auto Update</label>
<select id="ota-auto">
<option value="0">Disabled</option>
<option value="1" selected>Notify only</option>
<option value="2">Auto install</option>
</select>
</div>
<div class="form-group">
<label>Check Interval (hours)</label>
<input type="number" id="ota-interval" min="1" max="168" value="6">
</div>
<button class="btn" onclick="saveOtaConfig()">Save</button>
<div style="margin-top:12px">
<button class="btn-sm" onclick="checkOtaUpdate()">Check for Update</button>
</div>
<div id="ota-update-info" style="display:none;margin-top:8px;padding:8px;background:var(--info-bg,#d1ecf1);border-radius:4px">
<div class="kv"><span>Version</span><span id="ota-avail-ver">--</span></div>
<div class="kv"><span>Size</span><span id="ota-avail-size">--</span></div>
<div id="ota-avail-notes" style="margin:4px 0;font-size:0.9em"></div>
<button class="btn" onclick="installOtaAvailable()">Install Update</button>
</div>
<h3 style="margin-top:12px">Manual Upload</h3>
<div class="form-group">
<input type="file" id="ota-file" accept=".bin">
</div>
<button class="btn" onclick="uploadFirmware()">Upload &amp; Install</button>
<div id="ota-progress" style="display:none;margin-top:8px">
<div style="background:#e0e0e0;border-radius:4px;overflow:hidden;height:20px">
<div id="ota-progress-bar" style="background:var(--accent,#4CAF50);height:100%;width:0%;transition:width 0.3s"></div>
</div>
<span id="ota-progress-text">0%</span>
</div>
</div>
<div class="card">
<h2>Modbus Configuration</h2>
<div class="form-group">
<label>Slave Address (1-247)</label>
<input type="number" id="mb-addr" min="1" max="247" value="1">
</div>
<div class="form-group">
<label>Baud Rate</label>
<select id="mb-baud">
<option value="1200">1200</option>
<option value="2400">2400</option>
<option value="4800">4800</option>
<option value="9600" selected>9600</option>
<option value="19200">19200</option>
</select>
</div>
<button class="btn" onclick="saveModbus()">Save</button>
</div>
<div class="card">
<h2>Actions</h2>
<button class="btn warn" onclick="restartDevice()">Restart Device</button>
<button class="btn danger" onclick="factoryReset()">Factory Reset</button>
</div>
</section>
</main>
<div id="toast" class="toast hidden"></div>
<script src="/app.js"></script>
</body>
</html>