From 6029c79645579e6fe55da57848d68109f0b8e5fa Mon Sep 17 00:00:00 2001 From: julio Date: Tue, 6 Oct 2026 03:47:11 -0300 Subject: [PATCH] feat(ao-vivo): indicador ao vivo do sensor PIR Cada disparo do PIR vira um evento SSE 'pir' (mesmo dentro do intervalo do historico) e o painel mostra 'presenca agora' por 8 s em Ao vivo e no Historico; /estado inclui o estado do PIR. Co-Authored-By: Claude Opus 5.5 --- historico.py | 1 + kiosk-server.py | 2 + web/src/lib/componentes/PresencaPir.svelte | 52 ++++++++++++++++++++++ web/src/lib/estado.svelte.ts | 6 ++- web/src/lib/tipos.ts | 1 + web/src/telas/AoVivo.svelte | 8 +++- web/src/telas/Historico.svelte | 4 +- 7 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 web/src/lib/componentes/PresencaPir.svelte diff --git a/historico.py b/historico.py index 15a5123..3bc5554 100644 --- a/historico.py +++ b/historico.py @@ -192,6 +192,7 @@ class Historico: if time.monotonic() - inicio < 2: # o pino "assenta" ao ligar o pull-down continue self.pir['ultimo'] = time.time() + self.publicar('pir', dict(ultimo=self.pir['ultimo'])) # todo disparo, ao vivo self.registrar('pir', 'presença') erro = (self.processo_pir.stderr.read() or '').strip()[:200] except OSError as e: diff --git a/kiosk-server.py b/kiosk-server.py index 3584e10..20ac048 100644 --- a/kiosk-server.py +++ b/kiosk-server.py @@ -260,6 +260,8 @@ class Handler(SimpleHTTPRequestHandler): dados["treinamento"] = dict(ocupado=s.treinamento.busy, erro=s.treinamento.erro) if hasattr(s, "api_config"): dados["api"] = s.api_config.status() + if hasattr(s, "historico"): + dados["pir"] = dict(s.historico.pir) return dados def do_GET(self): diff --git a/web/src/lib/componentes/PresencaPir.svelte b/web/src/lib/componentes/PresencaPir.svelte new file mode 100644 index 0000000..ac1f16a --- /dev/null +++ b/web/src/lib/componentes/PresencaPir.svelte @@ -0,0 +1,52 @@ + + + + {texto} + + + diff --git a/web/src/lib/estado.svelte.ts b/web/src/lib/estado.svelte.ts index 35915b7..8e9279f 100644 --- a/web/src/lib/estado.svelte.ts +++ b/web/src/lib/estado.svelte.ts @@ -34,6 +34,8 @@ class Estado { treinamento = $state({ ocupado: false, erro: '' }); api = $state(null); feed = $state([]); + /** Sensor PIR da placa: último disparo (s, epoch) e se está ligado. */ + pir = $state<{ disponivel: boolean; ultimo: number | null } | null>(null); /** Relógio de 1 s para derivar "online" sem esperar evento novo. */ agora = $state(Date.now()); @@ -106,6 +108,7 @@ function aplicarFoto(f: EstadoServidor) { estado.reacao = f.reacao ?? null; if (f.treinamento) estado.treinamento = f.treinamento; if (f.api) estado.api = f.api; + if (f.pir) estado.pir = { disponivel: f.pir.disponivel, ultimo: f.pir.ultimo }; } const tratadores: Record void> = { @@ -139,6 +142,7 @@ const tratadores: Record void> = { if (d?.receita?.nome) noFeed('reacao', 'Reação: ' + d.receita.nome); }, treinamento: (d) => (estado.treinamento = d), + pir: (d: { ultimo: number }) => (estado.pir = { disponivel: true, ultimo: d.ultimo }), historico: (d) => { const n = d?.novo; if (n) noFeed('historico', { pir: '🚶 Presença no PIR', movimento: '👀 Movimento na câmera', som: '🔊 Som alto' }[n.tipo as string] ?? 'Detecção'); @@ -165,7 +169,7 @@ function abrirEventos() { if (estado.conexao !== 'token') estado.conexao = 'caiu'; }; for (const tipo of ['expressao', 'telemetria', 'app_config', 'aparencia', 'motores', 'estop', 'conversa', 'ia', - 'reacao', 'treinamento', 'historico']) { + 'reacao', 'treinamento', 'historico', 'pir']) { fonte.addEventListener(tipo, (e) => { const dados = JSON.parse((e as MessageEvent).data); tratadores[tipo]?.(dados); diff --git a/web/src/lib/tipos.ts b/web/src/lib/tipos.ts index c575a12..77aac28 100644 --- a/web/src/lib/tipos.ts +++ b/web/src/lib/tipos.ts @@ -81,4 +81,5 @@ export interface EstadoServidor { reacao?: Reacao | null; treinamento?: { ocupado: boolean; erro: string }; api?: { provedor: string; modelo: string; configurada: boolean; ultimo_teste: unknown }; + pir?: { disponivel: boolean; erro: string; linha: string; ultimo: number | null }; } diff --git a/web/src/telas/AoVivo.svelte b/web/src/telas/AoVivo.svelte index da782ea..27b700a 100644 --- a/web/src/telas/AoVivo.svelte +++ b/web/src/telas/AoVivo.svelte @@ -1,6 +1,6 @@