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 += ``;
+ }
+ 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);