示例#1
0
    def _process_data(self) -> None:
        """Process new data."""
        try:
            rendered = dict(self._static_rendered)

            for key in self._to_render_simple:
                rendered[key] = self._config[key].async_render(
                    self.coordinator.data["run_variables"],
                    parse_result=key in self._parse_result,
                )

            for key in self._to_render_complex:
                rendered[key] = template.render_complex(
                    self._config[key],
                    self.coordinator.data["run_variables"],
                )

            if CONF_ATTRIBUTES in self._config:
                rendered[CONF_ATTRIBUTES] = template.render_complex(
                    self._config[CONF_ATTRIBUTES],
                    self.coordinator.data["run_variables"],
                )

            self._rendered = rendered
        except TemplateError as err:
            logging.getLogger(
                f"{__package__}.{self.entity_id.split('.')[0]}").error(
                    "Error rendering %s template for %s: %s", key,
                    self.entity_id, err)
            self._rendered = self._static_rendered

        self.async_set_context(self.coordinator.data["context"])
示例#2
0
    async def async_update(self, log_errors=True):
        """Get the latest data from REST service with provided method."""
        if not self._async_client:
            self._async_client = httpx.AsyncClient(verify=self._verify_ssl,
                                                   proxies=self._proxies)

        rendered_headers = template.render_complex(self._headers,
                                                   parse_result=False)
        rendered_params = template.render_complex(self._params)

        _LOGGER.debug("Updating from %s", self._resource)
        try:
            response = await self._async_client.request(
                self._method,
                self._resource,
                headers=rendered_headers,
                params=rendered_params,
                auth=self._auth,
                data=self._request_data,
                timeout=self._timeout,
                follow_redirects=True,
            )
            self.data = response.text
            self.headers = response.headers
        except httpx.RequestError as ex:
            if log_errors:
                _LOGGER.warning("Error fetching data: %s failed with %s",
                                self._resource, ex)
            self.last_exception = ex
            self.data = None
            self.headers = None
示例#3
0
文件: notify.py 项目: jcgoette/core
        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], hass)
                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], hass)
                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
示例#4
0
    async def get_device_state(self, hass):
        """Get the latest data from REST API and update the state."""
        websession = async_get_clientsession(hass, self._verify_ssl)

        state_resource = self._resource

        if self._resource_template is not None:
            state_resource = self._resource_template.async_render(parse_result=False)

        if self._state_resource is not None:
            state_resource = self._state_resource

        if self._state_resource_template is not None:
            state_resource = self._state_resource_template.async_render(parse_result=False)

        rendered_headers = template.render_complex(self._headers, parse_result=False)
        rendered_params = template.render_complex(self._params)

        async with async_timeout.timeout(self._timeout):
            req = await websession.get(
                state_resource,
                auth=self._auth,
                headers=rendered_headers,
                params=rendered_params,
            )
            text = await req.text()

        _LOGGER.debug("[%s] Raw response is (%s): %s", self._name, req.status, text)

        if self._is_on_template is not None:
            text = self._is_on_template.async_render_with_possible_json_value(
                text, "None"
            )

            _LOGGER.debug("[%s] Value after template rendering: %s", self._name, text)
            text = text.lower()

            if text == "true":
                self._state = True
            elif text == "false":
                self._state = False
            else:
                self._state = None
        else:
            if text == self._body_on.template:
                self._state = True
            elif text == self._body_off.template:
                self._state = False
            else:
                self._state = None

        return req
 def test_passing_vars_as_dict_element(self):
     """Test passing variables as list."""
     self.assertEqual(
         'bar',
         template.render_complex(template.Template('{{ hello.foo }}',
                                 self.hass),
                                 {'hello': {'foo': 'bar'}}))
示例#6
0
 def test_passing_vars_as_list_element(self):
     """Test passing variables as list."""
     self.assertEqual(
         'bar',
         template.render_complex(
             template.Template('{{ hello[1] }}', self.hass),
             {'hello': ['foo', 'bar']}))
示例#7
0
    def _handle_coordinator_update(self) -> None:
        """Handle updated data from the coordinator."""
        try:
            rendered = dict(self._static_rendered)

            for key in self._to_render:
                rendered[key] = self._config[key].async_render(
                    self.coordinator.data["run_variables"], parse_result=False
                )

            if CONF_ATTRIBUTES in self._config:
                rendered[CONF_ATTRIBUTES] = template.render_complex(
                    self._config[CONF_ATTRIBUTES],
                    self.coordinator.data["run_variables"],
                )

            self._rendered = rendered
        except template.TemplateError as err:
            logging.getLogger(f"{__package__}.{self.entity_id.split('.')[0]}").error(
                "Error rendering %s template for %s: %s", key, self.entity_id, err
            )
            self._rendered = self._static_rendered

        self.async_set_context(self.coordinator.data["context"])
        self.async_write_ha_state()
