Notificações do Frigate no WhatsApp com o Node-RED (Tutorial avançado)

Fala pessoal, sussa?

Como alguns aqui sabem, sou entusiasta de Docker e por isso mesmo não tenho o Home Assistant instalado de forma nativa (Hassio) em nenhuma das minhas máquinas (sim, tenho um cluster com 3 SBC rodando tudo em Docker), então esse tutorial inicialmente foi feito para solucionar um problema que afeta quem precisa dessas notificações e não tem ou não quer ter o HA instalado. Mas não se preocupem, pois ele também funciona por dentro do HA (com algumas pequenas alterações).

Para quem está lendo e acredita que vou iniciar o tutorial informando como fazer uma instalação Docker, esqueça, pois o propósito aqui é outro bem diferente. Caso tenham interesse, posso depois fazer alguns tutoriais abordando o assunto, indo desde a escolha do SBC, passando pelo melhor OS para cada tipo de utilização que queira montar até a instalação, configuração e publicação dos serviços… com acesso externo e sem utilizar o DuckDNS.

A idéia aqui é mostrar como podemos utilizar o Node-RED para automatizar o envio das notificações/visualizações do Frigate pelo WhatsApp de forma simples e direta. Sei que tem alguns tópicos aqui que abordam o tema, inclusive um do nosso amigo @Choske, que foi o start para que pudesse montar esse tutorial e que deixo abaixo como referência (até porque a parte do GreenAPI ele demonstra no vídeo e por isso julgo desnecessário abordar).

Mas vamos ao que realmente interessa. Do que vamos precisar de imediato?

  • MQTT Broker - Pode ser o Mosquito ou qualquer outro. Eu recomendo o eMQX pelos seus recursos e fácil configuração/administração no Docker;
  • Node-RED - De preferência que esteja atualizado. Não precisa instalar nenhum módulo adicional para que funcione;
  • Frigate - Qualquer versão da 13 em diante funciona bem. Quem for atualizar para a versão 14.x muito cuidado, pois se não estiver utilizando uma TPU (Coral/Avada) ou a NPU da máquina, ele vai consumir muito do processamento.
  • GreenAPI - Basta seguir os passos demonstrado no vídeo do @Choske. Recomendo a criação de um grupo específico para essa finalidade, pois pode-se adicionar outros membros posteriormente, sem a necessidade de modificação do código;

Pulando a parte de instalações e configurações, que acredito que quem for seguir esse tutorial já deva ter feito ou saiba como fazer sem a necessidade de explicação, vamos direto para a implementação do código.

Dentro do Node-RED, vamos precisar dos seguintes nós (que já passo com as configurações):

Screenshot 2024-11-25 at 17.15.57

Frigate Reviews (MQTT In)

Action - Subscribe to single topic
Topic - frigate/reviews
Output - a parsed JSON object

Filter (Switch)

Property - msg.payload.type
Type - == new

Create Notifications (Function)
// Frigate Notification v1.0 (2024)
// Matheus Britto (matheus.britto@me.com)

// Green API Credentials
var greenApiUrl = "https://api.green-api.com/waInstance";
var greenApiInstanceId = "XXX"; // Sua instância
var greenApiToken = "XXX"; // Seu Token
var chatId = "XXX@g.us" // Seu grupo do WhatsApp

// Get Frigate Events
var urlServer = "https://server.nom"; // Endereço externo, acessível pela porta 5000
msg.camera_id = msg.payload.after.camera;
msg.label_id = msg.payload.after.data.objects;

msg.latest = urlServer + "/api/" + msg.camera_id + "/latest.jpg";
msg.thumbnail = urlServer + "/api/" + msg.camera_id + "/" + msg.label_id + "/thumbnail.jpg";
msg.snapshot = urlServer + "/api/" + msg.camera_id + "/" + msg.label_id + "/snapshot-clean.png";
msg.clip = urlServer + "/api/" + msg.camera_id + "/" + msg.label_id + "/clip.mp4";

// Construct WhatsApp Message
msg.topic = msg.camera_id + "-" + msg.label_id;
msg.relativetime = msg.relativetime || "1 minutes";
msg.message = msg.label_id + " detected";

// Sending Method
var sendFile = "/sendFileByUrl/";
var sendMessage = "/sendMessage/";

// HTTP Payload for Green API
msg.url = greenApiUrl + greenApiInstanceId + sendFile + greenApiToken;
msg.payload = {
    chatId: chatId,
    urlFile: msg.latest,
    fileName: "frigate.png",
    caption: msg.message,
};

msg.headers = {
    "Content-Type": "application/json"
};

