Beispiel #1
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     try:
         self._assistant = get_backend('assistant.google')
     except KeyError as e:
         try:
             self._assistant = get_backend('assistant.google.pushtotalk')
         except KeyError as e:
             logging.warning(
                 'google.assistant backend not configured/initialized')
             self._assistant = None
Beispiel #2
0
    def notify_web_clients(self, event):
        backends = Config.get_backends()
        if 'http' not in backends: return

        backend = get_backend('http')
        if backend:
            backend.notify_web_clients(event)

        backend = get_backend('websocket')
        if backend:
            backend.notify_web_clients(event)
Beispiel #3
0
    def _get_redis():
        from redis import Redis

        redis_args = {
            'host': 'localhost',
        }

        redis_backend = get_backend('redis')
        if redis_backend:
            redis_args = get_backend('redis').redis_args
        redis_args['socket_timeout'] = 1
        return Redis(**redis_args)
Beispiel #4
0
def build(args):
    global workdir

    ports = set()
    deps = set()

    parser = argparse.ArgumentParser(prog='platydock build',
                                     description='Build a Platypush image ' +
                                     'from a config.yaml')

    parser.add_argument('-c',
                        '--config',
                        type=str,
                        required=True,
                        help='Path to the platypush configuration file')

    opts, args = parser.parse_known_args(args)

    cfgfile = os.path.abspath(os.path.expanduser(opts.config))
    Config.init(cfgfile)
    register_backends()
    backend_config = Config.get_backends()

    if backend_config.get('http'):
        http_backend = get_backend('http')
        ports.add(http_backend.port)
        if http_backend.websocket_port:
            ports.add(http_backend.websocket_port)

    if backend_config.get('tcp'):
        ports.add(get_backend('tcp').port)

    if backend_config.get('websocket'):
        ports.add(get_backend('websocket').port)

    for name in Config.get_backends().keys():
        deps.update(_parse_deps(get_backend(name)))

    for name in Config.get_plugins().keys():
        try:
            deps.update(_parse_deps(get_plugin(name)))
        except:
            pass

    devdir = os.path.join(workdir, Config.get('device_id'))
    generate_dockerfile(deps=deps, ports=ports, cfgfile=cfgfile, devdir=devdir)

    subprocess.call([
        'docker', 'build', '-t',
        'platypush-{}'.format(Config.get('device_id')), devdir
    ])
Beispiel #5
0
    def send_message(self, msg, topic, server=None, **kwargs):
        """
        :param msg: Message to send - as a string, bytes stream, JSON, Platypush message, dictionary, or anything that implements ``__str__``

        :param server: Kafka server name or address + port (format: ``host:port``). If None, then the default server will be used
        :type server: str
        """

        from kafka import KafkaProducer

        if not server:
            if not self.server:
                try:
                    kafka_backend = get_backend('kafka')
                    server = kafka_backend.server
                except:
                    raise RuntimeError(
                        'No Kafka server nor default server specified')
            else:
                server = self.server

        if isinstance(msg, dict) or isinstance(msg, list):
            msg = json.dumps(msg)
        msg = str(msg).encode('utf-8')

        producer = KafkaProducer(bootstrap_servers=server)
        producer.send(topic, msg)
        producer.flush()
Beispiel #6
0
def _get_http_port() -> int:
    http = None
    if Config.get('backend.http'):
        http = get_backend('http')

    assert http, 'The http backend is required in order to subscribe to notifications'
    return http.port
Beispiel #7
0
    def __init__(self,
                 host='localhost',
                 port=_DEFAULT_SNAPCAST_PORT,
                 *args,
                 **kwargs):
        """
        :param host: Default Snapcast server host (default: localhost)
        :type host: str

        :param port: Default Snapcast server control port (default: 1705)
        :type port: int
        """

        super().__init__(**kwargs)

        self.host = host
        self.port = port
        self._latest_req_id = 0
        self._latest_req_id_lock = threading.RLock()

        backend = get_backend('music.snapcast')
        backend_hosts = backend.hosts if backend else [self.host]
        backend_ports = backend.ports if backend else [self.port]
        self.backend_hosts = backend_hosts
        self.backend_ports = backend_ports
Beispiel #8
0
    def _send_event(self, event):
        backend = get_backend('redis')
        if not backend:
            self.logger.warning(
                'Redis backend not configured, I cannot propagate the following event: {}'
                .format(event))
            return

        backend.send_message(event)
