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
2 changes: 2 additions & 0 deletions backend/api/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from .carbon import carbon_bp
from .logistics import smart_freight_bp
from .carbon_v2 import carbon_v2_bp
from .iot_sensors import iot_bp
from .government_scheme import gov_scheme_bp
from .soil_analytics import soil_analytics_bp
from .crop_advisory import advisory_bp
Expand Down Expand Up @@ -112,6 +113,7 @@
api_v1.register_blueprint(spatial_yield_bp, url_prefix="/spatial-yield")
api_v1.register_blueprint(carbon_bp, url_prefix="/carbon")
api_v1.register_blueprint(carbon_v2_bp, url_prefix="/carbon-v2")
api_v1.register_blueprint(iot_bp, url_prefix="/iot")
api_v1.register_blueprint(gov_scheme_bp, url_prefix="/government-schemes")
api_v1.register_blueprint(soil_analytics_bp, url_prefix="/soil-analytics")
api_v1.register_blueprint(gov_schemes_bp, url_prefix="/government-schemes")
Expand Down
149 changes: 149 additions & 0 deletions backend/api/v1/iot_sensors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
from flask import Blueprint, request, jsonify
from backend.services.iot_sensor_service import IoTSensorService
from auth_utils import token_required

iot_bp = Blueprint("iot", __name__)


@iot_bp.route("/sensors", methods=["GET"])
@token_required
def get_sensors(current_user):
sensors = IoTSensorService.get_user_sensors(current_user.id)
return jsonify({"status": "success", "data": [s.to_dict() for s in sensors]})


@iot_bp.route("/sensors", methods=["POST"])
@token_required
def register_sensor(current_user):
data = request.get_json()
if (
not data
or "sensor_id" not in data
or "farm_id" not in data
or "sensor_type" not in data
or "location" not in data
):
return jsonify({"status": "error", "message": "Missing required fields"}), 400

sensor = IoTSensorService.register_sensor(
user_id=current_user.id,
farm_id=data["farm_id"],
sensor_id=data["sensor_id"],
sensor_type=data["sensor_type"],
location=data["location"],
)

return jsonify(
{
"status": "success",
"message": "Sensor registered successfully",
"data": sensor.to_dict(),
}
), 201


@iot_bp.route("/sensors/<int:sensor_id>", methods=["GET"])
@token_required
def get_sensor(current_user, sensor_id):
sensor = IoTSensorService.get_sensor_by_id(sensor_id)
if not sensor:
return jsonify({"status": "error", "message": "Sensor not found"}), 404

if sensor.user_id != current_user.id:
return jsonify({"status": "error", "message": "Access denied"}), 403

return jsonify({"status": "success", "data": sensor.to_dict()})


@iot_bp.route("/sensors/<int:sensor_id>", methods=["DELETE"])
@token_required
def delete_sensor(current_user, sensor_id):
sensor = IoTSensorService.get_sensor_by_id(sensor_id)
if not sensor:
return jsonify({"status": "error", "message": "Sensor not found"}), 404

if sensor.user_id != current_user.id:
return jsonify({"status": "error", "message": "Access denied"}), 403

IoTSensorService.delete_sensor(sensor_id)

return jsonify({"status": "success", "message": "Sensor deleted successfully"})


@iot_bp.route("/sensors/<int:sensor_id>/readings", methods=["POST"])
@token_required
def receive_reading(current_user, sensor_id):
sensor = IoTSensorService.get_sensor_by_id(sensor_id)
if not sensor:
return jsonify({"status": "error", "message": "Sensor not found"}), 404

if sensor.user_id != current_user.id:
return jsonify({"status": "error", "message": "Access denied"}), 403

data = request.get_json()
if not data:
return jsonify({"status": "error", "message": "No reading data provided"}), 400

reading = IoTSensorService.receive_reading(sensor_id, data)

return jsonify(
{"status": "success", "message": "Reading received", "data": reading.to_dict()}
), 201


@iot_bp.route("/sensors/<int:sensor_id>/readings", methods=["GET"])
@token_required
def get_readings(current_user, sensor_id):
sensor = IoTSensorService.get_sensor_by_id(sensor_id)
if not sensor:
return jsonify({"status": "error", "message": "Sensor not found"}), 404

if sensor.user_id != current_user.id:
return jsonify({"status": "error", "message": "Access denied"}), 403

hours = request.args.get("hours", 24, type=int)
readings = IoTSensorService.get_sensor_readings(sensor_id, hours)

return jsonify({"status": "success", "data": [r.to_dict() for r in readings]})


@iot_bp.route("/sensors/<int:sensor_id>/history", methods=["GET"])
@token_required
def get_history(current_user, sensor_id):
sensor = IoTSensorService.get_sensor_by_id(sensor_id)
if not sensor:
return jsonify({"status": "error", "message": "Sensor not found"}), 404

