159 lines
5.9 KiB
JavaScript
159 lines
5.9 KiB
JavaScript
/**
|
|
* overview.js — Lógica de supervisão da Visão Geral
|
|
* Monitora métricas do robô, bateria, IA Gemini, reações e gerencia ações rápidas.
|
|
*/
|
|
(() => {
|
|
const el = id => document.getElementById(id);
|
|
let reload = null;
|
|
let start = 0;
|
|
let ultimoEstadoOnline = null;
|
|
|
|
function adicionarEvento(texto, tipo = 'info') {
|
|
const feed = el('overviewFeed');
|
|
if (!feed) return;
|
|
const item = document.createElement('div');
|
|
item.style.cssText = 'display:flex;align-items:center;gap:10px;animation:fadeInMsg .2s ease-out';
|
|
const cor = tipo === 'online' ? '#10b981' : tipo === 'alerta' ? '#f59e0b' : 'var(--ciano)';
|
|
const hora = new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
item.innerHTML = `
|
|
<span class="badge-dot" style="background:${cor};box-shadow:0 0 8px ${cor}"></span>
|
|
<span style="color:var(--texto)">${texto}</span>
|
|
<span style="margin-left:auto;font-size:11px;font-family:'JetBrains Mono',monospace;color:var(--fraco)">${hora}</span>
|
|
`;
|
|
feed.prepend(item);
|
|
while (feed.children.length > 7) {
|
|
feed.removeChild(feed.lastChild);
|
|
}
|
|
}
|
|
|
|
async function refresh() {
|
|
try {
|
|
const [s, c, t] = await Promise.all(['/app/status', '/conversa/status', '/treinamento/status'].map(async p => {
|
|
const r = await fetch(p, { cache: 'no-store', signal: AbortSignal.timeout(3000) });
|
|
if (!r.ok) throw Error('Servidor indisponível');
|
|
return r.json();
|
|
}));
|
|
|
|
// Estado do Celular
|
|
if (el('overviewPhone')) {
|
|
el('overviewPhone').textContent = s.online ? 'Conectado' : 'Desconectado';
|
|
el('overviewPhone').style.color = s.online ? '#34d399' : 'var(--fraco)';
|
|
}
|
|
if (el('overviewIP')) {
|
|
el('overviewIP').textContent = s.celular_ip || (s.online ? 'IP Detectado' : 'Abra o Carinha no celular');
|
|
}
|
|
|
|
// Registra evento de conexão/desconexão
|
|
if (ultimoEstadoOnline !== null && ultimoEstadoOnline !== s.online) {
|
|
if (s.online) {
|
|
adicionarEvento('Galaxy S10 conectado e sincronizado', 'online');
|
|
if (window.RobotAudio) window.RobotAudio.happy();
|
|
} else {
|
|
adicionarEvento('Galaxy S10 desconectou da Central', 'alerta');
|
|
}
|
|
}
|
|
ultimoEstadoOnline = s.online;
|
|
|
|
// Bateria
|
|
if (el('overviewBattery')) {
|
|
const nivel = s.online && s.estado?.bateria?.nivel != null ? s.estado.bateria.nivel + '%' : '—';
|
|
el('overviewBattery').textContent = nivel;
|
|
if (s.estado?.bateria?.nivel != null) {
|
|
if (s.estado.bateria.nivel <= 20) {
|
|
el('overviewBattery').style.color = '#fb7185';
|
|
} else {
|
|
el('overviewBattery').style.color = '#f0f6fc';
|
|
}
|
|
}
|
|
}
|
|
if (el('overviewCharge')) {
|
|
const carregando = s.estado?.bateria?.carregando;
|
|
el('overviewCharge').textContent = carregando ? '⚡ Carregando na tomada' : 'Operando na bateria';
|
|
el('overviewCharge').style.color = carregando ? 'var(--ciano)' : 'var(--fraco)';
|
|
}
|
|
|
|
// Inteligência IA
|
|
if (el('overviewAI')) {
|
|
el('overviewAI').textContent = c.provedor === 'gemini' ? 'Google Gemini' : 'Qwen Local';
|
|
}
|
|
if (el('overviewModel')) {
|
|
el('overviewModel').textContent = c.modelo || 'IA Pronta';
|
|
}
|
|
|
|
// Biblioteca de reações
|
|
if (el('overviewReactions')) {
|
|
el('overviewReactions').textContent = Array.isArray(t.reacoes) ? t.reacoes.length : '0';
|
|
}
|
|
|
|
// Botão de recarga
|
|
if (el('overviewReload')) {
|
|
el('overviewReload').disabled = !s.online;
|
|
}
|
|
|
|
// Confirmação de recarga remota
|
|
if (reload && s.recarga?.id === reload && s.recarga.confirmada) {
|
|
el('overviewMessage').textContent = '✅ Tela atualizada: Galaxy S10 confirmou o recarregamento.';
|
|
adicionarEvento('Tela do Galaxy S10 recarregada com sucesso', 'online');
|
|
reload = null;
|
|
} else if (reload && Date.now() - start > 12000) {
|
|
el('overviewMessage').textContent = '⚠️ Sem confirmação. Reabra o app Carinha no celular.';
|
|
reload = null;
|
|
}
|
|
} catch (e) {
|
|
if (el('overviewMessage')) el('overviewMessage').textContent = e.message;
|
|
}
|
|
}
|
|
|
|
// Ação de recarregar a tela do celular
|
|
if (el('overviewReload')) {
|
|
el('overviewReload').onclick = async () => {
|
|
try {
|
|
if (window.RobotAudio) window.RobotAudio.click();
|
|
const r = await fetch('/app/recarregar', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: '{}'
|
|
});
|
|
const d = await r.json();
|
|
if (!r.ok) throw Error(d.erro);
|
|
el('overviewMessage').textContent = d.mensagem;
|
|
adicionarEvento('Solicitação de recarga enviada ao celular...');
|
|
const s = await (await fetch('/app/status')).json();
|
|
reload = s.recarga?.id;
|
|
start = Date.now();
|
|
} catch (e) {
|
|
el('overviewMessage').textContent = 'Erro: ' + e.message;
|
|
}
|
|
};
|
|
}
|
|
|
|
// Botões de Expressão Rápida (Mood Buttons)
|
|
document.querySelectorAll('.btn-mood').forEach(btn => {
|
|
btn.onclick = async () => {
|
|
const gesto = btn.getAttribute('data-gesto');
|
|
if (!gesto) return;
|
|
try {
|
|
if (window.RobotAudio) window.RobotAudio.tocarGesto(gesto);
|
|
await fetch('/gesto/' + encodeURIComponent(gesto));
|
|
adicionarEvento(`Expressão disparada: ${gesto}`);
|
|
btn.style.transform = 'scale(0.94)';
|
|
setTimeout(() => btn.style.transform = '', 160);
|
|
} catch (err) {
|
|
console.error('Falha ao acionar gesto:', err);
|
|
}
|
|
};
|
|
});
|
|
|
|
// Botão de Teste Sonoro
|
|
if (el('overviewSfxHappy')) {
|
|
el('overviewSfxHappy').onclick = () => {
|
|
if (window.RobotAudio) {
|
|
window.RobotAudio.happy();
|
|
adicionarEvento('Sintetizador gerou trinado alegre');
|
|
}
|
|
};
|
|
}
|
|
|
|
refresh();
|
|
setInterval(refresh, 3500);
|
|
})(); |