380 lines
15 KiB
HTML
380 lines
15 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Binglab Enviro Dashboard</title>
|
|
<link
|
|
rel="stylesheet"
|
|
href="{{ url_for('static', filename='style.css') }}"
|
|
/>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<h1>MBTECH Binglab Enviro Dashboard</h1>
|
|
<p class="subtitle">Environmental Monitoring System</p>
|
|
</header>
|
|
|
|
<div class="current-readings">
|
|
<h2>Current Conditions</h2>
|
|
<div class="reading-cards">
|
|
<div class="card temperature-card">
|
|
<div class="card-icon">🌡️</div>
|
|
<div class="card-content">
|
|
<div class="card-value" id="current-temp">--</div>
|
|
<div class="card-label">Temperature (°C)</div>
|
|
<div class="card-sublabel" id="temp-raw" style="font-size: 0.85em; color: #888; margin-top: 4px;">Raw: --°C</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card temperature-card">
|
|
<div class="card-icon">🌡️</div>
|
|
<div class="card-content">
|
|
<div class="card-value" id="current-temp-f">--</div>
|
|
<div class="card-label">Temperature (°F)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card pressure-card">
|
|
<div class="card-icon">🔽</div>
|
|
<div class="card-content">
|
|
<div class="card-value" id="current-pressure">
|
|
--
|
|
</div>
|
|
<div class="card-label">Pressure (hPa)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card light-card">
|
|
<div class="card-icon">💡</div>
|
|
<div class="card-content">
|
|
<div class="card-value" id="current-light">--</div>
|
|
<div class="card-label">Light (lux)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card color-card">
|
|
<div class="card-icon">🎨</div>
|
|
<div class="card-content">
|
|
<div class="color-swatch" id="color-swatch"></div>
|
|
<div class="card-label">Detected Color</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="last-update">
|
|
Last update: <span id="last-update">--</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stats-section">
|
|
<h2>Today's Statistics</h2>
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-title">Temperature</div>
|
|
<div class="stat-values">
|
|
<div class="stat-item">
|
|
<span class="stat-label">Min:</span>
|
|
<span class="stat-value" id="temp-min">--</span
|
|
>°C
|
|
</div>
|
|
<div class="stat-item">
|
|
<span class="stat-label">Max:</span>
|
|
<span class="stat-value" id="temp-max">--</span
|
|
>°C
|
|
</div>
|
|
<div class="stat-item">
|
|
<span class="stat-label">Avg:</span>
|
|
<span class="stat-value" id="temp-avg">--</span
|
|
>°C
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-title">Pressure</div>
|
|
<div class="stat-values">
|
|
<div class="stat-item">
|
|
<span class="stat-label">Min:</span>
|
|
<span class="stat-value" id="pressure-min"
|
|
>--</span
|
|
>
|
|
hPa
|
|
</div>
|
|
<div class="stat-item">
|
|
<span class="stat-label">Max:</span>
|
|
<span class="stat-value" id="pressure-max"
|
|
>--</span
|
|
>
|
|
hPa
|
|
</div>
|
|
<div class="stat-item">
|
|
<span class="stat-label">Avg:</span>
|
|
<span class="stat-value" id="pressure-avg"
|
|
>--</span
|
|
>
|
|
hPa
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-title">Light</div>
|
|
<div class="stat-values">
|
|
<div class="stat-item">
|
|
<span class="stat-label">Min:</span>
|
|
<span class="stat-value" id="light-min"
|
|
>--</span
|
|
>
|
|
lux
|
|
</div>
|
|
<div class="stat-item">
|
|
<span class="stat-label">Max:</span>
|
|
<span class="stat-value" id="light-max"
|
|
>--</span
|
|
>
|
|
lux
|
|
</div>
|
|
<div class="stat-item">
|
|
<span class="stat-label">Avg:</span>
|
|
<span class="stat-value" id="light-avg"
|
|
>--</span
|
|
>
|
|
lux
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="charts-section">
|
|
<h2>Historical Data</h2>
|
|
<div class="chart-container">
|
|
<canvas id="temperatureChart"></canvas>
|
|
</div>
|
|
<div class="chart-container">
|
|
<canvas id="pressureChart"></canvas>
|
|
</div>
|
|
<div class="chart-container">
|
|
<canvas id="lightChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let tempChart, pressureChart, lightChart;
|
|
|
|
// Initialize charts
|
|
function initCharts() {
|
|
const commonOptions = {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
aspectRatio: 3,
|
|
plugins: {
|
|
legend: {
|
|
display: false,
|
|
},
|
|
},
|
|
scales: {
|
|
x: {
|
|
type: "category",
|
|
ticks: {
|
|
maxTicksLimit: 10,
|
|
},
|
|
},
|
|
y: {
|
|
beginAtZero: false,
|
|
},
|
|
},
|
|
};
|
|
|
|
tempChart = new Chart(
|
|
document.getElementById("temperatureChart"),
|
|
{
|
|
type: "line",
|
|
data: {
|
|
labels: [],
|
|
datasets: [
|
|
{
|
|
label: "Temperature (°C)",
|
|
data: [],
|
|
borderColor: "#ff6b6b",
|
|
backgroundColor: "rgba(255, 107, 107, 0.1)",
|
|
tension: 0.4,
|
|
fill: true,
|
|
},
|
|
],
|
|
},
|
|
options: commonOptions,
|
|
},
|
|
);
|
|
|
|
pressureChart = new Chart(
|
|
document.getElementById("pressureChart"),
|
|
{
|
|
type: "line",
|
|
data: {
|
|
labels: [],
|
|
datasets: [
|
|
{
|
|
label: "Pressure (hPa)",
|
|
data: [],
|
|
borderColor: "#4ecdc4",
|
|
backgroundColor: "rgba(78, 205, 196, 0.1)",
|
|
tension: 0.4,
|
|
fill: true,
|
|
},
|
|
],
|
|
},
|
|
options: commonOptions,
|
|
},
|
|
);
|
|
|
|
lightChart = new Chart(document.getElementById("lightChart"), {
|
|
type: "line",
|
|
data: {
|
|
labels: [],
|
|
datasets: [
|
|
{
|
|
label: "Light (lux)",
|
|
data: [],
|
|
borderColor: "#ffe66d",
|
|
backgroundColor: "rgba(255, 230, 109, 0.1)",
|
|
tension: 0.4,
|
|
fill: true,
|
|
},
|
|
],
|
|
},
|
|
options: commonOptions,
|
|
});
|
|
}
|
|
|
|
// Update current readings
|
|
async function updateCurrent() {
|
|
try {
|
|
const response = await fetch("/api/current");
|
|
const data = await response.json();
|
|
|
|
document.getElementById("current-temp").textContent =
|
|
data.temperature;
|
|
|
|
// Calculate and update Fahrenheit temperature
|
|
const tempF = (data.temperature * 9/5) + 32;
|
|
document.getElementById("current-temp-f").textContent =
|
|
tempF.toFixed(2);
|
|
|
|
// Update raw temperature if available
|
|
if (data.temperature_raw !== undefined) {
|
|
document.getElementById("temp-raw").textContent =
|
|
`Raw: ${data.temperature_raw}°C`;
|
|
}
|
|
|
|
document.getElementById("current-pressure").textContent =
|
|
data.pressure;
|
|
document.getElementById("current-light").textContent =
|
|
data.light;
|
|
document.getElementById("last-update").textContent =
|
|
data.timestamp;
|
|
|
|
// Update color swatch if RGB data is available
|
|
if (data.rgb) {
|
|
const colorSwatch = document.getElementById("color-swatch");
|
|
colorSwatch.style.backgroundColor = `rgb(${data.rgb.r}, ${data.rgb.g}, ${data.rgb.b})`;
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching current data:", error);
|
|
}
|
|
}
|
|
|
|
// Update statistics
|
|
async function updateStats() {
|
|
try {
|
|
const response = await fetch("/api/stats");
|
|
const data = await response.json();
|
|
|
|
if (data.temperature) {
|
|
document.getElementById("temp-min").textContent =
|
|
data.temperature.min;
|
|
document.getElementById("temp-max").textContent =
|
|
data.temperature.max;
|
|
document.getElementById("temp-avg").textContent =
|
|
data.temperature.avg;
|
|
}
|
|
|
|
if (data.pressure) {
|
|
document.getElementById("pressure-min").textContent =
|
|
data.pressure.min;
|
|
document.getElementById("pressure-max").textContent =
|
|
data.pressure.max;
|
|
document.getElementById("pressure-avg").textContent =
|
|
data.pressure.avg;
|
|
}
|
|
|
|
if (data.light) {
|
|
document.getElementById("light-min").textContent =
|
|
data.light.min;
|
|
document.getElementById("light-max").textContent =
|
|
data.light.max;
|
|
document.getElementById("light-avg").textContent =
|
|
data.light.avg;
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching stats:", error);
|
|
}
|
|
}
|
|
|
|
// Update historical charts
|
|
async function updateHistory() {
|
|
try {
|
|
const response = await fetch("/api/history");
|
|
const data = await response.json();
|
|
|
|
if (data.length === 0) return;
|
|
|
|
const labels = data.map((d) => {
|
|
const date = new Date(d.timestamp);
|
|
return date.toLocaleTimeString("en-US", {
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
});
|
|
});
|
|
|
|
const temps = data.map((d) => d.temperature);
|
|
const pressures = data.map((d) => d.pressure);
|
|
const lights = data.map((d) => d.light);
|
|
|
|
tempChart.data.labels = labels;
|
|
tempChart.data.datasets[0].data = temps;
|
|
tempChart.update();
|
|
|
|
pressureChart.data.labels = labels;
|
|
pressureChart.data.datasets[0].data = pressures;
|
|
pressureChart.update();
|
|
|
|
lightChart.data.labels = labels;
|
|
lightChart.data.datasets[0].data = lights;
|
|
lightChart.update();
|
|
} catch (error) {
|
|
console.error("Error fetching history:", error);
|
|
}
|
|
}
|
|
|
|
// Initialize and start auto-refresh
|
|
initCharts();
|
|
updateCurrent();
|
|
updateStats();
|
|
updateHistory();
|
|
|
|
// Refresh current readings every 10 seconds
|
|
setInterval(updateCurrent, 10000);
|
|
|
|
// Refresh stats and history every 60 seconds
|
|
setInterval(() => {
|
|
updateStats();
|
|
updateHistory();
|
|
}, 60000);
|
|
</script>
|
|
</body>
|
|
</html>
|