#include "SettingsManager.hpp" #include #include #include namespace cyd { SettingsManager::SettingsManager() = default; SettingsManager::~SettingsManager() { end(); } bool SettingsManager::begin() { if (begun_) { return true; } begun_ = prefs_.begin(kNamespace, false); if (begun_) { load(); } return begun_; } void SettingsManager::end() { if (begun_) { prefs_.end(); begun_ = false; } } void SettingsManager::resetToDefaults() { Settings defaults; settings_ = defaults; } bool SettingsManager::load() { if (!begun_) { return false; } size_t len = prefs_.getBytesLength(kSettingsKey); if (len == 0) { Serial.println("Settings: no stored settings found, using defaults"); resetToDefaults(); return true; } std::vector buffer; buffer.resize(len); size_t read = prefs_.getBytes(kSettingsKey, buffer.data(), len); if (read != len) { Serial.println("Settings: read size mismatch, resetting"); resetToDefaults(); return false; } JsonDocument doc; DeserializationError err = deserializeJson(doc, buffer.data(), len); if (err != DeserializationError::Ok) { Serial.printf("Settings: JSON parse error: %s\n", err.c_str()); resetToDefaults(); return false; } uint16_t storedVersion = doc["version"] | 0; if (storedVersion != kCurrentVersion) { Serial.printf("Settings: migrating from version %d to %d\n", storedVersion, kCurrentVersion); migrateFrom(storedVersion); return true; } settings_.wifiSsid = doc["wifiSsid"] | ""; settings_.wifiPass = doc["wifiPass"] | ""; settings_.thermoproMac = doc["thermoproMac"] | ""; settings_.apiEndpoint = doc["apiEndpoint"] | ""; settings_.apiToken = doc["apiToken"] | ""; settings_.pollIntervalSec = doc["pollIntervalSec"] | 15; String unit = doc["unit"] | "F"; settings_.unit = unit.length() > 0 ? unit.charAt(0) : 'F'; settings_.deviceId = doc["deviceId"] | "cyd-smoker"; JsonArray names = doc["probeNames"].as(); for (size_t i = 0; i < settings_.probeNames.size(); ++i) { if (i < names.size()) { settings_.probeNames[i] = names[i].as(); } else { settings_.probeNames[i] = "Probe " + String(i + 1); } } Serial.println("Settings: loaded from NVS"); return true; } bool SettingsManager::save() { if (!begun_) { return false; } JsonDocument doc; doc["version"] = kCurrentVersion; doc["wifiSsid"] = settings_.wifiSsid; doc["wifiPass"] = settings_.wifiPass; doc["thermoproMac"] = settings_.thermoproMac; doc["apiEndpoint"] = settings_.apiEndpoint; doc["apiToken"] = settings_.apiToken; doc["pollIntervalSec"] = settings_.pollIntervalSec; doc["unit"] = String(settings_.unit); doc["deviceId"] = settings_.deviceId; JsonArray names = doc["probeNames"].to(); for (const auto& name : settings_.probeNames) { names.add(name); } size_t size = measureJson(doc); std::vector buffer; buffer.resize(size); serializeJson(doc, buffer.data(), size); prefs_.putBytes(kSettingsKey, buffer.data(), size); Serial.println("Settings: saved to NVS"); return true; } String SettingsManager::getProbeName(size_t index) const { if (index < settings_.probeNames.size()) { return settings_.probeNames[index]; } return "Probe " + String(index + 1); } void SettingsManager::setProbeName(size_t index, const String& value) { if (index < settings_.probeNames.size()) { settings_.probeNames[index] = value.length() ? value : ("Probe " + String(index + 1)); } } bool SettingsManager::hasThermoproMac() const { return isValidMac(settings_.thermoproMac); } bool SettingsManager::hasWifiCredentials() const { return settings_.wifiSsid.length() > 0; } bool SettingsManager::isValidMac(const String& mac) { if (mac.length() != 17) { return false; } for (int i = 0; i < mac.length(); ++i) { char c = mac.charAt(i); if ((i + 1) % 3 == 0) { if (c != ':') { return false; } } else { if (!isxdigit(c)) { return false; } } } return true; } void SettingsManager::migrateFrom(uint16_t oldVersion) { // For now, any version mismatch simply resets to defaults. As the schema // evolves we can add per-version migrations here. (void)oldVersion; resetToDefaults(); save(); } } // namespace cyd