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 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> GetBackupsAsync(Predicate filter) { Uri uri = new Uri(Supervisor_Base_Uri_Str + "/backups"); var response = await GetJsonResponseAsync(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 CreateBackupAsync(string backupName, bool appendTimestamp = true, bool compressed = true, string? password = null, IEnumerable? folders = null, IEnumerable? 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 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> GetAddonsAsync() { Uri uri = new Uri(Supervisor_Base_Uri_Str + "/addons"); var response = await GetJsonResponseAsync(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 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 GetTimeZoneAsync() { Uri uri = new Uri(Supervisor_Base_Uri_Str + "/supervisor/info"); var response = await GetJsonResponseAsync(uri); var ret = response.DataProperty.Timezone; return ret; } private async Task GetJsonResponseAsync(Uri uri) { string response = await _httpClient.GetStringAsync(uri); T ret = JsonConvert.DeserializeObject(response)!; return ret; } public async Task GetAddonInfo(string slug) { Uri uri = new Uri(Supervisor_Base_Uri_Str + $"/addons/{slug}/info"); var response = await GetJsonResponseAsync(uri); return response; } } }