diff --git a/api/mqtt_listener.php b/api/mqtt_listener.php index c3a5ec6..a0bc6d9 100644 --- a/api/mqtt_listener.php +++ b/api/mqtt_listener.php @@ -65,9 +65,6 @@ $mqttPass = 'xupa'; $deviceSecret = 'K82A91F0'; -$bootstrapSecret = "XUPA_TEMP_SECRET_2026"; -$certPath = "/etc/mosquitto/certs/server.crt"; - /* ========================= * TLS CLIENT (8883) * ========================= */ @@ -75,50 +72,43 @@ $tlsSettings = (new ConnectionSettings()) ->setUsername($mqttUser) ->setPassword($mqttPass) ->setUseTls(true) - ->setTlsSelfSignedAllowed(true) + ->setTlsSelfSignedAllowed(true) ->setKeepAliveInterval(30) ->setConnectTimeout(5) ->setSocketTimeout(1) ->setReconnectAutomatically(true); - - -$mqttTls = new MqttClient($mqttHost, 8883, 'listener-php'); -/* ========================= - * BOOTSTRAP CLIENT (1883) - * ========================= */ -$bootstrapSettings = (new ConnectionSettings()) - ->setUsername($mqttUser) - ->setPassword($mqttPass) - ->setUseTls(false) - ->setKeepAliveInterval(30) - ->setReconnectAutomatically(true); - - -$mqttBootstrap = new MqttClient($mqttHost, 1883, 'listener-bootstrap'); +$mqttTls = new MqttClient($mqttHost, 8883, 'listener-php'); /* ========================= * SUBSCRIPTIONS TLS * ========================= */ $subscribeTls = function () use ($mqttTls, $db, $deviceSecret) { - // ========================= // STATUS ESP - // ========================= $mqttTls->subscribe('esp/+/status', function (string $topic, string $msg) use ($db, $mqttTls, $deviceSecret) { $uid = explode('/', $topic)[1] ?? null; if (!$uid) return; - $json = json_decode($msg, true); - $uptime = isset($json['uptime']) ? (int)$json['uptime'] : null; - $heap = isset($json['heap']) ? (int)$json['heap'] : null; - $rssi = isset($json['rssi']) ? (int)$json['rssi'] : null; - $broker = isset($json['broker']) ? (string)$json['broker'] : null; - $hmac = isset($json['hmac']) ? strtoupper((string)$json['hmac']) : null; + $json = json_decode($msg, true); + + // LWT do broker — marcar offline sem precisar de HMAC + if (($json['status'] ?? '') === 'offline') { + $db->prepare("UPDATE contadores SET online=0 WHERE uid=:uid") + ->execute(['uid' => $uid]); + logmsg("LWT OFFLINE $uid"); + return; + } + + $uptime = isset($json['uptime']) ? (int)$json['uptime'] : null; + $heap = isset($json['heap']) ? (int)$json['heap'] : null; + $rssi = isset($json['rssi']) ? (int)$json['rssi'] : null; + $broker = isset($json['broker']) ? (string)$json['broker'] : null; + $brightness = isset($json['brightness']) ? (int)$json['brightness'] : null; + $hmac = isset($json['hmac']) ? strtoupper((string)$json['hmac']) : null; - // Validar HMAC — apenas aceitar heartbeats assinados if ($hmac === null || $uptime === null) { logmsg("STATUS sem HMAC/uptime $uid — ignorado", LOG_ERROR); return; @@ -131,35 +121,36 @@ $subscribeTls = function () use ($mqttTls, $db, $deviceSecret) { return; } - // HMAC OK → actualizar DB $stmt = $db->prepare(" INSERT INTO contadores (uid, entradas, saidas, entradas_total, saidas_total, last_seen, online, - uptime, heap, rssi, current_broker) + uptime, heap, rssi, current_broker, brightness) VALUES (:uid, 0, 0, 0, 0, NOW(), 1, - :uptime, :heap, :rssi, :broker) + :uptime, :heap, :rssi, :broker, :brightness) ON DUPLICATE KEY UPDATE last_seen=NOW(), online=1, uptime=COALESCE(:uptime2, uptime), heap=COALESCE(:heap2, heap), rssi=COALESCE(:rssi2, rssi), - current_broker=COALESCE(:broker2, current_broker) + current_broker=COALESCE(:broker2, current_broker), + brightness=COALESCE(:brightness2, brightness) "); $stmt->execute([ - 'uid' => $uid, - 'uptime' => $uptime, - 'heap' => $heap, - 'rssi' => $rssi, - 'broker' => $broker, - 'uptime2' => $uptime, - 'heap2' => $heap, - 'rssi2' => $rssi, - 'broker2' => $broker, + 'uid' => $uid, + 'uptime' => $uptime, + 'heap' => $heap, + 'rssi' => $rssi, + 'broker' => $broker, + 'brightness' => $brightness, + 'uptime2' => $uptime, + 'heap2' => $heap, + 'rssi2' => $rssi, + 'broker2' => $broker, + 'brightness2' => $brightness, ]); - // Responder com HEARTBEAT_OK → ESP arranca serviços (sem retain) $mqttTls->publish("esp/$uid/cmd", json_encode(['cmd' => 'HEARTBEAT_OK']), 1, false); @@ -167,84 +158,20 @@ $subscribeTls = function () use ($mqttTls, $db, $deviceSecret) { }, 0); - // ========================= // PEDIDO DE HORA GLOBAL - // ========================= $mqttTls->subscribe('time/request', function (string $topic, string $msg) use ($mqttTls) { - - logmsg("Pedido global de hora"); - - $payload = json_encode([ - 'h' => (int)date('H'), - 'm' => (int)date('i') - ]); - + $payload = json_encode(['h' => (int)date('H'), 'm' => (int)date('i')]); $mqttTls->publish('time/now', $payload, 0); - logmsg("Hora enviada: $payload"); - }, 0); - // ========================= // PEDIDO DE HORA POR DEVICE - // time/request/esp_xxxxxx - // ========================= $mqttTls->subscribe('time/request/+', function (string $topic, string $msg) use ($mqttTls) { - $uid = explode('/', $topic)[2] ?? null; if (!$uid) return; - - logmsg("Pedido hora de $uid"); - - $payload = json_encode([ - 'h' => (int)date('H'), - 'm' => (int)date('i') - ]); - + $payload = json_encode(['h' => (int)date('H'), 'm' => (int)date('i')]); $mqttTls->publish("time/now/$uid", $payload, 0); - logmsg("Hora enviada para $uid"); - - }, 0); - -}; - -/* ========================= - * SUBSCRIPTIONS BOOTSTRAP - * ========================= */ -$subscribeBootstrap = function () use ($mqttBootstrap, $bootstrapSecret, $certPath) { - - $mqttBootstrap->subscribe('esp/+/bootstrap_request', function (string $topic, string $msg) - use ($mqttBootstrap, $bootstrapSecret, $certPath) { - - $uid = explode('/', $topic)[1] ?? null; - if (!$uid) return; - - logmsg("BOOTSTRAP pedido de $uid"); - - if (!file_exists($certPath)) { - logmsg("ERRO: certificado não encontrado", LOG_ERROR); - return; - } - - $cert = file_get_contents($certPath); - $certB64 = base64_encode($cert); - $ts = time(); - - $dataToSign = $certB64 . $ts; - $hmac = hash_hmac('sha256', $dataToSign, $bootstrapSecret); - - $payload = json_encode([ - 'cmd' => 'update_cert', - 'cert' => $certB64, - 'ts' => $ts, - 'hmac' => $hmac - ]); - - $mqttBootstrap->publish("esp/$uid/bootstrap_response", $payload, 0); - - logmsg("BOOTSTRAP enviado para $uid"); - }, 0); }; @@ -260,17 +187,11 @@ while (true) { $subscribeTls(); logmsg("TLS ligado"); - logmsg("Ligando Bootstrap 1883..."); - $mqttBootstrap->connect($bootstrapSettings, false); - $subscribeBootstrap(); - logmsg("Bootstrap ligado"); - $nextOfflineCheck = time() + 15; - while ($mqttTls->isConnected() && $mqttBootstrap->isConnected()) { + while ($mqttTls->isConnected()) { $mqttTls->loop(true, false); - $mqttBootstrap->loop(true, false); if (time() >= $nextOfflineCheck) { $nextOfflineCheck = time() + 15; @@ -291,11 +212,10 @@ while (true) { } $mqttTls->disconnect(); - $mqttBootstrap->disconnect(); } catch (Throwable $e) { logmsg("Erro: " . $e->getMessage(), LOG_ERROR); sleep(5); } -} \ No newline at end of file +} diff --git a/api/mqtt_publish.php b/api/mqtt_publish.php new file mode 100644 index 0000000..3718359 --- /dev/null +++ b/api/mqtt_publish.php @@ -0,0 +1,46 @@ + false, 'error' => 'parametros invalidos']); + exit; +} + +// Validar JSON +if (json_decode($payload) === null) { + http_response_code(400); + echo json_encode(['ok' => false, 'error' => 'payload invalido']); + exit; +} + +try { + $settings = (new ConnectionSettings()) + ->setUsername('xupa') + ->setPassword('xupa') + ->setUseTls(true) + ->setTlsSelfSignedAllowed(true) + ->setConnectTimeout(5) + ->setSocketTimeout(3); + + $mqtt = new MqttClient('mqtt.xupas.mywire.org', 8883, 'web-pub-' . getmypid()); + $mqtt->connect($settings, false); + $mqtt->publish("esp/$uid/cmd", $payload, 1, false); + $mqtt->disconnect(); + + echo json_encode(['ok' => true, 'uid' => $uid]); + +} catch (Throwable $e) { + http_response_code(500); + echo json_encode(['ok' => false, 'error' => $e->getMessage()]); +} diff --git a/api/set_brightness.php b/api/set_brightness.php new file mode 100644 index 0000000..15ce046 --- /dev/null +++ b/api/set_brightness.php @@ -0,0 +1,48 @@ + false, 'error' => 'vendor not found: ' . __DIR__ . '/../vendor/autoload.php']); + exit; +} +require __DIR__ . '/../vendor/autoload.php'; + +use PhpMqtt\Client\MqttClient; +use PhpMqtt\Client\ConnectionSettings; + +$uid = trim($_POST['uid'] ?? ''); +$value = (int)($_POST['value'] ?? -1); + +if ($uid === '' || $value < 0 || $value > 255) { + http_response_code(400); + echo json_encode(['ok' => false, 'error' => 'parametros invalidos']); + exit; +} + +try { + $settings = (new ConnectionSettings()) + ->setUsername('xupa') + ->setPassword('xupa') + ->setUseTls(true) + ->setTlsSelfSignedAllowed(true) + ->setConnectTimeout(5) + ->setSocketTimeout(3); + + $mqtt = new MqttClient('mqtt.xupas.mywire.org', 8883, 'web-brightness-' . getmypid()); + $mqtt->connect($settings, false); + + $payload = json_encode(['cmd' => 'BRIGHTNESS', 'value' => $value]); + $mqtt->publish("esp/$uid/cmd", $payload, 1, false); + + $mqtt->disconnect(); + + echo json_encode(['ok' => true, 'uid' => $uid, 'value' => $value]); + +} catch (Throwable $e) { + http_response_code(500); + echo json_encode(['ok' => false, 'error' => $e->getMessage()]); +} diff --git a/comandos.php b/comandos.php index df45f6e..e9f8980 100644 --- a/comandos.php +++ b/comandos.php @@ -7,71 +7,210 @@ try { PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); - // Se submeteu o formulário - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $uid = $_POST['uid'] ?? ''; - $cmd = $_POST['cmd'] ?? ''; - + // 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) { - $stmt = $pdo->prepare("INSERT INTO comandos (uid, cmd, status, queued_at) VALUES (?, ?, 'pending', NOW())"); - $stmt->execute([$uid, $cmd]); - $msg = "✅ Comando '$cmd' enviado para o dispositivo $uid!"; + $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 { - $msg = "⚠️ Escolhe um dispositivo e um comando."; + http_response_code(400); + echo json_encode(['ok' => false, 'error' => 'parametros invalidos']); } + exit; } - // Buscar lista de devices - $devices = $pdo->query("SELECT uid FROM contadores ORDER BY uid ASC")->fetchAll(); + $devices = $pdo->query("SELECT uid, COALESCE(brightness, 128) AS brightness FROM contadores ORDER BY uid ASC")->fetchAll(); } catch (Exception $e) { die("Erro: " . $e->getMessage()); } ?> - - + +
- -