Aller au contenu

Vue d'ensemble de la Connexion et Messagerie MQTT

L'intégration MQTT permet à la couche WebView (JavaScript) de se connecter à un broker MQTT via le service Android natif (BleService), de publier des messages et de s'abonner/se désabonner des sujets. Toutes les interactions se font via les gestionnaires bridgeWebView.

Flux de Communication MQTT

┌──────────┐
│   START  │
└────┬─────┘
     │
     ▼
┌─────────────────────────┐
│ Register Callbacks      │
│ - connectCallback       │
│ - messageArrivedCallback│
└────┬────────────────────┘
     │
     ▼
┌─────────────────────────┐
│ Connect to Broker       │ ──────> connectMqtt({
└────┬────────────────────┘           hostname,
     │                                 port,
     │ (Async Connection)              clientId
     │                               })
     ▼
┌─────────────────────────┐
│  connectCallback        │
│  Called                 │
└────┬────────────────────┘
     │
     ├───────────┬───────────┐
     ▼           ▼           ▼
  Success    Failed      Timeout
     │
     ▼
┌─────────────────────────┐
│ Subscribe to Topics     │ ──────> mqttSubTopic({
└────┬────────────────────┘           topic,
     │                                 qos
     │                               })
     │
     ├─────────────────────────────┐
     │                             │
     │                             ▼
     │                    ┌─────────────────┐
     │                    │ Publish Message │
     │                    │                 │
     │                    │ mqttPublishMsg({│
     │                    │   topic,        │
     │                    │   content,      │
     │                    │   qos           │
     │                    │ })              │
     │                    └─────────────────┘
     │
     ▼
┌─────────────────────────┐
│ Wait for Messages       │
│                         │
│ messageArrivedCallback  │ <──── (Messages arrive)
│ fired when msg received │
└────┬────────────────────┘
     │
     │ (Process messages...)
     │
     ▼
┌─────────────────────────┐
│ Unsubscribe             │ ──────> mqttUnSubTopic()
└────┬────────────────────┘
     │
     ▼
┌──────────┐
│   END    │
└──────────┘

À un niveau élevé, le flux MQTT est :

  1. Se connecter au broker MQTT via connectMqtt
  2. S'abonner aux sujets via mqttSubTopic
  3. Recevoir des messages via le rappel JavaScript mqttMsgArrivedCallBack
  4. Publier des messages via mqttPublishMsg
  5. Se désabonner via mqttUnSubTopic quand ce n'est plus nécessaire

En interne, la connectivité MQTT est gérée par BleService qui encapsule une instance MqttClientUtil.

Gestionnaires MQTT Exposés à JavaScript

Toutes les opérations MQTT sont exposées comme gestionnaires de pont enregistrés sur bridgeWebView à l'intérieur de BaseWebViewActivity :

  • connectMqtt
  • mqttSubTopic
  • mqttUnSubTopic
  • mqttPublishMsg

De plus, les messages arrivant du broker sont envoyés du natif à JavaScript via le rappel : mqttMsgArrivedCallBack

Chaque gestionnaire accepte des paramètres sous forme de chaîne JSON et retourne un objet Result encodé en JSON au rappel JavaScript.

En JS, vous utilisez généralement :

const result = JSON.parse(response);
if (result.success) {
  // ...
}

1. Connexion au Broker MQTT – connectMqtt

Objectif : - Établir une connexion à un broker MQTT en utilisant la configuration fournie par JavaScript.

Gestionnaire de Pont

bridgeWebView.registerHandler("connectMqtt", new BridgeHandler() {
    @Override
    public void handler(String data, CallBackFunction function) {
        // ...
    }
});

Entrée (depuis JavaScript)

  • data : chaîne JSON représentant un objet MqttConfig.

Champs requis minimum (validés dans BaseWebViewActivity) : - hostname – hôte / IP du broker - port – port du broker (ex. 1883 / 8883) - clientId – identifiant unique du client MQTT

Exemple de charge utile depuis JS :

const config = {
  hostname: "mqtt.example.com",
  port: 1883,
  clientId: "my-client-id-123",
  // Possible additional fields depending on MqttConfig:
  // username, password, ssl, keepAlive, etc.
};

