krushimitravit's picture
Upload 9 files
0b91a54 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live Irrigation Status | Plant Disease Detector</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<!-- Google Fonts - Outfit -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<style>
/* ===== Color System ===== */
:root {
--deep-green: #1a5d3a;
--accent-green: #198754;
--darker-accent: #143d2e;
--bg-color: #f8f9fa;
--surface: #ffffff;
--text-dark: #212529;
--text-muted: #6c757d;
--border-color: #dee2e6;
}
/* ===== Typography ===== */
body {
font-family: 'Outfit', sans-serif;
background-color: var(--bg-color);
color: var(--text-dark);
font-weight: 400;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
}
/* ===== Header ===== */
.page-header {
background-color: var(--deep-green);
color: white;
padding: 2rem 0;
margin-bottom: 2rem;
}
.page-header h1 {
font-weight: 700;
font-size: 2rem;
margin: 0;
}
/* ===== Diagnostic Timeline (Results Page Exclusive) ===== */
.diagnostic-timeline {
display: flex;
justify-content: center;
align-items: center;
gap: 1.5rem;
margin: 2rem 0;
position: relative;
}
.timeline-step {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
position: relative;
z-index: 2;
}
.timeline-icon {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: var(--accent-green);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
box-shadow: 0 4px 12px rgba(25, 135, 84, 0.3);
}
.timeline-label {
font-weight: 500;
font-size: 0.85rem;
color: var(--text-dark);
}
.timeline-connector {
flex: 1;
height: 2px;
background-color: var(--accent-green);
margin: 0 -0.5rem;
position: relative;
top: -15px;
}
/* ===== Cards ===== */
.card {
background: var(--surface);
border-radius: 20px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
border: none;
margin-bottom: 1.5rem;
}
.card-header {
background-color: var(--deep-green);
color: white;
font-weight: 600;
font-size: 1.25rem;
padding: 1rem 1.5rem;
border-radius: 20px 20px 0 0;
text-align: center;
}
.card-body {
padding: 2rem;
}
/* ===== Status Cards ===== */
.status-card {
text-align: center;
}
.status-label {
font-size: 1rem;
color: var(--text-muted);
font-weight: 500;
margin-bottom: 0.5rem;
}
.status-text {
font-size: 1.75rem;
font-weight: 600;
padding: 0.75rem 1.5rem;
border-radius: 12px;
display: inline-block;
transition: all 0.3s ease;
}
.status-text.on {
color: var(--accent-green);
background-color: #e9f5e9;
border: 2px solid var(--accent-green);
}
.status-text.off {
color: #dc3545;
background-color: #f8d7da;
border: 2px solid #dc3545;
}
/* ===== Chart Container ===== */
.chart-container {
width: 100%;
height: 400px;
}
/* ===== Responsive ===== */
@media (max-width: 768px) {
.page-header h1 {
font-size: 1.5rem;
}
.diagnostic-timeline {
gap: 1rem;
}
.timeline-connector {
display: none;
}
.card-body {
padding: 1.5rem;
}
}
</style>
</head>
<body>
<!-- Page Header -->
<div class="page-header">
<div class="container">
<h1><i class="bi bi-droplet-fill"></i> Live Irrigation Status</h1>
</div>
</div>
<div class="container">
<!-- Diagnostic Timeline (Results Page Exclusive) -->
<div class="diagnostic-timeline">
<div class="timeline-step">
<div class="timeline-icon">
<i class="bi bi-search"></i>
</div>
<div class="timeline-label">Monitor</div>
</div>
<div class="timeline-connector"></div>
<div class="timeline-step">
<div class="timeline-icon">
<i class="bi bi-graph-up"></i>
</div>
<div class="timeline-label">Analyze</div>
</div>
<div class="timeline-connector"></div>
<div class="timeline-step">
<div class="timeline-icon">
<i class="bi bi-gear-fill"></i>
</div>
<div class="timeline-label">Control</div>
</div>
</div>
<!-- Main Content Card -->
<div class="card">
<div class="card-header">Real-Time Irrigation Dashboard</div>
<div class="card-body">
<div class="row text-center mb-4">
<div class="col-md-6">
<div class="status-label">Current Pump Status</div>
<div id="pumpStatus" class="status-text off">Loading...</div>
</div>
<div class="col-md-6 mt-3 mt-md-0">
<div class="status-label">Time Elapsed</div>
<div id="time-counter" class="status-text" style="color: #5a2d0c; background-color: #f0e6e0; border-color: #5a2d0c;">0 seconds</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-body">
<div id="gauge" class="chart-container"></div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-body">
<div id="graph1" class="chart-container"></div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-body">
<div id="graph2" class="chart-container"></div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-body">
<div id="graph3" class="chart-container"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let alertSound; // Will be initialized after user interaction
function initializeAudio() {
if (!alertSound) {
alertSound = new Audio('{{ url_for("static", filename="alarn_tune.mp3") }}');
console.log("Audio initialized.");
}
}
document.body.addEventListener('click', initializeAudio, { once: true });
function fetchPumpStatus() {
fetch('/update_pump_status')
.then(response => response.json())
.then(data => {
const statusElement = document.getElementById('pumpStatus');
const newStatus = data.pump_status;
const oldStatus = statusElement.innerText;
statusElement.innerText = newStatus;
statusElement.className = 'status-text ' + (newStatus === 'On' ? 'on' : 'off');
if (newStatus === 'Off' && oldStatus === 'On' && alertSound) {
alertSound.play().catch(e => console.error("Audio play failed:", e));
}
});
}
function fetchGraphData() {
fetch('/update_graph')
.then(response => response.json())
.then(data => {
if (data.length === 0) return;
const time = data.map((_, i) => i * 2); // Assuming 2 second intervals
const soilMoisture = data.map(entry => entry[0]);
const pumpStatus = data.map(entry => entry[1]);
const currentSoilMoisture = soilMoisture[soilMoisture.length - 1];
// Responsive layout for all plots
const responsiveLayout = { margin: { t: 40, b: 50, l: 50, r: 20 }, autosize: true };
Plotly.react('gauge', getGaugeData(currentSoilMoisture), { ...responsiveLayout, title: 'Soil Moisture' });
Plotly.react('graph1', getPumpStatusData(time, pumpStatus), { ...responsiveLayout, title: 'Pump Status vs. Time', yaxis: { tickvals: [-1, 1], ticktext: ['Off', 'On'], range: [-1.5, 1.5] }});
Plotly.react('graph2', getSoilMoistureData(time, soilMoisture), { ...responsiveLayout, title: 'Soil Moisture vs. Time' });
Plotly.react('graph3', getMoistureVsStatusData(soilMoisture, pumpStatus), { ...responsiveLayout, title: 'Pump Status vs. Soil Moisture', yaxis: { tickvals: [-1, 1], ticktext: ['Off', 'On'], range: [-1.5, 1.5] }});
});
}
// --- Chart Data Functions ---
const getGaugeData = value => [{ type: "indicator", mode: "gauge+number", value: value, gauge: { axis: { range: [0, 100] }, steps: [{ range: [0, 30], color: "#ea4335" },{ range: [30, 60], color: "#fbbc05" },{ range: [60, 100], color: "#34a853" }]}}];
const getPumpStatusData = (x, y) => [{ x, y, mode: 'lines+markers', type: 'scatter', line: { color: '#4285f4' } }];
const getSoilMoistureData = (x, y) => [{ x, y, mode: 'lines+markers', type: 'scatter', line: { color: '#34a853' } }];
const getMoistureVsStatusData = (x, y) => [{ x, y, mode: 'lines', type: 'scatter', line: { color: '#ea4335' } }];
let timeCounter = 0;
setInterval(() => { document.getElementById('time-counter').innerText = `${++timeCounter} seconds`; }, 1000);
setInterval(fetchPumpStatus, 2000);
setInterval(fetchGraphData, 2000);
</script>
</body>
</html>