diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/AddPlug.razor b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/AddPlug.razor index efabc58..c6e1f26 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Pages/AddPlug.razor +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Pages/AddPlug.razor @@ -2,13 +2,18 @@ @using Blazorise.Components; @using Elektrifikatsiya.Models; @using System.Net +@using Elektrifikatsiya.Services +@using Elektrifikatsiya.Services.Implementations +@using Elektrifikatsiya.Utilities @inject IVersionProvider VersionProvider +@inject IDeviceManagmentService DeviceManagmentService +@inject IAuthenticationService AuthenticationService @@ -18,17 +23,17 @@ - @context.Item.Name + @context.Item - IP: @context.Item.IpAddress + IP: @context.Item - @@ -40,5 +45,14 @@ @code { - List plugs = new List() { new Device("ffff:ffff:ffff:ffff", "Huso", IPAddress.Broadcast, new User("Joe", "qiowruioewjfopa", Role.Admin), "Raum"), new Device("ffff:ffff:ffff:ffff", "Huso", IPAddress.Broadcast, new User("Joe", "qiowruioewjfopa", Role.Admin), "Raum"), new Device("ffff:ffff:ffff:ffff", "Huso", IPAddress.Broadcast, new User("Joe", "qiowruioewjfopa", Role.Admin), "Raum"), }; + List ipAddresses = new List(); + + protected override async Task OnInitializedAsync() + { + MdnsDiscovery.OnDeviceFound += address => + { + ipAddresses.Add(address); + InvokeAsync(StateHasChanged); + }; + } } diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs index 4f9d13e..3c21487 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Program.cs @@ -15,16 +15,20 @@ WebApplicationBuilder builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); +builder.Services.AddHttpContextAccessor(); builder.Services.AddHostedService(); builder.Services.AddSingleton(); builder.Services.AddTransient(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddDbContext(options => options.UseSqlite("Data Source=./UserDatabase.sqlite")); builder.Services.AddDbContext((options) => options.UseSqlite("Data Source=./DeviceManagement.sqlite")); builder.Services.AddBootstrapProviders(); builder.Services.AddHttpClient(); builder.Services.AddBlazorise(options => { - options.Immediate = true; + options.Immediate = true; }); AddBlazorise(builder.Services); @@ -34,9 +38,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(); @@ -46,12 +50,11 @@ app.UseStaticFiles(); app.UseRouting(); app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); +app.MapControllers(); IServiceScope serviceScope = app.Services.GetRequiredService().CreateScope(); serviceScope.ServiceProvider.GetRequiredService().Database.EnsureCreated(); -app.MapControllers(); - AsyncServiceScope scope = app.Services.CreateAsyncScope(); scope.ServiceProvider.GetRequiredService().Database.EnsureCreated(); @@ -59,7 +62,7 @@ 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/Properties/launchSettings.json b/src/Elektrifikatsiya/Elektrifikatsiya/Properties/launchSettings.json index 71c02c5..fe359ad 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Properties/launchSettings.json +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Properties/launchSettings.json @@ -7,7 +7,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7297;http://localhost:5255" + "applicationUrl": "https://localhost:56656;http://localhost:5255" }, "IIS Express": { "commandName": "IISExpress", diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceManagmentService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceManagmentService.cs index bda0e67..764ee22 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceManagmentService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceManagmentService.cs @@ -8,9 +8,9 @@ namespace Elektrifikatsiya.Services; public interface IDeviceManagmentService { - public Task> Register(IPAddress ip, User user, string? name = null, string room = "default"); + public Task> RegisterDevice(IPAddress ip, User user, string? name = null, string room = "default"); - public Task Unregister(string macAdress); + public Task UnregisterDevice(string macAdress); public Result GetDevice(string macAdress); @@ -19,4 +19,6 @@ public interface IDeviceManagmentService public Result> GetDevicesInRoom(string room); public Result> GetDevicesOfUser(int userId); + + public Task UpdateDevice(Device device); } \ 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 7cb9954..11937e6 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs @@ -10,123 +10,138 @@ namespace Elektrifikatsiya.Services.Implementations; public class DeviceManagmentService : IDeviceManagmentService { - private readonly DeviceManagmentDatabaseContext deviceManagmentDatabaseContext; - private readonly IDeviceStatusService deviceStatusService; - private readonly IServiceScopeFactory serviceScopeFactory; - private readonly HttpClient httpClient; + private readonly DeviceManagmentDatabaseContext deviceManagmentDatabaseContext; + private readonly IServiceScopeFactory serviceScopeFactory; + private readonly IDeviceStatusService deviceStatusService; + private readonly HttpClient httpClient; - public DeviceManagmentService(IServiceScopeFactory serviceScopeFactory, IDeviceStatusService deviceStatusService, HttpClient httpClient) - { - this.deviceManagmentDatabaseContext = deviceManagmentDatabaseContext; - this.deviceStatusService = deviceStatusService; - this.httpClient = httpClient; - } + public DeviceManagmentService(IServiceScopeFactory serviceScopeFactory, IDeviceStatusService deviceStatusService, HttpClient httpClient) + { + this.serviceScopeFactory = serviceScopeFactory; + this.deviceStatusService = deviceStatusService; + this.httpClient = httpClient; - public Result GetDevice(string macAdress) - { - Result> getDevicesResult = deviceStatusService.GetDevices(); + } - if (getDevicesResult.IsFailed) - { - return getDevicesResult.ToResult(); - } + public Result GetDevice(string macAdress) + { + Result> getDevicesResult = deviceStatusService.GetDevices(); - Device? device = getDevicesResult.Value.FirstOrDefault(d => d.MacAddress == macAdress); + if (getDevicesResult.IsFailed) + { + return getDevicesResult.ToResult(); + } - if (device is null) - { - return Result.Fail("Device does not exist!"); - } + Device? device = getDevicesResult.Value.FirstOrDefault(d => d.MacAddress == macAdress); - return device; - } + if (device is null) + { + return Result.Fail("Device does not exist!"); + } - public Result> GetDevices() - { - Result> getDevicesResult = deviceStatusService.GetDevices(); + return device; + } - if (getDevicesResult.IsFailed) - { - return getDevicesResult.ToResult(); - } + public Result> GetDevices() + { + Result> getDevicesResult = deviceStatusService.GetDevices(); - return getDevicesResult.Value.ToList(); - } + if (getDevicesResult.IsFailed) + { + return getDevicesResult.ToResult(); + } - public Result> GetDevicesInRoom(string room) - { - Result> getDevicesResult = deviceStatusService.GetDevices(); + return getDevicesResult.Value.ToList(); + } - if (getDevicesResult.IsFailed) - { - return getDevicesResult.ToResult(); - } + public Result> GetDevicesInRoom(string room) + { + Result> getDevicesResult = deviceStatusService.GetDevices(); - return getDevicesResult.Value.Where(d => d.Room == room).ToList(); - } + if (getDevicesResult.IsFailed) + { + return getDevicesResult.ToResult(); + } - public Result> GetDevicesOfUser(int userId) - { - Result> getDevicesResult = deviceStatusService.GetDevices(); + return getDevicesResult.Value.Where(d => d.Room == room).ToList(); + } - if (getDevicesResult.IsFailed) - { - return getDevicesResult.ToResult(); - } + public Result> GetDevicesOfUser(int userId) + { + Result> getDevicesResult = deviceStatusService.GetDevices(); - return getDevicesResult.Value.Where(d => d.User.Id == userId).ToList(); - } + if (getDevicesResult.IsFailed) + { + return getDevicesResult.ToResult(); + } - public async Task> Register(IPAddress ip, User user, string? name = null, string room = "default") - { - using IServiceScope scope = serviceScopeFactory.CreateScope(); - DeviceManagmentDatabaseContext deviceManagmentDatabaseContext = scope.ServiceProvider.GetRequiredService(); + return getDevicesResult.Value.Where(d => d.User.Id == userId).ToList(); + } - ShellyResponse? shellyResponse = await httpClient.GetFromJsonAsync($"{ip}/shelly"); + public async Task> RegisterDevice(IPAddress ip, User user, string? name = null, string room = "default") + { + using IServiceScope scope = serviceScopeFactory.CreateScope(); + DeviceManagmentDatabaseContext deviceManagmentDatabaseContext = scope.ServiceProvider.GetRequiredService(); - if (shellyResponse is null || shellyResponse.Type != "SHPLG-S") - { - return Result.Fail("Device is not a \"SHPLG-S\" or not reachable!"); - } + ShellyResponse? shellyResponse = await httpClient.GetFromJsonAsync($"http://{ip}/shelly"); - string mac = shellyResponse.Mac; + if (shellyResponse is null || shellyResponse.Type != "SHPLG-S") + { + return Result.Fail("Device is not reachable or not a \"SHPLG-S\"!"); + } - if (PhysicalAddress.TryParse(mac, out _)) - { - return Result.Fail("Invalid mac address!"); - } + string mac = shellyResponse.Mac; - Device device = new Device(mac, name ?? mac, ip, user, room); + if (!PhysicalAddress.TryParse(mac, out _)) + { + return Result.Fail("Invalid mac address!"); + } - deviceManagmentDatabaseContext.Add(device); - Result saveDatabaseChangesResult = await Result.Try(async Task () => await deviceManagmentDatabaseContext.SaveChangesAsync()); + Device device = new Device(mac, name ?? mac, ip, user, room); - Result trackDeviceResult = deviceStatusService.TrackDevice(device); + deviceManagmentDatabaseContext.Add(device); + Result saveDatabaseChangesResult = await Result.Try(Task () => deviceManagmentDatabaseContext.SaveChangesAsync()); - return Result.Merge(saveDatabaseChangesResult, trackDeviceResult).ToResult(device); - } + Result trackDeviceResult = deviceStatusService.TrackDevice(device); - public async Task Unregister(string macAdress) - { - using IServiceScope scope = serviceScopeFactory.CreateScope(); - DeviceManagmentDatabaseContext deviceManagmentDatabaseContext = scope.ServiceProvider.GetRequiredService(); + return Result.Merge(saveDatabaseChangesResult, trackDeviceResult).ToResult(device); + } + + public async Task UnregisterDevice(string macAdress) + { + using IServiceScope scope = serviceScopeFactory.CreateScope(); + DeviceManagmentDatabaseContext deviceManagmentDatabaseContext = scope.ServiceProvider.GetRequiredService(); Result getDeviceResult = GetDevice(macAdress); - if (getDeviceResult.IsFailed) - { - return getDeviceResult.ToResult(); - } + if (getDeviceResult.IsFailed) + { + return getDeviceResult.ToResult(); + } - Result untrackDeviceResult = deviceStatusService.UntrackDevice(macAdress); + Result untrackDeviceResult = deviceStatusService.UntrackDevice(macAdress); - if (untrackDeviceResult.IsFailed) - { - return untrackDeviceResult; - } + if (untrackDeviceResult.IsFailed) + { + return untrackDeviceResult; + } - deviceManagmentDatabaseContext.Remove(getDeviceResult.Value); + deviceManagmentDatabaseContext.Remove(getDeviceResult.Value); - return await Result.Try(async Task () => await deviceManagmentDatabaseContext.SaveChangesAsync()); - } + return await Result.Try(Task () => deviceManagmentDatabaseContext.SaveChangesAsync()); + } + + public async Task UpdateDevice(Device device) + { + Result result = deviceStatusService.UpdateDeviceStatus(device); + + if (result.IsFailed) + { + return result; + } + + deviceManagmentDatabaseContext.Update(device); + + return await Result.Try(Task () => deviceManagmentDatabaseContext.SaveChangesAsync()); + } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs index 3574cec..2e6e904 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs @@ -6,10 +6,15 @@ namespace Elektrifikatsiya.Services.Implementations; public class DeviceStatusService : IDeviceStatusService { - public event EventHandler? OnDeviceStatusChanged; + private readonly ILogger logger; + public event EventHandler? OnDeviceStatusChanged; private readonly Dictionary devices = new(); - + + public DeviceStatusService(ILogger logger) + { + this.logger = logger; + } public Result> GetDevices() { return devices.Values.ToList(); @@ -41,6 +46,8 @@ public class DeviceStatusService : IDeviceStatusService return Result.Fail("Device already tracked!"); } + logger.LogInformation("Tracking new Device {device}", device.IpAddress); + devices[device.MacAddress] = device; return Result.Ok(); diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/MdnsDiscovery.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/MdnsDiscovery.cs new file mode 100644 index 0000000..8b9c46a --- /dev/null +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/MdnsDiscovery.cs @@ -0,0 +1,20 @@ +using System.Net; +using FluentResults; +using Tmds.MDns; + +namespace Elektrifikatsiya.Utilities; + +public static class MdnsDiscovery +{ + public static event Action OnDeviceFound; + + static ServiceBrowser serviceBrowser = new ServiceBrowser(); + + static MdnsDiscovery() + { + serviceBrowser.StartBrowse("_http._tcp"); + serviceBrowser.ServiceAdded += (_, eventArgs) => OnDeviceFound?.Invoke(eventArgs.Announcement.Addresses.First()); + serviceBrowser.ServiceChanged += (_, eventArgs) => OnDeviceFound?.Invoke(eventArgs.Announcement.Addresses.First()); + serviceBrowser.ServiceRemoved += (_, eventArgs) => OnDeviceFound?.Invoke(eventArgs.Announcement.Addresses.First()); + } +} \ No newline at end of file