You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(backup): on-demand snapshot via scope=edits,action=create + UX polish
Three things from BAT validation feedback:
1. **On-demand snapshot for ``ha_manage_backup(scope='edits',
action='create', domain=..., entity_id=...)``.** Mirrors the
``@with_auto_backup`` decorator's path — same handler registry,
same throttle/rotation — but triggered explicitly when the user
is about to manually edit something in the HA UI (outside the
MCP-tool surface). Bypasses ``enable_auto_backup`` because the
request is explicit; clears the per-entity throttle tracker so
the capture always fires. Both ``domain`` and ``entity_id`` are
required; unknown domain returns a structured 400 listing the
registered domain set. The handler does ``object.__setattr__``
for the temporary toggle flip so the override is tight to this
one call (Settings is a shared singleton).
2. **Strip leading ``automation.`` prefix from snapshot filenames.**
When the caller passes ``identifier="automation.foo"`` (typical
``python_transform`` path with no config body), the snapshot
filename was ``automation.automation.foo.<ts>.yaml`` — the
domain segment duplicated. ``automation_backup_target`` now
strips the leading ``automation.`` from the identifier on the
fallback path; YAML body's ``entity_id`` keeps the full form
for restore-side compatibility. Other domains (helper, label,
etc.) already pass bare IDs and are unaffected.
3. **Bump default ``auto_backup_retain_per_entity`` from 20 to 100.**
Real-world file sizes from BAT testing: medium automation 4 KB,
complex AI-vision script 8 KB. 100 retention × 8 KB = 800 KB per
entity — trivial. Old 20 default was conservative; 100 gives a
week-plus of edit history at typical usage. Updated in:
``Settings`` field default, both addon config.yaml defaults,
start.py default, both translations description text, and the
``test_apply_overrides_keeps_defaults_for_unset_envs`` unit
assert.
E2E coverage:
- New ``TestEditsCreateOnDemandSnapshot`` class with a positive
round-trip (create → on-demand snapshot → list → cleanup) and a
negative (unknown domain → 400).
- Existing ``TestManageBackupGating.test_edits_create_rejected``
renamed + rewritten to ``test_edits_create_requires_domain_and_entity_id``
— the combo is no longer rejected outright, but bare call without
domain/entity_id still must fail with a structured validation error.
Tool docstring + routing-matrix updated with the new
``(edits, create)`` row and a usage example for the "snapshot before
manual UI edit" pattern.
Copy file name to clipboardExpand all lines: src/ha_mcp/tools/backup.py
+76Lines changed: 76 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -544,6 +544,7 @@ async def restore_backup(
544
544
_VALID_COMBOS: set[tuple[str, str]] = {
545
545
("snapshot", "create"),
546
546
("snapshot", "restore"),
547
+
("edits", "create"),
547
548
("edits", "list"),
548
549
("edits", "view"),
549
550
("edits", "restore"),
@@ -607,6 +608,7 @@ def register_backup_tools(
607
608
|---|---|---|
608
609
| `snapshot` | `create` | Create a full HA tarball (config + addons, no DB by default). Heavy, seconds-long. |
609
610
| `snapshot` | `restore` | Restore a full HA tarball. **Restarts HA.** Last-resort recovery. |
611
+
| `edits` | `create` | On-demand snapshot of one entity (`domain` + `entity_id` required). Use before the user manually edits in the HA UI. Same handler path the decorator takes on writes; bypasses the `enable_auto_backup` toggle. |
610
612
| `edits` | `list` | List per-entity auto-backups (lightweight). Filter by `domain` and/or `entity_id`. |
611
613
| `edits` | `view` | Read one auto-backup file by name; returns YAML and parsed `config`. |
612
614
| `edits` | `restore` | Re-apply one auto-backup. Creates a fresh safety snapshot first. **No HA restart.** |
@@ -624,6 +626,7 @@ def register_backup_tools(
624
626
**Examples:**
625
627
- Snapshot before risky op: `ha_manage_backup(scope="snapshot", action="create", name="Before_Big_Change")`
626
628
- Restore full snapshot: `ha_manage_backup(scope="snapshot", action="restore", backup_id="dd7550ed")`
629
+
- On-demand entity snapshot before a manual UI edit: `ha_manage_backup(scope="edits", action="create", domain="helper_input_boolean", entity_id="kitchen_lights_active")`
627
630
- List recent auto-backups for one automation: `ha_manage_backup(scope="edits", action="list", domain="automation", entity_id="kitchen_lights")`
628
631
- View an auto-backup: `ha_manage_backup(scope="edits", action="view", backup_name="automation.kitchen_lights.20260521_153000.yaml")`
629
632
- Restore an auto-backup: `ha_manage_backup(scope="edits", action="restore", backup_name="automation.kitchen_lights.20260521_153000.yaml")`
@@ -729,6 +732,79 @@ async def ha_manage_backup(
729
732
settings=get_global_settings()
730
733
mgr=get_backup_manager(client, settings)
731
734
735
+
ifaction=="create":
736
+
# On-demand snapshot for "I'm about to edit this in the HA UI,
737
+
# save the current state first." Mirrors the path the
738
+
# ``@with_auto_backup`` decorator takes on writes — same
739
+
# handler registry, same throttle/rotation rules — but
740
+
# triggered explicitly by the caller for entities they're
741
+
# about to mutate outside the MCP-tool surface. Bypasses
742
+
# ``enable_auto_backup`` because the request is explicit.
0 commit comments