예제 #1
0
    def _prepare(self, pbw_path):
        self._bundle = PebbleBundle(
            pbw_path,
            hardware=self._pebble.watch_info.running.hardware_platform)
        if not self._bundle.is_app_bundle:
            raise AppInstallError("This is not an app bundle.")

        self.total_size = self._bundle.zip.getinfo(
            self._bundle.get_app_path()).file_size
        if self._bundle.has_resources:
            self.total_size += self._bundle.zip.getinfo(
                self._bundle.get_resource_path()).file_size

        if self._bundle.has_worker:
            self.total_size += self._bundle.zip.getinfo(
                self._bundle.get_worker_path()).file_size
예제 #2
0
    def _prepare(self, pbw_path):
        self._bundle = PebbleBundle(pbw_path, hardware=self._pebble.watch_info.running.hardware_platform)
        if not self._bundle.is_app_bundle:
            raise AppInstallError("This is not an app bundle.")

        self.total_size = self._bundle.zip.getinfo(self._bundle.get_app_path()).file_size
        if self._bundle.has_resources:
            self.total_size += self._bundle.zip.getinfo(self._bundle.get_resource_path()).file_size

        if self._bundle.has_worker:
            self.total_size += self._bundle.zip.getinfo(self._bundle.get_worker_path()).file_size
예제 #3
0
 def _infer_installed_platform(self):
     available_prefixes = self.runtime.pbw.prefixes
     valid_prefixes = PebbleBundle.prefixes_for_hardware(self.pebble.watch_info.running.hardware_platform)
     for prefix in valid_prefixes:
         if prefix in available_prefixes:
             if prefix == '':
                 return 'aplite'
             else:
                 return prefix[:-1]
     raise JSRuntimeException("Internal consistency error: This app's prefixes (%s) and the supported "
                              "prefixes (%s) do not intersect!" % (available_prefixes, valid_prefixes))