bridgeWebView.callHandler("connectMqtt", JSON.stringify(config), function (response) {
  const result = JSON.parse(response);
  if (result.success) {
    console.log("MQTT connect request accepted, waiting for final callback...");
  } else {
    console.error("Invalid MQTT config:", result);
  }
});

Logique Native (simplifiée)

1. Valider la charge utile

  • Si data est vide → retourne Result.fail(PARAMETER_ERROR, false)
  • Analyser JSON en MqttConfig mqttConfig
  • Vérifier les champs requis : hostname, port, clientId

Si un champ requis est manquant ou invalide → retourne Result.fail(PARAMETER_ERROR, false)

2. Réponse immédiate à JS

Si la validation réussit, le natif retourne immédiatement Result.ok(true) au rappel JS appelant. La connexion réelle est effectuée de manière asynchrone dans un thread d'arrière-plan.

3. Connexion asynchrone

  • ThreadPool.getExecutor().execute(() -> { boolean b = bleService.connectMqtt(mqttConfig); ... })
  • Après que bleService.connectMqtt(mqttConfig) se termine, le résultat est publié sur le thread UI.

4. Rappel de résultat final à JS

  • En cas de succès :
    bridgeWebView.callHandler("connectMqttCallBack",
        gson.toJson(Result.ok(true)), ...);
    
  • En cas d'échec (ex. broker inaccessible, échec d'authentification) :
    bridgeWebView.callHandler("connectMqttCallBack",
        gson.toJson(Result.fail(MQTT_CONNECT_FAIL, false)), ...);
    

Comment gérer en JavaScript

Vous devez définir le gestionnaire connectMqttCallBack côté JS pour savoir quand la connexion a réellement réussi ou échoué :

bridgeWebView.registerHandler("connectMqttCallBack", function (data) {
  const result = JSON.parse(data);
  if (result.success) {
    console.log("MQTT connected successfully");
    // Now it's safe to subscribe and publish
  } else {
    console.error("MQTT connection failed:", result);
  }
});

2. Abonnement aux Sujets – mqttSubTopic

Objectif

Abonner le client MQTT actuel à un sujet donné avec un niveau QoS spécifié.

Gestionnaire de Pont

bridgeWebView.registerHandler("mqttSubTopic", new BridgeHandler() {
    @Override
    public void handler(String data, CallBackFunction function) {
        // ...
    }
});

Entrée (depuis JavaScript)

  • data est une chaîne JSON avec :
    • topic – chaîne de sujet MQTT (ex. "devices/abc123/status")
    • qos – niveau QoS (0, 1, ou 2)

Exemple d'appel :

const payload = {
  topic: "devices/abc123/status",
  qos: 1,
};

bridgeWebView.callHandler("mqttSubTopic", JSON.stringify(payload), function (response) {
  const result = JSON.parse(response);
  if (result.success) {
    console.log("Subscribed to topic successfully");
  } else {
    console.error("Failed to subscribe:", result);
  }
});
Exemple d'appel :
const payload = {
  topic: "devices/abc123/status",
  qos: 1,
};

bridgeWebView.callHandler("mqttSubTopic", JSON.stringify(payload), function (response) {
  const result = JSON.parse(response);
  if (result.success) {
    console.log("Subscribed to topic successfully");
  } else {
    console.error("Failed to subscribe:", result);
  }
});

Logique Native (simplifiée)

  1. Analyser data en JSONObject.
  2. Extraire topic et qos.
  3. Récupérer le client MQTT :

    MqttClientUtil mqttClientUtil = bleService.getMqttClientUtil();
    

  4. Vérifier la connexion :

    • Si mqttClientUtil == null ou !mqttClientUtil.isConnected()Result.fail(MQTT_CURRENT_NOT_CONNECTED, false)

Appel :

boolean subscribe = mqttClientUtil.subscribe(topic, qos);
6. Retourner : - Si subscribe == trueResult.ok(true) - Sinon →Result.fail(FAIL, false)

Pour toute exceptionResult.fail(PARAMETER_ERROR, false)

3. Désabonnement des Sujets – mqttUnSubTopic

Objectif

Se désabonner d'un sujet précédemment abonné.

