From 0f387214fcbacbe31759a361a8d0232b3b758c57 Mon Sep 17 00:00:00 2001 From: Arghya Biswas Date: Fri, 26 Sep 2025 11:55:05 +0530 Subject: [PATCH] Add new manual mode and fix initial distance read issue Signed-off-by: Arghya Biswas --- Scripts/Main.py | 48 ++++++++++++++++++++++++++++++++++------------ Scripts/Utility.py | 4 +++- Web/index.html | 12 ++++++++++++ Web/motor.php | 4 ++++ Web/script.js | 32 ++++++++++++++++++++++++++++++- Web/style.css | 30 ++++++++++++++++++++++++++++- config.yaml | 2 +- 7 files changed, 116 insertions(+), 16 deletions(-) diff --git a/Scripts/Main.py b/Scripts/Main.py index 6900320..a5de64d 100644 --- a/Scripts/Main.py +++ b/Scripts/Main.py @@ -65,7 +65,12 @@ def isNightTime(): def main(): gpio = GpioManager() setupGpio(gpio) - lastDistance = readDistance(gpio, ULTRASONIC_TRIG, ULTRASONIC_ECHO) + print("System initialized. Checking Database...") + + rtDb = readRtDb() + if rtDb == {}: + print(f"No Database found; Create new Database...") + writeRtDb(motorStatus="OFF", tankLevel=TANK_HEIGHT, configUpdateAvailable=True, mode="Auto") # Default valves and durations valve1Duration = VALVE1_DEFAULT_DURATION @@ -81,8 +86,15 @@ def main(): lastMotorStatus = "OFF" waterLevel = 0 lastWaterLevel = 0 + lastDistance = 0 lastDay = datetime.now().day + print("Reading initial distance...") + while lastDistance <= 0: + time.sleep(1) + lastDistance = readDistance(gpio, ULTRASONIC_TRIG, ULTRASONIC_ECHO) + print(f"Initial distance: {lastDistance} cm") + try: lastCheckTime = time.time() lastRtDbCheck = time.time() @@ -135,13 +147,17 @@ def main(): print("Processing config update from web interface") # If no read needed, use cached rtDb (already initialized above) - # Check for config updates for valves + # Check for config updates for valves and mode if configUpdateAvailable: valve1Duration = rtDb.get("valve1Duration", VALVE1_DEFAULT_DURATION) valve2Duration = rtDb.get("valve2Duration", VALVE2_DEFAULT_DURATION) print(f"Updated valve durations: Valve1 = {valve1Duration} min, Valve2 = {valve2Duration} min") + # Get current mode (default to Auto for backward compatibility) + currentMode = rtDb.get("mode", "Auto") + # Only check for scheduled valve operations once per minute to reduce processing + # Valves operate regardless of mode (they have their own schedule) if now.second == 0: # Only check at the beginning of each minute # Morning valve operation if now.hour == MORNING_8AM and not morning8RunDone: @@ -200,21 +216,26 @@ def main(): waterLevel = TANK_HEIGHT - distance motorStatus = rtDb.get("motorStatus", "OFF") + if configUpdateAvailable: # Apply config from rtDb.json if motorStatus == "ON": gpio.output(MOTOR_PIN, True) - print("Config update: Motor ON") + print(f"{currentMode} mode - Config update: Motor ON") else: gpio.output(MOTOR_PIN, False) - print("Config update: Motor OFF") - else: + print(f"{currentMode} mode - Config update: Motor OFF") + # In manual mode, don't change motor status automatically + + + elif currentMode == "Auto": + # Standard automatic control logic if isNightTime(): # Check if it's night time between 10 PM and 7 AM - print("Night time: Automatic motor control disabled. Only manual control available!") + print("Auto mode - Night time: Automatic motor control disabled. Only manual control available!") preNightFillActive = False else: if now.hour == NIGHT_9PM and ifWaterLevelBelowTwoThird(waterLevel) and not preNightFillActive: - print(f"Pre-night: Water < 2/3, filling tank for {MAX_PRE_NIGHT_FILL_TIME} seconds...") + print(f"Auto mode - Pre-night: Water < 2/3, filling tank for {MAX_PRE_NIGHT_FILL_TIME} seconds...") gpio.output(MOTOR_PIN, True) motorStatus = "ON" preNightFillActive = True @@ -223,24 +244,27 @@ def main(): if ifWaterLevelAboveMax(waterLevel) or ((currentTime - preNightFillStartTime) > MAX_PRE_NIGHT_FILL_TIME): gpio.output(MOTOR_PIN, False) motorStatus = "OFF" - print("Tank filled before night.") + print("Auto mode - Tank filled before night.") preNightFillActive = False else: gpio.output(MOTOR_PIN, True) motorStatus = "ON" - print("Pre-night filling in progress...") + print("Auto mode - Pre-night filling in progress...") elif ifWaterLevelAboveMax(waterLevel): # Assuming sensor is at the top. Small distance means full. gpio.output(MOTOR_PIN, False) motorStatus = "OFF" - print("Tank full: Motor OFF") + print("Auto mode - Tank full: Motor OFF") elif ifWaterLevelBelowMin(waterLevel): # Large distance means empty gpio.output(MOTOR_PIN, True) motorStatus = "ON" - print("Tank empty: Motor ON") + print("Auto mode - Tank empty: Motor ON") else: gpio.output(MOTOR_PIN, False) motorStatus = "OFF" - print("Tank level OK: Motor OFF") + print("Auto mode - Tank level OK: Motor OFF") + else: + print(f"Unknown mode '{currentMode}', defaulting to Auto mode behavior") + writeRtDb(mode = "Auto") lastCheckTime = currentTime diff --git a/Scripts/Utility.py b/Scripts/Utility.py index f625c7c..cdce016 100644 --- a/Scripts/Utility.py +++ b/Scripts/Utility.py @@ -10,7 +10,7 @@ def readRtDb(): return {} -def writeRtDb(motorStatus=None, tankLevel=None, configUpdateAvailable=None): +def writeRtDb(motorStatus=None, tankLevel=None, configUpdateAvailable=None, mode=None): db = readRtDb() if motorStatus is not None: db["motorStatus"] = motorStatus @@ -18,6 +18,8 @@ def writeRtDb(motorStatus=None, tankLevel=None, configUpdateAvailable=None): db["tankLevel"] = tankLevel if configUpdateAvailable is not None: db["configUpdateAvailable"] = configUpdateAvailable + if mode is not None: + db["mode"] = mode try: with open(RT_DB_FILE, 'w') as f: json.dump(db, f) diff --git a/Web/index.html b/Web/index.html index 288ddb5..76935dc 100644 --- a/Web/index.html +++ b/Web/index.html @@ -7,6 +7,18 @@ +
+

