System Integration Guide
Complete system integration guide covering cloud connectivity, data pipeline setup, dashboard development, and deployment strategies for industrial IoT applications.
System Integration Overview
The IoT Edge Device system integrates multiple components including edge devices, cloud infrastructure, data processing pipelines, and user interfaces to provide comprehensive industrial IoT monitoring and analytics.
Integration Components
• Edge Devices (ESP32-S3 based)
• Cloud Infrastructure (AWS/Azure/GCP)
• Message Broker (MQTT/Apache Kafka)
• Time Series Database (InfluxDB/TimescaleDB)
• Analytics Engine (Apache Spark/Flink)
• Web Dashboard (React/Vue.js)
• Mobile Application (React Native/Flutter)
• API Gateway (REST/GraphQL)
• Cloud Infrastructure (AWS/Azure/GCP)
• Message Broker (MQTT/Apache Kafka)
• Time Series Database (InfluxDB/TimescaleDB)
• Analytics Engine (Apache Spark/Flink)
• Web Dashboard (React/Vue.js)
• Mobile Application (React Native/Flutter)
• API Gateway (REST/GraphQL)
Cloud Infrastructure Setup
The cloud infrastructure provides scalable data processing, storage, and analytics capabilities for the IoT system.
AWS Architecture
# AWS Infrastructure as Code (Terraform)
provider "aws" {
region = "us-west-2"
}
# IoT Core for device management
resource "aws_iot_core" "iot_core" {
name = "iot-edge-device-core"
}
# Kinesis Data Streams for real-time data ingestion
resource "aws_kinesis_stream" "sensor_data_stream" {
name = "sensor-data-stream"
shard_count = 2
retention_period = 24
shard_level_metrics = [
"IncomingRecords",
"OutgoingRecords"
]
}
# Lambda functions for data processing
resource "aws_lambda_function" "data_processor" {
filename = "data_processor.zip"
function_name = "sensor-data-processor"
role = aws_iam_role.lambda_role.arn
handler = "index.handler"
runtime = "python3.9"
timeout = 60
environment {
variables = {
INFLUXDB_URL = aws_db_instance.influxdb.endpoint
}
}
}
# InfluxDB for time series data storage
resource "aws_db_instance" "influxdb" {
identifier = "influxdb-sensor-data"
engine = "influxdb"
engine_version = "2.7"
instance_class = "db.t3.medium"
allocated_storage = 100
storage_encrypted = true
}
Azure Architecture
# Azure Resource Manager Template
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"iotHubName": {
"type": "string",
"defaultValue": "iot-edge-device-hub"
}
},
"resources": [
{
"type": "Microsoft.Devices/IotHubs",
"apiVersion": "2021-07-02",
"name": "[parameters('iotHubName')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "S1",
"capacity": 1
},
"properties": {
"routing": {
"endpoints": {
"serviceBusQueues": [],
"serviceBusTopics": [],
"eventHubs": [],
"storageContainers": []
}
}
}
},
{
"type": "Microsoft.StreamAnalytics/streamingjobs",
"apiVersion": "2021-10-01-preview",
"name": "sensor-data-analytics",
"location": "[resourceGroup().location]",
"properties": {
"sku": {
"name": "Standard"
},
"eventsOutOfOrderPolicy": "adjust",
"outputErrorPolicy": "stop",
"eventsOutOfOrderMaxDelayInSeconds": 0,
"eventsLateArrivalMaxDelayInSeconds": 5
}
}
]
}
Data Pipeline Architecture
The data pipeline processes real-time sensor data from edge devices, performs analytics, and stores results for visualization and historical analysis.
Apache Kafka Integration
# Kafka Producer Configuration
from kafka import KafkaProducer
import json
import time
class SensorDataProducer:
def __init__(self, bootstrap_servers=['localhost:9092']):
self.producer = KafkaProducer(
bootstrap_servers=bootstrap_servers,
value_serializer=lambda v: json.dumps(v).encode('utf-8'),
key_serializer=lambda k: k.encode('utf-8') if k else None,
acks='all',
retries=3,
batch_size=16384,
linger_ms=10
)
def send_sensor_data(self, device_id, sensor_data):
topic = 'sensor-data'
key = device_id
message = {
'device_id': device_id,
'timestamp': int(time.time() * 1000),
'sensor_data': sensor_data,
'metadata': {
'firmware_version': '1.0.0',
'battery_level': sensor_data.get('battery_level', 100)
}
}
future = self.producer.send(topic, key=key, value=message)
return future.get(timeout=10)
# Kafka Consumer for real-time processing
from kafka import KafkaConsumer
import json
class SensorDataConsumer:
def __init__(self, bootstrap_servers=['localhost:9092']):
self.consumer = KafkaConsumer(
'sensor-data',
bootstrap_servers=bootstrap_servers,
value_deserializer=lambda m: json.loads(m.decode('utf-8')),
group_id='sensor-data-processor',
auto_offset_reset='latest',
enable_auto_commit=True
)
def process_messages(self):
for message in self.consumer:
sensor_data = message.value
self.process_sensor_data(sensor_data)
def process_sensor_data(self, data):
# Real-time anomaly detection
if self.detect_anomaly(data['sensor_data']):
self.send_alert(data)
# Store in time series database
self.store_in_database(data)
# Update real-time dashboard
self.update_dashboard(data)
Apache Spark Streaming
# Spark Streaming for real-time analytics
from pyspark.sql import SparkSession
from pyspark.sql.functions import *
from pyspark.sql.types import *
# Initialize Spark session
spark = SparkSession.builder \
.appName("IoT Sensor Analytics") \
.config("spark.sql.adaptive.enabled", "true") \
.config("spark.sql.adaptive.coalescePartitions.enabled", "true") \
.getOrCreate()
# Define schema for sensor data
sensor_schema = StructType([
StructField("device_id", StringType(), True),
StructField("timestamp", LongType(), True),
StructField("sensor_data", StructType([
StructField("imu", StructType([
StructField("accel_x", FloatType(), True),
StructField("accel_y", FloatType(), True),
StructField("accel_z", FloatType(), True),
StructField("gyro_x", FloatType(), True),
StructField("gyro_y", FloatType(), True),
StructField("gyro_z", FloatType(), True)
]), True),
StructField("environmental", StructType([
StructField("temperature", FloatType(), True),
StructField("humidity", FloatType(), True),
StructField("pressure", FloatType(), True),
StructField("light_level", FloatType(), True)
]), True),
StructField("audio", StructType([
StructField("level", FloatType(), True)
]), True)
]), True)
])
# Read from Kafka
df = spark \
.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.option("subscribe", "sensor-data") \
.load()
# Parse JSON data
parsed_df = df \
.select(from_json(col("value").cast("string"), sensor_schema).alias("data")) \
.select("data.*")
# Real-time analytics
analytics_df = parsed_df \
.withColumn("timestamp", from_unixtime(col("timestamp")/1000)) \
.withColumn("temperature_anomaly", when(col("sensor_data.environmental.temperature") > 50, 1).otherwise(0)) \
.withColumn("vibration_anomaly", when(sqrt(
col("sensor_data.imu.accel_x")**2 +
col("sensor_data.imu.accel_y")**2 +
col("sensor_data.imu.accel_z")**2
) > 20, 1).otherwise(0))
# Write to InfluxDB
def write_to_influxdb(df, epoch_id):
df.write \
.format("influxdb") \
.option("influxdb.database", "sensor_data") \
.option("influxdb.retentionPolicy", "autogen") \
.mode("append") \
.save()
# Start streaming query
query = analytics_df \
.writeStream \
.outputMode("append") \
.foreachBatch(write_to_influxdb) \
.trigger(processingTime='10 seconds') \
.start()
query.awaitTermination()
Database Integration
Time series databases are optimized for storing and querying sensor data with high throughput and efficient compression.
InfluxDB Integration
# InfluxDB client for Python
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS
class InfluxDBManager:
def __init__(self, url, token, org, bucket):
self.client = InfluxDBClient(url=url, token=token, org=org)
self.write_api = self.client.write_api(write_options=SYNCHRONOUS)
self.query_api = self.client.query_api()
self.bucket = bucket
self.org = org
def write_sensor_data(self, device_id, sensor_data, timestamp):
point = Point("sensor_data") \
.tag("device_id", device_id) \
.field("temperature", sensor_data['environmental']['temperature']) \
.field("humidity", sensor_data['environmental']['humidity']) \
.field("pressure", sensor_data['environmental']['pressure']) \
.field("light_level", sensor_data['environmental']['light_level']) \
.field("accel_x", sensor_data['imu']['accel_x']) \
.field("accel_y", sensor_data['imu']['accel_y']) \
.field("accel_z", sensor_data['imu']['accel_z']) \
.field("gyro_x", sensor_data['imu']['gyro_x']) \
.field("gyro_y", sensor_data['imu']['gyro_y']) \
.field("gyro_z", sensor_data['imu']['gyro_z']) \
.field("audio_level", sensor_data['audio']['level']) \
.time(timestamp, WritePrecision.MS)
self.write_api.write(bucket=self.bucket, org=self.org, record=point)
def query_sensor_data(self, device_id, start_time, end_time):
query = f'''
from(bucket: "{self.bucket}")
|> range(start: {start_time}, stop: {end_time})
|> filter(fn: (r) => r["_measurement"] == "sensor_data")
|> filter(fn: (r) => r["device_id"] == "{device_id}")
|> pivot(rowKey: ["_time"], columnKey: ["_field"], valueColumn: "_value")
'''
result = self.query_api.query(org=self.org, query=query)
return result
# Usage example
influxdb = InfluxDBManager(
url="http://localhost:8086",
token="your-token",
org="your-org",
bucket="sensor-data"
)
# Write data
sensor_data = {
'environmental': {'temperature': 22.5, 'humidity': 45.2, 'pressure': 1013.25, 'light_level': 100},
'imu': {'accel_x': 0.1, 'accel_y': 0.2, 'accel_z': 9.8, 'gyro_x': 0.0, 'gyro_y': 0.0, 'gyro_z': 0.0},
'audio': {'level': 45.8}
}
influxdb.write_sensor_data("device-001", sensor_data, int(time.time() * 1000))
REST API Development
RESTful APIs provide secure access to sensor data, device management, and analytics results.
FastAPI Implementation
# FastAPI application for IoT data API
from fastapi import FastAPI, HTTPException, Depends, Query
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel
from typing import List, Optional
import jwt
from datetime import datetime, timedelta
import asyncio
app = FastAPI(title="IoT Edge Device API", version="1.0.0")
security = HTTPBearer()
# Data models
class SensorData(BaseModel):
device_id: str
timestamp: int
sensor_data: dict
anomaly_probability: Optional[float] = None
class DeviceInfo(BaseModel):
device_id: str
status: str
last_seen: datetime
firmware_version: str
battery_level: int
signal_strength: int
class AnalyticsResult(BaseModel):
device_id: str
start_time: datetime
end_time: datetime
metrics: dict
anomalies: List[dict]
# Authentication
async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
try:
payload = jwt.decode(credentials.credentials, "secret-key", algorithms=["HS256"])
return payload
except jwt.PyJWTError:
raise HTTPException(status_code=401, detail="Invalid authentication token")
# API endpoints
@app.get("/devices", response_model=List[DeviceInfo])
async def get_devices(
limit: int = Query(10, ge=1, le=100),
offset: int = Query(0, ge=0),
user = Depends(verify_token)
):
"""Get list of devices with pagination"""
# Implementation to fetch devices from database
pass
@app.get("/devices/{device_id}/data", response_model=List[SensorData])
async def get_device_data(
device_id: str,
start_time: datetime = Query(...),
end_time: datetime = Query(...),
limit: int = Query(100, ge=1, le=1000),
user = Depends(verify_token)
):
"""Get sensor data for a specific device"""
# Implementation to fetch sensor data from time series database
pass
@app.get("/devices/{device_id}/analytics", response_model=AnalyticsResult)
async def get_device_analytics(
device_id: str,
start_time: datetime = Query(...),
end_time: datetime = Query(...),
user = Depends(verify_token)
):
"""Get analytics results for a device"""
# Implementation to calculate and return analytics
pass
@app.post("/devices/{device_id}/commands")
async def send_device_command(
device_id: str,
command: dict,
user = Depends(verify_token)
):
"""Send command to device"""
# Implementation to send command via MQTT
pass
@app.get("/alerts")
async def get_alerts(
severity: Optional[str] = Query(None),
start_time: Optional[datetime] = Query(None),
end_time: Optional[datetime] = Query(None),
limit: int = Query(50, ge=1, le=200),
user = Depends(verify_token)
):
"""Get system alerts"""
# Implementation to fetch alerts from database
pass
# WebSocket for real-time data
@app.websocket("/ws/{device_id}")
async def websocket_endpoint(websocket: WebSocket, device_id: str):
await websocket.accept()
try:
while True:
# Send real-time sensor data
data = await get_latest_sensor_data(device_id)
await websocket.send_json(data)
await asyncio.sleep(1) # Send data every second
except WebSocketDisconnect:
pass
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Web Dashboard Development
Modern web dashboards provide real-time visualization of sensor data, device status, and analytics results.
React Dashboard Components
// React component for real-time sensor data visualization
import React, { useState, useEffect } from 'react';
import { Line } from 'react-chartjs-2';
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
import { Badge } from './ui/badge';
const SensorDataChart = ({ deviceId }) => {
const [sensorData, setSensorData] = useState([]);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
const ws = new WebSocket(`ws://localhost:8000/ws/${deviceId}`);
ws.onopen = () => setIsConnected(true);
ws.onclose = () => setIsConnected(false);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setSensorData(prev => [...prev.slice(-100), data]); // Keep last 100 points
};
return () => ws.close();
}, [deviceId]);
const chartData = {
labels: sensorData.map(d => new Date(d.timestamp).toLocaleTimeString()),
datasets: [
{
label: 'Temperature (°C)',
data: sensorData.map(d => d.sensor_data.environmental.temperature),
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
tension: 0.1
},
{
label: 'Humidity (%)',
data: sensorData.map(d => d.sensor_data.environmental.humidity),
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
tension: 0.1
},
{
label: 'Pressure (hPa)',
data: sensorData.map(d => d.sensor_data.environmental.pressure),
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
tension: 0.1
}
]
};
const chartOptions = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Real-time Sensor Data'
}
},
scales: {
x: {
type: 'time',
time: {
displayFormats: {
second: 'HH:mm:ss'
}
}
}
}
};
return (
Device {deviceId}
{isConnected ? "Connected" : "Disconnected"}
);
};
// Device status overview component
const DeviceOverview = () => {
const [devices, setDevices] = useState([]);
useEffect(() => {
const fetchDevices = async () => {
const response = await fetch('/api/devices');
const data = await response.json();
setDevices(data);
};
fetchDevices();
const interval = setInterval(fetchDevices, 30000); // Update every 30 seconds
return () => clearInterval(interval);
}, []);
return (
{devices.map(device => (
{device.device_id}
{device.status}
))}
);
};
export { SensorDataChart, DeviceOverview };
Battery:
{device.battery_level}%
Signal:
{device.signal_strength} dBm
Last Seen:
{new Date(device.last_seen).toLocaleString()}
Deployment Strategies
Different deployment strategies are available depending on requirements for scalability, security, and cost optimization.
Docker Containerization
# Dockerfile for IoT API service
FROM python:3.9-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
g++ \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create non-root user
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Run application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# Docker Compose for full stack
version: '3.8'
services:
iot-api:
build: .
ports:
- "8000:8000"
environment:
- INFLUXDB_URL=http://influxdb:8086
- KAFKA_BROKERS=kafka:9092
depends_on:
- influxdb
- kafka
networks:
- iot-network
influxdb:
image: influxdb:2.7
ports:
- "8086:8086"
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=admin
- DOCKER_INFLUXDB_INIT_PASSWORD=password123
- DOCKER_INFLUXDB_INIT_ORG=iot-org
- DOCKER_INFLUXDB_INIT_BUCKET=sensor-data
volumes:
- influxdb_data:/var/lib/influxdb2
networks:
- iot-network
kafka:
image: confluentinc/cp-kafka:latest
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
depends_on:
- zookeeper
networks:
- iot-network
zookeeper:
image: confluentinc/cp-zookeeper:latest
ports:
- "2181:2181"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
networks:
- iot-network
dashboard:
build: ./dashboard
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:8000
depends_on:
- iot-api
networks:
- iot-network
volumes:
influxdb_data:
networks:
iot-network:
driver: bridge
Kubernetes Deployment
# Kubernetes deployment for IoT API
apiVersion: apps/v1
kind: Deployment
metadata:
name: iot-api
labels:
app: iot-api
spec:
replicas: 3
selector:
matchLabels:
app: iot-api
template:
metadata:
labels:
app: iot-api
spec:
containers:
- name: iot-api
image: iot-api:latest
ports:
- containerPort: 8000
env:
- name: INFLUXDB_URL
value: "http://influxdb-service:8086"
- name: KAFKA_BROKERS
value: "kafka-service:9092"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: iot-api-service
spec:
selector:
app: iot-api
ports:
- protocol: TCP
port: 80
targetPort: 8000
type: LoadBalancer
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: iot-api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: iot-api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Security Implementation
Comprehensive security measures protect the IoT system from unauthorized access and data breaches.
Security Measures
• TLS/SSL encryption for all communications
• JWT-based authentication and authorization
• Role-based access control (RBAC)
• API rate limiting and throttling
• Input validation and sanitization
• Audit logging and monitoring
• Network segmentation and firewalls
• Regular security updates and patches
• JWT-based authentication and authorization
• Role-based access control (RBAC)
• API rate limiting and throttling
• Input validation and sanitization
• Audit logging and monitoring
• Network segmentation and firewalls
• Regular security updates and patches
API Security
# API security middleware
from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
import jwt
import redis
from functools import wraps
import time
app = FastAPI()
# Security middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://dashboard.example.com"],
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["*"],
)
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["api.example.com", "*.example.com"]
)
# Rate limiting with Redis
redis_client = redis.Redis(host='localhost', port=6379, db=0)
def rate_limit(max_requests: int = 100, window_seconds: int = 3600):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
client_ip = kwargs.get('client_ip', 'unknown')
key = f"rate_limit:{client_ip}"
current_requests = redis_client.get(key)
if current_requests is None:
redis_client.setex(key, window_seconds, 1)
else:
if int(current_requests) >= max_requests:
raise HTTPException(status_code=429, detail="Rate limit exceeded")
redis_client.incr(key)
return await func(*args, **kwargs)
return wrapper
return decorator
# JWT authentication
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, "secret-key", algorithm="HS256")
return encoded_jwt
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(HTTPBearer())):
try:
payload = jwt.decode(credentials.credentials, "secret-key", algorithms=["HS256"])
username: str = payload.get("sub")
if username is None:
raise HTTPException(status_code=401, detail="Invalid authentication credentials")
return payload
except jwt.PyJWTError:
raise HTTPException(status_code=401, detail="Invalid authentication credentials")
# Secure API endpoint
@app.get("/secure-data")
@rate_limit(max_requests=1000, window_seconds=3600)
async def get_secure_data(
user = Depends(verify_token),
client_ip: str = None
):
"""Secure endpoint with authentication and rate limiting"""
return {"message": "Secure data", "user": user.get("sub")}
Monitoring and Observability
Comprehensive monitoring ensures system reliability and performance in production environments.
Prometheus and Grafana
# Prometheus metrics for IoT API
from prometheus_client import Counter, Histogram, Gauge, generate_latest
import time
# Metrics
REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])
REQUEST_LATENCY = Histogram('http_request_duration_seconds', 'HTTP request latency')
ACTIVE_CONNECTIONS = Gauge('active_websocket_connections', 'Active WebSocket connections')
SENSOR_DATA_RECEIVED = Counter('sensor_data_received_total', 'Total sensor data received', ['device_id'])
# FastAPI middleware for metrics
@app.middleware("http")
async def add_prometheus_metrics(request, call_next):
start_time = time.time()
response = await call_next(request)
# Record metrics
REQUEST_COUNT.labels(
method=request.method,
endpoint=request.url.path
).inc()
REQUEST_LATENCY.observe(time.time() - start_time)
return response
# Metrics endpoint
@app.get("/metrics")
async def get_metrics():
return Response(generate_latest(), media_type="text/plain")
# Grafana dashboard configuration
dashboard_config = {
"dashboard": {
"title": "IoT System Monitoring",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{{method}} {{endpoint}}"
}
]
},
{
"title": "Response Time",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, http_request_duration_seconds)",
"legendFormat": "95th percentile"
}
]
},
{
"title": "Active Connections",
"type": "singlestat",
"targets": [
{
"expr": "active_websocket_connections",
"legendFormat": "Active Connections"
}
]
},
{
"title": "Sensor Data Rate",
"type": "graph",
"targets": [
{
"expr": "rate(sensor_data_received_total[5m])",
"legendFormat": "{{device_id}}"
}
]
}
]
}
}