diff --git a/index.html b/index.html new file mode 100644 index 0000000..bf2a12e --- /dev/null +++ b/index.html @@ -0,0 +1,78 @@ + + + + + + Zone Temperatures + + + +

Zone Temperature Monitor

+
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..a665f95 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "mispair-demo", + "type": "module", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "hono": "^4.6.0", + "@hono/node-server": "^1.13.0" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..26871da --- /dev/null +++ b/server.js @@ -0,0 +1,27 @@ +import { serve } from '@hono/node-server'; +import { serveStatic } from '@hono/node-server/serve-static'; +import { Hono } from 'hono'; + +const counters = { + 'zone-a': 10, + 'zone-b': 15, + 'zone-c': 20, +}; + +const app = new Hono(); + +app.get('/api/v1/zones/:zoneId/primary/temperature', (c) => { + const zoneId = c.req.param('zoneId'); + if (!(zoneId in counters)) counters[zoneId] = 10; + const value = counters[zoneId]; + counters[zoneId] += Math.floor(Math.random() * 3) + 3; + return c.json({ value }); +}); + +app.use('/src/*', serveStatic({ root: '.' })); + +app.get('/', serveStatic({ path: './index.html', root: '.' })); + +serve({ fetch: app.fetch, port: 3001 }, (info) => { + console.log(`Server running on http://localhost:${info.port}`); +}); diff --git a/src/chart.js b/src/chart.js new file mode 100644 index 0000000..4ad008b --- /dev/null +++ b/src/chart.js @@ -0,0 +1,66 @@ +const MAX_HISTORY = 5; + +export function createChartForZone(zoneId) { + const history = []; + + function formatTime(d) { + return d.toLocaleTimeString('en-US', { hour12: false }); + } + + function maxValue() { + let m = 0; + for (const h of history) { + if (h.value > m) m = h.value; + } + return m || 100; + } + + function render() { + if (history.length === 0) { + chartEl.innerHTML = '
Waiting for data…
'; + return; + } + const latest = history[history.length - 1]; + header.innerHTML = ` + ${zoneId} + + ${latest.value}°C + ${formatTime(latest.time)} + + `; + + const max = maxValue(); + let html = ''; + for (const h of history) { + const pct = Math.max(4, (h.value / max) * 100); + html += `
${formatTime(h.time)}
`; + } + chartEl.innerHTML = html; + } + + const card = document.createElement('div'); + card.className = 'zone-card'; + card.id = zoneId; + + const header = document.createElement('div'); + header.className = 'zone-header'; + header.innerHTML = `${zoneId}`; + card.appendChild(header); + + const chartEl = document.createElement('div'); + chartEl.className = 'bar-chart'; + chartEl.innerHTML = '
Waiting for data…
'; + card.appendChild(chartEl); + + document.getElementById('zones').appendChild(card); + + return { + element: card, + update(val) { + const value = typeof val === 'object' && val !== null ? val.value : val; + history.push({ value, time: new Date() }); + if (history.length > MAX_HISTORY) history.shift(); + render(); + }, + }; +} diff --git a/src/ui.js b/src/ui.js index 0c0af46..6cc933c 100644 --- a/src/ui.js +++ b/src/ui.js @@ -1,8 +1,8 @@ -// This file can not be modified. -// Each zone has one corresponding chart. -// UI code calls attachChart for each zone and corresponding chart. import { attachChart } from './data.js'; -const zones = ['zone-a', 'zone-b', 'zone-c', /* ... */]; +import { createChartForZone } from './chart.js'; + +const zones = ['zone-a', 'zone-b', 'zone-c']; + for (const zoneId of zones) { const chart = createChartForZone(zoneId); attachChart(chart, zoneId);