Add manual camera refresh control

This commit is contained in:
2026-08-03 01:14:49 +02:00
parent 1290122e57
commit e403f4e6b4
7 changed files with 81 additions and 0 deletions
+13
View File
@@ -1660,6 +1660,19 @@ class CameraControlBridge:
self.send_response(401)
self.end_headers()
return
if parsed.path == "/refresh":
try:
bridge.refresh_frigate_inventory()
token_param = urllib.parse.parse_qs(parsed.query).get("token", [None])[0]
token_query = f"&token={urllib.parse.quote(token_param)}" if token_param else ""
self.send_response(303)
self.send_header("Location", f"/?lang={urllib.parse.quote(lang)}{token_query}")
self.end_headers()
except Exception as exc:
self.send_response(500)
self.end_headers()
self.wfile.write(str(exc).encode("utf-8"))
return
parts = parsed.path.strip("/").split("/")
if len(parts) == 3 and parts[0] == "action":
action_id = urllib.parse.unquote(parts[1])
@@ -76,6 +76,43 @@ h1 {
justify-content: flex-end;
}
.refresh-form {
margin: 0;
}
.refresh-button {
display: grid;
width: 38px;
height: 38px;
place-items: center;
border: 1px solid var(--line-strong);
border-radius: 6px;
padding: 0;
background: #ffffff;
color: #1f3a5f;
cursor: pointer;
}
.refresh-button:hover {
border-color: var(--blue);
color: var(--blue-dark);
}
.refresh-button:focus-visible {
outline: 3px solid rgba(29, 78, 216, 0.25);
outline-offset: 2px;
}
.refresh-button svg {
width: 20px;
height: 20px;
fill: none;
stroke: currentColor;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2;
}
.language-form {
display: inline-flex;
align-items: center;
@@ -517,6 +554,10 @@ h3 {
width: 100%;
}
.refresh-form {
margin-left: auto;
}
.grid-single,
.control-grid {
grid-template-columns: 1fr;
@@ -15,6 +15,16 @@
</div>
{global_actions}
<div class="toolbar">
<form method="post" action="/refresh?lang={lang}{token_query}" class="refresh-form">
<button class="refresh-button" type="submit" title="{refresh_label}" aria-label="{refresh_label}">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M20 6v5h-5"></path>
<path d="M4 18v-5h5"></path>
<path d="M18.6 9A7 7 0 0 0 6.3 6.4L4 8.6"></path>
<path d="M5.4 15A7 7 0 0 0 17.7 17.6L20 15.4"></path>
</svg>
</button>
</form>
<form method="get" action="/" class="language-form">
<label for="lang">{language_label}</label>
{token_input}
+2
View File
@@ -306,8 +306,10 @@ def render_page(actions, lang, token_param=None):
action_count=len(groups),
actions_label=html.escape(text_for(lang, "cameras")),
language_label=html.escape(text_for(lang, "language")),
refresh_label=html.escape(text_for(lang, "button.refresh_cameras")),
language_options=render_language_options(lang),
token_input=token_input,
token_query=html.escape(token_query),
global_actions=global_markup,
grid_class=html.escape(grid_class),
cards=cards_markup,
@@ -92,6 +92,9 @@ msgstr "OFF"
msgid "button.press"
msgstr "PRESS"
msgid "button.refresh_cameras"
msgstr "Refresh cameras"
msgid "action.all_off"
msgstr "{target} all off"
@@ -92,6 +92,9 @@ msgstr "WYLACZ"
msgid "button.press"
msgstr "URUCHOM"
msgid "button.refresh_cameras"
msgstr "Odswiez kamery"
msgid "action.all_off"
msgstr "{target} wylacz wszystko"
+9
View File
@@ -62,6 +62,15 @@ class UiRenderTest(unittest.TestCase):
self.assertIn('class="global-action"', html)
self.assertNotIn('class="camera-card"', html)
def test_refresh_button_preserves_language_and_token(self):
html = render_page([DummyAction()], "en", token_param="secret")
self.assertIn('class="refresh-form"', html)
self.assertIn('action="/refresh?lang=en&amp;token=secret"', html)
self.assertIn('title="Refresh cameras"', html)
self.assertIn('aria-label="Refresh cameras"', html)
self.assertIn("<svg", html)
if __name__ == "__main__":
unittest.main()