715 KiB
715 KiB
In [1]:
import pandas as pd
from datetime import datetime
from zoneinfo import ZoneInfo
from utils.database import get_state
local_tz = ZoneInfo("Europe/Copenhagen")
df = get_state(
entity_id="sensor.dishwasher_power",
# limit=100,
start_time=datetime(2026, 4, 1, 0, 0, tzinfo=local_tz),
end_time=datetime(2026, 4, 13, 21, 0, tzinfo=local_tz),
)
# Convert time to Copenhagen timezone
df["time"] = df["time"].dt.tz_convert(local_tz)
# Convert state to numeric, forcing errors to NaN
df["state"] = pd.to_numeric(df["state"], errors="coerce")
df.head()Out [1]:
| time | state | |
|---|---|---|
| 0 | 2026-04-13 19:11:26.464020+02:00 | 0.0 |
| 1 | 2026-04-13 19:10:23.729820+02:00 | NaN |
| 2 | 2026-04-13 13:23:15.374583+02:00 | 0.0 |
| 3 | 2026-04-13 12:34:36.715038+02:00 | 0.0 |
| 4 | 2026-04-13 12:34:34.650176+02:00 | 1.6 |
In [2]:
import plotly.express as px
# Create interactive line plot
fig = px.line(
df,
x="time",
y="state",
title="Power Consumption Over Time",
labels={"time": "Time", "state": "Power (W)"},
)
# Customize hover template
fig.update_traces(hovertemplate="<b>Time:</b> %{x}<br><b>Power:</b> %{y:.2f} W<extra></extra>")
fig.update_layout(height=600, hovermode="x unified")
fig.show()[Data output - unsupported data type map[string]interface {} for mime type application/vnd.plotly.v1+json]
In [3]:
# Calculate running average with time-based window
window_size = "1min" # Can use: '1min', '5min', '15min', '1h', etc.
df["state_smoothed"] = df.rolling(window=window_size, on="time", center=True)["state"].mean()
# Create plot with both raw and smoothed data
fig = px.line(
df,
x="time",
y=["state", "state_smoothed"],
title=f"Power Consumption - Raw vs Smoothed (window={window_size})",
labels={"time": "Time", "value": "Power (W)"},
)
# Update trace names
fig.data[0].name = "Raw"
fig.data[1].name = "Smoothed"
# Customize hover template
fig.update_traces(hovertemplate="<b>Time:</b> %{x}<br><b>Power:</b> %{y:.2f} W<extra></extra>")
fig.update_layout(height=600, hovermode="x unified", legend_title_text="Data Type")
fig.show()[Data output - unsupported data type map[string]interface {} for mime type application/vnd.plotly.v1+json]
In [4]:
# Calculate running average with time-based window
window_size = "5min" # Can use: '1min', '5min', '15min', '1h', etc.
df["state_baseline"] = df.rolling(window=window_size, on="time")["state"].mean()
# Create plot with both raw and smoothed data
fig = px.line(
df,
x="time",
y=["state", "state_baseline"],
title=f"Power Consumption - Smoothed vs Baseline (window={window_size})",
labels={"time": "Time", "value": "Power (W)"},
)
# Update trace names
fig.data[0].name = "Smoothed"
fig.data[1].name = "Baseline"
# Customize hover template
fig.update_traces(hovertemplate="<b>Time:</b> %{x}<br><b>Power:</b> %{y:.2f} W<extra></extra>")
fig.update_layout(height=600, hovermode="x unified", legend_title_text="Data Type")
fig.show()[Data output - unsupported data type map[string]interface {} for mime type application/vnd.plotly.v1+json]
In [6]:
# Identify periods when power above 1W
threshold = 1
# Create boolean mask when starting burner
df["above_threshold"] = df["state_baseline"] > threshold
# Identify high-to-low transitions (when power drops from >1W to ≤1W)
# This occurs when current row is below threshold AND previous row was above threshold
df["high_to_low"] = (df["above_threshold"]) & df["above_threshold"].ne(df["above_threshold"].shift())
# Extract the transition moments
transitions = df[df["high_to_low"]][["time", "state_baseline"]]
print(f"Found {len(transitions)} high-to-low transitions:\n")
print(transitions.to_string(index=False))
# Show statistics about the transitions
if len(transitions) > 0:
print(f"\n\nTransition Summary:")
print(f" Average power at transition: {transitions['state_baseline'].mean():.2f}W")
print(f" Min power at transition: {transitions['state_baseline'].min():.2f}W")
print(f" Max power at transition: {transitions['state_baseline'].max():.2f}W")Found 4 high-to-low transitions:
time state_baseline
2026-04-13 12:34:00.044517+02:00 3.933333
2026-04-10 00:10:10.660551+02:00 1.200000
2026-04-07 01:05:35.778996+02:00 1.300000
2026-04-04 13:55:00.034284+02:00 2.220000
Transition Summary:
Average power at transition: 2.16W
Min power at transition: 1.20W
Max power at transition: 3.93W
In [7]:
# Visualize the high-to-low transitions on the power consumption plot
import plotly.graph_objects as go
fig = go.Figure()
# Add the main power consumption line
fig.add_trace(go.Scatter(
x=df["time"],
y=df["state_baseline"],
mode="lines",
name="Power Consumption Baseline",
line=dict(color="blue", width=1),
hovertemplate="<b>Time:</b> %{x}<br><b>Power:</b> %{y:.2f} W<extra></extra>"
))
# Add threshold line
fig.add_hline(y=threshold, line_dash="dash", line_color="gray",
annotation_text=f"Threshold ({threshold}W)",
annotation_position="right")
# Add markers for high-to-low transitions
if len(transitions) > 0:
fig.add_trace(go.Scatter(
x=transitions["time"],
y=transitions["state_baseline"],
mode="markers",
name="High→Low Transitions",
marker=dict(color="red", size=10, symbol="circle"),
hovertemplate="<b>Transition Time:</b> %{x}<br><b>Power:</b> %{y:.2f} W<extra></extra>"
))
fig.update_layout(
title="Power Consumption with High-to-Low Transitions",
xaxis_title="Time",
yaxis_title="Power (W)",
height=600,
hovermode="x unified"
)
fig.show()[Data output - unsupported data type map[string]interface {} for mime type application/vnd.plotly.v1+json]
Cell:
[Cell type raw - unsupported, skipped]
Cell:
[Cell type raw - unsupported, skipped]