Exemple #1
0
        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
Exemple #2
0
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
Exemple #4
0
    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)
Exemple #5
0
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
Exemple #6
0
    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)
Exemple #7
0
 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)
Exemple #8
0
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)
Exemple #9
0
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)
Exemple #10
0
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
Exemple #14
0
    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)
Exemple #15
0
    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
            )
Exemple #16
0
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)