mirror of
https://github.com/Stefan-5422/-T5-Elektrifikatsiya.git
synced 2026-09-04 08:55:59 +02:00
Merge branch 'Development' of https://github.com/Stefan-5422/-T5-Elektrifikatsiya into Development
This commit is contained in:
@@ -353,3 +353,4 @@ MigrationBackup/
|
||||
*.sqlite*
|
||||
src/Elektrifikatsiya/Elektrifikatsiya/appsettings.Development.json
|
||||
/src/Elektrifikatsiya/Elektrifikatsiya/appsettings.Development.json~origin_Development
|
||||
/src/Elektrifikatsiya/Elektrifikatsiya/appsettings.Development.json
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
@inject IDeviceStatusService DeviceStatusService;
|
||||
@inject IDeviceManagmentService DeviceManagmentService;
|
||||
@inject IEventService EventService;
|
||||
@inject IEnergyPriceService EnergyPriceService;
|
||||
@inject MainDatabaseContext MainDatabaseContext;
|
||||
|
||||
<Div>
|
||||
@@ -107,19 +108,19 @@
|
||||
<Div Flex="Flex.JustifyContent.Between" Width="Width.Is100">
|
||||
<Heading Size="HeadingSize.Is6">Daily Cost</Heading>
|
||||
</Div>
|
||||
<Paragraph Margin="Margin.Is1.FromBottom">2€</Paragraph>
|
||||
<Paragraph Margin="Margin.Is1.FromBottom">@($"{EnergyPriceService.DailyEnergyCost:F2}")€</Paragraph>
|
||||
</ListGroupItem>
|
||||
<ListGroupItem>
|
||||
<Div Flex="Flex.JustifyContent.Between" Width="Width.Is100">
|
||||
<Heading Size="HeadingSize.Is6">Monthly Cost</Heading>
|
||||
</Div>
|
||||
<Paragraph Margin="Margin.Is1.FromBottom">60€</Paragraph>
|
||||
<Paragraph Margin="Margin.Is1.FromBottom">@($"{EnergyPriceService.MonthlyEnergyCost:F2}")€</Paragraph>
|
||||
</ListGroupItem>
|
||||
<ListGroupItem>
|
||||
<Div Flex="Flex.JustifyContent.Between" Width="Width.Is100">
|
||||
<Heading Size="HeadingSize.Is6">Yearly Cost</Heading>
|
||||
</Div>
|
||||
<Paragraph Margin="Margin.Is1.FromBottom">720€</Paragraph>
|
||||
<Paragraph Margin="Margin.Is1.FromBottom">@($"{EnergyPriceService.YearlyEnergyCost:F2}")€</Paragraph>
|
||||
</ListGroupItem>
|
||||
|
||||
</ListGroup>
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
using Blazorise.Charts;
|
||||
|
||||
using Elektrifikatsiya.Database;
|
||||
using Elektrifikatsiya.Models;
|
||||
using Elektrifikatsiya.Utilities;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Elektrifikatsiya.Services.Implementations;
|
||||
using Elektrifikatsiya.Database;
|
||||
|
||||
namespace Elektrifikatsiya.Pages;
|
||||
|
||||
public partial class Dashboard
|
||||
{
|
||||
private readonly List<string> labels = new List<string>();
|
||||
|
||||
//TODO: insert new event here if plug produces one
|
||||
private List<Event> events = new List<Event>() { new Event("Text", "Text", DateTime.Today, null) };
|
||||
|
||||
private List<string> labels = new List<string>();
|
||||
|
||||
//code for graph
|
||||
private LineChart<double> lineChart;
|
||||
|
||||
@@ -36,11 +32,11 @@ public partial class Dashboard
|
||||
plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List<Device>();
|
||||
events = EventService.Events;
|
||||
|
||||
EventService.OnEventCalled += (_, e) =>
|
||||
EventService.OnEventCalled += (__, e) =>
|
||||
{
|
||||
_ = InvokeAsync(StateHasChanged);
|
||||
};
|
||||
DeviceStatusService.OnDeviceStatusChanged += (_, e) =>
|
||||
DeviceStatusService.OnDeviceStatusChanged += (__, e) =>
|
||||
{
|
||||
_ = InvokeAsync(StateHasChanged);
|
||||
};
|
||||
@@ -61,14 +57,14 @@ public partial class Dashboard
|
||||
}
|
||||
plugnames = plugnames[0..^1];
|
||||
|
||||
double energyPrice = MainDatabaseContext.EnergyPriceChanges.OrderByDescending(e =>e.DateTime).FirstOrDefault()?.EnergyPrice ?? 0;
|
||||
double energyPrice = MainDatabaseContext.EnergyPriceChanges.OrderByDescending(e => e.DateTime).FirstOrDefault()?.EnergyPrice ?? 0;
|
||||
|
||||
PrometheusDataWrapper? deviceData = (await promQueryer.Query($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]"""))?.Data;
|
||||
//PrometheusDataWrapper? priceData = (await promQueryer.Query($$"""sum(sum_over_time(power{sensor=~"{{plugnames}}"}[1y])*{{energyPrice}})"""))?.Data;
|
||||
|
||||
//Console.WriteLine(priceData.VectorTypeToTimestampFloatTuple());
|
||||
|
||||
var r = new Random(DateTime.Now.Millisecond);
|
||||
Random r = new Random(DateTime.Now.Millisecond);
|
||||
|
||||
return deviceData?.MatrixTypeToTimestampFloatTuple().ValueOrDefault?.Select(x =>
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ using Elektrifikatsiya.Models;
|
||||
using Elektrifikatsiya.Services;
|
||||
using Elektrifikatsiya.Services.Implementations;
|
||||
using Elektrifikatsiya.Utilities;
|
||||
|
||||
using HiveMQtt.Client;
|
||||
using HiveMQtt.Client.Options;
|
||||
|
||||
@@ -30,22 +31,24 @@ builder.Services.AddHostedService<ServiceScheduler>();
|
||||
builder.Services.AddSingleton<IUpdateService, UpdateService>();
|
||||
builder.Services.AddSingleton<IDeviceStatusService, DeviceStatusService>();
|
||||
builder.Services.AddSingleton<INotificationService, NotifcationService>();
|
||||
builder.Services.AddSingleton<IEnergyPriceService, EnergyPriceService>();
|
||||
builder.Services.AddSingleton<IScheduledService>(s => s.GetRequiredService<IUpdateService>());
|
||||
builder.Services.AddSingleton<IScheduledService>(s => s.GetRequiredService<INotificationService>());
|
||||
builder.Services.AddSingleton<IScheduledService>(s => s.GetRequiredService<IEnergyPriceService>());
|
||||
builder.Services.AddSingleton<IEmailService, EmailService>();
|
||||
builder.Services.AddSingleton<IEventService, EventService>();
|
||||
builder.Services.AddSingleton<IHiveMQClient, HiveMQClient>((provider)=>
|
||||
builder.Services.AddSingleton<IHiveMQClient, HiveMQClient>((provider) =>
|
||||
{
|
||||
HiveMQClientOptions options = new()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 1883,
|
||||
UseTLS = false,
|
||||
};
|
||||
HiveMQClientOptions options = new()
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 1883,
|
||||
UseTLS = false,
|
||||
};
|
||||
|
||||
HiveMQClient client = new(options);
|
||||
client.ConnectAsync().ConfigureAwait(false);
|
||||
return client;
|
||||
HiveMQClient client = new(options);
|
||||
_ = client.ConnectAsync().ConfigureAwait(false);
|
||||
return client;
|
||||
});
|
||||
builder.Services.AddTransient<IDeviceManagmentService, DeviceManagmentService>();
|
||||
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
|
||||
@@ -56,7 +59,7 @@ builder.Services.AddBootstrapProviders();
|
||||
builder.Services.AddHttpClient<IDeviceManagmentService, DeviceManagmentService>();
|
||||
builder.Services.AddBlazorise(options =>
|
||||
{
|
||||
options.Immediate = true;
|
||||
options.Immediate = true;
|
||||
});
|
||||
|
||||
AddBlazorise(builder.Services);
|
||||
@@ -66,9 +69,9 @@ WebApplication app = builder.Build();
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
_ = app.UseExceptionHandler("/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
_ = app.UseHsts();
|
||||
_ = app.UseExceptionHandler("/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
_ = app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
@@ -80,7 +83,6 @@ app.MapBlazorHub();
|
||||
app.MapFallbackToPage("/_Host");
|
||||
app.MapControllers();
|
||||
|
||||
|
||||
app.Services.GetRequiredService<IOptions<EmailSettings>>().Value.CompileTemplates();
|
||||
|
||||
IServiceScope serviceScope = app.Services.GetRequiredService<IServiceScopeFactory>().CreateScope();
|
||||
@@ -99,7 +101,7 @@ app.Run();
|
||||
|
||||
static void AddBlazorise(IServiceCollection services)
|
||||
{
|
||||
_ = services.AddBlazorise();
|
||||
_ = services.AddMaterialProviders();
|
||||
_ = services.AddMaterialIcons();
|
||||
_ = services.AddBlazorise();
|
||||
_ = services.AddMaterialProviders();
|
||||
_ = services.AddMaterialIcons();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Elektrifikatsiya.Models;
|
||||
using Elektrifikatsiya.Utilities;
|
||||
|
||||
namespace Elektrifikatsiya.Services;
|
||||
|
||||
public interface IEnergyPriceService : IScheduledService
|
||||
{
|
||||
public double DailyEnergyCost { get; }
|
||||
public double MonthlyEnergyCost { get; }
|
||||
public double YearlyEnergyCost { get; }
|
||||
|
||||
public Task<double> GetEnergyPrice(DateTime startime, DateTime endTime, List<Device> plugs, List<EnergyPriceChange> changes);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Elektrifikatsiya.Database;
|
||||
using Elektrifikatsiya.Models;
|
||||
using Elektrifikatsiya.Utilities;
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace Elektrifikatsiya.Services.Implementations;
|
||||
|
||||
public class EnergyPriceService : IEnergyPriceService
|
||||
{
|
||||
private readonly MainDatabaseContext mainDatabaseContext;
|
||||
private readonly PrometheusQuery prometheusQuerier = new("http://127.0.0.1:9090");
|
||||
|
||||
public TimeSpan ExecutionRepeatDelay { get; } = TimeSpan.FromSeconds(5);
|
||||
public DateTime FirstExecutionTime { get; } = DateTime.Now.AddSeconds(5);
|
||||
public double MonthlyEnergyCost { get; private set; }
|
||||
|
||||
public double YearlyEnergyCost { get; private set; }
|
||||
|
||||
public double DailyEnergyCost { get; private set; }
|
||||
|
||||
public EnergyPriceService(IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
mainDatabaseContext = serviceScopeFactory.CreateScope().ServiceProvider.GetRequiredService<MainDatabaseContext>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the energy price for the given timespan
|
||||
/// </summary>
|
||||
/// <param name="startime">The timestamp that has the greater value</param>
|
||||
/// <param name="endTime">The timestamp that has the lower value</param>
|
||||
/// <param name="plugs"></param>
|
||||
/// <param name="changes"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public async Task<double> GetEnergyPrice(DateTime startime, DateTime endTime, List<Device> plugs, List<EnergyPriceChange> changes)
|
||||
{
|
||||
string plugnames = "";
|
||||
foreach (Device device in plugs)
|
||||
{
|
||||
plugnames += $"shellyplug-s-{device.MacAddress}/relay/0|";
|
||||
}
|
||||
plugnames = plugnames[0..^1];
|
||||
|
||||
if (startime < endTime)
|
||||
{
|
||||
throw new ArgumentException("Read the xmlDoc");
|
||||
}
|
||||
|
||||
changes = changes.OrderByDescending(x => x.DateTime).ToList();
|
||||
|
||||
double total = (await prometheusQuerier.Query($$"""sum_over_time(sum(power{sensor=~"{{plugnames}}"})[{{(int)Math.Ceiling((startime - changes.First().DateTime).TotalMinutes)}}m:1s] @ {{((DateTimeOffset)startime).ToUnixTimeSeconds()}}) * {{(changes.First().EnergyPrice / 3600).ToString(CultureInfo.InvariantCulture)}}"""))?.Data?.VectorTypeToTimestampFloatTuple().ValueOrDefault.Item2 ?? 0;
|
||||
|
||||
for (int i = 0; i < changes.Count - 1; i++)
|
||||
{
|
||||
bool inMinutes = true;
|
||||
int duration = (int)Math.Ceiling((changes[i].DateTime - changes[i + 1].DateTime).TotalMinutes);
|
||||
if (duration > 100000)
|
||||
{
|
||||
inMinutes = false;
|
||||
duration = (int)Math.Ceiling(duration / 1440.0);
|
||||
}
|
||||
long timestamp = ((DateTimeOffset)changes[i].DateTime).ToUnixTimeSeconds();
|
||||
|
||||
double price = changes[i + 1].EnergyPrice;
|
||||
|
||||
total += (await prometheusQuerier.Query($$"""sum_over_time(sum(power{sensor=~"{{plugnames}}"})[{{duration}}{{(inMinutes ? "m" : "d")}}:1s] @ {{timestamp}}) * {{(price / 3600).ToString(CultureInfo.InvariantCulture)}}"""))?.Data?.VectorTypeToTimestampFloatTuple().ValueOrDefault.Item2 ?? 0;
|
||||
if (changes[i].DateTime < endTime)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public async void Update()
|
||||
{
|
||||
List<Device> plugs = mainDatabaseContext.Devices.ToList();
|
||||
List<EnergyPriceChange> energyPriceChanges = mainDatabaseContext.EnergyPriceChanges.ToList();
|
||||
|
||||
DailyEnergyCost = await GetEnergyPrice(DateTime.Now, DateTime.Now.AddDays(-1), plugs, energyPriceChanges);
|
||||
MonthlyEnergyCost = await GetEnergyPrice(DateTime.Now, DateTime.Now.AddMonths(-1), plugs, energyPriceChanges);
|
||||
YearlyEnergyCost = await GetEnergyPrice(DateTime.Now, DateTime.Now.AddYears(-1), plugs, energyPriceChanges);
|
||||
}
|
||||
}
|
||||
+15
-10
@@ -1,24 +1,29 @@
|
||||
using Elektrifikatsiya.Utilities;
|
||||
namespace Elektrifikatsiya.Services.Implementations;
|
||||
|
||||
namespace Elektrifikatsiya.Services.Implementations;
|
||||
|
||||
public class NotifcationService : INotifcationService
|
||||
public class NotifcationService : INotificationService
|
||||
{
|
||||
private readonly IEmailService emailService;
|
||||
private readonly IEnergyPriceService energyPriceService;
|
||||
private readonly Microsoft.Extensions.Configuration.IConfiguration configuration;
|
||||
private readonly ILogger<NotifcationService> logger;
|
||||
public TimeSpan ExecutionRepeatDelay => TimeSpan.FromDays(1);
|
||||
public DateTime FirstExecutionTime => DateTime.Now.AddDays(0);
|
||||
private readonly IEmailService emailService;
|
||||
private readonly ILogger<NotifcationService> logger;
|
||||
|
||||
public NotifcationService(ILogger<NotifcationService> logger, IEmailService emailService)
|
||||
public NotifcationService(ILogger<NotifcationService> logger, IEmailService emailService, IEnergyPriceService energyPriceService, Microsoft.Extensions.Configuration.IConfiguration configuration)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.emailService = emailService;
|
||||
this.energyPriceService = energyPriceService;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public async void Update()
|
||||
{
|
||||
//logger.LogInformation("Sending Email");
|
||||
//await emailService.SendWithTemeplateAsync("[email protected]", "AAAA", "DailyNotification", templateParameters: new() { { "yesterday", "10" }, { "yearly", "100" }, { "link", "localhost:5565/yes" } });
|
||||
//logger.LogInformation("Done sending");
|
||||
logger.LogInformation("Sending Email");
|
||||
|
||||
string url = configuration.GetValue<string>("Url") ?? "https://http.cat/images/404.jpg";
|
||||
|
||||
await emailService.SendWithTemeplateAsync("[email protected]", "Daily Notification", "DailyNotification", templateParameters: new() { { "yesterday", $"{energyPriceService.DailyEnergyCost:F2}" }, { "yearly", $"{energyPriceService.YearlyEnergyCost:F2}" }, { "link", url } });
|
||||
logger.LogInformation("Done sending");
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,31 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Elektrifikatsiya.Models;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elektrifikatsiya.Models;
|
||||
using FluentResults;
|
||||
|
||||
namespace Elektrifikatsiya.Utilities;
|
||||
|
||||
public class PrometheusQuery
|
||||
{
|
||||
private readonly HttpClient client = new();
|
||||
private readonly HttpClient client = new();
|
||||
|
||||
public PrometheusQuery(string connectionString)
|
||||
{
|
||||
client.BaseAddress = new Uri(connectionString);
|
||||
}
|
||||
public PrometheusQuery(string connectionString)
|
||||
{
|
||||
client.BaseAddress = new Uri(connectionString);
|
||||
}
|
||||
|
||||
public Task<PrometheusQueryResult?> Query(string query)
|
||||
{
|
||||
return client.GetFromJsonAsync<PrometheusQueryResult>($"/api/v1/query?query={UrlEncoder.Create().Encode(query)}", new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters =
|
||||
{
|
||||
new JsonStringEnumConverter()
|
||||
}
|
||||
});
|
||||
}
|
||||
public Task<PrometheusQueryResult?> Query(string query)
|
||||
{
|
||||
Debug.WriteLine($"{client.BaseAddress}/api/v1/query?query={query}");
|
||||
return client.GetFromJsonAsync<PrometheusQueryResult>($"/api/v1/query?query={UrlEncoder.Create().Encode(query)}", new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters =
|
||||
{
|
||||
new JsonStringEnumConverter()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"DetailedErrors": true,
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"EmailSettings": {
|
||||
"User": "<SMTP_USER>",
|
||||
"Key": "<SMTP_KEY>",
|
||||
"DefaultEmail": "SMTP_EMAIL_ADDRESS",
|
||||
"Templates": {
|
||||
"DailyNotification": "../assets/DailyNotif.html"
|
||||
},
|
||||
"SmtpServer": "<SMTP_SERVER>",
|
||||
"SmtpPort": 0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user