"""Provide service that scans the network in intervals."""
|
|
import logging
|
|
import threading
|
|
import time
|
|
from collections import defaultdict
|
|
|
|
from .discovery import NetworkDiscovery
|
|
|
|
DEFAULT_INTERVAL = 300 # seconds
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class DiscoveryService(threading.Thread):
|
|
"""Service that will scan the network for devices each `interval` seconds.
|
|
|
|
Add listeners to the service to be notified of new services found.
|
|
"""
|
|
|
|
def __init__(self, interval=DEFAULT_INTERVAL):
|
|
"""Initialize the discovery."""
|
|
super(DiscoveryService, self).__init__()
|
|
|
|
# Scanning interval
|
|
self.interval = interval
|
|
|
|
# Listeners for new services
|
|
self.listeners = []
|
|
|
|
# To track when we have to stop
|
|
self._stop = threading.Event()
|
|
|
|
# Tell Python not to wait till this thread exits
|
|
self.daemon = True
|
|
|
|
# The discovery object
|
|
self.discovery = None
|
|
|
|
# Dict to keep track of found services. We do not want to
|
|
# broadcast the same found service twice.
|
|
self._found = defaultdict(list)
|
|
|
|
def add_listener(self, listener):
|
|
"""Add a listener for new services."""
|
|
self.listeners.append(listener)
|
|
|
|
def stop(self):
|
|
"""Stop the service."""
|
|
self._stop.set()
|
|
|
|
def run(self):
|
|
"""Start the discovery service."""
|
|
self.discovery = NetworkDiscovery()
|
|
|
|
while True:
|
|
self._scan()
|
|
|
|
seconds_since_scan = 0
|
|
|
|
while seconds_since_scan < self.interval:
|
|
if self._stop.is_set():
|
|
return
|
|
|
|
time.sleep(1)
|
|
seconds_since_scan += 1
|
|
|
|
def _scan(self):
|
|
"""Scan for new devices."""
|
|
_LOGGER.info("Scanning")
|
|
self.discovery.scan()
|
|
|
|
for disc in self.discovery.discover():
|
|
for service in self.discovery.get_info(disc):
|
|
self._service_found(disc, service)
|
|
|
|
self.discovery.stop()
|
|
|
|
def _service_found(self, disc, service):
|
|
"""Tell listeners a service was found."""
|
|
if service not in self._found[disc]:
|
|
self._found[disc].append(service)
|
|
|
|
for listener in self.listeners:
|
|
try:
|
|
listener(disc, service)
|
|
except Exception: # pylint: disable=broad-except
|
|
_LOGGER.exception(
|
|
"Error calling listener")
|