Refactor stores for modularity and API type updates

- Split energy store into sensor, room, analytics, and websocket stores
- Add new analytics, room, sensor, and websocket stores - Update API
types for sensors (SensorDevice, SensorAction) - Update sensorsApi to
use new SensorDevice type - Add central index for store exports and
types - Refactor energy store to delegate to modular stores - Remove
legacy code and consolidate API logic
This commit is contained in:
rafaeldpsilva
2025-09-25 17:09:42 +01:00
parent 326746b5ef
commit 3299472c85
8 changed files with 1139 additions and 857 deletions

355
src/stores/sensor.ts Normal file
View File

@@ -0,0 +1,355 @@
import { defineStore } from 'pinia'
import { ref, reactive } from 'vue'
import { sensorsApi, SensorType, SensorStatus, type SensorDevice, type SensorAction } from '@/services'
interface SensorReading {
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())
const latestReadings = reactive<Map<string, SensorReading>>(new Map())
const sensorsData = reactive<Map<string, any>>(new Map()) // Legacy support
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, parameters?: any) {
const sensor = sensorDevices.get(sensorId)
if (!sensor) return false
const action = sensor.capabilities.actions.find((a) => a.id === actionId)
if (!action) return false
console.log(`Executing action ${actionId} on sensor ${sensorId}`, parameters)
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 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,
})
}
}
function updateLatestReading(reading: SensorReading) {
latestReadings.set(reading.sensorId, reading)
}
// 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'
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)
}
}
apiError.value = errorMessage
console.error('API call failed:', errorMessage)
return null
} finally {
apiLoading.value = false
}
}
// Sensors API functions
async function fetchApiSensors(params?: { room?: string; sensor_type?: any; status?: any }) {
const result = await handleApiCall(() => sensorsApi.getSensors(params))
if (result && Array.isArray(result)) {
result.forEach((sensor) => {
sensorDevices.set(sensor.id, sensor)
})
}
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))
}
// Initialize mock sensor devices
function initializeMockSensors() {
const mockSensors: SensorDevice[] = [
{
id: 'sensor_1',
sensor_id: 'sensor_1',
name: 'Energy Monitor 1',
type: 'energy',
sensor_type: 'energy',
room: 'Conference Room A',
status: 'online',
lastSeen: Date.now() / 1000,
total_readings: 1250,
capabilities: {
monitoring: ['energy'],
actions: [],
},
metadata: {
location: 'Wall mounted',
model: 'EM-100',
firmware: '2.1.0',
},
} as SensorDevice,
{
id: 'sensor_2',
sensor_id: 'sensor_2',
name: 'HVAC Controller 1',
type: 'hvac',
sensor_type: 'hvac',
room: 'Conference Room A',
status: 'online',
lastSeen: Date.now() / 1000,
total_readings: 890,
capabilities: {
monitoring: ['temperature', 'co2'],
actions: [
{
id: 'temp_adjust',
name: 'Adjust Temperature',
type: 'adjust',
icon: '🌡️',
parameters: { min: 18, max: 28, step: 0.5 },
},
{
id: 'fan_speed',
name: 'Fan Speed',
type: 'adjust',
icon: '💨',
parameters: { min: 0, max: 5, step: 1 },
},
{ id: 'power_toggle', name: 'Power', type: 'toggle', icon: '⚡' },
],
},
metadata: {
location: 'Ceiling mounted',
model: 'HVAC-200',
firmware: '3.2.1',
},
} as SensorDevice,
{
id: 'sensor_3',
sensor_id: 'sensor_3',
name: 'Smart Light Controller',
type: 'lighting',
sensor_type: 'lighting',
room: 'Office Floor 1',
status: 'online',
lastSeen: Date.now() / 1000,
total_readings: 2100,
capabilities: {
monitoring: ['energy'],
actions: [
{
id: 'brightness',
name: 'Brightness',
type: 'adjust',
icon: '💡',
parameters: { min: 0, max: 100, step: 5 },
},
{ id: 'power_toggle', name: 'Power', type: 'toggle', icon: '⚡' },
{
id: 'scene',
name: 'Scene',
type: 'adjust',
icon: '🎨',
parameters: { options: ['Work', 'Meeting', 'Presentation', 'Relax'] },
},
],
},
metadata: {
location: 'Ceiling grid',
model: 'SL-300',
firmware: '1.5.2',
},
} as SensorDevice,
{
id: 'sensor_4',
sensor_id: 'sensor_4',
name: 'CO2 Sensor',
type: 'co2',
sensor_type: 'co2',
room: 'Meeting Room 1',
status: 'online',
lastSeen: Date.now() / 1000,
total_readings: 1580,
capabilities: {
monitoring: ['co2', 'temperature', 'humidity'],
actions: [{ id: 'calibrate', name: 'Calibrate', type: 'trigger', icon: '⚙️' }],
},
metadata: {
location: 'Wall mounted',
model: 'CO2-150',
firmware: '2.0.3',
battery: 85,
},
} as SensorDevice,
{
id: 'sensor_5',
sensor_id: 'sensor_5',
name: 'Security Camera',
type: 'security',
sensor_type: 'security',
room: 'Lobby',
status: 'online',
lastSeen: Date.now() / 1000,
total_readings: 945,
capabilities: {
monitoring: ['motion'],
actions: [
{ id: 'record_toggle', name: 'Recording', type: 'toggle', icon: '📹' },
{ id: 'ptz_control', name: 'Pan/Tilt/Zoom', type: 'trigger', icon: '🎥' },
{ id: 'night_mode', name: 'Night Mode', type: 'toggle', icon: '🌙' },
],
},
metadata: {
location: 'Corner ceiling',
model: 'SEC-400',
firmware: '4.1.0',
},
} as SensorDevice,
]
mockSensors.forEach((sensor) => {
sensorDevices.set(sensor.id, sensor)
})
}
// Initialize on store creation
initializeMockSensors()
return {
// State
sensorDevices,
latestReadings,
sensorsData,
apiLoading,
apiError,
// Actions
updateSensorRoom,
executeSensorAction,
getSensorsByRoom,
getSensorsByType,
updateSensorData,
updateLatestReading,
// API functions
fetchApiSensors,
fetchApiSensorData,
updateApiSensorMetadata,
deleteApiSensor,
exportApiData,
}
})