# \[ConcoursDash\] Hyperviseur SCADA Résidentiel : Pilotage 4000Wc & EDF Tempo ☀️

**URL:** <https://forum.hacf.fr/t/concoursdash-hyperviseur-scada-residentiel-pilotage-4000wc-edf-tempo/78194>\
**Category:** Interface / Lovelace / Thème / Custom Card\
**Tags:** tableau-de-bord-dashboard, concours\
**Created:** [Avril 6, 2026, 1:42 UTC](https://forum.hacf.fr/t/concoursdash-hyperviseur-scada-residentiel-pilotage-4000wc-edf-tempo/78194 "2026-04-06T01:42:06Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Teddy\_Geley](https://forum.hacf.fr/user_avatar/forum.hacf.fr/teddy_geley/32/128301_2.png) [@Teddy\_Geley](https://forum.hacf.fr/u/Teddy_Geley)\
**Post date:** [Avril 6, 2026, 1:42 UTC](https://forum.hacf.fr/t/concoursdash-hyperviseur-scada-residentiel-pilotage-4000wc-edf-tempo/78194/1 "2026-04-06T01:42:06Z")

</div>

**Salut la communauté !** 👋

Moi c'est **Teddy** , technicien de maintenance en robotique industrielle. Pour ce concours, j'ai décidé de sortir de la domotique "gadget" pour entrer dans l'ère de la **supervision SCADA**. Mon objectif ? Transformer ma maison en une véritable petite usine intelligente où chaque watt est traqué, analysé et optimisé ! 🏭💎

* * *

### 🧠 LE CONCEPT : L'IHM (Interface Homme-Machine) Haute Performance

Pas de superflu, ici on parle **d'aide à la décision**. Mon dashboard est structuré selon une règle d'or industrielle : la **sectorisation fonctionnelle** en grille 25/50/25. 📊

- **⬅ Zone 1 : Pilotage Réseau & Finances.** Gestion stricte du contrat **EDF Tempo**. On ne subit plus les jours rouges, on les anticipe ! 💶
- **⏺ Zone 2 : Synoptique de Flux.** La vision globale 360° des flux énergétiques en temps réel. ⚡
- **➡ Zone 3 : Automatismes & Sécurité.** L'intelligence active qui pilote la maison. 🤖🛡

* * *

📸 **Aperçu**

 ![image](https://forum.hacf.fr/uploads/default/original/3X/6/4/640bdab0f9403cd1512621c2199c08f99aa3ac30.jpeg)

* * *

### ⚡ SPÉCIFICATIONS TECHNIQUES (Le "Hard" 🛠)

Pour faire tourner cette bête, l'infrastructure est solide :

- ☀ **Solaire :** 4000 Wc de puissance installée (8 panneaux bifaciaux).
- 🔋 **Stockage :** 2x Anker Solarbank 3 E2700 Pro (Total 5,4 kWh).
- 🔄 **Zéro Injection :** Pilotage fin via Home Assistant pour un délestage dynamique.
- 📟 **Sonde :** Smartmeter intégré pour une lecture instantanée au watt près.
- 🌡 **Thermique :** Gestion multi-zones avec remontée hybride (Température + Hygrométrie) sur chaque tuile.

* * *

### 🔥 LES "KILLER FEATURES" (Ce qui fait la différence 🏆)

🟢 **1. L'Algorithme "Feu Vert" Électroménager 🚥** C'est le cerveau du dashboard ! Basé sur une logique conditionnelle stricte :

- **SI** `SOC Batterie > 90%` **ET** `Production Solaire >= 2000W`
- **ALORS** les boutons Lave-Linge / Sèche-Linge passent au **VERT** et une notification Push est envoyée sur nos deux smartphones ! 📱🚀
- _Résultat :_ Ma femme et moi savons exactement QUAND consommer gratuitement sans jamais ouvrir une application complexe.

💹 **2. Calculateur de Coûts Tempo en Temps Réel 💰** Oubliez les estimations vagues. Mon système calcule dynamiquement le coût de la journée, du mois et de l'année en croisant les index HP/HC avec les couleurs Tempo du jour. C'est précis au centime près !

📱 **3. UI Adaptive & Mobile-First (Responsive 2.0) 🔄** En tant que tech, je suis souvent en mouvement. J'ai injecté des **Media Queries CSS personnalisées** pour que le dashboard s'adapte parfaitement :

- **Sur PC :** Une grille large et imposante pour la salle de contrôle.
- **Sur Mobile :** Une réorganisation automatique en colonne unique, avec suppression des hauteurs fixes pour une ergonomie tactile parfaite. Pas de scroll horizontal, pas de texte coupé ! 📐✨

🌡 **4. Feedback Visuel de Chauffe 🟠** Chaque tuile de température est vivante. Si un radiateur ou le sèche-serviette demande de la puissance, la tuile s'illumine en orange. On voit la consommation avant même de consulter le compteur !

* * *

### 🛠 STACK LOGICIELLE (HACS Power)

Pour les curieux, voici les briques utilisées :

- `layout-card` (Pour la structure chirurgicale)
- `button-card` (Pour le design 100% custom et le JS embarqué)
- `power-flow-card-plus` (Pour l'animation des flux)
- `apexcharts-card` (Pour l'analyse de performance)

📄 **Le Code (YAML)** Voici le code complet du Dashboard. Il est "plug and play" si vous adaptez les entités.

```auto
type: custom:layout-card
layout_type: custom:grid-layout
layout:
  grid-template-columns: 28% 44% 28%
  grid-template-rows: auto
  grid-template-areas: |
    "gauche centre droite"
  mediaquery:
    "(max-width: 1200px)":
      grid-template-columns: 100%
      grid-template-areas: |
        "centre"
        "gauche"
        "droite"
cards:
  - view_layout:
      grid-area: gauche
    type: vertical-stack
    cards:
      - type: weather-forecast
        entity: weather.saint_denis_les_bourg
        name: Saint-Denis-lès-Bourg
        show_forecast: true
        show_current: true
        forecast_type: daily
      - type: custom:stack-in-card
        title: ⚡ BILAN ÉNERGÉTIQUE TEMPO
        keep:
          background: true
        cards:
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                entity: sensor.rte_tempo_couleur_actuelle
                name: Aujourd'hui
                show_label: true
                show_icon: true
                icon: mdi:flash
                label: >-
                  [[[ return 'Jour ' +
                  (states['sensor.rte_tempo_couleur_actuelle']?.state ||
                  'Inconnu'); ]]]
                styles:
                  card:
                    - height: 80px
                    - border-radius: 15px
                    - background-color: |
                        [[[
                          var c = states['sensor.rte_tempo_couleur_actuelle']?.state?.toLowerCase() || '';
                          if (c === 'rouge') return '#c62828 !important';
                          if (c === 'blanc') return '#f5f5f5 !important';
                          return '#1565c0 !important';
                        ]]]
                    - color: >-
                        [[[ return
                        (states['sensor.rte_tempo_couleur_actuelle']?.state?.toLowerCase()
                        === 'blanc') ? 'black' : 'white'; ]]]
                  grid:
                    - grid-template-areas: "\"i n\" \"i l\""
                    - grid-template-columns: 35% 1fr
                  icon:
                    - width: 35px
                  name:
                    - font-weight: bold
                    - font-size: 1.1em
                    - justify-self: start
                  label:
                    - font-size: 1em
                    - opacity: 0.9
                    - justify-self: start
              - type: custom:button-card
                entity: sensor.rte_tempo_prochaine_couleur
                name: Demain
                show_label: true
                show_icon: true
                icon: mdi:calendar-arrow-right
                label: >-
                  [[[ return 'Jour ' +
                  (states['sensor.rte_tempo_prochaine_couleur']?.state ||
                  'Inconnu'); ]]]
                styles:
                  card:
                    - height: 80px
                    - border-radius: 15px
                    - background-color: |
                        [[[
                          var c = states['sensor.rte_tempo_prochaine_couleur']?.state?.toLowerCase() || '';
                          if (c === 'rouge') return '#c62828 !important';
                          if (c === 'blanc') return '#f5f5f5 !important';
                          return '#1565c0 !important';
                        ]]]
                    - color: >-
                        [[[ return
                        (states['sensor.rte_tempo_prochaine_couleur']?.state?.toLowerCase()
                        === 'blanc') ? 'black' : 'white'; ]]]
                  grid:
                    - grid-template-areas: "\"i n\" \"i l\""
                    - grid-template-columns: 35% 1fr
                  icon:
                    - width: 35px
                  name:
                    - font-weight: bold
                    - font-size: 1.1em
                    - justify-self: start
                  label:
                    - font-size: 1em
                    - opacity: 0.9
                    - justify-self: start
          - type: custom:button-card
            entity: sensor.tarif_tempo_periode_actuelle
            show_name: false
            show_label: true
            label: |
              [[[
                var p = states['sensor.tarif_tempo_periode_actuelle'];
                var status = (p && p.state !== 'unknown') ? p.state : "Tempo";
                return "En " + status + " (HP 6h20-22h20 / HC 22h20-6h20)";
              ]]]
            styles:
              card:
                - height: 50px
                - margin: 5px
                - border-radius: 15px
                - background-color: rgba(255, 255, 255, 0.05) !important
              grid:
                - grid-template-areas: "\"i l\""
                - grid-template-columns: auto auto
                - justify-content: center
                - gap: 10px
              icon:
                - color: >-
                    [[[ return
                    (states['sensor.tarif_tempo_periode_actuelle']?.state?.includes('Heures
                    Pleines')) ? '#ffa600' : '#00d4ff'; ]]]
                - width: 22px
              label:
                - font-size: 0.95em
                - font-weight: bold
      - type: custom:stack-in-card
        title: COÛTS DU JOUR
        keep:
          background: true
        cards:
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                name: BLEU
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_journalier_bleu_hp']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_journalier_bleu_hc']?.state) || 0;
                    return "HP " + (hp * 0.1612).toFixed(2) + "€<br>HC " + (hc * 0.1325).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(21, 101, 192, 0.1) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(21, 101, 192, 0.3)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#1565c0"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
              - type: custom:button-card
                name: BLANC
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_journalier_blanc_hp']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_journalier_blanc_hc']?.state) || 0;
                    return "HP " + (hp * 0.1871).toFixed(2) + "€<br>HC " + (hc * 0.1499).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(245, 245, 245, 0.08) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(245, 245, 245, 0.2)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#f5f5f5"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
              - type: custom:button-card
                name: ROUGE
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_journalier_rouge_hp']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_journalier_rouge_hc']?.state) || 0;
                    return "HP " + (hp * 0.7060).toFixed(2) + "€<br>HC " + (hc * 0.1575).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(198, 40, 40, 0.1) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(198, 40, 40, 0.3)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#c62828"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
          - type: custom:button-card
            name: TOTAL JOUR
            triggers_update: all
            icon: mdi:lightning-bolt
            show_label: true
            label: |
              [[[
                var total = (parseFloat(states['sensor.suivi_tempo_journalier_bleu_hp']?.state) * 0.1612 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_journalier_bleu_hc']?.state) * 0.1325 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_journalier_blanc_hp']?.state) * 0.1871 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_journalier_blanc_hc']?.state) * 0.1499 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_journalier_rouge_hp']?.state) * 0.7060 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_journalier_rouge_hc']?.state) * 0.1575 || 0);
                return total.toFixed(2) + " €";
              ]]]
            styles:
              card:
                - height: 45px
                - background-color: rgba(255, 193, 7, 0.1) !important
                - border-radius: 10px
                - margin: 5px
              grid:
                - grid-template-areas: "\"i n l\""
                - grid-template-columns: 30px 1fr auto
              icon:
                - color: "#FFC107"
              name:
                - justify-self: start
                - font-size: 0.8em
                - font-weight: bold
                - opacity: 0.8
                - white-space: nowrap
              label:
                - justify-self: end
                - font-size: 1.1em
                - font-weight: bold
                - color: "#FFC107"
                - padding-right: 15px
      - type: custom:stack-in-card
        title: COÛTS DU MOIS
        keep:
          background: true
        cards:
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                name: BLEU
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_mensuel_bleu_hp']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_mensuel_bleu_hc']?.state) || 0;
                    return "HP " + (hp * 0.1612).toFixed(2) + "€<br>HC " + (hc * 0.1325).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(21, 101, 192, 0.1) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(21, 101, 192, 0.3)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#1565c0"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
              - type: custom:button-card
                name: BLANC
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_mensuel_blanc_hp']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_mensuel_blanc_hc']?.state) || 0;
                    return "HP " + (hp * 0.1871).toFixed(2) + "€<br>HC " + (hc * 0.1499).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(245, 245, 245, 0.08) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(245, 245, 245, 0.2)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#f5f5f5"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
              - type: custom:button-card
                name: ROUGE
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_mensuel_rouge_hp']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_mensuel_rouge_hc']?.state) || 0;
                    return "HP " + (hp * 0.7060).toFixed(2) + "€<br>HC " + (hc * 0.1575).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(198, 40, 40, 0.1) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(198, 40, 40, 0.3)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#c62828"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
          - type: custom:button-card
            name: TOTAL MOIS
            triggers_update: all
            icon: mdi:calendar-month
            show_label: true
            label: |
              [[[
                var total = (parseFloat(states['sensor.suivi_tempo_mensuel_bleu_hp']?.state) * 0.1612 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_mensuel_bleu_hc']?.state) * 0.1325 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_mensuel_blanc_hp']?.state) * 0.1871 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_mensuel_blanc_hc']?.state) * 0.1499 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_mensuel_rouge_hp']?.state) * 0.7060 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_mensuel_rouge_hc']?.state) * 0.1575 || 0);
                return total.toFixed(2) + " €";
              ]]]
            styles:
              card:
                - height: 45px
                - background-color: rgba(33, 150, 243, 0.15) !important
                - border-radius: 10px
                - margin: 5px
              grid:
                - grid-template-areas: "\"i n l\""
                - grid-template-columns: 30px 1fr auto
              icon:
                - color: "#2196F3"
              name:
                - justify-self: start
                - font-size: 0.8em
                - font-weight: bold
                - opacity: 0.8
                - white-space: nowrap
              label:
                - justify-self: end
                - font-size: 1.1em
                - font-weight: bold
                - color: "#2196F3"
                - padding-right: 15px
      - type: custom:stack-in-card
        title: COÛTS DE L'ANNÉE
        keep:
          background: true
        cards:
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                name: BLEU
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_annuel_hp_bleu']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_annuel_hc_bleu']?.state) || 0;
                    return "HP " + (hp * 0.1612).toFixed(2) + "€<br>HC " + (hc * 0.1325).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(21, 101, 192, 0.1) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(21, 101, 192, 0.3)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#1565c0"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
              - type: custom:button-card
                name: BLANC
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_annuel_hp_blanc']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_annuel_hc_blanc']?.state) || 0;
                    return "HP " + (hp * 0.1871).toFixed(2) + "€<br>HC " + (hc * 0.1499).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(245, 245, 245, 0.08) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(245, 245, 245, 0.2)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#f5f5f5"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
              - type: custom:button-card
                name: ROUGE
                triggers_update: all
                show_label: true
                label: |
                  [[[ 
                    var hp = parseFloat(states['sensor.suivi_tempo_annuel_hp_rouge']?.state) || 0;
                    var hc = parseFloat(states['sensor.suivi_tempo_annuel_hc_rouge']?.state) || 0;
                    return "HP " + (hp * 0.7060).toFixed(2) + "€<br>HC " + (hc * 0.1575).toFixed(2) + "€";
                  ]]]
                styles:
                  card:
                    - height: 70px
                    - background-color: rgba(198, 40, 40, 0.1) !important
                    - border-radius: 10px
                    - border: 1px solid rgba(198, 40, 40, 0.3)
                  grid:
                    - grid-template-areas: "\"n\" \"l\""
                    - grid-template-rows: 1fr 1fr
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
                    - color: "#c62828"
                  label:
                    - font-size: 0.85em
                    - line-height: 1.2
                    - text-align: center
          - type: custom:button-card
            name: TOTAL ANNÉE
            triggers_update: all
            icon: mdi:piggy-bank
            show_label: true
            label: |
              [[[
                var total = (parseFloat(states['sensor.suivi_tempo_annuel_hp_bleu']?.state) * 0.1612 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_annuel_hc_bleu']?.state) * 0.1325 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_annuel_hp_blanc']?.state) * 0.1871 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_annuel_hc_blanc']?.state) * 0.1499 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_annuel_hp_rouge']?.state) * 0.7060 || 0) +
                            (parseFloat(states['sensor.suivi_tempo_annuel_hc_rouge']?.state) * 0.1575 || 0);
                return total.toFixed(2) + " €";
              ]]]
            styles:
              card:
                - height: 45px
                - background-color: rgba(76, 175, 80, 0.1) !important
                - border-radius: 10px
                - margin: 5px
              grid:
                - grid-template-areas: "\"i n l\""
                - grid-template-columns: 30px 1fr auto
              icon:
                - color: "#4CAF50"
              name:
                - justify-self: start
                - font-size: 0.8em
                - font-weight: bold
                - opacity: 0.8
                - white-space: nowrap
              label:
                - justify-self: end
                - font-size: 1.1em
                - font-weight: bold
                - color: "#4CAF50"
                - padding-right: 15px
  - view_layout:
      grid-area: centre
    type: vertical-stack
    cards:
      - type: custom:power-flow-card-plus
        title: Flux Énergétique Réel
        entities:
          grid:
            name: Réseau EDF
            display_state: two_way
            entity:
              consumption: sensor.smart_meter_achat_sur_le_reseau
              production: sensor.smart_meter_alimentation_du_reseau
          solar:
            entity: sensor.production_solaire_totale
            name: Solaire
            display_zero_state: true
          battery:
            entity:
              consumption: sensor.batterie_decharge_totale
              production: sensor.batterie_charge_totale
            state_of_charge: sensor.batterie_soc_moyen
            name: Solarbanks
            color_icon: true
          home:
            entity: sensor.smart_meter_achat_sur_le_reseau
            name: Maison
            circle_animation: true
            subtract_individual: true
          individual:
            - entity: sensor.chauffe_eau_puissance
              name: Chauffe-eau
              icon: mdi:water-boiler
              display_zero_state: true
              display_zero: true
              unit_of_measurement: W
              decimals: 2
        w_decimals: 0
        kw_decimals: 1
        display_zero_lines: true
        clickable_entities: true
      - type: custom:button-card
        name: Énergie de la Maison
        triggers_update: all
        styles:
          card:
            - margin-top: 10px
            - height: 260px
            - border-radius: 25px
            - background-color: "#1c1c1e !important"
            - padding: 20px
            - color: white
          grid:
            - grid-template-areas: "\"n\" \"s\" \"b\" \"h\""
            - grid-template-rows: min-content 1fr 1fr 1fr
          name:
            - font-weight: bold
            - font-size: 1.15em
            - margin-bottom: 10px
        extra_styles: >
          .resp-icon { width: 30px; height: 30px; margin-right: 12px; }

          .resp-text { font-size: 1.05em; font-weight: 600; }

          .resp-val { font-size: 1.35em; font-weight: bold; }

          .resp-val-soc { font-size: 1.2em; font-weight: bold; }

          .resp-margin { margin-top: 10px; }

          .resp-border { margin-top: 15px; padding-top: 15px; border-top: 2px
          solid #3a3a3c; }

          .resp-bar { height: 10px; background: #3a3a3c; border-radius: 5px;
          overflow: hidden; }

          /* Mode Téléphone UNIQUEMENT */

          @media (max-width: 1200px) {
            #card { height: auto !important; padding: 15px !important; margin-top: 0px !important; }
            #name { font-size: 1em !important; margin-bottom: 5px !important; }
            .resp-icon { width: 22px; height: 22px; margin-right: 8px; }
            .resp-text { font-size: 0.85em; }
            .resp-val { font-size: 1.1em; }
            .resp-val-soc { font-size: 1em; }
            .resp-margin { margin-top: 8px !important; }
            .resp-border { margin-top: 12px !important; padding-top: 12px !important; }
            .resp-bar { height: 8px !important; }
          }
        custom_fields:
          s: |
            [[[
              var pwr = states['sensor.production_solaire_totale']?.state || 0;
              return `
                <div style="width: 100%; display: flex; justify-content: space-between; align-items: center;">
                  <div style="display: flex; align-items: center;">
                    <ha-icon icon="mdi:solar-power" style="color: #FFD60A;" class="resp-icon"></ha-icon>
                    <span class="resp-text">Production Totale</span>
                  </div>
                  <span style="color: #FFD60A;" class="resp-val">${pwr} W</span>
                </div>
              `
            ]]]
          b: |
            [[[
              var soc = states['sensor.batterie_soc_moyen']?.state || 0;
              var charge = parseFloat(states['sensor.batterie_charge_totale']?.state) || 0;
              var decharge = parseFloat(states['sensor.batterie_decharge_totale']?.state) || 0;
              var pwr_batt = charge > 0 ? charge : -decharge;
              return `
                <div style="width: 100%;" class="resp-margin">
                  <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                    <div style="display: flex; align-items: center;">
                      <ha-icon icon="mdi:battery-high" style="color: #0A84FF;" class="resp-icon"></ha-icon>
                      <span class="resp-text">Stockage Global (${pwr_batt} W)</span>
                    </div>
                    <span style="color: #0A84FF;" class="resp-val-soc">${soc}%</span>
                  </div>
                  <div style="width: 100%;" class="resp-bar">
                    <div style="width: ${soc}%; height: 100%; background: #0A84FF; border-radius: 5px; transition: width 1s;"></div>
                  </div>
                </div>
              `
            ]]]
          h: |
            [[[
              var grid = states['sensor.smart_meter_achat_sur_le_reseau']?.state || 0;
              return `
                <div style="width: 100%; display: flex; justify-content: space-between; align-items: center;" class="resp-border">
                  <div style="display: flex; align-items: center;">
                    <ha-icon icon="mdi:transmission-tower" style="color: #f5f5f5;" class="resp-icon"></ha-icon>
                    <span class="resp-text">Réseau EDF</span>
                  </div>
                  <span style="color: #f5f5f5;" class="resp-val">${grid} W</span>
                </div>
              `
            ]]]
      - type: custom:apexcharts-card
        header:
          show: true
          title: Suivi Production/Consommation
          show_states: true
          colorize_states: true
        graph_span: 24h
        apex_config:
          chart:
            height: 400
          stroke:
            curve: smooth
            dashArray:
              - 0
              - 0
              - 0
              - 5
          legend:
            show: false
          dataLabels:
            enabled: false
        series:
          - entity: sensor.production_solaire_totale
            name: Solaire
            type: area
            color: "#e68a00"
            opacity: 0.3
            stroke_width: 1
            group_by:
              func: avg
              duration: 15m
          - entity: sensor.smart_meter_achat_sur_le_reseau
            name: Réseau (Achat)
            type: area
            color: "#3477a3"
            opacity: 0.3
            stroke_width: 1
            group_by:
              func: avg
              duration: 15m
          - entity: sensor.smart_meter_alimentation_du_reseau
            name: Réseau (Injection)
            type: area
            color: "#3477a3"
            opacity: 0.3
            transform: return x * -1;
            stroke_width: 1
            group_by:
              func: avg
              duration: 15m
          - entity: sensor.consommation_totale_et
            name: Consommation (hors solaire)
            type: line
            color: "#ffffff"
            stroke_width: 1
            group_by:
              func: avg
              duration: 15m
  - view_layout:
      grid-area: droite
    type: vertical-stack
    cards:
      - type: custom:stack-in-card
        title: 🚥 AUTORISATION ÉLECTROMÉNAGER
        keep:
          background: true
        cards:
          - type: horizontal-stack
            cards:
              - type: custom:button-card
                name: Lave-Linge
                icon: mdi:washing-machine
                show_state: false
                triggers_update: all
                styles:
                  card:
                    - height: 70px
                    - border-radius: 10px
                    - margin: 5px
                    - background-color: |
                        [[[
                          var soc = parseFloat(states['sensor.batterie_soc_moyen']?.state) || 0;
                          var prod = parseFloat(states['sensor.production_solaire_totale']?.state) || 0;
                          return (soc > 90 && prod >= 2000) ? 'rgba(76, 175, 80, 0.2) !important' : 'rgba(158, 158, 158, 0.1) !important';
                        ]]]
                  icon:
                    - color: |
                        [[[
                          var soc = parseFloat(states['sensor.batterie_soc_moyen']?.state) || 0;
                          var prod = parseFloat(states['sensor.production_solaire_totale']?.state) || 0;
                          return (soc > 90 && prod >= 2000) ? '#4CAF50' : 'grey';
                        ]]]
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
              - type: custom:button-card
                name: Lave-Vaiss.
                icon: mdi:dishwasher
                show_state: false
                triggers_update: all
                styles:
                  card:
                    - height: 70px
                    - border-radius: 10px
                    - margin: 5px
                    - background-color: |
                        [[[
                          var soc = parseFloat(states['sensor.batterie_soc_moyen']?.state) || 0;
                          var prod = parseFloat(states['sensor.production_solaire_totale']?.state) || 0;
                          return (soc > 90 && prod >= 2000) ? 'rgba(76, 175, 80, 0.2) !important' : 'rgba(158, 158, 158, 0.1) !important';
                        ]]]
                  icon:
                    - color: |
                        [[[
                          var soc = parseFloat(states['sensor.batterie_soc_moyen']?.state) || 0;
                          var prod = parseFloat(states['sensor.production_solaire_totale']?.state) || 0;
                          return (soc > 90 && prod >= 2000) ? '#4CAF50' : 'grey';
                        ]]]
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
              - type: custom:button-card
                name: Sèche-Linge
                icon: mdi:tumble-dryer
                show_state: false
                triggers_update: all
                styles:
                  card:
                    - height: 70px
                    - border-radius: 10px
                    - margin: 5px
                    - background-color: |
                        [[[
                          var soc = parseFloat(states['sensor.batterie_soc_moyen']?.state) || 0;
                          var prod = parseFloat(states['sensor.production_solaire_totale']?.state) || 0;
                          return (soc > 90 && prod >= 2000) ? 'rgba(76, 175, 80, 0.2) !important' : 'rgba(158, 158, 158, 0.1) !important';
                        ]]]
                  icon:
                    - color: |
                        [[[
                          var soc = parseFloat(states['sensor.batterie_soc_moyen']?.state) || 0;
                          var prod = parseFloat(states['sensor.production_solaire_totale']?.state) || 0;
                          return (soc > 90 && prod >= 2000) ? '#4CAF50' : 'grey';
                        ]]]
                  name:
                    - font-size: 0.8em
                    - font-weight: bold
      - type: custom:stack-in-card
        title: 🌡️ CONFORT THERMIQUE
        keep:
          background: true
        cards:
          - type: grid
            columns: 2
            square: false
            cards:
              - type: custom:button-card
                name: Salon
                icon: mdi:thermometer
                show_label: true
                label: |
                  [[[
                    var t = states['sensor.salon_temperature']?.state;
                    var h = states['sensor.salon_humidite']?.state;
                    var temp = t && !isNaN(t) ? parseFloat(t).toFixed(1) : '--';
                    var hum = h && !isNaN(h) ? parseFloat(h).toFixed(0) : '--';
                    return temp + ' °C &nbsp;|&nbsp; 💧 ' + hum + ' %';
                  ]]]
                styles:
                  card:
                    - height: 60px
                    - border-radius: 12px
                    - background-color: >
                        [[[ return
                        (parseFloat(states['sensor.radiateur_salon_demande_instantanee']?.state)
                        > 10) ? 'rgba(255, 152, 0, 0.15) !important' :
                        'rgba(255, 255, 255, 0.05) !important'; ]]]
                  grid:
                    - grid-template-areas: "\"i n\" \"i l\""
                    - grid-template-columns: 40px 1fr
                  icon:
                    - color: >
                        [[[ return
                        (parseFloat(states['sensor.radiateur_salon_demande_instantanee']?.state)
                        > 10) ? '#FF9800' : '#2196F3'; ]]]
                    - width: 24px
                  name:
                    - justify-self: start
                    - font-weight: bold
                    - font-size: 0.9em
                  label:
                    - justify-self: start
                    - font-size: 0.85em
                    - opacity: 0.8
              - type: custom:button-card
                name: Parent
                icon: mdi:thermometer
                show_label: true
                label: |
                  [[[
                    var t = states['sensor.thermometre_chambre_parent_temperature']?.state;
                    var h = states['sensor.thermometre_chambre_parent_humidite']?.state;
                    var temp = t && !isNaN(t) ? parseFloat(t).toFixed(1) : '--';
                    var hum = h && !isNaN(h) ? parseFloat(h).toFixed(0) : '--';
                    return temp + ' °C &nbsp;|&nbsp; 💧 ' + hum + ' %';
                  ]]]
                styles:
                  card:
                    - height: 60px
                    - border-radius: 12px
                    - background-color: >
                        [[[ return
                        (parseFloat(states['sensor.radiateur_parent_demande_instantanee']?.state)
                        > 10) ? 'rgba(255, 152, 0, 0.15) !important' :
                        'rgba(255, 255, 255, 0.05) !important'; ]]]
                  grid:
                    - grid-template-areas: "\"i n\" \"i l\""
                    - grid-template-columns: 40px 1fr
                  icon:
                    - color: >
                        [[[ return
                        (parseFloat(states['sensor.radiateur_parent_demande_instantanee']?.state)
                        > 10) ? '#FF9800' : '#2196F3'; ]]]
                    - width: 24px
                  name:
                    - justify-self: start
                    - font-weight: bold
                    - font-size: 0.9em
                  label:
                    - justify-self: start
                    - font-size: 0.85em
                    - opacity: 0.8
              - type: custom:button-card
                name: Enfants
                icon: mdi:thermometer
                show_label: true
                label: |
                  [[[
                    var t = states['sensor.thermometre_enfants_temperature']?.state;
                    var h = states['sensor.thermometre_enfants_humidite']?.state;
                    var temp = t && !isNaN(t) ? parseFloat(t).toFixed(1) : '--';
                    var hum = h && !isNaN(h) ? parseFloat(h).toFixed(0) : '--';
                    return temp + ' °C &nbsp;|&nbsp; 💧 ' + hum + ' %';
                  ]]]
                styles:
                  card:
                    - height: 60px
                    - border-radius: 12px
                    - background-color: >
                        [[[ return
                        (parseFloat(states['sensor.radiateur_enfant_demande_instantanee']?.state)
                        > 10) ? 'rgba(255, 152, 0, 0.15) !important' :
                        'rgba(255, 255, 255, 0.05) !important'; ]]]
                  grid:
                    - grid-template-areas: "\"i n\" \"i l\""
                    - grid-template-columns: 40px 1fr
                  icon:
                    - color: >
                        [[[ return
                        (parseFloat(states['sensor.radiateur_enfant_demande_instantanee']?.state)
                        > 10) ? '#FF9800' : '#2196F3'; ]]]
                    - width: 24px
                  name:
                    - justify-self: start
                    - font-weight: bold
                    - font-size: 0.9em
                  label:
                    - justify-self: start
                    - font-size: 0.85em
                    - opacity: 0.8
              - type: custom:button-card
                name: Studio
                icon: mdi:thermometer
                show_label: true
                label: |
                  [[[
                    var t = states['sensor.studio_temperature']?.state;
                    var h = states['sensor.studio_humidite']?.state;
                    var temp = t && !isNaN(t) ? parseFloat(t).toFixed(1) : '--';
                    var hum = h && !isNaN(h) ? parseFloat(h).toFixed(0) : '--';
                    return temp + ' °C &nbsp;|&nbsp; 💧 ' + hum + ' %';
                  ]]]
                styles:
                  card:
                    - height: 60px
                    - border-radius: 12px
                    - background-color: |
                        [[[ 
                          var s1 = parseFloat(states['sensor.smart_wi_fi_plug_puissance']?.state) || 0;
                          var s2 = parseFloat(states['sensor.radiateur_bureau_demande_instantanee']?.state) || 0;
                          var s3 = parseFloat(states['sensor.radiateur_studio_demande_instantanee']?.state) || 0;
                          if (s1 > 10 || s2 > 10 || s3 > 10) return 'rgba(255, 152, 0, 0.15) !important';
                          return 'rgba(255, 255, 255, 0.05) !important';
                        ]]]
                  grid:
                    - grid-template-areas: "\"i n\" \"i l\""
                    - grid-template-columns: 40px 1fr
                  icon:
                    - color: |
                        [[[ 
                          var s1 = parseFloat(states['sensor.smart_wi_fi_plug_puissance']?.state) || 0;
                          var s2 = parseFloat(states['sensor.radiateur_bureau_demande_instantanee']?.state) || 0;
                          var s3 = parseFloat(states['sensor.radiateur_studio_demande_instantanee']?.state) || 0;
                          if (s1 > 10 || s2 > 10 || s3 > 10) return '#FF9800';
                          return '#2196F3';
                        ]]]
                    - width: 24px
                  name:
                    - justify-self: start
                    - font-weight: bold
                    - font-size: 0.9em
                  label:
                    - justify-self: start
                    - font-size: 0.85em
                    - opacity: 0.8
      - type: custom:stack-in-card
        title: 🛡️ SÉCURITÉ & ACCÈS
        cards:
          - type: entities
            entities:
              - entity: alarm_control_panel.maison_alarm
                name: Centrale Alarme Ring
              - entity: binary_sensor.porte_d_entree
                name: Porte d'Entrée
                icon: mdi:door
              - entity: binary_sensor.porte_de_derriere
                name: Porte de Derrière
                icon: mdi:door
              - entity: binary_sensor.porte_de_garage
                name: Porte de Garage
                icon: mdi:garage
              - entity: binary_sensor.fenetre_avant
                name: Fenêtre Avant
                icon: mdi:window-closed
              - entity: binary_sensor.fenetre_laterale
                name: Fenêtre Latérale
                icon: mdi:window-closed
              - entity: binary_sensor.detecteur_de_mouvement_d_entree
                name: Mouvement Entrée
                icon: mdi:motion-sensor
              - entity: binary_sensor.detecteur_de_mouvement_garage
                name: Mouvement Garage
                icon: mdi:motion-sensor
              - entity: binary_sensor.detecteur_de_mouvement_studio
                name: Mouvement Studio
                icon: mdi:motion-sensor
      - type: custom:stack-in-card
        title: 🤖 DÉLESTAGE AUTOMATIQUE
        cards:
          - type: entities
            entities:
              - entity: automation.tempo_bascule_des_tarifs
                name: Gestion Tarifs (Auto)
              - entity: automation.chauffe_eau_on_solaire
                name: Auto - Allumage ECS
              - entity: automation.chauffe_eau_off_solaire
                name: Auto - Coupure ECS
              - entity: switch.chauffe_eau_mise_a_jour_automatique
                name: Commutateur ECS
      - type: custom:scheduler-card
        title: 📅 PLANIFICATEUR CHAUFFAGE
        include:
          - climate
          - switch
        display_options:
          primary_info: name
          secondary_info: relative-time

```

**Merci pour la lecture !** J'espère que ce partage inspirera ceux qui veulent passer d'une domotique de confort à une véritable gestion énergétique industrielle.

**Des bisous !** 🏁🔥

---

<div class="post-metadata">

**Author:** ![WarC0zes](https://forum.hacf.fr/user_avatar/forum.hacf.fr/warc0zes/32/137471_2.png) [@WarC0zes](https://forum.hacf.fr/u/WarC0zes)\
**Post date:** [Avril 6, 2026, 2:32 UTC](https://forum.hacf.fr/t/concoursdash-hyperviseur-scada-residentiel-pilotage-4000wc-edf-tempo/78194/2 "2026-04-06T02:32:09Z")

</div>

> [@\[Concours\] Participez au concours HACF : Le Printemps du Dashboard](https://forum.hacf.fr/t/concours-participez-au-concours-hacf-le-printemps-du-dashboard/77867/1):
>
> **Choisissez une carte** de dashboard à présenter : juste **une carte** , pas un dashboard complet **(une participation par membre)**.

Bonjour,  
La règle est juste une carte à présenter et pas une page complète d’un dashboard.

---

<div class="post-metadata">

**Author:** ![Teddy\_Geley](https://forum.hacf.fr/user_avatar/forum.hacf.fr/teddy_geley/32/128301_2.png) [@Teddy\_Geley](https://forum.hacf.fr/u/Teddy_Geley)\
**Post date:** [Avril 6, 2026, 9:10 UTC](https://forum.hacf.fr/t/concoursdash-hyperviseur-scada-residentiel-pilotage-4000wc-edf-tempo/78194/3 "2026-04-06T09:10:56Z")

</div>

Salut **WarC0zes** ,

​Techniquement, c’est bien une **carte unique**. Sous Home Assistant, une carte ne se limite pas à un simple widget monobloc ou une tuile isolée ; c’est avant tout une unité de configuration Lovelace.

​Tout mon système est encapsulé sous une racine unique : type: custom:layout-card. Ce conteneur maître définit l’intelligence, la structure et le responsive. Séparer ces éléments en cartes distinctes n’aurait aucun sens technique, car elles forment un tout indissociable : mon algorithme «&nbsp;Feu Vert&nbsp;» à droite est asservi en temps réel aux données de flux du centre.

​C’est donc une proposition de **Master Card unique** de supervision : un seul bloc YAML complet pour une fonction de pilotage cohérente. 🛠🤖

Au final je suis dans les règles…

---

<div class="post-metadata">

**Author:** ![Nico33](https://forum.hacf.fr/user_avatar/forum.hacf.fr/nico33/32/115652_2.png) [@Nico33](https://forum.hacf.fr/u/Nico33)\
**Post date:** [Avril 8, 2026, 8:57 UTC](https://forum.hacf.fr/t/concoursdash-hyperviseur-scada-residentiel-pilotage-4000wc-edf-tempo/78194/4 "2026-04-08T08:57:40Z")

</div>

Même si peut-être hors concours, merci pour ce partage qui est inspirant. Ca va en aider plus d’un !

---

<div class="post-metadata">

**Author:** ![system](https://forum.hacf.fr/uploads/default/original/1X/f21bfb78c8ffe5e68d8964c0a162197808faff17.png) [@system](https://forum.hacf.fr/u/system)\
**Post date:** [Mai 6, 2026, 1:43 UTC](https://forum.hacf.fr/t/concoursdash-hyperviseur-scada-residentiel-pilotage-4000wc-edf-tempo/78194/5 "2026-05-06T01:43:01Z")

</div>

Ce sujet a été automatiquement fermé après 30 jours. Aucune réponse n’est permise dorénavant.