if sensor.user_id != current_user.id:
return jsonify({"status": "error", "message": "Access denied"}), 403

days = request.args.get("days", 7, type=int)
history = IoTSensorService.get_historical_data(sensor_id, days)

return jsonify({"status": "success", "data": history})


@iot_bp.route("/alerts", methods=["GET"])
@token_required
def get_alerts(current_user):
alerts = IoTSensorService.get_user_alerts(current_user.id)
return jsonify({"status": "success", "data": [a.to_dict() for a in alerts]})


@iot_bp.route("/alerts/<int:alert_id>/resolve", methods=["POST"])
@token_required
def resolve_alert(current_user, alert_id):
alert = IoTSensorService.resolve_alert(alert_id)
if not alert:
return jsonify({"status": "error", "message": "Alert not found"}), 404

if alert.user_id != current_user.id:
return jsonify({"status": "error", "message": "Access denied"}), 403

return jsonify(
{
"status": "success",
"message": "Alert resolved successfully",
"data": alert.to_dict(),
}
)
1 change: 1 addition & 0 deletions backend/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
EntryType,
TransactionType,
)
from .iot_sensor import IoTSensor, SensorReading, SensorAlert
from .government_schemes import GovernmentScheme, SchemeApplication, SchemeReminder

__all__ = [
Expand Down
106 changes: 106 additions & 0 deletions backend/models/iot_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from datetime import datetime
from backend.extensions import db


class IoTSensor(db.Model):
__tablename__ = "iot_sensors"

id = db.Column(db.Integer, primary_key=True)
sensor_id = db.Column(db.String(100), unique=True, nullable=False)
farm_id = db.Column(db.Integer, db.ForeignKey("farms.id"), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)

sensor_type = db.Column(db.String(50), nullable=False)
location = db.Column(db.String(200), nullable=False)

is_active = db.Column(db.Boolean, default=True)
last_seen = db.Column(db.DateTime)
installed_at = db.Column(db.DateTime, default=datetime.utcnow)

def to_dict(self):
return {
"id": self.id,
"sensor_id": self.sensor_id,
"farm_id": self.farm_id,
"user_id": self.user_id,
"sensor_type": self.sensor_type,
"location": self.location,
"is_active": self.is_active,
"last_seen": self.last_seen.isoformat() if self.last_seen else None,
"installed_at": self.installed_at.isoformat(),
}


class SensorReading(db.Model):
__tablename__ = "sensor_readings"

id = db.Column(db.Integer, primary_key=True)
sensor_id = db.Column(db.Integer, db.ForeignKey("iot_sensors.id"), nullable=False)

soil_moisture = db.Column(db.Float)
temperature = db.Column(db.Float)
humidity = db.Column(db.Float)
ph_level = db.Column(db.Float)
nitrogen_level = db.Column(db.Float)
phosphorus_level = db.Column(db.Float)
potassium_level = db.Column(db.Float)
light_intensity = db.Column(db.Float)
rainfall = db.Column(db.Float)

battery_level = db.Column(db.Float)
signal_strength = db.Column(db.Float)

is_abnormal = db.Column(db.Boolean, default=False)
alert_message = db.Column(db.String(500))

timestamp = db.Column(db.DateTime, default=datetime.utcnow)

def to_dict(self):
return {
"id": self.id,
"sensor_id": self.sensor_id,
"soil_moisture": self.soil_moisture,
"temperature": self.temperature,
"humidity": self.humidity,
"ph_level": self.ph_level,
"nitrogen_level": self.nitrogen_level,
"phosphorus_level": self.phosphorus_level,
"potassium_level": self.potassium_level,
"light_intensity": self.light_intensity,
"rainfall": self.rainfall,
"battery_level": self.battery_level,
"signal_strength": self.signal_strength,
"is_abnormal": self.is_abnormal,
"alert_message": self.alert_message,
"timestamp": self.timestamp.isoformat(),
}


class SensorAlert(db.Model):
__tablename__ = "sensor_alerts"

id = db.Column(db.Integer, primary_key=True)
sensor_id = db.Column(db.Integer, db.ForeignKey("iot_sensors.id"), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)

alert_type = db.Column(db.String(50), nullable=False)
severity = db.Column(db.String(20), nullable=False)
message = db.Column(db.String(500), nullable=False)

is_resolved = db.Column(db.Boolean, default=False)
resolved_at = db.Column(db.DateTime)

created_at = db.Column(db.DateTime, default=datetime.utcnow)

def to_dict(self):
return {
"id": self.id,
"sensor_id": self.sensor_id,
"user_id": self.user_id,
"alert_type": self.alert_type,
"severity": self.severity,
"message": self.message,
"is_resolved": self.is_resolved,
"resolved_at": self.resolved_at.isoformat() if self.resolved_at else None,
"created_at": self.created_at.isoformat(),
}
Loading
Loading