diff --git a/src/components/cards/AirQualityCard.vue b/src/components/cards/AirQualityCard.vue new file mode 100644 index 0000000..12452cb --- /dev/null +++ b/src/components/cards/AirQualityCard.vue @@ -0,0 +1,177 @@ + + + \ No newline at end of file diff --git a/src/components/cards/RoomMetricsCard.vue b/src/components/cards/RoomMetricsCard.vue new file mode 100644 index 0000000..5cea4d3 --- /dev/null +++ b/src/components/cards/RoomMetricsCard.vue @@ -0,0 +1,138 @@ + + + \ No newline at end of file diff --git a/src/stores/energy.ts b/src/stores/energy.ts index 57091e5..f4ce0e4 100644 --- a/src/stores/energy.ts +++ b/src/stores/energy.ts @@ -108,15 +108,24 @@ export const useEnergyStore = defineStore('energy', () => { if (newDataBuffer.length > 0) { const data = newDataBuffer.shift() // Get the oldest data point if (data) { - latestMessage.value = data - - // Update sensor-specific data - updateSensorData(data) - - // Update time series for chart (aggregate all sensors) - const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString() - timeSeriesData.labels.push(newLabel) - timeSeriesData.datasets[0].data.push(data.value) + // Handle both legacy and new data formats + if (isLegacyData(data)) { + latestMessage.value = data + updateSensorData(data) + + // Update time series for chart + const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString() + timeSeriesData.labels.push(newLabel) + timeSeriesData.datasets[0].data.push(data.value) + } else { + // Handle new multi-metric data + updateRoomData(data) + + // Update time series for chart (use energy values) + const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString() + timeSeriesData.labels.push(newLabel) + timeSeriesData.datasets[0].data.push(data.energy.value) + } if (timeSeriesData.labels.length > MAX_DATA_POINTS) { timeSeriesData.labels.shift() @@ -133,7 +142,11 @@ export const useEnergyStore = defineStore('energy', () => { } } - function updateSensorData(data: EnergyData) { + function isLegacyData(data: any): data is LegacyEnergyData { + return 'value' in data && !('energy' in data) + } + + function updateSensorData(data: LegacyEnergyData) { const existingSensor = sensorsData.get(data.sensorId) if (existingSensor) { @@ -161,5 +174,74 @@ export const useEnergyStore = defineStore('energy', () => { } } - return { isConnected, latestMessage, timeSeriesData, sensorsData, connect, disconnect } + function updateRoomData(data: SensorReading) { + // Store latest reading + latestReadings.set(data.sensorId, data) + + // Get or create room metrics + let roomMetrics = roomsData.get(data.room) + + if (!roomMetrics) { + roomMetrics = { + room: data.room, + sensors: [data.sensorId], + energy: { current: 0, total: 0, average: 0, unit: data.energy.unit }, + co2: { current: 0, average: 0, max: 0, status: 'good', unit: data.co2.unit }, + occupancyEstimate: 'low', + lastUpdated: data.timestamp + } + roomsData.set(data.room, roomMetrics) + } + + // Update room sensors list + if (!roomMetrics.sensors.includes(data.sensorId)) { + roomMetrics.sensors.push(data.sensorId) + } + + // Recalculate room metrics from all sensors in the room + const roomSensors = Array.from(latestReadings.values()).filter(reading => reading.room === data.room) + + // Energy calculations + roomMetrics.energy.current = roomSensors.reduce((sum, sensor) => sum + sensor.energy.value, 0) + roomMetrics.energy.total += data.energy.value // Accumulate total + roomMetrics.energy.average = roomMetrics.energy.total / roomSensors.length + + // CO2 calculations + const co2Values = roomSensors.map(sensor => sensor.co2.value) + roomMetrics.co2.current = co2Values.reduce((sum, val) => sum + val, 0) / co2Values.length + roomMetrics.co2.max = Math.max(roomMetrics.co2.max, ...co2Values) + roomMetrics.co2.average = (roomMetrics.co2.average + roomMetrics.co2.current) / 2 + + // CO2 status classification + if (roomMetrics.co2.current < 400) roomMetrics.co2.status = 'good' + else if (roomMetrics.co2.current < 1000) roomMetrics.co2.status = 'moderate' + else if (roomMetrics.co2.current < 5000) roomMetrics.co2.status = 'poor' + else roomMetrics.co2.status = 'critical' + + // Occupancy estimate based on CO2 levels + if (roomMetrics.co2.current < 600) roomMetrics.occupancyEstimate = 'low' + else if (roomMetrics.co2.current < 1200) roomMetrics.occupancyEstimate = 'medium' + else roomMetrics.occupancyEstimate = 'high' + + roomMetrics.lastUpdated = data.timestamp + } + + function getCO2Status(ppm: number): 'good' | 'moderate' | 'poor' | 'critical' { + if (ppm < 400) return 'good' + if (ppm < 1000) return 'moderate' + if (ppm < 5000) return 'poor' + return 'critical' + } + + return { + isConnected, + latestMessage, + timeSeriesData, + sensorsData, + roomsData, + latestReadings, + connect, + disconnect, + getCO2Status + } }) diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 81c6787..4520c65 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -47,6 +47,10 @@
+
+ + +
@@ -56,6 +60,8 @@ import RealtimeEnergyChartCard from '@/components/cards/RealtimeEnergyChartCard. import MetricCard from '@/components/cards/MetricCard.vue' import GraphMetricCard from '@/components/cards/GraphMetricCard.vue' import SensorConsumptionTable from '@/components/cards/SensorConsumptionTable.vue' +import RoomMetricsCard from '@/components/cards/RoomMetricsCard.vue' +import AirQualityCard from '@/components/cards/AirQualityCard.vue' import { useEnergyStore } from '@/stores/energy' import { computed, onMounted, onUnmounted } from 'vue'