std::string description(static_cast<char*>(request->data), request->len);
WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandUpgradeDescriptionNotify description = %s", description.c_str());
GraphicService::GetInstance()->PostGraphicEvent(
- std::bind(&OHOS::OtaModel::SetUpgradeDescription,OHOS::OtaModel::GetInstance(), description.c_str()));
+ std::bind(&OHOS::OtaModel::SetUpgradeDescription,OHOS::OtaModel::GetInstance(), description));
}
/* -------------------------------------------------------------------------- */
#ifdef __cplusplus
}
break;
}
+ case WS_CMD_DEVICE_UPGRADE:
+ {
+ printf("[WS] DeviceUpgrade:设备升级\n");
+ if (json_str)
+ {
+ ln_parse_device_upgrade(json_str, (int)strlen(json_str));
+ }
+ break;
+ }
case WS_CMD_PING:
{
printf("[WS] Ping\n");
/* 预创建运行期所需目录,避免因目录不存在导致文件创建失败 */
static void ln_init_required_dirs(void)
{
+ uapi_delete_files(LN_TOKEN_FILE);
const char *dirs[] = {
LN_DATA_PATH,
LN_DATA_PATH "/token", // 存放token的
void ln_parse_delete_emoji(const char *json_str, int json_len);
/* 上报角色表情组下载状态(从队列数据解析)*/
void ln_emoji_report_status(char *data);
-
+/* 解析设备升级 */
+void ln_parse_device_upgrade(const char *json_str, int json_len);
/* 从下发JSON中提取userId */
void ln_ws_ack_user_id_from_json(const char *json_str);
/* ========== Ack 发送接口 ========== */
int ln_ws_send_schedule_reminder_config_ack(int schedule_id, bool success, const char *msg);
int ln_ws_send_delete_emoji_ack(const char *character_id, const char *eyesColor, const char *eyesShape, bool success, const char *msg);
int ln_ws_send_download_emoji_ack(const char *character_id, const char *eyesColor, const char *eyesShape, bool success, const char *msg);
-
+int ln_ws_send_device_upgrade_ack(const char *update_id, const char *target_version, bool success, const char *msg);
/* =================== 时间线状态(Timeline getStatus) =================== */
/* 查询时间线状态(GET /v1/timeline/getStatus)*/
uint32_t ln_timeline_get_status_http(const char *device_id,
WS_CMD_AUDIO_RECORDING_START, /* 开始录音上传 */
WS_CMD_AUDIO_RECORDING_END, /* 结束录音上传 */
WS_CMD_SENTENCE_PLAYBACK_COMPLETE, /* 单句语音播放完成 */
+ WS_CMD_DEVICE_UPGRADE, /* 设备升级(DeviceUpgrade) */
WS_CMD_MAX /* 最大命令 */
} ws_command_t;
free(text);
}
+/* 解析设备升级命令 DeviceUpgrade */
+void ln_parse_device_upgrade(const char *json_buf, int json_len)
+{
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "解析设备升级命令JSON");
+ if (!json_buf || json_len <= 0)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[OTA] parse upgrade command: invalid json");
+ return;
+ }
+
+ char *text = (char *)malloc((size_t)json_len + 1);
+ if (!text) return;
+ memcpy(text, json_buf, (size_t)json_len);
+ text[json_len] = '\0';
+
+ cJSON *root = cJSON_Parse(text);
+ if (!root)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[OTA] parse upgrade command: json parse failed");
+ free(text);
+ return;
+ }
+
+ cJSON *data = cJSON_GetObjectItem(root, "data");
+ if (!data || !cJSON_IsObject(data))
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[OTA] upgrade command data missing");
+ cJSON_Delete(root);
+ free(text);
+ return;
+ }
+
+ /*
+ {
+ "deviceId": "DEVICE_001",
+ "timestamp": 1640995200,
+ "messageId": "msg_013",
+ "command": "DeviceUpgrade",
+ "data": {
+ "deviceId": "DEVICE_001",
+ "updateId": "upgrade_12345",
+ "sourceVersion": "1.0.0",
+ "targetVersion": "1.2.0",
+ "updateType": 1,
+ "packageId": 456,
+ "packageUrl": "https://example.com/firmware/update_1.2.0.bin",
+ "packageMd5": "a1b2c3d4e5f6789012345678901234567890",
+ "packageSize": 10485760,
+ "description": "修复若干bug,优化系统性能"
+ }
+ }
+ */
+
+ cJSON *updateId = cJSON_GetObjectItem(data, "updateId");
+ cJSON *sourceVersion = cJSON_GetObjectItem(data, "sourceVersion");
+ cJSON *targetVersion = cJSON_GetObjectItem(data, "targetVersion");
+ cJSON *updateType = cJSON_GetObjectItem(data, "updateType");
+ cJSON *packageId = cJSON_GetObjectItem(data, "packageId");
+ cJSON *packageUrl = cJSON_GetObjectItem(data, "packageUrl");
+ cJSON *packageMd5 = cJSON_GetObjectItem(data, "packageMd5");
+ cJSON *packageSize = cJSON_GetObjectItem(data, "packageSize");
+ cJSON *description = cJSON_GetObjectItem(data, "description");
+
+ /* 校验必要字段 */
+ if (!(updateId && cJSON_IsString(updateId) && updateId->valuestring &&
+ targetVersion && cJSON_IsString(targetVersion) && targetVersion->valuestring &&
+ packageUrl && cJSON_IsString(packageUrl) && packageUrl->valuestring &&
+ packageMd5 && cJSON_IsString(packageMd5) && packageMd5->valuestring))
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[OTA] upgrade command missing required fields");
+ ln_ws_send_device_upgrade_ack(
+ updateId ? updateId->valuestring : "",
+ targetVersion ? targetVersion->valuestring : "",
+ false,
+ "缺少必要的升级信息");
+ cJSON_Delete(root);
+ free(text);
+ return;
+ }
+
+ /* 兼容 updateType 为数字或字符串的两种情况 */
+ int ut = -1;
+ if (cJSON_IsNumber(updateType)) ut = (int)updateType->valuedouble;
+ else if (cJSON_IsString(updateType)) ut = atoi(updateType->valuestring);
+ g_ln_last_update_type = ut;
+
+ //传过来的ota包大小
+ g_ln_last_package_size = 0;
+ if (packageSize && cJSON_IsNumber(packageSize)) {
+ g_ln_last_package_size = (int)packageSize->valuedouble;
+ if (g_ln_last_package_size < 0) g_ln_last_package_size = 0;
+ }
+ /* 检查存储空间(这里仅为示例,实际需根据具体平台实现) */
+ bool storage_ok = true;
+ if (g_ln_last_package_size > 0)
+ {
+ if (can_download_file(UPG_FILE_NAME, g_ln_last_package_size) == 0)
+ {
+ storage_ok = false;
+ }
+ uapi_ln_set_ota_package_size(g_ln_last_package_size);
+ }
+ if (!storage_ok)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[OTA] insufficient storage space(存储空间不足,无法升级)");
+ ln_ws_send_device_upgrade_ack(
+ updateId->valuestring,
+ targetVersion->valuestring,
+ false,
+ "存储空间不足,无法升级");
+ cJSON_Delete(root);
+ free(text);
+ return;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"targetVersion=%s, updateType=%d, url=%s, md5=%s, packagesize=%d, description=%s",
+ targetVersion->valuestring,
+ g_ln_last_update_type,
+ packageUrl->valuestring ,
+ packageMd5->valuestring ,
+ g_ln_last_package_size,
+ description->valuestring);
+
+ /* 记录必要字段供后续 AT^LN_OTA_DOWNLOAD 使用 */
+ if (g_ln_last_target_version) { free(g_ln_last_target_version); g_ln_last_target_version = NULL; }
+ if (g_ln_last_package_url) { free(g_ln_last_package_url); g_ln_last_package_url = NULL; }
+ if (g_ln_last_package_md5) { free(g_ln_last_package_md5); g_ln_last_package_md5 = NULL; }
+
+ if (targetVersion && cJSON_IsString(targetVersion)) {
+ size_t len = strlen(targetVersion->valuestring);
+ g_ln_last_target_version = (char *)malloc(len + 1);
+ if (g_ln_last_target_version) memcpy(g_ln_last_target_version, targetVersion->valuestring, len + 1);
+ }
+ if (packageUrl && cJSON_IsString(packageUrl)) {
+ size_t len = strlen(packageUrl->valuestring);
+ g_ln_last_package_url = (char *)malloc(len + 1);
+ if (g_ln_last_package_url) memcpy(g_ln_last_package_url, packageUrl->valuestring, len + 1);
+ }
+ if (packageMd5 && cJSON_IsString(packageMd5)) {
+ size_t len = strlen(packageMd5->valuestring);
+ g_ln_last_package_md5 = (char *)malloc(len + 1);
+ if (g_ln_last_package_md5) memcpy(g_ln_last_package_md5, packageMd5->valuestring, len + 1);
+ }
+
+ if (description && cJSON_IsString(description)) {
+ const char *description_ = description->valuestring;
+ push_upgrade_description_notification(description_);
+ } else {
+ push_upgrade_description_notification("");
+ }
+
+ if (ut == 0)
+ {
+ /* 强制升级:直接触发下载 */
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] force upgrade: trigger download(强制升级:直接触发下载)");
+ push_ota_status(OTA_UI_STATUS_FORCE_START);
+ }
+ else
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] optional upgrade: waiting for user confirmation(可选升级:等待用户确认)");
+ push_ota_status(OTA_UI_STATUS_OPTIONAL_START);
+ }
+
+ /* 发送升级应答,确认任务已触发 */
+ ln_ws_send_device_upgrade_ack(
+ updateId->valuestring,
+ targetVersion->valuestring,
+ true,
+ "升级任务已触发,开始下载升级包");
+
+ cJSON_Delete(root);
+ free(text);
+}
+
uint32_t ln_ota_get_task_http(const char *device_id,
const char *device_token,
const char *source_version)
cJSON_free(json_string);
cJSON_Delete(json);
return ret;
+}
+
+/* 发送设备升级应答 DeviceUpgradeAck */
+int ln_ws_send_device_upgrade_ack(const char *update_id, const char *target_version, bool success, const char *msg)
+{
+ if (ln_ws_get_connection_state() != 2)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Cannot send DeviceUpgradeAck, connection state: %d\n", ln_ws_get_connection_state());
+ return -1;
+ }
+
+ uint32_t timestamp;
+ uapi_get_timestampe_ss(×tamp);
+
+ cJSON *json = cJSON_CreateObject();
+ if (!json)
+ return -1;
+
+ cJSON_AddStringToObject(json, "deviceId", uapi_ln_get_device_id());
+ cJSON_AddNumberToObject(json, "timestamp", (double)timestamp);
+
+ char message_id[64];
+ snprintf(message_id, sizeof(message_id), "ack_upgrade_%u", timestamp);
+ cJSON_AddStringToObject(json, "messageId", message_id);
+ cJSON_AddStringToObject(json, "command", "DeviceUpgradeAck");
+
+ cJSON *data = cJSON_CreateObject();
+ if (!data)
+ {
+ cJSON_Delete(json);
+ return -1;
+ }
+ cJSON_AddStringToObject(data, "updateId", (update_id && update_id[0]) ? update_id : "");
+ cJSON_AddStringToObject(data, "targetVersion", (target_version && target_version[0]) ? target_version : "");
+ cJSON_AddBoolToObject(data, "success", success);
+ cJSON_AddStringToObject(data, "message", msg ? msg : (success ? "升级任务已触发" : "升级任务触发失败"));
+ cJSON_AddItemToObject(json, "data", data);
+
+ char *json_string = cJSON_Print(json);
+ if (!json_string)
+ {
+ cJSON_Delete(json);
+ return -1;
+ }
+
+ int ret = uapi_ws_send_text(json_string);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sent DeviceUpgradeAck: %s\n", json_string);
+ cJSON_free(json_string);
+ cJSON_Delete(json);
+ return ret;
}
\ No newline at end of file
return WS_CMD_CHOOSE_STORY_RESPONSE;
} else if (strcmp(command_str, "RandomStoryResponse") == 0) {
return WS_CMD_RANDOM_STORY_RESPONSE;
+ } else if (strcmp(command_str, "DeviceUpgrade") == 0) {
+ return WS_CMD_DEVICE_UPGRADE;
} else {
return WS_CMD_UNKNOWN;
}
Device->>Device: 播放提醒内容\r
and 哄睡模式询问\r
Device->>Device: 识别到哄睡意图\r
- Device->>Server: WebSocket发送<br/>{"command": "SleepModeInquire", "data": {"intention": "START_SLEEP_MODE", "chatId": "..."}}\r
+ Device->>Server: WebSocket发送<br/>{"command": "SleepModeInquire", "data": {"intention": "StartSleepMode", "chatId": "..."}}\r
Server->>Server: 生成询问内容\r
Server->>Device: WebSocket推送<br/>{"command": "SleepModeInquireAck", "data": {"textContent": "...", "audioUrl": "...", ...}}\r
Server->>Device: WebSocket二进制消息<br/>流式发送音频数据\r
"messageId": "sleep_inquire_001",\r
"command": "SleepModeInquire",\r
"data": {\r
- "intention": "START_SLEEP_MODE",\r
+ "intention": "StartSleepMode",\r
"chatId": "chat_12345"\r
}\r
}\r
- 失败可能原因:网络异常、存储空间不足、下载超时等\r
- 建议设备实现断点续传机制\r
\r
+#### 设备升级应答 (DeviceUpgradeAck)\r
+**命令描述**:设备端对设备升级命令的应答,确认升级任务触发状态\r
+\r
+**消息格式**:\r
+```json\r
+{\r
+ "deviceId": "设备ID",\r
+ "timestamp": 1640995200,\r
+ "messageId": "消息ID",\r
+ "command": "DeviceUpgradeAck",\r
+ "data": {\r
+ "updateId": "任务ID",\r
+ "targetVersion": "目标版本号",\r
+ "success": true,\r
+ "message": "升级任务已触发"\r
+ }\r
+}\r
+```\r
+\r
+**消息示例**:\r
+```json\r
+{\r
+ "deviceId": "DEVICE_001",\r
+ "timestamp": 1640995200,\r
+ "messageId": "ack_upgrade_001",\r
+ "command": "DeviceUpgradeAck",\r
+ "data": {\r
+ "updateId": "upgrade_12345",\r
+ "targetVersion": "1.2.0",\r
+ "success": true,\r
+ "message": "升级任务已触发,开始下载升级包"\r
+ }\r
+}\r
+```\r
+\r
+**data字段说明**:\r
+\r
+| 字段名 | 类型 | 描述 |\r
+|--------|------|------|\r
+| updateId | String | 任务ID,与升级命令中的updateId对应 |\r
+| targetVersion | String | 目标版本号 |\r
+| success | Boolean | 是否成功触发升级任务 |\r
+| message | String | 结果描述信息 |\r
+\r
+**协议说明**:\r
+- 设备收到DeviceUpgrade命令后,开始下载升级包并发送此应答\r
+- success为true表示成功触发升级任务(开始下载),false表示触发失败\r
+- 此应答仅表示升级任务是否成功触发,不代表升级最终结果\r
+- 失败可能原因:存储空间不足、网络异常、版本校验失败等\r
+- 升级成功后设备会自动重启,重启后以新版本运行\r
+\r
### 服务端业务消息\r
\r
服务端通过WebSocket连接推送业务消息给设备端,消息采用JSON格式,消息结构如下:\r
"messageId": "sleep_inquire_ack_001",\r
"command": "SleepModeInquireAck",\r
"data": {\r
- "intention": "START_SLEEP_MODE",\r
+ "intention": "StartSleepMode",\r
"textContent": "你想听故事吗?如果想听,请告诉我你想听什么类型的故事",\r
"audioUrl": "https://example.com/audio/sleep_inquire.mp3",\r
"audioDuration": 3000,\r
"messageId": "msg_010",\r
"command": "ScheduleReminderConfig",\r
"data": {\r
- "scheduleName": "七点起床"\r
+ "scheduleName": "七点起床",\r
+ "scheduleId": 1,\r
+ "displayText": "提醒设置成功"\r
}\r
}\r
```\r
| 字段名 | 类型 | 描述 |\r
|--------|------|------|\r
| scheduleName | String | 日程名称 |\r
+| scheduleId | Integer | 日程ID |\r
+| displayText | String | 展示文本,如"提醒设置成功"、"提醒事项已删除" |\r
+\r
+设备端展示“displayText”字段的文本。\r
\r
#### 14. 下载表情 (DownloadEmoji)\r
**命令描述**:通知设备下载指定角色的表情包\r
- 删除完成后,调用`/v1/emoji/reportEmoji`接口上报删除状态\r
- 删除操作应清理本地缓存的表情文件\r
\r
+#### 16. 设备升级 (DeviceUpgrade)\r
+**命令描述**:通知设备执行固件升级\r
+**消息示例**:\r
+```json\r
+{\r
+ "deviceId": "DEVICE_001",\r
+ "timestamp": 1640995200,\r
+ "messageId": "msg_013",\r
+ "command": "DeviceUpgrade",\r
+ "data": {\r
+ "deviceId": "DEVICE_001",\r
+ "updateId": "upgrade_12345",\r
+ "sourceVersion": "1.0.0",\r
+ "targetVersion": "1.2.0",\r
+ "updateType": 1,\r
+ "description": "修复若干bug,优化系统性能",\r
+ "packageId": 456,\r
+ "packageUrl": "https://example.com/firmware/update_1.2.0.bin",\r
+ "packageMd5": "a1b2c3d4e5f6789012345678901234567890",\r
+ "packageSize": 10485760\r
+ }\r
+}\r
+```\r
+\r
+**data字段说明**:\r
+\r
+| 字段名 | 类型 | 描述 |\r
+|--------|------|------|\r
+| deviceId | String | 设备ID |\r
+| updateId | String | 任务ID,用于上报更新结果 |\r
+| sourceVersion | String | 源版本号 |\r
+| targetVersion | String | 目标版本号 |\r
+| updateType | Integer | 升级类型:0-手动升级,1-强制升级,2-强制下载 |\r
+| description | String | 升级描述信息 |\r
+| packageId | Integer | 升级包ID |\r
+| packageUrl | String | 升级包下载地址 |\r
+| packageMd5 | String | 升级包MD5校验值 |\r
+| packageSize | Long | 升级包大小(字节) |\r
+\r
+**升级类型说明**:\r
+\r
+| updateType值 | 说明 |\r
+|-------------|------|\r
+| 0 | 紧急更新 | \r
+| 1 | 重要更新 | \r
+| 2 | 普通更新 |\r
+\r
+\r
+**设备处理说明**:\r
+- 设备收到此命令后,根据updateType判断升级策略\r
+- 下载升级包:从packageUrl下载固件文件\r
+- 校验完整性:使用packageMd5校验下载的文件\r
+- 发送应答:下载开始时发送DeviceUpgradeAck应答确认触发结果\r
+- 执行升级:校验通过后,执行固件升级操作\r
+- 设备重启:升级完成后自动重启,以新版本运行\r
+- 错误处理:如果下载失败或校验失败,应在应答中说明失败原因\r
+\r
+**升级流程**:\r
+1. 小程序端触发升级操作\r
+2. 服务端通过Kafka发送升级消息\r
+3. device-api模块消费消息并通过WebSocket推送给设备\r
+4. 设备收到命令,开始下载升级包\r
+5. 设备发送DeviceUpgradeAck应答(表示开始升级)\r
+6. 下载完成后校验MD5\r
+7. 校验通过后执行升级并重启\r
+8. 重启后以新版本重新获取设备Token\r
+\r
\r
## 7. 表情管理接口\r
\r