using System.Net; using System.Net.NetworkInformation; namespace SddpViewer; using System; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using Rssdp; public partial class DiscoveredDeviceViewModel : ObservableObject, IDisposable { private readonly DiscoveredSsdpDevice _device; private SsdpDevice? _ssdpDevice; private readonly CancellationTokenSource _cancellationTokenSource = new(); public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device) { _device = device; ResponseHeader = GetResponseHeader(); DiscoveredAt = DateTime.Now; IpAddress = EvaluateIpAddress(device); MacAddress = EvaluateMacAddress(); HostName = EvaluateHostName(); RunPing(_cancellationTokenSource.Token); } private async void RunPing(CancellationToken token) { try { using Ping ping = new(); while (!token.IsCancellationRequested) { var result = await ping.SendPingAsync(IpAddress, TimeSpan.FromSeconds(1), cancellationToken: token).ConfigureAwait(true); Online = result.Status == IPStatus.Success; await Task.Delay(1000, token).ConfigureAwait(true); } } catch (TaskCanceledException e) { Console.WriteLine(e); } catch (OperationCanceledException e) { Console.WriteLine(e); } } public string HostName { get; set; } public string MacAddress { get; set; } public IPAddress IpAddress { get; set; } public DateTime DiscoveredAt { get; } [ObservableProperty] public partial string ResponseHeader { get; set; } [ObservableProperty] public partial bool Online { 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; /// /// Sets or returns the universal service name (USN) of the device. /// public string Usn => _device.Usn; /// /// Sets or returns a URL pointing to the device description document for this device. /// public Uri DescriptionLocation => _device.DescriptionLocation; /// /// Sets or returns the length of time this information is valid for (from the time). /// public TimeSpan CacheLifetime => _device.CacheLifetime; /// /// Sets or returns the date and time this information was received. /// public DateTimeOffset AsAt => _device.AsAt; [ObservableProperty] public partial string FriendlyName { get; set; } = ""; [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 void Cancel() { _cancellationTokenSource.Cancel(); } public async Task GetFurtherInformationAsync() { _ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false); FriendlyName = _ssdpDevice.FriendlyName; Icon = _ssdpDevice.Icons.MinBy(x => x.Height); PresentationUrl = _ssdpDevice.PresentationUrl; ModelNumber = _ssdpDevice.ModelNumber; Version = _ssdpDevice.SerialNumber?.Split(',').Last() ?? new Version().ToString(); } private string EvaluateHostName() => Dns.GetHostEntry(IpAddress).HostName; private string GetResponseHeader() { return _device.ResponseHeaders is null ? "" : string.Join("," + Environment.NewLine, _device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}")); } private IPAddress EvaluateIpAddress(DiscoveredSsdpDevice device) => IPAddress.TryParse(device.DescriptionLocation.Host, out var value) ? value : IPAddress.Any; private string EvaluateMacAddress() { if (!ArpLookup.Arp.IsSupported) return "not supported"; var lookupResult = ArpLookup.Arp.Lookup(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); } public void Dispose() { _cancellationTokenSource.Cancel(); _cancellationTokenSource.Dispose(); } }