Renan Oliveira
← todos os sistemas

▲ Caso 01 · Monitoramento de ativos distribuídos

O alarme que aprendeu a ficar calado

Monitorar usinas solares é leitura de API. O problema difícil é decidir quando falar, porque um alarme que dispara todo dia é desligado na primeira semana, e aí ninguém vê o dia em que o problema era real.

Período
2026
Meu papel
concepção, arquitetura e código
Linguagens
Python e JavaScript
Estado
em operação diária

O problema

Uma usina solar que para de gerar não emite sinal nenhum. O dono descobre trinta dias depois, quando a conta de luz chega mais alta do que devia, e não existe recuperar a geração de um mês que não aconteceu.

A resposta ingênua é alertar sempre que a geração cai. Ela produz centenas de disparos por mês e acerta em nenhum: o inversor fica offline toda noite, e um dia nublado derruba a geração da frota inteira ao mesmo tempo.

Quando o cliente escreve

O dono da usina conversa com um robô no WhatsApp. O robô responde com o dado do monitoramento, e não com o que o cliente acha que está acontecendo. Na conversa abaixo, o cliente disse que estava tudo normal. O robô conferiu, mostrou a hora em que o sistema parou e devolveu a pergunta.

O cliente diz que o monitoramento está normal e o robô responde que o sistema está offline desde as 10h
Tela real do atendimento, agosto de 2026. Nome do cliente coberto.

Quando o robô não resolve, ele chama uma pessoa

Ele escreve para a equipe o resumo da conversa, abre o chamado sozinho e avisa o cliente com o número do protocolo. Quem assume já chega sabendo o que foi dito.

Nota interna com o resumo da conversa e o chamado CH-9009 aberto pelo robô
A nota que a equipe lê, escrita pelo robô.
Mensagem ao cliente com o número do protocolo
A mensagem que o cliente recebe, com o protocolo.

O que ele manda sem ser chamado

Quando o sistema para durante o dia, o dono recebe o alerta com o que conferir, em ordem. Toda semana, recebe o resumo da geração. Todo mês, o relatório.

🔴 [nome], seu sistema solar parou de enviar dados há [N] horas. Estimativa: ~R$ [valor] não gerados ([N]h de produção perdidas). O que verificar agora: 1️⃣ Sua casa está com energia? Ligue uma lâmpada ou a TV. • Se não tiver energia, a rede pode ter caído. O inversor volta sozinho. • Se tiver energia normal, siga para o passo 2. 2️⃣ Veja o inversor: a tela está acesa ou com alguma luz? 3️⃣ Se tudo parecer normal e o aviso continuar, responda AJUDA que verifico com você. FysolPulse

O alerta de parada, resumido a partir do modelo que está no código. Os colchetes são preenchidos com os dados da usina.

☀️ [nome] — resumo da semana Seu sistema gerou [kWh] kWh na última semana ([data] a [data]). [a análise da semana, escrita pela regra conforme o resultado] 💰 Economia estimada: ~R$ [valor] 📊 Performance: [%]% do esperado FysolPulse · Monitoramento Solar

O resumo semanal. O cabeçalho muda com o resultado: semana excelente, normal, abaixo do esperado ou sem geração.
Primeira página do relatório mensal, com energia gerada, rendimento e economia
O relatório mensal, com dados de exemplo. O cliente recebe o resultado interpretado, e não a planilha.

Como está construído

Coleta
Python com requests contra as plataformas dos fabricantes de inversor. Um proxy próprio normaliza os formatos, porque cada fabricante devolve a geração de um jeito.
Borda
Seis Cloudflare Workers em JavaScript.
Estado e sessão
Cloudflare KV. Sessão é token aleatório de 32 bytes em hexadecimal, com expiração própria.
Senhas
PBKDF2-SHA256 com 100.000 iterações e salt de 16 bytes, pela Web Crypto API nativa do runtime. Comparação em tempo constante, para não vazar informação pelo tempo de resposta.
Orquestração
Rotinas de GitHub Actions: agendadas, por evento externo e por alteração de código. Além delas, um servidor virtual (VPS) roda as rotinas agendadas do monitoramento em cron.
Relatórios
reportlab para o PDF mensal, holidays para o calendário de dias úteis no cálculo pro rata, pycryptodome para o que trafega cifrado.
Cobrança
Webhook do Asaas, com o evento de pagamento confirmado promovendo o cliente de teste para assinante sem intervenção humana.
Testes
Testes automáticos rodando em cada alteração, pelo mesmo mecanismo que publica.

