import dash from dash import dcc, html, Input, Output 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'}), ]) ]) # Callback to update the building total graph @app.callback( Output('building-graph', 'figure'), [Input('time-range', 'value')] ) def update_building_graph(time_range): df = fetch_data(time_range) df = calculate_building_kwh(df) 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') # Update legend to show recent 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 @app.callback( Output('breakdown-graph', 'figure'), [Input('tabs', 'value'), Input('drill-down', 'value'), Input('time-range', 'value')] ) def update_breakdown_graph(tab, drill_down, time_range): 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) 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') # Update legend to show recent 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)