Files
SMATEC-FRONTEND/docs/mqtt-client.md

7.9 KiB

MQTT Client - Hướng dẫn sử dụng

Tổng quan

MQTT Client (mqttClient) là một utility singleton được thiết kế để quản lý kết nối MQTT trong ứng dụng. File này nằm tại src/utils/mqttClient.ts.

Cài đặt

Package mqtt được cài đặt qua npm:

npm install mqtt

Cấu trúc

src/utils/
├── mqttClient.ts    # MQTT Client utility
└── wsClient.ts      # WebSocket Client utility (legacy)

Cách sử dụng

1. Import

import { mqttClient } from '@/utils/mqttClient';

2. Kết nối

// Lấy credentials từ user metadata
const { frontend_thing_id, frontend_thing_key } =
  initialState?.currentUserProfile?.metadata || {};

// Kết nối
mqttClient.connect({
  username: frontend_thing_id,
  password: frontend_thing_key,
});

3. Đăng ký Event Handlers

// Khi kết nối thành công
const unConnect = mqttClient.onConnect(() => {
  console.log('MQTT Connected!');
});

// Khi có lỗi
const unError = mqttClient.onError((error) => {
  console.error('MQTT Error:', error);
});

// Khi kết nối đóng
const unClose = mqttClient.onClose(() => {
  console.log('MQTT Closed');
});

// Cleanup khi component unmount
return () => {
  unConnect();
  unError();
  unClose();
  mqttClient.disconnect();
};

4. Publish Message

const topic = `channels/${cfg_channel_id}/messages/cameraconfig/gmsv6`;
const payload = JSON.stringify(senmlData);

if (mqttClient.isConnected()) {
  mqttClient.publish(topic, payload);
}

5. Subscribe Topic

// Subscribe
mqttClient.subscribe('channels/123/messages/#');

// Nhận message
const unMessage = mqttClient.onMessage((topic, message, packet) => {
  console.log('Received:', topic, message.toString());
});

// Unsubscribe
mqttClient.unsubscribe('channels/123/messages/#');

API Reference

Method Mô tả
connect(credentials, url?) Kết nối MQTT với username/password
disconnect() Ngắt kết nối
subscribe(topic) Subscribe vào topic
unsubscribe(topic) Unsubscribe khỏi topic
publish(topic, payload) Publish message
onConnect(callback) Đăng ký callback khi kết nối
onClose(callback) Đăng ký callback khi đóng
onError(callback) Đăng ký callback khi lỗi
onMessage(callback) Đăng ký callback khi nhận message
isConnected() Kiểm tra trạng thái kết nối
getClient() Lấy MQTT client gốc

Cấu hình Proxy (Development)

Trong môi trường development, MQTT được proxy qua config trong config/proxy.ts:

"/mqtt": {
  target: "https://gms.smatec.com.vn",
  changeOrigin: true,
  ws: true,
},

Format SenML

Dữ liệu MQTT được format theo chuẩn SenML:

const senml = [
  {
    bn: `urn:dev:mac:${mac}:`, // Base name
    n: 'ack', // Name
    t: Date.now() / 1000, // Timestamp (seconds)
    vs: uuidv4(), // Value string (ACK ID)
  },
  {
    n: 'user@email.com', // Email (@ → :)
    t: Date.now() / 1000,
    vs: JSON.stringify(config), // Config dạng JSON string
  },
];

Topic Structure

channels/${channel_id}/messages/${type}/${gw_type}
Phần Mô tả
channel_id ID của kênh (từ thing.metadata.cfg_channel_id)
type Loại message: cameraconfig, config, log, notify
gw_type Loại gateway: gmsv6, gmsv5

Ví dụ hoàn chỉnh

import { mqttClient } from '@/utils/mqttClient';
import { useModel } from '@umijs/max';
import { useEffect, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';

const MyComponent = ({ thing }) => {
  const { initialState } = useModel('@@initialState');
  const [connected, setConnected] = useState(false);

  useEffect(() => {
    const { frontend_thing_id, frontend_thing_key } =
      initialState?.currentUserProfile?.metadata || {};

    if (!frontend_thing_id || !frontend_thing_key) return;

    mqttClient.connect({
      username: frontend_thing_id,
      password: frontend_thing_key,
    });

    const unConnect = mqttClient.onConnect(() => setConnected(true));
    const unClose = mqttClient.onClose(() => setConnected(false));

    return () => {
      unConnect();
      unClose();
      mqttClient.disconnect();
    };
  }, [initialState]);

  const handlePublish = () => {
    const { cfg_channel_id, external_id } = thing.metadata;
    const topic = `channels/${cfg_channel_id}/messages/cameraconfig/gmsv6`;

    const payload = [
      {
        bn: `urn:dev:mac:${external_id.replaceAll('-', '')}:`,
        n: 'ack',
        t: Date.now() / 1000,
        vs: uuidv4(),
      },
      {
        n: initialState?.currentUserProfile?.email?.replaceAll('@', ':'),
        t: Date.now() / 1000,
        vs: JSON.stringify({ record_type: 'all' }),
      },
    ];

    if (mqttClient.isConnected()) {
      mqttClient.publish(topic, JSON.stringify(payload));
    }
  };

  return (
    <button onClick={handlePublish} disabled={!connected}>
      {connected ? 'Publish' : 'Connecting...'}
    </button>
  );
};

So sánh mqttClient vs wsClient

Dự án có 2 utilities để giao tiếp real-time:

Bảng so sánh

Tiêu chí mqttClient wsClient
Thư viện mqtt (MQTT.js) reconnecting-websocket
Protocol MQTT over WebSocket WebSocket thuần
Xác thực Username/Password (MQTT credentials) Token (access_token) hoặc không
Topics Hỗ trợ MQTT topics, subscribe/unsubscribe Không có khái niệm topic
Publish publish(topic, payload) send(data)
Subscribe subscribe(topic) + onMessage() subscribe(callback)
Reconnect Tự động (built-in) Tự động (reconnecting-websocket)
Use case Giao tiếp với IoT Gateway/Devices Giao tiếp WebSocket server

Khi nào dùng mqttClient?

Dùng mqttClient khi:

  • Gửi/nhận dữ liệu với IoT Gateways (GMSv5, GMSv6)
  • Cấu hình thiết bị (camera, nodes, schedules)
  • Cần subscribe nhiều topics khác nhau
  • Làm việc với Mainflux platform

Khi nào dùng wsClient?

Dùng wsClient khi:

  • Cần WebSocket connection đơn giản
  • Giao tiếp với WebSocket server không phải MQTT broker
  • Xác thực bằng access_token

Code comparison

mqttClient:

import { mqttClient } from '@/utils/mqttClient';

// Connect với username/password
mqttClient.connect({
  username: 'thing_id',
  password: 'thing_key',
});

// Subscribe topic
mqttClient.subscribe('channels/123/messages/#');

// Publish với topic
mqttClient.publish('channels/123/messages/config', payload);

// Nhận message
mqttClient.onMessage((topic, message) => {
  console.log(topic, message.toString());
});

wsClient:

import { wsClient } from '@/utils/wsClient';

// Connect với hoặc không có token
wsClient.connect('/mqtt', false);

// Không có subscribe topic
// Chỉ nhận tất cả messages
wsClient.subscribe((data) => {
  console.log(data);
});

// Gửi data (không có topic)
wsClient.send({ action: 'update', data: payload });

Xem thêm