コード例 #1
0
ファイル: test_login.py プロジェクト: vvynohradov/coretools
def test_get_fleet():
    """Make sure we can get fleets."""

    auth_payload = {
        'jwt': 'big-token',
        'username': '******'
    }
    test_payload = {"count":1,
              "next":"Null",
              "previous":"Null",
              "results":[{"device":"d--0000-0000-0000-0001","always_on":True,"is_access_point":False}]}

    expected = {
        "d--0000-0000-0000-0001":{
            "always_on":True,
            "is_access_point":False}
    }
    manager = ConfigManager()
    with requests_mock.Mocker() as mocker:

        mocker.post('https://iotile.cloud/api/v1/auth/login/', json=auth_payload)
        link_cloud(manager, '*****@*****.**', 'password')
        cloud = IOTileCloud()
        mocker.get('https://iotile.cloud/api/v1/fleet/g--0000-0000-0001/devices/', json=test_payload)
        mocker.get('https://iotile.cloud/api/v1/fleet/g--0000-0000-0002/devices/', status_code=404)
        assert cloud.get_fleet(1) == expected
        with pytest.raises(ArgumentError):
            cloud.get_fleet(2)
        with pytest.raises(ArgumentError):
            cloud.get_fleet(pow(16,12) + 1)
コード例 #2
0
ファイル: test_login.py プロジェクト: vvynohradov/coretools
def test_alternative_domains(registry):
    """Make sure we can specify an alternative domain."""

    payload = {
        'jwt': 'big-token',
        'username': '******'
    }

    manager = ConfigManager()
    manager.set('cloud:server', 'https://testcloud.com')

    with requests_mock.Mocker() as mocker:
        mocker.post('https://testcloud.com/api/v1/auth/login/', json=payload)

        link_cloud(manager, '*****@*****.**', 'password')

    assert registry.get_config('arch:cloud_user') == 'user1'
    assert registry.get_config('arch:cloud_token') == 'big-token'

    cloud = IOTileCloud()

    payload = {
        'token': 'new-token'
    }

    with requests_mock.Mocker() as mocker:
        mocker.post('https://testcloud.com/api/v1/auth/api-jwt-refresh/', json=payload)

        cloud.refresh_token()

    assert registry.get_config('arch:cloud_token') == 'new-token'
コード例 #3
0
ファイル: cloud.py プロジェクト: sirinsoftware/coretools
    def __init__(self, domain=None, username=None, **kwargs):
        reg = ComponentRegistry()
        conf = ConfigManager()

        if domain is None:
            domain = conf.get('cloud:server')

        self.api = Api(domain=domain, **kwargs)
        self._domain = self.api.domain

        try:
            token = reg.get_config('arch:cloud_token')
            token_type = reg.get_config('arch:cloud_token_type', default='jwt')
            self.api.set_token(token, token_type=token_type)
        except ArgumentError:
            # If we are interactive, try to get the user to login for a single
            # session rather than making them call link_cloud to store a cloud token
            if type_system.interactive:
                username, password = self._prompt_user_pass(username, domain)
                ok_resp = self.api.login(email=username, password=password)

                if not ok_resp:
                    raise ExternalError("Could not login to %s as user %s" %
                                        (domain, username))
            else:
                raise ExternalError(
                    "No stored iotile cloud authentication information",
                    suggestion=
                    'Call iotile config link_cloud with your username and password'
                )

        self.token = self.api.token
        self.token_type = self.api.token_type
コード例 #4
0
ファイル: cloud.py プロジェクト: sirinsoftware/coretools
    def refresh_token(self):
        """Attempt to refresh out cloud token with iotile.cloud."""

        if self.token_type != 'jwt':
            raise DataError(
                "Attempting to refresh a token that does not need to be refreshed",
                token_type=self.token_type)

        conf = ConfigManager()
        domain = conf.get('cloud:server')

        url = '{}/api/v1/auth/api-jwt-refresh/'.format(domain)

        resp = self.api.session.post(url, json={'token': self.token})
        if resp.status_code != 200:
            raise ExternalError("Could not refresh token",
                                error_code=resp.status_code)

        data = resp.json()

        # Save token that we just refreshed to the registry and update our own token
        self.token = data['token']

        reg = ComponentRegistry()
        reg.set_config('arch:cloud_token', self.token)
