|
|
<!DOCTYPE html> |
|
|
<html> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Contrôleur Solaire - Console</title> |
|
|
<style> |
|
|
body { |
|
|
font-family: Arial, sans-serif; |
|
|
max-width: 900px; |
|
|
margin: 50px auto; |
|
|
padding: 20px; |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
min-height: 100vh; |
|
|
} |
|
|
|
|
|
.container { |
|
|
background: white; |
|
|
padding: 40px; |
|
|
border-radius: 10px; |
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.3); |
|
|
} |
|
|
|
|
|
h1 { |
|
|
color: #333; |
|
|
text-align: center; |
|
|
margin-bottom: 30px; |
|
|
} |
|
|
|
|
|
h2 { |
|
|
color: #667eea; |
|
|
border-bottom: 2px solid #667eea; |
|
|
padding-bottom: 10px; |
|
|
margin-top: 30px; |
|
|
} |
|
|
|
|
|
.nav-links { |
|
|
text-align: center; |
|
|
margin-top: 30px; |
|
|
} |
|
|
|
|
|
.nav-links a { |
|
|
display: inline-block; |
|
|
padding: 10px 20px; |
|
|
background: #95a5a6; |
|
|
color: white; |
|
|
text-decoration: none; |
|
|
border-radius: 5px; |
|
|
margin: 5px; |
|
|
transition: background 0.3s; |
|
|
} |
|
|
|
|
|
.nav-links a:hover { |
|
|
background: #7f8c8d; |
|
|
} |
|
|
|
|
|
.logout-btn { |
|
|
display: inline-block; |
|
|
padding: 8px 20px; |
|
|
background: #e74c3c; |
|
|
color: white; |
|
|
text-decoration: none; |
|
|
border-radius: 5px; |
|
|
font-size: 14px; |
|
|
transition: background 0.3s; |
|
|
} |
|
|
|
|
|
.logout-btn:hover { |
|
|
background: #c0392b; |
|
|
} |
|
|
|
|
|
.header-bar { |
|
|
text-align: right; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
|
|
|
.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; |
|
|
} |
|
|
|
|
|
.data-grid { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); |
|
|
gap: 20px; |
|
|
margin: 30px 0; |
|
|
} |
|
|
|
|
|
.data-card { |
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); |
|
|
padding: 15px; |
|
|
border-radius: 10px; |
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1); |
|
|
text-align: center; |
|
|
transition: transform 0.3s; |
|
|
} |
|
|
|
|
|
.data-card:hover { |
|
|
transform: translateY(-5px); |
|
|
} |
|
|
|
|
|
.mode-btn { |
|
|
padding: 15px 25px; |
|
|
border: 2px solid #667eea; |
|
|
background: white; |
|
|
color: #667eea; |
|
|
border-radius: 5px; |
|
|
font-size: 16px; |
|
|
font-weight: bold; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s; |
|
|
} |
|
|
|
|
|
.mode-btn:hover { |
|
|
background: #667eea; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.mode-btn.active { |
|
|
background: #667eea; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.mode-btn.off { |
|
|
border-color: #e74c3c; |
|
|
color: #e74c3c; |
|
|
} |
|
|
|
|
|
.mode-btn.off.active { |
|
|
background: #e74c3c; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.mode-btn.on { |
|
|
border-color: #27ae60; |
|
|
color: #27ae60; |
|
|
} |
|
|
|
|
|
.mode-btn.on.active { |
|
|
background: #27ae60; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.mode-btn.secondary { |
|
|
background: #95a5a6; |
|
|
border-color: #95a5a6; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.mode-btn.secondary:hover { |
|
|
background: #7f8c8d; |
|
|
border-color: #7f8c8d; |
|
|
} |
|
|
|
|
|
.data-card.solar { |
|
|
background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%); |
|
|
} |
|
|
.data-card.consumption { |
|
|
background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%); |
|
|
color: white; |
|
|
} |
|
|
.data-card.heater { |
|
|
background: linear-gradient(135deg, #ff7675 0%, #d63031 100%); |
|
|
color: white; |
|
|
} |
|
|
.data-card.temperature { |
|
|
background: linear-gradient(135deg, #a29bfe 0%, #6c5ce7 100%); |
|
|
color: white; |
|
|
} |
|
|
.data-card.net-consumption { |
|
|
background: linear-gradient(135deg, #55efc4 0%, #38ada9 100%); |
|
|
color: white; |
|
|
} |
|
|
.data-icon { |
|
|
font-size: 32px; |
|
|
margin-bottom: 5px; |
|
|
} |
|
|
.data-label { |
|
|
font-size: 14px; |
|
|
font-weight: bold; |
|
|
text-transform: uppercase; |
|
|
margin-bottom: 5px; |
|
|
opacity: 0.8; |
|
|
} |
|
|
.data-value { |
|
|
font-size: 28px; |
|
|
font-weight: bold; |
|
|
margin-bottom: 3px; |
|
|
} |
|
|
.data-unit { |
|
|
font-size: 14px; |
|
|
opacity: 0.9; |
|
|
} |
|
|
|
|
|
.status-indicator { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 8px; |
|
|
} |
|
|
.status-dot { |
|
|
width: 10px; |
|
|
height: 10px; |
|
|
border-radius: 50%; |
|
|
background: #27ae60; |
|
|
animation: pulse 2s infinite; |
|
|
} |
|
|
.status-dot.red { |
|
|
background: #e74c3c; |
|
|
} |
|
|
.status-dot.orange { |
|
|
background: #f39c12; |
|
|
} |
|
|
@keyframes pulse { |
|
|
0%, 100% { opacity: 1; } |
|
|
50% { opacity: 0.5; } |
|
|
} |
|
|
.progress-bar { |
|
|
width: 100%; |
|
|
height: 30px; |
|
|
background: #ecf0f1; |
|
|
border-radius: 15px; |
|
|
overflow: hidden; |
|
|
margin-top: 15px; |
|
|
position: relative; |
|
|
} |
|
|
.progress-fill { |
|
|
height: 100%; |
|
|
background: linear-gradient(90deg, #e74c3c 0%, #c0392b 100%); |
|
|
transition: width 0.5s ease; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
color: white; |
|
|
font-weight: bold; |
|
|
font-size: 14px; |
|
|
} |
|
|
.mode-selector { |
|
|
display: flex; |
|
|
gap: 10px; |
|
|
justify-content: center; |
|
|
flex-wrap: wrap; |
|
|
margin-top: 20px; |
|
|
} |
|
|
|
|
|
.help-icon { |
|
|
display: inline-block; |
|
|
width: 24px; |
|
|
height: 24px; |
|
|
border-radius: 50%; |
|
|
background: #667eea; |
|
|
color: white; |
|
|
text-align: center; |
|
|
line-height: 24px; |
|
|
cursor: pointer; |
|
|
font-size: 16px; |
|
|
font-weight: bold; |
|
|
margin-left: 10px; |
|
|
transition: background 0.3s; |
|
|
} |
|
|
|
|
|
.help-icon:hover { |
|
|
background: #764ba2; |
|
|
} |
|
|
|
|
|
.help-modal { |
|
|
display: none; |
|
|
position: fixed; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
background: rgba(0, 0, 0, 0.6); |
|
|
z-index: 1000; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
} |
|
|
|
|
|
.help-modal.show { |
|
|
display: flex; |
|
|
} |
|
|
|
|
|
.help-content { |
|
|
background: white; |
|
|
padding: 30px; |
|
|
border-radius: 10px; |
|
|
max-width: 600px; |
|
|
max-height: 80vh; |
|
|
overflow-y: auto; |
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.3); |
|
|
} |
|
|
|
|
|
.help-content h3 { |
|
|
color: #667eea; |
|
|
margin-top: 0; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
|
|
|
.help-content .mode-item { |
|
|
margin-bottom: 20px; |
|
|
padding: 15px; |
|
|
background: #f5f7fa; |
|
|
border-radius: 5px; |
|
|
border-left: 4px solid #667eea; |
|
|
} |
|
|
|
|
|
.help-content .mode-title { |
|
|
font-weight: bold; |
|
|
font-size: 16px; |
|
|
color: #333; |
|
|
margin-bottom: 8px; |
|
|
} |
|
|
|
|
|
.help-content .mode-desc { |
|
|
color: #555; |
|
|
line-height: 1.6; |
|
|
} |
|
|
|
|
|
.close-help { |
|
|
float: right; |
|
|
font-size: 28px; |
|
|
font-weight: bold; |
|
|
color: #aaa; |
|
|
cursor: pointer; |
|
|
line-height: 20px; |
|
|
} |
|
|
|
|
|
.close-help:hover { |
|
|
color: #333; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<div class="header-bar"> |
|
|
<a href="/logout" class="logout-btn">🔓 Déconnexion</a> |
|
|
</div> |
|
|
|
|
|
<h1>☀️ Contrôleur Solaire pour Chauffe-Eau</h1> |
|
|
|
|
|
<div id="statusBar" class="status-bar"> |
|
|
<div class="status-indicator"> |
|
|
<div class="status-dot" id="statusDot"></div> |
|
|
<span id="statusText">Chargement...</span> |
|
|
</div> |
|
|
<div style="display: flex; gap: 18px; align-items: center;"> |
|
|
<span id="sunriseStatus">🌅 --:--</span> |
|
|
<span id="sunsetStatus">🌇 --:--</span> |
|
|
<span id="lastUpdate">Dernière mise à jour: --</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="dataDisplay" style="display:none;"> |
|
|
<h2>📊 Données en temps réel</h2> |
|
|
|
|
|
<div class="data-grid"> |
|
|
<div class="data-card solar"> |
|
|
<div class="data-icon">☀️</div> |
|
|
<div class="data-label">Production Solaire</div> |
|
|
<div class="data-value" id="solarValue">--</div> |
|
|
<div class="data-unit">Watts</div> |
|
|
</div> |
|
|
|
|
|
<div class="data-card consumption"> |
|
|
<div class="data-icon">⚡</div> |
|
|
<div class="data-label">Consommation ENEDIS</div> |
|
|
<div class="data-value" id="consumptionValue">--</div> |
|
|
<div class="data-unit">Watts</div> |
|
|
</div> |
|
|
|
|
|
<div class="data-card net-consumption"> |
|
|
<div class="data-icon">🔌</div> |
|
|
<div class="data-label">Consommation Nette</div> |
|
|
<div class="data-value" id="netConsumptionValue">--</div> |
|
|
<div class="data-unit">Watts</div> |
|
|
</div> |
|
|
|
|
|
<div class="data-card heater"> |
|
|
<div class="data-icon">🔥</div> |
|
|
<div class="data-label">Puissance Chauffe-Eau</div> |
|
|
<div class="data-value" id="heaterValue">--</div> |
|
|
<div class="data-unit">%</div> |
|
|
<div class="progress-bar"> |
|
|
<div class="progress-fill" id="heaterProgress" style="width: 0%"></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="data-card temperature"> |
|
|
<div class="data-icon">🌡️</div> |
|
|
<div class="data-label">Température Eau</div> |
|
|
<div class="data-value" id="temperatureValue">--</div> |
|
|
<div class="data-unit">°C</div> |
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
<h2>🎛️ Mode de Fonctionnement <span class="help-icon" onclick="toggleHelp()">?</span></h2> |
|
|
<div class="mode-selector"> |
|
|
<button class="mode-btn" data-bit="16" onclick="toggleMode(16)">🟢 ON</button> |
|
|
<button class="mode-btn" data-bit="8" onclick="toggleMode(8)">🌙 NUIT</button> |
|
|
<button class="mode-btn" data-bit="4" onclick="toggleMode(4)">☀️ SOLEIL</button> |
|
|
<button class="mode-btn" data-bit="2" onclick="toggleMode(2)">☼ JOUR</button> |
|
|
<button class="mode-btn" data-bit="0" onclick="toggleMode(0)">🔴 OFF</button> |
|
|
</div> |
|
|
|
|
|
<div class="nav-links"> |
|
|
<a href="/settings.html" >⚙️ Configuration</a> |
|
|
<a href="/update.html" >📦 Mise à jour OTA</a> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<!-- Modal d'aide --> |
|
|
<div id="helpModal" class="help-modal" onclick="closeHelp(event)"> |
|
|
<div class="help-content" onclick="event.stopPropagation()"> |
|
|
<span class="close-help" onclick="toggleHelp()">×</span> |
|
|
<h3>📖 Guide des Modes de Chauffe</h3> |
|
|
|
|
|
<div class="mode-item"> |
|
|
<div class="mode-title">🟢 Mode ON</div> |
|
|
<div class="mode-desc"> |
|
|
Le chauffe-eau fonctionne à pleine puissance en permanence, indépendamment de la production solaire. |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mode-item"> |
|
|
<div class="mode-title">🔴 Mode OFF</div> |
|
|
<div class="mode-desc"> |
|
|
Le chauffe-eau est complètement désactivé. Aucune chauffe n'est effectuée. |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mode-item"> |
|
|
<div class="mode-title">🌙 Mode NUIT</div> |
|
|
<div class="mode-desc"> |
|
|
Active le chauffage entre <span id="nightHoursHelp">0h - 4h</span> pour atteindre |
|
|
la température minimale configurée de <span id="minTempHelp">40°C</span>.<br/>(Ces valeurs peuvent être modifiées dans les paramètres) |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mode-item"> |
|
|
<div class="mode-title">☀️ Mode SOLEIL</div> |
|
|
<div class="mode-desc"> |
|
|
Utilise uniquement l'excédent de production solaire pour chauffer l'eau. La puissance de chauffe |
|
|
s'ajuste automatiquement en fonction de la production solaire disponible. Optimise l'autoconsommation. |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mode-item"> |
|
|
<div class="mode-title">☼ Mode JOUR</div> |
|
|
<div class="mode-desc"> |
|
|
Chauffe intelligemment pendant les heures d'ensoleillement (lever à coucher du soleil) pour atteindre |
|
|
la température maximale configurée de<span id="maxTempHelp">60°C</span> à l'heure cible. La puissance est calculée en fonction du temps |
|
|
restant et de l'écart de température. Le chauffage s'arrête automatiquement une fois la température |
|
|
maximale atteinte jusqu'au lendemain. <em>(Le bouton devient gris quand la température max est atteinte)</em> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mode-item"> |
|
|
<div class="mode-title">🔄 Combinaisons</div> |
|
|
<div class="mode-desc"> |
|
|
Vous pouvez combiner les modes NUIT, SOLEIL et JOUR pour une gestion optimale : |
|
|
<ul style="margin: 10px 0 0 20px;"> |
|
|
<li><strong>NUIT + SOLEIL</strong> : Maintien nocturne + optimisation solaire diurne</li> |
|
|
<li><strong>NUIT + JOUR</strong> : Maintien nocturne + chauffe intelligente diurne</li> |
|
|
<li><strong>SOLEIL + JOUR</strong> : Utilise l'excédent + planifie la chauffe</li> |
|
|
<li><strong>NUIT + SOLEIL + JOUR</strong> : Combinaison complète pour un fonctionnement 24h/24</li> |
|
|
</ul> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
let refreshInterval; |
|
|
let currentMode = 0; // Mode par défaut (OFF) |
|
|
let configNightStart = 0; |
|
|
let configNightEnd = 4; |
|
|
let configMinTemp = 40; |
|
|
let configMaxTemp = 60; |
|
|
|
|
|
function toggleHelp() { |
|
|
const modal = document.getElementById('helpModal'); |
|
|
modal.classList.toggle('show'); |
|
|
// Mettre à jour les heures affichées dans le modal |
|
|
if (modal.classList.contains('show')) { |
|
|
document.getElementById('nightHoursHelp').textContent = |
|
|
configNightStart + 'h - ' + configNightEnd + 'h'; |
|
|
document.getElementById('minTempHelp').textContent = configMinTemp + '°C'; |
|
|
document.getElementById('maxTempHelp').textContent = configMaxTemp + '°C'; |
|
|
} |
|
|
} |
|
|
|
|
|
function closeHelp(event) { |
|
|
if (event.target.id === 'helpModal') { |
|
|
toggleHelp(); |
|
|
} |
|
|
} |
|
|
|
|
|
function showError(message) { |
|
|
const statusDot = document.getElementById('statusDot'); |
|
|
const statusText = document.getElementById('statusText'); |
|
|
statusDot.className = 'status-dot red'; |
|
|
statusText.textContent = message; |
|
|
setTimeout(() => { |
|
|
updateStatusBar(); |
|
|
}, 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() { |
|
|
// Mettre à jour l'affichage des boutons actifs |
|
|
document.querySelectorAll('.mode-btn').forEach(btn => { |
|
|
btn.classList.remove('active', 'on', 'off'); |
|
|
}); |
|
|
|
|
|
if (currentMode === 0) { |
|
|
// Mode OFF |
|
|
document.querySelector('[data-bit="0"]').classList.add('active', 'off'); |
|
|
} else if (currentMode === 16) { |
|
|
// Mode ON |
|
|
document.querySelector('[data-bit="16"]').classList.add('active', 'on'); |
|
|
} else { |
|
|
// Combinaisons NUIT, SOLEIL, JOUR |
|
|
if (currentMode & 8) { |
|
|
document.querySelector('[data-bit="8"]').classList.add('active'); |
|
|
} |
|
|
if (currentMode & 4) { |
|
|
document.querySelector('[data-bit="4"]').classList.add('active'); |
|
|
} |
|
|
if (currentMode & 2) { |
|
|
document.querySelector('[data-bit="2"]').classList.add('active'); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function toggleMode(bitValue) { |
|
|
let newMode; |
|
|
|
|
|
if (bitValue === 16) { |
|
|
// Bouton ON: activer uniquement ON (16) |
|
|
newMode = 16; |
|
|
} else if (bitValue === 0) { |
|
|
// Bouton OFF: désactiver tout (0) |
|
|
newMode = 0; |
|
|
} else { |
|
|
// Boutons NUIT (8), SOLEIL (4), JOUR (2) |
|
|
// Si ON ou OFF est actif, commencer avec 0 |
|
|
if (currentMode === 16 || currentMode === 0) { |
|
|
newMode = bitValue; |
|
|
} else { |
|
|
// Toggle le bit |
|
|
newMode = currentMode ^ bitValue; |
|
|
// Si tout est désactivé, mettre à 0 (OFF) |
|
|
if (newMode === 0) { |
|
|
newMode = 0; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
currentMode = newMode; |
|
|
updateModeDisplay(); |
|
|
|
|
|
// Envoyer le mode au serveur |
|
|
fetch('/api/mode', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify({ mode: currentMode }) |
|
|
}) |
|
|
.then(response => { |
|
|
if (response.status === 401) { |
|
|
// Session expirée, rediriger vers login |
|
|
window.location.href = '/login'; |
|
|
return; |
|
|
} |
|
|
if (!response.ok) { |
|
|
throw new Error('Erreur lors du changement de mode'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
if (!data) return; // Éviter l'erreur si redirection 401 |
|
|
console.log('Mode changé:', currentMode); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Erreur:', error); |
|
|
showError('Erreur lors du changement de mode'); |
|
|
}); |
|
|
} |
|
|
|
|
|
function loadMode() { |
|
|
fetch('/api/mode') |
|
|
.then(response => { |
|
|
if (response.status === 401) { |
|
|
// Session expirée, rediriger vers login |
|
|
window.location.href = '/login'; |
|
|
return; |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
if (!data) return; // Éviter l'erreur si redirection 401 |
|
|
if (typeof data.mode === 'number') { |
|
|
currentMode = data.mode; |
|
|
updateModeDisplay(); |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Erreur chargement mode:', error); |
|
|
}); |
|
|
} |
|
|
|
|
|
function updateData() { |
|
|
fetch('/api/data') |
|
|
.then(response => { |
|
|
if (response.status === 401) { |
|
|
window.location.href = '/login'; |
|
|
return; |
|
|
} |
|
|
if (!response.ok) { |
|
|
throw new Error('Erreur réseau'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
if (!data) return; |
|
|
// Mettre à jour le flag global d'erreur Enphase |
|
|
window.enphaseConnectionError = !!data.enphase_connection_error; |
|
|
updateStatusBar(); |
|
|
|
|
|
// Mettre à jour les heures de configuration |
|
|
if (data.night_start_hour !== undefined) { |
|
|
configNightStart = data.night_start_hour; |
|
|
} |
|
|
if (data.night_end_hour !== undefined) { |
|
|
configNightEnd = data.night_end_hour; |
|
|
} |
|
|
if (data.min_water_temp !== undefined) { |
|
|
configMinTemp = data.min_water_temp; |
|
|
} |
|
|
if (data.max_water_temp !== undefined) { |
|
|
configMaxTemp = data.max_water_temp; |
|
|
} |
|
|
|
|
|
// Mettre à jour le style du bouton JOUR si maxTempReachedToday |
|
|
const jourBtn = document.querySelector('[data-bit="2"]'); |
|
|
if (data.max_temp_reached_today && jourBtn.classList.contains('active')) { |
|
|
jourBtn.classList.add('secondary'); |
|
|
} else { |
|
|
jourBtn.classList.remove('secondary'); |
|
|
} |
|
|
|
|
|
document.getElementById('dataDisplay').style.display = 'block'; |
|
|
|
|
|
// Mettre à jour la production solaire |
|
|
document.getElementById('solarValue').textContent = |
|
|
data.solar_production.toFixed(0); |
|
|
|
|
|
// Mettre à jour la consommation importée |
|
|
document.getElementById('consumptionValue').textContent = |
|
|
data.power_import.toFixed(0); |
|
|
|
|
|
// Mettre à jour la consommation nette |
|
|
document.getElementById('netConsumptionValue').textContent = |
|
|
data.power_net_consumption.toFixed(0); |
|
|
|
|
|
// Mettre à jour la puissance du chauffe-eau |
|
|
document.getElementById('heaterValue').textContent = |
|
|
data.heater_power; |
|
|
|
|
|
// Mettre à jour la température de l'eau |
|
|
document.getElementById('temperatureValue').textContent = |
|
|
data.water_temperature ? data.water_temperature.toFixed(1) : '--'; |
|
|
|
|
|
// Mettre à jour les heures de lever/coucher du soleil |
|
|
document.getElementById('sunriseStatus').textContent = '🌅 ' + (data.sunrise_time || '--:--'); |
|
|
document.getElementById('sunsetStatus').textContent = '🌇 ' + (data.sunset_time || '--:--'); |
|
|
|
|
|
// Mettre à jour la barre de progression |
|
|
const progressBar = document.getElementById('heaterProgress'); |
|
|
progressBar.style.width = data.heater_power + '%'; |
|
|
|
|
|
// Mettre à jour l'heure de dernière mise à jour |
|
|
const now = new Date(); |
|
|
const timeString = now.toLocaleTimeString('fr-FR'); |
|
|
document.getElementById('lastUpdate').textContent = |
|
|
'Dernière mise à jour: ' + timeString; |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Erreur:', error); |
|
|
showError('Erreur lors de la récupération des données'); |
|
|
}); |
|
|
} |
|
|
|
|
|
// Charger les données au démarrage |
|
|
window.addEventListener('DOMContentLoaded', () => { |
|
|
loadMode(); // Charger le mode actuel |
|
|
updateData(); |
|
|
// Rafraîchir toutes les 2 secondes |
|
|
refreshInterval = setInterval(updateData, 2000); |
|
|
}); |
|
|
|
|
|
// Nettoyer l'intervalle quand on quitte la page |
|
|
window.addEventListener('beforeunload', () => { |
|
|
if (refreshInterval) { |
|
|
clearInterval(refreshInterval); |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html>
|
|
|
|