- 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
269 lines
8.3 KiB
TypeScript
269 lines
8.3 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { ref, reactive } from 'vue'
|
|
import {
|
|
sensorsApi,
|
|
SensorType,
|
|
type SensorDevice,
|
|
type SensorStatus,
|
|
type SensorReading,
|
|
} from '@/services'
|
|
|
|
export const useSensorStore = defineStore('sensor', () => {
|
|
// State
|
|
const sensorDevices = reactive<Map<string, SensorDevice>>(new Map())
|
|
const latestReadings = reactive<Map<string, SensorReading>>(new Map())
|
|
const sensorsData = reactive<Map<string, any>>(new Map()) // Legacy support
|
|
const recentlyUpdatedSensors = reactive<Set<string>>(new Set()) // Track recently updated sensors
|
|
const apiLoading = ref(false)
|
|
const apiError = ref<string | null>(null)
|
|
|
|
// Actions
|
|
function updateSensorRoom(sensorId: string, newRoom: string) {
|
|
const sensor = sensorDevices.get(sensorId)
|
|
if (sensor) {
|
|
sensor.room = newRoom
|
|
sensorDevices.set(sensorId, { ...sensor })
|
|
}
|
|
}
|
|
|
|
async function executeSensorAction(sensorId: string, actionId: string) {
|
|
const sensor = sensorDevices.get(sensorId)
|
|
if (!sensor) return false
|
|
|
|
const action = sensor.capabilities.actions.find((a) => a.id === actionId)
|
|
if (!action) return false
|
|
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
console.log(`Action ${action.name} executed successfully on ${sensor.name}`)
|
|
resolve(true)
|
|
}, 1000)
|
|
})
|
|
}
|
|
|
|
function getSensorsByRoom(room: string): SensorDevice[] {
|
|
return Array.from(sensorDevices.values()).filter((sensor) => sensor.room === room)
|
|
}
|
|
|
|
function getSensorsByType(type: SensorDevice['type']): SensorDevice[] {
|
|
return Array.from(sensorDevices.values()).filter((sensor) => sensor.type === type)
|
|
}
|
|
|
|
function updateEnergySensors(data: Sensor) {
|
|
console.log(data)
|
|
}
|
|
|
|
function updateLatestReading(reading: SensorReading) {
|
|
latestReadings.set(reading.sensor_id, reading)
|
|
|
|
// Mark sensor as recently updated
|
|
recentlyUpdatedSensors.add(reading.sensor_id)
|
|
// Remove from recently updated after 2 seconds
|
|
setTimeout(() => {
|
|
recentlyUpdatedSensors.delete(reading.sensor_id)
|
|
}, 2000)
|
|
}
|
|
|
|
// API Integration Functions
|
|
async function handleApiCall<T>(apiCall: () => Promise<T>): Promise<T | null> {
|
|
apiLoading.value = true
|
|
apiError.value = null
|
|
try {
|
|
const result = await apiCall()
|
|
return result
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
|
console.error('API call error:', error)
|
|
console.error('Error message:', errorMessage)
|
|
|
|
if (errorMessage.includes('401') || errorMessage.includes('Authorization')) {
|
|
console.warn('Authentication error detected, attempting to re-authenticate...')
|
|
|
|
try {
|
|
const authStore = (window as any).__AUTH_STORE__
|
|
if (authStore && typeof authStore.ensureAuthenticated === 'function') {
|
|
const authSuccess = await authStore.ensureAuthenticated()
|
|
if (authSuccess) {
|
|
console.log('Re-authentication successful, retrying API call...')
|
|
try {
|
|
const retryResult = await apiCall()
|
|
return retryResult
|
|
} catch (retryError) {
|
|
const retryErrorMessage =
|
|
retryError instanceof Error ? retryError.message : 'Retry failed'
|
|
apiError.value = retryErrorMessage
|
|
console.error('API retry failed:', retryErrorMessage)
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
} catch (authError) {
|
|
console.error('Re-authentication failed:', authError)
|
|
}
|
|
}
|
|
|
|
// Check if it's a connection error (backend not running)
|
|
if (
|
|
errorMessage.includes('fetch') ||
|
|
errorMessage.includes('ERR_CONNECTION') ||
|
|
errorMessage.includes('ECONNREFUSED')
|
|
) {
|
|
const backendError =
|
|
'Backend server not running on http://localhost:8000. Please start the backend service.'
|
|
apiError.value = backendError
|
|
console.error('Connection error - backend not running')
|
|
return null
|
|
}
|
|
|
|
apiError.value = errorMessage
|
|
console.error('API call failed:', errorMessage)
|
|
return null
|
|
} finally {
|
|
apiLoading.value = false
|
|
}
|
|
}
|
|
|
|
// Helper function to get default monitoring capabilities based on sensor type
|
|
function getDefaultMonitoringCapabilities(sensorType: string, sensorName: string): string[] {
|
|
// First check the sensor name for clues
|
|
const nameLower = sensorName?.toLowerCase() || ''
|
|
|
|
if (nameLower.includes('energy') || nameLower.includes('power')) {
|
|
return ['energy']
|
|
}
|
|
if (nameLower.includes('temperature') || nameLower.includes('temp')) {
|
|
return ['temperature']
|
|
}
|
|
if (nameLower.includes('humidity')) {
|
|
return ['humidity']
|
|
}
|
|
if (nameLower.includes('co2') || nameLower.includes('air quality')) {
|
|
return ['co2']
|
|
}
|
|
if (nameLower.includes('motion')) {
|
|
return ['motion']
|
|
}
|
|
|
|
// Then fall back to sensor type
|
|
switch (sensorType?.toLowerCase()) {
|
|
case 'energy':
|
|
return ['energy']
|
|
case 'temperature':
|
|
return ['temperature']
|
|
case 'humidity':
|
|
return ['humidity']
|
|
case 'co2':
|
|
return ['co2']
|
|
case 'motion':
|
|
return ['motion']
|
|
case 'hvac':
|
|
return ['temperature']
|
|
case 'lighting':
|
|
return [] // Lighting sensors don't typically monitor environmental data
|
|
case 'security':
|
|
return ['motion']
|
|
default:
|
|
return [] // No default monitoring capabilities
|
|
}
|
|
}
|
|
|
|
// Sensors API functions
|
|
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.sensor_id
|
|
const sensorType = sensor.sensor_type || sensor.type
|
|
const sensorName = sensor.name || ''
|
|
|
|
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,
|
|
},
|
|
lastSeen: sensor.last_seen || Date.now() / 1000,
|
|
}
|
|
|
|
sensorDevices.set(sensorKey, normalizedSensor)
|
|
})
|
|
} else {
|
|
console.warn('Unexpected result format:', typeof result, result)
|
|
}
|
|
} else {
|
|
console.error('No result received from API')
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
async function fetchApiSensorData(
|
|
sensorId: string,
|
|
params?: { start_time?: number; end_time?: number; limit?: number; offset?: number },
|
|
) {
|
|
return handleApiCall(() => sensorsApi.getSensorData(sensorId, params))
|
|
}
|
|
|
|
async function updateApiSensorMetadata(sensorId: string, metadata: Record<string, any>) {
|
|
return handleApiCall(() => sensorsApi.updateSensorMetadata(sensorId, metadata))
|
|
}
|
|
|
|
async function deleteApiSensor(sensorId: string) {
|
|
return handleApiCall(() => sensorsApi.deleteSensor(sensorId))
|
|
}
|
|
|
|
async function exportApiData(params: {
|
|
start_time: number
|
|
end_time: number
|
|
sensor_ids?: string
|
|
format?: 'json' | 'csv'
|
|
}) {
|
|
return handleApiCall(() => sensorsApi.exportData(params))
|
|
}
|
|
|
|
return {
|
|
// State
|
|
sensorDevices,
|
|
latestReadings,
|
|
sensorsData,
|
|
recentlyUpdatedSensors,
|
|
apiLoading,
|
|
apiError,
|
|
|
|
// Actions
|
|
updateEnergySensors,
|
|
updateSensorRoom,
|
|
executeSensorAction,
|
|
getSensorsByRoom,
|
|
getSensorsByType,
|
|
updateLatestReading,
|
|
|
|
// API functions
|
|
fetchApiSensors,
|
|
fetchApiSensorData,
|
|
updateApiSensorMetadata,
|
|
deleteApiSensor,
|
|
exportApiData,
|
|
}
|
|
})
|