Skip to content

Commit

Permalink
Merge pull request #285 from pkuehnel/feat/solarValuesOverMqtt
Browse files Browse the repository at this point in the history
feat/solarValuesOverMqtt
  • Loading branch information
pkuehnel authored Sep 20, 2022
2 parents 50a91cb + 85fe7c6 commit bc109dd
Show file tree
Hide file tree
Showing 20 changed files with 421 additions and 63 deletions.
13 changes: 13 additions & 0 deletions TeslaSolarCharger.Tests/Services/Server/PvValueService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ public void Can_Get_Integer_From_Json_Result(string text)
Assert.Equal(384, intValue);
}

[Theory]
[InlineData("384")]
[InlineData("384.0")]
[InlineData("384.00")]
public void Can_Get_Integer_From_Simple_Result(string text)
{
var json = $"{{\"value\": {text}}}";
var pvValueService = Mock.Create<TeslaSolarCharger.Server.Services.PvValueService>();
var intValue = pvValueService.GetValueFromResult("$.value", json, NodePatternType.Json, true);

Assert.Equal(384, intValue);
}

[Theory]
[InlineData("384")]
[InlineData("384.0")]
Expand Down
38 changes: 38 additions & 0 deletions TeslaSolarCharger.Tests/Services/Server/SolarMqttService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using TeslaSolarCharger.Shared.Contracts;
using TeslaSolarCharger.Shared.Dtos.Contracts;
using Xunit;
using Xunit.Abstractions;

namespace TeslaSolarCharger.Tests.Services.Server;

