UNPKG

node-red-contrib-kolibri

Version:

Node-RED nodes for connecting to a Kolibri Broker (e.g. HMS Hub)

287 lines 11.4 kB
"use strict"; /* * Copyright 2021 HMS Industrial Networks AB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http: //www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const kolibri_js_client_1 = require("@hms-networks/kolibri-js-client"); const nodeInit = (RED) => { class KolibriBrokerNode { constructor(config) { this.lazyThis = this; RED.nodes.createNode(this.lazyThis, config); // Configuration options passed by Node-RED this.name = config.name; this.broker = config.broker; this.port = config.port; this.project = config.project; this.path = config.path ? config.path : '/'; this.useProxy = config.useProxy; this.proxyHost = config.proxyHost; this.proxyPort = config.proxyPort; this.proxyProtocol = config.proxyProtocol; this.clientId = config.clientId; // Config node state this.brokerUrl = 'wss://' + this.broker + ':' + this.port; this.connected = false; this.connecting = false; this.closing = false; this.user = this.lazyThis.credentials.user; this.password = this.lazyThis.credentials.password; this.nodes = new Map(); this.subscriptions = new Map(); // Subscribe to closing node event of Node-RED this.lazyThis.on('close', (done) => { this.closing = true; if (typeof done === 'function') { done(); } }); const kolibriConfig = { host: this.brokerUrl, project: this.project, path: this.path, reconnect: { maxReconnects: 99, maxReconnectDelay: 15 } }; if (config.useProxy) { kolibriConfig.proxy = { host: config.proxyHost, port: config.proxyPort, protocol: config.proxyProtocol }; } this.client = kolibri_js_client_1.KolibriClientFactory.create(kolibriConfig); this.client.addOnDisconnectListener(async () => { if (this.connected || this.connecting) { this.connected = false; this.connecting = false; this.nodes.forEach((node) => { node.status({ fill: 'red', shape: 'ring', text: 'node-red:common.status.disconnected' }); }); } }); this.client.addOnWriteListener(async (data) => { data.forEach((node) => { const subscription = this.subscriptions.get(node.path); if (subscription) { subscription.handler(node.path, node.timestamp, node.quality, node.value); } }); }); this.client.addOnReconnectListener(async () => { this.connecting = false; this.connected = true; this.lazyThis.log('Logged in'); this.nodes.forEach((node) => { node.status({ fill: 'green', shape: 'dot', text: 'node-red:common.status.connected' }); }); this.subscriptions.forEach((_subscription, key) => { this.client.subscribe([{ path: key }]); }); }); } ; async connect() { if (this.connected || this.connecting) { return; } this.connecting = true; this.nodes.forEach((node) => { node.status({ fill: 'yellow', shape: 'ring', text: 'node-red:common.status.connecting' }); }); try { await this.client.connect(); } catch (e) { this.lazyThis.error((e === null || e === void 0 ? void 0 : e.message) || 'unknown error'); this.connected = false; this.connecting = false; this.nodes.forEach((node) => { node.status({ fill: 'red', shape: 'ring', text: (e === null || e === void 0 ? void 0 : e.message) || 'unknown error' }); }); return; } try { const loginParams = { user: this.user, password: this.password, interval: 15, timeout: 5 }; if (this.clientId) { loginParams.client = this.clientId; } else { const uuid = this.lazyThis.context().global.get('client-' + this.lazyThis.id); if (uuid) { loginParams.client = uuid; } } const loginResult = await this.client.login(loginParams); if (!this.clientId) { this.lazyThis.context().global.set('client-' + this.lazyThis.id, loginResult.client); } this.connecting = false; this.connected = true; this.lazyThis.log('Logged in'); this.nodes.forEach((node) => { node.status({ fill: 'green', shape: 'dot', text: 'node-red:common.status.connected' }); }); } catch (e) { this.lazyThis.error('Login failed: ' + (e === null || e === void 0 ? void 0 : e.message) || 'unknown error'); this.connected = false; this.connecting = false; if ((e === null || e === void 0 ? void 0 : e.message) === 'Access denied error') { e.message = 'invalid broker settings'; } this.nodes.forEach((node) => { node.status({ fill: 'red', shape: 'ring', text: (e === null || e === void 0 ? void 0 : e.message) || 'unknown error' }); }); } } async disconnect() { if (!this.connected) { return; } this.lazyThis.log('Closing connection'); this.connected = false; this.connecting = false; await this.client.disconnect(); } async register(node) { this.nodes.set(node.id, node); // Establish the Broker connection when the first node is registered if (this.nodes.size === 1) { await this.connect(); } } async deregister(node) { this.nodes.delete(node.id); if (this.nodes.size === 0) { await this.disconnect(); } } async subscribe(subscribeParams) { const subscription = this.subscriptions.get(subscribeParams.path); if (!this.connected || (subscription === null || subscription === void 0 ? void 0 : subscription.subscribed)) { return; } try { const results = await this.client.subscribe([subscribeParams]); results.forEach((result) => { const subscriptionToUpdate = this.subscriptions.get(result.path); if (subscriptionToUpdate) { subscriptionToUpdate.subscribed = true; } }); } catch (e) { this.lazyThis.error('Subscription failed: ' + (e === null || e === void 0 ? void 0 : e.message) || 'unknown error' + ' ' + subscribeParams.path); // Change status for node this.nodes.forEach((node) => { if (node.path === subscribeParams.path) { node.status({ fill: 'yellow', shape: 'ring', text: (e === null || e === void 0 ? void 0 : e.message) || 'unknown error' }); } }); } } addSubscribeListener(path, listener) { if (this.subscriptions.has(path)) { return; } this.subscriptions.set(path, { subscribed: false, handler: (mpath, mts, mqual, mval) => { if (mpath === path) { listener(mpath, mts, mqual, mval); } } }); } async unsubscribe(unsubscribeParams) { const subscription = this.subscriptions.get(unsubscribeParams.path); if (!this.connected || !subscription) { return; } try { await this.client.unsubscribe([unsubscribeParams]); subscription.subscribed = false; } catch (e) { this.lazyThis.warn('Unsubscribe failed ' + (e === null || e === void 0 ? void 0 : e.message) || 'unknown error'); } } async write(pointState) { if (!this.connected) { return; } const writeParams = { nodes: [pointState] }; try { await this.client.write(writeParams); } catch (e) { this.lazyThis.error('Write failed: ' + (e === null || e === void 0 ? void 0 : e.message) || 'unknown error' + ' on path: ' + pointState.path); this.nodes.forEach((node) => { if (node.path === pointState.path) { node.status({ fill: 'yellow', shape: 'ring', text: (e === null || e === void 0 ? void 0 : e.message) || 'unknown error' }); } }); } } } RED.nodes.registerType('kolibri-broker', KolibriBrokerNode, { credentials: { user: { type: 'text' }, password: { type: 'password' } } }); }; module.exports = nodeInit; module.exports = nodeInit; //# sourceMappingURL=kolibri-broker.js.map