Beispiel #9
0
    def __init__(self, *args, **kwargs):
        super().__init__()
        self.args = args
        self.kwargs = kwargs

        if not kwargs:
            try:
                redis_backend = get_backend('redis')
                if redis_backend and redis_backend.redis_args:
                    self.kwargs = redis_backend.redis_args
            except Exception as e:
                self.logger.debug(e)
Beispiel #10
0
    def __init__(self, *args, **kwargs):
        super().__init__()
        self.args = args
        self.kwargs = kwargs

        if not kwargs:
            try:
                redis_backend = get_backend('redis')
                if redis_backend and redis_backend.redis_args:
                    self.kwargs = redis_backend.redis_args
            except:
                pass
Beispiel #11
0
    def spotify_user_authenticate(self,
                                  scopes: Optional[Iterable[str]] = None):
        """
        Authenticate to the Spotify API for requests that require access to user data.
        """
        if self._spotify_user_token:
            return

        try:
            self._spotify_load_user_credentials(
                scopes=scopes or self.spotify_required_scopes)
            if self._spotify_user_token:
                return

            if self._spotify_refresh_token:
                try:
                    self._spotify_refresh_user_token()
                    return
                except Exception as e:
                    self._spotify_logger.error(
                        f'Unable to refresh the user access token: {e}')
        except MissingScopesException as e:
            self._spotify_logger.warning(e)

        http = get_backend('http')
        assert http, 'HTTP backend not configured'
        callback_url = '{scheme}://{host}:{port}/spotify/auth_callback'.format(
            scheme="https" if http.ssl_context else "http",
            host=get_ip_or_hostname(),
            port=http.port,
        )

        state = b64encode(bytes([randint(0, 255) for _ in range(18)])).decode()
        self._spotify_logger.warning(
            '\n\nUnauthenticated Spotify session or scopes not provided by the user. Please '
            'open the following URL in a browser to authenticate:\n'
            'https://accounts.spotify.com/authorize?client_id='
            f'{self._spotify_api_credentials[0]}&'
            f'response_type=code&redirect_uri={parse.quote(callback_url, safe="")}'
            f'&scope={parse.quote(" ".join(scopes))}&state={state}.\n'
            'Replace the host in the callback URL with the IP/hostname of this machine '
            f'accessible to your browser if required, and make sure to add {callback_url} '
            'to the list of whitelisted callbacks on your Spotify application page.\n'
        )

        redis = get_redis()
        msg = json.loads(
            redis.blpop(self.get_spotify_queue_for_state(state))[1].decode())
        assert not msg.get('error'), f'Authentication error: {msg["error"]}'
        self._spotify_user_authenticate_phase_2(code=msg['code'],
                                                callback_url=callback_url,
                                                scopes=scopes)
Beispiel #12
0
    def send_file(self, filename: str, device: str = None):
        """
        Send a file.

        :param device: Device ID or name (default: None, all devices)
        :param filename: Path to the local file
        """

        if device:
            device = self.get_device(device).output
            if not device:
                raise RuntimeError('No such device')

        pushbullet = get_backend('pushbullet')
        resp = requests.post('https://api.pushbullet.com/v2/upload-request',
                             data=json.dumps({'file_name': os.path.basename(filename)}),
                             headers={'Authorization': 'Bearer ' + self.token,
                                      'Content-Type': 'application/json'})

        if resp.status_code != 200:
            raise Exception('Pushbullet file upload request failed with status {}'.
                            format(resp.status_code))

        r = resp.json()
        resp = requests.post(r['upload_url'], data=r['data'],
                             files={'file': open(filename, 'rb')})

        if resp.status_code != 204:
            raise Exception('Pushbullet file upload failed with status {}'.
                            format(resp.status_code))

        resp = requests.post('https://api.pushbullet.com/v2/pushes',
                             headers={'Authorization': 'Bearer ' + self.token,
                                      'Content-Type': 'application/json'},

                             data=json.dumps({
                                 'type': 'file',
                                 'device_iden': device['iden'] if device else None,
                                 'file_name': r['file_name'],
                                 'file_type': r['file_type'],
                                 'file_url': r['file_url'] }))

        if resp.status_code >= 400:
            raise Exception('Pushbullet file push failed with status {}'.
                            format(resp.status_code))

        return {
            'filename': r['file_name'],
            'type': r['file_type'],
            'url': r['file_url']
        }
