Add manual camera refresh control
This commit is contained in:
@@ -1660,6 +1660,19 @@ class CameraControlBridge:
|
|||||||
self.send_response(401)
|
self.send_response(401)
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
return
|
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("/")
|
parts = parsed.path.strip("/").split("/")
|
||||||
if len(parts) == 3 and parts[0] == "action":
|
if len(parts) == 3 and parts[0] == "action":
|
||||||
action_id = urllib.parse.unquote(parts[1])
|
action_id = urllib.parse.unquote(parts[1])
|
||||||
|
|||||||
@@ -76,6 +76,43 @@ h1 {
|
|||||||
justify-content: flex-end;
|
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 {
|
.language-form {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -517,6 +554,10 @@ h3 {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.refresh-form {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.grid-single,
|
.grid-single,
|
||||||
.control-grid {
|
.control-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@@ -15,6 +15,16 @@
|
|||||||
</div>
|
</div>
|
||||||
{global_actions}
|
{global_actions}
|
||||||
<div class="toolbar">
|
<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">
|
<form method="get" action="/" class="language-form">
|
||||||
<label for="lang">{language_label}</label>
|
<label for="lang">{language_label}</label>
|
||||||
{token_input}
|
{token_input}
|
||||||
|
|||||||
@@ -306,8 +306,10 @@ def render_page(actions, lang, token_param=None):
|
|||||||
action_count=len(groups),
|
action_count=len(groups),
|
||||||
actions_label=html.escape(text_for(lang, "cameras")),
|
actions_label=html.escape(text_for(lang, "cameras")),
|
||||||
language_label=html.escape(text_for(lang, "language")),
|
language_label=html.escape(text_for(lang, "language")),
|
||||||
|
refresh_label=html.escape(text_for(lang, "button.refresh_cameras")),
|
||||||
language_options=render_language_options(lang),
|
language_options=render_language_options(lang),
|
||||||
token_input=token_input,
|
token_input=token_input,
|
||||||
|
token_query=html.escape(token_query),
|
||||||
global_actions=global_markup,
|
global_actions=global_markup,
|
||||||
grid_class=html.escape(grid_class),
|
grid_class=html.escape(grid_class),
|
||||||
cards=cards_markup,
|
cards=cards_markup,
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ msgstr "OFF"
|
|||||||
msgid "button.press"
|
msgid "button.press"
|
||||||
msgstr "PRESS"
|
msgstr "PRESS"
|
||||||
|
|
||||||
|
msgid "button.refresh_cameras"
|
||||||
|
msgstr "Refresh cameras"
|
||||||
|
|
||||||
msgid "action.all_off"
|
msgid "action.all_off"
|
||||||
msgstr "{target} all off"
|
msgstr "{target} all off"
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ msgstr "WYLACZ"
|
|||||||
msgid "button.press"
|
msgid "button.press"
|
||||||
msgstr "URUCHOM"
|
msgstr "URUCHOM"
|
||||||
|
|
||||||
|
msgid "button.refresh_cameras"
|
||||||
|
msgstr "Odswiez kamery"
|
||||||
|
|
||||||
msgid "action.all_off"
|
msgid "action.all_off"
|
||||||
msgstr "{target} wylacz wszystko"
|
msgstr "{target} wylacz wszystko"
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,15 @@ class UiRenderTest(unittest.TestCase):
|
|||||||
self.assertIn('class="global-action"', html)
|
self.assertIn('class="global-action"', html)
|
||||||
self.assertNotIn('class="camera-card"', 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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user