UNPKG

node-red-contrib-netpie

Version:

Node-RED module for connecting to NETPIE IoT Platform

265 lines (233 loc) 10.5 kB
module.exports = function(RED) { const axios = require('axios'); const CONFIG = require('./config'); function Message(config) { RED.nodes.createNode(this, config); let node = this; let current = {}; let getcounter = {}; let topiclist = config.topics.trim().split('\n'); // Store output type setting node.outputType = config.outputType || "String"; let eventlist = {}; function addListener(obj, event, callback) { if (eventlist[event]) return; eventlist[event] = 1; obj.on(event, callback) } // Get device config node const deviceConfigNode = RED.nodes.getNode(config.deviceconfig); if (!deviceConfigNode) { node.error("Device configuration node not found."); return; } config.deviceid = deviceConfigNode.deviceid; config.devicetoken = deviceConfigNode.devicetoken; if (!config.deviceid || !config.devicetoken) { node.error("deviceid and devicetoken are required."); return; } config.api_endpoint = CONFIG.NETPIE.apihost; // Check if we should use MQTT via flowchannel const useFlowChannel = deviceConfigNode.flowchannelmqtt; function updateNodeStatus(current) { let color, shape, text; if (current.clientstatus) { shape = 'dot'; color = 'green'; text = 'listening'; } else { color = 'grey'; text = ''; shape = 'ring'; } if (useFlowChannel) { node.status({fill: color, shape: shape, text: text}); } else { node.status({fill: null, shape: null, text: ''}); } } if (config.active && useFlowChannel) { node.status({}); function initializeMessage() { current.clientstatus = 1; getcounter = { device: 0 } node.log('message connected'); deviceConfigNode.getDeviceInfo(); getcounter.device++; updateNodeStatus(current); } if (deviceConfigNode.isConnected()) { initializeMessage(); } else { addListener(deviceConfigNode, 'connect', initializeMessage); } addListener(deviceConfigNode, `disconnect`, function() { current.clientstatus = 0; node.log('message disconnected'); updateNodeStatus(current); }); addListener(deviceConfigNode, `device/status/response:${config.deviceid}`, function(payload) { if (payload) { node.groupid = payload.groupid || null; node.projectid = payload.projectid || null; if (node.groupid && node.projectid) { subscribeToTopics(); } addListener(deviceConfigNode, `message:${node.groupid}`, function(packet) { if (packet && packet.topic) { handleIncomingMessage(packet); } }); } }); function subscribeToTopics() { for (let i in topiclist) { if (topiclist[i] != undefined) { topiclist[i] = topiclist[i].trim(); if (topiclist[i] != '') { let subtopic; if (topiclist[i].startsWith('@msg')) { subtopic = topiclist[i].substr(5); // Remove @msg/ prefix deviceConfigNode.subscribeMessage(subtopic); } } } } } function handleIncomingMessage(data) { function isMatched(filter, topic) { const filterArray = filter.split('/'); const length = filterArray.length; const topicArray = topic.split('/'); for (var i = 0; i < length; ++i) { var left = filterArray[i]; var right = topicArray[i]; if (left === '#') return topicArray.length >= length - 1; if (left !== '+' && left !== right) return false; } return length === topicArray.length; } let cleanTopic = data.topic; // Match against subscribed topics for (let i = 0; i < topiclist.length; i++) { if (isMatched(topiclist[i], cleanTopic)) { let payload = data.payload; // Convert payload based on outputType setting if (node.outputType === "Buffer") { if (typeof payload === 'string') { payload = Buffer.from(payload, 'utf8'); } else if (Buffer.isBuffer(payload)) { // Already a buffer, keep as is payload = payload; } else if (typeof payload === 'object') { payload = Buffer.from(JSON.stringify(payload), 'utf8'); } else { payload = Buffer.from(String(payload), 'utf8'); } } else if (node.outputType === "JSON") { // Try to parse as JSON, return null if parsing fails try { if (Buffer.isBuffer(payload)) { payload = JSON.parse(payload.toString('utf8')); } else if (typeof payload === 'string') { payload = JSON.parse(payload); } else if (typeof payload === 'object') { // Already an object, keep as is payload = payload; } else { payload = JSON.parse(String(payload)); } } catch (e) { payload = null; } } else { // Default to String - convert everything to string if (Buffer.isBuffer(payload)) { payload = payload.toString('utf8'); } else if (typeof payload === 'object') { payload = JSON.stringify(payload); } else { payload = String(payload); } } let msg = { topic: cleanTopic, payload: payload }; node.send(msg); break; } } } addListener(deviceConfigNode, `error`, function(error) { node.error(error); }); } else { updateNodeStatus(current); } node.on('close', function(done) { current.clientstatus = 0; updateNodeStatus(current); if (useFlowChannel && node.groupid && node.projectid) { for (let i in topiclist) { if (topiclist[i] != undefined) { topiclist[i] = topiclist[i].trim(); if (topiclist[i] != '' && topiclist[i].startsWith('@msg')) { let subtopic = topiclist[i].substr(5); deviceConfigNode.unsubscribeMessage(subtopic); } } } } setTimeout(() => { done(); }, 500); }); node.on('input', function(msg) { if (config.active) { if (useFlowChannel && deviceConfigNode && deviceConfigNode.isConnected()) { // if mqtt via flowchannel if (msg.topic) { if (msg.topic.startsWith('@msg/')) { let messageTopic = msg.topic.substr(5); deviceConfigNode.publishMessage(messageTopic, msg.payload); } else if (msg.topic.startsWith('@private/')) { let privateTopic = msg.topic.substr(9); deviceConfigNode.publishPrivate(privateTopic, msg.payload); } } } else { // REST API fallback if (msg.topic.startsWith('@msg/')) { let part = msg.topic.split('/').splice(1).join('/'); axios({ method: 'put', url: `${config.api_endpoint}/device/message?topic=${part}`, responseType: 'json', data: msg.payload, headers: {'Authorization': `Device ${config.deviceid}:${config.devicetoken}`} }).then((response) => { }); } else if (msg.topic.startsWith('@private/')) { let part = msg.topic.split('/').splice(1).join('/'); axios({ method: 'put', url: `${config.api_endpoint}/device/private?topic=${part}`, data: msg.payload, responseType: 'json', headers: {'Authorization': `Device ${config.deviceid}:${config.devicetoken}`} }).then((response) => { }); } } } }); } RED.nodes.registerType("message", Message); };