Add support for custom weekly backup schedule (#136079)

* Add support for custom weekly backup schedule

* Rename the new flag to custom_days

* Make the store change backwards compatible

* Improve comments
This commit is contained in:
Erik Montnemery 2025-01-21 08:27:41 +01:00 committed by GitHub
parent 79a43b8a50
commit fb4df00e3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 831 additions and 160 deletions

View File

@ -81,6 +81,7 @@ class BackupConfigData:
time = dt_util.parse_time(time_str) time = dt_util.parse_time(time_str)
else: else:
time = None time = None
days = [Day(day) for day in data["schedule"]["days"]]
return cls( return cls(
create_backup=CreateBackupConfig( create_backup=CreateBackupConfig(
@ -99,7 +100,10 @@ class BackupConfigData:
days=retention["days"], days=retention["days"],
), ),
schedule=BackupSchedule( schedule=BackupSchedule(
state=ScheduleState(data["schedule"]["state"]), time=time days=days,
recurrence=ScheduleRecurrence(data["schedule"]["recurrence"]),
state=ScheduleState(data["schedule"]["state"]),
time=time,
), ),
) )
@ -252,6 +256,8 @@ class RetentionParametersDict(TypedDict, total=False):
class StoredBackupSchedule(TypedDict): class StoredBackupSchedule(TypedDict):
"""Represent the stored backup schedule configuration.""" """Represent the stored backup schedule configuration."""
days: list[Day]
recurrence: ScheduleRecurrence
state: ScheduleState state: ScheduleState
time: str | None time: str | None
@ -259,13 +265,38 @@ class StoredBackupSchedule(TypedDict):
class ScheduleParametersDict(TypedDict, total=False): class ScheduleParametersDict(TypedDict, total=False):
"""Represent parameters for backup schedule.""" """Represent parameters for backup schedule."""
days: list[Day]
recurrence: ScheduleRecurrence
state: ScheduleState state: ScheduleState
time: dt.time | None time: dt.time | None
class ScheduleState(StrEnum): class Day(StrEnum):
"""Represent the day(s) in a custom schedule recurrence."""
MONDAY = "mon"
TUESDAY = "tue"
WEDNESDAY = "wed"
THURSDAY = "thu"
FRIDAY = "fri"
SATURDAY = "sat"
SUNDAY = "sun"
class ScheduleRecurrence(StrEnum):
"""Represent the schedule recurrence.""" """Represent the schedule recurrence."""
NEVER = "never"
DAILY = "daily"
CUSTOM_DAYS = "custom_days"
class ScheduleState(StrEnum):
"""Represent the schedule recurrence.
This is deprecated and can be remove in HA Core 2025.8.
"""
NEVER = "never" NEVER = "never"
DAILY = "daily" DAILY = "daily"
MONDAY = "mon" MONDAY = "mon"
@ -281,6 +312,10 @@ class ScheduleState(StrEnum):
class BackupSchedule: class BackupSchedule:
"""Represent the backup schedule.""" """Represent the backup schedule."""
days: list[Day] = field(default_factory=list)
recurrence: ScheduleRecurrence = ScheduleRecurrence.NEVER
# Although no longer used, state is kept for backwards compatibility.
# It can be removed in HA Core 2025.8.
state: ScheduleState = ScheduleState.NEVER state: ScheduleState = ScheduleState.NEVER
time: dt.time | None = None time: dt.time | None = None
cron_event: CronSim | None = field(init=False, default=None) cron_event: CronSim | None = field(init=False, default=None)
@ -293,22 +328,26 @@ class BackupSchedule:
) -> None: ) -> None:
"""Apply a new schedule. """Apply a new schedule.
There are only three possible state types: never, daily, or weekly. There are only three possible recurrence types: never, daily, or custom_days
""" """
if self.state is ScheduleState.NEVER: if self.recurrence is ScheduleRecurrence.NEVER or (
self.recurrence is ScheduleRecurrence.CUSTOM_DAYS and not self.days
):
self._unschedule_next(manager) self._unschedule_next(manager)
return return
time = self.time if self.time is not None else DEFAULT_BACKUP_TIME time = self.time if self.time is not None else DEFAULT_BACKUP_TIME
if self.state is ScheduleState.DAILY: if self.recurrence is ScheduleRecurrence.DAILY:
self._schedule_next( self._schedule_next(
CRON_PATTERN_DAILY.format(m=time.minute, h=time.hour), CRON_PATTERN_DAILY.format(m=time.minute, h=time.hour),
manager, manager,
) )
else: else: # ScheduleRecurrence.CUSTOM_DAYS
self._schedule_next( self._schedule_next(
CRON_PATTERN_WEEKLY.format( CRON_PATTERN_WEEKLY.format(
m=time.minute, h=time.hour, d=self.state.value m=time.minute,
h=time.hour,
d=",".join(day.value for day in self.days),
), ),
manager, manager,
) )
@ -376,6 +415,8 @@ class BackupSchedule:
def to_dict(self) -> StoredBackupSchedule: def to_dict(self) -> StoredBackupSchedule:
"""Convert backup schedule to a dict.""" """Convert backup schedule to a dict."""
return StoredBackupSchedule( return StoredBackupSchedule(
days=self.days,
recurrence=self.recurrence,
state=self.state, state=self.state,
time=self.time.isoformat() if self.time else None, time=self.time.isoformat() if self.time else None,
) )

