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 == "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']; $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 == "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 = '/var/www/nebuleair_pro_4g/sound_meter/sound_meter'; $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 == "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 $command = 'nmcli -g GENERAL.STATE device show eth0'; $eth0_connStatus = shell_exec($command); $eth0_connStatus = str_replace("\n", "", $eth0_connStatus); $command = 'nmcli -g IP4.ADDRESS device show eth0'; $eth0_IPAddr = shell_exec($command); $eth0_IPAddr = str_replace("\n", "", $eth0_IPAddr); //wlan0 $command = 'nmcli -g GENERAL.STATE device show wlan0'; $wlan0_connStatus = shell_exec($command); $wlan0_connStatus = str_replace("\n", "", $wlan0_connStatus); $command = 'nmcli -g IP4.ADDRESS device show wlan0'; $wlan0_IPAddr = shell_exec($command); $wlan0_IPAddr = str_replace("\n", "", $wlan0_IPAddr); $data= array( "ethernet" => array( "connection" => $eth0_connStatus, "IP" => $eth0_IPAddr ), "wifi" => array( "connection" => $wlan0_connStatus, "IP" => $wlan0_IPAddr ) ); $json_data = json_encode($data); echo $json_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_scan") { // Perform live WiFi scan instead of reading stale CSV file $output = shell_exec('nmcli -f SSID,SIGNAL,SECURITY device wifi list ifname wlan0 2>/dev/null'); // Initialize an array to hold the JSON data $jsonData = []; if ($output) { // Split the output into lines $lines = explode("\n", trim($output)); // Skip the header line and process each network for ($i = 1; $i < count($lines); $i++) { $line = trim($lines[$i]); if (empty($line)) continue; // Split by multiple spaces (nmcli uses column formatting) $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]) : '--' ]; } } } // Set the content type to JSON header('Content-Type: application/json'); // Convert the array to JSON format and output it 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-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-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-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() ]); } }