Skip to content

Commit f1efcbe

Browse files
committed
feat: 添加日程提醒功能
1 parent 221d4e7 commit f1efcbe

File tree

9 files changed

+1305
-2
lines changed

9 files changed

+1305
-2
lines changed

docs/schedule_reminder_usage.md

Lines changed: 473 additions & 0 deletions
Large diffs are not rendered by default.

main/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ set(SOURCES "audio/audio_codec.cc"
3737
"main.cc"
3838
)
3939

40-
set(INCLUDE_DIRS "." "display" "display/lvgl_display" "display/lvgl_display/jpg" "audio" "protocols")
40+
set(INCLUDE_DIRS "." "display" "display/lvgl_display" "display/lvgl_display/jpg" "audio" "protocols" "features")
4141

4242
# Add board common files
4343
file(GLOB BOARD_COMMON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/common/*.cc)
@@ -661,6 +661,9 @@ endif()
661661

662662
file(GLOB COMMON_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/common/*.ogg)
663663

664+
# Include features directory
665+
include("features/CMakeLists.txt")
666+
664667
# If target chip is ESP32, exclude specific files to avoid build errors
665668
if(CONFIG_IDF_TARGET_ESP32)
666669
list(REMOVE_ITEM SOURCES "audio/codecs/box_audio_codec.cc"

main/application.cc

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
#include "assets.h"
1111
#include "settings.h"
1212

13+
#if CONFIG_ENABLE_SCHEDULE_REMINDER
14+
#include "features/schedule_reminder/schedule_reminder.h"
15+
#include "features/schedule_reminder/schedule_manager.h"
16+
#endif
17+
1318
#include <cstring>
1419
#include <esp_log.h>
1520
#include <cJSON.h>
@@ -533,6 +538,11 @@ void Application::Start() {
533538
SystemInfo::PrintHeapStats();
534539
SetDeviceState(kDeviceStateIdle);
535540

541+
// Initialize schedule reminder feature
542+
#if CONFIG_ENABLE_SCHEDULE_REMINDER
543+
InitializeScheduleReminder();
544+
#endif
545+
536546
has_server_time_ = ota.HasServerTime();
537547
if (protocol_started) {
538548
std::string message = std::string(Lang::Strings::VERSION) + ota.GetCurrentVersion();
@@ -891,4 +901,45 @@ void Application::SetAecMode(AecMode mode) {
891901

892902
void Application::PlaySound(const std::string_view& sound) {
893903
audio_service_.PlaySound(sound);
894-
}
904+
}
905+
906+
#if CONFIG_ENABLE_SCHEDULE_REMINDER
907+
void Application::InitializeScheduleReminder() {
908+
auto& schedule_reminder = ScheduleReminder::GetInstance();
909+
910+
// 设置提醒回调 - 使用现有 Alert 系统
911+
schedule_reminder.SetReminderCallback([this](const ScheduleItem& item) {
912+
this->OnScheduleTriggered(item);
913+
});
914+
915+
// 初始化日程管理器
916+
if (!schedule_reminder.Initialize()) {
917+
ESP_LOGE(TAG, "Failed to initialize schedule reminder");
918+
return;
919+
}
920+
921+
// 注册 MCP 工具
922+
#if CONFIG_ENABLE_SCHEDULE_MCP_TOOLS
923+
ScheduleManager::RegisterMcpTools();
924+
#endif
925+
926+
ESP_LOGI(TAG, "Schedule reminder feature initialized successfully");
927+
}
928+
929+
void Application::OnScheduleTriggered(const ScheduleItem& item) {
930+
// 创建提醒消息
931+
char message[256];
932+
if (item.description.empty()) {
933+
snprintf(message, sizeof(message), "提醒: %s", item.title.c_str());
934+
} else {
935+
snprintf(message, sizeof(message), "提醒: %s - %s",
936+
item.title.c_str(), item.description.c_str());
937+
}
938+
939+
// 使用系统现有的通知机制
940+
Alert("日程提醒", message, "bell", Lang::Sounds::OGG_NOTIFICATION);
941+
942+
// 记录日志
943+
ESP_LOGI(TAG, "Schedule triggered: %s", item.title.c_str());
944+
}
945+
#endif

main/features/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Features CMakeLists.txt
2+
# This file includes all feature modules
3+
4+
# Schedule Reminder Feature
5+
if(CONFIG_ENABLE_SCHEDULE_REMINDER)
6+
set(SCHEDULE_REMINDER_SRCS
7+
"schedule_reminder/schedule_reminder.cc"
8+
"schedule_reminder/schedule_manager.cc"
9+
)
10+
11+
set(SCHEDULE_REMINDER_INCLUDES
12+
"schedule_reminder"
13+
)
14+
15+
list(APPEND COMPONENT_SRCS ${SCHEDULE_REMINDER_SRCS})
16+
list(APPEND COMPONENT_PRIV_INCLUDES ${SCHEDULE_REMINDER_INCLUDES})
17+
endif()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
menu "Schedule Reminder Features"
2+
config ENABLE_SCHEDULE_REMINDER
3+
bool "Enable Schedule Reminder"
4+
default n
5+
help
6+
Enable schedule reminder functionality
7+
8+
config MAX_SCHEDULE_ITEMS
9+
int "Maximum schedule items"
10+
range 1 50
11+
default 20
12+
depends on ENABLE_SCHEDULE_REMINDER
13+
help
14+
Maximum number of schedule items that can be stored
15+
16+
config SCHEDULE_CHECK_INTERVAL
17+
int "Schedule check interval (seconds)"
18+
range 10 300
19+
default 30
20+
depends on ENABLE_SCHEDULE_REMINDER
21+
help
22+
Interval to check for due schedules
23+
24+
config ENABLE_SCHEDULE_MCP_TOOLS
25+
bool "Enable Schedule MCP Tools"
26+
default y
27+
depends on ENABLE_SCHEDULE_REMINDER
28+
help
29+
Enable MCP tools for schedule management
30+
endmenu
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
#include "schedule_manager.h"
2+
#include <esp_log.h>
3+
#include <ctime>
4+
5+
#define TAG "ScheduleManager"
6+
7+
void ScheduleManager::RegisterMcpTools() {
8+
auto& mcp_server = McpServer::GetInstance();
9+
10+
// 添加日程工具
11+
mcp_server.AddTool("schedule.add",
12+
"Add a new schedule reminder",
13+
GetAddScheduleProperties(),
14+
AddScheduleTool);
15+
16+
// 列出日程工具
17+
mcp_server.AddTool("schedule.list",
18+
"List all schedule reminders",
19+
PropertyList(),
20+
ListSchedulesTool);
21+
22+
// 删除日程工具
23+
mcp_server.AddTool("schedule.remove",
24+
"Remove a schedule reminder",
25+
GetRemoveScheduleProperties(),
26+
RemoveScheduleTool);
27+
28+
// 更新日程工具
29+
mcp_server.AddTool("schedule.update",
30+
"Update an existing schedule reminder",
31+
GetUpdateScheduleProperties(),
32+
UpdateScheduleTool);
33+
34+
ESP_LOGI(TAG, "Schedule MCP tools registered");
35+
}
36+
37+
PropertyList ScheduleManager::GetAddScheduleProperties() {
38+
PropertyList props;
39+
props.AddProperty("title", "Schedule title", PropertyType::kString, true);
40+
props.AddProperty("description", "Schedule description", PropertyType::kString, false);
41+
props.AddProperty("trigger_time", "Trigger time (Unix timestamp)", PropertyType::kNumber, true);
42+
props.AddProperty("recurring", "Whether this is a recurring schedule", PropertyType::kBoolean, false);
43+
props.AddProperty("repeat_interval", "Repeat interval in seconds", PropertyType::kNumber, false);
44+
return props;
45+
}
46+
47+
PropertyList ScheduleManager::GetRemoveScheduleProperties() {
48+
PropertyList props;
49+
props.AddProperty("id", "Schedule ID to remove", PropertyType::kString, true);
50+
return props;
51+
}
52+
53+
PropertyList ScheduleManager::GetUpdateScheduleProperties() {
54+
PropertyList props;
55+
props.AddProperty("id", "Schedule ID to update", PropertyType::kString, true);
56+
props.AddProperty("title", "New schedule title", PropertyType::kString, false);
57+
props.AddProperty("description", "New schedule description", PropertyType::kString, false);
58+
props.AddProperty("trigger_time", "New trigger time (Unix timestamp)", PropertyType::kNumber, false);
59+
props.AddProperty("enabled", "Whether the schedule is enabled", PropertyType::kBoolean, false);
60+
props.AddProperty("recurring", "Whether this is a recurring schedule", PropertyType::kBoolean, false);
61+
props.AddProperty("repeat_interval", "Repeat interval in seconds", PropertyType::kNumber, false);
62+
return props;
63+
}
64+
65+
bool ScheduleManager::AddScheduleTool(const PropertyList& properties) {
66+
ScheduleItem item;
67+
item.id = std::to_string(time(nullptr)); // Use timestamp as ID
68+
item.title = properties.GetValue<std::string>("title");
69+
item.description = properties.GetValue<std::string>("description", "");
70+
item.trigger_time = properties.GetValue<int>("trigger_time");
71+
item.recurring = properties.GetValue<bool>("recurring", false);
72+
item.repeat_interval = properties.GetValue<int>("repeat_interval", 0);
73+
item.created_at = std::to_string(time(nullptr));
74+
75+
ScheduleError result = ScheduleReminder::GetInstance().AddSchedule(item);
76+
77+
switch (result) {
78+
case ScheduleError::kSuccess:
79+
ESP_LOGI(TAG, "Schedule added via MCP: %s", item.title.c_str());
80+
return true;
81+
case ScheduleError::kMaxItemsReached:
82+
ESP_LOGE(TAG, "Failed to add schedule via MCP: maximum items reached");
83+
return false;
84+
case ScheduleError::kDuplicateId:
85+
ESP_LOGE(TAG, "Failed to add schedule via MCP: duplicate ID");
86+
return false;
87+
case ScheduleError::kInvalidTime:
88+
ESP_LOGE(TAG, "Failed to add schedule via MCP: invalid trigger time");
89+
return false;
90+
case ScheduleError::kStorageError:
91+
ESP_LOGE(TAG, "Failed to add schedule via MCP: storage error");
92+
return false;
93+
case ScheduleError::kNotInitialized:
94+
ESP_LOGE(TAG, "Failed to add schedule via MCP: not initialized");
95+
return false;
96+
default:
97+
ESP_LOGE(TAG, "Failed to add schedule via MCP: unknown error");
98+
return false;
99+
}
100+
}
101+
102+
bool ScheduleManager::ListSchedulesTool(const PropertyList& properties) {
103+
auto schedules = ScheduleReminder::GetInstance().GetSchedules();
104+
105+
// 这里可以返回日程列表给 MCP 客户端
106+
// 实际实现中可能需要格式化输出
107+
108+
ESP_LOGI(TAG, "Listed %d schedules via MCP", schedules.size());
109+
110+
// 简单记录到日志
111+
for (const auto& schedule : schedules) {
112+
ESP_LOGI(TAG, "Schedule: %s (ID: %s, Time: %ld)",
113+
schedule.title.c_str(), schedule.id.c_str(), schedule.trigger_time);
114+
}
115+
116+
return true;
117+
}
118+
119+
bool ScheduleManager::RemoveScheduleTool(const PropertyList& properties) {
120+
std::string id = properties.GetValue<std::string>("id");
121+
ScheduleError result = ScheduleReminder::GetInstance().RemoveSchedule(id);
122+
123+
switch (result) {
124+
case ScheduleError::kSuccess:
125+
ESP_LOGI(TAG, "Schedule removed via MCP: %s", id.c_str());
126+
return true;
127+
case ScheduleError::kNotFound:
128+
ESP_LOGE(TAG, "Failed to remove schedule via MCP: schedule not found - %s", id.c_str());
129+
return false;
130+
case ScheduleError::kStorageError:
131+
ESP_LOGE(TAG, "Failed to remove schedule via MCP: storage error - %s", id.c_str());
132+
return false;
133+
case ScheduleError::kNotInitialized:
134+
ESP_LOGE(TAG, "Failed to remove schedule via MCP: not initialized - %s", id.c_str());
135+
return false;
136+
default:
137+
ESP_LOGE(TAG, "Failed to remove schedule via MCP: unknown error - %s", id.c_str());
138+
return false;
139+
}
140+
}
141+
142+
bool ScheduleManager::UpdateScheduleTool(const PropertyList& properties) {
143+
std::string id = properties.GetValue<std::string>("id");
144+
145+
// Get existing schedule
146+
ScheduleItem* existing_item = ScheduleReminder::GetInstance().GetSchedule(id);
147+
if (!existing_item) {
148+
ESP_LOGE(TAG, "Schedule not found for update: %s", id.c_str());
149+
return false;
150+
}
151+
152+
// Create updated schedule item
153+
ScheduleItem updated_item = *existing_item;
154+
155+
// Update provided fields
156+
if (properties.HasValue("title")) {
157+
updated_item.title = properties.GetValue<std::string>("title");
158+
}
159+
if (properties.HasValue("description")) {
160+
updated_item.description = properties.GetValue<std::string>("description");
161+
}
162+
if (properties.HasValue("trigger_time")) {
163+
updated_item.trigger_time = properties.GetValue<int>("trigger_time");
164+
}
165+
if (properties.HasValue("enabled")) {
166+
updated_item.enabled = properties.GetValue<bool>("enabled");
167+
}
168+
if (properties.HasValue("recurring")) {
169+
updated_item.recurring = properties.GetValue<bool>("recurring");
170+
}
171+
if (properties.HasValue("repeat_interval")) {
172+
updated_item.repeat_interval = properties.GetValue<int>("repeat_interval");
173+
}
174+
175+
ScheduleError result = ScheduleReminder::GetInstance().UpdateSchedule(id, updated_item);
176+
177+
switch (result) {
178+
case ScheduleError::kSuccess:
179+
ESP_LOGI(TAG, "Schedule updated via MCP: %s", id.c_str());
180+
return true;
181+
case ScheduleError::kNotFound:
182+
ESP_LOGE(TAG, "Failed to update schedule via MCP: schedule not found - %s", id.c_str());
183+
return false;
184+
case ScheduleError::kStorageError:
185+
ESP_LOGE(TAG, "Failed to update schedule via MCP: storage error - %s", id.c_str());
186+
return false;
187+
case ScheduleError::kNotInitialized:
188+
ESP_LOGE(TAG, "Failed to update schedule via MCP: not initialized - %s", id.c_str());
189+
return false;
190+
default:
191+
ESP_LOGE(TAG, "Failed to update schedule via MCP: unknown error - %s", id.c_str());
192+
return false;
193+
}
194+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef SCHEDULE_MANAGER_H
2+
#define SCHEDULE_MANAGER_H
3+
4+
#include "mcp_server.h"
5+
#include "schedule_reminder.h"
6+
7+
class ScheduleManager {
8+
public:
9+
static void RegisterMcpTools();
10+
11+
private:
12+
// MCP 工具实现
13+
static bool AddScheduleTool(const PropertyList& properties);
14+
static bool ListSchedulesTool(const PropertyList& properties);
15+
static bool RemoveScheduleTool(const PropertyList& properties);
16+
static bool UpdateScheduleTool(const PropertyList& properties);
17+
18+
// 工具属性定义
19+
static PropertyList GetAddScheduleProperties();
20+
static PropertyList GetRemoveScheduleProperties();
21+
static PropertyList GetUpdateScheduleProperties();
22+
};
23+
24+
#endif // SCHEDULE_MANAGER_H

0 commit comments

Comments
 (0)