Rename profiles to automations across backend and frontend
Rename the "profiles" entity to "automations" throughout the entire codebase for clarity. Updates Python models, storage, API routes/schemas, engine, frontend JS modules, HTML templates, CSS classes, i18n keys (en/ru/zh), dashboard, tutorials, and command palette. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ import { renderPerfSection, initPerfCharts, startPerfPolling, stopPerfPolling }
|
||||
import { startAutoRefresh, updateTabBadge } from './tabs.js';
|
||||
import {
|
||||
getTargetTypeIcon,
|
||||
ICON_TARGET, ICON_PROFILE, ICON_CLOCK, ICON_WARNING, ICON_OK,
|
||||
ICON_TARGET, ICON_AUTOMATION, ICON_CLOCK, ICON_WARNING, ICON_OK,
|
||||
ICON_STOP, ICON_STOP_PLAIN, ICON_START, ICON_AUTOSTART, ICON_HELP, ICON_SCENE,
|
||||
} from '../core/icons.js';
|
||||
import { loadScenePresets, renderScenePresetsSection } from './scene-presets.js';
|
||||
@@ -252,28 +252,28 @@ function _updateRunningMetrics(enrichedRunning) {
|
||||
|
||||
}
|
||||
|
||||
function _updateProfilesInPlace(profiles) {
|
||||
for (const p of profiles) {
|
||||
const card = document.querySelector(`[data-profile-id="${p.id}"]`);
|
||||
function _updateAutomationsInPlace(automations) {
|
||||
for (const a of automations) {
|
||||
const card = document.querySelector(`[data-automation-id="${a.id}"]`);
|
||||
if (!card) continue;
|
||||
const badge = card.querySelector('.dashboard-badge-active, .dashboard-badge-stopped');
|
||||
if (badge) {
|
||||
if (!p.enabled) {
|
||||
if (!a.enabled) {
|
||||
badge.className = 'dashboard-badge-stopped';
|
||||
badge.textContent = t('profiles.status.disabled');
|
||||
} else if (p.is_active) {
|
||||
badge.textContent = t('automations.status.disabled');
|
||||
} else if (a.is_active) {
|
||||
badge.className = 'dashboard-badge-active';
|
||||
badge.textContent = t('profiles.status.active');
|
||||
badge.textContent = t('automations.status.active');
|
||||
} else {
|
||||
badge.className = 'dashboard-badge-stopped';
|
||||
badge.textContent = t('profiles.status.inactive');
|
||||
badge.textContent = t('automations.status.inactive');
|
||||
}
|
||||
}
|
||||
const btn = card.querySelector('.dashboard-target-actions .dashboard-action-btn');
|
||||
if (btn) {
|
||||
btn.className = `dashboard-action-btn ${p.enabled ? 'stop' : 'start'}`;
|
||||
btn.setAttribute('onclick', `dashboardToggleProfile('${p.id}', ${!p.enabled})`);
|
||||
btn.innerHTML = p.enabled ? ICON_STOP_PLAIN : ICON_START;
|
||||
btn.className = `dashboard-action-btn ${a.enabled ? 'stop' : 'start'}`;
|
||||
btn.setAttribute('onclick', `dashboardToggleAutomation('${a.id}', ${!a.enabled})`);
|
||||
btn.innerHTML = a.enabled ? ICON_STOP_PLAIN : ICON_START;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,9 +368,9 @@ 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, scenePresets] = await Promise.all([
|
||||
const [targetsResp, automationsResp, devicesResp, cssResp, batchStatesResp, batchMetricsResp, scenePresets] = await Promise.all([
|
||||
fetchWithAuth('/picture-targets'),
|
||||
fetchWithAuth('/profiles').catch(() => null),
|
||||
fetchWithAuth('/automations').catch(() => null),
|
||||
fetchWithAuth('/devices').catch(() => null),
|
||||
fetchWithAuth('/color-strip-sources').catch(() => null),
|
||||
fetchWithAuth('/picture-targets/batch/states').catch(() => null),
|
||||
@@ -380,8 +380,8 @@ export async function loadDashboard(forceFullRender = false) {
|
||||
|
||||
const targetsData = await targetsResp.json();
|
||||
const targets = targetsData.targets || [];
|
||||
const profilesData = profilesResp && profilesResp.ok ? await profilesResp.json() : { profiles: [] };
|
||||
const profiles = profilesData.profiles || [];
|
||||
const automationsData = automationsResp && automationsResp.ok ? await automationsResp.json() : { automations: [] };
|
||||
const automations = automationsData.automations || [];
|
||||
const devicesData = devicesResp && devicesResp.ok ? await devicesResp.json() : { devices: [] };
|
||||
const devicesMap = {};
|
||||
for (const d of (devicesData.devices || [])) { devicesMap[d.id] = d; }
|
||||
@@ -392,12 +392,12 @@ export async function loadDashboard(forceFullRender = false) {
|
||||
const allStates = batchStatesResp && batchStatesResp.ok ? (await batchStatesResp.json()).states : {};
|
||||
const allMetrics = batchMetricsResp && batchMetricsResp.ok ? (await batchMetricsResp.json()).metrics : {};
|
||||
|
||||
// Build dynamic HTML (targets, profiles)
|
||||
// Build dynamic HTML (targets, automations)
|
||||
let dynamicHtml = '';
|
||||
let runningIds = [];
|
||||
let newAutoStartIds = '';
|
||||
|
||||
if (targets.length === 0 && profiles.length === 0 && scenePresets.length === 0) {
|
||||
if (targets.length === 0 && automations.length === 0 && scenePresets.length === 0) {
|
||||
dynamicHtml = `<div class="dashboard-no-targets">${t('dashboard.no_targets')}</div>`;
|
||||
} else {
|
||||
const enriched = targets.map(target => ({
|
||||
@@ -426,7 +426,7 @@ export async function loadDashboard(forceFullRender = false) {
|
||||
}
|
||||
if (structureUnchanged && forceFullRender) {
|
||||
if (running.length > 0) _updateRunningMetrics(running);
|
||||
_updateProfilesInPlace(profiles);
|
||||
_updateAutomationsInPlace(automations);
|
||||
_cacheUptimeElements();
|
||||
_startUptimeTimer();
|
||||
startPerfPolling();
|
||||
@@ -451,8 +451,8 @@ export async function loadDashboard(forceFullRender = false) {
|
||||
}
|
||||
}
|
||||
const statusBadge = isRunning
|
||||
? `<span class="dashboard-badge-active">${t('profiles.status.active')}</span>`
|
||||
: `<span class="dashboard-badge-stopped">${t('profiles.status.inactive')}</span>`;
|
||||
? `<span class="dashboard-badge-active">${t('automations.status.active')}</span>`
|
||||
: `<span class="dashboard-badge-stopped">${t('automations.status.inactive')}</span>`;
|
||||
const subtitle = subtitleParts.length ? `<div class="dashboard-target-subtitle">${escapeHtml(subtitleParts.join(' · '))}</div>` : '';
|
||||
const asNavSub = isLed ? 'led' : 'key_colors';
|
||||
const asNavSec = isLed ? 'led-targets' : 'kc-targets';
|
||||
@@ -480,16 +480,16 @@ export async function loadDashboard(forceFullRender = false) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (profiles.length > 0) {
|
||||
const activeProfiles = profiles.filter(p => p.is_active);
|
||||
const inactiveProfiles = profiles.filter(p => !p.is_active);
|
||||
updateTabBadge('profiles', activeProfiles.length);
|
||||
if (automations.length > 0) {
|
||||
const activeAutomations = automations.filter(a => a.is_active);
|
||||
const inactiveAutomations = automations.filter(a => !a.is_active);
|
||||
updateTabBadge('automations', activeAutomations.length);
|
||||
const sceneMap = new Map(scenePresets.map(s => [s.id, s]));
|
||||
const profileItems = [...activeProfiles, ...inactiveProfiles].map(p => renderDashboardProfile(p, sceneMap)).join('');
|
||||
const automationItems = [...activeAutomations, ...inactiveAutomations].map(a => renderDashboardAutomation(a, sceneMap)).join('');
|
||||
|
||||
dynamicHtml += `<div class="dashboard-section">
|
||||
${_sectionHeader('profiles', t('dashboard.section.profiles'), profiles.length)}
|
||||
${_sectionContent('profiles', profileItems)}
|
||||
${_sectionHeader('automations', t('dashboard.section.automations'), automations.length)}
|
||||
${_sectionContent('automations', automationItems)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -664,56 +664,56 @@ function renderDashboardTarget(target, isRunning, devicesMap = {}, cssSourceMap
|
||||
}
|
||||
}
|
||||
|
||||
function renderDashboardProfile(profile, sceneMap = new Map()) {
|
||||
const isActive = profile.is_active;
|
||||
const isDisabled = !profile.enabled;
|
||||
function renderDashboardAutomation(automation, sceneMap = new Map()) {
|
||||
const isActive = automation.is_active;
|
||||
const isDisabled = !automation.enabled;
|
||||
|
||||
let condSummary = '';
|
||||
if (profile.conditions.length > 0) {
|
||||
const parts = profile.conditions.map(c => {
|
||||
if (automation.conditions.length > 0) {
|
||||
const parts = automation.conditions.map(c => {
|
||||
if (c.condition_type === 'application') {
|
||||
const apps = (c.apps || []).join(', ');
|
||||
const matchLabel = c.match_type === 'topmost' ? t('profiles.condition.application.match_type.topmost') : t('profiles.condition.application.match_type.running');
|
||||
const matchLabel = c.match_type === 'topmost' ? t('automations.condition.application.match_type.topmost') : t('automations.condition.application.match_type.running');
|
||||
return `${apps} (${matchLabel})`;
|
||||
}
|
||||
return c.condition_type;
|
||||
});
|
||||
const logic = profile.condition_logic === 'and' ? ' & ' : ' | ';
|
||||
const logic = automation.condition_logic === 'and' ? ' & ' : ' | ';
|
||||
condSummary = parts.join(logic);
|
||||
}
|
||||
|
||||
const statusBadge = isDisabled
|
||||
? `<span class="dashboard-badge-stopped">${t('profiles.status.disabled')}</span>`
|
||||
? `<span class="dashboard-badge-stopped">${t('automations.status.disabled')}</span>`
|
||||
: isActive
|
||||
? `<span class="dashboard-badge-active">${t('profiles.status.active')}</span>`
|
||||
: `<span class="dashboard-badge-stopped">${t('profiles.status.inactive')}</span>`;
|
||||
? `<span class="dashboard-badge-active">${t('automations.status.active')}</span>`
|
||||
: `<span class="dashboard-badge-stopped">${t('automations.status.inactive')}</span>`;
|
||||
|
||||
// Scene info
|
||||
const scene = profile.scene_preset_id ? sceneMap.get(profile.scene_preset_id) : null;
|
||||
const sceneName = scene ? escapeHtml(scene.name) : t('profiles.scene.none_selected');
|
||||
const scene = automation.scene_preset_id ? sceneMap.get(automation.scene_preset_id) : null;
|
||||
const sceneName = scene ? escapeHtml(scene.name) : t('automations.scene.none_selected');
|
||||
|
||||
return `<div class="dashboard-target dashboard-profile dashboard-card-link" data-profile-id="${profile.id}" onclick="if(!event.target.closest('button')){navigateToCard('profiles',null,'profiles','data-profile-id','${profile.id}')}">
|
||||
return `<div class="dashboard-target dashboard-automation dashboard-card-link" data-automation-id="${automation.id}" onclick="if(!event.target.closest('button')){navigateToCard('automations',null,'automations','data-automation-id','${automation.id}')}">
|
||||
<div class="dashboard-target-info">
|
||||
<span class="dashboard-target-icon">${ICON_PROFILE}</span>
|
||||
<span class="dashboard-target-icon">${ICON_AUTOMATION}</span>
|
||||
<div>
|
||||
<div class="dashboard-target-name">${escapeHtml(profile.name)}</div>
|
||||
<div class="dashboard-target-name">${escapeHtml(automation.name)}</div>
|
||||
${condSummary ? `<div class="dashboard-target-subtitle">${escapeHtml(condSummary)}</div>` : ''}
|
||||
<div class="dashboard-target-subtitle">${ICON_SCENE} ${sceneName}</div>
|
||||
</div>
|
||||
${statusBadge}
|
||||
</div>
|
||||
<div class="dashboard-target-actions">
|
||||
<button class="dashboard-action-btn ${profile.enabled ? 'stop' : 'start'}" onclick="dashboardToggleProfile('${profile.id}', ${!profile.enabled})" title="${profile.enabled ? t('profiles.action.disable') : t('profiles.status.active')}">
|
||||
${profile.enabled ? ICON_STOP_PLAIN : ICON_START}
|
||||
<button class="dashboard-action-btn ${automation.enabled ? 'stop' : 'start'}" onclick="dashboardToggleAutomation('${automation.id}', ${!automation.enabled})" title="${automation.enabled ? t('automations.action.disable') : t('automations.status.active')}">
|
||||
${automation.enabled ? ICON_STOP_PLAIN : ICON_START}
|
||||
</button>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
export async function dashboardToggleProfile(profileId, enable) {
|
||||
export async function dashboardToggleAutomation(automationId, enable) {
|
||||
try {
|
||||
const endpoint = enable ? 'enable' : 'disable';
|
||||
const response = await fetchWithAuth(`/profiles/${profileId}/${endpoint}`, {
|
||||
const response = await fetchWithAuth(`/automations/${automationId}/${endpoint}`, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (response.ok) {
|
||||
@@ -721,7 +721,7 @@ export async function dashboardToggleProfile(profileId, enable) {
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.isAuth) return;
|
||||
showToast(t('dashboard.error.profile_toggle_failed'), 'error');
|
||||
showToast(t('dashboard.error.automation_toggle_failed'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,7 +817,7 @@ function _debouncedDashboardReload(forceFullRender = false) {
|
||||
}
|
||||
|
||||
document.addEventListener('server:state_change', () => _debouncedDashboardReload());
|
||||
document.addEventListener('server:profile_state_changed', () => _debouncedDashboardReload(true));
|
||||
document.addEventListener('server:automation_state_changed', () => _debouncedDashboardReload(true));
|
||||
|
||||
// Re-render dashboard when language changes
|
||||
document.addEventListener('languageChanged', () => {
|
||||
|
||||
Reference in New Issue
Block a user