Session 2026-05-19: OpenRemote map display investigation, cleanup, fresh install

- Investigated map display issues (agentLink, GeoJSON coords, realm config)
- Cleaned up all dashboards and containers
- Fresh Manager installation (PostgreSQL in recovery)
- Updated TODO.md with current status
- GeoJSON proxy: fixed coordinate order (lon/lat)
- Session resume saved
This commit is contained in:
Eric FELIXINE
2026-05-19 16:22:26 -04:00
parent d1e6bdb685
commit 2377bc07fd
3 changed files with 78 additions and 58 deletions

11
TODO.md
View File

@@ -1,6 +1,6 @@
# Smart City Digital Twin — TODO List
> Dernière mise à jour : 2026-05-17 20:00
> Dernière mise à jour : 2026-05-19 22:15
## ✅ Complété (5/13)
| ID | Tâche |
@@ -14,7 +14,7 @@
## 🔴 Bloqué (5/13)
| ID | Tâche | Raison |
|----|-------|--------|
| p1-or | OpenRemote agents MQTT | API 403, UI headless ne rend pas |
| p1-or | OpenRemote agents MQTT + map display | PostgreSQL en recovery après reinstallation |
| p4-ditto | Ditto.digitribe.fr | MongoDB localhost hardcodé |
| p1-prometheus | Prometheus + Grafana | Réseau interne inaccessible |
| p3-kepler | KeplerGL | Image Docker incomplète, build npm trop long |
@@ -37,6 +37,13 @@
| metabase | data-visualization | ✅ |
| contexus | iot | ✅ |
## 📝 Notes 2026-05-19
- **OpenRemote map display** : Investigation approfondie — points ne s'affichent pas malgré toutes les conditions remplies (location, agentLink, showOnDashboard, bon realm)
- **Décision** : Repartir de zéro avec installation fraîche du Manager
- **PostgreSQL** : En recovery (fsync du data directory) — peut prendre 10+ minutes
- **Prochaines étapes** : Attendre PostgreSQL → Vérifier Manager/Keycloak → Lancer simulateur → Créer dashboard via UI → Vérifier affichage
- **Custom project** : Répertoire `/home/eric/openremote/custom-project/` cloné — prêt pour développement custom
## Credentials
- **GeoServer**: admin / Digitribe972
- **PostGIS dédié**: smartcity / SmartCity972 (port 5433)

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""GeoJSON proxy service for OpenRemote assets map display.
Fetches IoT sensor assets from OpenRemote REST API and serves them as GeoJSON.
Fetches all assets with location from OpenRemote REST API and serves them as GeoJSON.
"""
import json
@@ -12,31 +12,10 @@ from http.server import HTTPServer, BaseHTTPRequestHandler
OR_URL = os.environ.get("OR_URL", "http://openremote_manager_1:8080")
OR_ADMIN_USER = os.environ.get("OR_ADMIN_USER", "admin")
OR_ADMIN_PASS = os.environ.get("OR_ADMIN_PASS", "")
OR_ADMIN_PASS = os.environ.get("OR_ADMIN_PASS", "Digitribe972")
OR_REALM = os.environ.get("OR_REALM", "master")
OR_CLIENT_SECRET = os.environ.get("OR_CLIENT_SECRET", "0oQjzTfiEELYmj5jFwT4iIuWUDtQDvVa")
# All known IOTSensor asset IDs (from simulator ASSET_MAP + DB)
ASSET_IDS = [
"429858caca3341f56fbf65", "301218322f5aaca9d6d168", "bd35fe2a90133118b9b004",
"da59ec9301c4efd3fd55c4", "834f4b7b9df848f5c5c2d8",
"0f922351a9894bc0144c94", "4f83219bbee703b3e0a255", "381cc31ab83dd66ed4be37",
"808b73c22ecd19589a33be", "03c18679226329183b44b6",
"0ee6689f5c0499643d48eb", "8fb6b2d0601d98b47a4172", "0c00bda9e5075d12d59694",
"ae981dc9d155d1313b9acf", "96020cc5aef95c5fda7bb4",
"0be31930e45d2eb5c12ccd", "1802e76e3432d5eda1deb7", "08edb6518750d50644afe3",
"93d09bfac36d2ed95fc858", "7942726d84d2bd29de1e5d",
"9942f881ab6df375d8d9fa", "5400fdf5c51a4fe4f5a89c", "1a3bf32aa5208892e68965",
"d3725f922f96085f2df3f7", "13be192a8c23dd8fdceada",
"1f4302946b1a4a1ded23f6", "35e6ef027ed9a157ad8780", "526538589aa981bdc77ce9",
"d4a6ac7f34d64e581937c0", "40bbe989be2ae5b2a98b30",
# Additional assets from DB
"8b8f50aa8d13d65b2bafb7", "d642131a593c1cddcca3df",
"f4afeba492308772a9a1a4", "988232e4b779fd2cde2157",
"b75157bd68fde1577eda4d", "f388f67ec11e7860c352a3",
"ba30baf1fb3c69bdcc1b44",
]
_token_cache = {"token": "", "expires": 0}
@@ -66,18 +45,50 @@ def get_token():
def fetch_assets():
"""Fetch IoT sensor assets from OpenRemote REST API."""
"""Fetch all assets with location from OpenRemote REST API."""
token = get_token()
features = []
for asset_id in ASSET_IDS:
# Query all assets with location attribute
try:
# Use the asset query API to get all assets with location
query = json.dumps({
"attributes": {
"location": {
"value": {"$exists": True}
}
}
}).encode()
req = urllib.request.Request(
f"{OR_URL}/api/{OR_REALM}/asset/query",
data=query,
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json"
},
method="POST"
)
with urllib.request.urlopen(req, timeout=30) as r:
assets = json.loads(r.read().decode())
if not isinstance(assets, list):
assets = [assets]
except Exception as e:
# Fallback: try to get all assets and filter
try:
req = urllib.request.Request(
f"{OR_URL}/api/{OR_REALM}/asset/{asset_id}",
f"{OR_URL}/api/{OR_REALM}/asset?limit=100",
headers={"Authorization": f"Bearer {token}", "Accept": "application/json"}
)
with urllib.request.urlopen(req, timeout=5) as r:
asset = json.loads(r.read().decode())
with urllib.request.urlopen(req, timeout=30) as r:
assets = json.loads(r.read().decode())
if not isinstance(assets, list):
assets = [assets]
except Exception as e2:
return {"type": "FeatureCollection", "features": [], "error": str(e2)}
for asset in assets:
try:
attrs = asset.get("attributes", {})
location = attrs.get("location", {})
value = location.get("value") if isinstance(location, dict) else None
@@ -102,9 +113,10 @@ def fetch_assets():
if v is not None and not isinstance(v, (dict, list)):
props[attr_name] = v
# GeoJSON coordinates are [longitude, latitude]
features.append({
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [coords[0], coords[1]]},
"geometry": {"type": "Point", "coordinates": [coords[1], coords[0]]},
"properties": props
})
except Exception:

View File

@@ -912,13 +912,14 @@ def publish_openremote(sid: str, sensor: dict, values: dict) -> bool:
"unit": "µg/m³" if "ugm3" in field else ("°C" if "celsius" in field else ("%" if "percent" in field or "humidity" in field else ("dB" if "db" in field else ""))),
}
# Ajouter la location du capteur (GeoJSON Point)
# Ajouter la location du capteur (GeoJSON Point) avec meta showOnDashboard
lat = sensor.get("lat", 0)
lon = sensor.get("lon", 0)
attrs["location"] = {
"type": "GeoJSONPoint",
"value": {"type": "Point", "coordinates": [lat, lon]},
"timestamp": now,
"meta": {"showOnDashboard": True},
}
payload = {