diff --git a/data/webui/index.html b/data/webui/index.html deleted file mode 100644 index 1ea5bef..0000000 --- a/data/webui/index.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - RTC_BL_PHONE Web UI - - - -
-

RTC_BL_PHONE Web UI

-

Chargement...

-
- - - -
-
-

Statut runtime

-

-    
- -
-

Configuration

-
- -
-

-        
-
-

Audio config (JSON)

- -
- -
-
-
-

Pins config (JSON)

- -
- -
-
-
-
- -
-

Réseau et bridges

-
-
-

WiFi

-
- - - -
-
- - - -
-

-        
- -
-

ESP-NOW

-
- - - -
-
- -

ESPNOW: envoi en broadcast uniquement

- -
-
- - - -
-

-          

-        
- -
-
- -
-

Contrôle

-
- - - - -
-
- - -
-

-    
-
- - - - diff --git a/data/webui/script.js b/data/webui/script.js deleted file mode 100644 index 07dfb3e..0000000 --- a/data/webui/script.js +++ /dev/null @@ -1,433 +0,0 @@ -const SECTION_MAP = { - dashboard: "dashboardSection", - config: "configSection", - network: "networkSection", - control: "controlSection", -}; -let realtimeSource = null; -let realtimeConnected = false; -let fallbackPollingTimer = null; - -function showSection(section) { - Object.values(SECTION_MAP).forEach((id) => { - const el = document.getElementById(id); - if (el) { - el.classList.remove("active"); - } - }); - const sectionEl = document.getElementById(SECTION_MAP[section]); - if (sectionEl) { - sectionEl.classList.add("active"); - } -} - -function setJson(id, value) { - const el = document.getElementById(id); - if (!el) { - return; - } - if (typeof value === "string") { - el.textContent = value; - return; - } - el.textContent = JSON.stringify(value, null, 2); -} - -async function requestJson(url, options = {}) { - const response = await fetch(url, options); - const text = await response.text(); - let parsed = {}; - if (text) { - try { - parsed = JSON.parse(text); - } catch (_) { - parsed = { raw: text }; - } - } - if (!response.ok) { - throw new Error(`HTTP ${response.status} ${text || ""}`.trim()); - } - return parsed; -} - -function jsonHeaders() { - return { "Content-Type": "application/json" }; -} - -function parseJsonInput(id) { - const raw = document.getElementById(id).value.trim(); - if (!raw) { - return {}; - } - return JSON.parse(raw); -} - -function parsePayloadValue(rawPayload) { - const trimmed = rawPayload.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - return JSON.parse(trimmed); - } catch (_) { - return rawPayload; - } - } - return rawPayload; -} - -function parseRealtimeData(raw) { - try { - return JSON.parse(raw); - } catch (_) { - return { raw }; - } -} - -function applyStatusSnapshot(status) { - const line = document.getElementById("statusLine"); - const telephonyState = status.telephony?.state || "n/a"; - const hook = status.telephony?.hook || "n/a"; - const wifiState = status.wifi?.state || "n/a"; - const peers = status.espnow?.peer_count ?? 0; - const liveState = realtimeConnected ? "live=on" : "live=off"; - line.textContent = - `board=${status.board_profile || "n/a"} telephony=${telephonyState} hook=${hook} ` + - `wifi=${wifiState} espnow_peers=${peers} ${liveState}`; - - setJson("statusJson", status); - if (status.wifi) { - setJson("wifiJson", status.wifi); - } - if (status.espnow) { - setJson("espnowJson", status.espnow); - setJson("espnowPeersJson", { peers: status.espnow.peers || [] }); - } -} - -function connectRealtime() { - if (!window.EventSource) { - return; - } - if (realtimeSource) { - realtimeSource.close(); - } - - realtimeSource = new EventSource("/api/events"); - - realtimeSource.onopen = () => { - realtimeConnected = true; - }; - - realtimeSource.addEventListener("hello", () => { - realtimeConnected = true; - }); - - realtimeSource.addEventListener("status", (event) => { - realtimeConnected = true; - const status = parseRealtimeData(event.data); - applyStatusSnapshot(status); - }); - - realtimeSource.addEventListener("dispatch", (event) => { - const payload = parseRealtimeData(event.data); - const out = { stream: "dispatch" }; - if (payload && typeof payload === "object" && !Array.isArray(payload)) { - Object.assign(out, payload); - } else { - out.payload = payload; - } - setJson("actionResult", out); - }); - - realtimeSource.addEventListener("effect", (event) => { - const payload = parseRealtimeData(event.data); - const out = { stream: "effect" }; - if (payload && typeof payload === "object" && !Array.isArray(payload)) { - Object.assign(out, payload); - } else { - out.payload = payload; - } - setJson("actionResult", out); - }); - - realtimeSource.onerror = () => { - realtimeConnected = false; - }; -} - -function ensureFallbackPolling() { - if (fallbackPollingTimer !== null) { - return; - } - fallbackPollingTimer = window.setInterval(() => { - if (!realtimeConnected) { - refreshStatus().catch(() => {}); - } - }, 2000); -} - -async function refreshStatus() { - try { - const status = await requestJson("/api/status"); - applyStatusSnapshot(status); - } catch (error) { - const line = document.getElementById("statusLine"); - const liveState = realtimeConnected ? "live=on" : "live=off"; - line.textContent = `Erreur statut: ${error.message}`; - line.textContent += ` ${liveState}`; - setJson("statusJson", { error: error.message }); - } -} - -async function refreshConfig() { - try { - const [pins, audio] = await Promise.all([ - requestJson("/api/config/pins"), - requestJson("/api/config/audio"), - ]); - setJson("configJson", { pins, audio }); - const audioInput = document.getElementById("audioConfigInput"); - const pinsInput = document.getElementById("pinsConfigInput"); - if (audioInput) { - audioInput.value = JSON.stringify(audio, null, 2); - } - if (pinsInput) { - pinsInput.value = JSON.stringify(pins, null, 2); - } - } catch (error) { - setJson("configJson", { error: error.message }); - } -} - -async function refreshNetwork() { - try { - const [wifi, espnow, peers] = await Promise.all([ - requestJson("/api/network/wifi"), - requestJson("/api/network/espnow"), - requestJson("/api/network/espnow/peer"), - ]); - setJson("wifiJson", wifi); - setJson("espnowJson", espnow); - setJson("espnowPeersJson", peers); - } catch (error) { - const err = { error: error.message }; - setJson("wifiJson", err); - setJson("espnowJson", err); - setJson("espnowPeersJson", err); - } -} - -async function sendControl(action) { - const result = await requestJson("/api/control", { - method: "POST", - headers: jsonHeaders(), - body: JSON.stringify({ action }), - }); - setJson("actionResult", result); - await Promise.all([refreshStatus(), refreshNetwork()]); - return result; -} - -function bindEvents() { - document.querySelectorAll("nav button[data-section]").forEach((button) => { - button.addEventListener("click", () => showSection(button.dataset.section)); - }); - - document.getElementById("refreshAllBtn").addEventListener("click", async () => { - await Promise.all([refreshStatus(), refreshConfig(), refreshNetwork()]); - }); - document.getElementById("refreshConfigBtn").addEventListener("click", refreshConfig); - document.getElementById("espnowRefreshBtn").addEventListener("click", refreshNetwork); - - document.getElementById("applyAudioConfigBtn").addEventListener("click", async () => { - try { - const payload = parseJsonInput("audioConfigInput"); - const result = await requestJson("/api/config/audio", { - method: "POST", - headers: jsonHeaders(), - body: JSON.stringify(payload), - }); - setJson("actionResult", result); - await Promise.all([refreshConfig(), refreshStatus()]); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("applyPinsConfigBtn").addEventListener("click", async () => { - try { - const payload = parseJsonInput("pinsConfigInput"); - const result = await requestJson("/api/config/pins", { - method: "POST", - headers: jsonHeaders(), - body: JSON.stringify(payload), - }); - setJson("actionResult", result); - await Promise.all([refreshConfig(), refreshStatus()]); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("wifiConnectForm").addEventListener("submit", async (event) => { - event.preventDefault(); - const ssid = document.getElementById("wifiSsid").value.trim(); - const pass = document.getElementById("wifiPass").value; - try { - const result = await requestJson("/api/network/wifi/connect", { - method: "POST", - headers: jsonHeaders(), - body: JSON.stringify({ ssid, pass }), - }); - setJson("actionResult", result); - await Promise.all([refreshStatus(), refreshNetwork()]); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("wifiDisconnectBtn").addEventListener("click", async () => { - try { - const result = await requestJson("/api/network/wifi/disconnect", { - method: "POST", - headers: jsonHeaders(), - body: "{}", - }); - setJson("actionResult", result); - await Promise.all([refreshStatus(), refreshNetwork()]); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("wifiReconnectBtn").addEventListener("click", async () => { - try { - const result = await requestJson("/api/network/wifi/reconnect", { - method: "POST", - headers: jsonHeaders(), - body: "{}", - }); - setJson("actionResult", result); - await Promise.all([refreshStatus(), refreshNetwork()]); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("wifiScanBtn").addEventListener("click", async () => { - try { - const result = await requestJson("/api/network/wifi/scan", { - method: "POST", - headers: jsonHeaders(), - body: "{}", - }); - setJson("actionResult", result); - await refreshNetwork(); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("espnowOnBtn").addEventListener("click", async () => { - try { - const result = await requestJson("/api/network/espnow/on", { - method: "POST", - headers: jsonHeaders(), - body: "{}", - }); - setJson("actionResult", result); - await Promise.all([refreshStatus(), refreshNetwork()]); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("espnowOffBtn").addEventListener("click", async () => { - try { - const result = await requestJson("/api/network/espnow/off", { - method: "POST", - headers: jsonHeaders(), - body: "{}", - }); - setJson("actionResult", result); - await Promise.all([refreshStatus(), refreshNetwork()]); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("espnowPeerForm").addEventListener("submit", async (event) => { - event.preventDefault(); - const mac = document.getElementById("espnowMac").value.trim(); - try { - const result = await requestJson("/api/network/espnow/peer", { - method: "POST", - headers: jsonHeaders(), - body: JSON.stringify({ mac }), - }); - setJson("actionResult", result); - await refreshNetwork(); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("espnowDelBtn").addEventListener("click", async () => { - const mac = document.getElementById("espnowMac").value.trim(); - try { - const result = await requestJson("/api/network/espnow/peer", { - method: "DELETE", - headers: jsonHeaders(), - body: JSON.stringify({ mac }), - }); - setJson("actionResult", result); - await refreshNetwork(); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.getElementById("espnowSendForm").addEventListener("submit", async (event) => { - event.preventDefault(); - const payloadRaw = document.getElementById("espnowPayload").value; - const payload = parsePayloadValue(payloadRaw); - try { - const result = await requestJson("/api/network/espnow/send", { - method: "POST", - headers: jsonHeaders(), - body: JSON.stringify({ payload }), - }); - setJson("actionResult", result); - await refreshNetwork(); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - - document.querySelectorAll("#controlSection button[data-action]").forEach((button) => { - button.addEventListener("click", async () => { - try { - await sendControl(button.dataset.action); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); - }); - - document.getElementById("rawCommandForm").addEventListener("submit", async (event) => { - event.preventDefault(); - const action = document.getElementById("rawCommandInput").value.trim(); - try { - await sendControl(action); - } catch (error) { - setJson("actionResult", { error: error.message }); - } - }); -} - -document.addEventListener("DOMContentLoaded", async () => { - bindEvents(); - connectRealtime(); - ensureFallbackPolling(); - await Promise.all([refreshStatus(), refreshConfig(), refreshNetwork()]); - showSection("dashboard"); -}); diff --git a/data/webui/style.css b/data/webui/style.css deleted file mode 100644 index 8b71e44..0000000 --- a/data/webui/style.css +++ /dev/null @@ -1,116 +0,0 @@ -:root { - --bg: #f4f6f8; - --panel: #ffffff; - --text: #1f2933; - --accent: #0f6ab6; - --accent-strong: #084a81; - --ok: #19753d; - --border: #d8dee4; - --warn: #9a5f00; -} - -* { - box-sizing: border-box; -} - -body { - margin: 0; - padding: 24px; - font-family: "Segoe UI", Tahoma, sans-serif; - background: linear-gradient(135deg, #edf4fa, #f8f9fb); - color: var(--text); -} - -header { - margin-bottom: 16px; -} - -h1 { - margin: 0 0 8px; - color: var(--accent); -} - -nav { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 16px; -} - -button { - background: var(--accent); - color: #fff; - border: none; - border-radius: 6px; - padding: 8px 12px; - cursor: pointer; -} - -button:hover { - background: var(--accent-strong); -} - -section { - display: none; - background: var(--panel); - border: 1px solid var(--border); - border-radius: 10px; - padding: 16px; -} - -section.active { - display: block; -} - -.grid2 { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 16px; -} - -.inline-actions { - margin: 8px 0; - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -input, -select, -textarea { - padding: 8px; - border: 1px solid var(--border); - border-radius: 6px; - font: inherit; - color: inherit; - background: #fff; -} - -form { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-top: 10px; -} - -pre { - background: #1f2933; - color: #e8eef4; - border-radius: 6px; - padding: 12px; - overflow-x: auto; - white-space: pre-wrap; - word-break: break-word; -} - -@media (max-width: 760px) { - body { - padding: 12px; - } - - button, - input, - textarea { - width: 100%; - } -} diff --git a/platformio.ini b/platformio.ini deleted file mode 100644 index 5519698..0000000 --- a/platformio.ini +++ /dev/null @@ -1,108 +0,0 @@ -; PlatformIO Project Configuration File - -[platformio] -default_envs = esp32-s3-devkitc-1 -build_dir = .pio/build - -[env] -platform = espressif32 -framework = arduino -monitor_speed = 115200 -test_build_src = yes -build_flags = - -DCORE_DEBUG_LEVEL=1 -lib_deps = - bblanchon/ArduinoJson@^7.0.4 - throwtheswitch/Unity@^2.6.1 - ESP32Async/AsyncTCP@^3.3.2 - ESP32Async/ESPAsyncWebServer@^3.6.0 - https://github.com/pschatzmann/arduino-audio-tools.git - https://github.com/bitluni/OsciDisplay.git -lib_ignore = - ESPAsyncTCP - RPAsyncTCP - - -; S3 scope only: ESP32-A252 build profile intentionally removed from active configs in this project phase. - -[env:esp32-s3-devkitc-1] -board = esp32-s3-devkitc-1 -board_build.partitions = partitions/esp32s3_no_ota_spiffs_16MB.csv -upload_port = /dev/cu.usbserial-0001 -monitor_port = /dev/cu.usbserial-0001 -build_flags = - ${env.build_flags} - -DBOARD_PROFILE_ESP32_S3 -build_src_filter = - + - +