/** Histórico de detecções (/historico/*): recarrega ao abrir e quando o servidor avisa por SSE. */ import { lerToken, pedir, postar } from './api'; import { avisos } from './avisos.svelte'; import { ouvir } from './estado.svelte'; export interface EventoHistorico { id: number; horario: number; tipo: 'pir' | 'movimento' | 'som'; evento: string; nivel: number | null; foto: boolean; } export interface StatusHistorico { config: { pir: boolean; movimento: boolean; som: boolean; foto: boolean; intervalo: number; dias: number; presenca: boolean; ausencia: number }; presenca: { estado: 'presente' | 'ausente'; desde: number; ultimo: number }; pir: { disponivel: boolean; erro: string; linha: string; ultimo: number | null }; eventos: EventoHistorico[]; total: number; max_eventos: number; max_fotos: number; } export const TIPOS_EVENTO: Record = { pir: { nome: 'Presença (PIR)', emoji: '🚶', cor: '#34c759' }, movimento: { nome: 'Movimento na câmera', emoji: '👀', cor: '#0a84ff' }, som: { nome: 'Som alto', emoji: '🔊', cor: '#ff9500' }, }; /** URL da foto com o token na query (a tag não manda cabeçalho). */ export function urlFoto(id: number) { const t = lerToken(); return `/historico/foto/evento-${id}.jpg` + (t ? '?t=' + encodeURIComponent(t) : ''); } class Historico { status = $state(null); private espera = 0; async carregar() { try { this.status = await pedir('/historico/status'); } catch (e) { avisos.erro(e); } } async configurar(parcial: Partial) { const antes = this.status ? { ...this.status.config } : null; if (this.status) this.status = { ...this.status, config: { ...this.status.config, ...parcial } }; try { this.status = await postar('/historico/config', parcial); } catch (e) { if (this.status && antes) this.status = { ...this.status, config: antes }; avisos.erro(e); } } async apagar(id: number) { try { await postar('/historico/apagar', { id }); } catch (e) { avisos.erro(e); } } async limpar() { try { await postar('/historico/limpar'); avisos.ok('Histórico apagado.'); } catch (e) { avisos.erro(e); } } /** Vários avisos seguidos (evento + foto) viram uma recarga só. */ agendar() { clearTimeout(this.espera); this.espera = window.setTimeout(() => this.carregar(), 300); } } export const historico = new Historico(); ouvir('historico', () => historico.status && historico.agendar());