Refactor sensor data handling for new API and WebSocket format

- Update SensorConsumptionTable to use new sensorStore and
websocketStore - Normalize sensor and reading interfaces for consistency
- Remove legacy energy data handling and mapping logic - Update API and
store types for new backend schema - Fetch sensors on mount in
SensorConsumptionTable - Simplify WebSocket data processing and remove
legacy code
This commit is contained in:
rafaeldpsilva
2025-09-30 17:58:06 +01:00
parent 90b6034465
commit 83eaa7e121
6 changed files with 126 additions and 321 deletions

View File

@@ -29,21 +29,37 @@
No sensor data available. Waiting for WebSocket connection... No sensor data available. Waiting for WebSocket connection...
</td> </td>
</tr> </tr>
<tr v-for="sensor in sensorList" :key="sensor.sensorId" class="hover:bg-gray-50"> <tr
v-for="sensor in sensorStore.latestReadings.values()"
:key="sensor.sensor_id"
class="hover:bg-gray-50"
>
<td class="py-3 text-sm font-medium text-gray-900"> <td class="py-3 text-sm font-medium text-gray-900">
{{ sensor.sensorId }} {{ sensor.sensor_id }}
</td> </td>
<td class="py-3 text-sm text-gray-600 text-right"> <td class="py-3 text-sm text-gray-600 text-right">
{{ sensor.latestValue.toFixed(2) }} {{ sensor.unit }} {{ sensor.room }}
</td> </td>
<td class="py-3 text-sm text-gray-600 text-right"> <td class="py-3 text-sm text-gray-600 text-right">
{{ sensor.totalConsumption.toFixed(2) }} {{ sensor.unit }} {{
sensor.energy?.value ||
sensor.co2?.value ||
sensor.temperature?.value ||
sensor.humidity?.value ||
'N/A'
}}
{{
sensor.energy?.unit ||
sensor.co2?.unit ||
sensor.temperature?.unit ||
sensor.humidity?.unit
}}
</td> </td>
<td class="py-3 text-sm text-gray-600 text-right"> <td class="py-3 text-sm text-gray-600 text-right">
{{ sensor.averageConsumption.toFixed(2) }} {{ sensor.unit }} {{ sensor.room }}
</td> </td>
<td class="py-3 text-sm text-gray-500 text-right"> <td class="py-3 text-sm text-gray-500 text-right">
{{ formatTime(sensor.lastUpdated) }} {{ formatTime(sensor.timestamp) }}
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -55,27 +71,25 @@
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div <div
class="w-2 h-2 rounded-full" class="w-2 h-2 rounded-full"
:class="energyStore.isConnected ? 'bg-green-500' : 'bg-red-500'" :class="websocketStore.isConnected ? 'bg-green-500' : 'bg-red-500'"
></div> ></div>
<span>{{ energyStore.isConnected ? 'Connected' : 'Disconnected' }}</span> <span>{{ websocketStore.isConnected ? 'Connected' : 'Disconnected' }}</span>
</div>
<div>
{{ sensorList.length }} sensor{{ sensorList.length !== 1 ? 's' : '' }} active
</div> </div>
<div>{{ sensorList.length }} sensor{{ sensorList.length !== 1 ? 's' : '' }} active</div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed, onMounted } from 'vue'
import { useEnergyStore } from '@/stores/energy' import { useSensorStore } from '@/stores/sensor'
import { useWebSocketStore } from '@/stores/websocket'
const energyStore = useEnergyStore() const sensorStore = useSensorStore()
const websocketStore = useWebSocketStore()
const sensorList = computed(() => { const sensorList = computed(() => {
return Array.from(energyStore.sensorsData.values()).sort((a, b) => return Array.from(sensorStore.sensorDevices.values()).sort((a, b) => a.name.localeCompare(b.name))
a.sensorId.localeCompare(b.sensorId)
)
}) })
const formatTime = (timestamp: number) => { const formatTime = (timestamp: number) => {
@@ -92,4 +106,8 @@ const formatTime = (timestamp: number) => {
return date.toLocaleTimeString() return date.toLocaleTimeString()
} }
} }
onMounted(async () => {
await sensorStore.fetchApiSensors()
})
</script> </script>

