node-red-contrib-knx-ultimate
Version:
Control your KNX intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer. Easy to use and highly configurable.
505 lines (438 loc) • 25.7 kB
HTML
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>
<script type="text/javascript">
RED.nodes.registerType('knxUltimateHueButton', {
category: "KNX Ultimate",
color: '#C0C7E9',
defaults: {
//buttonState: {value: true},
server: { type: "knxUltimate-config", required: false },
serverHue: { type: "hue-config", required: true },
name: { value: "" },
nameDim: { value: "" },
GArepeat: { value: "" },
dptrepeat: { value: "3.007" },
nameshort_release: { value: "" },
GAshort_release: { value: "" },
dptshort_release: { value: "1.001" },
nameshort_releaseStatus: { value: "" },
GAshort_releaseStatus: { value: "" },
dptshort_releaseStatus: { value: "1.001" },
toggleValues: { value: true },
hueDevice: { value: "" },
switchSend: { value: true },
dimSend: { value: "up" },
},
inputs: 0,
outputs: 1,
icon: "node-hue-icon.svg",
label: function () {
return (this.name);
},
paletteLabel: "Hue Button",
// button: {
// enabled: function() {
// // return whether or not the button is enabled, based on the current
// // configuration of the node
// return !this.changed
// },
// visible: function() {
// // return whether or not the button is visible, based on the current
// // configuration of the node
// return this.hasButton
// },
// //toggle: "buttonState",
// onclick: function() {}
// },
oneditprepare: function () {
// Go to the help panel
try {
RED.sidebar.show("help");
} catch (error) { }
var node = this;
var oNodeServer = RED.nodes.node($("#node-input-server").val()); // Store the config-node
var oNodeServerHue = RED.nodes.node($("#node-input-serverHue").val()); // Store the config-node
// 19/02/2020 Used to get the server sooner als deploy.
$("#node-input-server").change(function () {
try {
oNodeServer = RED.nodes.node($(this).val());
} catch (error) { }
});
// 19/02/2020 Used to get the server sooner als deploy.
$("#node-input-serverHue").change(function () {
try {
oNodeServerHue = RED.nodes.node($(this).val());
} catch (error) { }
});
$("#node-input-toggleValues").change(function () {
if ($("#node-input-toggleValues").is(":checked")) {
$("#divFixedValue").hide();
$("#divStatus").show();
} else {
$("#divFixedValue").show();
$("#divStatus").hide();
}
});
// Testing typedInput how it works
$("#node-input-switchSend").typedInput({
type: 'bool',
types: ['bool']
})
$("#node-input-dimSend").typedInput({
type: "direction", types: [{
value: "direction",
options: [
{ value: "up", label: "Up" },
{ value: "down", label: "Down" },
{ value: "stop", label: "Stop" },
]
}]
})
// DPT Dim
// ########################
$.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => {
data.forEach(dpt => {
if (dpt.value.startsWith("3.007")) {
$("#node-input-dptrepeat").append($("<option></option>")
.attr("value", dpt.value)
.text(dpt.text))
}
});
$("#node-input-dptrepeat").val(this.dptrepeat)
})
// Autocomplete suggestion with ETS csv File
$("#node-input-GArepeat").autocomplete({
minLength: 0,
source: function (request, response) {
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
response($.map(data, function (value, key) {
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
if (htmlUtilsfullCSVSearch(sSearch, request.term)) {
if (value.dpt.startsWith('3.007')) {
return {
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
value: value.ga // Value
}
} else { return null; }
} else {
return null;
}
}));
});
}, select: function (event, ui) {
// Sets Datapoint and device name automatically
var sDevName = ui.item.label.split("#")[1].trim();
try {
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
} catch (error) {
}
$('#node-input-nameDim').val(sDevName);
var optVal = $("#node-input-dptrepeat option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
// Select the option value
$("#node-input-dptrepeat").val(optVal);
}
}).focus(function () {
$(this).autocomplete('search', $(this).val() + 'exactmatch');
});
// DPT dptshort_release
// ########################
$.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => {
data.forEach(dpt => {
if (dpt.value.startsWith('1.')) {
$("#node-input-dptshort_release").append($("<option></option>")
.attr("value", dpt.value)
.text(dpt.text))
$("#node-input-dptshort_releaseStatus").append($("<option></option>")
.attr("value", dpt.value)
.text(dpt.text))
}
});
$("#node-input-dptshort_release").val(this.dptshort_release)
})
// Autocomplete suggestion with ETS csv File
$("#node-input-GAshort_release").autocomplete({
minLength: 0,
source: function (request, response) {
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
response($.map(data, function (value, key) {
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
if (htmlUtilsfullCSVSearch(sSearch, request.term + " 1.")) {
return {
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
value: value.ga // Value
}
} else {
return null;
}
}));
});
}, select: function (event, ui) {
// Sets Datapoint and device name automatically
var sDevName = ui.item.label.split("#")[1].trim();
try {
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
} catch (error) {
}
$('#node-input-nameshort_release').val(sDevName);
var optVal = $("#node-input-dptshort_release option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
// Select the option value
$("#node-input-dptshort_release").val(optVal);
}
}).focus(function () {
$(this).autocomplete('search', $(this).val() + 'exactmatch');
});
// Autocomplete suggestion with ETS csv File
$("#node-input-GAshort_releaseStatus").autocomplete({
minLength: 0,
source: function (request, response) {
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
response($.map(data, function (value, key) {
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
if (htmlUtilsfullCSVSearch(sSearch, request.term + " 1.")) {
return {
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
value: value.ga // Value
}
} else {
return null;
}
}));
});
}, select: function (event, ui) {
// Sets Datapoint and device name automatically
var sDevName = ui.item.label.split("#")[1].trim();
try {
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
} catch (error) {
}
$('#node-input-nameshort_releaseStatus').val(sDevName);
var optVal = $("#node-input-dptshort_releaseStatus option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
// Select the option value
$("#node-input-dptshort_releaseStatus").val(optVal);
}
}).focus(function () {
$(this).autocomplete('search', $(this).val() + 'exactmatch');
});
// Autocomplete suggestion with ETS csv File
$("#node-input-GAdouble_short_release").autocomplete({
minLength: 0,
source: function (request, response) {
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
response($.map(data, function (value, key) {
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
if (htmlUtilsfullCSVSearch(sSearch, request.term + " 1.")) {
return {
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
value: value.ga // Value
}
} else {
return null;
}
}));
});
}, select: function (event, ui) {
// Sets Datapoint and device name automatically
var sDevName = ui.item.label.split("#")[1].trim();
try {
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
} catch (error) {
}
$('#node-input-namedouble_short_release').val(sDevName);
var optVal = $("#node-input-dptdouble_short_release option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
// Select the option value
$("#node-input-dptdouble_short_release").val(optVal);
}
}).focus(function () {
$(this).autocomplete('search', $(this).val() + 'exactmatch');
});
// Autocomplete suggestion with ETS csv File
$("#node-input-GAdouble_short_releaseStatus").autocomplete({
minLength: 0,
source: function (request, response) {
$.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => {
response($.map(data, function (value, key) {
var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt);
if (htmlUtilsfullCSVSearch(sSearch, request.term + " 1.")) {
return {
label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display
value: value.ga // Value
}
} else {
return null;
}
}));
});
}, select: function (event, ui) {
// Sets Datapoint and device name automatically
var sDevName = ui.item.label.split("#")[1].trim();
try {
sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim();
} catch (error) {
}
$('#node-input-namedouble_short_releaseStatus').val(sDevName);
var optVal = $("#node-input-dptdouble_short_releaseStatus option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value');
// Select the option value
$("#node-input-dptdouble_short_releaseStatus").val(optVal);
}
}).focus(function () {
$(this).autocomplete('search', $(this).val() + 'exactmatch');
});
// Autocomplete suggestion with HUE
$("#node-input-name").autocomplete({
minLength: 0,
source: function (request, response) {
$.getJSON("KNXUltimateGetResourcesHUE?rtype=button&serverId=" + oNodeServerHue.id, (data) => {
response($.map(data.devices, function (value, key) {
//alert(JSON.stringify(value) + " "+ key)
var sSearch = (value.name);
if (htmlUtilsfullCSVSearch(sSearch, request.term)) {
return {
hueDevice: value.id, // Label for Display
value: value.name // Value
}
} else {
return null;
}
}));
});
}, select: function (event, ui) {
// Sets the fields
$('#node-input-hueDevice').val(ui.item.hueDevice)
}
}).focus(function () {
$(this).autocomplete('search', $(this).val() + 'exactmatch');
});
// ########################
},
oneditsave: function () {
// Return to the info tab
try {
RED.sidebar.show("info");
} catch (error) { }
},
oneditcancel: function () {
// Return to the info tab
try {
RED.sidebar.show("info");
} catch (error) { }
}
})
</script>
<script type="text/html" data-template-name="knxUltimateHueButton">
<div class="form-row">
<b>HUE Button node</b>  <span style="color:red"
 <i class="fa fa-youtube"></i></span> <a target="_blank" href="https://youtu.be/jjEUI1J8bkA"><u>Youtube sample</u></a>
