New Addon
Google BK
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Optional
|
||||
from dateutil.tz import tzutc
|
||||
from ..util import Estimator
|
||||
|
||||
from ..const import SOURCE_GOOGLE_DRIVE, SOURCE_HA
|
||||
from ..logger import getLogger
|
||||
from ..config import CreateOptions
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
PROP_TYPE = "type"
|
||||
PROP_VERSION = "version"
|
||||
PROP_PROTECTED = "protected"
|
||||
PROP_RETAINED = "retained"
|
||||
PROP_NOTE = "note"
|
||||
|
||||
DRIVE_KEY_TEXT = "Google Drive's backup metadata"
|
||||
HA_KEY_TEXT = "Home Assistant's backup metadata"
|
||||
|
||||
|
||||
class AbstractBackup():
|
||||
def __init__(self, name: str, slug: str, source: str, date: str, size: int, version: str, backupType: str, protected: bool, note=None, retained: bool = False, uploadable: bool = False, details={}, pending=False):
|
||||
self._options = None
|
||||
self._name = name
|
||||
self._slug = slug
|
||||
self._source = source
|
||||
self._date = date
|
||||
self._size = size
|
||||
self._retained = retained
|
||||
self._uploadable = uploadable
|
||||
self._details = details
|
||||
self._version = version
|
||||
self._backupType = backupType
|
||||
self._protected = protected
|
||||
self._ignore = False
|
||||
self._note = note
|
||||
self._pending = pending
|
||||
|
||||
def isPending(self):
|
||||
return self._pending
|
||||
|
||||
def setOptions(self, options: CreateOptions):
|
||||
self._options = options
|
||||
|
||||
def getOptions(self) -> CreateOptions:
|
||||
return self._options
|
||||
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
def slug(self) -> str:
|
||||
return self._slug
|
||||
|
||||
def size(self) -> int:
|
||||
return self._size
|
||||
|
||||
def note(self) -> str:
|
||||
return self._note
|
||||
|
||||
def sizeInt(self) -> int:
|
||||
try:
|
||||
return int(self.size())
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
def date(self) -> datetime:
|
||||
return self._date
|
||||
|
||||
def source(self) -> str:
|
||||
return self._source
|
||||
|
||||
def retained(self) -> str:
|
||||
return self._retained
|
||||
|
||||
def version(self):
|
||||
return self._version
|
||||
|
||||
def backupType(self):
|
||||
return self._backupType
|
||||
|
||||
def protected(self):
|
||||
return self._protected
|
||||
|
||||
def setRetained(self, retained):
|
||||
self._retained = retained
|
||||
|
||||
def uploadable(self) -> bool:
|
||||
return self._uploadable
|
||||
|
||||
def considerForPurge(self) -> bool:
|
||||
return not self.retained()
|
||||
|
||||
def setUploadable(self, uploadable):
|
||||
self._uploadable = uploadable
|
||||
|
||||
def details(self):
|
||||
return self._details
|
||||
|
||||
def setNote(self, note: str):
|
||||
self._note = note
|
||||
|
||||
def status(self):
|
||||
return None
|
||||
|
||||
def madeByTheAddon(self):
|
||||
return True
|
||||
|
||||
def ignore(self):
|
||||
return self._ignore
|
||||
|
||||
def setIgnore(self, ignore):
|
||||
self._ignore = ignore
|
||||
|
||||
|
||||
class Backup(object):
|
||||
"""
|
||||
Represents a Home Assistant backup stored on Google Drive, locally in
|
||||
Home Assistant, or a pending backup we expect to see show up later
|
||||
"""
|
||||
|
||||
def __init__(self, backup: Optional[AbstractBackup] = None):
|
||||
self.sources: Dict[str, AbstractBackup] = {}
|
||||
self._purgeNext: Dict[str, bool] = {}
|
||||
self._options = None
|
||||
self._status_override = None
|
||||
self._status_override_args = None
|
||||
self._state_detail = None
|
||||
self._upload_source = None
|
||||
self._upload_source_name = None
|
||||
self._upload_fail_info = None
|
||||
if backup is not None:
|
||||
self.addSource(backup)
|
||||
|
||||
def setOptions(self, options):
|
||||
self._options = options
|
||||
|
||||
def getOptions(self):
|
||||
return self._options
|
||||
|
||||
def updatePurge(self, source: str, purge: bool):
|
||||
self._purgeNext[source] = purge
|
||||
|
||||
def addSource(self, backup: AbstractBackup):
|
||||
self.sources[backup.source()] = backup
|
||||
if backup.getOptions() and not self.getOptions():
|
||||
self.setOptions(backup.getOptions())
|
||||
|
||||
def getStatusDetail(self):
|
||||
return self._state_detail
|
||||
|
||||
def setStatusDetail(self, info):
|
||||
self._state_detail = info
|
||||
|
||||
def removeSource(self, source):
|
||||
if source in self.sources:
|
||||
del self.sources[source]
|
||||
if source in self._purgeNext:
|
||||
del self._purgeNext[source]
|
||||
|
||||
def getPurges(self):
|
||||
return self._purgeNext
|
||||
|
||||
def uploadInfo(self):
|
||||
if not self._upload_source:
|
||||
return {}
|
||||
elif self._upload_source.progress() == 100:
|
||||
return {}
|
||||
else:
|
||||
return {
|
||||
'progress': self._upload_source.progress()
|
||||
}
|
||||
|
||||
def getSource(self, source: str):
|
||||
return self.sources.get(source, None)
|
||||
|
||||
def name(self):
|
||||
for backup in self.sources.values():
|
||||
return backup.name()
|
||||
return "error"
|
||||
|
||||
def note(self):
|
||||
longest = None
|
||||
for backup in self.sources.values():
|
||||
if backup.note() is not None and (longest is None or len(backup.note()) > len(longest)):
|
||||
longest = backup.note()
|
||||
return longest
|
||||
|
||||
def slug(self) -> str:
|
||||
for backup in self.sources.values():
|
||||
return backup.slug()
|
||||
return "error"
|
||||
|
||||
def size(self) -> int:
|
||||
for backup in self.sources.values():
|
||||
return backup.size()
|
||||
return 0
|
||||
|
||||
def sizeInt(self) -> int:
|
||||
for backup in self.sources.values():
|
||||
return backup.sizeInt()
|
||||
return 0
|
||||
|
||||
def backupType(self) -> str:
|
||||
for backup in self.sources.values():
|
||||
return backup.backupType()
|
||||
return "error"
|
||||
|
||||
def version(self) -> str:
|
||||
for backup in self.sources.values():
|
||||
if backup.version() is not None:
|
||||
return backup.version()
|
||||
return None
|
||||
|
||||
def details(self):
|
||||
for backup in self.sources.values():
|
||||
if backup.details() is not None:
|
||||
return backup.details()
|
||||
return {}
|
||||
|
||||
def getUploadInfo(self, time):
|
||||
if self._upload_source_name is None:
|
||||
return None
|
||||
ret = {
|
||||
'name': self._upload_source_name
|
||||
}
|
||||
if self._upload_fail_info:
|
||||
ret['failure'] = self._upload_fail_info
|
||||
elif self._upload_source is not None:
|
||||
ret['progress'] = self._upload_source.progress()
|
||||
ret['speed'] = self._upload_source.speed(timedelta(seconds=20))
|
||||
ret['total'] = self._upload_source.position()
|
||||
ret['started'] = time.formatDelta(self._upload_source.startTime())
|
||||
return ret
|
||||
|
||||
def protected(self) -> bool:
|
||||
for backup in self.sources.values():
|
||||
return backup.protected()
|
||||
return False
|
||||
|
||||
def ignore(self) -> bool:
|
||||
for backup in self.sources.values():
|
||||
if not backup.ignore():
|
||||
return False
|
||||
return True
|
||||
|
||||
def date(self) -> datetime:
|
||||
for backup in self.sources.values():
|
||||
return backup.date()
|
||||
return datetime.now(tzutc())
|
||||
|
||||
def sizeString(self) -> str:
|
||||
size_string = self.size()
|
||||
if type(size_string) == str:
|
||||
return size_string
|
||||
return Estimator.asSizeString(size_string)
|
||||
|
||||
def status(self) -> str:
|
||||
# TODO: Drive Specific
|
||||
if self._status_override is not None:
|
||||
return self._status_override.format(*self._status_override_args)
|
||||
|
||||
for backup in self.sources.values():
|
||||
status = backup.status()
|
||||
if status:
|
||||
return status
|
||||
|
||||
inDrive = self.getSource(SOURCE_GOOGLE_DRIVE) is not None
|
||||
inHa = self.getSource(SOURCE_HA) is not None
|
||||
|
||||
if inDrive and inHa:
|
||||
return "Backed Up"
|
||||
if inDrive:
|
||||
return "Drive Only"
|
||||
if inHa:
|
||||
return "HA Only"
|
||||
return "Deleted"
|
||||
|
||||
def isDeleted(self) -> bool:
|
||||
return len(self.sources) == 0
|
||||
|
||||
def overrideStatus(self, format, *args) -> None:
|
||||
self._status_override = format
|
||||
self._status_override_args = args
|
||||
|
||||
def setUploadSource(self, source_name: str, source):
|
||||
self._upload_source = source
|
||||
self._upload_source_name = source_name
|
||||
self._upload_fail_info = None
|
||||
|
||||
def clearUploadSource(self):
|
||||
self._upload_source = None
|
||||
self._upload_source_name = None
|
||||
self._upload_fail_info = None
|
||||
|
||||
def uploadFailure(self, info):
|
||||
self._upload_source = None
|
||||
self._upload_fail_info = info
|
||||
|
||||
def clearStatus(self):
|
||||
self._status_override = None
|
||||
self._status_override_args = None
|
||||
|
||||
def isPending(self):
|
||||
for backup in self.sources.values():
|
||||
if backup.isPending():
|
||||
return True
|
||||
return False
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "<Slug: {0} {1} {2}>".format(self.slug(), " ".join(self.sources), self.date().isoformat())
|
||||
|
||||
def __format__(self, format_spec: str) -> str:
|
||||
return self.__str__()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
Reference in New Issue
Block a user