示例#1
0
    def setUp(self):
        self.patchers = []
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.pexpect.spawn'))
        self.spawn = self.patchers[0].start()
        self.spawn.return_value.isalive.return_value = False
        self.spawn.return_value.read_nonblocking.side_effect = (
            pexpect.EOF(value=None))
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.subprocess'))
        self.patchers[1].start()

        # Just keep saying we got the "Connected" response
        def rate_limited_expect(*args, **kwargs):
            # Sleep a little bit to stop the receive thread from spinning and
            # eating 100% CPU during the tests
            time.sleep(0.001)
            # This is hacky, but we sort the event list in the GATTTool receiver
            # and hard code where we expect the "Connected" event to be.
            return 4

        self.spawn.return_value.expect.side_effect = rate_limited_expect

        self.backend = GATTToolBackend()
        self.backend.start()
 def setUp(self):
     raise SkipTest()
     self.patchers = []
     self.patchers.append(
         patch('pygatt.backends.gatttool.gatttool.pexpect.spawn'))
     self.spawn = self.patchers[0].start()
     self.spawn.return_value.isalive.return_value = False
     self.patchers.append(
         patch('pygatt.backends.gatttool.gatttool.subprocess'))
     self.patchers[1].start()
     self.backend = GATTToolBackend()
     self.mock_expect = patch.object(self.backend, '_expect').start()
     self.backend.start()
示例#3
0
 def run(self):
     """Thread that keeps connection alive."""
     cached_char = Characteristic(BLE_TEMP_UUID, BLE_TEMP_HANDLE)
     adapter = GATTToolBackend()
     while True:
         try:
             _LOGGER.debug("Connecting to %s", self.name)
             # We need concurrent connect, so lets not reset the device
             adapter.start(reset_on_start=False)
             # Seems only one connection can be initiated at a time
             with CONNECT_LOCK:
                 device = adapter.connect(
                     self.mac, CONNECT_TIMEOUT, BLEAddressType.random
                 )
             if SKIP_HANDLE_LOOKUP:
                 # HACK: inject handle mapping collected offline
                 # pylint: disable=protected-access
                 device._characteristics[UUID(BLE_TEMP_UUID)] = cached_char
             # Magic: writing this makes device happy
             device.char_write_handle(0x1B, bytearray([255]), False)
             device.subscribe(BLE_TEMP_UUID, self._update)
             _LOGGER.info("Subscribed to %s", self.name)
             while self.keep_going:
                 # protect against stale connections, just read temperature
                 device.char_read(BLE_TEMP_UUID, timeout=CONNECT_TIMEOUT)
                 self.event.wait(60)
             break
         except (BLEError, NotConnectedError, NotificationTimeout) as ex:
             _LOGGER.error("Exception: %s ", str(ex))
         finally:
             adapter.stop()
class GATTToolBackendTests(unittest.TestCase):
    def setUp(self):
        raise SkipTest()
        self.patchers = []
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.pexpect.spawn'))
        self.spawn = self.patchers[0].start()
        self.spawn.return_value.isalive.return_value = False
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.subprocess'))
        self.patchers[1].start()
        self.backend = GATTToolBackend()
        self.mock_expect = patch.object(self.backend, '_expect').start()
        self.backend.start()

    def tearDown(self):
        self.backend.stop()
        for patcher in self.patchers:
            patcher.stop()

    def test_scan(self):
        # TODO mock a successful scan
        devices = self.backend.scan()
        ok_(devices is not None)
        eq_(0, len(devices))

    def test_connect(self):
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        ok_(device is not None)
示例#5
0
    def setUp(self):
        self.patchers = []
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.pexpect.spawn'))
        self.spawn = self.patchers[0].start()
        self.spawn.return_value.isalive.return_value = False
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.subprocess'))
        self.patchers[1].start()

        # Just keep saying we got the "Connected" response
        def rate_limited_expect(*args, **kwargs):
            # Sleep a little bit to stop the receive thread from spinning and
            # eating 100% CPU during the tests
            time.sleep(0.001)
            # This is hacky, but we sort the event list in the GATTTool receiver
            # and hard code where we expect the "Connected" event to be.
            return 4

        self.spawn.return_value.expect.side_effect = rate_limited_expect

        self.backend = GATTToolBackend()
        self.backend.start()
