[Tutorial] Nobreak NHS no Home Assistant sem o software oficial — protocolo decifrado + MQTT

Depois de bater cabeça com um no-break NHS que nenhum driver aberto reconhece, acabei decifrando o protocolo dele e trazendo toda a telemetria pro Home Assistant sem depender do software oficial da NHS. Fica aqui o tutorial completo — e o método serve pra outros modelos NHS.

O que você tem no final: sensores de tensão de entrada/saída, tensão de bateria, carga %, temperatura e um binário “na bateria”, tudo via MQTT Discovery + um dashboard bonitinho.

Dashboard do no-break no Home Assistant


Ambiente testado

  • No-break: NHS Premium PDV GII 2200VA, USB-CDC (/dev/ttyACM0, USB ID 0925:1241), placa v18 / firmware v80
  • Host: Proxmox VE 9 (Debian 13) — mas serve pra qualquer Linux
  • HA: add-on Mosquitto (MQTT) + integração MQTT

:warning: Cada família de NHS pode ter pequenas diferenças de escala. Veja a seção “Adaptando pro seu modelo” — o método de captura serve pra todos.

Por que não dá pra usar NUT

Se você chegou aqui procurando “NHS + NUT”, já adianto: não funciona com este modelo. Testei tudo:

  • nhs_ups não existe na build do NUT do Debian (a premissa “o Debian já traz” está errada);
  • nhs_ser (compilado do fonte oficial) roda mas retorna 0 bytes;
  • nutdrv_qx / blazer_* (Megatec/Q1) → read: timeout em todos os comandos.

Motivo: o NHS é reply-only e fala um protocolo proprietário — só responde a um comando específico e não transmite sozinho (escuta passiva retorna 0 bytes). Por isso o clyra/nhs2mqtt (que é passivo) também não pega. A saída foi enviar o comando certo.

O protocolo (o que descobri)

  • Serial: 2400 8N1, sem paridade, CLOCAL (ignora DTR/RTS — não precisa mexer nas linhas), modo raw.
  • Enquadramento: FF <len> <payload...> <checksum> FE (início 0xFF, fim 0xFE).

Comandos capturados (o software oficial alterna os dois):

Enviar (hex) Resposta Conteúdo
FF 05 01 06 FE FF 15 44 …(21B)… FE (tipo 0x44=‘D’) Status: tensões, bateria, carga, temp
FF 09 53 03 00 00 00 5F FE FF 12 53 …(18B)… FE (tipo 0x53=‘S’) Equipamento: modelo, firmware, placa

Decode do frame de STATUS — seja b o frame sem o 0xFF inicial. Exemplo:
ff 15 44 74 00 88 00 07 73 00 74 00 75 00 24 00 00 40 00 1c fe

Campo Bytes Fórmula
Tensão de entrada (V) b[2],b[3] b2 + b3/100
Tensão de bateria (V) b[4],b[5] b4/5 (neste modelo; ver nota)
Carga (%) b[6] valor direto
Entrada mín / máx (V) b[7-8] / b[9-10] b + b/100
Tensão de saída (V) b[11],b[12] b + b/100
Temperatura (°C) b[13],b[14] b + b/100
Corrente do carregador (mA) b[15] b/25*750
Flags b[16] 0x40=rede presente · 0x80=carregador (cai o 0x40 na falta de rede)

Nota da bateria: aqui a bateria (24V nominal) veio como b[4]/5 (raw 136 → 27.2V). O parser do clyra/nhs2mqtt usa outra fórmula. Confira com o software oficial e calibre.

Como capturei o comando (método reutilizável)

O software oficial “NHS Control” (nhsupsserver) conversa com o aparelho. Grampeei a serial dele com strace e li o writev (o comando enviado):

# instale o NHS Control oficial (Linux x86_64), configure a porta
# /dev/ttyACM0 na UI web (localhost:2001) e então:
PID=$(pidof nhsupsserver)
strace -f -p $PID -e trace=%desc -s 500 -o /tmp/nhs.strace   # ~30s, Ctrl+C
grep -nE 'writev\([0-9]+,' /tmp/nhs.strace | head

Foi assim que apareceram os bytes FF 05 01 06 FE. %desc é essencial — o software usa readv/writev, que um strace -e read,write não pega. (Detalhe: o auto-detect do NHS Control só varre ttyS*; para USB ttyACM*, selecione a porta manualmente na UI.)

Instalação (poller → MQTT → HA)

Pré-requisitos: no-break no USB, um broker MQTT (add-on Mosquitto) + integração MQTT no HA, e Python 3:

