Rajkhanke007's picture
Update app.py
27b2b3b verified
from flask import Flask, render_template, request, redirect, url_for, jsonify
import joblib
import pandas as pd
import requests
import json
import plotly.graph_objects as go
from twilio.rest import Client
from twilio.twiml.messaging_response import MessagingResponse
import threading
import time
import os
app = Flask(__name__)
# Global variable to store the latest irrigation parameters
last_irrigation_params = {}
# Load the SVM model
svm_poly_model = joblib.load('svm_poly_model.pkl')
# Label encoding mappings for SVM model
crop_type_mapping = {
'BANANA': 0, 'BEAN': 1, 'CABBAGE': 2, 'CITRUS': 3, 'COTTON': 4,
'MAIZE': 5, 'MELON': 6, 'MUSTARD': 7, 'ONION': 8, 'OTHER': 9,
'POTATO': 10, 'RICE': 11, 'SOYABEAN': 12, 'SUGARCANE': 13,
'TOMATO': 14, 'WHEAT': 15
}
soil_type_mapping = {'DRY': 0, 'HUMID': 1, 'WET': 2}
weather_condition_mapping = {'NORMAL': 0, 'RAINY': 1, 'SUNNY': 2, 'WINDY': 3}
# Provided weather API key (hardcoded)
WEATHER_API_KEY = os.getenv('WEATHER_API')
def get_weather(city):
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}"
try:
response = requests.get(url)
response.raise_for_status()
except requests.exceptions.HTTPError:
return None, None, None, None
try:
data = json.loads(response.text)
if data['cod'] != 200:
return None, None, None, None
except json.JSONDecodeError:
return None, None, None, None
weather_description = data['weather'][0]['description']
temperature = data['main']['temp']
humidity = data['main']['humidity']
pressure = data['main']['pressure']
# Convert temperature from Kelvin to Celsius
temperature = round(temperature - 273.15, 2)
return temperature, humidity, weather_description, pressure
def send_whatsapp_message(crop_type, weather_description, temperature, humidity, pressure, motor_capacity,
water_requirement, estimated_time, time_unit, city):
"""
Send a WhatsApp message with irrigation details.
"""
message_text = (
f"Crop Name: {crop_type}\n"
f"Weather: {weather_description.capitalize()}, Temp: {temperature}°C, Humidity: {humidity}%, Pressure: {pressure} hPa\n"
f"Motor Discharge Rate: {motor_capacity} liters/sec\n"
f"Water Requirement for Irrigation: {water_requirement} m³/sq.m\n"
f"Total Motor On-Time: {estimated_time} {time_unit}\n"
f"Location: {city}\n\n"
"Do you want to start the motor?\nReply 1 to start, reply 0 to not start."
)
account_sid = 'AC490e071f8d01bf0df2f03d086c788d87'
auth_token = '224b23b950ad5a4052aba15893fdf083'
client = Client(account_sid, auth_token)
msg = client.messages.create(
from_='whatsapp:+14155238886',
body=message_text,
to='whatsapp:+917559355282'
)
print("Twilio Message SID:", msg.sid)
def send_irrigation_complete_message(crop_type, city, irrigation_time, time_unit):
"""
Send a WhatsApp message indicating that irrigation is over.
"""
message_text = (
f"Crop Name: {crop_type}\n"
f"Location: {city}\n"
f"Irrigation completed successfully.\n"
f"Total irrigation time: {irrigation_time} {time_unit}\n\n"
"Irrigation time is over and pump is auto offed."
)
account_sid = 'AC490e071f8d01bf0df2f03d086c788d87'
auth_token = '224b23b950ad5a4052aba15893fdf083'
client = Client(account_sid, auth_token)
msg = client.messages.create(
from_='whatsapp:+14155238886',
body=message_text,
to='whatsapp:+917559355282'
)
print("Irrigation Complete Message SID:", msg.sid)
def trigger_irrigation_complete():
"""
Function called by the timer to send the irrigation complete message.
"""
global last_irrigation_params
crop_type = last_irrigation_params.get('crop_type', 'Unknown Crop')
city = last_irrigation_params.get('city', 'Unknown Location')
estimated_time = last_irrigation_params.get('estimated_time_duration', 0)
time_unit = last_irrigation_params.get('time_unit', 'seconds')
# Format time for display
formatted_time = round(estimated_time, 2)
send_irrigation_complete_message(crop_type, city, formatted_time, time_unit)
print(f"Irrigation complete message sent automatically after {formatted_time} {time_unit}.")
@app.route('/')
def index():
return render_template('index.html')
@app.route('/fetch_weather', methods=['GET'])
def fetch_weather():
city = request.args.get('city')
if city:
temperature, humidity, weather_description, pressure = get_weather(city)
if temperature is not None and weather_description is not None:
return json.dumps({
'description': weather_description.capitalize(),
'temperature': temperature,
'humidity': humidity,
'pressure': pressure
})
return json.dumps(None)
@app.route('/predict', methods=['POST'])
def predict():
global last_irrigation_params
crop_type = request.form['crop_type']
soil_type = request.form['soil_type']
city = request.form['city']
motor_capacity = float(request.form['motor_capacity'])
# Get weather data
temperature, humidity, weather_description, pressure = get_weather(city)
if temperature is None or weather_description is None:
auto_weather_condition = 'NORMAL'
temperature = 32.0
weather_description = "Not Available"
else:
auto_weather_condition = ('SUNNY' if 'clear' in weather_description.lower() else
'RAINY' if 'rain' in weather_description.lower() else
'WINDY' if 'wind' in weather_description.lower() else
'NORMAL')
# Encode inputs for the model
crop_type_encoded = crop_type_mapping[crop_type]
soil_type_encoded = soil_type_mapping[soil_type]
weather_condition_encoded = weather_condition_mapping[auto_weather_condition]
# Create DataFrame for model input
user_data = pd.DataFrame({
'CROP TYPE': [crop_type_encoded],
'SOIL TYPE': [soil_type_encoded],
'TEMPERATURE': [temperature],
'WEATHER CONDITION': [weather_condition_encoded]
})
# Predict water requirement
water_requirement = svm_poly_model.predict(user_data)[0]
# Calculate estimated time duration (in seconds or minutes)
estimated_time_duration = water_requirement / motor_capacity if motor_capacity > 0 else 0
time_unit = "seconds" if estimated_time_duration < 60 else "minutes"
if time_unit == "minutes":
estimated_time_duration = estimated_time_duration / 60
# Round to 2 decimal places for display
formatted_estimated_time = round(estimated_time_duration, 2)
# Save irrigation parameters globally for later use when the farmer replies
last_irrigation_params = {
"estimated_time_duration": estimated_time_duration,
"time_unit": time_unit,
"crop_type": crop_type,
"city": city,
"water_requirement": water_requirement
}
# Send WhatsApp message with irrigation details
send_whatsapp_message(crop_type, weather_description, temperature, humidity, pressure, motor_capacity,
round(water_requirement, 2), formatted_estimated_time, time_unit, city)
# Create gauge charts for display
water_gauge = go.Figure(go.Indicator(
mode="gauge+number",
value=water_requirement,
title={"text": "Water Requirement (m³/sq.m)"},
gauge={
"axis": {"range": [None, 100]},
"bar": {"color": "blue"},
"steps": [
{"range": [0, 20], "color": "lightgray"},
{"range": [20, 50], "color": "yellow"},
{"range": [50, 100], "color": "green"}
]
}
))
max_range = 60 if time_unit == "seconds" else 120
time_gauge = go.Figure(go.Indicator(
mode="gauge+number",
value=formatted_estimated_time,
title={"text": f"Estimated Time ({time_unit.capitalize()})"},
gauge={
"axis": {"range": [None, max_range]},
"bar": {"color": "blue"},
"steps": [
{"range": [0, max_range * 0.33], "color": "lightgray"},
{"range": [max_range * 0.33, max_range * 0.66], "color": "yellow"},
{"range": [max_range * 0.66, max_range], "color": "green"}
]
}
))
water_gauge_path = water_gauge.to_html(full_html=False)
time_gauge_path = time_gauge.to_html(full_html=False)
weather_info = f"Weather in {city}: {weather_description.capitalize()}<br>" \
f"Temperature: {temperature}°C<br>" \
f"Humidity: {humidity}%<br>" \
f"Pressure: {pressure} hPa<br>"
return render_template('predict.html',
water_requirement=round(water_requirement, 2),
estimated_time_duration=formatted_estimated_time,
time_unit=time_unit,
weather_info=weather_info,
water_gauge=water_gauge_path,
time_gauge=time_gauge_path,
crop_type=crop_type,
city=city)
@app.route('/start_motor', methods=['POST'])
def start_motor():
"""
This route is called from the web interface but should not start the timer.
The timer is started only from WhatsApp when the user replies with '1'.
"""
estimated_time_duration = float(request.json.get('estimated_time_duration'))
time_unit = request.json.get('time_unit', 'seconds')
print(f"Web interface: Start motor button clicked - NOT starting timer")
# NO TIMER IS STARTED HERE - just return a status
return jsonify({"status": "motor_request_received"})
@app.route('/twilio_reply', methods=['POST'])
def twilio_reply():
"""
Handle incoming WhatsApp replies from Twilio.
THIS is where the timer should be started when the user replies with '1'.
"""
global last_irrigation_params
message_body = request.values.get('Body', '').strip()
resp = MessagingResponse()
print(f"Received WhatsApp reply: '{message_body}'")
if message_body == "1":
if last_irrigation_params:
estimated_time = last_irrigation_params.get('estimated_time_duration')
time_unit = last_irrigation_params.get('time_unit', 'seconds')
# Convert to seconds for the timer
if time_unit.lower() == "minutes":
estimated_time_seconds = estimated_time * 60
else:
estimated_time_seconds = estimated_time
print(f"Starting timer for {estimated_time_seconds} seconds due to WhatsApp reply '1'")
# THIS IS WHERE THE TIMER IS STARTED
t = threading.Timer(estimated_time_seconds, trigger_irrigation_complete)
t.daemon = True # Make thread exit when main thread exits
t.start()
resp.message(
f"Motor started as per your confirmation. Irrigation will run for {round(estimated_time, 2)} {time_unit} and will complete automatically.")
else:
resp.message("No irrigation parameters found. Cannot start motor.")
elif message_body == "0":
resp.message("Motor start canceled as per your reply.")
else:
resp.message("Invalid reply. Please reply 1 to start the motor or 0 to cancel.")
return str(resp)
@app.route('/irrigation_complete', methods=['POST'])
def irrigation_complete():
"""
Manual endpoint for irrigation completion - not used for automatic completion.
Automatic completion happens via the timer in twilio_reply.
"""
crop_type = request.json.get('crop_type', 'Unknown Crop')
city = request.json.get('city', 'Unknown Location')
estimated_time = last_irrigation_params.get('estimated_time_duration', 0)
time_unit = last_irrigation_params.get('time_unit', 'seconds')
send_irrigation_complete_message(crop_type, city, round(estimated_time, 2), time_unit)
return jsonify({"status": "irrigation_complete_message_sent"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)