Beispiel #13
0
    def __init__(self, assistant=None, *args, **kwargs):
        super().__init__(*args, **kwargs)

        if assistant:
            self._assistant = assistant
        else:
            self._assistant = get_backend('assistant.google')

            if not self._assistant:
                self._assistant = get_plugin('assistant.google.pushtotalk')

            if not self._assistant:
                logger.warning('Assistant plugin/backend not configured/initialized')
                self._assistant = None
Beispiel #14
0
    def start_streaming(self, media, subtitles=None, download=False):
        """
        Starts streaming local media over the specified HTTP port.
        The stream will be available to HTTP clients on
        `http://{this-ip}:{http_backend_port}/media/<media_id>`

        :param media: Media to stream
        :type media: str

        :param subtitles: Path or URL to the subtitles track to be used
        :type subtitles: str

        :param download: Set to True if you prefer to download the file from
            the streaming link instead of streaming it
        :type download: bool

        :return: dict containing the streaming URL.Example:

        .. code-block:: json

            {
                "id": "0123456abcdef.mp4",
                "source": "file:///mnt/media/movies/movie.mp4",
                "mime_type": "video/mp4",
                "url": "http://192.168.1.2:8008/media/0123456abcdef.mp4"
            }

        """
        import requests

        http = get_backend('http')
        if not http:
            self.logger.warning(
                'Unable to stream {}: HTTP backend unavailable'.format(media))
            return

        self.logger.info('Starting streaming {}'.format(media))
        response = requests.put('{url}/media{download}'.format(
            url=http.local_base_url, download='?download' if download else ''),
                                json={
                                    'source': media,
                                    'subtitles': subtitles
                                })

        if not response.ok:
            self.logger.warning('Unable to start streaming: {}'.format(
                response.text or response.reason))
            return None, (response.text or response.reason)

        return response.json()
Beispiel #15
0
    def _get_redis(self):
        import redis

        redis_backend = get_backend('redis')
        if not redis_backend:
            self.logger.warning(
                'Redis backend not configured - some ' +
                'web server features may not be working properly')
            redis_args = {}
        else:
            redis_args = redis_backend.redis_args

        redis = redis.Redis(**redis_args)
        return redis
Beispiel #16
0
    def stop_streaming(self, media_id):
        http = get_backend('http')
        if not http:
            self.logger.warning(
                'Cannot unregister {}: HTTP backend unavailable'.format(
                    media_id))
            return

        response = requests.delete('{url}/media/{id}'.format(
            url=http.local_base_url, id=media_id))

        if not response.ok:
            self.logger.warning('Unable to unregister media_id {}: {}'.format(
                media_id, response.reason))
            return

        return response.json()
Beispiel #17
0
    def send_file(self, filename):
        """
        Send a file.

        :param filename: Path to the local file
        :type filename: str
        """

        pushbullet = get_backend('pushbullet')
        resp = requests.post('https://api.pushbullet.com/v2/upload-request',
                             data=json.dumps({'file_name': os.path.basename(filename)}),
                             headers={'Authorization': 'Bearer ' + pushbullet.token,
                                      'Content-Type': 'application/json'})

        if resp.status_code != 200:
            raise Exception('Pushbullet file upload request failed with status {}'.
                            format(resp.status_code))

        r = resp.json()
        resp = requests.post(r['upload_url'], data=r['data'],
                             files={'file': open(filename, 'rb')})

        if resp.status_code != 204:
            raise Exception('Pushbullet file upload failed with status {}'.
                            format(resp.status_code))

        resp = requests.post('https://api.pushbullet.com/v2/pushes',
                             headers={'Authorization': 'Bearer ' + pushbullet.token,
                                      'Content-Type': 'application/json'},

                             data=json.dumps({
                                 'type': 'file',
                                 'file_name': r['file_name'],
                                 'file_type': r['file_type'],
                                 'file_url': r['file_url'] }))

        if resp.status_code >= 400:
            raise Exception('Pushbullet file push failed with status {}'.
                            format(resp.status_code))

        return {
            'filename': r['file_name'],
            'type': r['file_type'],
            'url': r['file_url']
        }
Beispiel #18
0
    def __init__(self, token: Optional[str] = None, **kwargs):
        """
        :param token: Pushbullet API token. If not set the plugin will try to retrieve it from
            the Pushbullet backend configuration, if available
        """
        super().__init__(**kwargs)

        if not token:
            backend = get_backend('pushbullet')
            if not backend or not backend.token:
                raise AttributeError('No Pushbullet token specified')

            self.token = backend.token
        else:
            self.token = token

        self._devices = []
        self._devices_by_id = {}
        self._devices_by_name = {}
