Пример #1
0
    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
Пример #2
0
    def get_whitelist(self, device_id):
        """ Returns the whitelist associated with the given device_id if any"""
        api = self.api
        slug = device_id_to_slug(device_id)
        try:
            fleets = api.fleet.get(device=slug)['results']
        except HttpNotFoundError:
            raise ExternalError(
                "Could not find the right URL. Are fleets enabled ?")

        if not fleets:
            # This is to be expected for devices set to take data from all project, or any device.
            raise ExternalError("The device isn't in any network !")

        networks = [
            self.get_fleet(fleet['id']) for fleet in fleets
            if fleet.get('is_network', False) is True
        ]
        networks_to_manage = [
            x for x in networks
            if x.get(slug, {}).get('is_access_point', False) is True
        ]

        out = {}
        for network in networks_to_manage:
            out.update(network)

        # Remove ourselves from the whitelist that we are supposed to manage
        if slug in out:
            del out[slug]

        if not out:
            raise ExternalError("No device to manage in these fleets !")

        return out
Пример #3
0
    def _load_providers(self):
        """Load all config_variables providers using pkg_resources
        """

        reg = ComponentRegistry()
        for name, provider in reg.load_extensions('iotile.config_variables'):
            try:
                prefix, conf_vars = provider()
            except (ValueError, TypeError) as exc:
                raise ExternalError("Error loading config variables",
                                    package=name,
                                    error=str(exc))

            for var in conf_vars:
                if len(var) != 3 and len(var) != 4:
                    raise ExternalError(
                        "Error loading config variable, invalid length",
                        data=var,
                        package=name)

                name = prefix + ':' + var[0]
                if len(var) == 3:
                    var_obj = ConfigVariable(var[0], var[1], var[2], MISSING)
                else:
                    var_obj = ConfigVariable(name, var[1], var[2], var[3])

                if name in self._known_variables:
                    raise ExternalError(
                        "The same config variable was defined twice",
                        name=name)

                self._known_variables[name] = var_obj
Пример #4
0
    def __init__(self, args):
        cert = args.get('certificate', None)
        key = args.get('private_key', None)
        root = args.get('root_certificate', None)
        endpoint = args.get('endpoint', None)
        iamkey = args.get('iam_key', None)
        iamsecret = args.get('iam_secret', None)
        iamsession = args.get('iam_session', None)
        use_websockets = args.get('use_websockets', False)

        try:
            if not use_websockets:
                if cert is None:
                    raise ExternalError("Certificate for AWS IOT not passed in certificate key")
                elif key is None:
                    raise ExternalError("Private key for certificate not passed in private_key key")
            else:
                if iamkey is None or iamsecret is None:
                    raise ExternalError("IAM Credentials need to be provided for websockets auth")
        except ExternalError:
            # If the correct information is not passed in, try and see if we get it from our environment
            # try to pull in root certs, endpoint name and iam or cognito session information
            reg = ComponentRegistry()

            if endpoint is None:
                endpoint = reg.get_config('awsiot-endpoint', default=None)

            if root is None:
                root = reg.get_config('awsiot-rootcert', default=None)

            iamkey = reg.get_config('awsiot-iamkey', default=None)
            iamsecret = reg.get_config('awsiot-iamtoken', default=None)
            iamsession = reg.get_config('awsiot-session', default=None)

            if iamkey is None or iamsecret is None:
                raise

            use_websockets = True

        if root is None:
            raise ExternalError("Root of certificate chain not passed in root_certificate key (and not in registry)")
        elif endpoint is None:
            raise ExternalError("AWS IOT endpoint not passed in endpoint key (and not in registry)")

        self.websockets = use_websockets
        self.iam_key = iamkey
        self.iam_secret = iamsecret
        self.iam_session = iamsession
        self.cert = cert
        self.key = key
        self.root = root
        self.endpoint = endpoint
        self.client = None
        self.sequencer = TopicSequencer()
        self.queues = {}
        self.wildcard_queues = []
        self._logger = logging.getLogger(__name__)
