You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
4.3 KiB
HTML
148 lines
4.3 KiB
HTML
4 weeks ago
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
<meta charset="UTF-8">
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
<title>Air Quality Monitor</title>
|
||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400&display=swap" rel="stylesheet">
|
||
|
<style>
|
||
|
/* General Styles */
|
||
|
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Roboto', sans-serif; }
|
||
|
body { display: flex; flex-direction: column; align-items: center; justify-content: center; background: #f5f5f5; color: #333; height: 100vh; }
|
||
|
|
||
|
h1 { font-size: 2em; color: #444; margin-bottom: 20px; }
|
||
|
|
||
|
/* Container for Sensors */
|
||
|
.sensor-container {
|
||
|
display: flex;
|
||
|
flex-wrap: wrap;
|
||
|
justify-content: center;
|
||
|
gap: 20px;
|
||
|
width: 100%;
|
||
|
max-width: 800px;
|
||
|
padding: 10px;
|
||
|
}
|
||
|
|
||
|
/* Sensor Box */
|
||
|
.sensor {
|
||
|
background: #fff;
|
||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||
|
border-radius: 10px;
|
||
|
width: 180px;
|
||
|
padding: 20px;
|
||
|
text-align: center;
|
||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||
|
}
|
||
|
.sensor:hover { transform: scale(1.05); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15); }
|
||
|
|
||
|
/* Sensor Titles */
|
||
|
.sensor-title {
|
||
|
font-weight: 400;
|
||
|
color: #555;
|
||
|
font-size: 1.2em;
|
||
|
margin-bottom: 10px;
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
}
|
||
|
|
||
|
/* Sensor Value */
|
||
|
.sensor-value {
|
||
|
font-size: 2.5em;
|
||
|
font-weight: 300;
|
||
|
color: #333;
|
||
|
margin-bottom: 5px;
|
||
|
animation: fadeIn 0.5s ease;
|
||
|
}
|
||
|
|
||
|
.unit { font-size: 0.8em; color: #777; }
|
||
|
|
||
|
/* Icon Styles */
|
||
|
.sensor-icon {
|
||
|
width: 30px;
|
||
|
height: 30px;
|
||
|
margin-right: 8px;
|
||
|
filter: grayscale(50%);
|
||
|
}
|
||
|
|
||
|
/* Keyframes for Smooth Animations */
|
||
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
|
||
|
<h1>Room Air Quality Monitor</h1>
|
||
|
<div class="sensor-container">
|
||
|
<div id="temperature" class="sensor">
|
||
|
<div class="sensor-title">
|
||
|
<img src="./images/thermometer-outline.svg" class="sensor-icon" alt="Temperature Icon">
|
||
|
Temperature
|
||
|
</div>
|
||
|
<div class="sensor-value" id="temp-value">--</div>
|
||
|
<div class="unit">℃</div>
|
||
|
</div>
|
||
|
|
||
|
<div id="humidity" class="sensor">
|
||
|
<div class="sensor-title">
|
||
|
<img src="./images/water-outline.svg" class="sensor-icon" alt="Humidity Icon">
|
||
|
Humidity
|
||
|
</div>
|
||
|
<div class="sensor-value" id="humidity-value">--</div>
|
||
|
<div class="unit">%</div>
|
||
|
</div>
|
||
|
|
||
|
<div id="co2" class="sensor">
|
||
|
<div class="sensor-title">
|
||
|
<img src="./images/cloud-outline.svg" class="sensor-icon" alt="CO2 Icon">
|
||
|
CO₂
|
||
|
</div>
|
||
|
<div class="sensor-value" id="co2-value">--</div>
|
||
|
<div class="unit">ppm</div>
|
||
|
</div>
|
||
|
|
||
|
<div id="pressure" class="sensor">
|
||
|
<div class="sensor-title">
|
||
|
<img src="./images/sunny-outline.svg" class="sensor-icon" alt="Pressure Icon">
|
||
|
Pressure
|
||
|
</div>
|
||
|
<div class="sensor-value" id="pressure-value">--</div>
|
||
|
<div class="unit">hPa</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<script>
|
||
|
async function fetchData() {
|
||
|
try {
|
||
|
const res = await Promise.all([
|
||
|
//fetch("./data/BH1750.json"),
|
||
|
//fetch("./data/BMP180.json"),
|
||
|
fetch("./data/HTU21D.json"),
|
||
|
//fetch("./data/MH_Z19.json")
|
||
|
]);
|
||
|
|
||
|
const data = (await Promise.all(res.map(r => r.json()))).reduce((acc, item) => ({ ...acc, ...item }), {});
|
||
|
return data;
|
||
|
} catch (error) {
|
||
|
console.error("Failed to fetch sensor data:", error);
|
||
|
return {};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function updateUI(data) {
|
||
|
document.getElementById("temp-value").textContent = data.temperature ? data.temperature.toFixed(1) : "--";
|
||
|
document.getElementById("humidity-value").textContent = data.humidity ? data.humidity.toFixed(1) : "--";
|
||
|
document.getElementById("co2-value").textContent = data.co2 ? data.co2 : "--";
|
||
|
document.getElementById("pressure-value").textContent = data.pressure ? data.pressure.toFixed(1) : "--";
|
||
|
}
|
||
|
|
||
|
async function refreshData() {
|
||
|
const data = await fetchData();
|
||
|
updateUI(data);
|
||
|
}
|
||
|
|
||
|
setInterval(refreshData, 6000); // Refresh every 60 seconds
|
||
|
refreshData(); // Initial fetch
|
||
|
</script>
|
||
|
</body>
|
||
|
</html>
|