From 8c8c5f332bca3778e796062e891cf06b48ddda31 Mon Sep 17 00:00:00 2001 From: lavinir <56975945+lavinir@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:55:49 +0200 Subject: [PATCH] 2.3.1 (#193) --- onedrive-backup/CHANGELOG.md | 9 ++++ onedrive-backup/Contracts/AddonOptions.cs | 2 +- .../Contracts/PersistantNotificationIds.cs | 15 ++++++ onedrive-backup/Graph/GraphHelper.cs | 51 +++++++++++++++---- onedrive-backup/Graph/IGraphHelper.cs | 2 +- onedrive-backup/Hass/BackupManager.cs | 31 ++++++----- onedrive-backup/Hass/HassioClient.cs | 13 +++-- onedrive-backup/Hass/HassioClientMock.cs | 4 +- onedrive-backup/Hass/IHassioClient.cs | 2 +- onedrive-backup/Orchestrator.cs | 7 ++- onedrive-backup/Pages/Settings.razor | 1 - onedrive-backup/Pages/_Host.cshtml | 14 ++++- onedrive-backup/Shared/BackupCard.razor | 4 +- onedrive-backup/Shared/MainLayout.razor | 9 +--- .../Shared/OneDriveSummaryCard.razor | 4 +- onedrive-backup/Storage/LocalStorage.cs | 3 +- onedrive-backup/config.yaml | 3 +- .../wwwroot/scripts/BlazorHelpers.js | 17 ------- test.onedrive-backup/BackupManagerTests.cs | 3 +- .../Mocks/BackupManagerMock.cs | 3 +- 20 files changed, 123 insertions(+), 74 deletions(-) create mode 100644 onedrive-backup/Contracts/PersistantNotificationIds.cs diff --git a/onedrive-backup/CHANGELOG.md b/onedrive-backup/CHANGELOG.md index 013ca73..0d63571 100644 --- a/onedrive-backup/CHANGELOG.md +++ b/onedrive-backup/CHANGELOG.md @@ -1,3 +1,12 @@ +## v2.3.1 [March 19th 2024] +### ❗Important +Upgrade to Version 2.3 included updates to authentication libraries which caused some connection resets with OneDrive. Please make sure that you have a working connection post upgrade. For troubleshooting please refer to [this link]("https://github.com/lavinir/hassio-onedrive-backup/issues/174") +### 🆕 Added +* Added more persistent notifications for alerting on errors and improved overall behavior of persistant notifications +### 🐞 Fixed +* Issue downloading a backup from OneDrive to HomeAssistant +* Minor UI fixes + ## v2.3 [Febuary 7th 2024] ### 🆕 Added * Added ability to retain backups indefinitely diff --git a/onedrive-backup/Contracts/AddonOptions.cs b/onedrive-backup/Contracts/AddonOptions.cs index f1caefc..dbac4f8 100644 --- a/onedrive-backup/Contracts/AddonOptions.cs +++ b/onedrive-backup/Contracts/AddonOptions.cs @@ -9,7 +9,7 @@ namespace hassio_onedrive_backup.Contracts { public class AddonOptions : IEqualityComparer { - public const string AddonVersion = "2.3"; + public const string AddonVersion = "2.3.1"; public event Action OnOptionsChanged; diff --git a/onedrive-backup/Contracts/PersistantNotificationIds.cs b/onedrive-backup/Contracts/PersistantNotificationIds.cs new file mode 100644 index 0000000..6bdf9f8 --- /dev/null +++ b/onedrive-backup/Contracts/PersistantNotificationIds.cs @@ -0,0 +1,15 @@ +namespace onedrive_backup.Contracts +{ + public static class PersistantNotificationIds + { + public static string OneDriveDelete = nameof(OneDriveDelete); + + public static string LocalDelete = nameof(LocalDelete); + + public static string BackupCreate = nameof(BackupCreate); + + public static string BackupUpload = nameof(BackupUpload); + + public static string Unexpected = nameof(Unexpected); + } +} diff --git a/onedrive-backup/Graph/GraphHelper.cs b/onedrive-backup/Graph/GraphHelper.cs index eac1352..fc59ce5 100644 --- a/onedrive-backup/Graph/GraphHelper.cs +++ b/onedrive-backup/Graph/GraphHelper.cs @@ -171,10 +171,10 @@ public async Task UploadFileAsync(string filePath, DateTime date, string? }); // todo: allow settings this in advanced configuration - int maxSlizeSize = ChunkSize; + int maxSliceSize = ChunkSize; long totalFileLength = fileStream.Length; - var fileUploadTask = new LargeFileUploadTask(uploadSession, fileStream, maxSlizeSize); - var lastShownPercentageHolder = new UploadProgressHolder(); + var fileUploadTask = new LargeFileUploadTask(uploadSession, fileStream, maxSliceSize); + var lastShownPercentageHolder = new TransferProgressHolder(); IProgress progress = new Progress(async prog => { (double delayMS, double speed) = transferSpeedHelper.MarkAndCalcThrottle(prog); @@ -257,31 +257,59 @@ public async Task GetFreeSpaceInGB() } } - public async Task DownloadFileAsync(string fileName, Action? progressCallback) + public async Task DownloadFileAsync(string fileName, TransferSpeedHelper transferSpeedHelper, Action? progressCallback) { var drive = await _userClient.Me.Drive.GetAsync(); - var itemStream = await _userClient.Drives[drive.Id].Special["approot"].WithUrl(fileName).Content.GetAsync(); + var driveItem = await _userClient.Me.Drive.GetAsync(); + var appFolder = await _userClient.Drives[driveItem.Id].Special["approot"].GetAsync(); + var item = await _userClient.Drives[driveItem?.Id] + .Items[appFolder.Id] + .ItemWithPath(fileName) + .GetAsync(); + + transferSpeedHelper.Start(); + var itemStream = await _userClient.Drives[driveItem?.Id] + .Items[appFolder.Id] + .ItemWithPath(fileName) + .Content + .GetAsync(); + + var fileInfo = new FileInfo($"{LocalStorage.TempFolder}/{fileName}"); using var fileStream = File.Create(fileInfo.FullName); long totalBytesDownloaded = 0; int attempt = 1; - while (totalBytesDownloaded < itemStream.Length) + int bytesRead = 1; + var lastShownPercentageHolder = new TransferProgressHolder(); + while (totalBytesDownloaded < item.Size && bytesRead > 0) { try { var buffer = new byte[ChunkSize]; - int bytesRead = await itemStream.ReadAsync(buffer, 0, ChunkSize); + bytesRead = await itemStream.ReadAsync(buffer, 0, ChunkSize); + await fileStream.WriteAsync(buffer, 0, bytesRead); totalBytesDownloaded += bytesRead; - progressCallback?.Invoke((int)(totalBytesDownloaded * 100 / itemStream.Length)); + (double delay, double speed) = transferSpeedHelper.MarkAndCalcThrottle(totalBytesDownloaded); + double percentage = Math.Round((totalBytesDownloaded / (double)item.Size), 2) * 100; + if (percentage - lastShownPercentageHolder.Percentage >= 10 || percentage == 100) + { + _logger.LogVerbose($"Downloaded {percentage}%"); + lastShownPercentageHolder.Percentage = percentage; + } + if (percentage - lastShownPercentageHolder.Percentage >= 5 || percentage == 100) + { + progressCallback?.Invoke((int)percentage, (int)speed); + } } catch (Exception ex) { if (attempt >= DownloadRetryCount) { _logger.LogError($"Failed downloading file {fileName}. {ex}", ex, _telemetryManager); - progressCallback?.Invoke(null); + progressCallback?.Invoke(0, 0); + transferSpeedHelper.Reset(); return null; } @@ -289,7 +317,8 @@ public async Task GetFreeSpaceInGB() } } - progressCallback?.Invoke(null); + progressCallback?.Invoke(0, 0); + transferSpeedHelper.Reset(); _logger.LogInfo($"{fileName} downloaded successfully"); return fileInfo.FullName; } @@ -358,7 +387,7 @@ private Task DeviceCodeBallBackPrompt(DeviceCodeInfo info, CancellationToken ct) return (url, code); } - private class UploadProgressHolder + private class TransferProgressHolder { public double Percentage { get; set; } = 0; } diff --git a/onedrive-backup/Graph/IGraphHelper.cs b/onedrive-backup/Graph/IGraphHelper.cs index 3be1869..4356487 100644 --- a/onedrive-backup/Graph/IGraphHelper.cs +++ b/onedrive-backup/Graph/IGraphHelper.cs @@ -24,7 +24,7 @@ public interface IGraphHelper Task UploadFileAsync(string filePath, DateTime date, string? instanceName, TransferSpeedHelper transferSpeedHelper, string? destinationFileName = null, Action? progressCallback = null, bool flatten = true, string description = null); - Task DownloadFileAsync(string fileName, Action? progressCallback); + Task DownloadFileAsync(string fileName, TransferSpeedHelper transferSpeedHelper, Action? progressCallback); Task GetFreeSpaceInGB(); } diff --git a/onedrive-backup/Hass/BackupManager.cs b/onedrive-backup/Hass/BackupManager.cs index 9d19364..272d2c2 100644 --- a/onedrive-backup/Hass/BackupManager.cs +++ b/onedrive-backup/Hass/BackupManager.cs @@ -22,7 +22,6 @@ public class BackupManager { private const int InstanceNameMaxLength = 20; private readonly HassOnedriveEntityState _hassEntityState; - private readonly TransferSpeedHelper? _transferSpeedHelper; private readonly HassContext _hassContext; private readonly ConsoleLogger _logger; private readonly IDateTimeProvider _dateTimeProvider; @@ -33,18 +32,18 @@ public class BackupManager private IGraphHelper _graphHelper; private IHassioClient _hassIoClient; private BitArray _allowedHours; - protected bool _isExecuting = false; + protected bool _isExecuting = false; + public List LocalBackups { get; private set; } public List OnlineBackups { get; private set; } - public BackupManager(IServiceProvider serviceProvider, TransferSpeedHelper? transferSpeedHelper) + public BackupManager(IServiceProvider serviceProvider) { _addonOptions = serviceProvider.GetService(); _graphHelper = serviceProvider.GetService(); _hassIoClient = serviceProvider.GetService(); _hassEntityState = serviceProvider.GetService(); - _transferSpeedHelper = transferSpeedHelper; _hassContext = serviceProvider.GetService(); _logger = serviceProvider.GetService(); _dateTimeProvider = serviceProvider.GetService(); @@ -185,7 +184,7 @@ public async Task PerformBackupsAsync() await _hassIoClient.PublishEventAsync(Events.OneDriveEvents.OneDriveBackupDeleteFailed); if (_addonOptions.NotifyOnError) { - await _hassIoClient.SendPersistentNotificationAsync("Failed deleting old backup from OneDrive. Check Addon logs for more details"); + await _hassIoClient.SendPersistentNotificationAsync("Failed deleting old backup from OneDrive. Check Addon logs for more details", PersistantNotificationIds.OneDriveDelete); } } } @@ -218,19 +217,19 @@ public async Task PerformBackupsAsync() if (localBackupsToRemove.Any()) { - //_hassEntityState.State = HassOnedriveEntityState.BackupState.Syncing; - //await _hassEntityState.UpdateBackupEntityInHass(); await _hassEntityState.SyncStart(); _logger.LogInfo($"Removing {numOfLocalBackupsToRemove} local backups"); foreach (var localBackup in localBackupsToRemove) { + _logger.LogVerbose($"Deleting local backup: {localBackup.Slug}"); bool deleteSuccess = await _hassIoClient.DeleteBackupAsync(localBackup); if (deleteSuccess == false) { + _logger.LogError($"Error removing local backup: {localBackup.Slug}"); await _hassIoClient.PublishEventAsync(Events.OneDriveEvents.LocalBackupDeleteFailed); if (_addonOptions.NotifyOnError) { - await _hassIoClient.SendPersistentNotificationAsync("Error Deleting Local Backup. Check Addon logs for more details"); + await _hassIoClient.SendPersistentNotificationAsync("Error Deleting Local Backup. Check Addon logs for more details", PersistantNotificationIds.LocalDelete); } } } @@ -239,6 +238,10 @@ public async Task PerformBackupsAsync() await _hassEntityState.SyncEnd(); await RefreshBackupsAndUpdateHassEntity(); + if (_hassEntityState.State == HassOnedriveEntityState.BackupState.Stale ) + { + + } } finally @@ -326,7 +329,7 @@ public async Task CreateLocalBackup() await _hassIoClient.PublishEventAsync(Events.OneDriveEvents.BackupCreateFailed); if (_addonOptions.NotifyOnError) { - await _hassIoClient.SendPersistentNotificationAsync("Failed creating local backup. Check Addon logs for more details"); + await _hassIoClient.SendPersistentNotificationAsync("Failed creating local backup. Check Addon logs for more details", PersistantNotificationIds.BackupCreate); } } else @@ -354,7 +357,7 @@ public async Task UploadLocalBackupToOneDrive(Backup backup, Action { if (updateHassEntityState) @@ -373,7 +376,7 @@ public async Task UploadLocalBackupToOneDrive(Backup backup, Action UploadLocalBackupToOneDrive(Backup backup, Action DownloadBackupFromOneDrive(OnedriveBackup onlineBackup, Action? progressCallback = null, bool updateHassEntityState = true) + public async Task DownloadBackupFromOneDrive(OnedriveBackup onlineBackup, Action? progressCallback = null, bool updateHassEntityState = true) { string? backupFile = null; try { _logger.LogInfo($"Downloading backup {onlineBackup.FileName}"); - backupFile = await _graphHelper.DownloadFileAsync(onlineBackup.FileName, async (prog) => + backupFile = await _graphHelper.DownloadFileAsync(onlineBackup.FileName, new TransferSpeedHelper(null), async (prog, speed) => { if (updateHassEntityState) { @@ -408,7 +411,7 @@ public async Task DownloadBackupFromOneDrive(OnedriveBackup onlineBackup, await _hassEntityState.UpdateBackupEntityInHass(); } - progressCallback?.Invoke(prog); + progressCallback?.Invoke(prog, speed); }); if (backupFile == null) diff --git a/onedrive-backup/Hass/HassioClient.cs b/onedrive-backup/Hass/HassioClient.cs index dc68e02..878d3a1 100644 --- a/onedrive-backup/Hass/HassioClient.cs +++ b/onedrive-backup/Hass/HassioClient.cs @@ -166,18 +166,23 @@ public async Task UploadBackupAsync(string filePath) return true; } - public async Task SendPersistentNotificationAsync(string message) + public async Task SendPersistentNotificationAsync(string message, string? notificationId = null) { try { - Uri uri = new Uri(Hass_Base_Uri_Str + "/services/notify/persistent_notification"); + Uri uri = new Uri(Hass_Base_Uri_Str + "/services/persistent_notification/create"); var payload = new { message = message, - title = "hassio-onedrive-backup" + title = "hassio-onedrive-backup", + notification_id = notificationId }; - string payloadStr = JsonConvert.SerializeObject(payload); + string payloadStr = JsonConvert.SerializeObject(payload, new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }); + await _httpClient.PostAsync(uri, new StringContent(payloadStr, Encoding.UTF8, "application/json")); } catch (Exception ex) diff --git a/onedrive-backup/Hass/HassioClientMock.cs b/onedrive-backup/Hass/HassioClientMock.cs index 1822937..63a1daf 100644 --- a/onedrive-backup/Hass/HassioClientMock.cs +++ b/onedrive-backup/Hass/HassioClientMock.cs @@ -57,7 +57,7 @@ public Task DownloadBackupAsync(string backupSlug) string backupFile = $"./{backupSlug}.tar"; // 15MB File - byte[] data = new byte[10 * 1024 * 1024]; + byte[] data = new byte[100 * 1024 * 1024]; new Random().NextBytes(data); File.WriteAllBytes(backupFile, data); return Task.FromResult(backupFile); @@ -105,7 +105,7 @@ public Task RestartSelf() return Task.CompletedTask; } - public Task SendPersistentNotificationAsync(string message) + public Task SendPersistentNotificationAsync(string message, string? notificationId = null) { var payload = new { diff --git a/onedrive-backup/Hass/IHassioClient.cs b/onedrive-backup/Hass/IHassioClient.cs index 5651261..57e37b0 100644 --- a/onedrive-backup/Hass/IHassioClient.cs +++ b/onedrive-backup/Hass/IHassioClient.cs @@ -10,7 +10,7 @@ public interface IHassioClient { Task> GetBackupsAsync(Predicate filter); - Task SendPersistentNotificationAsync(string message); + Task SendPersistentNotificationAsync(string message, string? notificationId = null); Task CreateBackupAsync(string backupName, DateTime timeStamp, bool appendTimestamp = true, bool compressed = true, string? password = null, IEnumerable? folders = null, IEnumerable? addons = null); diff --git a/onedrive-backup/Orchestrator.cs b/onedrive-backup/Orchestrator.cs index 6c94273..83629b0 100644 --- a/onedrive-backup/Orchestrator.cs +++ b/onedrive-backup/Orchestrator.cs @@ -3,6 +3,7 @@ using hassio_onedrive_backup.Hass; using hassio_onedrive_backup.Sync; using onedrive_backup; +using onedrive_backup.Contracts; using onedrive_backup.Graph; using onedrive_backup.Telemetry; using System.Collections; @@ -34,7 +35,7 @@ public Orchestrator(IServiceProvider serviceProvider) _telemetryManager = serviceProvider.GetService(); _logger = serviceProvider.GetService(); _allowedBackupHours = TimeRangeHelper.GetAllowedHours(_addonOptions.BackupAllowedHours); - BackupManager = new BackupManager(_serviceProvider, new TransferSpeedHelper(null)); + BackupManager = new BackupManager(_serviceProvider); _addonOptions.OnOptionsChanged += OnOptionsChanged; } @@ -94,6 +95,10 @@ public async Task Start() catch (Exception ex) { _logger.LogError($"Unexpected error. {ex}", ex, _telemetryManager); + if (_addonOptions.NotifyOnError) + { + await _hassIoClient.SendPersistentNotificationAsync("Unexpected Failure in OneDrive Backup Addon. Check Addon logs for more details", PersistantNotificationIds.Unexpected); + } } _logger.LogVerbose("Backup Interval Completed."); diff --git a/onedrive-backup/Pages/Settings.razor b/onedrive-backup/Pages/Settings.razor index 47ad674..22b66c9 100644 --- a/onedrive-backup/Pages/Settings.razor +++ b/onedrive-backup/Pages/Settings.razor @@ -264,7 +264,6 @@ { await JS.InvokeVoidAsync("addTooltips"); await JS.InvokeVoidAsync("addToasts"); - await JS.InvokeVoidAsync("hideSettingsPopover"); } if (string.IsNullOrWhiteSpace(newOptions.SyncPaths.LastOrDefault()) == false || newOptions.SyncPaths.Any() == false) diff --git a/onedrive-backup/Pages/_Host.cshtml b/onedrive-backup/Pages/_Host.cshtml index cf60f0a..4f625b7 100644 --- a/onedrive-backup/Pages/_Host.cshtml +++ b/onedrive-backup/Pages/_Host.cshtml @@ -30,11 +30,21 @@ @@ -94,7 +94,7 @@
@HassOnedriveEntityState.RetainedOneDriveBackups (Retained)
-
@HassOnedriveEntityState.BackupsInOnedrive / @AddonOptions.MaxOnedriveBackups (Total Size: @OneDriveBackupsSpace)
+
@HassOnedriveEntityState.BackupsInOnedrive / @AddonOptions.MaxOnedriveBackups (Total Size: @OneDriveBackupsSpace)
diff --git a/onedrive-backup/Storage/LocalStorage.cs b/onedrive-backup/Storage/LocalStorage.cs index 633c53b..3dcc087 100644 --- a/onedrive-backup/Storage/LocalStorage.cs +++ b/onedrive-backup/Storage/LocalStorage.cs @@ -85,8 +85,7 @@ public static bool CheckAndMarkFlag(Flag flag) public enum Flag { - NativeSettings, - ReleaseNotes_2_3, + ReleaseNotes_2_3_1, } } } diff --git a/onedrive-backup/config.yaml b/onedrive-backup/config.yaml index 19143ca..c1b7241 100644 --- a/onedrive-backup/config.yaml +++ b/onedrive-backup/config.yaml @@ -1,5 +1,5 @@ name: "OneDrive Backup" -version: "2.3" +version: "2.3.1" slug: hassio_onedrive_backup description: >- Automatic backups from Home Assistant to OneDrive @@ -14,7 +14,6 @@ map: - ssl:rw - addons:rw - media:rw - - homeassistant_config:rw - share:rw - backup:rw - all_addon_configs:rw diff --git a/onedrive-backup/wwwroot/scripts/BlazorHelpers.js b/onedrive-backup/wwwroot/scripts/BlazorHelpers.js index 90dffc3..535e95f 100644 --- a/onedrive-backup/wwwroot/scripts/BlazorHelpers.js +++ b/onedrive-backup/wwwroot/scripts/BlazorHelpers.js @@ -31,15 +31,6 @@ function showSaveToast() { saveToast.show(); } -function initSettingsPopover() { - settingsPopover = new bootstrap.Popover(document.querySelector('#settingsNav'), { - container: "body", - placement: "bottom", - content: "Configuration is now done in the Settings tab", - trigger: "manual" - }) -} - function showReleaseNotes() { var rnModal = new bootstrap.Modal(document.getElementById('rnotesModal'), { keyboard: false @@ -48,14 +39,6 @@ function showReleaseNotes() { rnModal.show(); } -function showSettingsPopover() { - settingsPopover.show(); -} - -function hideSettingsPopover() { - settingsPopover.hide(); -} - function copyToClipboard(txt) { navigator.clipboard.writeText(txt); } diff --git a/test.onedrive-backup/BackupManagerTests.cs b/test.onedrive-backup/BackupManagerTests.cs index ca08a5b..15ff6d4 100644 --- a/test.onedrive-backup/BackupManagerTests.cs +++ b/test.onedrive-backup/BackupManagerTests.cs @@ -63,8 +63,7 @@ public void Setup() _serviceProviderMock.Setup(provider => provider.GetService(typeof(ConsoleLogger))).Returns(consoleLogger); _backupManager = new BackupManagerMock( - _serviceProviderMock.Object, - _transferSpeedHelperMock.Object); + _serviceProviderMock.Object); } diff --git a/test.onedrive-backup/Mocks/BackupManagerMock.cs b/test.onedrive-backup/Mocks/BackupManagerMock.cs index 4ebe4cf..c1215cb 100644 --- a/test.onedrive-backup/Mocks/BackupManagerMock.cs +++ b/test.onedrive-backup/Mocks/BackupManagerMock.cs @@ -14,8 +14,7 @@ namespace test.onedrive_backup.Mocks public class BackupManagerMock : BackupManager { public BackupManagerMock( - IServiceProvider serviceProvider, - TransferSpeedHelper? transferSpeedHelper) : base(serviceProvider, transferSpeedHelper) + IServiceProvider serviceProvider) : base(serviceProvider) { }