HANDLE_JUMP_OTA_VIEW, //处理跳转ota界面
HAND_SCHEDULE_REMINDER_NOTIFY, // 日程提醒通知
HAND_TASK_REMINDER_NOTIFY, // 任务提醒通知
+ HAND_CHARACTER_CONFIG_CHANGE_NOTIFY, // 角色配置更改通知
MAX_NUM_HANDLER,
} UiServiceMsgId;
static void HandleJumpOtaView(Request *request);
static void HandScheduleReminderNotify(Request *request);
static void HandTaskReminderNotify(Request *request);
+static void HandTaskCharacterConfigNotify(Request *request);
// 消息处理程序项数组(每个消息类型对应一个处理函数,函数名以 Handle 开头)
MsgHandlerEntry msgHandlers[] = {
{HANDLE_JUMP_OTA_VIEW, HandleJumpOtaView},
{HAND_SCHEDULE_REMINDER_NOTIFY,HandScheduleReminderNotify},
- {HAND_TASK_REMINDER_NOTIFY,HandTaskReminderNotify}
+ {HAND_TASK_REMINDER_NOTIFY,HandTaskReminderNotify},
+ {HAND_CHARACTER_CONFIG_CHANGE_NOTIFY,HandTaskCharacterConfigNotify},
};
/* -------------------------------------------------------------------------- */
WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandTaskReminderNotify msg_id = %d",request->msgId);
int status = *(int *)request->data;
}
+void HandTaskCharacterConfigNotify(Request *request)
+{
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandTaskCharacterConfigNotify msg_id = %d",request->msgId);
+ int status = *(int *)request->data;
+}
/* -------------------------------------------------------------------------- */
#ifdef __cplusplus
}
return ERRCODE_SUCC;
}
+/* AT^LN_CHARACTER_CFG[=characterName,eyesShape,hairColor,eyesColor,hairStyle,screenImageAsEmoji] */
+static uint32_t ln_character_config_change_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+ VAD_UNUSED(strCommonCmd);
+ const char *deviceId = uapi_ln_get_device_id();
+ const char *id = (deviceId && deviceId[0]) ? deviceId : "DEVICE_LOCAL";
+
+ /* 默认值(与示例一致) */
+ const char *characterId = "char_at";
+ const char *characterName = "小艾";
+ const char *eyesShape = "upturned-eyes";
+ const char *hairColor = "blue";
+ const char *eyesColor = "green";
+ const char *hairStyle = "side-parted-comma";
+ const char *screenImage = "https://example.com/new_avatar.bin";
+ int screenImageAsEmoji = 1; // true
+
+ if (para != NULL && paraLen > 0) {
+ uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
+ uint32_t argc = 0;
+ (void)ConvertCmdToParam(para, paraLen, argv, &argc);
+ if (argc >= 1 && argv[0][0]) { characterName = (const char*)argv[0]; }
+ if (argc >= 2 && argv[1][0]) { eyesShape = (const char*)argv[1]; }
+ if (argc >= 3 && argv[2][0]) { hairColor = (const char*)argv[2]; }
+ if (argc >= 4 && argv[3][0]) { eyesColor = (const char*)argv[3]; }
+ if (argc >= 5 && argv[4][0]) { hairStyle = (const char*)argv[4]; }
+ if (argc >= 6 && argv[5][0]) { screenImageAsEmoji = atoi((char*)argv[5]) ? 1 : 0; }
+ }
+
+ char *json = (char*)malloc(1024);
+ if (json == NULL) {
+ printf("[LN][CHAR_CFG] OOM\n");
+ return ERRCODE_FAIL;
+ }
+ int n = snprintf(json, 1024,
+ "{\n"
+ " \"deviceId\": \"%s\",\n"
+ " \"timestamp\": %u,\n"
+ " \"messageId\": \"at_msg\",\n"
+ " \"command\": \"CharacterConfigChange\",\n"
+ " \"data\": {\n"
+ " \"characterId\": \"%s\",\n"
+ " \"characterName\": \"%s\",\n"
+ " \"characterAvatar\": {\n"
+ " \"eyesShape\": \"%s\",\n"
+ " \"hairColor\": \"%s\",\n"
+ " \"eyesColor\": \"%s\",\n"
+ " \"hairStyle\": \"%s\",\n"
+ " \"screenImage\": \"%s\",\n"
+ " \"screenImageAsEmoji\": %s\n"
+ " }\n"
+ " }\n"
+ "}\n",
+ id, 0U, characterId, characterName, eyesShape, hairColor, eyesColor, hairStyle, screenImage,
+ screenImageAsEmoji ? "true" : "false");
+ if (n <= 0 || n >= 1024) {
+ free(json);
+ printf("[LN][CHAR_CFG] build json failed\n");
+ return ERRCODE_FAIL;
+ }
+
+ ln_parse_character_config(json, n);
+ printf("\r\n[LN][CHAR_CFG] ok. name=%s eyesShape=%s hairColor=%s eyesColor=%s\n",
+ characterName, eyesShape, hairColor, eyesColor);
+
+ free(json);
+ return ERRCODE_SUCC;
+}
+
static const at_cmd_table_t g_ln_api_cmd[] = {
/* ----------------------------------- VAD ---------------------------------- */
{(char *)"AT^VAD_INIT", vad_init, (char *)"初始化 VAD"},
{(char *)"AT^LN_UI_OTA_PROGRESS", ln_ui_ota_progress_at, (char *)"推送OTA升级进度: AT^LN_UI_OTA_PROGRESS=100"},
{(char *)"AT^LN_SCHEDULE_REMINDER", ln_schedule_reminder_at, (char *)"日程提醒: AT^LN_SCHEDULE_REMINDER[=scheduleName,reminderContent]"},
{(char *)"AT^LN_SCHEDULE_REMINDER_CFG", ln_schedule_reminder_cfg_at, (char *)"日程提醒配置: AT^LN_SCHEDULE_REMINDER_CFG[=scheduleName]"},
+ {(char *)"AT^LN_CHARACTER_CFG", ln_character_config_change_at, (char *)"角色配置更改: AT^LN_CHARACTER_CFG[=name,eyesShape,hairColor,eyesColor,hairStyle,screenImageAsEmoji]"},
};
/* 获取录音音频路径 */
const char *uapi_ln_get_audio_path(void)
{
- // 判断目录是否存在,不存在则创建
- if (access(LN_DATA_PATH, F_OK) == -1)
- {
- uapi_create_folder(LN_DATA_PATH);
- }
const char *path = LN_DATA_PATH "/audio.mp3";
return path;
}
g_ln_is_device_bound = val;
}
-/* 获取设备角色配置 */
-LnCharacterConfig uapi_ln_get_character_config(void)
-{
- return g_ln_character_config;
-}
-
/* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
{
switch (type) {
case WS_CMD_CHARACTER_CHANGE: {
printf("[WS] CharacterChange:角色切换\n");
+ if(json_str)
+ {
+ ln_parse_character_config(json_str, (int)strlen(json_str));
+ }
break;
}
case WS_CMD_CHARACTER_CONFIG_CHANGE: {
}
}
+/* 预创建运行期所需目录,避免因目录不存在导致文件创建失败 */
+static void ln_init_required_dirs(void)
+{
+ const char *dirs[] = {
+ LN_DATA_PATH,
+ LN_DATA_PATH "/token", //存放token的
+ LN_DATA_PATH "/speech",
+ LN_DATA_PATH "/story",
+ LN_DATA_PATH "/character",
+ LN_DATA_PATH "/emoji",
+ LN_DATA_PATH "/story/choose",
+ LN_DATA_PATH "/story/random",
+ };
+ int count = (int)(sizeof(dirs) / sizeof(dirs[0]));
+ for (int i = 0; i < count; i++)
+ {
+ if (dirs[i] && dirs[i][0])
+ {
+ uapi_create_folder(dirs[i]);
+ }
+ }
+}
+
+//初始化一些配置信息
+static void ln_load_init_config(void)
+{
+ ln_read_character_config();
+}
+
// 雷鸟http服务初始化
errcode_t uapi_ln_http_server_init(void)
{
// /* 不中断HTTP服务,按需可返回失败 */
// }
- old_Start_AIVoice(LeiniaoDataCallback,NULL);
+ // old_Start_AIVoice(LeiniaoDataCallback,NULL);
+ ln_init_required_dirs();
+ ln_load_init_config();
return ERRCODE_SUCC;
}
#define LN_EMOJI_REPORT_URL LN_BASE_URL LN_EMOJI_REPORT_PATH
#define LN_EMOJI_REPORT_ALL_URL LN_BASE_URL LN_EMOJI_REPORT_ALL_PATH
#define LN_SIGN_SECRET "vDdXVXgPUEUM6_piUjz5AEhMvYTSFzQdPaN-ogKnSus="
-/* OTA 包保存目录*/
-#define LN_OTA_SAVE_DIR "/user/update"
/* 用户数据保存目录 */
-#define LN_DATA_PATH "/user/leiniao"
+#define LN_DATA_PATH "/user/userdata/leiniao"
+#define LN_TOKEN_FILE LN_DATA_PATH "/token/device_token.json" //token
+#define speech_file_path LN_DATA_PATH "/speech/speech.txt"
+#define story_choose_file_path LN_DATA_PATH "/story/choose.txt"
+#define story_random_file_path LN_DATA_PATH "/story/random.txt"
+#define LN_CHARACTER_CONFIG_FILE LN_DATA_PATH "/character/character_config.json"
#define LN_CLIENT_PREPARE_SIZE 2 * 1024
#define TEST_MP3_DOWNLOAD 0 // 测试下载mp3
uint32_t uapi_ln_refresh_device_token(void);
uint32_t uapi_ln_device_get_user_id(LnDeviceBindStatus *out_status, char *out_user_id, uint32_t out_user_id_len);
+/* 从本地读取角色配置 */
+void ln_read_character_config(void);
+
// 解析设备角色配置
void ln_parse_character_config(const char *json_buf, int json_len);
/* 解析哄睡模式开始 */
#include "ln_api.h"
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
#include <cJSON.h>
LnCharacterConfig g_ln_character_config = {0};
*dst = newp;
}
+/* 从本地读取角色配置 */
+void ln_read_character_config(void)
+{
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] read character config");
+ FILE *file = fopen(LN_CHARACTER_CONFIG_FILE, "rb");
+ if (!file)
+ {
+ printf("[DEVICE_CHARACTER] fopen failed\n");
+ return;
+ }
+ char json_buf[1024] = {0};
+ size_t n = fread(json_buf, 1, sizeof(json_buf) - 1, file);
+ if (n == 0)
+ {
+ printf("[DEVICE_CHARACTER] empty config file\n");
+ fclose(file);
+ return;
+ }
+ json_buf[n] = '\0';
+ ln_parse_character_config(json_buf, (int)n);
+ fclose(file);
+ printf("[DEVICE_CHARACTER] read character config success\n");
+}
+
+/* 保存角色配置到本地 */
+static void ln_save_character_config(const char *json_buf)
+{
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] save character config");
+ if (!json_buf)
+ {
+ printf("[DEVICE_CHARACTER] json_buf is NULL\n");
+ return;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LN_CHARACTER_CONFIG_FILE:%s",LN_CHARACTER_CONFIG_FILE);
+ FILE *file = fopen(LN_CHARACTER_CONFIG_FILE, "wb+");
+ if (!file)
+ {
+ printf("[DEVICE_CHARACTER] fopen failed\n");
+ return;
+ }
+ fwrite(json_buf, 1, strlen(json_buf), file);
+ fclose(file);
+ printf("[DEVICE_CHARACTER] save character config success\n");
+}
+
+/* 清空角色配置 */
+static void ln_clear_character_config(void)
+{
+ if (g_ln_character_config.character_id)
+ {
+ free(g_ln_character_config.character_id);
+ g_ln_character_config.character_id = NULL;
+ }
+ if (g_ln_character_config.character_name)
+ {
+ free(g_ln_character_config.character_name);
+ g_ln_character_config.character_name = NULL;
+ }
+ if (g_ln_character_config.avatar_hair_color)
+ {
+ free(g_ln_character_config.avatar_hair_color);
+ g_ln_character_config.avatar_hair_color = NULL;
+ }
+ if (g_ln_character_config.avatar_eyes_color)
+ {
+ free(g_ln_character_config.avatar_eyes_color);
+ g_ln_character_config.avatar_eyes_color = NULL;
+ }
+ if (g_ln_character_config.avatar_hair_style)
+ {
+ free(g_ln_character_config.avatar_hair_style);
+ g_ln_character_config.avatar_hair_style = NULL;
+ }
+ if (g_ln_character_config.avatar_eyes_shape)
+ {
+ free(g_ln_character_config.avatar_eyes_shape);
+ g_ln_character_config.avatar_eyes_shape = NULL;
+ }
+ if (g_ln_character_config.avatar_screen_image)
+ {
+ free(g_ln_character_config.avatar_screen_image);
+ g_ln_character_config.avatar_screen_image = NULL;
+ }
+ g_ln_character_config.avatar_screen_image_as_emoji = 0;
+ g_ln_character_config = (LnCharacterConfig){0};
+}
+
/* 解析设备角色配置 JSON 并保存到全局结构体 */
void ln_parse_character_config(const char *json_buf, int json_len)
{
g_ln_character_config.avatar_screen_image_as_emoji = 0;
}
+ //将json体落库
+ ln_save_character_config(text);
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] push character config change notification");
+
/* 打印解析结果 */
printf("[DEVICE_CHARACTER] parsed:\n");
printf(" character_id: %s\n", g_ln_character_config.character_id ? g_ln_character_config.character_id : "");
printf(" avatar_screen_image: %s\n", g_ln_character_config.avatar_screen_image ? g_ln_character_config.avatar_screen_image : "");
printf(" avatar_screen_image_as_emoji: %d\n", g_ln_character_config.avatar_screen_image_as_emoji);
+ /* 推送角色配置更改通知 */
+ push_character_config_change_notification(g_ln_character_config.character_id,
+ g_ln_character_config.character_name,
+ g_ln_character_config.avatar_hair_color,
+ g_ln_character_config.avatar_eyes_color,
+ g_ln_character_config.avatar_hair_style,
+ g_ln_character_config.avatar_eyes_shape,
+ g_ln_character_config.avatar_screen_image_as_emoji);
+
cJSON_Delete(root);
free(text);
+ ln_clear_character_config();
}
// 获取设备角色信息
#include "ln_api.h"
#include <stdio.h>
-#define speech_file_path "/user/leiniao/speech/speech.txt"
-
/* 读取整个文件到内存,返回缓冲区和长度;需调用者 free */
static uint8_t *ln_read_file_all_speech_chat(const char *path, int *out_len)
{
return ret;
}
- // 打开文件准备写入
- const char *save_path_folder = "/user/leiniao/speech/";
- // 判断目录是否存在,不存在则创建
- if (access(save_path_folder, F_OK) == -1)
- {
- uapi_create_folder(save_path_folder);
- }
FILE *fd = fopen(speech_file_path, "w+");
if (!fd)
{
#include "ln_api.h"
#include <stdio.h>
-#define story_choose_file_path "/user/leiniao/story/choose.txt"
-
/* 读取整个文件到内存,返回缓冲区和长度;需调用者 free */
static uint8_t *ln_read_file_all_story(const char *path, int *out_len)
{
return ret;
}
- // 打开文件准备写入
- const char *save_path_folder = "/user/leiniao/story/";
- // 判断目录是否存在,不存在则创建
- if (access(save_path_folder, F_OK) == -1)
- {
- uapi_create_folder(save_path_folder);
- }
FILE *fd = fopen(story_choose_file_path, "w+");
if (!fd)
{
#include <stdio.h>
#include <stdlib.h>
-#define story_random_file_path "/user/leiniao/story/random.txt"
-
static void ln_parse_story_random_file(void)
{
printf("Starting to parse story choose file...\n");
return ret;
}
- // 打开文件准备写入
- const char *save_path_folder = "/user/leiniao/story/";
- // 判断目录是否存在,不存在则创建
- if (access(save_path_folder, F_OK) == -1)
- {
- uapi_create_folder(save_path_folder);
- }
FILE *fd = fopen(story_random_file_path, "w+");
if (!fd)
{
#include <sys/stat.h>
#include <time.h>
-#define LN_TOKEN_DIR "/user/leiniao/token"
-#define LN_TOKEN_FILE "/user/leiniao/token/device_token.json"
-
static void ln_to_hex_lower(const uint8_t *in, size_t in_len, char *out)
{
static const char hex[] = "0123456789abcdef";
return 0;
}
-/* 创建目录 */
-static void ln_mkdir_p(const char *path)
-{
- if (!path)
- {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_mkdir_p: path is NULL");
- return;
- }
- (void)mkdir(path, 0755);
-}
-
/* 保存设备Token到本地 */
static uint32_t ln_token_save_json(const char *token, uint64_t expire_timestamp)
{
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_token_save_json: token is NULL");
return ERRCODE_FAIL;
}
- ln_mkdir_p(LN_DATA_PATH);
- ln_mkdir_p(LN_TOKEN_DIR);
/* 生成create_time字符串 */
char create_time_str[32] = {0};
SendMsgToUIService(HAND_TASK_REMINDER_NOTIFY, 0,(void *)scheduleName, (int32_t)strlen(scheduleName));
}
+// 推送角色配置更改通知(简洁载荷)
+void push_character_config_change_notification(const char* characterId,
+ const char *characterName,
+ const char *eyesShape,
+ const char *hairColor,
+ const char *eyesColor,
+ const char *hairStyle,
+ int screenImageAsEmoji)
+{
+ // 组装紧凑 payload: 使用分隔符拼装,UI 侧再拆分(或改用结构体)
+ char payload[512] = {0};
+ const char* id = characterId? characterId:"";
+ const char *n = characterName ? characterName : "";
+ const char *es = eyesShape ? eyesShape : "";
+ const char *hc = hairColor ? hairColor : "";
+ const char *ec = eyesColor ? eyesColor : "";
+ const char *hs = hairStyle ? hairStyle : "";
+ (void)snprintf(payload, sizeof(payload) - 1, "%s|%s|%s|%s|%s|%s|%d", id,n, es, hc, ec, hs, screenImageAsEmoji ? 1 : 0);
+
+ SendMsgToUIService(HAND_CHARACTER_CONFIG_CHANGE_NOTIFY, 0, (void *)payload, (int32_t)strlen(payload));
+}
#ifdef __cplusplus
};
void do_restore_factory_setting(void);
// 推送日程提醒通知
void push_schedule_reminder_notification(const char *title, const char *content);
+
// 推送任务提醒
void push_task_reminder_notification(const char *scheduleName);
+//推送角色(配置更改)/切换通知
+void push_character_config_change_notification(
+ const char* characterId,
+ const char *characterName,
+ const char *eyesShape,
+ const char *hairColor,
+ const char *eyesColor,
+ const char *hairStyle,
+ int screenImageAsEmoji);
#ifdef __cplusplus
}
#endif