コード例 #1
0
    async def async_added_to_hass(self):
        """Register callbacks."""
        self.add_template_attribute("_state", self._template, None, self._update_state)
        if (
            self._friendly_name_template is not None
            and not self._friendly_name_template.is_static
        ):
            self.add_template_attribute("_name", self._friendly_name_template)

        if self._delay_on_raw is not None:
            try:
                self._delay_on = cv.positive_time_period(self._delay_on_raw)
            except vol.Invalid:
                self.add_template_attribute(
                    "_delay_on", self._delay_on_raw, cv.positive_time_period
                )

        if self._delay_off_raw is not None:
            try:
                self._delay_off = cv.positive_time_period(self._delay_off_raw)
            except vol.Invalid:
                self.add_template_attribute(
                    "_delay_off", self._delay_off_raw, cv.positive_time_period
                )

        await super().async_added_to_hass()
コード例 #2
0
    def _set_state(self, state, _=None):
        """Set up auto off."""
        self._state = state
        self.async_set_context(self.coordinator.data["context"])
        self.async_write_ha_state()

        if not state:
            return

        auto_off_time = self._rendered.get(CONF_AUTO_OFF) or self._config.get(
            CONF_AUTO_OFF)

        if auto_off_time is None:
            return

        if not isinstance(auto_off_time, timedelta):
            try:
                auto_off_time = cv.positive_time_period(auto_off_time)
            except vol.Invalid as err:
                logging.getLogger(__name__).warning(
                    "Error rendering %s template: %s", CONF_AUTO_OFF, err)
                return

        @callback
        def _auto_off(_):
            """Set state of template binary sensor."""
            self._state = False
            self.async_write_ha_state()

        self._auto_off_cancel = async_call_later(self.hass,
                                                 auto_off_time.total_seconds(),
                                                 _auto_off)
コード例 #3
0
    def _set_state(self, state, _=None):
        """Set up auto off."""
        self._state = state
        self.async_set_context(self.coordinator.data["context"])
        self.async_write_ha_state()

        if not state:
            return

        auto_off_delay = self._rendered.get(CONF_AUTO_OFF) or self._config.get(
            CONF_AUTO_OFF)

        if auto_off_delay is None:
            return

        if not isinstance(auto_off_delay, timedelta):
            try:
                auto_off_delay = cv.positive_time_period(auto_off_delay)
            except vol.Invalid as err:
                logging.getLogger(__name__).warning(
                    "Error rendering %s template: %s", CONF_AUTO_OFF, err)
                return

        auto_off_time = dt_util.utcnow() + auto_off_delay
        self._set_auto_off(auto_off_time)
コード例 #4
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()
コード例 #5
0
ファイル: trigger.py プロジェクト: qq840873731/core-1
    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)
コード例 #6
0
ファイル: script.py プロジェクト: xuefer/home-assistant-core
 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
コード例 #7
0
ファイル: binary_sensor.py プロジェクト: jbouwh/core
    def _handle_coordinator_update(self) -> None:
        """Handle update of the data."""
        self._process_data()

        if self._delay_cancel:
            self._delay_cancel()
            self._delay_cancel = None

        if self._auto_off_cancel:
            self._auto_off_cancel()
            self._auto_off_cancel = None
            self._auto_off_time = None

        if not self.available:
            self.async_write_ha_state()
            return

        raw = self._rendered.get(CONF_STATE)
        state = template.result_as_boolean(raw)

        key = CONF_DELAY_ON if state else CONF_DELAY_OFF
        delay = self._rendered.get(key) or self._config.get(key)

        # state without delay. None means rendering failed.
        if self._state == state or state is None or delay is None:
            self._set_state(state)
            return

        if not isinstance(delay, timedelta):
            try:
                delay = cv.positive_time_period(delay)
            except vol.Invalid as err:
                logging.getLogger(__name__).warning(
                    "Error rendering %s template: %s", key, err
                )
                return

        # state with delay. Cancelled if new trigger received
        self._delay_cancel = async_call_later(
            self.hass, delay.total_seconds(), partial(self._set_state, state)
        )
