From a4ecbd7133e73f2107e53423fd0a35477f195021 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Apr 2026 11:22:26 +0200 Subject: [PATCH] docs(miotiq): drop generic server parser skeleton Will be moved to its own file later. Kept only the descriptor format spec and per-sensor binary layout in this doc. Co-Authored-By: Claude Opus 4.7 (1M context) --- formats/udp-miotiq.md | 81 +------------------------------------------ 1 file changed, 1 insertion(+), 80 deletions(-) diff --git a/formats/udp-miotiq.md b/formats/udp-miotiq.md index d7e6d51..23d0c68 100644 --- a/formats/udp-miotiq.md +++ b/formats/udp-miotiq.md @@ -12,7 +12,7 @@ Capteur ──UDP──> Miotiq ──HTTPS POST JSON──> api.aircarto.com/re Ce document couvre uniquement le **format du descripteur** et le **layout binaire** par capteur. Le schéma du JSON de sortie (champs, unités, bitfields, exemple complet) vit dans [`json-payload.md`](json-payload.md). -Métadonnées transport ajoutées par Miotiq en marge du JSON décodé (utiles pour l'audit / le rattachement device) : `srcImsi` (IMSI de la SIM — clé de correspondance device en DB), `rcvTime` (timestamp Unix UTC réception Miotiq), `srcIP`, `customerId`. En mode webhook « raw » (sans descripteur), Miotiq peut aussi poster le datagramme brut sous `payload` encodé base64 — utile si le backend veut re-décoder côté serveur (voir le squelette Python plus bas). +Métadonnées transport ajoutées par Miotiq en marge du JSON décodé (utiles pour l'audit / le rattachement device) : `srcImsi` (IMSI de la SIM — clé de correspondance device en DB), `rcvTime` (timestamp Unix UTC réception Miotiq), `srcIP`, `customerId`. En mode webhook « raw » (sans descripteur), Miotiq peut aussi poster le datagramme brut sous `payload` encodé base64 — utile si le backend veut re-décoder côté serveur (parser serveur à documenter séparément). ## Format descripteur Miotiq @@ -191,85 +191,6 @@ Format CSV (base64-décodé = chaîne ASCII) : Valeurs manquantes codées `-1` (sentinelle legacy). À **ne pas reproduire** pour les nouveaux formats. -## Parser serveur — squelette générique - -Un parser générique qui consomme un descripteur Miotiq complet (6 colonnes) et décode n'importe quelle trame. Utile si ton backend veut refaire le décodage côté serveur plutôt que s'appuyer uniquement sur le JSON pré-décodé de Miotiq (utile en cas de doute, ou pour rejouer des trames brutes). - -```python -import base64 -from dataclasses import dataclass - -@dataclass -class Field: - size: int # octets - name: str - fn: str # string | hex2dec | hex2bin | userdef | skip - unit: str - equation: str # ex. "x/10", "(x-32)*5/9", "" = identité - export: str # Y (défaut) | W | N - -def parse_descriptor(text: str) -> list[Field]: - fields = [] - for line in text.strip().splitlines(): - parts = line.split("|") - while len(parts) < 6: - parts.append("") - length, name, fn, unit, eq, export = parts[:6] - fields.append(Field(int(length) // 2, name, fn, unit, eq, export or "Y")) - return fields - -def _apply_equation(x, equation: str): - if not equation: - return x - # équation est une expression Miotiq en x ; on l'évalue avec x comme seul nom autorisé. - return eval(equation, {"__builtins__": {}}, {"x": x}) - -def decode(payload: bytes, fields: list[Field], emit_units: bool = True) -> dict: - out, off = {}, 0 - for f in fields: - chunk = payload[off:off + f.size] - off += f.size - if f.fn == "skip" or f.export == "N": - continue - if len(chunk) != f.size: - raise ValueError(f"payload trop court au champ {f.name}") - - if f.fn == "string": - # Miotiq: garde la représentation hex des octets, pas de décodage ASCII. - val = chunk.hex() - elif f.fn == "hex2dec": - val = _apply_equation(int.from_bytes(chunk, "big", signed=False), f.equation) - elif f.fn == "hex2bin": - val = "".join(f"{b:08b}" for b in chunk) - elif f.fn == "userdef": - val = chunk.hex() - else: - raise ValueError(f"base function inconnue: {f.fn}") - - out[f.name] = val - # export=Y : ajouter la ligne d'unité comme le fait Miotiq - if emit_units and f.export == "Y" and f.unit: - out[f"{f.name}_unit"] = f"{val} {f.unit}" - return out - -# Webhook Miotiq -def on_miotiq_webhook(body: dict, descriptor: str) -> dict: - raw = base64.b64decode(body["payload"]) - fields = parse_descriptor(descriptor) - total = sum(f.size for f in fields) - if len(raw) != total: - raise ValueError(f"payload {len(raw)} octets, descripteur attend {total}") - return { - "imsi": body.get("srcImsi"), - "rcvTime": body.get("rcvTime"), - "data": decode(raw, fields), - } -``` - -> Attention : `eval()` sur le champ `equation` est acceptable tant que le descripteur vient d'une source de confiance (ta conf Miotiq). Si un jour un descripteur peut être injecté par un tiers, remplacer par un parseur d'expressions arithmétiques restreint. - -Côté PHP (cf. implémentations existantes `udp_miotiq_byte.php` / `udp_miotiq_csv.php`), la logique sera à réécrire autour de ce même principe de descripteur au fur et à mesure de la migration. - ## Configuration Miotiq - Clé API serveur : stockée dans le code backend (`server/sites/gestion.aircarto.fr/server/routes/sensors.js`), référencée comme ``.