Add Typed Display Settings Administration

Implements admin-only Display settings with generation-checked
Apply, Save, Load, Defaults, and Reset operations across the web UI,
CLI, SSH dispatcher, and local UI owner. Adds bounded HTTP handling,
session-isolated operation results, browser lifecycle support, and
comprehensive host tests and documentation.
This commit is contained in:
2026-09-09 10:15:23 +02:00
parent 60d9c54bb4
commit d9ec3c08de
26 changed files with 1164 additions and 81 deletions
+61 -13
View File
@@ -203,6 +203,8 @@ static TickType_t s_diagnostic_hold_started;
static uint32_t s_external_activity_sequence;
static local_ui_config_t s_config;
static bool s_config_available;
static bool s_config_busy;
static uint32_t s_config_generation;
static const gpio_num_t s_button_gpios[LOCAL_STATUS_BUTTON_COUNT] = {
LOCAL_UI_BUTTON_PREVIOUS_GPIO,
@@ -1368,21 +1370,62 @@ esp_err_t local_status_ui_get_config(local_ui_config_t *config)
return available ? ESP_OK : ESP_ERR_INVALID_STATE;
}
esp_err_t local_status_ui_get_settings(local_ui_config_t *config, uint32_t *generation)
{
if (config == NULL || generation == NULL) return ESP_ERR_INVALID_ARG;
portENTER_CRITICAL(&s_timing_mux);
esp_err_t error = !s_config_available ? ESP_ERR_INVALID_STATE :
s_config_busy ? ESP_ERR_TIMEOUT : ESP_OK;
if (error == ESP_OK) {
*config = s_config;
*generation = s_config_generation;
}
portEXIT_CRITICAL(&s_timing_mux);
return error;
}
esp_err_t local_status_ui_update_settings(local_ui_settings_action_t action,
uint32_t expected_generation, const local_ui_config_t *config, bool *loaded_defaults)
{
if (loaded_defaults != NULL) *loaded_defaults = false;
if (action < LOCAL_UI_SETTINGS_APPLY || action > LOCAL_UI_SETTINGS_RESET ||
(action == LOCAL_UI_SETTINGS_APPLY && local_ui_config_validate(config) != ESP_OK))
return ESP_ERR_INVALID_ARG;
local_ui_config_t candidate;
portENTER_CRITICAL(&s_timing_mux);
esp_err_t error = !s_config_available ||
(expected_generation && expected_generation != s_config_generation) ||
s_config_generation == UINT32_MAX ? ESP_ERR_INVALID_STATE :
s_config_busy ? ESP_ERR_TIMEOUT : ESP_OK;
if (error == ESP_OK) {
candidate = action == LOCAL_UI_SETTINGS_APPLY ? *config : s_config;
s_config_busy = true;
}
portEXIT_CRITICAL(&s_timing_mux);
if (error != ESP_OK) return error;
bool stored = true;
if (action == LOCAL_UI_SETTINGS_LOAD) error = local_ui_config_load(&candidate, &stored);
if (action == LOCAL_UI_SETTINGS_DEFAULTS || action == LOCAL_UI_SETTINGS_RESET)
local_ui_config_defaults(&candidate);
if (action == LOCAL_UI_SETTINGS_SAVE || action == LOCAL_UI_SETTINGS_RESET)
error = local_ui_config_save(&candidate);
portENTER_CRITICAL(&s_timing_mux);
if (error == ESP_OK && action != LOCAL_UI_SETTINGS_SAVE) {
s_config = candidate;
++s_config_generation;
++s_external_activity_sequence;
}
s_config_busy = false;
portEXIT_CRITICAL(&s_timing_mux);
if (error == ESP_OK && loaded_defaults != NULL) *loaded_defaults = !stored;
return error;
}
esp_err_t local_status_ui_apply_config(const local_ui_config_t *config)
{
esp_err_t error = local_ui_config_validate(config);
if (error != ESP_OK) {
return error;
}
portENTER_CRITICAL(&s_timing_mux);
if (!s_config_available) {
portEXIT_CRITICAL(&s_timing_mux);
return ESP_ERR_INVALID_STATE;
}
s_config = *config;
++s_external_activity_sequence;
portEXIT_CRITICAL(&s_timing_mux);
return ESP_OK;
return local_status_ui_update_settings(LOCAL_UI_SETTINGS_APPLY, 0, config, NULL);
}
esp_err_t local_status_ui_start(const local_ui_config_t *config)
@@ -1397,6 +1440,11 @@ esp_err_t local_status_ui_start(const local_ui_config_t *config)
portENTER_CRITICAL(&s_timing_mux);
s_config = *config;
if (s_config_generation == UINT32_MAX) {
portEXIT_CRITICAL(&s_timing_mux);
return ESP_ERR_INVALID_STATE;
}
++s_config_generation;
s_config_available = true;
portEXIT_CRITICAL(&s_timing_mux);