Architecture & Flux
Comment Clouka communique avec un routeur Teltonika â de l'adoption initiale Ă la tĂ©lĂ©mĂ©trie temps rĂ©el et l'exĂ©cution de commandes Ă distance.
Vue d'ensemble
Adoption â Premier dĂ©marrage
HTTP uniquementOuvre /adopt, copie la commande curl
Exécute : curl ".../api/v1/adopt/pltk_xxx" | sh
Retourne le script bootstrap (sh) avec token embarqué
Détecte : SERIAL (mnf_info), FIRMWARE (/etc/version), MODEL, MAC
POST /api/v1/device/install { platform_token, serial, firmware, model, mac }
Crée le device en DB, génÚre dvtk_xxx, répond { device_token, mqtt_host, mqtt_port }
Sauve token â /etc/clouka/device.token · config â /etc/clouka/config
GET /devices/{serial}/script â tĂ©lĂ©charge clouka-agent.sh
clouka-agent.sh install â service procd actif + dĂ©marrage auto
TĂ©lĂ©mĂ©trie â Envoi toutes les 5 secondes
MQTT â ClickHouseCollecte les mĂ©triques systĂšme : CPU, RAM, tempĂ©rature, uptime
Collecte 4G via gsmctl : opérateur, RSRP, RSRQ, SINR, bande, Cell ID, IMEI
Collecte réseau : IP WAN, bytes RX/TX, clients LAN/WiFi
Collecte GPS via gpsctl (si disponible) : lat, lon, vitesse, satellites
mosquitto_pub â devices/{serial}/telemetry (JSON, QoS 1)
Reçoit et distribue le message à tous les abonnés
AbonnĂ© MQTT â transforme le JSON â insĂšre dans ClickHouse
Met Ă jour MySQL : last_seen_at, status=online, firmware, model
Poll 5s â relit MySQL + ClickHouse â met Ă jour le dashboard
telemetry. PartitionnĂ© par mois, indexĂ© par (serial, ts). RequĂȘtes sub-seconde mĂȘme sur des millions de lignes.Commandes â Envoi depuis le dashboard
MQTT bidirectionnelClique sur "Reboot" (ou Backup, RefreshâŠ) dans le dashboard
CrĂ©e un enregistrement Command en DB (status: pending â sent)
MqttPublisherService publie sur devices/{serial}/commands
Distribue le message au routeur abonné sur ce topic
mosquitto_sub reçoit la commande JSON
execute_command() : switch/case sur l'action (reboot, backup, execâŠ)
Publie le résultat sur devices/{serial}/response { id, status, output }
Reçoit la réponse, met à jour Command en DB (status: done/failed, result, completed_at)
Poll suivant â onglet Commandes affiche le rĂ©sultat
DĂ©tection hors ligne â LWT + timeout
Last Will TestamentCas 1 â DĂ©connexion propre (reboot, arrĂȘt)
Reçoit signal SIGTERM (arrĂȘt procd) â exĂ©cute le bloc stop
Publie devices/{serial}/status = "offline" (retained, QoS 1)
Reçoit â Device::update(status: offline)
Badge passe rouge au prochain poll (†5s)
Cas 2 â Perte rĂ©seau brutale (coupure 4G)
Keepalive expirĂ© â publie automatiquement le LWT enregistrĂ©
devices/{serial}/status = "offline" (configuré au connect par mosquitto_sub)
Reçoit â Device::update(status: offline)
Fallback : last_seen_at > 30s â retourne false mĂȘme sans LWT
Stack technique
Ăcrit en Rust. LĂ©ger, performant. Ăcoute sur :1883. API HTTP sur :6060 pour les stats.
AbonnĂ© MQTT (source) â parse JSON (transform VRL) â insert batch ClickHouse (sink). ZĂ©ro code.
Stocke la tĂ©lĂ©mĂ©trie. MergeTree engine, partitionnĂ© par mois. RequĂȘtes analytiques en ms.
Devices, comptes, commandes. Données structurées et état courant uniquement.
API REST pour l'adoption. mqtt:listen pour la synchro MySQL. Livewire pour l'UI réactive.
Poll toutes les 5s via XHR. Pas de rechargement de page. wire:ignore sur les graphes Chart.js.
Topics MQTT â Convention
| Topic | Direction | QoS | Retained | Contenu |
|---|---|---|---|---|
| devices/{serial}/telemetry | Router â Cloud | 1 | Non | JSON complet (CPU, RAM, 4G, GPS, rĂ©seauâŠ) toutes les 5s |
| devices/{serial}/status | Router â Cloud | 1 | Oui | "online" au dĂ©marrage · "offline" via LWT ou arrĂȘt propre |
| devices/{serial}/commands | Cloud â Router | 1 | Non | JSON : { id, action, payload } â envoyĂ© par Laravel |
| devices/{serial}/response | Router â Cloud | 1 | Non | JSON : { id, status, output } â rĂ©ponse de l'agent |
| devices/{serial}/events | Router â Cloud | 1 | Non | RĂ©servĂ© â Ă©vĂ©nements futurs (changement SIM, alerteâŠ) |