Пример #1
0
        async def home_assistant_startup(event):
            """Add listeners and get initial state."""
            entities_to_track = self._power_calculator.get_entities_to_track()

            track_entities = [
                entity for entity in entities_to_track
                if isinstance(entity, str)
            ]
            if not track_entities:
                track_entities = [self._source_entity]

            async_track_state_change_event(self.hass, track_entities,
                                           appliance_state_listener)

            track_templates = [
                template for template in entities_to_track
                if isinstance(template, TrackTemplate)
            ]
            if track_templates:
                async_track_template_result(
                    self.hass,
                    track_templates=track_templates,
                    action=template_change_listener,
                )

            for entity_id in track_entities:
                new_state = self.hass.states.get(entity_id)

                await self._update_power_sensor(entity_id, new_state)
Пример #2
0
async def handle_render_template(hass, connection, msg):
    """Handle render_template command."""
    template_str = msg["template"]
    template_obj = template.Template(template_str, hass)
    variables = msg.get("variables")
    timeout = msg.get("timeout")
    info = None

    if timeout:
        try:
            timed_out = await template_obj.async_render_will_timeout(
                timeout, strict=msg["strict"])
        except TemplateError as ex:
            connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(ex))
            return

        if timed_out:
            connection.send_error(
                msg["id"],
                const.ERR_TEMPLATE_ERROR,
                f"Exceeded maximum execution time of {timeout}s",
            )
            return

    @callback
    def _template_listener(event, updates):
        nonlocal info
        track_template_result = updates.pop()
        result = track_template_result.result
        if isinstance(result, TemplateError):
            connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR,
                                  str(result))
            return

        connection.send_message(
            messages.event_message(
                msg["id"],
                {
                    "result": result,
                    "listeners": info.listeners
                }  # type: ignore
            ))

    try:
        info = async_track_template_result(
            hass,
            [TrackTemplate(template_obj, variables)],
            _template_listener,
            raise_on_template_error=True,
            strict=msg["strict"],
        )
    except TemplateError as ex:
        connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(ex))
        return

    connection.subscriptions[msg["id"]] = info.async_remove

    connection.send_result(msg["id"])

    hass.loop.call_soon_threadsafe(info.async_refresh)
    async def _async_template_startup(self, *_) -> None:
        template_var_tups: list[TrackTemplate] = []
        has_availability_template = False

        values = {"this": TemplateStateFromEntityId(self.hass, self.entity_id)}

        for template, attributes in self._template_attrs.items():
            template_var_tup = TrackTemplate(template, values)
            is_availability_template = False
            for attribute in attributes:
                # pylint: disable-next=protected-access
                if attribute._attribute == "_attr_available":
                    has_availability_template = True
                    is_availability_template = True
                attribute.async_setup()
            # Insert the availability template first in the list
            if is_availability_template:
                template_var_tups.insert(0, template_var_tup)
            else:
                template_var_tups.append(template_var_tup)

        result_info = async_track_template_result(
            self.hass,
            template_var_tups,
            self._handle_results,
            has_super_template=has_availability_template,
        )
        self.async_on_remove(result_info.async_remove)
        self._async_update = result_info.async_refresh
        result_info.async_refresh()
