// Модуль сохранения/чтения настроек во флэше

String escapeJsonString(const String& input) {
  String result = input;
  result.replace("\\", "\\\\");
  result.replace("\"", "\\\"");
  result.replace("\n", "\\n");
  result.replace("\r", "\\r");
  result.replace("\t", "\\t");
  return result;
}
bool getJsonIntValue(const String& json, const char* key, int* result) {
  int startPos = json.indexOf(key);
  if (startPos == -1) return false;
  
  startPos += strlen(key);
  
  // Пропускаем пробелы и двоеточие
  while (startPos < json.length() && 
        (json.charAt(startPos) == ' ' || json.charAt(startPos) == ':' || json.charAt(startPos) == '\t')) {
    startPos++;
  }
  
  // Пропускаем открывающую кавычку, если есть
  if (startPos < json.length() && json.charAt(startPos) == '"') {
    startPos++;
  }
  
  int endPos = json.indexOf(',', startPos);
  if (endPos == -1) endPos = json.indexOf('}', startPos);
  
  // Если значение в кавычках, ищем закрывающую кавычку
  if (startPos > 0 && json.charAt(startPos-1) == '"') {
    int quotePos = json.indexOf('"', startPos);
    if (quotePos != -1 && quotePos < endPos) {
      endPos = quotePos;
    }
  }
  
  if (endPos == -1 || endPos <= startPos) return false;
  
  String valueStr = json.substring(startPos, endPos);
  // Убираем возможные кавычки
  valueStr.replace("\"", "");
  valueStr.trim();
  
  if (valueStr.length() == 0) return false;
  
  *result = valueStr.toInt();
  return true;
}
bool getJsonFloatValue(const String& json, const char* key, float* result) {
  int startPos = json.indexOf(key);
  if (startPos == -1) return false;
  
  startPos += strlen(key);
  
  // Пропускаем пробелы и двоеточие
  while (startPos < json.length() && 
        (json.charAt(startPos) == ' ' || json.charAt(startPos) == ':' || json.charAt(startPos) == '\t')) {
    startPos++;
  }
  
  // Пропускаем открывающую кавычку, если есть
  if (startPos < json.length() && json.charAt(startPos) == '"') {
    startPos++;
  }
  
  int endPos = json.indexOf(',', startPos);
  if (endPos == -1) endPos = json.indexOf('}', startPos);
  
  // Если значение в кавычках, ищем закрывающую кавычку
  if (startPos > 0 && json.charAt(startPos-1) == '"') {
    int quotePos = json.indexOf('"', startPos);
    if (quotePos != -1 && quotePos < endPos) {
      endPos = quotePos;
    }
  }
  
  if (endPos == -1 || endPos <= startPos) return false;
  
  String valueStr = json.substring(startPos, endPos);
  // Убираем возможные кавычки
  valueStr.replace("\"", "");
  valueStr.trim();
  
  if (valueStr.length() == 0) return false;
  
  *result = valueStr.toFloat();
  return true;
}
bool getJsonBoolValue(const String& json, const char* key, bool* result) {
  int intValue;
  if (getJsonIntValue(json, key, &intValue)) {
    *result = (intValue != 0);
    return true;
  }
  
  // Пробуем распарсить как строку "true"/"false"
  String stringValue;
  if (getJsonStringValue(json, key, &stringValue)) {
    stringValue.toLowerCase();
    if (stringValue == "true" || stringValue == "1") {
      *result = true;
      return true;
    } else if (stringValue == "false" || stringValue == "0") {
      *result = false;
      return true;
    }
  }
  
  return false;
}
bool getJsonStringValue(const String& json, const char* key, String* result) {
  int startPos = json.indexOf(key);
  if (startPos == -1) return false;
  
  startPos += strlen(key);
  
  // Пропускаем пробелы и двоеточие
  while (startPos < json.length() && 
        (json.charAt(startPos) == ' ' || json.charAt(startPos) == ':' || json.charAt(startPos) == '\t')) {
    startPos++;
  }
  
  if (startPos >= json.length()) return false;
  
  // Проверяем, является ли значение строкой (в кавычках)
  if (json.charAt(startPos) == '"') {
    // Значение в кавычках
    startPos++; // Пропускаем открывающую кавычку
    int endPos = json.indexOf('"', startPos);
    if (endPos == -1) return false;
    
    String value = json.substring(startPos, endPos);
    // Убираем экранирование
    value.replace("\\\"", "\"");
    value.replace("\\\\", "\\");
    value.replace("\\n", "\n");
    value.replace("\\r", "\r");
    value.replace("\\t", "\t");
    
    *result = value;
    return true;
  } else {
    // Числовое значение без кавычек
    int endPos = json.indexOf(',', startPos);
    if (endPos == -1) endPos = json.indexOf('}', startPos);
    
    if (endPos == -1 || endPos <= startPos) return false;
    
    *result = json.substring(startPos, endPos);
    result->trim();
    return true;
  }
}
void parseCSVStringToInt8(const String& input, int8_t* output, uint8_t maxValues) {
    // Инициализируем выходной массив нулями
    for (uint8_t i = 0; i < maxValues; i++) {
        output[i] = 0;
    }
    
    uint8_t index = 0;
    int startPos = 0;
    int commaPos = input.indexOf(',');
    
    while (commaPos != -1 && index < maxValues) {
        String numStr = input.substring(startPos, commaPos);
        numStr.trim();
        
        if (numStr.length() > 0) {
            output[index] = numStr.toInt(); // toInt() вернет int, который преобразуется в int8_t
        }
        
        index++;
        startPos = commaPos + 1;
        commaPos = input.indexOf(',', startPos);
    }
    
    if (index < maxValues) {
        String numStr = input.substring(startPos);
        numStr.trim();
        if (numStr.length() > 0) {
            output[index] = numStr.toInt();
        }
    }
}
void parseCSVStringToUInt16(const String& input, uint16_t* output, uint8_t maxValues) {
    for (uint8_t i = 0; i < maxValues; i++) {
        output[i] = 0;
    }
    
    uint8_t index = 0;
    int startPos = 0;
    int commaPos = input.indexOf(',');
    
    while (commaPos != -1 && index < maxValues) {
        String numStr = input.substring(startPos, commaPos);
        numStr.trim();
        
        if (numStr.length() > 0) {
            output[index] = numStr.toInt();
        }
        
        index++;
        startPos = commaPos + 1;
        commaPos = input.indexOf(',', startPos);
    }
    
    if (index < maxValues) {
        String numStr = input.substring(startPos);
        numStr.trim();
        if (numStr.length() > 0) {
            output[index] = numStr.toInt();
        }
    }
}
void SetupServerInit(void) {

  server.on("/ajax_setup", HTTP_GET, handleAjaxSetup);
  server.on("/command", HTTP_GET, []() {
    web_command();
  });
  server.on("/save", HTTP_POST, []() {
    handleSave();
  });
}
void handleAjaxSetup() {
  String json = "{";
  
  // Основные параметры
  json += "\"disp\":" + String(RegSetup.disp) + ",";
  
  // Температурные параметры
  json += "\"TpColor\":\"" + escapeJsonString(String(RegSetup.TpColor)) + "\",";
  json += "\"TcColor\":\"" + escapeJsonString(String(RegSetup.TcColor)) + "\",";
  json += "\"TwColor\":\"" + escapeJsonString(String(RegSetup.TwColor)) + "\",";
  json += "\"TbColor\":\"" + escapeJsonString(String(RegSetup.TbColor)) + "\",";
  json += "\"TtColor\":\"" + escapeJsonString(String(RegSetup.TtColor)) + "\",";
  
  json += "\"PaColor\":\"" + escapeJsonString(String(RegSetup.PaColor)) + "\",";
  json += "\"NColor\":\"" + escapeJsonString(String(RegSetup.NColor)) + "\",";
  json += "\"VColor\":\"" + escapeJsonString(String(RegSetup.VColor)) + "\",";
  json += "\"DwColor\":\"" + escapeJsonString(String(RegSetup.DwColor)) + "\",";
  json += "\"PColor\":\"" + escapeJsonString(String(RegSetup.PColor)) + "\",";
  json += "\"AbColor\":\"" + escapeJsonString(String(RegSetup.AbColor)) + "\",";
  json += "\"ApColor\":\"" + escapeJsonString(String(RegSetup.ApColor)) + "\",";
  json += "\"PbColor\":\"" + escapeJsonString(String(RegSetup.PbColor)) + "\",";
  
  // Прочие
  json += "\"TimeZone\":" + String(RegSetup.TimeZone) + ",";
  json += "\"LogPeriod\":" + String(RegSetup.LogPeriod) + ",";  
  // Настройки WiFi
  json += "\"SSID\":\"" + escapeJsonString(String(RegSetup.SavedSSID)) + "\",";
  json += "\"PASS\":\"" + escapeJsonString(String(RegSetup.SavedPASS)) + "\",";
  json += "\"UseMQTT\":" + String(RegSetup.UseMQTT ? 1 : 0) + ",";
  json += "\"dbg\":" + String(RegSetup.dbg ? 1 : 0) + ",";
  json += "\"MQTT_Port\":" + String(RegSetup.MQTT_Port) + ",";
  json += "\"udpLPort\":" + String(RegSetup.udpLPort) + ",";
  json += "\"STO\":" + String(RegSetup.Session_timeout) + ",";
  json += "\"OTA\":" + String(RegSetup.OTA ? 1 : 0);
  json += "}";
  
  server.send(200, "application/json", json);
}
void parseJsonToRegSetup(const String& json) {
  String cleanJson = json;
  // Не убираем пробелы полностью, только лишние
  cleanJson.replace("\n", "");
  cleanJson.replace("\r", "");
  cleanJson.replace("\t", "");
  
  int intValue;
  float floatValue;
  String stringValue;
  int indexValue;
  bool boolValue;
  
  if (getJsonIntValue(cleanJson, "\"MQTT_Port\"", &intValue)) RegSetup.MQTT_Port = intValue;
  uint16_t oldUDP = RegSetup.udpLPort;
  if (getJsonIntValue(cleanJson, "\"udpLPort\"", &intValue)) RegSetup.udpLPort = intValue;
  if (getJsonIntValue(cleanJson, "\"TimeZone\"", &intValue)) RegSetup.TimeZone = intValue;
  if (getJsonIntValue(cleanJson, "\"LogPeriod\"", &intValue)) RegSetup.LogPeriod = intValue;
  if (getJsonIntValue(cleanJson, "\"STO\"", &intValue)) RegSetup.Session_timeout = intValue;
  
  // Boolean параметры через getJsonBoolValue
  if (getJsonBoolValue(cleanJson, "\"OTA\"", &boolValue)) {
    if (RegSetup.OTA != boolValue && boolValue) is_reboot = true;
    RegSetup.OTA = boolValue ? 1 : 0;    
  }
    if (getJsonBoolValue(cleanJson, "\"UseMQTT\"", &boolValue)) {
    if (boolValue != RegSetup.UseMQTT) {
      RegSetup.UseMQTT = boolValue ? 1 : 0;
    }
  }
  if (getJsonBoolValue(cleanJson, "\"dbg\"", &boolValue)) RegSetup.dbg = boolValue ? 1 : 0;
    
  if (getJsonStringValue(cleanJson, "\"TpColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Т пара
    strlcpy(RegSetup.TpColor, stringValue.c_str(), sizeof(RegSetup.TpColor));
  } else RegSetup.TpColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"TcColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Т царги
    strlcpy(RegSetup.TcColor, stringValue.c_str(), sizeof(RegSetup.TcColor));
  } else RegSetup.TcColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"TwColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Т вода
    strlcpy(RegSetup.TwColor, stringValue.c_str(), sizeof(RegSetup.TwColor));
  } else RegSetup.TwColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"TbColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Т куб
    strlcpy(RegSetup.TbColor, stringValue.c_str(), sizeof(RegSetup.TbColor));
  } else RegSetup.TbColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"TtColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Т ТСА
    strlcpy(RegSetup.TtColor, stringValue.c_str(), sizeof(RegSetup.TtColor));
  } else RegSetup.TtColor[0] = '\0';


  
  if (getJsonStringValue(cleanJson, "\"PaColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Атмосферное давление
    strlcpy(RegSetup.PaColor, stringValue.c_str(), sizeof(RegSetup.PaColor));
  } else RegSetup.PaColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"NColor\"", &stringValue) && stringValue.length() > 0) {// Цвет № строки программы
    strlcpy(RegSetup.NColor, stringValue.c_str(), sizeof(RegSetup.NColor));
  } else RegSetup.NColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"VColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Скорость отбора
    strlcpy(RegSetup.VColor, stringValue.c_str(), sizeof(RegSetup.VColor));
  } else RegSetup.VColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"DwColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Мощность
    strlcpy(RegSetup.DwColor, stringValue.c_str(), sizeof(RegSetup.DwColor));
  } else RegSetup.DwColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"PColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Расход воды
    strlcpy(RegSetup.PColor, stringValue.c_str(), sizeof(RegSetup.PColor));
  } else RegSetup.PColor[0] = '\0';
  
  
  if (getJsonStringValue(cleanJson, "\"AbColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Спиртуозность куба
    strlcpy(RegSetup.AbColor, stringValue.c_str(), sizeof(RegSetup.AbColor));
  } else RegSetup.AbColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"ApColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Спиртуозность пара
    strlcpy(RegSetup.ApColor, stringValue.c_str(), sizeof(RegSetup.ApColor));
  } else RegSetup.ApColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"PbColor\"", &stringValue) && stringValue.length() > 0) {// Цвет Давление в кубе
    strlcpy(RegSetup.PbColor, stringValue.c_str(), sizeof(RegSetup.PbColor));
  } else RegSetup.PbColor[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"SSID\"", &stringValue) && stringValue.length() > 0) {
    strlcpy(RegSetup.SavedSSID, stringValue.c_str(), sizeof(RegSetup.SavedSSID));
  } else RegSetup.SavedSSID[0] = '\0';
  
  if (getJsonStringValue(cleanJson, "\"PASS\"", &stringValue) && stringValue.length() > 0) {
    strlcpy(RegSetup.SavedPASS, stringValue.c_str(), sizeof(RegSetup.SavedPASS));
  } else RegSetup.SavedPASS[0] = '\0';
    
    
  if (getJsonIntValue(cleanJson, "\"disp\":", &intValue)) {
    uint8_t new_disp = (uint8_t) intValue;
    if (RegSetup.disp!=new_disp) {
      if (new_disp!=0) is_reboot = true; // Перезагрузка при включении дисплея
      RegSetup.disp = new_disp;
    }
  }
}
void handleSave() {
  if (server.hasArg("plain")) {
    jsonstr2 = server.arg("plain");
    DbgMsg("Begin save", 1);
    
    parseJsonToRegSetup(jsonstr2);
    
    DbgMsg("Parsing end.", 1);
    server.send(200, "application/json", "Ok");
    delay(50);
    ValidateRegSetup();
    UseRegSetup();
    save_profile();
          
    DbgMsg("Save end", 1);
    
  } else {
    server.send(400, "text/plain", "No data received");
  }
  
  vTaskDelay(10 / portTICK_PERIOD_MS);
  if (is_reboot) {
    ESP.restart();
  }
}
void web_command() {
  // Защита от повторных команд
  static uint32_t last_command_time = 0;
  if (millis() - last_command_time < 1500) {
    server.send(200, "application/json", "Ok");
    return;
  }
  last_command_time = millis();
  if (server.hasArg("reboot")) {
      server.send(200, "application/json", "Ok");
      delay(500);
      ESP.restart();
    } else if (server.hasArg("resetwifi")) {
      reset_wifi();
    }
  server.send(200, "application/json", "Ok");
}

