Files
story/firmware/echoear-platform-agent/main/assets/wifi_configuration_ap.html
T

33 lines
6.0 KiB
HTML
Raw Normal View History

2026-09-06 17:04:11 +08:00
<!doctype html>
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>EchoEar 平台激活</title>
<style>body{font:16px system-ui;background:#f1f5f9;margin:0;color:#172b4d}main{max-width:420px;margin:32px auto;padding:24px;background:white;border-radius:16px}label{display:block;margin:18px 0}input,button{box-sizing:border-box;width:100%;padding:12px;border:1px solid #cbd5e1;border-radius:8px;font:inherit}input{margin-top:6px}button{cursor:pointer;margin-top:12px;background:#1769d2;color:white}button:disabled{opacity:.6;cursor:wait}small{color:#526079}#status{white-space:pre-wrap;line-height:1.6}#networks a{display:block;margin:12px 0}</style></head>
<body><main><h1>EchoEar 平台激活</h1><p>填写家中的2.4 GHz Wi-Fi 和平台分配给这台设备的凭据。</p>
<form id="form" novalidate>
<label>2.4 GHz Wi-Fi 名称<input id="ssid" required></label>
<label>Wi-Fi 密码<input id="password" type="password" maxlength="64" autocomplete="off"><small>无密码网络可留空。</small></label>
<label>平台 Device ID<input id="device_id" required minlength="36" maxlength="36" autocomplete="off"></label>
<label>平台 Device Secret<input id="device_secret" type="password" required minlength="32" maxlength="128" autocomplete="off"></label>
<p id="status" role="status" aria-live="polite">请保持手机连接设备热点。</p>
<button id="submit" type="submit">连接并激活</button><button id="scan" type="button">扫描附近的 Wi-Fi</button><div id="networks"></div>
</form></main><script>
const form=document.getElementById('form'), button=document.getElementById('submit'), scan=document.getElementById('scan'), status=document.getElementById('status'), networks=document.getElementById('networks');
let busy=false;
const messages={invalid_fields:'请检查配置:Wi-Fi 名称最多32字节,Device ID 为36位,Device Secret 为32至128位。',wifi_failed:'Wi-Fi 连接失败或超时。请检查2.4 GHz网络名称和密码,再重试。',save_failed:'设备保存配置失败,请重试。',receive_timeout:'设备未完整收到配置,请保持连接热点后重试。',no_memory:'设备暂时忙,请稍后重试。'};
function show(text,failed=false){status.textContent=text;status.style.color=failed?'#b42318':'#175cd3'}
function setBusy(value){busy=value;button.disabled=value;scan.disabled=value;button.textContent=value?'正在连接,请稍候…':'连接并激活'}
async function request(path,options={},timeout=7000){const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),timeout);try{const response=await fetch(path,{...options,cache:'no-store',signal:controller.signal}),data=await response.json();if(!response.ok)throw Object.assign(new Error(data.error||'request_failed'),{code:data.error});return data}finally{clearTimeout(timer)}}
const delay=ms=>new Promise(resolve=>setTimeout(resolve,ms));
async function watchResult(){const deadline=Date.now()+50000;while(Date.now()<deadline){let data;try{data=await request('/setup-status')}catch(_){show('暂时联系不到设备。请保持连接 VolcConvAI 热点,正在重试查询…');await delay(1500);continue}
if(data.state==='saved'){show('配置已保存!设备将在15秒内重启并进行平台激活。热点随后消失属于正常现象,请查看设备屏幕结果。');button.textContent='配置已保存';return}
if(data.state==='wifi_failed'||data.state==='save_failed'){setBusy(false);show(messages[data.state],true);return}
if(data.state==='idle'){setBusy(false);show('设备尚未收到连接任务,请检查配置后重新提交。',true);return}
show('正在连接家中 Wi-Fi,最多等待约30秒。请保持连接设备热点;暂时断开时请重新连接。');await delay(1500)}setBusy(false);show('暂时无法确认结果。若设备已重启,请查看屏幕;若热点仍在,请重新连接热点并刷新此页查询。',true)}
form.addEventListener('submit',async event=>{event.preventDefault();if(busy)return;const id=document.getElementById('device_id');id.value=id.value.trim();if(!form.checkValidity()){show('请完整填写 Wi-Fi 名称、36位 Device ID 和32至128位 Device Secret。',true);form.reportValidity();return}
const data={ssid:document.getElementById('ssid').value,password:document.getElementById('password').value,device_id:id.value,device_secret:document.getElementById('device_secret').value};
if(new TextEncoder().encode(data.ssid).length>32||new TextEncoder().encode(data.password).length>64){show('Wi-Fi 名称不能超过32字节,密码不能超过64字节(中文占多个字节)。',true);return}
setBusy(true);show('正在提交配置…');try{await request('/submit',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)})}catch(failure){if(failure.code&&failure.code!=='busy'){setBusy(false);show(messages[failure.code]||'设备拒绝请求,请检查配置后重试。',true);return}show('正在确认设备是否收到配置…')}await watchResult()});
scan.addEventListener('click',async()=>{if(busy)return;scan.disabled=true;button.disabled=true;networks.textContent='正在扫描附近的2.4 GHz Wi-Fi…';try{const data=await request('/scan',{},15000);networks.textContent=data.length?'点击选择网络:':'未找到网络,也可以手动填写。';data.forEach(item=>{const link=document.createElement('a');link.href='#';link.textContent=item.ssid+' ('+item.rssi+' dBm)';link.addEventListener('click',event=>{event.preventDefault();document.getElementById('ssid').value=item.ssid});networks.appendChild(link)})}catch(_){networks.textContent='扫描失败,可以手动填写 Wi-Fi 名称后连接。'}finally{scan.disabled=busy;button.disabled=busy}});
async function restoreStatus(){try{const data=await request('/setup-status');if(busy)return;if(data.state==='connecting'||data.state==='saved'){setBusy(true);await watchResult()}else if(messages[data.state])show(messages[data.state],true)}catch(_){if(!busy)show('请确认手机仍连接设备热点,再刷新此页。',true)}}
restoreStatus();
</script></body></html>