mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
WLED segment streaming support (#1556)
* WLED segment streaming support * Address CodeQL findings * WLED - Remove that interim color is shown when WLED is powered off * Allow LEDDevice to stay on after streaming * Apply stayOn on segment streamed to * Fix LED-Matrix Layout: Add Cabling direction selection element again
This commit is contained in:
@@ -1002,6 +1002,21 @@ $(document).ready(function () {
|
||||
|
||||
addJsonEditorHostValidation();
|
||||
|
||||
JSONEditor.defaults.custom_validators.push(function (schema, value, path) {
|
||||
var errors = [];
|
||||
|
||||
if (path === "root.specificOptions.segments.segmentList") {
|
||||
var overlapSegNames = validateWledSegmentConfig(value);
|
||||
if (overlapSegNames.length > 0) {
|
||||
errors.push({
|
||||
path: "root.specificOptions.segments",
|
||||
message: $.i18n('edt_dev_spec_segmentsOverlapValidation_error', overlapSegNames.length, overlapSegNames.join(', '))
|
||||
});
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
});
|
||||
|
||||
$("#leddevices").off().on("change", function () {
|
||||
var generalOptions = window.serverSchema.properties.device;
|
||||
|
||||
@@ -1080,8 +1095,8 @@ $(document).ready(function () {
|
||||
$('#btn_test_controller').hide();
|
||||
|
||||
switch (ledType) {
|
||||
case "cololight":
|
||||
case "wled":
|
||||
case "cololight":
|
||||
case "nanoleaf":
|
||||
showAllDeviceInputOptions("hostList", false);
|
||||
case "apa102":
|
||||
@@ -1107,7 +1122,22 @@ $(document).ready(function () {
|
||||
if (storedAccess === 'expert') {
|
||||
filter.discoverAll = true;
|
||||
}
|
||||
discover_device(ledType, filter);
|
||||
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
|
||||
discover_device(ledType, filter)
|
||||
.then(discoveryResult => {
|
||||
updateOutputSelectList(ledType, discoveryResult);
|
||||
})
|
||||
.then(discoveryResult => {
|
||||
if (ledType === "wled") {
|
||||
updateElementsWled(ledType);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showNotification('danger', "Device discovery for " + ledType + " failed with error:" + error);
|
||||
});
|
||||
|
||||
hwLedCountDefault = 1;
|
||||
colorOrderDefault = "rgb";
|
||||
break;
|
||||
@@ -1211,8 +1241,8 @@ $(document).ready(function () {
|
||||
}
|
||||
break;
|
||||
|
||||
case "cololight":
|
||||
case "wled":
|
||||
case "cololight":
|
||||
var hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue();
|
||||
if (hostList !== "SELECT") {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
@@ -1339,7 +1369,9 @@ $(document).ready(function () {
|
||||
break;
|
||||
|
||||
case "wled":
|
||||
params = { host: host, filter: "info" };
|
||||
//Ensure that elements are defaulted for new host
|
||||
updateElementsWled(ledType, host);
|
||||
params = { host: host };
|
||||
getProperties_device(ledType, host, params);
|
||||
break;
|
||||
|
||||
@@ -1452,6 +1484,10 @@ $(document).ready(function () {
|
||||
}
|
||||
conf_editor.getEditor("root.specificOptions.rateList").setValue(rate);
|
||||
break;
|
||||
case "wled":
|
||||
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
|
||||
validateWledLedCount(hardwareLedCount);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
@@ -1547,12 +1583,54 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
//WLED
|
||||
conf_editor.watch('root.specificOptions.segments.segmentList', () => {
|
||||
|
||||
//Update hidden streamSegmentId element
|
||||
var selectedSegment = conf_editor.getEditor("root.specificOptions.segments.segmentList").getValue();
|
||||
var streamSegmentId = parseInt(selectedSegment);
|
||||
conf_editor.getEditor("root.specificOptions.segments.streamSegmentId").setValue(streamSegmentId);
|
||||
|
||||
if (devicesProperties[ledType]) {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
var ledDeviceProperties = devicesProperties[ledType][host];
|
||||
|
||||
if (ledDeviceProperties) {
|
||||
var hardwareLedCount = 1;
|
||||
if (streamSegmentId > -1) {
|
||||
// Set hardware LED count to segment length
|
||||
if (ledDeviceProperties.state) {
|
||||
var segments = ledDeviceProperties.state.seg;
|
||||
var segmentConfig = segments.filter(seg => seg.id == streamSegmentId)[0];
|
||||
hardwareLedCount = segmentConfig.len;
|
||||
}
|
||||
} else {
|
||||
//"Use main segment only" is disabled, i.e. stream to all LEDs
|
||||
if (ledDeviceProperties.info) {
|
||||
hardwareLedCount = ledDeviceProperties.info.leds.count;
|
||||
}
|
||||
}
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Handle Hardware Led Count constraint list
|
||||
conf_editor.watch('root.generalOptions.hardwareLedCountList', () => {
|
||||
var hwLedCountSelected = conf_editor.getEditor("root.generalOptions.hardwareLedCountList").getValue();
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(Number(hwLedCountSelected));
|
||||
});
|
||||
|
||||
//Handle Hardware Led update and constraints
|
||||
conf_editor.watch('root.generalOptions.hardwareLedCount', () => {
|
||||
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
|
||||
switch (ledType) {
|
||||
case "wled":
|
||||
validateWledLedCount(hardwareLedCount);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//philipshueentertainment backward fix
|
||||
@@ -1798,8 +1876,8 @@ function saveLedConfig(genDefLayout = false) {
|
||||
location.reload();
|
||||
}
|
||||
|
||||
// build dynamic enum
|
||||
var updateSelectList = function (ledType, discoveryInfo) {
|
||||
// build dynamic enum for hosts or output paths
|
||||
var updateOutputSelectList = function (ledType, discoveryInfo) {
|
||||
// Only update, if ledType is equal of selected controller type and discovery info exists
|
||||
if (ledType !== $("#leddevices").val() || !discoveryInfo.devices) {
|
||||
return;
|
||||
@@ -1810,7 +1888,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
var key;
|
||||
var enumVals = [];
|
||||
var enumTitelVals = [];
|
||||
var enumTitleVals = [];
|
||||
var enumDefaultVal = "";
|
||||
var addSelect = false;
|
||||
var addCustom = false;
|
||||
@@ -1835,7 +1913,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
if (discoveryInfo.devices.length === 0) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
}
|
||||
else {
|
||||
var name;
|
||||
@@ -1876,7 +1954,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
}
|
||||
|
||||
enumVals.push(host);
|
||||
enumTitelVals.push(name);
|
||||
enumTitleVals.push(name);
|
||||
}
|
||||
|
||||
//Always allow to add custom configuration
|
||||
@@ -1904,7 +1982,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
if (discoveryInfo.devices.length == 0) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
showAllDeviceInputOptions(key, false);
|
||||
}
|
||||
@@ -1922,7 +2000,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
} else {
|
||||
enumVals.push(device.portName);
|
||||
}
|
||||
enumTitelVals.push(device.portName + " (" + device.vendorIdentifier + "|" + device.productIdentifier + ") - " + device.manufacturer);
|
||||
enumTitleVals.push(device.portName + " (" + device.vendorIdentifier + "|" + device.productIdentifier + ") - " + device.manufacturer);
|
||||
}
|
||||
|
||||
// Select configured device
|
||||
@@ -1951,7 +2029,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
if (discoveryInfo.devices.length == 0) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
showAllDeviceInputOptions(key, false);
|
||||
}
|
||||
@@ -1970,7 +2048,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
case "piblaster":
|
||||
for (const device of discoveryInfo.devices) {
|
||||
enumVals.push(device.systemLocation);
|
||||
enumTitelVals.push(device.deviceName + " (" + device.systemLocation + ")");
|
||||
enumTitleVals.push(device.deviceName + " (" + device.systemLocation + ")");
|
||||
}
|
||||
|
||||
// Select configured device
|
||||
@@ -1993,7 +2071,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
if (discoveryInfo.devices.length == 0) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
showAllDeviceInputOptions(key, false);
|
||||
|
||||
@@ -2004,18 +2082,19 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
}
|
||||
|
||||
if (enumVals.length > 0) {
|
||||
updateJsonEditorSelection(conf_editor, 'root.specificOptions', key, addSchemaElements, enumVals, enumTitelVals, enumDefaultVal, addSelect, addCustom);
|
||||
updateJsonEditorSelection(conf_editor, 'root.specificOptions', key, addSchemaElements, enumVals, enumTitleVals, enumDefaultVal, addSelect, addCustom);
|
||||
}
|
||||
};
|
||||
|
||||
async function discover_device(ledType, params) {
|
||||
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
|
||||
const result = await requestLedDeviceDiscovery(ledType, params);
|
||||
|
||||
var discoveryResult;
|
||||
if (result && !result.error) {
|
||||
var discoveryResult = {};
|
||||
if (result) {
|
||||
if (result.error) {
|
||||
throw (result.error);
|
||||
}
|
||||
discoveryResult = result.info;
|
||||
}
|
||||
else {
|
||||
@@ -2024,8 +2103,7 @@ async function discover_device(ledType, params) {
|
||||
ledDevicetype: ledType
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectList(ledType, discoveryResult);
|
||||
return discoveryResult;
|
||||
}
|
||||
|
||||
async function getProperties_device(ledType, key, params) {
|
||||
@@ -2089,23 +2167,7 @@ function updateElements(ledType, key) {
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
break;
|
||||
case "wled":
|
||||
var ledProperties = devicesProperties[ledType][key];
|
||||
|
||||
if (ledProperties && ledProperties.leds) {
|
||||
hardwareLedCount = ledProperties.leds.count;
|
||||
if (ledProperties.maxLedCount) {
|
||||
var maxLedCount = ledProperties.maxLedCount;
|
||||
if (hardwareLedCount > maxLedCount) {
|
||||
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled', hardwareLedCount, maxLedCount, maxLedCount));
|
||||
hardwareLedCount = maxLedCount;
|
||||
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue("RAW");
|
||||
//Workaround, as value seems to getting updated property when a 'getEditor("root.specificOptions").getValue()' is done during save
|
||||
var editor = conf_editor.getEditor("root.specificOptions");
|
||||
editor.value["streamProtocol"] = "RAW";
|
||||
}
|
||||
}
|
||||
}
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
updateElementsWled(ledType, key);
|
||||
break;
|
||||
|
||||
case "nanoleaf":
|
||||
@@ -2190,3 +2252,162 @@ function disableAutoResolvedGeneralOptions() {
|
||||
conf_editor.getEditor("root.generalOptions.colorOrder").disable();
|
||||
}
|
||||
|
||||
function validateWledSegmentConfig(streamSegmentId) {
|
||||
var overlapSegNames = [];
|
||||
if (streamSegmentId > -1) {
|
||||
if (!jQuery.isEmptyObject(devicesProperties)) {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
var ledProperties = devicesProperties['wled'][host];
|
||||
if (ledProperties && ledProperties.state) {
|
||||
var segments = ledProperties.state.seg;
|
||||
var segmentConfig = segments.filter(seg => seg.id == streamSegmentId)[0];
|
||||
|
||||
var overlappingSegments = segments.filter((seg) => {
|
||||
if (seg.id != streamSegmentId) {
|
||||
if ((segmentConfig.start >= seg.stop) || (segmentConfig.start < seg.start && segmentConfig.stop <= seg.start)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (overlappingSegments.length > 0) {
|
||||
var overlapSegNames = [];
|
||||
for (const segment of overlappingSegments) {
|
||||
if (segment.n) {
|
||||
overlapSegNames.push(segment.n);
|
||||
} else {
|
||||
overlapSegNames.push("Segment " + segment.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return overlapSegNames;
|
||||
}
|
||||
|
||||
function validateWledLedCount(hardwareLedCount) {
|
||||
|
||||
if (!jQuery.isEmptyObject(devicesProperties)) {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
var ledDeviceProperties = devicesProperties["wled"][host];
|
||||
|
||||
if (ledDeviceProperties) {
|
||||
|
||||
var streamProtocol = conf_editor.getEditor("root.specificOptions.streamProtocol").getValue();
|
||||
if (streamProtocol === "RAW") {
|
||||
var maxLedCount = 490;
|
||||
if (ledDeviceProperties.maxLedCount) {
|
||||
//WLED not DDP ready
|
||||
maxLedCount = ledDeviceProperties.maxLedCount;
|
||||
if (hardwareLedCount > maxLedCount) {
|
||||
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled', hardwareLedCount, maxLedCount, maxLedCount));
|
||||
hardwareLedCount = maxLedCount;
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue("RAW");
|
||||
}
|
||||
} else {
|
||||
//WLED is DDP ready
|
||||
if (hardwareLedCount > maxLedCount) {
|
||||
var newStreamingProtocol = "DDP";
|
||||
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled_protocol', hardwareLedCount, maxLedCount, newStreamingProtocol));
|
||||
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue(newStreamingProtocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateElementsWled(ledType, key) {
|
||||
|
||||
// Get configured device's details
|
||||
var configuredDeviceType = window.serverConfig.device.type;
|
||||
var configuredHost = window.serverConfig.device.host;
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
|
||||
//New segment selection list values
|
||||
var enumSegSelectVals = [];
|
||||
var enumSegSelectTitleVals = [];
|
||||
var enumSegSelectDefaultVal = "";
|
||||
|
||||
if (devicesProperties[ledType] && devicesProperties[ledType][key]) {
|
||||
var ledDeviceProperties = devicesProperties[ledType][key];
|
||||
|
||||
if (!jQuery.isEmptyObject(ledDeviceProperties)) {
|
||||
|
||||
if (ledDeviceProperties.info) {
|
||||
if (ledDeviceProperties.info.liveseg && ledDeviceProperties.info.liveseg < 0) {
|
||||
// "Use main segment only" is disabled
|
||||
var defaultSegmentId = "-1";
|
||||
enumSegSelectVals.push(defaultSegmentId);
|
||||
enumSegSelectTitleVals.push($.i18n('edt_dev_spec_segments_disabled_title'));
|
||||
enumSegSelectDefaultVal = defaultSegmentId;
|
||||
|
||||
} else {
|
||||
if (ledDeviceProperties.state) {
|
||||
//Prepare new segment selection list
|
||||
var segments = ledDeviceProperties.state.seg;
|
||||
for (const segment of segments) {
|
||||
enumSegSelectVals.push(segment.id.toString());
|
||||
if (segment.n) {
|
||||
enumSegSelectTitleVals.push(segment.n);
|
||||
} else {
|
||||
enumSegSelectTitleVals.push("Segment " + segment.id);
|
||||
}
|
||||
}
|
||||
var currentSegmentId = conf_editor.getEditor("root.specificOptions.segments.streamSegmentId").getValue().toString();
|
||||
enumSegSelectDefaultVal = currentSegmentId;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if currently configured segment is available at WLED
|
||||
var configuredDeviceType = window.serverConfig.device.type;
|
||||
var configuredHost = window.serverConfig.device.host;
|
||||
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
if (configuredDeviceType == ledType && configuredHost == host) {
|
||||
var configuredStreamSegmentId = window.serverConfig.device.segments.streamSegmentId.toString();
|
||||
var segmentIdFound = enumSegSelectVals.filter(segId => segId == configuredStreamSegmentId).length;
|
||||
if (!segmentIdFound) {
|
||||
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_wled_segment_missing', configuredStreamSegmentId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//If failed to get properties
|
||||
var hardwareLedCount;
|
||||
|
||||
if (configuredDeviceType == ledType && configuredHost == host) {
|
||||
// Populate elements from existing configuration
|
||||
var configuredstreamSegmentId = window.serverConfig.device.segments.streamSegmentId.toString();
|
||||
enumSegSelectVals = [configuredstreamSegmentId];
|
||||
enumSegSelectTitleVals = ["Segment " + configuredstreamSegmentId];
|
||||
enumSegSelectDefaultVal = configuredstreamSegmentId;
|
||||
|
||||
hardwareLedCount = window.serverConfig.device.hardwareLedCount;
|
||||
} else {
|
||||
// Populate elements with default values
|
||||
defaultSegmentId = "-1";
|
||||
enumSegSelectVals.push(defaultSegmentId);
|
||||
enumSegSelectTitleVals.push($.i18n('edt_dev_spec_segments_disabled_title'));
|
||||
enumSegSelectDefaultVal = defaultSegmentId;
|
||||
|
||||
hardwareLedCount = 1;
|
||||
}
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
}
|
||||
|
||||
updateJsonEditorSelection(conf_editor, 'root.specificOptions.segments',
|
||||
'segmentList', {}, enumSegSelectVals, enumSegSelectTitleVals, enumSegSelectDefaultVal, false, false);
|
||||
|
||||
//Show additional configuration options, if more than one segment is available
|
||||
var showAdditionalOptions = false;
|
||||
if (enumSegSelectVals.length > 1) {
|
||||
showAdditionalOptions = true;
|
||||
}
|
||||
showInputOptionForItem(conf_editor, "root.specificOptions.segments", "switchOffOtherSegments", showAdditionalOptions);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user