Complete Rebuild, with Subsystems for each component. RTOS Tasks. (help by Claude)
This commit is contained in:
603
vesper/src/OTAManager/OTAManager.cpp
Normal file
603
vesper/src/OTAManager/OTAManager.cpp
Normal file
@@ -0,0 +1,603 @@
|
||||
#include "OTAManager.hpp"
|
||||
#include "../ConfigManager/ConfigManager.hpp"
|
||||
#include "../Logging/Logging.hpp"
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
|
||||
OTAManager::OTAManager(ConfigManager& configManager)
|
||||
: _configManager(configManager)
|
||||
, _fileManager(nullptr)
|
||||
, _status(Status::IDLE)
|
||||
, _lastError(ErrorCode::NONE)
|
||||
, _availableVersion(0.0f)
|
||||
, _updateAvailable(false)
|
||||
, _availableChecksum("")
|
||||
, _updateChannel("stable")
|
||||
, _isMandatory(false)
|
||||
, _isEmergency(false)
|
||||
, _progressCallback(nullptr)
|
||||
, _statusCallback(nullptr) {
|
||||
}
|
||||
|
||||
void OTAManager::begin() {
|
||||
LOG_INFO("OTA Manager initialized");
|
||||
setStatus(Status::IDLE);
|
||||
}
|
||||
|
||||
void OTAManager::setFileManager(FileManager* fm) {
|
||||
_fileManager = fm;
|
||||
}
|
||||
|
||||
void OTAManager::checkForUpdates() {
|
||||
// Boot-time check: only check stable channel for emergency/mandatory updates
|
||||
checkForUpdates("stable");
|
||||
}
|
||||
|
||||
void OTAManager::checkForUpdates(const String& channel) {
|
||||
if (_status != Status::IDLE) {
|
||||
LOG_WARNING("OTA check already in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(Status::CHECKING_VERSION);
|
||||
LOG_INFO("Checking for firmware updates in %s channel for %s...",
|
||||
channel.c_str(), _configManager.getHardwareVariant().c_str());
|
||||
|
||||
if (checkVersion(channel)) {
|
||||
float currentVersion = getCurrentVersion();
|
||||
LOG_INFO("Current version: %.1f, Available version: %.1f (Channel: %s)",
|
||||
currentVersion, _availableVersion, channel.c_str());
|
||||
|
||||
if (_availableVersion > currentVersion) {
|
||||
_updateAvailable = true;
|
||||
LOG_INFO("New version available! Mandatory: %s, Emergency: %s",
|
||||
_isMandatory ? "YES" : "NO", _isEmergency ? "YES" : "NO");
|
||||
setStatus(Status::IDLE);
|
||||
|
||||
// Auto-update for emergency or mandatory updates during boot check
|
||||
if (channel == "stable" && (_isEmergency || _isMandatory)) {
|
||||
LOG_INFO("Emergency/Mandatory update detected - starting automatic update");
|
||||
update(channel);
|
||||
}
|
||||
} else {
|
||||
_updateAvailable = false;
|
||||
LOG_INFO("No new version available");
|
||||
setStatus(Status::IDLE);
|
||||
}
|
||||
} else {
|
||||
_updateAvailable = false;
|
||||
setStatus(Status::FAILED, _lastError);
|
||||
}
|
||||
}
|
||||
|
||||
void OTAManager::update() {
|
||||
update("stable"); // Default to stable channel
|
||||
}
|
||||
|
||||
void OTAManager::update(const String& channel) {
|
||||
if (_status != Status::IDLE) {
|
||||
LOG_WARNING("OTA update already in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_updateAvailable) {
|
||||
LOG_WARNING("No update available for channel: %s", channel.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Starting OTA update from %s channel...", channel.c_str());
|
||||
setStatus(Status::DOWNLOADING);
|
||||
|
||||
if (downloadAndInstall(channel)) {
|
||||
setStatus(Status::SUCCESS);
|
||||
LOG_INFO("Update successfully finished. Rebooting...");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
} else {
|
||||
setStatus(Status::FAILED, _lastError);
|
||||
}
|
||||
}
|
||||
|
||||
float OTAManager::getCurrentVersion() const {
|
||||
String fwVersionStr = _configManager.getFwVersion();
|
||||
return fwVersionStr.toFloat();
|
||||
}
|
||||
|
||||
void OTAManager::setStatus(Status status, ErrorCode error) {
|
||||
_status = status;
|
||||
_lastError = error;
|
||||
|
||||
if (_statusCallback) {
|
||||
_statusCallback(status, error);
|
||||
}
|
||||
}
|
||||
|
||||
void OTAManager::notifyProgress(size_t current, size_t total) {
|
||||
if (_progressCallback) {
|
||||
_progressCallback(current, total);
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced version checking with channel support and multiple servers
|
||||
bool OTAManager::checkVersion(const String& channel) {
|
||||
std::vector<String> servers = _configManager.getUpdateServers();
|
||||
auto& updateConfig = _configManager.getUpdateConfig();
|
||||
|
||||
for (size_t serverIndex = 0; serverIndex < servers.size(); serverIndex++) {
|
||||
String baseUrl = servers[serverIndex];
|
||||
String metadataUrl = baseUrl + "/ota/" + _configManager.getHardwareVariant() + "/" + channel + "/metadata.json";
|
||||
|
||||
LOG_INFO("OTA: Trying server %d/%d: %s", serverIndex + 1, servers.size(), baseUrl.c_str());
|
||||
|
||||
HTTPClient http;
|
||||
http.setTimeout(updateConfig.timeout);
|
||||
http.begin(metadataUrl.c_str());
|
||||
|
||||
int retryCount = 0;
|
||||
int httpCode = -1;
|
||||
|
||||
// Retry logic for current server
|
||||
while (retryCount < updateConfig.retries && httpCode != HTTP_CODE_OK) {
|
||||
if (retryCount > 0) {
|
||||
LOG_INFO("OTA: Retry %d/%d for %s", retryCount + 1, updateConfig.retries, baseUrl.c_str());
|
||||
delay(1000 * retryCount); // Exponential backoff
|
||||
}
|
||||
|
||||
httpCode = http.GET();
|
||||
retryCount++;
|
||||
}
|
||||
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
String jsonStr = http.getString();
|
||||
http.end();
|
||||
|
||||
// Parse JSON metadata
|
||||
DynamicJsonDocument doc(1024);
|
||||
DeserializationError error = deserializeJson(doc, jsonStr);
|
||||
|
||||
if (error) {
|
||||
LOG_ERROR("OTA: Failed to parse metadata JSON from %s: %s",
|
||||
baseUrl.c_str(), error.c_str());
|
||||
continue; // Try next server
|
||||
}
|
||||
|
||||
// Extract metadata
|
||||
_availableVersion = doc["version"].as<float>();
|
||||
_availableChecksum = doc["checksum"].as<String>();
|
||||
_updateChannel = doc["channel"].as<String>();
|
||||
_isMandatory = doc["mandatory"].as<bool>();
|
||||
_isEmergency = doc["emergency"].as<bool>();
|
||||
|
||||
// Validate hardware variant matches
|
||||
String hwVariant = doc["hardwareVariant"].as<String>();
|
||||
String ourHardwareVariant = _configManager.getHardwareVariant();
|
||||
if (!hwVariant.isEmpty() && hwVariant != ourHardwareVariant) {
|
||||
LOG_ERROR("OTA: Hardware variant mismatch! Expected: %s, Got: %s",
|
||||
ourHardwareVariant.c_str(), hwVariant.c_str());
|
||||
continue; // Try next server
|
||||
}
|
||||
|
||||
if (_availableVersion == 0.0f) {
|
||||
LOG_ERROR("OTA: Invalid version in metadata from %s", baseUrl.c_str());
|
||||
continue; // Try next server
|
||||
}
|
||||
|
||||
if (_availableChecksum.length() != 64) { // SHA256 is 64 hex characters
|
||||
LOG_ERROR("OTA: Invalid checksum in metadata from %s", baseUrl.c_str());
|
||||
continue; // Try next server
|
||||
}
|
||||
|
||||
LOG_INFO("OTA: Successfully got metadata from %s", baseUrl.c_str());
|
||||
return true; // Success!
|
||||
} else {
|
||||
LOG_ERROR("OTA: Server %s failed after %d retries. HTTP error: %d",
|
||||
baseUrl.c_str(), updateConfig.retries, httpCode);
|
||||
http.end();
|
||||
}
|
||||
}
|
||||
|
||||
// All servers failed
|
||||
LOG_ERROR("OTA: All %d servers failed to provide metadata", servers.size());
|
||||
_lastError = ErrorCode::HTTP_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enhanced download and install with channel support and multiple servers
|
||||
bool OTAManager::downloadAndInstall(const String& channel) {
|
||||
std::vector<String> servers = _configManager.getUpdateServers();
|
||||
|
||||
for (size_t serverIndex = 0; serverIndex < servers.size(); serverIndex++) {
|
||||
String baseUrl = servers[serverIndex];
|
||||
String firmwareUrl = baseUrl + "/ota/" + _configManager.getHardwareVariant() + "/" + channel + "/firmware.bin";
|
||||
|
||||
LOG_INFO("OTA: Trying firmware download from server %d/%d: %s",
|
||||
serverIndex + 1, servers.size(), baseUrl.c_str());
|
||||
|
||||
if (downloadToSD(firmwareUrl, _availableChecksum)) {
|
||||
// Success! Now install from SD
|
||||
return installFromSD("/firmware/staged_update.bin");
|
||||
} else {
|
||||
LOG_WARNING("OTA: Firmware download failed from %s, trying next server", baseUrl.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// All servers failed
|
||||
LOG_ERROR("OTA: All %d servers failed to provide firmware", servers.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OTAManager::downloadToSD(const String& url, const String& expectedChecksum) {
|
||||
// This method now receives the exact firmware URL from downloadAndInstall
|
||||
// The server selection logic is handled there
|
||||
if (!_fileManager) {
|
||||
LOG_ERROR("FileManager not set!");
|
||||
setStatus(Status::FAILED, ErrorCode::DOWNLOAD_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure firmware directory exists
|
||||
_fileManager->createDirectory("/firmware");
|
||||
|
||||
// Download to temporary file
|
||||
String tempPath = "/firmware/staged_update.bin";
|
||||
|
||||
HTTPClient http;
|
||||
http.begin(url.c_str());
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode != HTTP_CODE_OK) {
|
||||
LOG_ERROR("Download HTTP error code: %d", httpCode);
|
||||
setStatus(Status::FAILED, ErrorCode::HTTP_ERROR);
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
int contentLength = http.getSize();
|
||||
if (contentLength <= 0) {
|
||||
LOG_ERROR("Invalid content length");
|
||||
setStatus(Status::FAILED, ErrorCode::DOWNLOAD_FAILED);
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open file for writing
|
||||
File file = SD.open(tempPath.c_str(), FILE_WRITE);
|
||||
if (!file) {
|
||||
LOG_ERROR("Failed to create temporary update file");
|
||||
setStatus(Status::FAILED, ErrorCode::DOWNLOAD_FAILED);
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
WiFiClient* stream = http.getStreamPtr();
|
||||
uint8_t buffer[1024];
|
||||
size_t written = 0;
|
||||
|
||||
while (http.connected() && written < contentLength) {
|
||||
size_t available = stream->available();
|
||||
if (available) {
|
||||
size_t toRead = min(available, sizeof(buffer));
|
||||
size_t bytesRead = stream->readBytes(buffer, toRead);
|
||||
|
||||
if (bytesRead > 0) {
|
||||
size_t bytesWritten = file.write(buffer, bytesRead);
|
||||
if (bytesWritten != bytesRead) {
|
||||
LOG_ERROR("SD write failed");
|
||||
file.close();
|
||||
http.end();
|
||||
setStatus(Status::FAILED, ErrorCode::WRITE_FAILED);
|
||||
return false;
|
||||
}
|
||||
written += bytesWritten;
|
||||
notifyProgress(written, contentLength);
|
||||
}
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
file.close();
|
||||
http.end();
|
||||
|
||||
if (written != contentLength) {
|
||||
LOG_ERROR("Download incomplete: %d/%d bytes", written, contentLength);
|
||||
setStatus(Status::FAILED, ErrorCode::DOWNLOAD_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Download complete (%d bytes)", written);
|
||||
|
||||
// Verify checksum
|
||||
if (!verifyChecksum(tempPath, expectedChecksum)) {
|
||||
LOG_ERROR("Checksum verification failed after download");
|
||||
_fileManager->deleteFile(tempPath);
|
||||
setStatus(Status::FAILED, ErrorCode::CHECKSUM_MISMATCH);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Download and checksum verification successful");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OTAManager::verifyChecksum(const String& filePath, const String& expectedChecksum) {
|
||||
String calculatedChecksum = calculateSHA256(filePath);
|
||||
|
||||
if (calculatedChecksum.isEmpty()) {
|
||||
LOG_ERROR("Failed to calculate checksum");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = calculatedChecksum.equalsIgnoreCase(expectedChecksum);
|
||||
|
||||
if (match) {
|
||||
LOG_INFO("Checksum verification passed");
|
||||
} else {
|
||||
LOG_ERROR("Checksum mismatch!");
|
||||
LOG_ERROR("Expected: %s", expectedChecksum.c_str());
|
||||
LOG_ERROR("Calculated: %s", calculatedChecksum.c_str());
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
String OTAManager::calculateSHA256(const String& filePath) {
|
||||
File file = SD.open(filePath.c_str());
|
||||
if (!file) {
|
||||
LOG_ERROR("Failed to open file for checksum calculation: %s", filePath.c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
|
||||
mbedtls_md_starts(&ctx);
|
||||
|
||||
uint8_t buffer[1024];
|
||||
size_t bytesRead;
|
||||
|
||||
while ((bytesRead = file.readBytes((char*)buffer, sizeof(buffer))) > 0) {
|
||||
mbedtls_md_update(&ctx, buffer, bytesRead);
|
||||
}
|
||||
|
||||
uint8_t hash[32];
|
||||
mbedtls_md_finish(&ctx, hash);
|
||||
mbedtls_md_free(&ctx);
|
||||
|
||||
file.close();
|
||||
|
||||
// Convert to hex string
|
||||
String hashString = "";
|
||||
for (int i = 0; i < 32; i++) {
|
||||
String hex = String(hash[i], HEX);
|
||||
if (hex.length() == 1) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
hashString += hex;
|
||||
}
|
||||
|
||||
return hashString;
|
||||
}
|
||||
|
||||
bool OTAManager::installFromSD(const String& filePath) {
|
||||
size_t updateSize = _fileManager->getFileSize(filePath);
|
||||
if (updateSize == 0) {
|
||||
LOG_ERROR("Empty update file");
|
||||
setStatus(Status::FAILED, ErrorCode::DOWNLOAD_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Installing firmware from SD (%u bytes)...", updateSize);
|
||||
setStatus(Status::INSTALLING);
|
||||
|
||||
if (!Update.begin(updateSize)) {
|
||||
LOG_ERROR("Not enough space to begin update");
|
||||
setStatus(Status::FAILED, ErrorCode::INSUFFICIENT_SPACE);
|
||||
return false;
|
||||
}
|
||||
|
||||
File updateBin = SD.open(filePath.c_str());
|
||||
if (!updateBin) {
|
||||
LOG_ERROR("Failed to open update file: %s", filePath.c_str());
|
||||
setStatus(Status::FAILED, ErrorCode::DOWNLOAD_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t written = Update.writeStream(updateBin);
|
||||
updateBin.close();
|
||||
|
||||
if (written == updateSize) {
|
||||
LOG_INFO("Update written successfully (%u bytes)", written);
|
||||
} else {
|
||||
LOG_ERROR("Written only %u/%u bytes", written, updateSize);
|
||||
setStatus(Status::FAILED, ErrorCode::WRITE_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Update.end(true)) { // true = set new boot partition
|
||||
LOG_INFO("Update finished!");
|
||||
if (Update.isFinished()) {
|
||||
setStatus(Status::SUCCESS);
|
||||
LOG_INFO("Update complete. Cleaning up and rebooting...");
|
||||
|
||||
// Clean up the update files
|
||||
_fileManager->deleteFile(filePath);
|
||||
_fileManager->deleteFile("/firmware/staged_update.sha256");
|
||||
_fileManager->deleteFile("/firmware/update.sha256");
|
||||
|
||||
// Clear firmware validation state to force validation of new firmware
|
||||
nvs_handle_t nvsHandle;
|
||||
esp_err_t err = nvs_open("fw_validator", NVS_READWRITE, &nvsHandle);
|
||||
if (err == ESP_OK) {
|
||||
nvs_erase_key(nvsHandle, "val_state");
|
||||
nvs_erase_key(nvsHandle, "retry_count");
|
||||
nvs_erase_key(nvsHandle, "fail_count");
|
||||
nvs_commit(nvsHandle);
|
||||
nvs_close(nvsHandle);
|
||||
LOG_INFO("✅ OTA: Firmware validation state cleared - new firmware will be validated");
|
||||
} else {
|
||||
LOG_WARNING("⚠️ OTA: Failed to clear validation state: %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
_configManager.setFwVersion(String(_availableVersion, 1)); // 1 decimal place
|
||||
_configManager.saveDeviceConfig();
|
||||
delay(500);
|
||||
ESP.restart();
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERROR("Update not complete");
|
||||
setStatus(Status::FAILED, ErrorCode::VERIFICATION_FAILED);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Update error: %s", Update.errorString());
|
||||
setStatus(Status::FAILED, ErrorCode::VERIFICATION_FAILED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void OTAManager::checkFirmwareUpdateFromSD() {
|
||||
if (!_fileManager) {
|
||||
LOG_ERROR("FileManager not set!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_fileManager->fileExists("/firmware/update.bin")) {
|
||||
LOG_DEBUG("No update.bin found on SD card");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for checksum file
|
||||
String checksumFile = "/firmware/update.sha256";
|
||||
if (!_fileManager->fileExists(checksumFile)) {
|
||||
LOG_WARNING("No checksum file found, proceeding without verification");
|
||||
installFromSD("/firmware/update.bin");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read expected checksum
|
||||
File checksumFileHandle = SD.open(checksumFile.c_str());
|
||||
if (!checksumFileHandle) {
|
||||
LOG_ERROR("Failed to open checksum file");
|
||||
return;
|
||||
}
|
||||
|
||||
String expectedChecksum = checksumFileHandle.readString();
|
||||
checksumFileHandle.close();
|
||||
expectedChecksum.trim();
|
||||
|
||||
// Verify checksum
|
||||
if (!verifyChecksum("/firmware/update.bin", expectedChecksum)) {
|
||||
LOG_ERROR("Checksum verification failed, aborting update");
|
||||
setStatus(Status::FAILED, ErrorCode::CHECKSUM_MISMATCH);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Checksum verified, proceeding with update");
|
||||
installFromSD("/firmware/update.bin");
|
||||
}
|
||||
|
||||
bool OTAManager::performManualUpdate() {
|
||||
return performManualUpdate("stable");
|
||||
}
|
||||
|
||||
bool OTAManager::performManualUpdate(const String& channel) {
|
||||
if (_status != Status::IDLE) {
|
||||
LOG_WARNING("OTA update already in progress");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for updates in the specified channel first
|
||||
checkForUpdates(channel);
|
||||
|
||||
if (!_updateAvailable) {
|
||||
LOG_WARNING("No update available in %s channel", channel.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Starting manual OTA update from %s channel via SD staging...", channel.c_str());
|
||||
setStatus(Status::DOWNLOADING);
|
||||
|
||||
String firmwareUrl = buildFirmwareUrl(channel);
|
||||
|
||||
// Download to SD first
|
||||
if (!downloadToSD(firmwareUrl, _availableChecksum)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Install from SD
|
||||
return installFromSD("/firmware/staged_update.bin");
|
||||
}
|
||||
|
||||
// Hardware variant management
|
||||
String OTAManager::getHardwareVariant() const {
|
||||
return _configManager.getHardwareVariant();
|
||||
}
|
||||
|
||||
void OTAManager::setHardwareVariant(const String& variant) {
|
||||
LOG_WARNING("OTAManager::setHardwareVariant is deprecated. Use ConfigManager::setHardwareVariant instead");
|
||||
// For backward compatibility, we could call configManager, but it's better to use ConfigManager directly
|
||||
}
|
||||
|
||||
// URL builders for multi-channel architecture
|
||||
String OTAManager::buildChannelUrl(const String& channel) const {
|
||||
auto& updateConfig = _configManager.getUpdateConfig();
|
||||
String baseUrl = updateConfig.fallbackServerUrl;
|
||||
|
||||
return baseUrl + "/" + _configManager.getHardwareVariant() + "/" + channel + "/";
|
||||
}
|
||||
|
||||
String OTAManager::buildMetadataUrl(const String& channel) const {
|
||||
return buildChannelUrl(channel) + "metadata.json";
|
||||
}
|
||||
|
||||
String OTAManager::buildFirmwareUrl(const String& channel) const {
|
||||
return buildChannelUrl(channel) + "firmware.bin";
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// HEALTH CHECK IMPLEMENTATION
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
bool OTAManager::isHealthy() const {
|
||||
// Check if FileManager dependency is set
|
||||
if (!_fileManager) {
|
||||
LOG_DEBUG("OTAManager: Unhealthy - FileManager not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we're not in a failed state
|
||||
if (_status == Status::FAILED) {
|
||||
LOG_DEBUG("OTAManager: Unhealthy - In failed state");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if ConfigManager has valid configuration
|
||||
String hwVariant = _configManager.getHardwareVariant();
|
||||
if (hwVariant.isEmpty() || hwVariant == "BellSystems") {
|
||||
LOG_DEBUG("OTAManager: Unhealthy - Invalid hardware variant: %s", hwVariant.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
String fwVersion = _configManager.getFwVersion();
|
||||
if (fwVersion.isEmpty() || fwVersion == "0") {
|
||||
LOG_DEBUG("OTAManager: Unhealthy - Invalid firmware version: %s", fwVersion.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if update servers are available
|
||||
std::vector<String> servers = _configManager.getUpdateServers();
|
||||
if (servers.empty()) {
|
||||
LOG_DEBUG("OTAManager: Unhealthy - No update servers configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if FileManager is healthy (can access SD card)
|
||||
if (!_fileManager->isHealthy()) {
|
||||
LOG_DEBUG("OTAManager: Unhealthy - FileManager is unhealthy");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user