Browse Source

Mise à jour notif dans config

Amélioration de la barre d'info sur la page index
Mise en place d'un flag de redémarrage si modif wifi
Modification du mode JOUR avec intégration d'un flag si température de consigne atteinte
master
scayac 2 months ago
parent
commit
afccd66ace
  1. 84
      data/index.html
  2. 85
      data/settings.html
  3. 153
      src/main.cpp

84
data/index.html

@ -74,20 +74,16 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
.error { .status-bar {
background: #fee; background: #f0f0f0;
color: #c33; padding: 10px 15px;
padding: 10px;
border-radius: 5px; border-radius: 5px;
margin-bottom: 20px; margin-top: 20px;
display: none; display: flex;
text-align: center; justify-content: space-between;
} align-items: center;
font-size: 12px;
.loading { color: #555;
text-align: center;
color: #667eea;
font-style: italic;
} }
.data-grid { .data-grid {
@ -187,17 +183,7 @@
font-size: 14px; font-size: 14px;
opacity: 0.9; opacity: 0.9;
} }
.status-bar {
background: #f0f0f0;
padding: 10px 15px;
border-radius: 5px;
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #555;
}
.status-indicator { .status-indicator {
display: flex; display: flex;
align-items: center; align-items: center;
@ -210,6 +196,12 @@
background: #27ae60; background: #27ae60;
animation: pulse 2s infinite; animation: pulse 2s infinite;
} }
.status-dot.red {
background: #e74c3c;
}
.status-dot.orange {
background: #f39c12;
}
@keyframes pulse { @keyframes pulse {
0%, 100% { opacity: 1; } 0%, 100% { opacity: 1; }
50% { opacity: 0.5; } 50% { opacity: 0.5; }
@ -251,13 +243,10 @@
<h1> Contrôleur Solaire pour Chauffe-Eau</h1> <h1> Contrôleur Solaire pour Chauffe-Eau</h1>
<div id="error" class="error"></div> <div id="statusBar" class="status-bar">
<div id="loading" class="loading">Chargement des données...</div>
<div class="status-bar">
<div class="status-indicator"> <div class="status-indicator">
<div class="status-dot"></div> <div class="status-dot" id="statusDot"></div>
<span>Connecté</span> <span id="statusText">Chargement...</span>
</div> </div>
<div style="display: flex; gap: 18px; align-items: center;"> <div style="display: flex; gap: 18px; align-items: center;">
<span id="sunriseStatus">🌅 --:--</span> <span id="sunriseStatus">🌅 --:--</span>
@ -290,7 +279,7 @@
<div class="data-value" id="heaterValue">--</div> <div class="data-value" id="heaterValue">--</div>
<div class="data-unit">%</div> <div class="data-unit">%</div>
<div class="progress-bar"> <div class="progress-bar">
<div class="progress-fill" id="heaterProgress" style="width: 0%">0%</div> <div class="progress-fill" id="heaterProgress" style="width: 0%"></div>
</div> </div>
</div> </div>
@ -323,13 +312,29 @@
let currentMode = 0; // Mode par défaut (OFF) let currentMode = 0; // Mode par défaut (OFF)
function showError(message) { function showError(message) {
document.getElementById('error').textContent = message; const statusDot = document.getElementById('statusDot');
document.getElementById('error').style.display = 'block'; const statusText = document.getElementById('statusText');
statusDot.className = 'status-dot red';
statusText.textContent = message;
setTimeout(() => { setTimeout(() => {
document.getElementById('error').style.display = 'none'; updateStatusBar();
}, 5000); }, 5000);
} }
function updateStatusBar() {
const statusDot = document.getElementById('statusDot');
const statusText = document.getElementById('statusText');
// Vérifier l'état d'erreur Enphase
if (window.enphaseConnectionError) {
statusDot.className = 'status-dot orange';
statusText.textContent = 'Erreur connexion Enphase';
} else {
statusDot.className = 'status-dot';
statusText.textContent = 'Connecté';
}
}
function updateModeDisplay() { function updateModeDisplay() {
// Mettre à jour l'affichage des boutons actifs // Mettre à jour l'affichage des boutons actifs
document.querySelectorAll('.mode-btn').forEach(btn => { document.querySelectorAll('.mode-btn').forEach(btn => {
@ -438,7 +443,6 @@
fetch('/api/data') fetch('/api/data')
.then(response => { .then(response => {
if (response.status === 401) { if (response.status === 401) {
// Session expirée, rediriger vers login
window.location.href = '/login'; window.location.href = '/login';
return; return;
} }
@ -448,9 +452,10 @@
return response.json(); return response.json();
}) })
.then(data => { .then(data => {
if (!data) return; // Éviter l'erreur si redirection 401 if (!data) return;
// Masquer le loading et afficher les données // Mettre à jour le flag global d'erreur Enphase
document.getElementById('loading').style.display = 'none'; window.enphaseConnectionError = !!data.enphase_connection_error;
updateStatusBar();
document.getElementById('dataDisplay').style.display = 'block'; document.getElementById('dataDisplay').style.display = 'block';
// Mettre à jour la production solaire // Mettre à jour la production solaire
@ -476,8 +481,7 @@
// Mettre à jour la barre de progression // Mettre à jour la barre de progression
const progressBar = document.getElementById('heaterProgress'); const progressBar = document.getElementById('heaterProgress');
progressBar.style.width = data.heater_power + '%'; progressBar.style.width = data.heater_power + '%';
progressBar.textContent = data.heater_power + '%';
// Mettre à jour l'heure de dernière mise à jour // Mettre à jour l'heure de dernière mise à jour
const now = new Date(); const now = new Date();
const timeString = now.toLocaleTimeString('fr-FR'); const timeString = now.toLocaleTimeString('fr-FR');

85
data/settings.html

@ -126,29 +126,46 @@
background: #7f8c8d; background: #7f8c8d;
} }
.message { .notification {
padding: 15px; position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
border-radius: 5px; border-radius: 5px;
margin-bottom: 20px; box-shadow: 0 4px 12px rgba(0,0,0,0.2);
z-index: 1000;
display: none; display: none;
min-width: 300px;
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
} }
.message.success { .notification.success {
background: #d4edda; background: #d4edda;
color: #155724; color: #155724;
border: 1px solid #c3e6cb; border: 1px solid #c3e6cb;
} }
.message.error { .notification.error {
background: #f8d7da; background: #f8d7da;
color: #721c24; color: #721c24;
border: 1px solid #f5c6cb; border: 1px solid #f5c6cb;
} }
.loading { .notification.info {
text-align: center; background: #e8f4f8;
color: #667eea; color: #0c5460;
font-style: italic; border: 1px solid #bee5eb;
} }
</style> </style>
</head> </head>
@ -156,8 +173,7 @@
<div class="container"> <div class="container">
<h1> Configuration</h1> <h1> Configuration</h1>
<div id="message" class="message"></div> <div id="notification" class="notification"></div>
<div id="loading" class="loading" style="display:none;">Chargement...</div>
<form id="settingsForm"> <form id="settingsForm">
<!-- Section Authentification --> <!-- Section Authentification -->
@ -304,7 +320,7 @@
<!-- Boutons d'action --> <!-- Boutons d'action -->
<div style="margin-top: 30px;"> <div style="margin-top: 30px;">
<button type="submit">💾 Enregistrer les modifications</button> <button type="submit">💾 Enregistrer les modifications</button>
<button type="button" class="secondary" onclick="loadSettings()">🔄 Recharger</button> <button type="button" onclick="loadSettings()">🔄 Recharger</button>
</div> </div>
</form> </form>
@ -318,24 +334,21 @@
// Charger les paramètres au démarrage // Charger les paramètres au démarrage
window.addEventListener('DOMContentLoaded', loadSettings); window.addEventListener('DOMContentLoaded', loadSettings);
function showMessage(text, type) { function showNotification(text, type = 'info') {
const messageDiv = document.getElementById('message'); const notification = document.getElementById('notification');
messageDiv.textContent = text; notification.textContent = text;
messageDiv.className = 'message ' + type; notification.className = 'notification ' + type;
messageDiv.style.display = 'block'; notification.style.display = 'block';
setTimeout(() => { setTimeout(() => {
messageDiv.style.display = 'none'; notification.style.display = 'none';
}, 5000); }, 3000);
} }
function loadSettings() { function loadSettings() {
const loading = document.getElementById('loading');
loading.style.display = 'block';
fetch('/api/settings') fetch('/api/settings')
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
loading.style.display = 'none';
document.getElementById('username').value = data.auth.username || 'admin'; document.getElementById('username').value = data.auth.username || 'admin';
@ -372,8 +385,7 @@
document.getElementById('confirm_password').value = ''; document.getElementById('confirm_password').value = '';
}) })
.catch(error => { .catch(error => {
loading.style.display = 'none'; showNotification('Erreur lors du chargement des paramètres', 'error');
showMessage('Erreur lors du chargement des paramètres', 'error');
console.error('Erreur:', error); console.error('Erreur:', error);
}); });
} }
@ -387,17 +399,17 @@
// Validation du mot de passe // Validation du mot de passe
if (newPassword && newPassword !== confirmPassword) { if (newPassword && newPassword !== confirmPassword) {
showMessage('Les mots de passe ne correspondent pas', 'error'); showNotification('Les mots de passe ne correspondent pas', 'error');
return; return;
} }
if (newPassword && !currentPassword) { if (newPassword && !currentPassword) {
showMessage('Le mot de passe actuel est requis pour changer le mot de passe', 'error'); showNotification('Le mot de passe actuel est requis pour changer le mot de passe', 'error');
return; return;
} }
if (newPassword && newPassword.length < 4) { if (newPassword && newPassword.length < 4) {
showMessage('Le nouveau mot de passe doit contenir au moins 4 caractères', 'error'); showNotification('Le nouveau mot de passe doit contenir au moins 4 caractères', 'error');
return; return;
} }
@ -434,8 +446,7 @@
}; };
// Envoyer les données // Envoyer les données
const loading = document.getElementById('loading'); showNotification('Enregistrement en cours...', 'info');
loading.style.display = 'block';
fetch('/api/settings', { fetch('/api/settings', {
method: 'POST', method: 'POST',
@ -445,7 +456,6 @@
body: JSON.stringify(settings) body: JSON.stringify(settings)
}) })
.then(response => { .then(response => {
loading.style.display = 'none';
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} else { } else {
@ -453,15 +463,13 @@
} }
}) })
.then(data => { .then(data => {
const wifiChanged = document.getElementById('wifi_ssid').value || document.getElementById('wifi_password').value; showNotification(data.message || 'Paramètres sauvegardés avec succès!', 'success');
if (wifiChanged) { // Le backend indique via data.restart si un redémarrage est nécessaire
showMessage(data.message + ' - L\'ESP va redémarrer pour appliquer les changements WiFi.', 'success'); if (data.restart) {
setTimeout(() => { setTimeout(() => {
showMessage('Redémarrage en cours... Reconnexion au nouveau réseau nécessaire.', 'error'); showNotification('L\'ESP va redémarrer pour appliquer les changements WiFi. Reconnexion nécessaire.', 'info');
}, 3000); }, 2000);
} else {
showMessage(data.message || 'Paramètres sauvegardés avec succès!', 'success');
} }
// Si le mot de passe a changé, rediriger vers login après 2 secondes // Si le mot de passe a changé, rediriger vers login après 2 secondes
@ -477,8 +485,7 @@
} }
}) })
.catch(error => { .catch(error => {
loading.style.display = 'none'; showNotification('Erreur lors de la sauvegarde des paramètres', 'error');
showMessage('Erreur lors de la sauvegarde des paramètres', 'error');
console.error('Erreur:', error); console.error('Erreur:', error);
}); });
}); });