dmesg | grep -i -e tty -e acm      # confirme a porta (/dev/ttyACM0)
sudo apt install -y python3-serial python3-paho-mqtt
  1. Copie nhs2mqtt.py para /usr/local/bin/ e chmod +x.
  2. Copie o .conf.example para /etc/nhs2mqtt.conf, preencha host/usuário/senha do MQTT, chmod 600.
  3. Teste em primeiro plano: python3 /usr/local/bin/nhs2mqtt.py → deve imprimir {'vin': 124.0, 'load': 7, 'temp': 36.0, ...}.
  4. Instale como serviço:
sudo cp nhs2mqtt.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nhs2mqtt

Em segundos o HA cria sozinho (MQTT Discovery) o dispositivo “Nobreak NHS” com os sensores.

:warning: Se você tinha o NHS Control ou algum driver do NUT instalado, pare/desabilite — eles disputam a /dev/ttyACM0 com o poller.

Dashboard

Card em vertical-stack com Mushroom + mini-graph-card (via HACS): estado “na rede/na bateria”, carga, bateria, temperatura e gráficos de entrada×saída e carga/temperatura (24h). O YAML completo está no repositório.

Gráficos de 24h: entrada/saída e carga/temperatura

Dá até pra acompanhar a rede elétrica oscilando ao longo do dia:

Histórico da tensão de entrada

Shutdown automático — a pegadinha do religamento

Cuidado com uma armadilha clássica: a BIOS “Restore on AC Power Loss = Power On” só religa a máquina quando a fonte perde e recupera energia de verdade. Se você faz um shutdown gracioso com a bateria ainda cheia e a rede volta antes da bateria acabar, a fonte nunca perdeu energia → a BIOS não dispara → a máquina fica desligada (ruim pra site remoto).

Por isso aqui eu fiquei na opção “só telemetria + aviso”: deixo a bateria descarregar até o no-break cortar a saída sozinho; a máquina perde energia de fato e a BIOS religa quando a rede volta (o ZFS tolera bem a queda). Monte uma automação de aviso a partir do binary_sensor.*_nobreak_na_bateria.

Adaptando pro seu modelo NHS

  1. Rode o poller e compare com a UI do NHS Control (:2001), lado a lado (a porta é exclusiva, não simultâneo).
  2. Carga e temperatura costumam bater direto. Tensões/bateria podem precisar de outro fator — ajuste as fórmulas em decode().
  3. Se o seu NHS não responder a FF 05 01 06 FE, capture o comando dele pelo método do strace acima.
  4. Mapeie o flags (b[16]) tirando o aparelho da tomada e vendo quais bits mudam.

Código

Tudo no GitHub (poller, config de exemplo, serviço systemd e o card do dashboard):

:backhand_index_pointing_right: GitHub - Alltec12/nhs2mqtt-homeassistant: nhs2mqtt: telemetria de no-break NHS no Home Assistant via MQTT · GitHub

Se este tutorial te ajudou, deixa uma :star: no repositório — ajuda outros donos de NHS a encontrarem o projeto. :slightly_smiling_face:

Créditos ao clyra/nhs2mqtt (GitHub - clyra/nhs2mqtt: Um script simples para ler o status de um nobreak NHS com interface USB e enviar para um broker MQTT · GitHub) pela base do decode do frame — aqui adaptei pra enviar o comando de polling e calibrar este modelo.

Sem garantia — mexer na serial de um no-break é por sua conta e risco.


Alguém mais aí tem NHS e quer testar? Se rodarem em outro modelo, me contem os valores que aparecem no decode() que a gente vai mapeando a família toda. :slightly_smiling_face:

1 curtida

precisava fazer engenharia “perversa” não… eu entrei em contato com os caras da NHS e eles me passaram o doc do protocolo serial. Não sei se ainda tenho isso, mas posso procurar caso vc ainda precise de alguma coisa.

Boa tarde.
Sou o autor original do NHS pra nut. Já quero te adiantar que testei aqui em vários dispositivos (mais de 12) e tenho funcionando todos perfeitamente, incluindo um igualzinho o teu Premium PDV. Há uma issue em aberto e estou implementando um patch falando sobre alguns problemas principalmente com controle de fluxo via hardware, mas estou implementando e testando em equipamentos que tenho. O pessoal reclamou muito dos da linha gamer, mas este teu não havia nada registrado. Tem vários motivos que podem levar a este problema, mas está explicado no chamado.

Issue: nhs_ser: hardcoded CRTSCTS prevents communication on USB/CDC-ACM (and 3-wire serial) NHS UPSes · Issue #3510 · networkupstools/nut · GitHub
PR: drivers: nhs_ser: do not force CRTSCTS (breaks CDC-ACM and 3-wire ser… by jpastoreli · Pull Request #3511 · networkupstools/nut · GitHub

Peço que você recompile o nut fazendo o mesmo procedimento do colega que abriu a issue para verificar se resolve teu problema. Ajuda também comentar lá na issue se resolveu ou não. Se for essa string de inicialização o problema ajuda a implementarmos um patch para fazer funcionar, mas acredito que possa ser realmente algo relacionado ao controle de fluxo.