Пример #5
0
def _ensure_package_loaded(path, component):
    """Ensure that the given module is loaded as a submodule.

    Returns:
        str: The name that the module should be imported as.
    """

    logger = logging.getLogger(__name__)

    packages = component.find_products('support_package')
    if len(packages) == 0:
        return None
    elif len(packages) > 1:
        raise ExternalError(
            "Component had multiple products declared as 'support_package",
            products=packages)

    if len(path) > 2 and ':' in path[2:]:  # Don't flag windows C: type paths
        path, _, _ = path.rpartition(":")

    package_base = packages[0]
    relative_path = os.path.normpath(os.path.relpath(path, start=package_base))
    if relative_path.startswith('..'):
        raise ExternalError(
            "Component had python product output of support_package",
            package=package_base,
            product=path,
            relative_path=relative_path)

    if not relative_path.endswith('.py'):
        raise ExternalError("Python product did not end with .py", path=path)

    relative_path = relative_path[:-3]
    if os.pathsep in relative_path:
        raise ExternalError(
            "Python support wheels with multiple subpackages not yet supported",
            relative_path=relative_path)

    support_distro = component.support_distribution
    if support_distro not in sys.modules:
        logger.debug("Creating dynamic support wheel package: %s",
                     support_distro)
        spec = importlib.util.spec_from_file_location(
            os.path.basename(package_base), package_base)
        if spec is None:
            raise ExternalError("importlib cannot find module at this path",
                                path=package_base)
        module = importlib.util.module_from_spec(spec)
        sys.modules[support_distro] = module
        spec.loader.exec_module(module)

    return "{}.{}".format(support_distro, relative_path)
Пример #6
0
    def wait_reports(self, num_reports, timeout=2.0):
        """Wait for a fixed number of reports to be received

        Args:
            num_reports (int): The number of reports to wait for
            timeout (float): The maximum number of seconds to wait without
                receiving another report.
        """

        if self._stream_queue is None:
            raise ExternalError(
                "You have to enable streaming before you can wait for reports")

        reports = []

        for i in range(0, num_reports):
            try:
                report = self._stream_queue.get(timeout=timeout)
                reports.append(report)
            except Empty:
                raise TimeoutExpiredError("Timeout waiting for a report",
                                          expected_number=num_reports,
                                          received_number=i,
                                          received_reports=reports)

        return reports
Пример #7
0
    def highest_acknowledged(self, device_id, streamer):
        """Get the highest acknowledged reading for a given streamer.

        Args:
            device_id (int): The device whose streamer we are querying
            streamer (int): The streamer on the device that we want info
                about.

        Returns:
            int: The highest reading id that has been acknowledged by the cloud
        """

        slug = self._build_streamer_slug(device_id, streamer)

        try:
            data = self.api.streamer(slug).get()
        except RestHttpBaseException as exc:
            raise ArgumentError("Could not get information for streamer",
                                device_id=device_id,
                                streamer_id=streamer,
                                slug=slug,
                                err=str(exc))

        if 'last_id' not in data:
            raise ExternalError(
                "Response fom the cloud did not have last_id set",
                response=data)

        return data['last_id']
Пример #8
0
    def FindByName(cls, name):
        """Find an installed VirtualTile by name.

        This function searches for installed virtual tiles
        using the pkg_resources entry_point `iotile.virtual_tile`.

        If name is a path ending in .py, it is assumed to point to
        a module on disk and loaded directly rather than using
        pkg_resources.

        Args:
            name (str): The name of the tile to search
                for.

        Returns:
            VirtualTile class: A virtual tile subclass that can be
                instantiated to create a virtual tile.
        """

        if name.endswith('.py'):
            return cls.LoadFromFile(name)

        for entry in pkg_resources.iter_entry_points("iotile.virtual_tile",
                                                     name):
            obj = entry.load()
            if not issubclass(obj, VirtualTile):
                raise ExternalError(
                    "External virtual tile could not be loaded because it does not inherit from VirtualTile"
                )

            return obj

        raise ArgumentError("VirtualTile could not be found by name",
                            name=name)
