File size: 6,628 Bytes
12763b7
7aa7428
 
 
 
 
 
 
 
12763b7
 
7aa7428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f21f44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7aa7428
f44602f
12763b7
 
 
f44602f
 
12763b7
 
f44602f
 
12763b7
f44602f
 
 
 
 
 
 
 
331907c
 
f44602f
 
 
331907c
f44602f
 
12763b7
 
 
 
 
 
 
 
 
 
7aa7428
 
 
 
 
f44602f
 
 
 
 
 
 
 
7aa7428
12763b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f44602f
 
12763b7
 
 
 
 
 
f44602f
12763b7
7aa7428
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

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.")