View File

@@ -50,12 +50,11 @@ export interface DataResponse {
} }
export interface SensorReading { export interface SensorReading {
_id?: string
sensor_id: string sensor_id: string
room?: string room: string
sensor_type: string sensor_type: string
timestamp: number timestamp: number
created_at?: string type: string
energy?: { energy?: {
value: number value: number
unit: string unit: string
@@ -176,29 +175,18 @@ export interface SystemEvent {
} }
export interface SensorDevice { export interface SensorDevice {
id: string _id: string
sensor_id: string sensor_id: string
name: string name: string
type: SensorType type: SensorType
sensor_type: SensorType sensor_type: SensorType
room: string room: string
status: SensorStatus status: SensorStatus
location?: string
lastSeen: number
total_readings?: number
capabilities: { capabilities: {
monitoring: string[] monitoring: string[]
actions: SensorAction[] actions: SensorAction[]
} }
metadata: { metadata: SensorMetadata
location?: string
model?: string
firmware?: string
battery?: number
created_at?: string
updated_at?: string
manufacturer?: string
}
} }
export interface SensorAction { export interface SensorAction {
@@ -245,7 +233,20 @@ export enum SensorStatus {
ERROR = 'error', ERROR = 'error',
} }
// HTTP Client class export interface SensorMetadata {
time_factor?: number
occupancy_factor?: number
quality_level?: string
duration_seconds?: number
location?: string
model?: string
firmware?: string
battery?: number
created_at?: string
updated_at?: string
manufacturer?: string
}
class ApiClient { class ApiClient {
private baseUrl: string private baseUrl: string

View File

@@ -6,6 +6,7 @@ import {
type DataResponse, type DataResponse,
type SensorType, type SensorType,
type SensorStatus, type SensorStatus,
type SensorMetadata,
} from './api' } from './api'
export const sensorsApi = { export const sensorsApi = {
@@ -13,8 +14,24 @@ export const sensorsApi = {
room?: string room?: string
sensor_type?: SensorType sensor_type?: SensorType
status?: SensorStatus status?: SensorStatus
}): Promise<SensorDevice[]> { }): Promise<{
return apiClient.get<SensorDevice[]>('/api/v1/sensors/get', params) sensors: SensorDevice[]
count: number
filters: {
room: string
sensor_type: SensorType
status: SensorStatus
}
}> {
return apiClient.get<{
sensors: SensorDevice[]
count: number
filters: {
room: string
sensor_type: SensorType
status: SensorStatus
}
}>('/api/v1/sensors/get', params)
}, },
async getSensor(sensorId: string): Promise<SensorDevice> { async getSensor(sensorId: string): Promise<SensorDevice> {
@@ -39,7 +56,7 @@ export const sensorsApi = {
async updateSensorMetadata( async updateSensorMetadata(
sensorId: string, sensorId: string,
metadata: Record<string, any>, metadata: SensorMetadata,
): Promise<{ message: string }> { ): Promise<{ message: string }> {
return apiClient.put<{ message: string }>(`/api/v1/sensors/${sensorId}/metadata`, metadata) return apiClient.put<{ message: string }>(`/api/v1/sensors/${sensorId}/metadata`, metadata)
}, },

View File

@@ -3,18 +3,12 @@ import { computed } from 'vue'
import { useSensorStore } from './sensor' import { useSensorStore } from './sensor'
import { useRoomStore } from './room' import { useRoomStore } from './room'
import { useAnalyticsStore } from './analytics' import { useAnalyticsStore } from './analytics'
import { useWebSocketStore } from './websocket'
export const useEnergyStore = defineStore('energy', () => { export const useEnergyStore = defineStore('energy', () => {
// Get instances of other stores // Get instances of other stores
const sensorStore = useSensorStore() const sensorStore = useSensorStore()
const roomStore = useRoomStore() const roomStore = useRoomStore()
const analyticsStore = useAnalyticsStore() const analyticsStore = useAnalyticsStore()
const webSocketStore = useWebSocketStore()
// Delegate to WebSocket store
const connect = (url: string) => webSocketStore.connect(url)
const disconnect = () => webSocketStore.disconnect()
// Initialize data from APIs // Initialize data from APIs
async function initializeFromApi() { async function initializeFromApi() {
@@ -27,15 +21,9 @@ export const useEnergyStore = defineStore('energy', () => {
} }
return { return {
// WebSocket state (delegated)
isConnected: computed(() => webSocketStore.isConnected),
latestMessage: computed(() => webSocketStore.latestMessage),
timeSeriesData: computed(() => webSocketStore.timeSeriesData),
// Sensor state (delegated) // Sensor state (delegated)
sensorsData: computed(() => sensorStore.sensorsData), sensorsData: computed(() => sensorStore.sensorsData),
sensorDevices: computed(() => sensorStore.sensorDevices), sensorDevices: computed(() => sensorStore.sensorDevices),
latestReadings: computed(() => sensorStore.latestReadings),
apiSensors: computed(() => Array.from(sensorStore.sensorDevices.values())), // Convert Map to Array apiSensors: computed(() => Array.from(sensorStore.sensorDevices.values())), // Convert Map to Array
// Room state (delegated) // Room state (delegated)
@@ -51,13 +39,11 @@ export const useEnergyStore = defineStore('energy', () => {
healthStatus: computed(() => analyticsStore.healthStatus), healthStatus: computed(() => analyticsStore.healthStatus),
// Combined API loading/error state // Combined API loading/error state
apiLoading: computed(() => sensorStore.apiLoading || roomStore.apiLoading || analyticsStore.apiLoading), apiLoading: computed(
() => sensorStore.apiLoading || roomStore.apiLoading || analyticsStore.apiLoading,
),
apiError: computed(() => sensorStore.apiError || roomStore.apiError || analyticsStore.apiError), apiError: computed(() => sensorStore.apiError || roomStore.apiError || analyticsStore.apiError),
// WebSocket functions (delegated)
connect,
disconnect,
// Sensor functions (delegated) // Sensor functions (delegated)
updateSensorRoom: sensorStore.updateSensorRoom, updateSensorRoom: sensorStore.updateSensorRoom,
executeSensorAction: sensorStore.executeSensorAction, executeSensorAction: sensorStore.executeSensorAction,

View File

@@ -3,37 +3,11 @@ import { ref, reactive } from 'vue'
import { import {
sensorsApi, sensorsApi,
SensorType, SensorType,
SensorStatus,
type SensorDevice, type SensorDevice,
type SensorAction, type SensorStatus,
type SensorReading,
} from '@/services' } from '@/services'
interface SensorReading {
id: string
sensorId: string
room: string
timestamp: number
energy: {
value: number
unit: string
}
co2: {
value: number
unit: string
}
temperature?: {
value: number
unit: string
}
}
interface LegacyEnergyData {
sensorId: string
timestamp: number
value: number
unit: string
}
export const useSensorStore = defineStore('sensor', () => { export const useSensorStore = defineStore('sensor', () => {
// State // State
const sensorDevices = reactive<Map<string, SensorDevice>>(new Map()) const sensorDevices = reactive<Map<string, SensorDevice>>(new Map())
@@ -52,7 +26,7 @@ export const useSensorStore = defineStore('sensor', () => {
} }
} }
async function executeSensorAction(sensorId: string, actionId: string, parameters?: any) { async function executeSensorAction(sensorId: string, actionId: string) {
const sensor = sensorDevices.get(sensorId) const sensor = sensorDevices.get(sensorId)
if (!sensor) return false if (!sensor) return false
@@ -75,49 +49,18 @@ export const useSensorStore = defineStore('sensor', () => {
return Array.from(sensorDevices.values()).filter((sensor) => sensor.type === type) return Array.from(sensorDevices.values()).filter((sensor) => sensor.type === type)
} }
function updateSensorData(data: LegacyEnergyData) { function updateEnergySensors(data: Sensor) {
const existingSensor = sensorsData.get(data.sensorId) console.log(data)
if (existingSensor) {
const newTotal = existingSensor.totalConsumption + data.value
const dataPoints = Math.floor((data.timestamp - existingSensor.lastUpdated) / 60) + 1
sensorsData.set(data.sensorId, {
...existingSensor,
latestValue: data.value,
totalConsumption: newTotal,
averageConsumption: newTotal / dataPoints,
lastUpdated: data.timestamp,
})
} else {
sensorsData.set(data.sensorId, {
sensorId: data.sensorId,
latestValue: data.value,
totalConsumption: data.value,
averageConsumption: data.value,
lastUpdated: data.timestamp,
unit: data.unit,
})
}
// Mark sensor as recently updated for legacy data as well
recentlyUpdatedSensors.add(data.sensorId)
// Remove from recently updated after 2 seconds
setTimeout(() => {
recentlyUpdatedSensors.delete(data.sensorId)
}, 2000)
} }
function updateLatestReading(reading: SensorReading) { function updateLatestReading(reading: SensorReading) {
latestReadings.set(reading.sensorId, reading) latestReadings.set(reading.sensor_id, reading)
// Mark sensor as recently updated // Mark sensor as recently updated
recentlyUpdatedSensors.add(reading.sensorId) recentlyUpdatedSensors.add(reading.sensor_id)
console.log(reading.sensor_type)
// Remove from recently updated after 2 seconds // Remove from recently updated after 2 seconds
setTimeout(() => { setTimeout(() => {
recentlyUpdatedSensors.delete(reading.sensorId) recentlyUpdatedSensors.delete(reading.sensor_id)
}, 2000) }, 2000)
} }
@@ -225,17 +168,21 @@ export const useSensorStore = defineStore('sensor', () => {
} }
// Sensors API functions // Sensors API functions
async function fetchApiSensors(params?: { room?: string; sensor_type?: any; status?: any }) { async function fetchApiSensors(params?: {
room?: string
sensor_type?: SensorType
status?: SensorStatus
}) {
const result = await handleApiCall(() => sensorsApi.getSensors(params)) const result = await handleApiCall(() => sensorsApi.getSensors(params))
if (result) { if (result) {
console.log(result)
// Check if result has a sensors property (common API pattern) // Check if result has a sensors property (common API pattern)
if (result.sensors && Array.isArray(result.sensors)) { if (result.sensors && Array.isArray(result.sensors)) {
result.sensors.forEach((sensor) => { result.sensors.forEach((sensor) => {
const sensorKey = sensor.id || sensor._id || sensor.sensor_id const sensorKey = sensor._id || sensor.sensor_id
const sensorType = sensor.sensor_type || sensor.type const sensorType = sensor.sensor_type || sensor.type
const sensorName = sensor.name || '' const sensorName = sensor.name || ''
// Normalize sensor data structure for frontend compatibility
const normalizedSensor = { const normalizedSensor = {
...sensor, ...sensor,
id: sensorKey, id: sensorKey,
@@ -255,58 +202,18 @@ export const useSensorStore = defineStore('sensor', () => {
signalStrength: sensor.metadata?.signalStrength, signalStrength: sensor.metadata?.signalStrength,
...sensor.metadata, ...sensor.metadata,
}, },
tags: sensor.tags || [], lastSeen: sensor.last_seen || Date.now() / 1000,
lastSeen: sensor.last_seen || sensor.lastSeen || Date.now() / 1000,
} }
sensorDevices.set(sensorKey, normalizedSensor) sensorDevices.set(sensorKey, normalizedSensor)
}) })
} } else {
// Check if result is directly an array console.warn('Unexpected result format:', typeof result, result)
else if (Array.isArray(result)) {
console.log('Result is direct array:', result)
result.forEach((sensor) => {
console.log('Adding sensor:', sensor)
const sensorKey = sensor.id || sensor._id || sensor.sensor_id
const sensorType = sensor.sensor_type || sensor.type
const sensorName = sensor.name || ''
// Normalize sensor data structure for frontend compatibility
const normalizedSensor = {
...sensor,
id: sensorKey,
type: sensorType,
capabilities: {
actions: [], // Default empty actions array
monitoring:
sensor.capabilities?.monitoring ||
getDefaultMonitoringCapabilities(sensorType, sensorName),
...sensor.capabilities,
},
metadata: {
model: sensor.metadata?.model || 'Unknown',
firmware: sensor.metadata?.firmware || 'Unknown',
location: sensor.metadata?.location || sensor.room || 'Unknown',
battery: sensor.metadata?.battery,
signalStrength: sensor.metadata?.signalStrength,
...sensor.metadata,
},
tags: sensor.tags || [],
lastSeen: sensor.last_seen || sensor.lastSeen || Date.now() / 1000,
}
sensorDevices.set(sensorKey, normalizedSensor)
})
}
// Log what we actually got
else {
console.log('Unexpected result format:', typeof result, result)
} }
} else { } else {
console.log('No result received from API') console.error('No result received from API')
} }
console.log('Current sensor devices:', Array.from(sensorDevices.entries()))
return result return result
} }
@@ -344,11 +251,11 @@ export const useSensorStore = defineStore('sensor', () => {
apiError, apiError,
// Actions // Actions
updateEnergySensors,
updateSensorRoom, updateSensorRoom,
executeSensorAction, executeSensorAction,
getSensorsByRoom, getSensorsByRoom,
getSensorsByType, getSensorsByType,
updateSensorData,
updateLatestReading, updateLatestReading,
// API functions // API functions

View File

@@ -5,13 +5,6 @@ import { useRoomStore } from './room'
const MAX_DATA_POINTS = 100 const MAX_DATA_POINTS = 100
interface LegacyEnergyData {
sensorId: string
timestamp: number
value: number
unit: string
}
interface SensorReading { interface SensorReading {
sensorId: string sensorId: string
room: string room: string
@@ -31,9 +24,8 @@ interface SensorReading {
} }
export const useWebSocketStore = defineStore('websocket', () => { export const useWebSocketStore = defineStore('websocket', () => {
// State
const isConnected = ref(false) const isConnected = ref(false)
const latestMessage = ref<LegacyEnergyData | null>(null) const latestMessage = ref<SensorReading | null>(null)
const timeSeriesData = reactive<{ const timeSeriesData = reactive<{
labels: string[] labels: string[]
datasets: { data: number[] }[] datasets: { data: number[] }[]
@@ -43,16 +35,14 @@ export const useWebSocketStore = defineStore('websocket', () => {
}) })
let socket: WebSocket | null = null let socket: WebSocket | null = null
const newDataBuffer: (LegacyEnergyData | SensorReading)[] = [] const newDataBuffer: SensorReading[] = []
// Actions
function connect(url: string) { function connect(url: string) {
if (isConnected.value && socket) { if (isConnected.value && socket) {
console.log('Already connected.') console.log('Already connected.')
return return
} }
// Close any existing connection first
if (socket) { if (socket) {
socket.onclose = null socket.onclose = null
socket.onerror = null socket.onerror = null
@@ -61,11 +51,11 @@ export const useWebSocketStore = defineStore('websocket', () => {
socket = null socket = null
} }
console.log(`Connecting to WebSocket at ${url}`) console.info(`Connecting to WebSocket at ${url}`)
socket = new WebSocket(url) socket = new WebSocket(url)
socket.onopen = () => { socket.onopen = () => {
console.log('WebSocket connection established.') console.info('WebSocket connection established.')
isConnected.value = true isConnected.value = true
} }
@@ -74,7 +64,7 @@ export const useWebSocketStore = defineStore('websocket', () => {
const data = JSON.parse(event.data) const data = JSON.parse(event.data)
if (data.type === 'proxy_info' && data.sensor_service_url) { if (data.type === 'proxy_info' && data.sensor_service_url) {
console.log('Received proxy info, reconnecting to sensor service...') console.warn('Received proxy info, reconnecting to sensor service...')
if (socket) { if (socket) {
socket.onclose = null socket.onclose = null
socket.close() socket.close()
@@ -83,7 +73,7 @@ export const useWebSocketStore = defineStore('websocket', () => {
isConnected.value = false isConnected.value = false
setTimeout(() => { setTimeout(() => {
console.log('Connecting directly to sensor service at ws://localhost:8007/ws') console.info('Connecting directly to sensor service at ws://localhost:8007/ws')
connect('ws://localhost:8007/ws') connect('ws://localhost:8007/ws')
}, 100) }, 100)
return return
@@ -125,155 +115,41 @@ export const useWebSocketStore = defineStore('websocket', () => {
} }
} }
function isLegacyData(data: any): data is LegacyEnergyData { function processIncomingData(data: SensorReading) {
return 'value' in data && !('energy' in data) && !('co2' in data)
}
function mapWebSocketSensorIdd(webSocketSensorId: string): string {
const sensorStore = useSensorStore()
// First try exact match
if (sensorStore.sensorDevices.has(webSocketSensorId)) {
return webSocketSensorId
}
console.log(`Attempting to map WebSocket sensor ID: ${webSocketSensorId}`)
console.log(
'Available API sensors:',
Array.from(sensorStore.sensorDevices.entries()).map(([id, sensor]) => ({
id,
name: sensor.name,
room: sensor.room,
})),
)
// Try to find a sensor by matching room and type patterns
const sensors = Array.from(sensorStore.sensorDevices.entries())
// Pattern matching for common WebSocket ID formats
for (const [apiSensorId, sensor] of sensors) {
const sensorName = sensor.name.toLowerCase()
const sensorRoom = sensor.room?.toLowerCase() || ''
const wsId = webSocketSensorId.toLowerCase()
// Room-based matching (more comprehensive)
if (wsId.includes('living') && sensorName.includes('living')) return apiSensorId
if (wsId.includes('lr') && sensorName.includes('living')) return apiSensorId
if (wsId.includes('bt') && sensorName.includes('bathroom')) return apiSensorId // bt = bathroom
if (wsId.includes('bathroom') && sensorName.includes('bathroom')) return apiSensorId
if (wsId.includes('br') && sensorName.includes('bedroom')) return apiSensorId // br = bedroom
if (wsId.includes('bedroom') && sensorName.includes('bedroom')) return apiSensorId
if (wsId.includes('kt') && sensorName.includes('kitchen')) return apiSensorId // kt = kitchen
if (wsId.includes('kitchen') && sensorName.includes('kitchen')) return apiSensorId
if (wsId.includes('gr') && sensorName.includes('garage')) return apiSensorId // gr = garage
if (wsId.includes('garage') && sensorName.includes('garage')) return apiSensorId
// Type-based matching
if (wsId.includes('energy') && sensorName.includes('energy')) return apiSensorId
if (wsId.includes('co2') && sensorName.includes('co2')) return apiSensorId
if (wsId.includes('temp') && sensorName.includes('temp')) return apiSensorId
if (wsId.includes('humidity') && sensorName.includes('humidity')) return apiSensorId
// Combined room + type matching for better accuracy
if (
wsId.includes('bathroom') &&
wsId.includes('humidity') &&
sensorName.includes('bathroom') &&
sensorName.includes('humidity')
)
return apiSensorId
if (
wsId.includes('bedroom') &&
wsId.includes('temp') &&
sensorName.includes('bedroom') &&
sensorName.includes('temp')
)
return apiSensorId
if (
wsId.includes('kitchen') &&
wsId.includes('humidity') &&
sensorName.includes('kitchen') &&
sensorName.includes('humidity')
)
return apiSensorId
}
// If no mapping found, let's try to use the first sensor as a fallback for testing
if (sensors.length > 0) {
const fallbackSensor = sensors[0]
console.warn(
`No mapping found for ${webSocketSensorId}, using fallback sensor: ${fallbackSensor[1].name}`,
)
return fallbackSensor[0]
}
console.warn(`Could not map WebSocket sensor ID ${webSocketSensorId} to any API sensor`)
return webSocketSensorId // Return original if no mapping found
}
function processIncomingData(data: LegacyEnergyData | SensorReading) {
// Skip non-data messages // Skip non-data messages
if ('type' in data && (data.type === 'connection_established' || data.type === 'proxy_info')) { if ('type' in data && (data.type === 'connection_established' || data.type === 'proxy_info')) {
return return
} }
// Normalize property names: sensor_id -> sensorId
if ('sensor_id' in data && !('sensorId' in data)) {
data.sensorId = data.sensor_id
}
const sensorStore = useSensorStore() const sensorStore = useSensorStore()
const roomStore = useRoomStore() const roomStore = useRoomStore()
// Handle both legacy and new data formats // Handle new multi-metric data
if (isLegacyData(data)) { // Only update room data if we have the proper structure
latestMessage.value = data if (data.energy && data.co2 && data.room) {
sensorStore.updateSensorData(data) if (data.energy) {
sensorStore.updateEnergySensors(data)
// Convert legacy data to SensorReading format for individual sensor updates
const mappedSensorId = mapWebSocketSensorId(data.sensorId)
const sensorReading = {
sensorId: mappedSensorId,
room: 'Unknown', // Legacy data doesn't include room info
timestamp: data.timestamp,
energy: {
value: data.value,
unit: data.unit,
},
co2: {
value: 400, // Default CO2 value for legacy data
unit: 'ppm',
},
} }
sensorStore.updateLatestReading(sensorReading) roomStore.updateRoomData(data)
}
// Update time series for chart // Map the sensor ID for individual sensor updates
const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString() // const mappedSensorId = mapWebSocketSensorId(data.sensorId)
timeSeriesData.labels.push(newLabel) const mappedData = { ...data, sensorId: data.sensorId, id: data.sensorId }
timeSeriesData.datasets[0].data.push(data.value) sensorStore.updateLatestReading(data) // Update individual sensor readings for cards
} else {
// Handle new multi-metric data
// Only update room data if we have the proper structure
if (data.energy && data.co2 && data.room) {
roomStore.updateRoomData(data)
}
// Map the sensor ID for individual sensor updates
// const mappedSensorId = mapWebSocketSensorId(data.sensorId)
const mappedData = { ...data, sensorId: data.sensorId, id: data.sensorId }
sensorStore.updateLatestReading(mappedData) // Update individual sensor readings for cards
if (data.energy) {
// Update time series for chart (use energy values if available) // Update time series for chart (use energy values if available)
const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString() const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString()
timeSeriesData.labels.push(newLabel) timeSeriesData.labels.push(newLabel)
timeSeriesData.datasets[0].data.push(data.energy?.value || 0) timeSeriesData.datasets[0].data.push(data.energy?.value)
} }
}
// Keep only the latest data points // Keep only the latest data points
if (timeSeriesData.labels.length > MAX_DATA_POINTS) { if (timeSeriesData.labels.length > MAX_DATA_POINTS) {
timeSeriesData.labels.shift() timeSeriesData.labels.shift()
timeSeriesData.datasets[0].data.shift() timeSeriesData.datasets[0].data.shift()
}
} }
return { return {