return msg;
Rate Limiter (Function)
var interval = msg.ratelimitms || (1000*30); // minimum interval between messages (ms)
var countToTrigger = msg.countToTrigger || 1;
var lastReleasedKey = "lastReleased_" + (msg.topic.replace(/ /g, "_") || "null_topic");
var lastHitKey = "lastHit_" + (msg.topic.replace(/ /g, "_") || "null_topic");
var cntKey = "count_" + (msg.topic.replace(/ /g, "_") || "null_topic");
var lastReleased = (context.get(lastReleasedKey) || 0);
var lastHit = (context.get(lastHitKey) || 0);
var now = Date.now();
if (now-lastHit > interval) {
    node.warn("resetting count, too far apart.");
    context.set(cntKey,0); // Clear hits, too far apart.
}
context.set(lastHitKey,now);
if (now-lastReleased > interval) {
    var count = (context.get(cntKey) || 0);
    count++;
    if (count >= countToTrigger) {
        context.set(lastReleasedKey,now);
        context.set(cntKey,0);
        node.status({fill:"green",shape:"ring",text:"passed on"});
        return msg;
    } else {
        node.status({fill:"red",shape:"ring",text:"Waiting for " + 
            (countToTrigger - count) + " more hits."});
        context.set(cntKey,count);
        return null;
    }
} else {
    var timeLeft = (interval - (now-lastReleased)) / 1000;  
    node.status({fill:"red",shape:"ring",text:"blocked for " + timeLeft + " seconds"});
    return null;
}
Publish (HTTP Request)

Method - Post

Depois de tudo pronto, basta publicar e esperar as mensagens começarem a chegar. Mas antes que perguntem, vamos às pequenas dúvidas…

  1. Essa implementação roda independente da existência do Home Assistant, tornando-a livre para ser implementada em SBCs apenas para a finalidade de monitoramento;
  2. As melhores opções de imagens são Latest (última detecção gravada, independente da câmera) e Snapshot (melhor resolução, mas sem marcas de detecção). Thumbnail irá mostrar apenas o quadro detectado e dependendo da configuração do Frigate, essa imagem será bem pequena e de baixa resolução;
  3. Desaconselho fortemente a ultilização da opção Clip, mesmo sendo possível com essa implementação, pois além de precisar de um outro nó Switch para verificar quando o vídeo acabou de ser gravado antes do envio, em testes só foi possível visualizar vídeos com até 30 segundos;
  4. A partir da versão 14.x, o Frigate possibilita criar login/senha e trabalhar de forma mais segura através do SSL, mas ainda é necessário deixar a porta 5000 aberta para que a GreenAPI possa encontrar o caminho correto das imagens/vídeos. E esse endereço precisa, obrigatoriamente, ser acessível de forma externa à sua rede. Caso isso não seja feito/possível, as notificações nunca chegarão. Recomendo um tunelamento com domínio próprio para fazer isso de forma segura (ou menos perigosa);
  5. O Rate Limiter pode e deve ser modificado de acordo com as necessidade/exigências de cada usuário, mas levem em consideração que dependendo da quantidade de câmeras existentes e a quantidade de movimentações que houver, um tempo muito alto provocará um gargalo elevado nas notificações (chegarão com delay alto. Por outro lado, um tempo muito curto fará com que as notificações sigam tranquilas, mas serão muitas imagens chegando no WhatsApp em pouco tempo e isso também pode atrapalhar. Testem e vejam qual o tempo se adequa melhor;
  6. Por fim, mas não menos importante, espero que tenham gostado desse tutorial e, caso queiram divulgá-lo em outros canais, não esqueçam de dar os devidos créditos. Parece bobagem, mas ajuda a divulgar o trabalho na comunidade.

Caso alguém tenha interesse de implementar um SBC apenas para isso, entra em contato que tenho soluções já prontas :wink:

Forte abraço em todos(as).

3 Likes

Brabíssimo!!

Parabéns!

Fala @Choske, beleza?

Agradeço o elogio irmão, mas tudo começou com aquela sua postagem do vídeo e que fiquei de testar se funcionaria sem precisar do Frigate, lembra? Ai uma coisa puxou outra e terminei tendo que fazer para solucionar um problema que estava acontecendo com algumas instalações que administro e que não tinha/queriam o HA.

E ai, vou aproveitar e já deixar também o código para quem quer usar com o HA, pois a config é bem simples e parecida com a que fiz.

// Frigate Notification v1.0 (2024)
// Matheus Britto (matheus.britto@me.com)

// Green API Credentials
var greenApiUrl = "https://api.green-api.com/waInstance";
var greenApiInstanceId = "XXX"; // Sua instância
var greenApiToken = "XXX"; // Seu Token
var chatId = "XXX@g.us" // Seu grupo do WhatsApp

// Get Frigate Events
var urlServer = "https://server.nom"; // Endereço externo do Home Assistant
msg.event_id = msg.payload.after.id;
msg.label_id = msg.payload.after.label;
msg.camera_id = msg.payload.after.camera;

msg.thumbnail = urlServer + "/api/frigate/notifications/" + msg.event_id +  "/thumbnail.jpg";
msg.snapshot = urlServer + "/api/frigate/notifications/" + msg.event_id + "/snapshot.png";
msg.clip = urlServer + "/api/frigate/notifications/" + msg.event_id + "/" + msg.camera_id + "/clip.mp4";

// Construct WhatsApp Message
msg.topic = msg.camera_id + "-" + msg.label_id;
msg.relativetime = msg.relativetime || "1 minutes";
msg.message = msg.label_id + " detected";

// Sending Method
var sendFile = "/sendFileByUrl/";
var sendMessage = "/sendMessage/";

// HTTP Payload for Green API
msg.url = greenApiUrl + greenApiInstanceId + sendFile + greenApiToken;
msg.payload = {
    chatId: chatId,
    urlFile: msg.snapshot,
    fileName: "frigate.png",
    caption: msg.message,
};

msg.headers = {
    "Content-Type": "application/json"
};

return msg;

:wink: