Skip to content

Instantly share code, notes, and snippets.

@ramonsmits
Created January 23, 2023 16:53
Show Gist options
  • Select an option

  • Save ramonsmits/a3573660aeff18861aba6fa86e9cb822 to your computer and use it in GitHub Desktop.

Select an option

Save ramonsmits/a3573660aeff18861aba6fa86e9cb822 to your computer and use it in GitHub Desktop.
ChargeOrchestrator
using System.Globalization;
using Microsoft.Extensions.Logging;
using MQTTnet;
class ChargeOrchestrator
{
const double PackSizeRemainingAtFull = 85000; // kWh
readonly ILogger<ChargeOrchestrator> logger;
readonly IMessagePublisher messagePublisher;
double chargeSpeedPercentagePerHour = 17D; // 17%/h roughly 17kWh/h bu
List<Price> PriceData = new();
int BatteryLevel;
int ChargeLimitSoc;
public ChargeOrchestrator(ILogger<ChargeOrchestrator> logger, IMessagePublisher messagePublisher)
{
this.logger = logger;
this.messagePublisher = messagePublisher;
FetchDataLoop();
}
async void FetchDataLoop()
{
while (true)
{
using (var ctx = new EnergyContext())
{
PriceData = ctx.Prices.Where(x => x.At > DateTime.UtcNow).OrderBy(x => x.At).ToList();
logger.LogInformation("Price count: {Count}", PriceData.Count);
}
await Update(CancellationToken.None);
await Task.Delay(TimeSpan.FromHours(1));// Elke 6 uur... garandeerd een update tussen 18 en 24
}
}
[Subscribe("teslamate/cars/+/charge_current_request")]
public async Task HandleChargeCurrentRequest(MqttApplicationMessage msg, CancellationToken cancellationToken)
{
var amps = int.Parse(msg.ConvertPayloadToString());
var chargerPower = 230 * amps * 3;
var kWhPerPercent = PackSizeRemainingAtFull / 100; // 85000 /100 = 850 Wh
chargeSpeedPercentagePerHour = chargerPower / kWhPerPercent;// 17.000 / 850 =
await messagePublisher.Enqueue("chargeoptimizer/chargeSpeed", chargeSpeedPercentagePerHour.ToString(CultureInfo.InvariantCulture), retain: true, cancellationToken: cancellationToken);
logger.LogInformation("charge_current_request = {Amps} => {ChargeSpeedPercentagePerHour}", amps, chargeSpeedPercentagePerHour);
await Update(cancellationToken);
}
[Subscribe("teslamate/cars/+/battery_level")]
public async Task HandleBatteryLevel(MqttApplicationMessage msg, CancellationToken cancellationToken)
{
BatteryLevel = int.Parse(msg.ConvertPayloadToString());
await Update(cancellationToken);
}
[Subscribe("teslamate/cars/+/charge_limit_soc")]
public async Task HandleChargeLimitSoc(MqttApplicationMessage msg, CancellationToken cancellationToken)
{
ChargeLimitSoc = int.Parse(msg.ConvertPayloadToString());
await Update(cancellationToken);
}
async Task Update(CancellationToken cancellationToken)
{
if (ChargeLimitSoc == 0) return;
if (BatteryLevel == 0) return;
if (PriceData == null) return;
var diff = Math.Max(ChargeLimitSoc - BatteryLevel, 0);
var chargeDuration = TimeSpan.FromHours(diff / chargeSpeedPercentagePerHour);
var requiredHours = (int)Math.Ceiling(chargeDuration.TotalHours);
await messagePublisher.Enqueue("chargeoptimizer/chargeDuration", chargeDuration.ToString(), retain: true, cancellationToken: cancellationToken);
var cheapest = (index: 0, sum: double.MaxValue);
var now = DateTime.UtcNow;
var hourlyPrices = PriceData
.Where(x => x.At > now)
.ToArray();
for (int i = 0; i < (hourlyPrices.Length + 1 - requiredHours); i++)
{
var sum = hourlyPrices.Skip(i).Take(requiredHours).Sum(x => x.prijsANWB);
logger.LogTrace("Hourly prices sum from {firstHour:s} till {lastHour:s} = {sum}", hourlyPrices[i].At, hourlyPrices[i + requiredHours - 1].At, sum);
if (sum < cheapest.sum) cheapest = (i, sum);
}
var at = hourlyPrices[cheapest.index].At;
var startHourPrice = hourlyPrices[cheapest.index].prijs;
var endHourPrice = hourlyPrices[cheapest.index + requiredHours - 1].prijs;
var lastHourIsCheaperThenFirst = endHourPrice < startHourPrice;
if (lastHourIsCheaperThenFirst)
{
logger.LogTrace("Defer start to maximize last hour");
at = at.AddHours(-(chargeDuration.TotalHours - requiredHours));
}
logger.LogInformation("Start charging at: {at}", at);
await messagePublisher.Enqueue("chargeoptimizer/chargestart", at.ToString("s"), retain: true, cancellationToken: cancellationToken);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment