Files
matrix64/matrix.py

188 lines
6.7 KiB
Python

#!/usr/bin/env python3
"""
Matrix64 LED Display - Main Entry Point
Displays Home Assistant and Netdata data on a 64x64 LED matrix.
Layout (Option A - Vertical Stack):
[Weather 24x24] [Out 21.5° 65%]
[In 22.3° 58%]
[12:31:05 centered]
[HDD temps corner]
"""
from samplebase import SampleBase
from rgbmatrix import graphics
import time
import datetime
from config import LED_ROWS, LED_COLS
from weather_icons import draw_weather_icon
from home_assistant import (
get_weather, get_weather_description,
get_interior_weather, get_brightness
)
from netdata import get_hdd_temps
def format_temp(value):
"""Format temperature to one decimal place."""
try:
return f"{float(value):.1f}"
except (ValueError, TypeError):
return "?"
def format_humidity(value):
"""Format humidity as integer percentage."""
try:
return f"{int(float(value))}"
except (ValueError, TypeError):
return "?"
def get_temperature_color(temp):
"""Determine color based on temperature value."""
try:
temp_value = float(temp) if isinstance(temp, (int, float)) else float(str(temp).replace('°', '').strip())
if temp_value < 0:
return graphics.Color(0, 100, 255)
elif temp_value < 10:
return graphics.Color(100, 180, 255)
elif temp_value < 20:
return graphics.Color(0, 255, 100)
elif temp_value < 30:
return graphics.Color(255, 200, 0)
else:
return graphics.Color(255, 50, 50)
except ValueError:
return graphics.Color(255, 255, 255)
class Matrix64Display(SampleBase):
"""Main display class for the LED matrix."""
def __init__(self, *args, **kwargs):
super(Matrix64Display, self).__init__(*args, **kwargs)
self.parser.add_argument("-t", "--text", help="Text", default="")
self.temperature = None
self.humidity = None
self.interior_temperature = None
self.interior_humidity = None
self.last_update = time.time()
self.weather_desc = "unknown"
self.hdd_temps = None
def update_data(self):
"""Fetch all data from APIs."""
try:
weather = get_weather()
if weather.get("temperature") is not None:
self.temperature = float(weather.get("temperature"))
self.humidity = weather.get("humidity")
self.weather_desc = get_weather_description()
interior = get_interior_weather()
if interior.get("temperature") is not None:
self.interior_temperature = float(interior.get("temperature"))
self.interior_humidity = interior.get("humidity")
except Exception as e:
print(f"Error updating data: {e}")
def update_brightness(self):
"""Update LED matrix brightness."""
try:
brightness = get_brightness()
if brightness:
self.matrix.brightness = int(float(brightness)) / 10
except Exception as e:
self.matrix.brightness = 5
def update_hdd_temps(self):
"""Update HDD temperatures."""
self.hdd_temps = get_hdd_temps()
def run(self):
"""Main display loop."""
canvas = self.matrix.CreateFrameCanvas()
# Load fonts
font_dir = "/home/pi/rpi-rgb-led-matrix/fonts"
time_font = graphics.Font()
time_font.LoadFont(f"{font_dir}/7x13.bdf")
data_font = graphics.Font()
data_font.LoadFont(f"{font_dir}/5x8.bdf")
small_font = graphics.Font()
small_font.LoadFont(f"{font_dir}/4x6.bdf")
# Colors
time_color = graphics.Color(30, 90, 220)
humidity_color = graphics.Color(80, 160, 255)
hdd_color = graphics.Color(60, 60, 60)
label_color = graphics.Color(100, 100, 100)
degree_color = graphics.Color(150, 150, 150)
percent_color = graphics.Color(100, 140, 180)
# Initial fetch
self.update_brightness()
self.update_data()
self.update_hdd_temps()
while True:
now = datetime.datetime.now()
time_str = f"{now.hour:02d}:{now.minute:02d}:{now.second:02d}"
current_time = time.time()
if current_time - self.last_update >= 60:
self.update_data()
self.update_brightness()
self.update_hdd_temps()
self.last_update = current_time
canvas.Clear()
# === Weather Icon (top-left, 24x24) ===
draw_weather_icon(canvas, 0, 0, self.weather_desc)
# === Row 1: Outdoor - "Out" label + temp + humidity ===
graphics.DrawText(canvas, small_font, 26, 6, label_color, "Out")
if self.temperature is not None:
temp_str = format_temp(self.temperature)
temp_color = get_temperature_color(self.temperature)
graphics.DrawText(canvas, small_font, 40, 6, temp_color, temp_str)
if self.humidity is not None:
hum_str = format_humidity(self.humidity)
graphics.DrawText(canvas, small_font, 55, 6, humidity_color, hum_str)
# === Row 2: Indoor - "In" label + temp + humidity ===
graphics.DrawText(canvas, small_font, 26, 14, label_color, "In")
if self.interior_temperature is not None:
temp_str = format_temp(self.interior_temperature)
temp_color = get_temperature_color(self.interior_temperature)
graphics.DrawText(canvas, small_font, 40, 14, temp_color, temp_str)
if self.interior_humidity is not None:
hum_str = format_humidity(self.interior_humidity)
graphics.DrawText(canvas, small_font, 55, 14, humidity_color, hum_str)
# === Time Display (centered, y=38) ===
time_x = (64 - len(time_str) * 7) // 2
graphics.DrawText(canvas, time_font, time_x, 38, time_color, time_str)
# === HDD Temps (bottom-right corner) ===
if self.hdd_temps and len(self.hdd_temps) > 1:
# Compact: show all temps in 2 rows, right-aligned
temps = [str(int(t[0])) for t in self.hdd_temps[1:] if t]
if len(temps) >= 3:
row1 = " ".join(temps[:3])
graphics.DrawText(canvas, small_font, 40, 52, hdd_color, row1)
if len(temps) > 3:
row2 = " ".join(temps[3:6])
graphics.DrawText(canvas, small_font, 40, 60, hdd_color, row2)
time.sleep(0.5)
canvas = self.matrix.SwapOnVSync(canvas)
if __name__ == "__main__":
display = Matrix64Display()
if not display.process():
display.print_help()