/** * Device discovery — add device modal, network/serial scanning, device type switching. */ import { _discoveryScanRunning, set_discoveryScanRunning, _discoveryCache, set_discoveryCache, csptCache, } from '../core/state.ts'; import { API_BASE, fetchWithAuth, isSerialDevice, isMockDevice, isMqttDevice, isWsDevice, isOpenrgbDevice, isDmxDevice, isEspnowDevice, isHueDevice, isUsbhidDevice, isSpiDevice, isChromaDevice, isGameSenseDevice, escapeHtml } from '../core/api.ts'; import { devicesCache } from '../core/state.ts'; import { t } from '../core/i18n.ts'; import { showToast, desktopFocus } from '../core/ui.ts'; import { Modal } from '../core/modal.ts'; import { _computeMaxFps, _renderFpsHint } from './devices.ts'; import { getDeviceTypeIcon, ICON_RADIO, ICON_GLOBE, ICON_CPU, ICON_KEYBOARD, ICON_MOUSE, ICON_HEADPHONES, ICON_PLUG, ICON_TARGET_ICON, ICON_ACTIVITY, ICON_TEMPLATE } from '../core/icons.ts'; import { EntitySelect } from '../core/entity-palette.ts'; import { IconSelect, showTypePicker } from '../core/icon-select.ts'; class AddDeviceModal extends Modal { constructor() { super('add-device-modal'); } snapshotValues() { return { name: (document.getElementById('device-name') as HTMLInputElement).value, type: (document.getElementById('device-type') as HTMLSelectElement).value, url: (document.getElementById('device-url') as HTMLInputElement).value, serialPort: (document.getElementById('device-serial-port') as HTMLSelectElement).value, ledCount: (document.getElementById('device-led-count') as HTMLInputElement).value, baudRate: (document.getElementById('device-baud-rate') as HTMLSelectElement).value, ledType: (document.getElementById('device-led-type') as HTMLSelectElement)?.value || 'rgb', sendLatency: (document.getElementById('device-send-latency') as HTMLInputElement)?.value || '0', zones: JSON.stringify(_getCheckedZones('device-zone-list')), zoneMode: _getZoneMode(), csptId: (document.getElementById('device-css-processing-template') as HTMLSelectElement)?.value || '', dmxProtocol: (document.getElementById('device-dmx-protocol') as HTMLSelectElement)?.value || 'artnet', dmxStartUniverse: (document.getElementById('device-dmx-start-universe') as HTMLInputElement)?.value || '0', dmxStartChannel: (document.getElementById('device-dmx-start-channel') as HTMLInputElement)?.value || '1', }; } } const addDeviceModal = new AddDeviceModal(); /* ── Icon-grid type selector ──────────────────────────────────── */ 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 => ({ value: key, icon: getDeviceTypeIcon(key), label: t(`device.type.${key}`), desc: t(`device.type.${key}.desc`), })); } let _deviceTypeIconSelect: any = null; let _csptEntitySelect: any = null; function _ensureDeviceTypeIconSelect() { const sel = document.getElementById('device-type'); if (!sel) return; if (_deviceTypeIconSelect) { _deviceTypeIconSelect.updateItems(_buildDeviceTypeItems()); return; } _deviceTypeIconSelect = new IconSelect({ target: sel, items: _buildDeviceTypeItems(), columns: 3 } as any); } function _ensureCsptEntitySelect() { const sel = document.getElementById('device-css-processing-template'); if (!sel) return; const templates = csptCache.data || []; // Populate native