void ValidateRegSetup() {
  if (RegSetup.LogPeriod < 100) RegSetup.LogPeriod = 10;
  if (RegSetup.Session_timeout < 10) RegSetup.Session_timeout = 10;
}
void UseRegSetup() {
  
  NTP.setTimeOffset(RegSetup.TimeZone * 3600);
}
static void cleanupNVS() {
  Serial.println("Очистка NVS...");
  prefs.begin("device-config", false);
  prefs.clear();
  prefs.end();
}
// Функции для проверки и сохранения параметров
bool saveParamIfChangedUChar(const char* key, uint8_t value) {
  if (!prefs.isKey(key) || prefs.getUChar(key) != value) {
    prefs.putUChar(key, value);    
    DbgMsg("!!!Сохранен "+ String(key) + ", Value="+String(value),1);
    if (value != prefs.getUChar(key, 99))
    //DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 99)),1);
    return true;
  }
  return false;
}

bool saveParamIfChangedUShort(const char* key, uint16_t value) {
  if (!prefs.isKey(key) || prefs.getUShort(key) != value) {
    prefs.putUShort(key, value);
    DbgMsg("!!!Сохранен "+ String(key) + ", Value="+String(value),1);
    if (value != prefs.getUShort(key, 9999)) 
    //DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 9999)),1);
    return true;
  }
  return false;
}

