From 768517a6584ad1410f99fcfddd5252dfac2a9d2f Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Tue, 15 Sep 2026 17:14:07 -0400 Subject: [PATCH] fix(web): reject true and 5.5 for row_address_type and multiplexing Both still went straight through int(), so a JSON true saved as 1 and 5.5 as 5. They now use the shared hardware range check like the other panel fields. Review feedback on #586. Also: the RP1 Backend tooltip said it is ignored on Pi 3/4 (it is ignored on every model but the Pi 5), and the README gave the dynamic-duration default cap as 90s; the code default is 180s. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 9 +++++---- README.md | 2 +- test/test_api_v3_display_hardware.py | 5 ++++- web_interface/blueprints/api_v3/config.py | 19 +------------------ .../templates/v3/partials/display.html | 2 +- 5 files changed, 12 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab4c6b9b..42170491 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,10 +54,11 @@ Web interface: parallel is 1–3 and PWM dither bits 0–2, matching the library. A stored GPIO slowdown, PWM dither bits or refresh-rate cap of 0 no longer shows (and re-saves) as 3, 1 or 120, and the refresh cap accepts 0 (no cap). The config - API rejects out-of-range `rows`, `cols`, `chain_length`, `parallel`, - `brightness`, `scan_mode`, `pwm_bits`, `pwm_dither_bits`, - `pwm_lsb_nanoseconds`, `limit_refresh_rate_hz` and `gpio_slowdown` with a - 400 instead of saving a config the matrix refuses to start with. + API rejects out-of-range or non-integer `rows`, `cols`, `chain_length`, + `parallel`, `brightness`, `scan_mode`, `pwm_bits`, `pwm_dither_bits`, + `pwm_lsb_nanoseconds`, `limit_refresh_rate_hz`, `row_address_type`, + `multiplexing` and `gpio_slowdown` with a 400 (JSON `true` or `5.5` used to + save as 1 or 5) instead of saving a config the matrix refuses to start with. - Display setting help tips and README / config-reference entries corrected and completed: `panel_type` and `rp1_rio` are documented, `show_refresh_rate` prints to the console rather than drawing on the panel, diff --git a/README.md b/README.md index 9245fc56..30787068 100644 --- a/README.md +++ b/README.md @@ -701,7 +701,7 @@ Controls how long each installed plugin stays visible in seconds before switchin - Some plugins can automatically adjust their display time based on content - This setting limits how long they can extend (prevents one display from dominating) - Example: If set to 60, a plugin can extend up to 60 seconds even if it requests longer - - Leave unset to use the default cap (typically 90 seconds) + - Leave unset to use the default cap (180 seconds; the web UI accepts 30-1800) ### Example Configuration diff --git a/test/test_api_v3_display_hardware.py b/test/test_api_v3_display_hardware.py index c7e78008..04c507c7 100644 --- a/test/test_api_v3_display_hardware.py +++ b/test/test_api_v3_display_hardware.py @@ -87,7 +87,8 @@ def test_waveshare_96x48_v2_settings_all_save(api_v3_client, saved, as_strings): ('cols', 16), ('cols', 192), ('cols', 512), ('chain_length', 1), ('chain_length', 32), ('parallel', 3), - ('row_address_type', 0), ('row_address_type', 5), + ('row_address_type', 0), ('row_address_type', 5), ('row_address_type', 5.0), + ('multiplexing', 0), ('multiplexing', 22), ('gpio_slowdown', 0), ('gpio_slowdown', 10), ('pwm_bits', 1), ('pwm_bits', 11), ('pwm_dither_bits', 0), ('pwm_dither_bits', 2), @@ -108,6 +109,8 @@ def test_values_in_range_are_saved(api_v3_client, saved, field, value): ('chain_length', 0), ('parallel', 0), ('parallel', 4), ('row_address_type', -1), ('row_address_type', 6), + ('row_address_type', True), ('row_address_type', 5.5), + ('multiplexing', -1), ('multiplexing', 23), ('multiplexing', True), ('gpio_slowdown', -1), ('gpio_slowdown', 11), ('pwm_bits', 0), ('pwm_bits', 12), ('pwm_dither_bits', 3), diff --git a/web_interface/blueprints/api_v3/config.py b/web_interface/blueprints/api_v3/config.py index bd1ae53a..a18e6ed5 100644 --- a/web_interface/blueprints/api_v3/config.py +++ b/web_interface/blueprints/api_v3/config.py @@ -555,15 +555,6 @@ def save_main_config(): 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, OverflowError): - return jsonify({'status': 'error', 'message': f"Invalid multiplexing value '{data['multiplexing']}'. Must be an integer from 0 to 22."}), 400 - # Validate pixel_mapper_config (free-form mapper string, e.g. "U-mapper;Rotate:90") if 'pixel_mapper_config' in data and not isinstance(data['pixel_mapper_config'], str): return jsonify({'status': 'error', 'message': 'pixel_mapper_config must be a string (e.g. "U-mapper;Rotate:90" or empty)'}), 400 @@ -573,15 +564,6 @@ def save_main_config(): if 'orientation' in data and data['orientation'] not in ORIENTATION_ALLOWED: return jsonify({'status': 'error', 'message': f"Invalid orientation '{data['orientation']}'. Allowed values: {', '.join(sorted(ORIENTATION_ALLOWED))}"}), 400 - # Validate row_address_type - if 'row_address_type' in data: - try: - rat_val = int(data['row_address_type']) - if rat_val < 0 or rat_val > 5: - return jsonify({'status': 'error', 'message': f"Invalid row_address_type '{data['row_address_type']}'. Must be an integer from 0 to 5."}), 400 - except (ValueError, TypeError, OverflowError): - return jsonify({'status': 'error', 'message': f"Invalid row_address_type '{data['row_address_type']}'. Must be an integer from 0 to 5."}), 400 - # Panel geometry, PWM and GPIO timing, held to what the rgbmatrix library # accepts (RGBMatrix::Options::Validate in lib/options-initialize.cc, # the gpio_slowdown check in lib/led-matrix.cc). Outside those ranges @@ -617,6 +599,7 @@ def save_main_config(): ('pwm_bits', 1, 11, False), ('pwm_dither_bits', 0, 2, False), ('pwm_lsb_nanoseconds', 50, 3000, False), ('limit_refresh_rate_hz', 0, None, False), + ('row_address_type', 0, 5, False), ('multiplexing', 0, 22, False), ('gpio_slowdown', 0, 10, False)): if field in data: error = _hardware_int_error(field, low, high, even) diff --git a/web_interface/templates/v3/partials/display.html b/web_interface/templates/v3/partials/display.html index 12908028..0825e629 100644 --- a/web_interface/templates/v3/partials/display.html +++ b/web_interface/templates/v3/partials/display.html @@ -222,7 +222,7 @@