node-red-contrib-homekit-bridged
Version:
Node-RED nodes to simulate Apple HomeKit devices.
779 lines (719 loc) • 34.4 kB
HTML
<!--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"> </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;"> <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> <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> <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">
{
"On": 1,
"OutletInUse": 1
}
</code></pre>
</div>
<p><strong>Hint</strong>: to find out what <em>Characteristics</em> you can address, just send <code>{"foo":"bar"}</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°C</p>
<div>
<pre><code class="language-json">
{
"CurrentTemperature": {
"minValue": -100
}
}
</code></pre>
<p><strong>Example</strong>: limit fan speed multiples of 25%</p>
<div>
<pre><code class="language-json">
{
"RotationSpeed": {
"minStep": 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>