Skip to content
Open
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
26 changes: 26 additions & 0 deletions Frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,36 @@ <h1>Predict climate threats before they interrupt your day.</h1>
</div>
</section>

<!-- Sending Alert Enable button -->
<section class="sms-alert-section">
<div class="glass-card">
<div class="section-heading">
<span>Weather SMS Alerts</span>
<h2>Get severe weather alerts directly on your phone.</h2>
</div>
<div class="input-container">
<div class="input-group">
<label>City</label>
<input type="text" id="sms-city" placeholder="Enter city">
</div>
<div class="input-group">
<label>Phone Number</label>
<input type="text" id="sms-phone" placeholder="+919876543210">
</div>
</div>
<button onclick="enableSmsAlerts()">Enable SMS Alerts</button>
<div id="sms-status" style="margin-top:15px;"></div>
</div>
</section>
<!-- End of sending alert part-->

<section id="features" class="feature-section">
<div class="section-heading">
<span>Why it helps</span>
<h2>Built for quick decisions when weather conditions change.</h2>
<div class="hero-strip-item">
<strong>Step 2</strong>
<span>Enter city, state, and country</span>
</div>

<div class="feature-grid">
Expand Down
100 changes: 100 additions & 0 deletions Frontend/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,57 @@ window.onload = function () {
).join("<br>");
};

const SMS_API_URL =
window.location.hostname === "127.0.0.1" ||
window.location.hostname === "localhost"
? "http://127.0.0.1:5000/subscribe-alert"
: window.location.origin + "/subscribe-alert";

// Sending Alert Function
async function enableSmsAlerts() {

const city = document.getElementById("sms-city").value.trim();
const phone = document.getElementById("sms-phone").value.trim();
const status = document.getElementById("sms-status");

if (!city || !phone) {
status.innerHTML = "Please enter city and phone number.";
return;
}

try {
status.innerHTML = "Registering...";

const response = await fetch(
SMS_API_URL,
{
method: "POST",
headers: {
"Content-Type":
"application/json"
},
body: JSON.stringify({
city,
phone
})
}
);

const data = await response.json();

if (data.success) {
status.innerHTML = "SMS alerts enabled successfully.";

} else {
status.innerHTML = data.message || "Failed to subscribe.";
}

} catch (error) {

console.error(error);
status.innerHTML = "Server error.";
}
}
const scrollTopBtn = document.getElementById("scrollTopBtn");

if (scrollTopBtn) {
Expand All @@ -262,3 +313,52 @@ if (scrollTopBtn) {
});
}
};

// Sending Alert Function
const SMS_API_URL = "http://localhost:5000/subscribe-alert";

async function enableSmsAlerts() {

const city = document.getElementById("sms-city").value.trim();
const phone = document.getElementById("sms-phone").value.trim();
const status = document.getElementById("sms-status");

if (!city || !phone) {
status.innerHTML = "Please enter city and phone number.";
return;
}

try {
status.innerHTML = "Registering...";

const response = await fetch(
SMS_API_URL,
{
method: "POST",
headers: {
"Content-Type":
"application/json"
},
body: JSON.stringify({
city,
phone
})
}
);

const data = await response.json();

if (response.ok && data.success) {
status.innerHTML = "SMS alerts enabled successfully.";

} else {
status.innerHTML = data.message || data.subscription?.message || "Failed to subscribe.";
}

} catch (error) {

console.error(error);
status.innerHTML = "Server error.";
}
}

65 changes: 65 additions & 0 deletions Frontend/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -951,3 +951,68 @@ body::before {
[data-theme="light"] .footer-links a:hover {
color: #0369a1;
}

/* SMS Alert Feature */
.glass-card {
padding: 24px;
border-radius: 18px;
background: var(--panel);
border: 1px solid var(--border-color);
backdrop-filter: blur(12px);
box-shadow: var(--shadow);
}

.input-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin: 16px 0;
}

.input-group {
display: flex;
flex-direction: column;
gap: 6px;
}

.input-group label {
font-size: 0.85rem;
color: var(--muted);
font-weight: 600;
}

.input-group input {
padding: 10px 12px;
border-radius: 10px;
border: 1px solid var(--border-strong);
background: var(--input-bg);
color: var(--text-primary);
outline: none;
width: 100%;
}

.sms-alert-section button {
width: 100%;
display: block;
margin-top: 8px;
padding: 12px 24px;
border-radius: 999px;
border: none;
background: linear-gradient(135deg, var(--accent), var(--accent-2));
color: white;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease;
}

.sms-alert-section button:hover {
transform: translateY(-2px);
}

.sms-alert-section #sms-status {
text-align: center;
color: #7dd3fc;
width: 100%;
display: block;
font-weight: bold;
}
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ Climate Shield includes an integrated AI chatbot that provides:

The chatbot is lightweight and rule-based.

## Automatic SMS Alert
- **OpenWeatherMap-powered forecasts** — fetches real-time weather forecast data (rainfall, wind speed, humidity, temperature, and storm conditions) for each subscriber's city using the OpenWeatherMap API.

- **Automatic severe weather detection** — analyzes the forecast against defined thresholds to detect heavy rain, strong winds, flood risk, heatwaves, and thunderstorms.

- **Instant SMS notifications via Vonage** — when a severe condition is detected, an SMS alert is sent directly to the subscriber's phone with the relevant weather details.

- **Simple subscription with automated scheduling** — users subscribe once with their city and phone number, and APScheduler periodically rechecks OpenWeatherMap data to send alerts automatically.
---

# 🖥 Frontend
Expand Down
50 changes: 50 additions & 0 deletions backend/alertsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@

from dotenv import load_dotenv

from flask_cors import CORS

from sms_alert import save_subscriber,send_weather_alert,check_weather_and_send_alerts
from apscheduler.schedulers.background import BackgroundScheduler

load_dotenv()



GIS_ALERTS_URL = os.environ.get("GIS_ALERTS_URL", "https://example.com/gis/alerts")

def fetch_gis_alert_data():
Expand Down Expand Up @@ -46,6 +53,9 @@ def fetch_gis_alert_data():

from flask_cors import CORS

from sms_alert import save_subscriber,send_weather_alert,check_weather_and_send_alerts
from apscheduler.schedulers.background import BackgroundScheduler

# =========================================================
# APP CONFIG
# =========================================================
Expand Down Expand Up @@ -600,6 +610,46 @@ def chatbot():
"message":
"Chatbot unavailable."
})

# ============================================
# SENDING ALERT TO THE SUBSCRIBER
# ============================================
@app.route("/subscribe-alert", methods=["POST"])
def subscribe_alerts():

data = request.get_json()

city = data.get("city", "").strip()
phone = data.get("phone", "").strip()

print("Received:", city, phone)

result = save_subscriber(city, phone)

if not result["success"]:
return jsonify(result), 409

sms_result = send_weather_alert(city, phone)

print("SMS RESULT:", sms_result)

return jsonify({
"subscription": result,
"sms": sms_result})


scheduler = BackgroundScheduler()

scheduler.add_job(
func=check_weather_and_send_alerts,
trigger="interval",
minutes=1 # for testing : every one minute sms alert will send to registered
)

scheduler.start()

print("Weather alert scheduler started.")


# =========================================================
# LOCAL RUN
Expand Down
Loading