From bc8568604acca99a908c333403fbca6b214b67d0 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:16:21 -0500 Subject: [PATCH] feat(web): add LED RGB sequence, multiplexing, and panel type settings (#248) * feat(web): add LED RGB sequence, multiplexing, and panel type settings Expose three rpi-rgb-led-matrix hardware options in the Display Settings UI so users can configure non-standard panels without editing config.json manually. All defaults match existing behavior (RGB, Direct, Standard). Co-Authored-By: Claude Opus 4.6 * fix(api): validate led_rgb_sequence, multiplexing, and panel_type inputs Reject invalid values with 400 errors before writing to config: whitelist check for led_rgb_sequence and panel_type, range + type check for multiplexing. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Chuck Co-authored-by: Claude Opus 4.6 --- src/display_manager.py | 1 + web_interface/blueprints/api_v3.py | 29 +++++++++- .../templates/v3/partials/display.html | 55 +++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/display_manager.py b/src/display_manager.py index b945f9b9..ee7a1522 100644 --- a/src/display_manager.py +++ b/src/display_manager.py @@ -82,6 +82,7 @@ class DisplayManager: options.pixel_mapper_config = hardware_config.get('pixel_mapper_config', '') options.row_address_type = hardware_config.get('row_address_type', 0) options.multiplexing = hardware_config.get('multiplexing', 0) + options.panel_type = hardware_config.get('panel_type', '') options.disable_hardware_pulsing = hardware_config.get('disable_hardware_pulsing', False) options.show_refresh_rate = hardware_config.get('show_refresh_rate', False) options.limit_refresh_rate_hz = hardware_config.get('limit_refresh_rate_hz', 90) diff --git a/web_interface/blueprints/api_v3.py b/web_interface/blueprints/api_v3.py index b837099f..bd9f6cb1 100644 --- a/web_interface/blueprints/api_v3.py +++ b/web_interface/blueprints/api_v3.py @@ -702,7 +702,7 @@ def save_main_config(): display_fields = ['rows', 'cols', 'chain_length', 'parallel', 'brightness', 'hardware_mapping', 'gpio_slowdown', 'scan_mode', 'disable_hardware_pulsing', 'inverse_colors', 'show_refresh_rate', 'pwm_bits', 'pwm_dither_bits', 'pwm_lsb_nanoseconds', 'limit_refresh_rate_hz', 'use_short_date_format', - 'max_dynamic_duration_seconds'] + 'max_dynamic_duration_seconds', 'led_rgb_sequence', 'multiplexing', 'panel_type'] if any(k in data for k in display_fields): if 'display' not in current_config: @@ -712,12 +712,35 @@ def save_main_config(): if 'runtime' not in current_config['display']: current_config['display']['runtime'] = {} + # Allowed values for validated string fields + LED_RGB_ALLOWED = {'RGB', 'RBG', 'GRB', 'GBR', 'BRG', 'BGR'} + PANEL_TYPE_ALLOWED = {'', 'FM6126A', 'FM6127'} + + # Validate led_rgb_sequence + if 'led_rgb_sequence' in data and data['led_rgb_sequence'] not in LED_RGB_ALLOWED: + return jsonify({'status': 'error', 'message': f"Invalid LED RGB sequence '{data['led_rgb_sequence']}'. Allowed values: {', '.join(sorted(LED_RGB_ALLOWED))}"}), 400 + + # Validate panel_type + if 'panel_type' in data and data['panel_type'] not in PANEL_TYPE_ALLOWED: + return jsonify({'status': 'error', 'message': f"Invalid panel type '{data['panel_type']}'. Allowed values: Standard (empty), FM6126A, FM6127"}), 400 + + # Validate multiplexing + if 'multiplexing' in data: + try: + mux_val = int(data['multiplexing']) + if mux_val < 0 or mux_val > 22: + return jsonify({'status': 'error', 'message': f"Invalid multiplexing value '{data['multiplexing']}'. Must be an integer from 0 to 22."}), 400 + except (ValueError, TypeError): + return jsonify({'status': 'error', 'message': f"Invalid multiplexing value '{data['multiplexing']}'. Must be an integer from 0 to 22."}), 400 + # Handle hardware settings for field in ['rows', 'cols', 'chain_length', 'parallel', 'brightness', 'hardware_mapping', 'scan_mode', - 'pwm_bits', 'pwm_dither_bits', 'pwm_lsb_nanoseconds', 'limit_refresh_rate_hz']: + 'pwm_bits', 'pwm_dither_bits', 'pwm_lsb_nanoseconds', 'limit_refresh_rate_hz', + 'led_rgb_sequence', 'multiplexing', 'panel_type']: if field in data: if field in ['rows', 'cols', 'chain_length', 'parallel', 'brightness', 'scan_mode', - 'pwm_bits', 'pwm_dither_bits', 'pwm_lsb_nanoseconds', 'limit_refresh_rate_hz']: + 'pwm_bits', 'pwm_dither_bits', 'pwm_lsb_nanoseconds', 'limit_refresh_rate_hz', + 'multiplexing']: current_config['display']['hardware'][field] = int(data[field]) else: current_config['display']['hardware'][field] = data[field] diff --git a/web_interface/templates/v3/partials/display.html b/web_interface/templates/v3/partials/display.html index 1d80d275..aeebd915 100644 --- a/web_interface/templates/v3/partials/display.html +++ b/web_interface/templates/v3/partials/display.html @@ -94,6 +94,61 @@ +
+
+ + +

Color channel order for your LED panels

+
+ +
+ + +

Multiplexing scheme for your LED panels

+
+ +
+ + +

Special panel chipset initialization (use Standard unless your panel requires it)

+
+
+