manual add button
Build Images and Deploy / Update-PROD-Stack (push) Successful in 18s

This commit is contained in:
2026-03-30 22:10:30 -04:00
parent 0977a610ff
commit 70c98af759
2 changed files with 89 additions and 0 deletions
+85
View File
@@ -197,6 +197,7 @@
<div class="toolbar"> <div class="toolbar">
<h2>Devices</h2> <h2>Devices</h2>
<button class="btn btn-primary" id="btn-discover" onclick="discoverDevices()">⟳ Scan</button> <button class="btn btn-primary" id="btn-discover" onclick="discoverDevices()">⟳ Scan</button>
<button class="btn btn-secondary btn-sm" id="btn-manual-add" onclick="openManualModal()" title="Add device manually" style="width:auto;padding:9px 12px;"></button>
</div> </div>
<div id="page-header-devices" class="page-header"> <div id="page-header-devices" class="page-header">
<span class="ws-dot" id="ws-dot-d"></span> <span class="ws-dot" id="ws-dot-d"></span>
@@ -436,6 +437,29 @@
</div> </div>
</div> </div>
<!-- ── Manual Add Modal ── -->
<div class="modal-backdrop" id="modal-manual" onclick="closeManualModal(event)">
<div class="modal" onclick="event.stopPropagation()">
<div class="modal-header">
<span class="modal-title">Add Device Manually</span>
<button class="modal-close" onclick="closeManualModal()"></button>
</div>
<div class="form-group">
<label class="form-label">IP Address</label>
<input class="form-input" id="f-manual-host" type="text" placeholder="192.168.1.100" autofocus />
</div>
<div class="form-group">
<label class="form-label">Port</label>
<input class="form-input" id="f-manual-port" type="number" placeholder="49153" value="49153" />
</div>
<div id="modal-manual-error" style="display:none;color:var(--danger);font-size:13px;margin-bottom:10px;"></div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeManualModal()">Cancel</button>
<button class="btn btn-primary" id="btn-add-manual" onclick="addManualDevice()">Add</button>
</div>
</div>
</div>
<!-- ── Delete Confirm ── --> <!-- ── Delete Confirm ── -->
<div class="confirm-backdrop" id="confirm-delete"> <div class="confirm-backdrop" id="confirm-delete">
<div class="confirm-box"> <div class="confirm-box">
@@ -603,6 +627,67 @@ async function toggleDevice(i, e) {
} }
} }
// ── Manual Add ─────────────────────────────────────────────────────────────
function openManualModal() {
document.getElementById('f-manual-host').value = '';
document.getElementById('f-manual-port').value = '49153';
document.getElementById('modal-manual-error').style.display = 'none';
document.getElementById('modal-manual').classList.add('open');
setTimeout(() => document.getElementById('f-manual-host').focus(), 100);
}
function closeManualModal(e) {
if (e && e.target !== document.getElementById('modal-manual')) return;
document.getElementById('modal-manual').classList.remove('open');
}
async function addManualDevice() {
const errEl = document.getElementById('modal-manual-error');
errEl.style.display = 'none';
const host = document.getElementById('f-manual-host').value.trim();
const port = parseInt(document.getElementById('f-manual-port').value, 10) || 49153;
if (!host) {
errEl.textContent = '⚠ Enter an IP address';
errEl.style.display = 'block';
return;
}
const btn = document.getElementById('btn-add-manual');
btn.disabled = true; btn.textContent = 'Adding…';
try {
const result = await api('POST', '/api/devices/discover', {
manualEntries: [{ host, port }]
});
if (!result || result.length === 0) {
errEl.textContent = '⚠ No device found at that address';
errEl.style.display = 'block';
btn.disabled = false; btn.textContent = 'Add';
return;
}
const device = result.find((d) => d.host === host);
if (device) {
const friendlyName = device.friendlyName || device.name || host;
toast(`Added ${friendlyName}`, 'success');
devices = result;
renderDevices();
closeManualModal();
} else {
errEl.textContent = '⚠ Device added but not found in results';
errEl.style.display = 'block';
}
} catch (err) {
errEl.textContent = `${err.message}`;
errEl.style.display = 'block';
} finally {
btn.disabled = false; btn.textContent = 'Add';
}
}
// ── Rules list ───────────────────────────────────────────────────────────── // ── Rules list ─────────────────────────────────────────────────────────────
async function loadRules() { async function loadRules() {
try { try {
+4
View File
@@ -94,6 +94,10 @@ async function handleRequest(req, res) {
if (url === '/api/devices/discover' && method === 'POST') { if (url === '/api/devices/discover' && method === 'POST') {
const saved = store.getDevices(); const saved = store.getDevices();
const manual = saved.map((d) => ({ host: d.host, port: d.port })); const manual = saved.map((d) => ({ host: d.host, port: d.port }));
// Add any manual entries from the request body
if (body.manualEntries && Array.isArray(body.manualEntries)) {
manual.push(...body.manualEntries);
}
const devs = await wemo.discoverDevices(8000, manual); const devs = await wemo.discoverDevices(8000, manual);
store.saveDevices(devs); store.saveDevices(devs);
return json(res, devs); return json(res, devs);