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
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%ETProduction 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.
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 | 💧 ' + 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 | 💧 ' + 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 | 💧 ' + 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 | 💧 ' + 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 ! ![]()
![]()
