🪟 Painel de Cortinas com Janela Animada pelo Clima
button-card + shutter-card + layout-card + entidade weather
Olá, pessoal! 👋
Quero compartilhar o painel que montei para controlar as 4 cortinas motorizadas da minha casa: Cozinha Sala Quarto Escada.
A ideia foi sair do card padrão de cover e criar algo mais visual. Cada cortina aparece como uma janela de verdade, que você abre e fecha arrastando a alça. E pelo vidro dá para ver o tempo lá fora: sol, nuvens, chuva, neblina, tempestade, neve e céu estrelado à noite, tudo animado em CSS e alimentado pela integração de clima do Home Assistant.
📸 Como ficou


🎯 Por que precisei de cards personalizados
Os cards nativos resolvem o controle, mas não entregavam o que eu queria:
- Arrastar a cortina como se fosse física, e não só botões ou um slider genérico.
- Várias informações num mesmo card: nome, estado com porcentagem, a janela, o clima e uma dica de uso, num layout em grade controlado por mim.
- Estilo dinâmico: borda dourada quando a cortina está aberta, contorno tracejado quando está indisponível, céu que muda conforme o clima e o dia/noite.
- Botões inteligentes, que ficam esmaecidos e sem ação quando a cortina não suporta aquele comando.
- Layout responsivo, funcionando bem no celular e no tablet.
📦 Dependências (HACS)
| Card | Autor | Para que usei |
|---|---|---|
| button-card | RomRider | Base de tudo: templates JavaScript, custom_fields, variables, state_display, extra_styles. |
| layout-card | thomasloven | Grade responsiva com auto-fit + minmax(): 1 coluna no celular, várias no tablet/PC. |
| shutter-card | Deejayfool | A cortina arrastável. Aproveito o arraste nativo dele e redesenho todo o visual por CSS. |
Também uso duas cenas já existentes: scene.cortinas_aberta e scene.cortinas_fechadas.
🌦️ A integração com o clima (weather.casa)
O clima vem de uma entidade weather do Home Assistant. No meu caso ela se chama weather.casa.
⚠️ Atenção: se a sua entidade tiver outro nome, troque
weather.casano código. Como segurança, se ela não existir, o código usa automaticamente a primeira entidadeweather.*que encontrar.
Também uso o sun.sun para saber se é dia ou noite (sol acima ou abaixo do horizonte). Se ele não existir, o código cai para um horário fixo: antes das 6h ou depois das 18h é noite.
Com isso, cada janela mostra:
- 🏷️ Um chip com ícone, temperatura e condição em português (ex.:
☁ 18 Nublado). - 🖼️ O "céu" dentro do vidro, com duas camadas de efeito animadas atrás da cortina.
Mapeamento das condições
| Condição do HA | Efeito no vidro |
|---|---|
sunny | ☀️ Céu azul, sol pulsando e raios de luz |
clear-night | 🌙 Céu escuro com estrelas piscando e lua |
partlycloudy | ⛅ Nuvens passando + sol (à noite: nuvens + lua) |
cloudy | ☁️ Duas camadas de nuvens em velocidades diferentes |
windy / windy-variant | 💨 Nuvens passando rápido |
fog | 🌫️ Faixas de neblina subindo e descendo |
rainy | 🌧️ Chuva caindo e gotas escorrendo no vidro |
pouring | 🌧️ Chuva mais densa e rápida |
lightning / lightning-rainy | ⛈️ Chuva forte com clarões de relâmpago |
snowy / snowy-rainy / hail | ❄️ Flocos de neve em duas camadas |
| sem dados | ⚠️ Céu neutro e ícone de alerta |
💡 Resultado: quando o sol se põe, o céu escurece sozinho dentro de todas as janelas.
🧩 Como o código está organizado
1️⃣ Cabeçalho (largura total)
Um button-card que mostra:
- Relógio atualizado a cada segundo (
update_timer: 1000). - Saudação (Bom dia / Boa tarde / Boa noite) e a data por extenso em pt-BR.
- Resumo: quantas cortinas estão abertas, em movimento e indisponíveis, mais a temperatura lá fora.
- Um botão "Previsão da semana" (button-card aninhado via
custom_fields) que abre o more-info do clima.
2️⃣ Botões globais
Abrir todas e Fechar todas chamam scene.turn_on e pedem confirmação antes de executar, evitando toques acidentais. Têm feedback háptico e uma leve animação ao apertar.
3️⃣ Um bloco por cortina
Cada cortina é um vertical-stack com três partes:
🅰️ Card principal (button-card)
state_displayem JS mostraAberta · 75%,Fechada · 0%,Em movimento,IndisponívelouNão encontrada, validando securrent_positioné um número entre 0 e 100.variables.climaé um template JS que lê oweathere osun.sun, escolhe o efeito certo (com variantes noturnas comopartlyN,cloudyN,rainN,snowN) e devolve cores, gradientes, tamanhos e animações.triggers_update: [weather.casa, sun.sun]faz o card redesenhar quando o clima ou o sol mudam, mesmo que a cortina não mude.- Os valores calculados viram variáveis CSS (
--nx-sky,--nx-fx1,--nx-a1,--nx-fx2...) usadas emextra_styles.
💡 Ideia central: o JavaScript decide o quê desenhar; o CSS só desenha.
- Estilos por estado: borda e brilho dourados em
open, texto azul emopening/closing, borda tracejada emunavailable.
🅱️ A janela (shutter-card dentro de um custom_field)
O shutter-card fica embutido no button-card e é totalmente redesenhado por CSS (ele usa light DOM, então os seletores alcançam os elementos internos):
- Os botões e títulos originais do shutter-card são escondidos.
- A janela é desenhada com camadas de
background: caixa da cortina em madeira, vidro com o céu e divisória central. ::beforee::afterficam exatamente sobre o vidro com os dois efeitos animados, e comz-indexabaixo da cortina. Ao fechar, a cortina cobre o clima de verdade.- A cortina vira uma persiana listrada em tons de madeira, e a alça ganha uma barra dourada com botão ↕.
- Se a cortina estiver indisponível ou não suportar posição, a janela fica esmaecida e não aceita toque.
⚠️ Detalhe importante: desativei o efeito de "encolher ao tocar" (
transform: scale) nesse card. O shutter-card calcula o arraste por coordenadas da página, e a escala atrapalhava o movimento.
🅲 Botões de controle
- Abrir Parar Fechar e os atalhos 25% 50% 75%.
- Cada botão confere o
supported_featuresda entidade com operação bit a bit antes de agir. - Se o recurso não é suportado, ou a entidade está indisponível, o botão fica esmaecido e a ação vira
none.
| Bit | Recurso | Botão |
|---|---|---|
1 | OPEN | Abrir |
2 | CLOSE | Fechar |
4 | SET_POSITION | 25% / 50% / 75% e arraste |
8 | STOP | Parar |
4️⃣ Acessibilidade e desempenho
- ♿ Respeita
prefers-reduced-motion: se o sistema pedir menos animação, os efeitos de clima e dos botões são desligados. - ⚡ Tudo é CSS puro (
@keyframescombackground-position,opacityetransform), sem imagens externas nem JS rodando em loop.
🔁 Sobre a cortina da Escada: o motor dela está com sentido invertido, então no card dela Abrir chama
cover.close_covere Fechar chamacover.open_cover(a checagem desupported_featuresacompanha a troca). Se a sua não for invertida, desfaça essa troca.
🛠️ Como adaptar para a sua casa
- Instale pelo HACS: button-card, layout-card e shutter-card.
- Troque os
entity_id:cover.cortina_cozinha,cover.cortina_sala,cover.cortina_quarto,cover.cortina_escada. - Troque
weather.casapela sua entidade de clima (ou deixe, e o fallback usa a primeiraweather.*). - Crie ou troque as cenas
scene.cortinas_abertaescene.cortinas_fechadas. - Ajuste a lista de cortinas do resumo no cabeçalho (
nx_chips). - Se alguma cortina for invertida, veja a observação da Escada acima.
🚀 Próximos passos
O YAML ficou grande porque cada card de cortina é autossuficiente: o bloco de clima se repete em cada um. A próxima melhoria é mover isso para button_card_templates (ou decluttering-card), deixando uma única definição e cada cortina com poucas linhas.
Sugestões são muito bem-vindas! Se alguém já fez algo parecido, adoraria ver. 🙌
📄 Código completo
O YAML completo (o card e a visualização inteira) está no GitHub:
Qualquer dúvida, é só perguntar. Espero que sirva de inspiração! 🙂

