Fix: use sensor.home_dhcp_leases_lan with attributes.Leases (capital L), address/hostname/expires fields

This commit is contained in:
2026-05-16 19:52:37 +10:00
parent 8a5fe108dc
commit a566400746
+21 -14
View File
@@ -86,7 +86,7 @@ input[type=time]::-webkit-calendar-picker-indicator{filter:invert(.5)}
<span class="sc-logo">🛡️</span>
<h1>Parental Controls</h1>
<p>Enter your Home Assistant URL and a Long-Lived Access Token (HA Profile → Security → Long-Lived Access Tokens).</p>
<div class="fg"><label>Home Assistant URL</label><input id="s-url" class="fi" type="text" placeholder="https://ha.example.com" autocomplete="off"/></div>
<div class="fg"><label>Home Assistant URL</label><input id="s-url" class="fi" type="text" placeholder="http://10.0.0.55:8123" autocomplete="off"/></div>
<div class="fg"><label>Long-Lived Access Token</label><input id="s-tok" class="fi" type="password" placeholder="eyJ..." autocomplete="off"/></div>
<button class="btn btn-p btn-full" onclick="doSetup()">Connect →</button>
</div>
@@ -133,7 +133,9 @@ input[type=time]::-webkit-calendar-picker-indicator{filter:invert(.5)}
</div>
</div>
<script>
const DHCP='sensor.opnsense_dhcp_leases';
// Uses sensor.home_dhcp_leases_lan — the OPNsense integration DHCP sensor
// Leases attribute has capital L, fields: address, hostname, mac, expires
const DHCP='sensor.home_dhcp_leases_lan';
const CHUNKS=12, CHUNK_SIZE=250;
const chunkId=i=>`input_text.parental_config_${i}`;
const COLORS=['#ef5350','#ff7043','#ffa726','#66bb6a','#26c6da','#42a5f5','#7e57c2','#ec407a','#26a69a','#e6c229'];
@@ -239,7 +241,14 @@ async function saveCfg(){
catch(e){toast('Save failed','e');console.error('saveCfg',e);}
}
function getLeases(){return states[DHCP]?.attributes?.rows||[];}
// ── 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 updateDHCP(){
const s=states[DHCP];
const dot=document.getElementById('dhcp-dot');const lbl=document.getElementById('dhcp-lbl');
@@ -249,15 +258,12 @@ function updateDHCP(){
}
function getLease(mac){
const m=normMac(mac);
return getLeases().find(r=>normMac(r.mac||r.mac_address||'')===m)||null;
return getLeases().find(r=>normMac(r.mac||'')===m)||null;
}
function deviceInfo(mac){
const lease=getLease(mac);
if(lease){
const ip=lease.ipaddr||lease.address||lease.ip||'';
const online=lease.online==='1'||lease.online===true||lease.state==='active'||lease.state==='online';
return{online,ip,label:lease.hostname||lease.descr||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;
@@ -271,10 +277,11 @@ function discoveredDevices(){
const assigned=new Set(cfg.users.flatMap(u=>u.devices.map(d=>normMac(d.mac))));
const out=[];
for(const r of getLeases()){
const mac=normMac(r.mac||r.mac_address||'');
const mac=normMac(r.mac||'');
if(!mac||assigned.has(mac))continue;
out.push({mac,name:r.hostname||r.descr||mac,ip:r.ipaddr||r.address||r.ip||'',online:r.online==='1'||r.online===true||r.state==='active'});
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||'');
@@ -355,9 +362,9 @@ function openAddDeviceModal(userId){
document.getElementById('d-name').value='';document.getElementById('d-mac').value='';
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 loaded — add <code>opnsense_leases_url</code> to secrets.yaml and restart 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 found devices are assigned':'No DHCP data yet'}</div>`;}
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>`;
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');
}