<br />
<br />
<p align="center">
<i class="fa-regular fa-circle-dot fa-beat-fade fa-4x"></i>
</p>
<br />
<label for="node-input-server" >
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAEZyIDYgQXVnIDIwMTAgMjE6NTI6MTkgKzAxMDD84aS8AAAAB3RJTUUH3gYYCicNV+4WIQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAACUSURBVHjaY2CgFZg5c+Z/ZEyWAZ8+f/6/ZsWs/xoamqMGkGrA6Wla/1+fVARjEBuGsSoGmY4eZSCNL59d/g8DIDbIAHR14OgFGQByKjIGKX5+6/T///8gGMQGiV1+/B0Fg70GIkD+RMYgxf/O5/7//2MSmAZhkBi6OrgB6Bg5DGB4ajr3f2xqsYYLSDE2THJUDg0AAAqyDVd4tp4YAAAAAElFTkSuQmCC"></img>
KNX GW
</label>
<input type="text" id="node-input-server" />
</div>
<div class="form-row">
<label for="node-input-serverHue">
<img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAABFUlEQVQ4EZWSsWoCQRCG1yiENEFEi6QSkjqWWoqFoBYJ+Br6JHkMn8Iibd4ihQpaJIhWNkry/ZtdGZY78Qa+m39nZ+dm9s4550awglNBluS/gVtAX6KgDclf68w2OThgfR9iT/jnoEv4TtByDThWTCDKW4SSZTf/zj9/eZbN+izTDuKGimu0vPF8B/YN8aC8LmcOj/AAn9CFTEs70Js/oGqy79C69bqJ5XbQI2kGO5N8QL9D08S8zBtBF5ZaVsznpCMoqJnVdjTpb1Db0fwIWmQV6BLXzFOYgA6/gDVfQN9bBWp2J2hdWDPoBV5FrKnAJutHikk/CHHR8i7x4iG7qQ720IYvu3GFbpHjx3pFrOFYkA354z/5bkK826phyAAAAABJRU5ErkJggg=="/>
HUE Bridge
</label>
<input type="text" id="node-input-serverHue" />
</div>
<br/>
<p>
<b>Philips HUE</b>
</p>
<div class="form-row">
<label for="node-input-hueDevice" >
<i class="fa fa-play-circle"></i> Hue Button</label>
<input type="text" id="node-input-name" placeholder="Enter your hue device name" />
<input type="hidden" id="node-input-hueDevice" />
</div>
<br/>
<p>
<b>KNX</b>
</p>
<div class="form-row">
<label for="node-input-nameshort_release" style="width:100px;"><i class="fa fa-play-circle-o"></i> Switch</label>
<label for="node-input-GAhort_release" style="width:20px;">GA</label>
<input type="text" id="node-input-GAshort_release" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptshort_release" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
<select id="node-input-dptshort_release" style="width:140px;"></select>
<label for="node-input-nameshort_release" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
<input type="text" id="node-input-nameshort_release" style="width:200px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row" id="divStatus">
<label style="width:100px;"><i class="fa fa-question-circle"></i> Switch Status</label>
<label for="node-input-GAshort_releaseStatus" style="width:20px;">GA</label>
<input type="text" id="node-input-GAshort_releaseStatus" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptshort_releaseStatus" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
<select id="node-input-dptshort_releaseStatus" style="width:140px;"></select>
<label for="node-input-nameshort_releaseStatus" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
<input type="text" id="node-input-nameshort_releaseStatus" style="width:200px;margin-left: 5px; text-align: left;">
<br/>
</div>
<div class="form-row">
<label style="width:100px;"><i class="fa fa-play-circle-o"></i> Dim</span></label>
<label for="node-input-GArepeat" style="width:20px;">GA</label>
<input type="text" id="node-input-GArepeat" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptrepeat" style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
<select id="node-input-dptrepeat" style="width:140px;"></select>
<label for="node-input-nameDim" style="width:50px; margin-left: 0px; text-align: right;">Name</label>
<input type="text" id="node-input-nameDim" style="width:200px;margin-left: 5px; text-align: left;">
</div>
<br/>
<div class="form-row">
<input type="checkbox" id="node-input-toggleValues" style="display:inline-block; width:auto; vertical-align:top;" />
<label style="width:auto" for="node-input-toggleValues">
<img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAB7CAAAewgFu0HU+AAAFGmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjAtMDMtMjNUMTY6MjM6MjMrMDE6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTAzLTIzVDE2OjI1OjM0KzAxOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTAzLTIzVDE2OjI1OjM0KzAxOjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmJmNGM3NWVjLTIwNGYtNGY1YS05YTMxLTQ5NTU5YWJmZDE4NSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpiZjRjNzVlYy0yMDRmLTRmNWEtOWEzMS00OTU1OWFiZmQxODUiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpiZjRjNzVlYy0yMDRmLTRmNWEtOWEzMS00OTU1OWFiZmQxODUiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJmNGM3NWVjLTIwNGYtNGY1YS05YTMxLTQ5NTU5YWJmZDE4NSIgc3RFdnQ6d2hlbj0iMjAyMC0wMy0yM1QxNjoyMzoyMyswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjEgKE1hY2ludG9zaCkiLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+nhtLUgAAAE9JREFUKJG1UMsOACAIgub//7IdqvVQtjrExU1EEQLuiGCvgTNgl5D74MmVZPu4wIxQAgm+/sDec2VhgQPgf0sq1unjMlYJE/3MZrvy+kMFZQkZEWfC7ikAAAAASUVORK5CYII="></img>
Toggle values
</label>
</div>
<div id="divFixedValue" hidden>
<div class="form-row" >
<label for="node-input-switchSend" style="width:100px;"><i class="fa fa-play-circle-o"></i> Switch send</span></label>
<input type="text" id="node-input-switchSend">
</div>
<div class="form-row" >
<label for="node-input-dimSend" style="width:100px;"><i class="fa fa-play-circle-o"></i> Dim send</span></label>
<input type="text" id="node-input-dimSend">
</div>
</div>
<br/>
<br/>
</script>
<script type="text/markdown" data-help-name="knxUltimateHueButton">
<p>his node lets you get the events from your HUE button.</p>
Link the Group Adresses you want, to the respective events.
All fields are optional.
The **toggle values** option is enabled by default. This option toggles the value of each KNX group address (*true/false, increase/decrease dim*)
Start typing in the GA field, the name or group address of your KNX device, the avaiable devices start showing up while you're typing.
The Status fields need to be filled only if you set the *Toggle values* option.
This allows the internal logic to be aware of external devices, like wall switches.
**General**
|Property|Description|
|--|--|
| KNX GW | Select the KNX gateway to be used |
| HUE Bridge | Select the HUE Bridge to be used |
| Hue Button | HUE button to be used. The avaiable buttons start showing up while you're typing.|
<br/>
|Property|Description|
|--|--|
| Switch | As soon as you quickly press and release your HUE button, this event fires |
| Switch Status | To allow internal logic to take care of the external KNX devices, for example an external wall switch, you should set this group address |
| Dim | This event is used either to send DIM (increase/decrease) or true/false commands to the KNX group address |
| Toggle values | Enable or disable toggling values. If enabled, all values toggles, otherwise, all values are sent as *true* or *increase dim*, to the selected KNX group address. If unchecked, you'll able to select the payload to be sent to KNX bus every time you press the button. |
| Switch send | Only visible when *Toggle values* is *false*. Set the value to be sent when pushing the button. |
| Dim send | Only visible when *Toggle values* is *false*. Set the dimming direction to be sent when dimming. |
### Outputs
1. Standard output
: payload (string|object) : the standard output of the command.
### Details
`msg.payload` is used as the payload of the published message.
If it contains an Object it will be converted to a JSON string before being sent.
It contains the detailed event sent by your Hue devicem so you can use it for whatever you want.
</script>