View File

@ -48,8 +48,14 @@ class _BackupStore(Store[StoredBackupData]):
data = old_data data = old_data
if old_major_version == 1: if old_major_version == 1:
if old_minor_version < 2: if old_minor_version < 2:
# Version 1.2 adds configurable backup time # Version 1.2 adds configurable backup time and custom days
data["config"]["schedule"]["time"] = None data["config"]["schedule"]["time"] = None
if (state := data["config"]["schedule"]["state"]) in ("daily", "never"):
data["config"]["schedule"]["days"] = []
data["config"]["schedule"]["recurrence"] = state
else:
data["config"]["schedule"]["days"] = [state]
data["config"]["schedule"]["recurrence"] = "custom_days"
if old_major_version > 1: if old_major_version > 1:
raise NotImplementedError raise NotImplementedError

View File

@ -8,7 +8,7 @@ from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from .config import ScheduleState from .config import Day, ScheduleRecurrence
from .const import DATA_MANAGER, LOGGER from .const import DATA_MANAGER, LOGGER
from .manager import ( from .manager import (
DecryptOnDowloadNotSupported, DecryptOnDowloadNotSupported,
@ -320,13 +320,17 @@ async def handle_config_info(
) -> None: ) -> None:
"""Send the stored backup config.""" """Send the stored backup config."""
manager = hass.data[DATA_MANAGER] manager = hass.data[DATA_MANAGER]
config = manager.config.data.to_dict()
# Remove state from schedule, it's not needed in the frontend
# mypy doesn't like deleting from TypedDict, ignore it
del config["schedule"]["state"] # type: ignore[misc]
connection.send_result( connection.send_result(
msg["id"], msg["id"],
{ {
"config": manager.config.data.to_dict() "config": config
| { | {
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup "next_automatic_backup": manager.config.data.schedule.next_automatic_backup
}, }
}, },
) )
@ -358,7 +362,12 @@ async def handle_config_info(
), ),
vol.Optional("schedule"): vol.Schema( vol.Optional("schedule"): vol.Schema(
{ {
vol.Optional("state"): vol.All(str, vol.Coerce(ScheduleState)), vol.Optional("days"): vol.Any(
vol.All([vol.Coerce(Day)], vol.Unique()),
),
vol.Optional("recurrence"): vol.All(
str, vol.Coerce(ScheduleRecurrence)
),
vol.Optional("time"): vol.Any(cv.time, None), vol.Optional("time"): vol.Any(cv.time, None),
} }
), ),

View File

@ -28,6 +28,9 @@
'days': None, 'days': None,
}), }),
'schedule': dict({ 'schedule': dict({
'days': list([
]),
'recurrence': 'never',
'state': 'never', 'state': 'never',
'time': None, 'time': None,
}), }),

File diff suppressed because it is too large Load Diff

View File

