diff --git a/.editorconfig b/.editorconfig
index 3eb2a12..5d745b0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,9 +14,9 @@ indent_size = 2
#### Core EditorConfig Options ####
# Indentation and spacing
-indent_size = 4
-tab_width = 4
-
+indent_size = 2
+tab_width = 2
+max_line_length = 140
# New line preferences
end_of_line = crlf
insert_final_newline = false
diff --git a/src/App.axaml.cs b/src/App.axaml.cs
index 707af5c..9d3f700 100644
--- a/src/App.axaml.cs
+++ b/src/App.axaml.cs
@@ -5,18 +5,18 @@ namespace SddpViewer;
public partial class App : Application
{
- public override void Initialize()
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
- AvaloniaXamlLoader.Load(this);
+ desktop.MainWindow = new MainWindow();
}
- public override void OnFrameworkInitializationCompleted()
- {
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- desktop.MainWindow = new MainWindow();
- }
-
- base.OnFrameworkInitializationCompleted();
- }
+ base.OnFrameworkInitializationCompleted();
+ }
}
diff --git a/src/DiscoveredDeviceViewModel.cs b/src/DiscoveredDeviceViewModel.cs
index 2d694fe..616d53f 100644
--- a/src/DiscoveredDeviceViewModel.cs
+++ b/src/DiscoveredDeviceViewModel.cs
@@ -1,75 +1,106 @@
-namespace SddpViewer;
+using System.Net;
+
+namespace SddpViewer;
using System;
using System.Threading.Tasks;
-
using CommunityToolkit.Mvvm.ComponentModel;
-
using Rssdp;
public partial class DiscoveredDeviceViewModel : ObservableObject
{
- private readonly DiscoveredSsdpDevice _device;
+ private readonly DiscoveredSsdpDevice _device;
+ private SsdpDevice? _ssdpDevice;
- public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device)
- {
- _device = device;
- ResponseHeader = GetResponseHeader();
- }
+ public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device)
+ {
+ _device = device;
+ ResponseHeader = GetResponseHeader();
+ DiscoveredAt = DateTime.Now;
+ IpAddress = EvaluateIpAddress(device);
+ MacAddress = EvaluateMacAddress();
+ HostName = EvaluateHostName();
+ }
- private string GetResponseHeader()
- {
- return string.Join(
- "," + Environment.NewLine,
- _device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}")
- );
- }
+ public string HostName { get; set; }
- public string ResponseHeader { get; }
+ public string MacAddress { get; set; }
- ///
- /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
- ///
- public string NotificationType => _device.NotificationType;
+ public string IpAddress { get; set; }
- ///
- /// Sets or returns the universal service name (USN) of the device.
- ///
- public string Usn => _device.Usn;
+ public DateTime DiscoveredAt { get; }
- ///
- /// Sets or returns a URL pointing to the device description document for this device.
- ///
- public Uri DescriptionLocation => _device.DescriptionLocation;
+ [ObservableProperty]
+ public partial string ResponseHeader { get; set; }
- ///
- /// Sets or returns the length of time this information is valid for (from the time).
- ///
- public TimeSpan CacheLifetime => _device.CacheLifetime;
+ ///
+ /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
+ ///
+ public string NotificationType => _device.NotificationType;
- ///
- /// Sets or returns the date and time this information was received.
- ///
- public DateTimeOffset AsAt => _device.AsAt;
+ ///
+ /// Sets or returns the universal service name (USN) of the device.
+ ///
+ public string Usn => _device.Usn;
- [ObservableProperty]
- private string _friendlyName = "";
+ ///
+ /// Sets or returns a URL pointing to the device description document for this device.
+ ///
+ public Uri DescriptionLocation => _device.DescriptionLocation;
- [ObservableProperty]
- private SsdpDeviceIcon? _icon;
+ ///
+ /// Sets or returns the length of time this information is valid for (from the time).
+ ///
+ public TimeSpan CacheLifetime => _device.CacheLifetime;
- [ObservableProperty]
- private Uri? _presentationUrl;
+ ///
+ /// Sets or returns the date and time this information was received.
+ ///
+ public DateTimeOffset AsAt => _device.AsAt;
- [ObservableProperty]
- private string _modelNumber = "";
+ [ObservableProperty]
+ public partial string FriendlyName { get; set; } = "";
- public async Task GetFurtherInformationAsync()
- {
- var ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false);
- FriendlyName = ssdpDevice.FriendlyName;
- Icon = ssdpDevice.Icons.MinBy(x => x.Height);
- PresentationUrl = ssdpDevice.PresentationUrl;
- ModelNumber = ssdpDevice.ModelNumber;
- }
+ [ObservableProperty]
+ public partial SsdpDeviceIcon? Icon { get; set; }
+
+ [ObservableProperty]
+ public partial Uri? PresentationUrl { get; set; }
+
+ [ObservableProperty]
+ public partial string ModelNumber { get; set; } = "";
+
+ [ObservableProperty]
+ public partial string Version { get; set; } = "";
+
+ public async Task GetFurtherInformationAsync()
+ {
+ _ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false);
+ FriendlyName = _ssdpDevice.ModelDescription;
+ Icon = _ssdpDevice.Icons.MinBy(x => x.Height);
+ PresentationUrl = _ssdpDevice.PresentationUrl;
+ ModelNumber = _ssdpDevice.ModelNumber;
+ Version = _ssdpDevice.SerialNumber?.Split(',').Last() ?? new Version().ToString();
+ HttpClient client = new HttpClient();
+ var response = await client.GetAsync(_ssdpDevice.ModelUrl).ConfigureAwait(false);
+ ResponseHeader = await response.Content.ReadAsStringAsync();
+ }
+
+ private string EvaluateHostName() => Dns.GetHostEntry(IpAddress).HostName;
+
+ private string GetResponseHeader() =>
+ string.Join("," + Environment.NewLine, _device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}"));
+
+ private string EvaluateIpAddress(DiscoveredSsdpDevice device) => device.DescriptionLocation.Host;
+
+ private string EvaluateMacAddress()
+ {
+ var lookupResult = ArpLookup.Arp.Lookup(IPAddress.Parse(IpAddress));
+ return lookupResult is null ? "Unknown" : string.Join(":", lookupResult.GetAddressBytes().Select(b => $"{b:x2}"));
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return obj is DiscoveredDeviceViewModel viewModel && Equals(viewModel.Usn, Usn);
+ }
}
diff --git a/src/MainWindow.axaml b/src/MainWindow.axaml
index c9ad6bf..e2b8b73 100644
--- a/src/MainWindow.axaml
+++ b/src/MainWindow.axaml
@@ -14,7 +14,7 @@
-
+
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
diff --git a/src/MainWindow.axaml.cs b/src/MainWindow.axaml.cs
index dc452c7..3a5d282 100644
--- a/src/MainWindow.axaml.cs
+++ b/src/MainWindow.axaml.cs
@@ -4,8 +4,8 @@ namespace SddpViewer;
public partial class MainWindow : Window
{
- public MainWindow()
- {
- InitializeComponent();
- }
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
}
diff --git a/src/MainWindowViewModel.cs b/src/MainWindowViewModel.cs
index cad0bd2..2f05d94 100644
--- a/src/MainWindowViewModel.cs
+++ b/src/MainWindowViewModel.cs
@@ -1,85 +1,129 @@
-namespace SddpViewer;
+using System.Collections.Specialized;
-using Avalonia.Threading;
+namespace SddpViewer;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
-
+using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
-
using Rssdp;
public sealed partial class MainWindowViewModel : ObservableObject, IDisposable
{
- public MainWindowViewModel()
- {
- NetworkAdapters = NetworkAdapter.GetAvailableNetworkAdapter().ToArray();
- SelectedNetworkAdapter = NetworkAdapters.FirstOrDefault();
- }
+ public MainWindowViewModel()
+ {
+ NetworkAdapters = NetworkAdapter.GetAvailableNetworkAdapter().ToArray();
+ SelectedNetworkAdapter = NetworkAdapters.FirstOrDefault();
+ SddpDevices = new ObservableCollection();
+ SddpDevices.CollectionChanged += SddpDevices_OnCollectionChanged;
+ }
- private void LocatorOnDeviceUnavailable(object? sender, DeviceUnavailableEventArgs e)
+ private static async void SddpDevices_OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ try
{
- var existingDevice = SddpDevices.FirstOrDefault(
- x => string.Equals(x.Usn, e.DiscoveredDevice.Usn, StringComparison.Ordinal)
- );
- if (existingDevice is null)
- return;
- Dispatcher.UIThread.Invoke(() => SddpDevices.Remove(existingDevice));
- }
-
- private async void LocatorOnDeviceAvailable(object? sender, DeviceAvailableEventArgs e)
- {
- if (!e.IsNewlyDiscovered)
+ foreach (object eNewItem in e.NewItems ?? Array.Empty