Gestionnaire de Pont

bridgeWebView.registerHandler("mqttUnSubTopic", new BridgeHandler() {
    @Override
    public void handler(String data, CallBackFunction function) {
        // ...
    }
});

Entrée (depuis JavaScript)

data est une chaîne JSON avec : - topic – sujet MQTT pour se désabonner

Exemple d'appel :

const payload = {
  topic: "devices/abc123/status",
};

bridgeWebView.callHandler("mqttUnSubTopic", JSON.stringify(payload), function (response) {
  const result = JSON.parse(response);
  if (result.success) {
    console.log("Unsubscribed from topic");
  } else {
    console.error("Failed to unsubscribe:", result);
  }
});

Logique Native (simplifiée)

  1. Analyser le sujet depuis JSON.
  2. Récupérer MqttClientUtil via bleService.getMqttClientUtil().
  3. Si MQTT n'est pas connecté → Result.fail(MQTT_CURRENT_NOT_CONNECTED, false).
  4. Appel :
    boolean ok = mqttClientUtil.unSubscribe(topic);
    
  5. Si okResult.ok(true) Sinon → Result.fail(FAIL, false)
  6. Pour toute exceptionResult.fail(PARAMETER_ERROR, false)

4. Publication de Messages – mqttPublishMsg

Objectif

Publier un message sur un sujet MQTT spécifique avec un niveau QoS donné.

Gestionnaire de Pont

bridgeWebView.registerHandler("mqttPublishMsg", new BridgeHandler() {
    @Override
    public void handler(String data, CallBackFunction function) {
        // ...
    }
});

Entrée (depuis JavaScript)

  • data est une chaîne JSON :
  • topic – sujet MQTT sur lequel publier
  • content – charge utile/corps du message (chaîne)
  • qos – niveau QoS (0, 1, 2)

Exemple d'appel :

const payload = {
  topic: "devices/abc123/commands",
  content: JSON.stringify({ action: "reboot" }),
  qos: 1,
};

bridgeWebView.callHandler("mqttPublishMsg", JSON.stringify(payload), function (response) {
  const result = JSON.parse(response);
  if (result.success) {
    console.log("Message published");
  } else {
    console.error("Failed to publish:", result);
  }
});

Logique Native (simplifiée)

  1. Analyser topic, content, et qos depuis JSON.
  2. Récupérer MqttClientUtil via bleService.getMqttClientUtil().
  3. Vérifier la connexion ; si déconnecté → Result.fail(MQTT_CURRENT_NOT_CONNECTED, false).
  4. Publier :
    boolean ok = mqttClientUtil.publish(topic, qos, content.getBytes(StandardCharsets.UTF_8));
    

5. Réception des Messages MQTT – mqttMsgArrivedCallBack

Objectif

Livrer les messages MQTT entrants du natif (via EventBus) à JavaScript.

Flux Natif

BleService (via MqttClientUtil) reçoit les messages MQTT et les publie sur un canal EventBus avec le tag EventBusEnum.MQTT_MSG_ARRIVED. BaseWebViewActivity écoute ces événements :

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(EventBusMsg message) {
    // ...
    } else if (message.getTagEnum() == EventBusEnum.MQTT_MSG_ARRIVED) {
        bridgeWebView.callHandler("mqttMsgArrivedCallBack",
            (String) message.getT(),
            new CallBackFunction() {
                @Override
                public void onCallBack(String data) { }
            });
    }
    // ...
}
- message.getT() est censé être une représentation String de la charge utile MQTT (et éventuellement des métadonnées, selon la façon dont MqttClientUtil la formate).

Gestion en JavaScript

Vous devez enregistrer un gestionnaire pour mqttMsgArrivedCallBack pour recevoir et analyser les messages entrants :

bridgeWebView.registerHandler("mqttMsgArrivedCallBack", function (payload) {
  // `payload` is a string coming from native.
  // It might be plain text or JSON-encoded, depending on your MQTT publisher.

  try {
    const parsed = JSON.parse(payload);
    console.log("MQTT message (parsed):", parsed);
    // Handle structured message here
  } catch (e) {
    console.log("MQTT message (raw):", payload);
    // Handle plain string payload
  }
});
Flux typique : 1. Se connecter en utilisant connectMqtt. 2. S'abonner en utilisant mqttSubTopic. 3. Les messages entrants sur les sujets abonnés sont livrés via mqttMsgArrivedCallBack.

