diff --git a/avian/api/birdnet-api.php b/avian/api/birdnet-api.php index b33c379..575fada 100644 --- a/avian/api/birdnet-api.php +++ b/avian/api/birdnet-api.php @@ -216,6 +216,7 @@ switch ($action) { 'quiet_end' => 6, 'heal_hours' => 24, 'timeout' => 45, + 'frame_host' => '', 'basic_user' => '', 'basic_pass' => '', ]; diff --git a/avian/frontend/apt.js b/avian/frontend/apt.js index 5b18827..5c87f61 100644 --- a/avian/frontend/apt.js +++ b/avian/frontend/apt.js @@ -2435,6 +2435,9 @@ .then(function (fc) { adminBody.innerHTML = '
' + + '' + '
Display
' + frameSegmented('mode', 'Mode', 'how the time window works', fc.mode, [ { v: 'today', label: 'today' }, @@ -2465,6 +2468,7 @@ + frameSlider('mat_opening_w', 'Mat width', 'matte opening width (inches, 0=default)', fc.mat_opening_w, 0, 12, 0.5, 1) + frameSlider('mat_opening_h', 'Mat height', 'matte opening height (inches, 0=default)', fc.mat_opening_h, 0, 14, 0.5, 1) + frameSlider('mat', 'Extra mat', 'global content shrink inside opening', fc.mat, 0, 0.1, 0.01, 2) + + frameInput('frame_host', 'Frame address', 'IP or hostname of the frame Pi', fc.frame_host || '') + frameInput('panel', 'Panel driver', 'force driver (blank=auto)', fc.panel || '') + frameSlider('timeout', 'Timeout', 'seconds to wait for page render', fc.timeout, 10, 120, 5, 0) + '
Source
' @@ -2481,7 +2485,7 @@ + frameInput('basic_user', 'Username', 'basic auth username', fc.basic_user || '') + frameInput('basic_pass', 'Password', 'basic auth password', fc.basic_pass || '', true) + '
'; - wireFrameControls(); + wireFrameControls(fc); }) .catch(function (err) { adminBody.innerHTML = @@ -2549,8 +2553,19 @@ }, 500); } - function wireFrameControls() { + function wireFrameControls(fc) { var pending = {}; + var refreshBtn = document.getElementById('frameRefreshBtn'); + if (refreshBtn) { + refreshBtn.addEventListener('click', function () { + refreshBtn.textContent = 'Refreshing...'; + refreshBtn.disabled = true; + fetch('http://' + (fc.frame_host || 'birdpic.local') + ':8080/refresh', { method: 'POST', mode: 'cors' }) + .then(function () { refreshBtn.textContent = 'Triggered'; }) + .catch(function () { refreshBtn.textContent = 'Failed (check LAN)'; }) + .finally(function () { setTimeout(function () { refreshBtn.textContent = 'Refresh Now'; refreshBtn.disabled = false; }, 3000); }); + }); + } adminBody.querySelectorAll('.switch[data-frame-key]').forEach(function (sw) { sw.addEventListener('click', function () { var on = sw.getAttribute('aria-checked') !== 'true'; diff --git a/avian/frontend/styles.css b/avian/frontend/styles.css index ceed7bd..04c4643 100644 --- a/avian/frontend/styles.css +++ b/avian/frontend/styles.css @@ -1634,3 +1634,17 @@ body.av-local #dd-items { display: block; } } .frame-section-label:first-child { padding-top: 4px; } + + .frame-refresh-btn { + font: 600 13px/1 var(--sans, ui-sans-serif, system-ui, sans-serif); + padding: 10px 24px; + border: 1px solid var(--ink-soft, #8a8078); + border-radius: 6px; + background: transparent; + color: var(--ink, #1a1612); + cursor: pointer; + transition: background 0.15s, opacity 0.15s; + } + .frame-refresh-btn:hover { background: rgba(26,22,18,0.05); } + .frame-refresh-btn:disabled { opacity: 0.5; cursor: default; } +