node-red-contrib-zigbee2mqtt-devices
Version:
Nodes to interact with zigbee2mqtt for Node-RED
325 lines (286 loc) • 12.9 kB
HTML
<style>
.config-group {
background: #EEE;
padding: 20px 15px 1px 15px;
margin: 10px 0px;
}
.config-group .red-ui-typedInput-container {
width: 100% ;
}
.config-sub-group {
margin-left: 8px;
border-left: 5px solid #ddd;
padding-left: 7px;
}
/* PROPERTIES PANELS */
/* Font Awesome Icons shall have all the same width */
.zigbee2mqtt-devices-properties .form-row>label>.fa {
width: 16px;
text-align: center;
}
.zigbee2mqtt-devices-slider input[type=range] {
width: 60%;
display: inline;
}
.zigbee2mqtt-devices-slider input[type=text] {
width: 9%;
display: inline;
}
.zigbee2mqtt-error-box {
border: 1px solid rgb(255, 0, 0);
margin: 10px auto;
width: 380px;
padding: 7px;
color: white;
background: rgb(244, 67, 54);
text-align: center;
}
.zigbee2mqtt-error-box button{
margin-left: 10px;
}
</style>
<script type="text/javascript">
function refresh() {
RED.nodes.eachNode(function (node) {
});
}
/*
TODO: implement the tab, removed for now
RED.sidebar.addTab({
id: "zigbee2mqtt",
label: "Zigbee 2 MQTT",
name: "zigbee2mqtt",
content: "<div class=\"nr-db-sb\">TODO: Add overview of all devices</div>",
closeable: true,
pinned: true,
iconClass: "fa fa-cloud",
disableOnEdit: true,
onchange: function () { refresh(); }
});*/
if (RED.bavaria === undefined) {
RED.bavaria = {
validators: {
range: function (min, max) {
return function (v) {
return v <= max && v >= min;
}
}
},
converters: {
hexToRgb: function (hex) {
if (hex.startsWith("#")) {
hex = hex.substring(1);
}
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
if (hex.length != 6) {
return {
r: 0,
g: 0,
b: 0
}
}
var int16 = parseInt(hex, 16);
return {
r: (int16 >> 16) & 255,
g: (int16 >> 8) & 255,
b: int16 & 255
}
},
rgbToHex: function (r, g, b) {
r = parseInt(r).toString(16);
g = parseInt(g).toString(16);
b = parseInt(b).toString(16);
if (r.length == 1)
r = "0" + r;
if (g.length == 1)
g = "0" + g;
if (b.length == 1)
b = "0" + b;
return "#" + r + g + b;
}
},
ui: {
addNumInput: function (element) {
$(element).typedInput({
type: "num",
types: ["num"],
});
},
addNumInputs: function (elements) {
elements.forEach(RED.bavaria.ui.addNumInput);
},
addNumRangeInput: function (element, min, max) {
RED.bavaria.ui.addNumInput(element);
var validator = RED.bavaria.validators.range(min, max);
$(element).change(function () {
var valid = validator($(this).val());
var targetElement = $(this).parent().find('.red-ui-typedInput-container');
if (!valid) {
targetElement.addClass('input-error');
} else {
targetElement.removeClass('input-error');
}
});
},
addNumRangeInputs: function (elements, min, max) {
elements.forEach(element => {
RED.bavaria.ui.addNumRangeInput(element, min, max);
});
},
addMultiInput: function (node, name) {
var value = node["payload" + name];
var type = node["type" + name];
var options = {
type: type,
types: ["str", "json", "num", "bool"]
};
const index = options.types.indexOf(type);
if (index > -1) {
options.types.splice(index, 1);
options.types.splice(0, 0, type);
}
console.log(options);
$("#node-input-payload" + name).typedInput(options);
},
saveMultiInput: function (node, name) {
var splits = $("#node-input-payload" + name).next().children("button").children("span").children("img").first().attr("src").split("/");
var type = splits[splits.length - 1].split(".")[0];
switch (type) {
case "az":
type = "str";
break;
case "09":
type = "num";
break;
case "bool":
type = "bool";
break;
case "json":
type = "json";
break;
}
node["customPayload" + name] = $("#node-input-payload" + name).val() !== "";
node["type" + name] = type;
}
},
devices: {
loadDevices: function (selectedDevice, deviceType, vendor, model, emptyOption = false, inputId = "#node-input-deviceName") {
var bridgeId = $("#node-input-bridge").val().replace(".", "_");
$.getJSON(`z2m/devices/${bridgeId}/${deviceType}/${vendor}/${model}`, function (data) {
$(inputId).empty();
if (emptyOption === true) {
$(inputId).append("<option disabled selected value> -- select an option -- </option>");
}
data.devices.forEach(e => {
$(inputId).append("<option>" + e.friendly_name + "</option>");
});
$(inputId).val(selectedDevice);
});
},
createDeviceSelector: function (
rowId,
deviceName,
deviceType = "all",
vendor = "all",
model = "all",
isConfig = false,
devicePropertyName = "deviceName",
bridgePropertyName = "bridge"
) {
let inputId = `input-${devicePropertyName}`;
let bridgeId = `input-bridge`;
if (isConfig === true) {
inputId = "node-config-" + inputId;
bridgeId = "node-config-" + bridgeId;
} else {
inputId = "node-" + inputId;
bridgeId = "node-" + bridgeId;
}
let label = $("<label></label>")
.attr("for", inputId)
.append(
$("<i></i>")
.addClass("fa")
.addClass("fa-microchip"))
.append(" Device");
let select = $("<select></select>").attr("id", inputId).attr("style", "margin:0px 4px;");
let button = $("<button></button>")
.attr("id", "refresh-button-" + inputId)
.attr("type", "button")
.addClass("red-ui-button")
.append($("<i></i>")
.addClass("fa")
.addClass("fa-search"))
.append(" refresh");
let errorBox = $("<div></div>")
.attr("id", "device-error" + inputId)
.addClass("zigbee2mqtt-error-box")
.hide();
$("#" + rowId)
.prepend(errorBox)
.prepend(button)
.prepend(select)
.prepend(label);
let loadDevices = function (inputId, htmlBridgeId, selectedDevice, deviceType = "all", vendor = "all", model = "all") {
var bridgeId = $("#" + htmlBridgeId).val().replace(".", "_");
$.getJSON(`z2m/devices/${bridgeId}/${deviceType}/${vendor}/${model}`, function (data) {
let select = $("#" + inputId);
select.empty();
if (!data.success) {
$("#device-error" + inputId)
.html(data.message)
.append(
$("<button></button>")
.text("Switch to manual entry")
.click(() => {
let textbox = $("<input />", { "type": "text", "value": selectedDevice });
textbox.attr("id", inputId)
.attr("style", "margin-left: 4px;")
.attr("placeholder", "Enter friendly_name");
$("#" + inputId).replaceWith(textbox);
$("#refresh-button-" + inputId).remove();
$("#device-error" + inputId).hide();
})
);
$("#device-error" + inputId).show();
} else {
$("#device-error" + inputId).hide();
}
select.append("<option disabled selected value> -- select an option -- </option>");
data.devices.forEach(e => {
select.append("<option>" + e.friendly_name + "</option>");
});
select.val(selectedDevice);
}).fail(function () {
$("#device-error" + inputId)
.html("Failed to request device-list!")
.append(
$("<button></button>")
.text("Switch to manual entry")
.click(() => {
let textbox = $("<input />", { "type": "text", "value": selectedDevice });
textbox.attr("id", inputId)
.attr("style", "margin-left: 4px;")
.attr("placeholder", "Enter friendly_name");
$("#" + inputId).replaceWith(textbox);
$("#refresh-button-" + inputId).remove();
$("#device-error" + inputId).hide();
})
);
$("#device-error" + inputId).show();
});
}
button.click(() => {
loadDevices(inputId, bridgeId, deviceName, deviceType, vendor, model);
});
let bridgeIdSelector = "#" + bridgeId;
$(bridgeIdSelector).change(function () {
loadDevices(inputId, bridgeId, deviceName, deviceType, vendor, model);
});
}
}
}
}
</script>