コード例 #5
0
def test_getting_and_setting():
    """Test setting and getting variables with correct types
    """

    man = ConfigManager()
    man.add_variable('test:var', 'bool', 'test variable', 'false')

    val = man.get('test:var')
    assert val is False

    man.set('test:var', 'true')
    assert man.get('test:var') is True

    # Make sure we can get and set without a default value
    man.add_variable('test:var2', 'bool', 'test variable')

    with pytest.raises(ArgumentError):
        man.get('test:var2')

    man.set('test:var2', 'True')
    assert man.get('test:var2') is True
    man.set('test:var2', 'False')
    assert man.get('test:var2') is False

    # Make sure removing a variable works
    man.remove('test:var2')
    with pytest.raises(ArgumentError):
        man.get('test:var2')

    man.remove('test:var')
    assert man.get('test:var') is False
コード例 #6
0
    def __init__(self, port, on_scan=None, on_disconnect=None, passive=None, **kwargs):
        super(BLED112Adapter, self).__init__()

        # Get optional configuration flags
        stop_check_interval = kwargs.get('stop_check_interval', 0.1)

        #Make sure that if someone tries to connect to a device immediately after creating the adapter
        #we tell them we need time to accumulate device advertising packets first
        self.set_config('minimum_scan_time', 2.0)

        if on_scan is not None:
            self.add_callback('on_scan', on_scan)

        if on_disconnect is not None:
            self.add_callback('on_disconnect', on_disconnect)

        if port is None or port == '<auto>':
            devices = self.find_bled112_devices()
            if len(devices) > 0:
                port = devices[0]
            else:
                raise ValueError("Could not find any BLED112 adapters connected to this computer")

        self.scanning = False
        self.stopped = False

        if passive is not None:
            self._active_scan = not passive
        else:
            config = ConfigManager()
            self._active_scan = config.get('bled112:active-scan')

        #Prepare internal state of scannable and in progress devices
        # Do this before spinning off the BLED112CommandProcessor
        # in case a scanned device is seen immediately.
        self.partial_scan_responses = {}
        self._connections = {}
        self.count_lock = threading.Lock()
        self.connecting_count = 0
        self.maximum_connections = 0

        self._logger = logging.getLogger(__name__)
        self._logger.addHandler(logging.NullHandler())

        self._serial_port = serial.Serial(port, 256000, timeout=0.01, rtscts=True)
        self._stream = AsyncPacketBuffer(self._serial_port, header_length=4, length_function=packet_length)
        self._commands = Queue()
        self._command_task = BLED112CommandProcessor(self._stream, self._commands, stop_check_interval=stop_check_interval)
        self._command_task.event_handler = self._handle_event
        self._command_task.start()


        try:
            self.initialize_system_sync()
            self.start_scan(self._active_scan)
        except:
            self.stop_sync()
            raise
コード例 #7
0
def test_format_nodefault():
    """Test formatting of config variable without a default value
    """

    man = ConfigManager()
    man.add_variable('test:var', 'bool', 'test variable')
    desc = man.describe('test:var')

    assert desc == 'test:var (bool): test variable [no default]'
コード例 #8
0
def test_format_default():
    """Test formatting of config variable defaults
    """

    man = ConfigManager()
    man.add_variable('test:var', 'bool', 'test variable', 'false')
    desc = man.describe('test:var')

    assert desc == 'test:var (bool): test variable [default: false]'