示例#8
0
    async def _async_delay(self, action, variables, context):
        """Handle delay."""
        # Call ourselves in the future to continue work
        unsub = None

        @callback
        def async_script_delay(now):
            """Handle delay."""
            with suppress(ValueError):
                self._async_listener.remove(unsub)

            self.hass.async_create_task(self.async_run(variables, context))

        delay = action[CONF_DELAY]

        try:
            if isinstance(delay, template.Template):
                delay = vol.All(cv.time_period, cv.positive_timedelta)(
                    delay.async_render(variables))
            elif isinstance(delay, dict):
                delay_data = {}
                delay_data.update(template.render_complex(delay, variables))
                delay = cv.time_period(delay_data)
        except (exceptions.TemplateError, vol.Invalid) as ex:
            _LOGGER.error("Error rendering '%s' delay template: %s", self.name,
                          ex)
            raise _StopScript

        self.last_action = action.get(CONF_ALIAS, "delay {}".format(delay))
        self._log("Executing step %s" % self.last_action)

        unsub = async_track_point_in_utc_time(self.hass, async_script_delay,
                                              date_util.utcnow() + delay)
        self._async_listener.append(unsub)
        raise _SuspendScript
示例#9
0
    async def _async_delay_step(self):
        """Handle delay."""
        try:
            delay = vol.All(cv.time_period,
                            cv.positive_timedelta)(template.render_complex(
                                self._action[CONF_DELAY], self._variables))
        except (exceptions.TemplateError, vol.Invalid) as ex:
            self._log(
                "Error rendering %s delay template: %s",
                self._script.name,
                ex,
                level=logging.ERROR,
            )
            raise _StopScript

        self._script.last_action = self._action.get(CONF_ALIAS,
                                                    f"delay {delay}")
        self._log("Executing step %s", self._script.last_action)

        delay = delay.total_seconds()
        self._changed()
        try:
            async with timeout(delay):
                await self._stop.wait()
        except asyncio.TimeoutError:
            pass
示例#10
0
    def async_on_service_call(service: HomeassistantServiceCall) -> None:
        """Call service when user automation in ESPHome config is triggered."""
        domain, service_name = service.service.split(".", 1)
        service_data = service.data

        if service.data_template:
            try:
                data_template = {
                    key: Template(value)
                    for key, value in service.data_template.items()
                }
                template.attach(hass, data_template)
                service_data.update(
                    template.render_complex(data_template, service.variables))
            except TemplateError as ex:
                _LOGGER.error("Error rendering data template for %s: %s", host,
                              ex)
                return

        if service.is_event:
            # ESPHome uses servicecall packet for both events and service calls
            # Ensure the user can only send events of form 'esphome.xyz'
            if domain != "esphome":
                _LOGGER.error(
                    "Can only generate events under esphome domain! (%s)",
                    host)
                return
            hass.bus.async_fire(service.service, service_data)
        else:
            hass.async_create_task(
                hass.services.async_call(domain,
                                         service_name,
                                         service_data,
                                         blocking=True))
示例#11
0
文件: switch.py 项目: rikroe/core
    async def set_device_state(self, body):
        """Send a state update to the device."""
        websession = async_get_clientsession(self.hass, self._verify_ssl)

        rendered_headers = template.render_complex(self._headers,
                                                   parse_result=False)
        rendered_params = template.render_complex(self._params)

        async with async_timeout.timeout(self._timeout):
            req = await getattr(websession, self._method)(
                self._resource,
                auth=self._auth,
                data=bytes(body, "utf-8"),
                headers=rendered_headers,
                params=rendered_params,
            )
            return req
示例#12
0
def test_passing_vars_as_list_element(hass):
    """Test passing variables as list."""
    assert (
        template.render_complex(
            template.Template("{{ hello[1] }}", hass), {"hello": ["foo", "bar"]}
        )
        == "bar"
    )
示例#13
0
def test_passing_vars_as_dict(hass):
    """Test passing variables as list."""
    assert (
        template.render_complex(
            template.Template("{{ hello }}", hass), {"hello": {"foo": "bar"}}
        )
        == "{'foo': 'bar'}"
    )