Control Mode

+
+ + +

Selected: Loading...

+
+
+

Tank Water Status

Loading...
diff --git a/Web/motor.php b/Web/motor.php index fdfd142..18ba911 100644 --- a/Web/motor.php +++ b/Web/motor.php @@ -12,6 +12,7 @@ function getStatus() { "tankLevel" => isset($data['tankLevel']) ? $data['tankLevel'] : 0, "valve1Duration" => isset($data['valve1Duration']) ? $data['valve1Duration'] : 1, "valve2Duration" => isset($data['valve2Duration']) ? $data['valve2Duration'] : 1, + "mode" => isset($data['mode']) ? $data['mode'] : 'Auto', "configUpdateAvailable" => isset($data['configUpdateAvailable']) ? $data['configUpdateAvailable'] : false ]; } @@ -46,6 +47,9 @@ function setConfig($key, $value) { echo json_encode(setConfig('valve1Duration', (int)$_POST['valve1Duration'])); } elseif (isset($_POST['valve2Duration'])) { echo json_encode(setConfig('valve2Duration', (int)$_POST['valve2Duration'])); + } elseif (isset($_POST['mode'])) { + $mode = $_POST['mode'] === 'Auto' ? 'Auto' : 'Manual'; + echo json_encode(setConfig('mode', $mode)); } } else { echo json_encode(getStatus()); diff --git a/Web/script.js b/Web/script.js index 1e9d526..4c030c6 100644 --- a/Web/script.js +++ b/Web/script.js @@ -7,6 +7,14 @@ function fetchStatus() { (typeof data.tankLevel === 'number') ? `${data.tankLevel} cm` : 'Unknown'; document.getElementById('motorStatus').textContent = data.motorStatus === 'ON' ? 'ON' : 'OFF'; + + // Update mode display + const mode = data.mode || 'Auto'; + updateModeValue(mode); + + // Control motor button based on mode + const isManualMode = mode === 'Manual'; + if (data.motorStatus === 'ON') { toggleBtn.textContent = 'Turn Motor OFF'; toggleBtn.classList.add('on'); @@ -14,7 +22,15 @@ function fetchStatus() { toggleBtn.textContent = 'Turn Motor ON'; toggleBtn.classList.remove('on'); } - toggleBtn.disabled = false; + + // Enable/disable motor button based on mode + toggleBtn.disabled = !isManualMode; + + if (!isManualMode) { + toggleBtn.title = 'Motor control is disabled in Auto mode'; + } else { + toggleBtn.title = ''; + } // Update dropdowns and selected values updateConfigValue('valve1Duration', data.valve1Duration); @@ -78,6 +94,15 @@ function updateConfigValue(id, value) { } } +function updateModeValue(mode) { + const select = document.getElementById('modeSelect'); + const selectedSpan = document.getElementById('selectedMode'); + if (mode) { + select.value = mode; + selectedSpan.textContent = mode; + } +} + const motorToggleBtn = document.getElementById('motorToggleBtn'); motorToggleBtn.onclick = function() { motorToggleBtn.disabled = true; @@ -101,5 +126,10 @@ document.getElementById('valve2Duration').addEventListener('change', function() setConfig('valve2Duration', this.value); }); +// Add event listener for mode selection +document.getElementById('modeSelect').addEventListener('change', function() { + setConfig('mode', this.value); +}); + fetchStatus(); setInterval(fetchStatus, 3000); diff --git a/Web/style.css b/Web/style.css index b573d9e..c7d6b0a 100644 --- a/Web/style.css +++ b/Web/style.css @@ -47,8 +47,36 @@ button.on:hover { background: #b71c1c; } button:disabled { - opacity: 0.7; + opacity: 0.4; cursor: not-allowed; + background: #999 !important; +} +.mode-group, .config-group { + margin-bottom: 20px; +} +.mode-group label, .config-group label { + display: block; + margin-bottom: 8px; + font-weight: bold; + color: #333; +} +.mode-group select, .config-group select { + width: 100%; + padding: 10px; + font-size: 1em; + border: 2px solid #ddd; + border-radius: 6px; + background: #fff; + margin-bottom: 10px; +} +.mode-group select:focus, .config-group select:focus { + outline: none; + border-color: #1976d2; +} +.mode-group p, .config-group p { + margin: 0; + color: #666; + font-size: 0.9em; } @media (max-width: 500px) { .container { diff --git a/config.yaml b/config.yaml index 4cb9002..13462f7 100644 --- a/config.yaml +++ b/config.yaml @@ -1,2 +1,2 @@ -appVersion: 1.2.3.1006 +appVersion: 1.3.0.1007