예제 #1
0
 def _process_template(self, entity_observation):
     """Add entity to current_obs if template is true."""
     template = entity_observation.get(CONF_VALUE_TEMPLATE)
     template.hass = self.hass
     should_trigger = condition.async_template(self.hass, template,
                                               entity_observation)
     self._update_current_obs(entity_observation, should_trigger)
예제 #2
0
    async def _async_wait_template(self, action, variables, context):
        """Handle a wait template."""
        # Call ourselves in the future to continue work
        wait_template = action[CONF_WAIT_TEMPLATE]
        wait_template.hass = self.hass

        self.last_action = action.get(CONF_ALIAS, 'wait template')
        self._log("Executing step %s" % self.last_action)

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

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

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

        raise _SuspendScript
 def _process_template(self, entity_observation):
     """Add entity to current_obs if template is true."""
     template = entity_observation.get(CONF_VALUE_TEMPLATE)
     template.hass = self.hass
     should_trigger = condition.async_template(
         self.hass, template, entity_observation)
     self._update_current_obs(entity_observation, should_trigger)
예제 #4
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,
                        },
                    },
                    context=(to_s.context if to_s else None)))

        if not time_delta:
            call_action()
            return

        unsub_track_same = async_track_same_state(
            hass, time_delta, call_action,
            lambda _, _2, _3: condition.async_template(hass, value_template),
            value_template.extract_entities())
예제 #5
0
 def _process_template(self, entity_observation):
     """Return True if template condition is True."""
     template = entity_observation.get(CONF_VALUE_TEMPLATE)
     template.hass = self.hass
     should_trigger = condition.async_template(self.hass, template,
                                               entity_observation)
     return should_trigger
예제 #6
0
    async def _async_wait_template_step(self):
        """Handle a wait template."""
        if CONF_TIMEOUT in self._action:
            delay = self._get_pos_time_period_template(
                CONF_TIMEOUT).total_seconds()
        else:
            delay = None

        self._script.last_action = self._action.get(CONF_ALIAS,
                                                    "wait template")
        self._log(
            "Executing step %s%s",
            self._script.last_action,
            "" if delay is None else f" (timeout: {timedelta(seconds=delay)})",
        )

        self._variables["wait"] = {"remaining": delay, "completed": False}

        wait_template = self._action[CONF_WAIT_TEMPLATE]
        wait_template.hass = self._hass

        # check if condition already okay
        if condition.async_template(self._hass, wait_template,
                                    self._variables):
            self._variables["wait"]["completed"] = True
            return

        @callback
        def async_script_wait(entity_id, from_s, to_s):
            """Handle script after template condition is true."""
            self._variables["wait"] = {
                "remaining": to_context.remaining if to_context else delay,
                "completed": True,
            }
            done.set()

        to_context = None
        unsub = async_track_template(self._hass, wait_template,
                                     async_script_wait, self._variables)

        self._changed()
        done = asyncio.Event()
        tasks = [
            self._hass.async_create_task(flag.wait())
            for flag in (self._stop, done)
        ]
        try:
            async with timeout(delay) as to_context:
                await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
        except asyncio.TimeoutError as ex:
            if not self._action.get(CONF_CONTINUE_ON_TIMEOUT, True):
                self._log(_TIMEOUT_MSG)
                raise _StopScript from ex
            self._variables["wait"]["remaining"] = 0.0
        finally:
            for task in tasks:
                task.cancel()
            unsub()
예제 #7
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(),
        )
예제 #8
0
    async def _async_wait_template_step(self):
        """Handle a wait template."""
        if CONF_TIMEOUT in self._action:
            timeout = self._get_pos_time_period_template(
                CONF_TIMEOUT).total_seconds()
        else:
            timeout = None

        self._step_log("wait template", timeout)

        self._variables["wait"] = {"remaining": timeout, "completed": False}
        trace_set_result(wait=self._variables["wait"])

        wait_template = self._action[CONF_WAIT_TEMPLATE]
        wait_template.hass = self._hass

        # check if condition already okay
        if condition.async_template(self._hass, wait_template, self._variables,
                                    False):
            self._variables["wait"]["completed"] = True
            return

        @callback
        def async_script_wait(entity_id, from_s, to_s):
            """Handle script after template condition is true."""
            wait_var = self._variables["wait"]
            wait_var[
                "remaining"] = to_context.remaining if to_context else timeout
            wait_var["completed"] = True
            done.set()

        to_context = None
        unsub = async_track_template(self._hass, wait_template,
                                     async_script_wait, self._variables)

        self._changed()
        done = asyncio.Event()
        tasks = [
            self._hass.async_create_task(flag.wait())
            for flag in (self._stop, done)
        ]
        try:
            async with async_timeout.timeout(timeout) as to_context:
                await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
        except asyncio.TimeoutError as ex:
            self._variables["wait"]["remaining"] = 0.0
            if not self._action.get(CONF_CONTINUE_ON_TIMEOUT, True):
                self._log(_TIMEOUT_MSG)
                trace_set_result(wait=self._variables["wait"], timeout=True)
                raise _StopScript from ex
        finally:
            for task in tasks:
                task.cancel()
            unsub()