예제 #4
0
class AppInstaller(EventSourceMixin):
    """
    Installs an app on the Pebble via Pebble Protocol.

    .. note:
       If you use a :class:`BlobDBClient` in use elsewhere, pass it in here. If none is passed it will create one,
       and they will conflict.

    :param pebble: The :class:`PebbleConnection` over which to install the app.
    :type pebble: .PebbleConnection
    :param pbw_path: The path to the PBW file to be installed on the filesystem.
    :type pbw_path: str
    :param blobdb_client: An optional :class:`BlobDBClient` to use, if one already exists. If omitted, one will be
                          created.
    :type blobdb_client: .BlobDBClient
    """
    def __init__(self, pebble, pbw_path, blobdb_client=None):
        self._pebble = pebble
        self._blobdb = blobdb_client or BlobDBClient(pebble)
        EventSourceMixin.__init__(self)
        #: Total number of bytes sent so far.
        self.total_sent = 0
        #: Total number of bytes to send.
        self.total_size = None
        self._prepare(pbw_path)

    def _prepare(self, pbw_path):
        self._bundle = PebbleBundle(pbw_path, hardware=self._pebble.watch_info.running.hardware_platform)
        if not self._bundle.is_app_bundle:
            raise AppInstallError("This is not an app bundle.")

        self.total_size = self._bundle.zip.getinfo(self._bundle.get_app_path()).file_size
        if self._bundle.has_resources:
            self.total_size += self._bundle.zip.getinfo(self._bundle.get_resource_path()).file_size

        if self._bundle.has_worker:
            self.total_size += self._bundle.zip.getinfo(self._bundle.get_worker_path()).file_size

    def install(self):
        """
        Installs an app. Blocks until the installation is complete, or raises :exc:`AppInstallError` if it fails.

        While this method runs, "progress" events will be emitted regularly with the following signature: ::

           (sent_this_interval, sent_total, total_size)
        """
        if self._pebble.firmware_version.major < 3:
            self._install_legacy2()
        else:
            self._install_modern()

    def _install_modern(self):
        metadata = self._bundle.get_app_metadata()
        app_uuid = metadata['uuid']
        blob_packet = AppMetadata(uuid=app_uuid, flags=metadata['flags'], icon=metadata['icon_resource_id'],
                                  app_version_major=metadata['app_version_major'],
                                  app_version_minor=metadata['app_version_minor'],
                                  sdk_version_major=metadata['sdk_version_major'],
                                  sdk_version_minor=metadata['sdk_version_minor'],
                                  app_face_bg_color=0, app_face_template_id=0, app_name=metadata['app_name'])

        result = SyncWrapper(self._blobdb.insert, BlobDatabaseID.App, app_uuid, blob_packet.serialise()).wait()
        if result != BlobStatus.Success:
            raise AppInstallError("BlobDB error: {!s}".format(result))

        # Start the app.
        self._pebble.send_packet(AppRunState(data=AppRunStateStart(uuid=app_uuid)))

        # Wait for a launch request.
        app_fetch = self._pebble.read_from_endpoint(AppFetchRequest)
        if app_fetch.uuid != app_uuid:
            self._pebble.send_packet(AppFetchResponse(response=AppFetchStatus.InvalidUUID))
            raise AppInstallError("App requested the wrong UUID! Asked for {}; expected {}".format(
                app_fetch.uuid, app_uuid))
        self._broadcast_event('progress', 0, self.total_sent, self.total_size)

        # Send the app over
        binary = self._bundle.zip.read(self._bundle.get_app_path())
        self._send_part(PutBytesType.Binary, binary, app_fetch.app_id)

        if self._bundle.has_resources:
            resources = self._bundle.zip.read(self._bundle.get_resource_path())
            self._send_part(PutBytesType.Resources, resources, app_fetch.app_id)

        if self._bundle.has_worker:
            worker = self._bundle.zip.read(self._bundle.get_worker_path())
            self._send_part(PutBytesType.Worker, worker, app_fetch.app_id)

    def _send_part(self, type, object, install_id):
        pb = PutBytes(self._pebble, type, object, app_install_id=install_id)
        pb.register_handler("progress", self._handle_progress)
        pb.send()

    def _install_legacy2(self):
        metadata = self._bundle.get_app_metadata()
        app_uuid = metadata['uuid']

        self._pebble.send_packet(LegacyAppInstallRequest(data=LegacyUpgradeAppUUID(uuid=app_uuid)))
        # We don't really care if this worked; we're just waiting for it.
        self._pebble.read_from_endpoint(LegacyAppInstallResponse)

        # Find somewhere to install to.
        self._pebble.send_packet(LegacyAppInstallRequest(data=LegacyBankInfoRequest()))
        result = self._pebble.read_from_endpoint(LegacyAppInstallResponse).data
        assert isinstance(result, LegacyBankInfoResponse)
        first_free = 0
        for app in result.apps:
            assert isinstance(app, LegacyBankEntry)
            if app.bank_number == first_free:
                first_free += 1
        if first_free == result.bank_count:
            raise AppInstallError("No app banks free.")

        # Send the app over
        binary = self._bundle.zip.read(self._bundle.get_app_path())
        self._send_part_legacy2(PutBytesType.Binary, binary, first_free)

        if self._bundle.has_resources:
            resources = self._bundle.zip.read(self._bundle.get_resource_path())
            self._send_part_legacy2(PutBytesType.Resources, resources, first_free)

        if self._bundle.has_worker:
            worker = self._bundle.zip.read(self._bundle.get_worker_path())
            self._send_part_legacy2(PutBytesType.Worker, worker, first_free)

        # Mark it as available
        self._pebble.send_packet(LegacyAppInstallRequest(data=LegacyAppAvailable(bank=first_free, vibrate=True)))
        self._pebble.read_from_endpoint(LegacyAppInstallResponse)

        # Launch it (which is painful on 2.x).
        appmessage = AppMessageService(self._pebble, message_type=LegacyAppLaunchMessage)
        appmessage.send_message(app_uuid, {
            LegacyAppLaunchMessage.Keys.RunState: AMUint8(LegacyAppLaunchMessage.States.Running)
        })
        appmessage.shutdown()

    def _send_part_legacy2(self, type, object, bank):
        pb = PutBytes(self._pebble, type, object, bank=bank)
        pb.register_handler("progress", self._handle_progress)
        pb.send()

    def _handle_progress(self, sent, total_sent, total_length):
        self.total_sent += sent
        self._broadcast_event('progress', sent, self.total_sent, self.total_size)