示例#6
0
class GATTToolBackendTests(unittest.TestCase):
    def setUp(self):
        self.patchers = []
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.pexpect.spawn'))
        self.spawn = self.patchers[0].start()
        self.spawn.return_value.isalive.return_value = False
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.subprocess'))
        self.patchers[1].start()

        # Just keep saying we got the "Connected" response
        def rate_limited_expect(*args, **kwargs):
            # Sleep a little bit to stop the receive thread from spinning and
            # eating 100% CPU during the tests
            time.sleep(0.001)
            # This is hacky, but we sort the event list in the GATTTool receiver
            # and hard code where we expect the "Connected" event to be.
            return 4

        self.spawn.return_value.expect.side_effect = rate_limited_expect

        self.backend = GATTToolBackend()
        self.backend.start()

    def tearDown(self):
        self.backend.stop()
        for patcher in self.patchers:
            patcher.stop()

    def test_scan(self):
        # TODO mock a successful scan
        devices = self.backend.scan()
        ok_(devices is not None)
        eq_(0, len(devices))

    def test_connect(self):
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        ok_(device is not None)

    def test_disconnect_callback(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        mock_callback = MagicMock()
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.register_disconnect_callback(mock_callback)
        eq_(
            mock_callback
            in device._backend._receiver._event_vector["disconnected"]
            ["callback"], True)

        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        ok_(mock_callback.called)

        device.remove_disconnect_callback(mock_callback)
        eq_(
            mock_callback
            not in device._backend._receiver._event_vector["disconnected"]
            ["callback"], True)
        eq_(
            len(device._backend._receiver._event_vector["disconnected"]
                ["callback"]) > 0, True)

    def test_auto_reconnect_call(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        address = "11:22:33:44:55:66"
        device = self.backend.connect(address, auto_reconnect=True)
        device._backend.reconnect = MagicMock()
        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        ok_(device._backend.reconnect.called)

    def test_no_reconnect_default(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device._backend.reconnect = MagicMock()
        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        ok_(not device._backend.reconnect.called)

    def test_no_reconnect_disconnect(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        address = "11:22:33:44:55:66"
        device = self.backend.connect(address, auto_reconnect=True)
        device._backend.reconnect = MagicMock()
        device.disconnect()
        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        ok_(not device._backend.reconnect.called)

    def test_auto_reconnect(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        # Just keep saying we got the "Connected" response
        def rate_limited_expect_c(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Connected" event
            return 4

        address = "11:22:33:44:55:66"
        device = self.backend.connect(address, auto_reconnect=True)
        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        device.resubscribe_all = MagicMock()
        self.spawn.return_value.expect.side_effect = rate_limited_expect_c
        time.sleep(0.1)
        ok_(device.resubscribe_all.called)

    def test_single_byte_notification(self):
        event = {
            'after': "Notification handle = 0x0024 value: 64".encode("utf8")
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(device.receive_notification.called)
        eq_(0x24, device.receive_notification.call_args[0][0])
        eq_(bytearray([0x64]), device.receive_notification.call_args[0][1])

    def test_multi_byte_notification(self):
        event = {
            'after':
            ("Notification handle = 0x0024 value: 64 46 72".encode("utf8"))
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(device.receive_notification.called)
        eq_(0x24, device.receive_notification.call_args[0][0])
        eq_(bytearray([0x64, 0x46, 0x72]),
            device.receive_notification.call_args[0][1])

    def test_empty_notification(self):
        event = {
            'after': "Notification handle = 0x0024 value: ".encode("utf8")
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(device.receive_notification.called)

    def test_malformed_notification(self):
        event = {'after': "Notification handle = 0x0024vlue: ".encode("utf8")}
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(not device.receive_notification.called)

    def test_indication(self):
        event = {
            'after': "Indication   handle = 0x0024 value: 64".encode("utf8")
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(device.receive_notification.called)
        eq_(0x24, device.receive_notification.call_args[0][0])
        eq_(bytearray([0x64]), device.receive_notification.call_args[0][1])
示例#7
0
class GATTToolBackendTests(unittest.TestCase):
    def setUp(self):
        self.patchers = []
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.pexpect.spawn'))
        self.spawn = self.patchers[0].start()
        self.spawn.return_value.isalive.return_value = False
        self.patchers.append(
            patch('pygatt.backends.gatttool.gatttool.subprocess'))
        self.patchers[1].start()

        # Just keep saying we got the "Connected" response
        def rate_limited_expect(*args, **kwargs):
            # Sleep a little bit to stop the receive thread from spinning and
            # eating 100% CPU during the tests
            time.sleep(0.001)
            # This is hacky, but we sort the event list in the GATTTool receiver
            # and hard code where we expect the "Connected" event to be.
            return 4

        self.spawn.return_value.expect.side_effect = rate_limited_expect

        self.backend = GATTToolBackend()
        self.backend.start()

    def tearDown(self):
        self.backend.stop()
        for patcher in self.patchers:
            patcher.stop()

    def test_scan(self):
        # TODO mock a successful scan
        devices = self.backend.scan()
        ok_(devices is not None)
        eq_(0, len(devices))

    def test_connect(self):
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        ok_(device is not None)

    def test_disconnect_callback(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
                time.sleep(0.001)
                # hard code the "Disconnected" event
                return 1

        mock_callback = MagicMock()
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.register_disconnect_callback(mock_callback)
        eq_(mock_callback in
            device._backend._receiver._event_vector[
                "disconnected"]["callback"],
            True)

        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        ok_(mock_callback.called)

        device.remove_disconnect_callback(mock_callback)
        eq_(mock_callback not in
            device._backend._receiver._event_vector[
                "disconnected"]["callback"],
            True)
        eq_(len(device._backend._receiver._event_vector[
                "disconnected"]["callback"]) > 0,
            True)

    def test_auto_reconnect_call(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        address = "11:22:33:44:55:66"
        device = self.backend.connect(address, auto_reconnect=True)
        device._backend.reconnect = MagicMock()
        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        ok_(device._backend.reconnect.called)

    def test_no_reconnect_default(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device._backend.reconnect = MagicMock()
        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        ok_(not device._backend.reconnect.called)

    def test_no_reconnect_disconnect(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        address = "11:22:33:44:55:66"
        device = self.backend.connect(address, auto_reconnect=True)
        device._backend.reconnect = MagicMock()
        device.disconnect()
        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        ok_(not device._backend.reconnect.called)

    def test_auto_reconnect(self):
        # Just keep saying we got the "Disconnected" response
        def rate_limited_expect_d(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Disconnected" event
            return 1

        # Just keep saying we got the "Connected" response
        def rate_limited_expect_c(*args, **kwargs):
            time.sleep(0.001)
            # hard code the "Connected" event
            return 4

        address = "11:22:33:44:55:66"
        device = self.backend.connect(address, auto_reconnect=True)
        self.spawn.return_value.expect.side_effect = rate_limited_expect_d
        time.sleep(0.1)
        device.resubscribe_all = MagicMock()
        self.spawn.return_value.expect.side_effect = rate_limited_expect_c
        time.sleep(0.1)
        ok_(device.resubscribe_all.called)

    def test_single_byte_notification(self):
        event = {
            'after': "Notification handle = 0x0024 value: 64".encode("utf8")
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(device.receive_notification.called)
        eq_(0x24, device.receive_notification.call_args[0][0])
        eq_(bytearray([0x64]), device.receive_notification.call_args[0][1])

    def test_multi_byte_notification(self):
        event = {
            'after': (
                "Notification handle = 0x0024 value: 64 46 72".encode("utf8"))
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(device.receive_notification.called)
        eq_(0x24, device.receive_notification.call_args[0][0])
        eq_(bytearray([0x64, 0x46, 0x72]),
            device.receive_notification.call_args[0][1])

    def test_empty_notification(self):
        event = {
            'after': "Notification handle = 0x0024 value: ".encode("utf8")
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(device.receive_notification.called)

    def test_malformed_notification(self):
        event = {
            'after': "Notification handle = 0x0024vlue: ".encode("utf8")
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(not device.receive_notification.called)

    def test_indication(self):
        event = {
            'after': "Indication   handle = 0x0024 value: 64".encode("utf8")
        }
        address = "11:22:33:44:55:66"
        device = self.backend.connect(address)
        device.receive_notification = MagicMock()
        device._backend._handle_notification_string(event)
        ok_(device.receive_notification.called)
        eq_(0x24, device.receive_notification.call_args[0][0])
        eq_(bytearray([0x64]), device.receive_notification.call_args[0][1])