diff --git a/data/app.js b/data/app.js
index a1a7648..8e7d990 100644
--- a/data/app.js
+++ b/data/app.js
@@ -51,7 +51,7 @@ function afficherOnglet(nom, bouton) {
document.getElementById(nom).classList.add('actif');
bouton.classList.add('active');
if (nom === 'regles') chargerRegles();
- if (nom === 'config') { chargerSleep(); chargerWifi(); chargerPrefsUI(); chargerModbus(); chargerWireGuard(); }
+ if (nom === 'config') { chargerSleep(); chargerWifi(); chargerPrefsUI(); chargerModbus(); chargerMqtt(); chargerWireGuard(); }
if (nom === 'historique') chargerHistorique();
if (nom === 'debug') chargerDebug();
if (nom === 'epever-config') lireConfigEpever();
@@ -848,6 +848,87 @@ function fermerSunPopup() {
if (modal) modal.classList.add('hidden');
}
+// --- MQTT ---
+
+function mqttAfficherTopics(base) {
+ const info = document.getElementById('mqtt-topics-info');
+ const pub = document.getElementById('mqtt-topics-list');
+ const cmd = document.getElementById('mqtt-cmd-list');
+ if (!info || !pub || !cmd) return;
+ const b = base || 'solar';
+ const pubTopics = [
+ 'pv/voltage', 'pv/current',
+ 'battery/voltage', 'battery/soc', 'battery/temperature', 'battery/status',
+ 'load/voltage', 'load/current', 'load/power',
+ 'energy/generated/today', 'energy/generated/total',
+ 'energy/consumed/today', 'energy/consumed/total',
+ 'sun', 'rs485/ok', 'relay/1', 'relay/2', 'input/1', 'input/2'
+ ];
+ pub.innerHTML = pubTopics.map(t => `${b}/${t}`).join(' ');
+ cmd.innerHTML = `${b}/relay/1/set ${b}/relay/2/set` +
+ `
Valeurs acceptées : ON / OFF`;
+ info.classList.remove('hidden');
+}
+
+async function chargerMqtt() {
+ try {
+ const d = await (await fetch('/api/mqtt')).json();
+ const bar = document.getElementById('mqtt-status-bar');
+ if (bar) {
+ if (d.enabled && d.connected) {
+ bar.textContent = '✓ Connecté — ' + d.server + ':' + d.port;
+ bar.className = 'ec-statusbar ec-ok';
+ } else if (d.enabled) {
+ bar.textContent = '⏳ Activé — en attente de connexion WiFi ou broker';
+ bar.className = 'ec-statusbar';
+ } else {
+ bar.textContent = 'Désactivé';
+ bar.className = 'ec-statusbar';
+ }
+ }
+ const s = id => document.getElementById(id);
+ s('mqtt-enabled').value = String(!!d.enabled);
+ if (s('mqtt-server')) s('mqtt-server').value = d.server || '192.168.1.36';
+ if (s('mqtt-port')) s('mqtt-port').value = d.port || 1883;
+ if (s('mqtt-user')) s('mqtt-user').value = d.user || '';
+ if (s('mqtt-pass')) s('mqtt-pass').value = d.pass || '';
+ if (s('mqtt-base')) s('mqtt-base').value = d.base || 'solar';
+ if (s('mqtt-interval')) s('mqtt-interval').value = d.interval || 30;
+ mqttAfficherTopics(d.base || 'solar');
+ } catch { /* silencieux */ }
+}
+
+async function sauvegarderMqtt() {
+ const g = id => (document.getElementById(id)?.value || '').trim();
+ const enabled = g('mqtt-enabled') === 'true';
+ const server = g('mqtt-server') || '192.168.1.36';
+ const port = parseInt(g('mqtt-port')) || 1883;
+ const user = g('mqtt-user');
+ const pass = g('mqtt-pass');
+ const base = g('mqtt-base') || 'solar';
+ const interval = parseInt(g('mqtt-interval')) || 30;
+
+ if (enabled && !server) { afficherToast('⚠ Adresse serveur requise'); return; }
+
+ try {
+ const res = await fetch('/api/mqtt', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ enabled, server, port, user, pass, base, interval })
+ });
+ const d = await res.json();
+ if (d.ok) {
+ afficherToast(enabled ? '✓ MQTT activé — connexion en cours' : '✓ MQTT désactivé');
+ mqttAfficherTopics(base);
+ setTimeout(chargerMqtt, 3000);
+ } else {
+ afficherToast('⚠ Erreur sauvegarde MQTT');
+ }
+ } catch(e) {
+ afficherToast('Erreur : ' + e.message);
+ }
+}
+
// --- WireGuard ---
async function chargerWireGuard() {
diff --git a/data/index.html b/data/index.html
index 942f6ae..1163f50 100644
--- a/data/index.html
+++ b/data/index.html
@@ -425,6 +425,57 @@
+
Envoie les données des capteurs vers un broker MQTT (Home Assistant, Mosquitto…). Les relais sont pilotables via les topics de commande.
+ + + +Tunnel chiffré vers votre serveur WireGuard. Nécessite une connexion WiFi (mode STA). Désactivé par défaut — la configuration locale reste accessible même si le VPN est coupé.
diff --git a/include/mqtt_client.h b/include/mqtt_client.h new file mode 100644 index 0000000..3a1e79c --- /dev/null +++ b/include/mqtt_client.h @@ -0,0 +1,10 @@ +#pragma once +#include