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) <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-04-24 11:22:26 +02:00
parent 232f1b7097
commit a4ecbd7133

View File

@@ -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 `<API_KEY>`.