demo D262
IoT Fleet Telemetry Dashboard: A Grid Per Device in ESM
One telemetry feed, a live grid per device class and a units-online chart, with no backend
createDataRouter · MockWebSocket · rng
This is a ready-made fleet telemetry screen: one telemetry feed drives a live grid per device class and a units-online chart, with no backend. It is a working layout for monitoring many devices from one stream, ready to point at real hardware.
This is the ES module version. The grid is imported straight from a CDN as a module, the same configuration as the vanilla tab without a global script tag, so it drops into any bundler or a bare module script.
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">
<div id="trucks" style="height: 300px"></div>
<div id="drones" style="height: 300px"></div>
<div id="sensors" style="height: 300px"></div>
<div id="chart" style="height: 200px"></div>
<script type="module">
import { createDataRouter } from '@toclocoinc/lattice-grid/modules/data-router';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import { MockWebSocket } from '@toclocoinc/lattice-grid/modules/mock-socket';
// Each class has its own columns; one feed is split by kind.
const trucks = createGrid(document.getElementById('trucks'), {
rowKey: 'id', highlightOnChange: { duration: 400 }, columns: [
{ field: 'id', title: 'Unit', layout: { width: 92 } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'speed', title: 'Speed', type: 'number' },
{ field: 'fuel', title: 'Fuel %', type: 'number' },
{ field: 'status', title: 'Status' },
],
});
const drones = createGrid(document.getElementById('drones'), {
rowKey: 'id', highlightOnChange: { duration: 400 }, columns: [
{ field: 'id', title: 'Unit', layout: { width: 92 } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'altitude', title: 'Alt (m)', type: 'number' },
{ field: 'battery', title: 'Battery %', type: 'number' },
{ field: 'status', title: 'Status' },
],
});
const sensors = createGrid(document.getElementById('sensors'), {
rowKey: 'id', highlightOnChange: { duration: 400 }, columns: [
{ field: 'id', title: 'Unit', layout: { width: 92 } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'tempC', title: 'Temp C', type: 'number' },
{ field: 'battery', title: 'Battery %', type: 'number' },
{ field: 'status', title: 'Status' },
],
});
const metrics = createHeadlessGrid({
rowKey: 't', columns: [{ field: 't', type: 'number' }, { field: 'online', type: 'number' }],
});
const router = createDataRouter({ key: 'kind', rowKey: 'id' });
router.attach(trucks, 'truck');
router.attach(drones, 'drone');
router.attach(sensors, 'sensor');
router.attach(metrics, 'metric', { rowKey: 't' });
createChart({ grid: metrics, container: document.getElementById('chart'), type: 'line', x: 't', y: 'online', title: 'Units online per tick' });
// A feed is any generator that yields { kind: 'snapshot', rows } first, then
// { kind: 'delta', changes } forever; each record carries a kind and an id.
// The vendored mock-socket module ships opsFeed and priceFeed only, so a
// fleet feed is written here, to the same contract.
function* fleetFeed() {
const kinds = [
{ kind: 'truck', n: 4, extra: () => ({ speed: 40 + Math.round(Math.random() * 40), fuel: 30 + Math.round(Math.random() * 60) }) },
{ kind: 'drone', n: 3, extra: () => ({ altitude: 80 + Math.round(Math.random() * 300), battery: 20 + Math.round(Math.random() * 70) }) },
{ kind: 'sensor', n: 3, extra: () => ({ tempC: 10 + Math.round(Math.random() * 25), battery: 20 + Math.round(Math.random() * 70) }) },
];
const regions = ['EMEA', 'AMER', 'APAC'];
const rows = [];
for (const k of kinds) for (let i = 0; i < k.n; i++) {
rows.push({ id: k.kind + '-' + i, kind: k.kind, region: regions[i % regions.length], status: 'ok', ...k.extra() });
}
yield { kind: 'snapshot', rows };
while (true) {
const r = rows[Math.floor(Math.random() * rows.length)];
const k = kinds.find((x) => x.kind === r.kind);
yield { kind: 'delta', changes: [{ op: 'upsert', row: { ...r, ...k.extra() } }] };
}
}
const socket = new MockWebSocket({ feed: fleetFeed(), rate: 800, jitter: 200 });
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.kind === 'snapshot') router.load(message.rows);
else router.apply(message.changes);
};
</script>