Пример #9
0
    def impersonate_device(self, device_id):
        """Convert our token to a permanent device token.

        This function is most useful for creating virtual IOTile devices whose access to iotile.cloud
        is based on their device id, not any particular user's account.

        There are a few differences between device tokens and user tokens:
         - Device tokens never expire and don't need to be refreshed
         - Device tokens are more restricted in what they can access in IOTile.cloud than user tokens

        Args:
            device_id (int): The id of the device that we want to get a token for.
        """

        slug = device_id_to_slug(device_id)
        token_type = IOTileCloud.DEVICE_TOKEN_TYPE

        try:
            resp = self.api.device(slug).key.get(
                type=IOTileCloud.DEVICE_TOKEN_TYPE)
            token = resp['key']
        except RestHttpBaseException as exc:
            raise ExternalError("Error calling method on iotile.cloud",
                                exception=exc,
                                response=exc.response.status_code)

        self.api.set_token(token, token_type=token_type)
        self.token = token
        self.token_type = token_type

        reg = ComponentRegistry()
        reg.set_config('arch:cloud_token', self.token)
        reg.set_config('arch:cloud_token_type', self.token_type)
        reg.set_config('arch:cloud_device', slug)
Пример #10
0
    def __init__(self, folder):
        self.folder = folder
        self.filter_prods = False

        modfile = os.path.join(self.folder, 'module_settings.json')

        try:
            with open(modfile, "r") as infile:
                settings = json.load(infile)
        except IOError:
            raise ExternalError(
                "Could not load module_settings.json file, make sure this directory is an IOTile component",
                path=self.folder)

        file_format = settings.get('file_format', IOTile.V1_FORMAT)
        if file_format == IOTile.V1_FORMAT:
            info = self._find_v1_settings(settings)
        elif file_format == IOTile.V2_FORMAT:
            info = self._find_v2_settings(settings)
        else:
            raise DataError("Unknown file format in module_settings.json",
                            format=file_format,
                            path=modfile)

        self._load_settings(info)
Пример #11
0
    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)
Пример #12
0
    def run(self, refresh_interval=0.05):
        """Set up the loop, check that the tool is installed"""
        try:
            from asciimatics.screen import Screen
        except ImportError:
            raise ExternalError("You must have asciimatics installed to use LinebufferUI", suggestion="pip install iotilecore[ui]")

        Screen.wrapper(self._run_loop, arguments=[refresh_interval])
Пример #13
0
    def unfreeze_extensions(self):
        """Remove a previously frozen list of extensions."""

        output_path = os.path.join(_registry_folder(),
                                   'frozen_extensions.json')
        if not os.path.isfile(output_path):
            raise ExternalError("There is no frozen extension list")

        os.remove(output_path)
        ComponentRegistry._frozen_extensions = None
Пример #14
0
    def set_sensorgraph(self, device_id, new_sg, app_tag=None):
        """The the cloud's sensor graph id that informs what kind of device this is.

        Is app_tag is passed, verify that the sensorgraph explicitly matches
        the expected app_tag by making another API call.

        Args:
            device_id (int): The id of the device that we want to change the sensorgraph for.
            new_sg (string): Name of a valid sensorgraph that you wish to set the device to
            app_tag (int): Optional. The intended app_tag of the sensorgraph will be set. If the
                app_tag passed into this function does not match the app_tag of the sensorgraph
                in iotile.cloud, raise an error.
        """
        try:
            sg = self.api.sg(new_sg).get()
        except RestHttpBaseException as exc:
            raise ExternalError("Error calling method on iotile.cloud",
                                exception=exc,
                                response=exc.response.status_code)

        if app_tag is not None:
            if sg.get('app_tag', None) != app_tag:
                raise ArgumentError(
                    "Cloud sensorgraph record does not match app tag",
                    value=new_sg,
                    cloud_sg_app_tag=sg.get('app_tag', None),
                    app_tag_set=app_tag)

        slug = device_id_to_slug(device_id)
        patch = {'sg': new_sg}

        try:
            self.api.device(slug).patch(patch)
        except RestHttpBaseException as exc:
            if exc.response.status_code == 400:
                raise ArgumentError(
                    "Error setting sensor graph, invalid value",
                    value=new_sg,
                    error_code=exc.response.status_code)
            else:
                raise ExternalError("Error calling method on iotile.cloud",
                                    exception=exc,
                                    response=exc.response.status_code)
