Add typed serial settings operations

Route bounded admin mutations through the existing administration
dispatcher,
covering apply, lifecycle, persistence, authorization, and result
tracking.
Add the browser controls, automatic result refresh, regression coverage,
and
phase documentation.
This commit is contained in:
2026-09-08 00:25:31 +02:00
parent 5a2aa0d4d8
commit 42548f6334
27 changed files with 1398 additions and 42 deletions
+26
View File
@@ -44,6 +44,7 @@ esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *, const char *);
admin = "--admin" in sys.argv
settings = "--settings" in sys.argv
serial_settings = "--serial-settings" in sys.argv
if admin:
HEADERS["esp_system.h"] = "#pragma once\nvoid esp_restart(void);\n"
HEADERS["esp_heap_caps.h"] = """#pragma once
@@ -113,7 +114,31 @@ with tempfile.TemporaryDirectory(prefix="web-cookie-auth-") as directory:
settings_source += function(config_source[config_source.index('const char *serial_config_' + name + '_to_string'):], 'serial_config_' + name + '_to_string') + '\n'
for name in ('set_common_headers', 'send_plain_error', 'authorize_or_respond', 'safe_string', 'serial_settings_handler'):
settings_source += function(server_source, name) + '\n'
# Exercise the serial projection of /api/status without doubling every
# unrelated subsystem. Copy its acquisition, format and arguments verbatim.
status = function(server_source, 'status_handler')
assert 'serial_service_get_config(' not in status, 'status must not use the blocking config getter'
assert 'serial_service_is_running(' not in status, 'status must use snapshot running state'
assert status.count('serial_service_get_snapshot(') == 1
acquisition = status[status.index(' bool serial_config_available ='):
status.index(' serial_service_get_counters(')]
serial_format = status[status.index(' " \\"serial\\":'):
status.index(' " \\"broker\\":')]
argument_start = status.index(' wifi_available ? (unsigned int)wifi.ap_client_count : 0U,')
argument_start = status.index('\n', argument_start) + 1
serial_arguments = status[argument_start:status.index(' broker_available ? "true"')].rstrip().removesuffix(',')
settings_source += '\nstatic int status_serial_projection(char *response, size_t capacity) {\n'
settings_source += ' serial_service_snapshot_t serial_snapshot = {0};\n'
settings_source += ' serial_service_counters_t serial_counters = {0};\n' + acquisition
settings_source += ' return snprintf(response, capacity,\n' + serial_format + ',\n' + serial_arguments + ');\n}\n'
(tmp / 'settings_production.h').write_text(settings_source)
if serial_settings:
config_source = (ROOT / 'src/serial_config.c').read_text()
names = ['serial_config_defaults', 'serial_config_validate']
names += ['serial_config_parse_' + name for name in ('data_bits', 'parity', 'stop_bits', 'flow_control', 'dtr_behavior')]
(tmp / 'serial_config_production.h').write_text('\n'.join(function(config_source, name) for name in names))
console_source = (ROOT / 'src/serial_console.c').read_text()
(tmp / 'serial_console_production.h').write_text('\n'.join(function(console_source, name) for name in ('parse_unsigned', 'set_parameter', 'command_serial')))
sources = [HERE / "test.c", tmp / "installed_httpd.c"]
sources += [ROOT / "src" / name for name in ["web_session_store.c", "web_auth_parse.c", "web_cookie_auth.c", "web_httpd_adapter.c"]]
if admin:
@@ -129,6 +154,7 @@ with tempfile.TemporaryDirectory(prefix="web-cookie-auth-") as directory:
subprocess.run(["cc", "-std=c11", "-Wall", "-Wextra", "-Werror", "-g", "-DHOST_OPENSSL",
*(["-DHOST_ADMIN"] if admin else []),
*(["-DHOST_SETTINGS"] if settings else []),
*(["-DHOST_SERIAL_SETTINGS"] if serial_settings else []),
"-I" + str(tmp), "-I" + str(ROOT / "src"), *map(str, sources), "-lcrypto",
"-o", str(tmp / "test")], check=True, timeout=30)
subprocess.run([str(tmp / "test")], check=True, timeout=20)