示例#14
0
    def state_automation_listener(entity, from_s, to_s):
        """Listen for state changes and calls action."""
        @callback
        def call_action():
            """Call action with right context."""
            hass.async_run_job(action({
                'trigger': {
                    'platform': 'state',
                    'entity_id': entity,
                    'from_state': from_s,
                    'to_state': to_s,
                    'for': time_delta if not time_delta else period[entity]
                }
            }, context=to_s.context))

        # Ignore changes to state attributes if from/to is in use
        if (not match_all and from_s is not None and to_s is not None and
                from_s.state == to_s.state):
            return

        if not time_delta:
            call_action()
            return

        variables = {
            'trigger': {
                'platform': 'state',
                'entity_id': entity,
                'from_state': from_s,
                'to_state': to_s,
            }
        }

        try:
            if isinstance(time_delta, template.Template):
                period[entity] = vol.All(
                    cv.time_period,
                    cv.positive_timedelta)(
                        time_delta.async_render(variables))
            elif isinstance(time_delta, dict):
                time_delta_data = {}
                time_delta_data.update(
                    template.render_complex(time_delta, variables))
                period[entity] = vol.All(
                    cv.time_period,
                    cv.positive_timedelta)(
                        time_delta_data)
            else:
                period[entity] = time_delta
        except (exceptions.TemplateError, vol.Invalid) as ex:
            _LOGGER.error("Error rendering '%s' for template: %s",
                          automation_info['name'], ex)
            return

        unsub_track_same[entity] = async_track_same_state(
            hass, period[entity], call_action,
            lambda _, _2, to_state: to_state.state == to_s.state,
            entity_ids=entity)
示例#15
0
    def template_listener(entity_id, from_s, to_s):
        """Listen for state changes and calls action."""
        nonlocal unsub_track_same

        @callback
        def call_action():
            """Call action with right context."""
            hass.async_run_job(
                action(
                    {
                        "trigger": {
                            "platform": "template",
                            "entity_id": entity_id,
                            "from_state": from_s,
                            "to_state": to_s,
                            "for": time_delta if not time_delta else period,
                        }
                    },
                    context=(to_s.context if to_s else None),
                ))

        if not time_delta:
            call_action()
            return

        variables = {
            "trigger": {
                "platform": platform_type,
                "entity_id": entity_id,
                "from_state": from_s,
                "to_state": to_s,
            }
        }

        try:
            if isinstance(time_delta, template.Template):
                period = vol.All(cv.time_period, cv.positive_timedelta)(
                    time_delta.async_render(variables))
            elif isinstance(time_delta, dict):
                time_delta_data = {}
                time_delta_data.update(
                    template.render_complex(time_delta, variables))
                period = vol.All(cv.time_period,
                                 cv.positive_timedelta)(time_delta_data)
            else:
                period = time_delta
        except (exceptions.TemplateError, vol.Invalid) as ex:
            _LOGGER.error("Error rendering '%s' for template: %s",
                          automation_info["name"], ex)
            return

        unsub_track_same = async_track_same_state(
            hass,
            period,
            call_action,
            lambda _, _2, _3: condition.async_template(hass, value_template),
            value_template.extract_entities(),
        )
示例#16
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.hass)
            data[ATTR_VALUE] = template_helper.render_complex(templ, None)

        self.hass.services.call(DOMAIN, SERVICE_SET_DEVICE_VALUE, data)
示例#17
0
 def test_passing_vars_as_dict(self):
     """Test passing variables as list."""
     self.assertEqual(
         "{'foo': 'bar'}",
         template.render_complex(
             template.Template('{{ hello }}', self.hass),
             {'hello': {
                 'foo': 'bar'
             }}))
