@lahoco/node-red-contrib-lahoco-core
Version:
Node-RED LAHOCO nodes bundle
221 lines (196 loc) • 7.37 kB
JavaScript
/*
* Copyright (c) 2025. KYMO SA
* All rights reserved.
*/
(function () {
if (!RED.nodes.lahocoUtils) {
RED.nodes.lahocoUtils = {};
}
// Only define it if it hasn't already been defined
if (!RED.nodes.lahocoUtils.defaultNode) {
RED.nodes.lahocoUtils.defaultNode = {
category: 'LAHOCO',
color: '#ffd740',
defaults: {
name: {value: ''},
config: {required: true, type: 'lahoco-server'},
deviceId: {required: true},
deviceType: {},
deviceGatewayId: {}
},
outputs: 1
};
}
})();
/**
* Initializes the event listeners for the device search and device/server dropdowns.
*
* Adds an event listener to the device search button that calls the provided search function
* with the ID of the currently selected server.
* Adds an event listener to the device dropdown that calls the changeNameFromSelectedDevice
* function to change the name of the node when the device is selected.
* Adds an event listener to the server dropdown that calls the provided search function
* with the ID of the currently selected server.
*
* @param {(serverId: string) => void} onSearch - The function to call for searching devices with the server ID.
* @param {(deviceId: string) => void} onChangeDevice - The function to call when the device is changed.
*/
function init(onSearch, onChangeDevice) {
let changeTimeout;
// device search on click
$('#node-input-scan-devices').click(() => {
clearTimeout(changeTimeout);
changeTimeout = setTimeout(() => {
search((serverId) => onSearch(serverId));
}, 200); // wait 200ms to see if another change occurs
});
// on change device
$('#node-input-deviceId').change(() => {
changeNameFromSelectedDevice();
onChangeDevice(parseInt($('#node-input-deviceId').val()));
});
// on change config
$('#node-input-config').change(() => {
clearTimeout(changeTimeout);
changeTimeout = setTimeout(() => {
search((serverId) => onSearch(serverId));
}, 200); // wait 200ms to see if another change occurs
});
}
/**
* Initiates a search for devices associated with the selected server.
*
* Retrieves the server ID from the server dropdown, verifies the server exists,
* and calls the provided search function to search for devices.
* If the server is not set, notifies the user to set and deploy the server.
*
* @param {(serverId: string) => void} onSearch - The function to call for searching devices with the server ID.
*/
function search(onSearch) {
const serverId = $('#node-input-config').val();
$('#node-input-scan-devices').prop('disabled', false);
$('#node-input-deviceId').empty();
if (serverId !== '_ADD_') {
const searchIcon = $('#node-input-scan-devices').find('i');
searchIcon.removeClass('fa-search');
searchIcon.addClass('fa-refresh');
searchIcon.addClass('fa-spin');
onSearch(serverId);
}
}
/**
* Changes the name of the node to the name of the selected device.
*/
function changeNameFromSelectedDevice() {
let name = $('#node-input-deviceId option:selected').text();
if (name.includes('(')) {
name = name.split(' (')[0];
if (name.includes('] ')) {
name = name.split('] ')[1];
}
}
$('#node-input-name').val(name);
}
/**
* Called when a device search fails.
*
* Notifies the user of the failure and resets the UI elements to the initial state.
*/
function searchDevicesError() {
RED.notify('No devices could be recovered', {type: 'error', timeout: 2000});
changeSearchToInitial();
}
/**
* Called when a scene search fails.
*
* Notifies the user of the failure and resets the UI elements to the initial state.
*/
function searchScenesError() {
RED.notify('No scenes could be recovered', {type: 'error', timeout: 2000});
changeSearchToInitial();
}
/**
* Resets the UI elements to the initial state before a device search.
*
* If `server` is true, clears the server dropdown.
* Otherwise, clears the device ID dropdown.
*
* @param {boolean} [server=false] - Whether to reset the server dropdown (`true`) or the device ID dropdown (`false`).
*/
function changeSearchToInitial(server = false) {
server ? $('#node-input-config').empty() : $('#node-input-deviceId').empty();
const searchIcon = $('#node-input-scan-devices').find('i');
searchIcon.removeClass('fa-refresh');
searchIcon.removeClass('fa-spin');
searchIcon.addClass('fa-search');
}
/**
* Updates the UI to reflect a successful device search.
*
* @param {{ id: string }[]} devices - Array of devices with at least an `id` field.
* @param {string} currentDeviceId - The ID of the currently selected device.
*/
function changeSearchToSuccess(devices, currentDeviceId) {
const deviceSelect = $('#node-input-deviceId');
// select current device
const device = devices?.find(device => device?.id?.toString() === currentDeviceId);
if (device) {
deviceSelect.val(device.id).change();
}
deviceSelect.removeAttr('disabled')
const searchIcon = $('#node-input-scan-devices').find('i');
searchIcon.removeClass('fa-search');
searchIcon.removeClass('fa-spin');
searchIcon.addClass('fa-refresh');
}
/**
* Updates the UI to indicate that no devices or scenes are available.
*
* @param {{ id: string }[]} devices devices - The array of devices to display. This is not used when showing no devices.
* @param {boolean} [isScene=false] - Determines whether the message should refer to scenes or devices.
*/
function showNoDevices(devices, isScene = false) {
const select = $('#node-input-deviceId');
select.html(
$('<option>', {
value: '',
text: isScene ? 'No scenes found' : 'No devices found',
selected: true,
disabled: true
})
);
select.prop('disabled', true);
const searchIcon = $('#node-input-scan-devices').find('i');
searchIcon.removeClass('fa-refresh');
searchIcon.removeClass('fa-spin');
searchIcon.addClass('fa-search');
}
/**
* Displays the result based on the provided devices and current device ID.
*
* @param {{ id: string }[]} devices devices - List of devices to evaluate.
* @param {string} currentDeviceId - Identifier of the current device.
* @param {boolean} [isScene=false] - Indicates if the result is for a scene.
*/
function showResult(devices, currentDeviceId, isScene = false) {
if (!devices || devices.length === 0) {
showNoDevices(devices, isScene);
} else {
changeSearchToSuccess(devices, currentDeviceId);
}
}
/**
* Adds a device option to a dropdown menu.
*
* @param {object} RED - The RED runtime object to manage context and utilities.
* @param {{id: number|string, deviceType: string|number, name: string, uname?: string}} device - The device object containing details about the device to be added.
* @param {{id: string|number, translation: string}[]} types - An array of type objects used to determine the device type translation.
* @return {void} This method does not return any value.
*/
function addDeviceOption(RED, device, types) {
const typeTranslation = types.find(type => type.id === device.deviceType)?.translation;
$('#node-input-deviceId').append($('<option>', {
value: `${device.id}`,
text: `[${RED._('@lahoco/node-red-contrib-lahoco-core/lahoco-server:commons.' + typeTranslation)}] ${device.name} ${device.uname ? '(' + device.uname + ')' : ''}`
})).change();
}