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

@@ -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()