Rename picture-targets to output-targets across entire codebase

Rename all Python modules, classes, API endpoints, config keys, frontend
fetch URLs, and Home Assistant integration URLs from picture-targets to
output-targets. Store loads both new and legacy JSON keys for backward
compatibility with existing data files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 10:55:36 +03:00
parent 5b4813368b
commit 353a1c2d85
37 changed files with 243 additions and 244 deletions

View File

@@ -112,7 +112,7 @@ function _buildItems(results, states = {}) {
// Maps endpoint → response key that holds the array
const _responseKeys = [
['/devices', 'devices'],
['/picture-targets', 'targets'],
['/output-targets', 'targets'],
['/color-strip-sources', 'sources'],
['/automations', 'automations'],
['/capture-templates', 'templates'],
@@ -126,7 +126,7 @@ const _responseKeys = [
async function _fetchAllEntities() {
const [statesData, ...results] = await Promise.all([
fetchWithAuth('/picture-targets/batch/states', { retry: false, timeout: 5000 })
fetchWithAuth('/output-targets/batch/states', { retry: false, timeout: 5000 })
.then(r => r.ok ? r.json() : {})
.then(data => data.states || {})
.catch(() => ({})),

View File

@@ -419,12 +419,12 @@ export async function loadDashboard(forceFullRender = false) {
try {
// Fire all requests in a single batch to avoid sequential RTTs
const [targetsResp, automationsResp, devicesResp, cssResp, batchStatesResp, batchMetricsResp, scenePresets, syncClocksResp] = await Promise.all([
fetchWithAuth('/picture-targets'),
fetchWithAuth('/output-targets'),
fetchWithAuth('/automations').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),
fetchWithAuth('/output-targets/batch/states').catch(() => null),
fetchWithAuth('/output-targets/batch/metrics').catch(() => null),
loadScenePresets(),
fetchWithAuth('/sync-clocks').catch(() => null),
]);
@@ -746,7 +746,7 @@ export async function dashboardToggleAutomation(automationId, enable) {
export async function dashboardStartTarget(targetId) {
try {
const response = await fetchWithAuth(`/picture-targets/${targetId}/start`, {
const response = await fetchWithAuth(`/output-targets/${targetId}/start`, {
method: 'POST',
});
if (response.ok) {
@@ -764,7 +764,7 @@ export async function dashboardStartTarget(targetId) {
export async function dashboardStopTarget(targetId) {
try {
const response = await fetchWithAuth(`/picture-targets/${targetId}/stop`, {
const response = await fetchWithAuth(`/output-targets/${targetId}/stop`, {
method: 'POST',
});
if (response.ok) {
@@ -783,15 +783,15 @@ export async function dashboardStopTarget(targetId) {
export async function dashboardStopAll() {
try {
const [targetsResp, statesResp] = await Promise.all([
fetchWithAuth('/picture-targets'),
fetchWithAuth('/picture-targets/batch/states'),
fetchWithAuth('/output-targets'),
fetchWithAuth('/output-targets/batch/states'),
]);
const data = await targetsResp.json();
const statesData = statesResp.ok ? await statesResp.json() : { states: {} };
const states = statesData.states || {};
const running = (data.targets || []).filter(t => states[t.id]?.processing);
await Promise.all(running.map(t =>
fetchWithAuth(`/picture-targets/${t.id}/stop`, { method: 'POST' }).catch(() => {})
fetchWithAuth(`/output-targets/${t.id}/stop`, { method: 'POST' }).catch(() => {})
));
loadDashboard();
} catch (error) {

View File

@@ -300,7 +300,7 @@ export function createKCTargetCard(target, sourceMap, patternTemplateMap, valueS
// ===== KEY COLORS TEST =====
export async function fetchKCTest(targetId) {
const response = await fetch(`${API_BASE}/picture-targets/${targetId}/test`, {
const response = await fetch(`${API_BASE}/output-targets/${targetId}/test`, {
method: 'POST',
headers: getHeaders(),
});
@@ -539,7 +539,7 @@ export async function showKCEditor(targetId = null, cloneData = null) {
_ensurePatternEntitySelect(patTemplates);
if (targetId) {
const resp = await fetch(`${API_BASE}/picture-targets/${targetId}`, { headers: getHeaders() });
const resp = await fetch(`${API_BASE}/output-targets/${targetId}`, { headers: getHeaders() });
if (!resp.ok) throw new Error('Failed to load target');
const target = await resp.json();
const kcSettings = target.key_colors_settings || {};
@@ -653,13 +653,13 @@ export async function saveKCEditor() {
try {
let response;
if (targetId) {
response = await fetchWithAuth(`/picture-targets/${targetId}`, {
response = await fetchWithAuth(`/output-targets/${targetId}`, {
method: 'PUT',
body: JSON.stringify(payload),
});
} else {
payload.target_type = 'key_colors';
response = await fetchWithAuth('/picture-targets', {
response = await fetchWithAuth('/output-targets', {
method: 'POST',
body: JSON.stringify(payload),
});
@@ -683,7 +683,7 @@ export async function saveKCEditor() {
export async function cloneKCTarget(targetId) {
try {
const resp = await fetchWithAuth(`/picture-targets/${targetId}`);
const resp = await fetchWithAuth(`/output-targets/${targetId}`);
if (!resp.ok) throw new Error('Failed to load target');
const target = await resp.json();
showKCEditor(null, target);
@@ -699,7 +699,7 @@ export async function deleteKCTarget(targetId) {
try {
disconnectKCWebSocket(targetId);
const response = await fetchWithAuth(`/picture-targets/${targetId}`, {
const response = await fetchWithAuth(`/output-targets/${targetId}`, {
method: 'DELETE',
});
if (response.ok) {
@@ -726,7 +726,7 @@ export function updateKCBrightnessLabel(targetId, value) {
export async function saveKCBrightness(targetId, value) {
const brightness = parseInt(value) / 255;
try {
await fetch(`${API_BASE}/picture-targets/${targetId}`, {
await fetch(`${API_BASE}/output-targets/${targetId}`, {
method: 'PUT',
headers: getHeaders(),
body: JSON.stringify({ key_colors_settings: { brightness } }),
@@ -747,7 +747,7 @@ export function connectKCWebSocket(targetId) {
if (!key) return;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}${API_BASE}/picture-targets/${targetId}/ws?token=${encodeURIComponent(key)}`;
const wsUrl = `${protocol}//${window.location.host}${API_BASE}/output-targets/${targetId}/ws?token=${encodeURIComponent(key)}`;
try {
const ws = new WebSocket(wsUrl);

View File

@@ -128,7 +128,7 @@ export async function openScenePresetCapture() {
selectorGroup.style.display = '';
targetList.innerHTML = '';
try {
const resp = await fetchWithAuth('/picture-targets');
const resp = await fetchWithAuth('/output-targets');
if (resp.ok) {
const data = await resp.json();
_allTargets = data.targets || [];
@@ -328,7 +328,7 @@ export async function cloneScenePreset(presetId) {
selectorGroup.style.display = '';
targetList.innerHTML = '';
try {
const resp = await fetchWithAuth('/picture-targets');
const resp = await fetchWithAuth('/output-targets');
if (resp.ok) {
const data = await resp.json();
_allTargets = data.targets || [];

View File

@@ -337,7 +337,7 @@ export async function showTargetEditor(targetId = null, cloneData = null) {
if (targetId) {
// Editing existing target
const resp = await fetch(`${API_BASE}/picture-targets/${targetId}`, { headers: getHeaders() });
const resp = await fetch(`${API_BASE}/output-targets/${targetId}`, { headers: getHeaders() });
if (!resp.ok) throw new Error('Failed to load target');
const target = await resp.json();
@@ -478,13 +478,13 @@ export async function saveTargetEditor() {
try {
let response;
if (targetId) {
response = await fetchWithAuth(`/picture-targets/${targetId}`, {
response = await fetchWithAuth(`/output-targets/${targetId}`, {
method: 'PUT',
body: JSON.stringify(payload),
});
} else {
payload.target_type = 'led';
response = await fetchWithAuth('/picture-targets', {
response = await fetchWithAuth('/output-targets', {
method: 'POST',
body: JSON.stringify(payload),
});
@@ -550,7 +550,7 @@ export async function loadTargetsTab() {
// use DataCache for picture sources, audio sources, value sources, sync clocks
const [devicesResp, targetsResp, cssResp, patResp, psArr, valueSrcArr, asSrcArr] = await Promise.all([
fetchWithAuth('/devices'),
fetchWithAuth('/picture-targets'),
fetchWithAuth('/output-targets'),
fetchWithAuth('/color-strip-sources').catch(() => null),
fetchWithAuth('/pattern-templates').catch(() => null),
streamsCache.fetch().catch(() => []),
@@ -591,8 +591,8 @@ export async function loadTargetsTab() {
// Fetch all device states, target states, and target metrics in batch
const [batchDevStatesResp, batchTgtStatesResp, batchTgtMetricsResp] = await Promise.all([
fetchWithAuth('/devices/batch/states'),
fetchWithAuth('/picture-targets/batch/states'),
fetchWithAuth('/picture-targets/batch/metrics'),
fetchWithAuth('/output-targets/batch/states'),
fetchWithAuth('/output-targets/batch/metrics'),
]);
const allDeviceStates = batchDevStatesResp.ok ? (await batchDevStatesResp.json()).states : {};
const allTargetStates = batchTgtStatesResp.ok ? (await batchTgtStatesResp.json()).states : {};
@@ -608,7 +608,7 @@ export async function loadTargetsTab() {
let latestColors = null;
if (target.target_type === 'key_colors' && state.processing) {
try {
const colorsResp = await fetch(`${API_BASE}/picture-targets/${target.id}/colors`, { headers: getHeaders() });
const colorsResp = await fetch(`${API_BASE}/output-targets/${target.id}/colors`, { headers: getHeaders() });
if (colorsResp.ok) latestColors = await colorsResp.json();
} catch {}
}
@@ -1046,7 +1046,7 @@ async function _targetAction(action) {
export async function startTargetProcessing(targetId) {
await _targetAction(async () => {
const response = await fetchWithAuth(`/picture-targets/${targetId}/start`, {
const response = await fetchWithAuth(`/output-targets/${targetId}/start`, {
method: 'POST',
});
if (response.ok) {
@@ -1060,7 +1060,7 @@ export async function startTargetProcessing(targetId) {
export async function stopTargetProcessing(targetId) {
await _targetAction(async () => {
const response = await fetchWithAuth(`/picture-targets/${targetId}/stop`, {
const response = await fetchWithAuth(`/output-targets/${targetId}/stop`, {
method: 'POST',
});
if (response.ok) {
@@ -1083,8 +1083,8 @@ export async function stopAllKCTargets() {
async function _stopAllByType(targetType) {
try {
const [targetsResp, statesResp] = await Promise.all([
fetchWithAuth('/picture-targets'),
fetchWithAuth('/picture-targets/batch/states'),
fetchWithAuth('/output-targets'),
fetchWithAuth('/output-targets/batch/states'),
]);
const data = await targetsResp.json();
const statesData = statesResp.ok ? await statesResp.json() : { states: {} };
@@ -1096,7 +1096,7 @@ async function _stopAllByType(targetType) {
return;
}
await Promise.all(running.map(t =>
fetchWithAuth(`/picture-targets/${t.id}/stop`, { method: 'POST' }).catch(() => {})
fetchWithAuth(`/output-targets/${t.id}/stop`, { method: 'POST' }).catch(() => {})
));
showToast(t('targets.stop_all.stopped', { count: running.length }), 'success');
loadTargetsTab();
@@ -1108,7 +1108,7 @@ async function _stopAllByType(targetType) {
export async function startTargetOverlay(targetId) {
await _targetAction(async () => {
const response = await fetchWithAuth(`/picture-targets/${targetId}/overlay/start`, {
const response = await fetchWithAuth(`/output-targets/${targetId}/overlay/start`, {
method: 'POST',
});
if (response.ok) {
@@ -1122,7 +1122,7 @@ export async function startTargetOverlay(targetId) {
export async function stopTargetOverlay(targetId) {
await _targetAction(async () => {
const response = await fetchWithAuth(`/picture-targets/${targetId}/overlay/stop`, {
const response = await fetchWithAuth(`/output-targets/${targetId}/overlay/stop`, {
method: 'POST',
});
if (response.ok) {
@@ -1136,7 +1136,7 @@ export async function stopTargetOverlay(targetId) {
export async function cloneTarget(targetId) {
try {
const resp = await fetch(`${API_BASE}/picture-targets/${targetId}`, { headers: getHeaders() });
const resp = await fetch(`${API_BASE}/output-targets/${targetId}`, { headers: getHeaders() });
if (!resp.ok) throw new Error('Failed to load target');
const target = await resp.json();
showTargetEditor(null, target);
@@ -1151,7 +1151,7 @@ export async function deleteTarget(targetId) {
if (!confirmed) return;
await _targetAction(async () => {
const response = await fetchWithAuth(`/picture-targets/${targetId}`, {
const response = await fetchWithAuth(`/output-targets/${targetId}`, {
method: 'DELETE',
});
if (response.ok) {
@@ -1282,7 +1282,7 @@ function connectLedPreviewWS(targetId) {
if (!key) return;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}${API_BASE}/picture-targets/${targetId}/led-preview/ws?token=${encodeURIComponent(key)}`;
const wsUrl = `${protocol}//${window.location.host}${API_BASE}/output-targets/${targetId}/led-preview/ws?token=${encodeURIComponent(key)}`;
try {
const ws = new WebSocket(wsUrl);

View File

@@ -7,7 +7,7 @@
* - Navigation: network-first with offline fallback
*/
const CACHE_NAME = 'ledgrab-v23';
const CACHE_NAME = 'ledgrab-v24';
// Only pre-cache static assets (no auth required).
// Do NOT pre-cache '/' — it requires API key auth and would cache an error page.