29-05-2025, 09:51 PM
bon la un petit fichier html qui fait la meme chose si vous avez pas d esp en rab
creez un fichier .html ,collez le code enregistrez et lancer avec un navigateur
Code :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Multi-Interfaces</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background: #222;
}
/* Styles pour le mode configuration */
.config-mode {
background-color: #f5f5f5 !important;
}
.config-container {
display: none;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.config-mode .config-container {
display: block;
}
.config-panel {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 30px;
}
.ip-list {
margin-bottom: 20px;
}
.ip-group {
margin-bottom: 15px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 5px;
display: flex;
align-items: center;
gap: 10px;
}
.ip-number {
font-weight: bold;
color: #555;
min-width: 30px;
}
input[type="text"] {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
input[type="text"]:focus {
outline: none;
border-color: #4CAF50;
}
.remove-btn {
background-color: #f44336;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.remove-btn:hover {
background-color: #d32f2f;
}
.add-btn {
background-color: #2196F3;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-bottom: 20px;
width: 100%;
}
.add-btn:hover {
background-color: #1976D2;
}
.button-group {
margin-top: 30px;
display: flex;
gap: 10px;
justify-content: center;
}
button {
padding: 12px 30px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
.save-btn {
background-color: #4CAF50;
color: white;
}
.save-btn:hover {
background-color: #45a049;
}
.message {
margin-top: 20px;
padding: 10px;
border-radius: 5px;
text-align: center;
display: none;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.layout-preview {
margin-top: 20px;
padding: 15px;
background-color: #e3f2fd;
border-radius: 5px;
text-align: center;
font-size: 14px;
color: #1976D2;
}
/* Styles pour le mode dashboard */
.dashboard-container {
display: none;
height: 100vh;
gap: 2px;
background: #222;
}
.dashboard-mode .dashboard-container {
display: grid;
}
.frame-container {
position: relative;
overflow: hidden;
background: white;
}
.frame-wrapper {
width: 100%;
height: 100%;
transform-origin: top left;
transition: transform 0.3s ease;
}
iframe {
width: 100%;
height: 100%;
border: none;
background: white;
}
/* Contrôles de zoom */
.zoom-controls {
position: absolute;
top: 5px;
right: 5px;
background: rgba(0,0,0,0.8);
padding: 5px;
border-radius: 3px;
z-index: 10;
display: flex;
gap: 5px;
}
.zoom-btn {
width: 25px;
height: 25px;
border: none;
background: #444;
color: white;
cursor: pointer;
border-radius: 3px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.zoom-btn:hover {
background: #666;
}
.zoom-value {
color: white;
font-family: Arial, sans-serif;
font-size: 12px;
min-width: 45px;
text-align: center;
display: flex;
align-items: center;
}
/* Label optionnel */
.frame-label {
position: absolute;
top: 5px;
left: 5px;
background: rgba(0,0,0,0.7);
color: white;
padding: 5px 10px;
font-family: Arial, sans-serif;
font-size: 12px;
border-radius: 3px;
z-index: 10;
pointer-events: none;
}
/* Panneau de contrôle global */
.global-controls {
display: none;
position: fixed;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.9);
padding: 10px 20px;
border-radius: 5px;
z-index: 100;
}
.dashboard-mode .global-controls {
display: block;
}
.global-btn {
background: #4CAF50;
color: white;
border: none;
padding: 8px 15px;
margin: 0 5px;
border-radius: 3px;
cursor: pointer;
}
.global-btn:hover {
background: #45a049;
}
.config-btn {
background: #FF9800;
}
.config-btn:hover {
background: #F57C00;
}
/* Message d'accueil */
.welcome-message {
text-align: center;
padding: 40px;
background-color: #e3f2fd;
border-radius: 10px;
margin-bottom: 30px;
}
.welcome-message h2 {
color: #1976D2;
margin-bottom: 10px;
}
</style>
</head>
<body class="config-mode">
<!-- Mode Configuration -->
<div class="config-container">
<div class="config-panel">
<h1>Dashboard Multi-Interfaces</h1>
<div id="welcomeMessage" class="welcome-message">
<h2>Bienvenue !</h2>
<p>Configurez vos adresses IP ci-dessous pour commencer.</p>
</div>
<div id="ipList" class="ip-list"></div>
<button class="add-btn" onclick="addIP()">+ Ajouter une interface</button>
<div id="layoutPreview" class="layout-preview"></div>
<div class="button-group">
<button class="save-btn" onclick="showDashboard()">Afficher le dashboard</button>
<button class="save-btn" style="background-color: #FF5722;" onclick="saveToFile()">? Sauvegarder config</button>
</div>
<div id="message" class="message"></div>
</div>
</div>
<!-- Mode Dashboard -->
<div id="dashboardContainer" class="dashboard-container"></div>
<!-- Contrôles globaux du dashboard -->
<div class="global-controls">
<button class="global-btn" onclick="resetAllZoom()">Réinitialiser tout</button>
<button class="global-btn" onclick="zoomAll(-0.1)">Tout réduire</button>
<button class="global-btn" onclick="zoomAll(0.1)">Tout agrandir</button>
<button class="global-btn config-btn" onclick="showConfig()">⚙️ Configuration</button>
</div>
<script>
// Configuration des IP - MODIFIÉE AUTOMATIQUEMENT
let ipAddresses = ['192.168.1.145'];
let zoomLevels = {};
let currentMode = 'config';
// Initialisation
window.onload = function() {
// Si des IP sont déjà configurées dans le fichier
if (ipAddresses.length > 0 && ipAddresses[0] !== '') {
document.getElementById('welcomeMessage').style.display = 'none';
// Aller directement au dashboard si configuré
if (ipAddresses.length > 1 || (ipAddresses.length === 1 && ipAddresses[0] !== '192.168.1.145')) {
showDashboard();
}
}
renderIPList();
};
// Afficher la liste des IP
function renderIPList() {
const container = document.getElementById('ipList');
container.innerHTML = '';
ipAddresses.forEach((ip, index) => {
const div = document.createElement('div');
div.className = 'ip-group';
div.innerHTML = `
<span class="ip-number">${index + 1}.</span>
<input type="text" id="ip${index}" value="${ip}" placeholder="Ex: 192.168.1.100">
${ipAddresses.length > 1 ? `<button class="remove-btn" onclick="removeIP(${index})">Supprimer</button>` : ''}
`;
container.appendChild(div);
});
updateLayoutPreview();
}
// Ajouter une IP
function addIP() {
ipAddresses.push('');
renderIPList();
}
// Supprimer une IP
function removeIP(index) {
ipAddresses.splice(index, 1);
renderIPList();
}
// Mettre à jour l'aperçu
function updateLayoutPreview() {
const preview = document.getElementById('layoutPreview');
const count = ipAddresses.length;
let layout = '';
if (count === 1) {
layout = 'Plein écran';
} else if (count === 2) {
layout = '2 colonnes côte à côte';
} else if (count === 3) {
layout = '1 grande vue à gauche + 2 petites à droite';
} else if (count === 4) {
layout = 'Grille 2x2';
} else if (count <= 6) {
layout = 'Grille 2x3';
} else if (count <= 9) {
layout = 'Grille 3x3';
} else {
layout = `Grille ${Math.ceil(Math.sqrt(count))}x${Math.ceil(count / Math.ceil(Math.sqrt(count)))}`;
}
preview.innerHTML = `<strong>Disposition :</strong> ${count} interface${count > 1 ? 's' : ''} - ${layout}`;
}
// Valider une adresse IP
function validateIP(ip) {
const pattern = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return pattern.test(ip);
}
// Afficher un message
function showMessage(text, type) {
const messageEl = document.getElementById('message');
messageEl.textContent = text;
messageEl.className = 'message ' + type;
messageEl.style.display = 'block';
setTimeout(() => {
messageEl.style.display = 'none';
}, 3000);
}
// Calculer la disposition
function calculateGridLayout(count) {
if (count === 1) return { cols: 1, rows: 1, special: 'single' };
if (count === 2) return { cols: 2, rows: 1, special: 'two-columns' };
if (count === 3) return { cols: 2, rows: 2, special: 'three-special' };
if (count === 4) return { cols: 2, rows: 2, special: 'grid' };
const cols = Math.ceil(Math.sqrt(count));
const rows = Math.ceil(count / cols);
return { cols, rows, special: 'grid' };
}
// Afficher le dashboard
function showDashboard() {
// Récupérer et valider les IP
const newIPs = [];
for (let i = 0; i < ipAddresses.length; i++) {
const ipInput = document.getElementById(`ip${i}`);
const ip = ipInput ? ipInput.value.trim() : ipAddresses[i];
if (!ip) {
showMessage('Veuillez remplir toutes les adresses IP', 'error');
return;
}
if (!validateIP(ip)) {
showMessage(`L'adresse IP n°${i + 1} n'est pas valide`, 'error');
return;
}
newIPs.push(ip);
}
ipAddresses = newIPs;
// Construire le dashboard
buildDashboard();
// Basculer en mode dashboard
document.body.className = 'dashboard-mode';
currentMode = 'dashboard';
}
// Sauvegarder la configuration dans le fichier actuel
function saveToFile() {
// Récupérer et valider les IP
const newIPs = [];
for (let i = 0; i < ipAddresses.length; i++) {
const ip = document.getElementById(`ip${i}`).value.trim();
if (!ip) {
showMessage('Veuillez remplir toutes les adresses IP', 'error');
return;
}
if (!validateIP(ip)) {
showMessage(`L'adresse IP n°${i + 1} n'est pas valide`, 'error');
return;
}
newIPs.push(ip);
}
// Demander le nom du fichier
let filename = prompt('Nom du fichier à sauvegarder :', 'dashboard.html');
if (!filename) return; // Annulé
// Ajouter .html si pas présent
if (!filename.endsWith('.html')) {
filename += '.html';
}
// Obtenir le HTML actuel
const currentHTML = document.documentElement.outerHTML;
// Remplacer la configuration actuelle
const modifiedHTML = currentHTML.replace(
/let ipAddresses = \[[^\]]*\];/,
`let ipAddresses = ${JSON.stringify(newIPs)};`
);
// Créer et télécharger le fichier
const blob = new Blob([modifiedHTML], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showMessage(`Configuration sauvegardée dans ${filename} !`, 'success');
}
// Construire le dashboard
function buildDashboard() {
const container = document.getElementById('dashboardContainer');
const layout = calculateGridLayout(ipAddresses.length);
// Appliquer les styles de grille
if (layout.special === 'single') {
container.style.gridTemplateColumns = '1fr';
container.style.gridTemplateRows = '1fr';
} else if (layout.special === 'two-columns') {
container.style.gridTemplateColumns = '1fr 1fr';
container.style.gridTemplateRows = '1fr';
} else if (layout.special === 'three-special') {
container.style.gridTemplateColumns = '1fr 1fr';
container.style.gridTemplateRows = '1fr 1fr';
} else {
container.style.gridTemplateColumns = `repeat(${layout.cols}, 1fr)`;
container.style.gridTemplateRows = `repeat(${layout.rows}, 1fr)`;
}
// Créer les conteneurs
container.innerHTML = '';
ipAddresses.forEach((ip, index) => {
const frameDiv = document.createElement('div');
frameDiv.className = 'frame-container';
// Style spécial pour 3 interfaces
if (layout.special === 'three-special' && index === 0) {
frameDiv.style.gridRow = 'span 2';
}
frameDiv.innerHTML = `
<span class="frame-label">${ip}</span>
<div class="zoom-controls">
<button class="zoom-btn" onclick="zoom(${index + 1}, -0.1)">−</button>
<span class="zoom-value" id="zoom${index + 1}">100%</span>
<button class="zoom-btn" onclick="zoom(${index + 1}, 0.1)">+</button>
</div>
<div class="frame-wrapper" id="frame${index + 1}">
<iframe src="http://${ip}"></iframe>
</div>
`;
container.appendChild(frameDiv);
});
// Initialiser les niveaux de zoom
zoomLevels = {};
for (let i = 1; i <= ipAddresses.length; i++) {
zoomLevels[i] = 1;
}
}
// Retourner à la configuration
function showConfig() {
document.body.className = 'config-mode';
currentMode = 'config';
renderIPList();
}
// Fonctions de zoom
function zoom(frameNum, delta) {
zoomLevels[frameNum] = Math.max(0.2, Math.min(2, zoomLevels[frameNum] + delta));
updateZoom(frameNum);
}
function updateZoom(frameNum) {
const frame = document.getElementById(`frame${frameNum}`);
const zoomDisplay = document.getElementById(`zoom${frameNum}`);
const scale = zoomLevels[frameNum];
frame.style.transform = `scale(${scale})`;
frame.style.width = `${100 / scale}%`;
frame.style.height = `${100 / scale}%`;
zoomDisplay.textContent = `${Math.round(scale * 100)}%`;
}
function resetAllZoom() {
for (let i = 1; i <= ipAddresses.length; i++) {
zoomLevels[i] = 1;
updateZoom(i);
}
}
function zoomAll(delta) {
for (let i = 1; i <= ipAddresses.length; i++) {
zoom(i, delta);
}
}
</script>
</body>
</html>
creez un fichier .html ,collez le code enregistrez et lancer avec un navigateur
ESP32Wroom, Triac 40A "BTA40", Source UxIx2, Cumulus 300L 3000W.
Sonde temperature sur radiateur triac mise en route ventilateur a 25°
réactivité 30 seuil -50
2 esp32 pour gestion charge batteries
14 panneaux de 410wcc en autoconso micro-onduleur APS DS3
Suivi sur Domoticz
Sonde temperature sur radiateur triac mise en route ventilateur a 25°
réactivité 30 seuil -50
2 esp32 pour gestion charge batteries
14 panneaux de 410wcc en autoconso micro-onduleur APS DS3
Suivi sur Domoticz