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
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)
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)
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 ])
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()
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
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
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)
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)
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
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)
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'] }
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
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()
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
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()
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'] }
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 = {}
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()))
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()
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)
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)
def state(self): """ Return the state of the controller """ return get_backend('wiimote').get_state()
def close(self): """ Closes the connection with the WiiMote """ get_backend('wiimote').close()
def _get_wiimote(cls): return get_backend('wiimote').get_wiimote()
def start_conversation(self): assistant = get_backend('assistant.google.pushtotalk') assistant.start_conversation() return Response(output='', errors=[])
def stop_conversation(self): """ Programmatically stop a running conversation with the assistant """ assistant = get_backend('assistant.google') assistant.stop_conversation()
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
def _get_redis(): from redis import Redis redis_args = get_backend('redis').redis_args redis_args['socket_timeout'] = 1 return Redis(**redis_args)
def _get_backend() -> ZwaveBackend: backend = get_backend('zwave') if not backend: raise AssertionError('Z-Wave backend not configured') return backend