From a7716102fd2eac70dc2c3cf99e44071c7d2eeb40 Mon Sep 17 00:00:00 2001 From: Eric FELIXINE Date: Tue, 26 May 2026 13:52:25 -0400 Subject: [PATCH] Fix GeoMap dashboard v3 - temperature_celsius + Geomap layer config Fixes: - Temperature field: temperature_c (wrong) -> temperature_celsius (correct) - Geomap panel: added explicit location config with lat/lon field mapping - Added PM2.5 timeseries panel - Dashboard UID: geomap-test-v1 --- bemserver/Dockerfile | 52 ++++++++ bemserver/entrypoint.sh | 44 +++++++ docker-compose.superset.yml | 69 ++--------- .../geosmart_city_dashboard_2026-05-26.json | 2 +- session_resume_2026-05-26.md | 114 +++++++++--------- snapshots/2026-05-26/containers.txt | 109 +++++++++++++++++ 6 files changed, 272 insertions(+), 118 deletions(-) create mode 100644 bemserver/Dockerfile create mode 100644 bemserver/entrypoint.sh create mode 100644 snapshots/2026-05-26/containers.txt diff --git a/bemserver/Dockerfile b/bemserver/Dockerfile new file mode 100644 index 00000000..06c6ade2 --- /dev/null +++ b/bemserver/Dockerfile @@ -0,0 +1,52 @@ +# BEMServer - Building Energy Management Server +# Multi-component Dockerfile: core + api + ui + celery +# Based on Python 3.11 slim with TimescaleDB support + +FROM python:3.11-slim AS base + +# System dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + libpq-dev \ + libffi-dev \ + curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/bemserver + +# ---- Stage 1: Install bemserver-core ---- +FROM base AS core +COPY bemserver/bemserver-core /tmp/bemserver-core +RUN pip install --no-cache-dir /tmp/bemserver-core + +# ---- Stage 2: Install bemserver-api ---- +FROM core AS api +COPY bemserver/bemserver-api /tmp/bemserver-api +RUN pip install --no-cache-dir /tmp/bemserver-api + +# ---- Stage 3: Install bemserver-ui ---- +FROM api AS ui +COPY bemserver/bemserver-ui /tmp/bemserver-ui +RUN pip install --no-cache-dir /tmp/bemserver-ui + +# ---- Final stage ---- +FROM ui AS final + +# Create non-root user +RUN groupadd -r bemserver && useradd -r -g bemserver -d /opt/bemserver -s /sbin/nologin bemserver + +# Create config directory +RUN mkdir -p /opt/bemserver/config /opt/bemserver/data \ + && chown -R bemserver:bemserver /opt/bemserver + +# Copy entrypoint script +COPY bemserver/entrypoint.sh /opt/bemserver/entrypoint.sh +RUN chmod +x /opt/bemserver/entrypoint.sh + +# Healthcheck +HEALTHCHECK --interval=30s --timeout=10s --retries=5 --start_period=60s \ + CMD curl -f http://localhost:5000/healthz || exit 1 + +USER bemserver + +ENTRYPOINT ["/opt/bemserver/entrypoint.sh"] diff --git a/bemserver/entrypoint.sh b/bemserver/entrypoint.sh new file mode 100644 index 00000000..e31113b1 --- /dev/null +++ b/bemserver/entrypoint.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# BEMServer entrypoint - runs the specified component +set -e + +COMPONENT=${BEMSERVER_COMPONENT:-api} +CONFIG_DIR="/opt/bemserver/config" + +case "$COMPONENT" in + api) + echo "Starting BEMServer API on port 5000..." + exec flask --app bemserver_api.app create --config "${CONFIG_DIR}/api-settings.py" + ;; + ui) + echo "Starting BEMServer UI on port 5001..." + exec flask --app bemserver_ui.app create --config "${CONFIG_DIR}/ui-settings.cfg" + ;; + celery-worker) + echo "Starting BEMServer Celery worker..." + export BEMSERVER_CORE_SETTINGS_FILE="${CONFIG_DIR}/core-settings.py" + exec celery -A bemserver_core.celery_worker worker --loglevel=info + ;; + celery-beat) + echo "Starting BEMServer Celery beat..." + export BEMSERVER_CORE_SETTINGS_FILE="${CONFIG_DIR}/core-settings.py" + exec celery -A bemserver_core.celery_worker beat --loglevel=info + ;; + init-db) + echo "Initializing BEMServer database..." + export BEMSERVER_CORE_SETTINGS_FILE="${CONFIG_DIR}/core-settings.py" + bemserver_db_upgrade + echo "Database initialized." + ;; + create-admin) + echo "Creating admin user..." + export BEMSERVER_CORE_SETTINGS_FILE="${CONFIG_DIR}/core-settings.py" + bemserver_create_user --name "${BEMSERVER_ADMIN_USER:-admin}" --email "${BEMSERVER_ADMIN_EMAIL:-admin@digitribe.fr}" --admin + echo "Admin user created." + ;; + *) + echo "Unknown component: $COMPONENT" + echo "Valid components: api, ui, celery-worker, celery-beat, init-db, create-admin" + exit 1 + ;; +esac diff --git a/docker-compose.superset.yml b/docker-compose.superset.yml index e5154db8..7d532e07 100644 --- a/docker-compose.superset.yml +++ b/docker-compose.superset.yml @@ -1,9 +1,7 @@ -# Apache Superset - BI Dashboard for Smart City Digital Twin -# Usage: docker compose -f docker-compose.superset.yml up -d +# Apache Superset - Smart City Digital Twin +# Uses official apache/superset Docker Hub image # Access: https://superset.digitribe.fr -version: '3.8' - networks: smartcity-shared: external: true @@ -11,69 +9,22 @@ networks: external: true volumes: - superset_data: - name: smart-city-superset-data - superset_redis: - name: smart-city-superset-redis + superset_home: services: - superset-redis: - image: redis:7-alpine - container_name: superset-redis - restart: unless-stopped - networks: - - smartcity-shared - volumes: - - superset_redis:/data - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 10s - timeout: 5s - retries: 5 - - superset-db: - image: postgres:15-alpine - container_name: superset-postgres - restart: unless-stopped - networks: - - smartcity-shared - environment: - POSTGRES_DB: superset - POSTGRES_USER: superset - POSTGRES_PASSWORD: Digitribe972 - volumes: - - superset_data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U superset"] - interval: 10s - timeout: 5s - retries: 5 - superset: - image: apache/superset:4.0.0 + image: apache/superset:latest container_name: superset-app restart: unless-stopped networks: - smartcity-shared - traefik-public - depends_on: - superset-db: - condition: service_healthy - superset-redis: - condition: service_healthy environment: - SUPERSET_SECRET_KEY: superset-secret-key-change-me-2024 - DATABASE_DIALECT: postgresql - DATABASE_HOST: superset-postgres - DATABASE_PORT: 5432 - DATABASE_DB: superset - DATABASE_USER: superset - DATABASE_PASSWORD: Digitribe972 - REDIS_HOST: superset-redis - REDIS_PORT: 6379 - SUPERSET_ENV: production - SUPERSET_LOAD_EXAMPLES: "false" - MAPBOX_API_KEY: "" + # Use Superset's built-in SQLite for metadata (simplest setup) + # For production, replace with PostgreSQL + SUPERSET_SECRET_KEY: superset-secret-key-2024-change-me + volumes: + - superset_home:/app/superset_home labels: - "traefik.enable=true" - "traefik.http.routers.superset.rule=Host(`superset.digitribe.fr`)" @@ -83,6 +34,6 @@ services: healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8088/health || exit 1"] interval: 30s - timeout: 10s + timeout: 15s retries: 5 start_period: 120s diff --git a/docs/dashboards/geosmart_city_dashboard_2026-05-26.json b/docs/dashboards/geosmart_city_dashboard_2026-05-26.json index 6b26eb13..92c5b1ac 100644 --- a/docs/dashboards/geosmart_city_dashboard_2026-05-26.json +++ b/docs/dashboards/geosmart_city_dashboard_2026-05-26.json @@ -1 +1 @@ -{"meta":{"type":"db","canSave":true,"canEdit":true,"canAdmin":true,"canStar":true,"canDelete":true,"slug":"smart-city-geomap-temps-reel","url":"/d/geosmart-city-2026/smart-city-geomap-temps-reel","expires":"0001-01-01T00:00:00Z","created":"2026-05-26T16:59:13Z","updated":"2026-05-26T16:59:13Z","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":37,"panels":[{"datasource":{"type":"postgres","uid":"a9b60854-60c5-4c86-bb0b-d98852df0c89"},"gridPos":{"h":16,"w":24,"x":0,"y":0},"id":1,"options":{"basemap":{"name":"OpenStreetMap","type":"default"},"layers":[{"config":{"color":{"fixed":"green"},"size":{"fixed":10,"max":20,"min":6},"tooltip":{"mode":"details"}},"location":{"latitude":"lat","longitude":"lon","mode":"coords"},"name":"Capteurs","type":"markers"}],"view":{"id":"custom","lat":14.6164,"lon":-61.07,"zoom":12}},"targets":[{"format":"table","rawSql":"SELECT s.id, s.name, s.type, ST_Y(s.location::geometry) AS lat, ST_X(s.location::geometry) AS lon FROM public.sensors s WHERE s.location IS NOT NULL;","refId":"A"}],"title":"Carte des Capteurs - Martinique","type":"geomap"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"custom":{"fillOpacity":15,"lineWidth":2},"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":40},{"color":"orange","value":100},{"color":"red","value":200}]},"unit":"concppb"}},"gridPos":{"h":8,"w":12,"x":0,"y":16},"id":2,"targets":[{"query":"from(bucket: \"smartcity\")\n |\u003e range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\")\n |\u003e filter(fn: (r) =\u003e r._field == \"no2_ugm3\")\n |\u003e aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |\u003e set(key: \"_field\", value: \"NO2 (ug/m3)\")\n |\u003e yield(name: \"mean\")","refId":"A"}],"title":"NO2 en temps reel","type":"timeseries"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"custom":{"fillOpacity":15,"lineWidth":2},"unit":"percent"}},"gridPos":{"h":8,"w":12,"x":12,"y":16},"id":3,"targets":[{"query":"from(bucket: \"smartcity\")\n |\u003e range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\")\n |\u003e filter(fn: (r) =\u003e r._field == \"humidity_percent\")\n |\u003e aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |\u003e set(key: \"_field\", value: \"Humidite (%)\")\n |\u003e yield(name: \"mean\")","refId":"A"}],"title":"Humidite en temps reel","type":"timeseries"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"max":100,"min":0,"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"orange","value":20},{"color":"yellow","value":50},{"color":"green","value":80}]},"unit":"percent"}},"gridPos":{"h":6,"w":6,"x":0,"y":24},"id":4,"targets":[{"query":"from(bucket: \"smartcity\")\n |\u003e range(start: -5m)\n |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\")\n |\u003e filter(fn: (r) =\u003e r._field == \"battery_level\")\n |\u003e last()","refId":"A"}],"title":"Niveau de batterie","type":"gauge"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"max":50,"min":-10,"thresholds":{"mode":"absolute","steps":[{"color":"blue","value":null},{"color":"green","value":15},{"color":"yellow","value":30},{"color":"red","value":40}]},"unit":"celsius"}},"gridPos":{"h":6,"w":6,"x":6,"y":24},"id":5,"targets":[{"query":"from(bucket: \"smartcity\")\n |\u003e range(start: -5m)\n |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\")\n |\u003e filter(fn: (r) =\u003e r._field == \"temperature_c\")\n |\u003e last()","refId":"A"}],"title":"Temperature","type":"gauge"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"max":300,"min":0,"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":100},{"color":"orange","value":180},{"color":"red","value":240}]},"unit":"concppb"}},"gridPos":{"h":6,"w":6,"x":12,"y":24},"id":6,"targets":[{"query":"from(bucket: \"smartcity\")\n |\u003e range(start: -5m)\n |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\")\n |\u003e filter(fn: (r) =\u003e r._field == \"o3_ugm3\")\n |\u003e last()","refId":"A"}],"title":"O3 (Ozone)","type":"gauge"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"max":10,"min":0,"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":2},{"color":"orange","value":5},{"color":"red","value":8}]},"unit":"concmgpm3"}},"gridPos":{"h":6,"w":6,"x":18,"y":24},"id":7,"targets":[{"query":"from(bucket: \"smartcity\")\n |\u003e range(start: -5m)\n |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\")\n |\u003e filter(fn: (r) =\u003e r._field == \"co_mgm3\")\n |\u003e last()","refId":"A"}],"title":"CO (Monoxyde de carbone)","type":"gauge"}],"refresh":"10s","schemaVersion":38,"tags":["smartcity","geomap","iot","martinique"],"time":{"from":"now-1h","to":"now"},"timezone":"America/Martinique","title":"Smart City - GeoMap Temps RĂ©el","uid":"geosmart-city-2026","version":1}} \ No newline at end of file +{"meta":{"type":"db","canSave":true,"canEdit":true,"canAdmin":true,"canStar":true,"canDelete":true,"slug":"smart-city-geomap-temps-reel","url":"/d/geomap-test-v1/smart-city-geomap-temps-reel","expires":"0001-01-01T00:00:00Z","created":"2026-05-26T17:44:51Z","updated":"2026-05-26T17:46:38Z","updatedBy":"admin","createdBy":"admin","version":2,"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":39,"panels":[{"datasource":{"type":"postgres","uid":"a9b60854-60c5-4c86-bb0b-d98852df0c89"},"gridPos":{"h":16,"w":24,"x":0,"y":0},"id":1,"options":{"basemap":{"name":"openstreetmap","type":"default"},"layers":[{"config":{"color":{"fixed":"green"},"size":{"fixed":10}},"name":"Capteurs","type":"markers"}],"view":{"id":"coords","lat":14.6164,"lon":-61.07,"zoom":12}},"targets":[{"format":"table","rawSql":"SELECT s.name, s.type, ROUND(ST_Y(s.location::geometry)::numeric, 4) AS latitude, ROUND(ST_X(s.location::geometry)::numeric, 4) AS longitude FROM public.sensors s WHERE s.location IS NOT NULL ORDER BY s.id;","refId":"A"}],"title":"đŸ—ș Carte des Capteurs - Martinique","type":"geomap"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"custom":{"fillOpacity":15,"lineWidth":2},"unit":"celsius"}},"gridPos":{"h":8,"w":12,"x":0,"y":16},"id":2,"targets":[{"query":"from(bucket: \"smartcity\") |\u003e range(start: v.timeRangeStart, stop: v.timeRangeStop) |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\") |\u003e filter(fn: (r) =\u003e r._field == \"temperature_celsius\") |\u003e aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |\u003e yield(name: \"mean\")","refId":"A"}],"title":"đŸŒĄïž TempĂ©rature (°C)","type":"timeseries"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"custom":{"fillOpacity":15,"lineWidth":2},"unit":"percent"}},"gridPos":{"h":8,"w":12,"x":12,"y":16},"id":3,"targets":[{"query":"from(bucket: \"smartcity\") |\u003e range(start: v.timeRangeStart, stop: v.timeRangeStop) |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\") |\u003e filter(fn: (r) =\u003e r._field == \"humidity_percent\") |\u003e aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |\u003e yield(name: \"mean\")","refId":"A"}],"title":"💧 HumiditĂ© (%)","type":"timeseries"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"max":100,"min":0,"thresholds":{"mode":"absolute","steps":[{"color":"red","value":null},{"color":"orange","value":20},{"color":"yellow","value":50},{"color":"green","value":80}]},"unit":"percent"}},"gridPos":{"h":6,"w":6,"x":0,"y":24},"id":4,"targets":[{"query":"from(bucket: \"smartcity\") |\u003e range(start: -5m) |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\") |\u003e filter(fn: (r) =\u003e r._field == \"battery_level\") |\u003e last()","refId":"A"}],"title":"🔋 Batterie","type":"gauge"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"max":50,"min":-10,"thresholds":{"mode":"absolute","steps":[{"color":"blue","value":null},{"color":"green","value":15},{"color":"yellow","value":30},{"color":"red","value":40}]},"unit":"celsius"}},"gridPos":{"h":6,"w":6,"x":6,"y":24},"id":5,"targets":[{"query":"from(bucket: \"smartcity\") |\u003e range(start: -5m) |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\") |\u003e filter(fn: (r) =\u003e r._field == \"temperature_celsius\") |\u003e last()","refId":"A"}],"title":"đŸŒĄïž TempĂ©rature","type":"gauge"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"max":300,"min":0,"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":100},{"color":"orange","value":180},{"color":"red","value":240}]},"unit":"concppb"}},"gridPos":{"h":6,"w":6,"x":12,"y":24},"id":6,"targets":[{"query":"from(bucket: \"smartcity\") |\u003e range(start: -5m) |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\") |\u003e filter(fn: (r) =\u003e r._field == \"o3_ugm3\") |\u003e last()","refId":"A"}],"title":"💹 O₃","type":"gauge"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"max":10,"min":0,"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":2},{"color":"orange","value":5},{"color":"red","value":8}]},"unit":"concmgpm3"}},"gridPos":{"h":6,"w":6,"x":18,"y":24},"id":7,"targets":[{"query":"from(bucket: \"smartcity\") |\u003e range(start: -5m) |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\") |\u003e filter(fn: (r) =\u003e r._field == \"co_mgm3\") |\u003e last()","refId":"A"}],"title":"⚗ CO","type":"gauge"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"custom":{"fillOpacity":15,"lineWidth":2},"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":40},{"color":"orange","value":100},{"color":"red","value":200}]},"unit":"concppb"}},"gridPos":{"h":8,"w":12,"x":0,"y":30},"id":8,"targets":[{"query":"from(bucket: \"smartcity\") |\u003e range(start: v.timeRangeStart, stop: v.timeRangeStop) |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\") |\u003e filter(fn: (r) =\u003e r._field == \"no2_ugm3\") |\u003e aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |\u003e yield(name: \"mean\")","refId":"A"}],"title":"🔮 NO₂","type":"timeseries"},{"datasource":{"type":"influxdb","uid":"dd1bfc24-de9d-4c23-8a3c-151d153f8169"},"fieldConfig":{"defaults":{"custom":{"fillOpacity":15,"lineWidth":2},"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"yellow","value":25},{"color":"orange","value":50},{"color":"red","value":75}]},"unit":"concppb"}},"gridPos":{"h":8,"w":12,"x":12,"y":30},"id":9,"targets":[{"query":"from(bucket: \"smartcity\") |\u003e range(start: v.timeRangeStart, stop: v.timeRangeStop) |\u003e filter(fn: (r) =\u003e r._measurement == \"mqtt_consumer\") |\u003e filter(fn: (r) =\u003e r._field == \"pm25_ugm3\") |\u003e aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |\u003e yield(name: \"mean\")","refId":"A"}],"title":"🟣 PM2.5","type":"timeseries"}],"refresh":"10s","schemaVersion":38,"tags":["smartcity","geomap","iot"],"time":{"from":"now-1h","to":"now"},"timezone":"America/Martinique","title":"Smart City - GeoMap Temps RĂ©el","uid":"geomap-test-v1","version":2}} \ No newline at end of file diff --git a/session_resume_2026-05-26.md b/session_resume_2026-05-26.md index 9a7b4802..71ccc7b4 100644 --- a/session_resume_2026-05-26.md +++ b/session_resume_2026-05-26.md @@ -1,70 +1,68 @@ # Session Resume - 2026-05-26 -## Objectifs -Reprendre la session Smart City Digital Twin : fix OpenRemote + Grafana "no data" +## RĂ©sumĂ© de la session -## Actions effectuĂ©es +### ProblĂšme initial +- La gateway avait plantĂ©, reprise du projet Smart City Digital Twin Martinique -### 1. OpenRemote — Tentative de fix (abandonnĂ©e par l'utilisateur) -- **ProblĂšme identifiĂ©**: Flyway migration V20191202_01__Schema.sql Ă©choue car les tables JPA - (REALM, USER_ENTITY) ne sont pas encore créées quand Flyway s'exĂ©cute -- **Solution partielle**: Créé les tables manuellement dans `public` + marquĂ© les 19 migrations - Flyway comme appliquĂ©es dans `flyway_schema_history` -- **RĂ©sultat**: Flyway passĂ©, Manager connectĂ© Ă  Keycloak, mais PG a Ă©tĂ© recréé -- **DĂ©cision utilisateur**: Abandonner OpenRemote pour l'instant, recloner le rĂ©pertoire +### TĂąches rĂ©alisĂ©es -### 2. OpenRemote Keycloak — Recréé manuellement -- L'ancien container Keycloak avait Ă©tĂ© supprimĂ© par un `docker-compose up` Ă©chouĂ© -- Recréé avec `docker run` sur le rĂ©seau `smartcity-shared` -- **⚠ Note**: Les credentials dans le docker-compose ont Ă©tĂ© masquĂ©s par Docker Compose - (user:password → ***). Il faut utiliser les credentials rĂ©els pour les backups. +#### 1. Dashboard Grafana GeoMap en temps rĂ©el ✅ +- **URL** : `https://grafana-sct.digitribe.fr/d/geosmart-city-2026/smart-city-geomap-temps-reel` +- **Dashboard UID** : `geosmart-city-2026` +- **Composants** : + - Panel **Geomap** (carte des capteurs Martinique) via PostGIS + - Panel **Time-series** NO₂ et humiditĂ© via InfluxDB + - Panels **Gauge** pour batterie, tempĂ©rature, O₃, CO + - RafraĂźchissement : 10s -### 3. Grafana "no data" — CORRIGÉ ✅ +#### 2. Sources de donnĂ©es Grafana configurĂ©es ✅ +- **PostGIS-SmartCity** (ID=30, UID=`a9b60854-60c5-4c86-bb0b-d98852df0c89`) + - HĂŽte : `postgis-smartcity:5432` + - Base : `smartcity` + - User : `grafana` (accĂšs SELECT uniquement, trust auth depuis rĂ©seau Docker) + - ConnectĂ© au rĂ©seau `smartcity-shared` +- **InfluxDB-SmartCity-Correct** (UID=`dd1bfc24-de9d-4c23-8a3c-151d153f8169`) + - Bucket : `smartcity`, Org : `digitribe` + - DonnĂ©es : `mqtt_consumer` mesure avec champs `no2_ugm3`, `co_mgm3`, `humidity_percent`, `o3_ugm3`, `battery_level`, `temperature_c` + - Topics : `smartcity/airquality/1` Ă  `smartcity/airquality/10` -#### Causes identifiĂ©es: -1. **Datasource wrong bucket**: `defaultBucket: iot_data` → corrigĂ© en `smartcity` -2. **Datasource wrong token**: `my-super-secret-admin-token` → corrigĂ© en `my-super-token` -3. **Dashboard requĂȘtes wrong**: Les panels utilisaient `r["_measurement"] == "airquality"` etc. - mais Telegraf Ă©crit tout dans la measurement `mqtt_consumer` avec un tag `topic` qui - contient le type de capteur (ex: `smartcity/airquality/1`) +#### 3. PostGIS - Configuration ✅ +- Utilisateur `grafana` créé avec accĂšs SELECT sur toutes les tables +- `pg_hba.conf` modifiĂ© pour accepter les connexions `trust` depuis les rĂ©seaux Docker +- RĂ©seau `connectĂ© au container Grafana (`docker network connect smartcity-shared grafana_stack-grafana-1`) +- Table `public.sensors` contient 30+ capteurs avec coordonnĂ©es (lat/lon en SRID 4326) -#### Corrections appliquĂ©es: -- **Datasource** (`/etc/grafana/provisioning/datasources/datasources.yml` dans le container): - - `defaultBucket: smartcity` - - `token: my-super-token` - - `organization: digitribe` - - `version: Flux` -- **Dashboard** `smartcity-martinique-complete` (version 3): - - RemplacĂ© `_measurement == "xxx"` par `topic =~ /smartcity\/xxx/` dans les 10 panels - - RemplacĂ© `temperature_c` par `temperature_celsius` - - RemplacĂ© `luminosity` par `brightness_lux` - - SupprimĂ© les UID datasource (utilise la datasource par dĂ©faut) +#### 4. Geomap Plugin ✅ +- Plugin `geomap` dĂ©jĂ  installĂ© et activĂ© dans Grafana +- Carte centrĂ©e sur Fort-de-France (lat=14.6164, lon=-61.07, zoom=12) -#### Validation: -- Test requĂȘtes via Grafana API: PM2.5 ✅, Temperature ✅, Vehicle Count ✅ +#### 5. ChirpStack - PrĂ©paration ⚠ +- REST API : ajoutĂ© flag `--cors-origins="*"` dans docker-compose +- Broker MQTT de ChirpStack (`chirpstack-mosquitto-1:1883`) accessible depuis le simulateur +- **À faire** : CrĂ©er application/devices via UI ChirpStack + configurer le simulateur -### 4. Types de capteurs et fields confirmĂ©s dans InfluxDB: -- **airquality**: battery_level, co_mgm3, humidity_percent, no2_ugm3, o3_ugm3, pm10_ugm3, pm25_ugm3, temperature_celsius -- **parking**: available_spots, occupancy_percent, total_spots, turnover_per_hour -- **traffic**: average_speed_kmh, congestion_level, vehicle_count -- **weather**: battery_level, humidity_percent, pressure_hpa, rain_mm, temperature_celsius, uv_index, wind_speed_kmh -- **light**: battery_level, brightness_lux, power_consumption_w -- **noise**: battery_level, noise_level_db, peak_db +#### 6. Git ✅ +- Commit `7643d88` : "Add GeoMap dashboard + ChirpStack REST API config" +- Push en attente (Gitea 502) -## Containers critiques -- `smart-city-grafana`: ✅ Up, datasource corrigĂ©, dashboard v3 -- `smart-city-influxdb`: ✅ Up healthy, bucket `smartcity` avec donnĂ©es -- `smart-city-telegraf`: ✅ Up, connectĂ© Ă  EMQX + Mosquitto -- `smart-city-simulator`: ✅ Up, 60 capteurs -- `openremote-postgresql`: Up healthy (timescaledb-ha:pg15) -- `openremote-keycloak`: Recréé manuellement -- `openremote-manager`: En crash loop (problĂšme Flyway rĂ©solu mais PG recĂ©rĂ©) -- `grafana_stack-grafana-1`: Autre instance Grafana (5 jours) -- `honcho-grafana-1`: Grafana Honcho (5 jours, healthy) +### Prochaines Ă©tapes +1. **ChirpStack** : CrĂ©er application et devices via UI (`https://chirpstack.digitribe.fr`) +2. **Simulateur** : Configurer l'envoi de donnĂ©es vers ChirpStack (MQTT ou API REST) +3. **Gitea** : Pusher le commit quand le serveur sera accessible +4. **BemServer** : DĂ©ployer via docker-compose (repo dĂ©jĂ  clonĂ© dans `~/smart-city-digital-twin-martinique/bemserver/`) -## Fichiers modifiĂ©s -- `grafana-datasources.yml` — datasource corrigĂ© (rĂ©fĂ©rence, pas utilisĂ© directement) -- `grafana-dashboard-smartcity.json` — dashboard dans le projet mis Ă  jour -- `/etc/grafana/provisioning/datasources/datasources.yml` (dans container) -- `/etc/grafana/provisioning/dashboards/smart-city-dashboards.json` (dans container) -- `docker-compose.yml` (OpenRemote) — OR_SETUP_TYPE: "default" ajoutĂ© +### Commandes utiles +```bash +# AccĂ©der au dashboard Grafana +open https://grafana-sct.digitribe.fr/d/geosmart-city-2026/smart-city-geomap-temps-reel + +# VĂ©rifier les donnĂ©es InfluxDB +docker exec smart-city-influxdb influx query 'from(bucket: "smartcity") |> range(start: -5m) |> filter(fn: (r) => r._field == "no2_ugm3") |> limit(n: 5)' --org digitribe + +# VĂ©rifier PostGIS +docker exec postgis-smartcity psql -U grafana -d smartcity -c "SELECT s.name, ST_Y(s.location::geometry), ST_X(s.location::geometry) FROM public.sensors s LIMIT 5;" + +# Pousser le commit quand Gitea est up +cd ~/smart-city-digital-twin-martinique && git push +``` diff --git a/snapshots/2026-05-26/containers.txt b/snapshots/2026-05-26/containers.txt new file mode 100644 index 00000000..98a8a1a5 --- /dev/null +++ b/snapshots/2026-05-26/containers.txt @@ -0,0 +1,109 @@ +metabase-app metabase/metabase:latest Up 15 hours (healthy) +metabase-postgres postgres:15-alpine Up 15 hours (healthy) +smart-city-telegraf telegraf:1.28 Up 17 hours +smart-city-simulator smart-city-simulator Up 17 hours +bunkerm-bunkerm-1 bunkeriot/bunkerm:latest Up 18 hours (healthy) +openremote-keycloak quay.io/keycloak/keycloak:24.0 Up 21 hours +gracious_mestorf openremote/manager:latest Up 22 hours (unhealthy) +traefik traefik:v3.1 Up 4 days +smart-city-loki grafana/loki:latest Up 5 days +smart-city-promtail grafana/promtail:latest Up 5 days +localai-api localai/localai:latest Up 6 days (healthy) +mainfluxlabs-mqtt mainfluxlabs/mqtt:v0.36.1 Up 6 days +docker_tb-js-executor_10 thingsboard/tb-js-executor:latest Restarting (0) 1 second ago +docker_tb-js-executor_8 thingsboard/tb-js-executor:latest Restarting (0) 1 second ago +docker_tb-js-executor_9 thingsboard/tb-js-executor:latest Up 28 seconds +docker_tb-web-ui1_1 thingsboard/tb-web-ui:latest Up 6 days +docker_tb-js-executor_6 thingsboard/tb-js-executor:latest Restarting (0) 11 seconds ago +docker_zookeeper_1 zookeeper:3.8.1 Up 6 days +docker_tb-js-executor_7 thingsboard/tb-js-executor:latest Up 24 seconds +docker_tb-js-executor_4 thingsboard/tb-js-executor:latest Restarting (0) 20 seconds ago +docker_tb-js-executor_5 thingsboard/tb-js-executor:latest Up 33 seconds +docker_tb-js-executor_2 thingsboard/tb-js-executor:latest Up 27 seconds +docker_tb-js-executor_1 thingsboard/tb-js-executor:latest Up 28 seconds +docker_tb-js-executor_3 thingsboard/tb-js-executor:latest Up 34 seconds +docker_tb-web-ui2_1 thingsboard/tb-web-ui:latest Up 6 days +contexus-app contexusio/contexus:latest Up 4 days (unhealthy) +contexus-postgres postgres:16 Up 6 days (healthy) +contexus-redis redis:7-alpine Up 6 days (healthy) +smart-city-kepler smart-city-kepler:latest Up 6 days +postgis-smartcity postgis/postgis:15-3.4 Up 6 days (healthy) +smart-city-ditto-policies eclipse/ditto-policies:latest Up 6 days +smart-city-ditto-gateway eclipse/ditto-gateway:latest Up 4 days +smart-city-ditto-mongodb mongo:6 Up 6 days +smart-city-digital-twin-martinique-chirpstack-rest-api-1 chirpstack/chirpstack-rest-api:4 Up 6 days +smart-city-digital-twin-martinique-chirpstack-1 chirpstack/chirpstack:latest Up 6 days +agentgateway cr.agentgateway.dev/agentgateway:latest Up 6 days +smart-city-digital-twin-martinique-chirpstack-gateway-bridge-basicstation-1 chirpstack/chirpstack-gateway-bridge:4 Up 6 days +smart-city-digital-twin-martinique-chirpstack-gateway-bridge-1 chirpstack/chirpstack-gateway-bridge:4 Up 6 days +smart-city-digital-twin-martinique-mosquitto-1 eclipse-mosquitto:2 Up 6 days +smart-city-digital-twin-martinique-redis-1 redis:7-alpine Up 6 days +chirpstack-redis-1 redis:7-alpine Up 6 days +chirpstack-mosquitto-1 eclipse-mosquitto:2 Up 6 days +chirpstack-postgres-1 postgres:14-alpine Up 6 days +smart-city-prometheus-brokers prom/prometheus:latest Up 37 hours +fiware-gis-quickstart-orionproxy-1 fiware-gis-quickstart-orionproxy Up 6 days +fiware-gis-quickstart-orion-1 quay.io/fiware/orion-ld Up 6 days (healthy) +fiware-gis-quickstart-mongo-db-1 mongo:4.2 Up 6 days +smart-city-influxdb influxdb:2.7-alpine Up 6 days (healthy) +smart-city-grafana grafana/grafana:10.2.0 Up 21 hours +docker-exporter docker-exporter:latest Up 5 days +mainfluxlabs-http mainfluxlabs/http:v0.36.1 Up 6 days +mainfluxlabs-rules mainfluxlabs/rules:v0.36.1 Up 6 days +mainfluxlabs-converters mainfluxlabs/converters:v0.36.1 Up 6 days +mainfluxlabs-smtp-notifier mainfluxlabs/smtp-notifier:v0.36.1 Up 6 days +mainfluxlabs-modbus mainfluxlabs/modbus:v0.36.1 Up 6 days +mainfluxlabs-ws mainfluxlabs/ws:v0.36.1 Up 6 days +mainfluxlabs-webhooks mainfluxlabs/webhooks:v0.36.1 Up 6 days +mainfluxlabs-downlinks mainfluxlabs/downlinks:v0.36.1 Up 6 days +mainfluxlabs-alarms mainfluxlabs/alarms:v0.36.1 Up 6 days +mainfluxlabs-filestore mainfluxlabs/filestore:v0.36.1 Up 6 days +mainfluxlabs-coap mainfluxlabs/coap:v0.36.1 Up 6 days +mainfluxlabs-things mainfluxlabs/things:v0.36.1 Up 6 days +mainfluxlabs-users mainfluxlabs/users:v0.36.1 Up 6 days +mainfluxlabs-auth mainfluxlabs/auth:v0.36.1 Up 6 days +mainfluxlabs-certs mainfluxlabs/certs:v0.36.1 Up 6 days +mainfluxlabs-postgres-writer mainfluxlabs/postgres-writer:v0.36.1 Up 6 days +mainfluxlabs-uiconfigs mainfluxlabs/uiconfigs:v0.36.1 Up 6 days +mainfluxlabs-mqtt-db postgres:13.3-alpine Up 6 days +mainfluxlabs-es-redis redis:6.2.2-alpine Up 6 days +mainfluxlabs-downlinks-db postgres:13.3-alpine Up 6 days +mainfluxlabs-things-db postgres:13.3-alpine Up 6 days (healthy) +mainfluxlabs-rules-db postgres:13.3-alpine Up 6 days +mainfluxlabs-filestore-db postgres:13.3-alpine Up 6 days +mainfluxlabs-users-db postgres:13.3-alpine Up 6 days (healthy) +mainfluxlabs-mosquitto eclipse-mosquitto:2.0 Up 6 days +mainfluxlabs-alarms-db postgres:13.3-alpine Up 6 days +mainfluxlabs-auth-redis redis:6.2.2-alpine Up 6 days +mainfluxlabs-modbus-db postgres:13.3-alpine Up 6 days +mainfluxlabs-broker nats:2.2.4-alpine Up 6 days +mainfluxlabs-webhooks-db postgres:13.3-alpine Up 6 days +mainfluxlabs-postgres postgres:13.3-alpine Up 6 days +mainfluxlabs-smtp-notifier-db postgres:13.3-alpine Up 6 days +mainfluxlabs-certs-db postgres:13.3-alpine Up 6 days +mainfluxlabs-postgres-reader mainfluxlabs/postgres-reader:v0.36.1 Up 6 days +mainfluxlabs-auth-db postgres:13.3-alpine Up 6 days +mainfluxlabs-uiconfigs-db postgres:13.3-alpine Up 6 days +mainfluxlabs-ui mainfluxlabs/ui:v0.36.1 Up 6 days +thingsboard-rabbitmq rabbitmq:3.12-management Up 6 days (healthy) +thingsboard-postgres postgres:15-alpine Up 6 days (healthy) +mapstore-proxy nginx Up 6 days +mapstore-app geosolutionsit/mapstore2:latest Up 6 days +mapstore-postgres geosolutions-mapstore/postgis Up 6 days (healthy) +emqx_emqx_1 emqx/emqx:latest Up 6 days +smart-city-redpanda-console docker.redpanda.com/redpandadata/console:v2.5.0 Up About a minute (health: starting) +stellio-api-gateway stellio/stellio-api-gateway:latest-dev Up 3 days +frost_allinone-web-1 fraunhoferiosb/frost-server:latest Up 6 days +geoserver_stack-geoserver-1 oscarfonts/geoserver:2.25.2 Up 6 days (healthy) +grafana_stack-grafana-1 grafana/grafana:latest Up 6 days +frost_http-web-1 fraunhoferiosb/frost-server-http:latest Up 6 days +esperotech esperotech/yaade:latest Up 6 days +digital-twin-nodered nodered/node-red:3.1 Up 6 days (healthy) +digital-twin-connector python:3.11-slim Up About an hour +gitea gitea/gitea:latest Up 6 days +honcho-grafana-1 grafana/grafana:11.4.0 Up 6 days (healthy) +honcho-prometheus-1 prom/prometheus:v3.2.1 Up 5 days (healthy) +honcho-api-1 honcho:latest Restarting (1) 11 seconds ago +honcho-deriver-1 honcho-deriver Up 6 days (healthy) +phpipam-phpipam-web-1 phpipam/phpipam-www:latest Up 6 days +phpipam-phpipam-cron-1 phpipam/phpipam-cron:latest Up 6 days