|
2 | 2 |
|
3 | 3 | # pylint: disable=too-many-public-methods,too-many-instance-attributes |
4 | 4 |
|
5 | | -from queue import Queue |
| 5 | +from queue import Queue, Empty |
6 | 6 | import logging |
7 | 7 | import json |
8 | 8 | import abc |
9 | 9 | import time |
| 10 | +import socket |
10 | 11 |
|
11 | 12 | from .utils import printable_fields |
12 | 13 | from .utils import decrypt_password |
| 14 | +from .zeroconf import ServiceBrowser, Zeroconf |
13 | 15 |
|
14 | 16 | _LOGGER = logging.getLogger(__name__) |
15 | 17 |
|
@@ -64,6 +66,41 @@ def __repr__(self): |
64 | 66 | class DysonDevice: |
65 | 67 | """Abstract Dyson device.""" |
66 | 68 |
|
| 69 | + class DysonDeviceListener: |
| 70 | + """Message listener.""" |
| 71 | + |
| 72 | + def __init__(self, serial, add_device_function, serial_from_name): |
| 73 | + """Create a new message listener. |
| 74 | +
|
| 75 | + :param serial: Device serial |
| 76 | + :param add_device_function: Callback function |
| 77 | + """ |
| 78 | + self._serial = serial |
| 79 | + self.add_device_function = add_device_function |
| 80 | + self.serial_from_name = serial_from_name |
| 81 | + |
| 82 | + def remove_service(self, zeroconf, device_type, name): |
| 83 | + # pylint: disable=unused-argument,no-self-use |
| 84 | + """Remove listener.""" |
| 85 | + _LOGGER.info("Service %s removed", name) |
| 86 | + |
| 87 | + def add_service(self, zeroconf, device_type, name): |
| 88 | + """Add device. |
| 89 | +
|
| 90 | + :param zeroconf: MSDNS object |
| 91 | + :param device_type: Service type |
| 92 | + :param name: Device name |
| 93 | + """ |
| 94 | + device_serial = self.serial_from_name(name) |
| 95 | + if device_serial == self._serial: |
| 96 | + # Find searched device |
| 97 | + info = zeroconf.get_service_info(device_type, name) |
| 98 | + address = socket.inet_ntoa(info.address) |
| 99 | + network_device = NetworkDevice(device_serial, address, |
| 100 | + info.port) |
| 101 | + self.add_device_function(network_device) |
| 102 | + zeroconf.close() |
| 103 | + |
67 | 104 | @staticmethod |
68 | 105 | def on_connect(client, userdata, flags, return_code): |
69 | 106 | # pylint: disable=unused-argument |
@@ -119,6 +156,35 @@ def connect(self, device_ip, device_port=DEFAULT_PORT): |
119 | 156 | """ |
120 | 157 | return |
121 | 158 |
|
| 159 | + def _auto_connect(self, type_, timeout=5, retry=15): |
| 160 | + """Try to connect to device using mDNS.""" |
| 161 | + for i in range(retry): |
| 162 | + zeroconf = Zeroconf() |
| 163 | + listener = self.DysonDeviceListener(self._serial, |
| 164 | + self._add_network_device, |
| 165 | + self._device_serial_from_name) |
| 166 | + ServiceBrowser(zeroconf, type_, listener) |
| 167 | + try: |
| 168 | + self._network_device = self._search_device_queue.get( |
| 169 | + timeout=timeout) |
| 170 | + except Empty: |
| 171 | + # Unable to find device |
| 172 | + _LOGGER.warning("Unable to find device %s, try %s", |
| 173 | + self._serial, i) |
| 174 | + zeroconf.close() |
| 175 | + else: |
| 176 | + break |
| 177 | + if self._network_device is None: |
| 178 | + _LOGGER.error("Unable to connect to device %s", self._serial) |
| 179 | + return False |
| 180 | + return self._mqtt_connect() |
| 181 | + |
| 182 | + @staticmethod |
| 183 | + @abc.abstractmethod |
| 184 | + def _device_serial_from_name(name): |
| 185 | + """Get device serial from mDNS name.""" |
| 186 | + return |
| 187 | + |
122 | 188 | @property |
123 | 189 | @abc.abstractmethod |
124 | 190 | def status_topic(self): |
|
0 commit comments