Refactor to decouple energy, room, and sensor stores
- Remove room and sensor logic from energy store - Update components to use useRoomStore and useSensorStore directly - Fix sensor/room ID mismatches and API response handling in room store - Update AIOptimizationView to use useWebSocketStore for connection status - Update SensorManagementView to use useRoomStore and useSensorStore directly
This commit is contained in:
@@ -70,12 +70,12 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useEnergyStore } from '@/stores/energy'
|
||||
import { useRoomStore } from '@/stores/room'
|
||||
|
||||
const energyStore = useEnergyStore()
|
||||
const roomStore = useRoomStore()
|
||||
|
||||
const roomsList = computed(() => {
|
||||
return Array.from(energyStore.roomsData.values()).sort((a, b) =>
|
||||
return Array.from(roomStore.roomsData.values()).sort((a, b) =>
|
||||
b.co2.current - a.co2.current // Sort by CO2 level descending
|
||||
)
|
||||
})
|
||||
@@ -86,7 +86,7 @@ const overallCO2 = computed(() => {
|
||||
})
|
||||
|
||||
const overallStatus = computed(() => {
|
||||
return energyStore.getCO2Status(overallCO2.value)
|
||||
return roomStore.getCO2Status(overallCO2.value)
|
||||
})
|
||||
|
||||
const roomsWithGoodAir = computed(() => {
|
||||
|
||||
@@ -165,7 +165,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useEnergyStore } from '@/stores/energy'
|
||||
import { useSensorStore } from '@/stores/sensor'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -179,28 +178,25 @@ const emit = defineEmits<{
|
||||
executeAction: [sensor: any, action: any]
|
||||
}>()
|
||||
|
||||
const energyStore = useEnergyStore()
|
||||
const sensorStore = useSensorStore()
|
||||
|
||||
const getSensorValues = (sensor: any) => {
|
||||
const values = []
|
||||
|
||||
// Get real-time sensor reading from store
|
||||
const latestReading = energyStore.latestReadings.get(sensor.id)
|
||||
const latestReading = sensorStore.latestReadings.get(sensor.id) || sensorStore.latestReadings.get(sensor.sensor_id)
|
||||
console.log(`[Detailed] Getting values for sensor ${sensor.id}, found reading:`, latestReading)
|
||||
console.log('[Detailed] Available readings:', Array.from(energyStore.latestReadings.keys()))
|
||||
console.log('[Detailed] Available readings:', Array.from(sensorStore.latestReadings.keys()))
|
||||
console.log(`[Detailed] Sensor capabilities:`, sensor.capabilities?.monitoring)
|
||||
|
||||
// Only show energy if the sensor actually monitors energy
|
||||
if (sensor.capabilities?.monitoring?.includes('energy')) {
|
||||
const energyValue = latestReading?.energy?.value?.toFixed(2) ||
|
||||
energyStore.latestMessage?.value?.toFixed(2) ||
|
||||
'0.00'
|
||||
const energyValue = latestReading?.energy?.value?.toFixed(2) || '0.00'
|
||||
values.push({
|
||||
type: 'energy',
|
||||
label: 'Energy Consumption',
|
||||
value: energyValue,
|
||||
unit: latestReading?.energy?.unit || energyStore.latestMessage?.unit || 'kWh'
|
||||
unit: latestReading?.energy?.unit || 'kWh'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -72,12 +72,12 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useEnergyStore } from '@/stores/energy'
|
||||
import { useRoomStore } from '@/stores/room'
|
||||
|
||||
const energyStore = useEnergyStore()
|
||||
const roomStore = useRoomStore()
|
||||
|
||||
const roomsList = computed(() => {
|
||||
return Array.from(energyStore.roomsData.values()).sort((a, b) =>
|
||||
return Array.from(roomStore.roomsData.values()).sort((a, b) =>
|
||||
a.room.localeCompare(b.room)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -173,9 +173,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useEnergyStore } from '@/stores/energy'
|
||||
import { useRoomStore } from '@/stores/room'
|
||||
|
||||
const energyStore = useEnergyStore()
|
||||
const roomStore = useRoomStore()
|
||||
|
||||
// Emit events
|
||||
const emit = defineEmits<{
|
||||
@@ -191,7 +191,7 @@ const roomToDelete = ref<string | null>(null)
|
||||
|
||||
// Computed properties
|
||||
const roomsWithStats = computed(() => {
|
||||
return energyStore.getAllRoomsWithStats()
|
||||
return roomStore.getAllRoomsWithStats()
|
||||
})
|
||||
|
||||
// Methods
|
||||
@@ -215,8 +215,8 @@ const addNewRoom = async () => {
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
const success = energyStore.addRoom(newRoomName.value.trim())
|
||||
|
||||
const success = roomStore.addRoom(newRoomName.value.trim())
|
||||
|
||||
if (success) {
|
||||
newRoomName.value = ''
|
||||
// Show success feedback
|
||||
@@ -242,8 +242,8 @@ const deleteRoom = async () => {
|
||||
isDeleting.value = roomToDelete.value
|
||||
|
||||
try {
|
||||
const success = energyStore.removeRoom(roomToDelete.value)
|
||||
|
||||
const success = roomStore.removeRoom(roomToDelete.value)
|
||||
|
||||
if (success) {
|
||||
console.log(`Room "${roomToDelete.value}" deleted successfully`)
|
||||
} else {
|
||||
@@ -258,7 +258,7 @@ const deleteRoom = async () => {
|
||||
}
|
||||
|
||||
const getRoomStats = (roomName: string) => {
|
||||
return energyStore.getRoomStats(roomName)
|
||||
return roomStore.getRoomStats(roomName)
|
||||
}
|
||||
|
||||
const getCO2Color = (co2Level: number) => {
|
||||
|
||||
@@ -1,16 +1,52 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useWebSocketStore } from './websocket'
|
||||
import { useSensorStore } from './sensor'
|
||||
import { useRoomStore } from './room'
|
||||
import { useAnalyticsStore } from './analytics'
|
||||
|
||||
/**
|
||||
* Energy Store - Simplified to only track energy consumption metrics
|
||||
* For sensor data: use useSensorStore()
|
||||
* For room data: use useRoomStore()
|
||||
* For WebSocket: use useWebSocketStore()
|
||||
* For analytics: use useAnalyticsStore()
|
||||
*/
|
||||
export const useEnergyStore = defineStore('energy', () => {
|
||||
// Get instances of other stores
|
||||
const websocketStore = useWebSocketStore()
|
||||
const sensorStore = useSensorStore()
|
||||
const roomStore = useRoomStore()
|
||||
const analyticsStore = useAnalyticsStore()
|
||||
|
||||
// Initialize data from APIs
|
||||
// Energy-specific state
|
||||
const currentConsumption = ref(0) // Current energy consumption in kWh
|
||||
const averageConsumption = ref(0) // Average energy consumption in kWh
|
||||
|
||||
// Computed: Current energy value from WebSocket
|
||||
const currentEnergyValue = computed(() => {
|
||||
return websocketStore.latestMessage?.energy?.value || 0
|
||||
})
|
||||
|
||||
// Computed: Average energy usage from time series
|
||||
const averageEnergyUsage = computed(() => {
|
||||
const data = websocketStore.timeSeriesData.datasets[0].data
|
||||
if (data.length === 0) return 0
|
||||
const sum = data.reduce((acc, val) => acc + val, 0)
|
||||
return sum / data.length
|
||||
})
|
||||
|
||||
// Update current consumption (called from components or watchers)
|
||||
function updateCurrentConsumption(value: number) {
|
||||
currentConsumption.value = value
|
||||
}
|
||||
|
||||
// Update average consumption (called from components or watchers)
|
||||
function updateAverageConsumption(value: number) {
|
||||
averageConsumption.value = value
|
||||
}
|
||||
|
||||
// Initialize data from APIs (convenience function for AnalyticsView)
|
||||
async function initializeFromApi() {
|
||||
await Promise.allSettled([
|
||||
roomStore.loadRoomsFromAPI(),
|
||||
@@ -21,59 +57,30 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
}
|
||||
|
||||
return {
|
||||
// Sensor state (delegated)
|
||||
sensorsData: computed(() => sensorStore.sensorsData),
|
||||
sensorDevices: computed(() => sensorStore.sensorDevices),
|
||||
apiSensors: computed(() => Array.from(sensorStore.sensorDevices.values())), // Convert Map to Array
|
||||
// Energy-specific state
|
||||
currentConsumption,
|
||||
averageConsumption,
|
||||
currentEnergyValue,
|
||||
averageEnergyUsage,
|
||||
|
||||
// Room state (delegated)
|
||||
roomsData: computed(() => roomStore.roomsData),
|
||||
availableRooms: computed(() => roomStore.availableRooms),
|
||||
roomsLoading: computed(() => roomStore.roomsLoading),
|
||||
roomsLoaded: computed(() => roomStore.roomsLoaded),
|
||||
// Energy-specific actions
|
||||
updateCurrentConsumption,
|
||||
updateAverageConsumption,
|
||||
|
||||
// Legacy delegation for AnalyticsView (keep for now to avoid breaking)
|
||||
apiSensors: computed(() => Array.from(sensorStore.sensorDevices.values())),
|
||||
apiRooms: computed(() => roomStore.apiRooms),
|
||||
|
||||
// Analytics state (delegated)
|
||||
analyticsData: computed(() => analyticsStore.analyticsData),
|
||||
systemStatus: computed(() => analyticsStore.systemStatus),
|
||||
healthStatus: computed(() => analyticsStore.healthStatus),
|
||||
|
||||
// Combined API loading/error state
|
||||
apiLoading: computed(
|
||||
() => sensorStore.apiLoading || roomStore.apiLoading || analyticsStore.apiLoading,
|
||||
),
|
||||
apiError: computed(() => sensorStore.apiError || roomStore.apiError || analyticsStore.apiError),
|
||||
|
||||
// Sensor functions (delegated)
|
||||
updateSensorRoom: sensorStore.updateSensorRoom,
|
||||
executeSensorAction: sensorStore.executeSensorAction,
|
||||
getSensorsByRoom: sensorStore.getSensorsByRoom,
|
||||
getSensorsByType: sensorStore.getSensorsByType,
|
||||
// Legacy API functions for AnalyticsView
|
||||
fetchApiSensors: sensorStore.fetchApiSensors,
|
||||
fetchApiSensorData: sensorStore.fetchApiSensorData,
|
||||
updateApiSensorMetadata: sensorStore.updateApiSensorMetadata,
|
||||
deleteApiSensor: sensorStore.deleteApiSensor,
|
||||
exportApiData: sensorStore.exportApiData,
|
||||
|
||||
// Room functions (delegated)
|
||||
getCO2Status: roomStore.getCO2Status,
|
||||
loadRoomsFromAPI: roomStore.loadRoomsFromAPI,
|
||||
addRoom: roomStore.addRoom,
|
||||
removeRoom: roomStore.removeRoom,
|
||||
getRoomStats: roomStore.getRoomStats,
|
||||
getAllRoomsWithStats: roomStore.getAllRoomsWithStats,
|
||||
fetchApiRooms: roomStore.fetchApiRooms,
|
||||
fetchApiRoomData: roomStore.fetchApiRoomData,
|
||||
|
||||
// Analytics functions (delegated)
|
||||
fetchAnalyticsSummary: analyticsStore.fetchAnalyticsSummary,
|
||||
fetchEnergyTrends: analyticsStore.fetchEnergyTrends,
|
||||
fetchRoomComparison: analyticsStore.fetchRoomComparison,
|
||||
fetchSystemEvents: analyticsStore.fetchSystemEvents,
|
||||
fetchSystemStatus: analyticsStore.fetchSystemStatus,
|
||||
fetchHealthStatus: analyticsStore.fetchHealthStatus,
|
||||
|
||||
// Initialize function
|
||||
initializeFromApi,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { roomsApi, type RoomInfo as ApiRoomInfo } from '@/services'
|
||||
import { roomsApi, type RoomInfo as ApiRoomInfo, type SensorReading } from '@/services'
|
||||
import { useSensorStore } from './sensor'
|
||||
|
||||
interface RoomMetrics {
|
||||
@@ -23,24 +23,6 @@ interface RoomMetrics {
|
||||
lastUpdated: number
|
||||
}
|
||||
|
||||
interface SensorReading {
|
||||
sensorId: string
|
||||
room: string
|
||||
timestamp: number
|
||||
energy: {
|
||||
value: number
|
||||
unit: string
|
||||
}
|
||||
co2: {
|
||||
value: number
|
||||
unit: string
|
||||
}
|
||||
temperature?: {
|
||||
value: number
|
||||
unit: string
|
||||
}
|
||||
}
|
||||
|
||||
export const useRoomStore = defineStore('room', () => {
|
||||
// State
|
||||
const roomsData = reactive<Map<string, RoomMetrics>>(new Map())
|
||||
@@ -70,7 +52,7 @@ export const useRoomStore = defineStore('room', () => {
|
||||
if (!roomMetrics) {
|
||||
roomMetrics = {
|
||||
room: data.room,
|
||||
sensors: [data.sensorId],
|
||||
sensors: [data.sensor_id],
|
||||
energy: { current: 0, total: 0, average: 0, unit: data.energy?.unit || 'kWh' },
|
||||
co2: { current: 0, average: 0, max: 0, status: 'good', unit: data.co2?.unit || 'ppm' },
|
||||
occupancyEstimate: 'low',
|
||||
@@ -80,8 +62,8 @@ export const useRoomStore = defineStore('room', () => {
|
||||
}
|
||||
|
||||
// Update room sensors list
|
||||
if (!roomMetrics.sensors.includes(data.sensorId)) {
|
||||
roomMetrics.sensors.push(data.sensorId)
|
||||
if (!roomMetrics.sensors.includes(data.sensor_id)) {
|
||||
roomMetrics.sensors.push(data.sensor_id)
|
||||
}
|
||||
|
||||
// Recalculate room metrics from all sensors in the room
|
||||
@@ -173,7 +155,7 @@ export const useRoomStore = defineStore('room', () => {
|
||||
// Reassign sensors to empty room
|
||||
sensorsInRoom.forEach((sensor) => {
|
||||
sensor.room = ''
|
||||
sensorStore.sensorDevices.set(sensor.id, { ...sensor })
|
||||
sensorStore.sensorDevices.set(sensor.sensor_id, { ...sensor })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -258,9 +240,12 @@ export const useRoomStore = defineStore('room', () => {
|
||||
async function fetchApiRooms() {
|
||||
const result = await handleApiCall(() => roomsApi.getRooms())
|
||||
if (result) {
|
||||
apiRooms.value = result
|
||||
// Handle both response formats: {rooms: [...]} or direct array [...]
|
||||
const roomsArray = Array.isArray(result) ? result : result.rooms || []
|
||||
apiRooms.value = roomsArray
|
||||
|
||||
// Update available rooms from API data
|
||||
const roomNames = result.map((room) => room.room).filter((name) => name)
|
||||
const roomNames = roomsArray.map((room) => room.name || room.room).filter((name) => name)
|
||||
if (roomNames.length > 0) {
|
||||
availableRooms.value = [...new Set([...availableRooms.value, ...roomNames])].sort()
|
||||
}
|
||||
@@ -301,4 +286,4 @@ export const useRoomStore = defineStore('room', () => {
|
||||
fetchApiRooms,
|
||||
fetchApiRoomData,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
<div class="flex items-center gap-2 text-sm text-gray-600">
|
||||
<div
|
||||
class="w-3 h-3 rounded-full"
|
||||
:class="energyStore.isConnected ? 'bg-green-500' : 'bg-red-500'"
|
||||
:class="websocketStore.isConnected ? 'bg-green-500' : 'bg-red-500'"
|
||||
></div>
|
||||
<span>{{ energyStore.isConnected ? 'Connected' : 'Disconnected' }}</span>
|
||||
<span>{{ websocketStore.isConnected ? 'Connected' : 'Disconnected' }}</span>
|
||||
<span class="mx-2">•</span>
|
||||
<span>{{ activeOptimizations.length }} active optimizations</span>
|
||||
</div>
|
||||
@@ -598,10 +598,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useEnergyStore } from '@/stores/energy'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useWebSocketStore } from '@/stores/websocket'
|
||||
|
||||
const energyStore = useEnergyStore()
|
||||
const websocketStore = useWebSocketStore()
|
||||
|
||||
// Modal states
|
||||
const showNewOptimizationModal = ref(false)
|
||||
@@ -725,11 +725,4 @@ const pauseOptimization = (id: number) => {
|
||||
// In a real app, this would make an API call
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize connection
|
||||
onMounted(() => {
|
||||
if (!energyStore.isConnected) {
|
||||
energyStore.connect('ws://localhost:8000/ws')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
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">
|
||||
<option v-for="room in roomStore.availableRooms" :key="room" :value="room">
|
||||
{{ room }}
|
||||
</option>
|
||||
</select>
|
||||
@@ -145,7 +145,7 @@
|
||||
v-for="sensor in filteredSensors"
|
||||
:key="sensor.sensor_id"
|
||||
:sensor="sensor"
|
||||
:available-rooms="energyStore.availableRooms"
|
||||
:available-rooms="roomStore.availableRooms"
|
||||
:is-executing-action="isExecutingAction"
|
||||
@update-room="updateRoom"
|
||||
@execute-action="executeAction"
|
||||
@@ -154,7 +154,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div v-if="energyStore.apiLoading" class="text-center py-12">
|
||||
<div v-if="sensorStore.apiLoading" class="text-center py-12">
|
||||
<div
|
||||
class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"
|
||||
></div>
|
||||
@@ -163,10 +163,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div v-else-if="energyStore.apiError" class="text-center py-12">
|
||||
<div v-else-if="sensorStore.apiError" class="text-center py-12">
|
||||
<div class="text-red-400 text-6xl mb-4">⚠️</div>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Error loading sensors</h3>
|
||||
<p class="text-gray-600 mb-4">{{ energyStore.apiError }}</p>
|
||||
<p class="text-gray-600 mb-4">{{ sensorStore.apiError }}</p>
|
||||
<button
|
||||
@click="reloadSensors"
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
@@ -199,7 +199,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useSensorStore } from '@/stores/sensor'
|
||||
import { useEnergyStore } from '@/stores/energy'
|
||||
import { useRoomStore } from '@/stores/room'
|
||||
import { useWebSocketStore } from '@/stores/websocket'
|
||||
import ActionModal from '@/components/modals/ActionModal.vue'
|
||||
import RoomManagementModal from '@/components/modals/RoomManagementModal.vue'
|
||||
@@ -207,7 +207,7 @@ import SimpleSensorCard from '@/components/cards/SimpleSensorCard.vue'
|
||||
import DetailedSensorCard from '@/components/cards/DetailedSensorCard.vue'
|
||||
|
||||
const sensorStore = useSensorStore()
|
||||
const energyStore = useEnergyStore()
|
||||
const roomStore = useRoomStore()
|
||||
const websocketStore = useWebSocketStore()
|
||||
|
||||
const viewMode = ref<'simple' | 'detailed'>('simple')
|
||||
@@ -238,7 +238,7 @@ const filteredSensors = computed(() => {
|
||||
})
|
||||
|
||||
const updateRoom = (sensorId: string, newRoom: string) => {
|
||||
energyStore.updateSensorRoom(sensorId, newRoom)
|
||||
sensorStore.updateSensorRoom(sensorId, newRoom)
|
||||
}
|
||||
|
||||
const executeAction = (sensor: any, action: any) => {
|
||||
@@ -254,7 +254,7 @@ const executeAction = (sensor: any, action: any) => {
|
||||
const handleActionExecute = async (sensorId: string, actionId: string, parameters: any) => {
|
||||
isExecutingAction.value = true
|
||||
try {
|
||||
await energyStore.executeSensorAction(sensorId, actionId, parameters)
|
||||
await sensorStore.executeSensorAction(sensorId, actionId)
|
||||
} catch (error) {
|
||||
console.error('Action execution failed:', error)
|
||||
} finally {
|
||||
@@ -274,11 +274,12 @@ const showSensorDetails = () => {
|
||||
}
|
||||
|
||||
const reloadSensors = async () => {
|
||||
await energyStore.fetchApiSensors()
|
||||
await sensorStore.fetchApiSensors()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await sensorStore.fetchApiSensors()
|
||||
await roomStore.fetchApiRooms()
|
||||
|
||||
if (!websocketStore.isConnected) {
|
||||
websocketStore.connect('ws://localhost:8007/ws')
|
||||
@@ -286,6 +287,6 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// energyStore.disconnect()
|
||||
// WebSocket managed globally, no cleanup needed here
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user