You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
4.7 KiB

4 years ago
  1. """Provides helpful stuff for discoverables."""
  2. # pylint: disable=abstract-method
  3. import ipaddress
  4. from urllib.parse import urlparse
  5. from ..const import (
  6. ATTR_NAME, ATTR_MODEL_NAME, ATTR_HOST, ATTR_PORT, ATTR_SSDP_DESCRIPTION,
  7. ATTR_SERIAL, ATTR_MODEL_NUMBER, ATTR_HOSTNAME, ATTR_MAC_ADDRESS,
  8. ATTR_PROPERTIES, ATTR_MANUFACTURER, ATTR_UDN, ATTR_UPNP_DEVICE_TYPE)
  9. class BaseDiscoverable:
  10. """Base class for discoverable services or device types."""
  11. def is_discovered(self):
  12. """Return True if it is discovered."""
  13. return len(self.get_entries()) > 0
  14. def get_info(self):
  15. """Return a list with the important info for each item.
  16. Uses self.info_from_entry internally.
  17. """
  18. return [self.info_from_entry(entry) for entry in self.get_entries()]
  19. # pylint: disable=no-self-use
  20. def info_from_entry(self, entry):
  21. """Return an object with important info from the entry."""
  22. return entry
  23. def get_entries(self):
  24. """Return all the discovered entries."""
  25. raise NotImplementedError()
  26. class SSDPDiscoverable(BaseDiscoverable):
  27. """uPnP discoverable base class."""
  28. def __init__(self, netdis):
  29. """Initialize SSDPDiscoverable."""
  30. self.netdis = netdis
  31. def info_from_entry(self, entry):
  32. """Get most important info."""
  33. url = urlparse(entry.location)
  34. info = {
  35. ATTR_HOST: url.hostname,
  36. ATTR_PORT: url.port,
  37. ATTR_SSDP_DESCRIPTION: entry.location
  38. }
  39. device = entry.description.get('device')
  40. if device:
  41. info[ATTR_NAME] = device.get('friendlyName')
  42. info[ATTR_MODEL_NAME] = device.get('modelName')
  43. info[ATTR_MODEL_NUMBER] = device.get('modelNumber')
  44. info[ATTR_SERIAL] = device.get('serialNumber')
  45. info[ATTR_MANUFACTURER] = device.get('manufacturer')
  46. info[ATTR_UDN] = device.get('UDN')
  47. info[ATTR_UPNP_DEVICE_TYPE] = device.get('deviceType')
  48. return info
  49. # Helper functions
  50. # pylint: disable=invalid-name
  51. def find_by_st(self, st):
  52. """Find entries by ST (the device identifier)."""
  53. return self.netdis.ssdp.find_by_st(st)
  54. def find_by_device_description(self, values):
  55. """Find entries based on values from their description."""
  56. return self.netdis.ssdp.find_by_device_description(values)
  57. class MDNSDiscoverable(BaseDiscoverable):
  58. """mDNS Discoverable base class."""
  59. def __init__(self, netdis, typ):
  60. """Initialize MDNSDiscoverable."""
  61. self.netdis = netdis
  62. self.typ = typ
  63. self.services = {}
  64. netdis.mdns.register_service(self)
  65. def reset(self):
  66. """Reset found services."""
  67. self.services.clear()
  68. # pylint: disable=unused-argument
  69. def remove_service(self, zconf, typ, name):
  70. """Callback when a service is removed."""
  71. self.services.pop(name, None)
  72. def add_service(self, zconf, typ, name):
  73. """Callback when a service is found."""
  74. service = None
  75. tries = 0
  76. while service is None and tries < 3:
  77. service = zconf.get_service_info(typ, name)
  78. tries += 1
  79. if service is not None:
  80. self.services[name] = service
  81. def get_entries(self):
  82. """Return all found services."""
  83. return self.services.values()
  84. def info_from_entry(self, entry):
  85. """Return most important info from mDNS entries."""
  86. properties = {}
  87. for key, value in entry.properties.items():
  88. if isinstance(value, bytes):
  89. value = value.decode('utf-8')
  90. properties[key.decode('utf-8')] = value
  91. info = {
  92. ATTR_HOST: str(ipaddress.ip_address(entry.address)),
  93. ATTR_PORT: entry.port,
  94. ATTR_HOSTNAME: entry.server,
  95. ATTR_PROPERTIES: properties,
  96. }
  97. if "mac" in properties:
  98. info[ATTR_MAC_ADDRESS] = properties["mac"]
  99. return info
  100. def find_by_device_name(self, name):
  101. """Find entries based on the beginning of their entry names."""
  102. return [entry for entry in self.services.values()
  103. if entry.name.startswith(name)]
  104. class GDMDiscoverable(BaseDiscoverable):
  105. """GDM discoverable base class."""
  106. def __init__(self, netdis):
  107. """Initialize GDMDiscoverable."""
  108. self.netdis = netdis
  109. def find_by_content_type(self, value):
  110. """Find entries based on values from their content_type."""
  111. return self.netdis.gdm.find_by_content_type(value)
  112. def find_by_data(self, values):
  113. """Find entries based on values from any returned field."""
  114. return self.netdis.gdm.find_by_data(values)