mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-13 02:26:37 +00:00
Merge audit16-core/hardware into the September 16 core audit fixes
# Conflicts: # CHANGELOG.md
This commit is contained in:
@@ -12,7 +12,9 @@ from web_interface.blueprints.api_v3 import (
|
||||
success_response,
|
||||
)
|
||||
from src.common.path_safety import resolve_under
|
||||
from src.pi5_matrix_support import is_raspberry_pi_5, pi5_unsupported_settings
|
||||
from src.display_geometry import ORIENTATION_ROTATE_DEGREES
|
||||
from src.matrix_support import INT_SETTING_LIMITS, describe_range, library_refusals, refusal_message
|
||||
from src.pi5_matrix_support import is_raspberry_pi_5
|
||||
import web_interface.blueprints.api_v3 as _pkg
|
||||
# Read through the module rather than bound by value: tests patch these
|
||||
# as module attributes, and a value binding would not see the patch.
|
||||
@@ -561,28 +563,20 @@ def save_main_config():
|
||||
return jsonify({'status': 'error', 'message': 'pixel_mapper_config must be a string (e.g. "U-mapper;Rotate:90" or empty)'}), 400
|
||||
|
||||
# Validate orientation (physical mounting rotation; composed onto pixel_mapper_config at runtime)
|
||||
ORIENTATION_ALLOWED = {'normal', '180'}
|
||||
ORIENTATION_ALLOWED = set(ORIENTATION_ROTATE_DEGREES)
|
||||
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
|
||||
|
||||
# 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
|
||||
# the config used to save, then the matrix refused to start and the
|
||||
# display dropped to fallback mode. cols, chain_length and
|
||||
# limit_refresh_rate_hz (0 = no cap) have no upper bound in the
|
||||
# library. rows has none here by choice: the library currently
|
||||
# rejects more than 64 per panel, and that limit is left to it so a
|
||||
# library that lifts it needs no change here.
|
||||
def _hardware_int_error(field, low, high=None, even=False):
|
||||
# Panel geometry, PWM and GPIO timing, held to what the rgbmatrix
|
||||
# library and its Python binding accept (src/matrix_support.py:
|
||||
# Options::Validate, the gpio_slowdown check, and the binding's
|
||||
# uint8_t setters, which cap chain_length at 255). Outside those
|
||||
# ranges the library returns no matrix and the display service
|
||||
# crash-loops rather than falling back, so they never save.
|
||||
def _hardware_int_error(field, low, high, even=False):
|
||||
"""A 400 response if data[field] is not an allowed integer, else None."""
|
||||
raw = data[field]
|
||||
kind = "an even integer" if even else "an integer"
|
||||
if high is None:
|
||||
allowed = f"{kind} of at least {low}"
|
||||
else:
|
||||
allowed = f"{kind} from {low} to {high}"
|
||||
rejection = (jsonify({'status': 'error', 'message': f"Invalid {field} '{raw}'. Must be {allowed}."}), 400)
|
||||
rejection = (jsonify({'status': 'error', 'message': f"Invalid {field} '{raw}'. Must be {describe_range(low, high, even)}."}), 400)
|
||||
# int() would quietly turn true into 1 and 48.5 into 48.
|
||||
if isinstance(raw, bool) or (isinstance(raw, float) and not raw.is_integer()):
|
||||
return rejection
|
||||
@@ -590,34 +584,31 @@ def save_main_config():
|
||||
value = int(raw)
|
||||
except (ValueError, TypeError, OverflowError):
|
||||
return rejection
|
||||
if value < low or (high is not None and value > high) or (even and value % 2):
|
||||
if value < low or value > high or (even and value % 2):
|
||||
return rejection
|
||||
return None
|
||||
|
||||
for field, low, high, even in (('rows', 8, None, True), ('cols', 16, None, False),
|
||||
('chain_length', 1, None, False), ('parallel', 1, 3, False),
|
||||
('brightness', 1, 100, False), ('scan_mode', 0, 1, False),
|
||||
('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:
|
||||
# rp1_rio has its own check below.
|
||||
for field, (_section, low, high, even) in INT_SETTING_LIMITS.items():
|
||||
if field in data and field != 'rp1_rio':
|
||||
error = _hardware_int_error(field, low, high, even)
|
||||
if error:
|
||||
return error
|
||||
|
||||
# A Pi 5 can't drive every combination (src/pi5_matrix_support.py),
|
||||
# and one it can't crashes the display service instead of falling
|
||||
# back. Checked only when this request sets one of those fields, so
|
||||
# a combination already stored doesn't block unrelated saves.
|
||||
pi5_fields = ('row_address_type', 'parallel', 'hardware_mapping')
|
||||
if any(k in data for k in pi5_fields) and is_raspberry_pi_5():
|
||||
# Combinations the library can't start with: a hardware mapping it
|
||||
# doesn't have, more parallel chains than the mapping has outputs,
|
||||
# and on a Pi 5 its narrower RP1 support. Reported only when this
|
||||
# request sets one of the settings involved, so a problem already
|
||||
# stored doesn't block unrelated saves.
|
||||
combination_fields = ('hardware_mapping', 'parallel', 'row_address_type')
|
||||
if any(k in data for k in combination_fields):
|
||||
effective = dict(current_config['display']['hardware'])
|
||||
effective.update({k: data[k] for k in pi5_fields if k in data})
|
||||
unsupported = pi5_unsupported_settings(effective)
|
||||
if unsupported:
|
||||
return jsonify({'status': 'error', 'message': unsupported}), 400
|
||||
effective.update({k: v for k, v in data.items()
|
||||
if k in combination_fields or k in INT_SETTING_LIMITS})
|
||||
refusals = [r for r in library_refusals(effective, pi5=is_raspberry_pi_5())
|
||||
if any(f in data for f in r.fields)]
|
||||
if refusals:
|
||||
return jsonify({'status': 'error', 'message': refusal_message(refusals)}), 400
|
||||
|
||||
# Handle hardware settings
|
||||
for field in ['rows', 'cols', 'chain_length', 'parallel', 'brightness', 'hardware_mapping', 'scan_mode',
|
||||
|
||||
Reference in New Issue
Block a user