Note de ce sujet :
  • Moyenne : 0 (0 vote(s))
  • 1
  • 2
  • 3
  • 4
  • 5
Petit prog pour esp32
#38
(30-05-2025, 09:11 AM)grostoto a écrit : j'ai essayé de mette mon home assistant (192.168.1.XX:8123) dans la console, mais cela ne fonctionne pas..
je pense qu'il ne prend pas en compte le 8123.

alors homeassistant bloque les connexions iframe ....c est foutu pour HA pour l instant

re edition
pour HA : ajouter dans config.yam 

http:
  cors_allowed_origins:
    - "*"
  use_x_frame_options: false


pour l instant version html

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;
        }
       
        .help-text {
            font-size: 12px;
            color: #666;
            margin-top: 15px;
            padding: 10px;
            background-color: #f0f0f0;
            border-radius: 5px;
            line-height: 1.6;
        }
       
        /* 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;
            max-width: 70%;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
       
        /* 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;
        }
       
        /* Message d'erreur iframe */
        .iframe-error {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #f8d7da;
            color: #721c24;
            padding: 20px;
            border-radius: 8px;
            text-align: center;
            max-width: 80%;
            z-index: 20;
            display: none;
        }
       
        .iframe-error h3 {
            margin: 0 0 10px 0;
            font-size: 16px;
        }
       
        .iframe-error p {
            margin: 5px 0;
            font-size: 14px;
        }
       
        .iframe-error code {
            background: rgba(0,0,0,0.1);
            padding: 2px 5px;
            border-radius: 3px;
            font-size: 12px;
        }
    </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 ci-dessous pour commencer.</p>
            </div>
           
            <div id="ipList" class="ip-list"></div>
           
            <button class="add-btn" onclick="addIP()">+ Ajouter une interface</button>
           
            <div class="help-text">
                <strong>Formats acceptés :</strong><br>
                • IP : 192.168.1.100<br>
                • Domaine : example.com<br>
                • Sous-domaine : app.example.com<br>
                • URL complète : https://example.com/dashboard<br>
                • Port personnalisé : 192.168.1.100:8080 ou example.com:3000
            </div>
           
            <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 adresses - MODIFIÉE AUTOMATIQUEMENT
        let addresses = ['192.168.1.145'];
        let zoomLevels = {};
        let currentMode = 'config';
       
        // Initialisation
        window.onload = function() {
            // Si des adresses sont déjà configurées dans le fichier
            if (addresses.length > 0 && addresses[0] !== '') {
                document.getElementById('welcomeMessage').style.display = 'none';
                // Aller directement au dashboard si configuré
                if (addresses.length > 1 || (addresses.length === 1 && addresses[0] !== '192.168.1.145')) {
                    showDashboard();
                }
            }
           
            renderIPList();
        };
       
        // Afficher la liste des adresses
        function renderIPList() {
            const container = document.getElementById('ipList');
            container.innerHTML = '';
           
            addresses.forEach((address, index) => {
                const div = document.createElement('div');
                div.className = 'ip-group';
                div.innerHTML = `
                    <span class="ip-number">${index + 1}.</span>
                    <input type="text" id="address${index}" value="${address}" placeholder="Ex: 192.168.1.100, example.com, https://app.example.com">
                    ${addresses.length > 1 ? `<button class="remove-btn" onclick="removeIP(${index})">Supprimer</button>` : ''}
                `;
                container.appendChild(div);
            });
           
            updateLayoutPreview();
        }
       
        // Ajouter une adresse
        function addIP() {
            addresses.push('');
            renderIPList();
        }
       
        // Supprimer une adresse
        function removeIP(index) {
            addresses.splice(index, 1);
            renderIPList();
        }
       
        // Mettre à jour l'aperçu
        function updateLayoutPreview() {
            const preview = document.getElementById('layoutPreview');
            const count = addresses.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 et formater une adresse
        function validateAndFormatAddress(address) {
            // Supprimer les espaces
            address = address.trim();
           
            if (!address) {
                return { valid: false, error: "L'adresse ne peut pas être vide" };
            }
           
            // Si c'est déjà une URL complète avec protocole
            if (address.match(/^https?:\/\//)) {
                return { valid: true, formatted: address, display: address };
            }
           
            // Vérifier si c'est une IP valide
            const ipPattern = /^(?:(?: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]?)(?::\d{1,5})?$/;
            if (ipPattern.test(address)) {
                return {
                    valid: true,
                    formatted: `http://${address}`,
                    display: address
                };
            }
           
            // Vérifier si c'est un domaine ou sous-domaine valide
            const domainPattern = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(:\d{1,5})?(\/.*)?$/;
            const localhostPattern = /^localhost(:\d{1,5})?(\/.*)?$/;
           
            if (domainPattern.test(address) || localhostPattern.test(address)) {
                return {
                    valid: true,
                    formatted: `http://${address}`,
                    display: address
                };
            }
           
            // Si c'est juste un nom sans extension (peut-être un hostname local)
            if (/^[a-zA-Z0-9-]+(:\d{1,5})?(\/.*)?$/.test(address)) {
                return {
                    valid: true,
                    formatted: `http://${address}`,
                    display: address
                };
            }
           
            return { valid: false, error: "Format d'adresse non valide" };
        }
       
        // 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 adresses
            const validatedAddresses = [];
            for (let i = 0; i < addresses.length; i++) {
                const addressInput = document.getElementById(`address${i}`);
                const addressValue = addressInput ? addressInput.value.trim() : addresses[i];
               
                const validation = validateAndFormatAddress(addressValue);
                if (!validation.valid) {
                    showMessage(`Adresse n°${i + 1} : ${validation.error}`, 'error');
                    return;
                }
                validatedAddresses.push(validation);
            }
           
            addresses = validatedAddresses.map(v => v.display);
           
            // Construire le dashboard avec les adresses formatées
            buildDashboard(validatedAddresses);
           
            // 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 adresses
            const newAddresses = [];
            for (let i = 0; i < addresses.length; i++) {
                const addressValue = document.getElementById(`address${i}`).value.trim();
               
                const validation = validateAndFormatAddress(addressValue);
                if (!validation.valid) {
                    showMessage(`Adresse n°${i + 1} : ${validation.error}`, 'error');
                    return;
                }
                newAddresses.push(addressValue);
            }
           
            // 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 addresses = \[[^\]]*\];/,
                `let addresses = ${JSON.stringify(newAddresses)};`
            );
           
            // 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(validatedAddresses) {
            const container = document.getElementById('dashboardContainer');
            const layout = calculateGridLayout(validatedAddresses.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 = '';
            validatedAddresses.forEach((addressInfo, 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" title="${addressInfo.display}">${addressInfo.display}</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="${addressInfo.formatted}"
                            sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-modals allow-downloads allow-presentation allow-top-navigation"
                            referrerpolicy="no-referrer"
                            id="iframe${index + 1}"
                            onerror="handleIframeError(${index + 1}, '${addressInfo.display}')"
                        ></iframe>
                    </div>
                    <div class="iframe-error" id="error${index + 1}">
                        <h3>⚠️ Impossible de charger ${addressInfo.display}</h3>
                        <p>Cette interface ne peut pas être affichée dans une iframe.</p>
                        <p><small>Pour Home Assistant, ajoutez dans configuration.yaml :</small></p>
                        <code>http:<br>&nbsp;&nbsp;use_x_frame_options: false</code>
                        <p style="margin-top: 15px;">
                            <a href="${addressInfo.formatted}" target="_blank" style="color: #0056b3;">Ouvrir dans un nouvel onglet →</a>
                        </p>
                    </div>
                `;
               
                container.appendChild(frameDiv);
            });
           
            // Initialiser les niveaux de zoom
            zoomLevels = {};
            for (let i = 1; i <= validatedAddresses.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 <= addresses.length; i++) {
                zoomLevels[i] = 1;
                updateZoom(i);
            }
        }
       
        function zoomAll(delta) {
            for (let i = 1; i <= addresses.length; i++) {
                zoom(i, delta);
            }
        }
       
        // Gestion des erreurs d'iframe
        function handleIframeError(frameNum, address) {
            console.error(`Erreur de chargement pour l'iframe ${frameNum}: ${address}`);
            const errorDiv = document.getElementById(`error${frameNum}`);
            if (errorDiv) {
                errorDiv.style.display = 'block';
            }
        }
       
        // Détection des erreurs de chargement après un délai
        function checkIframeLoading() {
            if (currentMode === 'dashboard') {
                for (let i = 1; i <= addresses.length; i++) {
                    const iframe = document.getElementById(`iframe${i}`);
                    if (iframe) {
                        iframe.addEventListener('load', function() {
                            try {
                                // Tenter d'accéder au document pour vérifier si c'est bloqué
                                const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
                                // Si on arrive ici, l'iframe est chargée correctement
                            } catch (e) {
                                // Erreur de cross-origin, probablement bloqué
                                console.warn(`L'iframe ${i} pourrait être bloquée par des politiques de sécurité`);
                            }
                        });
                    }
                }
            }
        }
       
        // Appeler la vérification après le chargement du dashboard
        setTimeout(checkIframeLoading, 1000);
    </script>
</body>
</html>



j essaye de voir pour esp ....
Répondre


Messages dans ce sujet
Petit prog pour esp32 - par lucky - 27-05-2025, 09:04 PM
RE: Petit prog pour esp32 - par Sgb31 - 28-05-2025, 07:51 AM
RE: Petit prog pour esp32 - par lucky - 28-05-2025, 08:03 AM
RE: Petit prog pour esp32 - par Sgb31 - 28-05-2025, 09:52 AM
RE: Petit prog pour esp32 - par Serge111 - 28-05-2025, 03:09 PM
RE: Petit prog pour esp32 - par grostoto - 28-05-2025, 07:57 PM
RE: Petit prog pour esp32 - par lucky - 28-05-2025, 08:07 PM
RE: Petit prog pour esp32 - par grostoto - 28-05-2025, 09:48 PM
RE: Petit prog pour esp32 - par lucky - 28-05-2025, 10:03 PM
RE: Petit prog pour esp32 - par grostoto - 28-05-2025, 10:07 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 08:21 AM
RE: Petit prog pour esp32 - par Philmaz - 29-05-2025, 05:32 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 06:03 PM
RE: Petit prog pour esp32 - par Philmaz - 29-05-2025, 07:45 PM
RE: Petit prog pour esp32 - par grostoto - 29-05-2025, 06:08 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 06:17 PM
RE: Petit prog pour esp32 - par grostoto - 29-05-2025, 07:49 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 08:14 PM
RE: Petit prog pour esp32 - par grostoto - 29-05-2025, 09:01 PM
RE: Petit prog pour esp32 - par lucky - 29-05-2025, 09:51 PM
RE: Petit prog pour esp32 - par Philmaz - 29-05-2025, 10:32 PM
RE: Petit prog pour esp32 - par grostoto - 29-05-2025, 11:21 PM
RE: Petit prog pour esp32 - par Philmaz - 30-05-2025, 07:26 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 07:41 AM
RE: Petit prog pour esp32 - par grostoto - 30-05-2025, 08:02 AM
RE: Petit prog pour esp32 - par Philmaz - 30-05-2025, 08:19 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 08:26 AM
RE: Petit prog pour esp32 - par Philmaz - 30-05-2025, 08:29 AM
RE: Petit prog pour esp32 - par Philmaz - 30-05-2025, 09:01 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 09:02 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 09:00 AM
RE: Petit prog pour esp32 - par grostoto - 30-05-2025, 09:11 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 03:26 PM
RE: Petit prog pour esp32 - par PhDV61 - 30-05-2025, 09:12 AM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 09:26 AM
RE: Petit prog pour esp32 - par bernard62 - 30-05-2025, 12:12 PM
RE: Petit prog pour esp32 - par glu3 - 30-05-2025, 02:21 PM
RE: Petit prog pour esp32 - par pdunet - 30-05-2025, 02:29 PM
RE: Petit prog pour esp32 - par lucky - 30-05-2025, 05:33 PM
RE: Petit prog pour esp32 - par grostoto - 30-05-2025, 07:50 PM
RE: Petit prog pour esp32 - par 59jag - 30-05-2025, 10:03 PM
RE: Petit prog pour esp32 - par lucky - 31-05-2025, 08:03 AM
RE: Petit prog pour esp32 - par lucky - 31-05-2025, 08:04 AM
RE: Petit prog pour esp32 - par 59jag - 31-05-2025, 09:22 AM
RE: Petit prog pour esp32 - par Sgb31 - 31-05-2025, 09:35 AM
RE: Petit prog pour esp32 - par lucky - 31-05-2025, 12:01 PM
RE: Petit prog pour esp32 - par pdunet - Il y a 21 minutes

Atteindre :


Utilisateur(s) parcourant ce sujet : GPL, 1 visiteur(s)