153
src/main.cpp

@ -55,6 +55,11 @@ float solarProduction = 0.0; // Production solaire en Watts
float powerConsumption = 0.0; // Consommation électrique en Watts float powerConsumption = 0.0; // Consommation électrique en Watts
int heaterPower = 0; // Puissance de chauffe du chauffe-eau en % (0-100) int heaterPower = 0; // Puissance de chauffe du chauffe-eau en % (0-100)
float waterTemperature = 0.0; // Température de l'eau du chauffe-eau en °C float waterTemperature = 0.0; // Température de l'eau du chauffe-eau en °C
// Flag pour l'état de la connexion Enphase
bool enphaseConnectionError = false;
// Configuration GPIO
#define HEATER_GPIO 12 // GPIO12 pour contrôle chauffe-eau
// Configuration GPIO // Configuration GPIO
#define HEATER_GPIO 12 // GPIO12 pour contrôle chauffe-eau #define HEATER_GPIO 12 // GPIO12 pour contrôle chauffe-eau
@ -63,6 +68,9 @@ float waterTemperature = 0.0; // Température de l'eau du chauffe-eau en °C
bool otaRebootPending = false; bool otaRebootPending = false;
unsigned long otaRebootTime = 0; unsigned long otaRebootTime = 0;
// Variable pour le mode JOUR - mémoriser si la température max a été atteinte aujourd'hui
bool maxTempReachedToday = false;
// Variables pour la gestion PWM avec timer (période 1s) // Variables pour la gestion PWM avec timer (période 1s)
const unsigned long PWM_PERIOD = 1000; // Période de 1 seconde en ms const unsigned long PWM_PERIOD = 1000; // Période de 1 seconde en ms
volatile unsigned long pwmCycleCount = 0; // Compteur de cycles PWM volatile unsigned long pwmCycleCount = 0; // Compteur de cycles PWM
@ -409,50 +417,38 @@ void IRAM_ATTR onTimerISR() {
void fetchEnphaseData() { void fetchEnphaseData() {
if (enphaseGatewayIP.length() == 0 || enphaseToken.length() == 0) { if (enphaseGatewayIP.length() == 0 || enphaseToken.length() == 0) {
Serial.println("Configuration Enphase manquante"); Serial.println("Configuration Enphase manquante");
enphaseConnectionError = true;
return; return;
} }
WiFiClientSecure client; WiFiClientSecure client;
client.setInsecure(); // Ignorer la vérification du certificat SSL client.setInsecure(); // Ignorer la vérification du certificat SSL
HTTPClient http; HTTPClient http;
String url = "https://" + enphaseGatewayIP + "/ivp/meters/reports/consumption"; String url = "https://" + enphaseGatewayIP + "/ivp/meters/reports/consumption";
http.begin(client, url); http.begin(client, url);
http.addHeader("Accept", "application/json"); http.addHeader("Accept", "application/json");
http.addHeader("Authorization", "Bearer " + enphaseToken); http.addHeader("Authorization", "Bearer " + enphaseToken);
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // Suivre les redirections http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // Suivre les redirections
int httpCode = http.GET(); int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) { if (httpCode == HTTP_CODE_OK) {
String payload = http.getString(); String payload = http.getString();
JsonDocument doc; JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload); DeserializationError error = deserializeJson(doc, payload);
if (!error && doc.is<JsonArray>() && doc.size() >= 2) { if (!error && doc.is<JsonArray>() && doc.size() >= 2) {
// Selon le code Python:
// json[0]['cumulative']['currW'] = total (consommation totale)
// json[1]['cumulative']['currW'] = net (réseau Enedis)
// panneaux = total - net (production solaire)
float total = doc[0]["cumulative"]["currW"].as<float>(); float total = doc[0]["cumulative"]["currW"].as<float>();
float net = doc[1]["cumulative"]["currW"].as<float>(); float net = doc[1]["cumulative"]["currW"].as<float>();
solarProduction = round(total - net); // Production des panneaux arrondie solarProduction = round(total - net); // Production des panneaux arrondie
powerConsumption = round(total); // Consommation totale arrondie powerConsumption = round(total); // Consommation totale arrondie
Serial.printf("Enphase - Total: %.1fW, Panneaux: %.1fW, Net: %.1fW\n", Serial.printf("Enphase - Total: %.1fW, Panneaux: %.1fW, Net: %.1fW\n",
total, solarProduction, net); total, solarProduction, net);
enphaseConnectionError = false;
} else { } else {
Serial.println("Erreur parsing JSON Enphase"); Serial.println("Erreur parsing JSON Enphase");
enphaseConnectionError = true;
} }
} else { } else {
Serial.printf("Erreur HTTP Enphase: %d\n", httpCode); Serial.printf("Erreur HTTP Enphase: %d\n", httpCode);
enphaseConnectionError = true;
} }
http.end(); http.end();
} }
@ -482,6 +478,23 @@ void calculateHeaterPower() {
isNightPeriod = (currentHour >= nightStartHour || currentHour < nightEndHour); isNightPeriod = (currentHour >= nightStartHour || currentHour < nightEndHour);
} }
// Vérifier si on est dans la période jour (entre lever et coucher du soleil)
bool isDayPeriod = false;
if (sunriseTime != "--:--" && sunsetTime != "--:--") {
// Parser les heures de lever et coucher
int sunriseHour = sunriseTime.substring(0, 2).toInt();
int sunriseMinute = sunriseTime.substring(3, 5).toInt();
int sunsetHour = sunsetTime.substring(0, 2).toInt();
int sunsetMinute = sunsetTime.substring(3, 5).toInt();
// Convertir en minutes depuis minuit pour faciliter la comparaison
int currentTimeInMinutes = currentHour * 60 + currentMinute;
int sunriseTimeInMinutes = sunriseHour * 60 + sunriseMinute;
int sunsetTimeInMinutes = sunsetHour * 60 + sunsetMinute;
isDayPeriod = (currentTimeInMinutes >= sunriseTimeInMinutes && currentTimeInMinutes <= sunsetTimeInMinutes);
}
// Calculer l'excédent solaire et ajuster le pourcentage de chauffe // Calculer l'excédent solaire et ajuster le pourcentage de chauffe
float solarExcess = solarProduction - powerConsumption; float solarExcess = solarProduction - powerConsumption;
int calculatedHeaterPowerJour, calculatedHeaterPowerSoleil = 0; int calculatedHeaterPowerJour, calculatedHeaterPowerSoleil = 0;
@ -511,40 +524,50 @@ void calculateHeaterPower() {
} }
} else if (modeSOLEIL || modeJOUR) { } else if (modeSOLEIL || modeJOUR) {
if (modeJOUR) { if (modeJOUR && isDayPeriod) {
// Mode JOUR: calculer selon la formule // Mode JOUR: calculer selon la formule (uniquement pendant la journée)
// calculatedHeaterPowerJour = 100 * CoeffJour * (max_water_temp - water_temperature) / (secondes_restantes) // calculatedHeaterPowerJour = 100 * CoeffJour * (max_water_temp - water_temperature) / (secondes_restantes)
// Calculer le temps actuel en secondes depuis minuit // Vérifier si la température max a déjà été atteinte aujourd'hui
int currentTimeInSeconds = currentHour * 3600 + currentMinute * 60 + currentSecond; if (waterTemperature >= maxWaterTemp) {
maxTempReachedToday = true;
// Calculer l'heure cible en secondes depuis minuit }
int targetTimeInSeconds = targetMaxWaterTempHour * 3600;
// Calculer les secondes restantes
int secondsRemaining = targetTimeInSeconds - currentTimeInSeconds;
// // Si on est après l'heure cible ou à l'heure cible, calculer jusqu'au lendemain
// if (secondsRemaining < 0) {
// secondsRemaining = 86400 + secondsRemaining; // 86400 = 24h en secondes
// }
float tempDiff = maxWaterTemp - waterTemperature;
if (tempDiff > 0 && secondsRemaining > 0) { if (maxTempReachedToday) {
calculatedHeaterPowerJour = (int)(100.0 * coeffJour * tempDiff / secondsRemaining); // La température max a été atteinte aujourd'hui, ne pas chauffer
if (calculatedHeaterPowerJour > 100) calculatedHeaterPowerJour = 100;
if (calculatedHeaterPowerJour < 0) calculatedHeaterPowerJour = 0;
// Afficher avec les heures et minutes pour la lisibilité
// int hoursDisplay = secondsRemaining / 3600;
// int minutesDisplay = (secondsRemaining % 3600) / 60;
// Serial.printf("[JOUR] TempDiff: %.1f°C, Temps restant: %dh%02dm, Chauffe: %d%%\n",
// tempDiff, hoursDisplay, minutesDisplay, calculatedHeaterPowerJour);
} else {
calculatedHeaterPowerJour = 0; calculatedHeaterPowerJour = 0;
// Serial.printf("[JOUR] Température atteinte ou dépassée: %.1f°C >= %.1f°C\n", } else {
// waterTemperature, maxWaterTemp); // Calculer le temps actuel en secondes depuis minuit
int currentTimeInSeconds = currentHour * 3600 + currentMinute * 60 + currentSecond;
// Calculer l'heure cible en secondes depuis minuit
int targetTimeInSeconds = targetMaxWaterTempHour * 3600;
// Calculer les secondes restantes
int secondsRemaining = targetTimeInSeconds - currentTimeInSeconds;
// // Si on est après l'heure cible ou à l'heure cible, calculer jusqu'au lendemain
// if (secondsRemaining < 0) {
// secondsRemaining = 86400 + secondsRemaining; // 86400 = 24h en secondes
// }
float tempDiff = maxWaterTemp - waterTemperature;
if (tempDiff > 0 && secondsRemaining > 0) {
calculatedHeaterPowerJour = (int)(100.0 * coeffJour * tempDiff / secondsRemaining);
if (calculatedHeaterPowerJour > 100) calculatedHeaterPowerJour = 100;
if (calculatedHeaterPowerJour < 0) calculatedHeaterPowerJour = 0;
// Afficher avec les heures et minutes pour la lisibilité
// int hoursDisplay = secondsRemaining / 3600;
// int minutesDisplay = (secondsRemaining % 3600) / 60;
// Serial.printf("[JOUR] TempDiff: %.1f°C, Temps restant: %dh%02dm, Chauffe: %d%%\n",
// tempDiff, hoursDisplay, minutesDisplay, calculatedHeaterPowerJour);
} else {
calculatedHeaterPowerJour = 0;
// Serial.printf("[JOUR] Température atteinte ou dépassée: %.1f°C >= %.1f°C\n",
// waterTemperature, maxWaterTemp);
}
} }
} }
@ -774,12 +797,17 @@ void setup() {
// API POST /api/settings // API POST /api/settings
server.on("/api/settings", HTTP_POST, server.on("/api/settings", HTTP_POST,
[](AsyncWebServerRequest *request){ [](AsyncWebServerRequest *request){
Serial.println("[SETTINGS] POST /api/settings appelé");
// Handler appelé quand la requête est complète // Handler appelé quand la requête est complète
if (!checkAuthentication(request)) { if (!checkAuthentication(request)) {
Serial.println("[SETTINGS] Authentification échouée");
request->send(401, "text/plain", "Non autorisé"); request->send(401, "text/plain", "Non autorisé");
return; return;
} }
Serial.println("[SETTINGS] Authentification OK");
// Récupérer le body depuis _tempObject // Récupérer le body depuis _tempObject
if (request->_tempObject == NULL) { if (request->_tempObject == NULL) {
Serial.println("[SETTINGS] Erreur: Pas de body reçu"); Serial.println("[SETTINGS] Erreur: Pas de body reçu");
@ -852,16 +880,22 @@ void setup() {
if (requestDoc["wifi"]["ssid"] && requestDoc["wifi"]["ssid"].as<String>().length() > 0) { if (requestDoc["wifi"]["ssid"] && requestDoc["wifi"]["ssid"].as<String>().length() > 0) {
String newSSID = requestDoc["wifi"]["ssid"].as<String>(); String newSSID = requestDoc["wifi"]["ssid"].as<String>();
configDoc["wifi"]["ssid"] = newSSID; if (newSSID != wifiSSID) {
wifiSSID = newSSID; configDoc["wifi"]["ssid"] = newSSID;
wifiChanged = true; wifiSSID = newSSID;
wifiChanged = true;
Serial.println("[SETTINGS] SSID modifié, redémarrage nécessaire");
}
} }
if (requestDoc["wifi"]["password"] && requestDoc["wifi"]["password"].as<String>().length() > 0) { if (requestDoc["wifi"]["password"] && requestDoc["wifi"]["password"].as<String>().length() > 0) {
String newWifiPass = requestDoc["wifi"]["password"].as<String>(); String newWifiPass = requestDoc["wifi"]["password"].as<String>();
configDoc["wifi"]["password"] = newWifiPass; if (newWifiPass != wifiPassword) {
wifiPassword = newWifiPass; configDoc["wifi"]["password"] = newWifiPass;
wifiChanged = true; wifiPassword = newWifiPass;
wifiChanged = true;
Serial.println("[SETTINGS] Mot de passe WiFi modifié, redémarrage nécessaire");
}
} }
if (requestDoc["wifi"]["ap_password"] && requestDoc["wifi"]["ap_password"].as<String>().length() > 0) { if (requestDoc["wifi"]["ap_password"] && requestDoc["wifi"]["ap_password"].as<String>().length() > 0) {
@ -988,6 +1022,7 @@ void setup() {
JsonDocument responseDoc; JsonDocument responseDoc;
responseDoc["success"] = true; responseDoc["success"] = true;
responseDoc["restart"] = wifiChanged;
if (wifiChanged) { if (wifiChanged) {
responseDoc["message"] = "Paramètres sauvegardés. Redémarrage pour appliquer les changements WiFi..."; responseDoc["message"] = "Paramètres sauvegardés. Redémarrage pour appliquer les changements WiFi...";
@ -1013,12 +1048,21 @@ void setup() {
if (index == 0) { if (index == 0) {
// Premier chunk: créer le buffer // Premier chunk: créer le buffer
Serial.printf("[SETTINGS] Réception body: %d bytes total\n", total); Serial.printf("[SETTINGS] Réception body: %d bytes total\n", total);
if (total > 8192) {
Serial.println("[SETTINGS] ERREUR: Body trop grand!");
return;
}
request->_tempObject = malloc(total + 1); request->_tempObject = malloc(total + 1);
if (request->_tempObject == NULL) {
Serial.println("[SETTINGS] ERREUR: Échec allocation mémoire!");
return;
}
} }
if (request->_tempObject != NULL) { if (request->_tempObject != NULL) {
// Copier ce chunk // Copier ce chunk
memcpy((uint8_t*)request->_tempObject + index, data, len); memcpy((uint8_t*)request->_tempObject + index, data, len);
Serial.printf("[SETTINGS] Chunk reçu: index=%d len=%d total=%d\n", index, len, total);
if (index + len == total) { if (index + len == total) {
// Dernier chunk: terminer la chaîne // Dernier chunk: terminer la chaîne
@ -1043,7 +1087,7 @@ void setup() {
doc["sunrise_time"] = sunriseTime; doc["sunrise_time"] = sunriseTime;
doc["sunset_time"] = sunsetTime; doc["sunset_time"] = sunsetTime;
doc["timestamp"] = millis(); doc["timestamp"] = millis();
doc["enphase_connection_error"] = enphaseConnectionError;
String response; String response;
serializeJson(doc, response); serializeJson(doc, response);
request->send(200, "application/json", response); request->send(200, "application/json", response);
@ -1412,5 +1456,8 @@ void loop() {
if (timeinfo.tm_mday != lastDay) { if (timeinfo.tm_mday != lastDay) {
lastDay = timeinfo.tm_mday; lastDay = timeinfo.tm_mday;
calculateSunriseSunset(); calculateSunriseSunset();
// Réinitialiser le flag de température max atteinte pour le nouveau jour
maxTempReachedToday = false;
Serial.println("[JOUR] Nouveau jour - réinitialisation du flag température max");
} }
} }
Loading…
Cancel
Save