Пример #15
0
    def _raise_error(self, command, response):
        exc_name = response.get('exception_class')
        reason = response.get('reason')

        exc_class = self._allowed_exceptions.get(exc_name)
        if exc_class is not None:
            raise exc_class(reason)

        raise ExternalError("Command {} failed".format(command),
                            reason=response.get('reason'))
Пример #16
0
    def _prepare(self):
        self.slug = self._build_device_slug(self.iotile_id)
        self.client = OrderedAWSIOTClient(self._args)
        try:
            self.client.connect(self.slug)
        except Exception as exc:
            raise ExternalError("Could not connect to AWS IOT", error=str(exc))

        self.topics = MQTTTopicValidator(self.prefix +
                                         'devices/{}'.format(self.slug))
        self._bind_topics()
Пример #17
0
    def set_device_template(self, device_id, new_template, os_tag=None):
        """Sets the device template for the given device in iotile.cloud.

        Is os_tag is passed, verify that the device template explicitly matches
        the expected os_tag by making another API call.
        Args:
            device_id (int): The id of the device that we want to change the device template for.
            new_template (string): Name of a valid device template that you wish to set the device to
            os_tag (int): Optional. If the os_tag passed into this function does not match the
                os_tag of the device_template in iotile.cloud, raise an error.
        """
        try:
            dt = self.api.dt(new_template).get()
        except RestHttpBaseException as exc:
            raise ExternalError("Error calling method on iotile.cloud",
                                exception=exc,
                                response=exc.response.status_code)

        if os_tag is not None:
            if dt.get('os_tag', None) != os_tag:
                raise ArgumentError(
                    "Cloud device template record does not match os tag",
                    value=new_template,
                    cloud_sg_os_tag=dt.get('os_tag', None),
                    os_tag_set=os_tag)

        slug = device_id_to_slug(device_id)
        patch = {'template': new_template}

        try:
            self.api.device(slug).patch(patch, staff=1)
        except RestHttpBaseException as exc:
            if exc.response.status_code == 400:
                raise ArgumentError(
                    "Error setting device template, invalid value",
                    value=new_template,
                    error_code=exc.response.status_code)
            else:
                raise ExternalError("Error calling method on iotile.cloud",
                                    exception=exc,
                                    response=exc.response.status_code)
Пример #18
0
def _find_available_bled112(logger):
    devices = _find_bled112_devices(logger)
    if len(devices) == 0:
        raise ExternalError(
            "Could not find any BLED112 adapters connected to this computer")

    for port in devices:
        try:
            dev = serial.Serial(port,
                                _BAUD_RATE,
                                timeout=0.01,
                                rtscts=True,
                                exclusive=True)
            logger.info("Using first available BLED112 adapter at %s", port)
            return dev
        except serial.serialutil.SerialException:
            logger.debug("Can't use BLED112 device %s because it's locked",
                         port)

    raise ExternalError("There were %d BLED112 adapters but all were in use." %
                        len(devices))
Пример #19
0
    def unclaim(self, device_id, clean=True):
        """Unclaim a device that may have previously been claimed."""

        slug = device_id_to_slug(device_id)

        payload = {'clean_streams': clean}

        try:
            self.api.device(slug).unclaim.post(payload)
        except RestHttpBaseException as exc:
            raise ExternalError("Error calling method on iotile.cloud",
                                exception=exc,
                                response=exc.response.status_code)