示例#18
0
    def state_automation_listener(event):
        """Listen for state changes and calls action."""
        entity_id = event.data.get("entity_id")
        from_s = event.data.get("old_state")
        to_s = event.data.get("new_state")

        @callback
        def call_action():
            """Call action with right context."""
            hass.async_run_hass_job(
                job,
                {
                    "trigger": {
                        "platform": platform_type,
                        "entity_id": entity_id,
                        "below": below,
                        "above": above,
                        "from_state": from_s,
                        "to_state": to_s,
                        "for":
                        time_delta if not time_delta else period[entity_id],
                        "description": f"numeric state of {entity_id}",
                    }
                },
                to_s.context,
            )

        matching = check_numeric_state(entity_id, from_s, to_s)

        if not matching:
            entities_triggered.discard(entity_id)
        elif entity_id not in entities_triggered:
            entities_triggered.add(entity_id)

            if time_delta:
                try:
                    period[entity_id] = cv.positive_time_period(
                        template.render_complex(time_delta,
                                                variables(entity_id)))
                except (exceptions.TemplateError, vol.Invalid) as ex:
                    _LOGGER.error(
                        "Error rendering '%s' for template: %s",
                        automation_info["name"],
                        ex,
                    )
                    entities_triggered.discard(entity_id)
                    return

                unsub_track_same[entity_id] = async_track_same_state(
                    hass,
                    period[entity_id],
                    call_action,
                    entity_ids=entity_id,
                    async_check_same_func=check_numeric_state,
                )
            else:
                call_action()
示例#19
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.hass)
            data[ATTR_VALUE] = template_helper.render_complex(templ, None)

        self.hass.services.call(DOMAIN, SERVICE_SET_DEVICE_VALUE, data)
示例#20
0
    def template_listener(event, updates):
        """Listen for state changes and calls action."""
        nonlocal delay_cancel
        result = updates.pop().result

        if delay_cancel:
            # pylint: disable=not-callable
            delay_cancel()
            delay_cancel = None

        if not result_as_boolean(result):
            return

        entity_id = event.data.get("entity_id")
        from_s = event.data.get("old_state")
        to_s = event.data.get("new_state")

        @callback
        def call_action(*_):
            """Call action with right context."""
            hass.async_run_hass_job(
                job,
                {
                    "trigger": {
                        "platform": "template",
                        "entity_id": entity_id,
                        "from_state": from_s,
                        "to_state": to_s,
                        "for": time_delta if not time_delta else period,
                        "description": f"{entity_id} via template",
                    }
                },
                (to_s.context if to_s else None),
            )

        if not time_delta:
            call_action()
            return

        variables = {
            "trigger": {
                "platform": platform_type,
                "entity_id": entity_id,
                "from_state": from_s,
                "to_state": to_s,
            }
        }

        try:
            period = cv.positive_time_period(
                template.render_complex(time_delta, variables))
        except (exceptions.TemplateError, vol.Invalid) as ex:
            _LOGGER.error("Error rendering '%s' for template: %s",
                          automation_info["name"], ex)
            return

        delay_cancel = async_call_later(hass, period.seconds, call_action)
示例#21
0
async def async_call_from_config(hass,
                                 config,
                                 blocking=False,
                                 variables=None,
                                 validate_config=True,
                                 context=None):
    """Call a service based on a config hash."""
    if validate_config:
        try:
            config = cv.SERVICE_SCHEMA(config)
        except vol.Invalid as ex:
            _LOGGER.error("Invalid config for calling service: %s", ex)
            return

    if CONF_SERVICE in config:
        domain_service = config[CONF_SERVICE]
    else:
        try:
            config[CONF_SERVICE_TEMPLATE].hass = hass
            domain_service = config[CONF_SERVICE_TEMPLATE].async_render(
                variables)
            domain_service = cv.service(domain_service)
        except TemplateError as ex:
            if blocking:
                raise
            _LOGGER.error('Error rendering service name template: %s', ex)
            return
        except vol.Invalid:
            if blocking:
                raise
            _LOGGER.error('Template rendered invalid service: %s',
                          domain_service)
            return

    domain, service_name = domain_service.split('.', 1)
    service_data = dict(config.get(CONF_SERVICE_DATA, {}))

    if CONF_SERVICE_DATA_TEMPLATE in config:
        try:
            template.attach(hass, config[CONF_SERVICE_DATA_TEMPLATE])
            service_data.update(
                template.render_complex(config[CONF_SERVICE_DATA_TEMPLATE],
                                        variables))
        except TemplateError as ex:
            _LOGGER.error('Error rendering data template: %s', ex)
            return

    if CONF_SERVICE_ENTITY_ID in config:
        service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]

    await hass.services.async_call(domain,
                                   service_name,
                                   service_data,
                                   blocking=blocking,
                                   context=context)
