{"openapi":"3.1.0","info":{"title":"AMF25 Telemetry Relay","description":"\nAPI de telemetria do gerador **ComAp InteliLite NT AMF25** instalado na unidade\nda Defesa Civil de Joinville. Os dados sao coletados via Modbus RTU localmente\ne empurrados (POST) para esta API.\n\n**Arquitetura**\n\n- A maquina local (atras de firewall) le o controlador a cada 2s e POSTa o estado\n  para esta API sempre que algo muda + heartbeat periodico.\n- Esta API mantem **somente o estado atual em memoria** (sem banco). Se a maquina\n  Fly reiniciar, o estado se perde ate o proximo POST.\n- Historico estendido nao esta disponivel aqui. Os parceiros que precisarem de\n  serie temporal devem fazer polling em `/state` e armazenar do seu lado.\n\n**Como usar (parceiros)**\n\nFaca polling em `GET /state` na cadencia que achar adequada (sugerimos >= 5s).\nCheque o campo `seconds_since_update` — se for muito grande, os dados estao\ndesatualizados (gerador offline, rede caiu, ou maquina Fly reiniciou).\n\nPara receber alertas de partida/parada de motor, consulte `GET /events` e\ncompare com o evento anterior conhecido (ou inscreva-se no canal ntfy.sh\npublico: https://ntfy.sh/cigerdjoinville).\n\n**Sem autenticacao na leitura** — endpoints GET sao publicos.\n","contact":{"name":"Defesa Civil Joinville"},"license":{"name":"Proprietario"},"version":"1.0.0"},"paths":{"/ingest":{"post":{"tags":["Privado"],"summary":"Recebe uma amostra do coletor local (autenticado).","description":"Atualiza o estado atual mantido em memoria. Apenas o coletor local usa\neste endpoint. Requer header `Authorization: Bearer <token>`.","operationId":"ingest_ingest_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","default":"","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sample"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestOk"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/state":{"get":{"tags":["Publico"],"summary":"Retorna o ultimo estado conhecido do gerador.","description":"Estado mais recente recebido do coletor local. Use o campo\n`seconds_since_update` para julgar se os dados estao frescos.\n\nCodigo HTTP 404 se a API ainda nao recebeu nenhum POST desde a ultima\ninicializacao da maquina.","operationId":"state_state_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StateResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmptyResponse"}}}}}}},"/events":{"get":{"tags":["Publico"],"summary":"Retorna os ultimos eventos START/STOP do motor.","description":"Ring buffer em memoria com os ultimos eventos de partida e parada do\nmotor (max 100, configuravel por env var no servidor). Reset quando a\nmaquina reinicia.\n\nPara historico persistente, faca polling em `/state` e armazene do seu lado.","operationId":"events_events_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"tags":["Publico"],"summary":"Health check da API.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"EmptyResponse":{"properties":{"detail":{"type":"string","title":"Detail","default":"sem dados ainda"}},"type":"object","title":"EmptyResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IngestOk":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"received_at":{"type":"string","title":"Received At"}},"type":"object","required":["received_at"],"title":"IngestOk"},"Sample":{"properties":{"timestamp":{"type":"string","title":"Timestamp","description":"Timestamp local da leitura (gerado no coletor).","examples":["2026-06-04 17:30:42"]},"event":{"type":"string","title":"Event","description":"Vazio em leituras normais. 'START' quando motor partiu. 'STOP' quando parou.","default":"","examples":["START","STOP",""]},"modo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modo","description":"Modo do controlador: OFF, MAN, AUT ou TEST.","examples":["AUT"]},"engine_state_raw":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Engine State Raw","description":"Engine State como inteiro bruto retornado pelo Modbus.","examples":[30]},"engine_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Engine State","description":"Engine State decodificado (Ready, Cranking, Running, Cooling, Shutdown, etc.).","examples":["Ready"]},"breaker_state_raw":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Breaker State Raw","description":"Breaker State como inteiro bruto.","examples":[34]},"breaker_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breaker State","description":"Breaker State decodificado (MainsOper=rede alimenta carga, IslOper=gerador alimenta, etc.).","examples":["MainsOper"]},"oil_pressure_low":{"anyOf":[{"type":"integer","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Oil Pressure Low","description":"1 = pressostato de oleo fechado (baixa pressao). Com motor parado e normal. So vira alarme com motor rodando.","examples":[1]},"rpm":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rpm","description":"Rotacao do motor (RPM nominal 1800).","examples":[0]},"gen_freq_hz":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Gen Freq Hz","description":"Frequencia do gerador em Hz (nominal 60).","examples":[0.0]},"v_l1n":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"V L1N","description":"Tensao fase-neutro L1 do gerador em V (nominal 220).","examples":[0]},"load_kw":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Load Kw","description":"Potencia ativa em kW (nominal 67).","examples":[0]},"load_pf":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Load Pf","description":"Fator de potencia (0.0 a 1.0).","examples":[0.0]},"engine_temp_c":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Engine Temp C","description":"Temperatura do motor em °C (Sd em 98°C).","examples":[50]},"nivel_diesel_pct":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Nivel Diesel Pct","description":"Nivel de diesel em % (Sd em 0%, Wrn em 7%).","examples":[29]},"battery_v":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Battery V","description":"Tensao da bateria em V (range 9-18V).","examples":[13.0]},"run_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Run Hours","description":"Horas acumuladas de operacao do motor.","examples":[201.9]}},"type":"object","required":["timestamp"],"title":"Sample"},"StateResponse":{"properties":{"timestamp":{"type":"string","title":"Timestamp","description":"Timestamp local da leitura (gerado no coletor).","examples":["2026-06-04 17:30:42"]},"event":{"type":"string","title":"Event","description":"Vazio em leituras normais. 'START' quando motor partiu. 'STOP' quando parou.","default":"","examples":["START","STOP",""]},"modo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modo","description":"Modo do controlador: OFF, MAN, AUT ou TEST.","examples":["AUT"]},"engine_state_raw":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Engine State Raw","description":"Engine State como inteiro bruto retornado pelo Modbus.","examples":[30]},"engine_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Engine State","description":"Engine State decodificado (Ready, Cranking, Running, Cooling, Shutdown, etc.).","examples":["Ready"]},"breaker_state_raw":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Breaker State Raw","description":"Breaker State como inteiro bruto.","examples":[34]},"breaker_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breaker State","description":"Breaker State decodificado (MainsOper=rede alimenta carga, IslOper=gerador alimenta, etc.).","examples":["MainsOper"]},"oil_pressure_low":{"anyOf":[{"type":"integer","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Oil Pressure Low","description":"1 = pressostato de oleo fechado (baixa pressao). Com motor parado e normal. So vira alarme com motor rodando.","examples":[1]},"rpm":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rpm","description":"Rotacao do motor (RPM nominal 1800).","examples":[0]},"gen_freq_hz":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Gen Freq Hz","description":"Frequencia do gerador em Hz (nominal 60).","examples":[0.0]},"v_l1n":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"V L1N","description":"Tensao fase-neutro L1 do gerador em V (nominal 220).","examples":[0]},"load_kw":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Load Kw","description":"Potencia ativa em kW (nominal 67).","examples":[0]},"load_pf":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Load Pf","description":"Fator de potencia (0.0 a 1.0).","examples":[0.0]},"engine_temp_c":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Engine Temp C","description":"Temperatura do motor em °C (Sd em 98°C).","examples":[50]},"nivel_diesel_pct":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Nivel Diesel Pct","description":"Nivel de diesel em % (Sd em 0%, Wrn em 7%).","examples":[29]},"battery_v":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Battery V","description":"Tensao da bateria em V (range 9-18V).","examples":[13.0]},"run_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Run Hours","description":"Horas acumuladas de operacao do motor.","examples":[201.9]},"received_at":{"type":"string","title":"Received At","description":"Timestamp UTC em que esta API recebeu a amostra.","examples":["2026-06-04T20:38:35Z"]},"seconds_since_update":{"type":"number","title":"Seconds Since Update","description":"Idade da amostra em segundos. Use para detectar dados velhos.","examples":[3.2]}},"type":"object","required":["timestamp","received_at","seconds_since_update"],"title":"StateResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"Publico","description":"Endpoints abertos, sem autenticacao. Para parceiros consumirem."},{"name":"Privado","description":"Endpoints autenticados via Bearer token. Apenas o coletor local usa."}]}