Files
LibreNMS-power-dash/dash-power_room.py
2025-05-22 10:38:50 +01:00

147 lines
5.7 KiB
Python

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)