Skip to content

Commit 4f1b95e

Browse files
authored
Add Home Assistant Lights support (#1763)
* New HomeAssistant LEDDevice * Fix typos * Ping Qt for Windows to 6.7 until aqtinstaller is fixed * Fix HA default port handling * HA - Update default latchtime and range * Add HA Wizard and light selection * Naming consistency * Fix "Selected Hyperion instance is not running" * CodeQL findings * HA - allow to overwrite brightness by HA yes or no * HA - Support switch off on black * HA - Add transition time
1 parent df2b2b2 commit 4f1b95e

19 files changed

+1020
-76
lines changed

.github/workflows/qt5_6.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ jobs:
194194
version: ${{ inputs.qt_version == '6' && '6.7' || '5.15.*' }}
195195
target: 'desktop'
196196
modules: ${{ inputs.qt_version == '6' && 'qtserialport' || '' }}
197-
arch: 'win64_msvc2019_64'
198197
cache: 'true'
199198
cache-key-prefix: 'cache-qt-windows'
200199

assets/webconfig/i18n/en.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
"conf_leds_layout_cl_bottomleft": "Bottom Left (Corner)",
8686
"conf_leds_layout_cl_bottomright": "Bottom Right (Corner)",
8787
"conf_leds_layout_cl_cornergap": "Corner Gap",
88+
"conf_leds_layout_cl_disabled": "Deactivated",
8889
"conf_leds_layout_cl_edgegap": "Edge Gap",
8990
"conf_leds_layout_cl_entertainment": "Entertainment Area",
9091
"conf_leds_layout_cl_entertainment_center": "Entertainment Area Center",
@@ -103,6 +104,7 @@
103104
"conf_leds_layout_cl_lightPosBottomLeft112": "Bottom: 0 - 50% from Left",
104105
"conf_leds_layout_cl_lightPosBottomLeft121": "Bottom: 50 - 100% from Left",
105106
"conf_leds_layout_cl_lightPosBottomLeftNewMid": "Bottom: 25 - 75% from Left",
107+
"conf_leds_layout_cl_lightPosEntire": "Whole picture",
106108
"conf_leds_layout_cl_lightPosTopLeft112": "Top: 0 - 50% from Left",
107109
"conf_leds_layout_cl_lightPosTopLeft121": "Top: 50 - 100% from Left",
108110
"conf_leds_layout_cl_lightPosTopLeftNewMid": "Top: 25 - 75% from Left",
@@ -661,13 +663,14 @@
661663
"edt_dev_spec_colorComponent_title": "Colour component",
662664
"edt_dev_spec_debugLevel_title": "Debug Level",
663665
"edt_dev_spec_delayAfterConnect_title": "Delay after connect",
664-
"edt_dev_spec_devices_discovered_none": "No Devices Discovered",
665-
"edt_dev_spec_devices_discovered_title": "Devices Discovered",
666+
"edt_dev_spec_devices_discovered_none": "No Devices discovered",
667+
"edt_dev_spec_devices_discovered_title": "Devices discovered",
666668
"edt_dev_spec_devices_discovered_title_info": "Select your LED-Device discovered",
667669
"edt_dev_spec_devices_discovered_title_info_custom": "Select your LED-Device discovered or configure a custome one",
668670
"edt_dev_spec_devices_discovery_inprogress": "Discovery in progress",
669671
"edt_dev_spec_dithering_title": "Dithering",
670672
"edt_dev_spec_dmaNumber_title": "DMA channel",
673+
"edt_dev_spec_fullBrightnessAtStart_title": "Full brightness at start",
671674
"edt_dev_spec_gamma_title": "Gamma",
672675
"edt_dev_spec_globalBrightnessControlMaxLevel_title": "Max Current Level",
673676
"edt_dev_spec_globalBrightnessControlThreshold_title": "Adaptive Current Threshold",
@@ -685,6 +688,7 @@
685688
"edt_dev_spec_ledType_title": "LED Type",
686689
"edt_dev_spec_lightid_itemtitle": "ID",
687690
"edt_dev_spec_lightid_title": "Light ID(s)",
691+
"edt_dev_spec_lights_discovered_none": "No Lights discovered",
688692
"edt_dev_spec_lights_itemtitle": "Light",
689693
"edt_dev_spec_lights_name": "Name",
690694
"edt_dev_spec_lights_title": "Light(s)",
@@ -1184,9 +1188,10 @@
11841188
"wiz_identify_tip": "Identify configured device by lighting it up",
11851189
"wiz_identify_light": "Identify $1",
11861190
"wiz_layout": "Generate Layout",
1191+
"wiz_layout_led_position_title": "LED position",
1192+
"wiz_layout_led_positions_title": "LED position layout wizard",
1193+
"wiz_layout_led_positions_expl": "Select the LED position for the $1 controller lights.",
11871194
"wiz_layout_tip": "Generate a layout for the configured device",
1188-
"wiz_ids_disabled": "Deactivated",
1189-
"wiz_ids_entire": "Whole picture",
11901195
"wiz_nanoleaf_failure_auth_token": "Please press the Nanoleaf Power On/Off button within 30 seconds",
11911196
"wiz_nanoleaf_failure_auth_token_t": "User authorization token generating timeout",
11921197
"wiz_nanoleaf_press_onoff_button": "Please press the Power On/Off button on your Nanoleaf device for 5-7 seconds",

assets/webconfig/js/content_index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ $(document).ready(function () {
197197
removeStorage("loginToken");
198198
requestRequiresDefaultPasswortChange();
199199
}
200-
else if (event.reason == "Selected Hyperion instance isn't running") {
200+
else if (event.reason == "Selected Hyperion instance is not running") {
201201
//Switch to default instance
202202
instanceSwitch(0);
203203
} else {

assets/webconfig/js/content_leds.js

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var devSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk68
2222
var devFTDI = ['apa102_ftdi', 'sk6812_ftdi', 'ws2812_ftdi'];
2323
var devRPiPWM = ['ws281x'];
2424
var devRPiGPIO = ['piblaster'];
25-
var devNET = ['atmoorb', 'cololight', 'fadecandy', 'philipshue', 'nanoleaf', 'razer', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udpddp', 'udph801', 'udpraw', 'wled', 'yeelight'];
25+
var devNET = ['atmoorb', 'cololight', 'fadecandy', 'homeassistant', 'philipshue', 'nanoleaf', 'razer', 'tinkerforge', 'tpm2net', 'udpe131', 'udpartnet', 'udpddp', 'udph801', 'udpraw', 'wled', 'yeelight'];
2626
var devSerial = ['adalight', 'dmx', 'atmo', 'sedu', 'tpm2', 'karate'];
2727
var devHID = ['hyperionusbasp', 'lightpack', 'paintpack', 'rawhid'];
2828

@@ -1100,6 +1100,7 @@ $(document).ready(function () {
11001100
switch (ledType) {
11011101
case "wled":
11021102
case "cololight":
1103+
case "homeassistant":
11031104
case "nanoleaf":
11041105
showAllDeviceInputOptions("hostList", false);
11051106
case "apa102":
@@ -1279,7 +1280,21 @@ $(document).ready(function () {
12791280
if (hostList !== "SELECT") {
12801281
const host = conf_editor.getEditor("root.specificOptions.host").getValue();
12811282
const token = conf_editor.getEditor("root.specificOptions.token").getValue();
1282-
if (host !== "" && token !== "") {
1283+
if (host !== "" && token !== "" && entityIds) {
1284+
canIdentify = true;
1285+
canSave = true;
1286+
}
1287+
}
1288+
}
1289+
break;
1290+
1291+
case "homeassistant": {
1292+
const hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue();
1293+
if (hostList !== "SELECT") {
1294+
const host = conf_editor.getEditor("root.specificOptions.host").getValue();
1295+
const token = conf_editor.getEditor("root.specificOptions.token").getValue();
1296+
const entityIds = conf_editor.getEditor("root.specificOptions.entityIds").getValue();
1297+
if (host !== "" && token !== "" && entityIds) {
12831298
canIdentify = true;
12841299
canSave = true;
12851300
}
@@ -1387,6 +1402,16 @@ $(document).ready(function () {
13871402
getProperties_device(ledType, host, params);
13881403
break;
13891404

1405+
case "homeassistant":
1406+
var token = conf_editor.getEditor("root.specificOptions.token").getValue();
1407+
if (token === "") {
1408+
return;
1409+
}
1410+
1411+
params = { host: host, token: token, filter: "states" };
1412+
getProperties_device(ledType, host, params);
1413+
break;
1414+
13901415
case "nanoleaf":
13911416
$('#btn_wiz_holder').show();
13921417

@@ -1552,6 +1577,14 @@ $(document).ready(function () {
15521577

15531578
var host = "";
15541579
switch (ledType) {
1580+
case "homeassistant":
1581+
host = conf_editor.getEditor("root.specificOptions.host").getValue();
1582+
if (host === "") {
1583+
return
1584+
}
1585+
params = { host: host, token: token, filter: "states" };
1586+
break;
1587+
15551588
case "nanoleaf":
15561589
host = conf_editor.getEditor("root.specificOptions.host").getValue();
15571590
if (host === "") {
@@ -1654,6 +1687,16 @@ $(document).ready(function () {
16541687
default:
16551688
}
16561689
});
1690+
1691+
conf_editor.watch('root.specificOptions.entityIds', () => {
1692+
var entityIds = conf_editor.getEditor("root.specificOptions.entityIds").getValue();
1693+
if (entityIds.length > 0) {
1694+
$('#btn_test_controller').prop('disabled', false);
1695+
} else {
1696+
$('#btn_test_controller').prop('disabled', true);
1697+
}
1698+
});
1699+
16571700
});
16581701

16591702
//philipshueentertainment backward fix
@@ -1684,7 +1727,7 @@ $(document).ready(function () {
16841727
else if ($.inArray(ledDevices[idx], devHID) != -1)
16851728
optArr[4].push(ledDevices[idx]);
16861729
else if (ledDevices[idx].endsWith("_ftdi")) {
1687-
var title = ledDevices[idx].replace('_ftdi','');
1730+
var title = ledDevices[idx].replace('_ftdi', '');
16881731
optArr[5].push(ledDevices[idx] + ":" + title);
16891732
}
16901733
else
@@ -1744,6 +1787,13 @@ $(document).ready(function () {
17441787
params = { host: host };
17451788
break;
17461789

1790+
case "homeassistant":
1791+
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
1792+
var token = conf_editor.getEditor("root.specificOptions.token").getValue();
1793+
const entityIds = conf_editor.getEditor("root.specificOptions.entityIds").getValue();
1794+
params = { host: host, token: token, entity_id: entityIds };
1795+
break;
1796+
17471797
case "nanoleaf":
17481798
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
17491799
var token = conf_editor.getEditor("root.specificOptions.token").getValue();
@@ -1878,6 +1928,7 @@ function saveLedConfig(genDefLayout = false) {
18781928
}
18791929
break;
18801930

1931+
case "homeassistant":
18811932
case "nanoleaf":
18821933
case "wled":
18831934
case "yeelight":
@@ -2311,6 +2362,12 @@ function updateElements(ledType, key) {
23112362
}
23122363
break;
23132364

2365+
case "homeassistant":
2366+
updateElementsHomeAssistant(ledType, key);
2367+
hardwareLedCount = 1;
2368+
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
2369+
break;
2370+
23142371
case "atmo":
23152372
case "karate":
23162373
var ledProperties = devicesProperties[ledType][key];
@@ -2438,6 +2495,63 @@ function validateWledLedCount(hardwareLedCount) {
24382495
}
24392496
}
24402497

2498+
function updateElementsHomeAssistant(ledType, key) {
2499+
2500+
// Get configured device's details
2501+
var configuredDeviceType = window.serverConfig.device.type;
2502+
var configuredHost = window.serverConfig.device.host;
2503+
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
2504+
2505+
// New light selection list values
2506+
var enumVals = [];
2507+
var enumTitleVals = [];
2508+
var enumDefaultVal = [];
2509+
2510+
if (devicesProperties[ledType] && devicesProperties[ledType][key]) {
2511+
var ledDeviceProperties = devicesProperties[ledType][key];
2512+
2513+
if (!jQuery.isEmptyObject(ledDeviceProperties)) {
2514+
if (ledDeviceProperties && ledDeviceProperties.lightEntities) {
2515+
2516+
2517+
for (const light of ledDeviceProperties.lightEntities) {
2518+
enumVals.push(light.entity_id);
2519+
enumTitleVals.push(light.attributes.friendly_name);
2520+
}
2521+
2522+
}
2523+
}
2524+
}
2525+
2526+
// Select configured device
2527+
if (configuredDeviceType == ledType && configuredHost == host) {
2528+
let configuredEntityIds = window.serverConfig.device.entityIds;
2529+
for (const light of configuredEntityIds) {
2530+
if ($.inArray(enumVals, light) != -1) {
2531+
enumVals.push(light);
2532+
}
2533+
enumDefaultVal.push(light);
2534+
}
2535+
}
2536+
2537+
if (enumVals.length < 1) {
2538+
enumVals.push("NONE");
2539+
enumTitleVals.push($.i18n('edt_dev_spec_lights_discovered_none'));
2540+
}
2541+
else {
2542+
$('#btn_wiz_holder').show();
2543+
}
2544+
2545+
2546+
let addSchemaElements = {
2547+
"uniqueItems": true,
2548+
"minItems": 1,
2549+
"required": true
2550+
};
2551+
2552+
updateJsonEditorMultiSelection(conf_editor, 'root.specificOptions', 'entityIds', addSchemaElements, enumVals, enumTitleVals, enumDefaultVal);
2553+
}
2554+
24412555
function updateElementsWled(ledType, key) {
24422556

24432557
// Get configured device's details
@@ -2533,6 +2647,7 @@ function updateElementsWled(ledType, key) {
25332647
}
25342648
showInputOptionForItem(conf_editor, "root.specificOptions.segments", "switchOffOtherSegments", showAdditionalOptions);
25352649
}
2650+
25362651
function sortByPanelCoordinates(arr, topToBottom, leftToRight) {
25372652
arr.sort((a, b) => {
25382653
//Nanoleaf corodinates start at bottom left, therefore reverse topToBottom
@@ -2591,7 +2706,7 @@ function nanoleafGeneratelayout(panelLayout, panelOrderTopDown, panelOrderLeftRi
25912706
29: { name: "4DLightstrip", led: true, sideLengthX: 50, sideLengthY: 50 },
25922707
30: { name: "Skylight Panel", led: true, sideLengthX: 180, sideLengthY: 180 },
25932708
31: { name: "SkylightControllerPrimary", led: true, sideLengthX: 180, sideLengthY: 180 },
2594-
32: { name: "SkylightControllerPassive", led: true, sideLengthX: 180, sideLengthY: 180 },
2709+
32: { name: "SkylightControllerPassive", led: true, sideLengthX: 180, sideLengthY: 180 },
25952710
999: { name: "Unknown", led: true, sideLengthX: 100, sideLengthY: 100 }
25962711
};
25972712

assets/webconfig/js/ui_utils.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ function showInfoDialog(type, header, message) {
321321
$(document).on('click', '[data-dismiss-modal]', function () {
322322
var target = $(this).data('dismiss-modal');
323323
$($.find(target)).modal('hide');
324-
});
324+
});
325325
}
326326

327327
function createHintH(type, text, container) {
@@ -478,7 +478,7 @@ function createJsonEditor(container, schema, setconfig, usePanel, arrayre) {
478478
return editor;
479479
}
480480

481-
function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVals, newTitelVals, newDefaultVal, addSelect, addCustom, addCustomAsFirst, customText) {
481+
function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVals, newTitleVals, newDefaultVal, addSelect, addCustom, addCustomAsFirst, customText) {
482482
var editor = rootEditor.getEditor(path);
483483
var orginalProperties = editor.schema.properties[key];
484484

@@ -516,8 +516,8 @@ function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVa
516516

517517
if (addCustom) {
518518

519-
if (newTitelVals.length === 0) {
520-
newTitelVals = [...newEnumVals];
519+
if (newTitleVals.length === 0) {
520+
newTitleVals = [...newEnumVals];
521521
}
522522

523523
if (!!!customText) {
@@ -526,10 +526,10 @@ function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVa
526526

527527
if (addCustomAsFirst) {
528528
newEnumVals.unshift("CUSTOM");
529-
newTitelVals.unshift(customText);
529+
newTitleVals.unshift(customText);
530530
} else {
531531
newEnumVals.push("CUSTOM");
532-
newTitelVals.push(customText);
532+
newTitleVals.push(customText);
533533
}
534534

535535
if (newSchema[key].options.infoText) {
@@ -540,16 +540,16 @@ function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVa
540540

541541
if (addSelect) {
542542
newEnumVals.unshift("SELECT");
543-
newTitelVals.unshift("edt_conf_enum_please_select");
543+
newTitleVals.unshift("edt_conf_enum_please_select");
544544
newDefaultVal = "SELECT";
545545
}
546546

547547
if (newEnumVals) {
548548
newSchema[key]["enum"] = newEnumVals;
549549
}
550550

551-
if (newTitelVals) {
552-
newSchema[key]["options"]["enum_titles"] = newTitelVals;
551+
if (newTitleVals) {
552+
newSchema[key]["options"]["enum_titles"] = newTitleVals;
553553
}
554554
if (newDefaultVal) {
555555
newSchema[key]["default"] = newDefaultVal;
@@ -572,7 +572,7 @@ function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVa
572572
rootEditor.notifyWatchers(path + "." + key);
573573
}
574574

575-
function updateJsonEditorMultiSelection(rootEditor, path, key, addElements, newEnumVals, newTitelVals, newDefaultVal) {
575+
function updateJsonEditorMultiSelection(rootEditor, path, key, addElements, newEnumVals, newTitleVals, newDefaultVal) {
576576
var editor = rootEditor.getEditor(path);
577577
var orginalProperties = editor.schema.properties[key];
578578

@@ -617,8 +617,8 @@ function updateJsonEditorMultiSelection(rootEditor, path, key, addElements, newE
617617
newSchema[key]["items"]["enum"] = newEnumVals;
618618
}
619619

620-
if (newTitelVals) {
621-
newSchema[key]["items"]["options"]["enum_titles"] = newTitelVals;
620+
if (newTitleVals) {
621+
newSchema[key]["items"]["options"]["enum_titles"] = newTitleVals;
622622
}
623623

624624
if (newDefaultVal) {
@@ -923,8 +923,8 @@ function createTableRow(list, head, align) {
923923
el.style.verticalAlign = "middle";
924924

925925
var purifyConfig = {
926-
ADD_TAGS: ['button'],
927-
ADD_ATTR: ['onclick']
926+
ADD_TAGS: ['button'],
927+
ADD_ATTR: ['onclick']
928928
};
929929
el.innerHTML = DOMPurify.sanitize(list[i], purifyConfig);
930930
row.appendChild(el);
@@ -1403,7 +1403,7 @@ function loadScript(src, callback, ...params) {
14031403
if (isScriptLoaded(src)) {
14041404
debugMessage('Script ' + src + ' already loaded');
14051405
if (callback && typeof callback === 'function') {
1406-
callback( ...params);
1406+
callback(...params);
14071407
}
14081408
return;
14091409
}

0 commit comments

Comments
 (0)