diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/BaseTodoItems.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/BaseTodoItems.cs deleted file mode 100644 index 9a7799c..0000000 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/BaseTodoItems.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Blazorise; - -using Elektrifikatsiya.Components.TodoApp; - -using Microsoft.AspNetCore.Components; - -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Elektrifikatsiya.Components.TodoApp; -public abstract class BaseTodoItems : ComponentBase -{ - protected Validations validations; - - protected string description; - - protected Filter filter = Filter.All; - - protected List todos = new() - { - new() { Description = "Buy milk" }, - new() { Description = "Call John regarding the meeting" }, - new() { Description = "Walk a dog" }, - }; - - protected IEnumerable Todos - { - get - { - var query = from t in todos select t; - - if (filter == Filter.Active) - query = from q in query where !q.Completed select q; - - if (filter == Filter.Completed) - query = from q in query where q.Completed select q; - - return query; - } - } - - protected void SetFilter(Filter filter) - { - this.filter = filter; - } - - protected void OnCheckAll(bool isChecked) - { - todos.ForEach(x => x.Completed = isChecked); - } - - protected async Task OnAddTodo() - { - if (await validations.ValidateAll()) - { - todos.Add(new() { Description = description }); - description = null; - - await validations.ClearAll(); - } - } - - protected void OnClearCompleted() - { - todos.RemoveAll(x => x.Completed); - filter = Filter.All; - } - - protected Task OnTodoStatusChanged(bool isChecked) - { - return InvokeAsync(StateHasChanged); - } -} diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/Filter.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/Filter.cs deleted file mode 100644 index 7cd3ac5..0000000 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/Filter.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Elektrifikatsiya.Components.TodoApp; - -public enum Filter -{ - All, - Active, - Completed, -} diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/Todo.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/Todo.cs deleted file mode 100644 index fdf4888..0000000 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/Todo.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Elektrifikatsiya.Components.TodoApp; - -public class Todo -{ - public bool Completed { get; set; } - - public string Description { get; set; } -} diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/TodoItem.razor b/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/TodoItem.razor deleted file mode 100644 index 8233b49..0000000 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/TodoItem.razor +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - @Todo.Description - - - -@code{ - Task OnCheckedChanged( bool isChecked ) - { - Todo.Completed = isChecked; - - return StatusChanged?.Invoke( isChecked ); - } - - [Parameter] public Todo Todo { get; set; } - - [Parameter] public Func StatusChanged { get; set; } -} \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/TodoItems.razor b/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/TodoItems.razor deleted file mode 100644 index a9db980..0000000 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Components/TodoApp/TodoItems.razor +++ /dev/null @@ -1,57 +0,0 @@ -@inherits BaseTodoItems - - - - - - Todo List - - - - - All - - - - - - - - - - - - - - - - - - - - @foreach ( var todo in Todos ) - { - - } - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj b/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj index 72696db..89ebdd6 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj @@ -42,6 +42,7 @@ + diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Layouts/LoginLayout.razor b/src/Elektrifikatsiya/Elektrifikatsiya/Layouts/LoginLayout.razor index 091b551..daca9d9 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Layouts/LoginLayout.razor +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Layouts/LoginLayout.razor @@ -1,17 +1,42 @@ @using System.Diagnostics; +@using FluentResults; + +@inject Elektrifikatsiya.Services.IAuthenticationService AuthenticationService +@inject NavigationManager NavigationManager + @inherits LayoutComponentBase - + - + - - + + @code { - + private string username = string.Empty; + private string password = string.Empty; + private Color color = Color.Primary; + private bool disabled; + private async Task Clicked() + { + color = Color.Primary; + disabled = true; + + Result loginResult = await AuthenticationService.LoginUserAsync(username, password); + + if (loginResult.IsSuccess) + { + NavigationManager.NavigateTo("/", true); + } + else + { + color = Color.Danger; + disabled = false; + } + } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Models/Device.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Models/Device.cs index b41d572..000e5ef 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Models/Device.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Models/Device.cs @@ -24,7 +24,7 @@ public class Device public string Room { get; set; } [NotMapped] - public bool Available { get; set; } + public bool Enabled { get; set; } public Device(string macAddress, string name, IPAddress address, User user, string room) { @@ -35,5 +35,22 @@ public class Device Room = room; } - private Device(){} + public Device CopyDevice() + { + return new Device(MacAddress, Name, IpAddress, User, Room); + } + + public void OverwiteDevice(Device overwriter) + { + Room = overwriter.Room; + Enabled = overwriter.Enabled; + PowerUsage = overwriter.PowerUsage; + Name = overwriter.Name; + User = overwriter.User; + MacAddress = overwriter.MacAddress; + IpAddress = overwriter.IpAddress; + } + + private Device() + { } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Models/PrometheusQueryResult.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Models/PrometheusQueryResult.cs index 7121698..a8f8d06 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Models/PrometheusQueryResult.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Models/PrometheusQueryResult.cs @@ -1,6 +1,6 @@ using System.Diagnostics; +using System.Globalization; using System.Text.Json.Serialization; -using Microsoft.AspNetCore.Mvc.Diagnostics; namespace Elektrifikatsiya.Models; @@ -47,51 +47,50 @@ public class PrometheusDataWrapper ResultType = resultType; Result = result; } - + public FluentResults.Result> MatrixTypeToTimestampFloatTuple() { - if (ResultType != ResultType.Matrix) - { - return FluentResults.Result.Fail("The response did not have the Matrix Type"); - } + if (ResultType != ResultType.Matrix) + { + return FluentResults.Result.Fail("The response did not have the Matrix Type"); + } List<(double, double)> result = new List<(double, double)>(); if (Result.Count == 0 || Result[0]?.Values is null || Result[0]?.Values?[0] is null) { - return FluentResults.Result.Fail("There was no result in the Response Body"); + return FluentResults.Result.Fail("There was no result in the Response Body"); } foreach (object value in Result[0]!.Values!) - { + { - string[] segment = value.ToString()!.Split(","); + string[] segment = value.ToString()!.Split(","); - result.Add((Convert.ToDouble(segment[0][2..^1]), Convert.ToDouble(segment[1][1..^2]))); - } + result.Add((Convert.ToDouble(segment[0][2..^1], CultureInfo.InvariantCulture), Convert.ToDouble(segment[1][1..^2], CultureInfo.InvariantCulture))); + } Debug.WriteLine(result); return result; } public FluentResults.Result<(double, double)> VectorTypeToTimestampFloatTuple() { - if (ResultType != ResultType.Vector) - { - return FluentResults.Result.Fail("The response did not have the Vector Type"); - } + if (ResultType != ResultType.Vector) + { + return FluentResults.Result.Fail("The response did not have the Vector Type"); + } - string[]? segment = Result.FirstOrDefault()?.Value.ToString()?.Split(",") ?? null; + string[]? segment = Result.FirstOrDefault()?.Value?.ToString()?.Split(","); - if (segment is null) - { - return FluentResults.Result.Fail("There was no result in the Response Body"); - } + if (segment is null) + { + return FluentResults.Result.Fail("There was no result in the Response Body"); + } - return((Convert.ToDouble(segment[0][2..^1]), Convert.ToDouble(segment[1][1..^2]))); + return (Convert.ToDouble(segment[0][2..^1]), Convert.ToDouble(segment[1][1..^2], CultureInfo.InvariantCulture)); } } - public class PrometheusDataMetric { [JsonPropertyName("__name__")] public string Name { get; set; } diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/AddPlug.razor b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/AddPlug.razor index d78020d..d7c1b7f 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/AddPlug.razor +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/AddPlug.razor @@ -14,144 +14,155 @@ - - - - - - - Add a plug via wifi - - - - - - - - - - - - - - - - - IP: @context.Item - - - - - - - - - - - - + + + + + + + Add a plug via wifi + + + + + + + + + + + + + + + + + IP: @context.Item + + + + + + + + + + + + - - - - - - - Add a plug via IP - - - - -
- - - -
- -
-
-
-
-
-
- -
+ + + + + + + Add a plug via IP + + + + +
+ + + + + + Please enter a ip address. + Ip address is valid. + Ip address is not valid! + + +
+ +
+
+
+
+
+
+
+ +
@code { - List ipAddresses = new List(); + List ipAddresses = new List(); - Validation? ipValidation; + Validation? ipValidation; - bool visible = true; + bool visible = true; + bool disabled = false; - string ipText = null!; + string ipText = null!; - protected override void OnInitialized() - { - MdnsDiscovery.OnDeviceFound += async ipAddress => - { - visible = false; - if (!ipAddresses.Contains(ipAddress)) - { - ipAddresses.Add(ipAddress); - await InvokeAsync(StateHasChanged); - } - }; + protected override void OnInitialized() + { + MdnsDiscovery.OnDeviceFound += async ipAddress => + { + visible = false; + if (!ipAddresses.Contains(ipAddress)) + { + ipAddresses.Add(ipAddress); + await InvokeAsync(StateHasChanged); + } + }; - MdnsDiscovery.FetchChachedDevices(); - } + MdnsDiscovery.FetchChachedDevices(); + } - private async Task RegisterDevice(IPAddress ipAddress) - { - Result result = await DeviceManagmentService.RegisterDevice(ipAddress, (await AuthenticationService.GetUserAsync()).ValueOrDefault); + private async Task RegisterDevice(IPAddress ipAddress) + { + disabled = true; + Result result = await DeviceManagmentService.RegisterDevice(ipAddress, (await AuthenticationService.GetUserAsync()).ValueOrDefault); - if (result.IsSuccess) - { - await MessageService.Success("Device added!"); - } - else - { - await MessageService.Error(string.Join(',', result.Errors.Select(e => e.Message)), "Adding device failed!"); - } - } + if (result.IsSuccess) + { + await MessageService.Success("Device added!"); + } + else + { + await MessageService.Error(string.Join(',', result.Errors.Select(e => e.Message)), "Adding device failed!"); + } + disabled = false; + } - public void ValidateIPv4(ValidatorEventArgs e) - { - string? input = Convert.ToString(e.Value); + public void ValidateIPv4(ValidatorEventArgs e) + { + string? input = Convert.ToString(e.Value); - if (string.IsNullOrWhiteSpace(input)) - { - e.Status = ValidationStatus.None; - return; - } + if (string.IsNullOrWhiteSpace(input)) + { + e.Status = ValidationStatus.None; + return; + } - string[] splitValues = input.Split('.'); - if (splitValues.Length != 4) - { - e.Status = ValidationStatus.Error; - return; - } + string[] splitValues = input.Split('.'); + if (splitValues.Length != 4) + { + e.Status = ValidationStatus.Error; + return; + } - e.Status = splitValues.All(r => byte.TryParse(r, out _)) ? ValidationStatus.Success : ValidationStatus.Error; - } + e.Status = splitValues.All(r => byte.TryParse(r, out _)) ? ValidationStatus.Success : ValidationStatus.Error; + } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Dashboard.razor b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Dashboard.razor index 5b11650..66a3051 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Dashboard.razor +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Dashboard.razor @@ -5,165 +5,92 @@ @using Elektrifikatsiya.Services @using Elektrifikatsiya.Utilities @using System.Diagnostics +@using Elektrifikatsiya.Services.Implementations @inject IVersionProvider VersionProvider @inject IDeviceStatusService DeviceStatusService; +@inject IDeviceManagmentService DeviceManagmentService;
- -
- - - - Plugs - - - - - - - - - - @context.Item.Name - - - - - User: @context.Item.User.Name -
- Room: @context.Item.Room -
- - @context.Item.PowerUsage W - -
-
- - - -
-
-
-
-
- - - - Logs - - - - -
- @context.Item.EventName - @context.Item.Date -
- @context.Item.Description -
-
-
-
-
- - - - - - + +
+ + + + Plugs + + + + + + + + + + @context.Item.Name + + + + + User: @context.Item.User.Name +
+ Room: @context.Item.Room +
+ + @context.Item.PowerUsage W + +
+
+ + + +
+
+
+
+
+ + + + Logs + + + + +
+ @context.Item.EventName + @context.Item.Date +
+ @context.Item.Description +
+
+
+
+
+ + + + + +
- - -@code { - //TODO: insert new event here if plug produces one - List events = new List() { new Event("Placeholder", "Placeholder", DateTime.Now), new Event("Placeholder", "Placeholder", DateTime.Now), new Event("Placeholder", "Placeholder", DateTime.Now) }; - //TODO: insert new Device here if user adds one - List plugs = new List(); - //code for graph - LineChart lineChart; - - protected override void OnInitialized() - { - plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List(); - - DeviceStatusService.OnDeviceStatusChanged += (_, e) => - { - InvokeAsync(StateHasChanged); - }; - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await HandleRedraw(); - } - } - - async Task HandleRedraw() - { - labels.Clear(); - await lineChart.Clear(); - await lineChart.AddLabelsDatasetsAndUpdate(labels, await GetLineChartDataset()); - } - - //TODO: insert dataset of current and last voltage usages - async Task> GetLineChartDataset() - { - return new LineChartDataset - { - Label = "Wattage", - Data = await RandomizeData(), - Fill = true, - PointRadius = 3, - CubicInterpolationMode = "monotone", - }; - } - - List labels = new List(); - - async Task> RandomizeData() - { - 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]; - - Debug.WriteLine($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]"""); - - PrometheusDataWrapper? deviceData = (await promQueryer.Query($$"""sum(power{sensor=~"{{plugnames}}"})[1h:1m]"""))?.Data; - - 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(); - } -} \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Dashboard.razor.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Dashboard.razor.cs index fa4bbf2..3d70f26 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Dashboard.razor.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Dashboard.razor.cs @@ -1,23 +1,97 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Components; -using System.Net.Http; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.Forms; -using Microsoft.AspNetCore.Components.Routing; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.Web.Virtualization; -using Microsoft.JSInterop; -using Elektrifikatsiya; -using Blazorise; +using Blazorise.Charts; -namespace Elektrifikatsiya.Pages +using Elektrifikatsiya.Models; +using Elektrifikatsiya.Utilities; + +using System.Diagnostics; +using System.Text; + +namespace Elektrifikatsiya.Pages; + +public partial class Dashboard { - public partial class Dashboard - { + //TODO: insert new event here if plug produces one + private readonly List events = new List() { new Event("Placeholder", "Placeholder", DateTime.Now), new Event("Placeholder", "Placeholder", DateTime.Now), new Event("Placeholder", "Placeholder", DateTime.Now) }; + //TODO: insert new Device here if user adds one + private List plugs = new List(); + + //code for graph + private LineChart lineChart; + + protected override void OnInitialized() + { + plugs = DeviceStatusService.GetDevices().ValueOrDefault ?? new List(); + + DeviceStatusService.OnDeviceStatusChanged += (_, e) => + { + _ = InvokeAsync(StateHasChanged); + }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await HandleRedraw(); + } + } + + private async Task Switch(Device device) + { + device.Enabled = !device.Enabled; + _ = await DeviceManagmentService.UpdateDevice(device); + } + + private async Task HandleRedraw() + { + labels.Clear(); + await lineChart.Clear(); + await lineChart.AddLabelsDatasetsAndUpdate(labels, await GetLineChartDataset()); + } + + //TODO: insert dataset of current and last voltage usages + private async Task> GetLineChartDataset() + { + return new LineChartDataset + { + Label = "Wattage", + Data = await DeviceData(), + Fill = true, + PointRadius = 3, + CubicInterpolationMode = "monotone", + }; + } + + private readonly List labels = new List(); + + private async Task> DeviceData() + { + PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090"); + + StringBuilder builder = new(); + foreach (Device device in plugs) + { + _ = builder.Append($"shellyplug-s-{device.MacAddress}/relay/0|"); + } + + 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(); } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Settings.razor b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Settings.razor index 46f8278..263b2af 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Settings.razor +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/Settings.razor @@ -2,6 +2,14 @@ @using Blazorise.Components; @using Elektrifikatsiya.Models; @using System.Net +@using System.Text.Json +@using Elektrifikatsiya.Services.Implementations +@using Elektrifikatsiya.Services; +@using FluentResults + +@inject IDeviceManagmentService DeviceManagmentService +@inject IMessageService MessageService +
@@ -34,7 +42,7 @@ - User: @context.Item.User + User: @context.Item.User.Name
Room: @context.Item.Room
@@ -43,14 +51,14 @@

- @@ -62,6 +70,9 @@ + + @if (DeviceCopy is not null) + { + } - +
+ @code { - List plugs = new List() { new Device("ffff:ffff:ffff:ffff", "Josne", IPAddress.Broadcast, new User("Joe", "qiowruioewjfopa", Role.Admin), "Raum"), new Device("ffff:ffff:ffff:ffff", "Josne", IPAddress.Broadcast, new User("Joe", "Josne", Role.Admin), "Raum"), new Device("ffff:ffff:ffff:ffff", "Josne", IPAddress.Broadcast, new User("Joe", "qiowruioewjfopa", Role.Admin), "Raum"), }; + List plugs = new List(); private bool hideButtonSettings = true; - private void Toggle() + Device? SelectedDevice = null; + Device? DeviceCopy = null; + + protected override void OnInitialized() + { + Result> getDevicesResult = DeviceManagmentService.GetDevices(); + + if (getDevicesResult.IsSuccess) + { + plugs = getDevicesResult.Value; + } + } + + private void Toggle(Device device) { hideButtonSettings = !hideButtonSettings; + SelectedDevice = device; + DeviceCopy = device.CopyDevice(); + } + + private void OnSave() + { + if (hideButtonSettings || DeviceCopy is null || SelectedDevice is null) + { + + } + else + { + SelectedDevice.OverwiteDevice(DeviceCopy); + DeviceManagmentService.UpdateDevice(SelectedDevice); + } + } + + private async Task Delete(Device contextItem) + { + bool succ = await MessageService.Confirm($"Do you really want to delete the device \"{contextItem.Name}\"?"); + + if(!succ) + { + return; + } + + await DeviceManagmentService.UnregisterDevice(contextItem.MacAddress); + + int index = plugs.FindIndex(x => x.MacAddress == contextItem.MacAddress); + + if(index != -1) + { + plugs.RemoveAt(index); + } } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/TodoAppPage.razor b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/TodoAppPage.razor deleted file mode 100644 index ed5201a..0000000 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/TodoAppPage.razor +++ /dev/null @@ -1,3 +0,0 @@ -@using Elektrifikatsiya.Components.TodoApp -@page "/apps/todo" - \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs index a9408b5..6615bf5 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs @@ -8,6 +8,9 @@ using Elektrifikatsiya.Models; using Elektrifikatsiya.Services; using Elektrifikatsiya.Services.Implementations; +using HiveMQtt.Client; +using HiveMQtt.Client.Options; + using Microsoft.EntityFrameworkCore; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); @@ -18,6 +21,19 @@ builder.Services.AddServerSideBlazor(); builder.Services.AddHttpContextAccessor(); builder.Services.AddHostedService(); builder.Services.AddSingleton(); +builder.Services.AddSingleton((provider)=> +{ + HiveMQClientOptions options = new() + { + Host = "localhost", + Port = 1883, + UseTLS = false, + }; + + HiveMQClient client = new(options); + client.ConnectAsync().ConfigureAwait(false); + return client; +}); builder.Services.AddTransient(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -27,7 +43,7 @@ builder.Services.AddBootstrapProviders(); builder.Services.AddHttpClient(); builder.Services.AddBlazorise(options => { - options.Immediate = true; + options.Immediate = true; }); AddBlazorise(builder.Services); @@ -37,9 +53,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(); @@ -60,14 +76,14 @@ IAuthenticationService authenticationService = serviceScope.ServiceProvider.GetR if (!mainDatabase.Users.Any()) { - authenticationService.RegisterUserAsync("admin", "admin", Role.Admin); + _ = authenticationService.RegisterUserAsync("admin", "admin", Role.Admin); } app.Run(); void AddBlazorise(IServiceCollection services) { - _ = services.AddBlazorise(); - _ = services.AddMaterialProviders(); - _ = services.AddMaterialIcons(); + _ = services.AddBlazorise(); + _ = services.AddMaterialProviders(); + _ = services.AddMaterialIcons(); } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs index 3343c76..12e4bb1 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs @@ -3,9 +3,13 @@ using Elektrifikatsiya.Models; using FluentResults; +using HiveMQtt.Client; +using HiveMQtt.Client.Results; + +using Microsoft.EntityFrameworkCore; + using System.Net; using System.Net.NetworkInformation; -using Microsoft.EntityFrameworkCore; namespace Elektrifikatsiya.Services.Implementations; @@ -14,13 +18,15 @@ public class DeviceManagmentService : IDeviceManagmentService private readonly IServiceScopeFactory serviceScopeFactory; private readonly IDeviceStatusService deviceStatusService; private readonly ILogger logger; + private readonly IHiveMQClient hiveMQClient; private readonly HttpClient httpClient; - public DeviceManagmentService(IServiceScopeFactory serviceScopeFactory, IDeviceStatusService deviceStatusService, ILogger logger, HttpClient httpClient) + public DeviceManagmentService(IServiceScopeFactory serviceScopeFactory, IDeviceStatusService deviceStatusService, ILogger logger, IHiveMQClient hiveMQClient, HttpClient httpClient) { this.serviceScopeFactory = serviceScopeFactory; this.deviceStatusService = deviceStatusService; this.logger = logger; + this.hiveMQClient = hiveMQClient; this.httpClient = httpClient; } @@ -151,13 +157,22 @@ public class DeviceManagmentService : IDeviceManagmentService using IServiceScope scope = serviceScopeFactory.CreateScope(); MainDatabaseContext mainDatabaseContext = scope.ServiceProvider.GetRequiredService(); - Result result = deviceStatusService.UpdateDeviceStatus(device); + Result getDeviceResult = GetDevice(device.MacAddress); - if (result.IsFailed) + if (getDeviceResult.IsFailed) { - return result; + return getDeviceResult.ToResult(); } + Result updateDeviceResult = deviceStatusService.UpdateDeviceStatus(device); + + if (updateDeviceResult.IsFailed) + { + return updateDeviceResult; + } + + PublishResult res = await hiveMQClient.PublishAsync($"shellies/shellyplug-s-{device.MacAddress}/relay/0/command", device.Enabled ? "on" : "off"); + _ = mainDatabaseContext.Update(device); return await Result.Try(Task () => mainDatabaseContext.SaveChangesAsync()); diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs index 2e6e904..becc6f4 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs @@ -6,15 +6,17 @@ namespace Elektrifikatsiya.Services.Implementations; public class DeviceStatusService : IDeviceStatusService { - private readonly ILogger logger; - public event EventHandler? OnDeviceStatusChanged; + private readonly ILogger logger; + + public event EventHandler? OnDeviceStatusChanged; private readonly Dictionary devices = new(); - + public DeviceStatusService(ILogger logger) { - this.logger = logger; + this.logger = logger; } + public Result> GetDevices() { return devices.Values.ToList(); @@ -30,7 +32,7 @@ public class DeviceStatusService : IDeviceStatusService } modDevice.PowerUsage = device.PowerUsage; - modDevice.Available = device.Available; + modDevice.Enabled = device.Enabled; modDevice.IpAddress = device.IpAddress; modDevice.Name = device.Name; diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/UpdateService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/UpdateService.cs index a0b7ef0..0a3ef4a 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/UpdateService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/UpdateService.cs @@ -1,6 +1,7 @@ using Elektrifikatsiya.Database; using Elektrifikatsiya.Models; using Elektrifikatsiya.Utilities; + using FluentResults; using Microsoft.EntityFrameworkCore; @@ -9,75 +10,77 @@ namespace Elektrifikatsiya.Services.Implementations; public class UpdateService : IHostedService, IDisposable { - private readonly ILogger logger; - private readonly IDeviceStatusService deviceStatusService; - private readonly IServiceScopeFactory serviceScopeFactory; - private Timer? timer = null; + private readonly ILogger logger; + private readonly IDeviceStatusService deviceStatusService; + private readonly IServiceScopeFactory serviceScopeFactory; + private Timer? timer = null; - public UpdateService(ILogger logger, IDeviceStatusService deviceStatusService, IServiceScopeFactory serviceScopeFactory) - { - this.logger = logger; - this.deviceStatusService = deviceStatusService; - this.serviceScopeFactory = serviceScopeFactory; - } + public UpdateService(ILogger logger, IDeviceStatusService deviceStatusService, IServiceScopeFactory serviceScopeFactory) + { + this.logger = logger; + this.deviceStatusService = deviceStatusService; + this.serviceScopeFactory = serviceScopeFactory; + } - public async Task StartAsync(CancellationToken cancellationToken) - { - IServiceScope serviceScope = serviceScopeFactory.CreateScope(); - MainDatabaseContext mainDatabaseContext = serviceScope.ServiceProvider.GetRequiredService(); + public async Task StartAsync(CancellationToken cancellationToken) + { + IServiceScope serviceScope = serviceScopeFactory.CreateScope(); + MainDatabaseContext mainDatabaseContext = serviceScope.ServiceProvider.GetRequiredService(); - logger.LogInformation("Starting update service..."); + logger.LogInformation("Starting update service..."); - foreach (Device device in await mainDatabaseContext.Devices.Include(d=>d.User).AsNoTracking().ToListAsync(cancellationToken)) - { - _ = deviceStatusService.TrackDevice(device); - } + foreach (Device device in await mainDatabaseContext.Devices.Include(d => d.User).AsNoTracking().ToListAsync(cancellationToken)) + { + _ = deviceStatusService.TrackDevice(device); + } - timer = new Timer((_) => Update(), null, TimeSpan.Zero, TimeSpan.FromSeconds(15)); + timer = new Timer(async (_) => await Update(), null, TimeSpan.Zero, TimeSpan.FromSeconds(15)); - logger.LogInformation("Update service started."); - } + logger.LogInformation("Update service started."); + } - private async void Update() - { - Result> getDeviceStatusResult = deviceStatusService.GetDevices(); + private async Task Update() + { + Result> getDeviceStatusResult = deviceStatusService.GetDevices(); - if (getDeviceStatusResult.IsFailed) - { - logger.LogError("Updating devices failed!"); - } - PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090"); - foreach (Device device in getDeviceStatusResult.Value) - { - PrometheusDataWrapper? deviceData = (await promQueryer.Query($"power{{sensor=\"shellyplug-s-{device.MacAddress}/relay/0\"}}"))?.Data; + if (getDeviceStatusResult.IsFailed) + { + logger.LogError("Updating devices failed!"); + } + PrometheusQuery promQueryer = new PrometheusQuery("http://localhost:9090"); + foreach (Device device in getDeviceStatusResult.Value) + { + PrometheusDataWrapper? devicePowerData = (await promQueryer.Query($"power{{sensor=\"shellyplug-s-{device.MacAddress}/relay/0\"}}"))?.Data; + PrometheusDataWrapper? deviceStatusData = (await promQueryer.Query($"state{{sensor=\"shellyplug-s-{device.MacAddress}/relay\"}}"))?.Data; - if (deviceData is not null) - { - device.PowerUsage = deviceData.VectorTypeToTimestampFloatTuple()?.ValueOrDefault.Item2 ?? 0; - deviceStatusService.UpdateDeviceStatus(device); + if (devicePowerData is not null && deviceStatusData is not null) + { + device.PowerUsage = devicePowerData.VectorTypeToTimestampFloatTuple()?.ValueOrDefault.Item2 ?? 0; + device.Enabled = (deviceStatusData.VectorTypeToTimestampFloatTuple()?.ValueOrDefault.Item2 ?? 0) == 1; + _ = deviceStatusService.UpdateDeviceStatus(device); } } - } + } - public Task StopAsync(CancellationToken cancellationToken) - { - logger.LogInformation("Stopping update service."); + public Task StopAsync(CancellationToken cancellationToken) + { + logger.LogInformation("Stopping update service."); - _ = timer?.Change(Timeout.Infinite, 0); + _ = timer?.Change(Timeout.Infinite, 0); - logger.LogInformation("Stopped update service."); + logger.LogInformation("Stopped update service."); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected virtual void Dispose(bool disposing) - { - timer?.Dispose(); - } + protected virtual void Dispose(bool disposing) + { + timer?.Dispose(); + } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/UserDatabase.sqlite-shm b/src/Elektrifikatsiya/Elektrifikatsiya/UserDatabase.sqlite-shm deleted file mode 100644 index fe9ac28..0000000 Binary files a/src/Elektrifikatsiya/Elektrifikatsiya/UserDatabase.sqlite-shm and /dev/null differ diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/UserDatabase.sqlite-wal b/src/Elektrifikatsiya/Elektrifikatsiya/UserDatabase.sqlite-wal deleted file mode 100644 index e69de29..0000000 diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/PrometheusQuery.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/PrometheusQuery.cs index 456521f..c952d7f 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/PrometheusQuery.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/PrometheusQuery.cs @@ -10,18 +10,15 @@ namespace Elektrifikatsiya.Utilities; public class PrometheusQuery { - private string connectionString; private readonly HttpClient client = new(); public PrometheusQuery(string connectionString) { - this.connectionString = connectionString; client.BaseAddress = new Uri(connectionString); } public Task Query(string query) { - var res = client.GetStringAsync($"/api/v1/query?query={UrlEncoder.Create().Encode(query)}").Result; return client.GetFromJsonAsync($"/api/v1/query?query={UrlEncoder.Create().Encode(query)}", new JsonSerializerOptions() { PropertyNameCaseInsensitive = true,