mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-01 08:48:05 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0c80d934a |
+23
-28
@@ -199,10 +199,6 @@ class DisplayController:
|
||||
self.wifi_status_file = WIFI_STATUS_FILE
|
||||
self.wifi_status_active = False
|
||||
self.wifi_status_expires_at: Optional[float] = None
|
||||
# _check_wifi_status_message throttle state (checked at frame rate,
|
||||
# stat'd at most once per second)
|
||||
self._wifi_status_check_ts = 0.0
|
||||
self._wifi_status_last_result: Optional[Dict[str, Any]] = None
|
||||
|
||||
# Plugin display() signature cache — must be initialised before the plugin
|
||||
# loading loop below so the .pop() invalidation at load time is always safe.
|
||||
@@ -625,18 +621,28 @@ class DisplayController:
|
||||
|
||||
current_day = current_time.strftime('%A').lower() # e.g. 'monday'
|
||||
current_time_only = current_time.time()
|
||||
|
||||
|
||||
# Check if per-day schedule is configured
|
||||
days_config = schedule_config.get('days')
|
||||
|
||||
# Determine which schedule to use
|
||||
|
||||
# Determine which schedule to use. Respect an explicit 'mode' field
|
||||
# (like the dim schedule does) so a stray/legacy 'days' dict left over
|
||||
# from config migration or a prior per-day setup can't silently
|
||||
# override a user's Global schedule selection.
|
||||
mode = schedule_config.get('mode')
|
||||
mode_normalized = mode.replace('_', '-') if mode else None
|
||||
|
||||
use_per_day = False
|
||||
if days_config:
|
||||
# Check if days dict is not empty and contains current day
|
||||
if days_config and current_day in days_config:
|
||||
if mode_normalized == 'global':
|
||||
use_per_day = False
|
||||
elif mode_normalized == 'per-day':
|
||||
use_per_day = bool(days_config and current_day in days_config)
|
||||
elif days_config:
|
||||
# No explicit mode recorded (legacy config) - fall back to
|
||||
# inferring from presence of a 'days' dict for the current day.
|
||||
if current_day in days_config:
|
||||
use_per_day = True
|
||||
elif days_config:
|
||||
# Days dict exists but doesn't have current day - fall back to global
|
||||
else:
|
||||
logger.debug("Per-day schedule exists but %s not configured, using global schedule", current_day)
|
||||
|
||||
if use_per_day:
|
||||
@@ -1639,7 +1645,7 @@ class DisplayController:
|
||||
self._sleep_with_plugin_updates(60)
|
||||
continue
|
||||
|
||||
logger.debug("Display active, processing mode: %s", self.current_display_mode)
|
||||
logger.info(f"Display active, processing mode: {self.current_display_mode}")
|
||||
|
||||
# Plugins update on their own schedules - no forced sync updates needed
|
||||
# Each plugin has its own update_interval and background services
|
||||
@@ -1807,7 +1813,7 @@ class DisplayController:
|
||||
if self.plugin_manager and hasattr(self.plugin_manager, 'health_tracker') and self.plugin_manager.health_tracker:
|
||||
should_skip = self.plugin_manager.health_tracker.should_skip_plugin(plugin_id)
|
||||
if should_skip:
|
||||
logger.info("Skipping plugin %s due to circuit breaker (mode: %s)", plugin_id, active_mode)
|
||||
logger.info(f"Skipping plugin {plugin_id} due to circuit breaker (mode: {active_mode})")
|
||||
display_result = False
|
||||
# Skip to next mode - let existing logic handle it
|
||||
manager_to_display = None
|
||||
@@ -1865,7 +1871,7 @@ class DisplayController:
|
||||
if isinstance(result, bool):
|
||||
display_result = result
|
||||
if not display_result:
|
||||
logger.info("Plugin %s display() returned False for mode %s", plugin_id, active_mode)
|
||||
logger.info(f"Plugin {plugin_id} display() returned False for mode {active_mode}")
|
||||
|
||||
# Record success if display completed without exception
|
||||
if self.plugin_manager and hasattr(self.plugin_manager, 'health_tracker') and self.plugin_manager.health_tracker:
|
||||
@@ -2358,16 +2364,6 @@ class DisplayController:
|
||||
Returns None on any error or if message is expired/invalid.
|
||||
"""
|
||||
try:
|
||||
# Throttle the existence stat to ~1 Hz: this runs on every render
|
||||
# iteration (60+ fps), and the file usually doesn't exist — the
|
||||
# status message's lifetime is measured in seconds anyway.
|
||||
# Both attributes are initialised in __init__.
|
||||
now = time.time()
|
||||
if (now - self._wifi_status_check_ts) < 1.0:
|
||||
return self._wifi_status_last_result
|
||||
self._wifi_status_check_ts = now
|
||||
self._wifi_status_last_result = None
|
||||
|
||||
# Check if file exists
|
||||
if not self.wifi_status_file or not self.wifi_status_file.exists():
|
||||
return None
|
||||
@@ -2418,14 +2414,13 @@ class DisplayController:
|
||||
pass
|
||||
return None
|
||||
|
||||
# Message is valid and not expired — cache for the throttle window
|
||||
self._wifi_status_last_result = {
|
||||
# Message is valid and not expired
|
||||
return {
|
||||
'message': message,
|
||||
'timestamp': timestamp,
|
||||
'duration': duration,
|
||||
'expires_at': expires_at
|
||||
}
|
||||
return self._wifi_status_last_result
|
||||
|
||||
except Exception as e:
|
||||
# Catch-all for any unexpected errors - log but don't break the display
|
||||
|
||||
@@ -66,10 +66,6 @@ class RenderPipeline:
|
||||
else display_manager.height
|
||||
)
|
||||
|
||||
# Reusable blank frame for cycle-end pushes (allocated lazily,
|
||||
# re-blacked before each reuse)
|
||||
self._blank_frame = None
|
||||
|
||||
# ScrollHelper for optimized scrolling
|
||||
self.scroll_helper = ScrollHelper(
|
||||
self.display_width,
|
||||
@@ -238,19 +234,11 @@ class RenderPipeline:
|
||||
)
|
||||
# Push blank immediately so the hardware never shows any
|
||||
# post-wrap content while the coordinator recomposes the
|
||||
# next cycle (~100 ms). The blank is allocated once and
|
||||
# reused across cycle wraps (fresh paste each time in case
|
||||
# a consumer drew on the previous one).
|
||||
# next cycle (~100 ms).
|
||||
try:
|
||||
if self._blank_frame is None or self._blank_frame.size != (
|
||||
self.display_width, self.display_height):
|
||||
self._blank_frame = Image.new(
|
||||
'RGB', (self.display_width, self.display_height))
|
||||
else:
|
||||
self._blank_frame.paste(
|
||||
(0, 0, 0),
|
||||
(0, 0, self.display_width, self.display_height))
|
||||
self.display_manager.image = self._blank_frame
|
||||
from PIL import Image as _Image
|
||||
blank = _Image.new('RGB', (self.display_width, self.display_height))
|
||||
self.display_manager.image = blank
|
||||
self.display_manager.update_display()
|
||||
except Exception:
|
||||
logger.exception("Failed to write blank frame to display at cycle end")
|
||||
|
||||
@@ -329,6 +329,7 @@ def save_schedule_config():
|
||||
}
|
||||
|
||||
mode = data.get('mode', 'global')
|
||||
schedule_config['mode'] = mode
|
||||
|
||||
if mode == 'global':
|
||||
# Simple global schedule
|
||||
|
||||
Reference in New Issue
Block a user