コード例 #9
0
def setup_environment(chip):
    """
    Setup the SCons environment for compiling arm cortex code
    """

    config = ConfigManager()

    #Make sure we never get MSVC settings for windows since that has the wrong command line flags for gcc
    if platform.system() == 'Windows':
        env = Environment(tools=['mingw'], ENV=os.environ)
    else:
        env = Environment(tools=['default'], ENV=os.environ)

    env['INCPREFIX'] = '-I"'
    env['INCSUFFIX'] = '"'
    env['CPPPATH'] = chip.includes()
    env['ARCH'] = chip

    #Setup Cross Compiler
    env['CC'] = 'arm-none-eabi-gcc'
    env['AS'] = 'arm-none-eabi-gcc'
    env['LINK'] = 'arm-none-eabi-gcc'
    env['AR'] = 'arm-none-eabi-ar'
    env['RANLIB'] = 'arm-none-eabi-ranlib'

    #AS command line is by default setup for call as directly so we need to modify it to call via *-gcc to allow for preprocessing
    env['ASCOM'] = "$AS $ASFLAGS -o $TARGET -c $SOURCES"

    # Setup nice display strings unless we're asked to show raw commands
    if not config.get('build:show-commands'):
        env['CCCOMSTR'] = "Compiling $TARGET"
        env['ARCOMSTR'] = "Building static library $TARGET"
        env['RANLIBCOMSTR'] = "Indexing static library $TARGET"
        env['LINKCOMSTR'] = "Linking $TARGET"

    #Setup Compiler Flags
    env['CCFLAGS'] = chip.combined_properties('cflags')
    env['LINKFLAGS'] = chip.combined_properties('ldflags')
    env['ARFLAGS'].append(chip.combined_properties(
        'arflags'))  #There are default ARFLAGS that are necessary to keep
    env['ASFLAGS'].append(chip.combined_properties('asflags'))

    #Add in compile tile definitions
    defines = utilities.build_defines(chip.property('defines', {}))
    env['CCFLAGS'].append(defines)

    #Setup Target Architecture
    env['CCFLAGS'].append('-mcpu=%s' % chip.property('cpu'))
    env['ASFLAGS'].append('-mcpu=%s' % chip.property('cpu'))
    env['LINKFLAGS'].append('-mcpu=%s' % chip.property('cpu'))

    #Initialize library paths (all libraries are added via dependencies)
    env['LIBPATH'] = []
    env['LIBS'] = []

    return env
コード例 #10
0
def test_list_vars():
    """Test listing variables using a glob
    """

    man = ConfigManager()
    man.add_variable('test:var', 'bool', 'test variable', 'false')
    man.add_variable('test:var2', 'bool', 'test variable', 'false')
    man.add_variable('test2:var', 'bool', 'test variable', 'false')
    man.add_variable('test:hello', 'string', 'test variable', 'false')

    # Note that other plugins may register config vars here
    assert len(man.list("*")) >= 4
    assert len(man.list('test:*')) == 3
    assert len(man.list('test:var*')) == 2
    assert len(man.list('test:hello')) == 1
コード例 #11
0
ファイル: test_login.py プロジェクト: vvynohradov/coretools
def test_login(registry):
    """Make sure successful login is properly handled."""

    payload = {
        'jwt': 'big-token',
        'username': '******'
    }

    manager = ConfigManager()

    with requests_mock.Mocker() as mocker:
        mocker.post('https://iotile.cloud/api/v1/auth/login/', json=payload)

        link_cloud(manager, '*****@*****.**', 'password')

    assert registry.get_config('arch:cloud_user') == 'user1'
    assert registry.get_config('arch:cloud_token') == 'big-token'
コード例 #12
0
def test_setting_config_function():
    """Test adding a config function to ConfigManager
    """

    man = ConfigManager()

    def conf_function(self, arg1):
        if arg1 == 5:
            raise ArgumentError("test")

        return arg1

    with pytest.raises(AttributeError):
        man.test_conf(5)

    man.add_function('test_conf', conf_function)

    with pytest.raises(ArgumentError):
        man.test_conf(5)

    assert man.test_conf(3) == 3
コード例 #13
0
ファイル: test_login.py プロジェクト: vvynohradov/coretools
def test_check_time():
    """ Make sure we can check if the time is correct"""

    json_true = {'now': datetime.datetime.now(tzutc()).strftime('%a, %d %b %Y %X %Z')}
    json_false = {'now': 'Wed, 01 Sep 2010 17:30:32 GMT'}
    payload = {
        'jwt': 'big-token',
        'username': '******'
    }

    manager = ConfigManager()

    with requests_mock.Mocker() as mocker:

        mocker.post('https://iotile.cloud/api/v1/auth/login/', json=payload)
        link_cloud(manager, '*****@*****.**', 'password')
        cloud = IOTileCloud()
        mocker.get('https://iotile.cloud/api/v1/server/', json=json_true)
        assert cloud.check_time() == True
        mocker.get('https://iotile.cloud/api/v1/server/', json=json_false)
        assert cloud.check_time() == False
