webui: add AP settings and organize config page
This commit is contained in:
@@ -174,9 +174,44 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
|
|||||||
|
|
||||||
<div id="configPage" class="page">
|
<div id="configPage" class="page">
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
|
<section class="card span12 configIntro">
|
||||||
|
<div class="configGroupHeader">
|
||||||
|
<div>
|
||||||
|
<div class="configGroupTitle">Settings</div>
|
||||||
|
<div class="configGroupHelp">Configure the Cargo ESP32 controller. The Cargo ESP remains the source of truth for relays, sensors, WiFi, and AP settings.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="card span12">
|
<section class="card span12">
|
||||||
<div class="label">WiFi Config</div>
|
<div class="configGroupHeader">
|
||||||
<div class="sub">AP remains available at 192.168.4.1. Lower priority number is tried first.</div>
|
<div>
|
||||||
|
<div class="configGroupTitle">Access Point</div>
|
||||||
|
<div class="configGroupHelp">This is the local camp network hosted by the Cargo ESP32. Phones and the dashboard connect here when no Starlink/home WiFi is available.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list">
|
||||||
|
<div class="row"><span class="muted">Current AP</span><span class="v" id="apCurrentSsid">--</span></div>
|
||||||
|
<div class="row"><span class="muted">Security</span><span class="v" id="apSecurity">--</span></div>
|
||||||
|
<input id="apSsidInput" placeholder="AP SSID, example: Xterra-Camp">
|
||||||
|
<div class="inputHelp">SSID must be 1-32 characters.</div>
|
||||||
|
<input id="apPasswordInput" placeholder="New AP password" type="password">
|
||||||
|
<div class="inputHelp">Password must be 8-63 characters. The current password is never displayed.</div>
|
||||||
|
<div class="inlineActions">
|
||||||
|
<button onclick="toggleApPassword()">Show / Hide Password</button>
|
||||||
|
<button class="on" onclick="saveApConfig()">Save AP Settings</button>
|
||||||
|
</div>
|
||||||
|
<div class="configWarning">Saving AP settings restarts the access point. Your phone may disconnect and need to reconnect to the new SSID/password. Dashboard auto-migration is planned but not implemented yet.</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card span12">
|
||||||
|
<div class="configGroupHeader">
|
||||||
|
<div>
|
||||||
|
<div class="configGroupTitle">Home / Starlink WiFi</div>
|
||||||
|
<div class="configGroupHelp">Optional client networks the Cargo ESP can join. Lower priority number is tried first. The AP remains available for local access.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<input id="w1s" placeholder="Network 1 SSID">
|
<input id="w1s" placeholder="Network 1 SSID">
|
||||||
<input id="w1p" placeholder="Network 1 password" type="password">
|
<input id="w1p" placeholder="Network 1 password" type="password">
|
||||||
@@ -193,15 +228,23 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card span12">
|
<section class="card span12">
|
||||||
<div class="label">Relay Config</div>
|
<div class="configGroupHeader">
|
||||||
<div class="sub">Rename relay outputs. IDs stay fixed for API/UART compatibility.</div>
|
<div>
|
||||||
|
<div class="configGroupTitle">Relay Outputs</div>
|
||||||
|
<div class="configGroupHelp">Rename relay outputs. IDs stay fixed for API/UART compatibility.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="list" id="relayConfigList"></div>
|
<div class="list" id="relayConfigList"></div>
|
||||||
<button onclick="saveRelayConfig()">Save Relay Names</button>
|
<button onclick="saveRelayConfig()">Save Relay Names</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card span12">
|
<section class="card span12">
|
||||||
<div class="label">Temperature Assignment</div>
|
<div class="configGroupHeader">
|
||||||
<div class="sub">Scan probes, then assign a discovered sensor to a configured temp slot.</div>
|
<div>
|
||||||
|
<div class="configGroupTitle">Temperature Assignment</div>
|
||||||
|
<div class="configGroupHelp">Scan probes, then assign a discovered sensor to a configured temp slot.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<button onclick="scanTemps()">Scan Temp Probes</button>
|
<button onclick="scanTemps()">Scan Temp Probes</button>
|
||||||
<div id="tempScanResults" class="list"></div>
|
<div id="tempScanResults" class="list"></div>
|
||||||
@@ -219,7 +262,12 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card span12">
|
<section class="card span12">
|
||||||
<div class="label">Device Config</div>
|
<div class="configGroupHeader">
|
||||||
|
<div>
|
||||||
|
<div class="configGroupTitle">Device</div>
|
||||||
|
<div class="configGroupHelp">Controller identity and BMS connection settings.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<input id="deviceNameInput" placeholder="Device name">
|
<input id="deviceNameInput" placeholder="Device name">
|
||||||
<button onclick="saveDeviceConfig()">Save Device Name</button>
|
<button onclick="saveDeviceConfig()">Save Device Name</button>
|
||||||
@@ -309,6 +357,59 @@ function weatherEmoji(f){
|
|||||||
if(f < 85) return "☀️";
|
if(f < 85) return "☀️";
|
||||||
return "🔥";
|
return "🔥";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleApPassword(){
|
||||||
|
const input=$("apPasswordInput");
|
||||||
|
if(!input) return;
|
||||||
|
input.type=input.type==="password" ? "text" : "password";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderApConfig(data){
|
||||||
|
const n=data.network||{};
|
||||||
|
if($("apCurrentSsid")) $("apCurrentSsid").textContent=n.ap_ssid||"--";
|
||||||
|
if($("apSecurity")) $("apSecurity").textContent=n.ap_auth||"--";
|
||||||
|
if($("apSsidInput") && !$("apSsidInput").dataset.touched){
|
||||||
|
$("apSsidInput").value=n.ap_ssid||"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveApConfig(){
|
||||||
|
const ssid=$("apSsidInput")?.value.trim()||"";
|
||||||
|
const password=$("apPasswordInput")?.value||"";
|
||||||
|
|
||||||
|
if(!ssid){
|
||||||
|
alert("AP SSID is required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(password.length<8 || password.length>63){
|
||||||
|
alert("AP password must be 8-63 characters.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!confirm("Saving AP settings will restart the controller access point. Your phone may disconnect. Continue?")){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const r=await fetch(api("/config/ap"),{
|
||||||
|
method:"POST",
|
||||||
|
headers:{"Content-Type":"application/json"},
|
||||||
|
body:JSON.stringify({ssid,password})
|
||||||
|
});
|
||||||
|
|
||||||
|
const text=await r.text();
|
||||||
|
let body=null;
|
||||||
|
try{ body=JSON.parse(text); }catch(e){}
|
||||||
|
|
||||||
|
if(!r.ok || body?.ok===false){
|
||||||
|
alert("Failed to save AP settings: "+(body?.error||text||r.status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("apPasswordInput").value="";
|
||||||
|
alert("AP settings saved. Reconnect to the new WiFi network if this page stops updating.");
|
||||||
|
}
|
||||||
|
|
||||||
function renderWeatherBadge(data){
|
function renderWeatherBadge(data){
|
||||||
const badge=$("weatherBadge");
|
const badge=$("weatherBadge");
|
||||||
if(!badge) return;
|
if(!badge) return;
|
||||||
@@ -431,6 +532,7 @@ function render(data){
|
|||||||
$("conn").className="pill good";
|
$("conn").className="pill good";
|
||||||
$("device").textContent=data.config?.device_name||"Overland Controller";
|
$("device").textContent=data.config?.device_name||"Overland Controller";
|
||||||
renderWeatherBadge(data);
|
renderWeatherBadge(data);
|
||||||
|
renderApConfig(data);
|
||||||
|
|
||||||
const b=data.battery||{};
|
const b=data.battery||{};
|
||||||
$("soc").textContent=fmt(b.soc,0);
|
$("soc").textContent=fmt(b.soc,0);
|
||||||
|
|||||||
@@ -329,3 +329,26 @@ def test_cargo_ap_password_is_not_returned_by_get_config_ap():
|
|||||||
|
|
||||||
assert 'response["password_set"]' in get_body
|
assert 'response["password_set"]' in get_body
|
||||||
assert 'response["password"]' not in get_body
|
assert 'response["password"]' not in get_body
|
||||||
|
|
||||||
|
|
||||||
|
def test_embedded_webui_has_ap_settings_controls():
|
||||||
|
source = firmware_source()
|
||||||
|
|
||||||
|
assert "Access Point" in source
|
||||||
|
assert 'id="apSsidInput"' in source
|
||||||
|
assert 'id="apPasswordInput"' in source
|
||||||
|
assert "function saveApConfig()" in source
|
||||||
|
assert 'api("/config/ap")' in source
|
||||||
|
assert "toggleApPassword" in source
|
||||||
|
assert "renderApConfig(data)" in source
|
||||||
|
assert "Dashboard auto-migration is planned but not implemented yet" in source
|
||||||
|
|
||||||
|
|
||||||
|
def test_embedded_webui_config_page_is_grouped():
|
||||||
|
source = firmware_source()
|
||||||
|
|
||||||
|
assert "configGroupTitle" in source
|
||||||
|
assert "Home / Starlink WiFi" in source
|
||||||
|
assert "Relay Outputs" in source
|
||||||
|
assert "Temperature Assignment" in source
|
||||||
|
assert "Maintenance" in source
|
||||||
|
|||||||
Reference in New Issue
Block a user