Fix empty display after save (lastSaveTime guard) + OPNsense JSON payload fix

This commit is contained in:
2026-05-17 00:28:26 +10:00
parent 77d9e9c523
commit 4142d1c9e1
+9 -15
View File
@@ -133,8 +133,9 @@ input[type=time]::-webkit-calendar-picker-indicator{filter:invert(.5)}
</div>
</div>
<script>
// Uses sensor.home_dhcp_leases_lan — the OPNsense integration DHCP sensor
// Leases attribute has capital L, fields: address, hostname, mac, expires
// DHCP source: sensor.home_dhcp_leases_lan
// attributes.Leases (capital L): [{address, hostname, mac, expires, type}]
// Online = expires is in the future
const DHCP='sensor.home_dhcp_leases_lan';
const CHUNKS=12, CHUNK_SIZE=250;
const chunkId=i=>`input_text.parental_config_${i}`;
@@ -142,7 +143,7 @@ const COLORS=['#ef5350','#ff7043','#ffa726','#66bb6a','#26c6da','#42a5f5','#7e57
const MAC_RE=/^([0-9a-f]{2}:){5}[0-9a-f]{2}$/i;
let ws,wsId=1,pending={},retryTimer;
let states={},cfg={users:[]},prefs={url:'',token:''};
let devTargetUser=null,schedOpen={},renderTimer=null;
let devTargetUser=null,schedOpen={},renderTimer=null,lastSaveTime=0;
function loadPrefs(){try{const p=localStorage.getItem('pc2_prefs');if(p){prefs=JSON.parse(p);return true;}}catch(e){}return false;}
function savePrefs(){localStorage.setItem('pc2_prefs',JSON.stringify(prefs));}
@@ -162,7 +163,6 @@ function saveSettings(){
prefs={url,token:tok};savePrefs();
closeModal('settings-modal');if(ws)ws.close();connect();
}
function connect(){
setHA('conn');clearTimeout(retryTimer);
const wsUrl=prefs.url.replace(/^https?/,m=>m==='https'?'wss':'ws')+'/api/websocket';
@@ -207,7 +207,8 @@ async function onAuth(){
}
function onStateChange({entity_id,new_state}){
states[entity_id]=new_state;
if(entity_id===chunkId(0)){parseCfg();schedRender();return;}
// Only re-parse config if the change didn't come from our own save (5s grace period)
if(entity_id===chunkId(0)){if(Date.now()-lastSaveTime>5000)parseCfg();schedRender();return;}
if(entity_id===DHCP){updateDHCP();schedRender();return;}
if(entity_id.startsWith('device_tracker.')){schedRender();}
}
@@ -229,6 +230,7 @@ function parseCfg(){
}
}
async function saveCfg(){
lastSaveTime=Date.now();
const json=JSON.stringify(cfg);
try{localStorage.setItem('pc2_backup',json);}catch(e){}
const saves=[];
@@ -241,14 +243,8 @@ async function saveCfg(){
catch(e){toast('Save failed','e');console.error('saveCfg',e);}
}
// ── DHCP: sensor.home_dhcp_leases_lan ──
// attributes.Leases (capital L) = array of {address, hostname, mac, expires, type}
// Online = expires timestamp is in the future
function getLeases(){return states[DHCP]?.attributes?.Leases||[];}
function isOnline(lease){
if(!lease.expires)return true;
return new Date(lease.expires)>new Date();
}
function isOnline(lease){if(!lease.expires)return true;return new Date(lease.expires)>new Date();}
function updateDHCP(){
const s=states[DHCP];
const dot=document.getElementById('dhcp-dot');const lbl=document.getElementById('dhcp-lbl');
@@ -263,7 +259,6 @@ function getLease(mac){
function deviceInfo(mac){
const lease=getLease(mac);
if(lease)return{online:isOnline(lease),ip:lease.address||'',label:lease.hostname||mac};
// fallback: GPS device_tracker entities
const m=normMac(mac);
for(const[eid,s]of Object.entries(states)){
if(!eid.startsWith('device_tracker.'))continue;
@@ -281,7 +276,6 @@ function discoveredDevices(){
if(!mac||assigned.has(mac))continue;
out.push({mac,name:r.hostname||mac,ip:r.address||'',online:isOnline(r)});
}
// also include any GPS trackers with MACs not already in leases
for(const[eid,s]of Object.entries(states)){
if(!eid.startsWith('device_tracker.'))continue;
const a=s.attributes||{};const mac=normMac(a.mac||a.mac_address||a.macaddress||'');
@@ -363,7 +357,7 @@ function openAddDeviceModal(userId){
const disc=discoveredDevices();const list=document.getElementById('disc-list');
const note=document.getElementById('dhcp-note');
const hasDHCP=!!states[DHCP]&&states[DHCP].state!=='unavailable'&&states[DHCP].state!=='unknown';
note.innerHTML=hasDHCP?'':`<div class="ibox">DHCP sensor not found. Check that <code>sensor.home_dhcp_leases_lan</code> exists in HA. You can still enter a MAC manually.</div>`;
note.innerHTML=hasDHCP?'':`<div class="ibox">DHCP sensor not found. Check <code>sensor.home_dhcp_leases_lan</code> exists in HA. You can still enter a MAC manually.</div>`;
if(!disc.length){list.innerHTML=`<div style="padding:10px;text-align:center;font-size:.8rem;color:var(--muted)">${hasDHCP?'All devices already assigned':'No DHCP data available'}</div>`;}
else{list.innerHTML=disc.map(d=>`<div class="dlist-item" data-mac="${d.mac}" data-name="${esc(d.name)}" onclick="pickDisc(this)"><span style="width:7px;height:7px;border-radius:50%;background:${d.online?'var(--ok)':'var(--muted)'};flex-shrink:0"></span><div><div class="dl-name">${esc(d.name)}</div><div class="dl-sub">${d.mac}${d.ip?' · '+d.ip:''}</div></div></div>`).join('');}
openModal('add-dev-modal');