v1.9.13: Capteur CO2 Senseair S88 - scaffolding

Table data_S88, flag config S88 + port configurable S88_port
(default /dev/ttyAMA5), service/timer systemd 10s, carte
sensors.html, endpoint launcher.php, toggle admin.html.

read_co2() est un stub NotImplementedError en attente du datasheet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
PaulVua
2026-06-01 16:15:37 +02:00
parent 05734715a7
commit 239bdfea69
10 changed files with 304 additions and 5 deletions

View File

@@ -139,6 +139,13 @@
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" value="" id="check_s88" onchange="update_config_sqlite('S88', this.checked)">
<label class="form-check-label" for="check_s88">
Send CO2 sensor data (Senseair S88)
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" value="" id="check_wifi_power_saving" onchange="update_config_sqlite('wifi_power_saving', this.checked)">
<label class="form-check-label" for="check_wifi_power_saving">
@@ -580,6 +587,7 @@ window.onload = function() {
const checkbox_solar = document.getElementById("check_solarBattery");
const checkbox_noise = document.getElementById("check_NOISE");
const checkbox_mhz19 = document.getElementById("check_mhz19");
const checkbox_s88 = document.getElementById("check_s88");
const checkbox_wifi_power_saving = document.getElementById("check_wifi_power_saving");
checkbox_bme.checked = response["BME280"];
@@ -589,6 +597,7 @@ window.onload = function() {
checkbox_wind.checked = response["windMeter"];
checkbox_noise.checked = response["NOISE"];
checkbox_mhz19.checked = response["MHZ19"];
checkbox_s88.checked = response["S88"];
checkbox_wifi_power_saving.checked = response["wifi_power_saving"];
checkbox_uSpot.checked = response["send_uSpot"];

View File

@@ -794,7 +794,7 @@ if ($type == "db_table_stats") {
$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'];
$tables = ['data_NPM', 'data_NPM_5channels', 'data_BME280', 'data_envea', 'data_WIND', 'data_MPPT', 'data_NOISE', 'data_MHZ19', 'data_S88'];
$tableStats = [];
foreach ($tables as $tableName) {
@@ -844,7 +844,7 @@ if ($type == "download_full_table") {
$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'];
$allowedTables = ['data_NPM', 'data_NPM_5channels', 'data_BME280', 'data_envea', 'data_WIND', 'data_MPPT', 'data_NOISE', 'data_MHZ19', 'data_S88'];
if (!in_array($table, $allowedTables)) {
header('Content-Type: application/json');
@@ -861,7 +861,8 @@ if ($type == "download_full_table") {
'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'
'data_MHZ19' => 'TimestampUTC,CO2_ppm',
'data_S88' => 'TimestampUTC,CO2_ppm'
];
try {
@@ -1009,6 +1010,12 @@ if ($type == "mhz19") {
echo $output;
}
if ($type == "s88") {
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/S88/get_data.py';
$output = shell_exec($command);
echo $output;
}
if ($type == "table_mesure") {
$table=$_GET['table'];
@@ -1666,6 +1673,10 @@ if ($type == "get_systemd_services") {
'description' => 'Reads CO2 concentration from MH-Z19 sensor',
'frequency' => 'Every 2 minutes'
],
'nebuleair-s88-data.timer' => [
'description' => 'Reads CO2 concentration from Senseair S88 sensor',
'frequency' => 'Every 10 seconds'
],
'nebuleair-db-cleanup-data.timer' => [
'description' => 'Cleans up old data from database',
'frequency' => 'Daily'
@@ -1752,6 +1763,7 @@ if ($type == "restart_systemd_service") {
'nebuleair-mppt-data.timer',
'nebuleair-noise-data.timer',
'nebuleair-mhz19-data.timer',
'nebuleair-s88-data.timer',
'nebuleair-db-cleanup-data.timer',
'nebuleair-wifi-powersave.timer',
'nebuleair-cpu-power.service',
@@ -1817,6 +1829,7 @@ if ($type == "toggle_systemd_service") {
'nebuleair-mppt-data.timer',
'nebuleair-noise-data.timer',
'nebuleair-mhz19-data.timer',
'nebuleair-s88-data.timer',
'nebuleair-db-cleanup-data.timer',
'nebuleair-wifi-powersave.timer',
'nebuleair-cpu-power.service',

View File

@@ -363,6 +363,52 @@
});
}
function getS88_values() {
console.log("Data from Senseair S88 CO2 sensor:");
$("#loading_s88").show();
$.ajax({
url: 'launcher.php?type=s88',
dataType: 'json',
method: 'GET',
success: function (response) {
console.log(response);
const tableBody = document.getElementById("data-table-body_s88");
tableBody.innerHTML = "";
$("#loading_s88").hide();
if (response.error) {
$("#data-table-body_s88").append(`
<tr>
<td colspan="2" class="text-danger">
${response.error}
</td>
</tr>
`);
} else if (response.CO2 !== undefined) {
$("#data-table-body_s88").append(`
<tr>
<td>CO2</td>
<td>${response.CO2} ppm</td>
</tr>
`);
}
},
error: function (xhr, status, error) {
console.error('AJAX request failed:', status, error);
$("#loading_s88").hide();
const tableBody = document.getElementById("data-table-body_s88");
tableBody.innerHTML = `
<tr>
<td colspan="2" class="text-danger">
⚠ Erreur de communication avec le capteur
</td>
</tr>
`;
}
});
}
function getMHZ19_values() {
console.log("Data from MH-Z19 CO2 sensor:");
$("#loading_mhz19").show();
@@ -606,6 +652,29 @@
container.innerHTML += MHZ19_HTML;
}
//creates Senseair S88 CO2 card
if (config.S88) {
const S88_HTML = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port UART
</div>
<div class="card-body">
<h5 class="card-title">Senseair S88 CO2</h5>
<p class="card-text">Capteur de dioxyde de carbone.</p>
<button class="btn btn-primary mb-1" onclick="getS88_values()" data-i18n="common.getData">Get Data</button>
<div id="loading_s88" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_s88"></tbody>
</table>
</div>
</div>
</div>`;
container.innerHTML += S88_HTML;
}
//Si on a des SONDES ENVEA connectée il faut faire un deuxième call dans la table envea_sondes_table
//creates ENVEA debug card
if (config.envea) {