feat(ui): add sensor tests to modem self-test
Add dynamic sensor testing (NPM, BME280, Noise, Envea) to the self-test based on enabled sensors in config. Results are included in the diagnostic report. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
199
html/saraR4.html
199
html/saraR4.html
@@ -419,6 +419,14 @@
|
||||
</div>
|
||||
<span id="test_network_status" class="badge bg-secondary">Pending</span>
|
||||
</div>
|
||||
|
||||
<!-- Separator for sensor tests -->
|
||||
<div id="sensor_tests_separator" class="list-group-item bg-light text-center py-1" style="display:none;">
|
||||
<small class="text-muted fw-bold">SENSOR TESTS</small>
|
||||
</div>
|
||||
|
||||
<!-- Dynamic sensor test entries will be added here -->
|
||||
<div id="sensor_tests_container"></div>
|
||||
</div>
|
||||
|
||||
<!-- Logs section -->
|
||||
@@ -1603,6 +1611,10 @@ function resetSelfTestUI() {
|
||||
document.getElementById('test_network_status').textContent = 'Pending';
|
||||
document.getElementById('test_network_detail').textContent = 'Waiting...';
|
||||
|
||||
// Reset sensor tests
|
||||
document.getElementById('sensor_tests_separator').style.display = 'none';
|
||||
document.getElementById('sensor_tests_container').innerHTML = '';
|
||||
|
||||
// Reset logs
|
||||
document.getElementById('selftest_logs').innerHTML = '';
|
||||
|
||||
@@ -1725,6 +1737,7 @@ async function selfTestSequence() {
|
||||
selfTestReport.modemVersion = configResponse.modem_version || 'Unknown';
|
||||
selfTestReport.latitude = configResponse.latitude_raw || 'N/A';
|
||||
selfTestReport.longitude = configResponse.longitude_raw || 'N/A';
|
||||
selfTestReport.config = configResponse;
|
||||
|
||||
// Get RTC time
|
||||
try {
|
||||
@@ -2011,6 +2024,186 @@ async function selfTestSequence() {
|
||||
testsFailed++;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// SENSOR TESTS - Test enabled sensors based on config
|
||||
// ═══════════════════════════════════════════════════════
|
||||
const config = selfTestReport.config || {};
|
||||
const sensorTests = [];
|
||||
|
||||
// NPM is always present
|
||||
sensorTests.push({ id: 'npm', name: 'NextPM (Particles)', type: 'npm', port: 'ttyAMA5' });
|
||||
|
||||
// BME280 if enabled
|
||||
if (config.BME280) {
|
||||
sensorTests.push({ id: 'bme280', name: 'BME280 (Temp/Hum)', type: 'BME280' });
|
||||
}
|
||||
|
||||
// Noise if enabled
|
||||
if (config.NOISE) {
|
||||
sensorTests.push({ id: 'noise', name: 'Noise Sensor', type: 'noise' });
|
||||
}
|
||||
|
||||
// Envea if enabled
|
||||
if (config.envea) {
|
||||
sensorTests.push({ id: 'envea', name: 'Envea (Gas Sensors)', type: 'envea' });
|
||||
}
|
||||
|
||||
// Create sensor test UI entries dynamically
|
||||
if (sensorTests.length > 0) {
|
||||
document.getElementById('sensor_tests_separator').style.display = '';
|
||||
const sensorContainer = document.getElementById('sensor_tests_container');
|
||||
sensorContainer.innerHTML = '';
|
||||
|
||||
sensorTests.forEach(sensor => {
|
||||
sensorContainer.innerHTML += `
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center" id="test_${sensor.id}">
|
||||
<div>
|
||||
<strong>${sensor.name}</strong>
|
||||
<div class="small text-muted" id="test_${sensor.id}_detail">Waiting...</div>
|
||||
</div>
|
||||
<span id="test_${sensor.id}_status" class="badge bg-secondary">Pending</span>
|
||||
</div>`;
|
||||
});
|
||||
}
|
||||
|
||||
addSelfTestLog('');
|
||||
addSelfTestLog('────────────────────────────────────────────────────────');
|
||||
addSelfTestLog('SENSOR TESTS');
|
||||
addSelfTestLog('────────────────────────────────────────────────────────');
|
||||
|
||||
// Run each sensor test
|
||||
for (const sensor of sensorTests) {
|
||||
await delay(500);
|
||||
|
||||
document.getElementById('selftest_status').innerHTML = `
|
||||
<div class="d-flex align-items-center text-primary">
|
||||
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||
<span>Testing ${sensor.name}...</span>
|
||||
</div>`;
|
||||
|
||||
updateTestStatus(sensor.id, 'Testing...', 'Reading sensor data...', 'bg-info');
|
||||
addSelfTestLog(`Testing ${sensor.name}...`);
|
||||
|
||||
try {
|
||||
if (sensor.type === 'npm') {
|
||||
// NPM sensor test
|
||||
const npmResult = await new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=npm&port=' + sensor.port,
|
||||
dataType: 'json',
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
success: function(data) { resolve(data); },
|
||||
error: function(xhr, status, error) { reject(new Error(error || status)); }
|
||||
});
|
||||
});
|
||||
|
||||
selfTestReport.rawResponses['NPM Sensor'] = JSON.stringify(npmResult, null, 2);
|
||||
addSelfTestLog(`NPM response: PM1=${npmResult.PM1}, PM2.5=${npmResult.PM25}, PM10=${npmResult.PM10}`);
|
||||
|
||||
// Check for errors
|
||||
const npmErrors = ['notReady', 'fanError', 'laserError', 'heatError', 't_rhError', 'memoryError', 'degradedState'];
|
||||
const activeErrors = npmErrors.filter(e => npmResult[e] === 1);
|
||||
|
||||
if (activeErrors.length > 0) {
|
||||
updateTestStatus(sensor.id, 'Warning', `Errors: ${activeErrors.join(', ')}`, 'bg-warning');
|
||||
testsFailed++;
|
||||
} else if (npmResult.PM1 !== undefined && npmResult.PM25 !== undefined && npmResult.PM10 !== undefined) {
|
||||
updateTestStatus(sensor.id, 'Passed', `PM1: ${npmResult.PM1} | PM2.5: ${npmResult.PM25} | PM10: ${npmResult.PM10} ug/m3`, 'bg-success');
|
||||
testsPassed++;
|
||||
} else {
|
||||
updateTestStatus(sensor.id, 'Warning', 'Incomplete data received', 'bg-warning');
|
||||
testsFailed++;
|
||||
}
|
||||
|
||||
} else if (sensor.type === 'BME280') {
|
||||
// BME280 sensor test
|
||||
const bme280Result = await new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=BME280',
|
||||
dataType: 'text',
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
success: function(data) { resolve(data); },
|
||||
error: function(xhr, status, error) { reject(new Error(error || status)); }
|
||||
});
|
||||
});
|
||||
|
||||
const bmeData = JSON.parse(bme280Result);
|
||||
selfTestReport.rawResponses['BME280 Sensor'] = JSON.stringify(bmeData, null, 2);
|
||||
addSelfTestLog(`BME280 response: temp=${bmeData.temp}, hum=${bmeData.hum}, press=${bmeData.press}`);
|
||||
|
||||
if (bmeData.temp !== undefined && bmeData.hum !== undefined && bmeData.press !== undefined) {
|
||||
updateTestStatus(sensor.id, 'Passed', `${bmeData.temp}°C | ${bmeData.hum}% | ${bmeData.press} hPa`, 'bg-success');
|
||||
testsPassed++;
|
||||
} else {
|
||||
updateTestStatus(sensor.id, 'Warning', 'Incomplete data received', 'bg-warning');
|
||||
testsFailed++;
|
||||
}
|
||||
|
||||
} else if (sensor.type === 'noise') {
|
||||
// Noise sensor test
|
||||
const noiseResult = await new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=noise',
|
||||
dataType: 'text',
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
success: function(data) { resolve(data); },
|
||||
error: function(xhr, status, error) { reject(new Error(error || status)); }
|
||||
});
|
||||
});
|
||||
|
||||
selfTestReport.rawResponses['Noise Sensor'] = noiseResult;
|
||||
addSelfTestLog(`Noise response: ${noiseResult.trim()}`);
|
||||
|
||||
const noiseValue = parseFloat(noiseResult.trim());
|
||||
if (!isNaN(noiseValue) && noiseValue > 0) {
|
||||
updateTestStatus(sensor.id, 'Passed', `${noiseValue} dB`, 'bg-success');
|
||||
testsPassed++;
|
||||
} else if (noiseResult.trim() !== '') {
|
||||
updateTestStatus(sensor.id, 'Warning', `Unexpected value: ${noiseResult.trim()}`, 'bg-warning');
|
||||
testsFailed++;
|
||||
} else {
|
||||
updateTestStatus(sensor.id, 'Failed', 'No data received', 'bg-danger');
|
||||
testsFailed++;
|
||||
}
|
||||
|
||||
} else if (sensor.type === 'envea') {
|
||||
// Envea sensor test - use the debug endpoint for all sensors
|
||||
const enveaResult = await new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=envea_debug',
|
||||
dataType: 'text',
|
||||
method: 'GET',
|
||||
timeout: 30000,
|
||||
success: function(data) { resolve(data); },
|
||||
error: function(xhr, status, error) { reject(new Error(error || status)); }
|
||||
});
|
||||
});
|
||||
|
||||
selfTestReport.rawResponses['Envea Sensors'] = enveaResult;
|
||||
addSelfTestLog(`Envea response: ${enveaResult.trim().substring(0, 200)}`);
|
||||
|
||||
if (enveaResult.trim() !== '' && !enveaResult.toLowerCase().includes('error')) {
|
||||
updateTestStatus(sensor.id, 'Passed', 'Sensors responding', 'bg-success');
|
||||
testsPassed++;
|
||||
} else if (enveaResult.toLowerCase().includes('error')) {
|
||||
updateTestStatus(sensor.id, 'Failed', 'Sensor error detected', 'bg-danger');
|
||||
testsFailed++;
|
||||
} else {
|
||||
updateTestStatus(sensor.id, 'Failed', 'No data received', 'bg-danger');
|
||||
testsFailed++;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
addSelfTestLog(`${sensor.name} test error: ${error.message}`);
|
||||
updateTestStatus(sensor.id, 'Failed', error.message, 'bg-danger');
|
||||
selfTestReport.rawResponses[`${sensor.name}`] = `ERROR: ${error.message}`;
|
||||
testsFailed++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
addSelfTestLog(`Test sequence error: ${error.message}`);
|
||||
} finally {
|
||||
@@ -2101,7 +2294,11 @@ GPS Location: ${selfTestReport.latitude || 'N/A'}, ${selfTestReport.longitude
|
||||
modem: 'Modem Connection',
|
||||
sim: 'SIM Card',
|
||||
signal: 'Signal Strength',
|
||||
network: 'Network Connection'
|
||||
network: 'Network Connection',
|
||||
npm: 'NextPM (Particles)',
|
||||
bme280: 'BME280 (Temp/Hum)',
|
||||
noise: 'Noise Sensor',
|
||||
envea: 'Envea (Gas Sensors)'
|
||||
};
|
||||
|
||||
for (const [testId, name] of Object.entries(testNames)) {
|
||||
|
||||
Reference in New Issue
Block a user