def _worker(host, port): try: if exclude_local and (host == 'localhost' or host == Config.get('device_id')): return server_status = self.status(host=host, port=port).output client_status = self.status(host=host, port=port, client=Config.get('device_id')).output if client_status.get('config', {}).get('volume', {}).get('muted'): return group = [g for g in server_status.get('groups', {}) if g.get('id') == client_status.get('group_id')].pop(0) if group.get('muted'): return stream = [s for s in server_status.get('streams') if s.get('id') == group.get('stream_id')].pop(0) if stream.get('status') != 'playing': return playing_hosts[host] = port except Exception as e: self.logger.warning(('Error while retrieving the status of ' + 'Snapcast host at {}:{}: {}').format( host, port, str(e)))
def plugin_route(plugin): """ Route to the plugin pane template """ js_folder = os.path.abspath(os.path.join(template_folder, '..', 'static', 'js')) style_folder = os.path.abspath(os.path.join(template_folder, '..', 'static', 'css', 'dist')) template_file = os.path.join(template_folder, 'plugins', plugin, 'index.html') script_file = os.path.join(js_folder, 'plugins', plugin, 'index.js') style_file = os.path.join(style_folder, 'webpanel', 'plugins', plugin+'.css') conf = Config.get(plugin) or {} if os.path.isfile(template_file): conf['_template_file'] = '/' + '/'.join(template_file.split(os.sep)[-3:]) if os.path.isfile(script_file): conf['_script_file'] = '/'.join(script_file.split(os.sep)[-4:]) if os.path.isfile(style_file): conf['_style_file'] = 'css/dist/' + style_file[len(style_folder)+1:] http_conf = Config.get('backend.http') return render_template('plugin.html', plugin=plugin, conf=conf, template=conf.get('_template_file', {}), script=conf.get('_script_file', {}), style=conf.get('_style_file', {}), utils=HttpUtils, token=Config.get('token'), websocket_port=get_websocket_port(), template_folder=template_folder, static_folder=static_folder, has_ssl=http_conf.get('ssl_cert') is not None)
def build(args): global workdir ports = 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') parser.add_argument('-p', '--python-version', type=str, default='3.9', help='Python version to be used') opts, args = parser.parse_known_args(args) cfgfile = os.path.abspath(os.path.expanduser(opts.config)) manifest._available_package_manager = 'apt' # Force apt for Debian-based Docker images install_cmds = manifest.get_dependencies_from_conf(cfgfile) python_version = opts.python_version backend_config = Config.get_backends() # Container exposed ports if backend_config.get('http'): from platypush.backend.http import HttpBackend # noinspection PyProtectedMember ports.add(backend_config['http'].get('port', HttpBackend._DEFAULT_HTTP_PORT)) # noinspection PyProtectedMember ports.add(backend_config['http'].get( 'websocket_port', HttpBackend._DEFAULT_WEBSOCKET_PORT)) if backend_config.get('tcp'): ports.add(backend_config['tcp']['port']) if backend_config.get('websocket'): from platypush.backend.websocket import WebsocketBackend # noinspection PyProtectedMember ports.add(backend_config['websocket'].get( 'port', WebsocketBackend._default_websocket_port)) dev_dir = os.path.join(workdir, Config.get('device_id')) generate_dockerfile(deps=dict(install_cmds), ports=ports, cfgfile=cfgfile, device_dir=dev_dir, python_version=python_version) subprocess.call([ 'docker', 'build', '-t', 'platypush-{}'.format(Config.get('device_id')), dev_dir ])
def dashboard(): """ Route for the fullscreen dashboard """ http_conf = Config.get('backend.http') dashboard_conf = http_conf.get('dashboard', {}) return render_template('dashboard.html', config=dashboard_conf, utils=HttpUtils, token=Config.get('token'), static_folder=static_folder, template_folder=template_folder, websocket_port=get_websocket_port(), has_ssl=http_conf.get('ssl_cert') is not None)
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 __init__(self, url, title=None, headers=None, params=None, max_entries=None, extract_content=False, digest_format=None, user_agent: str = user_agent, body_style: str = 'font-size: 22px; ' + 'font-family: "Merriweather", Georgia, "Times New Roman", Times, serif;', title_style: str = 'margin-top: 30px', subtitle_style: str = 'margin-top: 10px; page-break-after: always', article_title_style: str = 'page-break-before: always', article_link_style: str = 'color: #555; text-decoration: none; border-bottom: 1px dotted', article_content_style: str = '', *argv, **kwargs): """ :param url: URL to the RSS feed to be monitored. :param title: Optional title for the feed. :param headers: Extra headers to be passed to the request. :param params: Extra GET parameters to be appended to the URL. :param max_entries: Maximum number of entries that will be returned in a single :class:`platypush.message.event.http.rss.NewFeedEvent` event. :param extract_content: Whether the context should also be extracted (through the :class:`platypush.plugins.http.webpage.HttpWebpagePlugin` plugin) (default: ``False``). :param digest_format: Format of the digest output file (default: None, text. Other supported types: ``html`` and ``pdf`` (requires the ``weasyprint`` module installed). :param user_agent: User agent string to be passed on the request. :param body_style: CSS style for the body. :param title_style: CSS style for the feed title. :param subtitle_style: CSS style for the feed subtitle. :param article_title_style: CSS style for the article titles. :param article_link_style: CSS style for the article link. :param article_content_style: CSS style for the article content. """ self.workdir = os.path.join(os.path.expanduser(Config.get('workdir')), 'feeds') self.dbfile = os.path.join(self.workdir, 'rss.db') self.url = url self.title = title self.max_entries = max_entries self.user_agent = user_agent self.body_style = body_style self.title_style = title_style self.subtitle_style = subtitle_style self.article_title_style = article_title_style self.article_link_style = article_link_style self.article_content_style = article_content_style # If true, then the http.webpage plugin will be used to parse the content self.extract_content = extract_content self.digest_format = digest_format.lower() if digest_format else None # Supported formats: html, pdf os.makedirs(os.path.expanduser(os.path.dirname(self.dbfile)), exist_ok=True) if headers is None: headers = {} headers['User-Agent'] = self.user_agent request_args = { 'method': 'get', 'url': self.url, 'headers': headers, 'params': params or {}, } super().__init__(skip_first_call=False, args=request_args, *argv, **kwargs)
def resources_path(path): """ Custom static resources """ path_tokens = path.split('/') filename = path_tokens.pop(-1) http_conf = Config.get('backend.http') resource_dirs = http_conf.get('resource_dirs', {}) while path_tokens: if '/'.join(path_tokens) in resource_dirs: break path_tokens.pop() if not path_tokens: # Requested resource not found in the allowed resource_dirs abort(404) base_path = '/'.join(path_tokens) real_base_path = os.path.abspath(os.path.expanduser(resource_dirs[base_path])) real_path = real_base_path file_path = [s for s in re.sub(r'^{}(.*)$'.format(base_path), '\\1', path) .split('/') if s] for p in file_path[:-1]: real_path += os.sep + p file_path.pop(0) file_path = file_path.pop(0) if not real_path.startswith(real_base_path): # Directory climbing attempt abort(404) return send_from_directory(real_path, file_path)
def get_or_create_ngrok_tunnel() -> str: """ This method creates an ngrok tunnel for the local web application, useful to register public HTTPS callback URLs on the fly from plugins and backends. """ global _app_tunnel_url with _app_tunnel_lock: if _app_tunnel_url: return _app_tunnel_url local_port = _get_http_port() ngrok = None if Config.get('ngrok'): ngrok = get_plugin('ngrok') assert ngrok, 'The ngrok plugin is required in order to subscribe to notifications' tunnel_response = ngrok.create_tunnel( resource=local_port, protocol='http', bind_tls=True, ).output _app_tunnel_url = tunnel_response.get('url') assert _app_tunnel_url, 'Unable to create an ngrok tunnel' return _app_tunnel_url
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: Optional[str] = None, port: int = 8002, timeout: Optional[int] = 5, name='platypush', token_file: Optional[str] = None, **kwargs): """ :param host: IP address or host name of the smart TV. :param port: Websocket port (default: 8002). :param timeout: Connection timeout in seconds (default: 5, specify 0 or None for no timeout). :param name: Name of the remote device (default: platypush). :param token_file: Path to the token file (default: ``~/.local/share/platypush/samsungtvws/token.txt``) """ super().__init__(**kwargs) self.workdir = os.path.join(Config.get('workdir'), 'samsungtvws') if not token_file: token_file = os.path.join(self.workdir, 'token.txt') self.host = host self.port = port self.timeout = timeout self.name = name self.token_file = token_file self._connections: Dict[Tuple[host, port], SamsungTVWS] = {} os.makedirs(self.workdir, mode=0o700, exist_ok=True)
def __init__(self, config_file=None, backend=None, on_response=None): """ Constructor. Params: config_file -- Path to the configuration file - default: ~/.config/platypush/config.yaml or /etc/platypush/config.yaml) backend -- Name of the backend where pusher will send the request and wait for the response (kafka or pushbullet). Default: whatever is specified with pusher=true in your configuration file on_response -- Method that will be invoked upon response receipt. Takes a platypush.message.response.Response as arg. Default: print the response and exit. """ # Initialize the configuration self.config_file = config_file log_conf = Config.get('logging') Config.init(config_file) logging.basicConfig(level=log_conf['level'] if log_conf and 'level' in log_conf else logging.info, stream=sys.stdout) self.on_response = on_response or self.default_on_response() self.backend = backend or Config.get_default_pusher_backend() self.bus = Bus()
def get_switch_plugins(self) -> dict: """ :return: The list of enabled switch plugins as a ``name -> configuration`` map. """ from platypush.plugins.switch import SwitchPlugin return { name: Config.get(name) for name, plugin in get_enabled_plugins().items() if isinstance(plugin, SwitchPlugin) }
def get_credentials_filename(scope): from platypush.config import Config scope_name = scope.split('/')[-1] credentials_dir = os.path.join(Config.get('workdir'), 'credentials', 'google') if not os.path.exists(credentials_dir): os.makedirs(credentials_dir) return os.path.join(credentials_dir, scope_name + '.json')