6. Codes d'Erreur et Gestion des Résultats

Les gestionnaires liés à MQTT utilisent Result.ok(...) et Result.fail(code, ...) pour encapsuler les réponses. Les codes d'erreur pertinents incluent :

  • MQTT_CONNECT_FAIL – la tentative de connexion au broker a échoué (configuration invalide, erreur réseau, erreur d'authentification, etc.)
  • MQTT_CURRENT_NOT_CONNECTED – toute opération nécessitant une connexion MQTT active (s'abonner, se désabonner, publier) a été appelée alors que le client est null ou déconnecté.
  • PARAMETER_ERROR – la charge utile de la requête est manquante des champs requis ou ne peut pas être analysée.
  • FAIL – échec générique (ex. subscribe / publish a retourné false).

Côté JS, vous devriez vérifier systématiquement :

const result = JSON.parse(response);
if (!result.success) {
  console.error("MQTT error:", result.code, result.message);
}

7. Flux d'Utilisation MQTT Typique (Depuis JavaScript)

  1. Configurer et Se Connecter
    • Construire un objet MqttConfig.
    • Appeler connectMqtt avec la configuration JSON.
    • Attendre connectMqttCallBack avec success = true.
  2. S'Abonner aux Sujets
    • Pour chaque sujet, appeler mqttSubTopic avec { topic, qos }.
  3. Enregistrer le Gestionnaire de Messages
    • Enregistrer mqttMsgArrivedCallBack pour traiter les messages entrants.
  4. Publier des Messages
    • Utiliser mqttPublishMsg pour envoyer des commandes ou des données au broker.
  5. Se Désabonner et Nettoyer
    • Lorsque vous quittez l'écran ou n'avez plus besoin d'un sujet, appeler mqttUnSubTopic.
    • La déconnexion MQTT est actuellement gérée dans la couche native (aucun gestionnaire de pont disconnectMqtt explicite n'est exposé dans BaseWebViewActivity).

8. Mise à Jour d'État

Une fois tous les gestionnaires enregistrés, dispatch({ type: "SET_BRIDGE_INITIALIZED", payload: true }) met à jour l'état de l'application pour indiquer que le pont est maintenant initialisé. Cet indicateur empêche la ré-initialisation lors des appels futurs, assurant que l'application maintient un seul point de communication entre JavaScript et WebView.


9. Résumé des Fonctions

La fonction setupBridge fournit une méthode organisée et sécurisée pour gérer diverses interactions basées sur Bluetooth, QR code et MQTT dans l'application. Chaque gestionnaire valide et traite les données, assurant une gestion cohérente des états et réduisant les problèmes potentiels liés à la gestion asynchrone des données. Cette structure permet une communication transparente entre WebView et l'état de l'application, permettant des échanges de données complexes et multi-sources avec un seul appel d'initialisation.

Résumé

La technologie BLE utilisée ici semble être :

  • Central-Périphérique avec Profil GATT : L'application (centrale) scanne, se connecte et interagit avec des périphériques, récupérant les services et caractéristiques GATT.
  • Intégration IoT via MQTT : Les données BLE ou les mises à jour d'état peuvent être envoyées à un serveur via MQTT, suggérant une intégration avec un écosystème IoT plus large.
  • Structure d'Application Hybride : La présence de WebViewJavascriptBridge indique une application WebView ou hybride, permettant la fonctionnalité BLE dans un environnement multi-plateforme.

Cette configuration permettrait à une application mobile de scanner, de se connecter, d'initialiser et d'interagir avec des appareils BLE dans un contexte IoT, en utilisant potentiellement MQTT pour envoyer des données à un service cloud ou à un backend.

Cette séparation maintient la connexion MQTT et la gestion bas niveau dans le code natif, tandis que la couche WebView/JavaScript n'a besoin que d'utiliser un petit ensemble de méthodes de pont et de rappels pour intégrer MQTT dans l'IU et la logique métier.