Spaces:
Sleeping
Sleeping
Update templates/predict.html
Browse files- templates/predict.html +66 -101
templates/predict.html
CHANGED
|
@@ -1,25 +1,28 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="en">
|
| 3 |
<head>
|
| 4 |
-
<meta charset="UTF-8"
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0"
|
| 6 |
<title>Prediction Results</title>
|
| 7 |
-
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
|
| 8 |
<style>
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
| 20 |
</style>
|
| 21 |
</head>
|
| 22 |
<body>
|
|
|
|
| 23 |
<div class="header">
|
| 24 |
<h1>Water Requirement Prediction Results</h1>
|
| 25 |
</div>
|
|
@@ -39,9 +42,8 @@
|
|
| 39 |
</div>
|
| 40 |
|
| 41 |
<div class="info-card">
|
| 42 |
-
<h4>
|
| 43 |
-
<p class="mt-3">
|
| 44 |
-
<p>Make sure to reply to the WhatsApp message we sent earlier. (If you didn't receive it, check your phone number or resubmit prediction.)</p>
|
| 45 |
</div>
|
| 46 |
|
| 47 |
<div class="camera-container" id="cameraContainer">
|
|
@@ -49,119 +51,83 @@
|
|
| 49 |
<video id="videoElement" autoplay playsinline></video>
|
| 50 |
<div class="timer" id="timer"></div>
|
| 51 |
</div>
|
| 52 |
-
|
| 53 |
-
<div class="alert-message" id="alertMessage"></div>
|
| 54 |
</div>
|
| 55 |
-
|
| 56 |
-
<!-- Modal that appears after prediction, instructing the user to reply '1' on WhatsApp -->
|
| 57 |
-
<div class="modal fade" id="whatsappModal" tabindex="-1" aria-labelledby="whatsappModalLabel" aria-hidden="true">
|
| 58 |
-
<div class="modal-dialog modal-dialog-centered">
|
| 59 |
-
<div class="modal-content">
|
| 60 |
-
<div class="modal-header" style="background-color:#28a745;color:white;">
|
| 61 |
-
<h5 class="modal-title" id="whatsappModalLabel">Start Monitoring</h5>
|
| 62 |
-
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
| 63 |
-
</div>
|
| 64 |
-
<div class="modal-body">
|
| 65 |
-
<p>To start live monitoring (camera + motor), please reply <strong>1</strong> on WhatsApp to the message we just sent you. Reply <strong>0</strong> to cancel.</p>
|
| 66 |
-
<p class="mb-0">This page will start the camera automatically when your WhatsApp reply is received.</p>
|
| 67 |
-
</div>
|
| 68 |
-
<div class="modal-footer">
|
| 69 |
-
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Ok, got it</button>
|
| 70 |
-
</div>
|
| 71 |
-
</div>
|
| 72 |
-
</div>
|
| 73 |
-
</div>
|
| 74 |
-
|
| 75 |
-
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
| 76 |
<script>
|
| 77 |
-
//
|
| 78 |
-
|
| 79 |
-
const timeUnit = "{{ time_unit }}";
|
| 80 |
-
const estimatedTimeSecondsFromServer = timeUnit === "minutes" ? estimatedTime * 60 : estimatedTime;
|
| 81 |
let clientTimerInterval = null;
|
| 82 |
let cameraStream = null;
|
| 83 |
-
let
|
| 84 |
-
let currentlyMonitoring = false;
|
| 85 |
|
| 86 |
-
//
|
| 87 |
window.onload = function() {
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
var bsModal = new bootstrap.Modal(whatsappModalEl, { backdrop: 'static', keyboard: false });
|
| 92 |
-
bsModal.show();
|
| 93 |
-
|
| 94 |
-
// start polling for monitor status
|
| 95 |
-
pollingInterval = setInterval(checkMonitorStatus, 2000);
|
| 96 |
-
// check once immediately
|
| 97 |
-
checkMonitorStatus();
|
| 98 |
};
|
| 99 |
-
|
|
|
|
| 100 |
function checkMonitorStatus() {
|
| 101 |
-
fetch('/
|
| 102 |
.then(resp => resp.json())
|
| 103 |
.then(data => {
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
} else if (!
|
| 111 |
-
//
|
| 112 |
-
|
|
|
|
|
|
|
| 113 |
stopCameraAndTimer();
|
| 114 |
-
document.getElementById('alertMessage').textContent = "Monitoring stopped.";
|
| 115 |
-
} else {
|
| 116 |
-
// no change - optionally update time remaining
|
| 117 |
-
if (isMon && data.time_remaining != null) {
|
| 118 |
-
// update timer display if running
|
| 119 |
-
const t = data.time_remaining;
|
| 120 |
-
document.getElementById('timer').textContent = t + "s";
|
| 121 |
-
}
|
| 122 |
}
|
| 123 |
})
|
| 124 |
.catch(err => {
|
| 125 |
-
console.error('
|
|
|
|
|
|
|
| 126 |
});
|
| 127 |
}
|
| 128 |
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
cameraContainer.style.display = 'flex';
|
| 133 |
-
|
| 134 |
-
|
| 135 |
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
| 136 |
navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, audio: false })
|
| 137 |
.then(function(stream) {
|
| 138 |
cameraStream = stream;
|
| 139 |
-
|
| 140 |
})
|
| 141 |
.catch(function(err) {
|
| 142 |
-
console.error("Camera Error: "
|
| 143 |
alert("Could not access the camera. Please ensure you have given permission.");
|
| 144 |
});
|
| 145 |
} else {
|
| 146 |
alert("Camera API not supported in this browser.");
|
| 147 |
}
|
| 148 |
|
| 149 |
-
//
|
| 150 |
-
|
| 151 |
-
let
|
| 152 |
-
const timerElem = document.getElementById('timer');
|
| 153 |
timerElem.classList.remove('timer-finished');
|
| 154 |
|
|
|
|
| 155 |
clientTimerInterval = setInterval(function() {
|
| 156 |
if (timeRemaining <= 0) {
|
| 157 |
clearInterval(clientTimerInterval);
|
| 158 |
timerElem.textContent = "Finished";
|
| 159 |
timerElem.classList.add('timer-finished');
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
beep.play().catch(()=>{});
|
| 163 |
-
} catch(e) {}
|
| 164 |
-
document.getElementById('alertMessage').textContent = "Irrigation time is over. Motor is off automatically.";
|
| 165 |
} else {
|
| 166 |
timerElem.textContent = timeRemaining + "s";
|
| 167 |
}
|
|
@@ -169,20 +135,19 @@
|
|
| 169 |
}, 1000);
|
| 170 |
}
|
| 171 |
|
|
|
|
| 172 |
function stopCameraAndTimer() {
|
| 173 |
-
// Stop client timer
|
| 174 |
clearInterval(clientTimerInterval);
|
| 175 |
-
|
| 176 |
-
timerElem.textContent = "";
|
| 177 |
-
timerElem.classList.remove('timer-finished');
|
| 178 |
|
| 179 |
-
// Stop camera stream
|
| 180 |
if (cameraStream) {
|
| 181 |
cameraStream.getTracks().forEach(track => track.stop());
|
| 182 |
cameraStream = null;
|
| 183 |
}
|
| 184 |
-
|
|
|
|
|
|
|
| 185 |
}
|
| 186 |
</script>
|
| 187 |
</body>
|
| 188 |
-
</html>
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="en">
|
| 3 |
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>Prediction Results</title>
|
| 7 |
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
| 8 |
<style>
|
| 9 |
+
body { background-color: #f4f8f4; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
|
| 10 |
+
.header { background-color: #28a745; color: white; padding: 20px; text-align: center; font-weight: bold; }
|
| 11 |
+
.container { margin-top: 30px; padding: 20px; background-color: white; border-radius: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
|
| 12 |
+
.result-label { font-weight: bold; color: #555; }
|
| 13 |
+
.form-control[readonly] { font-size: 1.2rem; text-align: center; font-weight: bold; color: #28a745; background-color: #e9f5e9; border: 2px solid #28a745; }
|
| 14 |
+
.gauge-container { margin-top: 20px; text-align: center; }
|
| 15 |
+
.info-card { background-color: #f8f9fa; border: 1px solid #dee2e6; border-radius: 10px; padding: 25px; margin-top: 40px; text-align: center; }
|
| 16 |
+
.camera-container { margin-top: 20px; display: none; flex-direction: column; align-items: center; gap: 20px; }
|
| 17 |
+
video { width: 100%; max-width: 720px; height: auto; border-radius: 10px; border: 2px solid #ddd; background-color: #000; }
|
| 18 |
+
.timer { font-size: 2rem; color: white; background-color: #28a745; font-weight: bold; padding: 10px 25px; border-radius: 10px; display: inline-block; min-width: 120px; text-align: center; }
|
| 19 |
+
.timer-finished { font-size: 1.5rem; padding: 15px 40px; background-color: #dc3545; }
|
| 20 |
+
.alert-message { font-size: 1.2rem; font-weight: bold; margin-top: 20px; text-align: center; }
|
| 21 |
+
.modal-header { background-color: #28a745; color: white; }
|
| 22 |
</style>
|
| 23 |
</head>
|
| 24 |
<body>
|
| 25 |
+
|
| 26 |
<div class="header">
|
| 27 |
<h1>Water Requirement Prediction Results</h1>
|
| 28 |
</div>
|
|
|
|
| 42 |
</div>
|
| 43 |
|
| 44 |
<div class="info-card">
|
| 45 |
+
<h4>AI-Driven Irrigation Monitoring</h4>
|
| 46 |
+
<p class="mt-3">A message has been sent to your WhatsApp. Please reply <strong>1</strong> to start the motor and activate live monitoring here, or reply <strong>0</strong> to cancel the operation.</p>
|
|
|
|
| 47 |
</div>
|
| 48 |
|
| 49 |
<div class="camera-container" id="cameraContainer">
|
|
|
|
| 51 |
<video id="videoElement" autoplay playsinline></video>
|
| 52 |
<div class="timer" id="timer"></div>
|
| 53 |
</div>
|
| 54 |
+
|
| 55 |
+
<div class="alert-message text-success" id="alertMessage">Waiting for WhatsApp confirmation...</div>
|
| 56 |
</div>
|
| 57 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
<script>
|
| 59 |
+
// --- GLOBAL VARIABLES ---
|
| 60 |
+
let pollingInterval = null;
|
|
|
|
|
|
|
| 61 |
let clientTimerInterval = null;
|
| 62 |
let cameraStream = null;
|
| 63 |
+
let isMonitoringActive = false;
|
|
|
|
| 64 |
|
| 65 |
+
// --- MAIN FUNCTION TO START POLLING ---
|
| 66 |
window.onload = function() {
|
| 67 |
+
// Start polling the server for status updates every 3 seconds
|
| 68 |
+
pollingInterval = setInterval(checkMonitorStatus, 3000);
|
| 69 |
+
checkMonitorStatus(); // Check immediately on load
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
};
|
| 71 |
+
|
| 72 |
+
// --- FUNCTION TO CHECK STATUS WITH THE SERVER ---
|
| 73 |
function checkMonitorStatus() {
|
| 74 |
+
fetch('/monitoring_status')
|
| 75 |
.then(resp => resp.json())
|
| 76 |
.then(data => {
|
| 77 |
+
if (data.monitoring && !isMonitoringActive) {
|
| 78 |
+
// --- START MONITORING ---
|
| 79 |
+
isMonitoringActive = true;
|
| 80 |
+
console.log("Server indicated to start monitoring.");
|
| 81 |
+
document.getElementById('alertMessage').textContent = "✅ Monitoring started via WhatsApp confirmation!";
|
| 82 |
+
startCameraAndTimer(data.time_remaining);
|
| 83 |
+
} else if (!data.monitoring && isMonitoringActive) {
|
| 84 |
+
// --- STOP MONITORING (e.g., if task is canceled or completed on server) ---
|
| 85 |
+
isMonitoringActive = false;
|
| 86 |
+
console.log("Server indicated to stop monitoring.");
|
| 87 |
+
document.getElementById('alertMessage').textContent = "Monitoring has been stopped or completed.";
|
| 88 |
stopCameraAndTimer();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
}
|
| 90 |
})
|
| 91 |
.catch(err => {
|
| 92 |
+
console.error('Error polling for status:', err);
|
| 93 |
+
// Stop polling if there is an error to avoid spamming the console
|
| 94 |
+
clearInterval(pollingInterval);
|
| 95 |
});
|
| 96 |
}
|
| 97 |
|
| 98 |
+
// --- FUNCTION TO ACTIVATE CAMERA AND VISUAL TIMER ---
|
| 99 |
+
function startCameraAndTimer(timeRemainingSeconds) {
|
| 100 |
+
// Show camera container
|
| 101 |
+
document.getElementById('cameraContainer').style.display = 'flex';
|
| 102 |
+
|
| 103 |
+
// Start Camera
|
| 104 |
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
| 105 |
navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, audio: false })
|
| 106 |
.then(function(stream) {
|
| 107 |
cameraStream = stream;
|
| 108 |
+
document.getElementById('videoElement').srcObject = stream;
|
| 109 |
})
|
| 110 |
.catch(function(err) {
|
| 111 |
+
console.error("Camera Error: ", err);
|
| 112 |
alert("Could not access the camera. Please ensure you have given permission.");
|
| 113 |
});
|
| 114 |
} else {
|
| 115 |
alert("Camera API not supported in this browser.");
|
| 116 |
}
|
| 117 |
|
| 118 |
+
// Start client-side visual countdown timer
|
| 119 |
+
let timeRemaining = Math.floor(timeRemainingSeconds);
|
| 120 |
+
let timerElem = document.getElementById('timer');
|
|
|
|
| 121 |
timerElem.classList.remove('timer-finished');
|
| 122 |
|
| 123 |
+
clearInterval(clientTimerInterval); // Clear any existing timer
|
| 124 |
clientTimerInterval = setInterval(function() {
|
| 125 |
if (timeRemaining <= 0) {
|
| 126 |
clearInterval(clientTimerInterval);
|
| 127 |
timerElem.textContent = "Finished";
|
| 128 |
timerElem.classList.add('timer-finished');
|
| 129 |
+
document.getElementById('alertMessage').textContent = "Irrigation time is over. The motor has stopped automatically.";
|
| 130 |
+
stopCameraAndTimer(); // Also stop camera stream
|
|
|
|
|
|
|
|
|
|
| 131 |
} else {
|
| 132 |
timerElem.textContent = timeRemaining + "s";
|
| 133 |
}
|
|
|
|
| 135 |
}, 1000);
|
| 136 |
}
|
| 137 |
|
| 138 |
+
// --- FUNCTION TO STOP CAMERA AND TIMER ---
|
| 139 |
function stopCameraAndTimer() {
|
|
|
|
| 140 |
clearInterval(clientTimerInterval);
|
| 141 |
+
clearInterval(pollingInterval); // Stop polling once task is over
|
|
|
|
|
|
|
| 142 |
|
|
|
|
| 143 |
if (cameraStream) {
|
| 144 |
cameraStream.getTracks().forEach(track => track.stop());
|
| 145 |
cameraStream = null;
|
| 146 |
}
|
| 147 |
+
|
| 148 |
+
// We can hide the camera container or leave it visible with a "Finished" message
|
| 149 |
+
// document.getElementById('cameraContainer').style.display = 'none';
|
| 150 |
}
|
| 151 |
</script>
|
| 152 |
</body>
|
| 153 |
+
</html>
|