#include "tool_set_timezone.h" #include "mimi_config.h" #include #include #include #include #include "esp_log.h" #include "nvs.h" #include "cJSON.h" static const char *TAG = "tool_timezone"; /* Common timezone mappings for user-friendly names */ typedef struct { const char *name; const char *posix_tz; } tz_mapping_t; static const tz_mapping_t tz_mappings[] = { { "Asia/Shanghai", "CST-8" }, { "Asia/Beijing", "CST-8" }, { "Asia/Hong_Kong", "HKT-8" }, { "Asia/Tokyo", "JST-9" }, { "Asia/Seoul", "KST-9" }, { "Asia/Singapore", "SGT-8" }, { "Asia/Kolkata", "IST-5:30" }, { "Asia/Dubai", "GST-4" }, { "Europe/London", "GMT0BST,M3.5.0/1,M10.5.0" }, { "Europe/Paris", "CET-1CEST,M3.5.0,M10.5.0/3" }, { "Europe/Berlin", "CET-1CEST,M3.5.0,M10.5.0/3" }, { "America/New_York", "EST5EDT,M3.2.0,M11.1.0" }, { "America/Chicago", "CST6CDT,M3.2.0,M11.1.0" }, { "America/Denver", "MST7MDT,M3.2.0,M11.1.0" }, { "America/Los_Angeles", "PST8PDT,M3.2.0,M11.1.0" }, { "Australia/Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3" }, { "UTC", "UTC0" }, { "GMT", "GMT0" }, }; static const char *resolve_timezone(const char *tz_str) { if (!tz_str || !tz_str[0]) return NULL; for (size_t i = 0; i < sizeof(tz_mappings) / sizeof(tz_mappings[0]); i++) { if (strcmp(tz_str, tz_mappings[i].name) == 0) { return tz_mappings[i].posix_tz; } } return tz_str; } static bool validate_timezone(const char *tz_str) { if (!tz_str || !tz_str[0]) return false; if (strlen(tz_str) > 64) return false; for (size_t i = 0; i < sizeof(tz_mappings) / sizeof(tz_mappings[0]); i++) { if (strcmp(tz_str, tz_mappings[i].name) == 0) return true; } if (strchr(tz_str, '+') || strchr(tz_str, '-') || strcmp(tz_str, "UTC0") == 0 || strcmp(tz_str, "GMT0") == 0) { return true; } return false; } static esp_err_t save_timezone_nvs(const char *tz_str) { nvs_handle_t nvs; esp_err_t err = nvs_open("system_config", NVS_READWRITE, &nvs); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err)); return err; } err = nvs_set_str(nvs, MIMI_NVS_KEY_TIMEZONE, tz_str); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to save timezone: %s", esp_err_to_name(err)); nvs_close(nvs); return err; } err = nvs_commit(nvs); nvs_close(nvs); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to commit NVS: %s", esp_err_to_name(err)); return err; } return ESP_OK; } esp_err_t tool_set_timezone_execute(const char *input_json, char *output, size_t output_size) { ESP_LOGI(TAG, "Setting timezone..."); cJSON *root = cJSON_Parse(input_json); if (!root) { snprintf(output, output_size, "Error: invalid JSON input"); return ESP_ERR_INVALID_ARG; } cJSON *tz_item = cJSON_GetObjectItem(root, "timezone"); if (!tz_item || !cJSON_IsString(tz_item)) { cJSON_Delete(root); snprintf(output, output_size, "Error: 'timezone' field required (string)"); return ESP_ERR_INVALID_ARG; } const char *input_tz = tz_item->valuestring; const char *resolved_tz = resolve_timezone(input_tz); if (!resolved_tz || !validate_timezone(resolved_tz)) { cJSON_Delete(root); snprintf(output, output_size, "Error: invalid timezone format '%s'. Use POSIX format (e.g. CST-8) or city name (e.g. Asia/Shanghai)", input_tz); return ESP_ERR_INVALID_ARG; } esp_err_t err = save_timezone_nvs(resolved_tz); if (err != ESP_OK) { cJSON_Delete(root); snprintf(output, output_size, "Error: failed to save timezone (%s)", esp_err_to_name(err)); return err; } setenv("TZ", resolved_tz, 1); tzset(); time_t now = time(NULL); struct tm tm_now; localtime_r(&now, &tm_now); char time_str[64]; strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S %Z", &tm_now); cJSON_Delete(root); snprintf(output, output_size, "Timezone set to '%s'. Current time: %s", resolved_tz, time_str); ESP_LOGI(TAG, "Timezone set to: %s, current time: %s", resolved_tz, time_str); return ESP_OK; }