Fix: use sensor.home_dhcp_leases_lan with attributes.Leases (capital L), address/hostname/expires fields
This commit is contained in:
+21
-14
@@ -86,7 +86,7 @@ input[type=time]::-webkit-calendar-picker-indicator{filter:invert(.5)}
|
|||||||
<span class="sc-logo">🛡️</span>
|
<span class="sc-logo">🛡️</span>
|
||||||
<h1>Parental Controls</h1>
|
<h1>Parental Controls</h1>
|
||||||
<p>Enter your Home Assistant URL and a Long-Lived Access Token (HA Profile → Security → Long-Lived Access Tokens).</p>
|
<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>
|
<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>
|
<button class="btn btn-p btn-full" onclick="doSetup()">Connect →</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,7 +133,9 @@ input[type=time]::-webkit-calendar-picker-indicator{filter:invert(.5)}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<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 CHUNKS=12, CHUNK_SIZE=250;
|
||||||
const chunkId=i=>`input_text.parental_config_${i}`;
|
const chunkId=i=>`input_text.parental_config_${i}`;
|
||||||
const COLORS=['#ef5350','#ff7043','#ffa726','#66bb6a','#26c6da','#42a5f5','#7e57c2','#ec407a','#26a69a','#e6c229'];
|
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);}
|
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(){
|
function updateDHCP(){
|
||||||
const s=states[DHCP];
|
const s=states[DHCP];
|
||||||
const dot=document.getElementById('dhcp-dot');const lbl=document.getElementById('dhcp-lbl');
|
const dot=document.getElementById('dhcp-dot');const lbl=document.getElementById('dhcp-lbl');
|
||||||
@@ -249,15 +258,12 @@ function updateDHCP(){
|
|||||||
}
|
}
|
||||||
function getLease(mac){
|
function getLease(mac){
|
||||||
const m=normMac(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){
|
function deviceInfo(mac){
|
||||||
const lease=getLease(mac);
|
const lease=getLease(mac);
|
||||||
if(lease){
|
if(lease)return{online:isOnline(lease),ip:lease.address||'',label:lease.hostname||mac};
|
||||||
const ip=lease.ipaddr||lease.address||lease.ip||'';
|
// fallback: GPS device_tracker entities
|
||||||
const online=lease.online==='1'||lease.online===true||lease.state==='active'||lease.state==='online';
|
|
||||||
return{online,ip,label:lease.hostname||lease.descr||mac};
|
|
||||||
}
|
|
||||||
const m=normMac(mac);
|
const m=normMac(mac);
|
||||||
for(const[eid,s]of Object.entries(states)){
|
for(const[eid,s]of Object.entries(states)){
|
||||||
if(!eid.startsWith('device_tracker.'))continue;
|
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 assigned=new Set(cfg.users.flatMap(u=>u.devices.map(d=>normMac(d.mac))));
|
||||||
const out=[];
|
const out=[];
|
||||||
for(const r of getLeases()){
|
for(const r of getLeases()){
|
||||||
const mac=normMac(r.mac||r.mac_address||'');
|
const mac=normMac(r.mac||'');
|
||||||
if(!mac||assigned.has(mac))continue;
|
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)){
|
for(const[eid,s]of Object.entries(states)){
|
||||||
if(!eid.startsWith('device_tracker.'))continue;
|
if(!eid.startsWith('device_tracker.'))continue;
|
||||||
const a=s.attributes||{};const mac=normMac(a.mac||a.mac_address||a.macaddress||'');
|
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='';
|
document.getElementById('d-name').value='';document.getElementById('d-mac').value='';
|
||||||
const disc=discoveredDevices();const list=document.getElementById('disc-list');
|
const disc=discoveredDevices();const list=document.getElementById('disc-list');
|
||||||
const note=document.getElementById('dhcp-note');
|
const note=document.getElementById('dhcp-note');
|
||||||
const hasDHCP=states[DHCP]&&states[DHCP].state!=='unavailable'&&states[DHCP].state!=='unknown';
|
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>`;
|
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 found devices are assigned':'No DHCP data yet'}</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('');}
|
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');
|
openModal('add-dev-modal');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user