WIP: Dockerfile update + Grafana dashboard JSON + InfluxDB population script

This commit is contained in:
Eric FELIXINE
2026-05-04 18:54:22 -04:00
parent 1d12a0b370
commit c69ecb5a48
3 changed files with 78 additions and 1 deletions

View File

@@ -1,6 +1,6 @@
FROM python:3.12-slim FROM python:3.12-slim
WORKDIR /app WORKDIR /app
RUN pip install --no-cache-dir paho-mqtt requests RUN pip install --no-cache-dir paho-mqtt requests influxdb-client
COPY simulator.py /app/ COPY simulator.py /app/
EXPOSE 8081 EXPOSE 8081
# Healthcheck endpoint (simple HTTP server) # Healthcheck endpoint (simple HTTP server)

View File

@@ -0,0 +1 @@
{"meta":{"type":"db","canSave":true,"canEdit":true,"canAdmin":true,"canStar":true,"canDelete":true,"slug":"smart-city-overview","url":"/d/e09bfbc3-6e4b-4134-963c-c98f9ffbb0f8/smart-city-overview","expires":"0001-01-01T00:00:00Z","created":"2026-05-04T15:26:52Z","updated":"2026-05-04T15:26:52Z","updatedBy":"admin","createdBy":"admin","version":1,"hasAcl":false,"isFolder":false,"folderId":0,"folderUid":"","folderTitle":"General","folderUrl":"","provisioned":false,"provisionedExternalId":"","annotationsPermissions":{"dashboard":{"canAdd":true,"canEdit":true,"canDelete":true},"organization":{"canAdd":true,"canEdit":true,"canDelete":true}}},"dashboard":{"id":4,"panels":[{"fieldConfig":{"defaults":{"color":{"mode":"thresholds"},"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"yellow","value":10},{"color":"green","value":15}]},"unit":"short"}},"gridPos":{"h":4,"w":4,"x":0,"y":0},"id":1,"targets":[{"query":"from(bucket: \"iot_data\") |\u003e range(start: -1h) |\u003e filter(fn: (r) =\u003e r[\"_measurement\"] == \"sensors\") |\u003e distinct(column: \"device\") |\u003e count()"}],"title":"Active Sensors","type":"stat"},{"fieldConfig":{"defaults":{"color":{"mode":"thresholds"},"unit":"short"}},"gridPos":{"h":4,"w":4,"x":4,"y":0},"id":2,"targets":[{"query":"from(bucket: \"iot_data\") |\u003e range(start: -1h) |\u003e filter(fn: (r) =\u003e r[\"_measurement\"] == \"traffic\") |\u003e mean(column: \"vehicle_count\")"}],"title":"Average Traffic","type":"stat"},{"fieldConfig":{"defaults":{"color":{"mode":"thresholds"},"max":5,"min":1,"thresholds":{"steps":[{"color":"green","value":null},{"color":"yellow","value":2},{"color":"orange","value":3},{"color":"red","value":4}]},"unit":"short"}},"gridPos":{"h":4,"w":4,"x":8,"y":0},"id":3,"targets":[{"query":"from(bucket: \"iot_data\") |\u003e range(start: -1h) |\u003e filter(fn: (r) =\u003e r[\"_measurement\"] == \"airquality\") |\u003e mean(column: \"air_quality_index\")"}],"title":"Air Quality Index","type":"gauge"},{"fieldConfig":{"defaults":{"color":{"mode":"thresholds"},"unit":"short"}},"gridPos":{"h":4,"w":4,"x":12,"y":0},"id":4,"targets":[{"query":"from(bucket: \"iot_data\") |\u003e range(start: -1h) |\u003e filter(fn: (r) =\u003e r[\"_measurement\"] == \"parking\") |\u003e mean(column: \"available_spots\")"}],"title":"Parking Availability","type":"gauge"},{"fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"unit":"short"}},"gridPos":{"h":6,"w":12,"x":0,"y":4},"id":5,"targets":[{"query":"from(bucket: \"iot_data\") |\u003e range(start: -24h) |\u003e filter(fn: (r) =\u003e r[\"_measurement\"] == \"traffic\") |\u003e aggregateWindow(every: 5m, fn: mean)"}],"title":"Traffic Over Time","type":"timeseries"},{"gridPos":{"h":6,"w":12,"x":12,"y":4},"id":6,"options":{"layers":[{"config":{"location":{"mode":"coords"},"value":"pm25_ugm3"},"type":"markers"}],"view":{"lat":46.1667,"lon":-1.15,"zoom":12}},"targets":[{"query":"from(bucket: \"iot_data\") |\u003e range(start: -1h) |\u003e filter(fn: (r) =\u003e r[\"_measurement\"] == \"airquality\")"}],"title":"Air Quality Map","type":"geomap"},{"gridPos":{"h":3,"w":24,"x":0,"y":10},"id":7,"options":{"content":"## Current Weather\nTemperature: 18°C | Humidity: 65% | Wind: 15 km/h | UV Index: 3","mode":"markdown"},"title":"Weather Conditions","type":"text"}],"tags":["smart-city","digital-twin","overview"],"timezone":"browser","title":"Smart City Overview","uid":"e09bfbc3-6e4b-4134-963c-c98f9ffbb0f8","version":1}}

