Playwright travando um Mini PC: troquei por shell script

Playwright travando um Mini PC: troquei por shell script

Um dashboard de indicadores (OEE) precisava ficar sempre visível numa TV, num navegador em tela cheia, numa fábrica. Se o navegador travasse, mudasse de site, minimizasse ou ficasse coberto por outra janela, ninguém percebia até alguém passar na frente da TV e notar.

Quando assumi esse projeto, a automação já existia, feita em Python com Playwright. Troquei para shell script, e não foi por preferência pessoal, foi porque o problema que eu estava resolvendo mudou.

O problema

A máquina conectada em cada TV é um Mini PC com hardware bem limitado: 4GB de RAM e processador Intel Celeron. Ela precisa manter um navegador aberto, em modo kiosk, exibindo o dashboard certo pra aquela linha de produção específica, indefinidamente.

Na configuração antiga do sistema de dashboard, não existia um link direto por linha de produção. Pra chegar na tela certa, era preciso automatizar a navegação: abrir o sistema, selecionar a linha correspondente, e só então chegar no dashboard que devia ficar fixo na TV.

Esse processo de navegação era longo, rodando numa máquina já limitada de hardware. Na prática, isso significava que qualquer instabilidade, mesmo uma sem relação nenhuma com o robô, como uma oscilação de internet, travava o processo inteiro.

Do ponto de vista de quem via a TV parada, inclusive a diretoria, não havia diferença entre “problema temporário de conexão” e “o robô não funciona”.

De Python com Playwright pra shell script

Automatizar aquela navegação já era feito com Python e Playwright quando assumi o projeto, ferramenta desenhada exatamente pra controlar um navegador programaticamente: abrir páginas, clicar, preencher campo, esperar elemento aparecer.

O problema é que o Playwright sobe e mantém uma instância inteira de navegador sob seu controle, além do consumo do próprio navegador exibindo o dashboard. Num Mini PC com 4GB de RAM e um Celeron, isso era pesado demais. A máquina travava com frequência, exatamente o oposto do que uma TV que deveria ficar exibindo dado o tempo todo precisa.

A virada aconteceu quando o próprio sistema do dashboard ganhou uma melhoria: passou a existir um link direto pra cada linha de produção. A partir daí, não havia mais nada pra navegar. A tarefa toda virou “abrir uma URL fixa e garantir que o navegador continue nela”, sem clique nenhum programático envolvido.

Sem a necessidade de controlar o navegador por dentro, o Playwright virou peso morto. Shell script, sendo tecnologia nativa do Linux, resolvia a mesma tarefa (manter uma aba fixa aberta, verificar e corrigir o estado dela) com muito menos consumo de recurso, o que importa especialmente numa máquina já limitada de hardware.

A checagem passou a rodar por um systemd timer, disparando uma verificação pontual em intervalos definidos, sem manter processo nenhum residente em memória entre uma execução e outra. O instalador do projeto gera as duas unidades (.service e .timer) sozinho, a partir do intervalo escolhido na instalação:

cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=Watchdog do dashboard OEE (verifica se o Chrome está aberto no site correto)

[Service]
Type=oneshot
ExecStart=${SCRIPT_PATH}
EOF

cat > "/etc/systemd/system/${SERVICE_NAME}.timer" <<EOF
[Unit]
Description=Dispara o ${SERVICE_NAME} a cada ${INTERVALO_MIN} minuto(s)

[Timer]
OnBootSec=${INTERVALO_MIN}min
OnUnitActiveSec=${INTERVALO_MIN}min
AccuracySec=5s
Persistent=true

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload
systemctl enable --now "${SERVICE_NAME}.timer"

Persistent=true garante que, se a máquina estiver desligada no horário em que o timer dispararia, ele roda assim que ela ligar de novo, em vez de simplesmente pular aquela execução.

De “o robô não funciona” pra um aviso que qualquer um entende

A troca de ferramenta resolveu o problema de travamento por hardware, mas não resolvia sozinha o problema de percepção: mesmo com o watchdog cuidando da recuperação automática, uma queda de internet ainda deixava a TV com o dashboard fora do ar por um tempo, e continuava parecendo “o robô quebrou” pra quem só via a tela de fora.

