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.

88 lines
2.4 KiB

4 years ago
  1. """Provide service that scans the network in intervals."""
  2. import logging
  3. import threading
  4. import time
  5. from collections import defaultdict
  6. from .discovery import NetworkDiscovery
  7. DEFAULT_INTERVAL = 300 # seconds
  8. _LOGGER = logging.getLogger(__name__)
  9. class DiscoveryService(threading.Thread):
  10. """Service that will scan the network for devices each `interval` seconds.
  11. Add listeners to the service to be notified of new services found.
  12. """
  13. def __init__(self, interval=DEFAULT_INTERVAL):
  14. """Initialize the discovery."""
  15. super(DiscoveryService, self).__init__()
  16. # Scanning interval
  17. self.interval = interval
  18. # Listeners for new services
  19. self.listeners = []
  20. # To track when we have to stop
  21. self._stop = threading.Event()
  22. # Tell Python not to wait till this thread exits
  23. self.daemon = True
  24. # The discovery object
  25. self.discovery = None
  26. # Dict to keep track of found services. We do not want to
  27. # broadcast the same found service twice.
  28. self._found = defaultdict(list)
  29. def add_listener(self, listener):
  30. """Add a listener for new services."""
  31. self.listeners.append(listener)
  32. def stop(self):
  33. """Stop the service."""
  34. self._stop.set()
  35. def run(self):
  36. """Start the discovery service."""
  37. self.discovery = NetworkDiscovery()
  38. while True:
  39. self._scan()
  40. seconds_since_scan = 0
  41. while seconds_since_scan < self.interval:
  42. if self._stop.is_set():
  43. return
  44. time.sleep(1)
  45. seconds_since_scan += 1
  46. def _scan(self):
  47. """Scan for new devices."""
  48. _LOGGER.info("Scanning")
  49. self.discovery.scan()
  50. for disc in self.discovery.discover():
  51. for service in self.discovery.get_info(disc):
  52. self._service_found(disc, service)
  53. self.discovery.stop()
  54. def _service_found(self, disc, service):
  55. """Tell listeners a service was found."""
  56. if service not in self._found[disc]:
  57. self._found[disc].append(service)
  58. for listener in self.listeners:
  59. try:
  60. listener(disc, service)
  61. except Exception: # pylint: disable=broad-except
  62. _LOGGER.exception(
  63. "Error calling listener")