Add manual camera refresh control
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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&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()
|
||||
|
||||
Reference in New Issue
Block a user