using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using Rssdp.Infrastructure; namespace Rssdp { /// /// Base class representing the common details of a (root or embedded) device, either to be published or that has been located. /// /// /// Do not derive new types directly from this class. New device classes should derive from either or . /// /// /// public abstract class SsdpDevice { private string _Udn; private string _DeviceType; private string _DeviceTypeNamespace; private int _DeviceVersion; private IList _Devices; /// /// Raised when a new child device is added. /// /// /// public event EventHandler DeviceAdded; /// /// Raised when a child device is removed. /// /// /// public event EventHandler DeviceRemoved; /// /// Derived type constructor, allows constructing a device with no parent. Should only be used from derived types that are or inherit from . /// protected SsdpDevice() { _DeviceTypeNamespace = SsdpConstants.UpnpDeviceTypeNamespace; _DeviceType = SsdpConstants.UpnpDeviceTypeBasicDevice; _DeviceVersion = 1; _Devices = new List(); this.Devices = new ReadOnlyCollection(_Devices); } public SsdpRootDevice ToRootDevice() { var device = this; var rootDevice = device as SsdpRootDevice; if (rootDevice == null) { rootDevice = ((SsdpEmbeddedDevice)device).RootDevice; } return rootDevice; } /// /// Sets or returns the core device type (not including namespace, version etc.). Required. /// /// Defaults to the UPnP basic device type. /// /// /// public string DeviceType { get { return _DeviceType; } set { _DeviceType = value; } } public string DeviceClass { get; set; } /// /// Sets or returns the namespace for the of this device. Optional, but defaults to UPnP schema so should be changed if is not a UPnP device type. /// /// Defaults to the UPnP standard namespace. /// /// /// public string DeviceTypeNamespace { get { return _DeviceTypeNamespace; } set { _DeviceTypeNamespace = value; } } /// /// Sets or returns the version of the device type. Optional, defaults to 1. /// /// Defaults to a value of 1. /// /// /// public int DeviceVersion { get { return _DeviceVersion; } set { _DeviceVersion = value; } } /// /// Returns the full device type string. /// /// /// The format used is urn::device:: /// public string FullDeviceType { get { return String.Format( CultureInfo.InvariantCulture, "urn:{0}:{3}:{1}:{2}", this.DeviceTypeNamespace ?? String.Empty, this.DeviceType ?? String.Empty, this.DeviceVersion, this.DeviceClass ?? "device"); } } /// /// Sets or returns the universally unique identifier for this device (without the uuid: prefix). Required. /// /// /// Must be the same over time for a specific device instance (i.e. must survive reboots). /// For UPnP 1.0 this can be any unique string. For UPnP 1.1 this should be a 128 bit number formatted in a specific way, preferably generated using the time and MAC based algorithm. See section 1.1.4 of http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf for details. /// Technically this library implements UPnP 1.0, so any value is allowed, but we advise using UPnP 1.1 compatible values for good behaviour and forward compatibility with future versions. /// public string Uuid { get; set; } /// /// Returns (or sets*) a unique device name for this device. Optional, not recommended to be explicitly set. /// /// /// * In general you should not explicitly set this property. If it is not set (or set to null/empty string) the property will return a UDN value that is correct as per the UPnP specification, based on the other device properties. /// The setter is provided to allow for devices that do not correctly follow the specification (when we discover them), rather than to intentionally deviate from the specification. /// If a value is explicitly set, it is used verbatim, and so any prefix (such as uuid:) must be provided in the value. /// public string Udn { get { if (String.IsNullOrEmpty(_Udn) && !String.IsNullOrEmpty(this.Uuid)) { return "uuid:" + this.Uuid; } return _Udn; } set { _Udn = value; } } /// /// Sets or returns a friendly/display name for this device on the network. Something the user can identify the device/instance by, i.e Lounge Main Light. Required. /// /// A short description for the end user. public string FriendlyName { get; set; } /// /// Sets or returns the name of the manufacturer of this device. Required. /// public string Manufacturer { get; set; } /// /// Sets or returns a URL to the manufacturers web site. Optional. /// public Uri ManufacturerUrl { get; set; } /// /// Sets or returns a description of this device model. Recommended. /// /// A long description for the end user. public string ModelDescription { get; set; } /// /// Sets or returns the name of this model. Required. /// public string ModelName { get; set; } /// /// Sets or returns the number of this model. Recommended. /// public string ModelNumber { get; set; } /// /// Sets or returns a URL to a web page with details of this device model. Optional. /// /// /// Optional. May be relative to base URL. /// public Uri ModelUrl { get; set; } /// /// Sets or returns the serial number for this device. Recommended. /// public string SerialNumber { get; set; } /// /// Sets or returns the universal product code of the device, if any. Optional. /// /// /// If not blank, must be exactly 12 numeric digits. /// public string Upc { get; set; } /// /// Sets or returns the URL to a web page that can be used to configure/manager/use the device. Recommended. /// /// /// May be relative to base URL. /// public Uri PresentationUrl { get; set; } /// /// Returns a read-only enumerable set of objects representing children of this device. Child devices are optional. /// /// /// public IList Devices { get; private set; } /// /// Adds a child device to the collection. /// /// The instance to add. /// /// If the device is already a member of the collection, this method does nothing. /// Also sets the property of the added device and all descendant devices to the relevant instance. /// /// Thrown if the argument is null. /// Thrown if the is already associated with a different instance than used in this tree. Can occur if you try to add the same device instance to more than one tree. Also thrown if you try to add a device to itself. /// public void AddDevice(SsdpEmbeddedDevice device) { if (device == null) { throw new ArgumentNullException(nameof(device)); } if (device.RootDevice != null && device.RootDevice != this.ToRootDevice()) { throw new InvalidOperationException("This device is already associated with a different root device (has been added as a child in another branch)."); } if (device == this) { throw new InvalidOperationException("Can't add device to itself."); } bool wasAdded = false; lock (_Devices) { device.RootDevice = this.ToRootDevice(); _Devices.Add(device); wasAdded = true; } if (wasAdded) { OnDeviceAdded(device); } } /// /// Removes a child device from the collection. /// /// The instance to remove. /// /// If the device is not a member of the collection, this method does nothing. /// Also sets the property to null for the removed device and all descendant devices. /// /// Thrown if the argument is null. /// public void RemoveDevice(SsdpEmbeddedDevice device) { if (device == null) { throw new ArgumentNullException(nameof(device)); } bool wasRemoved = false; lock (_Devices) { wasRemoved = _Devices.Remove(device); if (wasRemoved) { device.RootDevice = null; } } if (wasRemoved) { OnDeviceRemoved(device); } } /// /// Raises the event. /// /// The instance added to the collection. /// /// protected virtual void OnDeviceAdded(SsdpEmbeddedDevice device) { var handlers = this.DeviceAdded; handlers?.Invoke(this, new DeviceEventArgs(device)); } /// /// Raises the event. /// /// The instance removed from the collection. /// /// protected virtual void OnDeviceRemoved(SsdpEmbeddedDevice device) { var handlers = this.DeviceRemoved; handlers?.Invoke(this, new DeviceEventArgs(device)); } } }