Initial commit
This commit is contained in:
16
.env.example
Normal file
16
.env.example
Normal file
@@ -0,0 +1,16 @@
|
||||
# Home Assistant Configuration
|
||||
HA_TOKEN=your_home_assistant_long_lived_access_token
|
||||
HASS_URL=https://your-home-assistant-url.com
|
||||
|
||||
# Entity IDs
|
||||
BRIGHTNESS_ENTITY_ID=sensor.your_brightness_sensor
|
||||
WEATHER_ENTITY_ID=weather.forecast_home
|
||||
INTERIOR_TEMP_ENTITY_ID=sensor.your_temperature_sensor
|
||||
INTERIOR_HUMIDITY_ENTITY_ID=sensor.your_humidity_sensor
|
||||
|
||||
# Netdata Configuration
|
||||
NETDATA_URL=http://your-netdata-host:19999
|
||||
|
||||
# LED Matrix Configuration
|
||||
LED_ROWS=64
|
||||
LED_COLS=64
|
||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Environment variables (contains secrets)
|
||||
.env
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
|
||||
# Virtual environments
|
||||
venv/
|
||||
.venv/
|
||||
ENV/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
26
config.py
Normal file
26
config.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Configuration module for Matrix64 LED display.
|
||||
Loads environment variables from .env file.
|
||||
"""
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# Home Assistant Configuration
|
||||
HA_TOKEN = os.getenv('HA_TOKEN')
|
||||
HASS_URL = os.getenv('HASS_URL')
|
||||
|
||||
# Entity IDs
|
||||
BRIGHTNESS_ENTITY_ID = os.getenv('BRIGHTNESS_ENTITY_ID')
|
||||
WEATHER_ENTITY_ID = os.getenv('WEATHER_ENTITY_ID')
|
||||
INTERIOR_TEMP_ENTITY_ID = os.getenv('INTERIOR_TEMP_ENTITY_ID')
|
||||
INTERIOR_HUMIDITY_ENTITY_ID = os.getenv('INTERIOR_HUMIDITY_ENTITY_ID')
|
||||
|
||||
# Netdata Configuration
|
||||
NETDATA_URL = os.getenv('NETDATA_URL')
|
||||
|
||||
# LED Matrix Configuration
|
||||
LED_ROWS = int(os.getenv('LED_ROWS', '64'))
|
||||
LED_COLS = int(os.getenv('LED_COLS', '64'))
|
||||
63
home_assistant.py
Normal file
63
home_assistant.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
Home Assistant API client for Matrix64 LED display.
|
||||
"""
|
||||
import requests
|
||||
from config import (
|
||||
HA_TOKEN, HASS_URL,
|
||||
BRIGHTNESS_ENTITY_ID, WEATHER_ENTITY_ID,
|
||||
INTERIOR_TEMP_ENTITY_ID, INTERIOR_HUMIDITY_ENTITY_ID
|
||||
)
|
||||
|
||||
|
||||
def get_entity_value(entity_id):
|
||||
"""Fetch entity state from Home Assistant."""
|
||||
headers = {
|
||||
"Authorization": f"Bearer {HA_TOKEN}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = f"{HASS_URL}/api/states/{entity_id}"
|
||||
try:
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
print(f"Failed to retrieve entity {entity_id}: {response.text}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error fetching {entity_id}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_weather():
|
||||
"""Get weather attributes from Home Assistant."""
|
||||
weather = get_entity_value(WEATHER_ENTITY_ID)
|
||||
if weather:
|
||||
return weather.get("attributes", {})
|
||||
return {}
|
||||
|
||||
|
||||
def get_weather_description():
|
||||
"""Get current weather state/condition."""
|
||||
weather = get_entity_value(WEATHER_ENTITY_ID)
|
||||
if weather:
|
||||
return weather.get("state", "unknown")
|
||||
return "unknown"
|
||||
|
||||
|
||||
def get_interior_weather():
|
||||
"""Get interior temperature and humidity from BTH01-3132 sensor."""
|
||||
temp_data = get_entity_value(INTERIOR_TEMP_ENTITY_ID)
|
||||
humidity_data = get_entity_value(INTERIOR_HUMIDITY_ENTITY_ID)
|
||||
|
||||
temperature = temp_data.get("state") if temp_data else None
|
||||
humidity = humidity_data.get("state") if humidity_data else None
|
||||
|
||||
return {"humidity": humidity, "temperature": temperature}
|
||||
|
||||
|
||||
def get_brightness():
|
||||
"""Get screen brightness value."""
|
||||
brightness = get_entity_value(BRIGHTNESS_ENTITY_ID)
|
||||
if brightness:
|
||||
return brightness.get("state")
|
||||
return None
|
||||
56
install.sh
Normal file
56
install.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
# Installation script for Matrix64 LED Display
|
||||
# Run this once on your Raspberry Pi
|
||||
|
||||
set -e
|
||||
|
||||
INSTALL_DIR="/opt/matrix64"
|
||||
REPO_URL="https://gitlab.kingstudio.es/jocaru/matrix64"
|
||||
|
||||
echo "=== Matrix64 LED Display Installation ==="
|
||||
|
||||
# Clone or update repository
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
echo "Directory exists, updating..."
|
||||
cd "$INSTALL_DIR"
|
||||
git pull origin master
|
||||
else
|
||||
echo "Cloning repository..."
|
||||
git clone "$REPO_URL" "$INSTALL_DIR"
|
||||
cd "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
# Install Python dependencies
|
||||
echo "Installing Python dependencies..."
|
||||
pip3 install -r requirements.txt
|
||||
|
||||
# Create .env from example if not exists
|
||||
if [ ! -f "$INSTALL_DIR/.env" ]; then
|
||||
echo "Creating .env from template..."
|
||||
cp "$INSTALL_DIR/.env.example" "$INSTALL_DIR/.env"
|
||||
echo "IMPORTANT: Edit $INSTALL_DIR/.env with your actual values!"
|
||||
fi
|
||||
|
||||
# Install systemd service
|
||||
echo "Installing systemd service..."
|
||||
cp "$INSTALL_DIR/matrix64.service" /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable matrix64
|
||||
|
||||
# Make update script executable
|
||||
chmod +x "$INSTALL_DIR/update.sh"
|
||||
|
||||
# Set up cron job for auto-updates (every 5 minutes)
|
||||
CRON_JOB="*/5 * * * * $INSTALL_DIR/update.sh"
|
||||
(crontab -l 2>/dev/null | grep -v "matrix64/update.sh"; echo "$CRON_JOB") | crontab -
|
||||
|
||||
echo ""
|
||||
echo "=== Installation Complete ==="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Edit /opt/matrix64/.env with your Home Assistant token and URLs"
|
||||
echo "2. Start the service: sudo systemctl start matrix64"
|
||||
echo "3. Check status: sudo systemctl status matrix64"
|
||||
echo "4. View logs: sudo journalctl -u matrix64 -f"
|
||||
echo ""
|
||||
echo "Auto-updates are enabled (every 5 minutes via cron)"
|
||||
153
matrix.py
Normal file
153
matrix.py
Normal file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Matrix64 LED Display - Main Entry Point
|
||||
|
||||
Displays Home Assistant and Netdata data on a 64x64 LED matrix.
|
||||
"""
|
||||
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 get_temperature_color(temp):
|
||||
"""Determine color based on temperature value."""
|
||||
try:
|
||||
temp_value = float(str(temp).replace('°C', '').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="The text to scroll on the RGB LED panel",
|
||||
default="Hello world!"
|
||||
)
|
||||
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 data
|
||||
weather = get_weather()
|
||||
if weather.get("temperature") is not None:
|
||||
self.temperature = f'{weather.get("temperature")}°C'
|
||||
self.humidity = weather.get("humidity")
|
||||
self.weather_desc = get_weather_description()
|
||||
|
||||
# Interior data
|
||||
interior = get_interior_weather()
|
||||
if interior.get("temperature") is not None:
|
||||
self.interior_temperature = f'{interior.get("temperature")}°C'
|
||||
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
|
||||
print(f"Error updating brightness: {e}")
|
||||
|
||||
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 = graphics.Font()
|
||||
font.LoadFont("../../../fonts/7x13.bdf")
|
||||
temp_font = graphics.Font()
|
||||
temp_font.LoadFont("../../../fonts/5x8.bdf")
|
||||
|
||||
text_color = graphics.Color(20, 75, 200)
|
||||
|
||||
# Initial data 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()
|
||||
|
||||
# Update every 60 seconds
|
||||
if current_time - self.last_update >= 60:
|
||||
self.update_data()
|
||||
self.update_brightness()
|
||||
self.update_hdd_temps()
|
||||
self.last_update = current_time
|
||||
|
||||
canvas.Clear()
|
||||
|
||||
# Time display
|
||||
graphics.DrawText(canvas, font, 4, 42, text_color, time_str)
|
||||
|
||||
# Weather icon
|
||||
draw_weather_icon(canvas, 0, 0, self.weather_desc)
|
||||
|
||||
# Outdoor temperature (right-aligned)
|
||||
if self.temperature:
|
||||
color = get_temperature_color(self.temperature)
|
||||
length = graphics.DrawText(canvas, temp_font, 0, 0, color, self.temperature)
|
||||
graphics.DrawText(canvas, temp_font, 64 - length, 8, color, self.temperature)
|
||||
|
||||
# Interior temperature (right-aligned)
|
||||
if self.interior_temperature:
|
||||
color = get_temperature_color(self.interior_temperature)
|
||||
length = graphics.DrawText(canvas, temp_font, 0, 0, color, self.interior_temperature)
|
||||
graphics.DrawText(canvas, temp_font, 64 - length, 16, color, self.interior_temperature)
|
||||
|
||||
# HDD temperatures
|
||||
if self.hdd_temps and len(self.hdd_temps) > 4:
|
||||
row1 = " ".join(str(t[0])[:2] for t in self.hdd_temps[1:4])
|
||||
row2 = " ".join(str(t[0])[:2] for t in self.hdd_temps[4:])
|
||||
graphics.DrawText(canvas, temp_font, 12, 52, text_color, row1)
|
||||
graphics.DrawText(canvas, temp_font, 12, 60, text_color, row2)
|
||||
|
||||
time.sleep(0.5)
|
||||
canvas = self.matrix.SwapOnVSync(canvas)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
display = Matrix64Display()
|
||||
if not display.process():
|
||||
display.print_help()
|
||||
17
matrix64.service
Normal file
17
matrix64.service
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Matrix64 LED Display
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/matrix64
|
||||
ExecStart=/usr/bin/python3 /opt/matrix64/matrix.py --led-rows=64 --led-cols=64
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
17
netdata.py
Normal file
17
netdata.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Netdata API client for Matrix64 LED display.
|
||||
"""
|
||||
import requests
|
||||
from config import NETDATA_URL
|
||||
|
||||
|
||||
def get_hdd_temps():
|
||||
"""Get HDD temperatures from Netdata."""
|
||||
try:
|
||||
url = f"{NETDATA_URL}/api/v2/data?contexts=hddtemp.disk_temperature&points=1&group_by=instance"
|
||||
response = requests.get(url, timeout=10)
|
||||
hdd_temps = response.json()['result']['data'][0]
|
||||
return hdd_temps
|
||||
except Exception as e:
|
||||
print(f"Error fetching HDD temps: {e}")
|
||||
return None
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
python-dotenv>=1.0.0
|
||||
requests>=2.28.0
|
||||
29
update.sh
Normal file
29
update.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# Update script for Matrix64 LED Display
|
||||
# Pulls latest changes from git and restarts service if updated
|
||||
|
||||
INSTALL_DIR="/opt/matrix64"
|
||||
LOGFILE="/var/log/matrix64-update.log"
|
||||
|
||||
cd "$INSTALL_DIR" || exit 1
|
||||
|
||||
# Fetch latest changes
|
||||
git fetch origin
|
||||
|
||||
# Check if there are updates
|
||||
LOCAL=$(git rev-parse HEAD)
|
||||
REMOTE=$(git rev-parse origin/master)
|
||||
|
||||
if [ "$LOCAL" != "$REMOTE" ]; then
|
||||
echo "$(date): Updates found, pulling changes..." >> "$LOGFILE"
|
||||
git pull origin master >> "$LOGFILE" 2>&1
|
||||
|
||||
# Install any new dependencies
|
||||
pip3 install -r requirements.txt >> "$LOGFILE" 2>&1
|
||||
|
||||
# Restart the service
|
||||
systemctl restart matrix64
|
||||
echo "$(date): Service restarted" >> "$LOGFILE"
|
||||
else
|
||||
echo "$(date): No updates" >> "$LOGFILE"
|
||||
fi
|
||||
236
weather_icons.py
Normal file
236
weather_icons.py
Normal file
@@ -0,0 +1,236 @@
|
||||
"""
|
||||
Weather icon drawing functions for 64x64 LED matrix.
|
||||
All icons are designed for a 24x24 pixel area.
|
||||
"""
|
||||
from rgbmatrix import graphics
|
||||
|
||||
|
||||
def draw_sunny(canvas, x, y):
|
||||
"""Draw a sun with rays - clear/sunny weather."""
|
||||
sun_color = graphics.Color(255, 200, 0)
|
||||
ray_color = graphics.Color(255, 140, 0)
|
||||
|
||||
# Draw sun rays (8 directions)
|
||||
graphics.DrawLine(canvas, x + 12, y + 2, x + 12, y + 5, ray_color)
|
||||
graphics.DrawLine(canvas, x + 12, y + 19, x + 12, y + 22, ray_color)
|
||||
graphics.DrawLine(canvas, x + 2, y + 12, x + 5, y + 12, ray_color)
|
||||
graphics.DrawLine(canvas, x + 19, y + 12, x + 22, y + 12, ray_color)
|
||||
graphics.DrawLine(canvas, x + 5, y + 5, x + 7, y + 7, ray_color)
|
||||
graphics.DrawLine(canvas, x + 17, y + 17, x + 19, y + 19, ray_color)
|
||||
graphics.DrawLine(canvas, x + 5, y + 19, x + 7, y + 17, ray_color)
|
||||
graphics.DrawLine(canvas, x + 17, y + 7, x + 19, y + 5, ray_color)
|
||||
|
||||
# Draw filled sun
|
||||
for r in range(6, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 12, y + 12, r, sun_color)
|
||||
|
||||
|
||||
def draw_clear_night(canvas, x, y):
|
||||
"""Draw a crescent moon with stars."""
|
||||
moon_color = graphics.Color(255, 255, 200)
|
||||
|
||||
# Draw moon
|
||||
for r in range(7, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 10, y + 12, r, moon_color)
|
||||
|
||||
# Draw stars
|
||||
canvas.SetPixel(x + 20, y + 6, 255, 255, 255)
|
||||
canvas.SetPixel(x + 18, y + 10, 255, 255, 255)
|
||||
canvas.SetPixel(x + 22, y + 14, 255, 255, 255)
|
||||
canvas.SetPixel(x + 19, y + 18, 255, 255, 255)
|
||||
|
||||
|
||||
def _draw_cloud_shape(canvas, x, y, color):
|
||||
"""Helper to draw a cloud shape."""
|
||||
for r in range(4, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 4, y + 4, r, color)
|
||||
for r in range(5, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 10, y + 3, r, color)
|
||||
for r in range(4, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 15, y + 5, r, color)
|
||||
for r in range(3, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 6, y + 7, r, color)
|
||||
for r in range(3, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 12, y + 7, r, color)
|
||||
|
||||
|
||||
def draw_partly_cloudy(canvas, x, y):
|
||||
"""Draw sun partially covered by cloud."""
|
||||
sun_color = graphics.Color(255, 200, 0)
|
||||
ray_color = graphics.Color(255, 140, 0)
|
||||
cloud_color = graphics.Color(220, 220, 220)
|
||||
|
||||
# Draw partial sun
|
||||
graphics.DrawLine(canvas, x + 8, y + 2, x + 8, y + 4, ray_color)
|
||||
graphics.DrawLine(canvas, x + 2, y + 8, x + 4, y + 8, ray_color)
|
||||
graphics.DrawLine(canvas, x + 3, y + 3, x + 5, y + 5, ray_color)
|
||||
for r in range(4, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 8, y + 8, r, sun_color)
|
||||
|
||||
# Draw cloud
|
||||
_draw_cloud_shape(canvas, x + 6, y + 10, cloud_color)
|
||||
|
||||
|
||||
def draw_partly_cloudy_night(canvas, x, y):
|
||||
"""Draw moon partially covered by cloud."""
|
||||
moon_color = graphics.Color(255, 255, 200)
|
||||
cloud_color = graphics.Color(180, 180, 200)
|
||||
|
||||
for r in range(4, 0, -1):
|
||||
graphics.DrawCircle(canvas, x + 8, y + 8, r, moon_color)
|
||||
_draw_cloud_shape(canvas, x + 6, y + 10, cloud_color)
|
||||
|
||||
|
||||
def draw_cloudy(canvas, x, y):
|
||||
"""Draw overcast clouds."""
|
||||
cloud_light = graphics.Color(200, 200, 200)
|
||||
cloud_dark = graphics.Color(150, 150, 150)
|
||||
|
||||
_draw_cloud_shape(canvas, x + 2, y + 6, cloud_dark)
|
||||
_draw_cloud_shape(canvas, x + 6, y + 10, cloud_light)
|
||||
|
||||
|
||||
def draw_rainy(canvas, x, y):
|
||||
"""Draw cloud with rain drops."""
|
||||
cloud_color = graphics.Color(150, 150, 170)
|
||||
rain_color = graphics.Color(100, 150, 255)
|
||||
|
||||
_draw_cloud_shape(canvas, x + 4, y + 4, cloud_color)
|
||||
|
||||
for i in range(4):
|
||||
x_start = x + 6 + i * 4
|
||||
graphics.DrawLine(canvas, x_start, y + 16, x_start - 2, y + 20, rain_color)
|
||||
graphics.DrawLine(canvas, x_start + 1, y + 18, x_start - 1, y + 22, rain_color)
|
||||
|
||||
|
||||
def draw_pouring(canvas, x, y):
|
||||
"""Draw cloud with heavy rain."""
|
||||
cloud_color = graphics.Color(100, 100, 120)
|
||||
rain_color = graphics.Color(80, 130, 255)
|
||||
|
||||
_draw_cloud_shape(canvas, x + 4, y + 2, cloud_color)
|
||||
|
||||
for i in range(5):
|
||||
x_start = x + 4 + i * 4
|
||||
graphics.DrawLine(canvas, x_start, y + 14, x_start - 3, y + 22, rain_color)
|
||||
|
||||
|
||||
def draw_snowy(canvas, x, y):
|
||||
"""Draw cloud with snowflakes."""
|
||||
cloud_color = graphics.Color(180, 180, 200)
|
||||
|
||||
_draw_cloud_shape(canvas, x + 4, y + 4, cloud_color)
|
||||
|
||||
for i in range(3):
|
||||
sx = x + 8 + i * 5
|
||||
sy = y + 18 + (i % 2) * 3
|
||||
canvas.SetPixel(sx, sy, 255, 255, 255)
|
||||
canvas.SetPixel(sx - 1, sy, 255, 255, 255)
|
||||
canvas.SetPixel(sx + 1, sy, 255, 255, 255)
|
||||
canvas.SetPixel(sx, sy - 1, 255, 255, 255)
|
||||
canvas.SetPixel(sx, sy + 1, 255, 255, 255)
|
||||
|
||||
|
||||
def draw_thunderstorm(canvas, x, y):
|
||||
"""Draw cloud with lightning bolt."""
|
||||
cloud_color = graphics.Color(80, 80, 100)
|
||||
lightning_color = graphics.Color(255, 255, 0)
|
||||
|
||||
_draw_cloud_shape(canvas, x + 4, y + 2, cloud_color)
|
||||
|
||||
graphics.DrawLine(canvas, x + 12, y + 12, x + 10, y + 16, lightning_color)
|
||||
graphics.DrawLine(canvas, x + 10, y + 16, x + 14, y + 16, lightning_color)
|
||||
graphics.DrawLine(canvas, x + 14, y + 16, x + 10, y + 22, lightning_color)
|
||||
|
||||
|
||||
def draw_foggy(canvas, x, y):
|
||||
"""Draw horizontal fog lines."""
|
||||
fog_color = graphics.Color(180, 180, 180)
|
||||
|
||||
for i in range(5):
|
||||
y_pos = y + 6 + i * 3
|
||||
graphics.DrawLine(canvas, x + 2, y_pos, x + 20, y_pos, fog_color)
|
||||
|
||||
|
||||
def draw_windy(canvas, x, y):
|
||||
"""Draw wind lines."""
|
||||
wind_color = graphics.Color(150, 200, 255)
|
||||
|
||||
graphics.DrawLine(canvas, x + 2, y + 8, x + 18, y + 8, wind_color)
|
||||
graphics.DrawLine(canvas, x + 18, y + 8, x + 20, y + 6, wind_color)
|
||||
graphics.DrawLine(canvas, x + 4, y + 12, x + 22, y + 12, wind_color)
|
||||
graphics.DrawLine(canvas, x + 22, y + 12, x + 23, y + 10, wind_color)
|
||||
graphics.DrawLine(canvas, x + 2, y + 16, x + 16, y + 16, wind_color)
|
||||
graphics.DrawLine(canvas, x + 16, y + 16, x + 18, y + 14, wind_color)
|
||||
|
||||
|
||||
def draw_hail(canvas, x, y):
|
||||
"""Draw cloud with hail."""
|
||||
cloud_color = graphics.Color(140, 140, 160)
|
||||
hail_color = graphics.Color(200, 220, 255)
|
||||
|
||||
_draw_cloud_shape(canvas, x + 4, y + 4, cloud_color)
|
||||
|
||||
for i in range(3):
|
||||
hx = x + 8 + i * 5
|
||||
hy = y + 18 + (i % 2) * 2
|
||||
graphics.DrawCircle(canvas, hx, hy, 2, hail_color)
|
||||
|
||||
|
||||
def draw_unknown(canvas, x, y):
|
||||
"""Draw question mark for unknown weather."""
|
||||
color = graphics.Color(255, 255, 255)
|
||||
|
||||
graphics.DrawCircle(canvas, x + 12, y + 8, 4, color)
|
||||
canvas.SetPixel(x + 12, y + 14, 255, 255, 255)
|
||||
canvas.SetPixel(x + 12, y + 17, 255, 255, 255)
|
||||
|
||||
|
||||
# Weather state to icon function mapping
|
||||
WEATHER_ICONS = {
|
||||
'sunny': draw_sunny,
|
||||
'clear-day': draw_sunny,
|
||||
'clear-night': draw_clear_night,
|
||||
'partlycloudy': draw_partly_cloudy,
|
||||
'partly-cloudy-day': draw_partly_cloudy,
|
||||
'partly-cloudy-night': draw_partly_cloudy_night,
|
||||
'cloudy': draw_cloudy,
|
||||
'overcast': draw_cloudy,
|
||||
'rainy': draw_rainy,
|
||||
'rain': draw_rainy,
|
||||
'showers': draw_rainy,
|
||||
'pouring': draw_pouring,
|
||||
'snowy': draw_snowy,
|
||||
'snow': draw_snowy,
|
||||
'snowy-rainy': draw_snowy,
|
||||
'sleet': draw_snowy,
|
||||
'hail': draw_hail,
|
||||
'lightning': draw_thunderstorm,
|
||||
'lightning-rainy': draw_thunderstorm,
|
||||
'thunderstorm': draw_thunderstorm,
|
||||
'fog': draw_foggy,
|
||||
'foggy': draw_foggy,
|
||||
'mist': draw_foggy,
|
||||
'hazy': draw_foggy,
|
||||
'windy': draw_windy,
|
||||
'windy-variant': draw_windy,
|
||||
'exceptional': draw_unknown,
|
||||
}
|
||||
|
||||
|
||||
def draw_weather_icon(canvas, x, y, weather_state):
|
||||
"""Draw weather icon based on state string."""
|
||||
weather_lower = weather_state.lower() if weather_state else ''
|
||||
|
||||
# Try exact match
|
||||
if weather_lower in WEATHER_ICONS:
|
||||
WEATHER_ICONS[weather_lower](canvas, x, y)
|
||||
return
|
||||
|
||||
# Try partial match
|
||||
for key, draw_func in WEATHER_ICONS.items():
|
||||
if key in weather_lower or weather_lower in key:
|
||||
draw_func(canvas, x, y)
|
||||
return
|
||||
|
||||
draw_unknown(canvas, x, y)
|
||||
Reference in New Issue
Block a user