Пример #20
0
    async def send_command(self, command, args, validator, timeout=10.0):
        """Send a command and synchronously wait for a single response.

        Args:
            command (string): The command name
            args (dict): Optional arguments.
            validator (Verifier): A SchemaVerifier to verify the response
                payload.
            timeout (float): The maximum time to wait for a response.
                Defaults to 10 seconds.

        Returns:
            dict: The response payload

        Raises:
            ExternalError: If the server is not connected or the command
                fails.
            asyncio.TimeoutError: If the command times out.
            ValidationError: If the response payload does not match the
                given validator.
        """

        if not self._implementation.connected:
            raise ExternalError("No websock connection established")

        cmd_uuid = str(uuid.uuid4())
        msg = dict(type='command',
                   operation=command,
                   uuid=cmd_uuid,
                   payload=args)

        packed = pack(msg)

        # Note: register future before sending to avoid race conditions
        response_future = self._manager.wait_for(type="response",
                                                 uuid=cmd_uuid,
                                                 timeout=timeout)

        await self._implementation.send(packed)

        response = await response_future

        if response.get('success') is False:
            self._raise_error(command, response)

        if validator is None:
            return response.get('payload')

        return validator.verify(response.get('payload'))
Пример #21
0
    def resolve(self, depinfo, destdir):
        from iotile.core.dev.registry import ComponentRegistry

        reg = ComponentRegistry()

        try:
            comp = reg.find_component(depinfo['name'])
        except ArgumentError:
            return {'found': False}

        # Make sure the tile we found in the registry has the required version
        reqver = depinfo['required_version']
        if not reqver.check(comp.parsed_version):
            return {'found': False}

        # If the component is in the local registry but hasn't been built,
        # raise an error.

        if not os.path.exists(comp.output_folder):
            raise ExternalError(
                "Component found in registry but has not been built",
                path=comp.folder,
                name=comp.name,
                suggestion="Run iotile build on this component first")

        try:
            IOTile(comp.output_folder)
        except IOTileException:
            raise ExternalError(
                "Component found in registry but its build/output folder is not valid",
                path=comp.folder,
                name=comp.name,
                suggestion="Cleanly rebuild the component")

        self._copy_folder_contents(comp.output_folder, destdir)
        return {'found': True}
Пример #22
0
    def _load_functions(self):
        """Load all config functions that should be bound to this ConfigManager

        Config functions allow you to add functions that will appear under ConfigManager
        but call your specified function.  This is useful for adding complex configuration
        behavior that is callable from the iotile command line tool
        """

        reg = ComponentRegistry()
        for _, conf_func in reg.load_extensions('iotile.config_function'):
            try:
                name = conf_func.__name__

                self.add_function(name, conf_func)
            except (ValueError, TypeError) as exc:
                raise ExternalError("Error loading config function", name=name, error=str(exc))
Пример #23
0
    def KeyTypeToStreamType(cls, key_type):
        """Converts key type into the type of signed report that can be encrypted with this key

        Args:
            int: a type of the key, see AuthProvider

        Returns:
            ReportSignatureFlags: a type of the stream report
        """
        if key_type == AuthProvider.NoKey:
            return ReportSignatureFlags.SIGNED_WITH_HASH
        elif key_type == AuthProvider.UserKey:
            return ReportSignatureFlags.SIGNED_WITH_USER_KEY
        elif key_type == AuthProvider.DeviceKey:
            return ReportSignatureFlags.SIGNED_WITH_DEVICE_KEY
        else:
            raise ExternalError("Unsupported key type {}".format(key_type))
Пример #24
0
    def _load_functions(self):
        """Load all config functions that should be bound to this ConfigManager

        Config functions allow you to add functions that will appear under ConfigManager
        but call your specified function.  This is useful for adding complex configuration
        behavior that is callable from the iotile command line tool
        """

        for entry in pkg_resources.iter_entry_points('iotile.config_function'):
            try:
                conf_func = entry.load()
                name = conf_func.__name__

                self.add_function(name, conf_func)
            except (ValueError, TypeError) as exc:
                raise ExternalError("Error loading config function",
                                    name=name,
                                    error=str(exc))
