#include "device_manager.h" #include #include #include #include #include #include "board.h" #include "server_config.h" #define TAG "DeviceManager" DeviceManager& DeviceManager::GetInstance() { static DeviceManager instance; return instance; } DeviceManager::DeviceManager() : is_bound_(false) { // 初始化NVS(如果尚未初始化) esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // 获取MAC地址 uint8_t mac[6]; esp_err_t err = esp_efuse_mac_get_default(mac); if (err == ESP_OK) { char mac_str[18]; snprintf(mac_str, sizeof(mac_str), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mac_address_ = std::string(mac_str); ESP_LOGI(TAG, "Device MAC Address: %s", mac_address_.c_str()); } else { ESP_LOGE(TAG, "Failed to get MAC address"); } // 从NVS加载配置 LoadFromNVS(); // 如果没有 token,尝试从服务器自动获取(可能已在网页端绑定) if (device_token_.empty() && !mac_address_.empty()) { ESP_LOGI(TAG, "No token found, trying to fetch from server..."); TryFetchTokenFromServer(); } } DeviceManager::~DeviceManager() { } std::string DeviceManager::GetMACAddress() { return mac_address_; } void DeviceManager::LoadFromNVS() { nvs_handle_t nvs_handle; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle); if (err == ESP_OK) { // 读取Token size_t token_len = 0; err = nvs_get_str(nvs_handle, NVS_KEY_TOKEN, nullptr, &token_len); if (err == ESP_OK && token_len > 0) { char* token_buf = new char[token_len]; err = nvs_get_str(nvs_handle, NVS_KEY_TOKEN, token_buf, &token_len); if (err == ESP_OK) { device_token_ = std::string(token_buf); is_bound_ = true; ESP_LOGI(TAG, "Loaded device token from NVS (length: %d)", token_len - 1); } delete[] token_buf; } // 读取用户名 size_t username_len = 0; err = nvs_get_str(nvs_handle, NVS_KEY_USERNAME, nullptr, &username_len); if (err == ESP_OK && username_len > 0) { char* username_buf = new char[username_len]; err = nvs_get_str(nvs_handle, NVS_KEY_USERNAME, username_buf, &username_len); if (err == ESP_OK) { bound_username_ = std::string(username_buf); ESP_LOGI(TAG, "Device bound to user: %s", bound_username_.c_str()); } delete[] username_buf; } nvs_close(nvs_handle); } else if (err == ESP_ERR_NVS_NOT_FOUND) { ESP_LOGI(TAG, "No device binding found in NVS"); } else { ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err)); } } bool DeviceManager::SaveDeviceToken(const std::string& token) { nvs_handle_t nvs_handle; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to open NVS for writing: %s", esp_err_to_name(err)); return false; } err = nvs_set_str(nvs_handle, NVS_KEY_TOKEN, token.c_str()); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to write token to NVS: %s", esp_err_to_name(err)); nvs_close(nvs_handle); return false; } err = nvs_commit(nvs_handle); nvs_close(nvs_handle); if (err == ESP_OK) { device_token_ = token; is_bound_ = true; ESP_LOGI(TAG, "Device token saved to NVS"); return true; } else { ESP_LOGE(TAG, "Failed to commit NVS: %s", esp_err_to_name(err)); return false; } } std::string DeviceManager::GetDeviceToken() { return device_token_; } bool DeviceManager::ClearDeviceToken() { nvs_handle_t nvs_handle; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to open NVS for erasing: %s", esp_err_to_name(err)); return false; } nvs_erase_key(nvs_handle, NVS_KEY_TOKEN); nvs_erase_key(nvs_handle, NVS_KEY_USERNAME); err = nvs_commit(nvs_handle); nvs_close(nvs_handle); if (err == ESP_OK) { device_token_.clear(); bound_username_.clear(); is_bound_ = false; ESP_LOGI(TAG, "Device token cleared"); return true; } else { ESP_LOGE(TAG, "Failed to clear device token: %s", esp_err_to_name(err)); return false; } } bool DeviceManager::BindDevice(const std::string& binding_code, const std::string& device_name) { ESP_LOGI(TAG, "Starting device binding with code: %s", binding_code.c_str()); if (mac_address_.empty()) { ESP_LOGE(TAG, "MAC address not available"); return false; } // 构建请求JSON cJSON* request = cJSON_CreateObject(); cJSON_AddStringToObject(request, "mac", mac_address_.c_str()); cJSON_AddStringToObject(request, "binding_code", binding_code.c_str()); if (!device_name.empty()) { cJSON_AddStringToObject(request, "device_name", device_name.c_str()); } else { cJSON_AddStringToObject(request, "device_name", "ESP32音乐播放器"); } char* request_str = cJSON_PrintUnformatted(request); std::string request_body(request_str); cJSON_Delete(request); cJSON_free(request_str); // 发送HTTP请求到服务器 auto& board = Board::GetInstance(); auto network = board.GetNetwork(); auto http = network->CreateHttp(0); std::string bind_url = DEVICE_BIND_API_URL; http->SetHeader("Content-Type", "application/json"); http->SetHeader("User-Agent", "ESP32-Music-Player/1.0"); ESP_LOGI(TAG, "Sending bind request to: %s", bind_url.c_str()); ESP_LOGD(TAG, "Request body: %s", request_body.c_str()); if (!http->Open("POST", bind_url)) { ESP_LOGE(TAG, "Failed to connect to bind API"); return false; } // 发送请求体 http->Write(request_body.c_str(), request_body.length()); int status_code = http->GetStatusCode(); ESP_LOGI(TAG, "Bind request status code: %d", status_code); if (status_code != 200) { ESP_LOGE(TAG, "Bind request failed with status: %d", status_code); http->Close(); return false; } // 读取响应 std::string response = http->ReadAll(); http->Close(); ESP_LOGD(TAG, "Bind response: %s", response.c_str()); // 解析响应JSON cJSON* response_json = cJSON_Parse(response.c_str()); if (!response_json) { ESP_LOGE(TAG, "Failed to parse bind response"); return false; } cJSON* success = cJSON_GetObjectItem(response_json, "success"); if (!success || !cJSON_IsBool(success) || !cJSON_IsTrue(success)) { ESP_LOGE(TAG, "Bind request was not successful"); cJSON_Delete(response_json); return false; } cJSON* token = cJSON_GetObjectItem(response_json, "token"); cJSON* username = cJSON_GetObjectItem(response_json, "username"); if (!token || !cJSON_IsString(token)) { ESP_LOGE(TAG, "No token in bind response"); cJSON_Delete(response_json); return false; } std::string token_str = token->valuestring; std::string username_str = username && cJSON_IsString(username) ? username->valuestring : ""; cJSON_Delete(response_json); // 保存Token到NVS if (!SaveDeviceToken(token_str)) { ESP_LOGE(TAG, "Failed to save token"); return false; } // 保存用户名到NVS if (!username_str.empty()) { nvs_handle_t nvs_handle; if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle) == ESP_OK) { nvs_set_str(nvs_handle, NVS_KEY_USERNAME, username_str.c_str()); nvs_commit(nvs_handle); nvs_close(nvs_handle); bound_username_ = username_str; } } ESP_LOGI(TAG, "Device successfully bound to user: %s", username_str.c_str()); return true; } bool DeviceManager::VerifyDevice() { if (device_token_.empty()) { ESP_LOGW(TAG, "No device token available for verification"); return false; } auto& board = Board::GetInstance(); auto network = board.GetNetwork(); auto http = network->CreateHttp(0); std::string verify_url = DEVICE_VERIFY_API_URL; http->SetHeader("X-Device-Token", device_token_.c_str()); http->SetHeader("User-Agent", "ESP32-Music-Player/1.0"); if (!http->Open("GET", verify_url)) { ESP_LOGE(TAG, "Failed to connect to verify API"); return false; } int status_code = http->GetStatusCode(); http->Close(); if (status_code == 200) { ESP_LOGI(TAG, "Device verification successful"); return true; } else { ESP_LOGW(TAG, "Device verification failed with status: %d", status_code); return false; } } std::string DeviceManager::GetFavorites() { if (device_token_.empty()) { return ""; } auto& board = Board::GetInstance(); auto network = board.GetNetwork(); auto http = network->CreateHttp(0); std::string url = FAVORITE_LIST_API_URL; http->SetHeader("X-Device-Token", device_token_.c_str()); http->SetHeader("User-Agent", "ESP32-Music-Player/1.0"); if (!http->Open("GET", url)) { ESP_LOGE(TAG, "Failed to connect to favorites API"); return ""; } int status_code = http->GetStatusCode(); std::string response = http->ReadAll(); http->Close(); if (status_code == 200) { return response; } else { ESP_LOGW(TAG, "GetFavorites failed with status: %d", status_code); return ""; } } std::string DeviceManager::GetUserPlaylists() { if (device_token_.empty()) { return ""; } auto& board = Board::GetInstance(); auto network = board.GetNetwork(); auto http = network->CreateHttp(0); std::string url = PLAYLIST_LIST_API_URL; http->SetHeader("X-Device-Token", device_token_.c_str()); http->SetHeader("User-Agent", "ESP32-Music-Player/1.0"); if (!http->Open("GET", url)) { ESP_LOGE(TAG, "Failed to connect to playlist API"); return ""; } int status_code = http->GetStatusCode(); std::string response = http->ReadAll(); http->Close(); if (status_code == 200) { return response; } else { ESP_LOGW(TAG, "GetUserPlaylists failed with status: %d", status_code); return ""; } } bool DeviceManager::IsDeviceBound() { return is_bound_ && !device_token_.empty(); } std::string DeviceManager::GetBoundUsername() { return bound_username_; } bool DeviceManager::TryFetchTokenFromServer() { ESP_LOGI(TAG, "Attempting to fetch token from server using MAC: %s", mac_address_.c_str()); auto& board = Board::GetInstance(); auto network = board.GetNetwork(); auto http = network->CreateHttp(0); // 构建请求 JSON cJSON* request = cJSON_CreateObject(); cJSON_AddStringToObject(request, "mac", mac_address_.c_str()); char* request_str = cJSON_PrintUnformatted(request); std::string request_body(request_str); cJSON_Delete(request); cJSON_free(request_str); std::string sync_url = MUSIC_SERVER_URL; sync_url += "/api/esp32/sync"; http->SetHeader("Content-Type", "application/json"); http->SetHeader("User-Agent", "ESP32-Music-Player/1.0"); if (!http->Open("POST", sync_url)) { ESP_LOGW(TAG, "Failed to connect to sync API"); return false; } http->Write(request_body.c_str(), request_body.length()); int status_code = http->GetStatusCode(); std::string response = http->ReadAll(); http->Close(); if (status_code == 404) { ESP_LOGI(TAG, "Device not bound on server yet"); return false; } if (status_code != 200) { ESP_LOGW(TAG, "Token sync failed with status: %d", status_code); return false; } // 解析响应 cJSON* response_json = cJSON_Parse(response.c_str()); if (!response_json) { ESP_LOGE(TAG, "Failed to parse sync response"); return false; } cJSON* token = cJSON_GetObjectItem(response_json, "token"); cJSON* username = cJSON_GetObjectItem(response_json, "username"); if (!token || !cJSON_IsString(token)) { ESP_LOGE(TAG, "Invalid sync response: missing token"); cJSON_Delete(response_json); return false; } std::string token_str = token->valuestring; std::string username_str = username && cJSON_IsString(username) ? username->valuestring : ""; cJSON_Delete(response_json); // 保存 Token 到 NVS if (!SaveDeviceToken(token_str)) { ESP_LOGE(TAG, "Failed to save synced token"); return false; } // 保存用户名到 NVS if (!username_str.empty()) { nvs_handle_t nvs_handle; if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle) == ESP_OK) { nvs_set_str(nvs_handle, NVS_KEY_USERNAME, username_str.c_str()); nvs_commit(nvs_handle); nvs_close(nvs_handle); bound_username_ = username_str; is_bound_ = true; } } ESP_LOGI(TAG, "✅ Token synced successfully for user: %s", username_str.c_str()); return true; }