web preview fixes

This commit is contained in:
Chuck
2025-08-10 13:33:51 -05:00
parent 4f1736fb0f
commit 1f7285cfc4
2 changed files with 48 additions and 9 deletions

View File

@@ -25,6 +25,10 @@ class DisplayManager:
start_time = time.time()
self.config = config or {}
self._force_fallback = force_fallback
# Snapshot settings for web preview integration (service writes, web reads)
self._snapshot_path = "/tmp/led_matrix_preview.png"
self._snapshot_min_interval_sec = 0.2 # max ~5 fps
self._last_snapshot_ts = 0.0
self._setup_matrix()
logger.info("Matrix setup completed in %.3f seconds", time.time() - start_time)
@@ -186,6 +190,8 @@ class DisplayManager:
if self.matrix is None:
# Fallback mode - no actual hardware to update
logger.debug("Update display called in fallback mode (no hardware)")
# Still write a snapshot so the web UI can preview
self._write_snapshot_if_due()
return
# Copy the current image to the offscreen canvas
@@ -196,6 +202,9 @@ class DisplayManager:
# Swap our canvas references
self.offscreen_canvas, self.current_canvas = self.current_canvas, self.offscreen_canvas
# Write a snapshot for the web preview (throttled)
self._write_snapshot_if_due()
except Exception as e:
logger.error(f"Error updating display: {e}")
@@ -615,4 +624,21 @@ class DisplayManager:
else:
suffix = {1: 'st', 2: 'nd', 3: 'rd'}.get(day % 10, 'th')
return dt.strftime(f"%b %-d{suffix}")
return dt.strftime(f"%b %-d{suffix}")
def _write_snapshot_if_due(self) -> None:
"""Write the current image to a PNG snapshot file at a limited frequency."""
try:
now = time.time()
if (now - self._last_snapshot_ts) < self._snapshot_min_interval_sec:
return
# Ensure directory exists
snapshot_dir = os.path.dirname(self._snapshot_path)
if snapshot_dir and not os.path.exists(snapshot_dir):
os.makedirs(snapshot_dir, exist_ok=True)
# Write PNG snapshot
self.image.save(self._snapshot_path, format='PNG')
self._last_snapshot_ts = now
except Exception as e:
# Snapshot failures should never break display; log at debug to avoid noise
logger.debug(f"Snapshot write skipped: {e}")

View File

@@ -57,32 +57,45 @@ class DisplayMonitor:
def _monitor_loop(self):
global display_manager, current_display_data
snapshot_path = "/tmp/led_matrix_preview.png"
while self.running:
try:
if display_manager and hasattr(display_manager, 'image'):
# Convert raw image to base64 (no server-side scaling; client scales visually)
# Prefer service-provided snapshot if available (works when ledmatrix service is running)
if os.path.exists(snapshot_path):
with open(snapshot_path, 'rb') as f:
img_bytes = f.read()
img_str = base64.b64encode(img_bytes).decode()
# If we can infer dimensions from display_manager, include them; else leave 0
width = display_manager.width if display_manager else 0
height = display_manager.height if display_manager else 0
current_display_data = {
'image': img_str,
'width': width,
'height': height,
'timestamp': time.time()
}
socketio.emit('display_update', current_display_data)
elif display_manager and hasattr(display_manager, 'image'):
# Fallback to in-process manager image
img_buffer = io.BytesIO()
display_manager.image.save(img_buffer, format='PNG')
img_str = base64.b64encode(img_buffer.getvalue()).decode()
current_display_data = {
'image': img_str,
'width': display_manager.width,
'height': display_manager.height,
'timestamp': time.time()
}
# Emit to all connected clients
socketio.emit('display_update', current_display_data)
except Exception as e:
logger.error(f"Display monitor error: {e}", exc_info=True)
# Yield to the async loop; target ~10 FPS to reduce load
# Yield to the async loop; target ~5-10 FPS
try:
socketio.sleep(0.1)
socketio.sleep(0.2)
except Exception:
time.sleep(0.1)
time.sleep(0.2)
display_monitor = DisplayMonitor()