{"id":269,"date":"2026-03-06T14:47:16","date_gmt":"2026-03-06T17:47:16","guid":{"rendered":"https:\/\/www.bernabauer.com\/blog\/?p=269"},"modified":"2026-03-06T14:47:16","modified_gmt":"2026-03-06T17:47:16","slug":"cards-para-disjuntores-e-falta-de-luz-no-home-assistant","status":"publish","type":"post","link":"https:\/\/www.bernabauer.com\/blog\/cards-para-disjuntores-e-falta-de-luz-no-home-assistant\/","title":{"rendered":"Cards para disjuntores e falta de luz no Home Assistant"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Quando voc\u00ea come\u00e7a a colocar&nbsp;<strong>disjuntores inteligentes<\/strong>&nbsp;na rotina da casa, a automa\u00e7\u00e3o deixa de ser \u201clegal\u201d e passa a ser&nbsp;<strong>infraestrutura<\/strong>. E infraestrutura precisa de duas coisas:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Controle r\u00e1pido<\/strong>&nbsp;(ligar\/desligar, entender status, ver pot\u00eancia)<\/li>\n\n\n\n<li><strong>Confian\u00e7a visual<\/strong>&nbsp;(saber em um olhar se est\u00e1 tudo normal ou se tem algo errado)<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Meu problema era simples: eu tinha&nbsp;<strong>v\u00e1rios disjuntores<\/strong>&nbsp;e precisava de uma p\u00e1gina \u00fanica no Home Assistant onde eu conseguisse:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ver&nbsp;<strong>todos<\/strong>&nbsp;os disjuntores na mesma tela<\/li>\n\n\n\n<li>Saber imediatamente se cada um est\u00e1&nbsp;<strong>OK \/ com problema<\/strong><\/li>\n\n\n\n<li>Ter o&nbsp;<strong>estado operacional<\/strong>&nbsp;(on\/standby\/off) e o&nbsp;<strong>controle de energia<\/strong><\/li>\n\n\n\n<li>Acompanhar&nbsp;<strong>tend\u00eancias<\/strong>&nbsp;(pot\u00eancia ao longo das \u00faltimas horas)<\/li>\n\n\n\n<li>E, principalmente, ter um card separado que mostrasse&nbsp;<strong>quando faltou luz<\/strong>, quantas vezes e por quanto tempo<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">No fim, eu cheguei em dois cards principais:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>um&nbsp;<strong>card de \u201ccontrole do disjuntor\u201d<\/strong>&nbsp;(status + power + m\u00e9tricas + gr\u00e1fico)<\/li>\n\n\n\n<li>um&nbsp;<strong>card de \u201cestat\u00edsticas de queda de energia\u201d<\/strong>&nbsp;(Today\/MTD\/YTD\/Lifetime com contagem e tempo)<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">O detalhe interessante \u00e9 que esse resultado veio de uma mistura de&nbsp;<strong>ChatGPT Pro<\/strong>&nbsp;com&nbsp;<strong>muita lapida\u00e7\u00e3o manual<\/strong>&nbsp;no YAML\u2014porque o \u201cjeito certo\u201d \u00e9 aquele que se encaixa no seu uso real.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Card 1 \u2014 O \u201ccockpit\u201d do disjuntor: controle + leitura r\u00e1pida<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O primeiro card virou meu \u201cpainel de comando\u201d por disjuntor.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">O que ele mostra (sem polui\u00e7\u00e3o visual)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Nome do disjuntor<\/strong>\u00a0(ex.: D2)<\/li>\n\n\n\n<li><strong>Status<\/strong>\u00a0(um pill \u201con\/off\/unknown\u201d com cor)<\/li>\n\n\n\n<li><strong>Bot\u00e3o power<\/strong>\u00a0(chip de toggle)<\/li>\n\n\n\n<li><strong>Um ret\u00e2ngulo de sa\u00fade (OK\/PROBLEM)<\/strong>\u00a0baseado em um\u00a0<code>binary_sensor<\/code><\/li>\n\n\n\n<li><strong>Total Power<\/strong>\u00a0em destaque<\/li>\n\n\n\n<li><strong>Voltage \/ Current \/ Temperature<\/strong>\u00a0como \u201cresumo de instrumenta\u00e7\u00e3o\u201d<\/li>\n\n\n\n<li>Um\u00a0<strong>mini gr\u00e1fico<\/strong>\u00a0ao fundo com o hist\u00f3rico recente de pot\u00eancia<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A base t\u00e9cnica aqui \u00e9 a combina\u00e7\u00e3o de:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>custom:button-card<\/code>\u00a0(para grid, custom_fields e layout livre)<\/li>\n\n\n\n<li><code>mini-graph-card<\/code>\u00a0(para o hist\u00f3rico compacto dentro do card)<\/li>\n\n\n\n<li><code>mushroom-chips-card<\/code>\u00a0(toggle bonito e consistente)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Por que isso funciona bem na pr\u00e1tica?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Porque ele te d\u00e1 tr\u00eas n\u00edveis de informa\u00e7\u00e3o no mesmo componente:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>N\u00edvel 1 (instante):<\/strong>\u00a0\u201ct\u00e1 tudo OK?\u201d (pill verde\/vermelho)<\/li>\n\n\n\n<li><strong>N\u00edvel 2 (controle):<\/strong>\u00a0\u201cposso desligar\/ligar agora?\u201d (chip power)<\/li>\n\n\n\n<li><strong>N\u00edvel 3 (diagn\u00f3stico):<\/strong>\u00a0pot\u00eancia e instrumenta\u00e7\u00e3o + tend\u00eancia (gr\u00e1fico)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Voc\u00ea n\u00e3o precisa entrar em \u201cmais detalhes\u201d para tomar as a\u00e7\u00f5es comuns.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">C\u00f3digo Card Largo<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"472\" height=\"494\" src=\"https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.36.49.png\" alt=\"\" class=\"wp-image-271\" srcset=\"https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.36.49.png 472w, https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.36.49-287x300.png 287w\" sizes=\"auto, (max-width: 472px) 100vw, 472px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>type: custom:button-card\nshow_icon: false\nshow_name: true\nname: D2\nshow_state: false\nstyles:\n  name:\n    - font-weight: 200\n    - font-size: 22px\n    - justify-self: start\n    - align-self: start\n  card:\n    - padding: 16px\n    - border-radius: 18px\n    - overflow: visible\n  grid:\n    - grid-template-areas: |\n        \"n status switch\"\n        \"problem problem problem\"\n        \"power power power\"\n        \"volt cur temp\"\n    - grid-template-columns: 1fr 1fr 1fr\n    - row-gap: 12px\n    - column-gap: 12px\n    - align-items: center\n    - overflow: visible\n  custom_fields:\n    graph:\n      - position: absolute\n      - width: 100%\n      - left: 0\n      - bottom: 0\n    problem:\n      - justify-self: stretch\n      - align-self: center\n      - overflow: visible\n    status:\n      - justify-self: stretch\n      - align-self: center\n      - overflow: visible\n    switch:\n      - justify-self: end\n      - align-self: center\n    power:\n      - justify-self: stretch\n      - align-self: center\n      - overflow: visible\ncustom_fields:\n  graph:\n    card:\n      type: custom:mini-graph-card\n      entities:\n        - entity: sensor.d2_test_power\n          color: \"#a6cf9d\"\n      line_width: 0\n      height: 100\n      hours_to_show: 12\n      show:\n        icon: false\n        name: false\n        state: false\n        legend: false\n        labels: false\n      card_mod:\n        style: |\n          ha-card { border-style: none; background: none; }\n  status:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: switch.d2_test\n      tap_action:\n        action: toggle\n      hold_action:\n        action: more-info\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'switch.d2_test']?.state;\n          const val = (!st || st === 'unavailable' || st === 'unknown')\n            ? 'unknown'\n            : (st === 'on' ? 'on' : 'off');\n\n          const bg =\n            val === 'off' ? '#f57c00' :\n            val === 'on' ? '#2e7d32' :\n            'rgba(255,255,255,0.10)';\n\n          const fg =\n            val === 'off' ? 'black' :\n            val === 'on' ? 'white' :\n            'var(--primary-text-color)';\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:12px; opacity:0.85;\">Status&lt;\/div>\n              &lt;div style=\"\n                font-size:15px;\n                display:inline-block;\n                margin-top:4px;\n                padding:2px 8px;\n                border-radius:8px;\n                font-weight:600;\n                background:${bg};\n                color:${fg};\n              \">\n                ${val}\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      styles:\n        card:\n          - border: 0px\n          - border-radius: 10px\n          - padding: 6px\n          - background: none\n          - overflow: visible\n          - width: max-content\n          - justify-self: start\n          - align-self: start\n        name:\n          - text-align: center\n          - font-weight: 600\n          - overflow: visible\n  problem:\n    card:\n      type: custom:button-card\n      entity: binary_sensor.d2_test_problem\n      show_icon: false\n      show_name: false\n      show_state: false\n      styles:\n        card:\n          - border: 0\n          - background: none\n          - padding: 0\n          - overflow: visible\n        grid:\n          - grid-template-areas: \"\\\"bar\\\"\"\n          - grid-template-columns: 1fr\n          - justify-items: stretch\n          - align-items: stretch\n        custom_fields:\n          bar:\n            - width: 100%\n            - justify-self: stretch\n            - align-self: stretch\n      custom_fields:\n        bar: |\n          &#91;&#91;&#91;\n            const stRaw = states&#91;'binary_sensor.d2_test_problem']?.state;\n            const badState = (!stRaw || stRaw === 'unavailable' || stRaw === 'unknown');\n\n            \/\/ OFF means OK\n            const isOk = (!badState &amp;&amp; stRaw === 'off');\n            const label = badState ? String(stRaw || 'unknown').toUpperCase() : (isOk ? 'OK' : 'PROBLEM');\n\n            const bg = badState\n              ? 'rgba(0,0,0,0.06)'\n              : isOk\n                ? 'rgba(46, 125, 50, 0.18)'\n                : 'rgba(211, 47, 47, 0.18)';\n\n            const fg = badState\n              ? 'rgba(0,0,0,0.45)'\n              : isOk\n                ? '#2e7d32'\n                : '#d32f2f';\n\n            return `\n              &lt;div style=\"\n                width:100%;\n                display:block;\n                box-sizing:border-box;\n                text-align:center;\n                padding: 4px 10px;\n                font-weight: 800;\n                font-size: 13px;\n                line-height: 1;\n                background: ${bg};\n                color: ${fg};\n                letter-spacing: 0.3px;\n                text-transform: uppercase;\n              \">\n                ${label}\n              &lt;\/div>\n            `;\n          ]]]\n  switch:\n    card:\n      type: custom:mushroom-chips-card\n      alignment: end\n      chips:\n        - type: template\n          entity: switch.d2_test\n          icon: mdi:power\n          icon_color: >\n            {% if is_state(config.entity, 'on') %} white {% else %}\n            var(--secondary-text-color) {% endif %}\n          tap_action:\n            action: toggle\n          hold_action:\n            action: more-info\n          card_mod:\n            style: |\n              :host {\n                --chip-background: {{ 'var(--primary-color)' if is_state(config.entity,'on') else 'rgba(255,255,255,0.06)' }};\n              }\n              ha-card {\n                --chip-height: 46px;\n                --chip-font-size: 16px;\n                --mdc-icon-size: 24px;\n                border-radius: 28px !important;\n              }\n  power:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: sensor.d2_test_power\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'sensor.d2_test_power']?.state;\n          const bad = (!st || st === 'unavailable' || st === 'unknown');\n          const p = bad ? null : parseFloat(String(st).replace(',', '.'));\n          const val = (p === null || Number.isNaN(p)) ? 'N\/A' : p.toFixed(1);\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:13px; color: var(--secondary-text-color); margin-bottom:6px;\">\n                Total Power\n              &lt;\/div>\n              &lt;div style=\"font-size:32px; font-weight:500; letter-spacing:-0.6px; line-height:1;\">\n                ${val} &lt;span style=\"font-size:34px; font-weight:700; opacity:0.95;\">W&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      tap_action:\n        action: more-info\n      styles:\n        card:\n          - border: 0px\n          - background: none\n          - padding: 4px 0 0 0\n          - overflow: visible\n  volt:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: sensor.d2_test_voltage\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'sensor.d2_test_voltage']?.state;\n          const bad = (!st || st === 'unavailable' || st === 'unknown');\n          const v = bad ? null : parseFloat(String(st).replace(',', '.'));\n          const val = (v === null || Number.isNaN(v)) ? '\u2014' : String(Math.round(v));\n\n          let col = 'var(--secondary-text-color)';\n          if (!bad &amp;&amp; v !== null &amp;&amp; Number.isFinite(v)) {\n            if (v >= 110 &amp;&amp; v &lt;= 130) col = '#2e7d32';\n            else if ((v > 130 &amp;&amp; v &lt;= 135) || (v >= 100 &amp;&amp; v &lt; 110)) col = '#f57c00';\n            else col = '#d32f2f';\n          }\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:11px; color: var(--secondary-text-color); margin-bottom:3px;\">\n                Voltage\n              &lt;\/div>\n              &lt;div style=\"font-size:16px; font-weight:500; line-height:1; color:${col};\">\n                ${val}\n                &lt;span style=\"font-size:11px; font-weight:300; color: var(--secondary-text-color);\">V&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      tap_action:\n        action: more-info\n      styles:\n        card:\n          - border: 0px\n          - background: none\n          - padding: 0px\n          - overflow: visible\n  cur:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: sensor.d2_test_current\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'sensor.d2_test_current']?.state;\n          const bad = (!st || st === 'unavailable' || st === 'unknown');\n          const a = bad ? null : parseFloat(String(st).replace(',', '.'));\n          const val = (a === null || Number.isNaN(a)) ? '\u2014' : a.toFixed(2);\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:11px; color: var(--secondary-text-color); margin-bottom:3px;\">\n                Current\n              &lt;\/div>\n              &lt;div style=\"font-size:16px; font-weight:500; line-height:1;\">\n                ${val}\n                &lt;span style=\"font-size:11px; font-weight:300; color: var(--secondary-text-color);\">A&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      tap_action:\n        action: more-info\n      styles:\n        card:\n          - border: 0px\n          - background: none\n          - padding: 0px\n          - overflow: visible\n  temp:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: sensor.d2_test_temperature\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'sensor.d2_test_temperature']?.state;\n          const bad = (!st || st === 'unavailable' || st === 'unknown');\n          const t = bad ? null : parseFloat(String(st).replace(',', '.'));\n          const val = (t === null || Number.isNaN(t)) ? '\u2014' : String(Math.round(t));\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:11px; color: var(--secondary-text-color); margin-bottom:3px;\">\n                Temp.\n              &lt;\/div>\n              &lt;div style=\"font-size:16px; font-weight:500; line-height:1;\">\n                ${val}\n                &lt;span style=\"font-size:11px; font-weight:300; color: var(--secondary-text-color);\">\u00b0C&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      tap_action:\n        action: more-info\n      styles:\n        card:\n          - border: 0px\n          - background: none\n          - padding: 0px\n          - overflow: visible\ngrid_options:\n  columns: 6\n  rows: auto\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">C\u00f3digo Card Fino<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"322\" height=\"860\" src=\"https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.37.15.png\" alt=\"\" class=\"wp-image-270\" srcset=\"https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.37.15.png 322w, https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.37.15-112x300.png 112w\" sizes=\"auto, (max-width: 322px) 100vw, 322px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>type: custom:button-card\nshow_icon: false\nshow_name: true\nname: D2\nshow_state: false\nstyles:\n  name:\n    - font-weight: 200\n    - font-size: 22px\n  card:\n    - padding: 16px\n    - border-radius: 18px\n    - overflow: visible\n    - justify-self: stretch\n    - align-self: center\n    - overflow: visible\n  grid:\n    - grid-template-areas: |\n        \"n\"\n        \"status\"\n        \"switch\"\n        \"problem\"\n        \"power\"\n        \"volt \"\n        \"cur\"\n        \"temp\"\n    - grid-template-columns: 1fr\n    - row-gap: 12px\n    - align-items: center\n    - overflow: visible\n  custom_fields:\n    graph:\n      - position: absolute\n      - width: 100%\n      - left: 0\n      - bottom: 0\ncustom_fields:\n  graph:\n    card:\n      type: custom:mini-graph-card\n      entities:\n        - entity: sensor.d2_test_power\n          color: \"#a6cf9d\"\n      line_width: 0\n      height: 100\n      hours_to_show: 12\n      show:\n        icon: false\n        name: false\n        state: false\n        legend: false\n        labels: false\n      card_mod:\n        style: |\n          ha-card { border-style: none; background: none; }\n  status:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: switch.d2_test\n      tap_action:\n        action: toggle\n      hold_action:\n        action: more-info\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'switch.d2_test']?.state;\n          const val = (!st || st === 'unavailable' || st === 'unknown')\n            ? 'unknown'\n            : (st === 'on' ? 'on' : 'off');\n\n          const bg =\n            val === 'off' ? '#f57c00' :\n            val === 'on' ? '#2e7d32' :\n            'rgba(255,255,255,0.10)';\n\n          const fg =\n            val === 'off' ? 'black' :\n            val === 'on' ? 'white' :\n            'var(--primary-text-color)';\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:12px; opacity:0.85;\">Status&lt;\/div>\n              &lt;div style=\"\n                font-size:15px;\n                display:inline-block;\n                margin-top:4px;\n                padding:2px 8px;\n                border-radius:8px;\n                font-weight:600;\n                background:${bg};\n                color:${fg};\n              \">\n                ${val}\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      styles:\n        card:\n          - border: 0px\n          - justify-self: start\n          - align-self: start\n        name:\n          - text-align: center\n          - font-weight: 600\n          - overflow: visible\n  problem:\n    card:\n      type: custom:button-card\n      entity: binary_sensor.d2_test_problem\n      show_icon: false\n      show_name: false\n      show_state: false\n      styles:\n        card:\n          - border: 0\n          - background: none\n          - padding: 0\n          - overflow: visible\n        grid:\n          - grid-template-areas: \"\\\"bar\\\"\"\n          - grid-template-columns: 1fr\n          - justify-items: stretch\n          - align-items: stretch\n        custom_fields:\n          bar:\n            - width: 100%\n            - justify-self: stretch\n            - align-self: stretch\n      custom_fields:\n        bar: |\n          &#91;&#91;&#91;\n            const stRaw = states&#91;'binary_sensor.d2_test_problem']?.state;\n            const badState = (!stRaw || stRaw === 'unavailable' || stRaw === 'unknown');\n\n            \/\/ OFF means OK\n            const isOk = (!badState &amp;&amp; stRaw === 'off');\n            const label = badState ? String(stRaw || 'unknown').toUpperCase() : (isOk ? 'OK' : 'PROBLEM');\n\n            const bg = badState\n              ? 'rgba(0,0,0,0.06)'\n              : isOk\n                ? 'rgba(46, 125, 50, 0.18)'\n                : 'rgba(211, 47, 47, 0.18)';\n\n            const fg = badState\n              ? 'rgba(0,0,0,0.45)'\n              : isOk\n                ? '#2e7d32'\n                : '#d32f2f';\n\n            return `\n              &lt;div style=\"\n                width:100%;\n                display:block;\n                box-sizing:border-box;\n                text-align:center;\n                padding: 4px 10px;\n                font-weight: 800;\n                font-size: 13px;\n                line-height: 1;\n                background: ${bg};\n                color: ${fg};\n                letter-spacing: 0.3px;\n                text-transform: uppercase;\n              \">\n                ${label}\n              &lt;\/div>\n            `;\n          ]]]\n  switch:\n    card:\n      type: custom:mushroom-chips-card\n      alignment: center\n      chips:\n        - type: template\n          entity: switch.d2_test\n          icon: mdi:power\n          icon_color: >\n            {% if is_state(config.entity, 'on') %} white {% else %}\n            var(--secondary-text-color) {% endif %}\n          tap_action:\n            action: toggle\n          hold_action:\n            action: more-info\n          card_mod:\n            style: |\n              :host {\n                --chip-background: {{ 'var(--primary-color)' if is_state(config.entity,'on') else 'rgba(255,255,255,0.06)' }};\n              }\n              ha-card {\n                --chip-height: 46px;\n                --chip-font-size: 16px;\n                --mdc-icon-size: 24px;\n                border-radius: 28px !important;\n              }\n  power:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: sensor.d2_test_power\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'sensor.d2_test_power']?.state;\n          const bad = (!st || st === 'unavailable' || st === 'unknown');\n          const p = bad ? null : parseFloat(String(st).replace(',', '.'));\n          const val = (p === null || Number.isNaN(p)) ? 'N\/A' : p.toFixed(1);\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:13px; color: var(--secondary-text-color); margin-bottom:6px;\">\n                Total Power\n              &lt;\/div>\n              &lt;div style=\"font-size:32px; font-weight:500; letter-spacing:-0.6px; line-height:1;\">\n                ${val} &lt;span style=\"font-size:34px; font-weight:700; opacity:0.95;\">W&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      tap_action:\n        action: more-info\n      styles:\n        card:\n          - border: 0px\n          - background: none\n          - padding: 4px 0 0 0\n          - overflow: visible\n  volt:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: sensor.d2_test_voltage\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'sensor.d2_test_voltage']?.state;\n          const bad = (!st || st === 'unavailable' || st === 'unknown');\n          const v = bad ? null : parseFloat(String(st).replace(',', '.'));\n          const val = (v === null || Number.isNaN(v)) ? '\u2014' : String(Math.round(v));\n\n          let col = 'var(--secondary-text-color)';\n          if (!bad &amp;&amp; v !== null &amp;&amp; Number.isFinite(v)) {\n            if (v >= 110 &amp;&amp; v &lt;= 130) col = '#2e7d32';\n            else if ((v > 130 &amp;&amp; v &lt;= 135) || (v >= 100 &amp;&amp; v &lt; 110)) col = '#f57c00';\n            else col = '#d32f2f';\n          }\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:11px; color: var(--secondary-text-color); margin-bottom:3px;\">\n                Voltage\n              &lt;\/div>\n              &lt;div style=\"font-size:16px; font-weight:500; line-height:1; color:${col};\">\n                ${val}\n                &lt;span style=\"font-size:11px; font-weight:300; color: var(--secondary-text-color);\">V&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      tap_action:\n        action: more-info\n      styles:\n        card:\n          - border: 0px\n          - background: none\n          - padding: 0px\n          - overflow: visible\n  cur:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: sensor.d2_test_current\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'sensor.d2_test_current']?.state;\n          const bad = (!st || st === 'unavailable' || st === 'unknown');\n          const a = bad ? null : parseFloat(String(st).replace(',', '.'));\n          const val = (a === null || Number.isNaN(a)) ? '\u2014' : a.toFixed(2);\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:11px; color: var(--secondary-text-color); margin-bottom:3px;\">\n                Current\n              &lt;\/div>\n              &lt;div style=\"font-size:16px; font-weight:500; line-height:1;\">\n                ${val}\n                &lt;span style=\"font-size:11px; font-weight:300; color: var(--secondary-text-color);\">A&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      tap_action:\n        action: more-info\n      styles:\n        card:\n          - border: 0px\n          - background: none\n          - padding: 0px\n          - overflow: visible\n  temp:\n    card:\n      type: custom:button-card\n      show_icon: false\n      show_name: true\n      show_state: false\n      entity: sensor.d2_test_temperature\n      name: |\n        &#91;&#91;&#91;\n          const st = states&#91;'sensor.d2_test_temperature']?.state;\n          const bad = (!st || st === 'unavailable' || st === 'unknown');\n          const t = bad ? null : parseFloat(String(st).replace(',', '.'));\n          const val = (t === null || Number.isNaN(t)) ? '\u2014' : String(Math.round(t));\n\n          return `\n            &lt;div style=\"text-align:center;\">\n              &lt;div style=\"font-size:11px; color: var(--secondary-text-color); margin-bottom:3px;\">\n                Temp.\n              &lt;\/div>\n              &lt;div style=\"font-size:16px; font-weight:500; line-height:1;\">\n                ${val}\n                &lt;span style=\"font-size:11px; font-weight:300; color: var(--secondary-text-color);\">\u00b0C&lt;\/span>\n              &lt;\/div>\n            &lt;\/div>\n          `;\n        ]]]\n      tap_action:\n        action: more-info\n      styles:\n        card:\n          - border: 0px\n          - background: none\n          - padding: 0px\n          - overflow: visible\ngrid_options:\n  columns: 4\n  rows: auto\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Card 2 \u2014 Outage stats: quantas vezes faltou luz e por quanto tempo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O segundo card nasceu de uma dor bem real:<br>\u00e0s vezes a energia \u201csome e volta\u201d r\u00e1pido, e voc\u00ea s\u00f3 percebe depois quando algum equipamento reinicia ou um log acusa falha.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ent\u00e3o eu quis um card que respondesse, de forma objetiva:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Faltou luz\u00a0<strong>hoje<\/strong>? Quantas vezes? Quanto tempo?<\/li>\n\n\n\n<li>E no\u00a0<strong>m\u00eas<\/strong>, no\u00a0<strong>ano<\/strong>, e no\u00a0<strong>acumulado total<\/strong>?<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A ideia foi: detectar \u201cqueda\u201d com base na&nbsp;<strong>voltagem<\/strong>&nbsp;(A\/B\/C) e gerar sensores de:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>count<\/code>\u00a0(quantas ocorr\u00eancias)<\/li>\n\n\n\n<li><code>time<\/code>\u00a0(dura\u00e7\u00e3o total)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Depois, no card, exibir em colunas \u201cToday \/ MTD \/ YTD \/ Lifetime\u201d com&nbsp;<strong>Count<\/strong>&nbsp;e&nbsp;<strong>Time<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esse card resolve o que eu chamo de \u201ctelemetria da realidade\u201d: a casa est\u00e1 est\u00e1vel mesmo quando voc\u00ea n\u00e3o est\u00e1 olhando?<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"930\" height=\"414\" src=\"https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.44.41.png\" alt=\"\" class=\"wp-image-272\" srcset=\"https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.44.41.png 930w, https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.44.41-300x134.png 300w, https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.44.41-768x342.png 768w\" sizes=\"auto, (max-width: 930px) 100vw, 930px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>type: custom:button-card\nentity: binary_sensor.d01_power_outage\nshow_icon: false\nshow_name: false\nshow_state: false\ntap_action:\n  action: none\nhold_action:\n  action: more-info\n  entity: sensor.d01_voltage_a\nstyles:\n  card:\n    - border-radius: 16px\n    - padding: 12px\n  grid:\n    - grid-template-areas: \"\\\"left right\\\"\"\n    - grid-template-columns: 1fr 1.35fr\n    - column-gap: 12px\n    - align-items: start\ncustom_fields:\n  left: |\n    &#91;&#91;&#91;\n      const st = entity?.state;\n      const outage = (st === 'on');\n      const ok = (st === 'off');\n\n      const icon = outage ? 'mdi:flash-off' : 'mdi:flash';\n      const iconColor = outage ? '#d32f2f' : ok ? '#2e7d32' : 'rgba(0,0,0,0.35)';\n      const bg = outage ? 'rgba(211, 47, 47, 0.18)' : ok ? 'rgba(46, 125, 50, 0.18)' : 'rgba(0,0,0,0.08)';\n      const fg = outage ? '#d32f2f' : ok ? '#2e7d32' : 'rgba(0,0,0,0.45)';\n      const label = outage ? 'Outage' : ok ? 'Power OK' : 'Unknown';\n\n      return `\n        &lt;div\n          onclick=\"this.dispatchEvent(new CustomEvent('hass-more-info', { composed: true, bubbles: true, detail: { entityId: 'binary_sensor.d01_power_outage' } }))\"\n          style=\"padding-top:10px; display:flex; flex-direction:column; gap:8px; align-items:center; cursor:pointer;\"\n        >\n          &lt;ha-icon icon=\"${icon}\" style=\"width:28px; height:28px; color:${iconColor};\">&lt;\/ha-icon>\n          &lt;div style=\"padding-top:10px; font-size:18px; font-weight:800; line-height:1.1;\">D01 Power&lt;\/div>\n          &lt;span style=\"\n            display:inline-block; padding:4px 10px; border-radius:10px;\n            font-weight:700; font-size:11px; letter-spacing:0.2px;\n            background:${bg}; color:${fg};\n          \">${label}&lt;\/span>\n          &lt;div style=\"padding-top:10px; font-size:11px; color:var(--secondary-text-color); opacity:0.85;\">\n            Based on voltage (A\/B\/C)\n          &lt;\/div>\n        &lt;\/div>\n      `;\n    ]]]\n  right: |\n    &#91;&#91;&#91;\n      const num = (eid) => {\n        const s = states&#91;eid]?.state;\n        if (!s || s === 'unavailable' || s === 'unknown') return null;\n        const n = parseFloat(String(s).replace(',', '.'));\n        return Number.isFinite(n) ? n : null;\n      };\n\n      const hoursToMin = (h) => (h === null ? null : Math.round(h * 60));\n      const fmtMin = (m) => (m === null ? '\u2014' : `${m} min`);\n      const fmtCnt = (c) => (c === null ? '\u2014' : `${Math.round(c)}x`);\n\n      const rows = &#91;\n        { label: 'Today',    t: 'sensor.d01_outage_time_today',    c: 'sensor.d01_outage_count_today' },\n        { label: 'MTD',      t: 'sensor.d01_outage_time_mtd',      c: 'sensor.d01_outage_count_mtd' },\n        { label: 'YTD',      t: 'sensor.d01_outage_time_ytd',      c: 'sensor.d01_outage_count_ytd' },\n        { label: 'Lifetime', t: 'sensor.d01_outage_time_lifetime', c: 'sensor.d01_outage_count_lifetime' },\n      ];\n\n      const moreInfo = (eid) => `\n        this.dispatchEvent(new CustomEvent('hass-more-info', {\n          composed: true, bubbles: true, detail: { entityId: '${eid}' }\n        }));\n        event.stopPropagation();\n      `;\n\n      const header = `\n        &lt;div style=\"display:flex; justify-content:space-between; gap:10px; margin-bottom:6px;\">\n          &lt;div style=\"font-size:11px; opacity:0.7;\">Period&lt;\/div>\n          &lt;div style=\"display:flex; gap:10px;\">\n            &lt;div style=\"font-size:11px; opacity:0.7; width:56px; text-align:right;\">Count&lt;\/div>\n            &lt;div style=\"font-size:11px; opacity:0.7; width:78px; text-align:right;\">Time&lt;\/div>\n          &lt;\/div>\n        &lt;\/div>\n      `;\n\n      const body = rows.map(r => {\n        const c = num(r.c);\n        const h = num(r.t);\n        const m = hoursToMin(h);\n\n        return `\n          &lt;div style=\"display:flex; justify-content:space-between; gap:10px; padding:6px 0;\">\n            &lt;div style=\"font-size:12px; opacity:0.8;\">${r.label}&lt;\/div>\n            &lt;div style=\"display:flex; gap:10px; align-items:baseline;\">\n\n              &lt;div\n                onclick=\"${moreInfo(r.c)}\"\n                style=\"\n                  width:56px; text-align:right; font-size:14px; font-weight:800;\n                  cursor:pointer; border-radius:6px; padding:2px 4px;\n                  transition: background 0.15s;\n                \"\n                onmouseenter=\"this.style.background='rgba(var(--rgb-primary-color, 33,150,243),0.12)'\"\n                onmouseleave=\"this.style.background='transparent'\"\n              >${fmtCnt(c)}&lt;\/div>\n\n              &lt;div\n                onclick=\"${moreInfo(r.t)}\"\n                style=\"\n                  width:78px; text-align:right; font-size:14px; font-weight:800;\n                  cursor:pointer; border-radius:6px; padding:2px 4px;\n                  transition: background 0.15s;\n                \"\n                onmouseenter=\"this.style.background='rgba(var(--rgb-primary-color, 33,150,243),0.12)'\"\n                onmouseleave=\"this.style.background='transparent'\"\n              >${fmtMin(m)}&lt;\/div>\n\n            &lt;\/div>\n          &lt;\/div>\n        `;\n      }).join('');\n\n      return `\n        &lt;div style=\"display:flex; flex-direction:column;\">\n          &lt;div style=\"font-size:12px; opacity:0.7; letter-spacing:0.3px; text-align:right;\">\n            Outage stats\n          &lt;\/div>\n          &lt;div style=\"margin-top:8px;\">\n            ${header}\n            ${body}\n          &lt;\/div>\n        &lt;\/div>\n      `;\n    ]]]\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Quando voc\u00ea come\u00e7a a colocar&nbsp;disjuntores inteligentes&nbsp;na rotina da casa, a automa\u00e7\u00e3o deixa de ser \u201clegal\u201d e passa a ser&nbsp;infraestrutura. E infraestrutura precisa de duas coisas: Meu problema era simples: eu tinha&nbsp;v\u00e1rios disjuntores&nbsp;e precisava de uma p\u00e1gina \u00fanica no Home Assistant onde eu conseguisse: No fim, eu cheguei em dois cards principais: O detalhe interessante \u00e9&#8230;<\/p>\n","protected":false},"author":1,"featured_media":271,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[6],"tags":[77,76],"class_list":["post-269","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-automacao","tag-card","tag-home-assistant"],"jetpack_featured_media_url":"https:\/\/www.bernabauer.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-06-at-14.36.49.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/posts\/269","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/comments?post=269"}],"version-history":[{"count":2,"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/posts\/269\/revisions"}],"predecessor-version":[{"id":274,"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/posts\/269\/revisions\/274"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/media\/271"}],"wp:attachment":[{"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/media?parent=269"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/categories?post=269"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bernabauer.com\/blog\/wp-json\/wp\/v2\/tags?post=269"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}