コード例 #14
0
ファイル: hwmanager.py プロジェクト: kgosalia/coretools
    def __init__(self, port=None, record=None, adapter=None):
        if port is None and adapter is None:
            try:
                conf = ConfigManager()
                port = conf.get('core:default-port')
            except ArgumentError:
                raise ArgumentError(
                    "No port given and no core:default-port config variable set",
                    suggestion=
                    "Specify the port to use to connect to the IOTile devices")
        elif port is None:
            port = ""

        transport, _, arg = port.partition(':')

        self.transport = transport
        self.port = None
        if arg != "":
            self.port = arg

        self._record = record

        self.stream = self._create_stream(adapter)

        self._stream_queue = None
        self._trace_queue = None
        self._broadcast_queue = None
        self._trace_data = bytearray()

        self._proxies = {'TileBusProxyObject': TileBusProxyObject}
        self._name_map = {
            TileBusProxyObject.ModuleName(): [TileBusProxyObject]
        }

        self._known_apps = {}
        self._named_apps = {}

        self._setup_proxies()
        self._setup_apps()
コード例 #15
0
ファイル: test_login.py プロジェクト: vvynohradov/coretools
def test_get_whitelist():
    """ Make sure we can retrieve the whitelist correctly """
    with open('test/large_mock_answer.json') as lma:
        j = json.load(lma)
        test_payload = j['whitelist_test']
        p1 = j['whitelist_g1']
        p2 = j['whitelist_g2']
        p3 = j['whitelist_g3']
        expected = j['expected']
        empty_whitelist_test = j['empty_whitelist_test']
        p4 = j['whitelist_g4']
    payload = {
        'jwt': 'big-token',
        'username': '******'
    }

    manager = ConfigManager()
    with requests_mock.Mocker() as mocker:

        mocker.post('https://iotile.cloud/api/v1/auth/login/', json=payload)
        link_cloud(manager, '*****@*****.**', 'password')
        cloud = IOTileCloud()
        mocker.get('https://iotile.cloud/api/v1/fleet/?device=d--0000-0000-0000-0001', status_code=404)
        with pytest.raises(ExternalError):
            cloud.get_whitelist(1)
        mocker.get('https://iotile.cloud/api/v1/fleet/?device=d--0000-0000-0000-0002', json={'results':[]})
        with pytest.raises(ExternalError):
            cloud.get_whitelist(2)
        mocker.get('https://iotile.cloud/api/v1/fleet/?device=d--0000-0000-0000-01bd', json=test_payload)
        mocker.get('https://iotile.cloud/api/v1/fleet/g--0000-0000-0001/devices/', json=p1)
        mocker.get('https://iotile.cloud/api/v1/fleet/g--0000-0000-0002/devices/', json=p2)
        mocker.get('https://iotile.cloud/api/v1/fleet/g--0000-0000-0003/devices/', json=p3)
        assert cloud.get_whitelist(0x1bd) == expected
        mocker.get('https://iotile.cloud/api/v1/fleet/?device=d--0000-0000-0000-01bd', json=empty_whitelist_test)
        mocker.get('https://iotile.cloud/api/v1/fleet/g--0000-0000-0004/devices/', json=p4)
        with pytest.raises(ExternalError):
            cloud.get_whitelist(0x1bd)
