CODE
MASTER
Bibliothèque Brutale

Le Script Hub

Sélectionne un projet pour voir son tutoriel complet.

CLIQUE POUR CHOISIR TON SCRIPT
NOUVEAU

Pourquoi ce script ?

Suivez avec élégance l'état de la batterie de vos appareils. Ce script trie automatiquement du plus faible au plus chargé, intègre des indicateurs visuels et compteurs statistiques avec une micro-animation de charge électrique, le tout dans une carte Premium Brutaliste.

1. Installer les dépendances

  • HACS (Frontend) : Assurez-vous d'avoir la carte button-card installée.
  • Important : Remplacez les entités sensor.xxx_battery_level par vos entités dans le tableau devices. Pour l'animation de charge, spécifiez un chargingEntity s'il est distinct de la batterie.

2. Le Code Dashboard (YAML)

Ajoutez une carte manuelle "Bouton" (ou Custom: Button-Card) et collez ce code :

ui-lovelace.yaml
type: custom:button-card
entity: sensor.sm_s921b_battery_level
show_name: false
show_icon: false
show_state: false
tap_action:
  action: none
styles:
  card:
    - padding: 16px
    - background: rgba(255, 255, 255, 0.08) !important
    - backdrop-filter: blur(20px) saturate(180%)
    - "-webkit-backdrop-filter": blur(20px) saturate(180%)
    - border-radius: 15px
    - border: 1px solid rgba(255, 255, 255, 0.15)
    - box-shadow: >-
        0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.2),
        inset 0 -1px 0 rgba(0, 0, 0, 0.1)
    - overflow: hidden
    - position: relative
  grid:
    - grid-template-areas: "\"main\""
  custom_fields:
    main:
      - width: 100%
