Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
473 changes: 473 additions & 0 deletions docs/schedule_reminder_usage.md

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ set(SOURCES "audio/audio_codec.cc"
"main.cc"
)

set(INCLUDE_DIRS "." "display" "display/lvgl_display" "display/lvgl_display/jpg" "audio" "protocols")
set(INCLUDE_DIRS "." "display" "display/lvgl_display" "display/lvgl_display/jpg" "audio" "protocols" "features")

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

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

# Include features directory
include("features/CMakeLists.txt")

# If target chip is ESP32, exclude specific files to avoid build errors
if(CONFIG_IDF_TARGET_ESP32)
list(REMOVE_ITEM SOURCES "audio/codecs/box_audio_codec.cc"
Expand Down
53 changes: 52 additions & 1 deletion main/application.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
#include "assets.h"
#include "settings.h"

#if CONFIG_ENABLE_SCHEDULE_REMINDER
#include "features/schedule_reminder/schedule_reminder.h"
#include "features/schedule_reminder/schedule_manager.h"
#endif

#include <cstring>
#include <esp_log.h>
#include <cJSON.h>
Expand Down Expand Up @@ -533,6 +538,11 @@ void Application::Start() {
SystemInfo::PrintHeapStats();
SetDeviceState(kDeviceStateIdle);

// Initialize schedule reminder feature
#if CONFIG_ENABLE_SCHEDULE_REMINDER
InitializeScheduleReminder();
#endif

has_server_time_ = ota.HasServerTime();
if (protocol_started) {
std::string message = std::string(Lang::Strings::VERSION) + ota.GetCurrentVersion();
Expand Down Expand Up @@ -891,4 +901,45 @@ void Application::SetAecMode(AecMode mode) {

void Application::PlaySound(const std::string_view& sound) {
audio_service_.PlaySound(sound);
}
}

#if CONFIG_ENABLE_SCHEDULE_REMINDER
void Application::InitializeScheduleReminder() {
auto& schedule_reminder = ScheduleReminder::GetInstance();

// 设置提醒回调 - 使用现有 Alert 系统
schedule_reminder.SetReminderCallback([this](const ScheduleItem& item) {
this->OnScheduleTriggered(item);
});

// 初始化日程管理器
if (!schedule_reminder.Initialize()) {
ESP_LOGE(TAG, "Failed to initialize schedule reminder");
return;
}

// 注册 MCP 工具
#if CONFIG_ENABLE_SCHEDULE_MCP_TOOLS
ScheduleManager::RegisterMcpTools();
#endif

ESP_LOGI(TAG, "Schedule reminder feature initialized successfully");
}

void Application::OnScheduleTriggered(const ScheduleItem& item) {
// 创建提醒消息
char message[256];
if (item.description.empty()) {
snprintf(message, sizeof(message), "提醒: %s", item.title.c_str());
} else {
snprintf(message, sizeof(message), "提醒: %s - %s",
item.title.c_str(), item.description.c_str());
}

// 使用系统现有的通知机制
Alert("日程提醒", message, "bell", Lang::Sounds::OGG_NOTIFICATION);

// 记录日志
ESP_LOGI(TAG, "Schedule triggered: %s", item.title.c_str());
}
#endif
17 changes: 17 additions & 0 deletions main/features/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Features CMakeLists.txt
# This file includes all feature modules

# Schedule Reminder Feature
if(CONFIG_ENABLE_SCHEDULE_REMINDER)
set(SCHEDULE_REMINDER_SRCS
"schedule_reminder/schedule_reminder.cc"
"schedule_reminder/schedule_manager.cc"
)

set(SCHEDULE_REMINDER_INCLUDES
"schedule_reminder"
)

list(APPEND COMPONENT_SRCS ${SCHEDULE_REMINDER_SRCS})
list(APPEND COMPONENT_PRIV_INCLUDES ${SCHEDULE_REMINDER_INCLUDES})
endif()
30 changes: 30 additions & 0 deletions main/features/schedule_reminder/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
menu "Schedule Reminder Features"
config ENABLE_SCHEDULE_REMINDER
bool "Enable Schedule Reminder"
default n
help
Enable schedule reminder functionality

config MAX_SCHEDULE_ITEMS
int "Maximum schedule items"
range 1 50
default 20
depends on ENABLE_SCHEDULE_REMINDER
help
Maximum number of schedule items that can be stored

config SCHEDULE_CHECK_INTERVAL
int "Schedule check interval (seconds)"
range 10 300
default 30
depends on ENABLE_SCHEDULE_REMINDER
help
Interval to check for due schedules

config ENABLE_SCHEDULE_MCP_TOOLS
bool "Enable Schedule MCP Tools"
default y
depends on ENABLE_SCHEDULE_REMINDER
help
Enable MCP tools for schedule management
endmenu
194 changes: 194 additions & 0 deletions main/features/schedule_reminder/schedule_manager.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#include "schedule_manager.h"
#include <esp_log.h>
#include <ctime>

#define TAG "ScheduleManager"

void ScheduleManager::RegisterMcpTools() {
auto& mcp_server = McpServer::GetInstance();

// 添加日程工具
mcp_server.AddTool("schedule.add",
"Add a new schedule reminder",
GetAddScheduleProperties(),
AddScheduleTool);

// 列出日程工具
mcp_server.AddTool("schedule.list",
"List all schedule reminders",
PropertyList(),
ListSchedulesTool);

// 删除日程工具
mcp_server.AddTool("schedule.remove",
"Remove a schedule reminder",
GetRemoveScheduleProperties(),
RemoveScheduleTool);

// 更新日程工具
mcp_server.AddTool("schedule.update",
"Update an existing schedule reminder",
GetUpdateScheduleProperties(),
UpdateScheduleTool);

ESP_LOGI(TAG, "Schedule MCP tools registered");
}

PropertyList ScheduleManager::GetAddScheduleProperties() {
PropertyList props;
props.AddProperty("title", "Schedule title", PropertyType::kString, true);
props.AddProperty("description", "Schedule description", PropertyType::kString, false);
props.AddProperty("trigger_time", "Trigger time (Unix timestamp)", PropertyType::kNumber, true);
props.AddProperty("recurring", "Whether this is a recurring schedule", PropertyType::kBoolean, false);
props.AddProperty("repeat_interval", "Repeat interval in seconds", PropertyType::kNumber, false);
return props;
}

PropertyList ScheduleManager::GetRemoveScheduleProperties() {
PropertyList props;
props.AddProperty("id", "Schedule ID to remove", PropertyType::kString, true);
return props;
}

PropertyList ScheduleManager::GetUpdateScheduleProperties() {
PropertyList props;
props.AddProperty("id", "Schedule ID to update", PropertyType::kString, true);
props.AddProperty("title", "New schedule title", PropertyType::kString, false);
props.AddProperty("description", "New schedule description", PropertyType::kString, false);
props.AddProperty("trigger_time", "New trigger time (Unix timestamp)", PropertyType::kNumber, false);
props.AddProperty("enabled", "Whether the schedule is enabled", PropertyType::kBoolean, false);
props.AddProperty("recurring", "Whether this is a recurring schedule", PropertyType::kBoolean, false);
props.AddProperty("repeat_interval", "Repeat interval in seconds", PropertyType::kNumber, false);
return props;
}

bool ScheduleManager::AddScheduleTool(const PropertyList& properties) {
ScheduleItem item;
item.id = std::to_string(time(nullptr)); // Use timestamp as ID
item.title = properties.GetValue<std::string>("title");
item.description = properties.GetValue<std::string>("description", "");
item.trigger_time = properties.GetValue<int>("trigger_time");
item.recurring = properties.GetValue<bool>("recurring", false);
item.repeat_interval = properties.GetValue<int>("repeat_interval", 0);
item.created_at = std::to_string(time(nullptr));

ScheduleError result = ScheduleReminder::GetInstance().AddSchedule(item);

switch (result) {
case ScheduleError::kSuccess:
ESP_LOGI(TAG, "Schedule added via MCP: %s", item.title.c_str());
return true;
case ScheduleError::kMaxItemsReached:
ESP_LOGE(TAG, "Failed to add schedule via MCP: maximum items reached");
return false;
case ScheduleError::kDuplicateId:
ESP_LOGE(TAG, "Failed to add schedule via MCP: duplicate ID");
return false;
case ScheduleError::kInvalidTime:
ESP_LOGE(TAG, "Failed to add schedule via MCP: invalid trigger time");
return false;
case ScheduleError::kStorageError:
ESP_LOGE(TAG, "Failed to add schedule via MCP: storage error");
return false;
case ScheduleError::kNotInitialized:
ESP_LOGE(TAG, "Failed to add schedule via MCP: not initialized");
return false;
default:
ESP_LOGE(TAG, "Failed to add schedule via MCP: unknown error");
return false;
}
}

bool ScheduleManager::ListSchedulesTool(const PropertyList& properties) {
auto schedules = ScheduleReminder::GetInstance().GetSchedules();

// 这里可以返回日程列表给 MCP 客户端
// 实际实现中可能需要格式化输出

ESP_LOGI(TAG, "Listed %d schedules via MCP", schedules.size());

// 简单记录到日志
for (const auto& schedule : schedules) {
ESP_LOGI(TAG, "Schedule: %s (ID: %s, Time: %ld)",
schedule.title.c_str(), schedule.id.c_str(), schedule.trigger_time);
}

return true;
}

bool ScheduleManager::RemoveScheduleTool(const PropertyList& properties) {
std::string id = properties.GetValue<std::string>("id");
ScheduleError result = ScheduleReminder::GetInstance().RemoveSchedule(id);

switch (result) {
case ScheduleError::kSuccess:
ESP_LOGI(TAG, "Schedule removed via MCP: %s", id.c_str());
return true;
case ScheduleError::kNotFound:
ESP_LOGE(TAG, "Failed to remove schedule via MCP: schedule not found - %s", id.c_str());
return false;
case ScheduleError::kStorageError:
ESP_LOGE(TAG, "Failed to remove schedule via MCP: storage error - %s", id.c_str());
return false;
case ScheduleError::kNotInitialized:
ESP_LOGE(TAG, "Failed to remove schedule via MCP: not initialized - %s", id.c_str());
return false;
default:
ESP_LOGE(TAG, "Failed to remove schedule via MCP: unknown error - %s", id.c_str());
return false;
}
}

bool ScheduleManager::UpdateScheduleTool(const PropertyList& properties) {
std::string id = properties.GetValue<std::string>("id");

// Get existing schedule
ScheduleItem* existing_item = ScheduleReminder::GetInstance().GetSchedule(id);
if (!existing_item) {
ESP_LOGE(TAG, "Schedule not found for update: %s", id.c_str());
return false;
}

// Create updated schedule item
ScheduleItem updated_item = *existing_item;

// Update provided fields
if (properties.HasValue("title")) {
updated_item.title = properties.GetValue<std::string>("title");
}
if (properties.HasValue("description")) {
updated_item.description = properties.GetValue<std::string>("description");
}
if (properties.HasValue("trigger_time")) {
updated_item.trigger_time = properties.GetValue<int>("trigger_time");
}
if (properties.HasValue("enabled")) {
updated_item.enabled = properties.GetValue<bool>("enabled");
}
if (properties.HasValue("recurring")) {
updated_item.recurring = properties.GetValue<bool>("recurring");
}
if (properties.HasValue("repeat_interval")) {
updated_item.repeat_interval = properties.GetValue<int>("repeat_interval");
}

ScheduleError result = ScheduleReminder::GetInstance().UpdateSchedule(id, updated_item);

switch (result) {
case ScheduleError::kSuccess:
ESP_LOGI(TAG, "Schedule updated via MCP: %s", id.c_str());
return true;
case ScheduleError::kNotFound:
ESP_LOGE(TAG, "Failed to update schedule via MCP: schedule not found - %s", id.c_str());
return false;
case ScheduleError::kStorageError:
ESP_LOGE(TAG, "Failed to update schedule via MCP: storage error - %s", id.c_str());
return false;
case ScheduleError::kNotInitialized:
ESP_LOGE(TAG, "Failed to update schedule via MCP: not initialized - %s", id.c_str());
return false;
default:
ESP_LOGE(TAG, "Failed to update schedule via MCP: unknown error - %s", id.c_str());
return false;
}
}
24 changes: 24 additions & 0 deletions main/features/schedule_reminder/schedule_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef SCHEDULE_MANAGER_H
#define SCHEDULE_MANAGER_H

#include "mcp_server.h"
#include "schedule_reminder.h"

class ScheduleManager {
public:
static void RegisterMcpTools();

private:
// MCP 工具实现
static bool AddScheduleTool(const PropertyList& properties);
static bool ListSchedulesTool(const PropertyList& properties);
static bool RemoveScheduleTool(const PropertyList& properties);
static bool UpdateScheduleTool(const PropertyList& properties);

// 工具属性定义
static PropertyList GetAddScheduleProperties();
static PropertyList GetRemoveScheduleProperties();
static PropertyList GetUpdateScheduleProperties();
};

#endif // SCHEDULE_MANAGER_H
Loading