Heat-Tracker/server.js
2025-04-22 00:01:38 -04:00

126 lines
4.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const knex = require('knex');
const axios = require('axios');
// Initialize MariaDB connection via Knex
const db = knex({
client: process.env.DB_CLIENT,
connection: {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
}
});
const app = express();
const PORT = process.env.PORT || 3000;
const slackWebhook = process.env.SLACK_WEBHOOK_URL;
// Create table if not exists, now with direction
(async () => {
if (!await db.schema.hasTable('readings')) {
await db.schema.createTable('readings', table => {
table.increments('id').primary();
table.integer('dockDoor');
table.string('direction');
table.timestamp('timestamp');
table.float('temperature');
table.float('humidity');
table.float('heatIndex');
});
}
})();
// Compute heat index (NOAA formula)
function computeHeatIndex(T, R) {
const c1 = -42.379, c2 = 2.04901523, c3 = 10.14333127;
const c4 = -0.22475541, c5 = -6.83783e-3, c6 = -5.481717e-2;
const c7 = 1.22874e-3, c8 = 8.5282e-4, c9 = -1.99e-6;
const HI = c1 + c2*T + c3*R + c4*T*R + c5*T*T + c6*R*R + c7*T*T*R + c8*T*R*R + c9*T*T*R*R;
return Math.round(HI * 100) / 100;
}
// Determine direction based on door number
function getDirection(door) {
door = Number(door);
if (door >= 124 && door <= 138) return 'Inbound';
if (door >= 142 && door <= 201) return 'Outbound';
if (door >= 202 && door <= 209) return 'Inbound';
return 'Unknown';
}
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'public')));
let clients = [];
app.get('/api/stream', (req, res) => {
res.set({ 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' });
res.flushHeaders();
clients.push(res);
req.on('close', () => { clients = clients.filter(c => c !== res); });
});
function broadcast(event, data) {
const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
clients.forEach(res => res.write(payload));
}
app.post('/api/readings', async (req, res) => {
try {
const { inbound, outbound } = req.body; // each: {dockDoor,temperature,humidity}
const timestamp = new Date();
const entries = [inbound, outbound].map(r => {
const direction = getDirection(r.dockDoor);
const heatIndex = computeHeatIndex(r.temperature, r.humidity);
return { ...r, direction, timestamp, heatIndex };
});
// Insert both
const ids = await db('readings').insert(entries);
const saved = entries.map((e, i) => ({ id: ids[i], ...e }));
// Broadcast and respond
saved.forEach(reading => broadcast('new-reading', reading));
// Slack notification with both
if (slackWebhook) {
const textLines = saved.map(r =>
`Door *${r.dockDoor}* (${r.direction}) Temp: ${r.temperature}°F, Humidity: ${r.humidity}%, HI: ${r.heatIndex}`
);
await axios.post(slackWebhook, { text: 'New dual readings:\n' + textLines.join('\n') });
}
res.json(saved);
} catch (err) {
console.error('Error saving readings or sending Slack:', err);
res.status(500).json({ error: err.message });
}
});
app.get('/api/readings', async (req, res) => {
try {
const rows = await db('readings').orderBy('timestamp', 'asc');
res.json(rows);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.get('/api/export', async (req, res) => {
try {
const rows = await db('readings').orderBy('timestamp', 'asc');
res.setHeader('Content-disposition', 'attachment; filename=readings.csv');
res.set('Content-Type', 'text/csv');
res.write('id,dockDoor,direction,timestamp,temperature,humidity,heatIndex\n');
rows.forEach(r =>
res.write(`${r.id},${r.dockDoor},${r.direction},${r.timestamp},${r.temperature},${r.humidity},${r.heatIndex}\n`)
);
res.end();
} catch (err) {
res.status(500).send(err.message);
}
});
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));