diff --git a/backend/devices_json.py b/backend/devices_json.py index 042560b..1f44ccd 100644 --- a/backend/devices_json.py +++ b/backend/devices_json.py @@ -1,8 +1,15 @@ import json import asyncio import random +import os + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) deviceFile = "devices.json" +selectedUserFile = "selected_user.json" +usersDBFile = os.path.abspath(os.path.join(BASE_DIR, "../database/users_db.json")) + +selected_user_devices = "selected_user_devices.json" updates = [] # Stores messages for frontend @@ -148,4 +155,4 @@ def getUpdates(): global updates messages = updates[:] updates.clear() - return messages + return messages \ No newline at end of file diff --git a/backend/energy/daily_energy.json b/backend/energy/daily_energy.json new file mode 100644 index 0000000..3b9331a --- /dev/null +++ b/backend/energy/daily_energy.json @@ -0,0 +1,32 @@ +[ + { "timestamp": "2025-03-01T00:00:00", "power_usage": 4800 }, + { "timestamp": "2025-03-02T00:00:00", "power_usage": 5120 }, + { "timestamp": "2025-03-03T00:00:00", "power_usage": 4500 }, + { "timestamp": "2025-03-04T00:00:00", "power_usage": 4700 }, + { "timestamp": "2025-03-05T00:00:00", "power_usage": 5300 }, + { "timestamp": "2025-03-06T00:00:00", "power_usage": 5100 }, + { "timestamp": "2025-03-07T00:00:00", "power_usage": 4900 }, + { "timestamp": "2025-03-08T00:00:00", "power_usage": 4780 }, + { "timestamp": "2025-03-09T00:00:00", "power_usage": 5000 }, + { "timestamp": "2025-03-10T00:00:00", "power_usage": 5150 }, + { "timestamp": "2025-03-11T00:00:00", "power_usage": 5600 }, + { "timestamp": "2025-03-12T00:00:00", "power_usage": 5400 }, + { "timestamp": "2025-03-13T00:00:00", "power_usage": 5900 }, + { "timestamp": "2025-03-14T00:00:00", "power_usage": 4700 }, + { "timestamp": "2025-03-15T00:00:00", "power_usage": 6000 }, + { "timestamp": "2025-03-16T00:00:00", "power_usage": 6300 }, + { "timestamp": "2025-03-17T00:00:00", "power_usage": 6250 }, + { "timestamp": "2025-03-18T00:00:00", "power_usage": 5950 }, + { "timestamp": "2025-03-19T00:00:00", "power_usage": 6400 }, + { "timestamp": "2025-03-20T00:00:00", "power_usage": 6600 }, + { "timestamp": "2025-03-21T00:00:00", "power_usage": 6100 }, + { "timestamp": "2025-03-22T00:00:00", "power_usage": 6800 }, + { "timestamp": "2025-03-23T00:00:00", "power_usage": 6700 }, + { "timestamp": "2025-03-24T00:00:00", "power_usage": 7150 }, + { "timestamp": "2025-03-25T00:00:00", "power_usage": 6800 }, + { "timestamp": "2025-03-26T00:00:00", "power_usage": 7000 }, + { "timestamp": "2025-03-27T00:00:00", "power_usage": 7500 }, + { "timestamp": "2025-03-28T00:00:00", "power_usage": 7400 }, + { "timestamp": "2025-03-29T00:00:00", "power_usage": 7100 }, + { "timestamp": "2025-03-30T00:00:00", "power_usage": 6900 } +] diff --git a/backend/energy/monthly_energy.json b/backend/energy/monthly_energy.json new file mode 100644 index 0000000..dde32ac --- /dev/null +++ b/backend/energy/monthly_energy.json @@ -0,0 +1,14 @@ +[ + { "timestamp": "2024-01-01T00:00:00", "power_usage": 150000 }, + { "timestamp": "2024-02-01T00:00:00", "power_usage": 153500 }, + { "timestamp": "2024-03-01T00:00:00", "power_usage": 160000 }, + { "timestamp": "2024-04-01T00:00:00", "power_usage": 170000 }, + { "timestamp": "2024-05-01T00:00:00", "power_usage": 165000 }, + { "timestamp": "2024-06-01T00:00:00", "power_usage": 180000 }, + { "timestamp": "2024-07-01T00:00:00", "power_usage": 175000 }, + { "timestamp": "2024-08-01T00:00:00", "power_usage": 185500 }, + { "timestamp": "2024-09-01T00:00:00", "power_usage": 190500 }, + { "timestamp": "2024-10-01T00:00:00", "power_usage": 195000 }, + { "timestamp": "2024-11-01T00:00:00", "power_usage": 200500 }, + { "timestamp": "2024-12-01T00:00:00", "power_usage": 210000 } +] diff --git a/backend/energy_json.py b/backend/energy_json.py index 8080bff..91fe474 100644 --- a/backend/energy_json.py +++ b/backend/energy_json.py @@ -1,5 +1,21 @@ -import devices_json as dj -import random import json +import os -deviceFile = "energy_tips.json" +ENERGY_FOLDER = os.path.join(os.path.dirname(__file__), "energy") +print(ENERGY_FOLDER) + +def get_energy_data(time_range: str): + """Fetch energy usage data from JSON files.""" + + if time_range not in ["daily", "monthly"]: + return {"error": "Invalid time range. Use 'daily' or 'monthly'."} + + file_path = os.path.join(ENERGY_FOLDER, f"{time_range}_energy.json") + + try: + with open(file_path, "r") as file: + return json.load(file) + except FileNotFoundError: + return {"error": f"{time_range}_energy.json not found."} + except json.JSONDecodeError: + return {"error": f"Invalid JSON format in {time_range}_energy.json."} diff --git a/backend/fastAPI.py b/backend/fastAPI.py index 17b25ff..ca27bcd 100644 --- a/backend/fastAPI.py +++ b/backend/fastAPI.py @@ -1,13 +1,15 @@ from typing import List, Optional from pydantic import BaseModel + import devices_json as dj import users +import energy_json as ej import asyncio import os import json -from fastapi import FastAPI +from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware # Serve user data (Only for testing purposes) @@ -108,4 +110,17 @@ def add_new_user(user: UserRequest): @app.delete("/delete_user/{user_name}/{user_password}") def delete_user(user_name: str, user_password: str): """Deletes a user with the given name and password.""" - return users.delete_user(user_name, user_password) \ No newline at end of file + return users.delete_user(user_name, user_password) + +@app.get("/energy_usage") +def fetch_energy_usage(range: str): + if range == "daily": + return ej.get_energy_data("daily") + elif range == "monthly": + return ej.get_energy_data("monthly") + else: + raise HTTPException(status_code=400, detail="Invalid range") + +@app.get("/energy_usage/{time_range}") +def fetch_energy_usage(time_range: str): + return ej.get_energy_data(time_range) \ No newline at end of file diff --git a/database/users_db.json b/database/users_db.json index 55e0cbd..cb045ee 100644 --- a/database/users_db.json +++ b/database/users_db.json @@ -20,7 +20,7 @@ "user_id": 2, "user_name": "David F", "user_password": "1415", - "allocated_devices": [], + "allocated_devices": ["7", "8"], "user_role": "sub_user" }, { diff --git a/frontend/src/app/dashboard/page.jsx b/frontend/src/app/dashboard/page.jsx index 1898532..6387ffc 100644 --- a/frontend/src/app/dashboard/page.jsx +++ b/frontend/src/app/dashboard/page.jsx @@ -48,6 +48,7 @@ const Dashboard = () => { const [data, setData] = useState([]); const [checked, setChecked] = useState([]); const [timeRange, setTimeRange] = useState("realtime"); + const [energyData, setEnergyData] = useState({ daily: [], monthly: [] }); const theme = useTheme(); const boxShadow = @@ -60,11 +61,12 @@ const Dashboard = () => { useEffect(() => { const fetchData = async () => { try { - const response = await fetch("http://localhost:8000/device_info"); - const result = await response.json(); + // Fetch device data + const deviceResponse = await fetch("http://localhost:8000/device_info"); + const deviceResult = await deviceResponse.json(); - if (result && Array.isArray(result.smart_home_devices)) { - const connectedDevices = result.smart_home_devices.filter( + if (deviceResult && Array.isArray(deviceResult.smart_home_devices)) { + const connectedDevices = deviceResult.smart_home_devices.filter( (device) => device.connection_status === "connected" ); setData(connectedDevices); @@ -74,15 +76,26 @@ const Dashboard = () => { .map((device) => device.id); setChecked(initialChecked); } else { - console.error("Invalid response structure", result); + console.error("Invalid response structure", deviceResult); } + + const dailyResponse = await fetch("http://localhost:8000/energy_usage/daily"); + const dailyResult = await dailyResponse.json(); + + const monthlyResponse = await fetch("http://localhost:8000/energy_usage/monthly"); + const monthlyResult = await monthlyResponse.json(); + + setEnergyData({ + daily: dailyResult, + monthly: monthlyResult, + }); } catch (error) { - console.error("Error fetching smart home devices:", error); + console.error("Error fetching data:", error); } }; fetchData(); - const interval = setInterval(fetchData, 1000); + const interval = setInterval(fetchData, 1000); // Update every 10 seconds return () => clearInterval(interval); }, []); @@ -232,7 +245,7 @@ const Dashboard = () => { textDecoration: "none", color: "inherit", transition: "transform 0.2s ease-in-out", - "&:hover": { transform: "scale(1.01)" }, // Slight hover effect + "&:hover": { transform: "scale(1.01)" }, }} > { flexDirection: "column", // boxShadow: boxShadow, width: "100%", - textDecoration: "none", // Prevents underline - color: "inherit", // Keeps original text color + textDecoration: "none", + color: "inherit", transition: "transform 0.2s ease-in-out", - "&:hover": { transform: "scale(1.01)" }, // Slight hover effect + "&:hover": { transform: "scale(1.01)" }, }} > { width: "100%", }} > - + { +const EnergyUsageChart = ({ data, timeRange }) => { const [dataPoints, setDataPoints] = useState([]); - const [buffer, setBuffer] = useState([]); // Store data for averaging const chartRef = useRef(null); const theme = useTheme(); const strokeColor = theme.palette.mode === "dark" ? "#8253d7" : "#1F99FC"; - // Function to get current power usage const getCurrentPowerUsage = () => { if (!data || data.length === 0) return 0; return data.reduce((acc, device) => acc + (device.power_usage || 0), 0); }; - // Initialize with first data point useEffect(() => { - const initialPower = getCurrentPowerUsage(); - setDataPoints([{ time: new Date(), power: initialPower }]); - }, []); // Runs only on first render - - // Update buffer every second with new power usage - useEffect(() => { - if (!data || data.length === 0) return; - - const currentPower = getCurrentPowerUsage(); - setBuffer((prevBuffer) => { - const newBuffer = [...prevBuffer, currentPower]; - - // Keep only the last 5 minutes (assuming data updates every second) - return newBuffer.length > 300 ? newBuffer.slice(-300) : newBuffer; - }); - }, [data]); - - // Every 5 minutes, plot the average power usage - useEffect(() => { - const interval = setInterval(() => { - if (buffer.length === 0) return; - - const averagePower = - buffer.reduce((acc, val) => acc + val, 0) / buffer.length || 0; - const timestamp = new Date(); - - if (!isNaN(averagePower) && averagePower !== undefined) { + if (timeRange === "realtime") { + const fetchRealTimeData = () => { + const currentPower = getCurrentPowerUsage(); setDataPoints((prevData) => { const newData = [ ...prevData, - { time: timestamp, power: averagePower }, + { time: new Date(), power: currentPower }, ]; return newData.length > 10 ? newData.slice(1) : newData; }); - } + }; + + const interval = setInterval(fetchRealTimeData, 1000); + return () => clearInterval(interval); + } + }, [data, timeRange]); - setBuffer([]); - }, 1000); + useEffect(() => { + if (timeRange === "daily" || timeRange === "monthly") { + const fetchHistoricalData = async () => { + try { + const response = await fetch( + `http://localhost:8000/energy_usage?range=${timeRange}` + ); + const result = await response.json(); + if (Array.isArray(result)) { + setDataPoints( + result.map((entry) => ({ + time: new Date(entry.timestamp), + power: entry.power_usage, + })) + ); + } + } catch (error) { + console.error("Error fetching historical energy data:", error); + } + }; - return () => clearInterval(interval); - }, [buffer]); + fetchHistoricalData(); + } + }, [timeRange]); return ( <> @@ -82,23 +79,34 @@ const EnergyUsageChart = ({ data }) => { color: "primary.main", }} > - Energy Usage Trend + {timeRange === "realtime" + ? "Real-time Energy Usage" + : timeRange === "daily" + ? "Daily Energy Usage" + : "Monthly Energy Usage"} - {/* Ensure chart only renders with valid data */} {dataPoints.length > 0 ? ( - + dayjs(time).format("HH:mm")} - /> - + timeRange === "monthly" + ? dayjs(time).format("MMM DD") + : timeRange === "daily" + ? dayjs(time).format("MMM DD") + : dayjs(time).format("HH:mm") + } /> + - `Time: ${dayjs(label).format("HH:mm:ss")}` + `Time: ${ + timeRange === "monthly" + ? dayjs(label).format("MMM DD") + : dayjs(label).format("HH:mm") + }` } formatter={(value) => [`${value} W`, "Power Usage"]} /> @@ -111,9 +119,7 @@ const EnergyUsageChart = ({ data }) => { ) : ( - + No data available yet... )}