示例#22
0
def async_prepare_call_from_config(
    hass: HomeAssistantType,
    config: ConfigType,
    variables: TemplateVarsType = None,
    validate_config: bool = False,
) -> Tuple[str, str, Dict[str, Any]]:
    """Prepare to call a service based on a config hash."""
    if validate_config:
        try:
            config = cv.SERVICE_SCHEMA(config)
        except vol.Invalid as ex:
            raise HomeAssistantError(
                f"Invalid config for calling service: {ex}"
            ) from ex

    if CONF_SERVICE in config:
        domain_service = config[CONF_SERVICE]
    else:
        domain_service = config[CONF_SERVICE_TEMPLATE]

    if isinstance(domain_service, template.Template):
        try:
            domain_service.hass = hass
            domain_service = domain_service.async_render(variables)
            domain_service = cv.service(domain_service)
        except TemplateError as ex:
            raise HomeAssistantError(
                f"Error rendering service name template: {ex}"
            ) from ex
        except vol.Invalid as ex:
            raise HomeAssistantError(
                f"Template rendered invalid service: {domain_service}"
            ) from ex

    domain, service = domain_service.split(".", 1)

    service_data = {}

    if CONF_TARGET in config:
        service_data.update(config[CONF_TARGET])

    for conf in [CONF_SERVICE_DATA, CONF_SERVICE_DATA_TEMPLATE]:
        if conf not in config:
            continue
        try:
            template.attach(hass, config[conf])
            service_data.update(template.render_complex(config[conf], variables))
        except TemplateError as ex:
            raise HomeAssistantError(f"Error rendering data template: {ex}") from ex

    if CONF_SERVICE_ENTITY_ID in config:
        service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]

    return domain, service, service_data
示例#23
0
 def _get_pos_time_period_template(self, key):
     try:
         return cv.positive_time_period(
             template.render_complex(self._action[key], self._variables))
     except (exceptions.TemplateError, vol.Invalid) as ex:
         self._log(
             "Error rendering %s %s template: %s",
             self._script.name,
             key,
             ex,
             level=logging.ERROR,
         )
         raise _StopScript from ex
示例#24
0
    def async_on_service_call(service: HomeassistantServiceCall) -> None:
        """Call service when user automation in ESPHome config is triggered."""
        domain, service_name = service.service.split(".", 1)
        service_data = service.data

        if service.data_template:
            try:
                data_template = {
                    key: Template(value)  # type: ignore[no-untyped-call]
                    for key, value in service.data_template.items()
                }
                template.attach(hass, data_template)
                service_data.update(
                    template.render_complex(data_template, service.variables))
            except TemplateError as ex:
                _LOGGER.error("Error rendering data template for %s: %s", host,
                              ex)
                return

        if service.is_event:
            # ESPHome uses servicecall packet for both events and service calls
            # Ensure the user can only send events of form 'esphome.xyz'
            if domain != "esphome":
                _LOGGER.error(
                    "Can only generate events under esphome domain! (%s)",
                    host)
                return

            # Call native tag scan
            if service_name == "tag_scanned" and device_id is not None:
                # Importing tag via hass.components in case it is overridden
                # in a custom_components (custom_components.tag)
                tag = hass.components.tag
                tag_id = service_data["tag_id"]
                hass.async_create_task(tag.async_scan_tag(tag_id, device_id))
                return

            hass.bus.async_fire(
                service.service,
                {
                    ATTR_DEVICE_ID: device_id,
                    **service_data,
                },
            )
        else:
            hass.async_create_task(
                hass.services.async_call(domain,
                                         service_name,
                                         service_data,
                                         blocking=True))
示例#25
0
    def _async_fire_event(self, action, variables):
        """Fire an event."""
        self.last_action = action.get(CONF_ALIAS, action[CONF_EVENT])
        self._log("Executing step %s" % self.last_action)
        event_data = dict(action.get(CONF_EVENT_DATA, {}))
        if CONF_EVENT_DATA_TEMPLATE in action:
            try:
                event_data.update(template.render_complex(
                    action[CONF_EVENT_DATA_TEMPLATE], variables))
            except TemplateError as ex:
                _LOGGER.error('Error rendering event data template: %s', ex)

        self.hass.bus.async_fire(action[CONF_EVENT],
                                 event_data)
示例#26
0
    def _async_fire_event(self, action, variables):
        """Fire an event."""
        self.last_action = action.get(CONF_ALIAS, action[CONF_EVENT])
        self._log("Executing step %s" % self.last_action)
        event_data = dict(action.get(CONF_EVENT_DATA, {}))
        if CONF_EVENT_DATA_TEMPLATE in action:
            try:
                event_data.update(template.render_complex(
                    action[CONF_EVENT_DATA_TEMPLATE], variables))
            except TemplateError as ex:
                _LOGGER.error('Error rendering event data template: %s', ex)

        self.hass.bus.async_fire(action[CONF_EVENT],
                                 event_data)
