diff --git a/src/static/devices.js b/src/static/devices.js index 6a97acb..5f7541b 100644 --- a/src/static/devices.js +++ b/src/static/devices.js @@ -76,6 +76,13 @@ function normalizeDeviceMacKey(mac) { .replace(/[:-]/g, ''); } +function normalizeMacInput(raw) { + return String(raw || '') + .trim() + .toLowerCase() + .replace(/[:-]/g, ''); +} + function findPingResponse(responses, deviceId) { if (!responses || typeof responses !== 'object') return null; const want = normalizeDeviceMacKey(deviceId); @@ -430,6 +437,69 @@ async function loadDevicesModal() { } } +async function createDeviceFromModal() { + const nameEl = document.getElementById('devices-add-name'); + const trEl = document.getElementById('devices-add-transport'); + const macEl = document.getElementById('devices-add-mac'); + const addrEl = document.getElementById('devices-add-address'); + const statusEl = document.getElementById('devices-add-status'); + const btn = document.getElementById('devices-add-btn'); + const name = (nameEl && nameEl.value.trim()) || ''; + const transport = (trEl && trEl.value) || 'espnow'; + const mac = normalizeMacInput(macEl && macEl.value); + const address = (addrEl && addrEl.value.trim()) || ''; + + if (!name) { + if (statusEl) statusEl.textContent = 'Name is required'; + return; + } + if (mac.length !== 12) { + if (statusEl) statusEl.textContent = 'MAC must be 12 hex characters'; + return; + } + if (transport === 'wifi' && !address) { + if (statusEl) statusEl.textContent = 'Address is required for Wi-Fi devices'; + return; + } + + if (btn) { + btn.disabled = true; + btn.textContent = 'Adding…'; + } + if (statusEl) statusEl.textContent = 'Creating device…'; + try { + const payload = { + name, + transport, + type: 'led', + mac, + address: transport === 'wifi' ? address : mac, + }; + const res = await fetch('/devices', { + method: 'POST', + headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, + body: JSON.stringify(payload), + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) { + if (statusEl) statusEl.textContent = data.error || 'Create failed'; + return; + } + if (statusEl) statusEl.textContent = 'Device added'; + if (nameEl) nameEl.value = ''; + if (macEl) macEl.value = ''; + if (addrEl) addrEl.value = ''; + await loadDevicesModal(); + } catch (e) { + if (statusEl) statusEl.textContent = e.message || 'Create failed'; + } finally { + if (btn) { + btn.disabled = false; + btn.textContent = 'Add device'; + } + } +} + function renderDevicesList(devices) { const container = document.getElementById('devices-list-modal'); if (!container) return; @@ -750,6 +820,9 @@ document.addEventListener('DOMContentLoaded', () => { const editForm = document.getElementById('edit-device-form'); const editCloseBtn = document.getElementById('edit-device-close-btn'); const editDeviceModal = document.getElementById('edit-device-modal'); + const addTransport = document.getElementById('devices-add-transport'); + const addAddress = document.getElementById('devices-add-address'); + const addBtn = document.getElementById('devices-add-btn'); if (devicesBtn && devicesModal) { devicesBtn.addEventListener('click', () => { @@ -768,6 +841,17 @@ document.addEventListener('DOMContentLoaded', () => { }); } + if (addTransport && addAddress) { + const syncAddAddress = () => { + addAddress.hidden = addTransport.value !== 'wifi'; + }; + addTransport.addEventListener('change', syncAddAddress); + syncAddAddress(); + } + if (addBtn) { + addBtn.addEventListener('click', () => createDeviceFromModal()); + } + const devicesPingBtn = document.getElementById('devices-ping-btn'); if (devicesPingBtn) { devicesPingBtn.addEventListener('click', () => { diff --git a/src/templates/index.html b/src/templates/index.html index a805308..b709317 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -166,6 +166,19 @@