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",
|
"timezone": "America/Chicago",
|
||||||
|
"location": {
|
||||||
|
"city": "Dallas",
|
||||||
|
"state": "Texas",
|
||||||
|
"country": "US"
|
||||||
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"brightness": 50,
|
"brightness": 50,
|
||||||
"rows": 32,
|
"rows": 32,
|
||||||
"cols": 64,
|
"cols": 64,
|
||||||
"chain_length": 2
|
"chain_length": 2,
|
||||||
|
"rotation_interval": 10
|
||||||
},
|
},
|
||||||
"clock": {
|
"clock": {
|
||||||
"format": "%H:%M:%S",
|
"format": "%H:%M:%S",
|
||||||
"update_interval": 1
|
"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
|
rpi-rgb-led-matrix==1.0.0
|
||||||
Pillow==10.0.0
|
Pillow==10.0.0
|
||||||
pytz==2023.3
|
pytz==2023.3
|
||||||
|
requests==2.31.0
|
||||||
|
timezonefinder==6.2.0
|
||||||
|
geopy==2.4.1
|
||||||
65
src/clock.py
65
src/clock.py
@@ -1,38 +1,65 @@
|
|||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import pytz
|
import pytz
|
||||||
|
from typing import Dict, Any
|
||||||
from config_manager import ConfigManager
|
from config_manager import ConfigManager
|
||||||
from display_manager import DisplayManager
|
from display_manager import DisplayManager
|
||||||
|
|
||||||
class Clock:
|
class Clock:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.config_manager = ConfigManager()
|
self.config_manager = ConfigManager()
|
||||||
self.display_manager = DisplayManager(self.config_manager.get_display_config())
|
self.config = self.config_manager.config
|
||||||
self.timezone = pytz.timezone(self.config_manager.get_timezone())
|
self.display_manager = DisplayManager(self.config.get('display', {}))
|
||||||
self.clock_config = self.config_manager.get_clock_config()
|
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:
|
def get_current_time(self) -> str:
|
||||||
"""Get the current time in the configured timezone."""
|
"""Get the current time in the configured timezone."""
|
||||||
current_time = datetime.now(self.timezone)
|
current_time = datetime.now(self.timezone)
|
||||||
return current_time.strftime(self.clock_config.get('format', '%H:%M:%S'))
|
return current_time.strftime(self.clock_config.get('format', '%H:%M:%S'))
|
||||||
|
|
||||||
def run(self):
|
def display_time(self) -> None:
|
||||||
"""Run the clock display."""
|
"""Display the current time."""
|
||||||
try:
|
current_time = self.get_current_time()
|
||||||
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)
|
# Center the text on the display
|
||||||
time.sleep(self.clock_config.get('update_interval', 1))
|
text_width = self.display_manager.font.getlength(current_time)
|
||||||
except KeyboardInterrupt:
|
x = (self.display_manager.matrix.width - text_width) // 2
|
||||||
print("Clock stopped by user")
|
y = (self.display_manager.matrix.height - 24) // 2
|
||||||
finally:
|
|
||||||
self.display_manager.cleanup()
|
self.display_manager.clear()
|
||||||
|
self.display_manager.draw_text(current_time, x, y)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
clock = Clock()
|
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