lazy commit

This commit is contained in:
Hickmeister
2025-01-04 21:06:25 +00:00
parent ca0007515f
commit e31bd43eaa
13 changed files with 651 additions and 135 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

5
composer.json Normal file
View File

@@ -0,0 +1,5 @@
{
"require": {
"php-mqtt/client": "^2.2"
}
}

189
composer.lock generated Normal file
View File

@@ -0,0 +1,189 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "447c16b903c1f7b070e4b4bfb41716da",
"packages": [
{
"name": "myclabs/php-enum",
"version": "1.8.4",
"source": {
"type": "git",
"url": "https://github.com/myclabs/php-enum.git",
"reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
"reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.3 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "1.*",
"vimeo/psalm": "^4.6.2"
},
"type": "library",
"autoload": {
"psr-4": {
"MyCLabs\\Enum\\": "src/"
},
"classmap": [
"stubs/Stringable.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP Enum contributors",
"homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
}
],
"description": "PHP Enum implementation",
"homepage": "http://github.com/myclabs/php-enum",
"keywords": [
"enum"
],
"support": {
"issues": "https://github.com/myclabs/php-enum/issues",
"source": "https://github.com/myclabs/php-enum/tree/1.8.4"
},
"funding": [
{
"url": "https://github.com/mnapoli",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
"type": "tidelift"
}
],
"time": "2022-08-04T09:53:51+00:00"
},
{
"name": "php-mqtt/client",
"version": "v2.2.0",
"source": {
"type": "git",
"url": "https://github.com/php-mqtt/client.git",
"reference": "8042ad93e72da8666e27168dc90670e45bdea274"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-mqtt/client/zipball/8042ad93e72da8666e27168dc90670e45bdea274",
"reference": "8042ad93e72da8666e27168dc90670e45bdea274",
"shasum": ""
},
"require": {
"myclabs/php-enum": "^1.7",
"php": "^8.0",
"psr/log": "^1.1|^2.0|^3.0"
},
"require-dev": {
"phpunit/php-invoker": "^3.0",
"phpunit/phpunit": "^9.0",
"squizlabs/php_codesniffer": "^3.5"
},
"suggest": {
"ext-redis": "Required for the RedisRepository"
},
"type": "library",
"autoload": {
"psr-4": {
"PhpMqtt\\Client\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marvin Mall",
"email": "marvin-mall@msn.com",
"role": "developer"
}
],
"description": "An MQTT client written in and for PHP.",
"keywords": [
"client",
"mqtt",
"publish",
"subscribe"
],
"support": {
"issues": "https://github.com/php-mqtt/client/issues",
"source": "https://github.com/php-mqtt/client/tree/v2.2.0"
},
"time": "2024-11-24T20:54:32+00:00"
},
{
"name": "psr/log",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/3.0.2"
},
"time": "2024-09-11T13:17:53+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

View File

@@ -2,6 +2,6 @@
session_start();
session_unset();
session_destroy();
header("Location: /login.php");
header("Location: login.php");
exit();
?>

249
public/viewPrinters.php Normal file
View File

@@ -0,0 +1,249 @@
<?php include '../src/session_check.php'; ?>
<html lang="en" data-bs-theme="light">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--favicon-->
<link rel="icon" href="../assets/images/favicon-32x32.png" type="image/png">
<!--plugins-->
<link href="../assets/plugins/vectormap/jquery-jvectormap-2.0.2.css" rel="stylesheet">
<link href="../assets/plugins/simplebar/css/simplebar.css" rel="stylesheet">
<link href="../assets/plugins/perfect-scrollbar/css/perfect-scrollbar.css" rel="stylesheet">
<link href="../assets/plugins/metismenu/css/metisMenu.min.css" rel="stylesheet">
<!-- loader-->
<link href="../assets/css/pace.min.css" rel="stylesheet"/>
<script src="../assets/js/pace.min.js"></script>
<!-- Bootstrap CSS -->
<link href="../assets/css/bootstrap.min.css" rel="stylesheet">
<link href="../assets/css/bootstrap-extended.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap" rel="stylesheet">
<link href="../assets/sass/app.css" rel="stylesheet">
<link href="../assets/css/icons.css" rel="stylesheet">
<link href='https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css' rel='stylesheet'>
<!-- Theme Style CSS -->
<link rel="stylesheet" href="../assets/sass/dark-theme.css">
<link rel="stylesheet" href="../assets/sass/semi-dark.css">
<link rel="stylesheet" href="../assets/sass/bordered-theme.css">
<title>TOD Dashboard</title>
</head>
<body>
<!--wrapper-->
<div class="wrapper">
<!--sidebar wrapper -->
<?php include '../src/nav.php'; ?>
<!--end sidebar wrapper -->
<!--start header -->
<?php include '../src/header.php'; ?>
<!--end header -->
<!--start page wrapper -->
<div class="page-wrapper">
<div class="page-content">
<!--start page content -->
<!-- Breadcrumb -->
<div class="page-breadcrumb d-none d-sm-flex align-items-center mb-3">
<div class="breadcrumb-title pe-3">Printer Dashboard</div>
<div class="ps-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0 p-0">
<li class="breadcrumb-item"><a href="index.php"><i class="bx bx-home-alt"></i></a></li>
<li class="breadcrumb-item active" aria-current="page">Your Printers</li>
</ol>
</nav>
</div>
<div class="ms-auto">
<div class="btn-group">
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
Refresh Interval: <span id="selectedIntervalText">60s</span>
</button>
<ul class="dropdown-menu dropdown-menu-end" id="intervalDropdown">
<li><a class="dropdown-item set-interval" data-interval="5000" href="#">5 seconds</a></li>
<li><a class="dropdown-item set-interval" data-interval="10000" href="#">10 seconds</a></li>
<li><a class="dropdown-item set-interval" data-interval="30000" href="#">30 seconds</a></li>
<li><a class="dropdown-item set-interval" data-interval="60000" href="#">60 seconds</a></li>
<li><a class="dropdown-item set-interval" data-interval="off" href="#">Off</a></li>
</ul>
</div>
</div>
</div>
<!-- End Breadcrumb -->
<div class="container">
<div class="main-body">
<div id="alertContainer" class="container mt-3">
<!-- Alerts will be dynamically inserted here -->
</div>
<div class="row" id="printerContainer">
<!-- Printer cards will be dynamically inserted here -->
</div>
</div>
</div>
<!--end page content -->
</div>
</div>
<!--end page wrapper -->
<!--start overlay-->
<div class="overlay mobile-toggle-icon"></div>
<!--end overlay-->
<!--Start Back To Top Button-->
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<!--End Back To Top Button-->
</div>
<!--end wrapper-->
<!-- search modal -->
<div class="modal" id="SearchModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fullscreen-md-down">
<div class="modal-content">
<div class="modal-header gap-2">
<div class="position-relative popup-search w-100">
<input class="form-control form-control-lg ps-5 border border-3 border-primary" type="search" placeholder="Search">
<span class="position-absolute top-50 search-show ms-3 translate-middle-y start-0 top-50 fs-4"><i class='bx bx-search'></i></span>
</div>
<button type="button" class="btn-close d-md-none" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
</div>
</div>
<!-- end search modal -->
<!-- Bootstrap JS -->
<script src="../assets/js/bootstrap.bundle.min.js"></script>
<!--plugins-->
<script src="../assets/js/jquery.min.js"></script>
<script src="../assets/plugins/simplebar/js/simplebar.min.js"></script>
<script src="../assets/plugins/metismenu/js/metisMenu.min.js"></script>
<script src="../assets/plugins/perfect-scrollbar/js/perfect-scrollbar.js"></script>
<script src="../assets/plugins/apexcharts-bundle/js/apexcharts.min.js"></script>
<!--app JS-->
<script src="../assets/js/app.js"></script>
<script src="../assets/js/index.js"></script>
<script src="../assets/plugins/peity/jquery.peity.min.js"></script>
<script>
$(".data-attributes span").peity("donut")
</script>
<script>
$(document).ready(function() {
let refreshInterval = 60000; // Default interval of 10s
let intervalHandler;
// Load printers asynchronously
async function loadPrinters() {
try {
const response = await $.ajax({
url: '../src/printers/getPrinters.php',
method: 'GET',
dataType: 'json',
cache: false
});
let container = $('#printerContainer');
if (response.status === 'success' && response.data.length > 0) {
response.data.forEach(function(printer) {
let existingCard = container.find(`.printer-card[data-serial="${printer.serialNumber}"]`);
let trayHTML = '';
printer.trays.forEach(tray => {
let trayColor = `#${tray.color.slice(0, 6)}`;
trayHTML += `
<div class="tray-slot text-center">
<div class="tray-circle" style="background-color: ${trayColor}; width: 40px; height: 40px; border-radius: 50%;"></div>
<p>Slot ${parseInt(tray.id) + 1}</p>
</div>`;
});
let printerCard = `
<div class="col-lg-4 printer-card" data-serial="${printer.serialNumber}">
<div class="card mb-4">
<div class="card-body text-center">
<img src="../assets/images/printer-icon.png" alt="Printer" class="rounded-circle p-1" width="110">
<div class="mt-3">
<h4>${printer.printerName}</h4>
<p class="text-secondary mb-1">Current Job: <span>${printer.jobName}</span></p>
<p class="text-muted font-size-sm">
Bed Temp: <span>${printer.bedTemp}°C</span> |
Nozzle Temp: <span>${printer.nozzleTemp}°C</span>
</p>
</div>
<div class="d-flex justify-content-center gap-4 mt-3">
${trayHTML}
</div>
<div class="mt-4 w-100">
<label for="humidityBar${printer.serialNumber}" class="form-label">AMS Humidity</label>
<div class="progress" style="height: 20px;">
<div id="humidityBar${printer.serialNumber}" class="progress-bar" role="progressbar"
style="width: ${printer.humidity * 20}%;"
aria-valuemin="1"
aria-valuemax="5">
${printer.humidity}/5
</div>
</div>
</div>
</div>
</div>
</div>`;
if (existingCard.length) {
existingCard.replaceWith(printerCard);
} else {
container.append(printerCard);
}
});
}
} catch (error) {
console.log('Failed to load printers:', error);
}
}
// Polling function
function startPolling(interval) {
clearInterval(intervalHandler);
if (interval !== 'off') {
intervalHandler = setInterval(loadPrinters, interval);
}
}
// Handle refresh dropdown
$('.set-interval').on('click', function(e) {
e.preventDefault();
let selectedInterval = $(this).data('interval');
if (selectedInterval === 'off') {
clearInterval(intervalHandler);
console.log("Auto-refresh turned off.");
} else {
refreshInterval = parseInt(selectedInterval);
startPolling(refreshInterval);
console.log(`Refresh interval set to ${refreshInterval / 1000} seconds.`);
}
$('#selectedIntervalText').text(selectedInterval === 'off' ? 'Off' : `${selectedInterval / 1000}s`);
});
loadPrinters();
startPolling(refreshInterval); // Start polling immediately
});
</script>
</body>
</html>

View File

@@ -70,7 +70,7 @@
<li>
<div class="dropdown-divider mb-0"></div>
</li>
<li><a class="dropdown-item d-flex align-items-center" href="logout.php"><i class="bx bx-log-out-circle"></i><span>Logout</span></a>
<li><a class="dropdown-item d-flex align-items-center" href="../public/logout.php"><i class="bx bx-log-out-circle"></i><span>Logout</span></a>
</li>
</ul>
</div>

View File

@@ -12,7 +12,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($user && password_verify($password, $user['password']) && !$user['disabled']) {
// Store user ID, username, and role in session
$_SESSION['user_id'] = $user['id'];
$_SESSION['userId'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role']; // Store user role
echo 'success';

View File

@@ -28,131 +28,10 @@
</ul>
</li>
<li>
<a href="javascript:;" class="has-arrow">
<div class="parent-icon"><i class="bx bx-category"></i>
</div>
<div class="menu-title">Application</div>
</a>
<ul>
<li> <a href="app-emailbox.html"><i class='bx bx-radio-circle'></i>Email</a>
</li>
<li> <a href="app-chat-box.html"><i class='bx bx-radio-circle'></i>Chat Box</a>
</li>
<li> <a href="app-file-manager.html"><i class='bx bx-radio-circle'></i>File Manager</a>
</li>
<li> <a href="app-contact-list.html"><i class='bx bx-radio-circle'></i>Contatcs</a>
</li>
<li> <a href="app-to-do.html"><i class='bx bx-radio-circle'></i>Todo List</a>
</li>
<li> <a href="app-invoice.html"><i class='bx bx-radio-circle'></i>Invoice</a>
</li>
<li> <a href="app-fullcalender.html"><i class='bx bx-radio-circle'></i>Calendar</a>
</li>
</ul>
</li>
<li class="menu-label">UI Elements</li>
<li>
<a href="widgets.html">
<div class="parent-icon"><i class='bx bx-cookie'></i>
</div>
<div class="menu-title">Widgets</div>
</a>
</li>
<li>
<a href="javascript:;" class="has-arrow">
<div class="parent-icon"><i class='bx bx-cart'></i>
</div>
<div class="menu-title">eCommerce</div>
</a>
<ul>
<li> <a href="ecommerce-products.html"><i class='bx bx-radio-circle'></i>Products</a>
</li>
<li> <a href="ecommerce-products-details.html"><i class='bx bx-radio-circle'></i>Product Details</a>
</li>
<li> <a href="ecommerce-add-new-products.html"><i class='bx bx-radio-circle'></i>Add New Products</a>
</li>
<li> <a href="ecommerce-orders.html"><i class='bx bx-radio-circle'></i>Orders</a>
</li>
</ul>
</li>
<li>
<a class="has-arrow" href="javascript:;">
<div class="parent-icon"><i class='bx bx-bookmark-heart'></i>
</div>
<div class="menu-title">Components</div>
</a>
<ul>
<li> <a href="component-alerts.html"><i class='bx bx-radio-circle'></i>Alerts</a>
</li>
<li> <a href="component-accordions.html"><i class='bx bx-radio-circle'></i>Accordions</a>
</li>
<li> <a href="component-badges.html"><i class='bx bx-radio-circle'></i>Badges</a>
</li>
<li> <a href="component-buttons.html"><i class='bx bx-radio-circle'></i>Buttons</a>
</li>
<li> <a href="component-cards.html"><i class='bx bx-radio-circle'></i>Cards</a>
</li>
<li> <a href="component-carousels.html"><i class='bx bx-radio-circle'></i>Carousels</a>
</li>
<li> <a href="component-list-groups.html"><i class='bx bx-radio-circle'></i>List Groups</a>
</li>
<li> <a href="component-media-object.html"><i class='bx bx-radio-circle'></i>Media Objects</a>
</li>
<li> <a href="component-modals.html"><i class='bx bx-radio-circle'></i>Modals</a>
</li>
<li> <a href="component-navs-tabs.html"><i class='bx bx-radio-circle'></i>Navs & Tabs</a>
</li>
<li> <a href="component-navbar.html"><i class='bx bx-radio-circle'></i>Navbar</a>
</li>
<li> <a href="component-paginations.html"><i class='bx bx-radio-circle'></i>Pagination</a>
</li>
<li> <a href="component-popovers-tooltips.html"><i class='bx bx-radio-circle'></i>Popovers & Tooltips</a>
</li>
<li> <a href="component-progress-bars.html"><i class='bx bx-radio-circle'></i>Progress</a>
</li>
<li> <a href="component-spinners.html"><i class='bx bx-radio-circle'></i>Spinners</a>
</li>
<li> <a href="component-notifications.html"><i class='bx bx-radio-circle'></i>Notifications</a>
</li>
<li> <a href="component-avtars-chips.html"><i class='bx bx-radio-circle'></i>Avatrs & Chips</a>
</li>
</ul>
</li>
<li>
<a class="has-arrow" href="javascript:;">
<div class="parent-icon"><i class="bx bx-repeat"></i>
</div>
<div class="menu-title">Content</div>
</a>
<ul>
<li> <a href="content-grid-system.html"><i class='bx bx-radio-circle'></i>Grid System</a>
</li>
<li> <a href="content-typography.html"><i class='bx bx-radio-circle'></i>Typography</a>
</li>
<li> <a href="content-text-utilities.html"><i class='bx bx-radio-circle'></i>Text Utilities</a>
</li>
</ul>
</li>
<li>
<a class="has-arrow" href="javascript:;">
<div class="parent-icon"> <i class="bx bx-donate-blood"></i>
</div>
<div class="menu-title">Icons</div>
</a>
<ul>
<li> <a href="icons-line-icons.html"><i class='bx bx-radio-circle'></i>Line Icons</a>
</li>
<li> <a href="icons-boxicons.html"><i class='bx bx-radio-circle'></i>Boxicons</a>
</li>
<li> <a href="icons-feather-icons.html"><i class='bx bx-radio-circle'></i>Feather Icons</a>
</li>
</ul>
</li>
<li>
<a href="form-froala-editor.html">
<a href="viewPrinters.php">
<div class="parent-icon"><i class='bx bx-code-alt'></i>
</div>
<div class="menu-title">Froala Editor</div>
<div class="menu-title">Printer Dashboard</div>
</a>
</li>
</ul>

View File

@@ -1,12 +1,21 @@
<?php
session_start(); // Ensure session is started to get logged-in user ID
require '../db.php';
require_once '../envLoader.php';
loadEnv(__DIR__ . '/../../.env');
header('Content-Type: application/json');
// Check if the user is logged in
if (!isset($_SESSION['userId'])) {
echo json_encode(['status' => 'error', 'message' => 'User not authenticated.']);
exit;
}
$userId = $_SESSION['userId']; // Retrieve user ID from session
// Handle POST Request
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header('Content-Type: application/json'); // Return JSON response
// Collect and sanitize form data
$printerName = filter_input(INPUT_POST, 'printerName', FILTER_SANITIZE_STRING);
$printerIp = filter_input(INPUT_POST, 'printerIp', FILTER_VALIDATE_IP);
@@ -19,10 +28,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
try {
// Prepare SQL insert statement
// Prepare SQL insert statement with user ID
$stmt = $pdo->prepare("
INSERT INTO bambuPrinters (printerName, printerIp, mqttPassword, serialNumber)
VALUES (:printerName, :printerIp, :mqttPassword, :serialNumber)
INSERT INTO bambuPrinters (printerName, printerIp, mqttPassword, serialNumber, userId)
VALUES (:printerName, :printerIp, :mqttPassword, :serialNumber, :userId)
");
// Execute the query with bound parameters
@@ -30,7 +39,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
':printerName' => $printerName,
':printerIp' => $printerIp,
':mqttPassword' => $accessCode,
':serialNumber' => $serialNumber
':serialNumber' => $serialNumber,
':userId' => $userId
]);
echo json_encode(['status' => 'success', 'message' => 'Printer successfully added!']);

View File

@@ -0,0 +1,79 @@
<?php
session_start();
require '../db.php';
header('Content-Type: application/json');
if (!isset($_SESSION['userId'])) {
echo json_encode(['status' => 'error', 'message' => 'User not authenticated.']);
exit;
}
$userId = $_SESSION['userId'];
try {
// Subquery to fetch latest telemetry for each printer
$stmt = $pdo->prepare("
SELECT p.*, t.telemetry, t.recordedAt
FROM bambuPrinters p
LEFT JOIN printerTelemetry t
ON t.id = (
SELECT id FROM printerTelemetry
WHERE printerId = p.id
ORDER BY recordedAt DESC
LIMIT 1
)
WHERE p.userId = :userId
");
$stmt->execute([':userId' => $userId]);
$printers = $stmt->fetchAll(PDO::FETCH_ASSOC);
$result = [];
foreach ($printers as $printer) {
$telemetry = json_decode($printer['telemetry'], true);
$bedTemp = isset($telemetry['print']['bed_temper']) ? $telemetry['print']['bed_temper'] : 'N/A';
$nozzleTemp = isset($telemetry['print']['device']['nozzle']['0']['temp'])
? $telemetry['print']['device']['nozzle']['0']['temp']
: 'N/A';
$jobName = isset($telemetry['subtask_name']) ? $telemetry['subtask_name'] : 'No job running';
$status = isset($telemetry['print']['gcode_state']) ? $telemetry['print']['gcode_state'] : 'Unknown';
// AMS Data
$humidity = isset($telemetry['print']['ams']['ams'][0]['humidity'])
? $telemetry['print']['ams']['ams'][0]['humidity']
: 0;
$trays = isset($telemetry['print']['ams']['ams'][0]['tray'])
? $telemetry['print']['ams']['ams'][0]['tray']
: [];
// Structure tray data for direct output
$trayData = [];
foreach ($trays as $tray) {
$trayData[] = [
'id' => $tray['id'],
'color' => isset($tray['tray_color']) ? $tray['tray_color'] : 'ccc'
];
}
$result[] = [
'printerId' => $printer['id'],
'printerName' => $printer['printerName'],
'serialNumber' => $printer['serialNumber'],
'printerIp' => $printer['printerIp'],
'bedTemp' => $bedTemp,
'nozzleTemp' => $nozzleTemp,
'jobName' => $jobName,
'status' => $status,
'humidity' => $humidity,
'trays' => $trayData,
'recordedAt' => $printer['recordedAt'] ?? 'Never'
];
}
echo json_encode(['status' => 'success', 'data' => $result]);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => 'Failed to fetch printers: ' . $e->getMessage()]);
}
?>

View File

@@ -0,0 +1,105 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require '/mnt/www-live/TechOdyssey_Designs_Dashboard/vendor/autoload.php';
require '/mnt/www-live/TechOdyssey_Designs_Dashboard/src/db.php';
require_once '/mnt/www-live/TechOdyssey_Designs_Dashboard/src/envLoader.php';
loadEnv(__DIR__ . '/../../.env');
use PhpMqtt\Client\MqttClient;
use PhpMqtt\Client\ConnectionSettings;
session_start();
// Enable asynchronous signal handling
pcntl_async_signals(true);
// MQTT Configuration from .env
$mqttPort = $_ENV['MQTT_PORT'];
$mqttUsername = $_ENV['MQTT_USERNAME'];
$clientId = 'printer_monitor_' . uniqid();
// Fetch printers associated with the logged-in user
try {
$stmt = $pdo->prepare("SELECT * FROM bambuPrinters");
$stmt->execute();
$printers = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die(json_encode(['status' => 'error', 'message' => 'Failed to fetch printers.']));
}
// Process 2 messages per printer
$maxMessagesPerPrinter = 2;
try {
foreach ($printers as $printer) {
echo "Connecting to " . $printer['printerName'] . " (" . $printer['serialNumber'] . ")...\n";
echo "IP Address: " . $printer['printerIp'] . "\n";
echo "MQTT Password: " . $printer['mqttPassword'] . "\n";
$mqttPassword = $printer['mqttPassword']; // Use mqttPassword from DB
$printerIp = $printer['printerIp']; // Printer IP from DB
$messageCount = 0; // Reset counter per printer
$mqtt = new MqttClient($printerIp, $mqttPort, $clientId);
// Handle SIGINT (CTRL + C) to gracefully exit
pcntl_signal(SIGINT, function () use ($mqtt) {
echo "Interrupt received. Disconnecting MQTT...\n";
$mqtt->interrupt();
});
$connectionSettings = (new ConnectionSettings())
->setUsername($mqttUsername)
->setPassword($mqttPassword)
->setKeepAliveInterval(60)
->setUseTls(true)
->setTlsVerifyPeer(false)
->setTlsVerifyPeerName(false);
// Establish Connection
$mqtt->connect($connectionSettings, true);
// Subscribe to the printer's report topic
$topic = "device/{$printer['serialNumber']}/report";
$mqtt->subscribe($topic, function (string $topic, string $message) use (
$printer, $pdo, &$messageCount, $maxMessagesPerPrinter, $mqtt
) {
echo "[" . $printer['printerName'] . "] [$topic] $message\n";
// Store full JSON telemetry report
try {
$stmt = $pdo->prepare("
INSERT INTO printerTelemetry
(printerId, telemetry)
VALUES
(:printerId, :telemetry)
");
$stmt->execute([
':printerId' => $printer['id'],
':telemetry' => $message
]);
} catch (PDOException $e) {
error_log("Failed to insert telemetry: " . $e->getMessage());
}
$messageCount++;
// Stop loop after 2 messages for this printer
if ($messageCount >= $maxMessagesPerPrinter) {
echo "Processed $maxMessagesPerPrinter messages for " . $printer['printerName'] . ". Disconnecting...\n";
$mqtt->interrupt();
}
}, 0);
// Start Listening for Messages
$mqtt->loop(true); // Loop until interrupted after processing 2 messages
$mqtt->disconnect();
}
} catch (Exception $e) {
die(json_encode(['status' => 'error', 'message' => 'MQTT connection failed. ' . $e->getMessage()]));
}
?>

View File

@@ -1,7 +1,7 @@
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header("Location: public/login.php");
if (!isset($_SESSION['userId'])) {
header("Location: login.php");
exit();
}
?>

View File

@@ -38,7 +38,7 @@
<?php include '../src/nav.php'; ?>
<!--end sidebar wrapper -->
<!--start header -->
<?php include '../src/nav.php'; ?>
<?php include '../src/header.php'; ?>
<!--end header -->
<!--start page wrapper -->