custom_fields:
  main: |
    [[[
      const devices = [
        { entity: 'sensor.sm_s921b_battery_level',                  name: 'Samsung Papa'        },
        { entity: 'sensor.sm_julie_battery_level',                   name: 'Samsung Maman'           },
        { entity: 'sensor.esteban_battery_level',                    name: 'Esteban'            },
        { entity: 'sensor.honor_battery_level',                      name: 'Tablette Honor'     },
        { entity: 'sensor.galaxy_watch6_classic_d4he_battery_level', name: 'Montre Papa'        },
        { entity: 'sensor.galaxy_watch8_fbxh_battery_level',         name: 'Montre Maman'       },
        { entity: 'sensor.bob_batterie',                             name: 'Aspirateur BOB',    chargingEntity: 'vacuum.bob' },
        { entity: 'sensor.dyad_air_2024_batterie',                   name: 'Aspirateur DYAD',   chargingEntity: 'sensor.dyad_air_2024_status' },
        { entity: 'sensor.maison_interrupteur_batterie',             name: 'Télécommande HUE'   },
        { entity: 'sensor.samsung_m2020_series_black_toner_s_n_crum_17091625519', name: 'Toner Imprimante' },
        { entity: 'sensor.detecterur_batterie',                      name: 'Boîte aux lettres'  },
        { entity: 'sensor.detecteur_1_batterie',                     name: 'Détecteur Cuisine'  },
        { entity: 'sensor.detecteur_2_batterie',                     name: 'Détecteur Escalier' },
        { entity: 'sensor.arriere_cour_battery_percentage',          name: 'Caméra Jardin'       },
        { entity: 'sensor.temp_batterie',                            name: 'Thermomètre Bureau'     },
      ];

      const isDeviceCharging = (state, device) => {
        if (!state) return false;
        const pct = parseFloat(state.state) || 0;
        if (device.chargingEntity) {
          const chgState = states[device.chargingEntity];
          if (chgState) {
            const val = String(chgState.state).toLowerCase();
            return (val === 'charging' || val === 'recharging' || val === 'docked') && pct < 100;
          }
          return false;
        }
        const icon = String(state.attributes?.icon ?? '').toLowerCase();
        return icon.includes('charging') && !icon.includes('wireless');
      };

      const getStatusColor = (pct, isCharging) => {
        if (isCharging) return '#3b82f6';
        if (pct <= 20)  return '#ef4444';
        if (pct <= 50)  return '#f59e0b';
        return '#22c55e';
      };

      const data = devices.map(d => {
        const state = states[d.entity];
        const pct = parseFloat(state?.state) || 0;
        const isCharging = isDeviceCharging(state, d);
        return { ...d, pct, isCharging, color: getStatusColor(pct, isCharging) };
      });

      const sorted = [...data].sort((a, b) => a.pct - b.pct);
      const low = data.filter(d => d.pct <= 20 && !d.isCharging).length;
      const charging = data.filter(d => d.isCharging).length;
      const avgPct = Math.round(data.reduce((s, d) => s + d.pct, 0) / data.length);

      const rows = sorted.map(d => {
        const chargingIcon = d.isCharging 
          ? `<span style="font-size:10px;margin-left:4px;color:#3b82f6;animation:chargePulse 1.5s infinite;display:inline-block;">⚡</span>` 
          : '';
        return `
          <div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;">
            <div style="font-size:12px;color:rgba(255,255,255,0.6);width:115px;flex-shrink:0;text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">
              ${d.name}${chargingIcon}
            </div>
            <div style="flex:1;height:8px;background:rgba(255,255,255,0.08);border-radius:99px;overflow:hidden;border:1px solid rgba(255,255,255,0.05);">
              <div style="width:${d.pct}%;height:100%;background:${d.color};border-radius:99px;box-shadow:0 0 6px ${d.color}44;transition:width 0.6s ease;"></div>
            </div>
            <div style="font-size:11px;font-weight:600;color:${d.color};width:35px;flex-shrink:0;text-align:right;">
              ${Math.round(d.pct)}<span style="font-size:9px;opacity:0.6;">%</span>
            </div>
          </div>`;
      }).join('');

      return `
        <div style="font-family:var(--primary-font-family,sans-serif);width:100%;">
          <style>
            @keyframes battFloat { 0%,100% { transform:rotate(-5deg) scale(1); opacity:0.07; } 50% { transform:rotate(-2deg) scale(1.08); opacity:0.12; } }
            @keyframes chargePulse { 0%,100% { opacity:1; transform:scale(1); } 50% { opacity:0.5; transform:scale(1.2); } }
          </style>
          <div style="position:absolute;right:-5px;bottom:-20px;font-size:6rem;opacity:0.07;pointer-events:none;animation:battFloat 6s ease-in-out infinite;">🔋</div>
          <div style="font-size:11px;color:rgba(255,255,255,0.5);margin-bottom:14px;letter-spacing:0.05em;text-transform:uppercase;text-align:center;">🔋 État des équipements</div>
          ${rows}
          <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:16px;">
            <div style="background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);border-radius:10px;padding:10px 5px;text-align:center;">
              <div style="font-size:10px;color:rgba(255,255,255,0.4);margin-bottom:3px;">Moyenne</div>
              <div style="font-size:16px;font-weight:600;color:#fff;">${avgPct}<span style="font-size:10px;opacity:0.5;"> %</span></div>
            </div>
            <div style="background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);border-radius:10px;padding:10px 5px;text-align:center;">
              <div style="font-size:10px;color:rgba(255,255,255,0.4);margin-bottom:3px;">En charge</div>
              <div style="font-size:16px;font-weight:600;color:#3b82f6;">${charging}<span style="font-size:10px;opacity:0.5;color:#fff;"> /15</span></div>
            </div>
            <div style="background:rgba(255,255,255,0.06);border:1px solid ${low > 0 ? 'rgba(239,68,68,0.3)' : 'rgba(255,255,255,0.1)'};border-radius:10px;padding:10px 5px;text-align:center;">
              <div style="font-size:10px;color:rgba(255,255,255,0.4);margin-bottom:3px;">Critique</div>
              <div style="font-size:16px;font-weight:600;color:${low > 0 ? '#ef4444' : '#fff'};">${low}<span style="font-size:10px;opacity:0.5;color:#fff;"> /15</span></div>
            </div>
          </div>
        </div>

3. Aperçu du Rendu (Simulation)

Un rendu dynamique et coloré pour vos batteries (vert > 50%, orange > 20%, rouge critique, bleu en charge).

🔋
🔋 État des équipements
Tel Maman
12%
Montre Papa
65%
Tel Papa
95%
Moyenne
57 %
En charge
1 /15
Critique
1 /15
UN AUTOMATISME REBELLE ?

EXPLORE NOTRE MEGA FAQ

Si ton automatisation ne se déclenche pas, la solution est sûrement parmi nos 800 réponses répertoriées.

VOIR LES SOLUTIONS
Zéro Solitude

La communauté

TiktokTiktok

Tuto Minute

Les erreurs à éviter.

DiscordDiscord

L'Aide Directe

Pose tes questions.

GithubGithub

Fichiers YAML

Retrouve les codes.

X / TwitterX

News Direct

Actu domotique.

-10% CODE
Sonoff

Boutique Officielle

Code : TECHENCLAIR