@@ -441,6 +452,74 @@ function downloadCSV(response, table) {
document.body.removeChild(a);
}
+// Function to empty all sensor tables
+function emptySensorTables() {
+ // Show confirmation dialog
+ const confirmed = confirm(
+ "WARNING: This will permanently delete ALL sensor data from the database!\n\n" +
+ "The following tables will be emptied:\n" +
+ "- data_NPM\n" +
+ "- data_NPM_5channels\n" +
+ "- data_BME280\n" +
+ "- data_envea\n" +
+ "- data_WIND\n" +
+ "- data_MPPT\n" +
+ "- data_NOISE\n\n" +
+ "Configuration and timestamp tables will be preserved.\n\n" +
+ "Are you absolutely sure you want to continue?"
+ );
+
+ if (!confirmed) {
+ console.log("Empty sensor tables operation cancelled by user");
+ return;
+ }
+
+ // Show loading message
+ const tableDataDiv = document.getElementById("table_data");
+ tableDataDiv.innerHTML = '
Emptying sensor tables... Please wait...
';
+
+ // Make AJAX request to empty tables
+ $.ajax({
+ url: 'launcher.php?type=empty_sensor_tables',
+ dataType: 'json',
+ method: 'GET',
+ success: function(response) {
+ console.log("Empty sensor tables response:", response);
+
+ if (response.success) {
+ // Show success message
+ let message = '
';
+ message += '
Success!
';
+ message += '
' + response.message + '
';
+
+ if (response.tables_processed && response.tables_processed.length > 0) {
+ message += '
Tables emptied:
';
+ response.tables_processed.forEach(table => {
+ message += `- ${table.name}: ${table.deleted} records deleted
`;
+ });
+ message += '
';
+ }
+
+ message += '
';
+ tableDataDiv.innerHTML = message;
+ } else {
+ // Show error message
+ tableDataDiv.innerHTML = `
+
Error!
+
${response.message || response.error || 'Unknown error occurred'}
+
`;
+ }
+ },
+ error: function(xhr, status, error) {
+ console.error('AJAX request failed:', status, error);
+ tableDataDiv.innerHTML = `
+
Error!
+
Failed to empty sensor tables: ${error}
+
`;
+ }
+ });
+}
+
diff --git a/html/lang/en.json b/html/lang/en.json
index b28f838..12dd96d 100644
--- a/html/lang/en.json
+++ b/html/lang/en.json
@@ -88,7 +88,11 @@
"timestampTable": "Timestamp Table",
"downloadData": "Download Data",
"startDate": "Start date:",
- "endDate": "End date:"
+ "endDate": "End date:",
+ "dangerZone": "Danger Zone",
+ "dangerWarning": "Warning: This action is irreversible!",
+ "emptyAllTables": "Empty all sensor tables",
+ "emptyTablesNote": "Note: Configuration and timestamp tables will be preserved."
},
"logs": {
"title": "The Log",
diff --git a/html/lang/fr.json b/html/lang/fr.json
index 7d1717b..e48b065 100644
--- a/html/lang/fr.json
+++ b/html/lang/fr.json
@@ -88,7 +88,11 @@
"timestampTable": "Timestamp Table",
"downloadData": "Télécharger les données",
"startDate": "Date de début:",
- "endDate": "Date de fin:"
+ "endDate": "Date de fin:",
+ "dangerZone": "Zone dangereuse",
+ "dangerWarning": "Attention: Cette action est irréversible!",
+ "emptyAllTables": "Vider toutes les tables de capteurs",
+ "emptyTablesNote": "Note: Les tables de configuration et horodatage seront préservées."
},
"logs": {
"title": "Le journal",
diff --git a/html/launcher.php b/html/launcher.php
index 1449535..2a67916 100755
--- a/html/launcher.php
+++ b/html/launcher.php
@@ -1270,13 +1270,68 @@ if ($type == "toggle_systemd_service") {
}
}
+// 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
diff --git a/sqlite/empty_sensor_tables.py b/sqlite/empty_sensor_tables.py
new file mode 100644
index 0000000..65d7edc
--- /dev/null
+++ b/sqlite/empty_sensor_tables.py
@@ -0,0 +1,204 @@
+'''
+ ____ ___ _ _ _
+ / ___| / _ \| | (_) |_ ___
+ \___ \| | | | | | | __/ _ \
+ ___) | |_| | |___| | || __/
+ |____/ \__\_\_____|_|\__\___|
+
+Script to empty (delete all data from) sensor tables in the SQLite database
+This script empties sensor data tables but preserves:
+- timestamp_table
+- config_table
+- envea_sondes_table
+- config_scripts_table
+
+/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/empty_sensor_tables.py
+'''
+
+import sqlite3
+import sys
+import json
+
+def table_exists(cursor, table_name):
+ """Check if a table exists in the database"""
+ try:
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
+ return cursor.fetchone() is not None
+ except sqlite3.Error as e:
+ print(f"[ERROR] Failed to check if table '{table_name}' exists: {e}")
+ return False
+
+
+def get_table_count(cursor, table_name):
+ """Get the number of records in a table"""
+ try:
+ cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
+ return cursor.fetchone()[0]
+ except sqlite3.Error as e:
+ print(f"[WARNING] Could not get count for table '{table_name}': {e}")
+ return 0
+
+
+def empty_table(cursor, table_name):
+ """Delete all records from a specific table"""
+ try:
+ # Get record count before deletion
+ initial_count = get_table_count(cursor, table_name)
+
+ if initial_count == 0:
+ print(f"[INFO] Table '{table_name}' is already empty")
+ return True, 0
+
+ # Delete all records
+ cursor.execute(f"DELETE FROM {table_name}")
+ deleted_count = cursor.rowcount
+
+ print(f"[SUCCESS] Deleted {deleted_count} records from '{table_name}'")
+ return True, deleted_count
+
+ except sqlite3.Error as e:
+ print(f"[ERROR] Failed to empty table '{table_name}': {e}")
+ return False, 0
+
+
+def main():
+ result = {
+ 'success': False,
+ 'message': '',
+ 'tables_processed': [],
+ 'total_deleted': 0
+ }
+
+ try:
+ # Connect to the SQLite database
+ print("[INFO] Connecting to database...")
+ conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
+ cursor = conn.cursor()
+
+ # Check database connection
+ cursor.execute("SELECT sqlite_version()")
+ version = cursor.fetchone()[0]
+ print(f"[INFO] Connected to SQLite version: {version}")
+
+ # List of sensor tables to empty (EXCLUDING timestamp_table and config tables)
+ sensor_tables = [
+ "data_NPM",
+ "data_NPM_5channels",
+ "data_BME280",
+ "data_envea",
+ "data_WIND",
+ "data_MPPT",
+ "data_NOISE",
+ "modem_status"
+ ]
+
+ # Tables to PRESERVE (not empty)
+ preserved_tables = [
+ "timestamp_table",
+ "config_table",
+ "envea_sondes_table",
+ "config_scripts_table"
+ ]
+
+ print(f"[INFO] Will empty the following sensor tables: {', '.join(sensor_tables)}")
+ print(f"[INFO] Will preserve the following tables: {', '.join(preserved_tables)}")
+
+ # Check which tables actually exist
+ existing_tables = []
+ missing_tables = []
+
+ for table in sensor_tables:
+ if table_exists(cursor, table):
+ existing_tables.append(table)
+ record_count = get_table_count(cursor, table)
+ print(f"[INFO] Table '{table}' exists with {record_count} records")
+ else:
+ missing_tables.append(table)
+ print(f"[WARNING] Table '{table}' does not exist - skipping")
+
+ if missing_tables:
+ print(f"[INFO] Missing tables: {', '.join(missing_tables)}")
+
+ if not existing_tables:
+ result['success'] = True
+ result['message'] = "No sensor tables found to empty"
+ print("[WARNING] No sensor tables found to empty!")
+ print(json.dumps(result))
+ return True
+
+ # Loop through existing tables and empty them
+ successful_operations = 0
+ failed_operations = 0
+ total_deleted = 0
+
+ for table in existing_tables:
+ success, deleted = empty_table(cursor, table)
+ if success:
+ successful_operations += 1
+ total_deleted += deleted
+ result['tables_processed'].append({
+ 'name': table,
+ 'deleted': deleted
+ })
+ else:
+ failed_operations += 1
+
+ # Commit changes
+ print("[INFO] Committing changes...")
+ conn.commit()
+ print("[SUCCESS] Changes committed successfully!")
+
+ # Run VACUUM to optimize database space
+ if total_deleted > 0:
+ print("[INFO] Running VACUUM to optimize database space...")
+ try:
+ cursor.execute("VACUUM")
+ print("[SUCCESS] Database optimized successfully!")
+ except sqlite3.Error as e:
+ print(f"[WARNING] VACUUM failed: {e}")
+
+ # Summary
+ print(f"\n[SUMMARY]")
+ print(f"Tables emptied successfully: {successful_operations}")
+ print(f"Tables with errors: {failed_operations}")
+ print(f"Tables skipped (missing): {len(missing_tables)}")
+ print(f"Total records deleted: {total_deleted}")
+
+ result['success'] = True
+ result['message'] = f"Successfully emptied {successful_operations} sensor tables. Total records deleted: {total_deleted}"
+ result['total_deleted'] = total_deleted
+
+ if failed_operations == 0:
+ print("[SUCCESS] All sensor tables emptied successfully!")
+ else:
+ result['message'] = f"Partial success: {successful_operations} tables emptied, {failed_operations} failed"
+ print("[WARNING] Some operations failed - check logs above")
+
+ # Output JSON result for web interface
+ print("\n[JSON_RESULT]")
+ print(json.dumps(result))
+
+ return failed_operations == 0
+
+ except sqlite3.Error as e:
+ result['message'] = f"Database error: {e}"
+ print(f"[ERROR] Database error: {e}")
+ print("\n[JSON_RESULT]")
+ print(json.dumps(result))
+ return False
+ except Exception as e:
+ result['message'] = f"Unexpected error: {e}"
+ print(f"[ERROR] Unexpected error: {e}")
+ print("\n[JSON_RESULT]")
+ print(json.dumps(result))
+ return False
+ finally:
+ # Always close the database connection
+ if 'conn' in locals():
+ conn.close()
+ print("[INFO] Database connection closed")
+
+
+if __name__ == "__main__":
+ success = main()
+ sys.exit(0 if success else 1)