O shell script de hoje trata essa situação separadamente do resto: ele mesmo verifica a conectividade com a internet, e se detectar que caiu, troca o que está na tela por uma página local própria, avisando “Sem conexão com a internet”.

A página deixa claro que o dashboard abre automaticamente assim que a conexão voltar, e que, se a mensagem persistir, é pra acionar a equipe de TI. Ela ainda mostra máquina, usuário, linha de produção e horário da última verificação, informação suficiente pra quem for investigar já saber onde procurar, sem precisar abrir terminal nenhum.

Não é só um detalhe de UX. É a diferença entre uma instabilidade de rede, sem relação nenhuma com o robô, virar motivo pra questionar se a automação funciona, e essa mesma instabilidade ser reconhecida pelo que realmente é, com instrução clara de quando (e pra quem) escalar se não se resolver sozinha.

Detectando se a janela está realmente visível, sem interface gráfica nenhuma

A parte mais interessante do script não foi abrir o navegador, foi confirmar que ele estava mesmo visível. “O processo do navegador está rodando” não quer dizer “o dashboard está na tela”: a janela pode estar minimizada, coberta por outra, ou fora da tela cheia.

A função que faz essa checagem é essa:

janela_visivel() {
    local titulo="$1"
    local total_janelas window_id estado ativa
    # Mais de uma janela batendo com o título é estado inválido por si só — nem tenta
    # adivinhar qual é a certa, força reabrir (mata tudo e sobe uma única janela).
    total_janelas=$(wmctrl -l | grep -ci "$titulo")
    if [ "$total_janelas" -ne 1 ]; then
        return 1
    fi

    window_id=$(wmctrl -l | grep -i "$titulo" | awk '{print $1}')
    estado=$(xprop -id "$window_id" _NET_WM_STATE 2>/dev/null)
    echo "$estado" | grep -q "_NET_WM_STATE_FULLSCREEN" || return 1
    echo "$estado" | grep -q "_NET_WM_STATE_HIDDEN" && return 1

    ativa=$(xprop -root _NET_ACTIVE_WINDOW 2>/dev/null | grep -oE '0x[0-9a-fA-F]+')
    [ -n "$ativa" ] && [ "$((window_id))" -eq "$((ativa))" ]
}

wmctrl -l lista as janelas abertas com seus IDs e títulos. Antes de qualquer outra checagem, a função já descarta o caso de existir mais de uma janela com o título esperado, tratando isso como estado inválido sem tentar adivinhar qual das duas é a certa.

A partir do ID da janela, xprop consulta o estado dela direto no protocolo X11: _NET_WM_STATE_FULLSCREEN precisa estar presente (senão não está em tela cheia), e _NET_WM_STATE_HIDDEN não pode estar presente (senão está minimizada).

Só isso ainda não garante que a janela está visível de verdade: ela pode estar em tela cheia, sem estar minimizada, mas coberta por outra janela também em tela cheia por cima. Por isso a função compara o ID da janela com _NET_ACTIVE_WINDOW, a janela realmente em foco no momento.

Essa comparação usa aritmética do próprio shell ($(( ))) pra tratar os dois IDs hexadecimais como número, já que wmctrl e xprop formatam esses IDs de um jeito um pouco diferente entre si.

Se qualquer uma dessas checagens falhar, a função retorna que a janela não está visível, e o script segue pra reabrir o navegador do zero.

Resultado

O watchdog está em produção há cerca de 4 semanas, sem nenhum incidente relacionado diretamente a ele, mesmo rodando num Mini PC com hardware limitado que antes travava com frequência sob o Playwright.

Além disso, quedas de internet, que antes eram lidas como falha do robô, agora aparecem como o que realmente são: um aviso claro de instabilidade de conexão, com instrução de quando acionar a TI.

Pontos importantes