public class SolarMqttService : TestBase
{
public SolarMqttService(ITestOutputHelper outputHelper)
: base(outputHelper)
{
}

[Fact]
public void Can_Extract_MqttServer()
{
var insertedValue = "192.168.1.50";
Mock.Mock<IConfigurationWrapper>().Setup(s => s.SolarMqttServer()).Returns(insertedValue);

var solarMqttService = Mock.Create<TeslaSolarCharger.Server.Services.SolarMqttService>();
var mqttServer = solarMqttService.GetMqttServerAndPort(out var mqttServerPort);
Assert.Equal(insertedValue, mqttServer);
Assert.Null(mqttServerPort);
}

[Fact]
public void Can_Extract_MqttServerAndPort()
{
var insertedValue = "192.168.1.50:1883";
Mock.Mock<IConfigurationWrapper>().Setup(s => s.SolarMqttServer()).Returns(insertedValue);

var solarMqttService = Mock.Create<TeslaSolarCharger.Server.Services.SolarMqttService>();
var mqttServer = solarMqttService.GetMqttServerAndPort(out var mqttServerPort);
Assert.Equal("192.168.1.50", mqttServer);
Assert.Equal(mqttServerPort, 1883);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

namespace TeslaSolarCharger.Tests.Services.Server;

public class MqttService : TestBase
public class TeslaMateMqttService : TestBase
{
public MqttService(ITestOutputHelper outputHelper)
public TeslaMateMqttService(ITestOutputHelper outputHelper)
: base(outputHelper)
{
}
Expand All @@ -38,7 +38,7 @@ public void ReducesActualCurrentToLastSetAmpIfDifferenceIsOneAndBelow5AAndEqualT
};
Mock.Mock<ISettings>().Setup(s => s.Cars).Returns(cars);

var mqttService = Mock.Create<TeslaSolarCharger.Server.Services.MqttService>();
var mqttService = Mock.Create<TeslaSolarCharger.Server.Services.TeslaMateMqttService>();

var teslamateValue = new TeslaMateValue()
{
Expand Down Expand Up @@ -67,4 +67,4 @@ public void ReducesActualCurrentToLastSetAmpIfDifferenceIsOneAndBelow5AAndEqualT
throw new NotImplementedException();
}
}
}
}
36 changes: 36 additions & 0 deletions TeslaSolarCharger/Client/Pages/BaseConfiguration.razor
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ else
<small id="currentPowerToGridCorrectionFactorHelp" class="form-text text-muted">Use this if consuming 1 W from the grid is not the value 1. E.g. if sending 1500 Watt to the grid is returned as 1.5 you have to enter -1000 here</small>
</div>
</div>
<div class="form-group">
<label for="solarMqttServer">Solar Mqtt-Server</label>
<InputText id="solarMqttServer" @bind-Value="_dtoBaseConfiguration.SolarMqttServer" class="form-control" />
<div>
<small id="solarMqttServer" class="form-text text-muted">This is only needed if you get solar values via MQTT</small>
</div>
</div>
<div class="form-group">
<label for="currentPowerToGridMqttTopic">Grid Power Mqtt-Topic</label>
<InputText id="currentPowerToGridMqttTopic" @bind-Value="_dtoBaseConfiguration.CurrentPowerToGridMqttTopic" class="form-control" />
<div>
<small id="currentPowerToGridMqttTopic" class="form-text text-muted">This is only needed if you get solar values via MQTT</small>
</div>
</div>
<div class="form-group">
<label for="powerBuffer">Power Buffer (Watt)</label>
<InputNumber id="powerBuffer" @bind-Value="_dtoBaseConfiguration.PowerBuffer" class="form-control" />
Expand Down Expand Up @@ -133,6 +147,13 @@ else
<label for="homeBatterySocUrl">HomeBatterySoc Url</label>
<InputText id="homeBatterySocUrl" @bind-Value="_dtoBaseConfiguration.HomeBatterySocUrl" class="form-control" />
</div>
<div class="form-group">
<label for="homeBatterySocMqttTopic">HomeBatterySoc Mqtt-Topic</label>
<InputText id="homeBatterySocMqttTopic" @bind-Value="_dtoBaseConfiguration.HomeBatterySocMqttTopic" class="form-control" />
<div>
<small id="homeBatterySocMqttTopic" class="form-text text-muted">This is only needed if you get solar values via MQTT</small>
</div>
</div>
<div class="form-group">
<label for="homeBatterySocCorrectionFactor">Correction Factor</label>
<InputText id="homeBatterySocCorrectionFactor" @bind-Value="_dtoBaseConfiguration.HomeBatterySocCorrectionFactorString" class="form-control" />
Expand Down Expand Up @@ -178,10 +199,18 @@ else
<label for="homeBatterySocXmlAttributeValueName">Home Battery Soc XML Attribute Header Value</label>
<InputText id="homeBatterySocXmlAttributeValueName" @bind-Value="_dtoBaseConfiguration.HomeBatterySocXmlAttributeValueName" placeholder="AttributeValueName" class="form-control" />
</div>
<hr />
<div class="form-group">
<label for="homeBatteryPowerUrl">HomeBatteryPower Url</label>
<InputText id="homeBatteryPowerUrl" @bind-Value="_dtoBaseConfiguration.HomeBatteryPowerUrl" class="form-control" />
</div>
<div class="form-group">
<label for="homeBatteryPowerMqttTopic">HomeBatteryPower Mqtt-Topic</label>
<InputText id="homeBatteryPowerMqttTopic" @bind-Value="_dtoBaseConfiguration.HomeBatteryPowerMqttTopic" class="form-control" />
<div>
<small id="homeBatteryPowerMqttTopic" class="form-text text-muted">This is only needed if you get solar values via MQTT</small>
</div>
</div>
<div class="form-group">
<label for="homeBatteryPowerCorrectionFactor">Correction Factor</label>
<InputText id="homeBatteryPowerCorrectionFactor" @bind-Value="_dtoBaseConfiguration.HomeBatteryPowerCorrectionFactorString" class="form-control" />
Expand Down Expand Up @@ -232,6 +261,13 @@ else
<label for="currentInverterPowerUrl">Inverter Power Url</label>
<InputText id="currentInverterPowerUrl" @bind-Value="_dtoBaseConfiguration.CurrentInverterPowerUrl" class="form-control" />
</div>
<div class="form-group">
<label for="currentInverterPowerMqttTopic">Inverter Power Mqtt-Topic</label>
<InputText id="currentInverterPowerMqttTopic" @bind-Value="_dtoBaseConfiguration.CurrentInverterPowerMqttTopic" class="form-control" />
<div>
<small id="currentInverterPowerMqttTopic" class="form-text text-muted">This is only needed if you get solar values via MQTT</small>
</div>
</div>
<div class="form-group">
<label for="currentInverterPowerCorrectionFactor">Correction Factor</label>
<InputText id="currentInverterPowerCorrectionFactor" @bind-Value="_dtoBaseConfiguration.CurrentInverterPowerCorrectionFactorString" class="form-control" />
Expand Down
2 changes: 1 addition & 1 deletion TeslaSolarCharger/Client/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ else
}
else
{
<div>Chargeprices will show up nine minutes after the first charge with at least version 1.26.0</div>
<div>Chargeprices will show up nine minutes after the first charge with at least version 2.6.0</div>
}

}
Expand Down
1 change: 1 addition & 0 deletions TeslaSolarCharger/Server/Contracts/IChargingCostService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ public interface IChargingCostService
Task<Dictionary<int, DtoChargeSummary>> GetChargeSummaries();
Task<DtoChargePrice> GetChargePriceById(int id);
Task DeleteChargePriceById(int id);
Task DeleteDuplicatedHandleCharges();
}
2 changes: 2 additions & 0 deletions TeslaSolarCharger/Server/Contracts/IPvValueService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ public interface IPvValueService
{
Task UpdatePvValues();
int GetAveragedOverage();
int? GetIntegerValueByString(string valueString, string? jsonPattern, string? xmlPattern, double correctionFactor);
void AddOverageValueToInMemoryList(int overage);
}
6 changes: 6 additions & 0 deletions TeslaSolarCharger/Server/Contracts/ISolarMqttService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TeslaSolarCharger.Server.Contracts;