bool saveParamIfChangedFloat(const char* key, float value) {
  if (!prefs.isKey(key) || prefs.getFloat(key) != value) {
    prefs.putFloat(key, value);
    DbgMsg("!!!Сохранен "+ String(key) + ", Value="+String(value),1);
    if (value != prefs.getFloat(key, 99.99)) 
    //DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 99.99)),1);
    return true;
  }
  return false;
}

bool saveParamIfChangedBool(const char* key, bool value) {
  if (!prefs.isKey(key) || prefs.getBool(key) != value) {
    prefs.putBool(key, value);
    DbgMsg("!!!Сохранен "+ String(key) + ", Value="+String(value),1);
    if (value != prefs.getBool(key, 0)) 
   // DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 0)),1);
    return true;
  }
  return false;
}

bool saveStringIfChanged(const char* key, const char* value) {
  String stored = prefs.getString(key, "");
  if (!prefs.isKey(key) || stored != value) {
    prefs.putString(key, value);
    DbgMsg("!!!Сохранена строка"+ String(key) + ", Value="+String(value),1);
    if (String(value) != prefs.getString(key, ""))
    //DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 0)),1);
    return true;
  }
  return false;
}

bool saveArrayIfChanged(const char* key, const void* array, size_t size) {
  bool changed = !prefs.isKey(key);
  if (!changed) {
    uint8_t stored_array[size];
    prefs.getBytes(key, stored_array, size);
    changed = (memcmp(stored_array, array, size) != 0);
  }
  if (changed) {
    prefs.putBytes(key, array, size);
    DbgMsg("!!!Сохранен "+ String(key),1);
  }
  return changed;
}

