async def upload_file(self, timeout=None): """Upload file to Jabber server and return new URL. upload a file with Jabber XEP_0363 from a remote URL or a local file path and return a URL of that file. """ if data.get(ATTR_URL_TEMPLATE): _LOGGER.debug("Got url template: %s", data[ATTR_URL_TEMPLATE]) templ = template_helper.Template(data[ATTR_URL_TEMPLATE], opp) get_url = template_helper.render_complex(templ, None) url = await self.upload_file_from_url(get_url, timeout=timeout) elif data.get(ATTR_URL): url = await self.upload_file_from_url(data[ATTR_URL], timeout=timeout) elif data.get(ATTR_PATH_TEMPLATE): _LOGGER.debug("Got path template: %s", data[ATTR_PATH_TEMPLATE]) templ = template_helper.Template(data[ATTR_PATH_TEMPLATE], opp) get_path = template_helper.render_complex(templ, None) url = await self.upload_file_from_path(get_path, timeout=timeout) elif data.get(ATTR_PATH): url = await self.upload_file_from_path(data[ATTR_PATH], timeout=timeout) else: url = None if url is None: _LOGGER.error("No path or URL found for file") raise FileUploadError("Could not upload file") return url
async def test_template_entity_requires_opp_set(): """Test template entity requires opp.to be set before accepting templates.""" entity = template_entity.TemplateEntity() with pytest.raises(AssertionError): entity.add_template_attribute("_hello", template.Template("Hello")) entity.opp = object() entity.add_template_attribute("_hello", template.Template("Hello", None)) tpl_with_opp = template.Template("Hello", entity.opp) entity.add_template_attribute("_hello", tpl_with_opp) # Because opp is set in `add_template_attribute`, both templates match `tpl_with_opp. assert len(entity._template_attrs.get(tpl_with_opp, [])) == 2
def test_string(opp): """Test string validation.""" schema = vol.Schema(cv.string) with pytest.raises(vol.Invalid): schema(None) with pytest.raises(vol.Invalid): schema([]) with pytest.raises(vol.Invalid): schema({}) for value in (True, 1, "hello"): schema(value) # Test template support for text, native in ( ("[1, 2]", [1, 2]), ("{1, 2}", {1, 2}), ("(1, 2)", (1, 2)), ('{"hello": True}', { "hello": True }), ): tpl = template.Template(text, opp) result = tpl.async_render() assert isinstance(result, template.ResultWrapper) assert result == native assert schema(result) == text
def update(self): """Get the latest data with a shell command.""" command = self.command if " " not in command: prog = command args = None args_compiled = None else: prog, args = command.split(" ", 1) args_compiled = template.Template(args, self.opp) if args_compiled: try: args_to_render = {"arguments": args} rendered_args = args_compiled.render(args_to_render) except TemplateError as ex: _LOGGER.exception("Error rendering command template: %s", ex) return else: rendered_args = None if rendered_args == args: # No template used. default behavior pass else: # Template used. Construct the string used in the shell command = f"{prog} {rendered_args}" _LOGGER.debug("Running command: %s", command) self.value = check_output_or_log(command, self.timeout)
def valid_subscribe_topic_template(value: Any) -> template.Template: """Validate either a jinja2 template or a valid MQTT subscription topic.""" tpl = template.Template(value) if tpl.is_static: valid_subscribe_topic(value) return tpl
def send_message(self, message="", **kwargs): """Send a notification to the device.""" data = {**self.data, **kwargs.get(ATTR_DATA, {})} if data.get(ATTR_VALUE) is not None: templ = template_helper.Template(self.data[ATTR_VALUE], self.opp) data[ATTR_VALUE] = template_helper.render_complex(templ, None) self.opp.services.call(DOMAIN, SERVICE_SET_DEVICE_VALUE, data)
async def post(self, request): """Render a template.""" if not request["opp_user"].is_admin: raise Unauthorized() try: data = await request.json() tpl = template.Template(data["template"], request.app["opp"]) return tpl.async_render(data.get("variables")) except (ValueError, TemplateError) as ex: return self.json_message(f"Error rendering template: {ex}", HTTP_BAD_REQUEST)
async def webhook_render_template(opp, config_entry, data): """Handle a render template webhook.""" resp = {} for key, item in data.items(): try: tpl = template.Template(item[ATTR_TEMPLATE], opp) resp[key] = tpl.async_render(item.get(ATTR_TEMPLATE_VARIABLES)) except template.TemplateError as ex: resp[key] = {"error": str(ex)} return webhook_response(resp, registration=config_entry.data)
def _async_templatize_blocks(opp: OpenPeerPower, value: Any) -> Any: """Recursive template creator helper function.""" if isinstance(value, list): return [_async_templatize_blocks(opp, item) for item in value] if isinstance(value, dict): return { key: _async_templatize_blocks(opp, item) for key, item in value.items() } tmpl = template.Template(value, opp=opp) # type: ignore # no-untyped-call return tmpl.async_render(parse_result=False)
def rewrite_legacy_to_modern_conf(cfg: dict[str, dict]) -> list[dict]: """Rewrite a legacy sensor definitions to modern ones.""" sensors = [] for object_id, entity_cfg in cfg.items(): entity_cfg = {**entity_cfg, CONF_OBJECT_ID: object_id} for from_key, to_key in LEGACY_FIELDS.items(): if from_key not in entity_cfg or to_key in entity_cfg: continue val = entity_cfg.pop(from_key) if isinstance(val, str): val = template.Template(val) entity_cfg[to_key] = val if CONF_NAME not in entity_cfg: entity_cfg[CONF_NAME] = template.Template(object_id) sensors.append(entity_cfg) return sensors
def template(value: Any | None) -> template_helper.Template: """Validate a jinja2 template.""" if value is None: raise vol.Invalid("template value is None") if isinstance(value, (list, dict, template_helper.Template)): raise vol.Invalid("template value should be a string") template_value = template_helper.Template(str(value)) # type: ignore try: template_value.ensure_valid() return template_value except TemplateError as ex: raise vol.Invalid(f"invalid template ({ex})") from ex
def dynamic_template(value: Any | None) -> template_helper.Template: """Validate a dynamic (non static) jinja2 template.""" if value is None: raise vol.Invalid("template value is None") if isinstance(value, (list, dict, template_helper.Template)): raise vol.Invalid("template value should be a string") if not template_helper.is_template_string(str(value)): raise vol.Invalid("template value does not contain a dynamic template") template_value = template_helper.Template(str(value)) # type: ignore try: template_value.ensure_valid() return template_value except TemplateError as ex: raise vol.Invalid(f"invalid template ({ex})") from ex
def async_condition_from_config( config: ConfigType, config_validation: bool) -> condition.ConditionCheckerType: """Create a function to test a device condition.""" if config_validation: config = CONDITION_SCHEMA(config) if config[CONF_TYPE] in STATE_CONDITION_TYPES: if config[CONF_TYPE] == "is_open": state = STATE_OPEN elif config[CONF_TYPE] == "is_closed": state = STATE_CLOSED elif config[CONF_TYPE] == "is_opening": state = STATE_OPENING elif config[CONF_TYPE] == "is_closing": state = STATE_CLOSING def test_is_state(opp: OpenPeerPower, variables: TemplateVarsType) -> bool: """Test if an entity is a certain state.""" return condition.state(opp, config[ATTR_ENTITY_ID], state) return test_is_state if config[CONF_TYPE] == "is_position": position = "current_position" if config[CONF_TYPE] == "is_tilt_position": position = "current_tilt_position" min_pos = config.get(CONF_ABOVE) max_pos = config.get(CONF_BELOW) value_template = template.Template( # type: ignore f"{{{{ state.attributes.{position} }}}}") @callback def template_if(opp: OpenPeerPower, variables: TemplateVarsType = None) -> bool: """Validate template based if-condition.""" value_template.opp = opp return condition.async_numeric_state(opp, config[ATTR_ENTITY_ID], max_pos, min_pos, value_template) return template_if
async def async_publish_service(call: ServiceCall): """Handle MQTT publish service calls.""" msg_topic: str = call.data[ATTR_TOPIC] payload = call.data.get(ATTR_PAYLOAD) payload_template = call.data.get(ATTR_PAYLOAD_TEMPLATE) qos: int = call.data[ATTR_QOS] retain: bool = call.data[ATTR_RETAIN] if payload_template is not None: try: payload = template.Template( payload_template, opp).async_render(parse_result=False) except template.jinja2.TemplateError as exc: _LOGGER.error( "Unable to publish to %s: rendering payload template of " "%s failed because %s", msg_topic, payload_template, exc, ) return await opp.data[DATA_MQTT].async_publish(msg_topic, payload, qos, retain)
async def async_service_handler(service: ServiceCall) -> None: """Execute a shell command service.""" cmd = conf[service.service] if cmd in cache: prog, args, args_compiled = cache[cmd] elif " " not in cmd: prog = cmd args = None args_compiled = None cache[cmd] = prog, args, args_compiled else: prog, args = cmd.split(" ", 1) args_compiled = template.Template(args, opp) cache[cmd] = prog, args, args_compiled if args_compiled: try: rendered_args = args_compiled.async_render( variables=service.data, parse_result=False ) except TemplateError as ex: _LOGGER.exception("Error rendering command template: %s", ex) return else: rendered_args = None if rendered_args == args: # No template used. default behavior # pylint: disable=no-member create_process = asyncio.subprocess.create_subprocess_shell( cmd, stdin=None, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) else: # Template used. Break into list and use create_subprocess_exec # (which uses shell=False) for security shlexed_cmd = [prog] + shlex.split(rendered_args) # pylint: disable=no-member create_process = asyncio.subprocess.create_subprocess_exec( *shlexed_cmd, stdin=None, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) process = await create_process try: stdout_data, stderr_data = await asyncio.wait_for( process.communicate(), COMMAND_TIMEOUT ) except asyncio.TimeoutError: _LOGGER.exception( "Timed out running command: `%s`, after: %ss", cmd, COMMAND_TIMEOUT ) if process: with suppress(TypeError): process.kill() del process return if stdout_data: _LOGGER.debug( "Stdout of command: `%s`, return code: %s:\n%s", cmd, process.returncode, stdout_data, ) if stderr_data: _LOGGER.debug( "Stderr of command: `%s`, return code: %s:\n%s", cmd, process.returncode, stderr_data, ) if process.returncode != 0: _LOGGER.exception( "Error running command: `%s`, return code: %s", cmd, process.returncode )
async def handle_render_template(opp: OpenPeerPower, connection: ActiveConnection, msg: dict[str, Any]) -> None: """Handle render_template command.""" template_str = msg["template"] template_obj = template.Template(template_str, opp) # type: ignore[no-untyped-call] variables = msg.get("variables") timeout = msg.get("timeout") info = None if timeout: try: timed_out = await template_obj.async_render_will_timeout( timeout, strict=msg["strict"]) except TemplateError as ex: connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(ex)) return if timed_out: connection.send_error( msg["id"], const.ERR_TEMPLATE_ERROR, f"Exceeded maximum execution time of {timeout}s", ) return @callback def _template_listener(event: Event, updates: list[TrackTemplateResult]) -> None: nonlocal info track_template_result = updates.pop() result = track_template_result.result if isinstance(result, TemplateError): connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(result)) return connection.send_message( messages.event_message( msg["id"], { "result": result, "listeners": info.listeners } # type: ignore[attr-defined] )) try: info = async_track_template_result( opp, [TrackTemplate(template_obj, variables)], _template_listener, raise_on_template_error=True, strict=msg["strict"], ) except TemplateError as ex: connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(ex)) return connection.subscriptions[msg["id"]] = info.async_remove connection.send_result(msg["id"]) opp.loop.call_soon_threadsafe(info.async_refresh)