Merge branch 'feature-DeviceRegistration' of https://github.com/Stefan-5422/-T5-Elektrifikatsiya into feature-DeviceRegistration

This commit is contained in:
Friend2868
2023-06-01 15:23:59 +02:00
7 changed files with 224 additions and 169 deletions
@@ -6,11 +6,13 @@ namespace Elektrifikatsiya.Database;
public class MainDatabaseContext : DbContext
{
public MainDatabaseContext(DbContextOptions<MainDatabaseContext> dbContextOptions) : base(dbContextOptions)
{
}
public DbSet<Device> Devices { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Device> Devices { get; set; }
public DbSet<Event> Events { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<EnergyPriceChange> EnergyPriceChanges { get; set; }
public MainDatabaseContext(DbContextOptions<MainDatabaseContext> dbContextOptions) : base(dbContextOptions)
{
}
}
@@ -0,0 +1,23 @@
using System.ComponentModel.DataAnnotations;
namespace Elektrifikatsiya.Models;
public class EnergyPriceChange
{
[Key]
public DateTime DateTime { get; set; }
public double EnergyPrice { get; set; }
public EnergyPriceChange(DateTime dateTime, double energyPrice)
{
DateTime = dateTime;
EnergyPrice = energyPrice;
}
public EnergyPriceChange(double energyPrice)
{
EnergyPrice = energyPrice;
DateTime = DateTime.Now;
}
}
@@ -1,5 +1,6 @@
@page "/"
@using Blazorise.Components;
@using Elektrifikatsiya.Database;
@using Elektrifikatsiya.Models;
@using System.Net
@using Elektrifikatsiya.Services
@@ -7,88 +8,92 @@
@using System.Diagnostics
@using Elektrifikatsiya.Services.Implementations
@inject IVersionProvider VersionProvider
@inject IDeviceStatusService DeviceStatusService
@inject IEventService EventService
<Row Padding="Padding.Is3">
<Div Display="Display.Flex.Row.OnDesktop" Margin="Margin.Is3.FromBottom" Width="Width.Is100">
<Column ColumnSize="ColumnSize.Is6.OnDesktop.Is12">
<Bar Breakpoint="Breakpoint.Desktop"
Background="Background.White"
ThemeContrast="ThemeContrast.None"
Shadow="Shadow.Remove"
Border="Border.Is1.RoundedTop">
<BarBrand>
Plugs
</BarBrand>
</Bar>
<ListView TItem="Device"
Data="@plugs"
TextField="@((_)=>(""))"
ValueField="@((_)=>(""))"
Mode="ListGroupMode.Static"
Style="border-radius: 0px 0px">
<ItemTemplate>
<ListGroupItem Border="Border.OnBottom">
<Row>
<Column ColumnSize="ColumnSize.Is8">
<Row>
<Column ColumnSize="ColumnSize.Is7">
<Heading Size="HeadingSize.Is6" Margin="Margin.Is1.FromBottom">@context.Item.Name</Heading>
</Column>
</Row>
<Row>
<Column>
<Small>User: @context.Item.User.Name</Small>
<br />
<Small>Room: @context.Item.Room</Small>
</Column>
<Column ColumnSize="ColumnSize.Is5">
<Paragraph Margin="Margin.Is1.OnY">@context.Item.PowerUsage W</Paragraph>
</Column>
</Row>
</Column>
<Column ColumnSize="ColumnSize.Is4">
<Button Position="Position.Absolute.Top.Is50.Start.Is50.Translate.Middle" Color="Color.Dark" Outline>
<BarIcon IconName="IconName.Bolt" />
</Button>
</Column>
</Row>
</ListGroupItem>
</ItemTemplate>
</ListView>
</Column>
<Column ColumnSize="ColumnSize.Is6.OnDesktop.Is12">
<Bar Breakpoint="Breakpoint.Desktop"
Background="Background.White"
ThemeContrast="ThemeContrast.None"
Shadow="Shadow.Remove"
Border="Border.Is1.Rounded.RoundedTop">
<BarBrand>
Logs
</BarBrand>
</Bar>
<ListView TItem="Event"
Data="@events"
TextField="@((_)=>(""))"
ValueField="@((_)=>(""))"
Mode="ListGroupMode.Static"
Style="border-radius: 0px 0px">
@inject IDeviceStatusService DeviceStatusService;
@inject IDeviceManagmentService DeviceManagmentService;
@inject IEventService EventService;
@inject MainDatabaseContext MainDatabaseContext;
<ItemTemplate>
<Div Flex="Flex.InlineFlex.JustifyContent.Between" Width="Width.Is100">
<Heading Size="HeadingSize.Is6" Margin="Margin.Is2.FromBottom">@context.Item.EventName</Heading>
<Small>@context.Item.Date</Small>
</Div>
<Paragraph Border="Border.OnBottom" Margin="Margin.Is2.FromBottom">@context.Item.Description</Paragraph>
</ItemTemplate>
</ListView>
</Column>
</Div>
</Row>
<Row Padding="Padding.Is3">
<Column>
<Button Color="Color.Primary" Style="border-radius: 0px" Clicked="@(async () => await HandleRedraw())">Aktualisieren</Button>
<LineChart @ref="lineChart" TItem="double" />
</Column>
</Row>
<Div>
<Row Padding="Padding.Is3">
<Div Display="Display.Flex.Row.OnDesktop" Margin=" Margin.Is3.FromBottom" Width="Width.Is100">
<Column ColumnSize="ColumnSize.Is6.OnDesktop.Is12">
<Bar Breakpoint="Breakpoint.Desktop"
Background="Background.White"
ThemeContrast="ThemeContrast.None"
Shadow="Shadow.Remove"
Border="Border.Is1.RoundedTop">
<BarBrand>
Plugs
</BarBrand>
</Bar>
<ListView TItem="Device"
Data="@plugs"
TextField="@((_)=>(""))"
ValueField="@((_)=>(""))"
Mode="ListGroupMode.Static"
Style="border-radius: 0px 0px">
<ItemTemplate>
<ListGroupItem>
<Row>
<Column ColumnSize="ColumnSize.Is8">
<Row>
<Column ColumnSize="ColumnSize.Is7">
<Heading Size="HeadingSize.Is6" Margin="Margin.Is1.FromBottom">@context.Item.Name</Heading>
</Column>
</Row>
<Row>
<Column>
<Small>User: @context.Item.User.Name</Small>
<br />
<Small>Room: @context.Item.Room</Small>
</Column>
<Column ColumnSize="ColumnSize.Is5">
<Paragraph Margin="Margin.Is1.OnY">@context.Item.PowerUsage W</Paragraph>
</Column>
</Row>
</Column>
<Column ColumnSize="ColumnSize.Is4">
<Button Clicked="() => Switch(context.Item)" Position="Position.Absolute.Top.Is50.Start.Is50.Translate.Middle" Color="Color.Primary" Primary Outline>
<BarIcon TextColor="(context.Item.Enabled?TextColor.Success:TextColor.Danger)" IconName="IconName.Bolt" />
</Button>
</Column>
</Row>
</ListGroupItem>
</ItemTemplate>
</ListView>
</Column>
<Column ColumnSize="ColumnSize.Is6.OnDesktop.Is12">
<Bar Breakpoint="Breakpoint.Desktop"
Background="Background.White"
ThemeContrast="ThemeContrast.None"
Shadow="Shadow.Remove"
Border="Border.Is1.Rounded.RoundedTop">
<BarBrand>
Logs
</BarBrand>
</Bar>
<ListView TItem="Event"
Data="@events"
TextField="@((_)=>(""))"
ValueField="@((_)=>(""))"
Mode="ListGroupMode.Static"
Style="border-radius: 0px 0px">
<ItemTemplate>
<Div Flex="Flex.InlineFlex.JustifyContent.Between" Width="Width.Is100">
<Heading Size="HeadingSize.Is6" Margin="Margin.Is2.FromBottom">@context.Item.EventName</Heading>
<Small>@context.Item.Date</Small>
</Div>
<Paragraph Margin="Margin.Is2.FromBottom">@context.Item.Description</Paragraph>
</ItemTemplate>
</ListView>
</Column>
</Div>
</Row>
<Row Padding="Padding.Is3">
<Column>
<Button Color="Color.Primary" Style="border-radius: 0px" Clicked="@(async () => await HandleRedraw())">Aktualisieren</Button>
<LineChart Height="Height.Is100" Width="Width.Is100" @ref="lineChart" TItem="double" />
</Column>
</Row>
</Div>
@@ -5,88 +5,101 @@ using Elektrifikatsiya.Utilities;
using System.Diagnostics;
using System.Text;
using Elektrifikatsiya.Services.Implementations;
using Elektrifikatsiya.Database;
namespace Elektrifikatsiya.Pages;
public partial class Dashboard
{
//TODO: insert new event here if plug produces one
List<Event> events = new List<Event>() { new Event("Text", "Text", DateTime.Today, null ) };
//TODO: insert new Device here if user adds one
List<Device> plugs = new List<Device>();
//code for graph
LineChart<double> lineChart;
//TODO: insert new event here if plug produces one
private List<Event> events = new List<Event>() { new Event("Text", "Text", DateTime.Today, null) };
protected override void OnInitialized()
{
plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List<Device>();
events = EventService.Events;
private List<string> labels = new List<string>();
EventService.OnEventCalled += (_, e) =>
{
_ = InvokeAsync(StateHasChanged);
};
DeviceStatusService.OnDeviceStatusChanged += (_, e) =>
{
_ = InvokeAsync(StateHasChanged);
};
}
//code for graph
private LineChart<double> lineChart;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await HandleRedraw();
}
}
//TODO: insert new Device here if user adds one
private List<Device> plugs = new List<Device>();
async Task HandleRedraw()
{
labels.Clear();
await lineChart.Clear();
await lineChart.AddLabelsDatasetsAndUpdate(labels, await GetLineChartDataset());
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await HandleRedraw();
}
}
//TODO: insert dataset of current and last voltage usages
async Task<LineChartDataset<double>> GetLineChartDataset()
{
return new LineChartDataset<double>
{
Label = "Wattage",
Data = await RandomizeData(),
Fill = true,
PointRadius = 3,
CubicInterpolationMode = "monotone",
};
}
protected override void OnInitialized()
{
plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List<Device>();
events = EventService.Events;
List<string> labels = new List<string>();
EventService.OnEventCalled += (_, e) =>
{
_ = InvokeAsync(StateHasChanged);
};
DeviceStatusService.OnDeviceStatusChanged += (_, e) =>
{
_ = InvokeAsync(StateHasChanged);
};
}
async Task<List<double>> RandomizeData()
{
if (plugs.Count == 0)
{
return new();
}
PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090");
private async Task<List<double>> GetData()
{
if (plugs.Count == 0)
{
return new();
}
PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090");
string plugnames = "";
foreach (Device device in plugs)
{
plugnames += $"shellyplug-s-{device.MacAddress}/relay/0|";
}
plugnames = plugnames[0..^1];
string plugnames = "";
foreach (Device device in plugs)
{
plugnames += $"shellyplug-s-{device.MacAddress}/relay/0|";
}
plugnames = plugnames[0..^1];
Debug.WriteLine($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]""");
double energyPrice = MainDatabaseContext.EnergyPriceChanges.OrderByDescending(e =>e.DateTime).FirstOrDefault()?.EnergyPrice ?? 0;
PrometheusDataWrapper? deviceData = (await promQueryer.Query($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]"""))?.Data;
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;
var r = new Random(DateTime.Now.Millisecond);
Console.WriteLine(priceData.VectorTypeToTimestampFloatTuple());
return deviceData?.MatrixTypeToTimestampFloatTuple().ValueOrDefault?.Select(x =>
{
labels.Add(DateTimeOffset.FromUnixTimeSeconds(long.Parse($"1{x.Item1}0")).UtcDateTime.ToLocalTime().ToShortTimeString());
return x.Item2;
}).ToList() ?? new List<double>();
}
var r = new Random(DateTime.Now.Millisecond);
return deviceData?.MatrixTypeToTimestampFloatTuple().ValueOrDefault?.Select(x =>
{
labels.Add(DateTimeOffset.FromUnixTimeSeconds(long.Parse($"1{x.Item1}0")).UtcDateTime.ToLocalTime().ToShortTimeString());
return x.Item2;
}).ToList() ?? new List<double>();
}
//TODO: insert dataset of current and last voltage usages
private async Task<LineChartDataset<double>> GetLineChartDataset()
{
return new LineChartDataset<double>
{
Label = "Wattage",
Data = await GetData(),
Fill = true,
PointRadius = 3,
CubicInterpolationMode = "monotone",
};
}
private async Task HandleRedraw()
{
labels.Clear();
await lineChart.Clear();
await lineChart.AddLabelsDatasetsAndUpdate(labels, await GetLineChartDataset());
}
private async Task Switch(Device device)
{
device.Enabled = !device.Enabled;
_ = await DeviceManagmentService.UpdateDevice(device);
}
}
@@ -1,5 +1,6 @@
@page "/Settings"
@using Blazorise.Components;
@using Elektrifikatsiya.Database;
@using Elektrifikatsiya.Models;
@using System.Net
@using System.Text.Json
@@ -9,6 +10,7 @@
@inject IDeviceManagmentService DeviceManagmentService
@inject IMessageService MessageService
@inject MainDatabaseContext MainDatabaseContext
<Div>
<Row Style="width: 100%; height: 100%" Margin="Margin.Is3.FromTop.OnMobile" Padding="Padding.Is4.FromStart.OnMobile">
@@ -124,7 +126,7 @@
Here you enter your electricity price. This is gonna calculate the overall Price of your System
</Paragraph>
</FieldLabel>
<TextEdit MaxLength="8" Placeholder="Enter Electricity Price" />
<NumericEdit @bind-Value="electricityPrice" MaxLength="8" Placeholder="Enter Electricity Price" />
</Field>
</Div>
<Column ColumnSize="ColumnSize.IsFull" TextAlignment="TextAlignment.End">
@@ -139,7 +141,9 @@
@code {
List<Device> plugs = new List<Device>();
private bool hideButtonSettings = true;
bool hideButtonSettings = true;
double electricityPrice = 0;
Device? SelectedDevice = null;
Device? DeviceCopy = null;
@@ -152,6 +156,8 @@
{
plugs = getDevicesResult.Value;
}
electricityPrice = MainDatabaseContext.EnergyPriceChanges.OrderByDescending(e=>e.DateTime).FirstOrDefault()?.EnergyPrice ?? 0;
}
private void Toggle(Device device)
@@ -161,16 +167,19 @@
DeviceCopy = device.CopyDevice();
}
private void OnSave()
private async void OnSave()
{
if (hideButtonSettings || DeviceCopy is null || SelectedDevice is null)
if (hideButtonSettings)
{
MainDatabaseContext.EnergyPriceChanges.Add(new EnergyPriceChange(electricityPrice));
await MainDatabaseContext.SaveChangesAsync();
}
else
else if(DeviceCopy is not null && SelectedDevice is not null)
{
SelectedDevice.OverwiteDevice(DeviceCopy);
DeviceManagmentService.UpdateDevice(SelectedDevice);
await DeviceManagmentService.UpdateDevice(SelectedDevice);
}
}
@@ -15,7 +15,8 @@ using HiveMQtt.Client.Options;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Prometheus;
using Prometheus.HttpMetrics;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
@@ -32,7 +33,7 @@ builder.Services.AddSingleton<IUpdateService, UpdateService>();
builder.Services.AddSingleton<IDeviceStatusService, DeviceStatusService>();
builder.Services.AddSingleton<INotificationService, NotifcationService>();
builder.Services.AddSingleton<IScheduledService>(s => s.GetRequiredService<IUpdateService>());
builder.Services.AddSingleton<IScheduledService>(s=>s.GetRequiredService<INotificationService>());
builder.Services.AddSingleton<IScheduledService>(s => s.GetRequiredService<INotificationService>());
builder.Services.AddSingleton<IEmailService, EmailService>();
builder.Services.AddSingleton<IEventService, EventService>();
builder.Services.AddSingleton<IHiveMQClient, HiveMQClient>((provider)=>
@@ -44,7 +45,7 @@ builder.Services.AddSingleton<IHiveMQClient, HiveMQClient>((provider)=>
UseTLS = false,
};
HiveMQClient client = new(options);
HiveMQClient client = new(options);
client.ConnectAsync().ConfigureAwait(false);
return client;
});
@@ -60,7 +61,6 @@ builder.Services.AddBlazorise(options =>
options.Immediate = true;
});
AddBlazorise(builder.Services);
WebApplication app = builder.Build();
@@ -81,6 +81,9 @@ app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.MapControllers();
app.MapMetrics();
app.UseHttpMetrics();
app.Services.GetRequiredService<IOptions<EmailSettings>>().Value.CompileTemplates();
@@ -98,7 +101,7 @@ if (!mainDatabase.Users.Any())
app.Run();
void AddBlazorise(IServiceCollection services)
static void AddBlazorise(IServiceCollection services)
{
_ = services.AddBlazorise();
_ = services.AddMaterialProviders();
@@ -4,7 +4,7 @@ namespace Elektrifikatsiya.Services.Implementations;
public class NotifcationService : INotifcationService
{
public TimeSpan ExecutionRepeatDelay => TimeSpan.FromSeconds(30);
public TimeSpan ExecutionRepeatDelay => TimeSpan.FromDays(1);
public DateTime FirstExecutionTime => DateTime.Now.AddDays(0);
private readonly IEmailService emailService;
private readonly ILogger<NotifcationService> logger;