feat: 添加时区设置功能,默认时区改为 CST-8
Some checks failed
Build / idf-build (push) Has been cancelled
Build & Release / build (push) Has been cancelled

- 新增 set_timezone LLM 工具,支持通过对话设置时区
- 新增 set_timezone / timezone_show CLI 命令
- 默认时区从 PST 改为 CST-8(中国标准时间 UTC+8)
- 支持 POSIX 格式和 18 个城市名映射(Asia/Shanghai 等)
- 时区通过 NVS 持久化存储(system_config namespace)
- config_show 中显示当前时区配置
- 更新 changelog.md 和 taolun.md 文档
This commit is contained in:
2026-04-01 00:50:41 +08:00
parent eedc6757d8
commit 7dc4122778
24 changed files with 645 additions and 52 deletions

View File

@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#ifndef GPIO_IS_VALID_GPIO

View File

@@ -4,6 +4,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <sys/time.h>
#include "esp_log.h"

View File

@@ -2,6 +2,7 @@
#include "mimi_config.h"
#include "tools/tool_web_search.h"
#include "tools/tool_get_time.h"
#include "tools/tool_set_timezone.h"
#include "tools/tool_files.h"
#include "tools/tool_cron.h"
#include "tools/tool_gpio.h"
@@ -83,6 +84,18 @@ esp_err_t tool_registry_init(void)
};
register_tool(&gt);
/* Register set_timezone */
mimi_tool_t stz = {
.name = "set_timezone",
.description = "Set the system timezone. Accepts POSIX format (e.g. CST-8, EST5EDT,M3.2.0,M11.1.0) or city name (e.g. Asia/Shanghai, America/New_York).",
.input_schema_json =
"{\"type\":\"object\","
"\"properties\":{\"timezone\":{\"type\":\"string\",\"description\":\"Timezone in POSIX format or city name (e.g. CST-8, Asia/Shanghai)\"}},"
"\"required\":[\"timezone\"]}",
.execute = tool_set_timezone_execute,
};
register_tool(&stz);
/* Register read_file */
mimi_tool_t rf = {
.name = "read_file",

View File

@@ -0,0 +1,145 @@
#include "tool_set_timezone.h"
#include "mimi_config.h"
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#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;
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "esp_err.h"
#include <stddef.h>
/**
* Execute set_timezone tool.
* Sets the system timezone via NVS and updates the TZ environment variable.
* Input JSON: {"timezone": "CST-8"} or {"timezone": "Asia/Shanghai"}
*/
esp_err_t tool_set_timezone_execute(const char *input_json, char *output, size_t output_size);