OneDrive Backup
OneDrive Backup
This commit is contained in:
@@ -0,0 +1,546 @@
|
||||
using hassio_onedrive_backup.Contracts;
|
||||
using hassio_onedrive_backup.Graph;
|
||||
using Microsoft.Graph;
|
||||
using Newtonsoft.Json;
|
||||
using onedrive_backup.Graph;
|
||||
using onedrive_backup.Hass;
|
||||
using System.Collections;
|
||||
using static hassio_onedrive_backup.Contracts.HassAddonsResponse;
|
||||
using static hassio_onedrive_backup.Contracts.HassBackupsResponse;
|
||||
|
||||
namespace hassio_onedrive_backup.Hass
|
||||
{
|
||||
public class BackupManager
|
||||
{
|
||||
private const int InstanceNameMaxLength = 20;
|
||||
private readonly HassOnedriveEntityState _hassEntityState;
|
||||
private readonly TransferSpeedHelper? _transferSpeedHelper;
|
||||
private readonly HassContext _hassContext;
|
||||
private AddonOptions _addonOptions;
|
||||
private IGraphHelper _graphHelper;
|
||||
private IHassioClient _hassIoClient;
|
||||
private BitArray _allowedHours;
|
||||
private bool _isExecuting = false;
|
||||
|
||||
public List<Backup> LocalBackups { get; private set; }
|
||||
public List<OnedriveBackup> OnlineBackups { get; private set; }
|
||||
|
||||
public BackupManager(IServiceProvider serviceProvider, BitArray allowedHours, TransferSpeedHelper? transferSpeedHelper)
|
||||
{
|
||||
_addonOptions = serviceProvider.GetService<AddonOptions>();
|
||||
_graphHelper = serviceProvider.GetService<IGraphHelper>();
|
||||
_hassIoClient = serviceProvider.GetService<IHassioClient>();
|
||||
_hassEntityState = serviceProvider.GetService<HassOnedriveEntityState>();
|
||||
_transferSpeedHelper = transferSpeedHelper;
|
||||
_hassContext = serviceProvider.GetService<HassContext>();
|
||||
_allowedHours = allowedHours;
|
||||
}
|
||||
|
||||
public event Action? LocalBackupsUpdated;
|
||||
|
||||
public event Action? OneDriveBackupsUpdated;
|
||||
|
||||
public async Task PerformBackupsAsync()
|
||||
{
|
||||
if (_isExecuting)
|
||||
{
|
||||
ConsoleLogger.LogVerbose("Previous backup iteration still executing. Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_isExecuting = true;
|
||||
await UpdateHassEntity();
|
||||
var now = DateTimeHelper.Instance!.Now;
|
||||
|
||||
// Get existing local backups
|
||||
ConsoleLogger.LogVerbose("Retrieving existing local backups...");
|
||||
var localBackups = await GetLocalBackups();
|
||||
|
||||
// Get existing online backups
|
||||
ConsoleLogger.LogVerbose("Retrieving existing online backups...");
|
||||
var onlineBackups = await GetOnlineBackupsAsync(_addonOptions.InstanceName);
|
||||
|
||||
DateTime lastLocalBackupTime = localBackups.Any() ? localBackups.Max(backup => backup.Date) : DateTime.MinValue;
|
||||
ConsoleLogger.LogVerbose($"Last local backup Date: {(lastLocalBackupTime == DateTime.MinValue ? "None" : lastLocalBackupTime)}");
|
||||
|
||||
DateTime lastOnlineBackupTime = onlineBackups.Any() ? onlineBackups.Max(backup => backup.BackupDate) : DateTime.MinValue;
|
||||
ConsoleLogger.LogVerbose($"Last online backup Date: {(lastOnlineBackupTime == DateTime.MinValue ? "None" : lastOnlineBackupTime)}");
|
||||
|
||||
// Create local backups if needed
|
||||
if ((now - lastLocalBackupTime).TotalHours >= _addonOptions.BackupIntervalHours && (now - lastOnlineBackupTime).TotalHours >= _addonOptions.BackupIntervalHours)
|
||||
{
|
||||
if (_allowedHours[now.Hour] == false)
|
||||
{
|
||||
ConsoleLogger.LogWarning("Not performing backup outside allowed times");
|
||||
}
|
||||
else
|
||||
{
|
||||
_hassEntityState.State = HassOnedriveEntityState.BackupState.Syncing;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
await CreateLocalBackup();
|
||||
}
|
||||
}
|
||||
|
||||
// Get Online backup candidates
|
||||
var onlineBackupCandiates = await GetOnlineBackupCandidatesAsync(localBackups);
|
||||
|
||||
var uploadCandidates = onlineBackupCandiates
|
||||
.Select(bc => new { Slug = bc.Slug, Date = bc.Date }).Union(onlineBackups.Select(ob => new { Slug = ob.Slug, Date = ob.BackupDate }))
|
||||
.OrderByDescending(backup => backup.Date)
|
||||
.Take(_addonOptions.MaxOnedriveBackups).ToList();
|
||||
|
||||
// Get Online Backups Candidates that have not yet been uploaded
|
||||
var backupsToUpload = new List<Backup>();
|
||||
foreach (var backupId in uploadCandidates)
|
||||
{
|
||||
if (onlineBackups.Any(ob => ob.Slug == backupId.Slug))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
backupsToUpload.Add(onlineBackupCandiates.Single(bc => bc.Slug == backupId.Slug));
|
||||
}
|
||||
|
||||
// Upload backups
|
||||
if (backupsToUpload.Any())
|
||||
{
|
||||
ConsoleLogger.LogInfo($"Found {backupsToUpload.Count()} backups to upload.");
|
||||
|
||||
// Set Home Assistant Entity state to Syncing
|
||||
_hassEntityState.State = HassOnedriveEntityState.BackupState.Syncing;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
|
||||
foreach (var backup in backupsToUpload)
|
||||
{
|
||||
await UploadLocalBackupToOneDrive(backup);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleLogger.LogVerbose("Online backups synced. No upload required");
|
||||
}
|
||||
|
||||
// Refresh Online Backups
|
||||
onlineBackups = await GetOnlineBackupsAsync(_addonOptions.InstanceName);
|
||||
int numOfOnlineBackupsToDelete = Math.Max(0, onlineBackups.Count - _addonOptions.MaxOnedriveBackups);
|
||||
|
||||
// Delete Old Online Backups
|
||||
var backupsToDelete = onlineBackups
|
||||
.OrderBy(onlineBackup => onlineBackup.BackupDate)
|
||||
.Take(numOfOnlineBackupsToDelete)
|
||||
.ToList();
|
||||
|
||||
if (backupsToDelete.Any())
|
||||
{
|
||||
_hassEntityState.State = HassOnedriveEntityState.BackupState.Syncing;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
|
||||
ConsoleLogger.LogInfo($"Found {backupsToDelete.Count()} backups to delete from OneDrive.");
|
||||
foreach (var backupToDelete in backupsToDelete)
|
||||
{
|
||||
bool deleteSuccessfull = await _graphHelper.DeleteItemFromAppFolderAsync(backupToDelete.FileName);
|
||||
if (deleteSuccessfull == false)
|
||||
{
|
||||
await _hassIoClient.PublishEventAsync(Events.OneDriveEvents.OneDriveBackupDeleteFailed);
|
||||
if (_addonOptions.NotifyOnError)
|
||||
{
|
||||
await _hassIoClient.SendPersistentNotificationAsync("Failed deleting old backup from OneDrive. Check Addon logs for more details");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Old Local Backups
|
||||
if (localBackups.Count > _addonOptions.MaxLocalBackups)
|
||||
{
|
||||
_hassEntityState.State = HassOnedriveEntityState.BackupState.Syncing;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
|
||||
int numOfLocalBackupsToRemove = localBackups.Count - _addonOptions.MaxLocalBackups;
|
||||
var localBackupsToRemove = localBackups
|
||||
.OrderBy(backup => backup.Date)
|
||||
.Take(numOfLocalBackupsToRemove)
|
||||
.ToList();
|
||||
|
||||
ConsoleLogger.LogInfo($"Removing {numOfLocalBackupsToRemove} local backups");
|
||||
foreach (var localBackup in localBackupsToRemove)
|
||||
{
|
||||
bool deleteSuccess = await _hassIoClient.DeleteBackupAsync(localBackup);
|
||||
if (deleteSuccess == false)
|
||||
{
|
||||
await _hassIoClient.PublishEventAsync(Events.OneDriveEvents.LocalBackupDeleteFailed);
|
||||
if (_addonOptions.NotifyOnError)
|
||||
{
|
||||
await _hassIoClient.SendPersistentNotificationAsync("Error Deleting Local Backup. Check Addon logs for more details");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await UpdateHassEntity();
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isExecuting = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> CreateLocalBackup()
|
||||
{
|
||||
List<string>? addons = null;
|
||||
List<string>? folders = null;
|
||||
|
||||
ConsoleLogger.LogInfo($"Creating new backup");
|
||||
if (_addonOptions.IsPartialBackup)
|
||||
{
|
||||
addons = _hassContext.Addons
|
||||
.Where(addon => _addonOptions.ExcludedAddons.Any(excludedAddon => excludedAddon.Equals(addon.Slug, StringComparison.OrdinalIgnoreCase)) == false)
|
||||
.Select(addon => addon.Slug)
|
||||
.ToList();
|
||||
folders = _addonOptions.IncludedFolderList;
|
||||
}
|
||||
|
||||
bool backupCreated = await _hassIoClient.CreateBackupAsync(
|
||||
_addonOptions.BackupNameSafe,
|
||||
compressed: true,
|
||||
password: String.IsNullOrEmpty(_addonOptions.BackupPassword) ? null : _addonOptions.BackupPassword,
|
||||
addons: addons,
|
||||
folders: folders);
|
||||
|
||||
if (backupCreated == false)
|
||||
{
|
||||
await _hassIoClient.PublishEventAsync(Events.OneDriveEvents.BackupCreateFailed);
|
||||
if (_addonOptions.NotifyOnError)
|
||||
{
|
||||
await _hassIoClient.SendPersistentNotificationAsync("Failed creating local backup. Check Addon logs for more details");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LocalBackups = await GetLocalBackups();
|
||||
}
|
||||
|
||||
return backupCreated;
|
||||
}
|
||||
|
||||
public async Task<bool> UploadLocalBackupToOneDrive(Backup backup, Action<int?, int?>? progressCallback = null, bool updateHassEntityState = true)
|
||||
{
|
||||
string? tempBackupFilePath = null;
|
||||
try
|
||||
{
|
||||
ConsoleLogger.LogInfo($"Uploading {backup.Name} ({backup.Date})");
|
||||
string? instanceSuffix = _addonOptions.InstanceName == null ? null : $".{_addonOptions.InstanceName.Substring(0, Math.Min(InstanceNameMaxLength, _addonOptions.InstanceName.Length))}";
|
||||
string destinationFileName = $"{backup.Name}{instanceSuffix}.tar";
|
||||
tempBackupFilePath = await _hassIoClient.DownloadBackupAsync(backup.Slug);
|
||||
var uploadSuccessful = await _graphHelper.UploadFileAsync(tempBackupFilePath, backup.Date, _addonOptions.InstanceName, _transferSpeedHelper, destinationFileName,
|
||||
async (prog, speed) =>
|
||||
{
|
||||
if (updateHassEntityState)
|
||||
{
|
||||
_hassEntityState.UploadPercentage = prog;
|
||||
_hassEntityState.UploadSpeed = speed / 1024;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
}
|
||||
|
||||
progressCallback?.Invoke(prog, speed);
|
||||
},
|
||||
description: SerializeBackupDescription(tempBackupFilePath, backup)
|
||||
);
|
||||
if (uploadSuccessful == false)
|
||||
{
|
||||
await _hassIoClient.PublishEventAsync(Events.OneDriveEvents.BackupUploadFailed);
|
||||
if (_addonOptions.NotifyOnError)
|
||||
{
|
||||
await _hassIoClient.SendPersistentNotificationAsync("Failed uploading backup to onedrive. Check Addon logs for more details");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogError($"Error uploading backup: {ex}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Delete temporary backup file
|
||||
if (tempBackupFilePath != null)
|
||||
{
|
||||
System.IO.File.Delete(tempBackupFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task DownloadCloudBackupsAsync()
|
||||
{
|
||||
_hassEntityState.State = HassOnedriveEntityState.BackupState.RecoveryMode;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
var onlineBackups = await GetOnlineBackupsAsync("*");
|
||||
var onlineInstanceBackups = onlineBackups.Where(backup => string.Equals(backup.InstanceName, _addonOptions.InstanceName, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
var localBackups = await GetLocalBackups();
|
||||
|
||||
if (onlineInstanceBackups.Count > 0)
|
||||
{
|
||||
ConsoleLogger.LogInfo($"Found {onlineInstanceBackups.Count} matching backups in OneDrive");
|
||||
}
|
||||
else if (onlineBackups.Count > 0)
|
||||
{
|
||||
var instanceNames = onlineBackups.Select(backup => backup.InstanceName ?? "*NoInstance*").Distinct();
|
||||
ConsoleLogger.LogInfo($"Found backups belonging to other instances: {string.Join(',', instanceNames)}. If you would like to use another instance backup please update the addon configuration and set the appropriate instance name");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleLogger.LogWarning($"No backups found in OneDrive");
|
||||
return;
|
||||
}
|
||||
|
||||
var localBackupNum = localBackups.Count;
|
||||
int numberOfBackupsToDownload = Math.Max(0, _addonOptions.MaxLocalBackups - localBackupNum);
|
||||
if (numberOfBackupsToDownload == 0)
|
||||
{
|
||||
ConsoleLogger.LogWarning(
|
||||
$"Local backups at maximum configured number ({_addonOptions.MaxLocalBackups}). To sync additional backups from OneDrive either delete some local backups or increase the configured maximum");
|
||||
return;
|
||||
}
|
||||
|
||||
var backupsToDownload = onlineInstanceBackups
|
||||
.OrderByDescending(backup => backup.BackupDate)
|
||||
.Where(backup => localBackups.Any(local => local.Slug.Equals(backup.Slug, StringComparison.OrdinalIgnoreCase)) == false)
|
||||
.Take(numberOfBackupsToDownload)
|
||||
.ToList();
|
||||
|
||||
if (backupsToDownload.Count == 0)
|
||||
{
|
||||
ConsoleLogger.LogInfo($"All {Math.Min(numberOfBackupsToDownload, onlineInstanceBackups.Count)} latest backups already exist locally");
|
||||
}
|
||||
|
||||
foreach (var onlineBackup in backupsToDownload)
|
||||
{
|
||||
await DownloadBackupFromOneDrive(onlineBackup);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadBackupFromOneDrive(OnedriveBackup onlineBackup, Action<int?>? progressCallback = null, bool updateHassEntityState = true)
|
||||
{
|
||||
string? backupFile = null;
|
||||
try
|
||||
{
|
||||
ConsoleLogger.LogInfo($"Downloading backup {onlineBackup.FileName}");
|
||||
backupFile = await _graphHelper.DownloadFileAsync(onlineBackup.FileName, async (prog) =>
|
||||
{
|
||||
if (updateHassEntityState)
|
||||
{
|
||||
_hassEntityState.DownloadPercentage = prog;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
}
|
||||
|
||||
progressCallback?.Invoke(prog);
|
||||
});
|
||||
|
||||
if (backupFile == null)
|
||||
{
|
||||
ConsoleLogger.LogError($"Error downloading backup {onlineBackup.FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upload backup to Home Assistant
|
||||
ConsoleLogger.LogInfo($"Loading backup {onlineBackup.FileName} to Home Assisant");
|
||||
await _hassIoClient.UploadBackupAsync(backupFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogError($"Error fetching backup {onlineBackup.FileName} from Onedrive to Home Assistant. {ex}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (backupFile != null)
|
||||
{
|
||||
System.IO.File.Delete(backupFile);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string SerializeBackupDescription(string originalFileName, Backup backup)
|
||||
{
|
||||
var description = new OnedriveItemDescription
|
||||
{
|
||||
Slug = Path.GetFileNameWithoutExtension(originalFileName),
|
||||
BackupDate = backup.Date,
|
||||
InstanceName = _addonOptions.InstanceName,
|
||||
BackupType = backup.Type,
|
||||
IsProtected = backup.Protected,
|
||||
Size = backup.Size,
|
||||
Addons = Enumerable.Empty<string>(), //Temporary workaround for size issue
|
||||
Folders = backup.Content?.Folders ?? Enumerable.Empty<string>()
|
||||
};
|
||||
|
||||
string serializedDesc = JsonConvert.SerializeObject(description);
|
||||
ConsoleLogger.LogVerbose($"Backup Description: {serializedDesc}");
|
||||
return serializedDesc;
|
||||
}
|
||||
|
||||
private async Task UpdateHassEntity()
|
||||
{
|
||||
var now = DateTimeHelper.Instance!.Now;
|
||||
var localBackups = await GetLocalBackups();
|
||||
var onlineBackups = await GetOnlineBackupsAsync(_addonOptions.InstanceName);
|
||||
_hassEntityState.BackupsInHomeAssistant = localBackups.Count;
|
||||
_hassEntityState.BackupsInOnedrive = onlineBackups.Count;
|
||||
_hassEntityState.LastLocalBackupDate = localBackups.Any() ? localBackups.Max(backup => backup.Date) : null;
|
||||
_hassEntityState.LastOnedriveBackupDate = onlineBackups.Any() ? onlineBackups.Max(backup => backup.BackupDate) : null;
|
||||
|
||||
bool onedriveSynced = false;
|
||||
bool localSynced = false;
|
||||
|
||||
if (_hassEntityState.LastOnedriveBackupDate != null && (now - _hassEntityState.LastOnedriveBackupDate.Value).TotalHours <= _addonOptions.BackupIntervalHours)
|
||||
{
|
||||
onedriveSynced = true;
|
||||
}
|
||||
|
||||
if (_hassEntityState.LastLocalBackupDate != null && (now - _hassEntityState.LastLocalBackupDate.Value).TotalHours <= _addonOptions.BackupIntervalHours)
|
||||
{
|
||||
localSynced = true;
|
||||
}
|
||||
|
||||
HassOnedriveEntityState.BackupState state = HassOnedriveEntityState.BackupState.Unknown;
|
||||
if (onedriveSynced && localSynced)
|
||||
{
|
||||
state = HassOnedriveEntityState.BackupState.Backed_Up;
|
||||
}
|
||||
else if (onedriveSynced)
|
||||
{
|
||||
state = HassOnedriveEntityState.BackupState.Backed_Up_Onedrive;
|
||||
}
|
||||
else if (localSynced)
|
||||
{
|
||||
state = HassOnedriveEntityState.BackupState.Backed_Up_Local;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = HassOnedriveEntityState.BackupState.Stale;
|
||||
}
|
||||
|
||||
_hassEntityState.State = state;
|
||||
await _hassEntityState.UpdateBackupEntityInHass();
|
||||
}
|
||||
|
||||
public async Task RefreshBackupData()
|
||||
{
|
||||
var getLocalBackupsTask = GetLocalBackups();
|
||||
var getOneDriveBackupTasks = GetOnlineBackupsAsync(_addonOptions.InstanceName);
|
||||
await Task.WhenAll(getLocalBackupsTask, getOneDriveBackupTasks);
|
||||
}
|
||||
|
||||
public async Task<List<OnedriveBackup>> GetOnlineBackupsAsync(string? instanceName)
|
||||
{
|
||||
var onlineBackups = (await _graphHelper.GetItemsInAppFolderAsync()).Select(CheckIfFileIsBackup).ToList();
|
||||
onlineBackups.RemoveAll(item => item == null);
|
||||
if (instanceName != "*")
|
||||
{
|
||||
onlineBackups = onlineBackups.Where(item => string.Equals(instanceName, item.InstanceName, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
}
|
||||
|
||||
OnlineBackups = onlineBackups;
|
||||
OneDriveBackupsUpdated?.Invoke();
|
||||
return onlineBackups;
|
||||
}
|
||||
|
||||
private OnedriveBackup? CheckIfFileIsBackup(DriveItem item)
|
||||
{
|
||||
OnedriveBackup ret = null;
|
||||
if (item.Folder != null && item.Name.Equals("FileSync"))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ret = new OnedriveBackup(item.Name, JsonConvert.DeserializeObject<OnedriveItemDescription>(item.Description)!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogWarning($"Unrecognized file found in backup folder : {item.Name}");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Task<List<Backup>> GetOnlineBackupCandidatesAsync(IEnumerable<Backup> localBackups)
|
||||
{
|
||||
var filteredLocalBackups = localBackups
|
||||
.Where(backup => backup.Compressed)
|
||||
.OrderByDescending(backup => backup.Date);
|
||||
|
||||
return Task.FromResult(filteredLocalBackups.ToList());
|
||||
}
|
||||
|
||||
public async Task<List<Backup>> GetLocalBackups()
|
||||
{
|
||||
var ret = await _hassIoClient.GetBackupsAsync(IsMonitoredBackup);
|
||||
LocalBackups = ret;
|
||||
LocalBackupsUpdated?.Invoke();
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool IsMonitoredBackup(Backup backup)
|
||||
{
|
||||
// Monitoring All Backups
|
||||
if (_addonOptions.MonitorAllLocalBackups)
|
||||
{
|
||||
// If should ignore upgrade backups and backup seems like an upgrade backup skip it
|
||||
if (_addonOptions.IgnoreUpgradeBackups && IsUpgradeBackup(backup))
|
||||
{
|
||||
ConsoleLogger.LogVerbose($"Ignoring Upgrade Backup: {backup.Name}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise back it up
|
||||
return true;
|
||||
}
|
||||
|
||||
// If addon backup always back up
|
||||
if (backup.Name.StartsWith(_addonOptions.BackupNameSafe, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleLogger.LogVerbose($"Ignoring 'External' backup: {backup.Name}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsUpgradeBackup(Backup backup)
|
||||
{
|
||||
// Check if addon upgrade backup
|
||||
if (backup.Name.StartsWith("addon_", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (backup.Content?.Homeassistant != true && backup.Content?.Addons?.Count() == 1)
|
||||
{
|
||||
ConsoleLogger.LogVerbose($"Backup {backup.Name} detected as Addon auto upgrade backup");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// check if core upgrade backup
|
||||
if (backup.Name.StartsWith("core_", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (backup.Content?.Homeassistant == true && backup.Content?.Addons.Count() == 0)
|
||||
{
|
||||
ConsoleLogger.LogVerbose($"Backup {backup.Name} detected as Home Assistant auto upgrade backup");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace hassio_onedrive_backup.Hass.Events
|
||||
{
|
||||
public enum OneDriveEvents
|
||||
{
|
||||
BackupCreateFailed,
|
||||
BackupUploadFailed,
|
||||
OneDriveBackupDeleteFailed,
|
||||
LocalBackupDeleteFailed,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.Graph;
|
||||
using static hassio_onedrive_backup.Contracts.HassAddonsResponse;
|
||||
|
||||
namespace onedrive_backup.Hass
|
||||
{
|
||||
public class HassContext
|
||||
{
|
||||
public string IngressUrl { get; set; }
|
||||
|
||||
public string HeaderIngressPath { get; set; }
|
||||
|
||||
public IEnumerable<Addon> Addons { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace hassio_onedrive_backup.Hass
|
||||
{
|
||||
internal class HassOnedriveEntityState : INotifyPropertyChanged
|
||||
{
|
||||
private const string OneDrive_Backup_Entity_ID = "sensor.onedrivebackup";
|
||||
private IHassioClient? _hassioClient;
|
||||
private BackupState state;
|
||||
private List<JsonConverter> entityStateConverters = new List<JsonConverter>
|
||||
{
|
||||
new StringEnumConverter()
|
||||
};
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public HassOnedriveEntityState(IHassioClient hassioClient)
|
||||
{
|
||||
_hassioClient = hassioClient;
|
||||
State = BackupState.Unknown;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public BackupState State
|
||||
{
|
||||
get => state;
|
||||
set
|
||||
{
|
||||
state = value;
|
||||
if (state != BackupState.Syncing)
|
||||
{
|
||||
UploadPercentage = null;
|
||||
UploadSpeed = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime? LastLocalBackupDate { get; set; }
|
||||
|
||||
public DateTime? LastOnedriveBackupDate { get; set; }
|
||||
|
||||
public int BackupsInHomeAssistant { get; set; }
|
||||
|
||||
public int BackupsInOnedrive { get; set; }
|
||||
|
||||
public int? UploadPercentage { get; set; }
|
||||
|
||||
public int? DownloadPercentage { get; set; }
|
||||
|
||||
// KB/s
|
||||
public int? UploadSpeed { get; set; }
|
||||
|
||||
public async Task UpdateBackupEntityInHass()
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
state = State,
|
||||
attributes = new Dictionary<string, string?>
|
||||
{
|
||||
{ BackupStateAttribute.LastLocalBackupDate, LastLocalBackupDate?.ToString(DateTimeHelper.DateTimeFormat) },
|
||||
{ BackupStateAttribute.LastOnedriveBackupDate, LastOnedriveBackupDate?.ToString(DateTimeHelper.DateTimeFormat) },
|
||||
{ BackupStateAttribute.BackupsInHomeAssistant, BackupsInHomeAssistant.ToString() },
|
||||
{ BackupStateAttribute.BackupsInOnedrive, BackupsInOnedrive.ToString() },
|
||||
{ BackupStateAttribute.UploadPercentage, UploadPercentage == null ? null : $"{UploadPercentage}%" },
|
||||
{ BackupStateAttribute.UploadSpeed, UploadSpeed== null ? null : $"{UploadSpeed} KB/s" },
|
||||
{ BackupStateAttribute.DownloadPercentage, DownloadPercentage == null ? null : $"{DownloadPercentage}%" }
|
||||
}
|
||||
};
|
||||
|
||||
string payloadStr = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
Converters = entityStateConverters
|
||||
});
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
|
||||
await _hassioClient.UpdateHassEntityStateAsync(OneDrive_Backup_Entity_ID, payloadStr);
|
||||
}
|
||||
|
||||
public class BackupStateAttribute
|
||||
{
|
||||
public const string LastLocalBackupDate = "Last Local backup date";
|
||||
|
||||
public const string LastOnedriveBackupDate = "Last OneDrive backup date";
|
||||
|
||||
public const string BackupsInHomeAssistant = "Backups in Home Assistant";
|
||||
|
||||
public const string BackupsInOnedrive = "Backups in OneDrive";
|
||||
|
||||
public const string UploadPercentage = "Current backup upload percentage";
|
||||
|
||||
public const string DownloadPercentage = "Backup download percentage";
|
||||
|
||||
public const string UploadSpeed = "Current backup upload speed (KB/s)";
|
||||
}
|
||||
public enum BackupState
|
||||
{
|
||||
Syncing,
|
||||
Backed_Up_Local,
|
||||
Backed_Up_Onedrive,
|
||||
Backed_Up,
|
||||
Stale,
|
||||
RecoveryMode,
|
||||
Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace hassio_onedrive_backup.Hass
|
||||
{
|
||||
internal class HassOnedriveFileSyncEntityState : INotifyPropertyChanged
|
||||
{
|
||||
private const string OneDrive_FileSync_Entity_ID = "sensor.onedrivefilesync";
|
||||
|
||||
private IHassioClient? _hassioClient;
|
||||
private static HassOnedriveFileSyncEntityState? _instance;
|
||||
private FileState state;
|
||||
private List<JsonConverter> entityStateConverters = new List<JsonConverter>
|
||||
{
|
||||
new StringEnumConverter()
|
||||
};
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public HassOnedriveFileSyncEntityState(IHassioClient hassioClient)
|
||||
{
|
||||
_hassioClient = hassioClient;
|
||||
State = FileState.Unknown;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public FileState State
|
||||
{
|
||||
get => state;
|
||||
set
|
||||
{
|
||||
state = value;
|
||||
if (state != FileState.Syncing)
|
||||
{
|
||||
UploadPercentage = null;
|
||||
UploadSpeed = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int? UploadPercentage { get; set; }
|
||||
|
||||
public int? UploadSpeed { get; set; }
|
||||
|
||||
|
||||
public async Task UpdateBackupEntityInHass()
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
state = State,
|
||||
attributes = new Dictionary<string, string?>
|
||||
{
|
||||
{ FileStateAttribute.UploadPercentage, UploadPercentage == null ? null : $"{UploadPercentage}%" },
|
||||
{ FileStateAttribute.UploadSpeed, UploadSpeed == null ? null : $"{UploadSpeed} KB/s" },
|
||||
}
|
||||
};
|
||||
|
||||
string payloadStr = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
Converters = entityStateConverters
|
||||
});
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
|
||||
await _hassioClient.UpdateHassEntityStateAsync(OneDrive_FileSync_Entity_ID, payloadStr);
|
||||
}
|
||||
|
||||
public class FileStateAttribute
|
||||
{
|
||||
public const string UploadPercentage = "Current file upload percentage";
|
||||
public const string UploadSpeed = "Current file upload speed (KB/s)";
|
||||
}
|
||||
|
||||
public enum FileState
|
||||
{
|
||||
Syncing,
|
||||
Synced,
|
||||
Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Newtonsoft.Json;
|
||||
using onedrive_backup.Contracts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace hassio_onedrive_backup.Hass
|
||||
{
|
||||
internal class HassOnedriveFreeSpaceEntityState : INotifyPropertyChanged
|
||||
{
|
||||
private const string OneDrive_FreeSpace_Entity_ID = "sensor.onedrivefreespace";
|
||||
private IHassioClient? _hassioClient;
|
||||
|
||||
public HassOnedriveFreeSpaceEntityState(IHassioClient hassioClient)
|
||||
{
|
||||
_hassioClient = hassioClient;
|
||||
}
|
||||
|
||||
public double? FreeSpaceGB { get; private set; }
|
||||
public double? TotalSpaceGB { get; private set; }
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public async Task UpdateOneDriveFreespaceSensorInHass(OneDriveFreeSpaceData freeSpaceData)
|
||||
{
|
||||
FreeSpaceGB = freeSpaceData.FreeSpace;
|
||||
TotalSpaceGB = freeSpaceData.TotalSpace;
|
||||
|
||||
var payload = new
|
||||
{
|
||||
state = FreeSpaceGB == null ? "-" : FreeSpaceGB.Value.ToString("0.00"),
|
||||
attributes = new Dictionary<string, string>
|
||||
{
|
||||
{ "unit_of_measurement", "GB" }
|
||||
}
|
||||
};
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
|
||||
string payloadStr = JsonConvert.SerializeObject(payload);
|
||||
await _hassioClient.UpdateHassEntityStateAsync(OneDrive_FreeSpace_Entity_ID, payloadStr);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
using hassio_onedrive_backup.Contracts;
|
||||
using hassio_onedrive_backup.Hass.Events;
|
||||
using hassio_onedrive_backup.Storage;
|
||||
using Newtonsoft.Json;
|
||||
using onedrive_backup.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using static hassio_onedrive_backup.Contracts.HassAddonsResponse;
|
||||
using static hassio_onedrive_backup.Contracts.HassBackupsResponse;
|
||||
|
||||
namespace hassio_onedrive_backup.Hass
|
||||
{
|
||||
internal class HassioClient : IHassioClient
|
||||
{
|
||||
private const string Supervisor_Base_Uri_Str = "http://supervisor";
|
||||
private const string Hass_Base_Uri_Str = "http://supervisor/core/api";
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public HassioClient(string token, int hassioTimeout)
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
_httpClient.Timeout = TimeSpan.FromMinutes(hassioTimeout);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteBackupAsync(Backup backup)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(Supervisor_Base_Uri_Str + $"/backups/{backup.Slug}");
|
||||
await _httpClient.DeleteAsync(uri);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogError($"Error deleting backup {backup.Slug}. {ex}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<List<Backup>> GetBackupsAsync(Predicate<Backup> filter)
|
||||
{
|
||||
Uri uri = new Uri(Supervisor_Base_Uri_Str + "/backups");
|
||||
var response = await GetJsonResponseAsync<HassBackupsResponse>(uri);
|
||||
if (response.Result.Equals("ok", StringComparison.OrdinalIgnoreCase) == false)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed getting Backups from Supervisor. Result: {response.Result}");
|
||||
}
|
||||
|
||||
var backups = response.DataProperty.Backups;
|
||||
|
||||
return filter != null ? backups.Where(backup => filter(backup)).ToList() : backups.ToList();
|
||||
}
|
||||
|
||||
public async Task<bool> CreateBackupAsync(string backupName, bool appendTimestamp = true, bool compressed = true, string? password = null, IEnumerable<string>? folders = null, IEnumerable<string>? addons = null)
|
||||
{
|
||||
DateTime timeStamp = DateTimeHelper.Instance!.Now;
|
||||
const string dt_format = "yyyy-MM-dd-HH-mm";
|
||||
|
||||
string? payloadStr;
|
||||
Uri? uri;
|
||||
|
||||
string finalBackupName = appendTimestamp ? $"{backupName}_{timeStamp.ToString(dt_format, CultureInfo.CurrentCulture)}" : backupName;
|
||||
|
||||
// Full Backup
|
||||
if (folders == null && addons == null)
|
||||
{
|
||||
uri = new Uri(Supervisor_Base_Uri_Str + "/backups/new/full");
|
||||
var fullPayload = new
|
||||
{
|
||||
name = finalBackupName,
|
||||
compressed = compressed,
|
||||
password = password
|
||||
};
|
||||
|
||||
payloadStr = JsonConvert.SerializeObject(fullPayload, new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
});
|
||||
|
||||
ConsoleLogger.LogInfo("Starting full local backup");
|
||||
}
|
||||
// Partial Backup
|
||||
else
|
||||
{
|
||||
uri = new Uri(Supervisor_Base_Uri_Str + "/backups/new/partial");
|
||||
var partialPayload = new
|
||||
{
|
||||
name = finalBackupName,
|
||||
compressed = compressed,
|
||||
password = password,
|
||||
homeassistant = true,
|
||||
addons = addons,
|
||||
folders = folders
|
||||
};
|
||||
|
||||
payloadStr = JsonConvert.SerializeObject(partialPayload, new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
});
|
||||
|
||||
ConsoleLogger.LogInfo("Starting partial local backup");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _httpClient.PostAsync(uri, new StringContent(payloadStr, Encoding.UTF8, "application/json"));
|
||||
ConsoleLogger.LogInfo("Backup complete");
|
||||
}
|
||||
catch (TaskCanceledException tce)
|
||||
{
|
||||
if (tce.InnerException is TimeoutException)
|
||||
{
|
||||
ConsoleLogger.LogError($"Backup request timed out. {tce}");
|
||||
ConsoleLogger.LogError($"Increase the timeout value in configuration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogError($"Failed creating new backup. {ex}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> UploadBackupAsync(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(Supervisor_Base_Uri_Str + "/backups/new/upload");
|
||||
using var multiPartFormContent = new MultipartFormDataContent();
|
||||
var fsContent = new StreamContent(System.IO.File.OpenRead(filePath));
|
||||
multiPartFormContent.Add(fsContent, name: "file", fileName: filePath);
|
||||
var response = await _httpClient.PostAsync(uri, multiPartFormContent);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogError($"Error uploading backup to Home Assistant. {ex}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task SendPersistentNotificationAsync(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(Hass_Base_Uri_Str + "/services/notify/persistent_notification");
|
||||
var payload = new
|
||||
{
|
||||
message = message,
|
||||
title = "hassio-onedrive-backup"
|
||||
};
|
||||
|
||||
string payloadStr = JsonConvert.SerializeObject(payload);
|
||||
await _httpClient.PostAsync(uri, new StringContent(payloadStr, Encoding.UTF8, "application/json"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConsoleLogger.LogError($"Failed sending persistent notification. {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<Addon>> GetAddonsAsync()
|
||||
{
|
||||
Uri uri = new Uri(Supervisor_Base_Uri_Str + "/addons");
|
||||
var response = await GetJsonResponseAsync<HassAddonsResponse>(uri);
|
||||
var ret = response.DataProperty.Addons.ToList();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task UpdateHassEntityStateAsync(string entityId, string payload)
|
||||
{
|
||||
Uri uri = new Uri(Hass_Base_Uri_Str + $"/states/{entityId}");
|
||||
await _httpClient.PostAsync(uri, new StringContent(payload, Encoding.UTF8, "application/json"));
|
||||
}
|
||||
|
||||
public async Task PublishEventAsync(OneDriveEvents eventType, string payload = "")
|
||||
{
|
||||
Uri uri = new Uri(Hass_Base_Uri_Str + $"/events/onedrive.{eventType}");
|
||||
await _httpClient.PostAsync(uri, new StringContent(payload, Encoding.UTF8, "application/json"));
|
||||
}
|
||||
|
||||
public async Task<string> DownloadBackupAsync(string backupSlug)
|
||||
{
|
||||
ConsoleLogger.LogInfo($"Fetching Local Backup (Slug:{backupSlug})");
|
||||
Uri uri = new Uri(Supervisor_Base_Uri_Str + $"/backups/{backupSlug}/download");
|
||||
var fileInfo = new FileInfo($"{LocalStorage.TempFolder}/{backupSlug}.tar");
|
||||
await using var memStream = await _httpClient.GetStreamAsync(uri);
|
||||
using var fileStream = System.IO.File.Create(fileInfo.FullName);
|
||||
await memStream.CopyToAsync(fileStream);
|
||||
ConsoleLogger.LogInfo($"Backup ({backupSlug}) fetched successfully");
|
||||
return fileInfo.FullName;
|
||||
}
|
||||
|
||||
public async Task<string> GetTimeZoneAsync()
|
||||
{
|
||||
Uri uri = new Uri(Supervisor_Base_Uri_Str + "/supervisor/info");
|
||||
var response = await GetJsonResponseAsync<HassSupervisorInfoResponse>(uri);
|
||||
var ret = response.DataProperty.Timezone;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private async Task<T> GetJsonResponseAsync<T>(Uri uri)
|
||||
{
|
||||
string response = await _httpClient.GetStringAsync(uri);
|
||||
T ret = JsonConvert.DeserializeObject<T>(response)!;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task<HassAddonInfoResponse> GetAddonInfo(string slug)
|
||||
{
|
||||
Uri uri = new Uri(Supervisor_Base_Uri_Str + $"/addons/{slug}/info");
|
||||
var response = await GetJsonResponseAsync<HassAddonInfoResponse>(uri);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using hassio_onedrive_backup.Contracts;
|
||||
using hassio_onedrive_backup.Hass.Events;
|
||||
using Newtonsoft.Json;
|
||||
using onedrive_backup.Contracts;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using static hassio_onedrive_backup.Contracts.HassAddonsResponse;
|
||||
using static hassio_onedrive_backup.Contracts.HassBackupsResponse;
|
||||
|
||||
namespace hassio_onedrive_backup.Hass
|
||||
{
|
||||
internal class HassioClientMock : IHassioClient
|
||||
{
|
||||
private List<Backup> _backups = new List<Backup>
|
||||
{
|
||||
new Backup
|
||||
{
|
||||
Compressed = true,
|
||||
Date = DateTime.Now.AddDays(-10),
|
||||
Name = "Mock1",
|
||||
Protected = false,
|
||||
Size = 1,
|
||||
Slug = "Mock1",
|
||||
Type = "partial",
|
||||
Content = new Content
|
||||
{
|
||||
Addons = new string[]
|
||||
{
|
||||
"Addon1", "Addon2"
|
||||
}
|
||||
}
|
||||
},
|
||||
new Backup
|
||||
{
|
||||
Compressed = true,
|
||||
Date = DateTime.Now.AddDays(-1),
|
||||
Name = "Mock2",
|
||||
Protected = false,
|
||||
Size = 2,
|
||||
Slug = "Mock2",
|
||||
Type = "full",
|
||||
Content = new Content
|
||||
{
|
||||
Addons = new string[]
|
||||
{
|
||||
"Addon1", "Addon2"
|
||||
},
|
||||
Folders = new string[]
|
||||
{
|
||||
"Folder1"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public Task<bool> CreateBackupAsync(string backupName, bool appendTimestamp = true, bool compressed = true, string? password = null, IEnumerable<string>? folders = null, IEnumerable<string>? addons = null)
|
||||
{
|
||||
DateTime timeStamp = DateTimeHelper.Instance!.Now;
|
||||
string finalBackupName = appendTimestamp ? $"{backupName}_{timeStamp.ToString("yyyy-MM-dd-HH-mm")}" : backupName;
|
||||
var backup = new Backup
|
||||
{
|
||||
Compressed = compressed,
|
||||
Name = finalBackupName,
|
||||
Protected = !string.IsNullOrEmpty(password),
|
||||
Type = folders == null && addons == null ? "full" : "partial",
|
||||
Date = DateTime.Now,
|
||||
Slug = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
_backups.Add(backup);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<bool> DeleteBackupAsync(Backup backup)
|
||||
{
|
||||
var backupToRemove = _backups.SingleOrDefault(bck => bck.Slug.Equals(backup.Slug, StringComparison.OrdinalIgnoreCase));
|
||||
if (backupToRemove != null)
|
||||
{
|
||||
_backups.Remove(backupToRemove);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
public Task<string> DownloadBackupAsync(string backupSlug)
|
||||
{
|
||||
string backupFile = $"./{backupSlug}.tar";
|
||||
|
||||
// 15MB File
|
||||
byte[] data = new byte[100 * 1024 * 1024];
|
||||
new Random().NextBytes(data);
|
||||
File.WriteAllBytes(backupFile, data);
|
||||
return Task.FromResult(backupFile);
|
||||
}
|
||||
|
||||
public Task<HassAddonInfoResponse> GetAddonInfo(string slug)
|
||||
{
|
||||
return Task.FromResult(new HassAddonInfoResponse
|
||||
{
|
||||
DataProperty = new HassAddonInfoResponse.Data
|
||||
{
|
||||
IngressEntry = "/",
|
||||
IngressUrl = "/"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Task<List<HassAddonsResponse.Addon>> GetAddonsAsync()
|
||||
{
|
||||
return Task.FromResult(new List<HassAddonsResponse.Addon>()
|
||||
{
|
||||
new HassAddonsResponse.Addon{ Slug = "Addon1", Name = "First Addon" },
|
||||
new HassAddonsResponse.Addon { Slug = "Addon2", Name = "Second Addon" }
|
||||
});
|
||||
}
|
||||
|
||||
public Task<List<Backup>> GetBackupsAsync(Predicate<Backup> filter)
|
||||
{
|
||||
return Task.FromResult(_backups);
|
||||
}
|
||||
|
||||
public Task<string> GetTimeZoneAsync()
|
||||
{
|
||||
return Task.FromResult("Local");
|
||||
}
|
||||
|
||||
public Task PublishEventAsync(OneDriveEvents eventType, string payload = "")
|
||||
{
|
||||
Debug.WriteLine($"EventType: {eventType}. Payload: {payload}");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SendPersistentNotificationAsync(string message)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
message = message,
|
||||
title = "hassio-onedrive-backup"
|
||||
};
|
||||
|
||||
string payloadStr = JsonConvert.SerializeObject(payload);
|
||||
Debug.WriteLine(payloadStr);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task UpdateHassEntityStateAsync(string entityId, string payload)
|
||||
{
|
||||
Debug.WriteLine($"EntityId: {entityId}. State: {payload}");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<bool> UploadBackupAsync(string filePath)
|
||||
{
|
||||
_backups.Add(new Backup
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(filePath),
|
||||
Slug = Guid.NewGuid().ToString()
|
||||
});
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using hassio_onedrive_backup.Hass.Events;
|
||||
using onedrive_backup.Contracts;
|
||||
using static hassio_onedrive_backup.Contracts.HassAddonsResponse;
|
||||
using static hassio_onedrive_backup.Contracts.HassBackupsResponse;
|
||||
|
||||
namespace hassio_onedrive_backup.Hass
|
||||
{
|
||||
public interface IHassioClient
|
||||
{
|
||||
Task<List<Backup>> GetBackupsAsync(Predicate<Backup> filter);
|
||||
|
||||
Task SendPersistentNotificationAsync(string message);
|
||||
|
||||
Task<bool> CreateBackupAsync(string backupName, bool appendTimestamp = true, bool compressed = true, string? password = null, IEnumerable<string>? folders = null, IEnumerable<string>? addons = null);
|
||||
|
||||
Task<bool> DeleteBackupAsync(Backup backup);
|
||||
|
||||
Task UpdateHassEntityStateAsync(string entityId, string payload);
|
||||
|
||||
Task<string> DownloadBackupAsync(string backupSlug);
|
||||
|
||||
Task<bool> UploadBackupAsync(string filePath);
|
||||
|
||||
Task<List<Addon>> GetAddonsAsync();
|
||||
|
||||
Task<HassAddonInfoResponse> GetAddonInfo(string slug);
|
||||
|
||||
Task<string> GetTimeZoneAsync();
|
||||
|
||||
Task PublishEventAsync(OneDriveEvents eventType, string payload = "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user