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 @@