コード例 #8
0
    def state_automation_listener(event: Event):
        """Listen for state changes and calls action."""
        entity: str = event.data["entity_id"]
        from_s: State | None = event.data.get("old_state")
        to_s: State | None = event.data.get("new_state")

        if from_s is None:
            old_value = None
        elif attribute is None:
            old_value = from_s.state
        else:
            old_value = from_s.attributes.get(attribute)

        if to_s is None:
            new_value = None
        elif attribute is None:
            new_value = to_s.state
        else:
            new_value = to_s.attributes.get(attribute)

        # When we listen for state changes with `match_all`, we
        # will trigger even if just an attribute changes. When
        # we listen to just an attribute, we should ignore all
        # other attribute changes.
        if attribute is not None and old_value == new_value:
            return

        if (not match_from_state(old_value) or not match_to_state(new_value)
                or (not match_all and old_value == new_value)):
            return

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

        if not time_delta:
            call_action()
            return

        trigger_info = {
            "trigger": {
                "platform": "state",
                "entity_id": entity,
                "from_state": from_s,
                "to_state": to_s,
            }
        }
        variables = {**_variables, **trigger_info}

        try:
            period[entity] = 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

        def _check_same_state(_, _2, new_st: State | None) -> bool:
            if new_st is None:
                return False

            cur_value: str | None
            if attribute is None:
                cur_value = new_st.state
            else:
                cur_value = new_st.attributes.get(attribute)

            if CONF_FROM in config and CONF_TO not in config:
                return cur_value != old_value

            return cur_value == new_value

        unsub_track_same[entity] = async_track_same_state(
            hass,
            period[entity],
            call_action,
            _check_same_state,
            entity_ids=entity,
        )
コード例 #9
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": {
                        **trigger_data,
                        "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,
            )

        @callback
        def check_numeric_state_no_raise(entity_id, from_s, to_s):
            """Return True if the criteria are now met, False otherwise."""
            try:
                return check_numeric_state(entity_id, from_s, to_s)
            except exceptions.ConditionError:
                # This is an internal same-state listener so we just drop the
                # error. The same error will be reached and logged by the
                # primary async_track_state_change_event() listener.
                return False

        try:
            matching = check_numeric_state(entity_id, from_s, to_s)
        except exceptions.ConditionError as ex:
            _LOGGER.warning("Error in '%s' trigger: %s", trigger_info["name"], ex)
            return

        if not matching:
            armed_entities.add(entity_id)
        elif entity_id in armed_entities:
            armed_entities.discard(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",
                        trigger_info["name"],
                        ex,
                    )
                    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_no_raise,
                )
            else:
                call_action()
コード例 #10
0
ファイル: trigger.py プロジェクト: testitesti22/core-1
    def template_listener(event, updates):
        """Listen for state changes and calls action."""
        nonlocal delay_cancel, armed
        result = updates.pop().result

        if isinstance(result, exceptions.TemplateError):
            _LOGGER.warning(
                "Error evaluating 'template' trigger for '%s': %s",
                automation_info["name"],
                result,
            )
            return

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

        if not result_as_boolean(result):
            armed = True
            return

        # Only fire when previously armed.
        if not armed:
            return

        # Fire!
        armed = False

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

        if entity_id is not None:
            description = f"{entity_id} via template"
        else:
            description = "time change or manual update via template"

        template_variables = {
            "platform": platform_type,
            "entity_id": entity_id,
            "from_state": from_s,
            "to_state": to_s,
        }
        trigger_variables = {
            "for": time_delta,
            "description": description,
            "id": trigger_id,
        }

        @callback
        def call_action(*_):
            """Call action with right context."""
            nonlocal trigger_variables
            hass.async_run_hass_job(
                job,
                {"trigger": {
                    **template_variables,
                    **trigger_variables
                }},
                (to_s.context if to_s else None),
            )

        if not time_delta:
            call_action()
            return

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

        trigger_variables["for"] = period

        delay_cancel = async_call_later(hass, period.seconds, call_action)
コード例 #11
0
        self._delay_off_raw = config.get(CONF_DELAY_OFF)

    async def async_added_to_hass(self):
        """Restore state and register callbacks."""
        if ((self._delay_on_raw is not None or self._delay_off_raw is not None)
                and
            (last_state := await self.async_get_last_state()) is not None and
                last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE)):
            self._state = last_state.state == STATE_ON

        self.add_template_attribute("_state", self._template, None,
                                    self._update_state)

        if self._delay_on_raw is not None:
            try:
                self._delay_on = cv.positive_time_period(self._delay_on_raw)
            except vol.Invalid:
                self.add_template_attribute("_delay_on", self._delay_on_raw,
                                            cv.positive_time_period)

        if self._delay_off_raw is not None:
            try:
                self._delay_off = cv.positive_time_period(self._delay_off_raw)
            except vol.Invalid:
                self.add_template_attribute("_delay_off", self._delay_off_raw,
                                            cv.positive_time_period)

        await super().async_added_to_hass()

    @callback
    def _update_state(self, result):