diff --git a/Plugins.Modbus/Plugins.Modbus.csproj b/Plugins.Modbus/Plugins.Modbus.csproj
index d71e445e2..cdce2cb23 100644
--- a/Plugins.Modbus/Plugins.Modbus.csproj
+++ b/Plugins.Modbus/Plugins.Modbus.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj b/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj
index d05c6a9bd..1bf763b70 100644
--- a/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj
+++ b/Plugins.SmaEnergymeter/Plugins.SmaEnergymeter.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/Plugins.SolarEdge/Plugins.SolarEdge.csproj b/Plugins.SolarEdge/Plugins.SolarEdge.csproj
index f8c5617e0..a045cbfa5 100644
--- a/Plugins.SolarEdge/Plugins.SolarEdge.csproj
+++ b/Plugins.SolarEdge/Plugins.SolarEdge.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/Plugins.Solax/Plugins.Solax.csproj b/Plugins.Solax/Plugins.Solax.csproj
index 920a8d7f0..746969dbf 100644
--- a/Plugins.Solax/Plugins.Solax.csproj
+++ b/Plugins.Solax/Plugins.Solax.csproj
@@ -8,9 +8,9 @@
-
+
-
+
diff --git a/TeslaSolarCharger.GridPriceProvider/TeslaSolarCharger.GridPriceProvider.csproj b/TeslaSolarCharger.GridPriceProvider/TeslaSolarCharger.GridPriceProvider.csproj
index 555bf694c..3a8200064 100644
--- a/TeslaSolarCharger.GridPriceProvider/TeslaSolarCharger.GridPriceProvider.csproj
+++ b/TeslaSolarCharger.GridPriceProvider/TeslaSolarCharger.GridPriceProvider.csproj
@@ -9,12 +9,12 @@
-
+
-
+
diff --git a/TeslaSolarCharger.Tests/Services/Server/ChargeTimeCalculationService.cs b/TeslaSolarCharger.Tests/Services/Server/ChargeTimeCalculationService.cs
index 97d7671ed..08185524e 100644
--- a/TeslaSolarCharger.Tests/Services/Server/ChargeTimeCalculationService.cs
+++ b/TeslaSolarCharger.Tests/Services/Server/ChargeTimeCalculationService.cs
@@ -50,7 +50,6 @@ public void Calculates_Correct_Full_Speed_Charge_Durations(int minimumSoc, int?
};
var chargeTimeCalculationService = Mock.Create();
- Mock.Mock().Setup(c => c.MinimumSocDifference).Returns(2);
var chargeDuration = chargeTimeCalculationService.CalculateTimeToReachMinSocAtFullSpeedCharge(car);
var expectedTimeSpan = TimeSpan.FromSeconds(expectedTotalSeconds);
diff --git a/TeslaSolarCharger.Tests/Services/Server/TeslaFleetApiService.cs b/TeslaSolarCharger.Tests/Services/Server/TeslaFleetApiService.cs
new file mode 100644
index 000000000..eb2bebb35
--- /dev/null
+++ b/TeslaSolarCharger.Tests/Services/Server/TeslaFleetApiService.cs
@@ -0,0 +1,26 @@
+using Newtonsoft.Json;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+using TeslaSolarCharger.Server.Dtos.TeslaFleetApi;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace TeslaSolarCharger.Tests.Services.Server;
+
+[SuppressMessage("ReSharper", "UseConfigureAwaitFalse")]
+public class TeslaFleetApiService(ITestOutputHelper outputHelper) : TestBase(outputHelper)
+{
+ [Fact]
+ public async Task CanHandleUnsignedCommands()
+ {
+ var commandResult = JsonConvert.DeserializeObject>("{\"response\":{\"result\":false,\"reason\":\"unsigned_cmds_hardlocked\"}}");
+ Assert.NotNull(commandResult?.Response);
+ var fleetApiService = Mock.Create();
+ var fleetApiProxyNeeded = await fleetApiService.IsFleetApiProxyNeededInDatabase();
+ Assert.False(fleetApiProxyNeeded);
+ await fleetApiService.HandleUnsignedCommands(commandResult.Response);
+ fleetApiProxyNeeded = await fleetApiService.IsFleetApiProxyNeededInDatabase();
+ Assert.True(fleetApiProxyNeeded);
+
+ }
+}
diff --git a/TeslaSolarCharger.Tests/Services/Server/TeslaMateApiService.cs b/TeslaSolarCharger.Tests/Services/Server/TeslaMateApiService.cs
index a3ecfbd52..c8b94cf00 100644
--- a/TeslaSolarCharger.Tests/Services/Server/TeslaMateApiService.cs
+++ b/TeslaSolarCharger.Tests/Services/Server/TeslaMateApiService.cs
@@ -5,13 +5,8 @@
namespace TeslaSolarCharger.Tests.Services.Server;
-public class TeslaMateApiService : TestBase
+public class TeslaMateApiService(ITestOutputHelper outputHelper) : TestBase(outputHelper)
{
- public TeslaMateApiService(ITestOutputHelper outputHelper)
- : base(outputHelper)
- {
- }
-
[Theory]
[InlineData(18, null, null, false)]
[InlineData(18, null, 19, true)]
diff --git a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj
index 8d3f2c2ec..854dc3878 100644
--- a/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj
+++ b/TeslaSolarCharger.Tests/TeslaSolarCharger.Tests.csproj
@@ -18,7 +18,7 @@
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/TeslaSolarCharger.Tests/TestBase.cs b/TeslaSolarCharger.Tests/TestBase.cs
index d7898f2be..bd6746ad9 100644
--- a/TeslaSolarCharger.Tests/TestBase.cs
+++ b/TeslaSolarCharger.Tests/TestBase.cs
@@ -16,7 +16,9 @@
using TeslaSolarCharger.Server.MappingExtensions;
using TeslaSolarCharger.Shared.Contracts;
using TeslaSolarCharger.Shared.TimeProviding;
+using TeslaSolarCharger.SharedBackend.Contracts;
using Xunit.Abstractions;
+using Constants = TeslaSolarCharger.SharedBackend.Values.Constants;
namespace TeslaSolarCharger.Tests;
@@ -58,6 +60,7 @@ protected TestBase(
_fake = new AutoFake();
_fake.Provide();
+ _fake.Provide();
_fake.Provide(new FakeDateTimeProvider(currentFakeTime));
_fake.Provide(configuration);
@@ -66,6 +69,7 @@ protected TestBase(
{
b.Register((_, _) => Context);
b.Register((_, _) => _fake.Resolve());
+ b.Register((_, _) => _fake.Resolve());
b.Register((_, _) => _fake.Resolve());
b.RegisterType();
//b.Register((_, _) => _fake.Resolve());
diff --git a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj
index 29c8ab838..f17c058ac 100644
--- a/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj
+++ b/TeslaSolarCharger/Client/TeslaSolarCharger.Client.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/TeslaSolarCharger/Server/Services/ChargingService.cs b/TeslaSolarCharger/Server/Services/ChargingService.cs
index e1451843d..e0a91580b 100644
--- a/TeslaSolarCharger/Server/Services/ChargingService.cs
+++ b/TeslaSolarCharger/Server/Services/ChargingService.cs
@@ -207,6 +207,7 @@ private double GetDistance(double longitude, double latitude, double otherLongit
public int CalculateAmpByPowerAndCar(int powerToControl, Car car)
{
+ _logger.LogTrace("{method}({powerToControl}, {carId})", nameof(CalculateAmpByPowerAndCar), powerToControl, car.Id);
return Convert.ToInt32(Math.Floor(powerToControl / ((double)(_settings.AverageHomeGridVoltage ?? 230) * car.CarState.ActualPhases)));
}
@@ -407,7 +408,7 @@ private async Task ChangeCarAmp(Car car, int ampToChange, DtoValue max
{
_logger.LogDebug("Charging should stop");
//Falls Ausschaltbefehl erst seit Kurzem
- if (car.CarState.EarliestSwitchOff > _dateTimeProvider.Now())
+ if ((car.CarState.EarliestSwitchOff == default) || (car.CarState.EarliestSwitchOff > _dateTimeProvider.Now()))
{
_logger.LogDebug("Can not stop charging: earliest Switch Off: {earliestSwitchOff}",
car.CarState.EarliestSwitchOff);
@@ -533,9 +534,11 @@ private void UpdateShouldStartStopChargingSince(Car car)
var actualCurrent = car.CarState.ChargerActualCurrent ?? 0;
_logger.LogTrace("Actual current: {actualCurrent}", actualCurrent);
//This is needed because sometimes actual current is higher than last set amp, leading to higher calculated amp to set, than actually needed
- if (actualCurrent > car.CarState.LastSetAmp)
+ var lastSetAmp = car.CarState.ChargerRequestedCurrent ?? car.CarState.LastSetAmp;
+ if (actualCurrent > lastSetAmp)
{
- actualCurrent = car.CarState.LastSetAmp;
+ _logger.LogTrace("Actual current {actualCurrent} higher than last set amp {lastSetAmp}. Setting actual current as last set amp.", actualCurrent, lastSetAmp);
+ actualCurrent = lastSetAmp;
}
ampToSet += actualCurrent;
}
diff --git a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs
index 69604ca8b..9a131c4c6 100644
--- a/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs
+++ b/TeslaSolarCharger/Server/Services/TeslaFleetApiService.cs
@@ -1,5 +1,6 @@
-using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
+using System;
using System.Globalization;
using System.Net;
using System.Net.Http.Headers;
@@ -107,9 +108,27 @@ public async Task StopCharging(int carId)
public async Task SetAmp(int carId, int amps)
{
logger.LogTrace("{method}({carId}, {amps})", nameof(SetAmp), carId, amps);
+ var car = settings.Cars.First(c => c.Id == carId);
+ if (car.CarState.ChargerRequestedCurrent == amps)
+ {
+ logger.LogDebug("Correct charging amp already set.");
+ return;
+ }
var vin = await GetVinByCarId(carId).ConfigureAwait(false);
var commandData = $"{{\"charging_amps\":{amps}}}";
var result = await SendCommandToTeslaApi(vin, SetChargingAmpsRequest, commandData).ConfigureAwait(false);
+ if (amps < 5 && car.CarState.LastSetAmp >= 5
+ || amps >= 5 && car.CarState.LastSetAmp < 5)
+ {
+ logger.LogDebug("Double set amp to be able to jump over or below 5A");
+ await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);
+ result = await SendCommandToTeslaApi(vin, SetChargingAmpsRequest, commandData).ConfigureAwait(false);
+ }
+
+ if (result?.Response?.Result == true)
+ {
+ car.CarState.LastSetAmp = amps;
+ }
}
public async Task SetScheduledCharging(int carId, DateTimeOffset? chargingStartTime)
@@ -327,29 +346,35 @@ await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameo
await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameof(SendCommandToTeslaApi),
$"Result of command request is false {fleetApiRequest.RequestUrl}, {contentData}. Response string: {responseString}")
.ConfigureAwait(false);
- if (string.Equals(vehicleCommandResult.Reason, "unsigned_cmds_hardlocked"))
- {
- settings.FleetApiProxyNeeded = true;
- //remove post after a few versions as only used for debugging
- await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameof(SendCommandToTeslaApi),
- "FleetAPI proxy needed set to true")
- .ConfigureAwait(false);
- if (!await IsFleetApiProxyNeededInDatabase().ConfigureAwait(false))
- {
- teslaSolarChargerContext.TscConfigurations.Add(new TscConfiguration()
- {
- Key = constants.FleetApiProxyNeeded,
- Value = true.ToString(),
- });
- }
-
- }
+ await HandleUnsignedCommands(vehicleCommandResult).ConfigureAwait(false);
}
}
logger.LogDebug("Response: {responseString}", responseString);
return teslaCommandResultResponse;
}
+ internal async Task HandleUnsignedCommands(DtoVehicleCommandResult vehicleCommandResult)
+ {
+ if (string.Equals(vehicleCommandResult.Reason, "unsigned_cmds_hardlocked"))
+ {
+ settings.FleetApiProxyNeeded = true;
+ //remove post after a few versions as only used for debugging
+ await backendApiService.PostErrorInformation(nameof(TeslaFleetApiService), nameof(SendCommandToTeslaApi),
+ "FleetAPI proxy needed set to true")
+ .ConfigureAwait(false);
+ if (!await IsFleetApiProxyNeededInDatabase().ConfigureAwait(false))
+ {
+ teslaSolarChargerContext.TscConfigurations.Add(new TscConfiguration()
+ {
+ Key = constants.FleetApiProxyNeeded,
+ Value = true.ToString(),
+ });
+ await teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false);
+ }
+
+ }
+ }
+
public async Task IsFleetApiProxyNeededInDatabase()
{
return await teslaSolarChargerContext.TscConfigurations.AnyAsync(c => c.Key == constants.FleetApiProxyNeeded).ConfigureAwait(false);
diff --git a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj
index ac031ba7c..92f4f511a 100644
--- a/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj
+++ b/TeslaSolarCharger/Server/TeslaSolarCharger.Server.csproj
@@ -34,8 +34,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -50,7 +50,7 @@
-
+
diff --git a/TeslaSolarCharger/Shared/TeslaSolarCharger.Shared.csproj b/TeslaSolarCharger/Shared/TeslaSolarCharger.Shared.csproj
index 141a2ab5d..464e43d79 100644
--- a/TeslaSolarCharger/Shared/TeslaSolarCharger.Shared.csproj
+++ b/TeslaSolarCharger/Shared/TeslaSolarCharger.Shared.csproj
@@ -17,7 +17,7 @@
-
+