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