76
populate_influx.py Normal file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""Populate InfluxDB with Smart City sensor data for Martinique."""
import os
import time
import random
from datetime import datetime, timezone
try:
import influxdb_client
from influxdb_client.client.write_api import SYNCHRONOUS
except ImportError:
print("❌ influxdb-client not installed. Run: pip install influxdb-client")
exit(1)
# Config
INFLUX_URL = os.environ.get("INFLUX_URL", "http://localhost:8086")
INFLUX_ORG = os.environ.get("INFLUX_ORG", "digitribe")
INFLUX_BUCKET = os.environ.get("INFLUX_BUCKET", "iot_data")
INFLUX_TOKEN = os.environ.get("INFLUX_TOKEN",
"yA8zFZYsPOLDdDxlviIfHw_5gELH8k439TANamk2JiJIyAbhyNCHDzUeYJkjL-hAy99fs_96j_59WprZy98A==")
# Martinique coordinates (Fort-de-France area)
BASE_LAT = 14.6091
BASE_LON = -61.2155
# Sensor types and their fields
SENSOR_TYPES = {
"traffic": {
"fields": {"vehicle_count": (10, 150), "average_speed_kmh": (10, 80), "congestion_level": (0, 5)},
"locations": ["Carrefour Central", "Avenue des Caraïbes", "Boulevard Pasteur", "Rue des Flamboyants", "Place de la République"]
},
"airquality": {
"fields": {"pm25_ugm3": (5, 80), "pm10_ugm3": (10, 150), "no2_ugm3": (5, 60), "o3_ugm3": (20, 120)},
"locations": ["Quartier Bonde", "Port de Fort-de-France", "Château Denis", "Lamentin Aéroport"]
},
"parking": {
"fields": {"total_spots": (50, 500), "available_spots": (0, 500), "occupancy_percent": (0, 100)},
"locations": ["Parking Rivière-Salée", "Parking Cluny", "Parking Médoc", "Parking Grand-Camp"]
},
}
def main():
print("📈 Connecting to InfluxDB...")
client = influxdb_client.InfluxDBClient(url=INFLUX_URL, token=INFLUX_TOKEN, org=INFLUX_ORG)
write_api = client.write_api(write_options=SYNCHRONOUS)
points = []
now = datetime.now(timezone.utc)
print("📊 Generating sensor data for Martinique...")
for stype, config in SENSOR_TYPES.items():
for i, location in enumerate(config["locations"]):
sid = f"{stype}_{i:03d}"
lat = BASE_LAT + random.uniform(-0.05, 0.05)
lon = BASE_LON + random.uniform(-0.05, 0.05)
for field, (lo, hi) in config["fields"].items():
value = round(random.uniform(lo, hi), 1)
p = influxdb_client.Point(stype)\
.tag("sensor_id", sid)\
.tag("location", location)\
.field(field, float(value))\
.field("lat", lat)\
.field("lon", lon)\
.time(now)
points.append(p)
print(f"📤 Writing {len(points)} points to bucket '{INFLUX_BUCKET}'...")
write_api.write(bucket=INFLUX_BUCKET, record=points)
print(f"✅ Done! {len(points)} points written.")
client.close()
if __name__ == "__main__":
main()