コード例 #16
0
ファイル: device_adapter.py プロジェクト: iotile/coretools
    def __init__(self,
                 port,
                 on_scan=None,
                 on_disconnect=None,
                 active_scan=None,
                 **kwargs):
        super(NativeBLEDeviceAdapter, self).__init__()

        # Create logger
        self._logger = logging.getLogger(__name__)
        self._logger.addHandler(logging.NullHandler())

        # Register configuration
        self.set_config(
            'minimum_scan_time',
            2.0)  # Time to accumulate device advertising packets first
        self.set_config('default_timeout',
                        10.0)  # Time before timeout an operation
        self.set_config('expiration_time',
                        60.0)  # Time before a scanned device expired
        self.set_config(
            'maximum_connections',
            3)  # Maximum number of simultaneous connections per controller

        # Create the baBLE interface to interact with BLE controllers
        self.bable = bable_interface.BaBLEInterface()

        # Get the list of BLE controllers
        self.bable.start(on_error=self._on_ble_error)
        controllers = self._find_ble_controllers()
        self.bable.stop()

        if len(controllers) == 0:
            raise ExternalError(
                "Could not find any BLE controller connected to this computer")

        # Parse port and check if it exists
        if port is None or port == '<auto>':
            self.controller_id = controllers[0].id
        else:
            self.controller_id = int(port)
            if not any(controller.id == self.controller_id
                       for controller in controllers):
                raise ExternalError(
                    "Could not find a BLE controller with the given ID, controller_id=%s"
                    .format(self.controller_id))

        # Restart baBLE with the selected controller id to prevent conflicts if multiple controllers
        self.bable.start(on_error=self._on_ble_error,
                         exit_on_sigint=False,
                         controller_id=self.controller_id)

        # Register callbacks
        if on_scan is not None:
            self.add_callback('on_scan', on_scan)
        if on_disconnect is not None:
            self.add_callback('on_disconnect', on_disconnect)

        self.scanning = False
        self.stopped = False

        if active_scan is not None:
            self._active_scan = active_scan
        else:
            config = ConfigManager()
            self._active_scan = config.get('ble:active-scan')

        # To register advertising packets waiting for a scan response (only if active scan)
        self.partial_scan_responses = {}

        # To manage multiple connections
        self.connections = ConnectionManager(self.id)
        self.connections.start()

        # Notification callbacks
        self.notification_callbacks_lock = threading.Lock()
        self.notification_callbacks = {}

        try:
            self._initialize_system_sync()
            self.start_scan(active=self._active_scan)
        except Exception:
            self.stop_sync()
            raise
コード例 #17
0
ファイル: arm.py プロジェクト: sirinsoftware/coretools
def setup_environment(chip, args_file=None):
    """Setup the SCons environment for compiling arm cortex code.

    This will return an env that has all of the correct settings and create a
    command line arguments file for GCC that contains all of the required
    flags. The use of a command line argument file passed with @./file_path is
    important since there can be many flags that exceed the maximum allowed length
    of a command line on Windows.
    """

    config = ConfigManager()

    # Make sure we never get MSVC settings for windows since that has the wrong command line flags for gcc
    if platform.system() == 'Windows':
        env = Environment(tools=['mingw'], ENV=os.environ)
    else:
        env = Environment(tools=['default'], ENV=os.environ)

    env['INCPREFIX'] = '-I"'
    env['INCSUFFIX'] = '"'
    env['CPPDEFPREFIX'] = ''
    env['CPPDEFSUFFIX'] = ''

    env['CPPPATH'] = chip.includes()
    env['ARCH'] = chip

    # Setup Cross Compiler
    env['CC'] = 'arm-none-eabi-gcc'
    env['AS'] = 'arm-none-eabi-gcc'
    env['LINK'] = 'arm-none-eabi-gcc'
    env['AR'] = 'arm-none-eabi-ar'
    env['RANLIB'] = 'arm-none-eabi-ranlib'

    # AS command line is by default setup for call as directly so we need
    # to modify it to call via *-gcc to allow for preprocessing
    env['ASCOM'] = "$AS $ASFLAGS -o $TARGET -c $SOURCES"

    # Setup nice display strings unless we're asked to show raw commands
    if not config.get('build:show-commands'):
        env['CCCOMSTR'] = "Compiling $TARGET"
        env['ARCOMSTR'] = "Building static library $TARGET"
        env['RANLIBCOMSTR'] = "Indexing static library $TARGET"
        env['LINKCOMSTR'] = "Linking $TARGET"

    # Setup Compiler Flags
    env['CCFLAGS'] = chip.combined_properties('cflags')
    env['LINKFLAGS'] = chip.combined_properties('ldflags')
    env['ARFLAGS'].append(chip.combined_properties(
        'arflags'))  # There are default ARFLAGS that are necessary to keep
    env['ASFLAGS'].append(chip.combined_properties('asflags'))

    # Add in compile tile definitions
    defines = utilities.build_defines(chip.property('defines', {}))
    env['CPPDEFINES'] = defines

    if args_file is not None:
        env['CCCOM'] = "$CC $CCFLAGS $CPPFLAGS @{} -c -o $TARGET $SOURCES".format(
            args_file)

    # Setup Target Architecture
    env['CCFLAGS'].append('-mcpu=%s' % chip.property('cpu'))
    env['ASFLAGS'].append('-mcpu=%s' % chip.property('cpu'))
    env['LINKFLAGS'].append('-mcpu=%s' % chip.property('cpu'))

    # Initialize library paths (all libraries are added via dependencies)
    env['LIBPATH'] = []
    env['LIBS'] = []

    return env