void save_profile() {
  prefs.begin("device-config", false);
  
  // Основные параметры
  saveParamIfChangedUChar("disp", RegSetup.disp);
  
  // Цвета
  saveStringIfChanged("TpColor", RegSetup.TpColor);
  saveStringIfChanged("TcColor", RegSetup.TcColor);
  saveStringIfChanged("TwColor", RegSetup.TwColor);
  saveStringIfChanged("TbColor", RegSetup.TbColor);
  saveStringIfChanged("TtColor", RegSetup.TtColor);

  saveStringIfChanged("PaColor", RegSetup.PaColor);   // Цвет Атмосферное давление
  saveStringIfChanged("NColor", RegSetup.NColor);     // Цвет № строки программы
  saveStringIfChanged("VColor", RegSetup.VColor);     // Цвет Скорость отбора
  saveStringIfChanged("DwColor", RegSetup.DwColor);   // Цвет Мощность
  saveStringIfChanged("PColor", RegSetup.PColor);     // Цвет Расход воды
  saveStringIfChanged("AbColor", RegSetup.AbColor);   // Цвет Спиртуозность куба
  saveStringIfChanged("ApColor", RegSetup.ApColor);   // Цвет Спиртуозность пара
  saveStringIfChanged("PbColor", RegSetup.PbColor);   // Цвет Давление в кубе

  // Сеть
  saveStringIfChanged("SSID", RegSetup.SavedSSID);
  saveStringIfChanged("PASS", RegSetup.SavedPASS);
  saveParamIfChangedBool("UseMQTT", RegSetup.UseMQTT);
  saveParamIfChangedUShort("udpLPort", RegSetup.udpLPort);
  // Прочие настройки
  saveParamIfChangedUChar("TimeZone", RegSetup.TimeZone);
  saveParamIfChangedUShort("LogPeriod", RegSetup.LogPeriod);
  saveParamIfChangedBool("dbg", RegSetup.dbg);
  saveParamIfChangedUShort("MQTT_Port", RegSetup.MQTT_Port);
  saveParamIfChangedUShort("STO", RegSetup.Session_timeout);
  saveParamIfChangedBool("flag", RegSetup.flag);
  saveParamIfChangedBool("OTA", RegSetup.OTA);
  prefs.end();
}