public interface ISolarMqttService
{
Task ConnectMqttClient();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace TeslaSolarCharger.Server.Contracts;

public interface IMqttService
public interface ITeslaMateMqttService
{
Task ConnectMqttClient();
bool IsMqttClientConnected { get; }
}
}
21 changes: 15 additions & 6 deletions TeslaSolarCharger/Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@
.AddSingleton<ISettings, Settings>()
.AddSingleton<IInMemoryValues, InMemoryValues>()
.AddSingleton<IConfigurationWrapper, ConfigurationWrapper>()
.AddSingleton<IMqttNetLogger, MqttNetNullLogger>()
.AddSingleton<IMqttClientAdapterFactory, MqttClientAdapterFactory>()
.AddSingleton<IMqttClient, MqttClient>()
.AddTransient<IMqttNetLogger, MqttNetNullLogger>()
.AddTransient<IMqttClientAdapterFactory, MqttClientAdapterFactory>()
.AddTransient<IMqttClient, MqttClient>()
.AddTransient<MqttFactory>()
.AddTransient<IMqttService, MqttService>()
.AddSingleton<ITeslaMateMqttService, TeslaMateMqttService>()
.AddSingleton<ISolarMqttService, SolarMqttService>()
.AddTransient<IPvValueService, PvValueService>()
.AddTransient<IBaseConfigurationService, BaseConfigurationService>()
.AddTransient<IDbConnectionStringHelper, DbConnectionStringHelper>()
Expand Down Expand Up @@ -106,10 +107,16 @@
var teslaSolarChargerContext = app.Services.GetRequiredService<ITeslaSolarChargerContext>();
await teslaSolarChargerContext.Database.MigrateAsync().ConfigureAwait(false);

var chargingCostService = app.Services.GetRequiredService<IChargingCostService>();
await chargingCostService.DeleteDuplicatedHandleCharges().ConfigureAwait(false);

