node-red-contrib-knx-ultimate
Version:
Control your KNX and KNX Secure 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.
208 lines (186 loc) • 9.51 kB
HTML
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>
<script type="text/javascript">
RED.nodes.registerType('knxUltimateWatchDog', {
category: "KNX Ultimate",
color: '#C7E9C0',
defaults: {
server: { type: "knxUltimate-config", required: true },
topic: { value: "12/0/0" },
maxRetry: { value: 6 }, // After this number is reached, throw a connection error
retryInterval: { value: 10 },
name: { value: "" },
autoStart: { value: true },
checkLevel: { value: "Ethernet" }
},
inputs: 1,
outputs: 1,
outputLabels: ["Output"],
icon: "node-watchdog-icon.svg",
label: function () {
return ((this.name || "KNX Watchdog") + " " + (this.checkLevel == "Ethernet" ? "Gateway IP" : this.topic));
},
paletteLabel: "KNX WatchDog",
oneditprepare: function () {
const node = this;
const $knxServerInput = $("#node-input-server");
const $gaInput = $("#node-input-topic");
const KNX_EMPTY_VALUES = new Set(['', '_ADD_', '__NONE__', 'none']);
// Go to the help panel
try {
RED.sidebar.show("help");
} catch (error) { }
$("#advancedOptionsAccordion").accordion({
header: "h3",
heightStyle: "content",
collapsible: true,
active: false
});
const KNX_GA_CACHE = node._knxGaCache || (node._knxGaCache = new Map());
const resolveKnxServerValue = () => {
const domValue = $knxServerInput.val();
if (domValue !== undefined && domValue !== null && !KNX_EMPTY_VALUES.has(String(domValue))) {
return domValue;
}
if (node.server !== undefined && node.server !== null && !KNX_EMPTY_VALUES.has(String(node.server))) {
return node.server;
}
return '';
};
const fetchGroupAddresses = (serverId) => {
if (!serverId) return Promise.resolve([]);
if (KNX_GA_CACHE.has(serverId)) return Promise.resolve(KNX_GA_CACHE.get(serverId));
return new Promise((resolve) => {
$.getJSON(`knxUltimatecsv?nodeID=${serverId}&_=${Date.now()}`, (data) => {
const list = Array.isArray(data) ? data : [];
KNX_GA_CACHE.set(serverId, list);
resolve(list);
}).fail(() => resolve([]));
});
};
const ensureGaAutocomplete = () => {
const serverId = resolveKnxServerValue();
if (!serverId) {
if ($gaInput.data('ui-autocomplete')) {
$gaInput.autocomplete('disable');
}
return;
}
const sourceFn = (request, response) => {
fetchGroupAddresses(serverId).then((data) => {
const items = [];
(data || []).forEach((entry) => {
const dpt = typeof entry.dpt === 'string' ? entry.dpt : '';
if (!dpt.startsWith('1.')) return; // Watchdog only accepts boolean GAs
const devName = entry.devicename || '';
const searchStr = `${entry.ga} (${devName}) DPT${dpt}`;
if (!htmlUtilsfullCSVSearch(searchStr, request.term || '')) return;
items.push({
label: `${entry.ga} # ${devName} # ${dpt}`,
value: entry.ga
});
});
response(items);
});
};
if ($gaInput.data('knx-watchdog-ga')) {
$gaInput.autocomplete('option', 'source', sourceFn);
$gaInput.autocomplete('enable');
} else {
$gaInput.autocomplete({
minLength: 0,
source: sourceFn,
select: function (event, ui) {
event.preventDefault();
$(this).val(ui.item.value);
const parts = ui.item.label.split('#');
let deviceName = (parts[1] || '').trim().replace(/^\)/, '').trim();
if (deviceName.indexOf('/') > -1) {
deviceName = deviceName.split('/').pop().trim();
}
const $nameInput = $('#node-input-name');
if (deviceName && $nameInput.length) {
$nameInput.val(deviceName);
}
}
}).on('focus.knxUltimateWatchDog click.knxUltimateWatchDog', function () {
const currentValue = $(this).val() || '';
try {
$(this).autocomplete('search', currentValue ? `${currentValue} exactmatch` : '');
} catch (error) { }
});
$gaInput.data('knx-watchdog-ga', true);
}
try {
const srv = RED.nodes.node(serverId);
if (srv && srv.id) KNX_enableSecureFormatting($gaInput, srv.id);
} catch (error) { }
};
$knxServerInput.on('change.knxUltimateWatchDog', () => {
KNX_GA_CACHE.clear();
ensureGaAutocomplete();
});
ensureGaAutocomplete();
const syncDivHost = () => {
const level = $("#node-input-checkLevel").val() || node.checkLevel || "Ethernet";
if (level === "Ethernet") {
$("#divHost").hide();
} else {
$("#divHost").show();
}
};
$("#node-input-checkLevel").on('change', function () {
syncDivHost();
});
syncDivHost();
},
oneditsave: function () {
// Return to the info tab
try {
RED.sidebar.show("info");
} catch (error) { }
}
})
</script>
<script type="text/markdown" data-help-name="knxUltimateWatchDog"></script>
<script type="text/html" data-template-name="knxUltimateWatchDog">
<div class="form-row">
<b><span data-i18n="knxUltimateWatchDog.title"></span></b>
<br/><br/>
<label for="node-input-server"><i class="fa fa-tag"></i> <span data-i18n="knxUltimateWatchDog.properties.node-input-server"></span> </label>
<input type="text" id="node-input-server">
</div>
<div class="form-row">
<label for="node-input-checkLevel"><i class="fa fa-search"></i> <span data-i18n="knxUltimateWatchDog.properties.node-input-checkLevel"></span> </label>
<select id="node-input-checkLevel">
<option value="Ethernet" data-i18n="knxUltimateWatchDog.selectlists.Ethernet"></option>
<option value="Eth+KNX" data-i18n="knxUltimateWatchDog.selectlists.EthKNX"></option>
</select>
</div>
<div class="form-row" id="divHost">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="knxUltimateWatchDog.properties.node-input-topic"></span></label>
<input style="width:90px;" type="text" id="node-input-topic" data-i18n="[placeholder]knxUltimateWatchDog.placeholder.monitor"> the DPT must be DPT1.x (Boolean)
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="knxUltimateWatchDog.properties.node-input-name"></span> </label>
<input type="text" id="node-input-name" data-i18n="[placeholder]knxUltimateWatchDog.properties.node-input-name" style="flex:1 1 240px; min-width:240px; max-width:240px;">
</div>
<div class="form-row">
<input type="checkbox" id="node-input-autoStart" style="display:inline-block; width:auto; vertical-align:top;">
<label style="width:auto" for="node-input-autoStart"> <i class="fa fa-play-circle"></i> <span data-i18n="knxUltimateWatchDog.properties.node-input-autoStart"></span> </label>
</div>
<div id="advancedOptionsAccordion">
<h3><span data-i18n="knxUltimateWatchDog.properties.advancedOptionsAccordion"></span></h3>
<div>
<p>
<div class="form-row">
<label for="node-input-retryInterval"><i class="fa fa-clock-o"></i> <span data-i18n="knxUltimateWatchDog.properties.node-input-retryInterval"></span></label>
<input type="text" id="node-input-retryInterval">
</div>
<div class="form-row">
<label for="node-input-maxRetry"><i class="fa fa-undo"></i> <span data-i18n="knxUltimateWatchDog.properties.node-input-maxRetry"></span></label>
<input type="text" id="node-input-maxRetry">
</div>
</p>
</div>
</div>
</script>