feat(travel): Implement settings UI and web server for MTR route configuration
- Added MainUIHandler class to manage the main UI and polling for arrival data. - Introduced SettingsUI class for displaying QR code and configuration options. - Created SettingsUIHandler to manage settings UI lifecycle and web server interactions. - Developed WebHandler to handle HTTP requests for MTR route settings, including adding and removing routes. - Implemented web endpoints for fetching MTR lines, routes, and saving settings. - Enhanced UI with responsive design for e-ink displays and added error handling for web interactions.
This commit is contained in:
170
main/ui/apps/travel/settings/settings_handler.cpp
Normal file
170
main/ui/apps/travel/settings/settings_handler.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "ui/apps/travel/settings/settings_handler.h"
|
||||
#include "cJSON.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char* TAG = "TravelSettings";
|
||||
|
||||
namespace travel {
|
||||
|
||||
SettingHandler::SettingHandler(std::unique_ptr<NVSStorageHandler> storage)
|
||||
: routes_()
|
||||
, polling_interval_sec_(DEFAULT_POLLING_INTERVAL)
|
||||
, storage_(std::move(storage)) {
|
||||
}
|
||||
|
||||
esp_err_t SettingHandler::init(const EventGroupHandle_t& system_event_group) {
|
||||
storage_->init(system_event_group);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void SettingHandler::load_settings() {
|
||||
// Load polling interval
|
||||
std::string poll_str = storage_->get(NVS_KEY_POLLING);
|
||||
if (!poll_str.empty()) {
|
||||
polling_interval_sec_ = std::stoul(poll_str);
|
||||
if (polling_interval_sec_ < MIN_POLLING_INTERVAL ||
|
||||
polling_interval_sec_ > MAX_POLLING_INTERVAL) {
|
||||
polling_interval_sec_ = DEFAULT_POLLING_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Load routes
|
||||
std::string routes_json = storage_->get(NVS_KEY_ROUTES);
|
||||
if (!routes_json.empty()) {
|
||||
routes_from_json(routes_json);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Loaded %d routes, polling interval: %d seconds",
|
||||
static_cast<int>(routes_.size()), static_cast<int>(polling_interval_sec_));
|
||||
}
|
||||
|
||||
void SettingHandler::save_settings() {
|
||||
// Save polling interval
|
||||
storage_->put(NVS_KEY_POLLING, std::to_string(polling_interval_sec_));
|
||||
|
||||
// Save routes
|
||||
std::string routes_json = routes_to_json();
|
||||
storage_->put(NVS_KEY_ROUTES, routes_json);
|
||||
|
||||
ESP_LOGI(TAG, "Saved %d routes, polling interval: %d seconds",
|
||||
static_cast<int>(routes_.size()), static_cast<int>(polling_interval_sec_));
|
||||
}
|
||||
|
||||
void SettingHandler::add_route(const RoutePair& route) {
|
||||
if (routes_.size() >= MAX_ROUTES) {
|
||||
ESP_LOGW(TAG, "Maximum number of routes reached (%d)", static_cast<int>(MAX_ROUTES));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
for (const auto& existing : routes_) {
|
||||
if (existing == route) {
|
||||
ESP_LOGW(TAG, "Route already exists");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
routes_.push_back(route);
|
||||
ESP_LOGI(TAG, "Added route: %s -> %s", route.station_name.c_str(), route.dest_name.c_str());
|
||||
}
|
||||
|
||||
void SettingHandler::remove_route(size_t index) {
|
||||
if (index < routes_.size()) {
|
||||
ESP_LOGI(TAG, "Removing route at index %d", static_cast<int>(index));
|
||||
routes_.erase(routes_.begin() + index);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingHandler::clear_routes() {
|
||||
routes_.clear();
|
||||
ESP_LOGI(TAG, "Cleared all routes");
|
||||
}
|
||||
|
||||
void SettingHandler::set_polling_interval(uint32_t seconds) {
|
||||
if (seconds < MIN_POLLING_INTERVAL) {
|
||||
seconds = MIN_POLLING_INTERVAL;
|
||||
} else if (seconds > MAX_POLLING_INTERVAL) {
|
||||
seconds = MAX_POLLING_INTERVAL;
|
||||
}
|
||||
polling_interval_sec_ = seconds;
|
||||
ESP_LOGI(TAG, "Set polling interval to %d seconds", static_cast<int>(seconds));
|
||||
}
|
||||
|
||||
std::string SettingHandler::routes_to_json() const {
|
||||
cJSON* root = cJSON_CreateArray();
|
||||
|
||||
for (const auto& route : routes_) {
|
||||
cJSON* route_obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(route_obj, "line_code", route.line_code.c_str());
|
||||
cJSON_AddStringToObject(route_obj, "line_name", route.line_name.c_str());
|
||||
cJSON_AddStringToObject(route_obj, "line_color", route.line_color.c_str());
|
||||
cJSON_AddStringToObject(route_obj, "station_code", route.station_code.c_str());
|
||||
cJSON_AddStringToObject(route_obj, "station_name", route.station_name.c_str());
|
||||
cJSON_AddStringToObject(route_obj, "dest_code", route.dest_code.c_str());
|
||||
cJSON_AddStringToObject(route_obj, "dest_name", route.dest_name.c_str());
|
||||
cJSON_AddItemToArray(root, route_obj);
|
||||
}
|
||||
|
||||
char* json_str = cJSON_PrintUnformatted(root);
|
||||
std::string result(json_str ? json_str : "[]");
|
||||
if (json_str) {
|
||||
free(json_str);
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SettingHandler::routes_from_json(const std::string& json) {
|
||||
routes_.clear();
|
||||
|
||||
cJSON* root = cJSON_Parse(json.c_str());
|
||||
if (!root || !cJSON_IsArray(root)) {
|
||||
ESP_LOGE(TAG, "Failed to parse routes JSON");
|
||||
if (root) {
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int array_size = cJSON_GetArraySize(root);
|
||||
for (int i = 0; i < array_size && i < static_cast<int>(MAX_ROUTES); i++) {
|
||||
cJSON* route_obj = cJSON_GetArrayItem(root, i);
|
||||
if (!route_obj || !cJSON_IsObject(route_obj)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RoutePair route;
|
||||
cJSON* item;
|
||||
|
||||
item = cJSON_GetObjectItem(route_obj, "line_code");
|
||||
if (item && cJSON_IsString(item)) route.line_code = item->valuestring;
|
||||
|
||||
item = cJSON_GetObjectItem(route_obj, "line_name");
|
||||
if (item && cJSON_IsString(item)) route.line_name = item->valuestring;
|
||||
|
||||
item = cJSON_GetObjectItem(route_obj, "line_color");
|
||||
if (item && cJSON_IsString(item)) route.line_color = item->valuestring;
|
||||
|
||||
item = cJSON_GetObjectItem(route_obj, "station_code");
|
||||
if (item && cJSON_IsString(item)) route.station_code = item->valuestring;
|
||||
|
||||
item = cJSON_GetObjectItem(route_obj, "station_name");
|
||||
if (item && cJSON_IsString(item)) route.station_name = item->valuestring;
|
||||
|
||||
item = cJSON_GetObjectItem(route_obj, "dest_code");
|
||||
if (item && cJSON_IsString(item)) route.dest_code = item->valuestring;
|
||||
|
||||
item = cJSON_GetObjectItem(route_obj, "dest_name");
|
||||
if (item && cJSON_IsString(item)) route.dest_name = item->valuestring;
|
||||
|
||||
if (!route.line_code.empty() && !route.station_code.empty() && !route.dest_code.empty()) {
|
||||
routes_.push_back(route);
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
ESP_LOGI(TAG, "Loaded %d routes from JSON", static_cast<int>(routes_.size()));
|
||||
}
|
||||
|
||||
} // namespace travel
|
||||
Reference in New Issue
Block a user