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 @@
+
+
+
Air Quality Status
+
+
+
+
+
+
+ {{ getOverallStatus() }}
+
+
+ Building Average: {{ overallCO2.toFixed(0) }} ppm
+
+
+
+
+
+
+
+
+
+ No air quality data available
+
+
+
+
+
+
{{ Math.round(room.co2.current) }} ppm
+
+ {{ room.co2.status.toUpperCase() }}
+
+
+
+
+
+
+
+
+
{{ roomsWithGoodAir }}
+
Good Air Quality
+
+
+
{{ roomsNeedingAttention }}
+
Need Attention
+
+
+
+
+
+
+
+
+
\ 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 @@
+
+
+
Room Overview
+
+
+
+ No room data available. Connect sensors to see room metrics.
+
+
+
+
+
+
{{ room.room }}
+
+
+
+
+
+
+
+
+
{{ room.occupancyEstimate }}
+
+
+
+
+
+
+
+
+
Energy
+
{{ room.energy.current.toFixed(2) }} {{ room.energy.unit }}
+
Total: {{ room.energy.total.toFixed(2) }}
+
+
+
+
+
CO2
+
{{ Math.round(room.co2.current) }} {{ room.co2.unit }}
+
{{ room.co2.status.toUpperCase() }}
+
+
+
+
+
+ {{ room.sensors.length }} sensor{{ room.sensors.length !== 1 ? 's' : '' }}
+ {{ formatTime(room.lastUpdated) }}
+
+
+
+
+
+
+
+
{{ roomsList.length }}
+
Rooms
+
+
+
{{ totalEnergy.toFixed(1) }} kWh
+
Total Energy
+
+
+
{{ averageCO2.toFixed(0) }} ppm
+
Avg CO2
+
+
+
+
+
+
\ 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'