tested running for a month
This commit is contained in:
205
dash_power.py
Normal file
205
dash_power.py
Normal file
@@ -0,0 +1,205 @@
|
||||
import dash
|
||||
from dash import dcc, html, Input, Output, callback_context
|
||||
import plotly.express as px
|
||||
import pandas as pd
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Initialize the Dash app
|
||||
app = dash.Dash(__name__)
|
||||
|
||||
# Connect to the SQLite database
|
||||
def get_db_connection():
|
||||
conn = sqlite3.connect('power_data.db')
|
||||
return conn
|
||||
|
||||
# Function to fetch data from the database
|
||||
def fetch_data(time_range):
|
||||
conn = get_db_connection()
|
||||
query = f"""
|
||||
SELECT * FROM building_totals
|
||||
WHERE timestamp >= datetime('now', '-{time_range} hours')
|
||||
"""
|
||||
df = pd.read_sql_query(query, conn)
|
||||
conn.close()
|
||||
return df
|
||||
|
||||
# Function to calculate kWh and round timestamps for building totals
|
||||
def calculate_building_kwh(df):
|
||||
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
||||
df['timestamp'] = df['timestamp'].dt.round('5min') # Round to 5-minute intervals
|
||||
df = df.set_index('timestamp')
|
||||
df['kWh'] = df['total_power'] * (5 / 60) # Convert power to kWh for 5-minute intervals
|
||||
return df
|
||||
|
||||
# Function to calculate kWh and round timestamps for room and customer breakdowns
|
||||
def calculate_breakdown_kwh(df):
|
||||
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
||||
df['timestamp'] = df['timestamp'].dt.round('5min') # Round to 5-minute intervals
|
||||
df = df.set_index('timestamp')
|
||||
df['kWh'] = df['power'] * (5 / 60) # Convert power to kWh for 5-minute intervals
|
||||
return df
|
||||
|
||||
# Define the layout of the dashboard
|
||||
app.layout = html.Div([
|
||||
html.H1("Power Usage Overview", style={'textAlign': 'center'}),
|
||||
|
||||
dcc.Dropdown(
|
||||
id='time-range',
|
||||
options=[
|
||||
{'label': '6 Hours', 'value': '6'},
|
||||
{'label': '12 Hours', 'value': '12'},
|
||||
{'label': '24 Hours', 'value': '24'},
|
||||
{'label': '1 Week', 'value': '168'},
|
||||
{'label': '2 Weeks', 'value': '336'},
|
||||
{'label': '1 Month', 'value': '720'},
|
||||
{'label': '2 Months', 'value': '1440'},
|
||||
{'label': '1 Year', 'value': '8760'},
|
||||
],
|
||||
value='6',
|
||||
style={'width': '200px', 'margin': '0 auto'}
|
||||
),
|
||||
|
||||
dcc.Graph(id='building-graph', style={'width': '100%', 'height': '400px'}),
|
||||
|
||||
dcc.Tabs(id='tabs', value='room-breakdown', children=[
|
||||
dcc.Tab(label='Room Breakdown', value='room-breakdown'),
|
||||
dcc.Tab(label='Customer Breakdown', value='customer-breakdown'),
|
||||
]),
|
||||
|
||||
html.Div([
|
||||
dcc.Dropdown(id='drill-down', multi=False, style={'width': '200px', 'margin': '0 auto'}),
|
||||
dcc.Graph(id='breakdown-graph', style={'width': '100%', 'height': '400px'}),
|
||||
])
|
||||
])
|
||||
|
||||
# Combined callback to update the building total graph and handle zoom events
|
||||
@app.callback(
|
||||
Output('building-graph', 'figure'),
|
||||
[Input('time-range', 'value'),
|
||||
Input('building-graph', 'relayoutData')],
|
||||
[dash.dependencies.State('building-graph', 'figure')]
|
||||
)
|
||||
def update_building_graph(time_range, relayoutData, figure):
|
||||
ctx = callback_context
|
||||
|
||||
if not ctx.triggered:
|
||||
return dash.no_update
|
||||
|
||||
df = fetch_data(time_range)
|
||||
df = calculate_building_kwh(df)
|
||||
|
||||
# Initialize fig
|
||||
fig = px.line(df, x=df.index, y=['total_current', 'total_power', 'kWh'],
|
||||
labels={'value': 'Value', 'variable': 'Metric'},
|
||||
title='Building Total Metrics')
|
||||
fig.update_layout(legend_title_text='Metrics', yaxis_type="log") # Use logarithmic scale
|
||||
|
||||
if 'time-range' in ctx.triggered[0]['prop_id']:
|
||||
pass # fig is already initialized
|
||||
|
||||
elif relayoutData and 'xaxis.range[0]' in relayoutData and 'xaxis.range[1]' in relayoutData:
|
||||
start = pd.to_datetime(relayoutData['xaxis.range[0]'])
|
||||
end = pd.to_datetime(relayoutData['xaxis.range[1]'])
|
||||
df = df.loc[start:end]
|
||||
fig = px.line(df, x=df.index, y=['total_current', 'total_power', 'kWh'],
|
||||
labels={'value': 'Value', 'variable': 'Metric'},
|
||||
title='Building Total Metrics')
|
||||
fig.update_layout(legend_title_text='Metrics', yaxis_type="log") # Use logarithmic scale
|
||||
|
||||
# Update legend to show total values
|
||||
for trace in fig.data:
|
||||
if trace.name == 'kWh':
|
||||
trace.name = f"{trace.name}: {df['kWh'].sum():.2f}"
|
||||
else:
|
||||
trace.name = f"{trace.name}: {df[trace.name].iloc[-1]:.2f}"
|
||||
|
||||
return fig
|
||||
|
||||
# Callback to update the drill-down dropdown
|
||||
@app.callback(
|
||||
Output('drill-down', 'options'),
|
||||
[Input('tabs', 'value')]
|
||||
)
|
||||
def update_drill_down_options(tab):
|
||||
conn = get_db_connection()
|
||||
if tab == 'room-breakdown':
|
||||
query = "SELECT DISTINCT room_number FROM room_breakdown"
|
||||
else:
|
||||
query = "SELECT DISTINCT customer_name FROM customer_breakdown"
|
||||
df = pd.read_sql_query(query, conn)
|
||||
conn.close()
|
||||
return [{'label': i, 'value': i} for i in df.iloc[:, 0]]
|
||||
|
||||
# Callback to set the default value for the drill-down dropdown
|
||||
@app.callback(
|
||||
Output('drill-down', 'value'),
|
||||
[Input('drill-down', 'options')]
|
||||
)
|
||||
def set_drill_down_value(options):
|
||||
if options:
|
||||
return options[0]['value']
|
||||
return None
|
||||
|
||||
# Callback to update the breakdown graph and handle zoom events
|
||||
@app.callback(
|
||||
Output('breakdown-graph', 'figure'),
|
||||
[Input('tabs', 'value'),
|
||||
Input('drill-down', 'value'),
|
||||
Input('time-range', 'value'),
|
||||
Input('breakdown-graph', 'relayoutData')],
|
||||
[dash.dependencies.State('breakdown-graph', 'figure')]
|
||||
)
|
||||
def update_breakdown_graph(tab, drill_down, time_range, relayoutData, figure):
|
||||
ctx = callback_context
|
||||
|
||||
if not ctx.triggered:
|
||||
return dash.no_update
|
||||
|
||||
conn = get_db_connection()
|
||||
if tab == 'room-breakdown':
|
||||
query = f"""
|
||||
SELECT * FROM room_breakdown
|
||||
WHERE room_number = '{drill_down}'
|
||||
AND timestamp >= datetime('now', '-{time_range} hours')
|
||||
"""
|
||||
else:
|
||||
query = f"""
|
||||
SELECT * FROM customer_breakdown
|
||||
WHERE customer_name = '{drill_down}'
|
||||
AND timestamp >= datetime('now', '-{time_range} hours')
|
||||
"""
|
||||
df = pd.read_sql_query(query, conn)
|
||||
conn.close()
|
||||
df = calculate_breakdown_kwh(df)
|
||||
|
||||
# Initialize fig
|
||||
fig = px.line(df, x=df.index, y=['current', 'power', 'kWh'],
|
||||
labels={'value': 'Value', 'variable': 'Metric'},
|
||||
title=f'{tab.replace("-", " ").title()} Metrics')
|
||||
fig.update_layout(legend_title_text='Metrics', yaxis_type="log") # Use logarithmic scale
|
||||
|
||||
if 'time-range' in ctx.triggered[0]['prop_id'] or 'drill-down' in ctx.triggered[0]['prop_id']:
|
||||
pass # fig is already initialized
|
||||
|
||||
elif relayoutData and 'xaxis.range[0]' in relayoutData and 'xaxis.range[1]' in relayoutData:
|
||||
start = pd.to_datetime(relayoutData['xaxis.range[0]'])
|
||||
end = pd.to_datetime(relayoutData['xaxis.range[1]'])
|
||||
df = df.loc[start:end]
|
||||
fig = px.line(df, x=df.index, y=['current', 'power', 'kWh'],
|
||||
labels={'value': 'Value', 'variable': 'Metric'},
|
||||
title=f'{tab.replace("-", " ").title()} Metrics')
|
||||
fig.update_layout(legend_title_text='Metrics', yaxis_type="log") # Use logarithmic scale
|
||||
|
||||
# Update legend to show total values
|
||||
for trace in fig.data:
|
||||
if trace.name == 'kWh':
|
||||
trace.name = f"{trace.name}: {df['kWh'].sum():.2f}"
|
||||
else:
|
||||
trace.name = f"{trace.name}: {df[trace.name].iloc[-1]:.2f}"
|
||||
|
||||
return fig
|
||||
|
||||
# Run the app
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=8050, debug=True)
|
||||
Reference in New Issue
Block a user