#include #include "server/ai_server.h" #include "echo_cloud.h" #include "echo_random.h" #include "json_c/json_tokener.h" #define ECHO_CLOUD_STREAM_URL "http://api.echocloud.com/stream" #define ECHO_CLOUD_MSG_URL "http://api.echocloud.com/entry" typedef void (*funcptr)(json_object *, void *); typedef struct _jsonc_handle { const char *rsp_msg; funcptr func; } Jsonc_handle; static const char echo_cloud_enc_http_tab[] = "POST /stream HTTP/1.1\r\n" "Host: api.echocloud.com\r\n" "Connection: close\r\n" "Transfer-Encoding: chunked\r\n" "X-Echocloud-Version: 3\r\n" "X-Echocloud-Channel-Uuid: %s\r\n" "X-Echocloud-Device-ID: %s\r\n" "X-Echocloud-Nonce: %s\r\n" "X-Echocloud-Signature: %s\r\n" "X-Echocloud-Type: %s\r\n" "Content-type: audio/amr;bit=16;rate=8000;frame=32\r\n" "\r\n"; static const char echo_cloud_msg_http_tab[] = "POST /entry HTTP/1.1\r\n" "Host: api.echocloud.com\r\n" #ifdef HTTP_KEEP_ALIVE "Connection: keep-alive\r\n" #else "Connection: close\r\n" #endif "X-Echocloud-Version: 3\r\n" "Content-type: application/json\r\n" "Content-Length: %lu\r\n" "\r\n"; static const char *const msg_type_name_array[] = { "device.log", "device.heartbeat", "device.online", "device.offline", "bandwidth.changed", "volume.changed", "light.changed", "battery.changed", "battery.charge_started", "battery.charge_ended", "media_player.started", "media_player.finished", "media_player.stopped", "media_player.paused", "media_player.resumed", "media_player.progress_changed", "ai_dialog.started", "ai_dialog.finished", "ai_dialog.stopped", "intercom.started", "intercom.finished", "intercom.stopped", "intercom.alerted", "alert.scheduled", "alert.canceled", "alert.triggered", "alert.finished", "alert.terminated", "child_lock.changed", "firmware.upgrade_started", "firmware.upgrade_succeeded", "firmware.upgrade_failed", "wifi.soundwave_configuration_succeeded", "ai_dialog.input.device", "intercom.receive.device", "device.request_code.device", "volume.change.device", "media_player.pause.device", "media_player.resume.device", "media_player.previous.device", "media_player.next.device", "media_player.favorite.device", "media_player.play_favorite.device", "child_lock.change.device", "media_player.add_favorite.device", "media_player.delete_favorite.device", "custom.event.device", "media_player.play_custom_art_lists.device", "playlist_mode.change.device", "skill.enter.device", "month_age.get.device", }; static const char *cmd_to_msg_type(DEVICE_MSG_TYPE cmd) { if (cmd < MAX_DEVICE_MSG) { return msg_type_name_array[cmd]; } return ""; } static struct json_object *echo_cloud_create_cjson(struct echo_cloud_http_hdl *hdl, DEVICE_MSG_TYPE cmd, json_object *send_data) { char nonce[33]; char signature_str[65]; avGenRand(nonce, 32); nonce[32] = 0; echo_cloud_get_hmac_sha256(hdl->auth->device_id, hdl->auth->channel_uuid, nonce, signature_str, hdl->auth->hash); signature_str[64] = 0; struct json_object *root = json_object_new_object(); struct json_object *message = json_object_new_object(); struct json_object *meta = json_object_new_object(); json_object_object_add(root, "nonce", json_object_new_string(nonce)); json_object_object_add(root, "signature", json_object_new_string(signature_str)); json_object_object_add(root, "message", message); json_object_object_add(root, "meta", meta); json_object_object_add(meta, "channel_uuid", json_object_new_string(hdl->auth->channel_uuid)); json_object_object_add(meta, "device_id", json_object_new_string(hdl->auth->device_id)); json_object_object_add(message, "data", send_data); json_object_object_add(message, "type", json_object_new_string(cmd_to_msg_type(cmd))); return root; } static void aiDialogPlayHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *tmp = NULL; tmp = json_object_object_get(data, "audio_enabled"); if (tmp) { if (FALSE == json_object_get_boolean(tmp)) { return; } } tmp = json_object_object_get(data, "token"); if (tmp) { strcpy(p->token.ai_dialog_token, json_object_get_string(tmp)); } tmp = json_object_object_get(data, "audio_url"); if (tmp && p->media_play_opt != MEDIA_PLAY_IMMEDIATELY && p->media_play_opt != MEDIA_IGNORE_SPEEK) { JL_echo_cloud_media_speak_play(json_object_get_string(tmp)); /* echo_cloud_enc_http_close(&p->http); */ /* json_object *cmd_data = json_object_new_object(); */ /* json_object_object_add(cmd_data, "token", json_object_new_string(p->token.ai_dialog_token)); */ /* echo_cloud_msg_http_req(&p->http, AI_DIALOG_STARTED, cmd_data); */ } } static void intercomAlertHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *tmp = NULL; tmp = json_object_object_get(data, "audio_url"); if (tmp) { /* JL_echo_cloud_media_speak_play(json_object_get_string(tmp)); */ } } static void intercomNotifyHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *tmp = NULL; tmp = json_object_object_get(data, "token"); if (tmp) { strcpy(p->token.intercom_notify_token, json_object_get_string(tmp)); } tmp = json_object_object_get(data, "alert_url"); if (tmp) { /* JL_echo_cloud_media_speak_play(json_object_get_string(tmp)); */ } tmp = json_object_object_get(data, "audio_url"); if (tmp) { JL_echo_cloud_wechat_speak_play(json_object_get_string(tmp)); } } static void mediaPlayerStartHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *tmp = NULL; tmp = json_object_object_get(data, "audio_enabled"); if (tmp) { if (FALSE == json_object_get_boolean(tmp)) { return; } } tmp = json_object_object_get(data, "token"); if (tmp) { strcpy(p->token.media_play_token, json_object_get_string(tmp)); } tmp = json_object_object_get(data, "audio_url"); if (tmp) { if (json_object_get_string_len(tmp) + 1 > p->media_url_len) { free(p->media_url); p->media_url_len = json_object_get_string_len(tmp) + 1; p->media_url = (char *)malloc(p->media_url_len); if (!p->media_url) { return; } } strcpy(p->media_url, json_object_get_string(tmp)); if (p->media_play_opt == MEDIA_PLAY_IMMEDIATELY || p->media_play_opt == MEDIA_IGNORE_SPEEK) { if (!p->is_record) { JL_echo_cloud_media_audio_play(p->media_url); p->media_play_state = ECHO_CLOUD_MEDIA_STATE_START; json_object *cmd_data = json_object_new_object(); json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token)); echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_STARTED, cmd_data); } } else { p->media_play_opt = MEDIA_WAIT_PLAY; } } } static void mediaPlayerStopHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; if (p->media_play_state != ECHO_CLOUD_MEDIA_STATE_STOP) { JL_echo_cloud_media_audio_pause(NULL); p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP; json_object *cmd_data = json_object_new_object(); json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token)); json_object_object_add(cmd_data, "offset", json_object_new_int(1000 * get_app_music_playtime())); echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_STOPPED, cmd_data); } } static void mediaPlayerPauseHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; if (p->media_play_state == ECHO_CLOUD_MEDIA_STATE_START) { JL_echo_cloud_media_audio_pause(NULL); p->media_play_state = ECHO_CLOUD_MEDIA_STATE_PAUSE; json_object *cmd_data = json_object_new_object(); json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token)); json_object_object_add(cmd_data, "offset", json_object_new_int(1000 * get_app_music_playtime())); echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_PAUSED, cmd_data); } } static void mediaPlayerResumeHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; if (p->media_play_state == ECHO_CLOUD_MEDIA_STATE_PAUSE) { JL_echo_cloud_media_audio_continue(NULL); p->media_play_state = ECHO_CLOUD_MEDIA_STATE_START; json_object *cmd_data = json_object_new_object(); echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_RESUMED, cmd_data); } } static void VolumeChangeHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *volume = json_object_object_get(data, "volume"); if (volume) { JL_echo_cloud_volume_change_notify(json_object_get_int(volume)); json_object *cmd_data = json_object_new_object(); json_object_object_add(cmd_data, "volume", json_object_new_int(json_object_get_int(volume))); echo_cloud_msg_http_req(&p->http, VOLUME_CHANGED, cmd_data); } } static void alertTriggerHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *tmp = json_object_object_get(data, "token"); if (tmp) { strcpy(p->token.alert_schedule_token, json_object_get_string(tmp)); } tmp = json_object_object_get(data, "audio_urls"); if (tmp) { json_object *val = json_object_array_get_idx(tmp, 1); if (val) { p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP; JL_echo_cloud_media_speak_play(json_object_get_string(val)); return; } val = json_object_array_get_idx(tmp, 0); if (val) { p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP; JL_echo_cloud_media_speak_play(json_object_get_string(val)); } } } static void FirmwareUpgradeHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *version = json_object_object_get(data, "version"); json_object *checksum = json_object_object_get(data, "checksum"); json_object *cmd_data = NULL; json_object *url = json_object_object_get(data, "url"); if (!version || !checksum || !url) { return; } if (0 != check_echo_cloud_firmware_version(json_object_get_string(url), json_object_get_string(version), json_object_get_string(checksum))) { return; } cmd_data = json_object_new_object(); echo_cloud_msg_http_req(&p->http, FIRMWARE_UPGRADE_STARTED, cmd_data); p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP; echo_cloud_msg_http_close(&p->http); int error = echo_cloud_get_firmware_data(&p->http.ctx, json_object_get_string(url), json_object_get_string(version)); if (0 == error) { cmd_data = json_object_new_object(); echo_cloud_msg_http_req(&p->http, FIRMWARE_UPGRADE_SUCCEEDED, cmd_data); } else { char error_code[32]; snprintf(error_code, sizeof(error_code), "error code is %d", error); cmd_data = json_object_new_object(); json_object_object_add(cmd_data, "log", json_object_new_string(error_code)); echo_cloud_msg_http_req(&p->http, FIRMWARE_UPGRADE_FAILED, cmd_data); } } static void ChildLockHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *child_lock = json_object_object_get(data, "child_lock"); if (child_lock) { JL_echo_cloud_child_lock_change_notify(json_object_get_boolean(child_lock)); json_object *cmd_data = json_object_new_object(); json_object_object_add(cmd_data, "child_lock", json_object_new_boolean(json_object_get_boolean(child_lock))); echo_cloud_msg_http_req(&p->http, CHILD_LOCK_CHANGED, cmd_data); } } static void LightChangeHandle(json_object *data, void *priv) { struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv; json_object *light = json_object_object_get(data, "light"); if (light) { JL_echo_cloud_light_change_notify(json_object_get_int(light)); json_object *cmd_data = json_object_new_object(); json_object_object_add(cmd_data, "light", json_object_new_int(json_object_get_int(light))); echo_cloud_msg_http_req(&p->http, LIGHT_CHANGED, cmd_data); } } static const Jsonc_handle jsonc_handle[] = { {"ai_dialog.play", aiDialogPlayHandle}, {"intercom.alert", intercomAlertHandle}, {"intercom.play", NULL/*intercomPlayHandle*/}, {"intercom.notify", intercomNotifyHandle}, {"media_player.start", mediaPlayerStartHandle}, {"media_player.stop", mediaPlayerStopHandle}, {"media_player.pause", mediaPlayerPauseHandle}, {"media_player.resume", mediaPlayerResumeHandle}, {"alert.trigger", alertTriggerHandle}, #if 0 {"alert.schedule", alertScheduleHandle}, {"alert.cancel", alertCancelHandle}, {"alert.terminate", alertTerminateHandle}, #endif {"volume.change", VolumeChangeHandle}, {"firmware.upgrade", FirmwareUpgradeHandle}, {"child_lock.change", ChildLockHandle}, {"light.change", LightChangeHandle}, #if 0 {"device.purge", NULL/*DevicePurgeHandle*/}, {"device.sleep", NULL/*DeviceSleepHandle*/}, {"device.output_verification_code", NULL}, {"custom.command", NULL}, {"vad.start", NULL}, {"month_age.send", NULL}, {"device.bind_succeeded", NULL}, #endif }; int echo_cloud_rcv_json_handler(void *priv, const char *rsp_data) { int ret = 0; printf("rsp : %s\n", rsp_data); json_object *recvJson = json_tokener_parse(rsp_data); if (NULL == recvJson) { return -1; } json_object *val = json_object_object_get(recvJson, "count"); if (NULL == val) { json_object_put(recvJson); return -1; } int count = json_object_get_int(val); if (0 == count) { json_object_put(recvJson); return -1; } json_object *msgJson = json_object_object_get(recvJson, "messages"); if (NULL == msgJson) { json_object_put(recvJson); return -1; } for (int i = 0; i < count; i++) { val = json_object_array_get_idx(msgJson, i); json_object *type = json_object_object_get(val, "type"); if (NULL == type) { continue; } json_object *data = json_object_object_get(val, "data"); if (NULL == data) { continue; } for (int j = 0; j < ARRAY_SIZE(jsonc_handle); j++) { if (jsonc_handle[j].func && !strcmp(json_object_get_string(type), jsonc_handle[j].rsp_msg)) { printf("handle : %s\n", jsonc_handle[j].rsp_msg); jsonc_handle[j].func(data, priv); break; } } } json_object_put(recvJson); return ret; } int echo_cloud_enc_http_start(struct echo_cloud_http_hdl *hdl, u8 voice_mode) { int error = HERROR_OK; char *http_head = NULL; memset(&hdl->rec_ctx, 0x0, sizeof(httpcli_ctx)); memset(&hdl->rec_body, 0x0, sizeof(http_body_obj)); hdl->rec_body.recv_len = 0; hdl->rec_body.buf_len = 1024; // 接受buf大小,根据需求设置 hdl->rec_body.buf_count = 1; hdl->rec_body.p = (char *) malloc(hdl->rec_body.buf_len * sizeof(char)); if (hdl->rec_body.p == NULL) { return HERROR_MEM; } hdl->rec_ctx.url = ECHO_CLOUD_STREAM_URL; // 链接地址 hdl->rec_ctx.timeout_millsec = hdl->timeout_ms; // 连接超时设置 // 设置一些http头部参数。如果设置了user_http_header参数, // httpcli_post()或者heepcli_get()内部将不在设置http头部 http_head = malloc(1024); if (http_head == NULL) { error = HERROR_MEM; goto __exit; } char nonce[33]; char signature_str[65]; avGenRand(nonce, 32); nonce[32] = 0; echo_cloud_get_hmac_sha256(hdl->auth->device_id, hdl->auth->channel_uuid, nonce, signature_str, hdl->auth->hash); signature_str[64] = 0; if (AI_MODE == voice_mode) { snprintf(http_head, 1024, echo_cloud_enc_http_tab, hdl->auth->channel_uuid, hdl->auth->device_id, nonce, signature_str, "ai_dialog.recognize.device" ); } else if (TRANSLATE_MODE == voice_mode) { int length = snprintf(http_head, 1024, echo_cloud_enc_http_tab, hdl->auth->channel_uuid, hdl->auth->device_id, nonce, signature_str, "translator.input.device" ); snprintf(http_head + length - 2, 1024 - length, "%s%s", "X-Echocloud-Payload-From: zh\r\n", "X-Echocloud-Payload-To: en\r\n\r\n" ); } else if (WECHAT_MODE == voice_mode) { snprintf(http_head, 1024, echo_cloud_enc_http_tab, hdl->auth->channel_uuid, hdl->auth->device_id, nonce, signature_str, "intercom.input.device" ); } hdl->rec_ctx.user_http_header = http_head; hdl->rec_ctx.priv = &hdl->rec_body; error = httpcli_post_header(&hdl->rec_ctx); __exit: if (error) { httpcli_close(&hdl->rec_ctx); if (hdl->rec_body.p) { free(hdl->rec_body.p); hdl->rec_body.p = NULL; } } if (http_head) { free(http_head); } return error; } int echo_cloud_enc_http_send(struct echo_cloud_http_hdl *hdl, void *data, u32 data_len) { int error = HERROR_OK; if (hdl->rec_body.p == NULL) { return HERROR_MEM; } hdl->rec_ctx.post_data = data; hdl->rec_ctx.data_len = data_len; error = httpcli_chunked_send(&hdl->rec_ctx); if (error == HERROR_OK && !data_len) { if (hdl->out_cb) { error = hdl->out_cb(hdl->priv, hdl->rec_body.p); } } return error; } int echo_cloud_enc_http_close(struct echo_cloud_http_hdl *hdl) { httpcli_close(&hdl->rec_ctx); if (hdl->rec_body.p) { free(hdl->rec_body.p); hdl->rec_body.p = NULL; } return 0; } int echo_cloud_msg_http_close(struct echo_cloud_http_hdl *hdl) { httpcli_close(&hdl->ctx); if (hdl->body.p) { free(hdl->body.p); hdl->body.p = NULL; } return 0; } int echo_cloud_msg_http_req(struct echo_cloud_http_hdl *hdl, int cmd, json_object *cmd_data) { struct json_object *root = NULL; const char *send_data = NULL; int error = HERROR_OK; char http_head[256]; u8 first_try = 1; #ifdef HTTP_KEEP_ALIVE if (hdl->body.p) { free(hdl->body.p); hdl->body.p = NULL; } #else echo_cloud_msg_http_close(hdl); #endif memset(&hdl->body, 0x0, sizeof(http_body_obj)); hdl->body.recv_len = 0; hdl->body.buf_len = 1024; // 接受buf大小,根据需求设置 hdl->body.buf_count = 1; hdl->body.p = (char *) malloc(hdl->body.buf_len * sizeof(char)); if (hdl->body.p == NULL) { error = HERROR_MEM; goto __exit; } root = echo_cloud_create_cjson(hdl, cmd, cmd_data); if (!root) { goto __exit; } send_data = json_object_to_json_string(root); snprintf(http_head, sizeof(http_head), echo_cloud_msg_http_tab, strlen(send_data)); printf("request data : %s\n", send_data); __req_again: #ifdef HTTP_KEEP_ALIVE if (hdl->ctx.sock_hdl == NULL) { hdl->body.recv_len = 0; memset(&hdl->ctx, 0x0, sizeof(httpcli_ctx)); hdl->ctx.url = ECHO_CLOUD_MSG_URL; // 链接地址 hdl->ctx.timeout_millsec = hdl->timeout_ms; // 连接超时设置 hdl->ctx.priv = &hdl->body; error = httpcli_post_keepalive_init(&hdl->ctx); if (error != HERROR_OK) { goto __exit; } } #else memset(&hdl->ctx, 0x0, sizeof(httpcli_ctx)); hdl->ctx.url = ECHO_CLOUD_MSG_URL; // 链接地址 hdl->ctx.timeout_millsec = hdl->timeout_ms; // 连接超时设置 hdl->ctx.priv = &hdl->body; #endif hdl->ctx.user_http_header = http_head; hdl->ctx.post_data = send_data; hdl->ctx.data_len = strlen(send_data); #ifdef HTTP_KEEP_ALIVE error = httpcli_post_keepalive_send(&hdl->ctx); #else error = httpcli_post(&hdl->ctx); #endif if (error == HERROR_OK) { if (hdl->out_cb) { error = hdl->out_cb(hdl->priv, hdl->body.p); } #ifdef HTTP_KEEP_ALIVE } else { httpcli_close(&hdl->ctx); if (first_try && error != HERROR_CALLBACK) { first_try = 0; goto __req_again; } #endif } __exit: if (hdl->body.p) { free(hdl->body.p); hdl->body.p = NULL; } if (cmd_data) { json_object_put(cmd_data); } if (root) { json_object_put(root); } return error; }