- mqtt_listener.php: brightness DB sync, LWT offline, remove 1883 - set_brightness.php e mqtt_publish.php adicionados - comandos.php: tabela de brilho com valor real do ESP - console.php: pausar refresh ao editar notas Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
217 lines
8.9 KiB
PHP
217 lines
8.9 KiB
PHP
<?php
|
|
require_once __DIR__ . "/api/protecao.php";
|
|
|
|
try {
|
|
$pdo = new PDO("mysql:host=localhost;dbname=cagalhao;charset=utf8mb4", "master", "master", [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
]);
|
|
|
|
// AJAX — enviar comando via DB queue
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax'])) {
|
|
header('Content-Type: application/json');
|
|
$uid = trim($_POST['uid'] ?? '');
|
|
$cmd = trim($_POST['cmd'] ?? '');
|
|
if ($uid && $cmd) {
|
|
$pdo->prepare("INSERT INTO comandos (uid, cmd, status, queued_at) VALUES (?, ?, 'pending', NOW())")
|
|
->execute([$uid, $cmd]);
|
|
echo json_encode(['ok' => true, 'uid' => $uid, 'cmd' => $cmd]);
|
|
} else {
|
|
http_response_code(400);
|
|
echo json_encode(['ok' => false, 'error' => 'parametros invalidos']);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
$devices = $pdo->query("SELECT uid, COALESCE(brightness, 128) AS brightness FROM contadores ORDER BY uid ASC")->fetchAll();
|
|
|
|
} catch (Exception $e) {
|
|
die("Erro: " . $e->getMessage());
|
|
}
|
|
?>
|
|
<!doctype html>
|
|
<html lang="pt" data-bs-theme="dark">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Controlo de Dispositivos</title>
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
|
<style>
|
|
body { background:#0b0b0b; color:#e0e0e0; font-family:Arial,sans-serif; }
|
|
.card { background:#151515; border:1px solid #2a2a2a; }
|
|
.header-title { color:#00ffcc; text-shadow:0 0 8px rgba(0,255,180,0.4); }
|
|
.form-range::-webkit-slider-thumb { background:#00ffcc; }
|
|
.form-range::-moz-range-thumb { background:#00ffcc; }
|
|
.form-range::-webkit-slider-runnable-track { background:#2a2a2a; }
|
|
.feedback { font-size:.8rem; }
|
|
.form-select, .form-control {
|
|
background:#1e1e1e; border-color:#333; color:#e0e0e0;
|
|
}
|
|
.form-select:focus, .form-control:focus {
|
|
background:#1e1e1e; border-color:#00ffcc; color:#e0e0e0;
|
|
box-shadow:0 0 0 .2rem rgba(0,255,180,.15);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container-fluid py-4" style="max-width:1000px">
|
|
|
|
<h1 class="mb-4 header-title">🛠️ Controlo de Dispositivos</h1>
|
|
|
|
<!-- ══════════════════════════════════════
|
|
BRILHO — tabela
|
|
══════════════════════════════════════ -->
|
|
<h4 class="text-secondary mb-3">🔆 Brilho</h4>
|
|
|
|
<div class="card mb-5" style="overflow:hidden">
|
|
<table class="table table-dark table-hover mb-0" style="border-color:#2a2a2a">
|
|
<thead style="background:#1a1a1a;color:#888;font-size:.8rem;text-transform:uppercase;letter-spacing:.05em">
|
|
<tr>
|
|
<th style="width:120px;padding:.6rem 1rem">Dispositivo</th>
|
|
<th style="padding:.6rem 1rem">Brilho</th>
|
|
<th style="width:52px;padding:.6rem .5rem;text-align:center">Val</th>
|
|
<th style="width:100px;padding:.6rem 1rem"></th>
|
|
<th style="padding:.6rem 1rem"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($devices as $d):
|
|
$uid = htmlspecialchars($d['uid']); ?>
|
|
<tr style="border-color:#222">
|
|
<td style="padding:.5rem 1rem;font-family:monospace;color:#00ffcc;font-weight:700;vertical-align:middle"><?= $uid ?></td>
|
|
<td style="padding:.5rem 1rem;vertical-align:middle">
|
|
<input type="range" class="form-range bri-slider" min="0" max="255" value="<?= (int)$d['brightness'] ?>"
|
|
data-uid="<?= $uid ?>" style="margin:0">
|
|
</td>
|
|
<td style="padding:.5rem .5rem;text-align:center;vertical-align:middle;font-weight:700;color:#00ffcc">
|
|
<span class="bri-val"><?= (int)$d['brightness'] ?></span>
|
|
</td>
|
|
<td style="padding:.5rem 1rem;vertical-align:middle">
|
|
<button class="btn btn-sm bri-save w-100"
|
|
style="background:#00ffcc;color:#000;font-weight:600"
|
|
data-uid="<?= $uid ?>">💾 Gravar</button>
|
|
</td>
|
|
<td style="padding:.5rem 1rem;vertical-align:middle;font-size:.8rem">
|
|
<span class="feedback"></span>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ══════════════════════════════════════
|
|
COMANDOS
|
|
══════════════════════════════════════ -->
|
|
<h4 class="text-secondary mb-3">📡 Comandos</h4>
|
|
|
|
<div class="card p-4 mb-5" style="max-width:420px">
|
|
<div class="mb-3">
|
|
<label class="form-label text-secondary">Dispositivo</label>
|
|
<select id="cmd-uid" class="form-select">
|
|
<option value="">-- Escolhe --</option>
|
|
<?php foreach ($devices as $d): ?>
|
|
<option value="<?= htmlspecialchars($d['uid']) ?>"><?= htmlspecialchars($d['uid']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label text-secondary">Comando</label>
|
|
<select id="cmd-sel" class="form-select">
|
|
<option value="">-- Seleciona --</option>
|
|
<optgroup label="Sistema">
|
|
<option value="REBOOT">REBOOT</option>
|
|
<option value="STATUS">STATUS</option>
|
|
<option value="CLEAR_WIFI">CLEAR_WIFI</option>
|
|
</optgroup>
|
|
<optgroup label="Relógio">
|
|
<option value="CLOCK_START">CLOCK_START</option>
|
|
<option value="CLOCK_STOP">CLOCK_STOP</option>
|
|
</optgroup>
|
|
<optgroup label="Startup (grava NVS)">
|
|
<option value="STARTUP_CLOCK">Startup → Relógio</option>
|
|
<option value="STARTUP_BLANK">Startup → Branco</option>
|
|
</optgroup>
|
|
<optgroup label="Animações (só S3)">
|
|
<option value="TETRIS_START">TETRIS_START</option>
|
|
<option value="TETRIS_STOP">TETRIS_STOP</option>
|
|
<option value="STOP_ANIM">STOP_ANIM</option>
|
|
</optgroup>
|
|
</select>
|
|
</div>
|
|
<button id="cmd-btn" class="btn w-100"
|
|
style="background:#00ffcc;color:#000;font-weight:600">
|
|
🚀 Enviar Comando
|
|
</button>
|
|
<div class="feedback mt-2 text-center" id="cmd-msg"></div>
|
|
</div>
|
|
|
|
<a href="api/menu.php" class="btn btn-outline-secondary">← Menu</a>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
// ── Brilho ──────────────────────────────────────────────
|
|
document.querySelectorAll('.bri-slider').forEach(slider => {
|
|
const row = slider.closest('tr');
|
|
const val = row.querySelector('.bri-val');
|
|
const btn = row.querySelector('.bri-save');
|
|
const msg = row.querySelector('.feedback');
|
|
|
|
slider.addEventListener('input', () => val.textContent = slider.value);
|
|
|
|
btn.addEventListener('click', async () => {
|
|
btn.disabled = true;
|
|
msg.innerHTML = '<span class="text-secondary">A enviar…</span>';
|
|
try {
|
|
const fd = new FormData();
|
|
fd.append('uid', slider.dataset.uid);
|
|
fd.append('value', slider.value);
|
|
const r = await (await fetch('api/set_brightness.php', { method:'POST', body:fd })).json();
|
|
msg.innerHTML = r.ok ? '' : `<span class="text-danger">❌</span>`;
|
|
} catch(e) {
|
|
msg.innerHTML = `<span class="text-danger">❌ ${e.message}</span>`;
|
|
}
|
|
btn.disabled = false;
|
|
});
|
|
});
|
|
|
|
// ── Comandos ────────────────────────────────────────────
|
|
const MQTT_DIRECT = {
|
|
'STARTUP_CLOCK': { cmd: 'STARTUP_MODE', mode: 'clock' },
|
|
'STARTUP_BLANK': { cmd: 'STARTUP_MODE', mode: 'blank' },
|
|
};
|
|
|
|
document.getElementById('cmd-btn').addEventListener('click', async () => {
|
|
const uid = document.getElementById('cmd-uid').value;
|
|
const cmd = document.getElementById('cmd-sel').value;
|
|
const msg = document.getElementById('cmd-msg');
|
|
if (!uid || !cmd) {
|
|
msg.innerHTML = '<span class="text-warning">⚠️ Escolhe dispositivo e comando.</span>';
|
|
return;
|
|
}
|
|
msg.innerHTML = '<span class="text-secondary">A enviar…</span>';
|
|
try {
|
|
let r;
|
|
if (MQTT_DIRECT[cmd]) {
|
|
const fd = new FormData();
|
|
fd.append('uid', uid);
|
|
fd.append('payload', JSON.stringify(MQTT_DIRECT[cmd]));
|
|
r = await (await fetch('api/mqtt_publish.php', { method:'POST', body:fd })).json();
|
|
} else {
|
|
const fd = new FormData();
|
|
fd.append('ajax', '1');
|
|
fd.append('uid', uid);
|
|
fd.append('cmd', cmd);
|
|
r = await (await fetch('comandos.php', { method:'POST', body:fd })).json();
|
|
}
|
|
msg.innerHTML = r.ok
|
|
? `<span style="color:#00ffcc">✅ ${cmd} → ${uid}</span>`
|
|
: `<span class="text-danger">❌ ${r.error}</span>`;
|
|
} catch(e) {
|
|
msg.innerHTML = `<span class="text-danger">❌ ${e.message}</span>`;
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|