From 0d2667b21440a7e1b9a82ade2f63ecf930a887b7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 15 Oct 2022 09:48:40 +0200 Subject: [PATCH] build: separate js and html parts - split javascript and html parts in ccu-*.html - store javascript in src/nodes-html/*/editor.js - store html in src/nodes-html/*/main.html --- src/nodes-html/ccu-alexa/editor.js | 135 +++++ src/nodes-html/ccu-alexa/main.html | 46 ++ src/nodes-html/ccu-connection/editor.js | 133 +++++ src/nodes-html/ccu-connection/main.html | 180 ++++++ src/nodes-html/ccu-display/editor.js | 225 +++++++ src/nodes-html/ccu-display/main.html | 151 +++++ src/nodes-html/ccu-get-value/editor.js | 420 +++++++++++++ src/nodes-html/ccu-get-value/main.html | 71 +++ src/nodes-html/ccu-mqtt/editor.js | 46 ++ src/nodes-html/ccu-mqtt/main.html | 117 ++++ src/nodes-html/ccu-poll/editor.js | 26 + src/nodes-html/ccu-poll/main.html | 29 + src/nodes-html/ccu-program/editor.js | 60 ++ src/nodes-html/ccu-program/main.html | 92 +++ src/nodes-html/ccu-rpc-event/editor.js | 500 ++++++++++++++++ src/nodes-html/ccu-rpc-event/main.html | 150 +++++ src/nodes-html/ccu-rpc/editor.js | 125 ++++ src/nodes-html/ccu-rpc/main.html | 114 ++++ src/nodes-html/ccu-script/editor.js | 27 + src/nodes-html/ccu-script/main.html | 31 + src/nodes-html/ccu-set-value/editor.js | 512 ++++++++++++++++ src/nodes-html/ccu-set-value/main.html | 117 ++++ src/nodes-html/ccu-signal/editor.js | 451 ++++++++++++++ src/nodes-html/ccu-signal/main.html | 195 ++++++ src/nodes-html/ccu-switch/editor.js | 759 ++++++++++++++++++++++++ src/nodes-html/ccu-switch/main.html | 79 +++ src/nodes-html/ccu-sysvar/editor.js | 72 +++ src/nodes-html/ccu-sysvar/main.html | 103 ++++ src/nodes-html/ccu-value/editor.js | 395 ++++++++++++ src/nodes-html/ccu-value/main.html | 195 ++++++ 30 files changed, 5556 insertions(+) create mode 100644 src/nodes-html/ccu-alexa/editor.js create mode 100644 src/nodes-html/ccu-alexa/main.html create mode 100644 src/nodes-html/ccu-connection/editor.js create mode 100644 src/nodes-html/ccu-connection/main.html create mode 100644 src/nodes-html/ccu-display/editor.js create mode 100644 src/nodes-html/ccu-display/main.html create mode 100644 src/nodes-html/ccu-get-value/editor.js create mode 100644 src/nodes-html/ccu-get-value/main.html create mode 100644 src/nodes-html/ccu-mqtt/editor.js create mode 100644 src/nodes-html/ccu-mqtt/main.html create mode 100644 src/nodes-html/ccu-poll/editor.js create mode 100644 src/nodes-html/ccu-poll/main.html create mode 100644 src/nodes-html/ccu-program/editor.js create mode 100644 src/nodes-html/ccu-program/main.html create mode 100644 src/nodes-html/ccu-rpc-event/editor.js create mode 100644 src/nodes-html/ccu-rpc-event/main.html create mode 100644 src/nodes-html/ccu-rpc/editor.js create mode 100644 src/nodes-html/ccu-rpc/main.html create mode 100644 src/nodes-html/ccu-script/editor.js create mode 100644 src/nodes-html/ccu-script/main.html create mode 100644 src/nodes-html/ccu-set-value/editor.js create mode 100644 src/nodes-html/ccu-set-value/main.html create mode 100644 src/nodes-html/ccu-signal/editor.js create mode 100644 src/nodes-html/ccu-signal/main.html create mode 100644 src/nodes-html/ccu-switch/editor.js create mode 100644 src/nodes-html/ccu-switch/main.html create mode 100644 src/nodes-html/ccu-sysvar/editor.js create mode 100644 src/nodes-html/ccu-sysvar/main.html create mode 100644 src/nodes-html/ccu-value/editor.js create mode 100644 src/nodes-html/ccu-value/main.html diff --git a/src/nodes-html/ccu-alexa/editor.js b/src/nodes-html/ccu-alexa/editor.js new file mode 100644 index 0000000..958e001 --- /dev/null +++ b/src/nodes-html/ccu-alexa/editor.js @@ -0,0 +1,135 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-alexa', { + category: 'ccu', + defaults: { + name: {value: ''}, + iface: {value: ''}, + channel: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + }, + inputs: 1, + outputs: 1, + icon: 'ccu.png', + color: '#c2d5e4', + paletteLabel: 'alexa', + align: 'right', + label() { + return this.name || this.channel || 'alexa'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditprepare() { + console.log(this); + + const $nodeInputIface = $('#node-input-iface'); + const $nodeInputCcuConfig = $('#node-input-ccuConfig'); + const $nodeInputChannel = $('#node-input-channel'); + + let data; + let ifacesLoaded = false; + let ifacesPending = false; + + function loadIfaces(iface, cb) { + if (ifacesPending) { + return; + } + + ifacesPending = true; + console.log('loadIfaces()'); + $nodeInputIface.html(''); + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId === '_ADD_') { + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + } else { + const url = 'ccu?config=' + nodeId + '&type=ifaces'; + $.getJSON(url, d => { + Object.keys(d).forEach(i => { + if (i !== 'ReGaHSS') { + $nodeInputIface.append('' + i + ''); + } + }); + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + }); + } + } + + function loadConfig() { + if (!ifacesLoaded) { + return; + } + + console.log('loadConfig()'); + const nodeId = $nodeInputCcuConfig.val(); + const url = 'ccu?config=' + nodeId + '&type=channels&iface=' + $nodeInputIface.val(); + $.getJSON(url, d => { + data = d; + console.log(d); + autocompleteChannel(); + }); + } + + $nodeInputCcuConfig.change(() => { + console.log('$nodeInputCcuConfig change'); + loadIfaces(this.iface, () => { + ifacesLoaded = true; + $nodeInputIface.removeAttr('disabled'); + loadConfig(); + }); + }); + + $nodeInputChannel.autocomplete({ + source: [], + close() { + // autocompleteDatapoint(); + }, + delay: 0, + minLength: 0, + }); + + $nodeInputChannel.on('focus', () => { + $nodeInputChannel.autocomplete('search'); + }); + + function autocompleteChannel() { + if (!data) { + return; + } + + const channels = []; + Object.keys(data).forEach(addr => { + if (/:\d+$/.test(addr)) { + if (data[addr].name) { + addr += ' ' + data[addr].name; + } + + channels.push(addr); + } + }); + channels.sort((a, b) => a.localeCompare(b)); + $nodeInputChannel.autocomplete('option', 'source', channels); + + if (!data[$nodeInputChannel.val().split(' ')[0]]) { + $nodeInputChannel.val(''); + } + } + + $nodeInputIface.change(() => { + console.log('$nodeInputIface change'); + loadConfig(); + }); + }, + + oneditsave() {}, + }); +}()); diff --git a/src/nodes-html/ccu-alexa/main.html b/src/nodes-html/ccu-alexa/main.html new file mode 100644 index 0000000..3c992d6 --- /dev/null +++ b/src/nodes-html/ccu-alexa/main.html @@ -0,0 +1,46 @@ + + + + + diff --git a/src/nodes-html/ccu-connection/editor.js b/src/nodes-html/ccu-connection/editor.js new file mode 100644 index 0000000..1653a8d --- /dev/null +++ b/src/nodes-html/ccu-connection/editor.js @@ -0,0 +1,133 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-connection', { + category: 'config', + defaults: { + name: {value: ''}, + host: {value: '', required: true}, + + regaEnabled: {value: true}, + bcrfEnabled: {value: true}, + iprfEnabled: {value: true}, + virtEnabled: {value: true}, + bcwiEnabled: {value: false}, + jackEnabled: {value: false}, + cuxdEnabled: {value: false}, + + regaPoll: {value: true}, + regaInterval: {value: 30}, + + rpcPingTimeout: {value: 60}, + rpcInitAddress: {value: ''}, + rpcServerHost: {value: '', required: true}, + rpcBinPort: {value: '', required: true}, + rpcXmlPort: {value: '', required: true}, + + tls: {value: false}, + inSecure: {value: false}, + authentication: {value: false}, + username: {value: ''}, + password: {value: ''}, + + queueTimeout: {value: 5000, required: true}, + queuePause: {value: 250, required: true}, + + contextStore: {value: ''}, + }, + label() { + return this.name || this.host; + }, + + oneditprepare() { + const $nodeConfigInputHost = $('#node-config-input-host'); + const $nodeConfigInputName = $('#node-config-input-name'); + const $nodeConfigInputRpcServerHost = $('#node-config-input-rpcServerHost'); + const $nodeInputContextStore = $('#node-config-input-contextStore'); + + if (typeof this.queueTimeout === 'undefined') { + $('#node-config-input-queueTimeout').val(5000); + } + + if (typeof this.queuePause === 'undefined') { + $('#node-config-input-queuePause').val(250); + } + + RED.settings.context.stores.forEach(store => { + $nodeInputContextStore.append(''); + }); + + $nodeConfigInputHost.on('focus', () => $nodeConfigInputHost.autocomplete('search', '')); + + $.getJSON('ccu', data => { + const discovered = []; + data.discover.forEach(ccu => { + discovered.push(ccu.address + ' ' + ccu.serial); + }); + + $nodeConfigInputHost.autocomplete({ + source: discovered, + select(_, ui) { + const name = ui.item.label; + const address = name.split(' ').shift(); + const serial = name.split(' ').pop(); + + $nodeConfigInputHost.val(address); + $nodeConfigInputName.val(serial); + + data.discover.forEach(ccu => { + if (ccu.address === address) { + $('#node-config-input-regaEnabled').prop('checked', ccu.interfaces.ReGaHSS); + $('#node-config-input-bcrfEnabled').prop('checked', ccu.interfaces['BidCos-RF']); + $('#node-config-input-iprfEnabled').prop('checked', ccu.interfaces['HmIP-RF']); + $('#node-config-input-virtEnabled').prop('checked', ccu.interfaces.VirtualDevices); + $('#node-config-input-bcwiEnabled').prop('checked', ccu.interfaces['BidCos-Wired']); + $('#node-config-input-jackEnabled').prop('checked', ccu.interfaces['CCU-Jack']); + $('#node-config-input-cuxdEnabled').prop('checked', ccu.interfaces.CuXD); + } + }); + }, + delay: 0, + minLength: 0, + }); + + $nodeConfigInputHost.on('change', () => { + if (!$nodeConfigInputHost.val().endsWith($nodeConfigInputName.val())) { + $nodeConfigInputName.val($nodeConfigInputHost.val()); + } + }); + + data.listen.forEach(addr => { + $nodeConfigInputRpcServerHost.append(''); + }); + + $nodeConfigInputRpcServerHost.val(this.rpcServerHost || data.listen[1]); + + if (!this.rpcBinPort) { + $('#node-config-input-rpcBinPort').val(data.ports[0]); + } + + if (!this.rpcXmlPort) { + $('#node-config-input-rpcXmlPort').val(data.ports[1]); + } + }); + + setTimeout(() => { + if (!this.host) { + $nodeConfigInputHost.focus(); + } + }, 250); + }, + }); + + /* eslint-disable no-unused-vars, no-undef, no-alert */ + function TLSSSL() { + if (document.getElementById('node-config-input-authentication').checked && !document.getElementById('node-config-input-tls').checked) { + document.getElementById('node-config-input-tls').checked = true; + alert('Authentication activated. \nWithout TLS/SSL your credentials would be sent unencrypted!'); + } + } + /* eslint-enable no-unused-vars, no-undef, no-alert */ +}()); diff --git a/src/nodes-html/ccu-connection/main.html b/src/nodes-html/ccu-connection/main.html new file mode 100644 index 0000000..64665c7 --- /dev/null +++ b/src/nodes-html/ccu-connection/main.html @@ -0,0 +1,180 @@ + + + diff --git a/src/nodes-html/ccu-display/editor.js b/src/nodes-html/ccu-display/editor.js new file mode 100644 index 0000000..f39db4a --- /dev/null +++ b/src/nodes-html/ccu-display/editor.js @@ -0,0 +1,225 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-display', { + category: 'ccu', + defaults: { + name: {value: ''}, + iface: {value: 'BidCos-RF', required: true}, + channel: {value: '', required: true}, + disable1: {value: false}, + line1: {value: ''}, + icon1: {value: ''}, + color1: {value: '0x80'}, + disable2: {value: false}, + line2: {value: ''}, + icon2: {value: ''}, + color2: {value: '0x80'}, + disable3: {value: false}, + line3: {value: ''}, + icon3: {value: ''}, + color3: {value: '0x80'}, + disable4: {value: false}, + line4: {value: ''}, + icon4: {value: ''}, + color4: {value: '0x80'}, + disable5: {value: false}, + line5: {value: ''}, + icon5: {value: ''}, + color5: {value: '0x80'}, + disable6: {value: false}, + line6: {value: ''}, + icon6: {value: ''}, + color6: {value: '0x80'}, + channelType: {value: ''}, + signal: {value: '0xF0'}, + repeat: {value: '0xD0'}, + pause: {value: '0xE0'}, + sound: {value: '0xC0'}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + }, + inputs: 1, + outputs: 0, + icon: 'ccu.png', + color: '#4691BA', + paletteLabel: 'display', + align: 'right', + label() { + return this.name || 'display'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditprepare() { + // get templates for line, color & icon + const $nodeLineX = $('#node-line-X')[0]; + const $nodeColorX = $('#node-color-X')[0]; + const $nodeIconX = $('#node-icon-X')[0]; + const $nodeDisableI = $('#node-disable-X')[0]; + + // replace X -> number and insert templates + for (let i = 6; i > 0; i--) { + let extraClass = 'HM-Dis-EP-WM55'; + if (i >= 4) { + extraClass = ''; + } + + const nodeIconI = $nodeIconX.innerHTML.replaceAll('X', i); + $nodeLineX.insertAdjacentHTML('afterend', '
' + nodeIconI + '
'); + $('#node-input-icon' + i)[0].value = this['icon' + i]; + + const nodeColorI = $nodeColorX.innerHTML.replaceAll('X', i); + $nodeLineX.insertAdjacentHTML('afterend', '
' + nodeColorI + '
'); + $('#node-input-color' + i)[0].value = this['color' + i]; + + const nodeLineI = $nodeLineX.innerHTML.replaceAll('X', i); + $nodeLineX.insertAdjacentHTML('afterend', '
' + nodeLineI + '
'); + $('#node-input-line' + i)[0].value = this['line' + i]; + + const nodeDisableI = $nodeDisableI.innerHTML.replaceAll('X', i); + $nodeLineX.insertAdjacentHTML('afterend', '
' + nodeDisableI + '
'); + $('#node-input-disable' + i).on('change', () => { + const dis = $('#node-input-disable' + i)[0].checked; + $('#node-input-icon' + i).prop('disabled', dis); + $('#node-input-color' + i).prop('disabled', dis); + $('#node-input-line' + i).prop('disabled', dis); + }); + + $('#node-input-disable' + i)[0].checked = this['disable' + i]; + } + + // remove templates + $nodeLineX.remove(); + $nodeColorX.remove(); + $nodeIconX.remove(); + $nodeDisableI.remove(); + + const $nodeInputName = $('#node-input-name'); + const $nodeInputChannel = $('#node-input-channel'); + const $nodeInputChannelType = $('#node-input-channelType'); + const $nodeInputCcuConfig = $('#node-input-ccuConfig'); + const $nodeInputIface = $('#node-input-iface'); + + let data; + + let ifacesLoaded = false; + let ifacesPending = false; + + function loadIfaces(iface, cb) { + if (ifacesPending) { + return; + } + + ifacesPending = true; + console.log('loadIfaces()'); + $nodeInputIface.html(''); + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId === '_ADD_') { + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + } else { + const url = 'ccu?config=' + nodeId + '&type=ifaces'; + $.getJSON(url, d => { + Object.keys(d).forEach(i => { + if (i !== 'ReGaHSS') { + $nodeInputIface.append('' + i + ''); + } + }); + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + }); + } + } + + function loadConfig() { + if (ifacesLoaded) { + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId && nodeId !== '__ADD__') { + console.log('loadConfig()'); + $.getJSON('ccu?type=display&config=' + nodeId + '&iface=' + $nodeInputIface.val(), d => { + data = d; + autoCompleteChannel(); + }); + } else { + autoCompleteChannel(); + } + } + } + + $nodeInputCcuConfig.change(() => { + console.log('$nodeInputCcuConfig change'); + loadIfaces(this.iface, () => { + ifacesLoaded = true; + $nodeInputIface.removeAttr('disabled'); + loadConfig(); + }); + }); + + $nodeInputIface.change(() => { + if (ifacesLoaded) { + loadConfig(); + } + }); + + $nodeInputChannel.autocomplete({ + source: [], + close() { + const n = $nodeInputChannel.val().split(' '); + const channel = n.shift(); + $nodeInputChannel.val(channel); + if (data && data[channel]) { + $nodeInputChannelType.val(data[channel].type).trigger('change'); + } + + if (!$nodeInputName.val()) { + $nodeInputName.val(n.join(' ')); + } + }, + delay: 0, + minLength: 0, + }); + + $nodeInputChannel.on('focus', () => { + $nodeInputChannel.autocomplete('search'); + }); + + function autoCompleteChannel() { + const channels = []; + if (data) { + Object.keys(data).forEach(addr => { + if (/:\d+$/.test(addr)) { + if (data[addr].name) { + addr += ' ' + data[addr].name; + } + + channels.push(addr); + } + }); + } + + console.log('autoCompleteChannel()', channels.length); + $nodeInputChannel.autocomplete('option', 'source', channels); + } + + $nodeInputChannel.change(() => { + const channel = $nodeInputChannel.val(); + if (data && data[channel]) { + $nodeInputChannelType.val(data[channel]); + } + }); + + $('.SUBMIT').hide(); + $('.SUBMIT.' + $nodeInputChannelType.val()).show(); + $nodeInputChannelType.change(() => { + $('.SUBMIT').hide(); + $('.SUBMIT.' + $nodeInputChannelType.val()).show(); + }); + }, + }); +}()); diff --git a/src/nodes-html/ccu-display/main.html b/src/nodes-html/ccu-display/main.html new file mode 100644 index 0000000..f31ad45 --- /dev/null +++ b/src/nodes-html/ccu-display/main.html @@ -0,0 +1,151 @@ + diff --git a/src/nodes-html/ccu-get-value/editor.js b/src/nodes-html/ccu-get-value/editor.js new file mode 100644 index 0000000..24e08d3 --- /dev/null +++ b/src/nodes-html/ccu-get-value/editor.js @@ -0,0 +1,420 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-get-value', { + color: '#c2d5e4', + category: 'ccu', + defaults: { + name: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + iface: {value: 'ReGaHSS'}, + channel: {value: ''}, + sysvar: {value: ''}, + sysvarProperty: {value: 'value'}, + datapoint: {value: ''}, + datapointProperty: {value: 'value'}, + setProp: {value: 'payload'}, + setPropType: {value: 'msg'}, + }, + inputs: 1, + outputs: 1, + icon: 'ccu.png', + paletteLabel() { + return 'get value'; + }, + label() { + return this.name || 'get value'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditprepare() { + const node = this; + + const $nodeSetProp = $('#node-input-setProp'); + const $nodeSetPropType = $('#node-input-setPropType'); + const $nodeInputIface = $('#node-input-iface'); + const $nodeInputCcuConfig = $('#node-input-ccuConfig'); + const $nodeInputChannel = $('#node-input-channel'); + const $nodeInputSysvar = $('#node-input-sysvar'); + const $nodeInputSysvarProperty = $('#node-input-sysvarProperty'); + const $nodeInputDatapoint = $('#node-input-datapoint'); + const $nodeInputDatapointProperty = $('#node-input-datapointProperty'); + const $configLookupBtn = $('#config-lookup-btn'); + const $configLookupTree = $('#config-lookup-tree'); + + let data; + let channelArray; + + let ifacesLoaded = false; + let ifacesPending = false; + + if (!this.setProp) { + this.setProp = 'payload'; + $nodeSetProp.val('payload'); + } + + if (!this.setPropType) { + this.setPropType = 'msg'; + } + + $nodeSetProp.typedInput({ + default: this.setPropType, + types: [{ + value: 'cmsg', + label: 'msg', + hasValue: false, + }, 'msg', 'flow', 'global'], + typeField: $nodeSetPropType, + }); + + $('.form-row.datapoint').hide(); + $('.form-row.sysvar').hide(); + + const sysvarProperties = ['value', 'valueEnum', 'ts', 'lc']; + const datapointProperties = ['value', 'ts', 'lc', 'working', 'direction', 'all']; + + sysvarProperties.forEach(value => $nodeInputSysvarProperty.append( + $('').val(value).text(node._('node-red-contrib-ccu/ccu-connection:common.label.' + value)), + )); + datapointProperties.forEach(value => $nodeInputDatapointProperty.append( + $('').val(value).text(node._('node-red-contrib-ccu/ccu-connection:common.label.' + value)), + )); + $nodeInputSysvarProperty.val(node.sysvarProperty || sysvarProperties[0]); + $nodeInputDatapointProperty.val(node.datapointProperty || datapointProperties[0]); + + function loadIfaces(iface, cb) { + if (ifacesPending) { + return; + } + + ifacesPending = true; + console.log('loadIfaces()'); + + $nodeInputIface.html(''); + + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId !== '_ADD_') { + const url = 'ccu?config=' + nodeId + '&type=ifaces'; + $.getJSON(url, d => { + $nodeInputIface.append(''); + Object.keys(d).forEach(i => { + $nodeInputIface.append('' + i + ''); + }); + + $nodeInputIface.removeAttr('disabled'); + $configLookupBtn.removeAttr('disabled'); + + $nodeInputIface.trigger('change'); + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + }); + $.getJSON('ccu?type=sysvar&config=' + nodeId, data => { + $nodeInputSysvar.html(''); + if (data) { + Object.keys(data).forEach(name => { + $nodeInputSysvar.append(''); + }); + } + }); + } + } + + function loadConfig() { + if (!ifacesLoaded) { + return; + } + + console.log('loadConfig()'); + const nodeId = $nodeInputCcuConfig.val(); + const url = 'ccu?config=' + nodeId + '&type=channels&iface=' + $nodeInputIface.val(); + $.getJSON(url, d => { + data = d; + autocompleteChannel(); + autocompleteDatapoint(); + }); + } + + $nodeInputCcuConfig.change(() => { + console.log('$nodeInputCcuConfig change'); + loadIfaces(this.iface, () => { + ifacesLoaded = true; + loadConfig(); + }); + }); + + $nodeInputChannel.autocomplete({ + source: [], + close() { + autocompleteDatapoint(); + }, + delay: 0, + minLength: 0, + }); + + $nodeInputChannel.on('focus', () => { + $nodeInputChannel.autocomplete('search'); + }); + $nodeInputDatapoint.autocomplete({ + source: [], + delay: 0, + minLength: 0, + }); + + $nodeInputDatapoint.on('focus', () => { + $nodeInputDatapoint.autocomplete('search'); + }); + + $nodeInputChannel.on('blur', autocompleteDatapoint); + + function autocompleteChannel() { + if (!data) { + return; + } + + const channels = []; + Object.keys(data).forEach(addr => { + if (/:\d+$/.test(addr)) { + if (data[addr].name) { + addr += ' ' + data[addr].name; + } + + channels.push(addr); + } + }); + channels.sort((a, b) => a.localeCompare(b)); + $nodeInputChannel.autocomplete('option', 'source', channels); + + if (!data[$nodeInputChannel.val().split(' ')[0]]) { + $nodeInputChannel.val(''); + $nodeInputDatapoint.val(''); + } + } + + function autocompleteDatapoint() { + const c = data[$nodeInputChannel.val().split(' ')[0]]; + if (c) { + c.datapoints.sort((a, b) => a.localeCompare(b)); + $nodeInputDatapoint.autocomplete('option', 'source', c.datapoints); + if (!c.datapoints.includes($nodeInputDatapoint.val())) { + $nodeInputDatapoint.val('').autocomplete('search'); + } + } + } + + $nodeInputIface.change(() => { + console.log('$nodeInputIface change'); + if ($nodeInputIface.val() === 'ReGaHSS') { + $('.form-row.datapoint').hide(); + $('.form-row.sysvar').show(); + } else if ($nodeInputIface.val()) { + $('.form-row.sysvar').hide(); + $('.form-row.datapoint').show(); + } + + loadConfig(); + }); + + const $dialog = $('#dialog-select-datapoint').dialog({ + autoOpen: false, + height: 400, + width: 600, + modal: true, + }); + + if (typeof $().treeList === 'undefined') { + // maybe wrong node-red version + $configLookupBtn.hide(); + } else { + $configLookupTree.css({width: '100%', height: '100%'}).treeList({ + data: [{ + id: 'rooms', + label: node._('node-red-contrib-ccu/ccu-connection:common.label.rooms'), + icon: 'fa fa-home fa-fw', + children: (done, item) => loadTreeData('rooms', done, item), + }, { + id: 'functions', + label: node._('node-red-contrib-ccu/ccu-connection:common.label.functions'), + icon: 'fa fa-cogs fa-fw', + children: (done, item) => loadTreeData('functions', done, item), + }, { + id: 'all', + label: node._('node-red-contrib-ccu/ccu-connection:common.label.allDevices'), + icon: 'fa fa-slack fa-fw', + children(done) { + const nodeId = $nodeInputCcuConfig.val(); + const url = 'ccu?config=' + nodeId + '&type=ifaces'; + $.getJSON(url, d => { + const enabledInterfaces = []; + Object.keys(d).forEach(ifId => { + const iface = d[ifId]; + if (ifId !== 'ReGaHSS' && iface.enabled) { + enabledInterfaces.push({ + id: ifId, + label: ifId, + icon: 'fa fa-empire fa-fw', + children: (done, item) => loadTreeData('tree', done, item, ifId), + }); + } + }); + done(enabledInterfaces); + }); + }, + }, { + id: 'sysVars', + label: node._('node-red-contrib-ccu/ccu-connection:common.label.sysVars'), + icon: 'fa fa-cog fa-fw', + children: (done, item) => loadTreeData('sysvar', done, item), + }], + }); + } + + $configLookupTree.on('treelistselect', (event, item) => { + if (item && item.iface && item.channel && item.datapoint && item.property) { + $('.form-row.sysvar').hide(); + $('.form-row.datapoint').show(); + $nodeInputIface.val(item.iface); + $nodeInputChannel.val(item.channel); + $nodeInputDatapoint.val(item.datapoint); + $nodeInputDatapointProperty.val(item.property); + $dialog.dialog('close'); + loadConfig(); + } else if (item && item.sysvar && item.property) { + $('.form-row.datapoint').hide(); + $('.form-row.sysvar').show(); + $nodeInputIface.val('ReGaHSS'); + $nodeInputSysvar.val(item.sysvar); + $nodeInputSysvarProperty.val(item.property); + $dialog.dialog('close'); + loadConfig(); + } + }); + + $configLookupBtn.click(() => { + $dialog.dialog('open'); + if (!channelArray) { + channelArray = []; + const url = 'ccu?config=' + $nodeInputCcuConfig.val() + '&type=tree'; + $.getJSON(url, data => { + Object.keys(data).forEach(addr => { + channelArray.push(data[addr]); + if (data[addr].children) { + data[addr].children.forEach(dp => { + if (!dp.children) { + dp.children = getDPProp(dp); + } + }); + } + }); + }); + } + }); + + function getDPProp(dp) { + const result = []; + + datapointProperties.forEach(property => { + result.push({ + id: dp.id + '.' + property, + label: node._('node-red-contrib-ccu/ccu-connection:common.label.' + property), + icon: 'fa fa-tasks fa-fw', + iface: dp.iface, + channel: dp.channel, + datapoint: dp.label, + property, + }); + }); + + return result; + } + + function loadTreeData(type, done, item, ifId) { + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId === '_ADD_') { + return; + } + + let url = 'ccu?config=' + nodeId + '&type=' + type; + + if (type === 'tree') { + url += '&iface=' + ifId; + $.getJSON(url, devices => { + const deviceIDs = []; + Object.keys(devices).forEach(id => { + const dev = devices[id]; + dev.children.forEach(ch => { + if (ch.children) { + ch.children.sort((a, b) => a.label.localeCompare(b.label)); + ch.children.forEach(dp => { + if (!dp.children) { + dp.children = getDPProp(dp); + } + }); + } + }); + deviceIDs.push(dev); + }); + deviceIDs.sort((a, b) => a.label.localeCompare(b.label)); + done(deviceIDs); + }); + } else if (type === 'sysvar') { + $.getJSON(url, d => { + const sysVars = []; + Object.keys(d).forEach(id => { + sysVars.push({ + id, + label: id, + icon: 'fa fa-tags fa-fw', + children(done) { + const result = []; + sysvarProperties.forEach(property => { + result.push({ + label: node._('node-red-contrib-ccu/ccu-connection:common.label.' + property), + icon: 'fa fa-tag fa-fw', + sysvar: id, + property, + }); + }); + done(result); + }, + }); + }); + done(sysVars); + }); + } else { + const url = 'ccu?config=' + nodeId + '&type=' + type; + $.getJSON(url, r => { + const types = []; + if (r && r[type]) { + for (let index = 0; index < r[type].length; index++) { + const lbl = r[type][index]; + types.push({ + label: lbl, + children(done) { + const channels = []; + if (channelArray) { + channelArray.forEach(ch => { + if (ch[type] && ch[type].includes(lbl)) { + channels.push(ch); + } + }); + } + + channels.sort((a, b) => a.label.localeCompare(b.label)); + done(channels); + }, + }); + } + } + + done(types); + }); + } + } + }, + }); +}()); diff --git a/src/nodes-html/ccu-get-value/main.html b/src/nodes-html/ccu-get-value/main.html new file mode 100644 index 0000000..a3d0873 --- /dev/null +++ b/src/nodes-html/ccu-get-value/main.html @@ -0,0 +1,71 @@ + + + + + diff --git a/src/nodes-html/ccu-mqtt/editor.js b/src/nodes-html/ccu-mqtt/editor.js new file mode 100644 index 0000000..51c60ac --- /dev/null +++ b/src/nodes-html/ccu-mqtt/editor.js @@ -0,0 +1,46 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-mqtt', { + category: 'ccu', + defaults: { + name: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + + cache: {value: false}, + + topicOutputEvent: {value: 'hm/status/${channelName}/${datapoint}'}, // eslint-disable-line no-template-curly-in-string + topicInputSetValue: {value: 'hm/set/${channelNameOrAddress}/${datapoint}'}, // eslint-disable-line no-template-curly-in-string + + topicOutputSysvar: {value: 'hm/status/${name}'}, // eslint-disable-line no-template-curly-in-string + topicInputSysvar: {value: 'hm/set/${name}'}, // eslint-disable-line no-template-curly-in-string + + topicInputPutParam: {value: 'hm/paramset/${channelNameOrAddress}/${paramset}/${param}'}, // eslint-disable-line no-template-curly-in-string + topicInputPutParamset: {value: 'hm/paramset/${channelNameOrAddress}/${paramset}'}, // eslint-disable-line no-template-curly-in-string + + topicInputRpc: {value: 'hm/rpc/${iface}/${method}/${command}/${callid}'}, // eslint-disable-line no-template-curly-in-string + topicOutputRpc: {value: 'hm/response/${callid}'}, // eslint-disable-line no-template-curly-in-string + + topicCounters: {value: 'hm/status/counter/${iface}/${rxtx}'}, // eslint-disable-line no-template-curly-in-string + + payloadOutput: {value: 'mqsh-extended'}, + }, + inputs: 1, + outputs: 1, + icon: 'ccu.png', + color: '#c2d5e4', + paletteLabel: 'mqtt', + align: 'right', + label() { + return this.name || 'mqtt'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditsave() { + console.log(this); + }, + }); +}()); diff --git a/src/nodes-html/ccu-mqtt/main.html b/src/nodes-html/ccu-mqtt/main.html new file mode 100644 index 0000000..a208343 --- /dev/null +++ b/src/nodes-html/ccu-mqtt/main.html @@ -0,0 +1,117 @@ + + + + + diff --git a/src/nodes-html/ccu-poll/editor.js b/src/nodes-html/ccu-poll/editor.js new file mode 100644 index 0000000..c424806 --- /dev/null +++ b/src/nodes-html/ccu-poll/editor.js @@ -0,0 +1,26 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-poll', { + category: 'ccu', + defaults: { + name: {value: ''}, + script: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + }, + inputs: 1, + outputs: 0, + icon: 'ccu.png', + color: '#8BB9D2', + paletteLabel: 'poll', + align: 'right', + label() { + return this.name || 'poll'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + }); +}()); diff --git a/src/nodes-html/ccu-poll/main.html b/src/nodes-html/ccu-poll/main.html new file mode 100644 index 0000000..9ee61aa --- /dev/null +++ b/src/nodes-html/ccu-poll/main.html @@ -0,0 +1,29 @@ + + + + + diff --git a/src/nodes-html/ccu-program/editor.js b/src/nodes-html/ccu-program/editor.js new file mode 100644 index 0000000..cd27e1d --- /dev/null +++ b/src/nodes-html/ccu-program/editor.js @@ -0,0 +1,60 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-program', { + category: 'ccu', + defaults: { + name: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + topic: {value: 'ReGaHSS/${Name}'}, // eslint-disable-line no-template-curly-in-string + }, + inputs: 1, + outputs: 1, + icon: 'ccu.png', + color: '#8BB9D2', + paletteLabel: 'program', + align: 'right', + label() { + return this.name || 'program'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditprepare() { + const cname = this.name; + function getConf(nodeId) { + $('#select-input-name').html('').hide(); + $('#node-input-name').val('').show(); + + if (nodeId && nodeId !== '_ADD_') { + $.getJSON('ccu?type=program&config=' + nodeId, data => { + $('#select-input-name').html(''); + if (data) { + Object.keys(data).forEach(name => { + $('#select-input-name').append(''); + }); + $('#node-input-name').hide(); + $('#select-input-name').show(); + } else { + $('#select-input-name').hide(); + $('#node-input-name').show(); + } + }); + } + } + + getConf(this.ccuConfig); + + $('#node-input-ccuConfig').change(() => { + getConf($('#node-input-ccuConfig').val()); + }); + }, + oneditsave() { + if ($('#select-input-name').is(':visible')) { + $('#node-input-name').val($('#select-input-name').val()); + } + }, + }); +}()); diff --git a/src/nodes-html/ccu-program/main.html b/src/nodes-html/ccu-program/main.html new file mode 100644 index 0000000..0d29495 --- /dev/null +++ b/src/nodes-html/ccu-program/main.html @@ -0,0 +1,92 @@ + + + + + diff --git a/src/nodes-html/ccu-rpc-event/editor.js b/src/nodes-html/ccu-rpc-event/editor.js new file mode 100644 index 0000000..c2fbb9f --- /dev/null +++ b/src/nodes-html/ccu-rpc-event/editor.js @@ -0,0 +1,500 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-rpc-event', { + category: 'ccu', + defaults: { + name: {value: ''}, + iface: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + rooms: {value: ''}, + roomsRx: {value: 'str'}, + functions: {value: ''}, + functionsRx: {value: 'str'}, + device: {value: ''}, + deviceRx: {value: 'str'}, + deviceName: {value: ''}, + deviceNameRx: {value: 'str'}, + deviceType: {value: ''}, + deviceTypeRx: {value: 'str'}, + channel: {value: ''}, + channelRx: {value: 'str'}, + channelName: {value: ''}, + channelNameRx: {value: 'str'}, + channelType: {value: ''}, + channelTypeRx: {value: 'str'}, + channelIndex: {value: ''}, + channelIndexRx: {value: 'str'}, + datapoint: {value: ''}, + datapointRx: {value: 'str'}, + change: {value: false}, + working: {value: false}, + cache: {value: false}, + topic: {value: '${CCU}/${Interface}/${channelName}/${datapoint}'}, // eslint-disable-line no-template-curly-in-string + }, + inputs: 0, + outputs: 1, + icon: 'ccu.png', + color: '#4691BA', + paletteLabel: 'rpc event', + label() { + return this.name || ('rpc event'); + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditprepare() { + const that = this; + const $nodeInputIface = $('#node-input-iface'); + const $nodeInputCcuConfig = $('#node-input-ccuConfig'); + + let data; + + let ifacesLoaded = false; + let ifacesPending = false; + + $('.filter').each(function () { + const id = $(this).prop('id'); + const $type = $('#' + id + 'Rx'); + const name = id.split('-').pop() + 'Rx'; + $type.val(that[name]); + + const $this = $(this); + + $this.typedInput({ + typeField: $type, + types: ['str', 're'], + }).typedInput('width', '70%'); + + const $input = $this.parent().find('.red-ui-typedInput-input'); + + $input.autocomplete({ + source: [], + close() { + $input.trigger('change'); + autocomplete(); + }, + delay: 0, + minLength: 0, + }); + + $input.on('focus', () => { + $input.autocomplete('search'); + }); + + $this.on('change', (type, value) => { + if (value === 're') { + $input.autocomplete('disable'); + } else { + $input.autocomplete('enable'); + } + }); + }); + + function loadIfaces(iface, cb) { + if (ifacesPending) { + return; + } + + ifacesPending = true; + console.log('loadIfaces()'); + $nodeInputIface.html(''); + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId === '_ADD_') { + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + } else { + const url = 'ccu?config=' + nodeId + '&type=ifaces'; + $.getJSON(url, d => { + Object.keys(d).forEach(i => { + if (i !== 'ReGaHSS') { + $nodeInputIface.append('' + i + ''); + } + }); + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + }); + } + } + + $nodeInputCcuConfig.change(() => { + loadIfaces(this.iface, () => { + ifacesLoaded = true; + $nodeInputIface.removeAttr('disabled'); + loadConfig(); + }); + }); + + function loadConfig() { + if (ifacesLoaded) { + console.log('loadConfig'); + const nodeId = $nodeInputCcuConfig.val(); + const url = 'ccu?config=' + nodeId; + $.getJSON(url, d => { + data = d; + processChannelAssignments(); + console.log('data', data); + autocomplete(); + }); + } + } + + function processChannelAssignments() { + data.roomChannels = {}; + data.roomDevices = {}; + data.functionChannels = {}; + data.functionDevices = {}; + data.nameChannels = {}; + data.nameDevices = {}; + + console.log(data); + + Object.keys(data.channelRooms).forEach(channel => { + const room = data.channelRooms[channel]; + const device = channel.split(':')[0]; + if (data.roomChannels[room]) { + data.roomChannels[room].push(channel); + } else { + data.roomChannels[room] = [channel]; + } + + if (!data.roomDevices[room]) { + data.roomDevices[room] = [device]; + } else if (!data.roomDevices[room].includes(device)) { + data.roomDevices[room].push(device); + } + }); + Object.keys(data.channelFunctions).forEach(channel => { + const func = data.channelFunctions[channel]; + const device = channel.split(':')[0]; + if (data.functionChannels[func]) { + data.functionChannels[func].push(channel); + } else { + data.functionChannels[func] = [channel]; + } + + if (!data.functionDevices[func]) { + data.functionDevices[func] = [device]; + } else if (!data.functionDevices[func].includes(device)) { + data.functionDevices[func].push(device); + } + }); + Object.keys(data.channelNames).forEach(channel => { + const name = data.channelNames[channel]; + if (/:\d+$/.test(channel)) { + data.nameChannels[name] = channel; + } else { + data.nameDevices[name] = channel; + } + }); + } + + $('#node-input-iface').change(autocomplete); + $('#node-input-room').change(autocomplete); + $('#node-input-function').change(autocomplete); + $('#node-input-device').change(autocomplete); + $('#node-input-deviceName').change(autocomplete); + $('#node-input-deviceType').change(autocomplete); + $('#node-input-channel').change(autocomplete); + $('#node-input-channelName').change(autocomplete); + $('#node-input-channelType').change(autocomplete); + $('#node-input-datapoint').change(autocomplete); + + function paramsetName(iface, device, paramset) { + let cType = ''; + let d; + if (device) { + if (device.PARENT) { + // channel + cType = device.TYPE; + d = data.metadata.devices[iface][device.PARENT]; + } else { + // device + d = device; + } + + return [iface, d.TYPE, d.FIRMWARE, d.VERSION, cType, paramset].join('/'); + } + } + + function autocomplete() { + if (!data) { + return; + } + + const iface = $('#node-input-iface').val(); + const rooms = $('#node-input-rooms').val() ? [$('#node-input-rooms').val()] : []; + const funcs = $('#node-input-functions').val() ? [$('#node-input-functions').val()] : []; + const devices = $('#node-input-device').val() ? [$('#node-input-device').val()] : []; + const deviceNames = $('#node-input-deviceName').val() ? [$('#node-input-deviceName').val()] : []; + const deviceTypes = $('#node-input-deviceType').val() ? [$('#node-input-deviceType').val()] : []; + const channels = $('#node-input-channel').val() ? [$('#node-input-channel').val()] : []; + const channelNames = $('#node-input-channelName').val() ? [$('#node-input-channelName').val()] : []; + const channelTypes = $('#node-input-channelType').val() ? [$('#node-input-channelType').val()] : []; + //const channelIndexes = $('#node-input-channelIndex').val() ? [$('#node-input-channelIndex').val()] : []; + const datapoints = $('#node-input-datapoint').val() ? [$('#node-input-datapoint').val()] : []; + + const lists = { + device: [], + deviceName: [], + deviceType: [], + channel: [], + channelName: [], + channelType: [], + channelIndex: [], + datapoint: [], + }; + + function composeLists(iface) { + if (!data.metadata.devices[iface]) { + return; + } + + if (devices.length === 0) { + if (deviceNames.length > 0) { + deviceNames.forEach(deviceName => { + if (deviceName) { + devices.push(data.nameDevices[deviceName]); + } + }); + } else if (deviceTypes.length > 0) { + deviceTypes.forEach(deviceType => { + if (data.metadata.types[iface][deviceType]) { + data.metadata.types[iface][deviceType].forEach(device => { + if (device && !devices.includes(device)) { + devices.push(device); + } + }); + } + }); + } + } + + if (channels.length === 0) { + if (channelNames.length > 0) { + channelNames.forEach(channelName => { + channels.push(data.nameChannels[channelName]); + }); + } else if (channelTypes.length > 0) { + channelTypes.forEach(channelType => { + if (data.metadata.types[iface][channelType]) { + data.metadata.types[iface][channelType].forEach(channel => { + if (channel && !channels.includes(channel)) { + channels.push(channel); + } + }); + } + }); + } + } + + console.log('autocomplete', {iface, rooms, funcs, devices, deviceNames, deviceTypes, channels, channelNames, channelTypes, datapoints}); + + function checkDatapoint(datapoint) { + let deviceCheck = false; + let channelCheck = false; + + if (channels && channels.length > 0) { + channels.forEach(channel => { + if (data.metadata.devices[iface][channel]) { + const psName = paramsetName(iface, data.metadata.devices[iface][channel], 'VALUES'); + if (Object.keys(data.paramsetDescriptions[psName]).includes(datapoint)) { + channelCheck = true; + } + } else { + channelCheck = true; + } + }); + deviceCheck = true; + } else { + if (devices && devices.length > 0) { + devices.forEach(device => { + if (data.metadata.devices[iface][device]) { + data.metadata.devices[iface][device].CHILDREN.forEach(channel => { + const psName = paramsetName(iface, data.metadata.devices[iface][channel], 'VALUES'); + if (data.paramsetDescriptions[psName]) { + Object.keys(data.paramsetDescriptions[psName]).forEach(dp => { + if (datapoint === dp) { + deviceCheck = true; + } + }); + } + }); + } else { + deviceCheck = true; + } + }); + } else { + deviceCheck = true; + } + + channelCheck = true; + } + + return channelCheck && deviceCheck; + } + + function checkChannel(channel) { + if (!channel || !data.metadata.devices[iface][channel]) { + return true; + } + + let roomCheck = false; + let functionCheck = false; + let deviceCheck = false; + + if (rooms.length > 0) { + rooms.forEach(room => { + if (data.roomChannels[room].includes(channel)) { + roomCheck = true; + } + }); + } else { + roomCheck = true; + } + + if (funcs.length > 0) { + funcs.forEach(func => { + if (data.functionChannels[func].includes(channel)) { + functionCheck = true; + } + }); + } else { + functionCheck = true; + } + + if (devices.length > 0) { + devices.forEach(device => { + if (channel.startsWith(device)) { + deviceCheck = true; + } + }); + } else { + deviceCheck = true; + } + + return roomCheck && functionCheck && deviceCheck; + } + + function checkDevice(device) { + if (!device || !data.metadata.devices[iface][device]) { + return true; + } + + let roomCheck = false; + let functionCheck = false; + + if (rooms.length > 0) { + rooms.forEach(room => { + if (data.roomDevices[room].includes(device)) { + roomCheck = true; + } + + roomCheck = roomCheck + || data.metadata.devices[iface][device].CHILDREN + .some(v => data.roomDevices[room].includes(v)); + }); + } else { + roomCheck = true; + } + + if (funcs.length > 0) { + funcs.forEach(func => { + if (data.functionDevices[func].includes(device)) { + functionCheck = true; + } + + functionCheck = functionCheck + || data.metadata.devices[iface][device].CHILDREN + .some(v => data.functionDevices[func].includes(v)); + }); + } else { + functionCheck = true; + } + + return roomCheck && functionCheck; + } + + Object.keys(data.metadata.devices[iface]).forEach(dev => { + if (/:\d+$/.test(dev)) { + if (!checkChannel(dev)) { + return; + } + + lists.channel.push(dev); + lists.channelName.push(data.channelNames[dev]); + if (!lists.channelType.includes(data.metadata.devices[iface][dev].TYPE)) { + lists.channelType.push(data.metadata.devices[iface][dev].TYPE); + } + + if (!lists.channelIndex.includes(dev.split(':')[1])) { + lists.channelIndex.push(dev.split(':')[1]); + } + + const psName = paramsetName(iface, data.metadata.devices[iface][dev], 'VALUES'); + if (data.paramsetDescriptions[psName]) { + Object.keys(data.paramsetDescriptions[psName]).forEach(dp => { + if (dp && !lists.datapoint.includes(dp) && checkDatapoint(dp)) { + lists.datapoint.push(dp); + } + }); + } + } else { + if (!checkDevice(dev)) { + return; + } + + lists.device.push(dev); + lists.deviceName.push(data.channelNames[dev]); + if (!lists.deviceType.includes(data.metadata.devices[iface][dev].TYPE)) { + lists.deviceType.push(data.metadata.devices[iface][dev].TYPE); + + data.metadata.devices[iface][dev].CHILDREN.forEach(channel => { + const psName = paramsetName(iface, data.metadata.devices[iface][channel], 'VALUES'); + if (data.paramsetDescriptions[psName]) { + Object.keys(data.paramsetDescriptions[psName]).forEach(dp => { + if (!lists.datapoint.includes(dp) && checkDatapoint(dp)) { + lists.datapoint.push(dp); + } + }); + } + }); + } + } + }); + } + + if (iface) { + composeLists(iface); + } else { + Object.keys(data.metadata.devices).forEach(iface => { + composeLists(iface); + }); + } + + Object.keys(lists).forEach(key => { + lists[key].sort((a, b) => a.localeCompare(b)); + }); + + $('#node-input-rooms').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', data.rooms); + $('#node-input-functions').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', data.functions); + + $('#node-input-device').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.device); + $('#node-input-deviceName').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.deviceName); + $('#node-input-deviceType').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.deviceType); + + $('#node-input-channel').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.channel); + $('#node-input-channelName').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.channelName); + $('#node-input-channelType').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.channelType); + + $('#node-input-datapoint').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.datapoint); + } + }, + }); +}()); diff --git a/src/nodes-html/ccu-rpc-event/main.html b/src/nodes-html/ccu-rpc-event/main.html new file mode 100644 index 0000000..bc34e42 --- /dev/null +++ b/src/nodes-html/ccu-rpc-event/main.html @@ -0,0 +1,150 @@ + + + + + diff --git a/src/nodes-html/ccu-rpc/editor.js b/src/nodes-html/ccu-rpc/editor.js new file mode 100644 index 0000000..8870c50 --- /dev/null +++ b/src/nodes-html/ccu-rpc/editor.js @@ -0,0 +1,125 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-rpc', { + category: 'ccu', + defaults: { + name: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + iface: {value: 'BidCos-RF'}, + method: {value: ''}, + params: {value: '[]', validate(p) { + try { + return $.trim(p) === '' || Array.isArray(JSON.parse(p)); + } catch { + return false; + } + }}, + topic: {value: '${CCU}/${Interface}/${Method}'}, // eslint-disable-line no-template-curly-in-string + }, + inputs: 1, + outputs: 1, + icon: 'ccu.png', + color: '#4691BA', + paletteLabel: 'rpc', + align: 'right', + label() { + return this.name || this.method || 'rpc'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditprepare() { + const $nodeInputIface = $('#node-input-iface'); + const $nodeInputCcuConfig = $('#node-input-ccuConfig'); + + let ifacesLoaded = false; + let ifacesPending = false; + + function loadIfaces(iface, cb) { + if (ifacesPending) { + return; + } + + ifacesPending = true; + console.log('loadIfaces()'); + $nodeInputIface.html(''); + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId === '_ADD_') { + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + } else { + const url = 'ccu?config=' + nodeId + '&type=ifaces'; + $.getJSON(url, d => { + Object.keys(d).forEach(i => { + $nodeInputIface.append('' + i + ''); + }); + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + }); + } + } + + $nodeInputCcuConfig.change(() => { + console.log('$nodeInputCcuConfig change'); + loadIfaces(this.iface, () => { + ifacesLoaded = true; + $nodeInputIface.removeAttr('disabled'); + autocompleteMethods(); + }); + }); + + $('#node-input-method').autocomplete({ + source: [], + close() {}, + delay: 0, + minLength: 0, + }); + + $('#node-input-iface').change(() => { + if (ifacesLoaded) { + console.log('#node-input-iface change'); + autocompleteMethods(); + } + }); + + function autocompleteMethods() { + const iface = $('#node-input-iface').val(); + let methods; + switch (iface) { + case 'BidCos-RF': + // TODO remove hardcoded methods, use system.listMethods... + methods = ['abortDeleteDevice', 'activateLinkParamset', 'addDevice', 'addLink', 'addVirtualDeviceInstance', 'changeKey', 'clearConfigCache', 'deleteDevice', 'deleteVolatileMetadata', 'determineParameter', 'exit', 'getAllMetadata', 'getDeviceDescription', 'getInstallMode', 'getKeyMismatchDevice', 'getLinkInfo', 'getLinkPeers', 'getLinks', 'getMetadata', 'getParamset', 'getParamsetDescription', 'getParamsetId', 'getServiceMessages', 'getValue', 'getVersion', 'getVolatileMetadata', 'hasVolatileMetadata', 'init', 'listBidcosInterfaces', 'listDevices', 'listReplaceableDevices', 'listTeams', 'logLevel', 'ping', 'putParamset', 'refreshDeployedDeviceFirmwareList', 'removeLink', 'replaceDevice', 'reportValueUsage', 'restoreConfigToDevice', 'rssiInfo', 'setBidcosInterface', 'setInstallMode', 'setInterfaceClock', 'setLinkInfo', 'setRFLGWInfoLED', 'setTeam', 'setTempKey', 'setValue', 'setVolatileMetadata', 'system.listMethods', 'system.methodHelp', 'updateFirmware', 'system.multicall']; + break; + case 'BidCos-Wired': + methods = ['addLink', 'clearConfigCache', 'deleteDevice', 'getDeviceDescription', 'getLGWStatus', 'getLinkInfo', 'getLinkPeers', 'getLinks', 'getParamset', 'getParamsetDescription', 'getParamsetId', 'getValue', 'init', 'listDevices', 'listReplaceableDevices', 'logLevel', 'ping', 'putParamset', 'removeLink', 'replaceDevice', 'reportValueUsage', 'searchDevices', 'setLinkInfo', 'setValue', 'system.listMethods', 'system.methodHelp', 'updateFirmware', 'system.multicall']; + break; + case 'VirtualDevices': + methods = ['init', 'getParamsetDescription', 'getLinks', 'getDeviceDescription', 'getParamsetId', 'getParamset', 'putParamset', 'system.listMethods', 'listDevices', 'getValue', 'setValue', 'listReplaceableDevices', 'deleteDevice']; + break; + case 'ReGaHSS': + methods = ['deleteDevices', 'event', 'listDevices', 'newDevices', 'replaceDevice', 'reportValueUsage', 'setReadyConfig', 'system.listMethods', 'system.methodHelp', 'updateDevice', 'system.multicall']; + break; + case 'HmIP-RF': + // https://github.com/eq-3/occu/issues/53 + methods = ['system.methodHelp', 'getLinks', 'ping', 'getParamset', 'getDeviceDescription', 'listDevices', 'setInstallModeWithWhitelist', 'getParamsetDescription', 'getServiceMessages', 'installFirmware', 'getVersion', 'setInstallMode', 'init', 'system.multicall', 'removeLink', 'deleteDevice', 'putParamset', 'reportValueUsage', 'refreshDeployedDeviceFirmwareList', 'setLinkInfo', 'getParamsetId', 'getValue', 'setValue', 'system.listMethods', 'listBidcosInterfaces', 'addLink', 'getLinkInfo', 'getInstallMode']; + break; + case 'CUxD': + methods = ['system.listMethods', 'system.multicall', 'init', 'listDevices', 'deleteDevice', 'getDeviceDescription', 'getParamsetDescription', 'getParamset', 'putParamset', 'getValue', 'setValue']; + break; + default: + methods = []; + } + + console.log('autocompleteMethods', iface, methods.length); + $('#node-input-method').autocomplete('option', 'source', methods); + } + }, + }); +}()); diff --git a/src/nodes-html/ccu-rpc/main.html b/src/nodes-html/ccu-rpc/main.html new file mode 100644 index 0000000..3339fb7 --- /dev/null +++ b/src/nodes-html/ccu-rpc/main.html @@ -0,0 +1,114 @@ + + + + + diff --git a/src/nodes-html/ccu-script/editor.js b/src/nodes-html/ccu-script/editor.js new file mode 100644 index 0000000..04e0e54 --- /dev/null +++ b/src/nodes-html/ccu-script/editor.js @@ -0,0 +1,27 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-script', { + category: 'ccu', + defaults: { + name: {value: ''}, + script: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + topic: {value: '${CCU}/${Interface}'}, // eslint-disable-line no-template-curly-in-string + }, + inputs: 1, + outputs: 1, + icon: 'ccu.png', + align: 'right', + color: '#8BB9D2', + paletteLabel: 'script', + label() { + return this.name || 'script'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + }); +}()); diff --git a/src/nodes-html/ccu-script/main.html b/src/nodes-html/ccu-script/main.html new file mode 100644 index 0000000..36627fe --- /dev/null +++ b/src/nodes-html/ccu-script/main.html @@ -0,0 +1,31 @@ + + + + + diff --git a/src/nodes-html/ccu-set-value/editor.js b/src/nodes-html/ccu-set-value/editor.js new file mode 100644 index 0000000..91ba779 --- /dev/null +++ b/src/nodes-html/ccu-set-value/editor.js @@ -0,0 +1,512 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + RED.nodes.registerType('ccu-set-value', { + category: 'ccu', + defaults: { + name: {value: ''}, + iface: {value: ''}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + rooms: {value: ''}, + roomsRx: {value: 'str'}, + functions: {value: ''}, + functionsRx: {value: 'str'}, + device: {value: ''}, + deviceRx: {value: 'str'}, + deviceName: {value: ''}, + deviceNameRx: {value: 'str'}, + deviceType: {value: ''}, + deviceTypeRx: {value: 'str'}, + channel: {value: ''}, + channelRx: {value: 'str'}, + channelName: {value: ''}, + channelNameRx: {value: 'str'}, + channelType: {value: ''}, + channelTypeRx: {value: 'str'}, + channelIndex: {value: ''}, + channelIndexRx: {value: 'str'}, + datapoint: {value: ''}, + datapointRx: {value: 'str'}, + force: {value: false}, + }, + inputs: 1, + outputs: 0, + icon: 'ccu.png', + color: '#4691BA', + paletteLabel: 'set value', + align: 'right', + label() { + return this.name || 'set value'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditprepare() { + const that = this; + const $nodeInputIface = $('#node-input-iface'); + const $nodeInputCcuConfig = $('#node-input-ccuConfig'); + + let data; + + let ifacesLoaded = false; + let ifacesPending = false; + + $('#node-input-force-dropdown').val(String(Boolean(this.force))); + + $('.filter').each(function () { + const id = $(this).prop('id'); + const $type = $('#' + id + 'Rx'); + const name = id.split('-').pop() + 'Rx'; + $type.val(that[name]); + + const $this = $(this); + + $this.typedInput({ + typeField: $type, + types: ['str', 're'], + }).typedInput('width', '70%'); + + const $input = $this.parent().find('.red-ui-typedInput-input'); + + $input.autocomplete({ + source: [], + close() { + $input.trigger('change'); + autocomplete(); + }, + delay: 0, + minLength: 0, + }); + + $input.on('focus', () => { + $input.autocomplete('search'); + }); + + $this.on('change', (type, value) => { + if (value === 're') { + $input.autocomplete('disable'); + } else { + $input.autocomplete('enable'); + } + }); + }); + + function loadIfaces(iface, cb) { + if (ifacesPending) { + return; + } + + ifacesPending = true; + console.log('loadIfaces()'); + $nodeInputIface.html(''); + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId === '_ADD_') { + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + } else { + const url = 'ccu?config=' + nodeId + '&type=ifaces'; + $.getJSON(url, d => { + Object.keys(d).forEach(i => { + if (i !== 'ReGaHSS') { + $nodeInputIface.append('' + i + ''); + } + }); + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + }); + } + } + + $nodeInputCcuConfig.change(() => { + loadIfaces(this.iface, () => { + ifacesLoaded = true; + $nodeInputIface.removeAttr('disabled'); + loadConfig(); + }); + }); + + function loadConfig() { + if (ifacesLoaded) { + console.log('loadConfig'); + const nodeId = $nodeInputCcuConfig.val(); + const url = 'ccu?config=' + nodeId; + $.getJSON(url, d => { + data = d; + processChannelAssignments(); + console.log('data', data); + autocomplete(); + }); + } + } + + function processChannelAssignments() { + data.roomChannels = {}; + data.roomDevices = {}; + data.functionChannels = {}; + data.functionDevices = {}; + data.nameChannels = {}; + data.nameDevices = {}; + + console.log(data); + + Object.keys(data.channelRooms).forEach(channel => { + const room = data.channelRooms[channel]; + const device = channel.split(':')[0]; + if (data.roomChannels[room]) { + data.roomChannels[room].push(channel); + } else { + data.roomChannels[room] = [channel]; + } + + if (!data.roomDevices[room]) { + data.roomDevices[room] = [device]; + } else if (!data.roomDevices[room].includes(device)) { + data.roomDevices[room].push(device); + } + }); + Object.keys(data.channelFunctions).forEach(channel => { + const func = data.channelFunctions[channel]; + const device = channel.split(':')[0]; + if (data.functionChannels[func]) { + data.functionChannels[func].push(channel); + } else { + data.functionChannels[func] = [channel]; + } + + if (!data.functionDevices[func]) { + data.functionDevices[func] = [device]; + } else if (!data.functionDevices[func].includes(device)) { + data.functionDevices[func].push(device); + } + }); + Object.keys(data.channelNames).forEach(channel => { + const name = data.channelNames[channel]; + if (/:\d+$/.test(channel)) { + data.nameChannels[name] = channel; + } else { + data.nameDevices[name] = channel; + } + }); + } + + $('#node-input-iface').change(autocomplete); + $('#node-input-room').change(autocomplete); + $('#node-input-function').change(autocomplete); + $('#node-input-device').change(autocomplete); + $('#node-input-deviceName').change(autocomplete); + $('#node-input-deviceType').change(autocomplete); + $('#node-input-channel').change(autocomplete); + $('#node-input-channelName').change(autocomplete); + $('#node-input-channelType').change(autocomplete); + $('#node-input-channelIndex').change(autocomplete); + $('#node-input-datapoint').change(autocomplete); + + function paramsetName(iface, device, paramset) { + let cType = ''; + let d; + if (device) { + if (device.PARENT) { + // channel + cType = device.TYPE; + d = data.metadata.devices[iface][device.PARENT]; + } else { + // device + d = device; + } + + return [iface, d.TYPE, d.FIRMWARE, d.VERSION, cType, paramset].join('/'); + } + } + + function autocomplete() { + if (!data) { + return; + } + + const iface = $('#node-input-iface').val(); + const rooms = $('#node-input-rooms').val() ? [$('#node-input-rooms').val()] : []; + const funcs = $('#node-input-functions').val() ? [$('#node-input-functions').val()] : []; + const devices = $('#node-input-device').val() ? [$('#node-input-device').val()] : []; + const deviceNames = $('#node-input-deviceName').val() ? [$('#node-input-deviceName').val()] : []; + const deviceTypes = $('#node-input-deviceType').val() ? [$('#node-input-deviceType').val()] : []; + const channels = $('#node-input-channel').val() ? [$('#node-input-channel').val()] : []; + const channelNames = $('#node-input-channelName').val() ? [$('#node-input-channelName').val()] : []; + const channelTypes = $('#node-input-channelType').val() ? [$('#node-input-channelType').val()] : []; + //const channelIndexes = $('#node-input-channelIndex').val() ? [$('#node-input-channelIndex').val()] : []; + const datapoints = $('#node-input-datapoint').val() ? [$('#node-input-datapoint').val()] : []; + + const lists = { + device: [], + deviceName: [], + deviceType: [], + channel: [], + channelName: [], + channelType: [], + channelIndex: [], + datapoint: [], + }; + + function composeLists(iface) { + if (!data.metadata.devices[iface]) { + return; + } + + if (devices.length === 0) { + if (deviceNames.length > 0) { + deviceNames.forEach(deviceName => { + if (deviceName) { + devices.push(data.nameDevices[deviceName]); + } + }); + } else if (deviceTypes.length > 0) { + deviceTypes.forEach(deviceType => { + if (data.metadata.types[iface][deviceType]) { + data.metadata.types[iface][deviceType].forEach(device => { + if (device && !devices.includes(device)) { + devices.push(device); + } + }); + } + }); + } + } + + if (channels.length === 0) { + if (channelNames.length > 0) { + channelNames.forEach(channelName => { + channels.push(data.nameChannels[channelName]); + }); + } else if (channelTypes.length > 0) { + channelTypes.forEach(channelType => { + if (data.metadata.types[iface][channelType]) { + data.metadata.types[iface][channelType].forEach(channel => { + if (channel && !channels.includes(channel)) { + channels.push(channel); + } + }); + } + }); + } + } + + console.log('autocomplete', {iface, rooms, funcs, devices, deviceNames, deviceTypes, channels, channelNames, channelTypes, datapoints}); + + function checkDatapoint(datapoint) { + let deviceCheck = false; + let channelCheck = false; + + if (channels && channels.length > 0) { + channels.forEach(channel => { + if (data.metadata.devices[iface][channel]) { + const psName = paramsetName(iface, data.metadata.devices[iface][channel], 'VALUES'); + if (Object.keys(data.paramsetDescriptions[psName]).includes(datapoint)) { + channelCheck = true; + } + } else { + channelCheck = true; + } + }); + deviceCheck = true; + } else { + if (devices && devices.length > 0) { + devices.forEach(device => { + if (data.metadata.devices[iface][device]) { + data.metadata.devices[iface][device].CHILDREN.forEach(channel => { + const psName = paramsetName(iface, data.metadata.devices[iface][channel], 'VALUES'); + if (data.paramsetDescriptions[psName]) { + Object.keys(data.paramsetDescriptions[psName]).forEach(dp => { + if (datapoint === dp) { + deviceCheck = true; + } + }); + } + }); + } else { + deviceCheck = true; + } + }); + } else { + deviceCheck = true; + } + + channelCheck = true; + } + + return channelCheck && deviceCheck; + } + + function checkChannel(channel) { + if (!channel || !data.metadata.devices[iface][channel]) { + return true; + } + + let roomCheck = false; + let functionCheck = false; + let deviceCheck = false; + + if (rooms.length > 0) { + rooms.forEach(room => { + if (data.roomChannels[room].includes(channel)) { + roomCheck = true; + } + }); + } else { + roomCheck = true; + } + + if (funcs.length > 0) { + funcs.forEach(func => { + if (data.functionChannels[func].includes(channel)) { + functionCheck = true; + } + }); + } else { + functionCheck = true; + } + + if (devices.length > 0) { + devices.forEach(device => { + if (channel.startsWith(device)) { + deviceCheck = true; + } + }); + } else { + deviceCheck = true; + } + + return roomCheck && functionCheck && deviceCheck; + } + + function checkDevice(device) { + if (!device || !data.metadata.devices[iface][device]) { + return true; + } + + let roomCheck = false; + let functionCheck = false; + + if (rooms.length > 0) { + rooms.forEach(room => { + if (data.roomDevices[room].includes(device)) { + roomCheck = true; + } + + roomCheck = roomCheck + || data.metadata.devices[iface][device].CHILDREN + .some(v => data.roomDevices[room].includes(v)); + }); + } else { + roomCheck = true; + } + + if (funcs.length > 0) { + funcs.forEach(func => { + if (data.functionDevices[func].includes(device)) { + functionCheck = true; + } + + functionCheck = functionCheck + || data.metadata.devices[iface][device].CHILDREN + .some(v => data.functionDevices[func].includes(v)); + }); + } else { + functionCheck = true; + } + + return roomCheck && functionCheck; + } + + Object.keys(data.metadata.devices[iface]).forEach(dev => { + if (/:\d+$/.test(dev)) { + if (!checkChannel(dev)) { + return; + } + + lists.channel.push(dev); + lists.channelName.push(data.channelNames[dev]); + if (!lists.channelType.includes(data.metadata.devices[iface][dev].TYPE)) { + lists.channelType.push(data.metadata.devices[iface][dev].TYPE); + } + + if (!lists.channelIndex.includes(dev.split(':')[1])) { + lists.channelIndex.push(dev.split(':')[1]); + } + + const psName = paramsetName(iface, data.metadata.devices[iface][dev], 'VALUES'); + if (data.paramsetDescriptions[psName]) { + Object.keys(data.paramsetDescriptions[psName]).forEach(dp => { + if (dp && !lists.datapoint.includes(dp) && checkDatapoint(dp)) { + lists.datapoint.push(dp); + } + }); + } + } else { + if (!checkDevice(dev)) { + return; + } + + lists.device.push(dev); + lists.deviceName.push(data.channelNames[dev]); + if (!lists.deviceType.includes(data.metadata.devices[iface][dev].TYPE)) { + lists.deviceType.push(data.metadata.devices[iface][dev].TYPE); + + data.metadata.devices[iface][dev].CHILDREN.forEach(channel => { + const psName = paramsetName(iface, data.metadata.devices[iface][channel], 'VALUES'); + if (data.paramsetDescriptions[psName]) { + Object.keys(data.paramsetDescriptions[psName]).forEach(dp => { + if (!lists.datapoint.includes(dp) && checkDatapoint(dp)) { + lists.datapoint.push(dp); + } + }); + } + }); + } + } + }); + } + + if (iface) { + composeLists(iface); + } else { + Object.keys(data.metadata.devices).forEach(iface => { + composeLists(iface); + }); + } + + console.log('lists', lists); + + Object.keys(lists).forEach(key => { + if (key === 'channelIndex') { + lists[key].sort((a, b) => Number(a) > Number(b)); + } else { + lists[key].sort((a, b) => a.localeCompare(b)); + } + }); + + $('#node-input-rooms').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', data.rooms); + $('#node-input-functions').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', data.functions); + + $('#node-input-device').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.device); + $('#node-input-deviceName').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.deviceName); + $('#node-input-deviceType').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.deviceType); + + $('#node-input-channel').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.channel); + $('#node-input-channelName').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.channelName); + $('#node-input-channelType').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.channelType); + $('#node-input-channelIndex').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.channelIndex); + + $('#node-input-datapoint').parent().find('.red-ui-typedInput-input').autocomplete('option', 'source', lists.datapoint); + } + }, + + oneditsave() { + this.force = $('#node-input-force-dropdown').val() === 'true'; + }, + }); +}()); diff --git a/src/nodes-html/ccu-set-value/main.html b/src/nodes-html/ccu-set-value/main.html new file mode 100644 index 0000000..d3896d6 --- /dev/null +++ b/src/nodes-html/ccu-set-value/main.html @@ -0,0 +1,117 @@ + + + + + + + diff --git a/src/nodes-html/ccu-signal/editor.js b/src/nodes-html/ccu-signal/editor.js new file mode 100644 index 0000000..cbd6c60 --- /dev/null +++ b/src/nodes-html/ccu-signal/editor.js @@ -0,0 +1,451 @@ +/* eslint-disable wrap-iife */ + +(function () { + 'use strict'; + + const ccuSignalColors = { + Pause: 2, + Rot: 17, + 'Rot lang': 18, + Grün: 33, + 'Grün lang': 34, + Orange: 49, + 'Orange lang': 50, + Blau: 65, + 'Blau lang': 66, + Violett: 81, + 'Violett lang': 82, + Cyan: 97, + 'Cyan lang': 98, + Weiss: 113, + 'Weiss lang': 114, + }; + + const ccuDimmerColors = { + Aus: 0, + Blau: 1, + Grün: 2, + Türkis: 3, + Rot: 4, + Violett: 5, + Gelb: 6, + Weiss: 7, + }; + + const ccuOnTime = { + '100ms': 0, + '200ms': 1, + '300ms': 2, + '400ms': 3, + '500ms': 4, + '700ms': 5, + '1s': 6, + '2s': 7, + '3s': 8, + '5s': 9, + '7s': 10, + '10s': 11, + '20s': 12, + '40s': 13, + '60s': 14, + Permanent: 15, + }; + + const ccuSound = { + 'Interner Sound': 0, + }; + + for (let i = 1; i < 253; i++) { + ccuSound['Datei ' + ('00' + i).slice(-3)] = i; + } + + ccuSound['Zufällig'] = 253; + ccuSound.Vorherig = 254; + ccuSound.Egal = 255; + + RED.nodes.registerType('ccu-signal', { + + category: 'ccu', + defaults: { + name: {value: ''}, + iface: {value: 'BidCos-RF', required: true}, + channel: {value: '', required: true}, + chime: {value: ''}, + length: {value: 108_000}, + repeat: {value: 1}, + repeatType: {value: 'num'}, + volume: {value: 100}, + volumeType: {value: 'num'}, + line1: {value: ''}, + icon1: {value: ''}, + line2: {value: ''}, + icon2: {value: ''}, + line3: {value: ''}, + icon3: {value: ''}, + signal: {value: ''}, + channelType: {value: ''}, + led: {value: ''}, + acousticAlarmSelection: {value: 'DISABLE_ACOUSTIC_SIGNAL'}, + durationUnit: {value: 'S'}, + durationValue: {value: 0}, + durationValueType: {value: 'num'}, + rampTimeUnit: {value: 'S'}, + rampTimeValue: {value: 0}, + rampTimeValueType: {value: 'num'}, + repetitions: {value: 0}, + dimmerColor: {value: 0}, + dimmerLevel: {value: 100}, + dimmerList: {value: []}, + soundLevel: {value: 50}, + soundLevelType: {value: 'num'}, + soundList: {value: []}, + opticalAlarmSelection: {value: 'DISABLE_OPTICAL_SIGNAL'}, + ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true}, + }, + inputs: 1, + outputs: 0, + icon: 'ccu.png', + color: '#4691BA', + paletteLabel: 'signal', + align: 'right', + label() { + return this.name || 'signal'; + }, + labelStyle() { + return this.name ? 'node_label_italic' : ''; + }, + oneditprepare() { + const $nodeInputChannel = $('#node-input-channel'); + const $nodeInputChannelType = $('#node-input-channelType'); + const $nodeInputCcuConfig = $('#node-input-ccuConfig'); + const $nodeInputIface = $('#node-input-iface'); + const $nodeInputName = $('#node-input-name'); + + $('#node-input-repeat').typedInput({ + default: 'num', + types: ['num', 'msg', 'flow', 'global', 'env'], + typeField: '#node-input-repeatType', + }); + + $('#node-input-volume').typedInput({ + default: 'num', + types: ['num', 'msg', 'flow', 'global', 'env'], + typeField: '#node-input-volumeType', + }); + + $('#node-input-durationValue').typedInput({ + default: 'num', + types: ['num', 'msg', 'flow', 'global', 'env'], + typeField: '#node-input-durationValueType', + }); + + $('#node-input-rampTimeValue').typedInput({ + default: 'num', + types: ['num', 'msg', 'flow', 'global', 'env'], + typeField: '#node-input-rampTimeValueType', + }); + + $('#node-input-soundLevel').typedInput({ + default: 'num', + types: ['num', 'msg', 'flow', 'global', 'env'], + typeField: '#node-input-soundLevelType', + }); + + let data; + + let ifacesLoaded = false; + let ifacesPending = false; + + function loadIfaces(iface, cb) { + if (ifacesPending) { + return; + } + + ifacesPending = true; + console.log('loadIfaces()'); + $nodeInputIface.html(''); + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId === '_ADD_') { + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + } else { + const url = 'ccu?config=' + nodeId + '&type=ifaces'; + $.getJSON(url, d => { + Object.keys(d).forEach(i => { + if (i !== 'ReGaHSS') { + $nodeInputIface.append('' + i + ''); + } + }); + if (typeof cb === 'function') { + cb(); + ifacesPending = false; + } + }); + } + } + + function loadConfig() { + if (ifacesLoaded) { + const nodeId = $nodeInputCcuConfig.val(); + if (nodeId && nodeId !== '__ADD__') { + console.log('loadConfig()'); + $.getJSON('ccu?type=signal&config=' + nodeId + '&iface=' + $nodeInputIface.val(), d => { + data = d; + autoCompleteChannel(); + }); + } else { + autoCompleteChannel(); + } + } + } + + $nodeInputCcuConfig.change(() => { + console.log('$nodeInputCcuConfig change'); + loadIfaces(this.iface, () => { + ifacesLoaded = true; + $nodeInputIface.removeAttr('disabled'); + loadConfig(); + }); + }); + + $nodeInputIface.change(() => { + if (ifacesLoaded) { + loadConfig(); + } + }); + + $nodeInputChannelType.change(() => { + $('.SUBMIT').hide(); + $('.SUBMIT.' + $nodeInputChannelType.val()).show(); + }); + + $nodeInputChannel.autocomplete({ + source: [], + close() { + const n = $nodeInputChannel.val().split(' '); + const channel = n.shift(); + $nodeInputChannel.val(channel); + if (data && data[channel]) { + let {type, deviceType} = data[channel]; + if (deviceType === 'HmIP-BSL') { + type = 'BSL_' + type; + } + + $nodeInputChannelType.val(type).trigger('change'); + } + + if (!$nodeInputName.val()) { + $nodeInputName.val(n.join(' ')); + } + }, + delay: 0, + minLength: 0, + }); + + $nodeInputChannel.on('focus', () => { + $nodeInputChannel.autocomplete('search'); + }); + + function autoCompleteChannel() { + const channels = []; + if (data) { + Object.keys(data).forEach(addr => { + if (/:\d+$/.test(addr)) { + if (data[addr].name) { + addr += ' ' + data[addr].name; + } + + channels.push(addr); + } + }); + } + + console.log('autoCompleteChannel()', channels.length); + $nodeInputChannel.autocomplete('option', 'source', channels); + + if (!data[$nodeInputChannel.val().split(' ')[0]]) { + $nodeInputChannel.val(''); + } + } + + $nodeInputChannel.change(() => { + const channel = $nodeInputChannel.val(); + if (data && data[channel]) { + $nodeInputChannelType.val(data[channel]); + } + }); + + $('#node-input-chime-container').css('min-height', '300px').css('min-width', '450px').editableList({ + sortable: true, + removable: true, + addItem(container, i, data) { + if ($('#node-input-chime-container').editableList('length') > 10) { + $('#node-input-chime-container').editableList('removeItem', data); + } else { + let html = ''; + $(html).appendTo(container); + container.find('input:last').focus(); + } + }, + }); + + $('#node-input-led-container').css('min-height', '300px').css('min-width', '450px').editableList({ + sortable: true, + removable: true, + addItem(container, i, data) { + if ($('#node-input-led-container').editableList('length') > 10) { + $('#node-input-led-container').editableList('removeItem', data); + return false; + } + + const select = $('').appendTo(container); + Object.keys(ccuDimmerColors).forEach(name => { + $('').appendTo(select); + }); + const ontime = $(``).appendTo(container); + Object.keys(ccuOnTime).forEach(name => { + $('').appendTo(ontime); + }); + }, + }); + + $('#node-input-acoustic-container').css('min-height', '300px').css('min-width', '450px').editableList({ + sortable: true, + removable: true, + addItem(container, i, data) { + if ($('#node-input-acoustic-container').editableList('length') > 12) { + $('#node-input-acoustic-container').editableList('removeItem', data); + return false; + } + + const select = $('', {style: 'width:120px; margin-left: 5px; text-align: center;'}).appendTo(row); + const group0 = $('', {label: 'value rules'}).appendTo(selectField); + for (var d in operators) { + if (operators[d].kind === 'V') { + group0.append($('').val(operators[d].v).text(node._(operators[d].t))); + } + } + + const group1 = $('', {label: 'sequence rules'}).appendTo(selectField); + for (var d in operators) { + if (operators[d].kind === 'S') { + group1.append($('').val(operators[d].v).text(node._(operators[d].t))); + } + } + + for (var d in operators) { + if (operators[d].kind === 'O') { + selectField.append($('').val(operators[d].v).text(node._(operators[d].t))); + } + } + + const valueField = $('', {class: 'node-input-rule-value', type: 'text', style: 'margin-left: 5px;'}).appendTo(row).typedInput({default: 'str', types: ['msg', 'flow', 'global', 'str', 'num', 'jsonata', 'env', previousValueType]}); + const numValueField = $('', {class: 'node-input-rule-num-value', type: 'text', style: 'margin-left: 5px;'}).appendTo(row).typedInput({default: 'num', types: ['flow', 'global', 'num', 'jsonata', 'env']}); + const expValueField = $('', {class: 'node-input-rule-exp-value', type: 'text', style: 'margin-left: 5px;'}).appendTo(row).typedInput({default: 'jsonata', types: ['jsonata']}); + const btwnValueField = $('', {class: 'node-input-rule-btwn-value', type: 'text', style: 'margin-left: 5px;'}).appendTo(row).typedInput({default: 'num', types: ['msg', 'flow', 'global', 'str', 'num', 'jsonata', 'env', previousValueType]}); + const btwnAndLabel = $('', {class: 'node-input-rule-btwn-label'}).text(' ' + andLabel + ' ').appendTo(row3); + const btwnValue2Field = $('', {class: 'node-input-rule-btwn-value2', type: 'text', style: 'margin-left:2px;'}).appendTo(row3).typedInput({default: 'num', types: ['msg', 'flow', 'global', 'str', 'num', 'jsonata', 'env', previousValueType]}); + const typeValueField = $('', {class: 'node-input-rule-type-value', type: 'text', style: 'margin-left: 5px;'}).appendTo(row) + .typedInput({ + default: 'string', types: [ + {value: 'string', label: 'string', hasValue: false}, + {value: 'number', label: 'number', hasValue: false}, + {value: 'boolean', label: 'boolean', hasValue: false}, + {value: 'array', label: 'array', hasValue: false}, + {value: 'buffer', label: 'buffer', hasValue: false}, + {value: 'object', label: 'object', hasValue: false}, + {value: 'json', label: 'JSON string', hasValue: false}, + {value: 'undefined', label: 'undefined', hasValue: false}, + {value: 'null', label: 'null', hasValue: false} + ]}); + const finalspan = $('', {style: 'float: right;margin-top: 6px;'}).appendTo(row); + finalspan.append(' → ' + (i + 1) + ' '); + const caseSensitive = $('', {id: 'node-input-rule-case-' + i, class: 'node-input-rule-case', type: 'checkbox', style: 'width:auto;vertical-align:top'}).appendTo(row2); + $('