Files
tf-iot-producer/site/app.py
T

122 lines
4.0 KiB
Python

"""
IoT Producer — Flask app that generates fake IoT sensor events
and sends them to Kafka (TLS + SASL).
Env vars (set by Terraform):
KAFKA_BROKERS — bootstrap server (host:port)
KAFKA_TOPIC — topic name
KAFKA_USERNAME — SASL username
KAFKA_PASSWORD — SASL password
PRODUCE_INTERVAL — seconds between events (default 3)
"""
import json
import os
import random
import ssl
import time
import threading
import uuid
from datetime import datetime, timezone
from flask import Flask, jsonify
from kafka import KafkaProducer
app = Flask(__name__)
# --- Config from env ---
KAFKA_BROKERS = os.environ["KAFKA_BROKERS"]
KAFKA_TOPIC = os.environ["KAFKA_TOPIC"]
KAFKA_USERNAME = os.environ["KAFKA_USERNAME"]
KAFKA_PASSWORD = os.environ["KAFKA_PASSWORD"]
PRODUCE_INTERVAL = float(os.environ.get("PRODUCE_INTERVAL", 3))
# --- IoT simulation config ---
SENSORS = [
{"id": "temp_living", "type": "temperature", "location": "living_room", "unit": "°C", "min": 18.0, "max": 28.0},
{"id": "temp_bedroom", "type": "temperature", "location": "bedroom", "unit": "°C", "min": 16.0, "max": 26.0},
{"id": "temp_office", "type": "temperature", "location": "office", "unit": "°C", "min": 19.0, "max": 27.0},
{"id": "humidity_living","type": "humidity", "location": "living_room", "unit": "%", "min": 30.0, "max": 70.0},
{"id": "humidity_bed", "type": "humidity", "location": "bedroom", "unit": "%", "min": 35.0, "max": 65.0},
{"id": "power_kitchen", "type": "power_meter", "location": "kitchen", "unit": "kW", "min": 0.1, "max": 3.5},
{"id": "power_living", "type": "power_meter", "location": "living_room", "unit": "kW", "min": 0.2, "max": 2.0},
{"id": "power_office", "type": "power_meter", "location": "office", "unit": "kW", "min": 0.3, "max": 4.0},
]
producer = None
events_sent = 0
errors_count = 0
def get_producer():
global producer
if producer is None:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
producer = KafkaProducer(
bootstrap_servers=KAFKA_BROKERS,
security_protocol="SASL_SSL",
sasl_mechanism="PLAIN",
sasl_plain_username=KAFKA_USERNAME,
sasl_plain_password=KAFKA_PASSWORD,
ssl_context=ctx,
value_serializer=lambda v: json.dumps(v).encode("utf-8"),
key_serializer=lambda k: k.encode("utf-8") if k else None,
)
return producer
def generate_event():
sensor = random.choice(SENSORS)
value = round(random.uniform(sensor["min"], sensor["max"]), 2)
return {
"event_id": str(uuid.uuid4()),
"sensor_id": sensor["id"],
"device_type": sensor["type"],
"location": sensor["location"],
"value": value,
"unit": sensor["unit"],
"timestamp": datetime.now(timezone.utc).isoformat(),
}
def produce_loop():
global events_sent, errors_count
while True:
try:
event = generate_event()
p = get_producer()
future = p.send(KAFKA_TOPIC, key=event["sensor_id"], value=event)
future.get(timeout=5)
events_sent += 1
print(f"[PRODUCER] Sent: {event['sensor_id']} = {event['value']}{event['unit']} ({event['location']})")
except Exception as e:
errors_count += 1
print(f"[PRODUCER] Error: {e}")
time.sleep(PRODUCE_INTERVAL)
@app.route("/")
def index():
return jsonify({
"service": "iot-producer",
"status": "running",
"kafka_topic": KAFKA_TOPIC,
"events_sent": events_sent,
"errors": errors_count,
"interval": PRODUCE_INTERVAL,
"sensors": len(SENSORS),
})
@app.route("/health")
def health():
return jsonify({"status": "ok", "events_sent": events_sent})
if __name__ == "__main__":
threading.Thread(target=produce_loop, daemon=True).start()
port = int(os.environ.get("PORT", 5000))
app.run(host="0.0.0.0", port=port)