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:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="bg-white rounded-2xl shadow-sm p-4">
|
||||
<h6 class="text-sm font-bold text-gray-500 mb-4">Sensor Consumption</h6>
|
||||
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full">
|
||||
<thead>
|
||||
@@ -29,21 +29,37 @@
|
||||
No sensor data available. Waiting for WebSocket connection...
|
||||
</td>
|
||||
</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">
|
||||
{{ sensor.sensorId }}
|
||||
{{ sensor.sensor_id }}
|
||||
</td>
|
||||
<td class="py-3 text-sm text-gray-600 text-right">
|
||||
{{ sensor.latestValue.toFixed(2) }} {{ sensor.unit }}
|
||||
{{ sensor.room }}
|
||||
</td>
|
||||
<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 class="py-3 text-sm text-gray-600 text-right">
|
||||
{{ sensor.averageConsumption.toFixed(2) }} {{ sensor.unit }}
|
||||
{{ sensor.room }}
|
||||
</td>
|
||||
<td class="py-3 text-sm text-gray-500 text-right">
|
||||
{{ formatTime(sensor.lastUpdated) }}
|
||||
{{ formatTime(sensor.timestamp) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -53,29 +69,27 @@
|
||||
<!-- Connection Status Indicator -->
|
||||
<div class="mt-4 flex items-center justify-between text-xs text-gray-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="w-2 h-2 rounded-full"
|
||||
:class="energyStore.isConnected ? 'bg-green-500' : 'bg-red-500'"
|
||||
<div
|
||||
class="w-2 h-2 rounded-full"
|
||||
:class="websocketStore.isConnected ? 'bg-green-500' : 'bg-red-500'"
|
||||
></div>
|
||||
<span>{{ energyStore.isConnected ? 'Connected' : 'Disconnected' }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ sensorList.length }} sensor{{ sensorList.length !== 1 ? 's' : '' }} active
|
||||
<span>{{ websocketStore.isConnected ? 'Connected' : 'Disconnected' }}</span>
|
||||
</div>
|
||||
<div>{{ sensorList.length }} sensor{{ sensorList.length !== 1 ? 's' : '' }} active</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useEnergyStore } from '@/stores/energy'
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useSensorStore } from '@/stores/sensor'
|
||||
import { useWebSocketStore } from '@/stores/websocket'
|
||||
|
||||
const energyStore = useEnergyStore()
|
||||
const sensorStore = useSensorStore()
|
||||
const websocketStore = useWebSocketStore()
|
||||
|
||||
const sensorList = computed(() => {
|
||||
return Array.from(energyStore.sensorsData.values()).sort((a, b) =>
|
||||
a.sensorId.localeCompare(b.sensorId)
|
||||
)
|
||||
return Array.from(sensorStore.sensorDevices.values()).sort((a, b) => a.name.localeCompare(b.name))
|
||||
})
|
||||
|
||||
const formatTime = (timestamp: number) => {
|
||||
@@ -83,7 +97,7 @@ const formatTime = (timestamp: number) => {
|
||||
const now = new Date()
|
||||
const diffMs = now.getTime() - date.getTime()
|
||||
const diffSecs = Math.floor(diffMs / 1000)
|
||||
|
||||
|
||||
if (diffSecs < 60) {
|
||||
return `${diffSecs}s ago`
|
||||
} else if (diffSecs < 3600) {
|
||||
@@ -92,4 +106,8 @@ const formatTime = (timestamp: number) => {
|
||||
return date.toLocaleTimeString()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
onMounted(async () => {
|
||||
await sensorStore.fetchApiSensors()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -50,12 +50,11 @@ export interface DataResponse {
|
||||
}
|
||||
|
||||
export interface SensorReading {
|
||||
_id?: string
|
||||
sensor_id: string
|
||||
room?: string
|
||||
room: string
|
||||
sensor_type: string
|
||||
timestamp: number
|
||||
created_at?: string
|
||||
type: string
|
||||
energy?: {
|
||||
value: number
|
||||
unit: string
|
||||
@@ -176,29 +175,18 @@ export interface SystemEvent {
|
||||
}
|
||||
|
||||
export interface SensorDevice {
|
||||
id: string
|
||||
_id: string
|
||||
sensor_id: string
|
||||
name: string
|
||||
type: SensorType
|
||||
sensor_type: SensorType
|
||||
room: string
|
||||
status: SensorStatus
|
||||
location?: string
|
||||
lastSeen: number
|
||||
total_readings?: number
|
||||
capabilities: {
|
||||
monitoring: string[]
|
||||
actions: SensorAction[]
|
||||
}
|
||||
metadata: {
|
||||
location?: string
|
||||
model?: string
|
||||
firmware?: string
|
||||
battery?: number
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
manufacturer?: string
|
||||
}
|
||||
metadata: SensorMetadata
|
||||
}
|
||||
|
||||
export interface SensorAction {
|
||||
@@ -245,7 +233,20 @@ export enum SensorStatus {
|
||||
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 {
|
||||
private baseUrl: string
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
type DataResponse,
|
||||
type SensorType,
|
||||
type SensorStatus,
|
||||
type SensorMetadata,
|
||||
} from './api'
|
||||
|
||||
export const sensorsApi = {
|
||||
@@ -13,8 +14,24 @@ export const sensorsApi = {
|
||||
room?: string
|
||||
sensor_type?: SensorType
|
||||
status?: SensorStatus
|
||||
}): Promise<SensorDevice[]> {
|
||||
return apiClient.get<SensorDevice[]>('/api/v1/sensors/get', params)
|
||||
}): Promise<{
|
||||
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> {
|
||||
@@ -39,7 +56,7 @@ export const sensorsApi = {
|
||||
|
||||
async updateSensorMetadata(
|
||||
sensorId: string,
|
||||
metadata: Record<string, any>,
|
||||
metadata: SensorMetadata,
|
||||
): Promise<{ message: string }> {
|
||||
return apiClient.put<{ message: string }>(`/api/v1/sensors/${sensorId}/metadata`, metadata)
|
||||
},
|
||||
|
||||
@@ -3,18 +3,12 @@ import { computed } from 'vue'
|
||||
import { useSensorStore } from './sensor'
|
||||
import { useRoomStore } from './room'
|
||||
import { useAnalyticsStore } from './analytics'
|
||||
import { useWebSocketStore } from './websocket'
|
||||
|
||||
export const useEnergyStore = defineStore('energy', () => {
|
||||
// Get instances of other stores
|
||||
const sensorStore = useSensorStore()
|
||||
const roomStore = useRoomStore()
|
||||
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
|
||||
async function initializeFromApi() {
|
||||
@@ -27,15 +21,9 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
}
|
||||
|
||||
return {
|
||||
// WebSocket state (delegated)
|
||||
isConnected: computed(() => webSocketStore.isConnected),
|
||||
latestMessage: computed(() => webSocketStore.latestMessage),
|
||||
timeSeriesData: computed(() => webSocketStore.timeSeriesData),
|
||||
|
||||
// Sensor state (delegated)
|
||||
sensorsData: computed(() => sensorStore.sensorsData),
|
||||
sensorDevices: computed(() => sensorStore.sensorDevices),
|
||||
latestReadings: computed(() => sensorStore.latestReadings),
|
||||
apiSensors: computed(() => Array.from(sensorStore.sensorDevices.values())), // Convert Map to Array
|
||||
|
||||
// Room state (delegated)
|
||||
@@ -51,13 +39,11 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
healthStatus: computed(() => analyticsStore.healthStatus),
|
||||
|
||||
// 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),
|
||||
|
||||
// WebSocket functions (delegated)
|
||||
connect,
|
||||
disconnect,
|
||||
|
||||
// Sensor functions (delegated)
|
||||
updateSensorRoom: sensorStore.updateSensorRoom,
|
||||
executeSensorAction: sensorStore.executeSensorAction,
|
||||
|
||||
@@ -3,37 +3,11 @@ import { ref, reactive } from 'vue'
|
||||
import {
|
||||
sensorsApi,
|
||||
SensorType,
|
||||
SensorStatus,
|
||||
type SensorDevice,
|
||||
type SensorAction,
|
||||
type SensorStatus,
|
||||
type SensorReading,
|
||||
} 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', () => {
|
||||
// State
|
||||
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)
|
||||
if (!sensor) return false
|
||||
|
||||
@@ -75,49 +49,18 @@ export const useSensorStore = defineStore('sensor', () => {
|
||||
return Array.from(sensorDevices.values()).filter((sensor) => sensor.type === type)
|
||||
}
|
||||
|
||||
function updateSensorData(data: LegacyEnergyData) {
|
||||
const existingSensor = sensorsData.get(data.sensorId)
|
||||
|
||||
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 updateEnergySensors(data: Sensor) {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
function updateLatestReading(reading: SensorReading) {
|
||||
latestReadings.set(reading.sensorId, reading)
|
||||
latestReadings.set(reading.sensor_id, reading)
|
||||
|
||||
// Mark sensor as recently updated
|
||||
recentlyUpdatedSensors.add(reading.sensorId)
|
||||
console.log(reading.sensor_type)
|
||||
recentlyUpdatedSensors.add(reading.sensor_id)
|
||||
// Remove from recently updated after 2 seconds
|
||||
setTimeout(() => {
|
||||
recentlyUpdatedSensors.delete(reading.sensorId)
|
||||
recentlyUpdatedSensors.delete(reading.sensor_id)
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
@@ -225,17 +168,21 @@ export const useSensorStore = defineStore('sensor', () => {
|
||||
}
|
||||
|
||||
// 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))
|
||||
if (result) {
|
||||
console.log(result)
|
||||
// Check if result has a sensors property (common API pattern)
|
||||
if (result.sensors && Array.isArray(result.sensors)) {
|
||||
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 sensorName = sensor.name || ''
|
||||
|
||||
// Normalize sensor data structure for frontend compatibility
|
||||
const normalizedSensor = {
|
||||
...sensor,
|
||||
id: sensorKey,
|
||||
@@ -255,58 +202,18 @@ export const useSensorStore = defineStore('sensor', () => {
|
||||
signalStrength: sensor.metadata?.signalStrength,
|
||||
...sensor.metadata,
|
||||
},
|
||||
tags: sensor.tags || [],
|
||||
lastSeen: sensor.last_seen || sensor.lastSeen || Date.now() / 1000,
|
||||
lastSeen: sensor.last_seen || Date.now() / 1000,
|
||||
}
|
||||
|
||||
sensorDevices.set(sensorKey, normalizedSensor)
|
||||
})
|
||||
}
|
||||
// Check if result is directly an array
|
||||
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 {
|
||||
console.warn('Unexpected result format:', typeof result, result)
|
||||
}
|
||||
} 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
|
||||
}
|
||||
|
||||
@@ -344,11 +251,11 @@ export const useSensorStore = defineStore('sensor', () => {
|
||||
apiError,
|
||||
|
||||
// Actions
|
||||
updateEnergySensors,
|
||||
updateSensorRoom,
|
||||
executeSensorAction,
|
||||
getSensorsByRoom,
|
||||
getSensorsByType,
|
||||
updateSensorData,
|
||||
updateLatestReading,
|
||||
|
||||
// API functions
|
||||
|
||||
@@ -5,13 +5,6 @@ import { useRoomStore } from './room'
|
||||
|
||||
const MAX_DATA_POINTS = 100
|
||||
|
||||
interface LegacyEnergyData {
|
||||
sensorId: string
|
||||
timestamp: number
|
||||
value: number
|
||||
unit: string
|
||||
}
|
||||
|
||||
interface SensorReading {
|
||||
sensorId: string
|
||||
room: string
|
||||
@@ -31,9 +24,8 @@ interface SensorReading {
|
||||
}
|
||||
|
||||
export const useWebSocketStore = defineStore('websocket', () => {
|
||||
// State
|
||||
const isConnected = ref(false)
|
||||
const latestMessage = ref<LegacyEnergyData | null>(null)
|
||||
const latestMessage = ref<SensorReading | null>(null)
|
||||
const timeSeriesData = reactive<{
|
||||
labels: string[]
|
||||
datasets: { data: number[] }[]
|
||||
@@ -43,16 +35,14 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
||||
})
|
||||
|
||||
let socket: WebSocket | null = null
|
||||
const newDataBuffer: (LegacyEnergyData | SensorReading)[] = []
|
||||
const newDataBuffer: SensorReading[] = []
|
||||
|
||||
// Actions
|
||||
function connect(url: string) {
|
||||
if (isConnected.value && socket) {
|
||||
console.log('Already connected.')
|
||||
return
|
||||
}
|
||||
|
||||
// Close any existing connection first
|
||||
if (socket) {
|
||||
socket.onclose = null
|
||||
socket.onerror = null
|
||||
@@ -61,11 +51,11 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
||||
socket = null
|
||||
}
|
||||
|
||||
console.log(`Connecting to WebSocket at ${url}`)
|
||||
console.info(`Connecting to WebSocket at ${url}`)
|
||||
socket = new WebSocket(url)
|
||||
|
||||
socket.onopen = () => {
|
||||
console.log('WebSocket connection established.')
|
||||
console.info('WebSocket connection established.')
|
||||
isConnected.value = true
|
||||
}
|
||||
|
||||
@@ -74,7 +64,7 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
||||
const data = JSON.parse(event.data)
|
||||
|
||||
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) {
|
||||
socket.onclose = null
|
||||
socket.close()
|
||||
@@ -83,7 +73,7 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
||||
isConnected.value = false
|
||||
|
||||
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')
|
||||
}, 100)
|
||||
return
|
||||
@@ -125,155 +115,41 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function isLegacyData(data: any): data is LegacyEnergyData {
|
||||
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) {
|
||||
function processIncomingData(data: SensorReading) {
|
||||
// Skip non-data messages
|
||||
if ('type' in data && (data.type === 'connection_established' || data.type === 'proxy_info')) {
|
||||
return
|
||||
}
|
||||
|
||||
// Normalize property names: sensor_id -> sensorId
|
||||
if ('sensor_id' in data && !('sensorId' in data)) {
|
||||
data.sensorId = data.sensor_id
|
||||
}
|
||||
|
||||
const sensorStore = useSensorStore()
|
||||
const roomStore = useRoomStore()
|
||||
|
||||
// Handle both legacy and new data formats
|
||||
if (isLegacyData(data)) {
|
||||
latestMessage.value = data
|
||||
sensorStore.updateSensorData(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',
|
||||
},
|
||||
// Handle new multi-metric data
|
||||
// Only update room data if we have the proper structure
|
||||
if (data.energy && data.co2 && data.room) {
|
||||
if (data.energy) {
|
||||
sensorStore.updateEnergySensors(data)
|
||||
}
|
||||
sensorStore.updateLatestReading(sensorReading)
|
||||
roomStore.updateRoomData(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
|
||||
// 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
|
||||
// Map the sensor ID for individual sensor updates
|
||||
// const mappedSensorId = mapWebSocketSensorId(data.sensorId)
|
||||
const mappedData = { ...data, sensorId: data.sensorId, id: data.sensorId }
|
||||
sensorStore.updateLatestReading(data) // Update individual sensor readings for cards
|
||||
|
||||
if (data.energy) {
|
||||
// Update time series for chart (use energy values if available)
|
||||
const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString()
|
||||
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
|
||||
if (timeSeriesData.labels.length > MAX_DATA_POINTS) {
|
||||
timeSeriesData.labels.shift()
|
||||
timeSeriesData.datasets[0].data.shift()
|
||||
}
|
||||
// Keep only the latest data points
|
||||
if (timeSeriesData.labels.length > MAX_DATA_POINTS) {
|
||||
timeSeriesData.labels.shift()
|
||||
timeSeriesData.datasets[0].data.shift()
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user