"yellow",
+ _ => "white",
+ })">
@issue.IssueMessage
@if (issue.PossibleSolutions.Length > 0)
{
diff --git a/TeslaSolarCharger/Server/Contracts/IBaseConfigurationService.cs b/TeslaSolarCharger/Server/Contracts/IBaseConfigurationService.cs
index 9de799882..4b82c9367 100644
--- a/TeslaSolarCharger/Server/Contracts/IBaseConfigurationService.cs
+++ b/TeslaSolarCharger/Server/Contracts/IBaseConfigurationService.cs
@@ -1,4 +1,5 @@
-using TeslaSolarCharger.Shared.Dtos.BaseConfiguration;
+using Microsoft.AspNetCore.Mvc;
+using TeslaSolarCharger.Shared.Dtos.BaseConfiguration;
namespace TeslaSolarCharger.Server.Contracts;
@@ -7,4 +8,5 @@ public interface IBaseConfigurationService
Task UpdateBaseConfigurationAsync(DtoBaseConfiguration baseConfiguration);
Task UpdateMaxCombinedCurrent(int? maxCombinedCurrent);
void UpdatePowerBuffer(int powerBuffer);
+ Task
DownloadBackup();
}
diff --git a/TeslaSolarCharger/Server/Controllers/BaseConfigurationController.cs b/TeslaSolarCharger/Server/Controllers/BaseConfigurationController.cs
index 7ae92209b..239f5a0e3 100644
--- a/TeslaSolarCharger/Server/Controllers/BaseConfigurationController.cs
+++ b/TeslaSolarCharger/Server/Controllers/BaseConfigurationController.cs
@@ -31,5 +31,12 @@ public void UpdateMaxCombinedCurrent(int? maxCombinedCurrent) =>
[HttpGet]
public void UpdatePowerBuffer(int powerBuffer) =>
_service.UpdatePowerBuffer(powerBuffer);
+
+ [HttpGet]
+ public async Task DownloadBackup()
+ {
+ var bytes = await _service.DownloadBackup().ConfigureAwait(false);
+ return File(bytes, "application/zip", "TSCBackup.zip");
+ }
}
}
diff --git a/TeslaSolarCharger/Server/Resources/PossibleIssues/IssueKeys.cs b/TeslaSolarCharger/Server/Resources/PossibleIssues/IssueKeys.cs
index e94054d7b..fbbe6ac69 100644
--- a/TeslaSolarCharger/Server/Resources/PossibleIssues/IssueKeys.cs
+++ b/TeslaSolarCharger/Server/Resources/PossibleIssues/IssueKeys.cs
@@ -19,4 +19,5 @@ public class IssueKeys
public string VersionNotUpToDate = "VersionNotUpToDate";
public string CorrectionFactorZero = "CorrectionFactorZero";
public string ServerTimeZoneDifferentFromClient = "ServerTimeZoneDifferentFromClient";
+ public string NewTeslaApi = "NewTeslaApi";
}
diff --git a/TeslaSolarCharger/Server/Resources/PossibleIssues/PossibleIssues.cs b/TeslaSolarCharger/Server/Resources/PossibleIssues/PossibleIssues.cs
index 8e5eca864..a25fdb6f0 100644
--- a/TeslaSolarCharger/Server/Resources/PossibleIssues/PossibleIssues.cs
+++ b/TeslaSolarCharger/Server/Resources/PossibleIssues/PossibleIssues.cs
@@ -134,6 +134,13 @@ public PossibleIssues(IssueKeys issueKeys)
"Update the TimeZone of the TeslaSolarChargerContainer in your docker-compose.yml."
)
},
+ {
+ issueKeys.NewTeslaApi, CreateIssue("New cars are currently not supported",
+ IssueType.Information,
+ "If there are any updates on this topic, I will post them here.",
+ "Sorry for this information beeing not removable - it will be possible in a future update."
+ )
+ },
};
}
diff --git a/TeslaSolarCharger/Server/Services/BaseConfigurationService.cs b/TeslaSolarCharger/Server/Services/BaseConfigurationService.cs
index e6f720226..27178b6a9 100644
--- a/TeslaSolarCharger/Server/Services/BaseConfigurationService.cs
+++ b/TeslaSolarCharger/Server/Services/BaseConfigurationService.cs
@@ -1,9 +1,16 @@
-using TeslaSolarCharger.Server.Contracts;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Data.Sqlite;
+using Microsoft.Net.Http.Headers;
+using System.IO.Compression;
+using System.Net;
+using TeslaSolarCharger.Model.Contracts;
+using TeslaSolarCharger.Server.Contracts;
using TeslaSolarCharger.Server.Scheduling;
using TeslaSolarCharger.Shared.Contracts;
using TeslaSolarCharger.Shared.Dtos.BaseConfiguration;
using TeslaSolarCharger.Shared.Dtos.Contracts;
using TeslaSolarCharger.Shared.Enums;
+using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
namespace TeslaSolarCharger.Server.Services;
@@ -16,10 +23,11 @@ public class BaseConfigurationService : IBaseConfigurationService
private readonly ISolarMqttService _solarMqttService;
private readonly ISettings _settings;
private readonly IPvValueService _pvValueService;
+ private readonly IDbConnectionStringHelper _dbConnectionStringHelper;
public BaseConfigurationService(ILogger logger, IConfigurationWrapper configurationWrapper,
JobManager jobManager, ITeslaMateMqttService teslaMateMqttService, ISolarMqttService solarMqttService,
- ISettings settings, IPvValueService pvValueService)
+ ISettings settings, IPvValueService pvValueService, IDbConnectionStringHelper dbConnectionStringHelper)
{
_logger = logger;
_configurationWrapper = configurationWrapper;
@@ -28,6 +36,7 @@ public BaseConfigurationService(ILogger logger, IConfi
_solarMqttService = solarMqttService;
_settings = settings;
_pvValueService = pvValueService;
+ _dbConnectionStringHelper = dbConnectionStringHelper;
}
public async Task UpdateBaseConfigurationAsync(DtoBaseConfiguration baseConfiguration)
@@ -69,4 +78,59 @@ public void UpdatePowerBuffer(int powerBuffer)
{
_settings.PowerBuffer = powerBuffer;
}
+
+ public async Task DownloadBackup()
+ {
+ try
+ {
+ await _jobManager.StopJobs().ConfigureAwait(false);
+
+ var backupCopyDestinationDirectory = _configurationWrapper.BackupCopyDestinationDirectory();
+ if (Directory.Exists(backupCopyDestinationDirectory))
+ {
+ Directory.Delete(backupCopyDestinationDirectory, true);
+ }
+ Directory.CreateDirectory(backupCopyDestinationDirectory);
+
+ //Backup Sqlite database
+ using (var source = new SqliteConnection(_dbConnectionStringHelper.GetTeslaSolarChargerDbPath()))
+ using (var destination = new SqliteConnection(string.Format($"Data Source={Path.Combine(backupCopyDestinationDirectory, _configurationWrapper.GetSqliteFileNameWithoutPath())};Pooling=False")))
+ {
+ source.Open();
+ destination.Open();
+ source.BackupDatabase(destination, "main", "main");
+ }
+
+ //Backup config files
+ var baseConfigFileFullName = _configurationWrapper.BaseConfigFileFullName();
+ var carConfigFileFullName = _configurationWrapper.CarConfigFileFullName();
+ File.Copy(baseConfigFileFullName, Path.Combine(backupCopyDestinationDirectory, Path.GetFileName(baseConfigFileFullName)), true);
+ File.Copy(carConfigFileFullName, Path.Combine(backupCopyDestinationDirectory, Path.GetFileName(carConfigFileFullName)), true);
+
+
+ var backupFileName = "TSC-Backup.zip";
+ var backupZipDirectory = _configurationWrapper.BackupZipDirectory();
+ if(Directory.Exists(backupZipDirectory))
+ {
+ Directory.Delete(backupZipDirectory, true);
+ }
+ Directory.CreateDirectory(backupZipDirectory);
+ var destinationArchiveFileName = Path.Combine(backupZipDirectory, backupFileName);
+ ZipFile.CreateFromDirectory(backupCopyDestinationDirectory, destinationArchiveFileName);
+ var bytes = await File.ReadAllBytesAsync(destinationArchiveFileName).ConfigureAwait(false);
+ return bytes;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Couldn't download backup");
+ throw;
+ }
+ finally
+ {
+ await _jobManager.StartJobs().ConfigureAwait(false);
+ }
+
+
+
+ }
}
diff --git a/TeslaSolarCharger/Server/Services/IssueValidationService.cs b/TeslaSolarCharger/Server/Services/IssueValidationService.cs
index a8cf7b99d..1d6c5eee8 100644
--- a/TeslaSolarCharger/Server/Services/IssueValidationService.cs
+++ b/TeslaSolarCharger/Server/Services/IssueValidationService.cs
@@ -43,7 +43,10 @@ public IssueValidationService(ILogger logger, ISettings
public async Task> RefreshIssues(TimeSpan clientTimeZoneId)
{
_logger.LogTrace("{method}()", nameof(RefreshIssues));
- var issueList = new List();
+ var issueList = new List
+ {
+ _possibleIssues.GetIssueByKey(_issueKeys.NewTeslaApi),
+ };
issueList.AddRange(GetServerConfigurationIssues(clientTimeZoneId));
if (Debugger.IsAttached)
{
diff --git a/TeslaSolarCharger/Server/appsettings.json b/TeslaSolarCharger/Server/appsettings.json
index 58de41eb7..e94baeeba 100644
--- a/TeslaSolarCharger/Server/appsettings.json
+++ b/TeslaSolarCharger/Server/appsettings.json
@@ -40,6 +40,8 @@
},
"AllowedHosts": "*",
"ConfigFileLocation": "configs",
+ "BackupCopyDestinationDirectory": "backups",
+ "BackupZipDirectory": "backupZips",
"CarConfigFilename": "carConfig.json",
"BaseConfigFileName": "baseConfig.json",
"SqliteFileName": "TeslaSolarCharger.db",
diff --git a/TeslaSolarCharger/Shared/Contracts/IConfigurationWrapper.cs b/TeslaSolarCharger/Shared/Contracts/IConfigurationWrapper.cs
index 4e56e570f..67e3897d5 100644
--- a/TeslaSolarCharger/Shared/Contracts/IConfigurationWrapper.cs
+++ b/TeslaSolarCharger/Shared/Contracts/IConfigurationWrapper.cs
@@ -83,4 +83,7 @@ public interface IConfigurationWrapper
bool AllowCors();
bool ShouldDisplayApiRequestCounter();
bool ShouldIgnoreSslErrors();
+ string BackupCopyDestinationDirectory();
+ string GetSqliteFileNameWithoutPath();
+ string BackupZipDirectory();
}
diff --git a/TeslaSolarCharger/Shared/Wrappers/ConfigurationWrapper.cs b/TeslaSolarCharger/Shared/Wrappers/ConfigurationWrapper.cs
index a27544105..491e5bd77 100644
--- a/TeslaSolarCharger/Shared/Wrappers/ConfigurationWrapper.cs
+++ b/TeslaSolarCharger/Shared/Wrappers/ConfigurationWrapper.cs
@@ -40,13 +40,37 @@ public string CarConfigFileFullName()
return Path.Combine(configFileDirectory, value);
}
+ public string BackupCopyDestinationDirectory()
+ {
+ var configFileDirectory = ConfigFileDirectory();
+ var environmentVariableName = "BackupCopyDestinationDirectory";
+ var value = GetNotNullableConfigurationValue(environmentVariableName);
+ _logger.LogTrace("Config value extracted: [{key}]: {value}", environmentVariableName, value);
+ return Path.Combine(configFileDirectory, value);
+ }
+
+ public string BackupZipDirectory()
+ {
+ var configFileDirectory = ConfigFileDirectory();
+ var environmentVariableName = "BackupZipDirectory";
+ var value = GetNotNullableConfigurationValue(environmentVariableName);
+ _logger.LogTrace("Config value extracted: [{key}]: {value}", environmentVariableName, value);
+ return Path.Combine(configFileDirectory, value);
+ }
+
public string SqliteFileFullName()
{
var configFileDirectory = ConfigFileDirectory();
+ var value = GetSqliteFileNameWithoutPath();
+ return Path.Combine(configFileDirectory, value);
+ }
+
+ public string GetSqliteFileNameWithoutPath()
+ {
var environmentVariableName = "SqliteFileName";
var value = GetNotNullableConfigurationValue(environmentVariableName);
_logger.LogTrace("Config value extracted: [{key}]: {value}", environmentVariableName, value);
- return Path.Combine(configFileDirectory, value);
+ return value;
}
public string BaseConfigFileFullName()