import dash from dash import dcc, html, Input, Output, State import plotly.express as px import sqlite3 import pandas as pd from datetime import datetime, timedelta # Create a Dash app app = dash.Dash(__name__) # Function to fetch data from the SQLite database def fetch_data(): conn = sqlite3.connect('power_data.db') building_totals = pd.read_sql_query('SELECT * FROM building_totals', conn) room_breakdown = pd.read_sql_query('SELECT * FROM room_breakdown', conn) conn.close() return building_totals, room_breakdown # Function to calculate kWh usage def calculate_kwh(data, power_column): data = data.copy() # Create a copy of the DataFrame to avoid SettingWithCopyWarning data.loc[:, 'timestamp'] = pd.to_datetime(data['timestamp'], format='ISO8601') data = data.sort_values('timestamp') data.loc[:, 'time_diff'] = data['timestamp'].diff().dt.total_seconds() / 3600 # Convert to hours data.loc[:, 'kwh'] = data[power_column] * data['time_diff'] data.loc[:, 'cumulative_kwh'] = data['kwh'].cumsum() return data # Define the layout of the dashboard app.layout = html.Div([ html.H1("Power and Current Data Dashboard"), dcc.Dropdown( id='time-range-selector', options=[ {'label': 'Last 6 Hours', 'value': 6}, {'label': 'Last 12 Hours', 'value': 12}, {'label': 'Last 1 Day', 'value': 24}, {'label': 'Last 2 Days', 'value': 48}, {'label': 'Last 1 Week', 'value': 168}, {'label': 'Last 1 Month', 'value': 720}, {'label': 'Last 2 Months', 'value': 1440}, {'label': 'Last 1 Year', 'value': 8760} ], value=6, clearable=False ), dcc.Graph(id='building-totals-graph'), dcc.Dropdown( id='room-selector', options=[{'label': room, 'value': room} for room in fetch_data()[1]['room_number'].unique()], value=fetch_data()[1]['room_number'].iloc[0] if not fetch_data()[1].empty else None, placeholder="Select a room" ), dcc.Graph(id='room-graph') ]) # Define callbacks to update the graphs @app.callback( Output('building-totals-graph', 'figure'), Input('time-range-selector', 'value'), Input('building-totals-graph', 'relayoutData') ) def update_building_totals_graph(time_range, relayoutData): building_totals, _ = fetch_data() building_totals = calculate_kwh(building_totals, 'total_power') # Handle time range selection end_time = datetime.now() start_time = end_time - timedelta(hours=time_range) filtered_data = building_totals[(building_totals['timestamp'] >= start_time) & (building_totals['timestamp'] <= end_time)] # Handle zoom level if relayoutData and 'xaxis.range[0]' in relayoutData: zoom_start = pd.to_datetime(relayoutData['xaxis.range[0]']) zoom_end = pd.to_datetime(relayoutData['xaxis.range[1]']) filtered_data = filtered_data[(filtered_data['timestamp'] >= zoom_start) & (filtered_data['timestamp'] <= zoom_end)] latest_data = filtered_data.iloc[-1] if not filtered_data.empty else None fig = px.line(filtered_data, x='timestamp', y=['total_current', 'total_power', 'cumulative_kwh'], title='Building Totals', labels={'value': 'Value', 'variable': 'Metric'}) if latest_data is not None: fig.update_traces( name=f"Total Current: {latest_data['total_current']} A", selector=dict(name="total_current") ) fig.update_traces( name=f"Total Power: {latest_data['total_power']} kW", selector=dict(name="total_power") ) fig.update_traces( name=f"Cumulative kWh: {round(latest_data['cumulative_kwh'], 3)} kWh", selector=dict(name="cumulative_kwh") ) return fig @app.callback( Output('room-graph', 'figure'), Input('time-range-selector', 'value'), Input('room-selector', 'value'), Input('room-graph', 'relayoutData') ) def update_room_graph(time_range, selected_room, relayoutData): if selected_room: _, room_breakdown = fetch_data() room_data = room_breakdown[room_breakdown['room_number'] == selected_room] room_data = calculate_kwh(room_data, 'power') # Handle time range selection end_time = datetime.now() start_time = end_time - timedelta(hours=time_range) filtered_data = room_data[(room_data['timestamp'] >= start_time) & (room_data['timestamp'] <= end_time)] # Handle zoom level if relayoutData and 'xaxis.range[0]' in relayoutData: zoom_start = pd.to_datetime(relayoutData['xaxis.range[0]']) zoom_end = pd.to_datetime(relayoutData['xaxis.range[1]']) filtered_data = filtered_data[(filtered_data['timestamp'] >= zoom_start) & (filtered_data['timestamp'] <= zoom_end)] latest_data = filtered_data.iloc[-1] if not filtered_data.empty else None fig = px.line(filtered_data, x='timestamp', y=['current', 'power', 'cumulative_kwh'], title=f'Room {selected_room}', labels={'value': 'Value', 'variable': 'Metric'}) if latest_data is not None: fig.update_traces( name=f"Current: {latest_data['current']} A", selector=dict(name="current") ) fig.update_traces( name=f"Power: {latest_data['power']} kW", selector=dict(name="power") ) fig.update_traces( name=f"Cumulative kWh: {round(latest_data['cumulative_kwh'], 3)} kWh", selector=dict(name="cumulative_kwh") ) return fig return px.line(title='Select a room to display its graph') # Run the app if __name__ == '__main__': app.run(host='0.0.0.0', port=8050, debug=True)