As decisões, e o que eu descartei em cada uma

Decisão 01

Nunca alertar o óbvio

O sistema consulta uma tabela de condições antes de abrir a boca. Offline durante o dia por mais de trinta minutos vira alerta. Offline à noite é silêncio, porque não tem sol. Recuperação ao amanhecer é silêncio, porque é o sol nascendo e não uma recuperação. Geração abaixo de setenta por cento do esperado só alerta se persistir por três horas em pleno dia.

O que eu descartei: limiar único de geração, que é o caminho de uma linha de código. Ele teria disparado todo dia ao anoitecer e em todo dia nublado.

Parte das regras dessa tabela existe para o sistema não falar.

Decisão 02

O clima entra na classificação, não no relatório

Antes de classificar uma queda como falha, o sistema cruza a geração do dia com a esperada para aquela potência instalada naquele lugar, e consulta a condição do tempo. Quando o céu explica a queda, a ocorrência é marcada como clima e sai da fila de trabalho. Quando não explica, sobe com a ação técnica já escrita ao lado.

O que eu descartei: mostrar o dado do tempo no relatório e deixar a pessoa concluir. Isso transfere o julgamento para quem tem menos contexto e mais pressa.

Decisão 03

Quando a coleta parou, o problema não estava onde todo mundo procura

Uma das fontes passou a recusar as chamadas do servidor. A suspeita natural recai sobre o endereço de origem, e a correção natural é trocar de endereço ou contratar um intermediário. O diagnóstico apontou outra camada.

O que eu descartei: contratar saída por intermediário, que custa por mês, some com a causa e transfere o problema para o mês seguinte.

O teste isolou a variável: mesma origem, mesmo minuto, mudando só a camada suspeita. Antes, nenhuma chamada passava. Depois, todas. E o caminho alternativo continua no código, para o caso de a correção deixar de valer.

Decisão 04

A camada de linguagem cai em cascata, e não em silêncio

O atendimento usa modelo de linguagem em dois papéis separados, classificar e redigir, e cada papel tem sua própria cadeia de tentativas: provedor principal com a primeira chave, a mesma provedora com uma segunda chave quando a cota diária estoura, e por último um provedor diferente, pago e sem cota.

A cadeia inteira é configurada por variável de ambiente. Sem as variáveis do último nível, o comportamento é idêntico ao anterior, o que permitiu subir a mudança antes de decidir ligá-la.

O que eu descartei: uma única chave de um único provedor. Quando a cota diária acabava, o sistema não tinha para onde ir: ou desabava direto no modelo caro, ou emudecia no meio de um atendimento.

A armadilha que só aparece implementando: cada família de modelo aceita parâmetros diferentes, e alguns recusam com erro o que outro exige. O corpo da requisição precisa ser remontado a cada tentativa. Montar uma vez fora do laço funciona no primeiro nível e quebra exatamente no nível que existe para salvar.

Decisão 05

Preço de serviço não sai do robô

Limpeza, visita técnica e manutenção não têm preço respondido pelo atendimento automático. O pedido é encaminhado para uma pessoa.

Por quê: preço de serviço depende de acesso ao telhado, altura, distância e estado do equipamento. Um número errado dito por robô vira compromisso comercial que alguém terá de honrar ou desdizer.

Console de operação do FysolPulse, com o trabalho agrupado por tipo de problema
Console de operação. O trabalho não vem como lista de usinas: vem agrupado por tipo de problema, com a ação escrita ao lado de cada grupo. Nomes das usinas substituídos por fictícios antes da captura.
Painel da frota inteira de usinas monitoradas
A frota numa tela, com a potência total somada.
Página de ativação do teste gratuito
O mesmo motor embalado como assinatura.
6workers na borda

Contagem feita no repositório em 22/09/2026.