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:
8
main/external/mtr/arrival.cpp
vendored
8
main/external/mtr/arrival.cpp
vendored
@@ -11,8 +11,8 @@ StationArrivalInfo::StationArrivalInfo(
|
||||
const std::string& train_line_code,
|
||||
const std::string& train_station_code
|
||||
) : _status(UNKNOWN_STATUS)
|
||||
, _train_line(train_line_code)
|
||||
, _train_station(train_station_code) {
|
||||
, _train_line(train_line_code)
|
||||
, _train_station(train_station_code) {
|
||||
|
||||
if (!arrival_json) {
|
||||
ESP_LOGE(TAG, "arrival_json is null");
|
||||
@@ -21,6 +21,8 @@ StationArrivalInfo::StationArrivalInfo(
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Parsing arrival JSON for %s-%s", train_line_code.c_str(), train_station_code.c_str());
|
||||
|
||||
// Parse status
|
||||
cJSON* status_json = cJSON_GetObjectItem(arrival_json, "status");
|
||||
if (status_json && cJSON_IsNumber(status_json)) {
|
||||
@@ -30,7 +32,7 @@ StationArrivalInfo::StationArrivalInfo(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: verify the arrival json parsing
|
||||
ESP_LOGD(TAG, "Status: %d, Message: %s", (int)_status, _message.c_str());
|
||||
|
||||
// Parse message (if present)
|
||||
cJSON* message_json = cJSON_GetObjectItem(arrival_json, "message");
|
||||
|
||||
8
main/external/mtr/line_info.cpp
vendored
8
main/external/mtr/line_info.cpp
vendored
@@ -17,6 +17,14 @@ LineInfo::LineInfo(cJSON* line_json) {
|
||||
ESP_LOGW(LINE_INFO_TAG, "Missing or invalid 'code' field");
|
||||
}
|
||||
|
||||
// Parse line name
|
||||
cJSON* name_json = cJSON_GetObjectItem(line_json, "name");
|
||||
if (name_json && cJSON_IsString(name_json)) {
|
||||
_name = name_json->valuestring;
|
||||
} else {
|
||||
ESP_LOGW(LINE_INFO_TAG, "Missing or invalid 'name' field");
|
||||
}
|
||||
|
||||
// Parse line color (note: field is 'line_color' in JSON, not 'color')
|
||||
cJSON* color_json = cJSON_GetObjectItem(line_json, "line_color");
|
||||
if (color_json && cJSON_IsString(color_json)) {
|
||||
|
||||
5
main/external/mtr/line_info.h
vendored
5
main/external/mtr/line_info.h
vendored
@@ -20,6 +20,10 @@ public:
|
||||
return _code.c_str();
|
||||
}
|
||||
// caller does not own the returned char pointers
|
||||
const char* name() const {
|
||||
return _name.c_str();
|
||||
}
|
||||
// caller does not own the returned char pointers
|
||||
const char* color() const {
|
||||
return _color.c_str();
|
||||
}
|
||||
@@ -40,6 +44,7 @@ private:
|
||||
);
|
||||
|
||||
std::string _code;
|
||||
std::string _name;
|
||||
std::string _color;
|
||||
std::vector<StationInfo> _stations;
|
||||
};
|
||||
|
||||
65
main/external/mtr/mtr.cpp
vendored
65
main/external/mtr/mtr.cpp
vendored
@@ -8,12 +8,13 @@
|
||||
#include "cJSON.h"
|
||||
#include "esp_log.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
static const char* TAG = "MTRNextTrainHandler";
|
||||
|
||||
// MTR Next Train API endpoint
|
||||
// Note: This is a placeholder - replace with actual MTR API endpoint
|
||||
static const char* MTR_API_BASE = "https://rt.data.gov.hk/v1/transport/mtr/getSchedule.php";
|
||||
|
||||
MTRNextTrainHandler::MTRNextTrainHandler() {
|
||||
@@ -102,13 +103,14 @@ MtrArrivalErrorCode MTRNextTrainHandler::get_next_arrival_info(
|
||||
}
|
||||
|
||||
// Build API URL
|
||||
std::ostringstream url;
|
||||
url << MTR_API_BASE << "?line=" << line_code << "&sta=" << station_code;
|
||||
std::string url_str = MTR_API_BASE;
|
||||
url_str += "?line=";
|
||||
url_str += line_code;
|
||||
url_str += "&sta=";
|
||||
url_str += station_code;
|
||||
if (lang == Language::EN) {
|
||||
url << "&lang=en";
|
||||
url_str += "&lang=en";
|
||||
}
|
||||
|
||||
std::string url_str = url.str();
|
||||
ESP_LOGI(TAG, "Fetching arrival info from: %s", url_str.c_str());
|
||||
|
||||
// Create HTTP client configuration
|
||||
@@ -116,8 +118,7 @@ MtrArrivalErrorCode MTRNextTrainHandler::get_next_arrival_info(
|
||||
http_config.url = url_str.c_str();
|
||||
http_config.timeout_ms = 10000;
|
||||
http_config.transport_type = HTTP_TRANSPORT_OVER_SSL;
|
||||
http_config.use_global_ca_store = true;
|
||||
http_config.skip_cert_common_name_check = false;
|
||||
http_config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
|
||||
// Get HTTP handler and perform request
|
||||
auto http_handler = network_handler->get_http_handler(std::move(http_config));
|
||||
@@ -146,21 +147,49 @@ MtrArrivalErrorCode MTRNextTrainHandler::get_next_arrival_info(
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Received %d bytes from MTR API", total_len);
|
||||
ESP_LOGD(TAG, "Response: %s", buffer);
|
||||
|
||||
// Parse JSON response
|
||||
cJSON* arrival_json = cJSON_Parse(buffer);
|
||||
free(buffer);
|
||||
ESP_LOGI(TAG, "Parsing full API response");
|
||||
cJSON* root_json = cJSON_Parse(buffer);
|
||||
delete[] buffer;
|
||||
|
||||
if (!arrival_json) {
|
||||
ESP_LOGE(TAG, "Failed to parse MTR API response");
|
||||
if (!root_json) {
|
||||
const char* error_ptr = cJSON_GetErrorPtr();
|
||||
if (error_ptr) {
|
||||
ESP_LOGE(TAG, "Failed to parse MTR API response at position: %s", error_ptr);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to parse MTR API response - unknown error");
|
||||
}
|
||||
return MtrArrivalErrorCode::NO_ARRIVAL_INFO;
|
||||
}
|
||||
|
||||
// Create StationArrivalInfo object
|
||||
out_info = new StationArrivalInfo(mtr_data, arrival_json, line_code, station_code);
|
||||
cJSON* data_json = cJSON_GetObjectItem(root_json, "data");
|
||||
if (!data_json) {
|
||||
ESP_LOGE(TAG, "Could not find 'data' object in response");
|
||||
cJSON_Delete(root_json);
|
||||
return MtrArrivalErrorCode::NO_ARRIVAL_INFO;
|
||||
}
|
||||
|
||||
cJSON_Delete(arrival_json);
|
||||
std::string station_key = line_code + "-" + station_code;
|
||||
cJSON* station_json = cJSON_GetObjectItem(data_json, station_key.c_str());
|
||||
if (!station_json) {
|
||||
ESP_LOGE(TAG, "Could not find station key '%s' in data object", station_key.c_str());
|
||||
cJSON_Delete(root_json);
|
||||
return MtrArrivalErrorCode::NO_ARRIVAL_INFO;
|
||||
}
|
||||
|
||||
cJSON* status_json = cJSON_GetObjectItem(root_json, "status");
|
||||
if (status_json && cJSON_IsNumber(status_json)) {
|
||||
cJSON_AddItemToObject(station_json, "status", cJSON_Duplicate(status_json, 1));
|
||||
}
|
||||
|
||||
cJSON* message_json = cJSON_GetObjectItem(root_json, "message");
|
||||
if (message_json && cJSON_IsString(message_json)) {
|
||||
cJSON_AddItemToObject(station_json, "message", cJSON_Duplicate(message_json, 1));
|
||||
}
|
||||
|
||||
out_info = new StationArrivalInfo(mtr_data, station_json, line_code, station_code);
|
||||
|
||||
cJSON_Delete(root_json);
|
||||
|
||||
ESP_LOGI(TAG, "Successfully retrieved arrival info for %s/%s", line_code.c_str(), station_code.c_str());
|
||||
return MtrArrivalErrorCode::NONE;
|
||||
|
||||
Reference in New Issue
Block a user