UNPKG

node-red-contrib-symi

Version:

Node-RED nodes for smart home device communication with Home Assistant integration, supporting HassKit, Clowire, Alive, LF and complete Symi protocols

277 lines (242 loc) 9.2 kB
module.exports = function (RED) { const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const tcpPort = require('./lib/tcp') const SerialConnection = require('./lib/serial') const handleTcp = require('./lib/handleTcp') const { ClimateObjToSerivce, SwitchObjToSerivce, PanelObjToSerivce, LightObjToSerivce, CurtainObjToSerivce, VentilationObjToSerivce, SceneObjToSerivce } = require('./lib/toHaService') let globalContext = null const nodeStatus = { yellow: { fill: "yellow", shape: "ring", text: "node-red:common.status.connecting" }, red: { fill: "red", shape: "ring", text: "node-red:common.status.disconnected" }, green: { fill: "green", shape: "dot", text: "node-red:common.status.connected" } } function symiConfig(n) { RED.nodes.createNode(this, n); /** * Symi设备连接配置 */ globalContext = this.context().global this.connectionType = n.connectionType || 'tcp' this.host = n.host || '127.0.0.1' this.port = n.port || 8666 this.serialPort = n.serialPort || '/dev/ttyUSB0' this.baudRate = n.baudRate || 9600 this.dataBits = n.dataBits || 8 this.stopBits = n.stopBits || 1 this.parity = n.parity || 'none' this.nodes = [] //设备实例 this.handleTcp = { 'hasskit': null, 'clowire': null, 'alive': null, 'lf': null, 'symi': null, getObjToService: function(domain){ switch(domain) { case 'climate': return ClimateObjToSerivce case 'panel': return PanelObjToSerivce case 'light': return LightObjToSerivce case 'hvac': return ClimateObjToSerivce case 'curtain': return CurtainObjToSerivce case 'heating': return ClimateObjToSerivce case 'ventilation': return VentilationObjToSerivce case 'scene': return SceneObjToSerivce default: return SwitchObjToSerivce } } } //处理通信对象集合 if (this.connectionType === 'serial') { this.serialClient = new SerialConnection({ path: this.serialPort, baudRate: this.baudRate, dataBits: this.dataBits, stopBits: this.stopBits, parity: this.parity }) this.communicationClient = this.serialClient } else { this.tcpClient = new tcpPort(this.host, this.port, this) this.communicationClient = this.tcpClient } this.on('error', function (e) { console.log(e) }) this.repeatSend = 0 let self = this this.on('close', function (done) { self.nodes = [] clearInterval(self.repeatSend) if (self.connectionType === 'serial') { self.serialClient.disconnect().then(() => done()) } else { self.tcpClient.close(function () { done() }) } }); // 设置连接事件处理 if (this.connectionType === 'serial') { this.serialClient.on('disconnect', function () { self.nodes.forEach(it => { it.status(nodeStatus.red); }) }) this.serialClient.on('connect', function () { self.nodes.forEach(it => { it.status(nodeStatus.green); }) console.log("串口连接成功") }) this.serialClient.on('error', function () { console.log("串口连接错误") self.nodes.forEach(it => { it.status(nodeStatus.red); }) }) } else { this.tcpClient.onClose = function () { self.nodes.forEach(it => { it.status(nodeStatus.red); }) } this.tcpClient.onConnect = function () { self.nodes.forEach(it => { it.status(nodeStatus.green); }) console.log("TCP连接成功") } this.tcpClient.onError = function () { console.log("TCP连接错误") self.nodes.forEach(it => { it.status(nodeStatus.red); }) } } this.register = function (cp) { if (!this.handleTcp[cp.devtype]) { this.handleTcp[cp.devtype] = new handleTcp(this.communicationClient, cp.devtype) this.handleTcp[cp.devtype].onData = function (data) { self.nodes.forEach(it => { if (it.devtype === cp.devtype) it.ondata(data) }) } } if (this.nodes.indexOf(cp) == -1) { cp.status(nodeStatus.yellow); this.nodes.push(cp) } } // 启动连接 if (this.connectionType === 'serial') { this.serialClient.connect().catch(err => { console.error('串口连接失败:', err) }) } else { this.tcpClient.connect() } } RED.nodes.registerType("symi config", symiConfig); function ceckInputData(msg, domain) { if (typeof msg.payload !== 'object') return if (msg.payload.event_type !== 'state_changed') return if (!msg.payload.event.new_state) return const curDomain = msg.payload.entity_id.split('.')[0] if(curDomain === 'light') return true if (domain === curDomain ) return true } function boardInit(n, domain){ RED.nodes.createNode(this, n); this.server = RED.nodes.getNode(n.server); this.uid = parseInt(n.uid) this.address = parseInt(n.address) || 0 this.channel = parseInt(n.channel) || 0 this.devtype = n.devtype this.entity_id = n.entity_id let node = this if (this.server) { this.server.register(this) const buf2obj = node.server.handleTcp[n.devtype].getBufToObj(domain) const obj2buf = node.server.handleTcp[n.devtype].getObjToBuf(domain) const ObjToService = node.server.handleTcp.getObjToService(domain) //收到消息 this.ondata = function (data) { let obj = buf2obj(data, node) if (!obj) return console.log(obj) obj = ObjToService(obj)//转成HA服务数据 if (!obj) return node.send({ call_service: obj.call_service, payload: obj.data }) } node.on('input', function (msg) { if (!ceckInputData(msg, domain)) return let buf = obj2buf(msg.payload.event.new_state, node) if (buf) { if(Array.isArray(buf[0])) for (let i of buf) node.server.handleTcp[n.devtype].write_lock(i) else node.server.handleTcp[n.devtype].write_lock(buf) } }) } else { node.status(nodeStatus.red); } } function climateBoard(n) { boardInit.call(this, n, 'climate') } RED.nodes.registerType("climate board", climateBoard); function switchBoard(n) { boardInit.call(this, n, 'switch') } RED.nodes.registerType("switch board", switchBoard); function lightBoard(n) { boardInit.call(this, n, 'light') } RED.nodes.registerType("light board", lightBoard); function hvacBoard(n) { boardInit.call(this, n, 'hvac') } RED.nodes.registerType("hvac board", hvacBoard); function curtainBoard(n) { boardInit.call(this, n, 'curtain') } RED.nodes.registerType("curtain board", curtainBoard); function heatingBoard(n) { boardInit.call(this, n, 'heating') } RED.nodes.registerType("heating board", heatingBoard); function ventilationBoard(n) { boardInit.call(this, n, 'ventilation') } RED.nodes.registerType("ventilation board", ventilationBoard); function sceneBoard(n) { boardInit.call(this, n, 'scene') } RED.nodes.registerType("scene board", sceneBoard); function panelBoard(n) { boardInit.call(this, n, 'panel') } RED.nodes.registerType("panel board", panelBoard); RED.httpAdmin.get("/GetHaDevList", function (req, res) { res.json({ data: globalContext.get('homeassistant') }) }); }