Move button actions to per-widget config, add hold-for-global-action
Next/back button assignment moves from a frame-level "Button assignments" card into each widget's own gear-icon dialog, prefilled with a sane default at creation (photos/calendar -> advance/back, whiteboard/weather -> check_now, others -> none). At most one binding per (widget, button) now -- cross-widget execution order never mattered since each widget's action only touches its own state. New firmware capability: holding NEXT or BACK past a configurable duration (min 3s, server-side default) triggers a frame-wide action instead of the per-widget short-press one -- cycling saved layouts, refreshing all widgets, or freezing/unfreezing every photo widget (see app/global_actions.py). Firmware next/back checks gain the same hold-duration polling the combo button already had; the threshold comes from the previous wake's /frame/config fetch (persisted in NVS), since this wake's button decision happens before that request. Not done here: firmware/version.txt is intentionally left unbumped -- this hasn't been built or hardware-tested (no ESP-IDF toolchain in this environment), so no firmware release build should be triggered yet.
This commit is contained in:
@@ -45,6 +45,7 @@ from ..image_upload import decode_upload
|
||||
from ..models import (
|
||||
CalendarWidgetConfig,
|
||||
Frame,
|
||||
FrameButtonAction,
|
||||
FrameCalendar,
|
||||
FrameTaskList,
|
||||
PhotoWidgetConfig,
|
||||
@@ -57,7 +58,7 @@ from ..models import (
|
||||
Widget,
|
||||
)
|
||||
from ..text_content import has_text, parse_rich_text
|
||||
from ..widgets import WIDGET_TYPES
|
||||
from ..widgets import WIDGET_TYPES, default_button_actions
|
||||
from ..widgets import battery as battery_widget
|
||||
from ..widgets import text as text_widget
|
||||
from .common import (
|
||||
@@ -214,6 +215,7 @@ def api_widget_create(
|
||||
db.add(widget)
|
||||
db.flush()
|
||||
db.add(WIDGET_CONFIG_MODELS[body.widget_type](widget_id=widget.id))
|
||||
db.add_all(default_button_actions(frame.id, widget.id, body.widget_type))
|
||||
db.commit()
|
||||
return _widget_dict(widget)
|
||||
|
||||
@@ -476,6 +478,54 @@ def api_widget_config_save(
|
||||
return {"status": "saved"}
|
||||
|
||||
|
||||
class WidgetButtonActionsRequest(BaseModel):
|
||||
next_button_action: str
|
||||
back_button_action: str
|
||||
|
||||
|
||||
@router.post("/api/frames/{frame_id}/widgets/{widget_id}/button-actions")
|
||||
def api_widget_button_actions(
|
||||
body: WidgetButtonActionsRequest, frame_widget: tuple[Frame, Widget] = Depends(require_widget_control),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Sets this widget's NEXT/BACK button bindings (models.FrameButtonAction)
|
||||
-- its own endpoint, not folded into api_widget_config_save, same
|
||||
reasoning as api_widget_border above: these rows live in their own
|
||||
table, not this widget's per-type config table. Replaces the old
|
||||
frame-level "Button assignments" card (routers/api_frames.py's
|
||||
api_buttons_get/api_buttons_save, now removed) -- each widget's own
|
||||
dialog edits its own binding directly, prefilled at widget-creation
|
||||
time with a sane default (see widgets.default_button_actions).
|
||||
|
||||
An empty string clears the binding for that button. Unlike
|
||||
api_widget_config_save's silent-ignore-unrecognized-value posture,
|
||||
a value outside this widget type's own ACTIONS is a 400 -- this
|
||||
request body is specifically about button actions, so a bad value
|
||||
here is a real client bug worth surfacing, not a stray field to
|
||||
shrug off."""
|
||||
frame, widget = frame_widget
|
||||
valid_actions = set(WIDGET_TYPES[widget.widget_type].ACTIONS)
|
||||
for value in (body.next_button_action, body.back_button_action):
|
||||
if value != "" and value not in valid_actions:
|
||||
raise HTTPException(400, f"{widget.widget_type} widgets don't support the {value!r} action")
|
||||
with frame_locked(db, frame.id):
|
||||
for button, value in (("next", body.next_button_action), ("back", body.back_button_action)):
|
||||
existing = db.scalars(
|
||||
select(FrameButtonAction).where(
|
||||
FrameButtonAction.widget_id == widget.id, FrameButtonAction.button == button
|
||||
)
|
||||
).first()
|
||||
if value == "":
|
||||
if existing is not None:
|
||||
db.delete(existing)
|
||||
elif existing is not None:
|
||||
existing.action = value
|
||||
else:
|
||||
db.add(FrameButtonAction(frame_id=frame.id, button=button, widget_id=widget.id, action=value))
|
||||
db.commit()
|
||||
return {"status": "saved"}
|
||||
|
||||
|
||||
# --- Photos: queue/thumbnail/preview ------------------------------------
|
||||
|
||||
@router.get("/api/frames/{frame_id}/widgets/{widget_id}/queue")
|
||||
|
||||
Reference in New Issue
Block a user