Пример #25
0
    def StreamTypeToKeyType(cls, stream_type):
        """Converts the stream type into the key type that was used to encryped this stream report

        Args:
            ReportSignatureFlags: a type of the stream report

        Returns:
            int: type of key, see AuthProvider
        """

        if stream_type == ReportSignatureFlags.SIGNED_WITH_HASH:
            return AuthProvider.NoKey
        elif stream_type == ReportSignatureFlags.SIGNED_WITH_USER_KEY:
            return AuthProvider.UserKey
        elif stream_type == ReportSignatureFlags.SIGNED_WITH_DEVICE_KEY:
            return AuthProvider.DeviceKey
        else:
            raise ExternalError(
                "Unsupported stream signature type {}".format(stream_type))
Пример #26
0
    def _process_elf(cls, in_path):
        tmp = tempfile.NamedTemporaryFile(delete=False)
        tmp.close()

        try:
            err = subprocess.call(
                ['arm-none-eabi-objcopy', '-O', 'ihex', in_path, tmp.name])
            if err != 0:
                raise ExternalError(
                    "Cannot convert elf to binary file",
                    error_code=err,
                    suggestion=
                    "Make sure arm-none-eabi-gcc is installed and in your PATH"
                )

            return cls._process_hex(tmp.name)
        finally:
            if os.path.isfile(tmp.name):
                os.remove(tmp.name)
Пример #27
0
    def __init__(self, args=None):
        super(ChainedAuthProvider, self).__init__(args)

        # FIXME: Allow overwriting default providers via args
        self._load_installed_providers()

        reg = ComponentRegistry()

        sub_providers = []
        for _, (priority, provider, provider_args
                ) in reg.load_extensions('iotile.default_auth_providers'):
            if provider not in self._auth_factories:
                raise ExternalError(
                    "Default authentication provider list references unknown auth provider",
                    provider_name=provider,
                    known_providers=self._auth_factories.keys())
            configured = self._auth_factories[provider](provider_args)
            sub_providers.append((priority, configured))

        sub_providers.sort(key=lambda x: x[0])
        self.providers = sub_providers
Пример #28
0
    def __init__(self, args=None):
        super(ChainedAuthProvider, self).__init__(args)

        #FIXME: Allow overwriting default providers via args
        self._load_installed_providers()

        sub_providers = []
        for entry in pkg_resources.iter_entry_points(
                'iotile.default_auth_providers'):
            priority, provider, args = entry.load()

            if provider not in self._auth_factories:
                raise ExternalError(
                    "Default authentication provider list references unknown auth provider",
                    provider_name=provider,
                    known_providers=self._auth_factories.keys())
            configured = self._auth_factories[provider](args)
            sub_providers.append((priority, configured))

        sub_providers.sort(key=lambda x: x[0])
        self.providers = sub_providers
Пример #29
0
    def __init__(self, args):
        super(NativeBLEVirtualInterface, self).__init__()

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

        # 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 args
        port = None
        if 'port' in args:
            port = args['port']

        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))

        if 'voltage' in args:
            self.voltage = float(args['voltage'])
        else:
            self.voltage = 3.8

        # 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 the callback function into baBLE
        self.bable.on_write_request(self._on_write_request)
        self.bable.on_connected(self._on_connected)
        self.bable.on_disconnected(self._on_disconnected)

        # Initialize state
        self.connected = False
        self._connection_handle = 0

        self.payload_notif = False
        self.header_notif = False
        self.streaming = False
        self.tracing = False

        # Keep track of whether we've launched our state machine
        # to stream or trace data so that when we find more data available
        # in process() we know not to restart the streaming/tracing process
        self._stream_sm_running = False
        self._trace_sm_running = False

        self.rpc_payload = bytearray(20)
        self.rpc_header = bytearray(20)

        try:
            self._initialize_system_sync()
        except Exception:
            self.stop_sync()
            raise
Пример #30
0
    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