Add consumption curve to PV graph, fix text-curve overlap
This commit is contained in:
111
matrix.py
111
matrix.py
@@ -134,6 +134,7 @@ class Matrix64Display(SampleBase):
|
||||
self.tesla = None
|
||||
self.solar = None
|
||||
self.pv_history = [] # list of (hour_float, watts) from HA history
|
||||
self.consumption_history = [] # list of (hour_float, watts)
|
||||
self.last_pv_history_update = 0
|
||||
|
||||
# View state
|
||||
@@ -678,7 +679,7 @@ class Matrix64Display(SampleBase):
|
||||
graphics.DrawText(canvas, small_font, 58, 62, label_color, "A")
|
||||
|
||||
def draw_view_6(self, canvas, fonts, colors):
|
||||
"""Vista Curva PV - Today's photovoltaic production curve"""
|
||||
"""Vista Curva PV - Today's production and consumption curves"""
|
||||
time_font, data_font, small_font = fonts
|
||||
time_color, humidity_color, hdd_color, label_color, line_color = colors
|
||||
|
||||
@@ -688,14 +689,14 @@ class Matrix64Display(SampleBase):
|
||||
graphics.DrawText(canvas, data_font, title_x, 8, time_color, title)
|
||||
graphics.DrawLine(canvas, 0, 10, 63, 10, line_color)
|
||||
|
||||
# Graph area: x=2..61 (60px), y=14..57 (44px)
|
||||
# Graph area: x=2..61 (60px), y=18..57 (40px) - top margin for labels
|
||||
graph_x = 2
|
||||
graph_w = 60
|
||||
graph_y_top = 14
|
||||
graph_y_top = 18
|
||||
graph_y_bot = 57
|
||||
graph_h = graph_y_bot - graph_y_top
|
||||
|
||||
# Time axis: 6:00 to 21:00 (15 hours of daylight)
|
||||
# Time axis: 6:00 to 21:00
|
||||
t_start = 6.0
|
||||
t_end = 21.0
|
||||
t_range = t_end - t_start
|
||||
@@ -710,62 +711,72 @@ class Matrix64Display(SampleBase):
|
||||
graphics.DrawText(canvas, small_font, graph_x + 38, 63, label_color, "15")
|
||||
graphics.DrawText(canvas, small_font, graph_x + 52, 63, label_color, "20")
|
||||
|
||||
if not self.pv_history:
|
||||
graphics.DrawText(canvas, small_font, 10, 36, label_color, "Sin datos")
|
||||
if not self.pv_history and not self.consumption_history:
|
||||
graphics.DrawText(canvas, small_font, 10, 38, label_color, "Sin datos")
|
||||
return
|
||||
|
||||
# Find max production for scaling
|
||||
max_prod = max(p for _, p in self.pv_history)
|
||||
if max_prod <= 0:
|
||||
max_prod = 1
|
||||
# Find max across both datasets for shared scale
|
||||
max_val = 1
|
||||
if self.pv_history:
|
||||
max_val = max(max_val, max(p for _, p in self.pv_history))
|
||||
if self.consumption_history:
|
||||
max_val = max(max_val, max(p for _, p in self.consumption_history))
|
||||
|
||||
# Draw max value label
|
||||
if max_prod >= 1000:
|
||||
max_label = f"{max_prod / 1000:.1f}kW"
|
||||
# Labels above graph area (with margin)
|
||||
if max_val >= 1000:
|
||||
max_label = f"{max_val / 1000:.1f}kW"
|
||||
else:
|
||||
max_label = f"{int(max_prod)}W"
|
||||
graphics.DrawText(canvas, small_font, graph_x + 1, graph_y_top + 5, graphics.Color(255, 200, 0), max_label)
|
||||
max_label = f"{int(max_val)}W"
|
||||
graphics.DrawText(canvas, small_font, graph_x + 1, graph_y_top - 2, label_color, max_label)
|
||||
|
||||
# Plot curve - bucket data points into pixel columns
|
||||
buckets = [[] for _ in range(graph_w)]
|
||||
for t, p in self.pv_history:
|
||||
if t_start <= t <= t_end:
|
||||
col = int((t - t_start) / t_range * (graph_w - 1))
|
||||
col = max(0, min(graph_w - 1, col))
|
||||
buckets[col].append(p)
|
||||
# Current values in top-right
|
||||
if self.solar:
|
||||
cur_pv = self.solar.get("production")
|
||||
if cur_pv is not None:
|
||||
graphics.DrawText(canvas, small_font, 34, graph_y_top - 2, graphics.Color(255, 200, 0), f"{int(cur_pv)}W")
|
||||
|
||||
prev_y = None
|
||||
pv_color = graphics.Color(255, 200, 0)
|
||||
fill_color = graphics.Color(60, 50, 0)
|
||||
def bucket_data(history):
|
||||
buckets = [[] for _ in range(graph_w)]
|
||||
for t, p in history:
|
||||
if t_start <= t <= t_end:
|
||||
col = int((t - t_start) / t_range * (graph_w - 1))
|
||||
col = max(0, min(graph_w - 1, col))
|
||||
buckets[col].append(p)
|
||||
return buckets
|
||||
|
||||
for col in range(graph_w):
|
||||
if buckets[col]:
|
||||
avg_p = sum(buckets[col]) / len(buckets[col])
|
||||
pixel_y = graph_y_bot - int((avg_p / max_prod) * graph_h)
|
||||
pixel_y = max(graph_y_top, min(graph_y_bot, pixel_y))
|
||||
def draw_curve(buckets, r, g, b, fill_r, fill_g, fill_b, fill=True):
|
||||
prev_y = None
|
||||
for col in range(graph_w):
|
||||
if buckets[col]:
|
||||
avg_p = sum(buckets[col]) / len(buckets[col])
|
||||
pixel_y = graph_y_bot - int((avg_p / max_val) * graph_h)
|
||||
pixel_y = max(graph_y_top, min(graph_y_bot, pixel_y))
|
||||
|
||||
# Fill area under curve
|
||||
if pixel_y < graph_y_bot:
|
||||
for fy in range(pixel_y + 1, graph_y_bot):
|
||||
canvas.SetPixel(graph_x + col, fy, 60, 50, 0)
|
||||
# Fill area under curve
|
||||
if fill and pixel_y < graph_y_bot:
|
||||
for fy in range(pixel_y + 1, graph_y_bot):
|
||||
canvas.SetPixel(graph_x + col, fy, fill_r, fill_g, fill_b)
|
||||
|
||||
# Draw point
|
||||
canvas.SetPixel(graph_x + col, pixel_y, 255, 200, 0)
|
||||
# Draw point
|
||||
canvas.SetPixel(graph_x + col, pixel_y, r, g, b)
|
||||
|
||||
# Connect to previous point
|
||||
if prev_y is not None:
|
||||
dy = pixel_y - prev_y
|
||||
steps = max(abs(dy), 1)
|
||||
for s in range(1, steps):
|
||||
iy = prev_y + int(dy * s / steps)
|
||||
canvas.SetPixel(graph_x + col, iy, 255, 200, 0)
|
||||
# Connect to previous
|
||||
if prev_y is not None:
|
||||
dy = pixel_y - prev_y
|
||||
steps = max(abs(dy), 1)
|
||||
for s in range(1, steps):
|
||||
iy = prev_y + int(dy * s / steps)
|
||||
canvas.SetPixel(graph_x + col, iy, r, g, b)
|
||||
|
||||
prev_y = pixel_y
|
||||
prev_y = pixel_y
|
||||
|
||||
# Current production value
|
||||
if self.solar and self.solar.get("production") is not None:
|
||||
cur = int(self.solar["production"])
|
||||
graphics.DrawText(canvas, small_font, 30, graph_y_top + 5, graphics.Color(200, 200, 200), f"{cur}W")
|
||||
# Draw consumption first (behind), then production (on top)
|
||||
if self.consumption_history:
|
||||
cons_buckets = bucket_data(self.consumption_history)
|
||||
draw_curve(cons_buckets, 80, 160, 255, 15, 25, 50)
|
||||
if self.pv_history:
|
||||
pv_buckets = bucket_data(self.pv_history)
|
||||
draw_curve(pv_buckets, 255, 200, 0, 60, 50, 0)
|
||||
|
||||
if self.auto_cycle:
|
||||
graphics.DrawText(canvas, small_font, 58, 62, label_color, "A")
|
||||
@@ -809,7 +820,9 @@ class Matrix64Display(SampleBase):
|
||||
# Update PV history every 5 minutes
|
||||
if current_time - self.last_pv_history_update >= 300:
|
||||
try:
|
||||
self.pv_history = get_solar_history()
|
||||
history = get_solar_history()
|
||||
self.pv_history = history.get("production", [])
|
||||
self.consumption_history = history.get("consumption", [])
|
||||
except Exception as e:
|
||||
print(f"Error updating PV history: {e}")
|
||||
self.last_pv_history_update = current_time
|
||||
|
||||
Reference in New Issue
Block a user