A ferramenta certa depende do que o problema pede, não de qual você usa por padrão: o Playwright era a ferramenta certa enquanto existia navegação programática de verdade pra fazer. Quando essa necessidade desapareceu (por uma melhoria de fora do meu controle, no próprio sistema do dashboard), continuar usando a mesma ferramenta só por hábito teria mantido um consumo de recurso desnecessário numa máquina que não podia bancar isso.

Hardware limitado é uma restrição técnica real, não só um detalhe de infraestrutura: um Mini PC com 4GB de RAM e Celeron trava sob carga que uma máquina comum absorveria sem perceber. Ferramentas mais pesadas (como controlar um navegador programaticamente) precisam ser avaliadas considerando o hardware onde vão rodar, não só a conveniência de manter a mesma stack.

Falha visível e explicada é diferente de falha silenciosa que parece pior do que é: quando quem acompanha o resultado não é técnico, um problema transitório sem aviso nenhum tende a ser interpretado da pior forma possível, “o robô não funciona”. Um aviso simples, dizendo o que está acontecendo e o que fazer se persistir, resolve isso sem precisar explicar nada por fora do próprio sistema. É o mesmo princípio, na direção oposta, de outro robô que já escrevi sobre, que ficava preso reprocessando o mesmo item pra sempre justamente por nunca deixar o problema aparecer.

Perguntas frequentes

Por que trocar Python com Playwright por shell script num robô de automação?

Nesse caso, porque o motivo de usar Playwright deixou de existir. Antes, era preciso automatizar a navegação até o dashboard certo de cada linha de produção, e o Playwright servia exatamente pra isso: controlar um navegador programaticamente. Depois que o próprio sistema do dashboard ganhou um link direto por linha, não havia mais nada pra navegar, só abrir uma URL fixa e manter o navegador nela, tarefa que shell script resolve de forma muito mais leve.

Por que o Playwright pode sobrecarregar uma máquina com hardware limitado?

Porque o Playwright sobe e controla uma instância real de navegador (Chromium, Firefox ou WebKit), mantendo o processo do navegador, o motor de renderização e a camada de automação rodando ao mesmo tempo. Numa máquina com pouca RAM e um processador de entrada (como um Intel Celeron com 4GB de RAM), esse consumo extra por cima do navegador já aberto pode ser o suficiente pra travar o sistema, mesmo que a página em si seja simples.

Como saber se uma janela está minimizada ou coberta usando só a linha de comando no Linux?

Com wmctrl e xprop, sem precisar abrir nenhuma interface gráfica ou biblioteca de automação visual. O wmctrl lista as janelas abertas e seus títulos; o xprop consulta propriedades da janela no protocolo X11, como _NET_WM_STATE (que indica se está minimizada ou em tela cheia) e _NET_ACTIVE_WINDOW (qual janela está realmente em foco no momento).

Como um robô de automação pode avisar sobre uma falha temporária sem parecer que quebrou de vez?

Mostrando uma mensagem clara na tela sobre o que está acontecendo, em vez de deixar a tela travada ou apagada sem explicação nenhuma. No caso do dashboard de fábrica, quando a internet cai, aparece um aviso dizendo que o sistema vai se reconectar sozinho assim que a conexão voltar, e que, se persistir, é pra acionar a equipe de TI responsável. Isso evita que uma falha transitória e sem relação com o robô seja interpretada como robô quebrado.

Quando ainda vale a pena usar Python com Playwright em vez de shell script?

Quando a automação precisa de fato navegar, clicar, preencher formulário ou extrair dado de dentro de uma página dinâmica, tarefas que shell script não resolve sozinho. O Playwright continua sendo a ferramenta certa pra isso. O que deixa de fazer sentido é mantê-lo rodando só pra manter uma aba aberta numa URL fixa, sem nenhuma interação programática acontecendo depois da navegação inicial.

Curtiu o conteúdo ou tem um problema parecido pra resolver? Bora trocar uma ideia.

Fale comigo no WhatsApp
David Alves

David Alves

Desenvolvedor Python | RPA

Desenvolvedor Python especializado em RPA e automação de processos, com foco em scripts inteligentes, integração de sistemas e APIs.