feat: Pulse threshold alerts with WebSocket broadcasting
This commit is contained in:
parent
c3dcefe857
commit
afbd5c69aa
1 changed files with 153 additions and 0 deletions
|
|
@ -175,4 +175,157 @@ export async function pulseRoutes(fastify: FastifyInstance) {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /pulse/thresholds
|
||||
* Get current threshold configuration
|
||||
*/
|
||||
fastify.get('/thresholds', {
|
||||
handler: async (request, reply) => {
|
||||
return {
|
||||
thresholds: pulseThresholds,
|
||||
lastUpdated: thresholdsLastUpdated?.toISOString() || null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /pulse/thresholds
|
||||
* Set threshold alerts for Pulse sensors
|
||||
*/
|
||||
fastify.post('/thresholds', {
|
||||
handler: async (request, reply) => {
|
||||
const config = request.body as {
|
||||
temperature?: { min: number; max: number };
|
||||
humidity?: { min: number; max: number };
|
||||
vpd?: { min: number; max: number };
|
||||
co2?: { min: number; max: number };
|
||||
};
|
||||
|
||||
if (config.temperature) {
|
||||
pulseThresholds.temperature = config.temperature;
|
||||
}
|
||||
if (config.humidity) {
|
||||
pulseThresholds.humidity = config.humidity;
|
||||
}
|
||||
if (config.vpd) {
|
||||
pulseThresholds.vpd = config.vpd;
|
||||
}
|
||||
if (config.co2) {
|
||||
pulseThresholds.co2 = config.co2;
|
||||
}
|
||||
|
||||
thresholdsLastUpdated = new Date();
|
||||
|
||||
fastify.log.info({ event: 'pulse_thresholds_updated', thresholds: pulseThresholds });
|
||||
|
||||
return {
|
||||
success: true,
|
||||
thresholds: pulseThresholds,
|
||||
message: 'Thresholds updated successfully'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /pulse/check
|
||||
* Check current readings against thresholds and broadcast alerts
|
||||
*/
|
||||
fastify.post('/check', {
|
||||
handler: async (request, reply) => {
|
||||
const pulse = getPulseService();
|
||||
|
||||
if (!pulse) {
|
||||
return reply.status(503).send({ error: 'Pulse not configured' });
|
||||
}
|
||||
|
||||
try {
|
||||
const readings = await pulse.getCurrentReadings();
|
||||
const alerts: any[] = [];
|
||||
|
||||
for (const reading of readings) {
|
||||
// Temperature check
|
||||
if (reading.temperature !== undefined) {
|
||||
if (pulseThresholds.temperature.max && reading.temperature > pulseThresholds.temperature.max) {
|
||||
alerts.push(createAlert(reading, 'TEMPERATURE_HIGH', reading.temperature, pulseThresholds.temperature.max));
|
||||
}
|
||||
if (pulseThresholds.temperature.min && reading.temperature < pulseThresholds.temperature.min) {
|
||||
alerts.push(createAlert(reading, 'TEMPERATURE_LOW', reading.temperature, pulseThresholds.temperature.min));
|
||||
}
|
||||
}
|
||||
|
||||
// Humidity check
|
||||
if (reading.humidity !== undefined) {
|
||||
if (pulseThresholds.humidity.max && reading.humidity > pulseThresholds.humidity.max) {
|
||||
alerts.push(createAlert(reading, 'HUMIDITY_HIGH', reading.humidity, pulseThresholds.humidity.max));
|
||||
}
|
||||
if (pulseThresholds.humidity.min && reading.humidity < pulseThresholds.humidity.min) {
|
||||
alerts.push(createAlert(reading, 'HUMIDITY_LOW', reading.humidity, pulseThresholds.humidity.min));
|
||||
}
|
||||
}
|
||||
|
||||
// VPD check
|
||||
if (reading.vpd !== undefined) {
|
||||
if (pulseThresholds.vpd.max && reading.vpd > pulseThresholds.vpd.max) {
|
||||
alerts.push(createAlert(reading, 'VPD_HIGH', reading.vpd, pulseThresholds.vpd.max));
|
||||
}
|
||||
if (pulseThresholds.vpd.min && reading.vpd < pulseThresholds.vpd.min) {
|
||||
alerts.push(createAlert(reading, 'VPD_LOW', reading.vpd, pulseThresholds.vpd.min));
|
||||
}
|
||||
}
|
||||
|
||||
// CO2 check
|
||||
if (reading.co2 !== undefined) {
|
||||
if (pulseThresholds.co2.max && reading.co2 > pulseThresholds.co2.max) {
|
||||
alerts.push(createAlert(reading, 'CO2_HIGH', reading.co2, pulseThresholds.co2.max));
|
||||
}
|
||||
if (pulseThresholds.co2.min && reading.co2 < pulseThresholds.co2.min) {
|
||||
alerts.push(createAlert(reading, 'CO2_LOW', reading.co2, pulseThresholds.co2.min));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast alerts via WebSocket
|
||||
for (const alert of alerts) {
|
||||
broadcastAlert(alert);
|
||||
}
|
||||
|
||||
return {
|
||||
devicesChecked: readings.length,
|
||||
alertsTriggered: alerts.length,
|
||||
alerts,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
} catch (error: any) {
|
||||
fastify.log.error(error);
|
||||
return reply.status(500).send({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// In-memory threshold storage (would be persisted to DB in production)
|
||||
const pulseThresholds = {
|
||||
temperature: { min: 65, max: 82 }, // °F
|
||||
humidity: { min: 40, max: 70 }, // %
|
||||
vpd: { min: 0.8, max: 1.2 }, // kPa
|
||||
co2: { min: 400, max: 1500 } // ppm
|
||||
};
|
||||
|
||||
let thresholdsLastUpdated: Date | null = null;
|
||||
|
||||
// Import broadcast function
|
||||
import { broadcastAlert } from '../plugins/websocket';
|
||||
|
||||
function createAlert(reading: any, type: string, value: number, threshold: number) {
|
||||
return {
|
||||
id: `pulse-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
||||
type,
|
||||
sensorName: reading.deviceName || `Device ${reading.deviceId}`,
|
||||
value,
|
||||
threshold,
|
||||
message: `${reading.deviceName || 'Pulse Sensor'}: ${type.replace('_', ' ')} (${value.toFixed(1)} vs threshold ${threshold})`,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue