diff --git a/docs/index.rst b/docs/index.rst index 4931c70..4a8b236 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -131,7 +131,7 @@ Automatic connection (mDNS) devices = dyson_account.devices() # Connect using discovery to the first device - connected = devices[0].connect() + connected = devices[0].auto_connect() # connected == device available, state values are available, sensor values are available @@ -155,7 +155,7 @@ Manual connection devices = dyson_account.devices() # Connect using discovery to the first device - connected = devices[0].connect(device_ip="192.168.1.2") + connected = devices[0].connect("192.168.1.2") # connected == device available, state values are available, sensor values are available diff --git a/libpurecoollink/dyson_360_eye.py b/libpurecoollink/dyson_360_eye.py index 3ae1746..e0e07ed 100644 --- a/libpurecoollink/dyson_360_eye.py +++ b/libpurecoollink/dyson_360_eye.py @@ -7,25 +7,25 @@ import paho.mqtt.client as mqtt -from .dyson_device import DysonDevice, NetworkDevice +from .dyson_device import DysonDevice, NetworkDevice, DEFAULT_PORT from .utils import printable_fields from .const import PowerMode, Dyson360EyeMode, Dyson360EyeCommand _LOGGER = logging.getLogger(__name__) -DEFAULT_PORT = 1883 - class Dyson360Eye(DysonDevice): """Dyson 360 Eye device.""" - def connect(self, device_ip): + def connect(self, device_ip, device_port=DEFAULT_PORT): """Try to connect to device. :param device_ip: Device IP address + :param device_port: Device Port (default: 1883) + :return: True if connected, else False """ self._network_device = NetworkDevice(self._name, device_ip, - DEFAULT_PORT) + device_port) self._mqtt = mqtt.Client(userdata=self, protocol=3) self._mqtt.username_pw_set(self._serial, self._credentials) diff --git a/libpurecoollink/dyson_device.py b/libpurecoollink/dyson_device.py index 54f9373..28aaa0f 100644 --- a/libpurecoollink/dyson_device.py +++ b/libpurecoollink/dyson_device.py @@ -22,6 +22,8 @@ 5: "Connection refused - not authorised" } +DEFAULT_PORT = 1883 + class NetworkDevice: """Network device.""" @@ -104,6 +106,16 @@ def connection_callback(self, connected): """Set function called when device is connected.""" self._connection_queue.put_nowait(connected) + @abc.abstractmethod + def connect(self, device_ip, device_port=DEFAULT_PORT): + """Connect to the device using ip address. + + :param device_ip: Device IP address + :param device_port: Device Port (default: 1883) + :return: True if connected, else False + """ + return + @property @abc.abstractmethod def status_topic(self): diff --git a/libpurecoollink/dyson_pure_cool_link.py b/libpurecoollink/dyson_pure_cool_link.py index a7fee7a..a36a251 100644 --- a/libpurecoollink/dyson_pure_cool_link.py +++ b/libpurecoollink/dyson_pure_cool_link.py @@ -12,7 +12,7 @@ import paho.mqtt.client as mqtt -from .dyson_device import DysonDevice, NetworkDevice +from .dyson_device import DysonDevice, NetworkDevice, DEFAULT_PORT from .utils import printable_fields, support_heating from .dyson_pure_state import DysonPureHotCoolState, DysonPureCoolState, \ DysonEnvironmentalSensorState @@ -20,8 +20,6 @@ _LOGGER = logging.getLogger(__name__) -DEFAULT_PORT = 1883 - class DysonPureCoolLink(DysonDevice): """Dyson device (fan).""" @@ -103,41 +101,47 @@ def on_message(client, userdata, msg): else: _LOGGER.warning("Unknown message: %s", payload) - def connect(self, on_message=None, device_ip=None, timeout=5, retry=15): - """Try to connect to device. - - If device_ip is provided, mDNS discovery step will be skipped. + def auto_connect(self, timeout=5, retry=15): + """Try to connect to device using mDNS. - :param on_message: On Message callback function - :param device_ip: Device IP address :param timeout: Timeout :param retry: Max retry + :return: True if connected, else False """ - if device_ip is None: - for i in range(retry): - zeroconf = Zeroconf() - listener = self.DysonDeviceListener(self._serial, - self._add_network_device) - ServiceBrowser(zeroconf, "_dyson_mqtt._tcp.local.", listener) - try: - self._network_device = self._search_device_queue.get( - timeout=timeout) - except Empty: - # Unable to find device - _LOGGER.warning("Unable to find device %s, try %s", - self._serial, i) - zeroconf.close() - else: - break - if self._network_device is None: - _LOGGER.error("Unable to connect to device %s", self._serial) - return False - else: - self._network_device = NetworkDevice(self._name, device_ip, - DEFAULT_PORT) + for i in range(retry): + zeroconf = Zeroconf() + listener = self.DysonDeviceListener(self._serial, + self._add_network_device) + ServiceBrowser(zeroconf, "_dyson_mqtt._tcp.local.", listener) + try: + self._network_device = self._search_device_queue.get( + timeout=timeout) + except Empty: + # Unable to find device + _LOGGER.warning("Unable to find device %s, try %s", + self._serial, i) + zeroconf.close() + else: + break + if self._network_device is None: + _LOGGER.error("Unable to connect to device %s", self._serial) + return False + return self._mqtt_connect() + + def connect(self, device_ip, device_port=DEFAULT_PORT): + """Connect to the device using ip address. - if on_message: - self._callback_message.append(on_message) + :param device_ip: Device IP address + :param device_port: Device Port (default: 1883) + :return: True if connected, else False + """ + self._network_device = NetworkDevice(self._name, device_ip, + device_port) + + return self._mqtt_connect() + + def _mqtt_connect(self): + """Connect to the MQTT broker.""" self._mqtt = mqtt.Client(userdata=self) self._mqtt.on_message = self.on_message self._mqtt.on_connect = self.on_connect @@ -159,7 +163,6 @@ def connect(self, on_message=None, device_ip=None, timeout=5, retry=15): self._device_available = True else: self._mqtt.loop_stop() - return self._connected def sensor_data_available(self): diff --git a/tests/test_libpurecoollink.py b/tests/test_libpurecoollink.py index 235f6d8..4a43127 100644 --- a/tests/test_libpurecoollink.py +++ b/tests/test_libpurecoollink.py @@ -131,7 +131,7 @@ def test_connect_device(self, mocked_connect, mocked_loop): device.sensor_data_available() device.connection_callback(True) device._add_network_device(network_device) - connected = device.connect(None) + connected = device.auto_connect() self.assertTrue(connected) self.assertIsNone(device.state) self.assertEqual(device.network_device, network_device) @@ -158,7 +158,7 @@ def test_connect_device_with_config(self, mocked_connect, mocked_loop): device.connection_callback(True) device.state_data_available() device.sensor_data_available() - connected = device.connect(Mock(), "192.168.0.2") + connected = device.connect("192.168.0.2") self.assertTrue(connected) self.assertIsNone(device.state) self.assertEqual(device.network_device.name, "device-1") @@ -189,7 +189,7 @@ def test_connect_device_with_config_failed(self, "ProductType": "475" }) device.connection_callback(False) - connected = device.connect(Mock(), "192.168.0.2") + connected = device.connect("192.168.0.2") self.assertFalse(connected) self.assertIsNone(device.state) self.assertEqual(device.network_device.name, "device-1") @@ -214,7 +214,7 @@ def test_connect_device_fail(self, mocked_close_zeroconf): "NewVersionAvailable": False, "ProductType": "475" }) - connected = device.connect(None, retry=1, timeout=1) + connected = device.auto_connect(retry=1, timeout=1) self.assertFalse(connected) self.assertEqual(mocked_close_zeroconf.call_count, 1) @@ -465,7 +465,7 @@ def test_set_configuration(self, mocked_connect, mocked_publish): device.connection_callback(True) device.state_data_available() device.sensor_data_available() - connected = device.connect(None) + connected = device.auto_connect() self.assertTrue(connected) self.assertEqual(mocked_connect.call_count, 1) device.set_configuration(fan_mode=FanMode.FAN, @@ -507,7 +507,7 @@ def test_set_configuration_hot(self, mocked_connect, mocked_publish): device.connection_callback(True) device.state_data_available() device.sensor_data_available() - connected = device.connect(None) + connected = device.auto_connect() self.assertTrue(connected) self.assertEqual(mocked_connect.call_count, 1) device.set_configuration(fan_mode=FanMode.FAN, @@ -553,7 +553,7 @@ def test_set_configuration_rst_filter(self, mocked_connect, device.connection_callback(True) device.state_data_available() device.sensor_data_available() - connected = device.connect(None) + connected = device.auto_connect() self.assertTrue(connected) self.assertEqual(mocked_connect.call_count, 1) device.set_configuration(fan_mode=FanMode.FAN, @@ -596,7 +596,7 @@ def test_set_configuration_timer(self, mocked_connect, mocked_publish): device.connection_callback(True) device.state_data_available() device.sensor_data_available() - connected = device.connect(None) + connected = device.auto_connect() self.assertTrue(connected) self.assertEqual(mocked_connect.call_count, 1) device.set_configuration(sleep_timer=10) @@ -632,7 +632,7 @@ def test_set_configuration_timer_off(self, mocked_connect, mocked_publish): device.connection_callback(True) device.state_data_available() device.sensor_data_available() - connected = device.connect(None) + connected = device.auto_connect() self.assertTrue(connected) self.assertEqual(mocked_connect.call_count, 1) device.set_configuration(sleep_timer=0) @@ -667,7 +667,7 @@ def test_dont_set_configuration_if_not_connected(self, mocked_connect, device._current_state = DysonPureCoolState( open("tests/data/state.json", "r").read()) device.connection_callback(False) - connected = device.connect(None) + connected = device.auto_connect() self.assertFalse(connected) self.assertEqual(mocked_connect.call_count, 1) device.set_configuration(fan_mode=FanMode.FAN,