Пример #4
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(event, updates):
            """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
        info = async_track_template_result(
            self._hass,
            [TrackTemplate(wait_template, self._variables)],
            _async_script_wait,
        )
        unsub = info.async_remove

        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()
Пример #5
0
    def __init__(self, hass, name, remote_entity_id, commands, min_temp,
                 max_temp, target_temp, target_temp_step, hvac_modes,
                 fan_modes, preset_modes, default_hvac_mode, default_fan_mode,
                 default_preset_mode, temp_entity_id, power_template, domain,
                 service, prefix):
        """Representation of a Xiaomi Remote Climate device."""

        self.hass = hass
        self._name = name
        self._remote_entity_id = remote_entity_id
        self._commands = commands
        self._domain = domain
        self._service = service
        self._prefix = prefix

        self._min_temp = min_temp
        self._max_temp = max_temp
        self._target_temperature = target_temp
        self._target_temperature_step = target_temp_step
        self._unit_of_measurement = hass.config.units.temperature_unit

        self._current_temperature = None
        self._default_hvac_mode = default_hvac_mode
        self._current_hvac_mode = default_hvac_mode
        self._last_hvac_mode = default_hvac_mode
        self._default_fan_mode = default_fan_mode
        self._current_fan_mode = default_fan_mode
        self._last_fan_mode = default_fan_mode
        self._default_preset_mode = default_preset_mode
        self._current_preset_mode = default_preset_mode
        self._last_preset_mode = default_preset_mode

        self._temp_entity_id = temp_entity_id
        self._power_template = power_template

        self._hvac_modes = hvac_modes
        self._fan_modes = fan_modes
        self._preset_modes = preset_modes

        self._support_flags = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE
        if preset_modes:
            self._support_flags |= SUPPORT_PRESET_MODE
        self._enabled_flags = self._support_flags

        if temp_entity_id:
            async_track_state_change(hass, temp_entity_id,
                                     self._async_temp_changed)

        if power_template:
            result = async_track_template_result(
                self.hass,
                [TrackTemplate(power_template, None)],
                self._async_power_changed,
            )
            result.async_refresh()
            self.async_on_remove(result.async_remove)
Пример #6
0
    async def _async_template_startup(self, *_) -> None:
        template_var_tups = []
        for template, attributes in self._template_attrs.items():
            template_var_tups.append(TrackTemplate(template, None))
            for attribute in attributes:
                attribute.async_setup()

        result_info = async_track_template_result(self.hass, template_var_tups,
                                                  self._handle_results)
        self.async_on_remove(result_info.async_remove)
        self._async_update = result_info.async_refresh
        result_info.async_refresh()
Пример #7
0
    async def _async_template_startup(self, *_) -> None:
        # _handle_results will not write state until "_async_update" is set
        template_var_tups = [
            TrackTemplate(template, None) for template in self._template_attrs
        ]

        result_info = async_track_template_result(self.hass, template_var_tups,
                                                  self._handle_results)
        self.async_on_remove(result_info.async_remove)
        result_info.async_refresh()
        self.async_write_ha_state()
        self._async_update = result_info.async_refresh
Пример #8
0
    async def async_added_to_hass(self):
        """Subscribe to children and template state changes."""

        @callback
        def _async_on_dependency_update(event):
            """Update ha state when dependencies update."""
            self.async_set_context(event.context)
            self.async_schedule_update_ha_state(True)

        @callback
        def _async_on_template_update(event, updates):
            """Update ha state when dependencies update."""
            result = updates.pop().result

            if isinstance(result, TemplateError):
                self._state_template_result = None
            else:
                self._state_template_result = result

            if event:
                self.async_set_context(event.context)

            self.async_schedule_update_ha_state(True)

        if self._state_template is not None:
            result = async_track_template_result(
                self.hass,
                [TrackTemplate(self._state_template, None)],
                _async_on_template_update,
            )
            self.hass.bus.async_listen_once(
                EVENT_HOMEASSISTANT_START, callback(lambda _: result.async_refresh())
            )

            self.async_on_remove(result.async_remove)

        depend = copy(self._children)
        for entity in self._attrs.values():
            depend.append(entity[0])

        self.async_on_remove(
            self.hass.helpers.event.async_track_state_change_event(
                list(set(depend)), _async_on_dependency_update
            )
        )
Пример #9
0
def handle_render_template(hass, connection, msg):
    """Handle render_template command."""
    template = msg["template"]
    template.hass = hass

    variables = msg.get("variables")
    info = None

    @callback
    def _template_listener(event, updates):
        nonlocal info
        track_template_result = updates.pop()
        result = track_template_result.result
        if isinstance(result, TemplateError):
            _LOGGER.error(
                "TemplateError('%s') "
                "while processing template '%s'",
                result,
                track_template_result.template,
            )

            result = None

        connection.send_message(
            messages.event_message(
                msg["id"],
                {
                    "result": result,
                    "listeners": info.listeners
                }  # type: ignore
            ))

    info = async_track_template_result(hass,
                                       [TrackTemplate(template, variables)],
                                       _template_listener)

    connection.subscriptions[msg["id"]] = info.async_remove

    connection.send_result(msg["id"])

    hass.loop.call_soon_threadsafe(info.async_refresh)
 def __init__(self, hass, config):
     self.hass = hass
     self.hub = hass.data[DOMAIN][HUB] = CrestronXsig()
     self.port = config.get(CONF_PORT)
     self.context = Context()
     self.to_hub = {}
     self.hub.register_sync_all_joins_callback(self.sync_joins_to_hub)
     if CONF_TO_HUB in config:
         track_templates = []
         for entity in config[CONF_TO_HUB]:
             template_string = None
             if CONF_VALUE_TEMPLATE in entity:
                 template = entity[CONF_VALUE_TEMPLATE]
                 self.to_hub[entity[CONF_JOIN]] = template
                 track_templates.append(TrackTemplate(template, None))
             elif CONF_ATTRIBUTE in entity and CONF_ENTITY_ID in entity:
                 template_string = (
                     "{{state_attr('"
                     + entity[CONF_ENTITY_ID]
                     + "','"
                     + entity[CONF_ATTRIBUTE]
                     + "')}}"
                 )
                 template = Template(template_string, hass)
                 self.to_hub[entity[CONF_JOIN]] = template
                 track_templates.append(TrackTemplate(template, None))
             elif CONF_ENTITY_ID in entity:
                 template_string = "{{states('" + entity[CONF_ENTITY_ID] + "')}}"
                 template = Template(template_string, hass)
                 self.to_hub[entity[CONF_JOIN]] = template
                 track_templates.append(TrackTemplate(template, None))
         self.tracker = async_track_template_result(
             self.hass, track_templates, self.template_change_callback
         )
     if CONF_FROM_HUB in config:
         self.from_hub = config[CONF_FROM_HUB]
         self.hub.register_callback(self.join_change_callback)
Пример #11
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
Пример #12
0
    async def async_added_to_hass(self):
        """
        Call when entity about to be added.

        All relevant update logic for instance attributes occurs within this closure.
        Other methods in this class are designed to avoid directly modifying instance
        attributes, by instead focusing on returning relevant data back to this method.

        The goal of this method is to ensure that `self.current_observations` and `self.probability`
        are set on a best-effort basis when this entity is register with hass.

        In addition, this method must register the state listener defined within, which
        will be called any time a relevant entity changes its state.
        """

        @callback
        def async_threshold_sensor_state_listener(event):
            """
            Handle sensor state changes.

            When a state changes, we must update our list of current observations,
            then calculate the new probability.
            """
            new_state = event.data.get("new_state")

            if new_state is None or new_state.state == STATE_UNKNOWN:
                return

            entity = event.data.get("entity_id")

            self.current_observations.update(self._record_entity_observations(entity))
            self.async_set_context(event.context)
            self._recalculate_and_write_state()

        self.async_on_remove(
            async_track_state_change_event(
                self.hass,
                list(self.observations_by_entity),
                async_threshold_sensor_state_listener,
            )
        )

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

        for template in self.observations_by_template:
            info = async_track_template_result(
                self.hass,
                [TrackTemplate(template, None)],
                _async_template_result_changed,
            )

            self._callbacks.append(info)
            self.async_on_remove(info.async_remove)
            info.async_refresh()

        self.current_observations.update(self._initialize_current_observations())
        self.probability = self._calculate_new_probability()
        self._deviation = bool(self.probability >= self._probability_threshold)
    def __init__(self, name, next_action, previous_action, play_action,
                 pause_action, vol_down_action, vol_up_action,
                 player_status_keyword, topics, mqtt, hass):
        """Initialize"""
        self.hass = hass
        self._domain = __name__.split(".")[-2]
        self._name = name
        self._volume = 0.0
        self._track_name = ""
        self._track_artist = ""
        self._track_album_name = ""
        self._mqtt_player_state = None
        self._state = None
        self._album_art = None
        self._next_script = None
        self._previous_script = None
        self._play_script = None
        self._pause_script = None
        self._vol_down_action = None
        self._vol_up_action = None
        self._vol_script = None

        if (next_action):
            self._next_script = Script(hass, next_action, self._name,
                                       self._domain)
        if (previous_action):
            self._previous_script = Script(hass, previous_action, self._name,
                                           self._domain)
        if (play_action):
            self._play_script = Script(hass, play_action, self._name,
                                       self._domain)
        if (pause_action):
            self._pause_script = Script(hass, pause_action, self._name,
                                        self._domain)
        if (vol_down_action):
            self._vol_down_action = Script(hass, vol_down_action, self._name,
                                           self._domain)
        if (vol_up_action):
            self._vol_up_action = Script(hass, vol_up_action, self._name,
                                         self._domain)

        self._player_status_keyword = player_status_keyword

        if topics is not None:
            for key, value in topics.items():

                if key == "song_title":
                    result = async_track_template_result(
                        self.hass, [TrackTemplate(value, None)],
                        self.tracktitle_listener)
                    self.async_on_remove(result.async_remove)

                if key == "song_artist":
                    result = async_track_template_result(
                        self.hass, [TrackTemplate(value, None)],
                        self.artist_listener)
                    self.async_on_remove(result.async_remove)

                if key == "song_album":
                    result = async_track_template_result(
                        self.hass, [TrackTemplate(value, None)],
                        self.album_listener)
                    self.async_on_remove(result.async_remove)

                if key == "song_volume":
                    result = async_track_template_result(
                        self.hass, [TrackTemplate(value, None)],
                        self.volume_listener)
                    self.async_on_remove(result.async_remove)

                if key == "album_art":
                    mqtt.subscribe(value, self.albumart_listener)

                if key == "player_status":
                    result = async_track_template_result(
                        self.hass, [TrackTemplate(value, None)],
                        self.state_listener)
                    self.async_on_remove(result.async_remove)

                if key == "volume":
                    self._vol_script = Script(hass, value, self._name,
                                              self._domain)
Пример #14
0
async def async_attach_trigger(hass,
                               config,
                               action,
                               automation_info,
                               *,
                               platform_type="template"):
    """Listen for state changes based on configuration."""
    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)

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

    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