Refactor API and store logic for sensor and room management

- Add sensor name to SensorInfo interface - Update API client to
conditionally include auth headers - Add saveToken endpoint to authApi -
Refactor roomsApi to use getRoomNames endpoint - Change sensorsApi to
use /api/v1/sensors/get - Improve token handling and JWT decoding in
auth store - Refactor room loading to use API client in energy store -
Add helper to transform API sensor data - Update SensorManagementView to
load sensors from API and fix filtering
This commit is contained in:
rafaeldpsilva
2025-09-25 14:48:48 +01:00
parent 6510468768
commit 326746b5ef
7 changed files with 445 additions and 369 deletions

View File

@@ -76,6 +76,7 @@ export interface SensorReading {
}
export interface SensorInfo {
name: string
sensor_id: string
sensor_type: SensorType
room?: string
@@ -211,13 +212,49 @@ class ApiClient {
this.baseUrl = baseUrl
}
private getAuthHeaders(): Record<string, string> {
private shouldIncludeAuth(endpoint: string): boolean {
// Endpoints that do NOT require authentication
const publicEndpoints = [
'/health',
'/api/v1/tokens/generate',
'/api/v1/tokens/validate',
'/api/v1/ingestion/',
]
// Special case: token save, revoke operations might need auth depending on backend implementation
// For now, let's include auth for them to be safe
return !publicEndpoints.some((publicPath) => endpoint.startsWith(publicPath))
}
private getAuthHeaders(endpoint: string): Record<string, string> {
// Only include auth headers for endpoints that require authentication
if (!this.shouldIncludeAuth(endpoint)) {
return {}
}
// Dynamically get auth headers to avoid circular imports
try {
// Try to get from window first (for when store is exposed)
const authStore = (window as any).__AUTH_STORE__
if (authStore && typeof authStore.getAuthHeader === 'function') {
return authStore.getAuthHeader()
}
// Fallback: try to access the auth store directly
// This requires the auth store to be initialized
const token = localStorage.getItem('dashboard_auth_token')
if (token) {
// Check if token is still valid
const expiry = localStorage.getItem('dashboard_token_expiry')
if (expiry) {
const expiryTime = new Date(expiry).getTime()
const currentTime = new Date().getTime()
if (currentTime < expiryTime) {
return { Authorization: `Bearer ${token}` }
}
}
}
} catch (error) {
console.warn('Could not get auth headers:', error)
}
@@ -227,7 +264,7 @@ class ApiClient {
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const url = `${this.baseUrl}${endpoint}`
const authHeaders = this.getAuthHeaders()
const authHeaders = this.getAuthHeaders(endpoint)
const config: RequestInit = {
headers: {
'Content-Type': 'application/json',
@@ -270,7 +307,7 @@ class ApiClient {
})
}
const authHeaders = this.getAuthHeaders()
const authHeaders = this.getAuthHeaders(endpoint)
const response = await fetch(url.toString(), {
method: 'GET',
headers: {

View File

@@ -1,18 +1,16 @@
/**
* Authentication API Service
* Handles JWT token generation and validation
*/
import { apiClient } from './api'
export interface TokenRequest {
name: string
list_of_resources: string[]
data_aggregation?: boolean
time_aggregation?: boolean
embargo?: number
exp_hours?: number
}
export interface TokenResponse {
token: string
expires_at: string
resources: string[]
}
export interface TokenValidation {
@@ -26,6 +24,10 @@ export const authApi = {
return apiClient.post<TokenResponse>('/api/v1/tokens/generate', request)
},
async saveToken(token: string): Promise<{ token: string; datetime: string; active: boolean }> {
return apiClient.post<{ token: string; datetime: string; active: boolean }>('/api/v1/tokens/save', { token })
},
async validateToken(token: string): Promise<TokenValidation> {
return apiClient.post<TokenValidation>('/api/v1/tokens/validate', { token })
},

View File

@@ -1,20 +1,14 @@
/**
* Rooms API Service
* Handles room-related API calls
*/
import { apiClient, type RoomInfo, type RoomData } from './api'
export const roomsApi = {
/**
* Get list of all rooms with sensor counts and latest metrics
*/
async getRoomNames(): Promise<{ rooms: string[] }> {
return apiClient.get<{ rooms: string[] }>('/api/v1/rooms/names')
},
async getRooms(): Promise<RoomInfo[]> {
return apiClient.get<RoomInfo[]>('/api/v1/rooms')
},
/**
* Get historical data for a specific room
*/
async getRoomData(
roomName: string,
params?: {

View File

@@ -14,7 +14,7 @@ export const sensorsApi = {
sensor_type?: SensorType
status?: SensorStatus
}): Promise<SensorInfo[]> {
return apiClient.get<SensorInfo[]>('/api/v1/sensors', params)
return apiClient.get<SensorInfo[]>('/api/v1/sensors/get', params)
},
async getSensor(sensorId: string): Promise<SensorInfo> {

View File

@@ -1,6 +1,24 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { authApi, type TokenRequest, type TokenResponse } from '@/services/authApi'
import { authApi, type TokenRequest } from '@/services/authApi'
// Helper function to decode JWT token payload (without verification)
function decodeJwtPayload(token: string) {
try {
const base64Url = token.split('.')[1]
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
const jsonPayload = decodeURIComponent(
atob(base64)
.split('')
.map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join(''),
)
return JSON.parse(jsonPayload)
} catch (error) {
console.error('Failed to decode JWT payload:', error)
return null
}
}
const TOKEN_STORAGE_KEY = 'dashboard_auth_token'
const TOKEN_EXPIRY_KEY = 'dashboard_token_expiry'
@@ -53,30 +71,35 @@ export const useAuthStore = defineStore('auth', () => {
try {
const request: TokenRequest = {
name,
list_of_resources: [
'sensors',
'rooms',
'analytics',
'health',
'data',
'export',
'events'
]
list_of_resources: ['sensors', 'rooms', 'analytics', 'health', 'data', 'export', 'events'],
data_aggregation: true,
time_aggregation: true,
embargo: 0,
exp_hours: 24,
}
const response = await authApi.generateToken(request)
// Save token to backend database immediately after generation
await authApi.saveToken(response.token)
// Decode the JWT to get expiration and resources
const payload = decodeJwtPayload(response.token)
if (!payload) {
throw new Error('Failed to decode token payload')
}
// Store token data
token.value = response.token
tokenExpiry.value = response.expires_at
tokenResources.value = response.resources || request.list_of_resources
tokenExpiry.value = new Date(payload.exp * 1000).toISOString()
tokenResources.value = payload.list_of_resources || request.list_of_resources
// Persist to localStorage
localStorage.setItem(TOKEN_STORAGE_KEY, response.token)
localStorage.setItem(TOKEN_EXPIRY_KEY, response.expires_at)
localStorage.setItem(TOKEN_EXPIRY_KEY, tokenExpiry.value)
localStorage.setItem(TOKEN_RESOURCES_KEY, JSON.stringify(tokenResources.value))
console.log('Authentication successful, token expires at:', response.expires_at)
console.log('Authentication successful, token expires at:', tokenExpiry.value)
return true
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to generate token'
@@ -123,7 +146,7 @@ export const useAuthStore = defineStore('auth', () => {
localStorage.removeItem(TOKEN_RESOURCES_KEY)
}
function loadTokenFromStorage() {
async function loadTokenFromStorage() {
const storedToken = localStorage.getItem(TOKEN_STORAGE_KEY)
const storedExpiry = localStorage.getItem(TOKEN_EXPIRY_KEY)
const storedResources = localStorage.getItem(TOKEN_RESOURCES_KEY)
@@ -159,7 +182,7 @@ export const useAuthStore = defineStore('auth', () => {
async function ensureAuthenticated(): Promise<boolean> {
// Try to load from storage first
if (loadTokenFromStorage() && isAuthenticated.value) {
if ((await loadTokenFromStorage()) && isAuthenticated.value) {
return true
}
@@ -175,8 +198,10 @@ export const useAuthStore = defineStore('auth', () => {
return {}
}
// Initialize on store creation
loadTokenFromStorage()
// Initialize on store creation (async)
loadTokenFromStorage().catch(error => {
console.warn('Failed to load token from storage:', error)
})
return {
// State
@@ -196,6 +221,6 @@ export const useAuthStore = defineStore('auth', () => {
clearToken,
loadTokenFromStorage,
ensureAuthenticated,
getAuthHeader
getAuthHeader,
}
})
})

View File

@@ -207,7 +207,10 @@ export const useEnergyStore = defineStore('energy', () => {
const data = newDataBuffer.shift() // Get the oldest data point
if (data) {
// Skip non-data messages (connection establishment, proxy info, etc.)
if (data.type && (data.type === 'connection_established' || data.type === 'proxy_info')) {
if (
'type' in data &&
(data.type === 'connection_established' || data.type === 'proxy_info')
) {
console.log('Received system message:', data.type)
return
}
@@ -339,6 +342,331 @@ export const useEnergyStore = defineStore('energy', () => {
return 'critical'
}
// Sensor management functions
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
// 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) => {
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)
}
// Room management functions
const loadRoomsFromAPI = async (): Promise<void> => {
if (roomsLoading.value || roomsLoaded.value) {
return // Already loading or loaded
}
roomsLoading.value = true
try {
// Use the API client which handles authentication properly
const data = await roomsApi.getRoomNames()
if (data.rooms && Array.isArray(data.rooms)) {
availableRooms.value = data.rooms.sort()
roomsLoaded.value = true
return
}
// If no rooms found, use empty list
console.warn('No rooms found in API response, starting with empty list')
availableRooms.value = []
roomsLoaded.value = true
} catch (error) {
console.error('Error loading rooms:', error)
// Start with empty list on error
availableRooms.value = []
roomsLoaded.value = true
} finally {
roomsLoading.value = false
}
}
function addRoom(roomName: string): boolean {
if (!roomName.trim()) return false
// Check if room already exists
if (availableRooms.value.includes(roomName.trim())) {
return false
}
// Add room to available rooms list
availableRooms.value.push(roomName.trim())
availableRooms.value.sort() // Keep rooms sorted alphabetically
console.log(`Added new room: ${roomName}`)
return true
}
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,
)
if (sensorsInRoom.length > 0) {
// Reassign sensors to 'Unassigned'
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
}
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))],
hasMetrics: !!roomMetrics,
energyConsumption: roomMetrics?.energy.current || 0,
co2Level: roomMetrics?.co2.current || 0,
lastUpdated: roomMetrics?.lastUpdated || null,
}
}
function getAllRoomsWithStats() {
return availableRooms.value.map((room) => ({
name: room,
...getRoomStats(room),
}))
}
// 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 to get fresh auth token
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...')
// Retry the original 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
}
}
// Helper function to transform API sensor to expected format
function transformApiSensor(apiSensor: any) {
return {
id: apiSensor.sensor_id,
sensor_id: apiSensor.sensor_id,
name: apiSensor.name || apiSensor.sensor_id,
type: apiSensor.sensor_type,
sensor_type: apiSensor.sensor_type,
room: apiSensor.room,
status: apiSensor.status === 'active' ? 'online' : apiSensor.status,
location: apiSensor.location,
// Add capabilities based on sensor type
capabilities: {
monitoring: [apiSensor.sensor_type],
actions: [], // API sensors don't have actions yet
},
// Add metadata
metadata: {
created_at: apiSensor.created_at,
updated_at: apiSensor.updated_at,
manufacturer: apiSensor.manufacturer,
model: apiSensor.model,
},
}
}
// Sensors API functions
async function fetchApiSensors(params?: { room?: string; sensor_type?: any; status?: any }) {
const result = await handleApiCall(() => sensorsApi.getSensors(params))
if (result.sensors) {
result.sensors.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))
}
// Rooms API functions
async function fetchApiRooms() {
const result = await handleApiCall(() => roomsApi.getRooms())
if (result) {
apiRooms.value = result
// Update available rooms from API data
const roomNames = result.map((room) => room.room).filter((name) => name)
if (roomNames.length > 0) {
availableRooms.value = [...new Set([...availableRooms.value, ...roomNames])].sort()
}
}
return result
}
async function fetchApiRoomData(
roomName: string,
params?: { start_time?: number; end_time?: number; limit?: number },
) {
return handleApiCall(() => roomsApi.getRoomData(roomName, params))
}
// Analytics API functions
async function fetchAnalyticsSummary(hours: number = 24) {
const result = await handleApiCall(() => analyticsApi.getAnalyticsSummary(hours))
if (result) {
analyticsData.value.summary = result
}
return result
}
async function fetchEnergyTrends(hours: number = 168) {
const result = await handleApiCall(() => analyticsApi.getEnergyTrends(hours))
if (result) {
analyticsData.value.trends = result
}
return result
}
async function fetchRoomComparison(hours: number = 24) {
const result = await handleApiCall(() => analyticsApi.getRoomComparison(hours))
if (result) {
analyticsData.value.roomComparison = result
}
return result
}
async function fetchSystemEvents(params?: {
severity?: string
event_type?: string
hours?: number
limit?: number
}) {
return handleApiCall(() => analyticsApi.getEvents(params))
}
// Health API functions
async function fetchSystemStatus() {
const result = await handleApiCall(() => healthApi.getStatus())
if (result) {
systemStatus.value = result
}
return result
}
async function fetchHealthStatus() {
const result = await handleApiCall(() => healthApi.getHealth())
if (result) {
healthStatus.value = result
}
return result
}
// Initialize data from APIs
async function initializeFromApi() {
await Promise.allSettled([
loadRoomsFromAPI(), // Load room names first
fetchApiSensors(),
fetchApiRooms(),
fetchAnalyticsSummary(),
fetchSystemStatus(),
fetchHealthStatus(),
])
}
// Initialize mock sensors on store creation
// Initialize mock sensor devices
function initializeMockSensors() {
const mockSensors: SensorDevice[] = [
@@ -470,322 +798,7 @@ export const useEnergyStore = defineStore('energy', () => {
sensorDevices.set(sensor.id, sensor)
})
}
// Sensor management functions
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
// 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) => {
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)
}
// Room management functions
const loadRoomsFromAPI = async (): Promise<void> => {
if (roomsLoading.value || roomsLoaded.value) {
return // Already loading or loaded
}
roomsLoading.value = true
try {
// Try to load from microservices API first
const response = await fetch('/api/v1/rooms/names')
if (response.ok) {
const data = await response.json()
if (data.rooms && Array.isArray(data.rooms)) {
availableRooms.value = data.rooms.sort()
roomsLoaded.value = true
console.log('Loaded rooms from microservices API:', data.rooms.length)
return
}
}
// Fallback: try direct sensor service connection
const directResponse = await fetch('http://localhost:8007/rooms/names')
if (directResponse.ok) {
const data = await directResponse.json()
if (data.rooms && Array.isArray(data.rooms)) {
availableRooms.value = data.rooms.sort()
roomsLoaded.value = true
console.log('Loaded rooms from sensor service:', data.rooms.length)
return
}
}
// If both fail, use empty list and log warning
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
availableRooms.value = []
roomsLoaded.value = true
} finally {
roomsLoading.value = false
}
}
function addRoom(roomName: string): boolean {
if (!roomName.trim()) return false
// Check if room already exists
if (availableRooms.value.includes(roomName.trim())) {
return false
}
// Add room to available rooms list
availableRooms.value.push(roomName.trim())
availableRooms.value.sort() // Keep rooms sorted alphabetically
console.log(`Added new room: ${roomName}`)
return true
}
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,
)
if (sensorsInRoom.length > 0) {
// Reassign sensors to 'Unassigned'
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
}
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))],
hasMetrics: !!roomMetrics,
energyConsumption: roomMetrics?.energy.current || 0,
co2Level: roomMetrics?.co2.current || 0,
lastUpdated: roomMetrics?.lastUpdated || null,
}
}
function getAllRoomsWithStats() {
return availableRooms.value.map((room) => ({
name: room,
...getRoomStats(room),
}))
}
// 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 to get fresh auth token
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...')
// Retry the original 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) {
apiSensors.value = result
}
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))
}
// Rooms API functions
async function fetchApiRooms() {
const result = await handleApiCall(() => roomsApi.getRooms())
if (result) {
apiRooms.value = result
// Update available rooms from API data
const roomNames = result.map((room) => room.room).filter((name) => name)
if (roomNames.length > 0) {
availableRooms.value = [...new Set([...availableRooms.value, ...roomNames])].sort()
}
}
return result
}
async function fetchApiRoomData(
roomName: string,
params?: { start_time?: number; end_time?: number; limit?: number },
) {
return handleApiCall(() => roomsApi.getRoomData(roomName, params))
}
// Analytics API functions
async function fetchAnalyticsSummary(hours: number = 24) {
const result = await handleApiCall(() => analyticsApi.getAnalyticsSummary(hours))
if (result) {
analyticsData.value.summary = result
}
return result
}
async function fetchEnergyTrends(hours: number = 168) {
const result = await handleApiCall(() => analyticsApi.getEnergyTrends(hours))
if (result) {
analyticsData.value.trends = result
}
return result
}
async function fetchRoomComparison(hours: number = 24) {
const result = await handleApiCall(() => analyticsApi.getRoomComparison(hours))
if (result) {
analyticsData.value.roomComparison = result
}
return result
}
async function fetchSystemEvents(params?: {
severity?: string
event_type?: string
hours?: number
limit?: number
}) {
return handleApiCall(() => analyticsApi.getEvents(params))
}
// Health API functions
async function fetchSystemStatus() {
const result = await handleApiCall(() => healthApi.getStatus())
if (result) {
systemStatus.value = result
}
return result
}
async function fetchHealthStatus() {
const result = await handleApiCall(() => healthApi.getHealth())
if (result) {
healthStatus.value = result
}
return result
}
// Initialize data from APIs
async function initializeFromApi() {
await Promise.allSettled([
loadRoomsFromAPI(), // Load room names first
fetchApiSensors(),
fetchApiRooms(),
fetchAnalyticsSummary(),
fetchSystemStatus(),
fetchHealthStatus(),
])
}
// Initialize mock sensors on store creation
initializeMockSensors()
//initializeMockSensors()
// Load rooms from API on store initialization
loadRoomsFromAPI()

View File

@@ -24,15 +24,6 @@
<!-- Filters -->
<div class="flex flex-col sm:flex-row gap-4 flex-1">
<div class="flex gap-2">
<select
v-model="selectedRoom"
class="px-4 py-2 border border-gray-200 rounded-lg bg-white flex-1"
>
<option value="">All Rooms</option>
<option v-for="room in energyStore.availableRooms" :key="room" :value="room">
{{ room }}
</option>
</select>
<button
@click="showRoomManagementModal = true"
class="px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-medium transition-colors flex items-center gap-1"
@@ -48,6 +39,15 @@
</svg>
<span class="hidden sm:inline">Rooms</span>
</button>
<select
v-model="selectedRoom"
class="px-4 py-2 border border-gray-200 rounded-lg bg-white flex-1"
>
<option value="">All Rooms</option>
<option v-for="room in energyStore.availableRooms" :key="room" :value="room">
{{ room }}
</option>
</select>
</div>
<select v-model="selectedType" class="px-4 py-2 border border-gray-200 rounded-lg bg-white">
@@ -241,13 +241,14 @@ const isExecutingAction = ref(false)
const showRoomManagementModal = ref(false)
const sensorList = computed(() => {
console.log(energyStore.sensorDevices)
return Array.from(energyStore.sensorDevices.values()).sort((a, b) => a.name.localeCompare(b.name))
})
const filteredSensors = computed(() => {
return sensorList.value.filter((sensor) => {
const matchesRoom = !selectedRoom.value || sensor.room === selectedRoom.value
const matchesType = !selectedType.value || sensor.type === selectedType.value
const matchesType = !selectedType.value || sensor.sensor_type === selectedType.value
const matchesStatus = !selectedStatus.value || sensor.status === selectedStatus.value
return matchesRoom && matchesType && matchesStatus
@@ -362,8 +363,12 @@ const getOccupancyColor = (occupancy: string) => {
}
}
// WebSocket connection for real-time updates
onMounted(() => {
// Load sensors from API and connect WebSocket for real-time updates
onMounted(async () => {
// First load sensors from database
await energyStore.fetchApiSensors()
// Then connect WebSocket for real-time updates
if (!energyStore.isConnected) {
energyStore.connect('ws://localhost:8000/ws')
}