mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
adding modularity and Weather
Adding weather
This commit is contained in:
@@ -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}"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
rpi-rgb-led-matrix==1.0.0
|
||||
Pillow==10.0.0
|
||||
pytz==2023.3
|
||||
pytz==2023.3
|
||||
requests==2.31.0
|
||||
timezonefinder==6.2.0
|
||||
geopy==2.4.1
|
||||
67
src/clock.py
67
src/clock.py
@@ -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:
|
||||
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.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()
|
||||
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)
|
||||
|
||||
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
46
src/display_controller.py
Normal 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
70
src/weather_manager.py
Normal 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)
|
||||
Reference in New Issue
Block a user