led-control.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. // LED Control Page - Unified interface for WLED and DW LEDs
  2. let ledConfig = null;
  3. // Utility function to show status messages
  4. function showStatus(message, type = 'info') {
  5. const statusDiv = document.getElementById('dw-leds-status');
  6. if (!statusDiv) return;
  7. const iconMap = {
  8. 'success': 'check_circle',
  9. 'error': 'error',
  10. 'warning': 'warning',
  11. 'info': 'info'
  12. };
  13. const colorMap = {
  14. 'success': 'text-green-700 bg-green-50 border-green-200',
  15. 'error': 'text-red-700 bg-red-50 border-red-200',
  16. 'warning': 'text-amber-700 bg-amber-50 border-amber-200',
  17. 'info': 'text-gray-700 bg-gray-100 border-slate-200'
  18. };
  19. const icon = iconMap[type] || 'info';
  20. const colorClass = colorMap[type] || colorMap.info;
  21. statusDiv.className = `p-4 rounded-lg border ${colorClass}`;
  22. statusDiv.innerHTML = `
  23. <div class="flex items-center gap-2">
  24. <span class="material-icons">${icon}</span>
  25. <span class="text-sm">${message}</span>
  26. </div>
  27. `;
  28. }
  29. // Initialize the page based on LED configuration
  30. async function initializeLedPage() {
  31. try {
  32. const response = await fetch('/get_led_config');
  33. if (!response.ok) throw new Error('Failed to fetch LED config');
  34. ledConfig = await response.json();
  35. const notConfigured = document.getElementById('led-not-configured');
  36. const wledContainer = document.getElementById('wled-container');
  37. const dwLedsContainer = document.getElementById('dw-leds-container');
  38. // Hide all containers first
  39. notConfigured.classList.add('hidden');
  40. wledContainer.classList.add('hidden');
  41. dwLedsContainer.classList.add('hidden');
  42. if (ledConfig.provider === 'wled' && ledConfig.wled_ip) {
  43. // Show WLED iframe
  44. wledContainer.classList.remove('hidden');
  45. const wledFrame = document.getElementById('wled-frame');
  46. if (wledFrame) {
  47. wledFrame.src = `http://${ledConfig.wled_ip}`;
  48. }
  49. } else if (ledConfig.provider === 'dw_leds') {
  50. // Show DW LEDs controls
  51. dwLedsContainer.classList.remove('hidden');
  52. await initializeDWLedsControls();
  53. } else {
  54. // Show not configured message
  55. notConfigured.classList.remove('hidden');
  56. }
  57. } catch (error) {
  58. console.error('Error initializing LED page:', error);
  59. document.getElementById('led-not-configured').classList.remove('hidden');
  60. }
  61. }
  62. // Initialize DW LEDs controls
  63. async function initializeDWLedsControls() {
  64. // Check status and load available effects/palettes
  65. await checkDWLedsStatus();
  66. await loadEffectsAndPalettes();
  67. // Power toggle button
  68. document.getElementById('dw-leds-power-toggle')?.addEventListener('click', async () => {
  69. try {
  70. const response = await fetch('/api/dw_leds/power', {
  71. method: 'POST',
  72. headers: { 'Content-Type': 'application/json' },
  73. body: JSON.stringify({ state: 2 }) // Toggle
  74. });
  75. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  76. const data = await response.json();
  77. if (data.connected) {
  78. showStatus(`Power ${data.power_on ? 'ON' : 'OFF'}`, 'success');
  79. await checkDWLedsStatus();
  80. } else {
  81. showStatus(data.error || 'Failed to toggle power', 'error');
  82. }
  83. } catch (error) {
  84. showStatus(`Failed to toggle power: ${error.message}`, 'error');
  85. }
  86. });
  87. // Brightness slider
  88. const brightnessSlider = document.getElementById('dw-leds-brightness');
  89. const brightnessValue = document.getElementById('dw-leds-brightness-value');
  90. brightnessSlider?.addEventListener('input', (e) => {
  91. brightnessValue.textContent = `${e.target.value}%`;
  92. });
  93. brightnessSlider?.addEventListener('change', async (e) => {
  94. try {
  95. const response = await fetch('/api/dw_leds/brightness', {
  96. method: 'POST',
  97. headers: { 'Content-Type': 'application/json' },
  98. body: JSON.stringify({ value: parseInt(e.target.value) })
  99. });
  100. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  101. const data = await response.json();
  102. if (data.connected) {
  103. showStatus(`Brightness set to ${e.target.value}%`, 'success');
  104. } else {
  105. showStatus(data.error || 'Failed to set brightness', 'error');
  106. }
  107. } catch (error) {
  108. showStatus(`Failed to set brightness: ${error.message}`, 'error');
  109. }
  110. });
  111. // Color picker - update display when color changes
  112. const colorPicker = document.getElementById('dw-leds-color');
  113. const colorHexDisplay = document.getElementById('dw-leds-color-hex');
  114. colorPicker?.addEventListener('input', (e) => {
  115. if (colorHexDisplay) {
  116. colorHexDisplay.textContent = e.target.value.toUpperCase();
  117. }
  118. });
  119. // Color picker - apply button
  120. document.getElementById('dw-leds-set-color')?.addEventListener('click', async () => {
  121. await applyColor(colorPicker.value);
  122. });
  123. // Quick color buttons
  124. document.querySelectorAll('.dw-leds-quick-color').forEach(button => {
  125. button.addEventListener('click', async () => {
  126. const hexColor = button.getAttribute('data-color');
  127. await applyColor(hexColor);
  128. // Update color picker and hex display to match
  129. if (colorPicker) colorPicker.value = hexColor;
  130. if (colorHexDisplay) colorHexDisplay.textContent = hexColor.toUpperCase();
  131. });
  132. });
  133. // Effect color pickers - apply immediately on change
  134. document.querySelectorAll('.effect-color-picker').forEach(picker => {
  135. picker.addEventListener('change', async () => {
  136. const color1 = document.getElementById('dw-leds-color1')?.value;
  137. const color2 = document.getElementById('dw-leds-color2')?.value;
  138. const color3 = document.getElementById('dw-leds-color3')?.value;
  139. if (color1 && color2 && color3) {
  140. await applyAllColors(color1, color2, color3);
  141. }
  142. });
  143. });
  144. // Effect selector
  145. document.getElementById('dw-leds-effect-select')?.addEventListener('change', async (e) => {
  146. const effectId = parseInt(e.target.value);
  147. if (isNaN(effectId)) return;
  148. try {
  149. const response = await fetch('/api/dw_leds/effect', {
  150. method: 'POST',
  151. headers: { 'Content-Type': 'application/json' },
  152. body: JSON.stringify({ effect_id: effectId })
  153. });
  154. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  155. const data = await response.json();
  156. if (data.connected) {
  157. showStatus(`Effect changed`, 'success');
  158. // Update power button state if backend auto-powered on
  159. if (data.power_on !== undefined) {
  160. updatePowerButtonUI(data.power_on);
  161. }
  162. } else {
  163. showStatus(data.error || 'Failed to set effect', 'error');
  164. }
  165. } catch (error) {
  166. showStatus(`Failed to set effect: ${error.message}`, 'error');
  167. }
  168. });
  169. // Palette selector
  170. document.getElementById('dw-leds-palette-select')?.addEventListener('change', async (e) => {
  171. const paletteId = parseInt(e.target.value);
  172. if (isNaN(paletteId)) return;
  173. try {
  174. const response = await fetch('/api/dw_leds/palette', {
  175. method: 'POST',
  176. headers: { 'Content-Type': 'application/json' },
  177. body: JSON.stringify({ palette_id: paletteId })
  178. });
  179. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  180. const data = await response.json();
  181. if (data.connected) {
  182. showStatus(`Palette changed`, 'success');
  183. // Update power button state if backend auto-powered on
  184. if (data.power_on !== undefined) {
  185. updatePowerButtonUI(data.power_on);
  186. }
  187. } else {
  188. showStatus(data.error || 'Failed to set palette', 'error');
  189. }
  190. } catch (error) {
  191. showStatus(`Failed to set palette: ${error.message}`, 'error');
  192. }
  193. });
  194. // Speed slider
  195. const speedSlider = document.getElementById('dw-leds-speed');
  196. const speedValue = document.getElementById('dw-leds-speed-value');
  197. speedSlider?.addEventListener('input', (e) => {
  198. speedValue.textContent = e.target.value;
  199. });
  200. speedSlider?.addEventListener('change', async (e) => {
  201. try {
  202. const response = await fetch('/api/dw_leds/speed', {
  203. method: 'POST',
  204. headers: { 'Content-Type': 'application/json' },
  205. body: JSON.stringify({ speed: parseInt(e.target.value) })
  206. });
  207. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  208. const data = await response.json();
  209. if (data.connected) {
  210. showStatus(`Speed updated`, 'success');
  211. } else {
  212. showStatus(data.error || 'Failed to set speed', 'error');
  213. }
  214. } catch (error) {
  215. showStatus(`Failed to set speed: ${error.message}`, 'error');
  216. }
  217. });
  218. // Intensity slider
  219. const intensitySlider = document.getElementById('dw-leds-intensity');
  220. const intensityValue = document.getElementById('dw-leds-intensity-value');
  221. intensitySlider?.addEventListener('input', (e) => {
  222. intensityValue.textContent = e.target.value;
  223. });
  224. intensitySlider?.addEventListener('change', async (e) => {
  225. try {
  226. const response = await fetch('/api/dw_leds/intensity', {
  227. method: 'POST',
  228. headers: { 'Content-Type': 'application/json' },
  229. body: JSON.stringify({ intensity: parseInt(e.target.value) })
  230. });
  231. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  232. const data = await response.json();
  233. if (data.connected) {
  234. showStatus(`Intensity updated`, 'success');
  235. } else {
  236. showStatus(data.error || 'Failed to set intensity', 'error');
  237. }
  238. } catch (error) {
  239. showStatus(`Failed to set intensity: ${error.message}`, 'error');
  240. }
  241. });
  242. // Save Current Idle Effect
  243. document.getElementById('dw-leds-save-current-idle')?.addEventListener('click', async () => {
  244. await saveCurrentEffectSettings('idle');
  245. });
  246. // Clear Idle Effect
  247. document.getElementById('dw-leds-clear-idle')?.addEventListener('click', async () => {
  248. await clearEffectSettings('idle');
  249. });
  250. // Save Current Playing Effect
  251. document.getElementById('dw-leds-save-current-playing')?.addEventListener('click', async () => {
  252. await saveCurrentEffectSettings('playing');
  253. });
  254. // Clear Playing Effect
  255. document.getElementById('dw-leds-clear-playing')?.addEventListener('click', async () => {
  256. await clearEffectSettings('playing');
  257. });
  258. // Load and display saved effect settings
  259. await loadEffectSettings();
  260. }
  261. // Save current LED settings as idle or playing effect
  262. async function saveCurrentEffectSettings(type) {
  263. try {
  264. const effectId = parseInt(document.getElementById('dw-leds-effect-select')?.value) || 0;
  265. const paletteId = parseInt(document.getElementById('dw-leds-palette-select')?.value) || 0;
  266. const speed = parseInt(document.getElementById('dw-leds-speed')?.value) || 128;
  267. const intensity = parseInt(document.getElementById('dw-leds-intensity')?.value) || 128;
  268. // Get effect colors
  269. const color1 = document.getElementById('dw-leds-color1')?.value || '#ff0000';
  270. const color2 = document.getElementById('dw-leds-color2')?.value || '#000000';
  271. const color3 = document.getElementById('dw-leds-color3')?.value || '#0000ff';
  272. const settings = {
  273. type: type, // 'idle' or 'playing'
  274. effect_id: effectId,
  275. palette_id: paletteId,
  276. speed: speed,
  277. intensity: intensity,
  278. color1: color1,
  279. color2: color2,
  280. color3: color3
  281. };
  282. const response = await fetch('/api/dw_leds/save_effect_settings', {
  283. method: 'POST',
  284. headers: { 'Content-Type': 'application/json' },
  285. body: JSON.stringify(settings)
  286. });
  287. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  288. const data = await response.json();
  289. showStatus(`${type.charAt(0).toUpperCase() + type.slice(1)} effect settings saved successfully`, 'success');
  290. // Refresh display
  291. await loadEffectSettings();
  292. } catch (error) {
  293. showStatus(`Failed to save ${type} effect settings: ${error.message}`, 'error');
  294. }
  295. }
  296. // Clear effect settings
  297. async function clearEffectSettings(type) {
  298. try {
  299. const response = await fetch('/api/dw_leds/clear_effect_settings', {
  300. method: 'POST',
  301. headers: { 'Content-Type': 'application/json' },
  302. body: JSON.stringify({ type: type })
  303. });
  304. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  305. showStatus(`${type.charAt(0).toUpperCase() + type.slice(1)} effect cleared`, 'success');
  306. // Refresh display
  307. await loadEffectSettings();
  308. } catch (error) {
  309. showStatus(`Failed to clear ${type} effect: ${error.message}`, 'error');
  310. }
  311. }
  312. // Load and display saved effect settings
  313. async function loadEffectSettings() {
  314. try {
  315. const response = await fetch('/api/dw_leds/get_effect_settings');
  316. if (!response.ok) return;
  317. const data = await response.json();
  318. // Display idle settings
  319. const idleDisplay = document.getElementById('idle-settings-display');
  320. if (idleDisplay) {
  321. idleDisplay.textContent = formatEffectSettings(data.idle_effect);
  322. }
  323. // Display playing settings
  324. const playingDisplay = document.getElementById('playing-settings-display');
  325. if (playingDisplay) {
  326. playingDisplay.textContent = formatEffectSettings(data.playing_effect);
  327. }
  328. } catch (error) {
  329. console.error('Failed to load effect settings:', error);
  330. }
  331. }
  332. // Format effect settings for display
  333. function formatEffectSettings(settings) {
  334. if (!settings) {
  335. return 'Not configured (LEDs will turn off)';
  336. }
  337. const parts = [];
  338. // Get effect name from select (if available)
  339. const effectSelect = document.getElementById('dw-leds-effect-select');
  340. if (effectSelect && settings.effect_id !== undefined) {
  341. const effectOption = effectSelect.querySelector(`option[value="${settings.effect_id}"]`);
  342. parts.push(`Effect: ${effectOption ? effectOption.textContent : settings.effect_id}`);
  343. }
  344. // Get palette name from select (if available)
  345. const paletteSelect = document.getElementById('dw-leds-palette-select');
  346. if (paletteSelect && settings.palette_id !== undefined) {
  347. const paletteOption = paletteSelect.querySelector(`option[value="${settings.palette_id}"]`);
  348. parts.push(`Palette: ${paletteOption ? paletteOption.textContent : settings.palette_id}`);
  349. }
  350. if (settings.speed !== undefined) {
  351. parts.push(`Speed: ${settings.speed}`);
  352. }
  353. if (settings.intensity !== undefined) {
  354. parts.push(`Intensity: ${settings.intensity}`);
  355. }
  356. if (settings.color1) {
  357. parts.push(`Colors: ${settings.color1}, ${settings.color2 || '#000000'}, ${settings.color3 || '#0000ff'}`);
  358. }
  359. return parts.join(' | ');
  360. }
  361. // Helper function to apply color
  362. async function applyColor(hexColor) {
  363. try {
  364. // Convert hex to RGB
  365. const r = parseInt(hexColor.slice(1, 3), 16);
  366. const g = parseInt(hexColor.slice(3, 5), 16);
  367. const b = parseInt(hexColor.slice(5, 7), 16);
  368. const response = await fetch('/api/dw_leds/color', {
  369. method: 'POST',
  370. headers: { 'Content-Type': 'application/json' },
  371. body: JSON.stringify({ r, g, b })
  372. });
  373. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  374. const data = await response.json();
  375. if (data.connected) {
  376. showStatus(`Color set to ${hexColor.toUpperCase()}`, 'success');
  377. // Update power button state if backend auto-powered on
  378. if (data.power_on !== undefined) {
  379. updatePowerButtonUI(data.power_on);
  380. }
  381. } else {
  382. showStatus(data.error || 'Failed to set color', 'error');
  383. }
  384. } catch (error) {
  385. showStatus(`Failed to set color: ${error.message}`, 'error');
  386. }
  387. }
  388. // Helper function to apply all effect colors
  389. async function applyAllColors(hexColor1, hexColor2, hexColor3) {
  390. try {
  391. const payload = {};
  392. if (hexColor1) {
  393. const r = parseInt(hexColor1.slice(1, 3), 16);
  394. const g = parseInt(hexColor1.slice(3, 5), 16);
  395. const b = parseInt(hexColor1.slice(5, 7), 16);
  396. payload.color1 = [r, g, b];
  397. }
  398. if (hexColor2) {
  399. const r = parseInt(hexColor2.slice(1, 3), 16);
  400. const g = parseInt(hexColor2.slice(3, 5), 16);
  401. const b = parseInt(hexColor2.slice(5, 7), 16);
  402. payload.color2 = [r, g, b];
  403. }
  404. if (hexColor3) {
  405. const r = parseInt(hexColor3.slice(1, 3), 16);
  406. const g = parseInt(hexColor3.slice(3, 5), 16);
  407. const b = parseInt(hexColor3.slice(5, 7), 16);
  408. payload.color3 = [r, g, b];
  409. }
  410. const response = await fetch('/api/dw_leds/colors', {
  411. method: 'POST',
  412. headers: { 'Content-Type': 'application/json' },
  413. body: JSON.stringify(payload)
  414. });
  415. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  416. const data = await response.json();
  417. if (data.connected) {
  418. showStatus(`Effect colors updated`, 'success');
  419. } else {
  420. showStatus(data.error || 'Failed to set colors', 'error');
  421. }
  422. } catch (error) {
  423. showStatus(`Failed to set colors: ${error.message}`, 'error');
  424. }
  425. }
  426. // Load available effects and palettes
  427. async function loadEffectsAndPalettes() {
  428. try {
  429. // Load effects
  430. const effectsResponse = await fetch('/api/dw_leds/effects');
  431. if (effectsResponse.ok) {
  432. const effectsData = await effectsResponse.json();
  433. const effectSelect = document.getElementById('dw-leds-effect-select');
  434. const idleEffectSelect = document.getElementById('dw-leds-idle-effect');
  435. const playingEffectSelect = document.getElementById('dw-leds-playing-effect');
  436. if (effectSelect && effectsData.effects) {
  437. effectSelect.innerHTML = '';
  438. effectsData.effects.forEach(([id, name]) => {
  439. const option = document.createElement('option');
  440. option.value = id;
  441. option.textContent = name;
  442. effectSelect.appendChild(option);
  443. });
  444. }
  445. // Add effects to automation selectors
  446. if (idleEffectSelect && effectsData.effects) {
  447. idleEffectSelect.innerHTML = '<option value="off">Off</option>';
  448. effectsData.effects.forEach(([, name]) => {
  449. const option = document.createElement('option');
  450. option.value = name.toLowerCase();
  451. option.textContent = name;
  452. idleEffectSelect.appendChild(option);
  453. });
  454. }
  455. if (playingEffectSelect && effectsData.effects) {
  456. playingEffectSelect.innerHTML = '<option value="off">Off</option>';
  457. effectsData.effects.forEach(([, name]) => {
  458. const option = document.createElement('option');
  459. option.value = name.toLowerCase();
  460. option.textContent = name;
  461. playingEffectSelect.appendChild(option);
  462. });
  463. }
  464. // Load saved automation settings
  465. const configResponse = await fetch('/get_led_config');
  466. if (configResponse.ok) {
  467. const config = await configResponse.json();
  468. if (idleEffectSelect && config.dw_led_idle_effect) {
  469. idleEffectSelect.value = config.dw_led_idle_effect;
  470. }
  471. if (playingEffectSelect && config.dw_led_playing_effect) {
  472. playingEffectSelect.value = config.dw_led_playing_effect;
  473. }
  474. }
  475. }
  476. // Load palettes
  477. const palettesResponse = await fetch('/api/dw_leds/palettes');
  478. if (palettesResponse.ok) {
  479. const palettesData = await palettesResponse.json();
  480. const paletteSelect = document.getElementById('dw-leds-palette-select');
  481. if (paletteSelect && palettesData.palettes) {
  482. paletteSelect.innerHTML = '';
  483. palettesData.palettes.forEach(([id, name]) => {
  484. const option = document.createElement('option');
  485. option.value = id;
  486. option.textContent = name;
  487. paletteSelect.appendChild(option);
  488. });
  489. }
  490. }
  491. } catch (error) {
  492. console.error('Failed to load effects and palettes:', error);
  493. showStatus('Failed to load effects and palettes', 'error');
  494. }
  495. }
  496. // Helper function to update power button UI based on power state
  497. function updatePowerButtonUI(powerOn) {
  498. const powerButton = document.getElementById('dw-leds-power-toggle');
  499. const powerButtonText = document.getElementById('dw-leds-power-text');
  500. if (powerButton && powerButtonText) {
  501. if (powerOn) {
  502. powerButton.className = 'flex items-center justify-center gap-2 rounded-lg bg-red-600 px-4 py-3 text-sm font-semibold text-white shadow-md hover:bg-red-700 transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-offset-2';
  503. powerButtonText.textContent = 'Turn OFF';
  504. } else {
  505. powerButton.className = 'flex items-center justify-center gap-2 rounded-lg bg-green-600 px-4 py-3 text-sm font-semibold text-white shadow-md hover:bg-green-700 transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-offset-2';
  506. powerButtonText.textContent = 'Turn ON';
  507. }
  508. }
  509. }
  510. // Check DW LEDs connection status
  511. async function checkDWLedsStatus() {
  512. try {
  513. const response = await fetch('/api/dw_leds/status');
  514. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  515. const data = await response.json();
  516. if (data.connected) {
  517. const powerState = data.power_on ? 'ON' : 'OFF';
  518. showStatus(`Connected: ${data.num_leds} LEDs on GPIO ${data.gpio_pin} - Power: ${powerState}`, 'success');
  519. // Update power button appearance
  520. updatePowerButtonUI(data.power_on);
  521. // Update slider values
  522. const brightnessSlider = document.getElementById('dw-leds-brightness');
  523. const brightnessValue = document.getElementById('dw-leds-brightness-value');
  524. if (brightnessSlider && data.brightness !== undefined) {
  525. brightnessSlider.value = data.brightness;
  526. if (brightnessValue) brightnessValue.textContent = `${data.brightness}%`;
  527. }
  528. const speedSlider = document.getElementById('dw-leds-speed');
  529. const speedValue = document.getElementById('dw-leds-speed-value');
  530. if (speedSlider && data.speed !== undefined) {
  531. speedSlider.value = data.speed;
  532. if (speedValue) speedValue.textContent = data.speed;
  533. }
  534. const intensitySlider = document.getElementById('dw-leds-intensity');
  535. const intensityValue = document.getElementById('dw-leds-intensity-value');
  536. if (intensitySlider && data.intensity !== undefined) {
  537. intensitySlider.value = data.intensity;
  538. if (intensityValue) intensityValue.textContent = data.intensity;
  539. }
  540. // Update effect and palette selectors
  541. const effectSelect = document.getElementById('dw-leds-effect-select');
  542. if (effectSelect && data.current_effect !== undefined) {
  543. effectSelect.value = data.current_effect;
  544. }
  545. const paletteSelect = document.getElementById('dw-leds-palette-select');
  546. if (paletteSelect && data.current_palette !== undefined) {
  547. paletteSelect.value = data.current_palette;
  548. }
  549. } else {
  550. // Show error message from controller
  551. const errorMsg = data.error || 'Connection failed';
  552. showStatus(errorMsg, 'error');
  553. }
  554. } catch (error) {
  555. showStatus(`Cannot connect to DW LEDs: ${error.message}`, 'error');
  556. }
  557. }
  558. // Initialize on page load
  559. document.addEventListener('DOMContentLoaded', initializeLedPage);