示例#27
0
文件: notify.py 项目: jcgoette/core
    async def async_send_message(self, message: str, **kwargs: Any) -> None:
        """Send a message to Slack."""
        data = kwargs.get(ATTR_DATA) or {}

        try:
            DATA_SCHEMA(data)
        except vol.Invalid as err:
            _LOGGER.error("Invalid message data: %s", err)
            data = {}

        title = kwargs.get(ATTR_TITLE)
        targets = _async_sanitize_channel_names(
            kwargs.get(ATTR_TARGET, [self._config[CONF_DEFAULT_CHANNEL]]))

        # Message Type 1: A text-only message
        if ATTR_FILE not in data:
            if ATTR_BLOCKS_TEMPLATE in data:
                value = cv.template_complex(data[ATTR_BLOCKS_TEMPLATE])
                template.attach(self._hass, value)
                blocks = template.render_complex(value)
            elif ATTR_BLOCKS in data:
                blocks = data[ATTR_BLOCKS]
            else:
                blocks = None

            return await self._async_send_text_only_message(
                targets,
                message,
                title,
                username=data.get(ATTR_USERNAME,
                                  self._config.get(ATTR_USERNAME)),
                icon=data.get(ATTR_ICON, self._config.get(ATTR_ICON)),
                blocks=blocks,
            )

        # Message Type 2: A message that uploads a remote file
        if ATTR_URL in data[ATTR_FILE]:
            return await self._async_send_remote_file_message(
                data[ATTR_FILE][ATTR_URL],
                targets,
                message,
                title,
                username=data[ATTR_FILE].get(ATTR_USERNAME),
                password=data[ATTR_FILE].get(ATTR_PASSWORD),
            )

        # Message Type 3: A message that uploads a local file
        return await self._async_send_local_file_message(
            data[ATTR_FILE][ATTR_PATH], targets, message, title)
示例#28
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], hass)
                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], hass)
                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
示例#29
0
async def async_call_from_config(hass, config, blocking=False, variables=None,
                                 validate_config=True, context=None):
    """Call a service based on a config hash."""
    if validate_config:
        try:
            config = cv.SERVICE_SCHEMA(config)
        except vol.Invalid as ex:
            _LOGGER.error("Invalid config for calling service: %s", ex)
            return

    if CONF_SERVICE in config:
        domain_service = config[CONF_SERVICE]
    else:
        try:
            config[CONF_SERVICE_TEMPLATE].hass = hass
            domain_service = config[CONF_SERVICE_TEMPLATE].async_render(
                variables)
            domain_service = cv.service(domain_service)
        except TemplateError as ex:
            if blocking:
                raise
            _LOGGER.error('Error rendering service name template: %s', ex)
            return
        except vol.Invalid:
            if blocking:
                raise
            _LOGGER.error('Template rendered invalid service: %s',
                          domain_service)
            return

    domain, service_name = domain_service.split('.', 1)
    service_data = dict(config.get(CONF_SERVICE_DATA, {}))

    if CONF_SERVICE_DATA_TEMPLATE in config:
        try:
            template.attach(hass, config[CONF_SERVICE_DATA_TEMPLATE])
            service_data.update(template.render_complex(
                config[CONF_SERVICE_DATA_TEMPLATE], variables))
        except TemplateError as ex:
            _LOGGER.error('Error rendering data template: %s', ex)
            return

    if CONF_SERVICE_ENTITY_ID in config:
        service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]

    await hass.services.async_call(
        domain, service_name, service_data, blocking=blocking, context=context)
示例#30
0
    def async_on_service_call(service: 'ServiceCall') -> None:
        """Call service when user automation in ESPHome config is triggered."""
        domain, service_name = service.service.split('.', 1)
        service_data = service.data

        if service.data_template:
            try:
                data_template = {key: Template(value) for key, value in
                                 service.data_template.items()}
                template.attach(hass, data_template)
                service_data.update(template.render_complex(
                    data_template, service.variables))
            except TemplateError as ex:
                _LOGGER.error('Error rendering data template: %s', ex)
                return

        hass.async_create_task(hass.services.async_call(
            domain, service_name, service_data, blocking=True))
