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:
@@ -14,7 +14,7 @@
|
||||
<link rel="stylesheet" href="/static/css/dashboard.css">
|
||||
<link rel="stylesheet" href="/static/css/streams.css">
|
||||
<link rel="stylesheet" href="/static/css/patterns.css">
|
||||
<link rel="stylesheet" href="/static/css/profiles.css">
|
||||
<link rel="stylesheet" href="/static/css/automations.css">
|
||||
<link rel="stylesheet" href="/static/css/tutorials.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
|
||||
</head>
|
||||
@@ -82,7 +82,7 @@
|
||||
<div class="tabs">
|
||||
<div class="tab-bar" role="tablist">
|
||||
<button class="tab-btn" data-tab="dashboard" onclick="switchTab('dashboard')" role="tab" aria-selected="true" aria-controls="tab-dashboard" id="tab-btn-dashboard" title="Ctrl+1"><svg class="icon" viewBox="0 0 24 24"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg> <span data-i18n="dashboard.title">Dashboard</span></button>
|
||||
<button class="tab-btn" data-tab="profiles" onclick="switchTab('profiles')" role="tab" aria-selected="false" aria-controls="tab-profiles" id="tab-btn-profiles" title="Ctrl+2"><svg class="icon" viewBox="0 0 24 24"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4"/><path d="M12 16h4"/><path d="M8 11h.01"/><path d="M8 16h.01"/></svg> <span data-i18n="profiles.title">Profiles</span><span class="tab-badge" id="tab-badge-profiles" style="display:none"></span></button>
|
||||
<button class="tab-btn" data-tab="automations" onclick="switchTab('automations')" role="tab" aria-selected="false" aria-controls="tab-automations" id="tab-btn-automations" title="Ctrl+2"><svg class="icon" viewBox="0 0 24 24"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4"/><path d="M12 16h4"/><path d="M8 11h.01"/><path d="M8 16h.01"/></svg> <span data-i18n="automations.title">Automations</span><span class="tab-badge" id="tab-badge-automations" style="display:none"></span></button>
|
||||
<button class="tab-btn" data-tab="targets" onclick="switchTab('targets')" role="tab" aria-selected="false" aria-controls="tab-targets" id="tab-btn-targets" title="Ctrl+3"><svg class="icon" viewBox="0 0 24 24"><path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/></svg> <span data-i18n="targets.title">Targets</span><span class="tab-badge" id="tab-badge-targets" style="display:none"></span></button>
|
||||
<button class="tab-btn" data-tab="streams" onclick="switchTab('streams')" role="tab" aria-selected="false" aria-controls="tab-streams" id="tab-btn-streams" title="Ctrl+4"><svg class="icon" viewBox="0 0 24 24"><path d="m17 2-5 5-5-5"/><rect width="20" height="15" x="2" y="7" rx="2"/></svg> <span data-i18n="streams.title">Sources</span></button>
|
||||
<button class="tab-btn" data-tab="scenes" onclick="switchTab('scenes')" role="tab" aria-selected="false" aria-controls="tab-scenes" id="tab-btn-scenes" title="Ctrl+5"><svg class="icon" viewBox="0 0 24 24"><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/><path d="M4 17v2"/><path d="M5 18H3"/></svg> <span data-i18n="scenes.title">Scenes</span><span class="tab-badge" id="tab-badge-scenes" style="display:none"></span></button>
|
||||
@@ -94,8 +94,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-panel" id="tab-profiles" role="tabpanel" aria-labelledby="tab-btn-profiles">
|
||||
<div id="profiles-content">
|
||||
<div class="tab-panel" id="tab-automations" role="tabpanel" aria-labelledby="tab-btn-automations">
|
||||
<div id="automations-content">
|
||||
<div class="loading-spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -159,7 +159,7 @@
|
||||
{% include 'modals/test-pp-template.html' %}
|
||||
{% include 'modals/stream.html' %}
|
||||
{% include 'modals/pp-template.html' %}
|
||||
{% include 'modals/profile-editor.html' %}
|
||||
{% include 'modals/automation-editor.html' %}
|
||||
{% include 'modals/scene-preset-editor.html' %}
|
||||
{% include 'modals/audio-source-editor.html' %}
|
||||
{% include 'modals/test-audio-source.html' %}
|
||||
@@ -329,7 +329,7 @@
|
||||
// Clear all tab panels
|
||||
const loginMsg = `<div class="loading">${t('auth.please_login')}</div>`;
|
||||
document.getElementById('dashboard-content').innerHTML = loginMsg;
|
||||
document.getElementById('profiles-content').innerHTML = loginMsg;
|
||||
document.getElementById('automations-content').innerHTML = loginMsg;
|
||||
document.getElementById('targets-panel-content').innerHTML = loginMsg;
|
||||
document.getElementById('streams-list').innerHTML = loginMsg;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<!-- Automation Editor Modal -->
|
||||
<div id="automation-editor-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="automation-editor-title">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="automation-editor-title"><svg class="icon" viewBox="0 0 24 24"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4"/><path d="M12 16h4"/><path d="M8 11h.01"/><path d="M8 16h.01"/></svg> <span data-i18n="automations.add">Add Automation</span></h2>
|
||||
<button class="modal-close-btn" onclick="closeAutomationEditorModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="automation-editor-form">
|
||||
<input type="hidden" id="automation-editor-id">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label for="automation-editor-name" data-i18n="automations.name">Name:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="automations.name.hint">A descriptive name for this automation</small>
|
||||
<input type="text" id="automation-editor-name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group settings-toggle-group">
|
||||
<div class="label-row">
|
||||
<label data-i18n="automations.enabled">Enabled:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="automations.enabled.hint">Disabled automations won't activate even when conditions are met</small>
|
||||
<label class="settings-toggle">
|
||||
<input type="checkbox" id="automation-editor-enabled" checked>
|
||||
<span class="settings-toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label for="automation-editor-logic" data-i18n="automations.condition_logic">Condition Logic:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="automations.condition_logic.hint">How multiple conditions are combined: ANY (OR) or ALL (AND)</small>
|
||||
<select id="automation-editor-logic">
|
||||
<option value="or" data-i18n="automations.condition_logic.or">Any condition (OR)</option>
|
||||
<option value="and" data-i18n="automations.condition_logic.and">All conditions (AND)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label data-i18n="automations.conditions">Conditions:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="automations.conditions.hint">Rules that determine when this automation activates</small>
|
||||
<div id="automation-conditions-list"></div>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="addAutomationCondition()" style="margin-top: 6px;">
|
||||
+ <span data-i18n="automations.conditions.add">Add Condition</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label data-i18n="automations.scene">Scene:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="automations.scene.hint">Scene preset to activate when conditions are met</small>
|
||||
<div id="automation-scene-selector" class="scene-selector">
|
||||
<input type="hidden" id="automation-scene-id">
|
||||
<div class="scene-selector-input-wrap">
|
||||
<input type="text" id="automation-scene-search" class="scene-selector-input" placeholder="Search scenes..." autocomplete="off" data-i18n-placeholder="automations.scene.search_placeholder">
|
||||
<button type="button" class="scene-selector-clear" id="automation-scene-clear" title="Clear">×</button>
|
||||
</div>
|
||||
<div class="scene-selector-dropdown" id="automation-scene-dropdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label for="automation-deactivation-mode" data-i18n="automations.deactivation_mode">Deactivation:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="automations.deactivation_mode.hint">What happens when conditions stop matching</small>
|
||||
<select id="automation-deactivation-mode">
|
||||
<option value="none" data-i18n="automations.deactivation_mode.none">None — keep current state</option>
|
||||
<option value="revert" data-i18n="automations.deactivation_mode.revert">Revert to previous state</option>
|
||||
<option value="fallback_scene" data-i18n="automations.deactivation_mode.fallback_scene">Activate fallback scene</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="automation-fallback-scene-group" style="display:none">
|
||||
<div class="label-row">
|
||||
<label data-i18n="automations.deactivation_scene">Fallback Scene:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="automations.deactivation_scene.hint">Scene to activate when this automation deactivates</small>
|
||||
<div id="automation-fallback-scene-selector" class="scene-selector">
|
||||
<input type="hidden" id="automation-fallback-scene-id">
|
||||
<div class="scene-selector-input-wrap">
|
||||
<input type="text" id="automation-fallback-scene-search" class="scene-selector-input" placeholder="Search scenes..." autocomplete="off" data-i18n-placeholder="automations.scene.search_placeholder">
|
||||
<button type="button" class="scene-selector-clear" id="automation-fallback-scene-clear" title="Clear">×</button>
|
||||
</div>
|
||||
<div class="scene-selector-dropdown" id="automation-fallback-scene-dropdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="automation-editor-error" class="error-message" style="display: none;"></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-icon btn-secondary" onclick="closeAutomationEditorModal()" title="Cancel" data-i18n-aria-label="aria.cancel">✕</button>
|
||||
<button class="btn btn-icon btn-primary" onclick="saveAutomationEditor()" title="Save" data-i18n-aria-label="aria.save">✓</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,110 +0,0 @@
|
||||
<!-- Profile Editor Modal -->
|
||||
<div id="profile-editor-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="profile-editor-title">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="profile-editor-title"><svg class="icon" viewBox="0 0 24 24"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4"/><path d="M12 16h4"/><path d="M8 11h.01"/><path d="M8 16h.01"/></svg> <span data-i18n="profiles.add">Add Profile</span></h2>
|
||||
<button class="modal-close-btn" onclick="closeProfileEditorModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="profile-editor-form">
|
||||
<input type="hidden" id="profile-editor-id">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label for="profile-editor-name" data-i18n="profiles.name">Name:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="profiles.name.hint">A descriptive name for this profile</small>
|
||||
<input type="text" id="profile-editor-name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group settings-toggle-group">
|
||||
<div class="label-row">
|
||||
<label data-i18n="profiles.enabled">Enabled:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="profiles.enabled.hint">Disabled profiles won't activate even when conditions are met</small>
|
||||
<label class="settings-toggle">
|
||||
<input type="checkbox" id="profile-editor-enabled" checked>
|
||||
<span class="settings-toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label for="profile-editor-logic" data-i18n="profiles.condition_logic">Condition Logic:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="profiles.condition_logic.hint">How multiple conditions are combined: ANY (OR) or ALL (AND)</small>
|
||||
<select id="profile-editor-logic">
|
||||
<option value="or" data-i18n="profiles.condition_logic.or">Any condition (OR)</option>
|
||||
<option value="and" data-i18n="profiles.condition_logic.and">All conditions (AND)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label data-i18n="profiles.conditions">Conditions:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="profiles.conditions.hint">Rules that determine when this profile activates</small>
|
||||
<div id="profile-conditions-list"></div>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="addProfileCondition()" style="margin-top: 6px;">
|
||||
+ <span data-i18n="profiles.conditions.add">Add Condition</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label data-i18n="profiles.scene">Scene:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="profiles.scene.hint">Scene preset to activate when conditions are met</small>
|
||||
<div id="profile-scene-selector" class="scene-selector">
|
||||
<input type="hidden" id="profile-scene-id">
|
||||
<div class="scene-selector-input-wrap">
|
||||
<input type="text" id="profile-scene-search" class="scene-selector-input" placeholder="Search scenes..." autocomplete="off" data-i18n-placeholder="profiles.scene.search_placeholder">
|
||||
<button type="button" class="scene-selector-clear" id="profile-scene-clear" title="Clear">×</button>
|
||||
</div>
|
||||
<div class="scene-selector-dropdown" id="profile-scene-dropdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label for="profile-deactivation-mode" data-i18n="profiles.deactivation_mode">Deactivation:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="profiles.deactivation_mode.hint">What happens when conditions stop matching</small>
|
||||
<select id="profile-deactivation-mode">
|
||||
<option value="none" data-i18n="profiles.deactivation_mode.none">None — keep current state</option>
|
||||
<option value="revert" data-i18n="profiles.deactivation_mode.revert">Revert to previous state</option>
|
||||
<option value="fallback_scene" data-i18n="profiles.deactivation_mode.fallback_scene">Activate fallback scene</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="profile-fallback-scene-group" style="display:none">
|
||||
<div class="label-row">
|
||||
<label data-i18n="profiles.deactivation_scene">Fallback Scene:</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="profiles.deactivation_scene.hint">Scene to activate when this profile deactivates</small>
|
||||
<div id="profile-fallback-scene-selector" class="scene-selector">
|
||||
<input type="hidden" id="profile-fallback-scene-id">
|
||||
<div class="scene-selector-input-wrap">
|
||||
<input type="text" id="profile-fallback-scene-search" class="scene-selector-input" placeholder="Search scenes..." autocomplete="off" data-i18n-placeholder="profiles.scene.search_placeholder">
|
||||
<button type="button" class="scene-selector-clear" id="profile-fallback-scene-clear" title="Clear">×</button>
|
||||
</div>
|
||||
<div class="scene-selector-dropdown" id="profile-fallback-scene-dropdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="profile-editor-error" class="error-message" style="display: none;"></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-icon btn-secondary" onclick="closeProfileEditorModal()" title="Cancel" data-i18n-aria-label="aria.cancel">✕</button>
|
||||
<button class="btn btn-icon btn-primary" onclick="saveProfileEditor()" title="Save" data-i18n-aria-label="aria.save">✓</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -12,7 +12,7 @@
|
||||
<label data-i18n="settings.backup.label">Backup Configuration</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="settings.backup.hint">Download all configuration (devices, targets, streams, templates, profiles) as a single JSON file.</small>
|
||||
<small class="input-hint" style="display:none" data-i18n="settings.backup.hint">Download all configuration (devices, targets, streams, templates, automations) as a single JSON file.</small>
|
||||
<button class="btn btn-primary" onclick="downloadBackup()" style="width:100%" data-i18n="settings.backup.button">Download Backup</button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user