adding modularity and Weather

Adding weather
This commit is contained in:
Chuck
2025-04-07 19:13:59 -05:00
parent ed392cf293
commit eebfb0b80d
5 changed files with 180 additions and 22 deletions

View File

@@ -1,13 +1,25 @@
{
"timezone": "America/Chicago",
"location": {
"city": "Dallas",
"state": "Texas",
"country": "US"
},
"display": {
"brightness": 50,
"rows": 32,
"cols": 64,
"chain_length": 2
"chain_length": 2,
"rotation_interval": 10
},
"clock": {
"format": "%H:%M:%S",
"update_interval": 1
},
"weather": {
"api_key": "YOUR_OPENWEATHERMAP_API_KEY",
"update_interval": 300,
"units": "imperial",
"display_format": "{temp}°F\n{condition}"
}
}

View File

@@ -1,3 +1,6 @@
rpi-rgb-led-matrix==1.0.0
Pillow==10.0.0
pytz==2023.3
requests==2.31.0
timezonefinder==6.2.0
geopy==2.4.1

View File

@@ -1,38 +1,65 @@
import time
from datetime import datetime
import pytz
from typing import Dict, Any
from config_manager import ConfigManager
from display_manager import DisplayManager
class Clock:
def __init__(self):
self.config_manager = ConfigManager()
self.display_manager = DisplayManager(self.config_manager.get_display_config())
self.timezone = pytz.timezone(self.config_manager.get_timezone())
self.clock_config = self.config_manager.get_clock_config()
self.config = self.config_manager.config
self.display_manager = DisplayManager(self.config.get('display', {}))
self.location = self.config.get('location', {})
self.clock_config = self.config.get('clock', {})
self.timezone = self._get_timezone()
def _get_timezone(self) -> str:
"""Get timezone based on location."""
from timezonefinder import TimezoneFinder
from geopy.geocoders import Nominatim
try:
# Get coordinates for the location
geolocator = Nominatim(user_agent="led_matrix_clock")
location_str = f"{self.location['city']}, {self.location['state']}, {self.location['country']}"
location = geolocator.geocode(location_str)
if location:
# Find timezone from coordinates
tf = TimezoneFinder()
timezone_str = tf.timezone_at(lng=location.longitude, lat=location.latitude)
return pytz.timezone(timezone_str)
except Exception as e:
print(f"Error finding timezone: {e}")
# Fallback to UTC
return pytz.UTC
def get_current_time(self) -> str:
"""Get the current time in the configured timezone."""
current_time = datetime.now(self.timezone)
return current_time.strftime(self.clock_config.get('format', '%H:%M:%S'))
def run(self):
"""Run the clock display."""
try:
while True:
def display_time(self) -> None:
"""Display the current time."""
current_time = self.get_current_time()
# Center the text on the display
text_width = self.display_manager.font.getlength(current_time)
x = (self.display_manager.matrix.width - text_width) // 2
y = (self.display_manager.matrix.height - 24) // 2
self.display_manager.clear()
self.display_manager.draw_text(current_time, x, y)
time.sleep(self.clock_config.get('update_interval', 1))
except KeyboardInterrupt:
print("Clock stopped by user")
finally:
self.display_manager.cleanup()
if __name__ == "__main__":
clock = Clock()
clock.run()
try:
while True:
clock.display_time()
time.sleep(clock.clock_config.get('update_interval', 1))
except KeyboardInterrupt:
print("\nClock stopped by user")
finally:
clock.display_manager.cleanup()

46
src/display_controller.py Normal file
View File

@@ -0,0 +1,46 @@
import time
from typing import Dict, Any
from clock import Clock
from weather_manager import WeatherManager
from display_manager import DisplayManager
from config_manager import ConfigManager
class DisplayController:
def __init__(self):
self.config_manager = ConfigManager()
self.config = self.config_manager.config
self.display_manager = DisplayManager(self.config.get('display', {}))
self.clock = Clock()
self.weather = WeatherManager(self.config, self.display_manager)
self.current_display = 'clock'
self.last_switch = time.time()
def run(self):
"""Run the display controller, switching between displays."""
try:
while True:
current_time = time.time()
rotation_interval = self.config['display'].get('rotation_interval', 10)
# Switch display if interval has passed
if current_time - self.last_switch > rotation_interval:
self.current_display = 'weather' if self.current_display == 'clock' else 'clock'
self.last_switch = current_time
# Display current screen
if self.current_display == 'clock':
self.clock.display_time()
else:
self.weather.display_weather()
# Small delay to prevent CPU overload
time.sleep(0.1)
except KeyboardInterrupt:
print("\nDisplay stopped by user")
finally:
self.display_manager.cleanup()
if __name__ == "__main__":
controller = DisplayController()
controller.run()

70
src/weather_manager.py Normal file
View File

@@ -0,0 +1,70 @@
import requests
import time
from typing import Dict, Any
from PIL import Image, ImageDraw
class WeatherManager:
def __init__(self, config: Dict[str, Any], display_manager):
self.config = config
self.display_manager = display_manager
self.weather_config = config.get('weather', {})
self.location = config.get('location', {})
self.last_update = 0
self.weather_data = None
def _fetch_weather(self) -> None:
"""Fetch weather data from OpenWeatherMap API."""
api_key = self.weather_config['api_key']
city = self.location['city']
state = self.location['state']
country = self.location['country']
units = self.weather_config.get('units', 'imperial')
url = f"http://api.openweathermap.org/data/2.5/weather?q={city},{state},{country}&appid={api_key}&units={units}"
try:
response = requests.get(url)
response.raise_for_status()
self.weather_data = response.json()
self.last_update = time.time()
except Exception as e:
print(f"Error fetching weather data: {e}")
self.weather_data = None
def get_weather(self) -> Dict[str, Any]:
"""Get current weather data, fetching new data if needed."""
current_time = time.time()
if (not self.weather_data or
current_time - self.last_update > self.weather_config.get('update_interval', 300)):
self._fetch_weather()
return self.weather_data
def display_weather(self) -> None:
"""Display weather information on the LED matrix."""
weather_data = self.get_weather()
if not weather_data:
return
temp = round(weather_data['main']['temp'])
condition = weather_data['weather'][0]['main']
# Format the display string
display_text = self.weather_config.get('display_format', '{temp}°F\n{condition}')
display_text = display_text.format(temp=temp, condition=condition)
# Split text into lines
lines = display_text.split('\n')
# Calculate vertical spacing
total_height = len(lines) * 24 # Assuming 24px font height
start_y = (self.display_manager.matrix.height - total_height) // 2
# Clear the display
self.display_manager.clear()
# Draw each line centered
for i, line in enumerate(lines):
text_width = self.display_manager.font.getlength(line)
x = (self.display_manager.matrix.width - text_width) // 2
y = start_y + (i * 24)
self.display_manager.draw_text(line, x, y)