예제 #5
0
class AppInstaller(EventSourceMixin):
    """
    Installs an app on the Pebble via Pebble Protocol.

    .. note:
       If you use a :class:`BlobDBClient` in use elsewhere, pass it in here. If none is passed it will create one,
       and they will conflict.

    :param pebble: The :class:`PebbleConnection` over which to install the app.
    :type pebble: .PebbleConnection
    :param pbw_path: The path to the PBW file to be installed on the filesystem.
    :type pbw_path: str
    :param blobdb_client: An optional :class:`BlobDBClient` to use, if one already exists. If omitted, one will be
                          created.
    :type blobdb_client: .BlobDBClient
    """
    def __init__(self, pebble, pbw_path, blobdb_client=None):
        self._pebble = pebble
        self._blobdb = blobdb_client or BlobDBClient(pebble)
        EventSourceMixin.__init__(self)
        #: Total number of bytes sent so far.
        self.total_sent = 0
        #: Total number of bytes to send.
        self.total_size = None
        self._prepare(pbw_path)

    def _prepare(self, pbw_path):
        self._bundle = PebbleBundle(
            pbw_path,
            hardware=self._pebble.watch_info.running.hardware_platform)
        if not self._bundle.is_app_bundle:
            raise AppInstallError("This is not an app bundle.")

        self.total_size = self._bundle.zip.getinfo(
            self._bundle.get_app_path()).file_size
        if self._bundle.has_resources:
            self.total_size += self._bundle.zip.getinfo(
                self._bundle.get_resource_path()).file_size

        if self._bundle.has_worker:
            self.total_size += self._bundle.zip.getinfo(
                self._bundle.get_worker_path()).file_size

    def install(self):
        """
        Installs an app. Blocks until the installation is complete, or raises :exc:`AppInstallError` if it fails.

        While this method runs, "progress" events will be emitted regularly with the following signature: ::

           (sent_this_interval, sent_total, total_size)
        """
        if self._pebble.firmware_version.major < 3:
            self._install_legacy2()
        else:
            self._install_modern()

    def _install_modern(self):
        metadata = self._bundle.get_app_metadata()
        app_uuid = metadata['uuid']
        blob_packet = AppMetadata(
            uuid=app_uuid,
            flags=metadata['flags'],
            icon=metadata['icon_resource_id'],
            app_version_major=metadata['app_version_major'],
            app_version_minor=metadata['app_version_minor'],
            sdk_version_major=metadata['sdk_version_major'],
            sdk_version_minor=metadata['sdk_version_minor'],
            app_face_bg_color=0,
            app_face_template_id=0,
            app_name=metadata['app_name'])

        result = SyncWrapper(self._blobdb.insert, BlobDatabaseID.App, app_uuid,
                             blob_packet.serialise()).wait()
        if result != BlobStatus.Success:
            raise AppInstallError("BlobDB error: {!s}".format(result))

        # Start the app.
        app_fetch = self._pebble.send_and_read(
            AppRunState(data=AppRunStateStart(uuid=app_uuid)), AppFetchRequest)
        if app_fetch.uuid != app_uuid:
            self._pebble.send_packet(
                AppFetchResponse(response=AppFetchStatus.InvalidUUID))
            raise AppInstallError(
                "App requested the wrong UUID! Asked for {}; expected {}".
                format(app_fetch.uuid, app_uuid))
        self._broadcast_event('progress', 0, self.total_sent, self.total_size)

        # Send the app over
        binary = self._bundle.zip.read(self._bundle.get_app_path())
        self._send_part(PutBytesType.Binary, binary, app_fetch.app_id)

        if self._bundle.has_resources:
            resources = self._bundle.zip.read(self._bundle.get_resource_path())
            self._send_part(PutBytesType.Resources, resources,
                            app_fetch.app_id)

        if self._bundle.has_worker:
            worker = self._bundle.zip.read(self._bundle.get_worker_path())
            self._send_part(PutBytesType.Worker, worker, app_fetch.app_id)

    def _send_part(self, type, object, install_id):
        pb = PutBytes(self._pebble, type, object, app_install_id=install_id)
        pb.register_handler("progress", self._handle_progress)
        pb.send()

    def _install_legacy2(self):
        metadata = self._bundle.get_app_metadata()
        app_uuid = metadata['uuid']

        # We don't really care if this worked; we're just waiting for it.
        self._pebble.send_and_read(
            LegacyAppInstallRequest(data=LegacyUpgradeAppUUID(uuid=app_uuid)),
            LegacyAppInstallResponse)

        # Find somewhere to install to.
        result = self._pebble.send_and_read(
            LegacyAppInstallRequest(data=LegacyBankInfoRequest()),
            LegacyAppInstallResponse).data
        assert isinstance(result, LegacyBankInfoResponse)
        first_free = 0
        for app in result.apps:
            assert isinstance(app, LegacyBankEntry)
            if app.bank_number == first_free:
                first_free += 1
        if first_free == result.bank_count:
            raise AppInstallError("No app banks free.")

        # Send the app over
        binary = self._bundle.zip.read(self._bundle.get_app_path())
        self._send_part_legacy2(PutBytesType.Binary, binary, first_free)

        if self._bundle.has_resources:
            resources = self._bundle.zip.read(self._bundle.get_resource_path())
            self._send_part_legacy2(PutBytesType.Resources, resources,
                                    first_free)

        if self._bundle.has_worker:
            worker = self._bundle.zip.read(self._bundle.get_worker_path())
            self._send_part_legacy2(PutBytesType.Worker, worker, first_free)

        # Mark it as available
        self._pebble.send_and_read(
            LegacyAppInstallRequest(
                data=LegacyAppAvailable(bank=first_free, vibrate=True)),
            LegacyAppInstallResponse)

        # Launch it (which is painful on 2.x).
        appmessage = AppMessageService(self._pebble,
                                       message_type=LegacyAppLaunchMessage)
        appmessage.send_message(
            app_uuid, {
                LegacyAppLaunchMessage.Keys.RunState:
                AMUint8(LegacyAppLaunchMessage.States.Running)
            })
        appmessage.shutdown()

    def _send_part_legacy2(self, type, object, bank):
        pb = PutBytes(self._pebble, type, object, bank=bank)
        pb.register_handler("progress", self._handle_progress)
        pb.send()

    def _handle_progress(self, sent, total_sent, total_length):
        self.total_sent += sent
        self._broadcast_event('progress', sent, self.total_sent,
                              self.total_size)