diff --git a/src/services/api.ts b/src/services/api.ts index 1094753..6d97559 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,8 +1,3 @@ -/** - * API Service Layer for Energy Monitoring Dashboard - * Handles all backend API communications - */ - // Base configuration const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000' diff --git a/src/services/authApi.ts b/src/services/authApi.ts index 341e7e3..d75db17 100644 --- a/src/services/authApi.ts +++ b/src/services/authApi.ts @@ -22,26 +22,17 @@ export interface TokenValidation { } export const authApi = { - /** - * Generate a new JWT token for the dashboard - */ async generateToken(request: TokenRequest): Promise { return apiClient.post('/api/v1/tokens/generate', request) }, - /** - * Validate an existing token - */ async validateToken(token: string): Promise { return apiClient.post('/api/v1/tokens/validate', { token }) }, - /** - * Revoke a token - */ async revokeToken(token: string): Promise<{ message: string }> { return apiClient.post<{ message: string }>('/api/v1/tokens/revoke', { token }) - } + }, } -export default authApi \ No newline at end of file +export default authApi diff --git a/src/services/sensorsApi.ts b/src/services/sensorsApi.ts index bfc68d3..8850267 100644 --- a/src/services/sensorsApi.ts +++ b/src/services/sensorsApi.ts @@ -1,7 +1,3 @@ -/** - * Sensors API Service - * Handles sensor-related API calls - */ import { apiClient, type SensorInfo, @@ -13,9 +9,6 @@ import { } from './api' export const sensorsApi = { - /** - * Get all sensors with optional filtering - */ async getSensors(params?: { room?: string sensor_type?: SensorType @@ -24,16 +17,10 @@ export const sensorsApi = { return apiClient.get('/api/v1/sensors', params) }, - /** - * Get detailed information about a specific sensor - */ async getSensor(sensorId: string): Promise { return apiClient.get(`/api/v1/sensors/${sensorId}`) }, - /** - * Get historical data for a specific sensor - */ async getSensorData( sensorId: string, params?: { @@ -46,16 +33,10 @@ export const sensorsApi = { return apiClient.get(`/api/v1/sensors/${sensorId}/data`, params) }, - /** - * Advanced data query with multiple filters - */ async queryData(query: DataQuery): Promise { return apiClient.post('/api/v1/data/query', query) }, - /** - * Update sensor metadata - */ async updateSensorMetadata( sensorId: string, metadata: Record, @@ -63,9 +44,6 @@ export const sensorsApi = { return apiClient.put<{ message: string }>(`/api/v1/sensors/${sensorId}/metadata`, metadata) }, - /** - * Delete sensor and all its data - */ async deleteSensor(sensorId: string): Promise<{ message: string readings_deleted: number @@ -74,9 +52,6 @@ export const sensorsApi = { return apiClient.delete(`/api/v1/sensors/${sensorId}`) }, - /** - * Export sensor data for specified time range - */ async exportData(params: { start_time: number end_time: number diff --git a/src/stores/energy.ts b/src/stores/energy.ts index 2abfeff..ee4e324 100644 --- a/src/stores/energy.ts +++ b/src/stores/energy.ts @@ -1,9 +1,9 @@ import { defineStore } from 'pinia' import { ref, reactive } from 'vue' -import { - sensorsApi, - roomsApi, - analyticsApi, +import { + sensorsApi, + roomsApi, + analyticsApi, healthApi, type SensorInfo as ApiSensorInfo, type RoomInfo as ApiRoomInfo, @@ -11,7 +11,7 @@ import { type EnergyTrends, type RoomComparison, type SystemStatus, - type HealthCheck + type HealthCheck, } from '@/services' const MAX_DATA_POINTS = 100 // Keep the last 100 data points for the chart @@ -104,7 +104,7 @@ export const useEnergyStore = defineStore('energy', () => { labels: [], datasets: [{ data: [] }], }) - + const sensorsData = reactive>(new Map()) // Legacy support const roomsData = reactive>(new Map()) const latestReadings = reactive>(new Map()) @@ -123,7 +123,7 @@ export const useEnergyStore = defineStore('energy', () => { }>({ summary: null, trends: null, - roomComparison: null + roomComparison: null, }) const systemStatus = ref(null) const healthStatus = ref(null) @@ -252,18 +252,18 @@ export const useEnergyStore = defineStore('energy', () => { function updateSensorData(data: LegacyEnergyData) { const existingSensor = sensorsData.get(data.sensorId) - + if (existingSensor) { // Update existing sensor const newTotal = existingSensor.totalConsumption + data.value const dataPoints = Math.floor((data.timestamp - existingSensor.lastUpdated) / 60) + 1 // Rough estimate - + sensorsData.set(data.sensorId, { ...existingSensor, latestValue: data.value, totalConsumption: newTotal, averageConsumption: newTotal / dataPoints, - lastUpdated: data.timestamp + lastUpdated: data.timestamp, }) } else { // Create new sensor entry @@ -273,7 +273,7 @@ export const useEnergyStore = defineStore('energy', () => { totalConsumption: data.value, averageConsumption: data.value, lastUpdated: data.timestamp, - unit: data.unit + unit: data.unit, }) } } @@ -281,10 +281,10 @@ export const useEnergyStore = defineStore('energy', () => { 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, @@ -292,7 +292,7 @@ export const useEnergyStore = defineStore('energy', () => { 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 + lastUpdated: data.timestamp, } roomsData.set(data.room, roomMetrics) } @@ -303,15 +303,17 @@ export const useEnergyStore = defineStore('energy', () => { } // Recalculate room metrics from all sensors in the room - const roomSensors = Array.from(latestReadings.values()).filter(reading => reading.room === data.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) + 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 @@ -349,13 +351,13 @@ export const useEnergyStore = defineStore('energy', () => { lastSeen: Date.now() / 1000, capabilities: { monitoring: ['energy'], - actions: [] + actions: [], }, metadata: { location: 'Wall mounted', model: 'EM-100', - firmware: '2.1.0' - } + firmware: '2.1.0', + }, }, { id: 'sensor_2', @@ -367,16 +369,28 @@ export const useEnergyStore = defineStore('energy', () => { 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: '⚡' } - ] + { + 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' - } + firmware: '3.2.1', + }, }, { id: 'sensor_3', @@ -388,16 +402,28 @@ export const useEnergyStore = defineStore('energy', () => { capabilities: { monitoring: ['energy'], actions: [ - { id: 'brightness', name: 'Brightness', type: 'adjust', icon: '💡', parameters: { min: 0, max: 100, step: 5 } }, + { + 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'] } } - ] + { + id: 'scene', + name: 'Scene', + type: 'adjust', + icon: '🎨', + parameters: { options: ['Work', 'Meeting', 'Presentation', 'Relax'] }, + }, + ], }, metadata: { location: 'Ceiling grid', model: 'SL-300', - firmware: '1.5.2' - } + firmware: '1.5.2', + }, }, { id: 'sensor_4', @@ -408,16 +434,14 @@ export const useEnergyStore = defineStore('energy', () => { lastSeen: Date.now() / 1000, capabilities: { monitoring: ['co2', 'temperature', 'humidity'], - actions: [ - { id: 'calibrate', name: 'Calibrate', type: 'trigger', icon: '⚙️' } - ] + actions: [{ id: 'calibrate', name: 'Calibrate', type: 'trigger', icon: '⚙️' }], }, metadata: { location: 'Wall mounted', model: 'CO2-150', firmware: '2.0.3', - battery: 85 - } + battery: 85, + }, }, { id: 'sensor_5', @@ -431,18 +455,18 @@ export const useEnergyStore = defineStore('energy', () => { 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: '🌙' } - ] + { id: 'night_mode', name: 'Night Mode', type: 'toggle', icon: '🌙' }, + ], }, metadata: { location: 'Corner ceiling', model: 'SEC-400', - firmware: '4.1.0' - } - } + firmware: '4.1.0', + }, + }, ] - mockSensors.forEach(sensor => { + mockSensors.forEach((sensor) => { sensorDevices.set(sensor.id, sensor) }) } @@ -460,12 +484,12 @@ export const useEnergyStore = defineStore('energy', () => { const sensor = sensorDevices.get(sensorId) if (!sensor) return false - const action = sensor.capabilities.actions.find(a => a.id === actionId) + const action = sensor.capabilities.actions.find((a) => a.id === actionId) if (!action) return false // Simulate API call to device console.log(`Executing action ${actionId} on sensor ${sensorId}`, parameters) - + // Here you would make the actual API call to control the device // For now, we'll simulate a successful action return new Promise((resolve) => { @@ -477,11 +501,11 @@ export const useEnergyStore = defineStore('energy', () => { } function getSensorsByRoom(room: string): SensorDevice[] { - return Array.from(sensorDevices.values()).filter(sensor => sensor.room === room) + 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) + return Array.from(sensorDevices.values()).filter((sensor) => sensor.type === type) } // Room management functions @@ -523,7 +547,6 @@ export const useEnergyStore = defineStore('energy', () => { console.warn('Failed to load rooms from API, starting with empty list') availableRooms.value = [] roomsLoaded.value = true - } catch (error) { console.error('Error loading rooms:', error) // Start with empty list on error @@ -553,23 +576,25 @@ export const useEnergyStore = defineStore('energy', () => { function removeRoom(roomName: string): boolean { const index = availableRooms.value.indexOf(roomName) if (index === -1) return false - + // Check if any sensors are assigned to this room - const sensorsInRoom = Array.from(sensorDevices.values()).filter(sensor => sensor.room === roomName) + const sensorsInRoom = Array.from(sensorDevices.values()).filter( + (sensor) => sensor.room === roomName, + ) if (sensorsInRoom.length > 0) { // Reassign sensors to 'Unassigned' - sensorsInRoom.forEach(sensor => { + sensorsInRoom.forEach((sensor) => { sensor.room = '' sensorDevices.set(sensor.id, { ...sensor }) }) } - + // Remove room data roomsData.delete(roomName) - + // Remove from available rooms availableRooms.value.splice(index, 1) - + console.log(`Removed room: ${roomName}`) return true } @@ -577,21 +602,21 @@ export const useEnergyStore = defineStore('energy', () => { function getRoomStats(roomName: string) { const sensorsInRoom = getSensorsByRoom(roomName) const roomMetrics = roomsData.get(roomName) - + return { sensorCount: sensorsInRoom.length, - sensorTypes: [...new Set(sensorsInRoom.map(s => s.type))], + sensorTypes: [...new Set(sensorsInRoom.map((s) => s.type))], hasMetrics: !!roomMetrics, energyConsumption: roomMetrics?.energy.current || 0, co2Level: roomMetrics?.co2.current || 0, - lastUpdated: roomMetrics?.lastUpdated || null + lastUpdated: roomMetrics?.lastUpdated || null, } } function getAllRoomsWithStats() { - return availableRooms.value.map(room => ({ + return availableRooms.value.map((room) => ({ name: room, - ...getRoomStats(room) + ...getRoomStats(room), })) } @@ -606,7 +631,6 @@ export const useEnergyStore = defineStore('energy', () => { } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' - // Handle authentication errors if (errorMessage.includes('401') || errorMessage.includes('Authorization')) { console.warn('Authentication error detected, attempting to re-authenticate...') @@ -622,7 +646,8 @@ export const useEnergyStore = defineStore('energy', () => { const retryResult = await apiCall() return retryResult } catch (retryError) { - const retryErrorMessage = retryError instanceof Error ? retryError.message : 'Retry failed' + const retryErrorMessage = + retryError instanceof Error ? retryError.message : 'Retry failed' apiError.value = retryErrorMessage console.error('API retry failed:', retryErrorMessage) return null @@ -652,8 +677,8 @@ export const useEnergyStore = defineStore('energy', () => { } async function fetchApiSensorData( - sensorId: string, - params?: { start_time?: number; end_time?: number; limit?: number; offset?: number } + sensorId: string, + params?: { start_time?: number; end_time?: number; limit?: number; offset?: number }, ) { return handleApiCall(() => sensorsApi.getSensorData(sensorId, params)) } @@ -681,7 +706,7 @@ export const useEnergyStore = defineStore('energy', () => { if (result) { apiRooms.value = result // Update available rooms from API data - const roomNames = result.map(room => room.room).filter(name => name) + const roomNames = result.map((room) => room.room).filter((name) => name) if (roomNames.length > 0) { availableRooms.value = [...new Set([...availableRooms.value, ...roomNames])].sort() } @@ -690,8 +715,8 @@ export const useEnergyStore = defineStore('energy', () => { } async function fetchApiRoomData( - roomName: string, - params?: { start_time?: number; end_time?: number; limit?: number } + roomName: string, + params?: { start_time?: number; end_time?: number; limit?: number }, ) { return handleApiCall(() => roomsApi.getRoomData(roomName, params)) } @@ -750,12 +775,12 @@ export const useEnergyStore = defineStore('energy', () => { // Initialize data from APIs async function initializeFromApi() { await Promise.allSettled([ - loadRoomsFromAPI(), // Load room names first + loadRoomsFromAPI(), // Load room names first fetchApiSensors(), fetchApiRooms(), fetchAnalyticsSummary(), fetchSystemStatus(), - fetchHealthStatus() + fetchHealthStatus(), ]) } @@ -765,19 +790,19 @@ export const useEnergyStore = defineStore('energy', () => { // Load rooms from API on store initialization loadRoomsFromAPI() - return { + return { // WebSocket state - isConnected, - latestMessage, - timeSeriesData, - sensorsData, + isConnected, + latestMessage, + timeSeriesData, + sensorsData, roomsData, latestReadings, sensorDevices, availableRooms, roomsLoading, roomsLoaded, - + // API state apiSensors, apiRooms, @@ -788,7 +813,7 @@ export const useEnergyStore = defineStore('energy', () => { apiError, // WebSocket functions - connect, + connect, disconnect, getCO2Status, updateSensorRoom, @@ -815,6 +840,6 @@ export const useEnergyStore = defineStore('energy', () => { fetchSystemEvents, fetchSystemStatus, fetchHealthStatus, - initializeFromApi + initializeFromApi, } }) diff --git a/src/stores/settings.ts b/src/stores/settings.ts index 33f5977..bbd6c2d 100644 --- a/src/stores/settings.ts +++ b/src/stores/settings.ts @@ -58,21 +58,17 @@ const DEFAULT_SETTINGS: AppSettings = { } export const useSettingsStore = defineStore('settings', () => { - // State const settings = reactive({ ...DEFAULT_SETTINGS }) const isLoading = ref(false) const lastSaved = ref(null) - // Local storage key const STORAGE_KEY = 'dashboard-settings' - // Load settings from localStorage function loadSettings() { try { const stored = localStorage.getItem(STORAGE_KEY) if (stored) { const parsed = JSON.parse(stored) - // Merge with defaults to handle new settings Object.assign(settings, { ...DEFAULT_SETTINGS, ...parsed }) console.log('Settings loaded from localStorage') } @@ -82,7 +78,6 @@ export const useSettingsStore = defineStore('settings', () => { } } - // Save settings to localStorage function saveSettings() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)) @@ -93,13 +88,11 @@ export const useSettingsStore = defineStore('settings', () => { } } - // Reset to default settings function resetToDefaults() { Object.assign(settings, DEFAULT_SETTINGS) saveSettings() } - // Update specific setting function updateSetting(path: string, value: any) { const keys = path.split('.') let current: any = settings @@ -112,7 +105,6 @@ export const useSettingsStore = defineStore('settings', () => { saveSettings() } - // Get setting value by path function getSetting(path: string): any { const keys = path.split('.') let current: any = settings @@ -125,16 +117,13 @@ export const useSettingsStore = defineStore('settings', () => { return current } - // Export settings function exportSettings(): string { return JSON.stringify(settings, null, 2) } - // Import settings function importSettings(settingsJson: string): boolean { try { const imported = JSON.parse(settingsJson) - // Validate structure if (typeof imported === 'object' && imported !== null) { Object.assign(settings, { ...DEFAULT_SETTINGS, ...imported }) saveSettings() @@ -147,7 +136,6 @@ export const useSettingsStore = defineStore('settings', () => { } } - // Theme helpers function applyTheme() { const root = document.documentElement @@ -156,13 +144,11 @@ export const useSettingsStore = defineStore('settings', () => { } else if (settings.theme === 'light') { root.classList.remove('dark') } else { - // System theme const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches root.classList.toggle('dark', prefersDark) } } - // WebSocket URL validation function isValidWebSocketUrl(url: string): boolean { try { const parsed = new URL(url) @@ -172,7 +158,6 @@ export const useSettingsStore = defineStore('settings', () => { } } - // Notification permission handling async function requestNotificationPermission(): Promise { if (!('Notification' in window)) { return false @@ -190,21 +175,17 @@ export const useSettingsStore = defineStore('settings', () => { return permission === 'granted' } - // Initialize store function initialize() { loadSettings() applyTheme() - // Watch for theme changes watch(() => settings.theme, applyTheme, { immediate: true }) - // Watch for system theme changes if (settings.theme === 'system') { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') mediaQuery.addEventListener('change', applyTheme) } - // Auto-save on changes (debounced) let saveTimeout: number | undefined watch( settings, @@ -216,7 +197,6 @@ export const useSettingsStore = defineStore('settings', () => { ) } - // Get available languages function getAvailableLanguages() { return [ { code: 'en', name: 'English', nativeName: 'English' }, @@ -226,7 +206,6 @@ export const useSettingsStore = defineStore('settings', () => { ] } - // Get theme options function getThemeOptions() { return [ { value: 'system', label: 'System Default', icon: '🔄' }, @@ -235,7 +214,6 @@ export const useSettingsStore = defineStore('settings', () => { ] } - // Get navigation mode options function getNavigationModeOptions() { return [ { @@ -260,12 +238,10 @@ export const useSettingsStore = defineStore('settings', () => { } return { - // State settings, isLoading, lastSaved, - // Actions loadSettings, saveSettings, resetToDefaults, @@ -278,7 +254,6 @@ export const useSettingsStore = defineStore('settings', () => { requestNotificationPermission, initialize, - // Getters getAvailableLanguages, getThemeOptions, getNavigationModeOptions,