Exemple #1
0
    def _template_changed_listener(event: Event,
                                   updates: List[TrackTemplateResult]) -> None:
        """Check if condition is correct and run action."""
        track_result = updates.pop()

        template = track_result.template
        last_result = track_result.last_result
        result = track_result.result

        if isinstance(result, TemplateError):
            _LOGGER.error(
                "Error while processing template: %s",
                template.template,
                exc_info=result,
            )
            return

        if (not isinstance(last_result, TemplateError)
                and result_as_boolean(last_result)
                or not result_as_boolean(result)):
            return

        hass.async_run_job(
            action,
            event.data.get("entity_id"),
            event.data.get("old_state"),
            event.data.get("new_state"),
        )
Exemple #2
0
    def _update_state(self, result):
        super()._update_state(result)

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

        state = None if isinstance(
            result, TemplateError) else result_as_boolean(result)

        if state == self._state:
            return

        # state without delay
        if (state is None or (state and not self._delay_on)
                or (not state and not self._delay_off)):
            self._state = state
            return

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

        delay = (self._delay_on if state else self._delay_off).seconds
        # state with delay. Cancelled if template result changes.
        self._delay_cancel = async_call_later(self.hass, delay, _set_state)
        def _async_template_result_changed(event, updates):
            track_template_result = updates.pop()
            template = track_template_result.template
            result = track_template_result.result
            entity = event and event.data.get("entity_id")

            if isinstance(result, TemplateError):
                _LOGGER.error(
                    "TemplateError('%s') "
                    "while processing template '%s' "
                    "in entity '%s'",
                    result,
                    template,
                    self.entity_id,
                )

                should_trigger = False
            else:
                should_trigger = result_as_boolean(result)

            for obs in self.observations_by_template[template]:
                if should_trigger:
                    obs_entry = {"entity_id": entity, **obs}
                else:
                    obs_entry = None
                self.current_observations[obs["id"]] = obs_entry

            if event:
                self.async_set_context(event.context)
            self._recalculate_and_write_state()
Exemple #4
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)
Exemple #5
0
    def _async_power_changed(self, event, updates):
        """Update current power."""
        result = updates.pop().result

        if isinstance(result, TemplateError):
            _LOGGER.warning('Unable to update power from template: %s', result)
        else:
            self._current_hvac_mode = self._last_hvac_mode if result_as_boolean(
                result) else HVAC_MODE_OFF
            self.schedule_update_ha_state()
Exemple #6
0
    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)
        )
Exemple #7
0
class PlexMediaSearchResult:
    """Represents results from a Plex media media_content_id search.

    Results are used by media_player.play_media implementations.
    """
    def __init__(self, media, params=None) -> None:
        """Initialize the result."""
        self.media = media
        self._params = params or {}

    @property
    def offset(self) -> int:
        """Provide the appropriate offset based on payload contents."""
        if offset := self._params.get("offset", 0):
            return offset * 1000
        resume = self._params.get("resume", False)
        if isinstance(resume, str):
            resume = result_as_boolean(resume)
        if resume:
            return self.media.viewOffset
        return 0
    def _update_available(self, result):
        if isinstance(result, TemplateError):
            self._available = True
            return

        self._available = result_as_boolean(result)
Exemple #9
0
    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)
Exemple #10
0
async def async_attach_trigger(hass,
                               config,
                               action,
                               automation_info,
                               *,
                               platform_type="template"):
    """Listen for state changes based on configuration."""
    trigger_id = automation_info.get("trigger_id") if automation_info else None
    value_template = config.get(CONF_VALUE_TEMPLATE)
    value_template.hass = hass
    time_delta = config.get(CONF_FOR)
    template.attach(hass, time_delta)
    delay_cancel = None
    job = HassJob(action)
    armed = False

    # Arm at setup if the template is already false.
    try:
        if not result_as_boolean(
                value_template.async_render(automation_info["variables"])):
            armed = True
    except exceptions.TemplateError as ex:
        _LOGGER.warning(
            "Error initializing 'template' trigger for '%s': %s",
            automation_info["name"],
            ex,
        )

    @callback
    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)

    info = async_track_template_result(
        hass,
        [TrackTemplate(value_template, automation_info["variables"])],
        template_listener,
    )
    unsub = info.async_remove

    @callback
    def async_remove():
        """Remove state listeners async."""
        unsub()
        if delay_cancel:
            # pylint: disable=not-callable
            delay_cancel()

    return async_remove
Exemple #11
0
 def shuffle(self) -> bool:
     """Return value of shuffle parameter."""
     shuffle = self._params.get("shuffle", False)
     if isinstance(shuffle, str):
         shuffle = result_as_boolean(shuffle)
     return shuffle