Multi-Sensor-Unterstützung hinzugefügt

- Sensor-Auswahl-Dropdown in feinstaub.html implementiert
- sensor_name Feld in README und Webhook-Konfiguration dokumentiert
- Automatische Erkennung und Filterung nach Sensoren

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 08:45:23 +00:00
parent 134bbbb485
commit 1986134a13
3 changed files with 109 additions and 26 deletions

View File

@@ -182,6 +182,28 @@
border-radius: 5px;
font-size: 14px;
}
.sensor-selection {
width: 100%;
margin-top: 20px;
padding: 20px;
background: #f7fafc;
border-radius: 5px;
border-left: 4px solid #48bb78;
display: none;
}
.sensor-selection h3 {
margin-bottom: 10px;
color: #2d3748;
}
.sensor-selection select {
width: 100%;
padding: 10px 15px;
border: 2px solid #e2e8f0;
border-radius: 5px;
font-size: 14px;
background: white;
cursor: pointer;
}
</style>
</head>
<body>
@@ -221,7 +243,14 @@
</div>
</div>
</div>
<div class="sensor-selection" id="sensorSelection">
<h3>Sensor-Auswahl</h3>
<select id="sensorSelect">
<option value="all">Alle Sensoren</option>
</select>
</div>
<div id="status"></div>
<div class="stats" id="stats" style="display: none;">
<div class="stat-card">
@@ -254,9 +283,45 @@
let filterSettings = {
enabled: true,
p1Max: 100,
p2Max: 100
p2Max: 100,
selectedSensor: 'all'
};
function detectAndPopulateSensors(data) {
// Prüfen, ob sensor_name Feld vorhanden ist
const hasSensorName = data.length > 0 && data.some(item => item.sensor_name);
if (!hasSensorName) {
// Sensor-Auswahl ausblenden, wenn kein sensor_name vorhanden
document.getElementById('sensorSelection').style.display = 'none';
filterSettings.selectedSensor = 'all';
return;
}
// Unique Sensoren extrahieren
const sensors = [...new Set(data.map(item => item.sensor_name).filter(Boolean))];
if (sensors.length > 1) {
// Sensor-Auswahl nur anzeigen, wenn mehrere Sensoren vorhanden sind
document.getElementById('sensorSelection').style.display = 'block';
// Dropdown befüllen
const select = document.getElementById('sensorSelect');
select.innerHTML = '<option value="all">Alle Sensoren</option>';
sensors.forEach(sensor => {
const option = document.createElement('option');
option.value = sensor;
option.textContent = sensor;
select.appendChild(option);
});
} else {
// Nur ein Sensor vorhanden, Auswahl nicht nötig
document.getElementById('sensorSelection').style.display = 'none';
filterSettings.selectedSensor = sensors[0] || 'all';
}
}
function calculateOutlierThreshold(values) {
const sorted = [...values].sort((a, b) => a - b);
const q1Index = Math.floor(sorted.length * 0.25);
@@ -269,20 +334,27 @@
}
function applyFilters(data) {
if (!filterSettings.enabled) {
document.getElementById('filteredCount').textContent = '0 Datenpunkte entfernt';
return data;
let filtered = data;
// Filter nach Sensor
if (filterSettings.selectedSensor !== 'all') {
filtered = filtered.filter(item => item.sensor_name === filterSettings.selectedSensor);
}
const filtered = data.filter(item => {
const p1 = parseFloat(item.SDS_P1) || 0;
const p2 = parseFloat(item.SDS_P2) || 0;
return p1 <= filterSettings.p1Max && p2 <= filterSettings.p2Max;
});
// Filter nach Ausreißern (nur wenn aktiviert)
if (filterSettings.enabled) {
const beforeOutlierFilter = filtered.length;
filtered = filtered.filter(item => {
const p1 = parseFloat(item.SDS_P1) || 0;
const p2 = parseFloat(item.SDS_P2) || 0;
return p1 <= filterSettings.p1Max && p2 <= filterSettings.p2Max;
});
const removed = beforeOutlierFilter - filtered.length;
document.getElementById('filteredCount').textContent = `${removed} Datenpunkte entfernt`;
} else {
document.getElementById('filteredCount').textContent = '0 Datenpunkte entfernt';
}
const removed = data.length - filtered.length;
document.getElementById('filteredCount').textContent = `${removed} Datenpunkte entfernt`;
return filtered;
}
@@ -334,13 +406,14 @@
function processData(jsonData) {
let data = Array.isArray(jsonData) ? jsonData : (jsonData.data || jsonData.records || []);
if (data.length === 0) {
showStatus('Keine Daten gefunden. Überprüfen Sie das JSON-Format.', 'error');
return;
}
rawData = data;
detectAndPopulateSensors(rawData);
autoDetectThresholds(rawData);
renderChart();
}
@@ -525,14 +598,20 @@
renderChart();
});
// Sensor-Auswahl Event Listener
document.getElementById('sensorSelect').addEventListener('change', function(e) {
filterSettings.selectedSensor = e.target.value;
renderChart();
});
// Beispieldaten für Demo
const exampleData = [
{ Uhrzeit: '09:45:36', SDS_P1: 17.53, SDS_P2: 96272.72 },
{ Uhrzeit: '10:00:38', SDS_P1: 9.15, SDS_P2: 6.22 },
{ Uhrzeit: '10:15:36', SDS_P1: 9.75, SDS_P2: 5.97 },
{ Uhrzeit: '10:30:36', SDS_P1: 9.93, SDS_P2: 6.35 },
{ Uhrzeit: '10:45:36', SDS_P1: 9.55, SDS_P2: 5.88 },
{ Uhrzeit: '11:00:36', SDS_P1: 12.15, SDS_P2: 6.93 }
{ Uhrzeit: '09:45:36', SDS_P1: 17.53, SDS_P2: 96272.72, sensor_name: 'Sensor-1' },
{ Uhrzeit: '10:00:38', SDS_P1: 9.15, SDS_P2: 6.22, sensor_name: 'Sensor-1' },
{ Uhrzeit: '10:15:36', SDS_P1: 9.75, SDS_P2: 5.97, sensor_name: 'Sensor-2' },
{ Uhrzeit: '10:30:36', SDS_P1: 9.93, SDS_P2: 6.35, sensor_name: 'Sensor-1' },
{ Uhrzeit: '10:45:36', SDS_P1: 9.55, SDS_P2: 5.88, sensor_name: 'Sensor-2' },
{ Uhrzeit: '11:00:36', SDS_P1: 12.15, SDS_P2: 6.93, sensor_name: 'Sensor-2' }
];
// Demo-Daten initial laden