示例#31
0
    def async_on_service_call(service: 'ServiceCall') -> None:
        """Call service when user automation in ESPHome config is triggered."""
        domain, service_name = service.service.split('.', 1)
        service_data = service.data

        if service.data_template:
            try:
                data_template = {key: Template(value) for key, value in
                                 service.data_template.items()}
                template.attach(hass, data_template)
                service_data.update(template.render_complex(
                    data_template, service.variables))
            except TemplateError as ex:
                _LOGGER.error('Error rendering data template: %s', ex)
                return

        hass.async_create_task(hass.services.async_call(
            domain, service_name, service_data, blocking=True))
示例#32
0
    def _prep_delay_step(self):
        try:
            delay = vol.All(cv.time_period,
                            cv.positive_timedelta)(template.render_complex(
                                self._action[CONF_DELAY], self._variables))
        except (exceptions.TemplateError, vol.Invalid) as ex:
            self._raise(
                "Error rendering %s delay template: %s",
                self._script.name,
                ex,
                exception=_StopScript,
            )

        self._script.last_action = self._action.get(CONF_ALIAS,
                                                    f"delay {delay}")
        self._log("Executing step %s", self._script.last_action)

        return delay
示例#33
0
def async_prepare_call_from_config(hass,
                                   config,
                                   variables=None,
                                   validate_config=False):
    """Prepare to call a service based on a config hash."""
    if validate_config:
        try:
            config = cv.SERVICE_SCHEMA(config)
        except vol.Invalid as ex:
            raise HomeAssistantError(
                f"Invalid config for calling service: {ex}") from ex

    if CONF_SERVICE in config:
        domain_service = config[CONF_SERVICE]
    else:
        try:
            config[CONF_SERVICE_TEMPLATE].hass = hass
            domain_service = config[CONF_SERVICE_TEMPLATE].async_render(
                variables)
            domain_service = cv.service(domain_service)
        except TemplateError as ex:
            raise HomeAssistantError(
                f"Error rendering service name template: {ex}") from ex
        except vol.Invalid as ex:
            raise HomeAssistantError(
                f"Template rendered invalid service: {domain_service}") from ex

    domain, service = domain_service.split(".", 1)
    service_data = dict(config.get(CONF_SERVICE_DATA, {}))

    if CONF_SERVICE_DATA_TEMPLATE in config:
        try:
            template.attach(hass, config[CONF_SERVICE_DATA_TEMPLATE])
            service_data.update(
                template.render_complex(config[CONF_SERVICE_DATA_TEMPLATE],
                                        variables))
        except TemplateError as ex:
            raise HomeAssistantError(
                f"Error rendering data template: {ex}") from ex

    if CONF_SERVICE_ENTITY_ID in config:
        service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]

    return domain, service, service_data
示例#34
0
    async def _async_event_step(self):
        """Fire an event."""
        self._step_log(self._action.get(CONF_ALIAS, self._action[CONF_EVENT]))
        event_data = {}
        for conf in [CONF_EVENT_DATA, CONF_EVENT_DATA_TEMPLATE]:
            if conf not in self._action:
                continue

            try:
                event_data.update(
                    template.render_complex(self._action[conf],
                                            self._variables))
            except exceptions.TemplateError as ex:
                self._log("Error rendering event data template: %s",
                          ex,
                          level=logging.ERROR)

        self._hass.bus.async_fire(self._action[CONF_EVENT],
                                  event_data,
                                  context=self._context)
示例#35
0
    async def _async_event_step(self):
        """Fire an event."""
        self._script.last_action = self._action.get(CONF_ALIAS,
                                                    self._action[CONF_EVENT])
        self._log("Executing step %s", self._script.last_action)
        event_data = dict(self._action.get(CONF_EVENT_DATA, {}))
        if CONF_EVENT_DATA_TEMPLATE in self._action:
            try:
                event_data.update(
                    template.render_complex(
                        self._action[CONF_EVENT_DATA_TEMPLATE],
                        self._variables))
            except exceptions.TemplateError as ex:
                self._log("Error rendering event data template: %s",
                          ex,
                          level=logging.ERROR)

        self._hass.bus.async_fire(self._action[CONF_EVENT],
                                  event_data,
                                  context=self._context)
