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:
@@ -13,6 +13,7 @@ import {
|
||||
ICON_TARGET, ICON_PROFILE, ICON_CLOCK, ICON_WARNING, ICON_OK,
|
||||
ICON_STOP, ICON_STOP_PLAIN, ICON_START, ICON_AUTOSTART, ICON_HELP,
|
||||
} from '../core/icons.js';
|
||||
import { loadScenePresets, renderScenePresetsSection } from './scene-presets.js';
|
||||
|
||||
const DASHBOARD_COLLAPSED_KEY = 'dashboard_collapsed';
|
||||
const MAX_FPS_SAMPLES = 120;
|
||||
@@ -373,13 +374,14 @@ export async function loadDashboard(forceFullRender = false) {
|
||||
|
||||
try {
|
||||
// Fire all requests in a single batch to avoid sequential RTTs
|
||||
const [targetsResp, profilesResp, devicesResp, cssResp, batchStatesResp, batchMetricsResp] = await Promise.all([
|
||||
const [targetsResp, profilesResp, devicesResp, cssResp, batchStatesResp, batchMetricsResp, scenePresets] = await Promise.all([
|
||||
fetchWithAuth('/picture-targets'),
|
||||
fetchWithAuth('/profiles').catch(() => null),
|
||||
fetchWithAuth('/devices').catch(() => null),
|
||||
fetchWithAuth('/color-strip-sources').catch(() => null),
|
||||
fetchWithAuth('/picture-targets/batch/states').catch(() => null),
|
||||
fetchWithAuth('/picture-targets/batch/metrics').catch(() => null),
|
||||
loadScenePresets(),
|
||||
]);
|
||||
|
||||
const targetsData = await targetsResp.json();
|
||||
@@ -401,7 +403,7 @@ export async function loadDashboard(forceFullRender = false) {
|
||||
let runningIds = [];
|
||||
let newAutoStartIds = '';
|
||||
|
||||
if (targets.length === 0 && profiles.length === 0) {
|
||||
if (targets.length === 0 && profiles.length === 0 && scenePresets.length === 0) {
|
||||
dynamicHtml = `<div class="dashboard-no-targets">${t('dashboard.no_targets')}</div>`;
|
||||
} else {
|
||||
const enriched = targets.map(target => ({
|
||||
@@ -496,6 +498,17 @@ export async function loadDashboard(forceFullRender = false) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Scene Presets section
|
||||
if (scenePresets.length > 0) {
|
||||
const sceneSec = renderScenePresetsSection(scenePresets);
|
||||
if (sceneSec) {
|
||||
dynamicHtml += `<div class="dashboard-section">
|
||||
${_sectionHeader('scenes', t('dashboard.section.scenes'), scenePresets.length, sceneSec.headerExtra)}
|
||||
${_sectionContent('scenes', sceneSec.content)}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
if (targets.length > 0) {
|
||||
let targetsInner = '';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user