2.2 MiB
2.2 MiB
In [37]:
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.0x54ef44100140aacb_humidity",
# limit=100,
start_time=datetime(2026, 1, 22, 0, 0, tzinfo=local_tz),
end_time=datetime(2026, 1, 25, 20, 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")
# Reverse dataframe for chronological order
df = df.iloc[::-1].reset_index(drop=True)
df.info()<class 'pandas.DataFrame'> RangeIndex: 3647 entries, 0 to 3646 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 time 3647 non-null datetime64[ns, Europe/Copenhagen] 1 state 3643 non-null float64 dtypes: datetime64[ns, Europe/Copenhagen](1), float64(1) memory usage: 57.1 KB
In [38]:
import plotly.express as px
# Create interactive line plot
fig = px.line(
df,
x="time",
y="state",
title="Humidity Over Time",
labels={"time": "Time", "state": "Humidity (%)"},
)
# Customize hover template
fig.update_traces(hovertemplate="<b>Time:</b> %{x}<br><b>Humidity:</b> %{y:.2f} %<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 [39]:
# 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")["state"].mean()
# Create plot with both raw and smoothed data
fig = px.line(
df,
x="time",
y=["state", "state_smoothed"],
title=f"Humidity - Raw vs Smoothed (window={window_size})",
labels={"time": "Time", "value": "Humidity (%)"},
)
# 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>Humidity:</b> %{y:.2f} %<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 [40]:
# Calculate baseline humidity as mean of last hours
window_size = "24h"
df["baseline_humidity"] = df.rolling(window=window_size, on="time")["state"].mean()
# Create plot with both raw and baseline
fig = px.line(
df,
x="time",
y=["state", "baseline_humidity"],
title=f"Humidity - Smoothed vs Baseline (window={window_size})",
labels={"time": "Time", "value": "Humidity (%)"},
)
# 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>Humidity:</b> %{y:.2f} %<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 [41]:
# make binary timeseries: above baseline
df["above_baseline"] = (df["state"] > df["baseline_humidity"]).astype(int)
import plotly.graph_objects as go
fig = go.Figure()
# Add smoothed humidity (primary y-axis)
fig.add_trace(go.Scatter(x=df["time"], y=df["state"], mode="lines", name="Smoothed", yaxis="y1"))
fig.add_trace(go.Scatter(x=df["time"], y=df["baseline_humidity"], mode="lines", name="Baseline", yaxis="y1"))
# Add above baseline (secondary y-axis)
fig.add_trace(go.Scatter(x=df["time"], y=df["above_baseline"], mode="lines", name="Above Baseline", yaxis="y2"))
fig.update_layout(
title=f"Humidity - Smoothed vs Baseline (window={window_size})",
xaxis=dict(title="Time"),
yaxis=dict(title="Humidity (%)", side="left"),
yaxis2=dict(title="Above Baseline", overlaying="y", side="right", range=[-0.05, 1.05]),
legend_title_text="Data Type",
height=600,
hovermode="x unified"
)
fig.update_traces(hovertemplate="<b>Time:</b> %{x}<br><b>Value:</b> %{y}<extra></extra>")
fig.show()[Data output - unsupported data type map[string]interface {} for mime type application/vnd.plotly.v1+json]
In [42]:
# Get occupancy data
import pandas as pd
from datetime import datetime
from zoneinfo import ZoneInfo
from utils.database import get_state
local_tz = ZoneInfo("Europe/Copenhagen")
df_occupancy = get_state(
entity_id="binary_sensor.0xd44867fffe49155d_occupancy",
# limit=100,
start_time=datetime(2026, 1, 22, 0, 0, tzinfo=local_tz),
end_time=datetime(2026, 1, 25, 20, 0, tzinfo=local_tz),
)
# Convert time to Copenhagen timezone
df_occupancy["time"] = df_occupancy["time"].dt.tz_convert(local_tz)
# Keep only states 'on' and 'off'
df_occupancy = df_occupancy[df_occupancy["state"].isin(["on", "off"])].copy()
# Convert state to numeric: 'on'->1, 'off'->0
df_occupancy["state"] = df_occupancy["state"].map({"on": 1, "off": 0})
# Reverse dataframe for chronological order
df_occupancy = df_occupancy.iloc[::-1].reset_index(drop=True)
# Resample to 1 minute intervals, forward fill to propagate last known state
df_occupancy = df_occupancy.set_index("time").resample("1min").ffill().reset_index()
# Make every detected occupancy last for 10 minutes
on_indices = df_occupancy.index[df_occupancy["state"] == 1].tolist()
for idx in on_indices:
end_idx = min(idx + 10, len(df_occupancy))
df_occupancy.loc[idx:end_idx - 1, "state"] = 1
df_occupancy.info()<class 'pandas.DataFrame'> RangeIndex: 4106 entries, 0 to 4105 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 time 4106 non-null datetime64[ns, Europe/Copenhagen] 1 state 4105 non-null float64 dtypes: datetime64[ns, Europe/Copenhagen](1), float64(1) memory usage: 64.3 KB
In [43]:
import plotly.express as px
# Create interactive line plot
fig = px.line(
df_occupancy,
x="time",
y="state",
title="Occupancy Over Time",
labels={"time": "Time", "state": "Occupancy"},
)
# Customize hover template
fig.update_traces(hovertemplate="<b>Time:</b> %{x}<br><b>Occupancy:</b> %{y}<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 [44]:
# Plot occupancy and above baseline humidity on dual y-axes
import plotly.graph_objects as go
fig = go.Figure()
# Add occupancy state (primary y-axis)
fig.add_trace(go.Scatter(x=df_occupancy["time"], y=df_occupancy["state"], mode="lines", name="Occupancy", yaxis="y1"))
# Add above baseline humidity (secondary y-axis)
fig.add_trace(go.Scatter(x=df["time"], y=df["above_baseline"], mode="lines", name="Above Baseline Humidity", yaxis="y2"))
fig.update_layout(
title="Occupancy and Above Baseline Humidity Over Time",
xaxis=dict(title="Time"),
yaxis=dict(title="Occupancy", side="left", range=[-0.05, 1.05]),
yaxis2=dict(title="Above Baseline Humidity", overlaying="y", side="right", range=[-0.05, 1.05]),
legend_title_text="Data Type",
height=600,
hovermode="x unified"
)
fig.update_traces(hovertemplate="<b>Time:</b> %{x}<br><b>Value:</b> %{y}<extra></extra>")
fig.show()[Data output - unsupported data type map[string]interface {} for mime type application/vnd.plotly.v1+json]