diff --git a/html/admin.html b/html/admin.html
index 026dcd6..c4f619f 100755
--- a/html/admin.html
+++ b/html/admin.html
@@ -71,39 +71,41 @@
-
-
-
-
-
+
-
+
-
+
+
+
+
+
+
+
@@ -158,6 +160,44 @@
+
+
+
+
SystemD Services
+
+
+
+
+
+
+ | Service |
+ Description |
+ Frequency |
+ Status |
+ Enabled |
+ Actions |
+
+
+
+
+ | Loading services... |
+
+
+
+
+
+
+
+
@@ -218,126 +258,111 @@
window.onload = function() {
-//NEW way to get config (SQLite)
-$.ajax({
- url: 'launcher.php?type=get_config_sqlite',
- dataType:'json',
- //dataType: 'json', // Specify that you expect a JSON response
- method: 'GET', // Use GET or POST depending on your needs
- success: function(response) {
- console.log("Getting SQLite config table:");
- console.log(response);
- //device name
- const deviceName = document.getElementById("device_name");
- deviceName.value = response.deviceName;
- //device name_side bar
- const elements = document.querySelectorAll('.sideBar_sensorName');
- elements.forEach((element) => {
- element.innerText = response.deviceName;
- });
- //device ID
- const deviceID = response.deviceID.trim().toUpperCase();
- const device_ID = document.getElementById("device_ID");
- device_ID.value = response.deviceID.toUpperCase();
- //nextPM send 5 channels
- const checkbox_nmp5channels = document.getElementById("check_NPM_5channels");
- checkbox_nmp5channels.checked = response.npm_5channel;
-
-
-
-
- },
- error: function(xhr, status, error) {
- console.error('AJAX request failed:', status, error);
- }
- });//end AJAX
-
- //getting config_scripts table
+ //NEW way to get config (SQLite)
$.ajax({
- url: 'launcher.php?type=get_config_scripts_sqlite',
- dataType:'json',
- //dataType: 'json', // Specify that you expect a JSON response
- method: 'GET', // Use GET or POST depending on your needs
- success: function(response) {
- console.log("Getting SQLite config scripts table:");
- console.log(response);
+ url: 'launcher.php?type=get_config_sqlite',
+ dataType:'json',
+ //dataType: 'json', // Specify that you expect a JSON response
+ method: 'GET', // Use GET or POST depending on your needs
+ success: function(response) {
+ console.log("Getting SQLite config table:");
+ console.log(response);
+ //device name
+ const deviceName = document.getElementById("device_name");
+ deviceName.value = response.deviceName;
+ //device name_side bar
+ const elements = document.querySelectorAll('.sideBar_sensorName');
+ elements.forEach((element) => {
+ element.innerText = response.deviceName;
+ });
+ //device ID
+ const deviceID = response.deviceID.trim().toUpperCase();
+ const device_ID = document.getElementById("device_ID");
+ device_ID.value = response.deviceID.toUpperCase();
- const checkbox_NPM = document.getElementById("check_NPM");
- const checkbox_bme = document.getElementById("check_bme280");
- const checkbox_CO2 = document.getElementById("check_CO2");
- const checkbox_SFA30 = document.getElementById("check_sensirionSFA30");
-
- checkbox_NPM.checked = response["NPM/get_data_modbus_v3.py"];
- checkbox_bme.checked = response["BME280/get_data_v2.py"];
- checkbox_SFA30.checked = response["sensirion/SFA30_read.py"];
- checkbox_CO2.checked = response["MH-Z19/write_data.py"];
-
- //si sonde envea is true
- if (response["envea/read_value_v2.py"]) {
- add_sondeEnveaContainer();
+ const checkbox_nmp5channels = document.getElementById("check_NPM_5channels");
- }
- },
- error: function(xhr, status, error) {
- console.error('AJAX request failed:', status, error);
- }
- });//end AJAX
+ const checkbox_bme = document.getElementById("check_bme280");
+ const checkbox_CO2 = document.getElementById("check_CO2");
+ const checkbox_SFA30 = document.getElementById("check_sensirionSFA30");
-//get system time and RTC module
-$.ajax({
- url: 'launcher.php?type=sys_RTC_module_time',
- dataType: 'json', // Specify that you expect a JSON response
- method: 'GET', // Use GET or POST depending on your needs
- success: function(response) {
- console.log(response);
- // Update the input fields with the received JSON data
- document.getElementById("sys_local_time").value = response.system_local_time;
- document.getElementById("sys_UTC_time").value = response.system_utc_time;
- document.getElementById("RTC_utc_time").value = response.rtc_module_time;
+ checkbox_nmp5channels.checked = response.npm_5channel;
+ checkbox_bme.checked = response["BME280"];
+ checkbox_SFA30.checked = response["SFA30"];
+ checkbox_CO2.checked = response["MH-Z19"];
- // Get the time difference
- const timeDiff = response.time_difference_seconds;
+ //si sonde envea is true
+ if (response["envea"]) {
+ add_sondeEnveaContainer();
+ }
- // Reference to the alert container
- const alertContainer = document.getElementById("alert_container");
- // Remove any previous alert
- alertContainer.innerHTML = "";
+ },
+ error: function(xhr, status, error) {
+ console.error('AJAX request failed:', status, error);
+ }
+ });//end AJAX
+
+
- // Add an alert based on time difference
- if (typeof timeDiff === "number") {
- if (timeDiff >= 0 && timeDiff <= 10) {
- alertContainer.innerHTML = `
-
- RTC and system time are in sync (Difference: ${timeDiff} sec).
-
`;
- } else if (timeDiff > 10) {
- alertContainer.innerHTML = `
-
- RTC time is out of sync! (Difference: ${timeDiff} sec).
-
`;
- }
- }
- },
- error: function(xhr, status, error) {
- console.error('AJAX request failed:', status, error);
- }
- });//end ajax
+ //get system time and RTC module
+ $.ajax({
+ url: 'launcher.php?type=sys_RTC_module_time',
+ dataType: 'json', // Specify that you expect a JSON response
+ method: 'GET', // Use GET or POST depending on your needs
+ success: function(response) {
+ console.log(response);
+ // Update the input fields with the received JSON data
+ document.getElementById("sys_local_time").value = response.system_local_time;
+ document.getElementById("sys_UTC_time").value = response.system_utc_time;
+ document.getElementById("RTC_utc_time").value = response.rtc_module_time;
-//get local RTC
-$.ajax({
- url: 'launcher.php?type=RTC_time',
- dataType: 'text', // Specify that you expect a JSON response
- method: 'GET', // Use GET or POST depending on your needs
- success: function(response) {
- console.log("Local RTC: " + response);
- const RTC_Element = document.getElementById("RTC_time");
- RTC_Element.textContent = response;
- },
- error: function(xhr, status, error) {
- console.error('AJAX request failed:', status, error);
- }
- });//end ajax
+ // Get the time difference
+ const timeDiff = response.time_difference_seconds;
+
+ // Reference to the alert container
+ const alertContainer = document.getElementById("alert_container");
+
+ // Remove any previous alert
+ alertContainer.innerHTML = "";
+
+ // Add an alert based on time difference
+ if (typeof timeDiff === "number") {
+ if (timeDiff >= 0 && timeDiff <= 10) {
+ alertContainer.innerHTML = `
+
+ RTC and system time are in sync (Difference: ${timeDiff} sec).
+
`;
+ } else if (timeDiff > 10) {
+ alertContainer.innerHTML = `
+
+ RTC time is out of sync! (Difference: ${timeDiff} sec).
+
`;
+ }
+ }
+ },
+ error: function(xhr, status, error) {
+ console.error('AJAX request failed:', status, error);
+ }
+ });//end ajax
+
+ //get local RTC
+ $.ajax({
+ url: 'launcher.php?type=RTC_time',
+ dataType: 'text', // Specify that you expect a JSON response
+ method: 'GET', // Use GET or POST depending on your needs
+ success: function(response) {
+ console.log("Local RTC: " + response);
+ const RTC_Element = document.getElementById("RTC_time");
+ RTC_Element.textContent = response;
+ },
+ error: function(xhr, status, error) {
+ console.error('AJAX request failed:', status, error);
+ }
+ });//end ajax
+
+ // Load services on page load
+ refreshServices();
} //end windows on load
@@ -391,78 +416,6 @@ function update_config_sqlite(param, value){
});
}
-function update_config_scripts_sqlite(param, value) {
- console.log("Updating scripts sqlite ", param, " : ", value);
- const toastLiveExample = document.getElementById('liveToast')
- const toastBody = toastLiveExample.querySelector('.toast-body');
-
- $.ajax({
- url: 'launcher.php?type=update_config_scripts_sqlite¶m=' + param + '&value=' + value,
- dataType: 'json',
- method: 'GET',
- cache: false,
- success: function(response) {
- console.log(response);
- // Format the response nicely
- let formattedMessage = '';
-
- if (response.success) {
- // Success message
- toastLiveExample.classList.remove('text-bg-danger');
- toastLiveExample.classList.add('text-bg-success');
-
- formattedMessage = `
- Success!
- Parameter: ${response.script_path || param}
- Value: ${response.enabled !== undefined ? response.enabled : value}
- ${response.message || ''}
- `;
-
- if (response.script_path == "envea/read_value_v2.py") {
- console.log("envea sondes activated");
- add_sondeEnveaContainer();
-
- }
- } else {
- // Error message
- toastLiveExample.classList.remove('text-bg-success');
- toastLiveExample.classList.add('text-bg-danger');
-
- formattedMessage = `
- Error!
- ${response.error || 'Unknown error'}
- Parameter: ${response.script_path || param}
- `;
- }
-
- // Update the toast body with formatted content
- toastBody.innerHTML = formattedMessage;
- // Show the toast
- const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample)
- toastBootstrap.show()
- },
- error: function(xhr, status, error) {
- console.error('AJAX request failed:', status, error);
- }
- });
-}
-
-
-function update_config(param, value){
- console.log("Updating ",param," : ", value);
- $.ajax({
- url: 'launcher.php?type=update_config¶m='+param+'&value='+value,
- dataType: 'text', // Specify that you expect a JSON response
- method: 'GET', // Use GET or POST depending on your needs
- cache: false, // Prevent AJAX from caching
- success: function(response) {
- console.log(response);
- },
- error: function(xhr, status, error) {
- console.error('AJAX request failed:', status, error);
- }
- });
-}
function updateGitPull(){
console.log("Updating device (git pull)");
@@ -537,6 +490,253 @@ function set_RTC_withBrowser(){
}
+
+/*
+ ____ _ __ __ _
+ / ___| ___ _ ____ _(_) ___ ___| \/ | __ _ _ __ __ _ __ _ ___ _ __ ___ ___ _ __ | |_
+ \___ \ / _ \ '__\ \ / / |/ __/ _ \ |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '_ ` _ \ / _ \ '_ \| __|
+ ___) | __/ | \ V /| | (_| __/ | | | (_| | | | | (_| | (_| | __/ | | | | | __/ | | | |_
+ |____/ \___|_| \_/ |_|\___\___|_| |_|\__,_|_| |_|\__,_|\__, |\___|_| |_| |_|\___|_| |_|\__|
+ |___/
+*/
+
+function refreshServices() {
+ console.log("Refreshing services status");
+
+ $.ajax({
+ url: 'launcher.php?type=get_systemd_services',
+ dataType: 'json',
+ method: 'GET',
+ cache: false,
+ success: function(response) {
+ console.log("Services data:", response);
+
+ if (response.success) {
+ displayServices(response.services);
+ } else {
+ showServiceError("Failed to load services: " + response.error);
+ }
+ },
+ error: function(xhr, status, error) {
+ console.error('Failed to load services:', error);
+ showServiceError("Failed to load services: " + error);
+ }
+ });
+}
+
+function displayServices(services) {
+ const tbody = document.getElementById('services-tbody');
+ tbody.innerHTML = '';
+
+ services.forEach(function(service) {
+ const row = document.createElement('tr');
+
+ // Service name
+ const nameCell = document.createElement('td');
+ nameCell.textContent = service.display_name || service.name;
+ row.appendChild(nameCell);
+
+ // Description
+ const descCell = document.createElement('td');
+ descCell.textContent = service.description || 'No description available';
+ descCell.className = 'text-muted small';
+ row.appendChild(descCell);
+
+ // Frequency
+ const freqCell = document.createElement('td');
+ freqCell.textContent = service.frequency || 'Unknown';
+ freqCell.className = 'text-primary small fw-bold';
+ row.appendChild(freqCell);
+
+ // Status
+ const statusCell = document.createElement('td');
+ const statusBadge = document.createElement('span');
+ statusBadge.className = `badge ${service.active ? 'bg-success' : 'bg-danger'}`;
+ statusBadge.textContent = service.active ? 'Running' : 'Stopped';
+ statusCell.appendChild(statusBadge);
+ row.appendChild(statusCell);
+
+ // Enabled
+ const enabledCell = document.createElement('td');
+ const enabledBadge = document.createElement('span');
+ enabledBadge.className = `badge ${service.enabled ? 'bg-info' : 'bg-secondary'}`;
+ enabledBadge.textContent = service.enabled ? 'Enabled' : 'Disabled';
+ enabledCell.appendChild(enabledBadge);
+ row.appendChild(enabledCell);
+
+ // Actions
+ const actionsCell = document.createElement('td');
+
+ // Restart button
+ const restartBtn = document.createElement('button');
+ restartBtn.className = 'btn btn-sm btn-warning me-2';
+ restartBtn.innerHTML = ' Restart';
+ restartBtn.onclick = function() {
+ restartService(service.name);
+ };
+ actionsCell.appendChild(restartBtn);
+
+ // Enable/Disable button
+ const toggleBtn = document.createElement('button');
+ toggleBtn.className = `btn btn-sm ${service.enabled ? 'btn-danger' : 'btn-success'}`;
+ toggleBtn.innerHTML = service.enabled ? ' Disable' : ' Enable';
+ toggleBtn.onclick = function() {
+ toggleService(service.name, !service.enabled);
+ };
+ actionsCell.appendChild(toggleBtn);
+
+ row.appendChild(actionsCell);
+ tbody.appendChild(row);
+ });
+}
+
+function showServiceError(message) {
+ const tbody = document.getElementById('services-tbody');
+ tbody.innerHTML = `
+
+ |
+ ${message}
+ |
+
+ `;
+}
+
+function restartService(serviceName) {
+ console.log(`Restarting service: ${serviceName}`);
+
+ if (!confirm(`Are you sure you want to restart ${serviceName}?`)) {
+ return;
+ }
+
+ const toastLiveExample = document.getElementById('liveToast');
+ const toastBody = toastLiveExample.querySelector('.toast-body');
+
+ $.ajax({
+ url: 'launcher.php?type=restart_systemd_service&service=' + encodeURIComponent(serviceName),
+ dataType: 'json',
+ method: 'GET',
+ cache: false,
+ success: function(response) {
+ console.log('Service restart response:', response);
+
+ let formattedMessage = '';
+
+ if (response.success) {
+ // Success message
+ toastLiveExample.classList.remove('text-bg-danger');
+ toastLiveExample.classList.add('text-bg-success');
+
+ formattedMessage = `
+ Success!
+ Service: ${serviceName}
+ ${response.message || 'Service restarted successfully'}
+ `;
+
+ // Refresh services after a short delay
+ setTimeout(refreshServices, 2000);
+ } else {
+ // Error message
+ toastLiveExample.classList.remove('text-bg-success');
+ toastLiveExample.classList.add('text-bg-danger');
+
+ formattedMessage = `
+ Error!
+ Service: ${serviceName}
+ ${response.error || 'Unknown error occurred'}
+ `;
+ }
+
+ // Update and show toast
+ toastBody.innerHTML = formattedMessage;
+ const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample);
+ toastBootstrap.show();
+ },
+ error: function(xhr, status, error) {
+ console.error('Failed to restart service:', error);
+
+ // Show error toast
+ toastLiveExample.classList.remove('text-bg-success');
+ toastLiveExample.classList.add('text-bg-danger');
+ toastBody.innerHTML = `
+ Request Failed!
+ Service: ${serviceName}
+ Error: ${error}
+ `;
+ const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample);
+ toastBootstrap.show();
+ }
+ });
+}
+
+function toggleService(serviceName, enable) {
+ const action = enable ? 'enable' : 'disable';
+ console.log(`${action} service: ${serviceName}`);
+
+ if (!confirm(`Are you sure you want to ${action} ${serviceName}?`)) {
+ return;
+ }
+
+ const toastLiveExample = document.getElementById('liveToast');
+ const toastBody = toastLiveExample.querySelector('.toast-body');
+
+ $.ajax({
+ url: 'launcher.php?type=toggle_systemd_service&service=' + encodeURIComponent(serviceName) + '&enable=' + enable,
+ dataType: 'json',
+ method: 'GET',
+ cache: false,
+ success: function(response) {
+ console.log('Service toggle response:', response);
+
+ let formattedMessage = '';
+
+ if (response.success) {
+ // Success message
+ toastLiveExample.classList.remove('text-bg-danger');
+ toastLiveExample.classList.add('text-bg-success');
+
+ formattedMessage = `
+ Success!
+ Service: ${serviceName}
+ ${response.message || `Service ${action}d successfully`}
+ `;
+
+ // Refresh services after a short delay
+ setTimeout(refreshServices, 2000);
+ } else {
+ // Error message
+ toastLiveExample.classList.remove('text-bg-success');
+ toastLiveExample.classList.add('text-bg-danger');
+
+ formattedMessage = `
+ Error!
+ Service: ${serviceName}
+ ${response.error || 'Unknown error occurred'}
+ `;
+ }
+
+ // Update and show toast
+ toastBody.innerHTML = formattedMessage;
+ const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample);
+ toastBootstrap.show();
+ },
+ error: function(xhr, status, error) {
+ console.error('Failed to toggle service:', error);
+
+ // Show error toast
+ toastLiveExample.classList.remove('text-bg-success');
+ toastLiveExample.classList.add('text-bg-danger');
+ toastBody.innerHTML = `
+ Request Failed!
+ Service: ${serviceName}
+ Error: ${error}
+ `;
+ const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample);
+ toastBootstrap.show();
+ }
+ });
+}
+
+