docs: merge parsers/ into formats/ and drop misleading JSON wrapper
Parsers and formats are tightly linked (a parser produces a format) and the split made cross-links heavy for a single parser file. Also removed the confusing "Enveloppe JSON" block in udp-miotiq.md that mixed the raw webhook wrapper with what the backend actually consumes — the decoded payload schema lives in json-payload.md and is now referenced directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,7 @@
|
||||
## Nommage des fichiers
|
||||
|
||||
- Un capteur = un fichier `sensors/<nom>.md` en minuscules, sans tiret dans le nom s'il n'est pas dans la marque (`nextpm.md`, `sps30.md`, `bme280.md`).
|
||||
- Un parser = un fichier `parsers/<transport>-<origine>.md` (`udp-miotiq.md`, `mqtt-tb.md`).
|
||||
- Un format = un fichier `formats/<nom>.md` décrivant le schéma et les exemples.
|
||||
- Un format = un fichier `formats/<nom>.md` décrivant le schéma et les exemples. Les parsers associés (ex. descripteur binaire Miotiq) vivent dans le même dossier, nommés par transport : `formats/<transport>-<origine>.md` (`udp-miotiq.md`, `mqtt-tb.md`).
|
||||
|
||||
## Style Markdown
|
||||
|
||||
|
||||
19
README.md
19
README.md
@@ -10,15 +10,14 @@ Documentation de référence pour tous les capteurs AirCarto : protocoles de com
|
||||
aircarto-protocols/
|
||||
├── CONVENTIONS.md Nommage, versioning, style doc
|
||||
├── data-budget.md Estimation conso cellulaire vs budget Miotiq 1 Go/10 ans
|
||||
├── formats/ Formats d'échange de données
|
||||
├── formats/ Formats d'échange et parsers associés
|
||||
│ ├── json-payload.md Format JSON canonique des mesures
|
||||
│ ├── iso-pollutant-codes.md Mapping ISO_XX → polluant / grandeur
|
||||
│ └── mqtt.md Topics et conventions MQTT
|
||||
├── sensors/ Un fichier par capteur
|
||||
│ ├── _TEMPLATE.md Gabarit à copier pour tout nouveau capteur
|
||||
│ └── nextpm.md NextPM (Tera Sensor) — UART
|
||||
└── parsers/ Parsers côté serveur / passerelle
|
||||
└── udp-miotiq.md Webhook Miotiq (UDP → HTTPS JSON)
|
||||
│ ├── mqtt.md Topics et conventions MQTT
|
||||
│ └── udp-miotiq.md Webhook Miotiq (UDP → HTTPS JSON) + descripteur binaire
|
||||
└── sensors/ Un fichier par capteur
|
||||
├── _TEMPLATE.md Gabarit à copier pour tout nouveau capteur
|
||||
└── nextpm.md NextPM (Tera Sensor) — UART
|
||||
```
|
||||
|
||||
## Index des capteurs
|
||||
@@ -31,12 +30,12 @@ aircarto-protocols/
|
||||
|
||||
| Nom | Transport | Doc | État |
|
||||
|-----------------|------------------|-----------------------------------------------|-------------------------------|
|
||||
| UDP Miotiq | UDP → HTTPS JSON | [parsers/udp-miotiq.md](parsers/udp-miotiq.md) | Descripteur NebuleAir Pro 4G + legacy MobileAir |
|
||||
| UDP Miotiq | UDP → HTTPS JSON | [formats/udp-miotiq.md](formats/udp-miotiq.md) | Descripteur NebuleAir Pro 4G + legacy MobileAir |
|
||||
|
||||
## Comment ajouter une entrée
|
||||
|
||||
- **Nouveau capteur** : copier `sensors/_TEMPLATE.md` vers `sensors/<nom>.md`, remplir les sections, mettre à jour l'index ci-dessus.
|
||||
- **Nouveau format / parser** : créer le fichier sous `formats/` ou `parsers/`, mettre à jour l'index.
|
||||
- **Nouveau format ou parser** : créer le fichier sous `formats/`, mettre à jour l'index.
|
||||
- Voir [CONVENTIONS.md](CONVENTIONS.md) pour le style et le nommage.
|
||||
|
||||
## Pourquoi ce repo
|
||||
@@ -45,7 +44,7 @@ Avant : chaque firmware AirCarto (NebuleAir, ModuleAir, MobileAir…) redéfinis
|
||||
|
||||
Ici on centralise la **spécification** :
|
||||
|
||||
- **Capteur → Miotiq** : payload UDP binaire, décodé côté Miotiq via un *descripteur* ([`parsers/udp-miotiq.md`](parsers/udp-miotiq.md)).
|
||||
- **Capteur → Miotiq** : payload UDP binaire, décodé côté Miotiq via un *descripteur* ([`formats/udp-miotiq.md`](formats/udp-miotiq.md)).
|
||||
- **Miotiq → serveur AirCarto** : JSON canonique 2026 ([`formats/json-payload.md`](formats/json-payload.md)) posté sur `api.aircarto.com/receive_data`.
|
||||
- **Vocabulaire polluants** : codes ISO LCSQA ([`formats/iso-pollutant-codes.md`](formats/iso-pollutant-codes.md)).
|
||||
- **Capteurs physiques** : docs individuelles sous `sensors/` (protocole UART/I2C, câblage, commandes).
|
||||
|
||||
@@ -90,6 +90,6 @@ Les deux scénarios recommandés tiennent très confortablement dans le budget d
|
||||
|
||||
## À faire
|
||||
|
||||
- [ ] Remplacer cette estimation par une **mesure réelle** à partir des rapports de conso Miotiq (API `/api/device/detail` renvoie des compteurs de volume ; cf. [`parsers/udp-miotiq.md`](parsers/udp-miotiq.md)).
|
||||
- [ ] Remplacer cette estimation par une **mesure réelle** à partir des rapports de conso Miotiq (API `/api/device/detail` renvoie des compteurs de volume ; cf. [`formats/udp-miotiq.md`](formats/udp-miotiq.md)).
|
||||
- [ ] Vérifier la politique de comptage Miotiq exacte : payload UDP seul, UDP+IP, ou avec signaling ?
|
||||
- [ ] Mettre à jour ce document quand MobileAir migrera vers un descripteur Miotiq (payload probablement > 17 B).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Codes polluants ISO — convention AirCarto
|
||||
|
||||
Les descripteurs Miotiq (voir [`parsers/udp-miotiq.md`](../parsers/udp-miotiq.md)) et le JSON canonique (voir [`json-payload.md`](json-payload.md)) utilisent des codes `ISO_XX` pour désigner les polluants et grandeurs physiques. Ces codes suivent la nomenclature du **LCSQA** (Laboratoire Central de Surveillance de la Qualité de l'Air), basée sur la norme **ISO 7168**.
|
||||
Les descripteurs Miotiq (voir [`udp-miotiq.md`](udp-miotiq.md)) et le JSON canonique (voir [`json-payload.md`](json-payload.md)) utilisent des codes `ISO_XX` pour désigner les polluants et grandeurs physiques. Ces codes suivent la nomenclature du **LCSQA** (Laboratoire Central de Surveillance de la Qualité de l'Air), basée sur la norme **ISO 7168**.
|
||||
|
||||
Les codes vont théoriquement de `ISO_01` à `ISO_99`. Seuls ceux effectivement mesurés par au moins un capteur AirCarto sont documentés ici.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Le **type de capteur** est passé en query string. Modèles supportés :
|
||||
- **Tous les champs sont optionnels** sauf `device_id`. Un capteur n'envoie que les champs qu'il mesure.
|
||||
- **Valeur sentinelle `-1`** : donnée non disponible ou capteur non renseigné (ex. pas de GPS fix, pas de pression).
|
||||
- **Valeur sentinelle `255` (`0xFF`)** sur `error_flags`, `npm_status`, `device_status` uniquement : le firmware du capteur est antérieur à l'introduction du champ. À interpréter comme « non disponible », **pas** comme « toutes les erreurs actives ». Les nouveaux firmwares initialisent ces octets et envoient une valeur ≤ 254.
|
||||
- **Champs `<nom>_unit`** : optionnels, ajoutés par Miotiq quand la colonne `units` du descripteur est remplie et que l'export JSON est `Y` (voir [`parsers/udp-miotiq.md`](../parsers/udp-miotiq.md)). Format : `"<valeur> <unité>"`. Le backend peut les ignorer, la valeur canonique est toujours le champ sans suffixe.
|
||||
- **Champs `<nom>_unit`** : optionnels, ajoutés par Miotiq quand la colonne `units` du descripteur est remplie et que l'export JSON est `Y` (voir [`udp-miotiq.md`](udp-miotiq.md)). Format : `"<valeur> <unité>"`. Le backend peut les ignorer, la valeur canonique est toujours le champ sans suffixe.
|
||||
|
||||
## Identification
|
||||
|
||||
|
||||
@@ -8,29 +8,11 @@ Chemin de données :
|
||||
Capteur ──UDP──> Miotiq ──HTTPS POST JSON──> api.aircarto.com/receive_data ──> PostgreSQL + InfluxDB
|
||||
```
|
||||
|
||||
**Principe** : chaque capteur a un **descripteur Miotiq** (format texte pipe-séparé) qui décrit l'ordonnancement et le décodage de sa charge utile. Miotiq applique ce descripteur à l'ingestion, et produit le **JSON canonique AirCarto** (voir [`formats/json-payload.md`](../formats/json-payload.md)) qui est posté sur le backend.
|
||||
**Principe** : chaque capteur a un **descripteur Miotiq** (format texte pipe-séparé) qui décrit l'ordonnancement et le décodage de sa charge utile. Miotiq applique ce descripteur à l'ingestion et poste sur le backend le **JSON canonique AirCarto** spécifié dans [`json-payload.md`](json-payload.md) — c'est ce JSON-là que consomment les scripts PHP `receive_data`.
|
||||
|
||||
## Enveloppe JSON reçue du webhook
|
||||
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).
|
||||
|
||||
Le corps `application/json` reçu par le script PHP contient :
|
||||
|
||||
```json
|
||||
{
|
||||
"payload": "<base64 du datagramme UDP d'origine>",
|
||||
"customerId": "string",
|
||||
"rcvTime": 1713830400,
|
||||
"srcIP": "10.x.x.x",
|
||||
"srcImsi": "208xxxxxxxxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
| Champ | Type | Description |
|
||||
|--------------|---------|--------------------------------------------------------------------|
|
||||
| `payload` | string | **Base64** des octets UDP envoyés par le capteur. |
|
||||
| `customerId` | string | Identifiant client Miotiq. |
|
||||
| `rcvTime` | integer | Timestamp Unix UTC de la réception Miotiq, en secondes. |
|
||||
| `srcIP` | string | IP source du modem cellulaire (côté opérateur). |
|
||||
| `srcImsi` | string | IMSI de la SIM, sert à rattacher la mesure à un capteur en DB. |
|
||||
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).
|
||||
|
||||
## Format descripteur Miotiq
|
||||
|
||||
@@ -43,7 +25,7 @@ Un descripteur est une suite de lignes, une par champ. **Format officiel Miotiq*
|
||||
| Colonne | Description |
|
||||
|----------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `length` | Taille du champ en **caractères hexadécimaux** (2 chars = 1 octet). |
|
||||
| `variable name` | Identifiant logique du champ. Les codes polluants suivent [ISO 7168 AirCarto](../formats/iso-pollutant-codes.md). |
|
||||
| `variable name` | Identifiant logique du champ. Les codes polluants suivent [ISO 7168 AirCarto](iso-pollutant-codes.md). |
|
||||
| `base function` | Fonction de décodage appliquée aux octets bruts. Voir tableau ci-dessous. |
|
||||
| `units` | Unité physique finale (`ugm3`, `degC`, `%`, `hPa`, `ppb`, `dB`, `V`, `A`, `W`, `m/s`, `degrees`, `count`). Vide pour les champs d'état / versions. |
|
||||
| `equation` | Expression de transformation appliquée à la valeur décodée, où `x` est la valeur. Ex. `x/10`, `x/100`, `(x-32)*5/9`. Vide = pas de transformation. |
|
||||
@@ -155,16 +137,16 @@ Layout octet par octet :
|
||||
| 60 | 2 | `charger_status` | | | Bitfield, cf. firmware |
|
||||
| 62 | 2 | `wind_speed` | m/s | /10 | |
|
||||
| 64 | 2 | `wind_direction` | degrés | | 0–359, 0 = Nord |
|
||||
| 66 | 1 | `error_flags` | | | Bitfield erreurs système, détail dans [`formats/json-payload.md`](../formats/json-payload.md#error_flags--bitfield-système-1-octet). `0xFF` = firmware ancien. |
|
||||
| 67 | 1 | `npm_status` | | | Bitfield statut NextPM (copie du `STATE` UART, voir [`sensors/nextpm.md`](../sensors/nextpm.md) et [`json-payload.md`](../formats/json-payload.md#npm_status--bitfield-nextpm-1-octet)). `0xFF` = firmware ancien. |
|
||||
| 68 | 1 | `device_status` | | | Bitfield état boîtier, détail dans [`formats/json-payload.md`](../formats/json-payload.md#device_status--bitfield-boîtier-1-octet). `0xFF` = firmware ancien. |
|
||||
| 66 | 1 | `error_flags` | | | Bitfield erreurs système, détail dans [`json-payload.md`](json-payload.md#error_flags--bitfield-système-1-octet). `0xFF` = firmware ancien. |
|
||||
| 67 | 1 | `npm_status` | | | Bitfield statut NextPM (copie du `STATE` UART, voir [`sensors/nextpm.md`](../sensors/nextpm.md) et [`json-payload.md`](json-payload.md#npm_status--bitfield-nextpm-1-octet)). `0xFF` = firmware ancien. |
|
||||
| 68 | 1 | `device_status` | | | Bitfield état boîtier, détail dans [`json-payload.md`](json-payload.md#device_status--bitfield-boîtier-1-octet). `0xFF` = firmware ancien. |
|
||||
| 69 | 1 | `version_major` | | | Version firmware `X.y.z` |
|
||||
| 70 | 1 | `version_minor` | | | `x.Y.z` |
|
||||
| 71 | 1 | `version_patch` | | | `x.y.Z` |
|
||||
| 72 | 11 | `reserved` | | | À ignorer (évolution future du descripteur) |
|
||||
| **83** | total | | | | |
|
||||
|
||||
> Les champs `latitude`, `longitude`, `misc` présents dans le JSON final (voir [`json-payload.md`](../formats/json-payload.md#géolocalisation--contexte)) **ne sont pas** dans ce descripteur 83B. **À intégrer** : voir la section [À faire](#à-faire) — l'emplacement naturel est le bloc `reserved` de 11 octets (4+4+1 = 9 octets suffisent).
|
||||
> Les champs `latitude`, `longitude`, `misc` présents dans le JSON final (voir [`json-payload.md`](json-payload.md#géolocalisation--contexte)) **ne sont pas** dans ce descripteur 83B. **À intégrer** : voir la section [À faire](#à-faire) — l'emplacement naturel est le bloc `reserved` de 11 octets (4+4+1 = 9 octets suffisent).
|
||||
|
||||
### MobileAir (17 octets — legacy, pré-descripteur)
|
||||
|
||||
Reference in New Issue
Block a user