Merge remote-tracking branch 'origin/feature-DeviceRegistration' into feature-DeviceRegistration

This commit is contained in:
Stefan
2023-05-24 11:28:20 +02:00
10 changed files with 240 additions and 185 deletions
@@ -1,10 +1,10 @@
namespace Elektrifikatsiya.Models;
public class DeviceStatusChagedEventArgs : EventArgs
public class DeviceStatusChangedEventArgs : EventArgs
{
public string MacAddress { get; set; }
public DeviceStatusChagedEventArgs(string macAddress)
public DeviceStatusChangedEventArgs(string macAddress)
{
MacAddress = macAddress;
}
@@ -11,14 +11,16 @@ namespace Elektrifikatsiya.Models
public string EventName { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
public Device Plug { get; set; }
//if Plug Class implemented -> Property for which plug this event is from
public Event(string eventName, string description, DateTime date)
public Event() { }
public Event(string eventName, string description, DateTime date, Device plug)
{
EventName = eventName;
Description = description;
Date = date;
Plug = plug;
}
}
}
@@ -0,0 +1,11 @@
namespace Elektrifikatsiya.Models
{
public class EventServiceEventArgs : EventArgs
{
public Event NewEvent { get; set; } = new Event();
public EventServiceEventArgs(Event newEvent)
{
NewEvent = newEvent;
}
}
}
@@ -7,90 +7,88 @@
@using System.Diagnostics
@using Elektrifikatsiya.Services.Implementations
@inject IVersionProvider VersionProvider
@inject IDeviceStatusService DeviceStatusService
@inject IEventService EventService
@inject IDeviceStatusService DeviceStatusService;
@inject IDeviceManagmentService DeviceManagmentService;
<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">
<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>
<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>
@@ -10,88 +10,83 @@ namespace Elektrifikatsiya.Pages;
public partial class Dashboard
{
//TODO: insert new event here if plug produces one
private readonly List<Event> events = new List<Event>() { new Event("Placeholder", "Placeholder", DateTime.Now), new Event("Placeholder", "Placeholder", DateTime.Now), new Event("Placeholder", "Placeholder", DateTime.Now) };
//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 Device here if user adds one
private List<Device> plugs = new List<Device>();
protected override void OnInitialized()
{
plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List<Device>();
events = EventService.Events;
//code for graph
private LineChart<double> lineChart;
EventService.OnEventCalled += (_, e) =>
{
_ = InvokeAsync(StateHasChanged);
};
DeviceStatusService.OnDeviceStatusChanged += (_, e) =>
{
_ = InvokeAsync(StateHasChanged);
};
}
protected override void OnInitialized()
{
plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List<Device>();
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await HandleRedraw();
}
}
DeviceStatusService.OnDeviceStatusChanged += (_, e) =>
{
_ = InvokeAsync(StateHasChanged);
};
}
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",
};
}
private async Task Switch(Device device)
{
device.Enabled = !device.Enabled;
_ = await DeviceManagmentService.UpdateDevice(device);
}
List<string> labels = new List<string>();
private async Task HandleRedraw()
{
labels.Clear();
await lineChart.Clear();
await lineChart.AddLabelsDatasetsAndUpdate(labels, await GetLineChartDataset());
}
async Task<List<double>> RandomizeData()
{
if (plugs.Count == 0)
{
return new();
}
PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090");
//TODO: insert dataset of current and last voltage usages
private async Task<LineChartDataset<double>> GetLineChartDataset()
{
return new LineChartDataset<double>
{
Label = "Wattage",
Data = await DeviceData(),
Fill = true,
PointRadius = 3,
CubicInterpolationMode = "monotone",
};
}
string plugnames = "";
foreach (Device device in plugs)
{
plugnames += $"shellyplug-s-{device.MacAddress}/relay/0|";
}
plugnames = plugnames[0..^1];
private readonly List<string> labels = new List<string>();
Debug.WriteLine($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]""");
private async Task<List<double>> DeviceData()
{
PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090");
PrometheusDataWrapper? deviceData = (await promQueryer.Query($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]"""))?.Data;
StringBuilder builder = new();
foreach (Device device in plugs)
{
_ = builder.Append($"shellyplug-s-{device.MacAddress}/relay/0|");
}
var r = new Random(DateTime.Now.Millisecond);
if (builder.Length <= 1)
{
return new();
}
string plugnames = builder.ToString()[..^1];
Debug.WriteLine($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]""");
PrometheusDataWrapper? deviceData = (await promQueryer.Query($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]"""))?.Data;
Random 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>();
}
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>();
}
}
@@ -90,21 +90,7 @@
Here you can change the name of your plug
</Paragraph>
</FieldLabel>
<TextEdit @bind-Text="@DeviceCopy.Name" Placeholder="Enter Name" />
</Field>
<br />
<br />
<Field>
<FieldLabel>
<h5>
Change Max Output
</h5>
<Paragraph>
Here you can limit how much power your plug uses
</Paragraph>
</FieldLabel>
<TextEdit Placeholder="Enter Max Output" />
<!--TODO: What is this?-->
<TextEdit MaxLength="30" @bind-Text="@DeviceCopy.Name" Placeholder="Enter Name" />
</Field>
<br />
<br />
@@ -117,7 +103,7 @@
Here you select or change the room the plug belongs to.
</Paragraph>
</FieldLabel>
<TextEdit @bind-text="@DeviceCopy.Room" Placeholder="Enter the name of the room here"/>
<TextEdit MaxLength="30" @bind-text="@DeviceCopy.Room" Placeholder="Enter the name of the room here" />
</Field>
</Div>
}
@@ -138,7 +124,7 @@
Here you enter your electricity price. This is gonna calculate the overall Price of your System
</Paragraph>
</FieldLabel>
<TextEdit Placeholder="Enter Electricity Price" />
<TextEdit MaxLength="8" Placeholder="Enter Electricity Price" />
</Field>
</Div>
<Column ColumnSize="ColumnSize.IsFull" TextAlignment="TextAlignment.End">
@@ -6,7 +6,7 @@ namespace Elektrifikatsiya.Services;
public interface IDeviceStatusService
{
public event EventHandler<DeviceStatusChagedEventArgs> OnDeviceStatusChanged;
public event EventHandler<DeviceStatusChangedEventArgs> OnDeviceStatusChanged;
public Result<List<Device>> GetDevices();
@@ -0,0 +1,10 @@
using Elektrifikatsiya.Models;
namespace Elektrifikatsiya.Services
{
public interface IEventService
{
public List<Event> Events { get; }
public event EventHandler<EventServiceEventArgs> OnEventCalled;
}
}
@@ -1,5 +1,4 @@
using Elektrifikatsiya.Models;
using FluentResults;
namespace Elektrifikatsiya.Services.Implementations;
@@ -7,15 +6,14 @@ namespace Elektrifikatsiya.Services.Implementations;
public class DeviceStatusService : IDeviceStatusService
{
private readonly ILogger<DeviceStatusService> logger;
public event EventHandler<DeviceStatusChagedEventArgs>? OnDeviceStatusChanged;
public event EventHandler<DeviceStatusChangedEventArgs>? OnDeviceStatusChanged;
private readonly Dictionary<string, Device> devices = new();
public DeviceStatusService(ILogger<DeviceStatusService> logger)
{
this.logger = logger;
}
}
public Result<List<Device>> GetDevices()
{
@@ -36,7 +34,7 @@ public class DeviceStatusService : IDeviceStatusService
modDevice.IpAddress = device.IpAddress;
modDevice.Name = device.Name;
OnDeviceStatusChanged?.Invoke(this, new DeviceStatusChagedEventArgs(device.MacAddress));
OnDeviceStatusChanged?.Invoke(this, new DeviceStatusChangedEventArgs(device.MacAddress));
return Result.Ok();
}
@@ -0,0 +1,55 @@
using Elektrifikatsiya.Models;
using FluentResults;
using System.Reflection;
namespace Elektrifikatsiya.Services.Implementations
{
public class EventService : IEventService
{
private readonly ILogger<EventService> logger;
public event EventHandler<EventServiceEventArgs> OnEventCalled;
public List<Event> Events { get; } = new();
public EventService(ILogger<EventService> logger, IDeviceStatusService deviceStatusService, IDeviceManagmentService deviceManagmentService)
{
this.logger = logger;
List<Device> deviceList = deviceManagmentService.GetDevices().ValueOrDefault.Select(x=>x.CopyDevice()).ToList();
deviceStatusService.OnDeviceStatusChanged += (_, e) =>
{
bool newDevice = true;
Device currentDevice = deviceManagmentService.GetDevice(e.MacAddress).ValueOrDefault;
for (int i = 0; i < deviceList.Count; i++)
{
if (deviceList[i].MacAddress == currentDevice.MacAddress)
{
PropertyInfo[] properties = typeof(Device).GetProperties();
foreach(PropertyInfo property in properties) {
var currentvalue = property.GetValue(deviceList[i], null);
var newvalue = property.GetValue(currentDevice, null);
if(newvalue is not null && !newvalue.ToString().Equals(currentvalue.ToString()))
{
string eventName = $"Plug {deviceList[i].Name} changed: {property.Name}";
string description = $"Changed {property.Name} of plug [{currentvalue}] to [{newvalue}]";
DateTime dateTime = DateTime.Now;
Event newEvent = new Event(eventName, description, dateTime, currentDevice);
Events.Insert(0,newEvent);
OnEventCalled?.Invoke(this, new EventServiceEventArgs(newEvent));
}
}
deviceList[i].OverwiteDevice(currentDevice);
newDevice = false; break;
}
}
if (newDevice)
{
deviceList.Add(deviceManagmentService.GetDevice(e.MacAddress).ValueOrDefault);
}
};
}
}
}