feat(ui): add clear device presets action
Made-with: Cursor
This commit is contained in:
Submodule led-driver updated: a22702df4d...428ed8b884
@@ -214,6 +214,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const presetsCloseButton = document.getElementById('presets-close-btn');
|
const presetsCloseButton = document.getElementById('presets-close-btn');
|
||||||
const presetsList = document.getElementById('presets-list');
|
const presetsList = document.getElementById('presets-list');
|
||||||
const presetsAddButton = document.getElementById('preset-add-btn');
|
const presetsAddButton = document.getElementById('preset-add-btn');
|
||||||
|
const presetClearDeviceButton = document.getElementById('preset-clear-device-btn');
|
||||||
const presetEditorModal = document.getElementById('preset-editor-modal');
|
const presetEditorModal = document.getElementById('preset-editor-modal');
|
||||||
const presetEditorCloseButton = document.getElementById('preset-editor-close-btn');
|
const presetEditorCloseButton = document.getElementById('preset-editor-close-btn');
|
||||||
const presetNameInput = document.getElementById('preset-name-input');
|
const presetNameInput = document.getElementById('preset-name-input');
|
||||||
@@ -283,7 +284,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (!input) {
|
if (!input) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return parseInt(input.value, 10) || 0;
|
const n = parseInt(String(input.value).trim(), 10);
|
||||||
|
return Number.isFinite(n) ? n : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPresetColors = (colors, paletteRefs) => {
|
const renderPresetColors = (colors, paletteRefs) => {
|
||||||
@@ -564,14 +566,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const nKey = `n${i}`;
|
const nKey = `n${i}`;
|
||||||
const inputEl = document.getElementById(`preset-${nKey}-input`);
|
const inputEl = document.getElementById(`preset-${nKey}-input`);
|
||||||
if (inputEl) {
|
if (inputEl) {
|
||||||
if (preset[nKey] !== undefined) {
|
if (preset[nKey] !== undefined && preset[nKey] !== null) {
|
||||||
inputEl.value = preset[nKey] || 0;
|
const raw = preset[nKey];
|
||||||
|
const n = typeof raw === 'number' ? raw : parseInt(String(raw), 10);
|
||||||
|
inputEl.value = String(Number.isFinite(n) ? n : 0);
|
||||||
} else {
|
} else {
|
||||||
const label = nToLabel[nKey];
|
const label = nToLabel[nKey];
|
||||||
if (label && preset[label] !== undefined) {
|
if (label && preset[label] !== undefined && preset[label] !== null) {
|
||||||
inputEl.value = preset[label] || 0;
|
const rawL = preset[label];
|
||||||
|
const nL = typeof rawL === 'number' ? rawL : parseInt(String(rawL), 10);
|
||||||
|
inputEl.value = String(Number.isFinite(nL) ? nL : 0);
|
||||||
} else {
|
} else {
|
||||||
inputEl.value = 0;
|
inputEl.value = '0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -811,6 +817,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasPatternMeta =
|
||||||
|
patternConfig && typeof patternConfig === 'object' && Object.keys(patternConfig).length > 0;
|
||||||
|
const hasAnyNLabel = visibleNKeys.size > 0;
|
||||||
|
|
||||||
for (let i = 1; i <= 8; i++) {
|
for (let i = 1; i <= 8; i++) {
|
||||||
const nKey = `n${i}`;
|
const nKey = `n${i}`;
|
||||||
const labelEl = document.getElementById(`preset-${nKey}-label`);
|
const labelEl = document.getElementById(`preset-${nKey}-label`);
|
||||||
@@ -824,7 +834,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (groupEl) {
|
if (groupEl) {
|
||||||
groupEl.style.display = show ? '' : 'none';
|
groupEl.style.display = show ? '' : 'none';
|
||||||
}
|
}
|
||||||
if (inputEl && !show) {
|
// Only clear hidden n inputs when we know this pattern's metadata (avoids wiping n3..n4
|
||||||
|
// while definitions are still loading, or when twinkle exists only as a driver file).
|
||||||
|
if (inputEl && !show && (hasAnyNLabel || hasPatternMeta)) {
|
||||||
inputEl.value = '0';
|
inputEl.value = '0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -969,6 +981,30 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
openEditor();
|
openEditor();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (presetClearDeviceButton) {
|
||||||
|
presetClearDeviceButton.addEventListener('click', async () => {
|
||||||
|
const section = document.querySelector('.presets-section[data-zone-id]');
|
||||||
|
const deviceNames = tabDeviceNamesFromSection(section);
|
||||||
|
if (!deviceNames.length) {
|
||||||
|
alert('No devices found in the current zone.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!window.confirm('Clear all presets on current zone devices?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const targetMacs =
|
||||||
|
typeof window.tabsManager !== 'undefined' &&
|
||||||
|
typeof window.tabsManager.resolveTabDeviceMacs === 'function'
|
||||||
|
? await window.tabsManager.resolveTabDeviceMacs(deviceNames)
|
||||||
|
: [];
|
||||||
|
await postDriverSequence([{ v: '1', clear_presets: true, save: true }], targetMacs);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Clear device presets failed:', error);
|
||||||
|
alert('Failed to clear presets on devices.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const showAddPresetToTabModal = async (optionalTabId) => {
|
const showAddPresetToTabModal = async (optionalTabId) => {
|
||||||
let zoneId = optionalTabId;
|
let zoneId = optionalTabId;
|
||||||
@@ -1405,6 +1441,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
clearForm();
|
clearForm();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const coercePresetInt = (v, def = 0) => {
|
||||||
|
if (typeof v === 'number' && Number.isFinite(v)) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
const t = parseInt(String(v), 10);
|
||||||
|
return Number.isFinite(t) ? t : def;
|
||||||
|
};
|
||||||
|
|
||||||
// Build driver messages for a single preset; deliver via /presets/push (ESP-NOW + TCP).
|
// Build driver messages for a single preset; deliver via /presets/push (ESP-NOW + TCP).
|
||||||
// Send order:
|
// Send order:
|
||||||
// 1) preset payload (optionally with save)
|
// 1) preset payload (optionally with save)
|
||||||
@@ -1429,12 +1473,12 @@ const sendPresetViaEspNow = async (presetId, preset, deviceNames, saveToDevice =
|
|||||||
? preset.brightness
|
? preset.brightness
|
||||||
: (typeof preset.br === 'number' ? preset.br : 127),
|
: (typeof preset.br === 'number' ? preset.br : 127),
|
||||||
auto: typeof preset.auto === 'boolean' ? preset.auto : true,
|
auto: typeof preset.auto === 'boolean' ? preset.auto : true,
|
||||||
n1: typeof preset.n1 === 'number' ? preset.n1 : 0,
|
n1: coercePresetInt(preset.n1),
|
||||||
n2: typeof preset.n2 === 'number' ? preset.n2 : 0,
|
n2: coercePresetInt(preset.n2),
|
||||||
n3: typeof preset.n3 === 'number' ? preset.n3 : 0,
|
n3: coercePresetInt(preset.n3),
|
||||||
n4: typeof preset.n4 === 'number' ? preset.n4 : 0,
|
n4: coercePresetInt(preset.n4),
|
||||||
n5: typeof preset.n5 === 'number' ? preset.n5 : 0,
|
n5: coercePresetInt(preset.n5),
|
||||||
n6: typeof preset.n6 === 'number' ? preset.n6 : 0,
|
n6: coercePresetInt(preset.n6),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -160,6 +160,7 @@
|
|||||||
<h2>Presets</h2>
|
<h2>Presets</h2>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button class="btn btn-primary" id="preset-add-btn">Add</button>
|
<button class="btn btn-primary" id="preset-add-btn">Add</button>
|
||||||
|
<button class="btn btn-danger" id="preset-clear-device-btn">Clear Device Presets</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="presets-list" class="profiles-list"></div>
|
<div id="presets-list" class="profiles-list"></div>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
|
|||||||
Reference in New Issue
Block a user