node-red-contrib-zigbee2mqtt
Version:
Zigbee2mqtt connectivity nodes for node-red
395 lines (349 loc) • 15.5 kB
JavaScript
class Zigbee2MqttEditor {
constructor(node, config = {}) {
this.node = node;
this.devices = null;
this.config = Object.assign( {
allow_empty:false
}, config);
this.device_id = node.device_id||null;
this.property = node.state||null;
this.optionsValue = node.optionsValue||null;
this.optionsType = node.optionsType||null;
this.refresh = false;
return this;
}
bind() {
let that = this;
that.getRefreshBtn().off('click').on('click', () => {
that.refresh = true;
that.build();
});
that.getServerInput().off('change').on('change', (e) => {
// console.log('bind: getServerInput');
that.property = null;
that.refresh = true;
that.build();
});
that.getDeviceIdInput().off('change').on('change', () => {
that.device_id = that.getDeviceIdInput().multipleSelect('getSelects', 'value');
if (!that.isMultiple()) { //no need to build for multiple
// console.log('bind: getDeviceIdInput');
that.build();
} else {
that.setFriendlyName();
}
});
if (that.getDeviceOptionsInput()) {
that.getDeviceOptionsInput().off('change').on('change', (event, type, value) => {
// console.log('bind: getDeviceOptionsInput');
that.optionsValue = value;
that.optionsType = type;
that.buildDeviceOptionsHelpBlock();
});
}
that.getEnableMultipleCheckbox().off('change').on('change', (e) => {
// that.property = null;
// that.refresh = true;
// console.log('bind: getEnableMultipleCheckbox');
that.build();
});
}
async build() {
let that = this;
// console.log('build : '+(this.refresh?'true':false));
await that.buildDeviceIdInput().then(()=>{
that.buildDevicePropertyInput();
that.buildDeviceOptionsInput();
});
that.bind();
}
async buildDeviceIdInput() {
let that = this;
that.getFilterChanges().closest('.form-row').toggle(!that.isMultiple());
// console.log('BUILD buildDeviceIdInput');
let params = {
single: !that.isMultiple(),
minimumCountSelected: !that.isMultiple()?1:0,
numberDisplayed: 1,
maxHeight: 300,
dropWidth: 320,
width: 320,
filter: true,
formatAllSelected:function(){return RED._("node-red-contrib-zigbee2mqtt/server:editor.select_device")}
};
if (that.config.allow_empty && !that.isMultiple()) {
params.formatAllSelected = function(){return RED._("node-red-contrib-zigbee2mqtt/server:editor.msg_topic")};
}
that.getDeviceIdInput().children().remove();
that.getDeviceIdInput().multipleSelect('destroy').multipleSelect(params).multipleSelect('disable');
let data = await that.getDevices();
if (that.config.allow_empty && !that.isMultiple()) {
that.getDeviceIdInput().html('<option value="msg.topic">msg.topic</option>');
}
let html = '';
//groups
let groups = data[1];
if (groups.length) {
html = $('<optgroup/>', {label: RED._("node-red-contrib-zigbee2mqtt/server:editor.groups")});
html.appendTo(that.getDeviceIdInput());
$.each(groups, function(index, value) {
let text = '';
if ("devices" in value && typeof (value.devices) != 'undefined' && value.devices.length > 0) {
text = ' (' + value.devices.length + ')';
}
$('<option value="' + value.id + '" data-friendly_name="' + value.friendly_name + '">' + value.friendly_name + text + '</option>')
.appendTo(html);
});
}
//devices
let devices = data[0];
if (devices.length) {
html = $('<optgroup/>', {label: RED._("node-red-contrib-zigbee2mqtt/server:editor.devices")});
html.appendTo(that.getDeviceIdInput());
$.each(devices, function(index, value) {
var model = '';
if ("definition" in value && value.definition && "model" in value.definition && typeof (value.definition.model) !== undefined) {
model = ' (' + value.definition.model + ')';
}
$('<option value="' + value.ieee_address + '" data-friendly_name="' + value.friendly_name + '">' + value.friendly_name + model + '</option>')
.appendTo(html);
});
}
that.getDeviceIdInput().multipleSelect('enable');
that.getDeviceIdInput().multipleSelect('refresh');
that.setDeviceValue();
that.setFriendlyName();
return this;
}
async buildDevicePropertyInput() {
let that = this;
if (!that.getDevicePropertyInput()) return;
that.getDevicePropertyInput().closest('.form-row').toggle(!that.isMultiple());
if (that.isMultiple()) return;
// console.log('BUILD buildDevicePropertyInput');
that.getDevicePropertyInput().children().remove();
that.getDevicePropertyInput().multipleSelect('destroy').multipleSelect({
numberDisplayed: 1,
dropWidth: 320,
width: 320,
single: !(typeof $(this).attr('multiple') !== typeof undefined && $(this).attr('multiple') !== false)
}).multipleSelect('disable');
that.getDevicePropertyInput().html('<option value="0">'+ RED._("node-red-contrib-zigbee2mqtt/server:editor.complete_payload")+'</option>');
let html = '';
let device = that.getDevice();
if (device && 'definition' in device && device.definition && 'exposes' in device.definition) {
html = $('<optgroup/>', {label: RED._("node-red-contrib-zigbee2mqtt/server:editor.zigbee2mqtt")});
html.appendTo(that.getDevicePropertyInput());
$.each(device.definition.exposes, function(index, value) {
if ('features' in value) {
$.each(value.features, function(index2, value2) {
if ('property' in value2) {
$('<option value="' + value2.property + '">' + value2.name + (value2.unit ? ', ' + value2.unit : '') + '</option>')
.appendTo(html);
}
});
} else if ('property' in value) {
$('<option value="' + value.property + '">' + value.name + (value.unit ? ', ' + value.unit : '') + '</option>')
.appendTo(html);
}
});
}
if (device && 'homekit' in device && device.homekit && Object.keys(device.homekit).length) {
html = $('<optgroup/>', {label: RED._("node-red-contrib-zigbee2mqtt/server:editor.homekit")});
html.appendTo(that.getDevicePropertyInput());
$.each(device.homekit, function (index, value) {
$('<option value="homekit_' + index + '">' + index + '</option>').appendTo(html);
});
}
that.getDevicePropertyInput().multipleSelect('enable');
if (that.getDevicePropertyInput().find('option[value='+that.property+']').length) {
that.getDevicePropertyInput().val(that.property);
} else {
that.getDevicePropertyInput().val(that.getDevicePropertyInput().find('option').eq(0).attr('value'));
}
that.getDevicePropertyInput().multipleSelect('refresh');
}
buildDeviceOptionsInput() {
let that = this;
if (!that.getDeviceOptionsInput()) return;
that.getDeviceOptionsTypeHelpBlock().hide().find('div').text('').closest('.form-tips').find('span').text('');
that.getDeviceOptionsInput().closest('.form-row').toggle(!that.isMultiple());
if (that.isMultiple()) return;
// console.log('BUILD buildDeviceOptionsInput');
let device = that.getDevice();
let options = [];
options.push({'value': 'nothing', 'label': RED._("node-red-contrib-zigbee2mqtt/server:editor.nothing"), options:['']});
options.push('msg');
options.push('json');
if (device && 'definition' in device && device.definition && 'options' in device.definition) {
$.each(device.definition.options, function(k, v) {
options.push({'value': v.property, 'label': v.name});
});
}
that.getDeviceOptionsInput().typedInput({
default: 'nothing',
value: that.optionsType,
typeField: that.getDeviceOptionsTypeInput(),
});
that.getDeviceOptionsInput().typedInput('types', options);
that.getDeviceOptionsInput().typedInput('type', that.optionsType || 'nothing');
that.getDeviceOptionsInput().typedInput('value', that.optionsValue || '');
that.buildDeviceOptionsHelpBlock();
}
buildDeviceOptionsHelpBlock() {
let that = this;
if (!that.getDeviceOptionsTypeHelpBlock()) return;
that.getDeviceOptionsTypeHelpBlock().hide().find('div').text('').closest('.form-tips').find('span').text('');
if (that.isMultiple()) return;
// console.log('BUILD buildDeviceOptionsHelpBlock');
let device = that.getDevice();
let selectedOption = null;
if (device && 'definition' in device && device.definition && 'options' in device.definition) {
$.each(device.definition.options, function(k, v) {
if ('json' === that.optionsType) {
let json = {};
$.each(device.definition.options, function(k, v2) {
if ('property' in v2) {
let defaultVal = '';
if ('type' in v2) {
if (v2.type==='numeric') {
defaultVal = 0;
if ('value_min' in v2) {
defaultVal = v2.value_min;
}
} else if (v2.type==='binary') {
defaultVal = false;
}
}
json[v2.property] = defaultVal;
}
});
selectedOption = {'name':'JSON', 'description':JSON.stringify(json, null, 4)};
return false;
}
if (v.property === that.optionsType) {
selectedOption = v;
return false;
}
});
}
if (selectedOption && 'description' in selectedOption && selectedOption.description) {
that.getDeviceOptionsTypeHelpBlock().show().find('div').text(selectedOption.name).closest('.form-tips').find('span').text(selectedOption.description);
}
}
async getDevices() {
let that = this;
if (that.devices === null || that.refresh) {
const response = await fetch('zigbee2mqtt/getDevices?' + new URLSearchParams({
controllerID: that.getServerInput().val()
}).toString(), {
method: 'GET',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
}
});
that.refresh = false;
that.devices = await response.json();
return that.devices;
} else {
return await new Promise(function(resolve, reject) {
resolve(that.devices);
});
}
}
getDevice() {
let that = this;
let devices = that.devices[0];
let device = null;
if (devices.length && that.device_id) {
let selectedDevice = typeof(that.device_id) === 'object' ? that.device_id[0] : that.device_id;
$.each(devices, function (index, item) {
if (item.ieee_address === selectedDevice) {
device = item;
return false;
}
});
}
return device;
}
getDeviceIdInput() {
return $('#node-input-device_id');
}
getDevicePropertyInput() {
let $elem = $('#node-input-state');
return $elem.length?$elem:null;
}
getDeviceOptionsInput() {
let $elem = $('#node-input-optionsValue');
return $elem.length?$elem:null;
}
getDeviceOptionsTypeInput() {
let $elem = $('#node-input-optionsType');
return $elem.length?$elem:null;
}
getDeviceOptionsTypeHelpBlock() {
return $('.optionsType_description');
}
getDeviceFriendlyNameInput() {
return $('#node-input-friendly_name');
}
getServerInput() {
return $('#node-input-server');
}
getRefreshBtn() {
return $('#force-refresh');
}
getFilterChanges() {
return $('#node-input-filterChanges');
}
getEnableMultipleCheckbox() {
return $('#node-input-enableMultiple');
}
isMultiple() {
return this.getEnableMultipleCheckbox().is(':checked');
}
setDeviceValue() {
let that = this;
if (that.isMultiple()) {
if (typeof(that.device_id) == 'string') {
that.device_id = [that.device_id];
}
if (that.device_id) {
that.getDeviceIdInput().multipleSelect('setSelects', that.device_id);
}
} else if (that.device_id && that.device_id.length) {
if (typeof(that.device_id) == 'object') {
that.device_id = that.device_id[0]; //get the first device
}
if (that.getDeviceIdInput().find('option[value="'+that.device_id+'"]').length) {
that.getDeviceIdInput().val(that.device_id);
}
// that.getDeviceIdInput().multipleSelect('check', that.device_id); //does not work
that.getDeviceIdInput().multipleSelect('refresh');
} else {
that.device_id = null;
}
}
setFriendlyName() {
let that = this;
if (that.isMultiple()) {
if (typeof(that.device_id) == 'string') {
that.device_id = [that.device_id];
}
if (!that.device_id) {
that.device_id = [];
}
that.getDeviceFriendlyNameInput().val(that.device_id.length + ' ' + RED._("node-red-contrib-zigbee2mqtt/server:editor.selected"));
} else if (that.device_id && that.device_id.length) {
if (typeof(that.device_id) == 'object') {
that.device_id = that.device_id[0]; //get the first device
}
if (that.getDeviceIdInput().find('option[value="'+that.device_id+'"]').length) {
that.getDeviceFriendlyNameInput().val(that.getDeviceIdInput().multipleSelect('getSelects', 'text'));
}
} else {
that.getDeviceFriendlyNameInput().val('');
}
}
}