mirror of
https://github.com/Death916/deathclock.git
synced 2026-04-10 03:04:40 -07:00
add radio module drom github and fix weather hot reloads
This commit is contained in:
parent
7d0a0cdfa6
commit
4ea3436128
5 changed files with 1174 additions and 773 deletions
|
|
@ -9,6 +9,8 @@ from typing import Any, Dict, List
|
||||||
|
|
||||||
import reflex as rx
|
import reflex as rx
|
||||||
|
|
||||||
|
_background_tasks_registered = False
|
||||||
|
|
||||||
from utils.news import News
|
from utils.news import News
|
||||||
from utils.radio import Radio
|
from utils.radio import Radio
|
||||||
from utils.scores import NBAScores, mlbScores, nflScores
|
from utils.scores import NBAScores, mlbScores, nflScores
|
||||||
|
|
@ -45,6 +47,7 @@ class State(rx.State):
|
||||||
_nfl_client: nflScores | None = None
|
_nfl_client: nflScores | None = None
|
||||||
_radio_client: Radio = Radio()
|
_radio_client: Radio = Radio()
|
||||||
last_sports_update: float = 0.0
|
last_sports_update: float = 0.0
|
||||||
|
last_weather_fetch_time: float = 0.0
|
||||||
|
|
||||||
# --- Initialize Utility Client ---
|
# --- Initialize Utility Client ---
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
@ -65,18 +68,22 @@ class State(rx.State):
|
||||||
# Set error state if needed
|
# Set error state if needed
|
||||||
self.weather_img = "/error_placeholder.png"
|
self.weather_img = "/error_placeholder.png"
|
||||||
self.last_weather_update = "Client Init Error"
|
self.last_weather_update = "Client Init Error"
|
||||||
self.mlb_scores = ""
|
self.mlb_scores = []
|
||||||
self.nba_scores = ""
|
self.nba_scores = []
|
||||||
self.last_sports_update = 0.0
|
self.last_sports_update = 0.0
|
||||||
|
|
||||||
# --- on_load Handler ---
|
# --- on_load Handler ---
|
||||||
async def start_background_tasks(self):
|
async def start_background_tasks(self):
|
||||||
|
global _background_tasks_registered
|
||||||
"""Starts the weather background task when the page loads."""
|
"""Starts the weather background task when the page loads."""
|
||||||
rx.remove_local_storage(
|
rx.remove_local_storage("chakra-ui-color-mode")
|
||||||
"chakra-ui-color-mode"
|
if _background_tasks_registered:
|
||||||
) # trying to test themes remove after
|
logging.info(
|
||||||
|
"Background tasks already registered in this process; skipping."
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
_background_tasks_registered = True
|
||||||
logging.info("Triggering background tasks: Weather")
|
logging.info("Triggering background tasks: Weather")
|
||||||
# Return a list containing the handler references
|
|
||||||
return [
|
return [
|
||||||
State.fetch_weather,
|
State.fetch_weather,
|
||||||
State.fetch_sports,
|
State.fetch_sports,
|
||||||
|
|
@ -190,7 +197,6 @@ class State(rx.State):
|
||||||
@rx.event(background=True)
|
@rx.event(background=True)
|
||||||
async def fetch_weather(self):
|
async def fetch_weather(self):
|
||||||
"""Fetches the weather screenshot periodically."""
|
"""Fetches the weather screenshot periodically."""
|
||||||
# Check if the client initialized correctly
|
|
||||||
if not hasattr(self, "_weather_client") or self._weather_client is None:
|
if not hasattr(self, "_weather_client") or self._weather_client is None:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Weather client not initialized. Stopping fetch_weather task."
|
"Weather client not initialized. Stopping fetch_weather task."
|
||||||
|
|
@ -199,31 +205,40 @@ class State(rx.State):
|
||||||
async with self:
|
async with self:
|
||||||
self.last_weather_update = "Error: Weather client unavailable"
|
self.last_weather_update = "Error: Weather client unavailable"
|
||||||
yield
|
yield
|
||||||
return # Exit the task permanently if client init failed
|
return
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
if (
|
||||||
|
self.last_weather_fetch_time
|
||||||
|
and (time.time() - self.last_weather_fetch_time)
|
||||||
|
< WEATHER_FETCH_INTERVAL
|
||||||
|
):
|
||||||
|
logging.info(
|
||||||
|
"Recent weather fetch detected; skipping immediate fetch to avoid thrashing."
|
||||||
|
)
|
||||||
|
await asyncio.sleep(WEATHER_FETCH_INTERVAL)
|
||||||
|
continue
|
||||||
|
|
||||||
logging.info("Attempting to fetch weather screenshot...")
|
logging.info("Attempting to fetch weather screenshot...")
|
||||||
# Call the method from the initialized client
|
|
||||||
img_web_path = self._weather_client.get_weather_screenshot()
|
img_web_path = self._weather_client.get_weather_screenshot()
|
||||||
|
|
||||||
if img_web_path:
|
if img_web_path:
|
||||||
async with self:
|
async with self:
|
||||||
timestamp = int(time.time())
|
|
||||||
self.weather_img = f"{img_web_path}"
|
self.weather_img = f"{img_web_path}"
|
||||||
self.last_weather_update = datetime.now(timezone.utc).strftime(
|
self.last_weather_update = datetime.now(timezone.utc).strftime(
|
||||||
"%Y-%m-%d %H:%M:%S UTC"
|
"%Y-%m-%d %H:%M:%S UTC"
|
||||||
)
|
)
|
||||||
|
self.last_weather_fetch_time = time.time()
|
||||||
logging.info(
|
logging.info(
|
||||||
f"State.weather_img updated to: {self.weather_img}"
|
f"State.weather_img updated to: {self.weather_img}"
|
||||||
)
|
)
|
||||||
yield # Update frontend
|
yield
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"get_weather_screenshot returned None. State not updated."
|
"get_weather_screenshot returned None. State not updated."
|
||||||
)
|
)
|
||||||
# Optionally update status
|
|
||||||
async with self:
|
async with self:
|
||||||
self.last_weather_update = f"Failed fetch @ {datetime.now(timezone.utc).strftime('%H:%M:%S UTC')}"
|
self.last_weather_update = f"Failed fetch @ {datetime.now(timezone.utc).strftime('%H:%M:%S UTC')}"
|
||||||
yield
|
yield
|
||||||
|
|
@ -232,12 +247,13 @@ class State(rx.State):
|
||||||
logging.error(
|
logging.error(
|
||||||
f"Error in fetch_weather background task: {e}", exc_info=True
|
f"Error in fetch_weather background task: {e}", exc_info=True
|
||||||
)
|
)
|
||||||
async with self: # Update state to show error
|
async with self:
|
||||||
self.last_weather_update = (
|
self.last_weather_update = (
|
||||||
f"Error @ {datetime.now(timezone.utc).strftime('%H:%M:%S UTC')}"
|
f"Error @ {datetime.now(timezone.utc).strftime('%H:%M:%S UTC')}"
|
||||||
)
|
)
|
||||||
yield
|
yield
|
||||||
|
logging.info("Weather fetch completed")
|
||||||
|
logging.info("Sleeping for next fetch")
|
||||||
await asyncio.sleep(WEATHER_FETCH_INTERVAL)
|
await asyncio.sleep(WEATHER_FETCH_INTERVAL)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ dependencies = [
|
||||||
"reflex>=0.6.8",
|
"reflex>=0.6.8",
|
||||||
"aiohttp>=3.11.18",
|
"aiohttp>=3.11.18",
|
||||||
"reflex-text-loop>=0.0.1",
|
"reflex-text-loop>=0.0.1",
|
||||||
|
"pigpio>=1.78",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
# connect to rda5807 chip and control it and display the current station
|
# connect to rda5807 chip and control it and display the current station
|
||||||
|
# TODO: reference rd library in readme
|
||||||
import reflex as rx
|
import reflex as rx
|
||||||
|
|
||||||
|
# from utils.python_rd5807m.radio import Radio as Radio_lib
|
||||||
|
|
||||||
CURRENT_STATION = "90.9 FM"
|
CURRENT_STATION = "90.9 FM"
|
||||||
PLAYING = False
|
PLAYING = False
|
||||||
|
|
||||||
|
|
@ -31,8 +33,15 @@ class Radio(rx.Base):
|
||||||
|
|
||||||
|
|
||||||
class Radio_Control:
|
class Radio_Control:
|
||||||
def __init__(self):
|
def init_radio(self):
|
||||||
pass
|
self.radio = Radio_lib()
|
||||||
|
self.radio.initialize()
|
||||||
|
|
||||||
def play_radio(self):
|
def play_radio(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
## for testing chip
|
||||||
|
# if __name__ == "__main__":
|
||||||
|
# radio = Radio_Control()
|
||||||
|
# radio.play_radio()
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ logging.basicConfig(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Define the target filename consistently
|
# Define the target filename consistently
|
||||||
WEATHER_FILENAME = "weather.jpg"
|
WEATHER_FILENAME = "weather.png"
|
||||||
# Define the web path expected by the frontend
|
# Define the web path expected by the frontend
|
||||||
WEATHER_WEB_PATH = f"/{WEATHER_FILENAME}" # This should be relative to the assets dir
|
WEATHER_WEB_PATH = f"/{WEATHER_FILENAME}" # This should be relative to the assets dir
|
||||||
|
|
||||||
|
|
@ -51,28 +51,26 @@ class Weather(rx.Base):
|
||||||
"""
|
"""
|
||||||
assets_dir = self._get_assets_dir()
|
assets_dir = self._get_assets_dir()
|
||||||
screenshot_path = os.path.join(assets_dir, WEATHER_FILENAME)
|
screenshot_path = os.path.join(assets_dir, WEATHER_FILENAME)
|
||||||
|
# Delete the old file before creating the new one
|
||||||
try:
|
if os.path.exists(screenshot_path):
|
||||||
curl_command = [
|
|
||||||
"curl",
|
|
||||||
"-s", # Silent mode
|
|
||||||
"v2.wttr.in/Sacramento.png?0u", # Fetch PNG, no border, no terminal escapes
|
|
||||||
"-o",
|
|
||||||
screenshot_path, # Save to the correct assets path
|
|
||||||
]
|
|
||||||
|
|
||||||
# Delete the old file before creating the new one
|
|
||||||
self.delete_old_screenshots(assets_dir)
|
self.delete_old_screenshots(assets_dir)
|
||||||
|
|
||||||
logging.info(
|
curl_command = [
|
||||||
f"Running curl command to fetch weather: {' '.join(curl_command)}"
|
"curl",
|
||||||
)
|
"-s", # Silent mode
|
||||||
|
"v2.wttr.in/Sacramento.png?u0", # Fetch PNG, no border, no terminal escapes
|
||||||
|
"-o",
|
||||||
|
screenshot_path, # Save to the correct assets path
|
||||||
|
]
|
||||||
|
|
||||||
|
logging.info(f"Running curl command to fetch weather: {' '.join(curl_command)}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run(curl_command)
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Curl command successful. Weather image saved to: {screenshot_path}"
|
f"Curl command successful. Weather image saved to: {screenshot_path}"
|
||||||
) # Log correct save path
|
)
|
||||||
|
|
||||||
return WEATHER_WEB_PATH
|
return WEATHER_WEB_PATH
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
logging.error(f"Curl command failed for path {screenshot_path}: {e}")
|
logging.error(f"Curl command failed for path {screenshot_path}: {e}")
|
||||||
logging.error(f"Curl stderr: {e.stderr}")
|
logging.error(f"Curl stderr: {e.stderr}")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue