feat(patterns): add new pattern suite and improve mobile controls
Add a broad set of LED patterns with metadata/tests and update zone/profile preset seeding, while refining mobile/desktop UI behavior for scrolling, brightness controls, and bulk pattern sending.
This commit is contained in:
@@ -1,5 +1,47 @@
|
||||
// Zone management JavaScript
|
||||
let currentZoneId = null;
|
||||
let brightnessSendTimeout = null;
|
||||
|
||||
function sendZoneBrightness(value) {
|
||||
const val = Math.max(0, Math.min(255, parseInt(value, 10) || 0));
|
||||
const headerSlider = document.getElementById('header-brightness-slider');
|
||||
const menuSlider = document.getElementById('menu-brightness-slider');
|
||||
if (headerSlider && String(headerSlider.value) !== String(val)) {
|
||||
headerSlider.value = String(val);
|
||||
}
|
||||
if (menuSlider && String(menuSlider.value) !== String(val)) {
|
||||
menuSlider.value = String(val);
|
||||
}
|
||||
if (brightnessSendTimeout) {
|
||||
clearTimeout(brightnessSendTimeout);
|
||||
}
|
||||
brightnessSendTimeout = setTimeout(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const section = document.querySelector('.presets-section[data-zone-id]');
|
||||
const names = typeof window.parseTabDeviceNames === 'function'
|
||||
? window.parseTabDeviceNames(section)
|
||||
: [];
|
||||
const targetMacs =
|
||||
names.length > 0 &&
|
||||
typeof window.tabsManager !== 'undefined' &&
|
||||
typeof window.tabsManager.resolveTabDeviceMacs === 'function'
|
||||
? await window.tabsManager.resolveTabDeviceMacs(names)
|
||||
: [];
|
||||
if (typeof window.postDriverSequence === 'function') {
|
||||
await window.postDriverSequence([{ v: '1', b: val, save: true }], targetMacs, 0);
|
||||
return;
|
||||
}
|
||||
// Fallback to raw websocket sender if presets.js helper isn't available yet.
|
||||
if (typeof window.sendEspnowRaw === 'function') {
|
||||
window.sendEspnowRaw({ v: '1', b: val, save: true });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to send brightness via driver sequence:', err);
|
||||
}
|
||||
})();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
const isEditModeActive = () => {
|
||||
const toggle = document.querySelector('.ui-mode-toggle');
|
||||
@@ -468,37 +510,17 @@ async function loadZoneContent(zoneId) {
|
||||
const legacyAttr = legacyOk ? ` data-device-names="${escapeHtmlAttr(names.join(","))}"` : "";
|
||||
container.innerHTML = `
|
||||
<div class="presets-section" data-zone-id="${zoneId}" data-device-names-json="${namesJsonAttr}"${legacyAttr}>
|
||||
<div class="profiles-actions presets-toolbar" style="margin-bottom: 1rem;">
|
||||
<div class="zone-brightness-group">
|
||||
<label for="zone-brightness-slider">Brightness</label>
|
||||
<input type="range" id="zone-brightness-slider" min="0" max="255" value="255">
|
||||
</div>
|
||||
</div>
|
||||
<div id="presets-list-zone" class="presets-list">
|
||||
<!-- Presets will be loaded here by presets.js -->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Wire up per-zone brightness slider to send global brightness via ESPNow.
|
||||
const brightnessSlider = container.querySelector('#zone-brightness-slider');
|
||||
let brightnessSendTimeout = null;
|
||||
if (brightnessSlider) {
|
||||
brightnessSlider.addEventListener('input', (e) => {
|
||||
const val = parseInt(e.target.value, 10) || 0;
|
||||
if (brightnessSendTimeout) {
|
||||
clearTimeout(brightnessSendTimeout);
|
||||
}
|
||||
brightnessSendTimeout = setTimeout(() => {
|
||||
if (typeof window.sendEspnowRaw === 'function') {
|
||||
try {
|
||||
window.sendEspnowRaw({ v: '1', b: val, save: true });
|
||||
} catch (err) {
|
||||
console.error('Failed to send brightness via ESPNow:', err);
|
||||
}
|
||||
}
|
||||
}, 150);
|
||||
});
|
||||
// Keep header and menu brightness controls in sync.
|
||||
const brightnessSlider = document.getElementById('header-brightness-slider');
|
||||
const menuBrightnessSlider = document.getElementById('menu-brightness-slider');
|
||||
if (menuBrightnessSlider && brightnessSlider) {
|
||||
menuBrightnessSlider.value = brightnessSlider.value;
|
||||
}
|
||||
|
||||
// Trigger presets loading if the function exists
|
||||
@@ -967,6 +989,21 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
const menuBrightnessSlider = document.getElementById('menu-brightness-slider');
|
||||
if (menuBrightnessSlider) {
|
||||
menuBrightnessSlider.addEventListener('input', (e) => {
|
||||
sendZoneBrightness(e.target.value);
|
||||
});
|
||||
}
|
||||
const headerBrightnessSlider = document.getElementById('header-brightness-slider');
|
||||
if (headerBrightnessSlider) {
|
||||
headerBrightnessSlider.addEventListener('input', (e) => {
|
||||
sendZoneBrightness(e.target.value);
|
||||
});
|
||||
// Initial sync so both controls start aligned.
|
||||
sendZoneBrightness(headerBrightnessSlider.value);
|
||||
}
|
||||
|
||||
// When run/edit mode toggles, refresh tabs UI so edit actions show/hide immediately.
|
||||
document.querySelectorAll('.ui-mode-toggle').forEach((btn) => {
|
||||
btn.addEventListener('click', async () => {
|
||||
|
||||
Reference in New Issue
Block a user