SddpViewer/src/MainWindowViewModel.cs
2025-04-04 08:58:03 +02:00

159 lines
4.5 KiB
C#

using System.Collections.Specialized;
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(x => x.IpAddress.ToString() == DeviceIpAddress);
SddpDevices = [];
SddpDevices.CollectionChanged += SddpDevices_OnCollectionChanged;
}
private static async void SddpDevices_OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
await AddAction(e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemoveAction(e.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
RemoveAction(e.OldItems);
await AddAction(e.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
RemoveAction(sender as ObservableCollection<DiscoveredDeviceViewModel>);
break;
case NotifyCollectionChangedAction.Move:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private static void RemoveAction(IList? list)
{
foreach (object eOldItem in list ?? Array.Empty<object>())
{
if (eOldItem is DiscoveredDeviceViewModel discoveredDeviceViewModel)
{
discoveredDeviceViewModel.Dispose();
}
}
}
private static async Task AddAction(IList? list)
{
foreach (object eNewItem in list ?? Array.Empty<object>())
{
if (eNewItem is DiscoveredDeviceViewModel discoveredDeviceViewModel)
{
await discoveredDeviceViewModel.GetFurtherInformationAsync();
}
}
}
private void LocatorOnDeviceUnavailable(object? sender, DeviceUnavailableEventArgs e)
{
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 void LocatorOnDeviceAvailable(object? sender, DeviceAvailableEventArgs e)
{
if (!e.IsNewlyDiscovered)
{
return;
}
var discoveredDeviceViewModel = new DiscoveredDeviceViewModel(e.DiscoveredDevice);
if (!SddpDevices.Contains(discoveredDeviceViewModel))
Dispatcher.UIThread.Invoke(() => SddpDevices.Add(discoveredDeviceViewModel));
}
[ObservableProperty]
public partial IReadOnlyList<NetworkAdapter> NetworkAdapters { get; set; }
[ObservableProperty]
public partial NetworkAdapter? SelectedNetworkAdapter { get; set; }
[ObservableProperty]
public partial string DeviceIpAddress { get; set; } = "192.168.42.192";
[ObservableProperty]
public partial string NotificationFilter { get; set; } = "upnp:rootdevice";
private SsdpDeviceLocator? _locator;
public ObservableCollection<DiscoveredDeviceViewModel> SddpDevices { get; }
[RelayCommand]
private async Task SearchDevicesNowAsync()
{
SddpDevices.Clear();
if (_locator is not null)
{
_locator.StopListeningForNotifications();
_locator.DeviceAvailable -= LocatorOnDeviceAvailable;
_locator.DeviceUnavailable -= LocatorOnDeviceUnavailable;
_locator.Dispose();
}
await StartListening(async locator =>
{
foreach (var discoveredSsdpDevice in await locator.SearchAsync())
{
var discoveredDeviceViewModel = new DiscoveredDeviceViewModel(discoveredSsdpDevice);
Dispatcher.UIThread.Invoke(() => SddpDevices.Add(discoveredDeviceViewModel));
}
});
}
[RelayCommand]
private async Task ResearchAsync()
{
if (_locator is not null)
{
await _locator.SearchAsync();
}
}
[RelayCommand]
private async Task StartListening(Func<SsdpDeviceLocator, Task>? action = null)
{
_locator = new SsdpDeviceLocator(SelectedNetworkAdapter?.IpAddress.ToString());
if (!string.IsNullOrWhiteSpace(NotificationFilter))
{
_locator.NotificationFilter = NotificationFilter;
}
if (action is not null)
{
await action.Invoke(_locator);
}
_locator.DeviceAvailable += LocatorOnDeviceAvailable;
_locator.DeviceUnavailable += LocatorOnDeviceUnavailable;
_locator.StartListeningForNotifications();
}
public void Dispose()
{
_locator?.Dispose();
}
}