L'appel nmcli dans get_config_sqlite bloquait les workers Apache. Le statut WiFi est maintenant gere uniquement par les scripts shell (connexion.sh, forget_wifi.sh, boot_hotspot.sh). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1960 lines
67 KiB
PHP
Executable File
1960 lines
67 KiB
PHP
Executable File
<?php
|
|
//Prevents caching → Adds headers to ensure fresh response.
|
|
// to test this page http://192.168.1.127/html/launcher.php?type=get_config_scripts_sqlite
|
|
header("Content-Type: application/json");
|
|
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
|
header("Pragma: no-cache");
|
|
|
|
$database_path = "/var/www/nebuleair_pro_4g/sqlite/sensors.db";
|
|
|
|
|
|
$type=$_GET['type'];
|
|
|
|
if ($type == "get_npm_sqlite_data") {
|
|
//echo "Getting data from sqlite database";
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
// Fetch the last 30 records
|
|
$stmt = $db->query("SELECT timestamp, PM1, PM25, PM10 FROM data_NPM ORDER BY timestamp DESC LIMIT 30");
|
|
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
$reversedData = array_reverse($data); // Reverse the order
|
|
|
|
|
|
echo json_encode($reversedData);
|
|
} catch (PDOException $e) {
|
|
echo json_encode(["error" => $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
*/
|
|
//GETING data from config_table (SQLite DB)
|
|
if ($type == "get_config_sqlite") {
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
// Get all main configuration entries
|
|
$config_query = $db->query("SELECT key, value, type FROM config_table");
|
|
$config_data = $config_query->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Convert data types according to their 'type' field
|
|
$result = [];
|
|
foreach ($config_data as $item) {
|
|
$key = $item['key'];
|
|
$value = $item['value'];
|
|
$type = $item['type'];
|
|
|
|
// Convert value based on its type
|
|
switch ($type) {
|
|
case 'bool':
|
|
$parsed_value = ($value == '1' || $value == 'true') ? true : false;
|
|
break;
|
|
case 'int':
|
|
$parsed_value = intval($value);
|
|
break;
|
|
case 'float':
|
|
$parsed_value = floatval($value);
|
|
break;
|
|
default:
|
|
$parsed_value = $value;
|
|
}
|
|
|
|
$result[$key] = $parsed_value;
|
|
}
|
|
|
|
// Return JSON response
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result, JSON_PRETTY_PRINT);
|
|
|
|
} catch (PDOException $e) {
|
|
// Return error as JSON
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
// GET language preference from SQLite
|
|
if ($type == "get_language") {
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
$stmt = $db->prepare("SELECT value FROM config_table WHERE key = 'language'");
|
|
$stmt->execute();
|
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
$language = $result ? $result['value'] : 'fr'; // Default to French
|
|
echo json_encode(['language' => $language]);
|
|
} catch (Exception $e) {
|
|
echo json_encode(['language' => 'fr', 'error' => $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
// SET language preference in SQLite
|
|
if ($type == "set_language") {
|
|
$language = $_GET['language'];
|
|
|
|
// Validate language (only allow fr or en)
|
|
if (!in_array($language, ['fr', 'en'])) {
|
|
echo json_encode(['success' => false, 'error' => 'Invalid language']);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
$stmt = $db->prepare("UPDATE config_table SET value = ? WHERE key = 'language'");
|
|
$stmt->execute([$language]);
|
|
|
|
echo json_encode(['success' => true, 'language' => $language]);
|
|
} catch (Exception $e) {
|
|
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
*/
|
|
//GETING data from config_scrips_table (SQLite DB)
|
|
if ($type == "get_config_scripts_sqlite") {
|
|
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
// Get all main configuration entries
|
|
$config_query = $db->query("SELECT * FROM config_scripts_table");
|
|
$config_data = $config_query->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Convert data types according to their 'type' field
|
|
$result = [];
|
|
foreach ($config_data as $item) {
|
|
$script_path = $item['script_path'];
|
|
$enabled = $item['enabled'];
|
|
|
|
// Convert the enabled field to a proper boolean
|
|
$result[$script_path] = ($enabled == 1);
|
|
}
|
|
|
|
// Return JSON response
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
|
|
|
} catch (PDOException $e) {
|
|
// Return error as JSON
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
*/
|
|
//GETING data from envea_sondes_table (SQLite DB)
|
|
if ($type == "get_envea_sondes_table_sqlite") {
|
|
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
// Get all entries from envea_sondes_table
|
|
$query = $db->query("SELECT id, connected, port, name, coefficient FROM envea_sondes_table");
|
|
$data = $query->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Convert data types appropriately
|
|
$result = [];
|
|
foreach ($data as $item) {
|
|
// Create object for each sonde with proper data types
|
|
$sonde = [
|
|
'id' => (int)$item['id'],
|
|
'connected' => $item['connected'] == 1, // Convert to boolean
|
|
'port' => $item['port'],
|
|
'name' => $item['name'],
|
|
'coefficient' => (float)$item['coefficient'] // Convert to float
|
|
];
|
|
|
|
// Add to results array
|
|
$result[] = $sonde;
|
|
}
|
|
|
|
|
|
// Return JSON response
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
|
|
|
} catch (PDOException $e) {
|
|
// Return error as JSON
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//UPDATING the config_table from SQLite DB
|
|
if ($type == "update_config_sqlite") {
|
|
$param = $_GET['param'] ?? null;
|
|
$value = $_GET['value'] ?? null;
|
|
|
|
if ($param === null || $value === null) {
|
|
echo json_encode(["error" => "Missing parameter or value"]);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
// First, check if parameter exists and get its type
|
|
$checkStmt = $db->prepare("SELECT type FROM config_table WHERE key = :param");
|
|
$checkStmt->bindParam(':param', $param);
|
|
$checkStmt->execute();
|
|
$result = $checkStmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($result) {
|
|
// Parameter exists, determine type and update
|
|
$type = $result['type'];
|
|
|
|
// Convert value according to type if needed
|
|
$convertedValue = $value;
|
|
if ($type == "bool") {
|
|
// Convert various boolean representations to 0/1
|
|
$convertedValue = (filter_var($value, FILTER_VALIDATE_BOOLEAN)) ? "1" : "0";
|
|
} elseif ($type == "int") {
|
|
$convertedValue = (string)intval($value);
|
|
} elseif ($type == "float") {
|
|
$convertedValue = (string)floatval($value);
|
|
}
|
|
|
|
// Update the value
|
|
$updateStmt = $db->prepare("UPDATE config_table SET value = :value WHERE key = :param");
|
|
$updateStmt->bindParam(':value', $convertedValue);
|
|
$updateStmt->bindParam(':param', $param);
|
|
$updateStmt->execute();
|
|
|
|
echo json_encode([
|
|
"success" => true,
|
|
"message" => "Configuration updated successfully",
|
|
"param" => $param,
|
|
"value" => $convertedValue,
|
|
"type" => $type
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
"error" => "Parameter not found in configuration",
|
|
"param" => $param
|
|
]);
|
|
}
|
|
} catch (PDOException $e) {
|
|
echo json_encode(["error" => $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
|
|
//UPDATING the envea_sondes_table table from SQLite DB
|
|
if ($type == "update_sonde") {
|
|
$id = $_GET['id'] ?? null;
|
|
$field = $_GET['field'] ?? null;
|
|
$value = $_GET['value'] ?? null;
|
|
|
|
// Validate parameters
|
|
if ($id === null || $field === null || $value === null) {
|
|
echo json_encode([
|
|
"success" => false,
|
|
"error" => "Missing required parameters (id, field, or value)"
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Validate field name (whitelist approach for security)
|
|
$allowed_fields = ['connected', 'port', 'name', 'coefficient'];
|
|
if (!in_array($field, $allowed_fields)) {
|
|
echo json_encode([
|
|
"success" => false,
|
|
"error" => "Invalid field name: " . $field
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
// Connect to the database
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
// Check if the sonde exists
|
|
$checkStmt = $db->prepare("SELECT id FROM envea_sondes_table WHERE id = :id");
|
|
$checkStmt->bindParam(':id', $id, PDO::PARAM_INT);
|
|
$checkStmt->execute();
|
|
|
|
if (!$checkStmt->fetch()) {
|
|
echo json_encode([
|
|
"success" => false,
|
|
"error" => "Sonde with ID $id not found"
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Process value based on field type
|
|
if ($field == 'connected') {
|
|
// Convert to integer (0 or 1)
|
|
$processedValue = filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 1 : 0;
|
|
$paramType = PDO::PARAM_INT;
|
|
} else if ($field == 'coefficient') {
|
|
// Convert to float
|
|
$processedValue = floatval($value);
|
|
$paramType = PDO::PARAM_STR; // SQLite doesn't have PARAM_FLOAT
|
|
} else {
|
|
// For text fields (port, name)
|
|
$processedValue = $value;
|
|
$paramType = PDO::PARAM_STR;
|
|
}
|
|
|
|
// Update the sonde record
|
|
$updateStmt = $db->prepare("UPDATE envea_sondes_table SET $field = :value WHERE id = :id");
|
|
$updateStmt->bindParam(':value', $processedValue, $paramType);
|
|
$updateStmt->bindParam(':id', $id, PDO::PARAM_INT);
|
|
$updateStmt->execute();
|
|
|
|
// Return success response
|
|
echo json_encode([
|
|
"success" => true,
|
|
"message" => "Sonde $id updated successfully",
|
|
"field" => $field,
|
|
"value" => $processedValue
|
|
]);
|
|
|
|
} catch (PDOException $e) {
|
|
// Return error as JSON
|
|
echo json_encode([
|
|
"success" => false,
|
|
"error" => "Database error: " . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
//update the config (old JSON updating)
|
|
if ($type == "update_config") {
|
|
echo "updating.... ";
|
|
$param=$_GET['param'];
|
|
$value=$_GET['value'];
|
|
$configFile = '../config.json';
|
|
// Convert value to boolean, integer, or string
|
|
if ($value === "true") {
|
|
$value = true; // Convert "true" string to boolean true
|
|
} elseif ($value === "false") {
|
|
$value = false; // Convert "false" string to boolean false
|
|
} elseif (is_numeric($value)) {
|
|
$value = $value + 0; // Convert numeric strings to int or float
|
|
}
|
|
$configData = json_decode(file_get_contents($configFile), true);
|
|
$configData[$param] = $value;
|
|
file_put_contents($configFile, json_encode($configData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
|
echo "Config updated!";
|
|
}
|
|
|
|
if ($type == "getModem_busy") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/check_running.py';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "RTC_time") {
|
|
$time = shell_exec("date '+%d/%m/%Y %H:%M:%S'");
|
|
echo $time;
|
|
}
|
|
|
|
if ($type == "sys_RTC_module_time") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/RTC/read.py';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "sara_ping") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_ping.py';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "sara_psd_setup") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/R5/setPDP.py';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
|
|
if ($type == "git_pull") {
|
|
$command = 'sudo git pull';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "update_firmware") {
|
|
// Execute the comprehensive update script
|
|
$command = 'sudo /var/www/nebuleair_pro_4g/update_firmware.sh 2>&1';
|
|
$output = shell_exec($command);
|
|
|
|
// Return the output as JSON for better web display
|
|
header('Content-Type: application/json');
|
|
echo json_encode([
|
|
'success' => true,
|
|
'output' => $output,
|
|
'timestamp' => date('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
|
|
if ($type == "upload_firmware") {
|
|
// Firmware update via ZIP file upload (offline mode)
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['success' => false, 'message' => 'POST method required']);
|
|
exit;
|
|
}
|
|
|
|
// Check file upload
|
|
if (!isset($_FILES['firmware_file']) || $_FILES['firmware_file']['error'] !== UPLOAD_ERR_OK) {
|
|
$upload_errors = [
|
|
UPLOAD_ERR_INI_SIZE => 'File exceeds server upload limit',
|
|
UPLOAD_ERR_FORM_SIZE => 'File exceeds form upload limit',
|
|
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
|
|
UPLOAD_ERR_NO_FILE => 'No file was uploaded',
|
|
UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary folder',
|
|
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
|
|
];
|
|
$error_code = $_FILES['firmware_file']['error'] ?? UPLOAD_ERR_NO_FILE;
|
|
$error_msg = $upload_errors[$error_code] ?? 'Unknown upload error';
|
|
echo json_encode(['success' => false, 'message' => $error_msg]);
|
|
exit;
|
|
}
|
|
|
|
$file = $_FILES['firmware_file'];
|
|
|
|
// Validate extension
|
|
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
|
if ($ext !== 'zip') {
|
|
echo json_encode(['success' => false, 'message' => 'Only .zip files are allowed']);
|
|
exit;
|
|
}
|
|
|
|
// Validate size (50MB max)
|
|
if ($file['size'] > 50 * 1024 * 1024) {
|
|
echo json_encode(['success' => false, 'message' => 'File too large (max 50MB)']);
|
|
exit;
|
|
}
|
|
|
|
// Get current version before update
|
|
$old_version = 'unknown';
|
|
if (file_exists('/var/www/nebuleair_pro_4g/VERSION')) {
|
|
$old_version = trim(file_get_contents('/var/www/nebuleair_pro_4g/VERSION'));
|
|
}
|
|
|
|
// Prepare extraction directory
|
|
$tmp_dir = '/tmp/nebuleair_update';
|
|
$extract_dir = "$tmp_dir/extracted";
|
|
shell_exec("rm -rf $tmp_dir");
|
|
mkdir($extract_dir, 0755, true);
|
|
|
|
// Move uploaded file
|
|
$zip_path = "$tmp_dir/firmware.zip";
|
|
if (!move_uploaded_file($file['tmp_name'], $zip_path)) {
|
|
echo json_encode(['success' => false, 'message' => 'Failed to move uploaded file']);
|
|
exit;
|
|
}
|
|
|
|
// Extract ZIP
|
|
$unzip_output = shell_exec("unzip -o '$zip_path' -d '$extract_dir' 2>&1");
|
|
|
|
// Detect project root folder (Gitea creates nebuleair_pro_4g-main/ inside the zip)
|
|
$source_dir = $extract_dir;
|
|
$entries = scandir($extract_dir);
|
|
$subdirs = array_filter($entries, function($e) use ($extract_dir) {
|
|
return $e !== '.' && $e !== '..' && is_dir("$extract_dir/$e");
|
|
});
|
|
|
|
if (count($subdirs) === 1) {
|
|
$subdir = reset($subdirs);
|
|
$candidate = "$extract_dir/$subdir";
|
|
if (file_exists("$candidate/VERSION")) {
|
|
$source_dir = $candidate;
|
|
}
|
|
}
|
|
|
|
// Validate VERSION exists in the archive
|
|
if (!file_exists("$source_dir/VERSION")) {
|
|
shell_exec("rm -rf $tmp_dir");
|
|
echo json_encode(['success' => false, 'message' => 'Invalid archive: VERSION file not found']);
|
|
exit;
|
|
}
|
|
|
|
$new_version = trim(file_get_contents("$source_dir/VERSION"));
|
|
|
|
// Execute update script
|
|
$command = "sudo /var/www/nebuleair_pro_4g/update_firmware_from_file.sh '$source_dir' 2>&1";
|
|
$output = shell_exec($command);
|
|
|
|
// Cleanup (also done in script, but just in case)
|
|
shell_exec("rm -rf $tmp_dir");
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'output' => $output,
|
|
'old_version' => $old_version,
|
|
'new_version' => $new_version,
|
|
'timestamp' => date('Y-m-d H:i:s')
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
if ($type == "set_RTC_withNTP") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/RTC/set_with_NTP.py';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "set_RTC_withBrowser") {
|
|
$time = $_GET['time'];
|
|
// Validate time format
|
|
if (!strtotime($time)) {
|
|
echo json_encode(['success' => false, 'message' => 'Invalid time format']);
|
|
exit;
|
|
}
|
|
// Convert to RTC-compatible format (e.g., 'YYYY-MM-DD HH:MM:SS')
|
|
$rtc_time = date('Y-m-d H:i:s', strtotime($time));
|
|
|
|
// Execute Python script to update the RTC
|
|
$command = escapeshellcmd("sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/RTC/set_with_browserTime.py '$rtc_time'");
|
|
$output = shell_exec($command);
|
|
if ($output === null) {
|
|
echo json_encode(['success' => false, 'message' => 'Failed to update RTC']);
|
|
} else {
|
|
echo json_encode(['success' => true, 'message' => 'RTC updated successfully']);
|
|
}
|
|
}
|
|
|
|
|
|
if ($type == "clear_loopLogs") {
|
|
$response = array();
|
|
|
|
try {
|
|
$logPath = '/var/www/nebuleair_pro_4g/logs/master.log';
|
|
|
|
// Check if file exists and is writable
|
|
if (!file_exists($logPath)) {
|
|
throw new Exception("Log file does not exist");
|
|
}
|
|
|
|
if (!is_writable($logPath)) {
|
|
throw new Exception("Log file is not writable");
|
|
}
|
|
|
|
// Execute the command
|
|
$command = 'truncate -s 0 ' . escapeshellarg($logPath);
|
|
$output = shell_exec($command . ' 2>&1');
|
|
|
|
// Check if there was any error output
|
|
if (!empty($output)) {
|
|
throw new Exception("Command error: " . $output);
|
|
}
|
|
|
|
// Success response
|
|
$response = array(
|
|
'status' => 'success',
|
|
'message' => 'Logs cleared successfully',
|
|
'timestamp' => date('Y-m-d H:i:s')
|
|
);
|
|
} catch (Exception $e) {
|
|
// Error response
|
|
$response = array(
|
|
'status' => 'error',
|
|
'message' => $e->getMessage(),
|
|
'timestamp' => date('Y-m-d H:i:s')
|
|
);
|
|
}
|
|
|
|
// Set content type to JSON
|
|
header('Content-Type: application/json');
|
|
|
|
// Return the JSON response
|
|
echo json_encode($response);
|
|
exit;
|
|
}
|
|
|
|
if ($type == "database_size") {
|
|
|
|
// Path to the SQLite database file
|
|
$databasePath = '/var/www/nebuleair_pro_4g/sqlite/sensors.db';
|
|
|
|
// Check if the file exists
|
|
if (file_exists($databasePath)) {
|
|
try {
|
|
// Connect to the SQLite database
|
|
$db = new PDO("sqlite:$databasePath");
|
|
|
|
// Get the file size in bytes
|
|
$fileSizeBytes = filesize($databasePath);
|
|
|
|
// Convert the file size to human-readable formats
|
|
$fileSizeKilobytes = $fileSizeBytes / 1024; // KB
|
|
$fileSizeMegabytes = $fileSizeKilobytes / 1024; // MB
|
|
|
|
|
|
// Prepare the JSON response
|
|
$data = [
|
|
'path' => $databasePath,
|
|
'size_bytes' => $fileSizeBytes,
|
|
'size_kilobytes' => round($fileSizeKilobytes, 2),
|
|
'size_megabytes' => round($fileSizeMegabytes, 2),
|
|
];
|
|
|
|
// Output the JSON response
|
|
echo json_encode($data, JSON_PRETTY_PRINT);
|
|
} catch (PDOException $e) {
|
|
// Handle database connection errors
|
|
echo json_encode([
|
|
'error' => 'Database query failed: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
} else {
|
|
// Handle error if the file doesn't exist
|
|
echo json_encode([
|
|
'error' => 'Database file not found',
|
|
'path' => $databasePath
|
|
]);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if ($type == "db_table_stats") {
|
|
$databasePath = '/var/www/nebuleair_pro_4g/sqlite/sensors.db';
|
|
|
|
if (file_exists($databasePath)) {
|
|
try {
|
|
$db = new PDO("sqlite:$databasePath");
|
|
|
|
// Database file size
|
|
$fileSizeBytes = filesize($databasePath);
|
|
$fileSizeMB = round($fileSizeBytes / (1024 * 1024), 2);
|
|
|
|
// Sensor data tables to inspect
|
|
$tables = ['data_NPM', 'data_NPM_5channels', 'data_BME280', 'data_envea', 'data_WIND', 'data_MPPT', 'data_NOISE', 'data_MHZ19'];
|
|
|
|
$tableStats = [];
|
|
foreach ($tables as $tableName) {
|
|
// Check if table exists
|
|
$check = $db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='$tableName'");
|
|
if ($check->fetch()) {
|
|
$countResult = $db->query("SELECT COUNT(*) as cnt FROM $tableName")->fetch();
|
|
$count = (int)$countResult['cnt'];
|
|
|
|
$oldest = null;
|
|
$newest = null;
|
|
if ($count > 0) {
|
|
$oldestResult = $db->query("SELECT MIN(timestamp) as ts FROM $tableName")->fetch();
|
|
$newestResult = $db->query("SELECT MAX(timestamp) as ts FROM $tableName")->fetch();
|
|
$oldest = $oldestResult['ts'];
|
|
$newest = $newestResult['ts'];
|
|
}
|
|
|
|
$tableStats[] = [
|
|
'name' => $tableName,
|
|
'count' => $count,
|
|
'oldest' => $oldest,
|
|
'newest' => $newest
|
|
];
|
|
}
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'size_mb' => $fileSizeMB,
|
|
'size_bytes' => $fileSizeBytes,
|
|
'tables' => $tableStats
|
|
]);
|
|
} catch (PDOException $e) {
|
|
echo json_encode(['success' => false, 'error' => 'Database query failed: ' . $e->getMessage()]);
|
|
}
|
|
} else {
|
|
echo json_encode(['success' => false, 'error' => 'Database file not found']);
|
|
}
|
|
}
|
|
|
|
if ($type == "download_full_table") {
|
|
$databasePath = '/var/www/nebuleair_pro_4g/sqlite/sensors.db';
|
|
$table = $_GET['table'] ?? '';
|
|
|
|
// Whitelist of allowed tables
|
|
$allowedTables = ['data_NPM', 'data_NPM_5channels', 'data_BME280', 'data_envea', 'data_WIND', 'data_MPPT', 'data_NOISE', 'data_MHZ19'];
|
|
|
|
if (!in_array($table, $allowedTables)) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['error' => 'Invalid table name']);
|
|
exit;
|
|
}
|
|
|
|
// CSV headers per table
|
|
$csvHeaders = [
|
|
'data_NPM' => 'TimestampUTC,PM1,PM2.5,PM10,Temperature_sensor,Humidity_sensor',
|
|
'data_NPM_5channels' => 'TimestampUTC,PM_ch1,PM_ch2,PM_ch3,PM_ch4,PM_ch5',
|
|
'data_BME280' => 'TimestampUTC,Temperature,Humidity,Pressure',
|
|
'data_envea' => 'TimestampUTC,NO2,H2S,NH3,CO,O3,SO2',
|
|
'data_WIND' => 'TimestampUTC,Wind_speed_kmh,Wind_direction_V',
|
|
'data_MPPT' => 'TimestampUTC,Battery_voltage,Battery_current,Solar_voltage,Solar_power,Charger_status',
|
|
'data_NOISE' => 'TimestampUTC,Current_LEQ,DB_A_value',
|
|
'data_MHZ19' => 'TimestampUTC,CO2_ppm'
|
|
];
|
|
|
|
try {
|
|
$db = new PDO("sqlite:$databasePath");
|
|
$rows = $db->query("SELECT * FROM $table ORDER BY timestamp ASC")->fetchAll(PDO::FETCH_NUM);
|
|
|
|
header('Content-Type: text/csv; charset=utf-8');
|
|
header('Content-Disposition: attachment; filename="' . $table . '_full.csv"');
|
|
|
|
$output = fopen('php://output', 'w');
|
|
// Write header
|
|
fputcsv($output, explode(',', $csvHeaders[$table]));
|
|
// Write data rows
|
|
foreach ($rows as $row) {
|
|
fputcsv($output, $row);
|
|
}
|
|
fclose($output);
|
|
} catch (PDOException $e) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['error' => 'Database query failed: ' . $e->getMessage()]);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
if ($type == "linux_disk") {
|
|
$command = 'df -h /';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "linux_memory") {
|
|
$command = 'free -h';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "wifi_status") {
|
|
header('Content-Type: application/json');
|
|
|
|
$result = array(
|
|
'connected' => false,
|
|
'mode' => 'unknown',
|
|
'ssid' => '',
|
|
'ip' => '',
|
|
'hostname' => ''
|
|
);
|
|
|
|
// Get hostname
|
|
$result['hostname'] = trim(shell_exec('hostname'));
|
|
|
|
// Get wlan0 connection info
|
|
$connection = trim(shell_exec("nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2"));
|
|
|
|
if (!empty($connection) && $connection != '--') {
|
|
$result['connected'] = true;
|
|
$result['ssid'] = $connection;
|
|
|
|
// Check if it's a hotspot
|
|
if (strpos(strtolower($connection), 'hotspot') !== false || strpos($connection, 'nebuleair') !== false) {
|
|
$result['mode'] = 'hotspot';
|
|
} else {
|
|
$result['mode'] = 'wifi';
|
|
}
|
|
|
|
// Get IP address
|
|
$ip = trim(shell_exec("nmcli -t -f IP4.ADDRESS device show wlan0 2>/dev/null | head -1 | cut -d: -f2 | cut -d/ -f1"));
|
|
if (!empty($ip)) {
|
|
$result['ip'] = $ip;
|
|
}
|
|
} else {
|
|
// Check if eth0 is connected
|
|
$eth_ip = trim(shell_exec("nmcli -t -f IP4.ADDRESS device show eth0 2>/dev/null | head -1 | cut -d: -f2 | cut -d/ -f1"));
|
|
if (!empty($eth_ip)) {
|
|
$result['connected'] = true;
|
|
$result['mode'] = 'ethernet';
|
|
$result['ssid'] = 'Ethernet';
|
|
$result['ip'] = $eth_ip;
|
|
}
|
|
}
|
|
|
|
echo json_encode($result);
|
|
}
|
|
|
|
if ($type == "sshTunnel") {
|
|
$ssh_port=$_GET['ssh_port'];
|
|
$command = 'sudo ssh -i /var/www/.ssh/id_rsa -f -N -R "'.$ssh_port.':localhost:22" -p 50221 -o StrictHostKeyChecking=no "airlab_server1@aircarto.fr"';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "reboot") {
|
|
$command = 'sudo reboot';
|
|
$output = shell_exec($command);
|
|
}
|
|
|
|
if ($type == "npm") {
|
|
$port=$_GET['port'];
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/NPM/get_data.py ' . $port;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "npm_firmware") {
|
|
$port=$_GET['port'];
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/NPM/firmware_version.py ' . $port;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "envea") {
|
|
$port=$_GET['port'];
|
|
$name=$_GET['name'];
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/envea/read_value.py ' . $port;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "envea_debug") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/envea/read_value_v2.py -d 2>&1';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "noise") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/sound_meter/read.py';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "BME280") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/BME280/read.py';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "mhz19") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/MH-Z19/get_data.py ttyAMA4';
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
|
|
if ($type == "table_mesure") {
|
|
$table=$_GET['table'];
|
|
$limit=$_GET['limit'];
|
|
$download=$_GET['download'];
|
|
|
|
if ($download==="false") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/read.py '.$table.' '.$limit;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
} else{
|
|
$start_date=$_GET['start_date'];
|
|
$end_date=$_GET['end_date'];
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/read_select_date.py '.$table.' '.$start_date.' '.$end_date;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
}
|
|
|
|
# SARA R4 COMMANDS
|
|
if ($type == "sara") {
|
|
$port=$_GET['port'];
|
|
$sara_command=$_GET['command'];
|
|
$sara_command = escapeshellcmd($sara_command);
|
|
$timeout=$_GET['timeout'];
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara.py ' . $port . ' ' . $sara_command . ' ' . $timeout;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
# SARA R4 COMMANDS (MQTT)
|
|
if ($type == "sara_getMQTT_config") {
|
|
$port=$_GET['port'];
|
|
$timeout=$_GET['timeout'];
|
|
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/MQTT/get_config.py ' . $port . ' ' . $timeout;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
# SARA R4 COMMANDS (MQTT)
|
|
if ($type == "sara_getMQTT_login_logout") {
|
|
$port=$_GET['port'];
|
|
$timeout=$_GET['timeout'];
|
|
$login_logout=$_GET['login_logout'];
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/MQTT/login_logout.py ' . $port . ' ' . $login_logout . ' ' . $timeout;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
# SARA R4 COMMANDS (MQTT -> publish)
|
|
if ($type == "sara_MQTT_publish") {
|
|
$port=$_GET['port'];
|
|
$timeout=$_GET['timeout'];
|
|
$message=$_GET['message'];
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/MQTT/publish.py ' . $port . ' ' . $message . ' ' . $timeout;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
#Connect to network
|
|
if ($type == "sara_connectNetwork") {
|
|
$port=$_GET['port'];
|
|
$timeout=$_GET['timeout'];
|
|
$networkID=$_GET['networkID'];
|
|
$param="SARA_R4_neworkID";
|
|
|
|
//echo "updating SARA_R4_networkID in config file";
|
|
|
|
//OLD way to store data (JSON file)
|
|
|
|
// Convert `networkID` to an integer (or float if needed)
|
|
//$networkID = is_numeric($networkID) ? (strpos($networkID, '.') !== false ? (float)$networkID : (int)$networkID) : 0;
|
|
#save to config.json
|
|
//$configFile = '/var/www/nebuleair_pro_4g/config.json';
|
|
// Read the JSON file
|
|
//$jsonData = file_get_contents($configFile);
|
|
// Decode JSON data into an associative array
|
|
//$config = json_decode($jsonData, true);
|
|
// Check if decoding was successful
|
|
//if ($config === null) {
|
|
// die("Error: Could not decode JSON file.");
|
|
//}
|
|
// Update the value of SARA_R4_networkID
|
|
//$config['SARA_R4_neworkID'] = $networkID; // Replace 42 with the desired value
|
|
// Encode the array back to JSON with pretty printing
|
|
//$newJsonData = json_encode($config, JSON_PRETTY_PRINT);
|
|
// Check if encoding was successful
|
|
//if ($newJsonData === false) {
|
|
// die("Error: Could not encode JSON data.");
|
|
//}
|
|
|
|
// Write the updated JSON back to the file
|
|
//if (file_put_contents($configFile, $newJsonData) === false) {
|
|
// die("Error: Could not write to JSON file.");
|
|
//}
|
|
|
|
//echo "SARA_R4_networkID updated successfully.";
|
|
|
|
//NEW way to store data -> use SQLITE
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
$updateStmt = $db->prepare("UPDATE config_table SET value = :value WHERE key = :param");
|
|
$updateStmt->bindParam(':value', $networkID);
|
|
$updateStmt->bindParam(':param', $param);
|
|
$updateStmt->execute();
|
|
echo "SARA_R4_networkID updated successfully.";
|
|
|
|
} catch (PDOException $e) {
|
|
// Return error as JSON
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
|
|
}
|
|
|
|
//echo "connecting to network... please wait...";
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_connectNetwork.py ' . $port . ' ' . $networkID . ' ' . $timeout;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
|
|
|
|
}
|
|
|
|
#Setup Hostnmae
|
|
if ($type == "sara_setupHostname") {
|
|
$port=$_GET['port'];
|
|
$server_hostname=$_GET['networkID'];
|
|
$profileID=$_GET['profileID'];
|
|
|
|
//echo "connecting to network... please wait...";
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_setURL.py ' . $port . ' ' . $server_hostname . ' ' . $profileID;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
|
|
|
|
}
|
|
|
|
#SET THE URL for messaging (profile id 2)
|
|
if ($type == "sara_setURL") {
|
|
$port=$_GET['port'];
|
|
$url=$_GET['url'];
|
|
$profile_id = 2;
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_setURL.py ' . $port . ' ' . $url . ' ' . $profile_id;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
#SET APN
|
|
if ($type == "sara_APN") {
|
|
$port=$_GET['port'];
|
|
$timeout=$_GET['timeout'];
|
|
$APN_address=$_GET['APN_address'];
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_setAPN.py ' . $port . ' ' . $APN_address . ' ' . $timeout;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
#TO WRITE MESSAGE TO MEMORY
|
|
if ($type == "sara_writeMessage") {
|
|
$port=$_GET['port'];
|
|
$message=$_GET['message'];
|
|
$message = escapeshellcmd($message);
|
|
$type2=$_GET['type2'];
|
|
|
|
if ($type2 === "write") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_writeMessage.py ' . $port . ' ' . $message;
|
|
$output = shell_exec($command);
|
|
}
|
|
if ($type2 === "read") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_readMessage.py ' . $port . ' ' . $message;
|
|
$output = shell_exec($command);
|
|
}
|
|
if ($type2 === "erase") {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_eraseMessage.py ' . $port . ' ' . $message;
|
|
$output = shell_exec($command);
|
|
}
|
|
|
|
echo $output;
|
|
}
|
|
|
|
#Send the typed message to server (for ntfy notification)
|
|
if ($type == "sara_sendMessage") {
|
|
$port=$_GET['port'];
|
|
$endpoint=$_GET['endpoint'];
|
|
$endpoint = escapeshellcmd($endpoint);
|
|
$profile_id = 2;
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_sendMessage.py ' . $port . ' ' . $endpoint. ' ' . $profile_id;
|
|
$output = shell_exec($command);
|
|
echo $output;
|
|
}
|
|
|
|
if ($type == "internet") {
|
|
// eth0
|
|
$eth0_connStatus = str_replace("\n", "", shell_exec('nmcli -g GENERAL.STATE device show eth0 2>/dev/null'));
|
|
$eth0_IPAddr = str_replace("\n", "", shell_exec('nmcli -g IP4.ADDRESS device show eth0 2>/dev/null'));
|
|
|
|
// wlan0 basic
|
|
$wlan0_connStatus = str_replace("\n", "", shell_exec('nmcli -g GENERAL.STATE device show wlan0 2>/dev/null'));
|
|
$wlan0_IPAddr = str_replace("\n", "", shell_exec('nmcli -g IP4.ADDRESS device show wlan0 2>/dev/null'));
|
|
|
|
// wlan0 detailed info (connection name, signal, frequency, security, gateway, etc.)
|
|
$wlan0_connection = str_replace("\n", "", shell_exec("nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2"));
|
|
$wlan0_gateway = str_replace("\n", "", shell_exec('nmcli -g IP4.GATEWAY device show wlan0 2>/dev/null'));
|
|
|
|
// Get active WiFi details (signal, frequency, security) from nmcli
|
|
$wifi_signal = '';
|
|
$wifi_freq = '';
|
|
$wifi_security = '';
|
|
$wifi_ssid = '';
|
|
$wifi_output = shell_exec('nmcli -t -f ACTIVE,SSID,SIGNAL,FREQ,SECURITY device wifi list ifname wlan0 2>/dev/null');
|
|
if ($wifi_output) {
|
|
$lines = explode("\n", trim($wifi_output));
|
|
foreach ($lines as $line) {
|
|
// Active connection line starts with "yes:" (nmcli -t uses : separator)
|
|
if (strpos($line, 'yes:') === 0) {
|
|
// Format: yes:SSID:SIGNAL:FREQ:SECURITY
|
|
// Use explode with limit to handle SSIDs containing ':'
|
|
$parts = explode(':', $line);
|
|
if (count($parts) >= 5) {
|
|
$wifi_ssid = $parts[1];
|
|
$wifi_signal = $parts[2];
|
|
$wifi_freq = $parts[3];
|
|
$wifi_security = $parts[4];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hostname
|
|
$hostname = trim(shell_exec('hostname 2>/dev/null'));
|
|
|
|
$data = array(
|
|
"ethernet" => array(
|
|
"connection" => $eth0_connStatus,
|
|
"IP" => $eth0_IPAddr
|
|
),
|
|
"wifi" => array(
|
|
"connection" => $wlan0_connStatus,
|
|
"IP" => $wlan0_IPAddr,
|
|
"ssid" => $wifi_ssid ?: $wlan0_connection,
|
|
"signal" => $wifi_signal,
|
|
"frequency" => $wifi_freq,
|
|
"security" => $wifi_security,
|
|
"gateway" => $wlan0_gateway,
|
|
"hostname" => $hostname
|
|
)
|
|
);
|
|
|
|
echo json_encode($data);
|
|
}
|
|
|
|
# IMPORTANT
|
|
# c'est ici que la connexion vers le WIFI du client s'effectue.
|
|
if ($type == "wifi_connect") {
|
|
$SSID=$_GET['SSID'];
|
|
$PASS=$_GET['pass'];
|
|
|
|
// Get device name from database for instructions
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
$stmt = $db->prepare("SELECT value FROM config_table WHERE key = 'deviceName'");
|
|
$stmt->execute();
|
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
$deviceName = $result ? $result['value'] : 'NebuleAir';
|
|
$db = null;
|
|
} catch (PDOException $e) {
|
|
$deviceName = 'NebuleAir';
|
|
}
|
|
|
|
// Launch connection script in background
|
|
$script_path = '/var/www/nebuleair_pro_4g/connexion.sh';
|
|
$log_file = '/var/www/nebuleair_pro_4g/logs/app.log';
|
|
shell_exec("$script_path $SSID $PASS >> $log_file 2>&1 &");
|
|
|
|
// Return JSON response with instructions
|
|
header('Content-Type: application/json');
|
|
echo json_encode([
|
|
'success' => true,
|
|
'ssid' => $SSID,
|
|
'deviceName' => $deviceName,
|
|
'message' => 'Connection attempt started',
|
|
'instructions' => [
|
|
'fr' => [
|
|
'title' => 'Connexion en cours...',
|
|
'step1' => "Le capteur tente de se connecter au réseau « $SSID »",
|
|
'step2' => "Vous allez être déconnecté du hotspot dans quelques secondes",
|
|
'step3' => "Reconnectez-vous au WiFi « $SSID » sur votre appareil",
|
|
'step4' => "Accédez au capteur via http://$deviceName.local ou cherchez son IP dans votre routeur",
|
|
'warning' => "Si la connexion échoue, le capteur recréera automatiquement le hotspot"
|
|
],
|
|
'en' => [
|
|
'title' => 'Connection in progress...',
|
|
'step1' => "The sensor is attempting to connect to network « $SSID »",
|
|
'step2' => "You will be disconnected from the hotspot in a few seconds",
|
|
'step3' => "Reconnect your device to WiFi « $SSID »",
|
|
'step4' => "Access the sensor via http://$deviceName.local or find its IP in your router",
|
|
'warning' => "If connection fails, the sensor will automatically recreate the hotspot"
|
|
]
|
|
]
|
|
]);
|
|
}
|
|
|
|
|
|
if ($type == "wifi_forget") {
|
|
// Get device name from database
|
|
try {
|
|
$db = new PDO("sqlite:$database_path");
|
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
$stmt = $db->prepare("SELECT value FROM config_table WHERE key = 'deviceName'");
|
|
$stmt->execute();
|
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
$deviceName = $result ? $result['value'] : 'NebuleAir';
|
|
$db = null;
|
|
} catch (PDOException $e) {
|
|
$deviceName = 'NebuleAir';
|
|
}
|
|
|
|
// Launch forget script in background
|
|
$script_path = '/var/www/nebuleair_pro_4g/forget_wifi.sh';
|
|
$log_file = '/var/www/nebuleair_pro_4g/logs/app.log';
|
|
shell_exec("bash $script_path >> $log_file 2>&1 &");
|
|
|
|
header('Content-Type: application/json');
|
|
echo json_encode([
|
|
'success' => true,
|
|
'deviceName' => $deviceName,
|
|
'instructions' => [
|
|
'fr' => [
|
|
'title' => 'Réseau WiFi oublié',
|
|
'step1' => "Le capteur oublie le réseau WiFi actuel",
|
|
'step2' => "Le hotspot va démarrer automatiquement",
|
|
'step3' => "Connectez-vous au WiFi « $deviceName » (mot de passe : nebuleaircfg)",
|
|
'step4' => "Accédez au capteur via http://10.42.0.1/html/",
|
|
'warning' => "Le capteur ne se reconnectera plus automatiquement à ce réseau"
|
|
],
|
|
'en' => [
|
|
'title' => 'WiFi network forgotten',
|
|
'step1' => "The sensor is forgetting the current WiFi network",
|
|
'step2' => "The hotspot will start automatically",
|
|
'step3' => "Connect to WiFi « $deviceName » (password: nebuleaircfg)",
|
|
'step4' => "Access the sensor via http://10.42.0.1/html/",
|
|
'warning' => "The sensor will no longer auto-connect to this network"
|
|
]
|
|
]
|
|
]);
|
|
}
|
|
|
|
|
|
if ($type == "wifi_scan") {
|
|
|
|
$jsonData = [];
|
|
|
|
// Check if wlan0 is in hotspot mode — if so, use cached CSV from boot scan
|
|
// (live scan is impossible while wlan0 is serving the hotspot)
|
|
$wlan0_connection = trim(shell_exec("nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2"));
|
|
$is_hotspot = (strpos(strtolower($wlan0_connection), 'hotspot') !== false);
|
|
|
|
if ($is_hotspot) {
|
|
// Read cached scan from boot (wifi_list.csv)
|
|
$csv_path = '/var/www/nebuleair_pro_4g/wifi_list.csv';
|
|
if (file_exists($csv_path)) {
|
|
$lines = file($csv_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
// Skip header line (SSID,SIGNAL,SECURITY)
|
|
for ($i = 1; $i < count($lines); $i++) {
|
|
$parts = str_getcsv($lines[$i]);
|
|
if (count($parts) >= 2 && !empty(trim($parts[0]))) {
|
|
$jsonData[] = [
|
|
'SSID' => trim($parts[0]),
|
|
'SIGNAL' => trim($parts[1]),
|
|
'SECURITY' => isset($parts[2]) ? trim($parts[2]) : '--',
|
|
'cached' => true
|
|
];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Live scan (wlan0 is free)
|
|
$output = shell_exec('timeout 10 nmcli -f SSID,SIGNAL,SECURITY device wifi list ifname wlan0 2>/dev/null');
|
|
|
|
if ($output) {
|
|
$lines = explode("\n", trim($output));
|
|
for ($i = 1; $i < count($lines); $i++) {
|
|
$line = trim($lines[$i]);
|
|
if (empty($line)) continue;
|
|
|
|
$parts = preg_split('/\s{2,}/', $line, 3);
|
|
if (count($parts) >= 2) {
|
|
$jsonData[] = [
|
|
'SSID' => trim($parts[0]),
|
|
'SIGNAL' => trim($parts[1]),
|
|
'SECURITY' => isset($parts[2]) ? trim($parts[2]) : '--'
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
header('Content-Type: application/json');
|
|
echo json_encode($jsonData, JSON_PRETTY_PRINT);
|
|
|
|
}
|
|
|
|
if ($type == "wifi_scan_old") {
|
|
|
|
$output = shell_exec('nmcli device wifi list ifname wlan0');
|
|
// Split the output into lines
|
|
$lines = explode("\n", trim($output));
|
|
|
|
// Initialize an array to hold the results
|
|
$wifiNetworks = [];
|
|
|
|
// Loop through each line and extract the relevant information
|
|
foreach ($lines as $index => $line) {
|
|
// Skip the header line
|
|
if ($index === 0) {
|
|
continue;
|
|
}
|
|
|
|
// Split the line by whitespace and filter empty values
|
|
$columns = preg_split('/\s+/', $line);
|
|
|
|
// If the line has enough columns, store the relevant data
|
|
if (count($columns) >= 5) {
|
|
$wifiNetworks[] = [
|
|
'SSID' => $columns[2], // Network name
|
|
'BARS' => $columns[8],
|
|
'SIGNAL' => $columns[7], // Signal strength
|
|
|
|
];
|
|
}
|
|
}
|
|
$json_data = json_encode($wifiNetworks);
|
|
|
|
echo $json_data;
|
|
|
|
}
|
|
|
|
/*
|
|
_____ _ _
|
|
|_ _|__ _ __ _ __ ___ (_)_ __ __ _| |
|
|
| |/ _ \ '__| '_ ` _ \| | '_ \ / _` | |
|
|
| | __/ | | | | | | | | | | | (_| | |
|
|
|_|\___|_| |_| |_| |_|_|_| |_|\__,_|_|
|
|
|
|
*/
|
|
|
|
// Execute shell command with security restrictions
|
|
if ($type == "execute_command") {
|
|
// Verify that the request is using POST method
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['success' => false, 'message' => 'Invalid request method']);
|
|
exit;
|
|
}
|
|
|
|
// Get the command from POST data
|
|
$command = isset($_POST['command']) ? $_POST['command'] : '';
|
|
|
|
if (empty($command)) {
|
|
echo json_encode(['success' => false, 'message' => 'No command provided']);
|
|
exit;
|
|
}
|
|
|
|
// List of allowed commands (prefixes)
|
|
$allowedCommands = [
|
|
'ls', 'cat', 'cd', 'pwd', 'df', 'free', 'ifconfig', 'ip', 'ps', 'date', 'uptime',
|
|
'systemctl status', 'whoami', 'hostname', 'uname', 'grep', 'tail', 'head', 'find',
|
|
'less', 'more', 'du', 'echo', 'git'
|
|
];
|
|
|
|
// Check if command is allowed
|
|
$allowed = false;
|
|
foreach ($allowedCommands as $allowedCmd) {
|
|
if (strpos($command, $allowedCmd) === 0) {
|
|
$allowed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Special case for systemctl restart and reboot
|
|
if (strpos($command, 'systemctl restart') === 0 || $command === 'reboot') {
|
|
// These commands don't return output through shell_exec since they change process state
|
|
// We'll just acknowledge them
|
|
if ($command === 'reboot') {
|
|
// Execute the command with exec to avoid waiting for output
|
|
exec('sudo reboot > /dev/null 2>&1 &');
|
|
echo json_encode([
|
|
'success' => true,
|
|
'output' => 'System is rebooting...'
|
|
]);
|
|
} else {
|
|
// For systemctl restart, execute it and acknowledge
|
|
$serviceName = str_replace('systemctl restart ', '', $command);
|
|
exec('sudo systemctl restart ' . escapeshellarg($serviceName) . ' > /dev/null 2>&1 &');
|
|
echo json_encode([
|
|
'success' => true,
|
|
'output' => 'Service ' . $serviceName . ' is restarting...'
|
|
]);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
// Check for prohibited patterns
|
|
$prohibitedPatterns = [
|
|
'sudo rm', ';', '&&', '||', '|', '>', '>>', '&',
|
|
'wget', 'curl', 'nc', 'ssh', 'scp', 'ftp', 'telnet',
|
|
'iptables', 'passwd', 'chown', 'chmod', 'mkfs', ' dd ',
|
|
'mount', 'umount', 'kill', 'killall'
|
|
];
|
|
|
|
foreach ($prohibitedPatterns as $pattern) {
|
|
if (strpos($command, $pattern) !== false) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => 'Command contains prohibited operation: ' . $pattern
|
|
]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
if (!$allowed) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => 'Command not allowed for security reasons'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Execute the command with timeout protection
|
|
$descriptorspec = [
|
|
0 => ["pipe", "r"], // stdin
|
|
1 => ["pipe", "w"], // stdout
|
|
2 => ["pipe", "w"] // stderr
|
|
];
|
|
|
|
// Escape the command to prevent shell injection
|
|
$escapedCommand = escapeshellcmd($command);
|
|
|
|
// Add timeout of 5 seconds to prevent long-running commands
|
|
$process = proc_open("timeout 5 $escapedCommand", $descriptorspec, $pipes);
|
|
|
|
if (is_resource($process)) {
|
|
// Close stdin pipe
|
|
fclose($pipes[0]);
|
|
|
|
// Get output from stdout
|
|
$output = stream_get_contents($pipes[1]);
|
|
fclose($pipes[1]);
|
|
|
|
// Get any errors
|
|
$errors = stream_get_contents($pipes[2]);
|
|
fclose($pipes[2]);
|
|
|
|
// Close the process
|
|
$returnValue = proc_close($process);
|
|
|
|
// Check for errors
|
|
if ($returnValue !== 0) {
|
|
// If there was an error, but we have output, consider it a partial success
|
|
if (!empty($output)) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'output' => $output . "\n" . $errors . "\nCommand exited with code $returnValue"
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => empty($errors) ? "Command failed with exit code $returnValue" : $errors
|
|
]);
|
|
}
|
|
} else {
|
|
// Success
|
|
echo json_encode([
|
|
'success' => true,
|
|
'output' => $output
|
|
]);
|
|
}
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => 'Failed to execute command'
|
|
]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
____ _ ____ _ __ __ _
|
|
/ ___| _ _ ___| |_ ___ _ __ ___ | _ \ / ___| ___ _ ____ _(_) ___ ___| \/ | __ _ _ __ __ _ __ _ ___ _ __ ___ ___ _ __ | |_
|
|
\___ \| | | / __| __/ _ \ '_ ` _ \| | | | \___ \ / _ \ '__\ \ / / |/ __/ _ \ |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '_ ` _ \ / _ \ '_ \| __|
|
|
___) | |_| \__ \ || __/ | | | | | |_| | ___) | __/ | \ V /| | (_| __/ | | | (_| | | | | (_| | (_| | __/ | | | | | __/ | | | |_
|
|
|____/ \__, |___/\__\___|_| |_| |_|____/ |____/ \___|_| \_/ |_|\___\___|_| |_|\__,_|_| |_|\__,_|\__, |\___|_| |_| |_|\___|_| |_|\__|
|
|
|___/ |___/
|
|
*/
|
|
|
|
// Get systemd services status
|
|
if ($type == "get_systemd_services") {
|
|
try {
|
|
// List of NebuleAir services to monitor with descriptions and frequencies
|
|
$services = [
|
|
'nebuleair-npm-data.timer' => [
|
|
'description' => 'Collects particulate matter data from NextPM sensor',
|
|
'frequency' => 'Every 10 seconds'
|
|
],
|
|
'nebuleair-envea-data.timer' => [
|
|
'description' => 'Reads environmental data from Envea sensors',
|
|
'frequency' => 'Every 10 seconds'
|
|
],
|
|
'nebuleair-sara-data.timer' => [
|
|
'description' => 'Transmits collected data via 4G cellular modem',
|
|
'frequency' => 'Every 60 seconds'
|
|
],
|
|
'nebuleair-bme280-data.timer' => [
|
|
'description' => 'Monitors temperature and humidity (BME280)',
|
|
'frequency' => 'Every 2 minutes'
|
|
],
|
|
'nebuleair-mppt-data.timer' => [
|
|
'description' => 'Tracks solar panel and battery status',
|
|
'frequency' => 'Every 2 minutes'
|
|
],
|
|
'nebuleair-noise-data.timer' => [
|
|
'description' => 'Get Data from noise sensor',
|
|
'frequency' => 'Every minute'
|
|
],
|
|
'nebuleair-mhz19-data.timer' => [
|
|
'description' => 'Reads CO2 concentration from MH-Z19 sensor',
|
|
'frequency' => 'Every 2 minutes'
|
|
],
|
|
'nebuleair-db-cleanup-data.timer' => [
|
|
'description' => 'Cleans up old data from database',
|
|
'frequency' => 'Daily'
|
|
]
|
|
];
|
|
|
|
$serviceStatus = [];
|
|
|
|
foreach ($services as $service => $serviceInfo) {
|
|
// Get service active status
|
|
$activeCmd = "systemctl is-active " . escapeshellarg($service) . " 2>/dev/null";
|
|
$activeStatus = trim(shell_exec($activeCmd));
|
|
$isActive = ($activeStatus === 'active');
|
|
|
|
// Get service enabled status
|
|
$enabledCmd = "systemctl is-enabled " . escapeshellarg($service) . " 2>/dev/null";
|
|
$enabledStatus = trim(shell_exec($enabledCmd));
|
|
$isEnabled = ($enabledStatus === 'enabled');
|
|
|
|
// Clean up service name for display
|
|
$displayName = str_replace(['.timer', 'nebuleair-', '-data'], '', $service);
|
|
$displayName = ucfirst(str_replace('-', ' ', $displayName));
|
|
|
|
$serviceStatus[] = [
|
|
'name' => $service,
|
|
'display_name' => $displayName,
|
|
'description' => $serviceInfo['description'],
|
|
'frequency' => $serviceInfo['frequency'],
|
|
'active' => $isActive,
|
|
'enabled' => $isEnabled
|
|
];
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'services' => $serviceStatus
|
|
], JSON_PRETTY_PRINT);
|
|
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Restart a systemd service
|
|
if ($type == "restart_systemd_service") {
|
|
$service = $_GET['service'] ?? null;
|
|
|
|
if (empty($service)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'No service specified'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Validate service name (security check)
|
|
$allowedServices = [
|
|
'nebuleair-npm-data.timer',
|
|
'nebuleair-envea-data.timer',
|
|
'nebuleair-sara-data.timer',
|
|
'nebuleair-bme280-data.timer',
|
|
'nebuleair-mppt-data.timer',
|
|
'nebuleair-mhz19-data.timer',
|
|
'nebuleair-db-cleanup-data.timer'
|
|
];
|
|
|
|
if (!in_array($service, $allowedServices)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Invalid service name'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
// Restart the service
|
|
$command = "sudo systemctl restart " . escapeshellarg($service) . " 2>&1";
|
|
$output = shell_exec($command);
|
|
|
|
// Check if restart was successful
|
|
$statusCmd = "systemctl is-active " . escapeshellarg($service) . " 2>/dev/null";
|
|
$status = trim(shell_exec($statusCmd));
|
|
|
|
if ($status === 'active' || empty($output)) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => "Service $service restarted successfully"
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => "Failed to restart service: $output"
|
|
]);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Enable/disable a systemd service
|
|
if ($type == "toggle_systemd_service") {
|
|
$service = $_GET['service'] ?? null;
|
|
$enable = $_GET['enable'] ?? null;
|
|
|
|
if (empty($service) || $enable === null) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Missing service name or enable parameter'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Validate service name (security check)
|
|
$allowedServices = [
|
|
'nebuleair-npm-data.timer',
|
|
'nebuleair-envea-data.timer',
|
|
'nebuleair-sara-data.timer',
|
|
'nebuleair-bme280-data.timer',
|
|
'nebuleair-mppt-data.timer',
|
|
'nebuleair-mhz19-data.timer',
|
|
'nebuleair-db-cleanup-data.timer'
|
|
];
|
|
|
|
if (!in_array($service, $allowedServices)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Invalid service name'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
$enable = filter_var($enable, FILTER_VALIDATE_BOOLEAN);
|
|
$action = $enable ? 'enable' : 'disable';
|
|
|
|
// Enable/disable the service
|
|
$command = "sudo systemctl $action " . escapeshellarg($service) . " 2>&1";
|
|
$output = shell_exec($command);
|
|
|
|
// If disabling, also stop the service
|
|
if (!$enable) {
|
|
$stopCommand = "sudo systemctl stop " . escapeshellarg($service) . " 2>&1";
|
|
$stopOutput = shell_exec($stopCommand);
|
|
}
|
|
|
|
// If enabling, also start the service
|
|
if ($enable) {
|
|
$startCommand = "sudo systemctl start " . escapeshellarg($service) . " 2>&1";
|
|
$startOutput = shell_exec($startCommand);
|
|
}
|
|
|
|
// Check if the operation was successful
|
|
$statusCmd = "systemctl is-enabled " . escapeshellarg($service) . " 2>/dev/null";
|
|
$status = trim(shell_exec($statusCmd));
|
|
|
|
$expectedStatus = $enable ? 'enabled' : 'disabled';
|
|
|
|
if ($status === $expectedStatus) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => "Service $service " . ($enable ? 'enabled and started' : 'disabled and stopped') . " successfully"
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => "Failed to $action service: $output"
|
|
]);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Empty all sensor tables (preserve config and timestamp tables)
|
|
if ($type == "empty_sensor_tables") {
|
|
try {
|
|
// Execute the empty sensor tables script
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/empty_sensor_tables.py 2>&1';
|
|
$output = shell_exec($command);
|
|
|
|
// Try to extract JSON result from output
|
|
$json_start = strpos($output, '[JSON_RESULT]');
|
|
if ($json_start !== false) {
|
|
$json_data = substr($output, $json_start + strlen('[JSON_RESULT]'));
|
|
$json_data = trim($json_data);
|
|
|
|
// Find the first { and last }
|
|
$first_brace = strpos($json_data, '{');
|
|
$last_brace = strrpos($json_data, '}');
|
|
|
|
if ($first_brace !== false && $last_brace !== false) {
|
|
$json_data = substr($json_data, $first_brace, $last_brace - $first_brace + 1);
|
|
$result = json_decode($json_data, true);
|
|
|
|
if ($result !== null) {
|
|
echo json_encode($result);
|
|
} else {
|
|
// JSON decode failed, return raw output
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => 'Tables emptied',
|
|
'output' => $output
|
|
]);
|
|
}
|
|
} else {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => 'Tables emptied',
|
|
'output' => $output
|
|
]);
|
|
}
|
|
} else {
|
|
// No JSON marker found, return raw output
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => 'Tables emptied',
|
|
'output' => $output
|
|
]);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Script execution failed: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
_____ ____ _ _ _
|
|
| ____|_ ____ _____ __ _ | _ \ ___| |_ ___ ___| |_(_) ___ _ __
|
|
| _| | '_ \ \ / / _ \/ _` | | | | |/ _ \ __/ _ \/ __| __| |/ _ \| '_ \
|
|
| |___| | | \ V / __/ (_| | | |_| | __/ || __/ (__| |_| | (_) | | | |
|
|
|_____|_| |_|\_/ \___|\__,_| |____/ \___|\__\___|\___|\__|_|\___/|_| |_|
|
|
|
|
*/
|
|
|
|
// Detect Envea devices on specified port
|
|
if ($type == "detect_envea_device") {
|
|
$port = $_GET['port'] ?? null;
|
|
|
|
if (empty($port)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'No port specified'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Validate port name (security check)
|
|
$allowedPorts = ['ttyAMA2', 'ttyAMA3', 'ttyAMA4', 'ttyAMA5'];
|
|
|
|
if (!in_array($port, $allowedPorts)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Invalid port name'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
// Execute the envea detection script
|
|
$command = "sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/envea/read_ref.py " . escapeshellarg($port) . " 2>&1";
|
|
$output = shell_exec($command);
|
|
|
|
// Check if we got any meaningful output
|
|
$detected = false;
|
|
$device_info = '';
|
|
$raw_data = $output;
|
|
|
|
if (!empty($output)) {
|
|
// Look for indicators that a device is connected
|
|
if (strpos($output, 'Connexion ouverte') !== false) {
|
|
// Connection was successful
|
|
if (strpos($output, 'Données reçues brutes') !== false &&
|
|
strpos($output, 'b\'\'') === false) {
|
|
// We received actual data (not empty)
|
|
$detected = true;
|
|
$device_info = 'Envea CAIRSENS Device';
|
|
|
|
// Try to extract device type from ASCII data if available
|
|
if (preg_match('/Valeurs converties en ASCII : (.+)/', $output, $matches)) {
|
|
$ascii_data = trim($matches[1]);
|
|
if (!empty($ascii_data) && $ascii_data !== '........') {
|
|
$device_info = "Envea Device: " . $ascii_data;
|
|
}
|
|
}
|
|
} else {
|
|
// Connection successful but no data
|
|
$device_info = 'Port accessible but no Envea device detected';
|
|
}
|
|
} else if (strpos($output, 'Erreur de connexion série') !== false) {
|
|
// Serial connection error
|
|
$device_info = 'Serial connection error - port may be busy or not available';
|
|
} else {
|
|
// Other output
|
|
$device_info = 'Unexpected response from port';
|
|
}
|
|
} else {
|
|
// No output at all
|
|
$device_info = 'No response from port';
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'port' => $port,
|
|
'detected' => $detected,
|
|
'device_info' => $device_info,
|
|
'data' => $raw_data,
|
|
'timestamp' => date('Y-m-d H:i:s')
|
|
], JSON_PRETTY_PRINT);
|
|
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Script execution failed: ' . $e->getMessage(),
|
|
'port' => $port
|
|
]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
____ ____ _ _ ____ __ __ _
|
|
/ ___| _ \| | | | | _ \ _____ _____ _ _| \/ | __ _ _ __ __ _ __ _ ___ _ __ ___ ___ _ __ | |_
|
|
| | | |_) | | | | | |_) / _ \ \ /\ / / _ \ '__| |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '_ ` _ \ / _ \ '_ \| __|
|
|
| |___| __/| |_| | | __/ (_) \ V V / __/ | | | | | (_| | | | | (_| | (_| | __/ | | | | | __/ | | | |_
|
|
\____|_| \___/ |_| \___/ \_/\_/ \___|_| |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| |_| |_|\___|_| |_|\__|
|
|
|___/
|
|
*/
|
|
|
|
// Get firmware version from VERSION file
|
|
if ($type == "get_firmware_version") {
|
|
$versionFile = '/var/www/nebuleair_pro_4g/VERSION';
|
|
if (file_exists($versionFile)) {
|
|
$version = trim(file_get_contents($versionFile));
|
|
echo json_encode([
|
|
'success' => true,
|
|
'version' => $version
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'version' => 'unknown'
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Get changelog from changelog.json
|
|
if ($type == "get_changelog") {
|
|
$changelogFile = '/var/www/nebuleair_pro_4g/changelog.json';
|
|
if (file_exists($changelogFile)) {
|
|
$changelog = json_decode(file_get_contents($changelogFile), true);
|
|
if ($changelog !== null) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'changelog' => $changelog
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Invalid changelog format'
|
|
]);
|
|
}
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Changelog file not found'
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Get current CPU power mode
|
|
if ($type == "get_cpu_power_mode") {
|
|
try {
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/power/set_cpu_mode.py get 2>&1';
|
|
$output = shell_exec($command);
|
|
|
|
// Try to parse JSON output
|
|
$result = json_decode($output, true);
|
|
|
|
if ($result && isset($result['success']) && $result['success']) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'mode' => $result['config_mode'] ?? 'unknown',
|
|
'cpu_state' => $result['cpu_state'] ?? null
|
|
], JSON_PRETTY_PRINT);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Failed to get CPU power mode',
|
|
'output' => $output
|
|
]);
|
|
}
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Script execution failed: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Set CPU power mode
|
|
if ($type == "set_cpu_power_mode") {
|
|
$mode = $_GET['mode'] ?? null;
|
|
|
|
if (empty($mode)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'No mode specified'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Validate mode (whitelist)
|
|
$allowedModes = ['normal', 'powersave'];
|
|
|
|
if (!in_array($mode, $allowedModes)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Invalid mode. Allowed: normal, powersave'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
// Execute the CPU power mode script
|
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/power/set_cpu_mode.py ' . escapeshellarg($mode) . ' 2>&1';
|
|
$output = shell_exec($command);
|
|
|
|
// Try to parse JSON output
|
|
$result = json_decode($output, true);
|
|
|
|
if ($result && isset($result['success']) && $result['success']) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'mode' => $mode,
|
|
'message' => "CPU power mode set to: $mode",
|
|
'description' => $result['description'] ?? ''
|
|
], JSON_PRETTY_PRINT);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => $result['error'] ?? 'Failed to set CPU power mode',
|
|
'output' => $output
|
|
]);
|
|
}
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'error' => 'Script execution failed: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
if ($type == "screen_control") {
|
|
$action = $_GET['action'];
|
|
if ($action == "start") {
|
|
// Run as background process with sudo (requires nopasswd in sudoers)
|
|
// Redirecting to a temp log file to debug startup issues
|
|
$command = 'export DISPLAY=:0 && nohup sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/screen_control/screen.py > /tmp/screen_control.log 2>&1 &';
|
|
shell_exec($command);
|
|
echo "Started. Check /tmp/screen_control.log for details.";
|
|
} elseif ($action == "stop") {
|
|
$command = 'sudo pkill -f "screen.py" 2>&1';
|
|
$output = shell_exec($command);
|
|
echo "Stopped. Output: " . $output;
|
|
}
|
|
}
|