UNPKG

node-red-contrib-homekit-bridged

Version:
779 lines (719 loc) 34.4 kB
<!--suppress EqualityComparisonWithCoercionJS --> <script data-template-name="homekit-service2" type="text/x-red"> <div class="form-row"> <label for="node-config-input-isParent"><i class="fa fa-tasks"></i> Service Hierarchy</label> <select id="node-config-input-isParent" style="width: 70%"> <option value="true" selected="selected">Parent</option> <option value="false">Linked</option> </select> </div> <div class="form-row"> <label for="node-input-serviceName"> <i class="fa fa-cog"></i> Service</label> <select id="node-input-serviceName" style="width: 70%"> <option value="">Choose...</option> </select> </div> <div id="isParent"> <div class="form-row"> <label for="node-config-input-hostType"><i class="fa fa-tasks"></i> Host Type</label> <select id="node-config-input-hostType" style="width: 70%"> <option value="0" selected="selected">Bridge</option> <option value="1">Accessory</option> </select> </div> <div id="isOnBridge"> <div id="isBridgeInSubflow" class="alert alert-warning" role="alert"> Read more <b><a href="#" id="bridgeInSubflowNotice">here</a></b> about adding Bridge in a Subflow. </div> <div class="form-row" style="height: 34px;"> <label for="node-input-bridge"> <i class="fa fa-rocket"></i> Bridge</label> <input id="node-input-bridge"> </div> <div class="form-row"> <label for="node-input-manufacturer"><i class="fa fa-wrench"></i> Manufacturer</label> <input type="text" id="node-input-manufacturer" placeholder="Manufacturer"> </div> <div class="form-row"> <label for="node-input-serialNo"><i class="fa fa-wrench"></i> Serial Number</label> <input type="text" id="node-input-serialNo" placeholder="Serial Number"> </div> <div class="form-row"> <label for="node-input-model"><i class="fa fa-wrench"></i> Model</label> <input type="text" id="node-input-model" placeholder="Model"> </div> <div class="form-row"> <label for="node-input-firmwareRev"><i class="fa fa-wrench"></i> Firmware Revision</label> <input type="text" id="node-input-firmwareRev" placeholder="Firmware Revision"> </div> <div class="form-row"> <label for="node-input-hardwareRev"><i class="fa fa-wrench"></i> Hardware Revision</label> <input type="text" id="node-input-hardwareRev" placeholder="Hardware Revision"> </div> <div class="form-row"> <label for="node-input-softwareRev"><i class="fa fa-wrench"></i> Software Revision</label> <input type="text" id="node-input-softwareRev" placeholder="Software Revision"> </div> </div> <div id="isAccessory"> <div class="form-row" style="height: 34px;"> <label for="node-input-accessoryId"> <i class="fa fa-rocket"></i> Accessory</label> <input id="node-input-accessoryId"> </div> </div> </div> <div id="isLinked" style="display: none;"> <div class="form-row"> <label for="node-input-parentService"> <i class="fa fa-cog"></i> Parent Service</label> <select id="node-input-parentService"> <option value="">Choose...</option> </select> </div> </div> <div class="form-row"> <label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label> <input type="text" id="node-input-topic" placeholder="Topic"> </div> <div class="form-row"> <label class="visibleDesktop">&nbsp;</label> <div style="display: inline;"> <input type="checkbox" id="node-input-filter" style="display: inline-block; width: auto; vertical-align: top;"> <label for="node-input-filter" style="width: 120px;">&nbsp;&nbsp;<i class="fa fa-filter"></i> Filter on Topic</label> </div> </div> <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 id="camera-configuration" style="display: none;"> <label>&nbsp;&nbsp;<i class="fa fa-video-camera"></i> Camera Configuration</label> <div class="form-row"> <label for="node-input-cameraConfigVideoProcessor"><i class="fa fa-cog"></i> Video Processor</label> <input type="text" id="node-input-cameraConfigVideoProcessor" placeholder="ffmpeg"> </div> <div class="form-row"> <label for="node-input-cameraConfigSource"><i class="fa fa-tint"></i> Source</label> <input type="text" id="node-input-cameraConfigSource" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigStillImageSource"><i class="fa fa-picture-o"></i> Still Image Source</label> <input type="text" id="node-input-cameraConfigStillImageSource" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigMaxStreams"><i class="fa fa-tint"></i> Max Streams</label> <input type="text" id="node-input-cameraConfigMaxStreams" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigMaxWidth"><i class="fa fa-text-width"></i> Max Width</label> <input type="text" id="node-input-cameraConfigMaxWidth" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigMaxHeight"><i class="fa fa-text-height"></i> Max Height</label> <input type="text" id="node-input-cameraConfigMaxHeight" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigMaxFPS"><i class="fa fa-clock-o"></i> Max FPS</label> <input type="text" id="node-input-cameraConfigMaxFPS" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigMaxBitrate"><i class="fa fa-clock-o"></i> Max Bitrate</label> <input type="text" id="node-input-cameraConfigMaxBitrate" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigVideoCodec"><i class="fa fa-video-camera"></i> Video Codec</label> <input type="text" id="node-input-cameraConfigVideoCodec" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigAudioCodec"><i class="fa fa-video-camera"></i> Audio Codec</label> <input type="text" id="node-input-cameraConfigAudioCodec" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigAudio"><i class="fa fa-headphones"></i> Audio</label> <input type="checkbox" id="node-input-cameraConfigAudio"> </div> <div class="form-row"> <label for="node-input-cameraConfigPacketSize"><i class="fa fa-get-pocket"></i> Packet Size</label> <input type="text" id="node-input-cameraConfigPacketSize" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigVerticalFlip"><i class="fa fa-undo"></i> Vertical Flip</label> <input type="checkbox" id="node-input-cameraConfigVerticalFlip"> </div> <div class="form-row"> <label for="node-input-cameraConfigHorizontalFlip"><i class="fa fa-undo"></i> Horizontal Flip</label> <input type="checkbox" id="node-input-cameraConfigHorizontalFlip"> </div> <div class="form-row"> <label for="node-input-cameraConfigMapVideo"><i class="fa fa-video-camera"></i> Map Video</label> <input type="text" id="node-input-cameraConfigMapVideo" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigMapAudio"><i class="fa fa-headphones"></i> Map Audio</label> <input type="text" id="node-input-cameraConfigMapAudio" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigVideoFilter"><i class="fa fa-filter"></i> Video Filter</label> <input type="text" id="node-input-cameraConfigVideoFilter" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigAdditionalCommandLine"><i class="fa fa-plus"></i> Additional Command Line</label> <input type="text" id="node-input-cameraConfigAdditionalCommandLine" placeholder=""> </div> <div class="form-row"> <label for="node-input-cameraConfigDebug"><i class="fa fa-bug"></i> Debug</label> <input type="checkbox" id="node-input-cameraConfigDebug"> </div> <div class="form-row"> <label for="node-input-cameraConfigSnapshotOutput"><i class="fa fa-picture-o"></i> Snapshot output</label> <select id="node-input-cameraConfigSnapshotOutput"> <option value="disabled" selected="selected">Disabled</option> <option value="path">Path</option> <option value="content">Content</option> </select> </div> <div class="form-row"> <label for="node-input-cameraConfigInterfaceName"><i class="fa fa-smile-o"></i> Interface Name</label> <input type="text" id="node-input-cameraConfigInterfaceName" placeholder=""> </div> </div> <div id="adaptive-lightning-configuration" style="display: none; border: 1px solid var(--red-ui-secondary-border-color); padding: 12px 12px 0 12px; margin-bottom: 12px;"> <label>&nbsp;&nbsp;<i class="fa fa-cog"></i> Adaptive Lightning Configuration</label> <div class="form-row"> <label for="node-input-adaptiveLightingOptionsEnable"><i class="fa fa-toggle-on"></i> Enable</label> <input type="checkbox" id="node-input-adaptiveLightingOptionsEnable"> </div> <div class="form-row"> <label for="node-input-adaptiveLightingOptionsMode"><i class="fa fa-hand-o-up"></i> Mode</label> <select id="node-input-adaptiveLightingOptionsMode"> <option value="" selected hidden disabled>AUTOMATIC</option> <option value="1">AUTOMATIC</option> <option value="2">MANUAL</option> </select> </div> <div class="form-row"> <label for="node-input-adaptiveLightingOptionsCustomTemperatureAdjustment"><i class="fa fa-thermometer-quarter"></i> Custom Temperature Adjustment</label> <input type="number" id="node-input-adaptiveLightingOptionsCustomTemperatureAdjustment" placeholder="0"> </div> </div> <div class="form-row"> <label for="node-input-characteristicProperties"><i class="fa fa-wrench"></i> Characteristic Properties</label> <input type="text" id="node-input-characteristicProperties" style="width: 70%"> </div> <div class="form-row"> <label for="node-input-waitForSetupMsg"><i class="fa fa-bug"></i> Wait for Setup message</label> <input type="checkbox" id="node-input-waitForSetupMsg"> </div> <div class="form-row"> <label for="node-input-useEventCallback"><i class="fa fa-code-fork"></i> Use Event callback</label> <input type="checkbox" id="node-input-useEventCallback"> </div> </script> <script data-help-name="homekit-service2" type="text/x-red"> <h3 id="toc_5">Service</h3> <p>The Service node represents the single device you want to control or query.</p> <p>Every service node creates its own HAP accessory to keep things simple.</p> <ul> <li><strong>Service Hierarchy</strong>: Choose if this Service is Parent or Linked</li> <ul> <li>Parent has <strong>Bridge</strong> and <strong>Accessory Category</strong>: On what Bridge to host this Service and its Accessory and what kind of category is this Accessory, default <em>OTHER</em></li> <li>Linked has <strong>Parent Service</strong>: On what Parent Service link this Service.</li> </ul> <li><strong>Topic</strong>: An optional property that can be configured in the node or, if left blank, can be set by <code>msg.topic</code>. If <em>Filter on Topic</em> is selected <code>msg.topic</code> of incoming messages must match the configured value for the message to be accepted. If <em>Filter on Topic</em> is selected and no <em>Topic</em> is set on the node, then <code>msg.topic</code> must match the nodes <em>Name</em> </li> <li><strong>Manufacturer, Model, Serial Number</strong>: Can be anything you want.</li> <li><strong>Firmware Revision</strong>: Should be a version number string in the form of <em>MAJOR.MINOR.REVISION</em> e.g. <em>1.2.0</em>. Other types of strings are ignored and won't be displayed.</li> <li><strong>Hardware Revision</strong>: Should be a version number string in the form of <em>MAJOR.MINOR.REVISION</em> e.g. <em>1.2.0</em>. Other types of strings are ignored and won't be displayed.</li> <li><strong>Software Revision</strong>: Should be a version number string in the form of <em>MAJOR.MINOR.REVISION</em> e.g. <em>1.2.0</em>. Other types of strings are ignored and won't be displayed.</li> <li><strong>Service</strong>: Choose the type of Service from the list.</li> <li><strong>Name</strong>: <em>optional</em></li> <li><strong>Camera Configuration</strong>: Additional configuration for CameraControl service.</li> <ul> <li><strong>Video Processor</strong>: Video processor used for Camera. Default is <em>ffmpeg</em>.</li> <li><strong>Source</strong>: Camera source used for video processor. Example for ffmpeg <em>-re -i rtsp://192.168.0.227:8554/unicast</em></li> <li><strong>Still Image Source</strong>: Camera snapshot source used for video processor. Example for ffmpeg <em>-i http://faster_still_image_grab_url/this_is_optional.jpg</em></li> <li><strong>Max Streams</strong>: Maximum number of streams that will be generated for this camera, default <em>2</em>.</li> <li><strong>Max Width</strong>: Maximum width reported to HomeKit, default <em>1280</em>.</li> <li><strong>Max Height</strong>: Maximum height reported to HomeKit, default <em>720</em>.</li> <li><strong>Max FPS</strong>: Maximum frame rate of the stream, default <em>10</em>.</li> <li><strong>Max Bitrate</strong>: Maximum bit rate of the stream in kbit/s, default <em>300</em>.</li> <li><strong>Video Codec</strong>: If you're running on a RPi with the omx version of ffmpeg installed, you can change to the hardware accelerated video codec with this option, default <em>libx264</em>.</li> <li><strong>Audio Codec</strong>: If you're running on a RPi with the omx version of ffmpeg installed, you can change to the hardware accelerated audio codec with this option, default <em>libfdk_aac</em>.</li> <li><strong>Audio</strong>: Can be set to true to enable audio streaming from camera. To use audio ffmpeg must be compiled with --enable-libfdk-aac, default <em>false</em>.</li> <li><strong>Packet Size</strong>: If audio or video is choppy try a smaller value, set to a multiple of 188, default <em>1316</em>.</li> <li><strong>Vertical Flip</strong>: Flips the stream vertically, default <em>false</em>.</li> <li><strong>Horizontal Flip</strong>: Flips the stream horizontally, default <em>false</em>.</li> <li><strong>Map Video</strong>: Select the stream used for video, default <em>0:0</em>.</li> <li><strong>Map Audio</strong>: Select the stream used for audio, default <em>0:1</em>.</li> <li><strong>Video Filter</strong>: Allows a custom video filter to be passed to FFmpeg via -vf, defaults to <em>scale=1280:720</em> but is optional.</li> <li><strong>Additional Command Line</strong>: Allows additional of extra command line options to FFmpeg, default <em>-tune zerolatency</em> but is optional.</li> <li><strong>Debug</strong>: Show the output of ffmpeg in the log, default <em>false</em>.</li> <li><strong>Snapshot output</strong>: Choose how to output camera snapshot</li> <ul> <li><strong>Disabled</strong>: there will be no output</li> <li><strong>Path</strong>: file will be saved and path will be send to output, <em>msg.payload.cameraSnapshot</em> contains path value stored as a string.</li> <li><strong>Content</strong>: file content will be send to output, <em>msg.payload.cameraSnapshot</em> contains Buffer object {"type":"Buffer","data":[]}.</li> </ul> <li><strong>Interface Name</strong>: Selects the IP address of a given network interface.</li> </ul> <li><strong>Characteristic Properties</strong>: Customize the properties of characteristics.</li> <li><strong>Wait for Setup message</strong>: If yes then Service node will wait for a input message with appropriate payload.</li> <li><strong>Use Event callback</strong>: If yes then Service node will wait for a callback message to respond to get event.</li> </ul> <h2 id="toc_6">Input Messages</h2> <p>Input messages can be used to update any <em>Characteristic</em> that the selected <em>Service</em> provides. Simply pass the values-to-update as <code>msg.payload</code> object. </p> <p><strong>Example</strong>: to signal that an <em>Outlet</em> is turned on and in use, send the following payload</p> <div> <pre><code class="language-javascript"> { &quot;On&quot;: 1, &quot;OutletInUse&quot;: 1 } </code></pre> </div> <p><strong>Hint</strong>: to find out what <em>Characteristics</em> you can address, just send <code>{&quot;foo&quot;:&quot;bar&quot;}</code> and watch the debug tab ;)</p> <h2 id="toc_7">Output Messages</h2> <p>Output messages are in the same format as input messages. They are emitted from the node when it receives <em>Characteristics</em> updates from a paired iOS device.</p> <h2 id="toc_8">Characteristic Properties</h2> <p><strong>Example</strong>: allow temperatures below 0&deg;C</p> <div> <pre><code class="language-json"> { &quot;CurrentTemperature&quot;: { &quot;minValue&quot;: -100 } } </code></pre> <p><strong>Example</strong>: limit fan speed multiples of 25%</p> <div> <pre><code class="language-json"> { &quot;RotationSpeed&quot;: { &quot;minStep&quot;: 25 } } </code></pre> </div> </script> <script type="text/javascript"> if (nrchkbExperimental) { RED.nodes.registerType('homekit-service2', { category: "Apple HomeKit", paletteLabel: 'service 2', defaults: { isParent: { value: 'false', required: true, }, hostType: { // 0 is Bridge, 1 is Accessory value: 0, required: true, }, bridge: { value: '', type: 'homekit-bridge', validate: function (value) { if (this.isParent == true && this.hostType == 0) { return isValueDefined(value) && value.length > 0 } else return true }, }, accessoryId: { value: '', type: 'homekit-standalone', validate: function (value) { if (this.isParent == true && this.hostType == 1) { return isValueDefined(value) && value.length > 0 } else return true }, }, parentService: { value: '', validate: function (value) { if (this.isParent == false) { return isValueDefined(value) && value.length > 0 } else return true }, }, name: { value: '', required: true, validate: function (value) { return isValueDefined(value) && value.length > 0 }, }, serviceName: { value: '', required: true, }, topic: { value: '', }, filter: { value: false, }, manufacturer: { value: 'NRCHKB', required: true, }, model: { value: nrchkbVersion, required: true, }, serialNo: { value: 'Default Serial Number', required: true, }, firmwareRev: { value: nrchkbVersion, required: false, validate: versionValidator, }, hardwareRev: { value: nrchkbVersion, required: false, validate: versionValidator, }, softwareRev: { value: nrchkbVersion, required: false, validate: versionValidator, }, cameraConfigVideoProcessor: { value: 'ffmpeg', validate: cameraConfigRequiredField, }, cameraConfigSource: { validate: cameraConfigRequiredField, }, cameraConfigStillImageSource: { required: false, }, cameraConfigMaxStreams: { value: 2, validate: cameraConfigRequiredField, }, cameraConfigMaxWidth: { value: 1280, validate: cameraConfigRequiredField, }, cameraConfigMaxHeight: { value: 720, validate: cameraConfigRequiredField, }, cameraConfigMaxFPS: { value: 10, validate: cameraConfigRequiredField, }, cameraConfigMaxBitrate: { value: 300, validate: cameraConfigRequiredField, }, cameraConfigVideoCodec: { value: 'libx264', validate: cameraConfigRequiredField, }, cameraConfigAudioCodec: { value: 'libfdk_aac', validate: cameraConfigRequiredField, }, cameraConfigAudio: { value: false, }, cameraConfigPacketSize: { value: 1316, validate: cameraConfigRequiredField, }, cameraConfigVerticalFlip: { value: false, }, cameraConfigHorizontalFlip: { value: false, }, cameraConfigMapVideo: { value: '0:0', validate: cameraConfigRequiredField, }, cameraConfigMapAudio: { value: '0:1', validate: cameraConfigRequiredField, }, cameraConfigVideoFilter: { value: 'scale=1280:720', }, cameraConfigAdditionalCommandLine: { value: '-tune zerolatency', }, cameraConfigDebug: { value: false, }, cameraConfigSnapshotOutput: { value: 'disabled', validate: cameraConfigRequiredField, }, cameraConfigInterfaceName: { value: '', }, characteristicProperties: { value: '{}', validate: function (value) { if (value === undefined || value.length === 0) { return true } try { JSON.parse(value) } catch (e) { console.log(e, value) return false } return true }, }, waitForSetupMsg: { value: false }, useEventCallback: { value: false }, outputs: { value: 1, }, adaptiveLightingOptionsEnable: { value: false, }, adaptiveLightingOptionsMode: { validate: RED.validators.number(true) }, adaptiveLightingOptionsCustomTemperatureAdjustment: { validate: RED.validators.number(true) }, }, inputs: 1, outputs: 1, outputLabels: function (index) { if (index === 0) { return 'events' } return '' }, icon: 'homekit.png', color: '#fcc127', label: function () { return this.name || this.serviceName || 'Service2' }, labelStyle: function () { return this.name ? 'node_label_italic' : '' }, oneditprepare: function () { let node = this let isParentToggle = $('#node-config-input-isParent') if (node.isParent === false) { isParentToggle.val('false') } else { isParentToggle.val('true') } let isLinkedDiv = $('#isLinked') let isParentDiv = $('#isParent') const selectHostType = $('#node-config-input-hostType') isParentToggle .change(function () { if (isParentToggle.val() === 'true') { isLinkedDiv.fadeOut('fast') isParentDiv.fadeIn('fast') } else { isParentDiv.fadeOut('fast') isLinkedDiv.fadeIn('fast') selectHostType.val("_ADD_") } }) .change() if (node.hostType) { selectHostType.val(node.hostType) } else { // Make sure that host type is selected when upgrading nrchkb selectHostType.val(0) } let isOnBridgeDiv = $('#isOnBridge') let isBridgeInSubflow = $('#isBridgeInSubflow') let isAccessoryDiv = $('#isAccessory') const inSubflow = !!RED.nodes.subflow(node.z) selectHostType .change(function () { if (selectHostType.val() == 0) { isAccessoryDiv.hide() isOnBridgeDiv.show() if (inSubflow) { isBridgeInSubflow.show() } else { isBridgeInSubflow.hide() } $("#node-input-accessoryId").val("_ADD_"); } else { isOnBridgeDiv.hide() isAccessoryDiv.show() $("#node-input-bridge").val("_ADD_"); } }) .change() const selectServiceName = $('#node-input-serviceName') Object.keys(serviceTypes).sort().forEach(function (key) { const serviceOption = $('<option></option>') serviceOption .val(key) .text(key) if (serviceTypes[key].hasOwnProperty('nrchkbDisabledText')) { serviceOption.text(serviceTypes[key].nrchkbDisabledText) serviceOption.attr('disabled', 'disabled'); } selectServiceName.append(serviceOption) }) let cameraConfiguration = $('#camera-configuration') let adaptiveLightningConfiguration = $('#adaptive-lightning-configuration') selectServiceName .find('option') .filter(function () { return $(this).val() === node.serviceName }) .attr('selected', true) selectServiceName .change(function () { if (this.value === 'CameraControl') { cameraConfiguration.fadeIn('fast') node.outputs = 2 } else { cameraConfiguration.fadeOut('fast') node.outputs = 1 } }) .change() selectServiceName .change(function () { if (this.value === 'Lightbulb') { adaptiveLightningConfiguration.fadeIn('fast') } else { adaptiveLightningConfiguration.fadeOut('fast') } }) .change() $('#node-input-characteristicProperties').typedInput({ type: 'json', types: ['json'], }) const selectParentService = $('#node-input-parentService') const candidateNodes = [ ...RED.nodes.filterNodes({ type: 'homekit-service', }), ...RED.nodes.filterNodes({ type: 'homekit-service2', }) ] candidateNodes.forEach(function (n) { if (!n.name || n.name.length < 1 || !n.isParent) { return } if (n.id === node.id) { return } if (inSubflow) { if (n.z !== node.z) { return } } else { if (!!RED.nodes.subflow(n.z)) { return } } let sublabel let tab = RED.nodes.workspace(n.z) if (tab) { sublabel = tab.label || tab.id } else { tab = RED.nodes.subflow(n.z) sublabel = 'subflow : ' + tab.name } const value = n.id const text = n.name + ' (' + sublabel + ')' const hostType = n.hostType selectParentService.append( $('<option></option>') .val(value) .text(text) .attr("hostType", hostType), ) }) selectParentService .find('option') .filter(function () { return $(this).val() === node.parentService }) .attr('selected', true) selectParentService.change() $("#node-input-accessoryId option[value='${n.accessoryId}']").attr("disabled", false); candidateNodes.forEach(function (n) { if (n.id == node.id) { return } if (n.hostType == 0) { return } if (n.accessoryId) { if (n.accessoryId == node.accessoryId) { // Same accessory cannot be used twice! $("#node-input-accessoryId").val("_ADD_"); } const accessoryOption = $("#node-input-accessoryId option[value='" + n.accessoryId + "']"); accessoryOption.attr("disabled", true); accessoryOption.text(accessoryOption.text() + " already used by " + n.name) } }) isBridgeInSubflow.find('#bridgeInSubflowNotice').on("click", function () { $('<div>') .css({maxHeight: '80%'}) .html("<ul>" + "<li>If a Bridge is created within the Flow, then each Service node in a Subflow will be added to the existing (single) Bridge.</li>" + "<li>If a Bridge is created within the Subflow, a new Bridge will be created for each Subflow instance.</li>" + "<li>If a Standalone Accessory is being used, it is recommended to set it up completly within the Subflow editor. This will add a new Standalone Accessory for each Subflow instance in your Flow.</li>" + "<li>Visit our <b><a href=\"https://nrchkb.github.io/wiki/introduction/service-node/#bridge\">wiki</a></b> for more informations.</li>" + "</ul>") .dialog({ draggable: true, modal: true, resizable: false, width: 'auto', title: 'Important Notice', minHeight: 75, buttons: { Close: function () { $(this).dialog('destroy') } } }) }) }, oneditsave: function () { let node = this node.isParent = $('#node-config-input-isParent').val() === 'true' node.hostType = $('#node-config-input-hostType').val() if (!node.isParent) { const selectedParentService = $('#node-input-parentService option:selected') node.parentService = selectedParentService.val() node.hostType = selectedParentService.attr("hostType") } node.serviceName = $( '#node-input-serviceName option:selected', ).val() }, }) } </script>