Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions Scripts/Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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")
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The system logs that it's defaulting to Auto mode behavior for unknown modes, but it doesn't actually change the mode value or update the database. This could lead to inconsistent state where the UI shows an invalid mode while the system behaves as Auto.

Suggested change
print(f"Unknown mode '{currentMode}', defaulting to Auto mode behavior")
print(f"Unknown mode '{currentMode}', defaulting to Auto mode behavior")
currentMode = "Auto"
writeRtDb(mode="Auto")

Copilot uses AI. Check for mistakes.
writeRtDb(mode = "Auto")
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using extra spaces around the equals sign in the function call writeRtDb(mode = \"Auto\") is inconsistent with Python PEP 8 style guidelines. Should be writeRtDb(mode=\"Auto\").

Suggested change
writeRtDb(mode = "Auto")
writeRtDb(mode="Auto")

Copilot uses AI. Check for mistakes.

lastCheckTime = currentTime

Expand Down
4 changes: 3 additions & 1 deletion Scripts/Utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ 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
if tankLevel is not 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)
Expand Down
12 changes: 12 additions & 0 deletions Web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h2>Control Mode</h2>
<div class="mode-group">
<label for="modeSelect">Mode:</label>
<select id="modeSelect">
<option value="Auto">Auto</option>
<option value="Manual">Manual</option>
</select>
<p>Selected: <span id="selectedMode">Loading...</span></p>
</div>
</div>

<div class="container">
<h2>Tank Water Status</h2>
<div id="tankLevel" class="status-box">Loading...</div>
Expand Down
4 changes: 4 additions & 0 deletions Web/motor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
];
}
Expand Down Expand Up @@ -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));
Comment on lines +51 to +52
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mode validation only accepts 'Auto' or defaults to 'Manual' for any other value. This could allow unexpected values to be set as 'Manual'. Consider explicit validation that only accepts 'Auto' or 'Manual' and returns an error for invalid values.

Suggested change
$mode = $_POST['mode'] === 'Auto' ? 'Auto' : 'Manual';
echo json_encode(setConfig('mode', $mode));
$mode = $_POST['mode'];
if ($mode === 'Auto' || $mode === 'Manual') {
echo json_encode(setConfig('mode', $mode));
} else {
echo json_encode(['error' => "Invalid mode value. Only 'Auto' or 'Manual' are allowed."]);
}

Copilot uses AI. Check for mistakes.
}
} else {
echo json_encode(getStatus());
Expand Down
32 changes: 31 additions & 1 deletion Web/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,30 @@ 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');
} else {
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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
30 changes: 29 additions & 1 deletion Web/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
appVersion: 1.2.3.1006
appVersion: 1.3.0.1007