Add profile conditions, scene presets, MQTT integration, and Scenes tab

Feature 1 — Profile Conditions: time-of-day, system idle (Win32
GetLastInputInfo), and display state (GUID_CONSOLE_DISPLAY_STATE)
condition types for automatic profile activation.

Feature 2 — Scene Presets: snapshot/restore system that captures target
running states, device brightness, and profile enables. Server-side
capture with 5-step activation order. Dedicated Scenes tab with
CardSection-based card grid, command palette integration, and dashboard
quick-activate section.

Feature 3 — MQTT Integration: MQTTService singleton with aiomqtt,
MQTTLEDClient device provider for pixel output, MQTT profile condition
type with topic/payload matching, and frontend support for MQTT device
type and condition editor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 16:57:42 +03:00
parent bd8d7a019f
commit 2e747b5ece
38 changed files with 2269 additions and 32 deletions

View File

@@ -82,6 +82,11 @@ import {
toggleProfileEnabled, toggleProfileTargets, deleteProfile,
expandAllProfileSections, collapseAllProfileSections,
} from './features/profiles.js';
import {
loadScenes, expandAllSceneSections, collapseAllSceneSections,
openScenePresetCapture, editScenePreset, saveScenePreset, closeScenePresetEditor,
activateScenePreset, recaptureScenePreset, deleteScenePreset,
} from './features/scene-presets.js';
// Layer 5: device-discovery, targets
import {
@@ -307,6 +312,18 @@ Object.assign(window, {
expandAllProfileSections,
collapseAllProfileSections,
// scene presets
loadScenes,
expandAllSceneSections,
collapseAllSceneSections,
openScenePresetCapture,
editScenePreset,
saveScenePreset,
closeScenePresetEditor,
activateScenePreset,
recaptureScenePreset,
deleteScenePreset,
// device-discovery
onDeviceTypeChanged,
updateBaudFpsHint,
@@ -422,9 +439,9 @@ document.addEventListener('keydown', (e) => {
return;
}
// Tab shortcuts: Ctrl+1..4 (skip when typing in inputs)
// Tab shortcuts: Ctrl+1..5 (skip when typing in inputs)
if (!inInput && e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
const tabMap = { '1': 'dashboard', '2': 'profiles', '3': 'targets', '4': 'streams' };
const tabMap = { '1': 'dashboard', '2': 'profiles', '3': 'targets', '4': 'streams', '5': 'scenes' };
const tab = tabMap[e.key];
if (tab) {
e.preventDefault();