summaryrefslogtreecommitdiff
path: root/netdisco/service.py
blob: 3cfb6b1e0585a31e2d4337df789bc000d7d9c6fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""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")