void read_config() {
    prefs.begin("device-config", true);
        RegSetup.flag = prefs.getUChar("flag", 255); // В случае несовпадения ключа чистим NVS и грузим конфиг по умолчанию
        if (RegSetup.flag != Setup_Key) {
          prefs.clear();
          }
  // читаем отдельные параметры
  RegSetup.disp = prefs.getUChar("disp", 1);
  strlcpy(RegSetup.TpColor, prefs.getString("TpColor", "#3d3dff").c_str(), sizeof(RegSetup.TpColor));
  strlcpy(RegSetup.TcColor, prefs.getString("TcColor", "#9c269c").c_str(), sizeof(RegSetup.TcColor));
  strlcpy(RegSetup.TwColor, prefs.getString("TwColor", "#00ffff").c_str(), sizeof(RegSetup.TwColor));
  strlcpy(RegSetup.TbColor, prefs.getString("TbColor", "#ff0a0a").c_str(), sizeof(RegSetup.TbColor));
  strlcpy(RegSetup.TtColor, prefs.getString("TtColor", "#f39c12").c_str(), sizeof(RegSetup.TtColor));
  strlcpy(RegSetup.PaColor, prefs.getString("PaColor", "#21618C").c_str(), sizeof(RegSetup.PaColor));   // Цвет Атмосферное давление
  strlcpy(RegSetup.NColor, prefs.getString("NColor", "#2ecc71").c_str(), sizeof(RegSetup.NColor));      // Цвет № строки программы
  strlcpy(RegSetup.VColor, prefs.getString("VColor", "#8e44ad").c_str(), sizeof(RegSetup.VColor));      // Цвет Скорость отбора
  strlcpy(RegSetup.DwColor, prefs.getString("DwColor", "#1abc9c").c_str(), sizeof(RegSetup.DwColor));   // Цвет Мощность
  strlcpy(RegSetup.PColor, prefs.getString("PColor", "#d35400").c_str(), sizeof(RegSetup.PColor));      // Цвет Расход воды
  strlcpy(RegSetup.AbColor, prefs.getString("AbColor", "#c0392b").c_str(), sizeof(RegSetup.AbColor));   // Цвет Спиртуозность куба
  strlcpy(RegSetup.ApColor, prefs.getString("ApColor", "#7f8c8d").c_str(), sizeof(RegSetup.ApColor));   // Цвет Спиртуозность пара
  strlcpy(RegSetup.PbColor, prefs.getString("PbColor", "#27ae60").c_str(), sizeof(RegSetup.PbColor));   // Цвет Давление в кубе
      
  strlcpy(RegSetup.SavedSSID, prefs.getString("SSID", "").c_str(), sizeof(RegSetup.SavedSSID));
  strlcpy(RegSetup.SavedPASS, prefs.getString("PASS", "").c_str(), sizeof(RegSetup.SavedPASS));
  RegSetup.UseMQTT = prefs.getBool("UseMQTT", false);
  RegSetup.TimeZone = prefs.getUChar("TimeZone", 3);
  RegSetup.LogPeriod = prefs.getUShort("LogPeriod", 5);
  RegSetup.dbg = prefs.getBool("dbg", false);
  RegSetup.MQTT_Port = prefs.getUShort("MQTT_Port", 1883);
  RegSetup.Session_timeout = prefs.getUShort("STO", 600);
  RegSetup.udpLPort = prefs.getUShort("udpLPort", 48899);
  RegSetup.OTA = prefs.getBool("OTA", false);
  dispRotate = prefs.getBool("dispRotate", false);

  if (RegSetup.dbg) {
    nvs_stats_t nvs_stats;
    esp_err_t err = nvs_get_stats(NULL, &nvs_stats);
    
    if (err == ESP_OK) {
      Serial.println("=== Информация о NVS ===");
      Serial.printf("Всего записей: %d\n", nvs_stats.total_entries);
      Serial.printf("Использовано записей: %d\n", nvs_stats.used_entries);
      Serial.printf("Свободных записей: %d\n", nvs_stats.free_entries);
      Serial.printf("Всего пространства: %d байт\n", nvs_stats.total_entries * 32); // примерно 32 байта на запись
      Serial.printf("Свободно: %d байт\n", nvs_stats.free_entries * 32);
      Serial.println("========================");
    } else {
      Serial.println("Ошибка получения статистики NVS");
    }
  }
  prefs.end();
  if (RegSetup.flag != Setup_Key) {
    RegSetup.flag = Setup_Key;
    save_profile();
  }
    ValidateRegSetup();
    UseRegSetup();
}