async def async_call_action_from_config(hass: HomeAssistant, config: dict,
                                        variables: dict,
                                        context: Optional[Context]) -> None:
    """Execute a device action."""
    webhook_id = webhook_id_from_device_id(hass, config[CONF_DEVICE_ID])

    if webhook_id is None:
        raise InvalidDeviceAutomationConfig(
            "Unable to resolve webhook ID from the device ID")

    service_name = get_notify_service(hass, webhook_id)

    if service_name is None:
        raise InvalidDeviceAutomationConfig(
            "Unable to find notify service for webhook ID")

    service_data = {notify.ATTR_TARGET: webhook_id}

    # Render it here because we have access to variables here.
    for key in (notify.ATTR_MESSAGE, notify.ATTR_TITLE, notify.ATTR_DATA):
        if key not in config:
            continue

        value_template = config[key]
        template.attach(hass, value_template)

        try:
            service_data[key] = template.render_complex(
                value_template, variables)
        except template.TemplateError as err:
            raise InvalidDeviceAutomationConfig(
                f"Error rendering {key}: {err}") from err

    await hass.services.async_call(notify.DOMAIN,
                                   service_name,
                                   service_data,
                                   blocking=True,
                                   context=context)
示例#37
0
    async def async_run(self, variables: Optional[Sequence] = None,
                        context: Optional[Context] = None) -> None:
        """Run script.

        This method is a coroutine.
        """
        self.last_triggered = date_util.utcnow()
        if self._cur == -1:
            self._log('Running script')
            self._cur = 0

        # Unregister callback if we were in a delay or wait but turn on is
        # called again. In that case we just continue execution.
        self._async_remove_listener()

        for cur, action in islice(enumerate(self.sequence), self._cur, None):

            if CONF_DELAY in action:
                # Call ourselves in the future to continue work
                unsub = None

                @callback
                def async_script_delay(now):
                    """Handle delay."""
                    # pylint: disable=cell-var-from-loop
                    with suppress(ValueError):
                        self._async_listener.remove(unsub)

                    self.hass.async_create_task(
                        self.async_run(variables, context))

                delay = action[CONF_DELAY]

                try:
                    if isinstance(delay, template.Template):
                        delay = vol.All(
                            cv.time_period,
                            cv.positive_timedelta)(
                                delay.async_render(variables))
                    elif isinstance(delay, dict):
                        delay_data = {}
                        delay_data.update(
                            template.render_complex(delay, variables))
                        delay = cv.time_period(delay_data)
                except (TemplateError, vol.Invalid) as ex:
                    _LOGGER.error("Error rendering '%s' delay template: %s",
                                  self.name, ex)
                    break

                unsub = async_track_point_in_utc_time(
                    self.hass, async_script_delay,
                    date_util.utcnow() + delay
                )
                self._async_listener.append(unsub)

                self._cur = cur + 1
                if self._change_listener:
                    self.hass.async_add_job(self._change_listener)
                return

            if CONF_WAIT_TEMPLATE in action:
                # Call ourselves in the future to continue work
                wait_template = action[CONF_WAIT_TEMPLATE]
                wait_template.hass = self.hass

                # check if condition already okay
                if condition.async_template(
                        self.hass, wait_template, variables):
                    continue

                @callback
                def async_script_wait(entity_id, from_s, to_s):
                    """Handle script after template condition is true."""
                    self._async_remove_listener()
                    self.hass.async_create_task(
                        self.async_run(variables, context))

                self._async_listener.append(async_track_template(
                    self.hass, wait_template, async_script_wait, variables))

                self._cur = cur + 1
                if self._change_listener:
                    self.hass.async_add_job(self._change_listener)

                if CONF_TIMEOUT in action:
                    self._async_set_timeout(
                        action, variables, context,
                        action.get(CONF_CONTINUE, True))

                return

            if CONF_CONDITION in action:
                if not self._async_check_condition(action, variables):
                    break

            elif CONF_EVENT in action:
                self._async_fire_event(action, variables, context)

            else:
                await self._async_call_service(action, variables, context)

        self._cur = -1
        self.last_action = None
        if self._change_listener:
            self.hass.async_add_job(self._change_listener)
示例#38
0
 def test_passing_vars_as_list_element(self):
     """Test passing variables as list."""
     assert 'bar' == \
         template.render_complex(template.Template('{{ hello[1] }}',
                                 self.hass),
                                 {'hello': ['foo', 'bar']})
示例#39
0
 def test_passing_vars_as_dict(self):
     """Test passing variables as list."""
     assert "{'foo': 'bar'}" == \
         template.render_complex(template.Template('{{ hello }}',
                                 self.hass), {'hello': {'foo': 'bar'}})
 def test_passing_vars_as_list(self):
     """Test passing variables as list."""
     self.assertEqual(
         "['foo', 'bar']",
         template.render_complex(template.Template('{{ hello }}',
                                 self.hass), {'hello': ['foo', 'bar']}))