import dxCommonUtils from "../../dxmodules/dxCommonUtils.js"; import dxos from "../../dxmodules/dxOs.js"; import config from "../../dxmodules/dxConfig.js"; import logger from "../../dxmodules/dxLogger.js"; import ota from "../../dxmodules/dxOta.js"; import std from "../../dxmodules/dxStd.js"; import driver from "../driver.js"; import configService from "./configService.js"; import sqliteService from "./sqliteService.js"; import faceService from "./faceService.js"; import dxMap from '../../dxmodules/dxMap.js' import bus from "../../dxmodules/dxEventBus.js"; import api from './api.js' import weComService from "./weComService.js"; const mqttService = {} mqttService.receiveMsg = function (data) { // {"topic":"ddddd","payload":"{\n \"msg\": \"world\"\n}"} logger.info('[mqttService] receiveMsg :' + JSON.stringify(data.topic)) if (typeof mqttService[data.topic.match(/[^/]+$/)[0]] == 'function') { let payload = parsePayloadSafely(data) if (typeof payload == 'string') { return reply(data, payload, CODE.E_100) } mqttService[data.topic.match(/[^/]+$/)[0]](data) } else { logger.error("未实现的topic", data.topic) } } function parsePayloadSafely(event) { try { return JSON.parse(event.payload) } catch (error) { return "Invalid JSON format​​" } } // =================================权限增删改查================================= /** * 添加权限 */ mqttService.insertPermission = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data if (data.length > 100) { return reply(event, "data length should not be greater than 100", CODE.E_100) } let res = api.insertPermission(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 查询权限 */ mqttService.getPermission = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.getPermission(data) return reply(event, res) } /** * 删除权限 */ mqttService.delPermission = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.delPermission(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 修改权限 */ mqttService.modifyPermission = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data if (data.length > 100) { return reply(event, "data length should not be greater than 100", CODE.E_100) } let res = api.modifyPermission(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 清空权限 */ mqttService.clearPermission = function (event) { let ret = api.clearPermission() if (typeof ret == "string") { return reply(event, "sql error ret:" + ret, CODE.E_100) } else { return reply(event) } } // =================================人员增删改查================================= /** * 添加人员 */ mqttService.insertUser = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data if (data.length > 100) { return reply(event, "data length should not be greater than 100", CODE.E_100) } let res = api.insertUser(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 查询人员 */ mqttService.getUser = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.getUser(data) return reply(event, res) } /** * 删除人员 */ mqttService.delUser = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.delUser(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 清空人员 */ mqttService.clearUser = function (event) { let ret = api.clearUser() if (typeof ret == "string") { return reply(event, "sql error ret:" + ret, CODE.E_100) } else { return reply(event) } } /** * 修改人员 */ mqttService.modifyUser = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data if (data.length > 100) { return reply(event, "data length should not be greater than 100", CODE.E_100) } let res = api.modifyUser(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } // =================================凭证增删改查================================= /** * 添加凭证 */ mqttService.insertKey = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data if (data.length > 100) { return reply(event, "data length should not be greater than 100", CODE.E_100) } let res = api.insertKey(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 查询凭证 */ mqttService.getKey = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.getKey(data) if (typeof res == 'string') { return reply(event, res, CODE.E_100) } return reply(event, res) } /** * 删除凭证 */ mqttService.delKey = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.delKey(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 清空凭证 */ mqttService.clearKey = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let ret = api.clearKey(data) if (typeof ret == "string") { return reply(event, "sql error ret:" + ret, CODE.E_100) } else { return reply(event) } } /** * 修改凭证 */ mqttService.modifyKey = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data if (data.length > 100) { return reply(event, "data length should not be greater than 100", CODE.E_100) } let res = api.modifyKey(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } // =================================应急开仓密码增删改查================================= /** * 添加应急开仓密码 * @param {object} event - MQTT事件对象 */ mqttService.insertEmergencyPassword = function (event) { try { logger.info('[mqttService] 接收到insertEmergencyPassword命令:', JSON.stringify(event.topic)) logger.info('[mqttService] 命令payload:', event.payload) let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : {} logger.info('[mqttService] 解析后的参数:', JSON.stringify(data)) let res = this.insertEmergencyPasswordAgreement(data) if (typeof res == 'string') { logger.error('[mqttService] insertEmergencyPassword失败:', res) return reply(event, res, CODE.E_100) } logger.info('[mqttService] insertEmergencyPassword成功') return reply(event) } catch (error) { logger.error('[mqttService] insertEmergencyPassword error:', error) return reply(event, { error: error.message }, CODE.E_100) } } /** * 添加应急开仓密码通用协议格式 * @param {object} data - 应急开仓密码数据对象 * @returns {boolean|string} true表示成功,string表示错误信息 */ mqttService.insertEmergencyPasswordAgreement = function (data) { // 应急开仓密码在设备中仅有唯一的1个,所以先清空表 let deleteRet = sqliteService.d1_emergency_password.deleteAll() if (deleteRet != 0) { return "清空旧密码失败: " + deleteRet } // 检查密码是否有效 if (!data.password) { return "password cannot be empty" } // 检查密码长度是否大于等于8位 if (data.password.length < 8) { return "Password length must be at least 8 digits" } // 构建密码记录 let record = {} record.id = data.id || 'emergency_' + Date.now() // 如果没有id,自动生成 record.password = data.password record.description = "云端下发" // 通过MQTT和HTTP接口设置的应急开仓密码,description填充为"云端下发" record.createTime = Date.now() record.updateTime = Date.now() record.status = data.status || 1 // 保存密码 let ret = sqliteService.d1_emergency_password.save(record) if (ret == 0) { return true } else { return "sql error ret:" + ret } } /** * 查询应急开仓密码 * @param {object} event - MQTT事件对象 */ mqttService.getEmergencyPassword = function (event) { try { logger.info('[mqttService] 接收到getEmergencyPassword命令:', JSON.stringify(event.topic)) logger.info('[mqttService] 命令payload:', event.payload) let payload = JSON.parse(event.payload) let res = this.getEmergencyPasswordAgreement() logger.info('[mqttService] 查询结果:', JSON.stringify(res)) return reply(event, res) } catch (error) { logger.error('[mqttService] getEmergencyPassword error:', error) return reply(event, { error: error.message }, CODE.E_100) } } /** * 查询应急开仓密码通用协议格式 * @returns {object} 查询结果 */ mqttService.getEmergencyPasswordAgreement = function () { let passwords = sqliteService.d1_emergency_password.findAll() if (passwords && passwords.length > 0) { let password = passwords[0]; // 转换时间戳为字符串格式 if (password.createTime) { // 尝试将createTime转换为数字 const createTimeNum = Number(password.createTime); if (!isNaN(createTimeNum)) { // 检查时间戳是否为毫秒级(大于等于10位) if (createTimeNum.toString().length >= 10) { // 已经是毫秒级,转换为秒级 password.createTime = timestampToDateString(createTimeNum / 1000); } else { // 秒级,直接使用 password.createTime = timestampToDateString(createTimeNum); } } } if (password.updateTime) { // 尝试将updateTime转换为数字 const updateTimeNum = Number(password.updateTime); if (!isNaN(updateTimeNum)) { // 检查时间戳是否为毫秒级(大于等于10位) if (updateTimeNum.toString().length >= 10) { // 已经是毫秒级,转换为秒级 password.updateTime = timestampToDateString(updateTimeNum / 1000); } else { // 秒级,直接使用 password.updateTime = timestampToDateString(updateTimeNum); } } } return password; } return {}; } /** * 清空应急开仓密码 * @param {object} event - MQTT事件对象 */ mqttService.clearEmergencyPassword = function (event) { try { logger.info('[mqttService] 接收到clearEmergencyPassword命令:', JSON.stringify(event.topic)) logger.info('[mqttService] 命令payload:', event.payload) let ret = sqliteService.d1_emergency_password.deleteAll() if (ret == 0) { logger.info('[mqttService] clearEmergencyPassword成功') return reply(event) } else { logger.error('[mqttService] clearEmergencyPassword失败:', "sql error ret:" + ret) return reply(event, "sql error ret:" + ret, CODE.E_100) } } catch (error) { logger.error('[mqttService] clearEmergencyPassword error:', error) return reply(event, { error: error.message }, CODE.E_100) } } // =================================密钥增删改查================================= /** * 添加密钥 */ mqttService.insertSecurity = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data if (data.length > 100) { return reply(event, "data length should not be greater than 100", CODE.E_100) } let res = api.insertSecurity(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 查询密钥 */ mqttService.getSecurity = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.getSecurity(data) if (typeof res == 'string') { return reply(event, res, CODE.E_100) } return reply(event, res) } /** * 删除密钥 */ mqttService.delSecurity = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.delSecurity(data) if (Array.isArray(res)) { return reply(event, res, CODE.E_100) } return reply(event) } /** * 清空密钥 */ mqttService.clearSecurity = function (event) { let ret = api.clearSecurity() if (typeof ret == "string") { return reply(event, "sql error ret:" + ret, CODE.E_100) } else { return reply(event) } } // =================================应急开仓密码增删改查================================= /** * 添加应急开仓密码 * @param {object} event - MQTT事件对象 */ mqttService.insertEmergencyPassword = function (event) { try { logger.info('[mqttService] 接收到insertEmergencyPassword命令:', JSON.stringify(event.topic)) logger.info('[mqttService] 命令payload:', event.payload) let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : {} logger.info('[mqttService] 解析后的参数:', JSON.stringify(data)) let res = this.insertEmergencyPasswordAgreement(data) if (typeof res == 'string') { logger.error('[mqttService] insertEmergencyPassword失败:', res) return reply(event, res, CODE.E_100) } logger.info('[mqttService] insertEmergencyPassword成功') return reply(event) } catch (error) { logger.error('[mqttService] insertEmergencyPassword error:', error) return reply(event, { error: error.message }, CODE.E_100) } } /** * 添加应急开仓密码通用协议格式 * @param {object} data - 应急开仓密码数据对象 * @returns {boolean|string} true表示成功,string表示错误信息 */ mqttService.insertEmergencyPasswordAgreement = function (data) { // 应急开仓密码在设备中仅有唯一的1个,所以先清空表 let deleteRet = sqliteService.d1_emergency_password.deleteAll() if (deleteRet != 0) { return "清空旧密码失败: " + deleteRet } // 检查密码是否有效 if (!data.password) { return "password cannot be empty" } // 检查密码长度是否大于等于8位 if (data.password.length < 8) { return "Password length must be at least 8 digits" } // 构建密码记录 let record = {} record.id = data.id || 'emergency_' + Date.now() // 如果没有id,自动生成 record.password = data.password record.description = "云端下发" // 通过MQTT和HTTP接口设置的应急开仓密码,description填充为"云端下发" record.createTime = Date.now() record.updateTime = Date.now() record.status = data.status || 1 // 保存密码 let ret = sqliteService.d1_emergency_password.save(record) if (ret == 0) { return true } else { return "sql error ret:" + ret } } /** * 查询应急开仓密码 * @param {object} event - MQTT事件对象 */ mqttService.getEmergencyPassword = function (event) { try { logger.info('[mqttService] 接收到getEmergencyPassword命令:', JSON.stringify(event.topic)) logger.info('[mqttService] 命令payload:', event.payload) let payload = JSON.parse(event.payload) let res = this.getEmergencyPasswordAgreement() logger.info('[mqttService] 查询结果:', JSON.stringify(res)) return reply(event, res) } catch (error) { logger.error('[mqttService] getEmergencyPassword error:', error) return reply(event, { error: error.message }, CODE.E_100) } } /** * 查询应急开仓密码通用协议格式 * @returns {object} 查询结果 */ mqttService.getEmergencyPasswordAgreement = function () { let passwords = sqliteService.d1_emergency_password.findAll() if (passwords && passwords.length > 0) { let password = passwords[0]; // 转换时间戳为字符串格式 if (password.createTime) { // 尝试将createTime转换为数字 const createTimeNum = Number(password.createTime); if (!isNaN(createTimeNum)) { // 检查时间戳是否为毫秒级(大于等于10位) if (createTimeNum.toString().length >= 10) { // 已经是毫秒级,转换为秒级 password.createTime = timestampToDateString(createTimeNum / 1000); } else { // 秒级,直接使用 password.createTime = timestampToDateString(createTimeNum); } } } if (password.updateTime) { // 尝试将updateTime转换为数字 const updateTimeNum = Number(password.updateTime); if (!isNaN(updateTimeNum)) { // 检查时间戳是否为毫秒级(大于等于10位) if (updateTimeNum.toString().length >= 10) { // 已经是毫秒级,转换为秒级 password.updateTime = timestampToDateString(updateTimeNum / 1000); } else { // 秒级,直接使用 password.updateTime = timestampToDateString(updateTimeNum); } } } return password; } return {}; } /** * 清空应急开仓密码 * @param {object} event - MQTT事件对象 */ mqttService.clearEmergencyPassword = function (event) { try { logger.info('[mqttService] 接收到clearEmergencyPassword命令:', JSON.stringify(event.topic)) logger.info('[mqttService] 命令payload:', event.payload) let ret = sqliteService.d1_emergency_password.deleteAll() if (ret == 0) { logger.info('[mqttService] clearEmergencyPassword成功') return reply(event) } else { logger.error('[mqttService] clearEmergencyPassword失败:', "sql error ret:" + ret) return reply(event, "sql error ret:" + ret, CODE.E_100) } } catch (error) { logger.error('[mqttService] clearEmergencyPassword error:', error) return reply(event, { error: error.message }, CODE.E_100) } } /** * 远程控制 * @param {object} event - MQTT事件对象 */ mqttService.control = function (event) { try { logger.info('[mqttService] 接收到control命令:', JSON.stringify(event.topic)) logger.info('[mqttService] 命令payload:', event.payload) let payload = JSON.parse(event.payload) let data = payload.payload.data || {} switch (data.command) { case 0: //重启 logger.info('[mqttService] 执行重启命令') reply(event) common.asyncReboot(2) return case 1: //远程开门 logger.info('[mqttService] 执行远程开门命令') driver.gpio.open() break case 4: //重置 logger.info('[mqttService] 执行重置命令') common.systemBrief("rm -rf /app/data/config/*") common.systemBrief("rm -rf /app/data/db/*") common.systemBrief("rm -rf /app/data/user/*") common.systemBrief("rm -rf /app/data/user/*") common.systemBrief("rm -rf /vgmj.db") reply(event) common.asyncReboot(2) return case 5: //播放语音 logger.info('[mqttService] 执行播放语音命令') if (data.extra) { let res = common.systemWithRes(`test -e "/app/code/resource/wav/${data.extra.wav}.wav" && echo "OK" || echo "NO"`, 2) if (res.includes('OK')) { driver.alsa.play(`/app/code/resource/wav/${data.extra.wav}.wav`) } } break case 6: // 6:屏幕展示图片 // TODO logger.info('[mqttService] 执行屏幕展示图片命令') break case 7: // 7:屏幕展示文字 // TODO logger.info('[mqttService] 执行屏幕展示文字命令') break case 10: logger.info('[mqttService] 执行二维码展示命令') if (!isEmpty(data.extra.qrCodeBase64) && typeof data.extra.qrCodeBase64 == 'string') { //base64转图片保存 let src = `/app/code/resource/image/app_qrcode.png` std.ensurePathExists(src) common.base64_2binfile(src, data.extra.qrCodeBase64) logger.info('[mqttService] 二维码保存成功') return reply(event) } break default: logger.info('[mqttService] 未知命令:', data.command) break } logger.info('[mqttService] control命令执行完成') return reply(event) } catch (error) { logger.error('[mqttService] control error:', error) return reply(event, { error: error.message }, CODE.E_100) } } /** * 查询配置 */ mqttService.getConfig = function (event) { let payload = JSON.parse(event.payload) let data = payload.data let res = api.getConfig(data) if (typeof res == "string") { return reply(event, res, CODE.E_100) } else { return reply(event, res) } } /** * 修改配置 */ mqttService.setConfig = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.setConfig(data) if (typeof res == "string") { return reply(event, res, CODE.E_100) } else { return reply(event) } } /** * 升级固件 */ mqttService.upgradeFirmware = function (event) { logger.info('[mqttService] 接收到升级固件命令:', JSON.stringify(event.topic)) let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.upgradeFirmware(data) if (typeof res == "string") { return reply(event, res, CODE.E_100) } else { return reply(event) } } /** * 查询识别记录 */ mqttService.getRecords = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.getRecords(data, true) return reply(event, res) } /** * 删除记录 */ mqttService.delRecords = function (event) { let payload = JSON.parse(event.payload) let data = payload.payload && payload.payload.data ? payload.payload.data : payload.data let res = api.delRecords(data) if (typeof res == 'string') { return reply(event, res, CODE.E_100) } return reply(event) } /** * 通行上报回复 */ mqttService.access_reply = function (event) { let payload = JSON.parse(event.payload) if (payload.serialNo && payload.code == '000000') { let record = sqliteService.d1_pass_record.findAllById(payload.serialNo) if (record.length > 0) { dxos.systemBrief(`rm -rf ${record[0].code}`) } sqliteService.d1_pass_record.deleteByid(payload.serialNo) } } /** * 在线验证回复 */ mqttService.access_online_reply = function (data) { driver.sync.response("mqtt.getOnlinecheck", JSON.parse(data.payload)) } // 设备绑定二维码回复 mqttService.wecom_reply = function (data) { let payload = JSON.parse(data.payload) data = payload.data bus.fire("showQrCode", data) } const CODE = { // 成功 S_000: "000000", // 未知错误 E_100: "100000", // 设备已被禁用 E_101: "100001", // 设备正忙,请稍后再试 E_102: "100002", // 签名检验失败 E_103: "100003", // 超时错误 E_104: "100004", // 设备离线 E_105: "100005", } mqttService.CODE = CODE mqttService.report = function () { // 在线上报 let payloadReply = mqttReply(std.genRandomStr(10), { appVersion: config.get("sys.version") || '', mac: config.get("sys.mac") || '', clientId: dxMap.get("CLIENT").get("CLIENT_ID") || '', name: config.get("sys.deviceName") || '', type: config.get("net.type") || 1, ssid: config.get("net.ssid") || '', psk: config.get("net.psk") || '', dhcp: config.get("net.dhcp") || 2, ip: config.get("net.ip") || '', gateway: config.get("net.gateway") || '', dns: config.get("net.dns") || '', subnetMask: config.get("net.mask") || '', netMac: config.get("net.mac") || '' }, CODE.S_000) driver.mqtt.send("access_device/v2/event/connect", JSON.stringify(payloadReply)) // 企微绑定状态获取 if (weComService.isWeCom()) { driver.mqtt.send("access_device/v2/event/wecom", JSON.stringify(mqttReply(std.genRandomStr(10), { type: 0 }, CODE.S_000))) } } // mqtt请求统一回复 function reply(event, data, code) { let topic = getReplyTopic(event) let reply = JSON.stringify(mqttReply(JSON.parse(event.payload).serialNo, data, isEmpty(code) ? CODE.S_000 : code)) driver.mqtt.send(topic, reply) } /** * 获取回复主题 */ function getReplyTopic(data) { return data.topic.replace("/" + config.get("sys.sn"), '') + "_reply"; } // mqtt回复格式构建 function mqttReply(serialNo, data, code) { return { serialNo: serialNo, uuid: config.get("sys.uuid"), sign: '', code: code, data: data, time: Math.floor(Date.parse(new Date()) / 1000) } } mqttService.mqttReply = mqttReply /** * 时间戳转日期字符串 * @param {number} timestamp - 时间戳(秒级) * @returns {string} 日期字符串,格式:YYYY-MM-DD HH:MM:SS */ function timestampToDateString(timestamp) { const date = new Date(timestamp * 1000); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } mqttService.timestampToDateString = timestampToDateString // 判空 function isEmpty(value) { return value === undefined || value === null || value === "" } export default mqttService