예제 #9
0
    def _prep_wait_template_step(self, async_script_wait):
        wait_template = self._action[CONF_WAIT_TEMPLATE]
        wait_template.hass = self._hass

        self._script.last_action = self._action.get(CONF_ALIAS,
                                                    "wait template")
        self._log("Executing step %s", self._script.last_action)

        # check if condition already okay
        if condition.async_template(self._hass, wait_template,
                                    self._variables):
            return None

        return async_track_template(self._hass, wait_template,
                                    async_script_wait, self._variables)
예제 #10
0
    def state_changed_listener(entity_id, from_s, to_s):
        """Listen for state changes and calls action."""
        nonlocal already_triggered
        template_result = condition.async_template(hass, value_template)

        # Check to see if template returns true
        if template_result and not already_triggered:
            already_triggered = True
            hass.async_add_job(action, {
                'trigger': {
                    'platform': 'template',
                    'entity_id': entity_id,
                    'from_state': from_s,
                    'to_state': to_s,
                },
            })
        elif not template_result:
            already_triggered = False
예제 #11
0
    async def _async_wait_template_step(self):
        """Handle a wait template."""
        self._script.last_action = self._action.get(CONF_ALIAS,
                                                    "wait template")
        self._log("Executing step %s", self._script.last_action)

        wait_template = self._action[CONF_WAIT_TEMPLATE]
        wait_template.hass = self._hass

        # check if condition already okay
        if condition.async_template(self._hass, wait_template,
                                    self._variables):
            return

        @callback
        def async_script_wait(entity_id, from_s, to_s):
            """Handle script after template condition is true."""
            done.set()

        unsub = async_track_template(self._hass, wait_template,
                                     async_script_wait, self._variables)

        self._changed()
        try:
            delay = self._action[CONF_TIMEOUT].total_seconds()
        except KeyError:
            delay = None
        done = asyncio.Event()
        tasks = [
            self._hass.async_create_task(flag.wait())
            for flag in (self._stop, done)
        ]
        try:
            async with timeout(delay):
                await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
        except asyncio.TimeoutError:
            if not self._action.get(CONF_CONTINUE_ON_TIMEOUT, True):
                self._log(_TIMEOUT_MSG)
                raise _StopScript
        finally:
            for task in tasks:
                task.cancel()
            unsub()
예제 #12
0
    def state_changed_listener(entity_id, from_s, to_s):
        """Listen for state changes and calls action."""
        nonlocal already_triggered
        template_result = condition.async_template(hass, value_template)

        # Check to see if template returns true
        if template_result and not already_triggered:
            already_triggered = True
            hass.async_run_job(
                action, {
                    'trigger': {
                        'platform': 'template',
                        'entity_id': entity_id,
                        'from_state': from_s,
                        'to_state': to_s,
                    },
                })
        elif not template_result:
            already_triggered = False
예제 #13
0
    async def async_run(self, variables: Optional[Sequence] = 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
                    self._async_listener.remove(unsub)
                    self.hass.async_add_job(self.async_run(variables))

                delay = action[CONF_DELAY]

                try:
                    if isinstance(delay, template.Template):
                        delay = vol.All(
                            cv.time_period,
                            cv.positive_timedelta)(
                                delay.async_render(variables))
                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_add_job(self.async_run(variables))

                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, 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)

            else:
                await self._async_call_service(action, variables)

        self._cur = -1
        self.last_action = None
        if self._change_listener:
            self.hass.async_add_job(self._change_listener)
예제 #14
0
    def async_run(self, variables: Optional[Sequence]=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):
                    """Called after delay is done."""
                    # pylint: disable=cell-var-from-loop
                    self._async_listener.remove(unsub)
                    self.hass.async_add_job(self.async_run(variables))

                delay = action[CONF_DELAY]

                if isinstance(delay, template.Template):
                    delay = vol.All(
                        cv.time_period,
                        cv.positive_timedelta)(
                            delay.async_render(variables))

                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

            elif 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 allready okay
                if condition.async_template(
                        self.hass, wait_template, variables):
                    continue

                @callback
                def async_script_wait(entity_id, from_s, to_s):
                    """Called after template condition is true."""
                    self._async_remove_listener()
                    self.hass.async_add_job(self.async_run(variables))

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

                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)

                return

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

            elif CONF_EVENT in action:
                self._async_fire_event(action)

            else:
                yield from self._async_call_service(action, variables)

        self._cur = -1
        self.last_action = None
        if self._change_listener:
            self.hass.async_add_job(self._change_listener)
예제 #15
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)