コード例 #18
0
ファイル: hwmanager.py プロジェクト: sirinsoftware/coretools
    def watch_broadcasts(self, whitelist=None, blacklist=None):
        """Spawn an interactive terminal UI to watch broadcast data from devices.

        Devices are allowed to post a broadcast report containing stream data.
        This function will create a list in your console window with the latest
        broadcast value from each device in range.

        Args:
            whitelist (list(integer)): Only include devices with these listed ids.
            blacklist (list(integer)): Include every device **except** those with these
                specific ids.  If combined with whitelist, whitelist wins and this
                parameter has no effect.
        """

        title = "Watching Broadcast Reports (Ctrl-C to Stop)"
        subtitle = ""
        if self.transport == 'bled112':
            reg = ConfigManager()
            if not reg.get('bled112:active-scan'):
                subtitle = "Active Scanning not active, you won't see v1 broadcasts"

        if whitelist is not None:
            whitelist = set(whitelist)

        if blacklist is not None:
            blacklist = set(blacklist)

        def _title(_items):
            return [title, subtitle]

        def _poll():
            results = [x for x in self.iter_broadcast_reports(blocking=False)]
            return results

        def _text(item):
            fmt_uuid = "%08X" % item.origin
            fmt_uuid = fmt_uuid[:4] + '-' + fmt_uuid[4:]

            reading = item.visible_readings[0]
            return "{0: <15} stream: {1: 04X}    value: {2: <8}".format(
                fmt_uuid, reading.stream, reading.value)

        def _sort_order(item):
            return item.origin

        def _hash(item):
            uuid = item.origin
            stream_id = item.visible_readings[0].stream
            if whitelist is not None and uuid not in whitelist:
                return None

            if blacklist is not None and whitelist is None and uuid in blacklist:
                return None

            item_id = str(uuid) + "," + str(stream_id)
            return item_id

        line_ui = LinebufferUI(_poll,
                               _hash,
                               _text,
                               sortkey_func=_sort_order,
                               title=_title)
        line_ui.run()
コード例 #19
0
    def __init__(self, port, on_scan=None, on_disconnect=None, passive=None, **kwargs):
        super(BLED112Adapter, self).__init__()

        # Get optional configuration flags
        stop_check_interval = kwargs.get('stop_check_interval', 0.1)

        # Make sure that if someone tries to connect to a device immediately after creating the adapter
        # we tell them we need time to accumulate device advertising packets first
        self.set_config('minimum_scan_time', 2.0)

        if on_scan is not None:
            self.add_callback('on_scan', on_scan)

        if on_disconnect is not None:
            self.add_callback('on_disconnect', on_disconnect)

        self.scanning = False
        self.stopped = False

        config = ConfigManager()

        if passive is not None:
            self._active_scan = not passive
        else:
            self._active_scan = config.get('bled112:active-scan')

        self._throttle_broadcast = config.get('bled112:throttle-broadcast')
        self._throttle_scans = config.get('bled112:throttle-scan')
        self._throttle_timeout = config.get('bled112:throttle-timeout')

        # Prepare internal state of scannable and in progress devices
        # Do this before spinning off the BLED112CommandProcessor
        # in case a scanned device is seen immediately.
        self.partial_scan_responses = {}

        self._broadcast_state = {}
        self._connections = {}

        self.count_lock = threading.Lock()
        self.connecting_count = 0
        self.maximum_connections = 0

        self._scan_event_count = 0
        self._v1_scan_count = 0
        self._v1_scan_response_count = 0
        self._v2_scan_count = 0
        self._device_scan_counts = {}
        self._last_reset_time = time.monotonic()

        self._logger = logging.getLogger(__name__)
        self._logger.addHandler(logging.NullHandler())
        self._serial_port = open_bled112(port, self._logger)
        self._stream = AsyncPacketBuffer(self._serial_port, header_length=4, length_function=packet_length)
        self._commands = Queue()
        self._command_task = BLED112CommandProcessor(self._stream, self._commands, stop_check_interval=stop_check_interval)
        self._command_task.event_handler = self._handle_event
        self._command_task.start()

        try:
            self.initialize_system_sync()
            self.start_scan(self._active_scan)
        except:
            self.stop_sync()
            raise