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; namespace Elektrifikatsiya.Models;
public class DeviceStatusChagedEventArgs : EventArgs public class DeviceStatusChangedEventArgs : EventArgs
{ {
public string MacAddress { get; set; } public string MacAddress { get; set; }
public DeviceStatusChagedEventArgs(string macAddress) public DeviceStatusChangedEventArgs(string macAddress)
{ {
MacAddress = macAddress; MacAddress = macAddress;
} }
@@ -11,14 +11,16 @@ namespace Elektrifikatsiya.Models
public string EventName { get; set; } public string EventName { get; set; }
public string Description { get; set; } public string Description { get; set; }
public DateTime Date { get; set; } public DateTime Date { get; set; }
public Device Plug { get; set; }
//if Plug Class implemented -> Property for which plug this event is from //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; EventName = eventName;
Description = description; Description = description;
Date = date; 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 System.Diagnostics
@using Elektrifikatsiya.Services.Implementations @using Elektrifikatsiya.Services.Implementations
@inject IVersionProvider VersionProvider @inject IVersionProvider VersionProvider
@inject IDeviceStatusService DeviceStatusService
@inject IEventService EventService
@inject IDeviceStatusService DeviceStatusService; <Row Padding="Padding.Is3">
@inject IDeviceManagmentService DeviceManagmentService; <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> <ItemTemplate>
<Row Padding="Padding.Is3"> <Div Flex="Flex.InlineFlex.JustifyContent.Between" Width="Width.Is100">
<Div Display="Display.Flex.Row.OnDesktop" Margin=" Margin.Is3.FromBottom" Width="Width.Is100"> <Heading Size="HeadingSize.Is6" Margin="Margin.Is2.FromBottom">@context.Item.EventName</Heading>
<Column ColumnSize="ColumnSize.Is6.OnDesktop.Is12"> <Small>@context.Item.Date</Small>
<Bar Breakpoint="Breakpoint.Desktop" </Div>
Background="Background.White" <Paragraph Border="Border.OnBottom" Margin="Margin.Is2.FromBottom">@context.Item.Description</Paragraph>
ThemeContrast="ThemeContrast.None" </ItemTemplate>
Shadow="Shadow.Remove" </ListView>
Border="Border.Is1.RoundedTop"> </Column>
<BarBrand> </Div>
Plugs </Row>
</BarBrand> <Row Padding="Padding.Is3">
</Bar> <Column>
<ListView TItem="Device" <Button Color="Color.Primary" Style="border-radius: 0px" Clicked="@(async () => await HandleRedraw())">Aktualisieren</Button>
Data="@plugs" <LineChart @ref="lineChart" TItem="double" />
TextField="@((_)=>(""))" </Column>
ValueField="@((_)=>(""))" </Row>
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>
@@ -10,88 +10,83 @@ namespace Elektrifikatsiya.Pages;
public partial class Dashboard public partial class Dashboard
{ {
//TODO: insert new event here if plug produces one //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) }; 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 protected override void OnInitialized()
private List<Device> plugs = new List<Device>(); {
plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List<Device>();
events = EventService.Events;
//code for graph EventService.OnEventCalled += (_, e) =>
private LineChart<double> lineChart; {
_ = InvokeAsync(StateHasChanged);
};
DeviceStatusService.OnDeviceStatusChanged += (_, e) =>
{
_ = InvokeAsync(StateHasChanged);
};
}
protected override void OnInitialized() protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List<Device>(); if (firstRender)
{
await HandleRedraw();
}
}
DeviceStatusService.OnDeviceStatusChanged += (_, e) => async Task HandleRedraw()
{ {
_ = InvokeAsync(StateHasChanged); labels.Clear();
}; await lineChart.Clear();
} await lineChart.AddLabelsDatasetsAndUpdate(labels, await GetLineChartDataset());
}
protected override async Task OnAfterRenderAsync(bool firstRender) //TODO: insert dataset of current and last voltage usages
{ async Task<LineChartDataset<double>> GetLineChartDataset()
if (firstRender) {
{ return new LineChartDataset<double>
await HandleRedraw(); {
} Label = "Wattage",
} Data = await RandomizeData(),
Fill = true,
PointRadius = 3,
CubicInterpolationMode = "monotone",
};
}
private async Task Switch(Device device) List<string> labels = new List<string>();
{
device.Enabled = !device.Enabled;
_ = await DeviceManagmentService.UpdateDevice(device);
}
private async Task HandleRedraw() async Task<List<double>> RandomizeData()
{ {
labels.Clear(); if (plugs.Count == 0)
await lineChart.Clear(); {
await lineChart.AddLabelsDatasetsAndUpdate(labels, await GetLineChartDataset()); return new();
} }
PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090");
//TODO: insert dataset of current and last voltage usages string plugnames = "";
private async Task<LineChartDataset<double>> GetLineChartDataset() foreach (Device device in plugs)
{ {
return new LineChartDataset<double> plugnames += $"shellyplug-s-{device.MacAddress}/relay/0|";
{ }
Label = "Wattage", plugnames = plugnames[0..^1];
Data = await DeviceData(),
Fill = true,
PointRadius = 3,
CubicInterpolationMode = "monotone",
};
}
private readonly List<string> labels = new List<string>(); Debug.WriteLine($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]""");
private async Task<List<double>> DeviceData() PrometheusDataWrapper? deviceData = (await promQueryer.Query($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]"""))?.Data;
{
PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090");
StringBuilder builder = new(); var r = new Random(DateTime.Now.Millisecond);
foreach (Device device in plugs)
{
_ = builder.Append($"shellyplug-s-{device.MacAddress}/relay/0|");
}
if (builder.Length <= 1) return deviceData?.MatrixTypeToTimestampFloatTuple().ValueOrDefault?.Select(x =>
{ {
return new(); labels.Add(DateTimeOffset.FromUnixTimeSeconds(long.Parse($"1{x.Item1}0")).UtcDateTime.ToLocalTime().ToShortTimeString());
} return x.Item2;
}).ToList() ?? new List<double>();
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>();
}
} }
@@ -90,21 +90,7 @@
Here you can change the name of your plug Here you can change the name of your plug
</Paragraph> </Paragraph>
</FieldLabel> </FieldLabel>
<TextEdit @bind-Text="@DeviceCopy.Name" Placeholder="Enter Name" /> <TextEdit MaxLength="30" @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?-->
</Field> </Field>
<br /> <br />
<br /> <br />
@@ -117,7 +103,7 @@
Here you select or change the room the plug belongs to. Here you select or change the room the plug belongs to.
</Paragraph> </Paragraph>
</FieldLabel> </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> </Field>
</Div> </Div>
} }
@@ -138,7 +124,7 @@
Here you enter your electricity price. This is gonna calculate the overall Price of your System Here you enter your electricity price. This is gonna calculate the overall Price of your System
</Paragraph> </Paragraph>
</FieldLabel> </FieldLabel>
<TextEdit Placeholder="Enter Electricity Price" /> <TextEdit MaxLength="8" Placeholder="Enter Electricity Price" />
</Field> </Field>
</Div> </Div>
<Column ColumnSize="ColumnSize.IsFull" TextAlignment="TextAlignment.End"> <Column ColumnSize="ColumnSize.IsFull" TextAlignment="TextAlignment.End">
@@ -6,7 +6,7 @@ namespace Elektrifikatsiya.Services;
public interface IDeviceStatusService public interface IDeviceStatusService
{ {
public event EventHandler<DeviceStatusChagedEventArgs> OnDeviceStatusChanged; public event EventHandler<DeviceStatusChangedEventArgs> OnDeviceStatusChanged;
public Result<List<Device>> GetDevices(); 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 Elektrifikatsiya.Models;
using FluentResults; using FluentResults;
namespace Elektrifikatsiya.Services.Implementations; namespace Elektrifikatsiya.Services.Implementations;
@@ -7,15 +6,14 @@ namespace Elektrifikatsiya.Services.Implementations;
public class DeviceStatusService : IDeviceStatusService public class DeviceStatusService : IDeviceStatusService
{ {
private readonly ILogger<DeviceStatusService> logger; private readonly ILogger<DeviceStatusService> logger;
public event EventHandler<DeviceStatusChangedEventArgs>? OnDeviceStatusChanged;
public event EventHandler<DeviceStatusChagedEventArgs>? OnDeviceStatusChanged;
private readonly Dictionary<string, Device> devices = new(); private readonly Dictionary<string, Device> devices = new();
public DeviceStatusService(ILogger<DeviceStatusService> logger) public DeviceStatusService(ILogger<DeviceStatusService> logger)
{ {
this.logger = logger; this.logger = logger;
} }
public Result<List<Device>> GetDevices() public Result<List<Device>> GetDevices()
{ {
@@ -36,7 +34,7 @@ public class DeviceStatusService : IDeviceStatusService
modDevice.IpAddress = device.IpAddress; modDevice.IpAddress = device.IpAddress;
modDevice.Name = device.Name; modDevice.Name = device.Name;
OnDeviceStatusChanged?.Invoke(this, new DeviceStatusChagedEventArgs(device.MacAddress)); OnDeviceStatusChanged?.Invoke(this, new DeviceStatusChangedEventArgs(device.MacAddress));
return Result.Ok(); 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);
}
};
}
}
}