import os import time import pandas as pd import numpy as np import joblib import requests import streamlit as st from streamlit_autorefresh import st_autorefresh from sklearn.metrics import mean_absolute_error import plotly.express as px # Auto-refresh every 5 seconds st_autorefresh(interval=5000, key="refresh") # Load model @st.cache_resource def load_model(): return joblib.load("rf_model.pkl") model = load_model() # Supabase config SUPABASE_URL = os.environ["SUPABASE_URL"] SUPABASE_KEY = os.environ["SUPABASE_KEY"] TABLE = "smart_meter_readings_1year" # Initialize session state if "row_index" not in st.session_state: st.session_state.row_index = 0 if "history" not in st.session_state: st.session_state.history = pd.DataFrame() # Fetch all data @st.cache_data def fetch_all_data(): url = f"{SUPABASE_URL}/rest/v1/{TABLE}?select=*&order=timestamp.asc" headers = { "apikey": SUPABASE_KEY, "Authorization": f"Bearer {SUPABASE_KEY}" } r = requests.get(url, headers=headers) if r.ok: return pd.DataFrame(r.json()) else: st.error(f"❌ Error fetching data: {r.status_code}") return pd.DataFrame() df_all = fetch_all_data() # Debug sidebar st.sidebar.title("🛠 Debug Info") st.sidebar.write("Row index:", st.session_state.row_index) st.sidebar.write("Total rows:", len(df_all)) if not df_all.empty and st.session_state.row_index < len(df_all): st.sidebar.write("Next row:", df_all.iloc[st.session_state.row_index].to_dict()) # Get next row def get_next_row(): if st.session_state.row_index < len(df_all): row = df_all.iloc[[st.session_state.row_index]] st.session_state.row_index += 1 return row return pd.DataFrame() def forecast_next(df, model, steps=5): forecasts = [] df_copy = df.copy() expected_features = model.feature_names_in_.tolist() for i in range(steps): df_copy = engineer(df_copy).dropna() # Ensure all expected features are present for col in expected_features: if col not in df_copy.columns: df_copy[col] = 0 # Select and order input features input_row = df_copy.iloc[[-1]][expected_features] y_pred = model.predict(input_row)[0] # Prepare next row next_timestamp = df_copy.iloc[-1]["timestamp"] + 1800 new_row = df_copy.iloc[-1].copy() new_row["power_consumption_kwh"] = y_pred new_row["timestamp"] = next_timestamp df_copy = pd.concat([df_copy, pd.DataFrame([new_row])], ignore_index=True) forecasts.append({"timestamp": next_timestamp, "forecast_kwh": y_pred}) return pd.DataFrame(forecasts) # Forecast ahead logic (5 steps, 30min intervals) def forecast_next(df, model, steps=5): forecasts = [] df_copy = df.copy() expected_features = model.feature_names_in_.tolist() for i in range(steps): df_copy = engineer(df_copy).dropna() # Ensure columns are aligned to model input input_row = df_copy.iloc[[-1]][expected_features] y_pred = model.predict(input_row)[0] # Update the last row's power consumption with prediction df_copy.at[df_copy.index[-1], "power_consumption_kwh"] = y_pred # Prepare the next timestamp: add 1800 seconds (30 mins) next_timestamp = df_copy.iloc[-1]["timestamp"] + 1800 # Prepare new row: copy last row features but update timestamp and power consumption new_row = df_copy.iloc[-1].copy() new_row["power_consumption_kwh"] = y_pred new_row["timestamp"] = next_timestamp # Append new row safely df_copy = pd.concat([df_copy, pd.DataFrame([new_row])], ignore_index=True) forecasts.append({"timestamp": next_timestamp, "forecast_kwh": y_pred}) return pd.DataFrame(forecasts) # UI Layout st.set_page_config(layout="wide") st.title("⚡ Gridflux: Real-Time Smart Meter Dashboard") # Layout structure col1, col2 = st.columns([2, 1]) # Data ingestion and prediction new_row = get_next_row() if not new_row.empty: st.session_state.history = pd.concat([st.session_state.history, new_row], ignore_index=True) df_feat = engineer(st.session_state.history).dropna() if not df_feat.empty: # Align features exactly to what model expects expected_features = model.feature_names_in_.tolist() for col in expected_features: if col not in df_feat.columns: df_feat[col] = 0 latest_input = df_feat[expected_features].iloc[[-1]] prediction = model.predict(latest_input)[0] actual = new_row["power_consumption_kwh"].values[0] mae = mean_absolute_error([actual], [prediction]) with col1: st.subheader("📊 Real-Time Power Usage") chart_df = st.session_state.history.copy() chart_df["datetime"] = pd.to_datetime(chart_df["timestamp"]) chart_df.set_index("datetime", inplace=True) st.line_chart(chart_df["power_consumption_kwh"], use_container_width=True) st.subheader("🔮 Forecast (Next 2.5 Hours)") future_df = forecast_next(df_feat, model, steps=5) future_df["datetime"] = pd.to_datetime(future_df["timestamp"]) fig = px.line(future_df, x="datetime", y="forecast_kwh", title="Forecasted Power Usage (Next 2.5h)") st.plotly_chart(fig, use_container_width=True) with col2: st.metric("🔮 Predicted Power Usage (kWh)", f"{prediction:.3f}") st.metric("✅ Actual Power Usage (kWh)", f"{actual:.3f}") st.metric("📏 MAE", f"{mae:.3f}") st.subheader("🌍 Region-Wise Forecast") for region in ["east", "west", "north", "south"]: regional = df_feat[df_feat[f"region_{region}"] == 1] if not regional.empty: preds = model.predict(regional[expected_features]) st.write(f"**{region.title()} Region**: Avg Forecast: {np.mean(preds):.3f} kWh") st.subheader("🏠 Property Type Forecast") for region in ["east", "west", "north", "south"]: for ptype in ["commercial", "residential"]: filtered = df_feat[(df_feat[f"region_{region}"] == 1) & (df_feat[f"property_type_{ptype}"] == 1)] if not filtered.empty: preds = model.predict(filtered[expected_features]) st.write(f"{region.title()} / {ptype.title()}: {np.mean(preds):.2f} kWh") else: st.success("✅ All data processed.")