Beispiel #19
0
    def send_note(self, body=None, title=None, **kwargs):
        """
        Send a note push.

        :param kwargs: Push arguments, see https://docs.pushbullet.com/#create-push
        :type kwargs: dict
        """

        pushbullet = get_backend('pushbullet')
        kwargs['body'] = body
        kwargs['title'] = title
        kwargs['type'] = 'note'
        resp = requests.post('https://api.pushbullet.com/v2/pushes',
                             data=json.dumps(kwargs),
                             headers={'Authorization': 'Bearer ' + pushbullet.token,
                                      'Content-Type': 'application/json'})

        if resp.status_code >= 400:
            raise Exception('Pushbullet push failed with status {}: {}'.
                            format(resp.status_code, resp.json()))
Beispiel #20
0
    def send_clipboard(self, text: str):
        """
        Copy text to the clipboard of a device.

        :param text: Text to be copied.
        """
        backend = get_backend('pushbullet')
        device_id = backend.get_device_id() if backend else None

        resp = requests.post('https://api.pushbullet.com/v2/ephemerals',
                             data=json.dumps({
                                 'type': 'push',
                                 'push': {
                                     'body': text,
                                     'type': 'clip',
                                     'source_device_iden': device_id,
                                 },
                             }),
                             headers={'Authorization': 'Bearer ' + self.token,
                                      'Content-Type': 'application/json'})

        resp.raise_for_status()
Beispiel #21
0
    def send_message(self, msg, queue_name=None, **kwargs):
        """
        Sends a platypush.message.Message to a node.
        To be implemented in the derived classes. By default, if the Redis
        backend is configured then it will try to deliver the message to
        other consumers through the configured Redis main queue.

        :param msg: The message to send
        :param queue_name: Send the message on a specific queue (default: the queue_name configured on the Redis backend)
        """

        try:
            redis = get_backend('redis')
            if not redis:
                raise KeyError()
        except KeyError:
            self.logger.warning(
                "Backend {} does not implement send_message " +
                "and the fallback Redis backend isn't configured")
            return

        redis.send_message(msg, queue_name=queue_name)
Beispiel #22
0
    def run(self):
        super().run()

        if not self.redis:
            self.redis = get_backend('redis')

        if self.start_recording_on_startup:
            self.send_camera_action(self.CameraAction.START_RECORDING)

        self.logger.info('Initialized Pi camera backend')

        while not self.should_stop():
            try:
                msg = self.redis.get_message(self.redis_queue)

                if msg.get('action') == self.CameraAction.START_RECORDING:
                    self.start_recording()
                elif msg.get('action') == self.CameraAction.STOP_RECORDING:
                    self.stop_recording()
                elif msg.get('action') == self.CameraAction.TAKE_PICTURE:
                    self.take_picture(image_file=msg.get('image_file'))
            except Exception as e:
                self.logger.exception(e)
Beispiel #23
0
 def state(self):
     """
     Return the state of the controller
     """
     return get_backend('wiimote').get_state()
Beispiel #24
0
 def close(self):
     """
     Closes the connection with the WiiMote
     """
     get_backend('wiimote').close()
Beispiel #25
0
 def _get_wiimote(cls):
     return get_backend('wiimote').get_wiimote()
Beispiel #26
0
 def start_conversation(self):
     assistant = get_backend('assistant.google.pushtotalk')
     assistant.start_conversation()
     return Response(output='', errors=[])
Beispiel #27
0
 def stop_conversation(self):
     """
     Programmatically stop a running conversation with the assistant
     """
     assistant = get_backend('assistant.google')
     assistant.stop_conversation()
Beispiel #28
0
 def _get_redis(self, socket_timeout=1.0):
     if not self.redis:
         redis_args = get_backend('redis').redis_args
         redis_args['socket_timeout'] = socket_timeout
         self.redis = Redis(**redis_args)
     return self.redis
Beispiel #29
0
    def _get_redis():
        from redis import Redis

        redis_args = get_backend('redis').redis_args
        redis_args['socket_timeout'] = 1
        return Redis(**redis_args)
Beispiel #30
0
    def _get_backend() -> ZwaveBackend:
        backend = get_backend('zwave')
        if not backend:
            raise AssertionError('Z-Wave backend not configured')

        return backend