UNPKG

node-red-contrib-dynamic-websocket

Version:

A node that dynamically connects to a WebSocket URL, can send and receive messages, and reports connection state changes.

371 lines (337 loc) 19.7 kB
<script type="text/javascript"> RED.nodes.registerType('dynamic-websocket',{ category: 'network', color: '#a6bbcf', defaults: { name: {value:""}, url: {value:""}, allowSelfSigned: {value: false}, autoReconnect: {value: false}, reconnectAttempts: {value: 0}, reconnectInterval: {value: 5000}, useExponentialBackoff: {value: false}, authType: {value: "none"}, username: {value: ""}, password: {value: ""}, token: {value: ""}, tokenLocation: {value: "header"}, tokenKey: {value: "Authorization"}, headers: {value: ""}, transformMessages: {value: false}, messageFormat: {value: "json"}, binarySupport: {value: false}, validateMessages: {value: false}, messageTemplate: {value: ""} }, credentials: { password: {type: "password"}, token: {type: "password"} }, inputs:1, outputs:3, icon: "bridge.svg", label: function() { return this.name||"Dynamic WebSocket"; }, paletteLabel: "Dynamic WebSocket", oneditprepare: function() { var node = this; $("#node-input-url").on("change", function() { node.url = $(this).val(); }); // Show/hide reconnection settings based on autoReconnect checkbox function toggleReconnectOptions() { if ($("#node-input-autoReconnect").is(":checked")) { $("#reconnect-settings").show(); } else { $("#reconnect-settings").hide(); } } $("#node-input-autoReconnect").on("change", toggleReconnectOptions); toggleReconnectOptions(); // Show/hide authentication settings based on authType dropdown function toggleAuthOptions() { var authType = $("#node-input-authType").val(); $("#basic-auth-settings").hide(); $("#token-auth-settings").hide(); if (authType === "basic") { $("#basic-auth-settings").show(); } else if (authType === "token") { $("#token-auth-settings").show(); } } $("#node-input-authType").on("change", toggleAuthOptions); toggleAuthOptions(); // Show/hide token location settings function toggleTokenLocationOptions() { var tokenLocation = $("#node-input-tokenLocation").val(); $("#token-key-settings").show(); } $("#node-input-tokenLocation").on("change", toggleTokenLocationOptions); toggleTokenLocationOptions(); // Initialize the custom headers textarea with formatted JSON if (node.headers) { try { // If it's already an object, stringify it if (typeof node.headers === 'object') { $("#node-input-headers").val(JSON.stringify(node.headers, null, 2)); } else { // Try to parse and format if it's a string var headersObj = JSON.parse(node.headers); $("#node-input-headers").val(JSON.stringify(headersObj, null, 2)); } } catch(e) { // If it's not valid JSON, leave as is } } // Initialize the message template textarea with formatted JSON if (node.messageTemplate) { try { // If it's already an object, stringify it if (typeof node.messageTemplate === 'object') { $("#node-input-messageTemplate").val(JSON.stringify(node.messageTemplate, null, 2)); } else { // Try to parse and format if it's a string var templateObj = JSON.parse(node.messageTemplate); $("#node-input-messageTemplate").val(JSON.stringify(templateObj, null, 2)); } } catch(e) { // If it's not valid JSON, leave as is } } // Show/hide message transformation settings based on checkbox function toggleTransformOptions() { if ($("#node-input-transformMessages").is(":checked")) { $("#transform-settings").show(); } else { $("#transform-settings").hide(); } } $("#node-input-transformMessages").on("change", toggleTransformOptions); toggleTransformOptions(); // Add template examples $("#add-template-example").on("click", function() { var format = $("#node-input-messageFormat").val(); var template = ""; if (format === "json") { template = { "type": "message", "id": "$id", "content": "$content", "timestamp": new Date().toISOString() }; } else if (format === "mqtt") { template = { "topic": "$topic", "payload": "$payload", "qos": 0, "retain": false }; } $("#node-input-messageTemplate").val(JSON.stringify(template, null, 2)); }); } }); </script> <script type="text/html" data-template-name="dynamic-websocket"> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <input type="text" id="node-input-name" placeholder="Name"> </div> <div class="form-row"> <label for="node-input-url"><i class="fa fa-globe"></i> Default URL</label> <input type="text" id="node-input-url" placeholder="ws://example.com/socket"> </div> <div class="form-row"> <label for="node-input-allowSelfSigned"><i class="fa fa-lock"></i> Allow Self-Signed Certificates</label> <input type="checkbox" id="node-input-allowSelfSigned" style="display: inline-block; width: auto; vertical-align: top;"> <span style="margin-left: 5px;">Enable this for connections with self-signed or expired certificates</span> </div> <div class="form-row"> <label for="node-input-autoReconnect"><i class="fa fa-refresh"></i> Auto Reconnect</label> <input type="checkbox" id="node-input-autoReconnect" style="display: inline-block; width: auto; vertical-align: top;"> <span style="margin-left: 5px;">Automatically attempt to reconnect when disconnected</span> </div> <div id="reconnect-settings"> <div class="form-row"> <label for="node-input-reconnectAttempts"><i class="fa fa-repeat"></i> Max Reconnect Attempts</label> <input type="number" id="node-input-reconnectAttempts" min="0" style="width: 100px;"> <span style="margin-left: 5px;">0 = unlimited attempts</span> </div> <div class="form-row"> <label for="node-input-reconnectInterval"><i class="fa fa-clock-o"></i> Reconnect Interval (ms)</label> <input type="number" id="node-input-reconnectInterval" min="100" style="width: 100px;"> </div> <div class="form-row"> <label for="node-input-useExponentialBackoff"><i class="fa fa-line-chart"></i> Use Exponential Backoff</label> <input type="checkbox" id="node-input-useExponentialBackoff" style="display: inline-block; width: auto; vertical-align: top;"> <span style="margin-left: 5px;">Increase delay between reconnection attempts</span> </div> </div> <div class="form-row"> <label for="node-input-authType"><i class="fa fa-key"></i> Authentication</label> <select id="node-input-authType" style="width: 70%;"> <option value="none">None</option> <option value="basic">Basic Authentication</option> <option value="token">Token Authentication</option> </select> </div> <div id="basic-auth-settings"> <div class="form-row"> <label for="node-input-username"><i class="fa fa-user"></i> Username</label> <input type="text" id="node-input-username"> </div> <div class="form-row"> <label for="node-input-password"><i class="fa fa-lock"></i> Password</label> <input type="password" id="node-input-password"> </div> </div> <div id="token-auth-settings"> <div class="form-row"> <label for="node-input-token"><i class="fa fa-id-badge"></i> Token</label> <input type="password" id="node-input-token"> </div> <div class="form-row"> <label for="node-input-tokenLocation"><i class="fa fa-map-marker"></i> Token Location</label> <select id="node-input-tokenLocation" style="width: 70%;"> <option value="header">Header</option> <option value="url">URL Parameter</option> </select> </div> <div id="token-key-settings" class="form-row"> <label for="node-input-tokenKey"><i class="fa fa-tag"></i> Token Key</label> <input type="text" id="node-input-tokenKey" placeholder="Authorization"> <div style="max-width: 460px; margin-top: 5px; margin-left: 105px; color: #999;"> <small>For headers, use 'Authorization' for Bearer tokens. For URL, specify the parameter name.</small> </div> </div> </div> <div class="form-row"> <label for="node-input-headers"><i class="fa fa-list"></i> Custom Headers</label> <textarea id="node-input-headers" rows="4" style="width: 70%; font-family: monospace;" placeholder='{"X-Custom-Header": "value"}'></textarea> <div style="max-width: 460px; margin-top: 5px; margin-left: 105px; color: #999;"> <small>Enter custom headers as JSON object. Example: {"X-Custom-Header": "value"}</small> </div> </div> <div class="form-section-header"> <i class="fa fa-exchange"></i> Message Transformation </div> <div class="form-row"> <label for="node-input-transformMessages"><i class="fa fa-magic"></i> Transform Messages</label> <input type="checkbox" id="node-input-transformMessages" style="display: inline-block; width: auto; vertical-align: top;"> <span style="margin-left: 5px;">Apply template and validation to outgoing messages</span> </div> <div id="transform-settings"> <div class="form-row"> <label for="node-input-messageFormat"><i class="fa fa-code"></i> Message Format</label> <select id="node-input-messageFormat" style="width: 70%;"> <option value="json">JSON</option> <option value="mqtt">MQTT</option> <option value="custom">Custom</option> </select> </div> <div class="form-row"> <label for="node-input-binarySupport"><i class="fa fa-file-o"></i> Binary Support</label> <input type="checkbox" id="node-input-binarySupport" style="display: inline-block; width: auto; vertical-align: top;"> <span style="margin-left: 5px;">Enable support for binary data transmission</span> </div> <div class="form-row"> <label for="node-input-validateMessages"><i class="fa fa-check-circle"></i> Validate Messages</label> <input type="checkbox" id="node-input-validateMessages" style="display: inline-block; width: auto; vertical-align: top;"> <span style="margin-left: 5px;">Validate messages against template before sending</span> </div> <div class="form-row"> <label for="node-input-messageTemplate"><i class="fa fa-file-text-o"></i> Message Template</label> <textarea id="node-input-messageTemplate" rows="6" style="width: 70%; font-family: monospace;" placeholder='{"type": "message", "content": "$content"}'></textarea> <div style="max-width: 460px; margin-top: 5px; margin-left: 105px; color: #999;"> <small>Enter message template as JSON object. Use $placeholder for dynamic values.</small> <button id="add-template-example" type="button" style="margin-top: 5px;">Add Example Template</button> </div> </div> </div> </script> <script type="text/html" data-help-name="dynamic-websocket"> <p>A node that dynamically connects to a WebSocket URL, can send and receive messages, and reports connection state changes.</p> <h3>Input</h3> <dl class="message-properties"> <dt>url <span class="property-type">string</span></dt> <dd>The WebSocket URL to connect to. If provided, it will override the default URL and be stored for future use.</dd> <dt>close <span class="property-type">boolean</span></dt> <dd>If set to true, it will close the current WebSocket connection and clear the stored URL.</dd> <dt>message <span class="property-type">object | string</span></dt> <dd>The message to be sent through the WebSocket. This will be stringified before sending.</dd> <dt>allowSelfSigned <span class="property-type">boolean</span></dt> <dd>If set, overrides the node's configuration for accepting self-signed certificates for this connection.</dd> <dt>reconnect <span class="property-type">boolean</span></dt> <dd>If set to true, forces an immediate reconnection attempt using the current URL.</dd> <dt>autoReconnect <span class="property-type">boolean</span></dt> <dd>If set, overrides the node's configuration for automatic reconnection.</dd> <dt>reconnectAttempts <span class="property-type">number</span></dt> <dd>If set, overrides the node's configuration for maximum reconnection attempts (0 = unlimited).</dd> <dt>reconnectInterval <span class="property-type">number</span></dt> <dd>If set, overrides the node's configuration for reconnection interval in milliseconds.</dd> <dt>useExponentialBackoff <span class="property-type">boolean</span></dt> <dd>If set, overrides the node's configuration for using exponential backoff for reconnection attempts.</dd> <dt>authType <span class="property-type">string</span></dt> <dd>Authentication type: 'none', 'basic', or 'token'. Overrides the node's configuration.</dd> <dt>username <span class="property-type">string</span></dt> <dd>Username for basic authentication. Overrides the node's configuration.</dd> <dt>password <span class="property-type">string</span></dt> <dd>Password for basic authentication. Overrides the node's configuration.</dd> <dt>token <span class="property-type">string</span></dt> <dd>Token for token-based authentication. Overrides the node's configuration.</dd> <dt>tokenLocation <span class="property-type">string</span></dt> <dd>Location for token: 'header' or 'url'. Overrides the node's configuration.</dd> <dt>tokenKey <span class="property-type">string</span></dt> <dd>Key name for the token (header name or URL parameter). Overrides the node's configuration.</dd> <dt>headers <span class="property-type">object | string</span></dt> <dd>Custom headers to include in the WebSocket connection. Can be an object or a JSON string. Overrides the node's configuration.</dd> <dt>transformMessages <span class="property-type">boolean</span></dt> <dd>Enable or disable message transformation. Overrides the node's configuration.</dd> <dt>messageFormat <span class="property-type">string</span></dt> <dd>Set the message format ("json", "mqtt", "custom"). Overrides the node's configuration.</dd> <dt>binarySupport <span class="property-type">boolean</span></dt> <dd>Enable or disable binary data support. Overrides the node's configuration.</dd> <dt>validateMessages <span class="property-type">boolean</span></dt> <dd>Enable or disable message validation. Overrides the node's configuration.</dd> <dt>messageTemplate <span class="property-type">object | string</span></dt> <dd>Message template to apply. Can be an object or a JSON string. Overrides the node's configuration.</dd> <dt>binary <span class="property-type">boolean</span></dt> <dd>Set to true to send binary data. The message must be a Buffer.</dd> <dt>skipTransform <span class="property-type">boolean</span></dt> <dd>Set to true to skip transformation for this message only.</dd> </dl> <h3>Outputs</h3> <dl class="message-properties"> <dt>1. Received Output (top)</dt> <dd> <dl class="message-properties"> <dt>payload <span class="property-type">object | string</span></dt> <dd>The data received from the WebSocket connection. If the received data is valid JSON, it will be parsed into an object. Otherwise, it will be output as a string.</dd> </dl> </dd> <dt>2. State Output (middle)</dt> <dd> <dl class="message-properties"> <dt>state <span class="property-type">string</span></dt> <dd>The current state of the WebSocket connection. Can be "disconnected" or "error".</dd> <dt>error <span class="property-type">string</span></dt> <dd>If the state is "error", this will contain the error message.</dd> </dl> </dd> <dt>3. Connected Output (bottom)</dt> <dd> <dl class="message-properties"> <dt>state <span class="property-type">string</span></dt> <dd>Outputs "Connected" when a connection is established.</dd> </dl> </dd> </dl> <h3>Details</h3> <p>This node will remember the last URL it connected to, even after a Node-RED restart. To change the URL, send a new message with the desired URL in <code>msg.url</code>. To close the connection and clear the stored URL, send a message with <code>msg.close = true</code>.</p> <p>To send a message through the WebSocket, include <code>msg.message</code> in the input message.</p> <p>When connected, the node status will display the current WebSocket URL.</p> <p>The node attempts to parse incoming messages as JSON. If successful, the output will be a JSON object. If parsing fails, the raw message will be output as a string.</p> <p>The second output will emit a message whenever the connection state changes (except when closed by <code>msg.close</code>), allowing you to monitor and react to connection issues.</p> <p>The third output will emit a "Connected" message when a connection is established, allowing you to trigger actions upon successful connection.</p> </script>