var baseConfigurationConverter = app.Services.GetRequiredService<IBaseConfigurationConverter>();
await baseConfigurationConverter.ConvertAllEnvironmentVariables().ConfigureAwait(false);
await baseConfigurationConverter.ConvertBaseConfigToCurrentVersion().ConfigureAwait(false);

var configurationWrapper = app.Services.GetRequiredService<IConfigurationWrapper>();
await configurationWrapper.TryAutoFillUrls().ConfigureAwait(false);

var telegramService = app.Services.GetRequiredService<ITelegramService>();
await telegramService.SendMessage("Application starting up").ConfigureAwait(false);

Expand All @@ -120,9 +127,11 @@
var carDbUpdateService = app.Services.GetRequiredService<ICarDbUpdateService>();
await carDbUpdateService.UpdateMissingCarDataFromDatabase().ConfigureAwait(false);

var mqttHelper = app.Services.GetRequiredService<IMqttService>();
var teslaMateMqttService = app.Services.GetRequiredService<ITeslaMateMqttService>();
await teslaMateMqttService.ConnectMqttClient().ConfigureAwait(false);

await mqttHelper.ConnectMqttClient().ConfigureAwait(false);
var solarMqttService = app.Services.GetRequiredService<ISolarMqttService>();
await solarMqttService.ConnectMqttClient().ConfigureAwait(false);

var jobManager = app.Services.GetRequiredService<JobManager>();
await jobManager.StartJobs().ConfigureAwait(false);
Expand Down
11 changes: 7 additions & 4 deletions TeslaSolarCharger/Server/Services/BaseConfigurationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@ public class BaseConfigurationService : IBaseConfigurationService
private readonly ILogger<BaseConfigurationService> _logger;
private readonly IConfigurationWrapper _configurationWrapper;
private readonly JobManager _jobManager;
private readonly IMqttService _mqttService;
private readonly ITeslaMateMqttService _teslaMateMqttService;
private readonly ISolarMqttService _solarMqttService;

public BaseConfigurationService(ILogger<BaseConfigurationService> logger, IConfigurationWrapper configurationWrapper,
JobManager jobManager, IMqttService mqttService)
JobManager jobManager, ITeslaMateMqttService teslaMateMqttService, ISolarMqttService solarMqttService)
{
_logger = logger;
_configurationWrapper = configurationWrapper;
_jobManager = jobManager;
_mqttService = mqttService;
_teslaMateMqttService = teslaMateMqttService;
_solarMqttService = solarMqttService;
}

