原生小程序 TS 现在切屏就重新连接 websocket mqtt ,设置五分钟延迟断连保活也没生效。
// mqtt-client.ts
const mqtt = require("../utils/mqtt.min.js");
import { Encrypt, Decrypt } from '../utils/crypto';
// MQTT 消息接口定义
interface MqttMessage {
id: string;
code: number;
msg: string;
type: string;
ts: number;
expired: number;
data: {
houseId?: string; // 房屋ID
houseName?: string; // 房屋名称
role?: number; // 用户角色
deviceCode?: string;
online?: boolean;
lockStatus?: number;
userId?: string;
success?: boolean;
errorCode?: string;
errorMsg?: string;
keyType?: string;
hijack?: string;
password?: string;
imgUrl?: string;
platform?: string;
serialNo?: string;
opTime?: number;
leaveHouseId?: string; // 被移出的房屋ID
};
}
interface MqttClient {
connected?: boolean;
on(event: 'connect' | 'message' | 'error' | 'reconnect' | 'offline' | 'close', callback: (...args: any[]) => void): void;
removeListener(event: 'connect' | 'message' | 'error' | 'reconnect' | 'offline' | 'close', callback: (...args: any[]) => void): void;
removeAllListeners(event?: 'connect' | 'message' | 'error' | 'reconnect' | 'offline' | 'close'): void;
subscribe(topic: string | string[], opts?: any, callback?: (err: Error, granted: any) => void): void;
unsubscribe(topic: string | string[], opts?: any, callback?: (err: Error) => void): void;
publish(topic: string, message: string): void;
end(force?: boolean, opts?: any, cb?: () => void): void;
}
interface MqttClientConfig {
userId: string;
familyId: string;
deviceCode: string;
}
interface IClientOptions {
clientId: string;
username: string;
password: string;
reconnectPeriod: number;
connectTimeout: number;
protocol: string;
protocolVersion: number;
clean: boolean;
rejectUnauthorized: boolean;
keepalive: number;
path: string;
will?: {
topic: string;
payload: string;
qos: number;
retain: boolean;
};
}
interface EventEmitter {
on(event: string, listener: Function): void;
emit(event: string, ...args: any[]): void;
off(event: string, listener: Function): void;
removeAllListeners?(): void;
}
// 设备状态接口
interface DeviceStatus {
deviceCode: string;
online: boolean;
lockStatus?: number;
lastUpdateTime: number;
}
// 简单事件发射器实现
class SimpleEventEmitter implements EventEmitter {
private listeners: { [event: string]: Function[] } = {};
on(event: string, listener: Function): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
}
emit(event: string, ...args: any[]): void {
if (this.listeners[event]) {
this.listeners[event].forEach((listener) => {
try {
listener(...args);
} catch (error) {
console.error(`[EventEmitter] Error in listener for event ${event}:`, error);
}
});
}
}
off(event: string, listener: Function): void {
if (this.listeners[event]) {
this.listeners[event] = this.listeners[event].filter(l => l !== listener);
}
}
removeAllListeners(): void {
this.listeners = {};
}
}
// 全局单例实例
let globalClient: SmartDoorMQTTClient | null = null;
class SmartDoorMQTTClient {
private client: MqttClient | null = null;
private config: MqttClientConfig;
private eventEmitter: EventEmitter;
private deviceStatuses: Map<string, DeviceStatus> = new Map();
private reconnectAttempts: number = 0;
private readonly MAX_RECONNECT_ATTEMPTS: number = 15;
private isConnecting: boolean = false;
private connectionTimer: any = null;
private reconnectTimer: any = null;
private readonly MQTT_URL = 'wss://iot-door.lloong.com:30013';
private readonly RECONNECT_DELAY = 1000; // 3秒重连延迟
// 新增:记录后台切换前的连接状态
private wasConnectedBeforeBackground: boolean = false;
// 新增:消息队列,存储待发送的消息
private messageQueue: Array<{ topic: string, type: string, data: any }> = [];
// 新增:连接状态
private _connectingPromise: Promise<void> | null = null;
// 新增:重连提示弹窗
private reconnectModal: any = null;
private constructor(config: MqttClientConfig) {
this.config = config;
this.eventEmitter = new SimpleEventEmitter();
this.initDeviceStatus();
// 新增:注册小程序前后台切换事件
this.registerAppEvents();
}
// 单例获取方法
public static getInstance(config?: MqttClientConfig): SmartDoorMQTTClient {
if (!globalClient && config) {
globalClient = new SmartDoorMQTTClient(config);
} else if (globalClient && config) {
// 更新现有实例的配置
globalClient.updateConfig(config);
}
return globalClient as SmartDoorMQTTClient;
}
private updateConfig(config: MqttClientConfig): void {
this.config = config;
}
// 新增:注册小程序前后台切换事件
private registerAppEvents(): void {
let backgroundTimer: number;
wx.onAppHide(() => {
const app = getApp<IAppOption>();
// 优先检查全局导航标记
if (app.globalData.__pageNavigating) {
console.log('[MQTT] 页面导航中,保持连接');
return;
}
// 检查全局标记,判断是否是页面导航
// @ts-ignore - 访问自定义属性
if (getApp() && getApp().__pageNavigating === true) {
console.log('[MQTT] 检测到页面导航,不断开连接');
return;
}
this.wasConnectedBeforeBackground = this.isConnected();
this.closeConnection(1000, "App entering background");
backgroundTimer = setTimeout(() => {
this.cleanup();
this.messageQueue = [];
}, 5000) as unknown as number;
});
wx.onAppShow(() => {
const app = getApp<IAppOption>();
// 清除导航标记
if (app.globalData.__pageNavigating) {
console.log('[MQTT] 页面导航返回,保持连接');
app.globalData.__pageNavigating = false;
return;
}
// 检查全局标记,判断是否是页面导航
// @ts-ignore - 访问自定义属性
if (getApp() && getApp().__pageNavigating === true) {
console.log('[MQTT] 检测到页面导航返回,不触发重连');
// @ts-ignore
getApp().__pageNavigating = false;
return;
}
clearTimeout(backgroundTimer);
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
this.reconnectTimer = setTimeout(() => {
this.handleForegroundReconnect();
}, 300) as unknown as number;
});
}
// 新增前台重连处理方法
private handleForegroundReconnect(): void {
if (!this.wasConnectedBeforeBackground) return;
this.showReconnectModal();
this.connect()
.then(() => {
this.hideReconnectModal();
this.processQueuedMessages();
})
.catch(error => {
console.error('[MQTT][AppShow] Failed to reconnect:', error);
this.hideReconnectModal();
if (this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {
this.scheduleReconnect(); // 继续按策略重连
}
});
}
// 新增:显示重连提示弹窗
private showReconnectModal(): void {
if (this.reconnectModal) {
return; // 已存在弹窗,不重复显示
}
try {
this.reconnectModal = wx.showLoading({
title: '正在重新连接...',
mask: true
});
} catch (error) {
console.error('[MQTT][ShowReconnectModal] Failed to show modal:', error);
}
}
// 新增:隐藏重连提示弹窗
private hideReconnectModal(): void {
if (this.reconnectModal) {
try {
wx.hideLoading();
this.reconnectModal = null;
} catch (error) {
console.error('[MQTT][HideReconnectModal] Failed to hide modal:', error);
}
}
}
// 新增:手动关闭WebSocket连接方法
private closeConnection(code: number = 1000, reason: string = "Normal closure"): void {
try {
// 发送静默下线通知(不触发重连)
const silentClose = code === 1000 && reason.includes("background");
if (!silentClose) {
this.publishServerOffline();
}
if (this.client?.connected) {
// 发送下线通知
this.publishServerOffline();
// 取消订阅
if (this.config.userId && this.config.familyId) {
const userTopic = `mquser-to-${this.config.userId}`;
const familyTopic = `mquser-to-${this.config.familyId}`;
this.client.unsubscribe([userTopic, familyTopic], {}, () => {
this.cleanup();
});
} else {
this.cleanup();
}
} else {
this.cleanup();
}
} catch (error) {
console.error('[MQTT][CloseConnection] Error closing connection:', error);
this.cleanup();
}
}
// 新增:处理队列中的消息
private processQueuedMessages(): void {
if (this.messageQueue.length === 0) return;
if (this.isConnected()) {
// 处理队列中的所有消息
const messages = [...this.messageQueue];
this.messageQueue = [];
messages.forEach(msg => {
try {
this.publishMessage(msg.topic, msg.type, msg.data);
} catch (error) {
console.error('[MQTT][ProcessQueue] Failed to publish queued message:', error);
}
});
} else {
console.warn('[MQTT][ProcessQueue] Client not connected, messages remain queued');
}
}
// 初始化设备状态
private initDeviceStatus(): void {
try {
const storedStatuses = wx.getStorageSync('deviceStatuses');
if (storedStatuses) {
this.deviceStatuses = new Map(JSON.parse(storedStatuses));
console.log('[MQTT][InitDeviceStatus] Loaded stored statuses:',
Array.from(this.deviceStatuses.entries()));
}
} catch (error) {
console.error('[MQTT][InitDeviceStatus] Error loading stored statuses:', error);
}
}
// 保存设备状态
private saveDeviceStatuses(): void {
try {
const statusesArray = Array.from(this.deviceStatuses.entries());
wx.setStorageSync('deviceStatuses', JSON.stringify(statusesArray));
} catch (error) {
console.error('[MQTT][SaveDeviceStatuses] Error saving statuses:', error);
}
}
// 连接状态检查
public isConnected(): boolean {
return this.client?.connected || false;
}
// 连接方法,修改为返回 Promise
public connect(): Promise<void> {
// 如果已有连接进程在进行,返回现有 Promise
// 增加状态检查
if (this.isConnected()) {
return Promise.resolve();
}
// 增加网络状态检查
wx.getNetworkType({
success: res => {
if (res.networkType === 'none') {
throw new Error('No network connection');
}
}
});
if (this._connectingPromise) {
return this._connectingPromise;
}
if (this.isConnecting) {
this._connectingPromise = new Promise((resolve, reject) => {
const checkInterval = setInterval(() => {
if (this.isConnected()) {
clearInterval(checkInterval);
resolve();
}
}, 500);
// 设置超时
setTimeout(() => {
clearInterval(checkInterval);
reject(new Error('Connection timeout while waiting for existing connection'));
}, 2000);
});
return this._connectingPromise;
}
// 创建新的连接 Promise
this._connectingPromise = new Promise((resolve, reject) => {
try {
this.isConnecting = true;
// 清理现有连接
if (this.client) {
this.cleanup();
}
const clientId = `mquser-xcx-cid-${this.config.userId}-${Date.now()}`;
const willMessage: MqttMessage = {
id: Math.random().toString(16).substr(2, 16),
code: 0,
msg: "success",
type: "server/offline",
ts: Date.now(),
expired: Date.now() + 86400000,
data: {
userId: this.config.userId,
platform: "wechat"
}
};
const willTopic = "mqsvr-to-iot-door-user";
const encryptedWillMessage = Encrypt(JSON.stringify(willMessage));
const options: IClientOptions = {
clientId: clientId,
username: "xcxtest",
password: "xcxtest",
reconnectPeriod: 1000,
connectTimeout: 1000,
protocol: 'wxs',
protocolVersion: 4,
clean: true,
rejectUnauthorized: false,
keepalive: 10,
path: '/mqtt',
will: {
topic: willTopic,
payload: encryptedWillMessage,
qos: 1,
retain: false
}
};
// 设置连接超时处理
this.connectionTimer = setTimeout(() => {
if (this.isConnecting) {
this.cleanup();
this.scheduleReconnect();
this.eventEmitter.emit('connectionTimeout');
reject(new Error('Connection timeout'));
this._connectingPromise = null;
}
}, options.connectTimeout);
this.client = mqtt.connect(this.MQTT_URL, options);
if (!this.client) {
throw new Error('Failed to create MQTT client instance');
}
// 修改事件监听器,添加Promise处理
this.client.on('connect', () => {
this.isConnecting = false;
this.reconnectAttempts = 0;
if (this.connectionTimer) {
clearTimeout(this.connectionTimer);
this.connectionTimer = null;
}
this.handleConnect();
resolve(); // 解决Promise
this._connectingPromise = null;
});
this.setupEventListeners(() => {
// 在出错时reject Promise
reject(new Error('Connection failed or was closed'));
this._connectingPromise = null;
});
} catch (error) {
console.error('[MQTT][Connect] Setup failed:', error);
this.cleanup();
this.scheduleReconnect();
reject(error);
this._connectingPromise = null;
}
});
return this._connectingPromise;
}
// 修改 scheduleReconnect 方法
private scheduleReconnect(): void {
if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
this.eventEmitter.emit('maxReconnectAttemptsReached');
this.showReconnectFailedToast();
return;
}
// 使用固定延迟,不再使用指数退避策略
const delay = this.RECONNECT_DELAY;
this.reconnectTimer = setTimeout(() => {
this.connect().catch(console.error);
}, delay);
this.reconnectAttempts++;
}
// 新增重连失败提示
private showReconnectFailedToast(): void {
wx.showToast({
title: '连接断开,请检查网络',
icon: 'none',
duration: 2000
});
}
private cleanup(): void {
this.reconnectAttempts = 0; // 新增
// 清除所有定时器
[this.connectionTimer, this.reconnectTimer].forEach(timer => {
if (timer) {
clearTimeout(timer);
timer = null;
}
});
// 强制关闭连接
if (this.client) {
try {
this.client.removeAllListeners();
this.client.end(true);
} catch (error) {
console.error('[MQTT][Cleanup] Error during cleanup:', error);
}
this.client = null;
}
// 重置状态
this.isConnecting = false;
this._connectingPromise = null;
this.reconnectAttempts = 0; // 重置重试次数
}
private setupEventListeners(onError?: () => void): void {
if (!this.client) {
if (onError) onError();
return;
}
this.client.on('message', (topic: string, payload: string | ArrayBuffer) => {
this.handleMessage(topic, payload);
});
this.client.on('error', (error: Error) => {
this.handleError(error);
if (onError) onError();
});
this.client.on('offline', () => {
this.handleOffline();
});
this.client.on('close', () => {
this.scheduleReconnect();
if (onError) onError();
});
this.client.on('reconnect', () => {
this.reconnectAttempts++;
if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
console.error('[MQTT][Event:Reconnect] Max reconnection attempts reached');
this.cleanup();
this.eventEmitter.emit('maxReconnectAttemptsReached');
if (onError) onError();
}
});
}
private handleConnect(): void {
if (!this.client) {
console.error('[MQTT][HandleConnect] Cannot handle connect - client is null');
return;
}
const userTopic = `mquser-to-${this.config.userId}`;
const familyTopic = `mquser-to-${this.config.familyId}`;
this.client.subscribe([userTopic, familyTopic], { qos: 1 }, (err, granted) => {
if (err) {
console.error('[MQTT][HandleConnect] Subscribe error:', err);
return;
}
console.log('[MQTT][HandleConnect] Subscribe success:', granted);
this.publishUserOnline();
});
}
private decodeMessage(payload: string | ArrayBuffer): string {
if (typeof payload === 'string') {
return payload;
}
return typeof TextDecoder !== 'undefined'
? new TextDecoder().decode(payload)
: this.arrayBufferToString(payload);
}
private arrayBufferToString(buffer: ArrayBuffer): string {
const uint8Array = new Uint8Array(buffer);
let result = '';
for (let i = 0; i < uint8Array.length; i++) {
result += String.fromCharCode(uint8Array[i]);
}
return result;
}
private handleUserJoinHouse(message: MqttMessage): void {
try {
wx.setStorageSync('has_new_house', true);
} catch (e) {
console.error('[MQTT] Failed to set refresh flag:', e);
}
// 仍然触发事件以保持兼容性
this.eventEmitter.emit('userJoinHouse');
}
private handleMessage(topic: string, payload: string | ArrayBuffer): void {
try {
console.log('[MQTT][HandleMessage] 收到消息 - 主题:', topic);
const message = this.decodeMessage(payload);
const parsedMessage = JSON.parse(Decrypt(message)) as MqttMessage;
console.log('[MQTT][HandleMessage] 解密消息 - 类型:', parsedMessage.type, 'ID:', parsedMessage.id);
switch (parsedMessage.type) {
case 'family/user_online':
console.log('[MQTT][HandleMessage] 处理家庭用户上线消息');
this.handleFamilyUserOnline(parsedMessage);
break;
case 'server/offline':
console.log('[MQTT][HandleMessage] 处理服务器离线消息');
this.handleServerOffline(parsedMessage);
break;
case 'user/door_status':
console.log('[MQTT][HandleMessage] 处理用户门锁状态消息');
this.handleUserDoorStatus(parsedMessage);
break;
case 'family/door_status':
console.log('[MQTT][HandleMessage] 处理家庭门锁状态消息');
this.handleDoorStatus(parsedMessage);
break;
case 'user/online_status':
console.log('[MQTT][HandleMessage] 处理用户在线状态消息');
this.handleUserOnlineStatus(parsedMessage);
break;
case 'user/add_key_success':
console.log('[MQTT][HandleMessage] 处理添加密钥成功消息');
this.handleAddKeySuccess(parsedMessage);
break;
case 'door/lock_status':
console.log('[MQTT][HandleMessage] 处理门锁状态消息');
this.handleDoorLockStatus(parsedMessage);
break;
case 'door/open_door_result':
console.log('[MQTT][HandleMessage] 处理开门结果消息');
this.handleOpenDoorResult(parsedMessage);
break;
case 'door/delete_key_result':
console.log('[MQTT][HandleMessage] 处理删除密钥结果消息');
this.handleDeleteKeyResult(parsedMessage);
break;
case 'door/add_key_result':
console.log('[MQTT][HandleMessage] 处理添加密钥结果消息');
this.handleAddKeyResult(parsedMessage);
break;
case 'door/wallpaper_result':
console.log('[MQTT][HandleMessage] 处理壁纸结果消息');
this.handleWallpaperResult(parsedMessage);
break;
case 'user/reset_device':
console.log('[MQTT][HandleMessage] 处理重置设备消息');
this.handleResetDevice(parsedMessage);
break;
case 'user/join_house':
console.log('[MQTT][HandleMessage] 处理加入房屋消息');
this.handleUserJoinHouse(parsedMessage);
break;
case 'user/leave_house':
console.log('[MQTT][HandleMessage] 处理离开房屋消息');
this.handleUserLeaveHouse(parsedMessage);
break;
case 'user/house_user_role':
console.log('[MQTT][HandleMessage] 处理房屋用户角色消息');
this.handleHouseUserRole(parsedMessage);
break;
default:
console.log('[MQTT][HandleMessage] 未知消息类型:', parsedMessage.type);
break;
}
} catch (error) {
console.error('[MQTT][HandleMessage] Processing error:', error);
}
}
private handleHouseUserRole(message: MqttMessage): void {
const { houseId } = message.data;
if (!houseId) return;
// 触发权限变更事件,传递房屋ID
this.eventEmitter.emit('houseUserRole', { houseId });
}
private handleUserLeaveHouse(message: MqttMessage): void {
// 触发事件,由页面处理刷新逻辑
this.eventEmitter.emit('userLeaveHouse');
}
private handleResetDevice(message: MqttMessage): void {
const { serialNo, opTime } = message.data;
if (!serialNo) return;
this.eventEmitter.emit('deviceReset', {
serialNo,
opTime
});
Array.from(this.deviceStatuses.keys()).forEach(deviceCode => {
this.deviceStatuses.delete(deviceCode);
});
this.saveDeviceStatuses();
}
private handleFamilyUserOnline(message: MqttMessage): void {
const { userId } = message.data;
if (!userId) return;
this.eventEmitter.emit('familyUserOnline', { userId });
}
private handleUserDoorStatus(message: MqttMessage): void {
const { deviceCode, online, lockStatus } = message.data;
if (!deviceCode) return;
this.updateDeviceStatus(deviceCode, {
online: online ?? true,
lockStatus
});
this.eventEmitter.emit('doorLockStatusChanged', {
deviceCode,
online,
lockStatus
});
}
private handleServerOffline(message: MqttMessage): void {
const { userId, platform } = message.data;
if (!userId || !platform) return;
this.eventEmitter.emit('serverOffline', { userId, platform });
}
private handleUserOnlineStatus(message: MqttMessage): void {
const { deviceCode, online } = message.data;
if (!deviceCode || online === undefined) return;
this.updateDeviceStatus(deviceCode, { online });
}
private handleDoorStatus(message: MqttMessage): void {
const { deviceCode, online, lockStatus } = message.data;
if (!deviceCode) return;
this.updateDeviceStatus(deviceCode, {
online,
lockStatus
});
if (online) {
this.publishDoorOnlineStatus(deviceCode);
}
}
private handleDoorLockStatus(message: MqttMessage): void {
const { deviceCode, lockStatus } = message.data;
console.log('[MQTT][HandleDoorLockStatus] 收到门锁状态更新 - 设备码:', deviceCode, '状态:', lockStatus);
if (!deviceCode || lockStatus === undefined) {
console.warn('[MQTT][HandleDoorLockStatus] 收到无效的门锁状态消息');
return;
}
// 更新设备状态并触发事件
this.updateDeviceStatus(deviceCode, { lockStatus });
console.log('[MQTT][HandleDoorLockStatus] 更新设备状态并触发事件');
this.eventEmitter.emit('doorLockStatusChanged', {
deviceCode,
lockStatus
});
}
private handleAddKeySuccess(message: MqttMessage): void {
this.eventEmitter.emit('addKeySuccess', message);
}
private handleOpenDoorResult(message: MqttMessage): void {
const { deviceCode, success, errorCode, errorMsg } = message.data;
console.log('[MQTT][HandleOpenDoorResult] 收到开门结果 - 设备码:', deviceCode,
'成功:', success,
'错误码:', errorCode || 'N/A',
'错误信息:', errorMsg || 'N/A');
if (!deviceCode || success === undefined) {
console.warn('[MQTT][HandleOpenDoorResult] 收到无效的开门结果消息');
return;
}
this.eventEmitter.emit('openDoorResult', {
deviceCode,
success,
errorCode,
errorMsg
});
if (!success) {
// 只有在开门失败时才立即更新状态
console.log('[MQTT][HandleOpenDoorResult] 开门失败,更新锁状态为已上锁');
this.updateDeviceStatus(deviceCode, { lockStatus: 2 });
this.eventEmitter.emit('openDoorFailed', {
deviceCode,
errorCode,
errorMsg
});
}
}
private handleDeleteKeyResult(message: MqttMessage): void {
const { deviceCode, success, errorCode, errorMsg } = message.data;
if (!deviceCode || success === undefined) return;
this.eventEmitter.emit('deleteKeyResult', {
deviceCode,
success,
errorCode,
errorMsg
});
}
private handleAddKeyResult(message: MqttMessage): void {
const { deviceCode, success, errorCode, errorMsg } = message.data;
if (!deviceCode || success === undefined) return;
this.eventEmitter.emit('addKeyResult', {
deviceCode,
success,
errorCode,
errorMsg
});
}
private handleWallpaperResult(message: MqttMessage): void {
const { deviceCode, success, errorCode, errorMsg } = message.data;
if (!deviceCode || success === undefined) return;
this.eventEmitter.emit('wallpaperResult', {
deviceCode,
success,
errorCode,
errorMsg
});
}
private handleError(error: Error): void {
this.eventEmitter.emit('error', error);
}
private handleOffline(): void {
this.eventEmitter.emit('offline');
}
private updateDeviceStatus(deviceCode: string, updates: Partial<DeviceStatus>): void {
const currentStatus = this.deviceStatuses.get(deviceCode) || {
deviceCode,
online: false,
lastUpdateTime: Date.now()
};
const newStatus = {
...currentStatus,
...updates,
lastUpdateTime: Date.now()
};
this.deviceStatuses.set(deviceCode, newStatus);
this.saveDeviceStatuses();
this.eventEmitter.emit('deviceStatusChanged', newStatus);
if (updates.online !== undefined) {
this.eventEmitter.emit('deviceOnlineStatusChanged', {
deviceCode,
online: updates.online
});
}
}
// 公共方法
public getDeviceStatus(deviceCode: string): DeviceStatus | null {
return this.deviceStatuses.get(deviceCode) || null;
}
public getLockStatusText(status?: number): string {
switch (status) {
case 0: return '开锁中';
case 1: return '已开门';
case 2: return '已上锁';
default: return '未知状态';
}
}
public updateDeviceCode(deviceCode: string): void {
this.config.deviceCode = deviceCode;
}
public on(event: string, listener: Function): void {
this.eventEmitter.on(event, listener);
}
public off(event: string, listener: Function): void {
this.eventEmitter.off(event, listener);
}
public removeAllListeners(): void {
if (this.eventEmitter.removeAllListeners) {
this.eventEmitter.removeAllListeners();
}
}
// 修改消息发布方法,添加连接状态检查和消息队列
private publishMessage(topic: string, type: string, data: any): void {
console.log('[MQTT][PublishMessage] 准备发布消息 - 主题:', topic, '类型:', type, '数据:', JSON.stringify(data));
if (!this.isConnected()) {
console.log('[MQTT][PublishMessage] MQTT未连接,将消息加入队列');
// 将消息添加到队列
this.messageQueue.push({ topic, type, data });
// 如果没有连接,尝试重新连接
if (!this.isConnecting && !this._connectingPromise) {
console.log('[MQTT][PublishMessage] 尝试重新连接');
this.showReconnectModal();
this.connect().then(() => {
this.hideReconnectModal();
console.log('[MQTT][PublishMessage] 重连成功,处理队列消息');
this.processQueuedMessages();
}).catch(error => {
console.error('[MQTT][PublishMessage] Failed to reconnect:', error);
this.hideReconnectModal();
// 显示发送失败提示
wx.showToast({
title: '消息发送失败,请重试',
icon: 'none',
duration: 2000
});
});
}
return;
}
if (!this.client) {
console.error('[MQTT][PublishMessage] 客户端为空,无法发送消息');
throw new Error('Cannot publish - client is null');
}
try {
const message: MqttMessage = {
id: Math.random().toString(16).substr(2, 16),
code: 0,
msg: "success",
type,
ts: Date.now(),
expired: Date.now() + 86400000,
data
};
console.log('[MQTT][PublishMessage] 发送消息ID:', message.id);
const messageString = JSON.stringify(message);
const encryptedMessage = Encrypt(messageString);
this.client.publish(topic, encryptedMessage);
console.log('[MQTT][PublishMessage] 消息发送成功 - 主题:', topic);
} catch (error) {
console.error('[MQTT][PublishMessage] Failed to publish:', error);
throw error;
}
}
public publishUserOnline(): void {
const message = {
userId: this.config.userId,
platform: "wechat"
};
this.publishMessage(`mqdev-to-${this.config.familyId}`, 'family/user_online', message);
}
// 修改后的 publishServerOffline 方法
public publishServerOffline(): void {
try {
// 检查客户端连接状态
if (!this.client || !this.client.connected) {
return;
}
const message = {
userId: this.config.userId,
platform: "wechat"
};
// 直接使用 MQTT 客户端发送消息,确保消息能立即发送
try {
const mqttMessage: MqttMessage = {
id: Math.random().toString(16).substr(2, 16),
code: 0,
msg: "success",
type: "server/offline",
ts: Date.now(),
expired: Date.now() + 86400000,
data: message
};
const messageString = JSON.stringify(mqttMessage);
const encryptedMessage = Encrypt(messageString);
// 修改:使用新的 topic
const topic = "mqsvr-to-iot-door-user";
// 修正:只传递两个参数 - 主题和消息内容
this.client.publish(topic, encryptedMessage);
} catch (error) {
console.error('[MQTT][PublishServerOffline] 直接发送消息失败:', error);
// 如果直接发送失败,尝试使用队列方式发送(也使用新的 topic)
this.publishMessage("mqsvr-to-iot-door-user", 'server/offline', message);
}
} catch (error) {
console.error('[MQTT][PublishServerOffline] 发送离线消息失败:', error);
}
}
// 修改后的 handleLogout 方法
public handleLogout(): void {
try {
// 先发送离线消息,确保消息发送
if (this.client && this.client.connected) {
try {
// 直接使用 MQTT 客户端发送消息
const message = {
userId: this.config.userId,
platform: "wechat",
online: false
};
const mqttMessage: MqttMessage = {
id: Math.random().toString(16).substr(2, 16),
code: 0,
msg: "success",
type: "server/offline",
ts: Date.now(),
expired: Date.now() + 86400000,
data: message
};
const messageString = JSON.stringify(mqttMessage);
const encryptedMessage = Encrypt(messageString);
// 修改:使用新的 topic
const topic = "mqsvr-to-iot-door-user";
// 修正:只传递两个参数 - 主题和消息内容
this.client.publish(topic, encryptedMessage);
} catch (error) {
}
// 延迟一点时间确保消息能发送出去
setTimeout(() => {
this.cleanup();
globalClient = null;
}, 300);
} else {
this.cleanup();
globalClient = null;
}
// 清理设备状态和消息队列
this.deviceStatuses.clear();
this.saveDeviceStatuses();
this.messageQueue = [];
} catch (error) {
this.cleanup();
globalClient = null;
}
}
public publishDoorOnlineStatus(deviceCode: string): void {
const message = {
deviceCode: deviceCode,
online: true
};
this.publishMessage(`mqdev-to-${deviceCode}`, 'door/online_status', message);
}
// 修改开门方法,添加连接状态检查
public publishOpenDoor(deviceCode: string): void {
console.log('[MQTT][PublishOpenDoor] 准备发送开门指令 - 设备码:', deviceCode);
if (!this.isConnected()) {
console.log('[MQTT][PublishOpenDoor] MQTT未连接,取消操作');
wx.showToast({
title: '正在连接,请稍后重试',
icon: 'none',
duration: 2000
});
return;
}
const message = {
userId: this.config.userId
};
console.log('[MQTT][PublishOpenDoor] 发送开门消息 - 用户ID:', this.config.userId);
this.publishMessage(`mqdev-to-${deviceCode}`, 'door/open_door', message);
}
public publishAddKey(deviceCode: string, keyType: string, password?: string, hijack: string = 'false'): void {
const data: MqttMessage['data'] = {
userId: this.config.userId,
keyType,
hijack
};
if (password) {
data.password = password;
}
this.publishMessage(`mqdev-to-${deviceCode}`, 'door/start_add_key', data);
}
public publishDeleteKey(deviceCode: string, key: any): void {
this.publishMessage(`mqdev-to-${deviceCode}`, 'door/delete_key', key);
}
public publishResetDevice(deviceCode: string): void {
const message = {
deviceCode,
userId: this.config.userId
};
this.publishMessage(`mqdev-to-${deviceCode}`, 'door/reset_device', message);
}
public publishAddHouseUser(deviceCode: string): void {
this.publishMessage(`mqdev-to-${deviceCode}`, 'door/house_user_add', {});
}
public publishUpdateWallpaper(deviceCode: string, imgUrl: string): void {
const message = {
imgUrl
};
this.publishMessage(`mqdev-to-${deviceCode}`, 'door/wallpaper', message);
}
}
export default SmartDoorMQTTClient;
这个代码片段格式没有人能看得懂呀