Add 6 new device providers, IconSelect grids, and UI fixes
New device providers: ESP-NOW, Philips Hue, USB HID, SPI Direct, Razer Chroma SDK, and SteelSeries GameSense — each with client, provider, full backend registration, schemas, routes, and frontend support including discovery, form fields, and i18n. Add IconSelect grids for SPI LED chipset selector and GameSense peripheral type selector with new Lucide icons (cpu, keyboard, mouse, headphones). Replace emoji graph overlay buttons (eye, bell) with proper SVG path icons for consistent cross-platform rendering. Fix connection overlay causing horizontal scroll by adding overflow: hidden. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,30 @@ export function isDmxDevice(type) {
|
||||
return type === 'dmx';
|
||||
}
|
||||
|
||||
export function isEspnowDevice(type) {
|
||||
return type === 'espnow';
|
||||
}
|
||||
|
||||
export function isHueDevice(type) {
|
||||
return type === 'hue';
|
||||
}
|
||||
|
||||
export function isUsbhidDevice(type) {
|
||||
return type === 'usbhid';
|
||||
}
|
||||
|
||||
export function isSpiDevice(type) {
|
||||
return type === 'spi';
|
||||
}
|
||||
|
||||
export function isChromaDevice(type) {
|
||||
return type === 'chroma';
|
||||
}
|
||||
|
||||
export function isGameSenseDevice(type) {
|
||||
return type === 'gamesense';
|
||||
}
|
||||
|
||||
export function handle401Error() {
|
||||
if (!apiKey) return; // Already handled or no session
|
||||
localStorage.removeItem('wled_api_key');
|
||||
|
||||
@@ -315,12 +315,12 @@ function _createOverlay(node, nodeWidth, callbacks) {
|
||||
|
||||
// Test button for applicable kinds
|
||||
if (TEST_KINDS.has(node.kind) || (node.kind === 'output_target' && node.subtype === 'key_colors')) {
|
||||
btns.push({ icon: '\uD83D\uDC41', action: 'test', cls: '' }); // 👁 test/preview
|
||||
btns.push({ svgPath: P.eye, action: 'test', cls: '' });
|
||||
}
|
||||
|
||||
// Notification test for notification color strip sources
|
||||
if (node.kind === 'color_strip_source' && node.subtype === 'notification') {
|
||||
btns.push({ icon: '\uD83D\uDD14', action: 'notify', cls: '' }); // 🔔
|
||||
btns.push({ svgPath: P.bellRing, action: 'notify', cls: '' });
|
||||
}
|
||||
|
||||
// Always: edit and delete
|
||||
@@ -359,9 +359,22 @@ function _createOverlay(node, nodeWidth, callbacks) {
|
||||
const by = oy + 2;
|
||||
const bg = svgEl('g', { class: `graph-node-overlay-btn ${btn.cls}` });
|
||||
bg.appendChild(svgEl('rect', { x: bx, y: by, width: btnSize, height: btnSize }));
|
||||
const txt = svgEl('text', { x: bx + btnSize / 2, y: by + btnSize / 2 });
|
||||
txt.textContent = btn.icon;
|
||||
bg.appendChild(txt);
|
||||
if (btn.svgPath) {
|
||||
const iconG = svgEl('g', {
|
||||
transform: `translate(${bx + 2}, ${by + 2}) scale(${(btnSize - 4) / 24})`,
|
||||
});
|
||||
iconG.innerHTML = btn.svgPath;
|
||||
iconG.setAttribute('fill', 'none');
|
||||
iconG.setAttribute('stroke', 'currentColor');
|
||||
iconG.setAttribute('stroke-width', '2');
|
||||
iconG.setAttribute('stroke-linecap', 'round');
|
||||
iconG.setAttribute('stroke-linejoin', 'round');
|
||||
bg.appendChild(iconG);
|
||||
} else {
|
||||
const txt = svgEl('text', { x: bx + btnSize / 2, y: by + btnSize / 2 });
|
||||
txt.textContent = btn.icon;
|
||||
bg.appendChild(txt);
|
||||
}
|
||||
const btnTip = svgEl('title');
|
||||
btnTip.textContent = ACTION_LABELS[btn.action] || btn.action;
|
||||
bg.appendChild(btnTip);
|
||||
|
||||
@@ -74,3 +74,7 @@ export const power = '<path d="M18.36 6.64a9 9 0 1 1-12.73 0"/><line x1="
|
||||
export const wifi = '<path d="M12 20h.01"/><path d="M2 8.82a15 15 0 0 1 20 0"/><path d="M5 12.859a10 10 0 0 1 14 0"/><path d="M8.5 16.429a5 5 0 0 1 7 0"/>';
|
||||
export const flame = '<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>';
|
||||
export const usb = '<circle cx="10" cy="7" r="1"/><circle cx="4" cy="20" r="1"/><path d="M4.7 19.3 19 5"/><path d="m21 3-3 1 2 2Z"/><path d="M10 8v3a1 1 0 0 1-1 1H4"/><path d="M14 12v2a1 1 0 0 0 1 1h3"/><circle cx="20" cy="15" r="1"/>';
|
||||
export const cpu = '<rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M15 2v2"/><path d="M15 20v2"/><path d="M2 15h2"/><path d="M2 9h2"/><path d="M20 15h2"/><path d="M20 9h2"/><path d="M9 2v2"/><path d="M9 20v2"/>';
|
||||
export const keyboard = '<path d="M10 8h.01"/><path d="M12 12h.01"/><path d="M14 8h.01"/><path d="M16 12h.01"/><path d="M18 8h.01"/><path d="M6 8h.01"/><path d="M7 16h10"/><path d="M8 12h.01"/><rect width="20" height="16" x="2" y="4" rx="2"/>';
|
||||
export const mouse = '<rect x="5" y="2" width="14" height="20" rx="7"/><path d="M12 6v4"/>';
|
||||
export const headphones = '<path d="M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 18 0v7a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3"/>';
|
||||
|
||||
@@ -37,6 +37,8 @@ const _deviceTypeIcons = {
|
||||
wled: _svg(P.wifi), adalight: _svg(P.usb), ambiled: _svg(P.usb),
|
||||
mqtt: _svg(P.send), ws: _svg(P.globe), openrgb: _svg(P.palette),
|
||||
dmx: _svg(P.radio), mock: _svg(P.wrench),
|
||||
espnow: _svg(P.radio), hue: _svg(P.lightbulb), usbhid: _svg(P.usb),
|
||||
spi: _svg(P.plug), chroma: _svg(P.zap), gamesense: _svg(P.target),
|
||||
};
|
||||
const _engineTypeIcons = {
|
||||
mss: _svg(P.monitor), dxcam: _svg(P.zap), bettercam: _svg(P.rocket),
|
||||
@@ -171,3 +173,7 @@ export const ICON_UNDO = _svg(P.undo2);
|
||||
export const ICON_SCENE = _svg(P.sparkles);
|
||||
export const ICON_CAPTURE = _svg(P.camera);
|
||||
export const ICON_BELL = _svg(P.bellRing);
|
||||
export const ICON_CPU = _svg(P.cpu);
|
||||
export const ICON_KEYBOARD = _svg(P.keyboard);
|
||||
export const ICON_MOUSE = _svg(P.mouse);
|
||||
export const ICON_HEADPHONES = _svg(P.headphones);
|
||||
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
_discoveryScanRunning, set_discoveryScanRunning,
|
||||
_discoveryCache, set_discoveryCache,
|
||||
} from '../core/state.js';
|
||||
import { API_BASE, fetchWithAuth, isSerialDevice, isMockDevice, isMqttDevice, isWsDevice, isOpenrgbDevice, isDmxDevice, escapeHtml } from '../core/api.js';
|
||||
import { API_BASE, fetchWithAuth, isSerialDevice, isMockDevice, isMqttDevice, isWsDevice, isOpenrgbDevice, isDmxDevice, isEspnowDevice, isHueDevice, isUsbhidDevice, isSpiDevice, isChromaDevice, isGameSenseDevice, escapeHtml } from '../core/api.js';
|
||||
import { devicesCache } from '../core/state.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
import { showToast, desktopFocus } from '../core/ui.js';
|
||||
import { Modal } from '../core/modal.js';
|
||||
import { _computeMaxFps, _renderFpsHint } from './devices.js';
|
||||
import { getDeviceTypeIcon, ICON_RADIO, ICON_GLOBE } from '../core/icons.js';
|
||||
import { getDeviceTypeIcon, ICON_RADIO, ICON_GLOBE, ICON_CPU, ICON_KEYBOARD, ICON_MOUSE, ICON_HEADPHONES, ICON_PLUG, ICON_TARGET_ICON, ICON_ACTIVITY } from '../core/icons.js';
|
||||
import { IconSelect } from '../core/icon-select.js';
|
||||
|
||||
class AddDeviceModal extends Modal {
|
||||
@@ -41,7 +41,7 @@ const addDeviceModal = new AddDeviceModal();
|
||||
|
||||
/* ── Icon-grid type selector ──────────────────────────────────── */
|
||||
|
||||
const DEVICE_TYPE_KEYS = ['wled', 'adalight', 'ambiled', 'mqtt', 'ws', 'openrgb', 'dmx', 'mock'];
|
||||
const DEVICE_TYPE_KEYS = ['wled', 'adalight', 'ambiled', 'mqtt', 'ws', 'openrgb', 'dmx', 'espnow', 'hue', 'usbhid', 'spi', 'chroma', 'gamesense', 'mock'];
|
||||
|
||||
function _buildDeviceTypeItems() {
|
||||
return DEVICE_TYPE_KEYS.map(key => ({
|
||||
@@ -93,6 +93,76 @@ export function destroyDmxProtocolIconSelect(selectId) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Icon-grid SPI LED chipset selector ──────────────────────── */
|
||||
|
||||
function _buildSpiLedTypeItems() {
|
||||
return [
|
||||
{ value: 'WS2812B', icon: ICON_CPU, label: 'WS2812B', desc: t('device.spi.led_type.ws2812b.desc') },
|
||||
{ value: 'WS2812', icon: ICON_CPU, label: 'WS2812', desc: t('device.spi.led_type.ws2812.desc') },
|
||||
{ value: 'WS2811', icon: ICON_CPU, label: 'WS2811', desc: t('device.spi.led_type.ws2811.desc') },
|
||||
{ value: 'SK6812', icon: ICON_CPU, label: 'SK6812 (RGB)', desc: t('device.spi.led_type.sk6812.desc') },
|
||||
{ value: 'SK6812_RGBW', icon: ICON_CPU, label: 'SK6812 (RGBW)', desc: t('device.spi.led_type.sk6812_rgbw.desc') },
|
||||
];
|
||||
}
|
||||
|
||||
const _spiLedTypeIconSelects = {};
|
||||
|
||||
export function ensureSpiLedTypeIconSelect(selectId) {
|
||||
const sel = document.getElementById(selectId);
|
||||
if (!sel) return;
|
||||
if (_spiLedTypeIconSelects[selectId]) {
|
||||
_spiLedTypeIconSelects[selectId].updateItems(_buildSpiLedTypeItems());
|
||||
return;
|
||||
}
|
||||
_spiLedTypeIconSelects[selectId] = new IconSelect({
|
||||
target: sel,
|
||||
items: _buildSpiLedTypeItems(),
|
||||
columns: 3,
|
||||
});
|
||||
}
|
||||
|
||||
export function destroySpiLedTypeIconSelect(selectId) {
|
||||
if (_spiLedTypeIconSelects[selectId]) {
|
||||
_spiLedTypeIconSelects[selectId].destroy();
|
||||
delete _spiLedTypeIconSelects[selectId];
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Icon-grid GameSense peripheral type selector ────────────── */
|
||||
|
||||
function _buildGameSenseDeviceTypeItems() {
|
||||
return [
|
||||
{ value: 'keyboard', icon: ICON_KEYBOARD, label: t('device.gamesense.peripheral.keyboard'), desc: t('device.gamesense.peripheral.keyboard.desc') },
|
||||
{ value: 'mouse', icon: ICON_MOUSE, label: t('device.gamesense.peripheral.mouse'), desc: t('device.gamesense.peripheral.mouse.desc') },
|
||||
{ value: 'headset', icon: ICON_HEADPHONES, label: t('device.gamesense.peripheral.headset'), desc: t('device.gamesense.peripheral.headset.desc') },
|
||||
{ value: 'mousepad', icon: ICON_PLUG, label: t('device.gamesense.peripheral.mousepad'), desc: t('device.gamesense.peripheral.mousepad.desc') },
|
||||
{ value: 'indicator', icon: ICON_ACTIVITY, label: t('device.gamesense.peripheral.indicator'), desc: t('device.gamesense.peripheral.indicator.desc') },
|
||||
];
|
||||
}
|
||||
|
||||
const _gameSenseDeviceTypeIconSelects = {};
|
||||
|
||||
export function ensureGameSenseDeviceTypeIconSelect(selectId) {
|
||||
const sel = document.getElementById(selectId);
|
||||
if (!sel) return;
|
||||
if (_gameSenseDeviceTypeIconSelects[selectId]) {
|
||||
_gameSenseDeviceTypeIconSelects[selectId].updateItems(_buildGameSenseDeviceTypeItems());
|
||||
return;
|
||||
}
|
||||
_gameSenseDeviceTypeIconSelects[selectId] = new IconSelect({
|
||||
target: sel,
|
||||
items: _buildGameSenseDeviceTypeItems(),
|
||||
columns: 3,
|
||||
});
|
||||
}
|
||||
|
||||
export function destroyGameSenseDeviceTypeIconSelect(selectId) {
|
||||
if (_gameSenseDeviceTypeIconSelects[selectId]) {
|
||||
_gameSenseDeviceTypeIconSelects[selectId].destroy();
|
||||
delete _gameSenseDeviceTypeIconSelects[selectId];
|
||||
}
|
||||
}
|
||||
|
||||
export function onDeviceTypeChanged() {
|
||||
const deviceType = document.getElementById('device-type').value;
|
||||
if (_deviceTypeIconSelect) _deviceTypeIconSelect.setValue(deviceType);
|
||||
@@ -126,6 +196,13 @@ export function onDeviceTypeChanged() {
|
||||
if (dmxStartUniverseGroup) dmxStartUniverseGroup.style.display = 'none';
|
||||
if (dmxStartChannelGroup) dmxStartChannelGroup.style.display = 'none';
|
||||
|
||||
// Hide new device type fields by default
|
||||
_showEspnowFields(false);
|
||||
_showHueFields(false);
|
||||
_showSpiFields(false);
|
||||
_showChromaFields(false);
|
||||
_showGameSenseFields(false);
|
||||
|
||||
if (isMqttDevice(deviceType)) {
|
||||
// MQTT: show URL (topic), LED count; hide serial/baud/led-type/latency/discovery
|
||||
urlGroup.style.display = '';
|
||||
@@ -228,6 +305,126 @@ export function onDeviceTypeChanged() {
|
||||
} else {
|
||||
scanForDevices();
|
||||
}
|
||||
} else if (isEspnowDevice(deviceType)) {
|
||||
// ESP-NOW: serial port for gateway, LED count, baud rate, + ESP-NOW fields
|
||||
urlGroup.style.display = 'none';
|
||||
urlInput.removeAttribute('required');
|
||||
serialGroup.style.display = '';
|
||||
serialSelect.setAttribute('required', '');
|
||||
ledCountGroup.style.display = '';
|
||||
baudRateGroup.style.display = '';
|
||||
if (ledTypeGroup) ledTypeGroup.style.display = 'none';
|
||||
if (sendLatencyGroup) sendLatencyGroup.style.display = 'none';
|
||||
if (discoverySection) discoverySection.style.display = 'none';
|
||||
if (scanBtn) scanBtn.style.display = 'none';
|
||||
// Show ESP-NOW specific fields
|
||||
_showEspnowFields(true);
|
||||
// Populate serial ports
|
||||
if (deviceType in _discoveryCache) {
|
||||
_populateSerialPortDropdown(_discoveryCache[deviceType]);
|
||||
} else {
|
||||
serialSelect.innerHTML = '';
|
||||
const opt = document.createElement('option');
|
||||
opt.value = '';
|
||||
opt.textContent = t('device.serial_port.hint') || 'Click to discover ports...';
|
||||
opt.disabled = true;
|
||||
serialSelect.appendChild(opt);
|
||||
}
|
||||
} else if (isHueDevice(deviceType)) {
|
||||
// Hue: show URL (bridge IP), LED count, + Hue auth fields
|
||||
urlGroup.style.display = '';
|
||||
urlInput.setAttribute('required', '');
|
||||
serialGroup.style.display = 'none';
|
||||
serialSelect.removeAttribute('required');
|
||||
ledCountGroup.style.display = '';
|
||||
baudRateGroup.style.display = 'none';
|
||||
if (ledTypeGroup) ledTypeGroup.style.display = 'none';
|
||||
if (sendLatencyGroup) sendLatencyGroup.style.display = 'none';
|
||||
if (scanBtn) scanBtn.style.display = '';
|
||||
_showHueFields(true);
|
||||
if (urlLabel) urlLabel.textContent = t('device.hue.url') || 'Bridge IP';
|
||||
if (urlHint) urlHint.textContent = t('device.hue.url.hint') || 'IP address of your Hue bridge';
|
||||
urlInput.placeholder = 'hue://192.168.1.2';
|
||||
if (deviceType in _discoveryCache) {
|
||||
_renderDiscoveryList();
|
||||
} else {
|
||||
scanForDevices();
|
||||
}
|
||||
} else if (isUsbhidDevice(deviceType)) {
|
||||
// USB HID: show URL (VID:PID), LED count
|
||||
urlGroup.style.display = '';
|
||||
urlInput.setAttribute('required', '');
|
||||
serialGroup.style.display = 'none';
|
||||
serialSelect.removeAttribute('required');
|
||||
ledCountGroup.style.display = '';
|
||||
baudRateGroup.style.display = 'none';
|
||||
if (ledTypeGroup) ledTypeGroup.style.display = 'none';
|
||||
if (sendLatencyGroup) sendLatencyGroup.style.display = 'none';
|
||||
if (scanBtn) scanBtn.style.display = '';
|
||||
if (urlLabel) urlLabel.textContent = t('device.usbhid.url') || 'VID:PID';
|
||||
if (urlHint) urlHint.textContent = t('device.usbhid.url.hint') || 'USB Vendor:Product ID in hex';
|
||||
urlInput.placeholder = 'hid://1532:0084';
|
||||
if (deviceType in _discoveryCache) {
|
||||
_renderDiscoveryList();
|
||||
} else {
|
||||
scanForDevices();
|
||||
}
|
||||
} else if (isSpiDevice(deviceType)) {
|
||||
// SPI Direct: show URL (gpio/spidev), LED count, + SPI fields
|
||||
urlGroup.style.display = '';
|
||||
urlInput.setAttribute('required', '');
|
||||
serialGroup.style.display = 'none';
|
||||
serialSelect.removeAttribute('required');
|
||||
ledCountGroup.style.display = '';
|
||||
baudRateGroup.style.display = 'none';
|
||||
if (ledTypeGroup) ledTypeGroup.style.display = 'none';
|
||||
if (sendLatencyGroup) sendLatencyGroup.style.display = 'none';
|
||||
if (scanBtn) scanBtn.style.display = '';
|
||||
_showSpiFields(true);
|
||||
ensureSpiLedTypeIconSelect('device-spi-led-type');
|
||||
if (urlLabel) urlLabel.textContent = t('device.spi.url') || 'GPIO/SPI Path';
|
||||
if (urlHint) urlHint.textContent = t('device.spi.url.hint') || 'GPIO pin or SPI device path';
|
||||
urlInput.placeholder = 'spi://gpio:18';
|
||||
if (deviceType in _discoveryCache) {
|
||||
_renderDiscoveryList();
|
||||
} else {
|
||||
scanForDevices();
|
||||
}
|
||||
} else if (isChromaDevice(deviceType)) {
|
||||
// Razer Chroma: auto URL, LED count, + peripheral type selector
|
||||
urlGroup.style.display = 'none';
|
||||
urlInput.removeAttribute('required');
|
||||
serialGroup.style.display = 'none';
|
||||
serialSelect.removeAttribute('required');
|
||||
ledCountGroup.style.display = '';
|
||||
baudRateGroup.style.display = 'none';
|
||||
if (ledTypeGroup) ledTypeGroup.style.display = 'none';
|
||||
if (sendLatencyGroup) sendLatencyGroup.style.display = 'none';
|
||||
if (scanBtn) scanBtn.style.display = '';
|
||||
_showChromaFields(true);
|
||||
if (deviceType in _discoveryCache) {
|
||||
_renderDiscoveryList();
|
||||
} else {
|
||||
scanForDevices();
|
||||
}
|
||||
} else if (isGameSenseDevice(deviceType)) {
|
||||
// SteelSeries GameSense: auto URL, LED count, + device type selector
|
||||
urlGroup.style.display = 'none';
|
||||
urlInput.removeAttribute('required');
|
||||
serialGroup.style.display = 'none';
|
||||
serialSelect.removeAttribute('required');
|
||||
ledCountGroup.style.display = '';
|
||||
baudRateGroup.style.display = 'none';
|
||||
if (ledTypeGroup) ledTypeGroup.style.display = 'none';
|
||||
if (sendLatencyGroup) sendLatencyGroup.style.display = 'none';
|
||||
if (scanBtn) scanBtn.style.display = '';
|
||||
_showGameSenseFields(true);
|
||||
ensureGameSenseDeviceTypeIconSelect('device-gamesense-device-type');
|
||||
if (deviceType in _discoveryCache) {
|
||||
_renderDiscoveryList();
|
||||
} else {
|
||||
scanForDevices();
|
||||
}
|
||||
} else {
|
||||
urlGroup.style.display = '';
|
||||
urlInput.setAttribute('required', '');
|
||||
@@ -476,8 +673,13 @@ export async function handleAddDevice(event) {
|
||||
url = 'mock://';
|
||||
} else if (isWsDevice(deviceType)) {
|
||||
url = 'ws://';
|
||||
} else if (isSerialDevice(deviceType)) {
|
||||
} else if (isSerialDevice(deviceType) || isEspnowDevice(deviceType)) {
|
||||
url = document.getElementById('device-serial-port').value;
|
||||
} else if (isChromaDevice(deviceType)) {
|
||||
const chromaType = document.getElementById('device-chroma-device-type')?.value || 'chromalink';
|
||||
url = `chroma://${chromaType}`;
|
||||
} else if (isGameSenseDevice(deviceType)) {
|
||||
url = 'gamesense://auto';
|
||||
} else {
|
||||
url = document.getElementById('device-url').value.trim();
|
||||
}
|
||||
@@ -525,6 +727,26 @@ export async function handleAddDevice(event) {
|
||||
body.dmx_start_universe = parseInt(document.getElementById('device-dmx-start-universe')?.value || '0', 10);
|
||||
body.dmx_start_channel = parseInt(document.getElementById('device-dmx-start-channel')?.value || '1', 10);
|
||||
}
|
||||
if (isEspnowDevice(deviceType)) {
|
||||
body.espnow_peer_mac = document.getElementById('device-espnow-peer-mac')?.value || '';
|
||||
body.espnow_channel = parseInt(document.getElementById('device-espnow-channel')?.value || '1', 10);
|
||||
body.baud_rate = parseInt(document.getElementById('device-baud-rate')?.value || '921600', 10);
|
||||
}
|
||||
if (isHueDevice(deviceType)) {
|
||||
body.hue_username = document.getElementById('device-hue-username')?.value || '';
|
||||
body.hue_client_key = document.getElementById('device-hue-client-key')?.value || '';
|
||||
body.hue_entertainment_group_id = document.getElementById('device-hue-group-id')?.value || '';
|
||||
}
|
||||
if (isSpiDevice(deviceType)) {
|
||||
body.spi_speed_hz = parseInt(document.getElementById('device-spi-speed')?.value || '800000', 10);
|
||||
body.spi_led_type = document.getElementById('device-spi-led-type')?.value || 'WS2812B';
|
||||
}
|
||||
if (isChromaDevice(deviceType)) {
|
||||
body.chroma_device_type = document.getElementById('device-chroma-device-type')?.value || 'chromalink';
|
||||
}
|
||||
if (isGameSenseDevice(deviceType)) {
|
||||
body.gamesense_device_type = document.getElementById('device-gamesense-device-type')?.value || 'keyboard';
|
||||
}
|
||||
if (lastTemplateId) body.capture_template_id = lastTemplateId;
|
||||
|
||||
const response = await fetchWithAuth('/devices', {
|
||||
@@ -666,3 +888,39 @@ export function _getZoneMode(radioName = 'device-zone-mode') {
|
||||
const radio = document.querySelector(`input[name="${radioName}"]:checked`);
|
||||
return radio ? radio.value : 'combined';
|
||||
}
|
||||
|
||||
/* ── New device type field visibility helpers ──────────────────── */
|
||||
|
||||
function _showEspnowFields(show) {
|
||||
const ids = ['device-espnow-peer-mac-group', 'device-espnow-channel-group'];
|
||||
ids.forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.style.display = show ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function _showHueFields(show) {
|
||||
const ids = ['device-hue-username-group', 'device-hue-client-key-group', 'device-hue-group-id-group'];
|
||||
ids.forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.style.display = show ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function _showSpiFields(show) {
|
||||
const ids = ['device-spi-speed-group', 'device-spi-led-type-group'];
|
||||
ids.forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.style.display = show ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function _showChromaFields(show) {
|
||||
const el = document.getElementById('device-chroma-device-type-group');
|
||||
if (el) el.style.display = show ? '' : 'none';
|
||||
}
|
||||
|
||||
function _showGameSenseFields(show) {
|
||||
const el = document.getElementById('device-gamesense-device-type-group');
|
||||
if (el) el.style.display = show ? '' : 'none';
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '../core/state.js';
|
||||
import { API_BASE, getHeaders, fetchWithAuth, escapeHtml, isSerialDevice, isMockDevice, isMqttDevice, isWsDevice, isOpenrgbDevice, isDmxDevice } from '../core/api.js';
|
||||
import { devicesCache } from '../core/state.js';
|
||||
import { _fetchOpenrgbZones, _getCheckedZones, _splitOpenrgbZone, _getZoneMode, ensureDmxProtocolIconSelect, destroyDmxProtocolIconSelect } from './device-discovery.js';
|
||||
import { _fetchOpenrgbZones, _getCheckedZones, _splitOpenrgbZone, _getZoneMode, ensureDmxProtocolIconSelect, destroyDmxProtocolIconSelect, ensureSpiLedTypeIconSelect, destroySpiLedTypeIconSelect, ensureGameSenseDeviceTypeIconSelect, destroyGameSenseDeviceTypeIconSelect } from './device-discovery.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
import { showToast, showConfirm, desktopFocus } from '../core/ui.js';
|
||||
import { Modal } from '../core/modal.js';
|
||||
|
||||
Reference in New Issue
Block a user