# 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: ```bash 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 ```typescript import { mqttClient } from '@/utils/mqttClient'; ``` ### 2. Kết nối ```typescript // 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 ```typescript // 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 ```typescript const topic = `channels/${cfg_channel_id}/messages/cameraconfig/gmsv6`; const payload = JSON.stringify(senmlData); if (mqttClient.isConnected()) { mqttClient.publish(topic, payload); } ``` ### 5. Subscribe Topic ```typescript // 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`: ```typescript "/mqtt": { target: "https://gms.smatec.com.vn", changeOrigin: true, ws: true, }, ``` ## Format SenML Dữ liệu MQTT được format theo chuẩn SenML: ```typescript 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 ```typescript 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 ( ); }; ``` ## 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:** ```typescript 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:** ```typescript 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 - [Mainflux.md](../Mainflux.md) - Tài liệu giao tiếp MQTT chi tiết - [mqttClient.ts](../src/utils/mqttClient.ts) - Source code MQTT Client - [wsClient.ts](../src/utils/wsClient.ts) - Source code WebSocket Client