@ -71,9 +71,10 @@ DEFAULT_STORAGE_DATA: dict[str, Any] = {
"copies": None, "copies": None,
"days": None, "days": None,
}, },
"schedule": {"state": "never", "time": None}, "schedule": {"days": [], "recurrence": "never", "state": "never", "time": None},
}, },
} }
DAILY = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
@pytest.fixture @pytest.fixture
@ -924,7 +925,12 @@ async def test_agents_info(
"retention": {"copies": 3, "days": 7}, "retention": {"copies": 3, "days": 7},
"last_attempted_automatic_backup": "2024-10-26T04:45:00+01:00", "last_attempted_automatic_backup": "2024-10-26T04:45:00+01:00",
"last_completed_automatic_backup": "2024-10-26T04:45:00+01:00", "last_completed_automatic_backup": "2024-10-26T04:45:00+01:00",
"schedule": {"state": "daily", "time": None}, "schedule": {
"days": DAILY,
"recurrence": "custom_days",
"state": "never",
"time": None,
},
}, },
}, },
"key": DOMAIN, "key": DOMAIN,
@ -949,7 +955,12 @@ async def test_agents_info(
"retention": {"copies": 3, "days": None}, "retention": {"copies": 3, "days": None},
"last_attempted_automatic_backup": None, "last_attempted_automatic_backup": None,
"last_completed_automatic_backup": None, "last_completed_automatic_backup": None,
"schedule": {"state": "never", "time": None}, "schedule": {
"days": [],
"recurrence": "never",
"state": "never",
"time": None,
},
}, },
}, },
"key": DOMAIN, "key": DOMAIN,
@ -974,7 +985,12 @@ async def test_agents_info(
"retention": {"copies": None, "days": 7}, "retention": {"copies": None, "days": 7},
"last_attempted_automatic_backup": "2024-10-27T04:45:00+01:00", "last_attempted_automatic_backup": "2024-10-27T04:45:00+01:00",
"last_completed_automatic_backup": "2024-10-26T04:45:00+01:00", "last_completed_automatic_backup": "2024-10-26T04:45:00+01:00",
"schedule": {"state": "never", "time": None}, "schedule": {
"days": [],
"recurrence": "never",
"state": "never",
"time": None,
},
}, },
}, },
"key": DOMAIN, "key": DOMAIN,
@ -999,7 +1015,12 @@ async def test_agents_info(
"retention": {"copies": None, "days": None}, "retention": {"copies": None, "days": None},
"last_attempted_automatic_backup": None, "last_attempted_automatic_backup": None,
"last_completed_automatic_backup": None, "last_completed_automatic_backup": None,
"schedule": {"state": "mon", "time": None}, "schedule": {
"days": ["mon"],
"recurrence": "custom_days",
"state": "never",
"time": None,
},
}, },
}, },
"key": DOMAIN, "key": DOMAIN,
@ -1024,7 +1045,42 @@ async def test_agents_info(
"retention": {"copies": None, "days": None}, "retention": {"copies": None, "days": None},
"last_attempted_automatic_backup": None, "last_attempted_automatic_backup": None,
"last_completed_automatic_backup": None, "last_completed_automatic_backup": None,
"schedule": {"state": "sat", "time": None}, "schedule": {
"days": [],
"recurrence": "never",
"state": "never",
"time": None,
},
},
},
"key": DOMAIN,
"version": store.STORAGE_VERSION,
"minor_version": store.STORAGE_VERSION_MINOR,
},
},
{
"backup": {
"data": {
"backups": [],
"config": {
"create_backup": {
"agent_ids": ["test-agent"],
"include_addons": None,
"include_all_addons": False,
"include_database": False,
"include_folders": None,
"name": None,
"password": None,
},
"retention": {"copies": None, "days": None},
"last_attempted_automatic_backup": None,
"last_completed_automatic_backup": None,
"schedule": {
"days": ["mon", "sun"],
"recurrence": "custom_days",
"state": "never",
"time": None,
},
}, },
}, },
"key": DOMAIN, "key": DOMAIN,
@ -1069,17 +1125,22 @@ async def test_config_info(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"schedule": {"state": "daily", "time": "06:00"}, "schedule": {"recurrence": "daily", "time": "06:00"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"schedule": {"state": "mon"}, "schedule": {"days": ["mon"], "recurrence": "custom_days"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
},
{
"type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]},
"schedule": {"days": ["mon", "sun"], "recurrence": "custom_days"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
@ -1090,43 +1151,43 @@ async def test_config_info(
"name": "test-name", "name": "test-name",
"password": "test-password", "password": "test-password",
}, },
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": 3, "days": 7}, "retention": {"copies": 3, "days": 7},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": None}, "retention": {"copies": None, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": 3, "days": None}, "retention": {"copies": 3, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": 7}, "retention": {"copies": None, "days": 7},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": 3}, "retention": {"copies": 3},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"days": 7}, "retention": {"days": 7},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
], ],
) )
@ -1174,17 +1235,32 @@ async def test_config_update(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"schedule": "blah", "recurrence": "blah",
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"schedule": {"state": "someday"}, "recurrence": "never",
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"schedule": {"time": "early"}, "recurrence": {"state": "someday"},
},
{
"type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]},
"recurrence": {"time": "early"},
},
{
"type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]},
"recurrence": {"days": "mon"},
},
{
"type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]},
"recurrence": {"days": ["fun"]},
}, },
{ {
"type": "backup/config/update", "type": "backup/config/update",
@ -1260,7 +1336,7 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
} }
], ],
"2024-11-11T04:45:00+01:00", "2024-11-11T04:45:00+01:00",
@ -1279,7 +1355,7 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "mon"}, "schedule": {"days": ["mon"], "recurrence": "custom_days"},
} }
], ],
"2024-11-11T04:45:00+01:00", "2024-11-11T04:45:00+01:00",
@ -1298,7 +1374,11 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "mon", "time": "03:45"}, "schedule": {
"days": ["mon"],
"recurrence": "custom_days",
"time": "03:45",
},
} }
], ],
"2024-11-11T03:45:00+01:00", "2024-11-11T03:45:00+01:00",
@ -1317,7 +1397,7 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "daily", "time": "03:45"}, "schedule": {"recurrence": "daily", "time": "03:45"},
} }
], ],
"2024-11-11T03:45:00+01:00", "2024-11-11T03:45:00+01:00",
@ -1336,7 +1416,26 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "never"}, "schedule": {"days": ["wed", "fri"], "recurrence": "custom_days"},
}
],
"2024-11-11T04:45:00+01:00",
"2024-11-13T04:55:00+01:00",
"2024-11-15T04:55:00+01:00",
"2024-11-13T04:55:00+01:00",
"2024-11-13T04:55:00+01:00",
"2024-11-13T04:55:00+01:00",
1,
2,
BACKUP_CALL,
None,
),
(
[
{
"type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"recurrence": "never"},
} }
], ],
"2024-11-11T04:45:00+01:00", "2024-11-11T04:45:00+01:00",
@ -1355,7 +1454,26 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "daily"}, "schedule": {"days": [], "recurrence": "custom_days"},
}
],
"2024-11-11T04:45:00+01:00",
"2034-11-11T12:00:00+01:00", # ten years later and still no backups
"2034-11-11T13:00:00+01:00",
"2024-11-11T04:45:00+01:00",
"2024-11-11T04:45:00+01:00",
None,
0,
0,
None,
None,
),
(
[
{
"type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"recurrence": "daily"},
} }
], ],
"2024-10-26T04:45:00+01:00", "2024-10-26T04:45:00+01:00",
@ -1374,7 +1492,7 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "mon"}, "schedule": {"days": ["mon"], "recurrence": "custom_days"},
} }
], ],
"2024-10-26T04:45:00+01:00", "2024-10-26T04:45:00+01:00",
@ -1393,7 +1511,7 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
} }
], ],
"2024-10-26T04:45:00+01:00", "2024-10-26T04:45:00+01:00",
@ -1412,7 +1530,7 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
} }
], ],
"2024-11-11T04:45:00+01:00", "2024-11-11T04:45:00+01:00",
@ -1431,7 +1549,7 @@ async def test_config_update_errors(
{ {
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
} }
], ],
"2024-11-11T04:45:00+01:00", "2024-11-11T04:45:00+01:00",
@ -1483,7 +1601,12 @@ async def test_config_schedule_logic(
"retention": {"copies": None, "days": None}, "retention": {"copies": None, "days": None},
"last_attempted_automatic_backup": last_completed_automatic_backup, "last_attempted_automatic_backup": last_completed_automatic_backup,
"last_completed_automatic_backup": last_completed_automatic_backup, "last_completed_automatic_backup": last_completed_automatic_backup,
"schedule": {"state": "daily", "time": None}, "schedule": {
"days": [],
"recurrence": "daily",
"state": "never",
"time": None,
},
}, },
} }
hass_storage[DOMAIN] = { hass_storage[DOMAIN] = {
@ -1553,7 +1676,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": None, "days": None}, "retention": {"copies": None, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1592,7 +1715,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 3, "days": None}, "retention": {"copies": 3, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1631,7 +1754,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 3, "days": None}, "retention": {"copies": 3, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1660,7 +1783,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 3, "days": None}, "retention": {"copies": 3, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1704,7 +1827,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 2, "days": None}, "retention": {"copies": 2, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1748,7 +1871,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 2, "days": None}, "retention": {"copies": 2, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1787,7 +1910,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 2, "days": None}, "retention": {"copies": 2, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1826,7 +1949,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 0, "days": None}, "retention": {"copies": 0, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1870,7 +1993,7 @@ async def test_config_schedule_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 0, "days": None}, "retention": {"copies": 0, "days": None},
"schedule": {"state": "daily"}, "schedule": {"recurrence": "daily"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -1934,7 +2057,12 @@ async def test_config_retention_copies_logic(
"retention": {"copies": None, "days": None}, "retention": {"copies": None, "days": None},
"last_attempted_automatic_backup": None, "last_attempted_automatic_backup": None,
"last_completed_automatic_backup": last_backup_time, "last_completed_automatic_backup": last_backup_time,
"schedule": {"state": "daily", "time": None}, "schedule": {
"days": [],
"recurrence": "daily",
"state": "never",
"time": None,
},
}, },
} }
hass_storage[DOMAIN] = { hass_storage[DOMAIN] = {
@ -2005,7 +2133,7 @@ async def test_config_retention_copies_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": None, "days": None}, "retention": {"copies": None, "days": None},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -2041,7 +2169,7 @@ async def test_config_retention_copies_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 3, "days": None}, "retention": {"copies": 3, "days": None},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -2077,7 +2205,7 @@ async def test_config_retention_copies_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 3, "days": None}, "retention": {"copies": 3, "days": None},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -2118,7 +2246,7 @@ async def test_config_retention_copies_logic(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test.test-agent"]}, "create_backup": {"agent_ids": ["test.test-agent"]},
"retention": {"copies": 2, "days": None}, "retention": {"copies": 2, "days": None},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
}, },
{ {
"backup-1": MagicMock( "backup-1": MagicMock(
@ -2192,7 +2320,12 @@ async def test_config_retention_copies_logic_manual_backup(
"retention": {"copies": None, "days": None}, "retention": {"copies": None, "days": None},
"last_attempted_automatic_backup": None, "last_attempted_automatic_backup": None,
"last_completed_automatic_backup": None, "last_completed_automatic_backup": None,
"schedule": {"state": "daily", "time": None}, "schedule": {
"days": [],
"recurrence": "daily",
"state": "never",
"time": None,
},
}, },
} }
hass_storage[DOMAIN] = { hass_storage[DOMAIN] = {
@ -2320,7 +2453,7 @@ async def test_config_retention_copies_logic_manual_backup(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": 2}, "retention": {"copies": None, "days": 2},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
} }
], ],
{ {
@ -2356,7 +2489,7 @@ async def test_config_retention_copies_logic_manual_backup(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": 2}, "retention": {"copies": None, "days": 2},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
} }
], ],
{ {
@ -2392,7 +2525,7 @@ async def test_config_retention_copies_logic_manual_backup(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": 3}, "retention": {"copies": None, "days": 3},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
} }
], ],
{ {
@ -2428,7 +2561,7 @@ async def test_config_retention_copies_logic_manual_backup(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": 2}, "retention": {"copies": None, "days": 2},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
} }
], ],
{ {
@ -2469,7 +2602,7 @@ async def test_config_retention_copies_logic_manual_backup(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": 2}, "retention": {"copies": None, "days": 2},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
} }
], ],
{ {
@ -2505,7 +2638,7 @@ async def test_config_retention_copies_logic_manual_backup(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": 2}, "retention": {"copies": None, "days": 2},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
} }
], ],
{ {
@ -2541,7 +2674,7 @@ async def test_config_retention_copies_logic_manual_backup(
"type": "backup/config/update", "type": "backup/config/update",
"create_backup": {"agent_ids": ["test-agent"]}, "create_backup": {"agent_ids": ["test-agent"]},
"retention": {"copies": None, "days": 0}, "retention": {"copies": None, "days": 0},
"schedule": {"state": "never"}, "schedule": {"recurrence": "never"},
} }
], ],
{ {
@ -2613,7 +2746,12 @@ async def test_config_retention_days_logic(
"retention": {"copies": None, "days": stored_retained_days}, "retention": {"copies": None, "days": stored_retained_days},
"last_attempted_automatic_backup": None, "last_attempted_automatic_backup": None,
"last_completed_automatic_backup": last_backup_time, "last_completed_automatic_backup": last_backup_time,
"schedule": {"state": "never", "time": None}, "schedule": {
"days": [],
"recurrence": "never",
"state": "never",
"time": None,
},
}, },
} }
hass_storage[DOMAIN] = { hass_storage[DOMAIN] = {