Add static color support, HAOS light entity, and real-time profile updates
- Add static_color capability to WLED and serial providers with native set_color() dispatch (WLED uses JSON API, serial uses idle client) - Encapsulate device-specific logic in providers instead of device_type checks in ProcessorManager and API routes - Add HAOS light entity for devices with brightness_control + static_color (Adalight/AmbiLED get light entity, WLED keeps number entity) - Fix serial device brightness and turn-off: pass software_brightness through provider chain, clear device on color=null, re-send static color after brightness change - Add global events WebSocket (events-ws.js) replacing per-tab WS, enabling real-time profile state updates on both dashboard and profiles tabs - Fix profile activation: mark active when all targets already running, add asyncio.Lock to prevent concurrent evaluation races, skip process enumeration when no profile has conditions, trigger immediate evaluation on enable/create/update for instant target startup - Add reliable server restart script (restart.ps1) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* Dashboard — real-time target status overview.
|
||||
*/
|
||||
|
||||
import { apiKey, _dashboardWS, set_dashboardWS, _dashboardLoading, set_dashboardLoading, dashboardPollInterval, setDashboardPollInterval } from '../core/state.js';
|
||||
import { apiKey, _dashboardLoading, set_dashboardLoading, dashboardPollInterval, setDashboardPollInterval } from '../core/state.js';
|
||||
import { API_BASE, getHeaders, fetchWithAuth } from '../core/api.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
import { escapeHtml, handle401Error } from '../core/api.js';
|
||||
@@ -239,7 +239,7 @@ function formatUptime(seconds) {
|
||||
return `${s}s`;
|
||||
}
|
||||
|
||||
export async function loadDashboard() {
|
||||
export async function loadDashboard(forceFullRender = false) {
|
||||
if (_dashboardLoading) return;
|
||||
set_dashboardLoading(true);
|
||||
const container = document.getElementById('dashboard-content');
|
||||
@@ -289,7 +289,7 @@ export async function loadDashboard() {
|
||||
const newRunningIds = running.map(t => t.id).sort().join(',');
|
||||
const prevRunningIds = [..._lastRunningIds].sort().join(',');
|
||||
const hasExistingDom = !!container.querySelector('.dashboard-perf-persistent');
|
||||
if (hasExistingDom && newRunningIds === prevRunningIds && newRunningIds !== '') {
|
||||
if (!forceFullRender && hasExistingDom && newRunningIds === prevRunningIds && newRunningIds !== '') {
|
||||
_updateRunningMetrics(running);
|
||||
set_dashboardLoading(false);
|
||||
return;
|
||||
@@ -567,32 +567,23 @@ export async function dashboardStopAll() {
|
||||
}
|
||||
}
|
||||
|
||||
export function startDashboardWS() {
|
||||
stopDashboardWS();
|
||||
if (!apiKey) return;
|
||||
const wsProto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const url = `${wsProto}//${location.host}/api/v1/events/ws?token=${apiKey}`;
|
||||
try {
|
||||
set_dashboardWS(new WebSocket(url));
|
||||
_dashboardWS.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'state_change' || data.type === 'profile_state_changed') {
|
||||
loadDashboard();
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
_dashboardWS.onclose = () => { set_dashboardWS(null); };
|
||||
_dashboardWS.onerror = () => { set_dashboardWS(null); };
|
||||
} catch {
|
||||
set_dashboardWS(null);
|
||||
}
|
||||
}
|
||||
|
||||
export function stopUptimeTimer() {
|
||||
_stopUptimeTimer();
|
||||
}
|
||||
|
||||
// React to global server events when dashboard tab is active
|
||||
function _isDashboardActive() {
|
||||
return (localStorage.getItem('activeTab') || 'dashboard') === 'dashboard';
|
||||
}
|
||||
|
||||
document.addEventListener('server:state_change', () => {
|
||||
if (_isDashboardActive()) loadDashboard();
|
||||
});
|
||||
|
||||
document.addEventListener('server:profile_state_changed', () => {
|
||||
if (_isDashboardActive()) loadDashboard(true);
|
||||
});
|
||||
|
||||
// Re-render dashboard when language changes
|
||||
document.addEventListener('languageChanged', () => {
|
||||
if (!apiKey) return;
|
||||
@@ -601,10 +592,3 @@ document.addEventListener('languageChanged', () => {
|
||||
if (perfEl) perfEl.remove();
|
||||
loadDashboard();
|
||||
});
|
||||
|
||||
export function stopDashboardWS() {
|
||||
if (_dashboardWS) {
|
||||
_dashboardWS.close();
|
||||
set_dashboardWS(null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user