feat(ui): add separate Network·Devices band (IoT/personal) below Little Blue
Read-only static band from public/devices.json (ARP scan), grouped Smart Home / Entertainment / Personal / Network / Flagged. Kept distinct from Little Blue's service health band. Live discovery deferred. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
35
public/views/devices_band.js
Normal file
35
public/views/devices_band.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// Network Devices band — IoT / personal / unknown LAN devices, kept SEPARATE
|
||||
// from Little Blue's homelab-service health band. Read-only, static source
|
||||
// (public/devices.json), no health probing. Live discovery comes later.
|
||||
import { el, mount } from '../dom.js';
|
||||
|
||||
let host;
|
||||
async function load() {
|
||||
if (!host) return;
|
||||
try {
|
||||
const res = await fetch('/devices.json');
|
||||
const data = await res.json();
|
||||
const total = data.groups.reduce((n, g) => n + g.devices.length, 0);
|
||||
const sections = data.groups.map(g =>
|
||||
el('div', { class: 'dv-section' },
|
||||
el('div', { class: 'dv-group' },
|
||||
el('span', { class: 'gname' }, g.name),
|
||||
el('span', { class: 'gcount' }, String(g.devices.length)),
|
||||
el('span', { class: 'line' })),
|
||||
el('div', { class: 'dv-tiles' }, g.devices.map(d =>
|
||||
el('div', { class: 'dv-tile' + (d.flag ? ' flag' : '') },
|
||||
el('span', { class: 'dv-nm' }, d.name),
|
||||
el('span', { class: 'dv-ip' }, d.ip),
|
||||
el('span', { class: 'dv-vendor' }, d.vendor || ''))))));
|
||||
mount(host,
|
||||
el('div', { class: 'dv-hd' },
|
||||
el('div', { class: 'dv-title' }, 'Network · Devices'),
|
||||
el('span', { class: 'dv-count' }, `${total} on the LAN`)),
|
||||
el('div', { class: 'dv-note' }, data.note || ''),
|
||||
sections);
|
||||
} catch {
|
||||
mount(host, el('span', { class: 'muted' }, 'Device list unavailable'));
|
||||
}
|
||||
}
|
||||
export function renderDevicesBand(el_) { host = el_; load(); }
|
||||
export function stopDevicesBand() { host = null; }
|
||||
@@ -1,6 +1,7 @@
|
||||
import { el, mount } from '../dom.js';
|
||||
import { api } from '../api.js';
|
||||
import { renderHealthBand, stopHealthBand } from './health_band.js';
|
||||
import { renderDevicesBand, stopDevicesBand } from './devices_band.js';
|
||||
import { svCard } from '../components/sv_card.js';
|
||||
import { attachReorder } from '../components/sv_reorder.js';
|
||||
import { orderCards } from './cards/registry.js';
|
||||
@@ -18,12 +19,13 @@ let renderGen = 0; // guards against overlapping
|
||||
|
||||
export async function render(main) {
|
||||
const myGen = ++renderGen;
|
||||
active.forEach(c => c.stop && c.stop()); active = []; stopHealthBand();
|
||||
active.forEach(c => c.stop && c.stop()); active = []; stopHealthBand(); stopDevicesBand();
|
||||
mount(main,
|
||||
el('h1', { class: 'view-h1' }, 'Sacred Valley'),
|
||||
el('p', { class: 'view-sub' }, 'The homelab, at a glance.'),
|
||||
el('div', { id: 'sv-cards' }),
|
||||
el('div', { id: 'sv-health' })
|
||||
el('div', { id: 'sv-health' }),
|
||||
el('div', { id: 'sv-devices' })
|
||||
);
|
||||
|
||||
let layout = { card_order: [], hidden: [], sizes: {} };
|
||||
@@ -50,4 +52,5 @@ export async function render(main) {
|
||||
catch (e) { console.error('save layout', e); }
|
||||
});
|
||||
renderHealthBand(document.getElementById('sv-health'));
|
||||
renderDevicesBand(document.getElementById('sv-devices'));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user