diff --git a/.idea/.idea.SddpViewer/.idea/.gitignore b/.idea/.idea.SddpViewer/.idea/.gitignore
new file mode 100644
index 0000000..85bf2d1
--- /dev/null
+++ b/.idea/.idea.SddpViewer/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/modules.xml
+/projectSettingsUpdater.xml
+/.idea.SddpViewer.iml
+/contentModel.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.SddpViewer/.idea/avalonia.xml b/.idea/.idea.SddpViewer/.idea/avalonia.xml
new file mode 100644
index 0000000..cad2c12
--- /dev/null
+++ b/.idea/.idea.SddpViewer/.idea/avalonia.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.SddpViewer/.idea/encodings.xml b/.idea/.idea.SddpViewer/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.SddpViewer/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.SddpViewer/.idea/indexLayout.xml b/.idea/.idea.SddpViewer/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.SddpViewer/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.SddpViewer/.idea/vcs.xml b/.idea/.idea.SddpViewer/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/.idea.SddpViewer/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/App.axaml.cs b/App.axaml.cs
index 8b7ca73..ec292cc 100644
--- a/App.axaml.cs
+++ b/App.axaml.cs
@@ -20,4 +20,4 @@ public partial class App : Application
base.OnFrameworkInitializationCompleted();
}
-}
\ No newline at end of file
+}
diff --git a/DiscoveredDeviceViewModel.cs b/DiscoveredDeviceViewModel.cs
index 897ebc8..68ac14a 100644
--- a/DiscoveredDeviceViewModel.cs
+++ b/DiscoveredDeviceViewModel.cs
@@ -1,53 +1,53 @@
namespace SddpViewer
{
- using System;
- using System.Threading.Tasks;
+ using System;
+ using System.Threading.Tasks;
- using CommunityToolkit.Mvvm.ComponentModel;
+ using CommunityToolkit.Mvvm.ComponentModel;
- using Rssdp;
+ using Rssdp;
- public partial class DiscoveredDeviceViewModel : ObservableObject
- {
- private readonly DiscoveredSsdpDevice device;
-
- public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device)
+ public partial class DiscoveredDeviceViewModel : ObservableObject
{
- this.device = device;
+ private readonly DiscoveredSsdpDevice device;
+
+ public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device)
+ {
+ this.device = device;
+ }
+
+ ///
+ /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
+ ///
+ public string NotificationType => this.device.NotificationType;
+
+ ///
+ /// Sets or returns the universal service name (USN) of the device.
+ ///
+ public string Usn => this.device.Usn;
+
+ ///
+ /// Sets or returns a URL pointing to the device description document for this device.
+ ///
+ public Uri DescriptionLocation => this.device.DescriptionLocation;
+
+ ///
+ /// Sets or returns the length of time this information is valid for (from the time).
+ ///
+ public TimeSpan CacheLifetime => this.device.CacheLifetime;
+
+ ///
+ /// Sets or returns the date and time this information was received.
+ ///
+ public DateTimeOffset AsAt => this.device.AsAt;
+
+ [ObservableProperty]
+ private string _friendlyName = "";
+
+ public async Task GetFurtherInformationAsync()
+ {
+ var ssdpDevice = await this.device.GetDeviceInfo().ConfigureAwait(false);
+ FriendlyName = ssdpDevice.FriendlyName;
+ }
}
-
- ///
- /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
- ///
- public string NotificationType => this.device.NotificationType;
-
- ///
- /// Sets or returns the universal service name (USN) of the device.
- ///
- public string Usn => this.device.Usn;
-
- ///
- /// Sets or returns a URL pointing to the device description document for this device.
- ///
- public Uri DescriptionLocation => this.device.DescriptionLocation;
-
- ///
- /// Sets or returns the length of time this information is valid for (from the time).
- ///
- public TimeSpan CacheLifetime => this.device.CacheLifetime;
-
- ///
- /// Sets or returns the date and time this information was received.
- ///
- public DateTimeOffset AsAt => this.device.AsAt;
-
- [ObservableProperty]
- private string _friendlyName = "";
-
- public async Task GetFurtherInformationAsync()
- {
- var ssdpDevice = await this.device.GetDeviceInfo().ConfigureAwait(false);
- FriendlyName = ssdpDevice.FriendlyName;
- }
- }
-}
\ No newline at end of file
+}
diff --git a/MainWindow.axaml b/MainWindow.axaml
index 76c9f27..ccc749d 100644
--- a/MainWindow.axaml
+++ b/MainWindow.axaml
@@ -1,35 +1,34 @@
+ xmlns="https://github.com/avaloniaui"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:sddpViewer="clr-namespace:SddpViewer"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
-
-
-
+
+
+
+
diff --git a/MainWindow.axaml.cs b/MainWindow.axaml.cs
index befcecb..dc452c7 100644
--- a/MainWindow.axaml.cs
+++ b/MainWindow.axaml.cs
@@ -8,4 +8,4 @@ public partial class MainWindow : Window
{
InitializeComponent();
}
-}
\ No newline at end of file
+}
diff --git a/MainWindowViewModel.cs b/MainWindowViewModel.cs
index bbfbabb..866d293 100644
--- a/MainWindowViewModel.cs
+++ b/MainWindowViewModel.cs
@@ -1,4 +1,10 @@
-namespace SddpViewer;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+
+namespace SddpViewer;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
@@ -10,35 +16,53 @@ using Rssdp;
public partial class MainWindowViewModel : ObservableObject
{
- [ObservableProperty]
- private string _deviceIpAddress = "192.168.42.193";
-
- [ObservableProperty]
- private string _notificationFilter = "upnp:rootdevice";
-
- public ObservableCollection SddpDevices { get; } = new();
-
- [RelayCommand(AllowConcurrentExecutions = false)]
- private async Task SearchDevicesNowAsync()
- {
- SddpDevices.Clear();
- var locator = new SsdpDeviceLocator(DeviceIpAddress);
- if (!string.IsNullOrWhiteSpace(NotificationFilter))
+ public MainWindowViewModel()
{
- locator.NotificationFilter = NotificationFilter;
+ NetworkAdapters = NetworkAdapter.GetAvailableNetworkAdapter().ToArray();
+ SelectedNetworkAdapter = NetworkAdapters.FirstOrDefault();
}
- var availableDevices = await locator.SearchAsync().ConfigureAwait(true);
- foreach (var ssdpDevice in availableDevices)
+
+ [ObservableProperty]
+ private IReadOnlyList _networkAdapters;
+
+ [ObservableProperty]
+ private NetworkAdapter? _selectedNetworkAdapter;
+
+ [ObservableProperty]
+ private string _deviceIpAddress = "192.168.42.193";
+
+ [ObservableProperty]
+ private string _notificationFilter = "upnp:rootdevice";
+
+ public ObservableCollection SddpDevices { get; } = new();
+
+ [RelayCommand(AllowConcurrentExecutions = false)]
+ private async Task SearchDevicesNowAsync()
{
- var discoveredDeviceViewModel = new DiscoveredDeviceViewModel(ssdpDevice);
- SddpDevices.Add(discoveredDeviceViewModel);
+ SddpDevices.Clear();
+ if (SelectedNetworkAdapter is null)
+ {
+ throw new NotSupportedException();
+ }
+ var locator = new SsdpDeviceLocator(SelectedNetworkAdapter.IpAddress.ToString());
+ if (!string.IsNullOrWhiteSpace(NotificationFilter))
+ {
+ locator.NotificationFilter = NotificationFilter;
+ }
+ var availableDevices = await locator.SearchAsync().ConfigureAwait(true);
+ foreach (var ssdpDevice in availableDevices)
+ {
+ var discoveredDeviceViewModel = new DiscoveredDeviceViewModel(ssdpDevice);
+ SddpDevices.Add(discoveredDeviceViewModel);
+ }
+ await Parallel
+ .ForEachAsync(
+ SddpDevices,
+ async (device, token) =>
+ {
+ await device.GetFurtherInformationAsync().ConfigureAwait(true);
+ }
+ )
+ .ConfigureAwait(true);
}
- await Parallel.ForEachAsync(
- SddpDevices,
- async (device, token) =>
- {
- await device.GetFurtherInformationAsync().ConfigureAwait(true);
- }
- ).ConfigureAwait(true);
- }
}
diff --git a/NetworkAdapter.cs b/NetworkAdapter.cs
new file mode 100644
index 0000000..b4ccbf7
--- /dev/null
+++ b/NetworkAdapter.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+
+namespace SddpViewer;
+
+public record NetworkAdapter(string Name, IPAddress IpAddress)
+{
+ public string DisplayName => $"{Name} - {IpAddress}";
+
+ public static IEnumerable GetAvailableNetworkAdapter()
+ {
+ foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
+ {
+ if (nic.OperationalStatus != OperationalStatus.Up)
+ continue;
+ if (nic.NetworkInterfaceType == NetworkInterfaceType.Loopback)
+ continue;
+ var physicalAddress = nic.GetIPProperties()
+ .UnicastAddresses.FirstOrDefault(
+ x => x.Address.AddressFamily == AddressFamily.InterNetwork
+ );
+ if (physicalAddress is not null)
+ {
+ yield return new NetworkAdapter(nic.Name, physicalAddress.Address);
+ }
+ }
+ }
+}
diff --git a/Program.cs b/Program.cs
index b64bcf5..b52fa6d 100644
--- a/Program.cs
+++ b/Program.cs
@@ -9,13 +9,10 @@ class Program
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
- public static void Main(string[] args) => BuildAvaloniaApp()
- .StartWithClassicDesktopLifetime(args);
+ public static void Main(string[] args) =>
+ BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
- public static AppBuilder BuildAvaloniaApp()
- => AppBuilder.Configure()
- .UsePlatformDetect()
- .WithInterFont()
- .LogToTrace();
+ public static AppBuilder BuildAvaloniaApp() =>
+ AppBuilder.Configure().UsePlatformDetect().WithInterFont().LogToTrace();
}