public async Task UpdateBaseConfigurationAsync(DtoBaseConfiguration baseConfiguration)
{
_logger.LogTrace("{method}({@baseConfiguration})", nameof(UpdateBaseConfigurationAsync), baseConfiguration);
await _jobManager.StopJobs().ConfigureAwait(false);
await _configurationWrapper.UpdateBaseConfigurationAsync(baseConfiguration).ConfigureAwait(false);
await _mqttService.ConnectMqttClient().ConfigureAwait(false);
await _teslaMateMqttService.ConnectMqttClient().ConfigureAwait(false);
await _solarMqttService.ConnectMqttClient().ConfigureAwait(false);
await _jobManager.StartJobs().ConfigureAwait(false);
}
}
46 changes: 44 additions & 2 deletions TeslaSolarCharger/Server/Services/ChargingCostService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Collections.Generic;
using TeslaSolarCharger.Model.Contracts;
using TeslaSolarCharger.Model.Entities.TeslaSolarCharger;
using TeslaSolarCharger.Server.Contracts;
Expand Down Expand Up @@ -127,7 +128,7 @@ private async Task AddPowerDistribution(int carId, int? chargingPower, int? powe
if (latestOpenHandledCharge == default
|| latestOpenHandledCharge.ChargingProcessId != latestOpenChargingProcessId)
{

if (latestOpenChargingProcessId == default)
{
_logger.LogWarning("Seems like car {carId} is charging but there is no open charging process found in TeslaMate", carId);
Expand All @@ -153,7 +154,7 @@ private async Task AddPowerDistribution(int carId, int? chargingPower, int? powe
}

powerDistribution.HandledCharge = latestOpenHandledCharge;
powerDistribution.GridProportion = (float)(powerFromGrid / (float) chargingPower);
powerDistribution.GridProportion = (float)(powerFromGrid / (float)chargingPower);
_logger.LogTrace("Calculated grod proportion: {proportion}", powerDistribution.GridProportion);
if (powerDistribution.GridProportion < 0)
{
Expand All @@ -177,6 +178,47 @@ private async Task AddPowerDistribution(int carId, int? chargingPower, int? powe
return currentChargePrice;
}

public async Task DeleteDuplicatedHandleCharges()
{
var handledChargeChargingProcessIDs = await _teslaSolarChargerContext.HandledCharges
.Select(h => h.ChargingProcessId)
.ToListAsync().ConfigureAwait(false);

if (handledChargeChargingProcessIDs.Count == handledChargeChargingProcessIDs.Distinct().Count())
{
return;
}

var handledCharges = await _teslaSolarChargerContext.HandledCharges
.ToListAsync().ConfigureAwait(false);

var duplicates = handledCharges
.GroupBy(t => new { t.ChargingProcessId })
.Where(t => t.Count() > 1)
.SelectMany(x => x)
.ToList();

foreach (var duplicate in duplicates)
{
var chargeDistributions = await _teslaSolarChargerContext.PowerDistributions
.Where(p => p.HandledChargeId == duplicate.Id)
.ToListAsync().ConfigureAwait(false);
if (duplicate.ChargePriceId > 1)
{
var chargePrice = await _teslaSolarChargerContext.ChargePrices
.FirstOrDefaultAsync(c => c.Id == duplicate.ChargePriceId).ConfigureAwait(false);
if (chargePrice != default)
{
_teslaSolarChargerContext.ChargePrices.Remove(chargePrice);
}
}
_teslaSolarChargerContext.PowerDistributions.RemoveRange(chargeDistributions);
_teslaSolarChargerContext.HandledCharges.Remove(duplicate);
}

await _teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false);
}

public async Task FinalizeHandledCharges()
{
_logger.LogTrace("{method}()", nameof(FinalizeHandledCharges));
Expand Down
8 changes: 4 additions & 4 deletions TeslaSolarCharger/Server/Services/ChargingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ public class ChargingService : IChargingService
private readonly ITeslaService _teslaService;
private readonly IConfigurationWrapper _configurationWrapper;
private readonly IPvValueService _pvValueService;
private readonly IMqttService _mqttService;
private readonly ITeslaMateMqttService _teslaMateMqttService;
private readonly GlobalConstants _globalConstants;

public ChargingService(ILogger<ChargingService> logger,
ISettings settings, IDateTimeProvider dateTimeProvider, ITelegramService telegramService,
ITeslaService teslaService, IConfigurationWrapper configurationWrapper, IPvValueService pvValueService,
IMqttService mqttService, GlobalConstants globalConstants)
ITeslaMateMqttService teslaMateMqttService, GlobalConstants globalConstants)
{
_logger = logger;
_settings = settings;
Expand All @@ -33,7 +33,7 @@ public ChargingService(ILogger<ChargingService> logger,
_teslaService = teslaService;
_configurationWrapper = configurationWrapper;
_pvValueService = pvValueService;
_mqttService = mqttService;
_teslaMateMqttService = teslaMateMqttService;
_globalConstants = globalConstants;
}

Expand All @@ -46,7 +46,7 @@ public async Task SetNewChargingValues()
var geofence = _configurationWrapper.GeoFence();
_logger.LogDebug("Relevant Geofence: {geofence}", geofence);

if (!_mqttService.IsMqttClientConnected)
if (!_teslaMateMqttService.IsMqttClientConnected)
{
_logger.LogWarning("TeslaMate Mqtt Client is not connected. Charging Values won't be set.");
}
Expand Down
Loading

0 comments on commit bc109dd

Please sign in to comment.