node-red-contrib-wled3
Version:
NodeRed node for WLED control (modified)
283 lines (250 loc) • 11 kB
HTML
<script type="text/javascript">
RED.nodes.registerType("wled2", {
category: "function",
color: "#a6bbcf",
defaults: {
address: { value: "", required: true },
segmentId: {value: 0, validate: RED.validators.number() },
brightness: { value: 128 },
delay: { value: 0, validate: RED.validators.number() },
color1: { value: "#000000" },
color2: { value: "#000000" },
color3: { value: "#000000" },
effect: { value: 0 },
effectIntensity: { value: 128 },
effectSpeed: { value: 128 },
name: { value: "" },
palette: { value: "0" },
preset: { value: 0 },
state: { value: "on" },
debug: { value: "off" },
},
inputs: 1,
outputs: 1,
icon: "wled.png",
label: function() {
return this.name || "wled2";
},
labelStyle: function() {
return this.name ? "node_label_italic" : "";
},
oneditprepare: function() {
if (this.preset === "") {
this.preset = 0;
}
$("#node-input-address").change({ currentEffect: this.effect, currentPalette: this.palette }, loadDropdowns);
$("#node-input-delay").typedInput({
type: "num",
types: ["num"],
});
$("#node-config-discover").click(function() {
if ($("#node-input-address").prop("tagName") === "INPUT") {
searchAndSelectWled();
} else {
manualWledIP();
}
});
// Swaps the server input field to a text box with the current server value in it.
function manualWledIP() {
var current = $("#node-input-address").val();
$("#node-input-address").replaceWith('<input type="text" id="node-input-address" style="width: 100%"/>');
$("#node-input-address").val(current);
}
// Swaps the server input field to a dropdown and triggers auto-discovery.
async function searchAndSelectWled() {
var current = $("#node-input-address").val();
$("#node-input-address").replaceWith('<select id="node-input-address" style="width: 100%"></select>');
$("#node-input-address").append(
'<option selected="selected" value="null">Searching for WLED devices...</option>',
);
try {
const devices = await $.getJSON("wled2/discover");
if (!devices || devices.length == 0) {
RED.notify("No WLED devices found", "error");
}
$("#node-input-address").empty();
devices.map(device => {
$("#node-input-address").append(
`<option value="${device.address}">${device.address} ${device.name ? `(${device.name})` : ""}</option>`,
);
});
$("#node-input-address").val(current);
} catch (e) {
RED.notify(`Unable to discover devices: ${e.responseText}`);
}
}
function populateDropdown(dropdownId, items) {
const dropdown = $(dropdownId);
// Clear out all the old options, if any, first
$(`${dropdownId} option`).remove();
items.map(item => {
const newOption = $(`<option value="${item.id}">${item.name}</option>`);
dropdown.append(newOption);
});
}
async function getSortedEffects() {
const server = $("#node-input-address").val();
if (!server) return;
// Request the list of effects from the WLED device via the backend.
// This works around https/http issues when running this node in a
// NodeRed instance installed on Home Assistant.
let effects = await $.getJSON(`wled2/effects/${server}`);
if (!effects || effects.length == 0) {
RED.notify("No effects found.", "error");
}
// Drop the first item in the list, which is the default, before sorting. Then sort the list
// and add the default back in at the top.
const defaultEffect = effects.shift();
effects = effects.sort((a, b) => {
if (a.name == b.name) return 0;
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
});
effects.unshift(defaultEffect);
return effects;
}
async function getSortedPalettes() {
const server = $("#node-input-address").val();
if (!server) return;
// Request the list of palettes from the WLED device via the backend.
// This works around https/http issues when running this node in a
// NodeRed instance installed on Home Assistant.
let palettes = await $.getJSON(`wled2/palettes/${server}`);
if (!palettes || palettes.length == 0) {
RED.notify("No palettes found.", "error");
}
// Drop the first item in the list, which is the default, before sorting. Then sort the list
// and add the default back in at the top.
const defaultPalette = palettes.shift();
palettes = palettes.sort((a, b) => {
if (a.name == b.name) return 0;
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
});
palettes.unshift(defaultPalette);
return palettes;
}
async function loadDropdowns({ data: { currentEffect, currentPalette } }) {
// Attempt to retrieve from the WLED device
const server = $("#node-input-address").val();
if (!server) return;
// Populate the effects dropdown from the device directly
try {
const effects = await getSortedEffects();
populateDropdown("#node-input-effect", effects);
$(`#node-input-effect option[value=${currentEffect}]`).attr("selected", "selected");
} catch (e) {
RED.notify(`Unable to retrieve list of effects from WLED device. ${e}`, "error");
}
// Populate the palettes dropdown from the device directly
try {
const palettes = await getSortedPalettes();
populateDropdown("#node-input-palette", palettes);
$(`#node-input-palette option[value=${currentPalette}]`).attr("selected", "selected");
} catch (e) {
RED.notify(`Unable to retrieve list of palettes from WLED device. ${e}`, "error");
}
}
},
});
</script>
<script type="text/html" data-template-name="wled2">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-address"><i class="icon-server"></i> WLED server</label>
<div style="display: inline-block; position: relative; width: 70%; height: 20px;">
<div style="position: absolute; left: 0px; right: 40px;">
<input type="text" id="node-input-address" placeholder="127.0.0.1" style="width: 100%">
</div>
<a id="node-config-discover" class="editor-button" style="position: absolute; right: 0px; top: 0px;">
<i class="fa fa-search"></i>
</a>
</div>
</div>
<div class="form-row">
<label for="node-input-segmentId"><i class="icon-tag"></i> Segment ID</label>
<input type="number" step="1" min="0" max="65535" placeholder="0" id="node-input-segmentId" />
</div>
<div class="form-row">
<label for="node-input-state"><i class="icon-tag"></i> State</label>
<select id="node-input-state">
<option value="on">on</option>
<option value="off">off</option>
<option value="toggle">toggle</option>
</select>
</div>
<div class="form-row">
<label for="node-input-brightness"><i class="icon-tag"></i> Level</label>
<input type="range" id="node-input-brightness" min="1" max="255">
</div>
<div class="form-row">
<label for="node-input-color1"><i class="icon-tag"></i> Color</label>
<input type="color" id="node-input-color1">
</div>
<div class="form-row">
<label for="node-input-color2"><i class="icon-tag"></i> Color 2</label>
<input type="color" id="node-input-color2">
</div>
<div class="form-row">
<label for="node-input-color3"><i class="icon-tag"></i> Color 3</label>
<input type="color" id="node-input-color3">
</div>
<div class="form-row">
<label for="node-input-effect"><i class="icon-server"></i> Effect</label>
<select id="node-input-effect"></select>
</div>
<div class="form-row">
<label for="node-input-effectSpeed"><i class="icon-tag"></i> Effect speed</label>
<input type="range" id="node-input-effectSpeed" min="0" max="255">
</div>
<div class="form-row">
<label for="node-input-effectIntensity"><i class="icon-tag"></i> Effect intensity</label>
<input type="range" id="node-input-effectIntensity" min="0" max="255">
</div>
<div class="form-row">
<label for="node-input-palette"><i class="icon-server"></i> Palette</label>
<select id="node-input-palette"></select>
</div>
<div class="form-row">
<label for="node-input-preset"><i class="icon-tag"></i> Preset</label>
<input type="number" step="1" min="0" max="65535" placeholder="0" id="node-input-preset" />
</div>
<div class="form-row">
<label for="node-input-delay"><i class="fa fa-clock-o"></i> Delay before changing to solid (in seconds)</label>
<input type="text" id="node-input-delay" placeholder="0">
</div>
<div class="form-row">
<label for="node-input-debug"><i class="icon-tag"></i> Debug</label>
<select id="node-input-debug">
<option value="off">off</option>
<option value="on">on</option>
</select>
<small>output payload to debug window using node.warn</small>
</div>
</script>
<script type="text/html" data-help-name="wled2">
<p>A node for controlling lights via WLED with segment support</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">json</span>
</dt>
<dd>The WLED settings to set on the WLED device. These override any values specified in the node's properties.</dd>
<dt>payload.segRange
<span class="property-type">number</span>
</td>
<dd>The segment range to apply the settings to. <code>0</code> to <code>segRange</code> </dd>
</dl>
<ol class="node-ports">
<li>Standard output
<dl class="message-properties">
<dt>payload <span class="property-type">json</span></dt>
<dd>The payload that was received as input.</dd>
</dl>
</li>
</ol>
</script>
<script type="text/javascript"></script>