Esempio n. 1
0
async def validate_configs(hass, entity_configs):
    """Validate that entities exist and ensure templates are ready to use."""
    entity_registry = await hass.helpers.entity_registry.async_get_registry()
    for entity_id, entity_config in entity_configs.items():
        state = hass.states.get(entity_id)
        if state is None:
            _LOGGER.debug("Entity not found: %s", entity_id)
            continue

        entity = entity_registry.async_get(entity_id)
        if entity:
            entity_config[CONF_UNIQUE_ID] = get_system_unique_id(entity)
        else:
            entity_config[CONF_UNIQUE_ID] = entity_id

        if CONF_POWER in entity_config:
            power_val = entity_config[CONF_POWER]
            if isinstance(power_val, str) and is_template_string(power_val):
                entity_config[CONF_POWER] = Template(power_val, hass)
            elif isinstance(power_val, Template):
                entity_config[CONF_POWER].hass = hass
        elif CONF_POWER_ENTITY in entity_config:
            power_val = entity_config[CONF_POWER_ENTITY]
            if hass.states.get(power_val) is None:
                _LOGGER.debug("Sensor Entity not found: %s", power_val)
            else:
                entity_config[CONF_POWER] = power_val
        elif state.domain == SENSOR_DOMAIN:
            pass
        elif ATTR_CURRENT_POWER_W in state.attributes:
            pass
        else:
            _LOGGER.debug("No power value defined for: %s", entity_id)
Esempio n. 2
0
    def test_measure(self):
        """Test the history statistics sensor measure."""
        later = dt_util.utcnow() - timedelta(seconds=15)
        earlier = later - timedelta(minutes=30)

        fake_states = {
            'binary_sensor.test_id': [
                ha.State('binary_sensor.test_id', 'on', last_changed=earlier),
                ha.State('binary_sensor.test_id', 'off', last_changed=later),
            ]
        }

        start = Template('{{ as_timestamp(now()) - 3600 }}', self.hass)
        end = Template('{{ now() }}', self.hass)

        sensor1 = HistoryStatsSensor(self.hass, 'binary_sensor.test_id', 'on',
                                     start, end, None, 'Test')

        sensor2 = HistoryStatsSensor(self.hass, 'unknown.id', 'on', start, end,
                                     None, 'Test')

        with patch(
                'homeassistant.components.history.'
                'state_changes_during_period',
                return_value=fake_states):
            with patch('homeassistant.components.history.get_state',
                       return_value=None):
                sensor1.update()
                sensor2.update()

        self.assertEqual(sensor1.value, 0.5)
        self.assertEqual(sensor2.value, 0)
        self.assertEqual(sensor1.device_state_attributes['ratio'], '50.0%')
 def setup_method(self):
     """Set up things to be run when tests are started."""
     self.hass = get_test_home_assistant()
     self.name = "foo"
     self.method = "post"
     self.resource = "http://localhost/"
     self.state_resource = self.resource
     self.headers = {"Content-type": CONTENT_TYPE_JSON}
     self.auth = None
     self.body_on = Template("on", self.hass)
     self.body_off = Template("off", self.hass)
     self.switch = rest.RestSwitch(
         self.name,
         self.resource,
         self.state_resource,
         self.method,
         self.headers,
         self.auth,
         self.body_on,
         self.body_off,
         None,
         10,
         True,
     )
     self.switch.hass = self.hass
Esempio n. 4
0
 def __init__(
     self,
     hass: HomeAssistant,
     name_template: Template,
     value_template: Template,
     availability_template: Template | None,
     command_set_value: dict[str, Any],
     step_template: Template,
     minimum_template: Template | None,
     maximum_template: Template | None,
     optimistic: bool,
     unique_id: str | None,
 ) -> None:
     """Initialize the number."""
     super().__init__(availability_template=availability_template)
     self._attr_name = DEFAULT_NAME
     self._name_template = name_template
     name_template.hass = hass
     with contextlib.suppress(TemplateError):
         self._attr_name = name_template.async_render(parse_result=False)
     self._value_template = value_template
     domain = __name__.split(".")[-2]
     self._command_set_value = Script(hass, command_set_value,
                                      self._attr_name, domain)
     self._step_template = step_template
     self._min_value_template = minimum_template
     self._max_value_template = maximum_template
     self._attr_assumed_state = self._optimistic = optimistic
     self._attr_unique_id = unique_id
     self._attr_value = None
     self._attr_step = None
Esempio n. 5
0
 def setup_method(self):
     """Setup things to be run when tests are started."""
     self.hass = get_test_home_assistant()
     self.name = 'foo'
     self.resource = 'http://localhost/'
     self.body_on = Template('on', self.hass)
     self.body_off = Template('off', self.hass)
     self.switch = rest.RestSwitch(self.hass, self.name, self.resource,
                                   self.body_on, self.body_off, None, 10)
Esempio n. 6
0
    def test_measure(self):
        """Test the history statistics sensor measure."""
        t0 = dt_util.utcnow() - timedelta(minutes=40)
        t1 = t0 + timedelta(minutes=20)
        t2 = dt_util.utcnow() - timedelta(minutes=10)

        # Start     t0        t1        t2        End
        # |--20min--|--20min--|--10min--|--10min--|
        # |---off---|---on----|---off---|---on----|

        fake_states = {
            "binary_sensor.test_id": [
                ha.State("binary_sensor.test_id", "on", last_changed=t0),
                ha.State("binary_sensor.test_id", "off", last_changed=t1),
                ha.State("binary_sensor.test_id", "on", last_changed=t2),
            ]
        }

        start = Template("{{ as_timestamp(now()) - 3600 }}", self.hass)
        end = Template("{{ now() }}", self.hass)

        sensor1 = HistoryStatsSensor(
            self.hass, "binary_sensor.test_id", "on", start, end, None, "time", "Test"
        )

        sensor2 = HistoryStatsSensor(
            self.hass, "unknown.id", "on", start, end, None, "time", "Test"
        )

        sensor3 = HistoryStatsSensor(
            self.hass, "binary_sensor.test_id", "on", start, end, None, "count", "test"
        )

        sensor4 = HistoryStatsSensor(
            self.hass, "binary_sensor.test_id", "on", start, end, None, "ratio", "test"
        )

        assert sensor1._type == "time"
        assert sensor3._type == "count"
        assert sensor4._type == "ratio"

        with patch(
            "homeassistant.components.history." "state_changes_during_period",
            return_value=fake_states,
        ):
            with patch("homeassistant.components.history.get_state", return_value=None):
                sensor1.update()
                sensor2.update()
                sensor3.update()
                sensor4.update()

        assert sensor1.state == 0.5
        assert sensor2.state is None
        assert sensor3.state == 2
        assert sensor4.state == 50
Esempio n. 7
0
    def test_measure(self):
        """Test the history statistics sensor measure."""
        t0 = dt_util.utcnow() - timedelta(minutes=40)
        t1 = t0 + timedelta(minutes=20)
        t2 = dt_util.utcnow() - timedelta(minutes=10)

        # Start     t0        t1        t2        End
        # |--20min--|--20min--|--10min--|--10min--|
        # |---off---|---on----|---off---|---on----|

        fake_states = {
            'binary_sensor.test_id': [
                ha.State('binary_sensor.test_id', 'on', last_changed=t0),
                ha.State('binary_sensor.test_id', 'off', last_changed=t1),
                ha.State('binary_sensor.test_id', 'on', last_changed=t2),
            ]
        }

        start = Template('{{ as_timestamp(now()) - 3600 }}', self.hass)
        end = Template('{{ now() }}', self.hass)

        sensor1 = HistoryStatsSensor(
            self.hass, 'binary_sensor.test_id', 'on', start, end, None,
            'time', 'Test')

        sensor2 = HistoryStatsSensor(
            self.hass, 'unknown.id', 'on', start, end, None, 'time', 'Test')

        sensor3 = HistoryStatsSensor(
            self.hass, 'binary_sensor.test_id', 'on', start, end, None,
            'count', 'test')

        sensor4 = HistoryStatsSensor(
            self.hass, 'binary_sensor.test_id', 'on', start, end, None,
            'ratio', 'test')

        assert sensor1._type == 'time'
        assert sensor3._type == 'count'
        assert sensor4._type == 'ratio'

        with patch('homeassistant.components.history.'
                   'state_changes_during_period', return_value=fake_states):
            with patch('homeassistant.components.history.get_state',
                       return_value=None):
                sensor1.update()
                sensor2.update()
                sensor3.update()
                sensor4.update()

        assert sensor1.state == 0.5
        assert sensor2.state is None
        assert sensor3.state == 2
        assert sensor4.state == 50
Esempio n. 8
0
async def _create_counter(
    name: str,
    entity_types: Union[str, List[str]],
    states: Optional[List[str]] = None,
    prefix: Optional[str] = None,
    area: Optional[AreaSettings] = None,
    reject: bool = False,
    sum: bool = False,
) -> Optional[str]:
    """Create a counter or super counter if sum is true."""

    platform: EntityPlatform = get_base().hass.data[CONF_ENTITY_PLATFORM][PLATFORM][0]

    area_string = f"area_{area[ATTR_ID]}_" if area is not None else ""
    area_title = f"Area {area[ATTR_ID][-5:-1]} " if area is not None else ""

    device_id = f"{DOMAIN}_{area_string}{name}"
    entity_id = f"{PLATFORM}.{device_id}"
    friendly_name = f"{TITLE} {area_title}{name.replace('_', ' ').title()}"

    await remove_counter(entity_id)

    templates: CounterTemplates
    if sum:
        templates = _super_counter_templates(entity_types, area, prefix)
    else:
        templates = _counter_templates(entity_types[0], states, area, reject)

    if templates is None:
        return None

    await platform.async_add_entities(
        [
            await create_binary_sensor_entity(
                device_id,
                {
                    CONF_FRIENDLY_NAME: friendly_name,
                    CONF_ICON_TEMPLATE: Template("mdi:counter"),
                    CONF_VALUE_TEMPLATE: Template(templates["state_template"]),
                    CONF_ATTRIBUTE_TEMPLATES: {
                        CONF_COUNT: Template(templates["count_template"]),
                        CONF_ENTITIES: Template(templates["entity_template"]),
                        CONF_TRACKED_ENTITY_COUNT: Template(
                            templates["tracked_count_template"]
                        ),
                    },
                },
            )
        ]
    )

    return entity_id
Esempio n. 9
0
async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    """Set up the SQL sensor entry."""

    db_url: str = entry.options[CONF_DB_URL]
    name: str = entry.options[CONF_NAME]
    query_str: str = entry.options[CONF_QUERY]
    unit: str | None = entry.options.get(CONF_UNIT_OF_MEASUREMENT)
    template: str | None = entry.options.get(CONF_VALUE_TEMPLATE)
    column_name: str = entry.options[CONF_COLUMN_NAME]

    value_template: Template | None = None
    if template is not None:
        try:
            value_template = Template(template)
            value_template.ensure_valid()
        except TemplateError:
            value_template = None
        if value_template is not None:
            value_template.hass = hass

    try:
        engine = sqlalchemy.create_engine(db_url, future=True)
        sessmaker = scoped_session(sessionmaker(bind=engine, future=True))
    except SQLAlchemyError as err:
        _LOGGER.error("Can not open database %s", {redact_credentials(str(err))})
        return

    # MSSQL uses TOP and not LIMIT
    if not ("LIMIT" in query_str.upper() or "SELECT TOP" in query_str.upper()):
        if "mssql" in db_url:
            query_str = query_str.upper().replace("SELECT", "SELECT TOP 1")
        else:
            query_str = query_str.replace(";", "") + " LIMIT 1;"

    async_add_entities(
        [
            SQLSensor(
                name,
                sessmaker,
                query_str,
                column_name,
                unit,
                value_template,
                entry.entry_id,
            )
        ],
        True,
    )
Esempio n. 10
0
 def setup_method(self):
     """Set up things to be run when tests are started."""
     self.hass = get_test_home_assistant()
     self.name = 'foo'
     self.method = 'post'
     self.resource = 'http://localhost/'
     self.headers = {'Content-type': 'application/json'}
     self.auth = None
     self.body_on = Template('on', self.hass)
     self.body_off = Template('off', self.hass)
     self.switch = rest.RestSwitch(self.name, self.resource, self.method,
                                   self.headers, self.auth, self.body_on,
                                   self.body_off, None, 10)
     self.switch.hass = self.hass
Esempio n. 11
0
    def add_template_attribute(
        self,
        attribute: str,
        template: Template,
        validator: Callable[[Any], Any] = None,
        on_update: Callable[[Any], None] | None = None,
        none_on_template_error: bool = False,
    ) -> None:
        """
        Call in the constructor to add a template linked to a attribute.

        Parameters
        ----------
        attribute
            The name of the attribute to link to. This attribute must exist
            unless a custom on_update method is supplied.
        template
            The template to calculate.
        validator
            Validator function to parse the result and ensure it's valid.
        on_update
            Called to store the template result rather than storing it
            the supplied attribute. Passed the result of the validator, or None
            if the template or validator resulted in an error.

        """
        assert self.hass is not None, "hass cannot be None"
        template.hass = self.hass
        attribute = _TemplateAttribute(self, attribute, template, validator,
                                       on_update, none_on_template_error)
        self._template_attrs.setdefault(template, [])
        self._template_attrs[template].append(attribute)
Esempio n. 12
0
def _check_deprecated_turn_off(hass, turn_off_action):
    """Create an equivalent script for old turn off actions."""
    if isinstance(turn_off_action, str):
        method = DEPRECATED_TURN_OFF_ACTIONS[turn_off_action]
        new_config = OrderedDict([
            ("service", f"{DOMAIN}.{SERVICE_CALL_METHOD}"),
            (
                "data_template",
                OrderedDict([("entity_id", "{{ entity_id }}"),
                             ("method", method)]),
            ),
        ])
        example_conf = dump(OrderedDict([(CONF_TURN_OFF_ACTION, new_config)]))
        _LOGGER.warning(
            "The '%s' action for turn off Kodi is deprecated and "
            "will cease to function in a future release. You need to "
            "change it for a generic Home Assistant script sequence, "
            "which is, for this turn_off action, like this:\n%s",
            turn_off_action,
            example_conf,
        )
        new_config["data_template"] = OrderedDict([
            (key, Template(value, hass))
            for key, value in new_config["data_template"].items()
        ])
        turn_off_action = [new_config]
    return turn_off_action
Esempio n. 13
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))
Esempio n. 14
0
def async_track_template(
    hass: HomeAssistant,
    template: Template,
    action: Callable[[str, State, State], None],
    variables: Optional[Dict[str, Any]] = None,
) -> CALLBACK_TYPE:
    """Add a listener that track state changes with template condition."""
    from . import condition  # pylint: disable=import-outside-toplevel

    # Local variable to keep track of if the action has already been triggered
    already_triggered = False

    @callback
    def template_condition_listener(entity_id: str, from_s: State,
                                    to_s: State) -> None:
        """Check if condition is correct and run action."""
        nonlocal already_triggered
        template_result = condition.async_template(hass, template, variables)

        # Check to see if template returns true
        if template_result and not already_triggered:
            already_triggered = True
            hass.async_run_job(action, entity_id, from_s, to_s)
        elif not template_result:
            already_triggered = False

    return async_track_state_change(hass, template.extract_entities(variables),
                                    template_condition_listener)
Esempio n. 15
0
async def handle_render_template(hass, connection, msg):
    """Handle render_template command."""
    template_str = msg["template"]
    template = Template(template_str, hass)
    variables = msg.get("variables")
    timeout = msg.get("timeout")
    info = None

    if timeout:
        try:
            timed_out = await template.async_render_will_timeout(timeout)
        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, variables)],
            _template_listener,
            raise_on_template_error=True,
        )
    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)
Esempio n. 16
0
 def test_update_renders_value_in_template(self, mock_select, mock_socket):
     """Render the value in the provided template."""
     test_value = 'test_value'
     mock_socket = mock_socket().__enter__()
     mock_socket.recv.return_value = test_value.encode()
     config = copy(TEST_CONFIG['sensor'])
     config[tcp.CONF_VALUE_TEMPLATE] = Template('{{ value }} {{ 1+1 }}')
     sensor = tcp.TcpSensor(self.hass, config)
     assert sensor._state == '%s 2' % test_value
Esempio n. 17
0
    def test_wrong_date(self):
        """Test when start or end value is not a timestamp or a date."""
        good = Template("{{ now() }}", self.hass)
        bad = Template("{{ TEST }}", self.hass)

        sensor1 = HistoryStatsSensor(self.hass, "test", "on", good, bad, None,
                                     "time", "Test")
        sensor2 = HistoryStatsSensor(self.hass, "test", "on", bad, good, None,
                                     "time", "Test")

        before_update1 = sensor1._period
        before_update2 = sensor2._period

        sensor1.update_period()
        sensor2.update_period()

        assert before_update1 == sensor1._period
        assert before_update2 == sensor2._period
Esempio n. 18
0
def _setup_test_switch(hass):
    body_on = Template("on", hass)
    body_off = Template("off", hass)
    switch = rest.RestSwitch(
        NAME,
        RESOURCE,
        STATE_RESOURCE,
        METHOD,
        HEADERS,
        AUTH,
        body_on,
        body_off,
        None,
        10,
        True,
    )
    switch.hass = hass
    return switch, body_on, body_off
Esempio n. 19
0
    def test_wrong_date(self):
        """Test when start or end value is not a timestamp or a date."""
        good = Template('{{ now() }}', self.hass)
        bad = Template('{{ TEST }}', self.hass)

        sensor1 = HistoryStatsSensor(self.hass, 'test', 'on', good, bad, None,
                                     'time', 'Test')
        sensor2 = HistoryStatsSensor(self.hass, 'test', 'on', bad, good, None,
                                     'time', 'Test')

        before_update1 = sensor1._period
        before_update2 = sensor2._period

        sensor1.update_period()
        sensor2.update_period()

        assert before_update1 == sensor1._period
        assert before_update2 == sensor2._period
Esempio n. 20
0
    def test_template(self):
        """Test command sensor with template."""
        data = command_line.CommandSensorData('echo 50')

        entity = command_line.CommandSensor(
            self.hass, data, 'test', 'in',
            Template('{{ value | multiply(0.1) }}', self.hass))

        self.assertEqual(5, float(entity.state))
Esempio n. 21
0
async def test_extract_devices():
    """Test extracting devices."""
    assert (condition.async_extract_devices({
        "condition":
        "and",
        "conditions": [
            {
                "condition": "device",
                "device_id": "abcd",
                "domain": "light"
            },
            {
                "condition": "device",
                "device_id": "qwer",
                "domain": "switch"
            },
            {
                "condition": "state",
                "entity_id": "sensor.not_a_device",
                "state": "100",
            },
            {
                "condition":
                "not",
                "conditions": [
                    {
                        "condition": "device",
                        "device_id": "abcd_not",
                        "domain": "light",
                    },
                    {
                        "condition": "device",
                        "device_id": "qwer_not",
                        "domain": "switch",
                    },
                ],
            },
            {
                "condition":
                "or",
                "conditions": [
                    {
                        "condition": "device",
                        "device_id": "abcd_or",
                        "domain": "light",
                    },
                    {
                        "condition": "device",
                        "device_id": "qwer_or",
                        "domain": "switch",
                    },
                ],
            },
            Template("{{ is_state('light.example', 'on') }}"),
        ],
    }) == {"abcd", "qwer", "abcd_not", "qwer_not", "abcd_or", "qwer_or"})
Esempio n. 22
0
 def test_update_returns_if_template_render_fails(
         self, mock_select, mock_socket):
     """Return None if rendering the template fails."""
     test_value = 'test_value'
     mock_socket = mock_socket().__enter__()
     mock_socket.recv.return_value = test_value.encode()
     config = copy(TEST_CONFIG['sensor'])
     config[tcp.CONF_VALUE_TEMPLATE] = Template("{{ this won't work")
     sensor = tcp.TcpSensor(self.hass, config)
     assert sensor.update() is None
Esempio n. 23
0
def async_template(hass: HomeAssistant,
                   value_template: Template,
                   variables: TemplateVarsType = None) -> bool:
    """Test if template condition matches."""
    try:
        value: str = value_template.async_render(variables, parse_result=False)
    except TemplateError as ex:
        raise ConditionErrorMessage("template", str(ex)) from ex

    return value.lower() == "true"
Esempio n. 24
0
    def test_template(self):
        """Test command sensor with template."""
        data = command_line.CommandSensorData(self.hass, 'echo 50', 15)

        entity = command_line.CommandSensor(
            self.hass, data, 'test', 'in',
            Template('{{ value | multiply(0.1) }}', self.hass), [])

        entity.update()
        assert 5 == float(entity.state)
Esempio n. 25
0
def async_template(hass: HomeAssistant, value_template: Template,
                   variables: TemplateVarsType = None) -> bool:
    """Test if template condition matches."""
    try:
        value = value_template.async_render(variables)
    except TemplateError as ex:
        _LOGGER.error("Error during template condition: %s", ex)
        return False

    return value.lower() == 'true'
Esempio n. 26
0
def async_template(hass: HomeAssistant,
                   value_template: Template,
                   variables: TemplateVarsType = None) -> bool:
    """Test if template condition matches."""
    try:
        value = value_template.async_render(variables)
    except TemplateError as ex:
        _LOGGER.error("Error during template condition: %s", ex)
        return False

    return value.lower() == "true"
Esempio n. 27
0
def _setup_test_switch(hass):
    body_on = Template("on", hass)
    body_off = Template("off", hass)
    headers = {"Content-type": Template(CONTENT_TYPE_JSON, hass)}
    switch = rest.RestSwitch(
        NAME,
        DEVICE_CLASS,
        RESOURCE,
        STATE_RESOURCE,
        METHOD,
        headers,
        PARAMS,
        AUTH,
        body_on,
        body_off,
        None,
        10,
        True,
    )
    switch.hass = hass
    return switch, body_on, body_off
Esempio n. 28
0
 async def _valid_template(self, user_template):
     try:
         _LOGGER.debug(user_template)
         ut = Template(user_template, self.hass).async_render()
         if isinstance(ut, float):
             return True
         else:
             return False
     except Exception as e:
         _LOGGER.error(e)
         pass
     return False
Esempio n. 29
0
    def test_measure(self):
        """Test the history statistics sensor measure."""
        t0 = dt_util.utcnow() - timedelta(minutes=40)
        t1 = t0 + timedelta(minutes=20)
        t2 = dt_util.utcnow() - timedelta(minutes=10)

        # Start     t0        t1        t2        End
        # |--20min--|--20min--|--10min--|--10min--|
        # |---off---|---on----|---off---|---on----|

        fake_states = {
            'binary_sensor.test_id': [
                ha.State('binary_sensor.test_id', 'on', last_changed=t0),
                ha.State('binary_sensor.test_id', 'off', last_changed=t1),
                ha.State('binary_sensor.test_id', 'on', last_changed=t2),
            ]
        }

        start = Template('{{ as_timestamp(now()) - 3600 }}', self.hass)
        end = Template('{{ now() }}', self.hass)

        sensor1 = HistoryStatsSensor(self.hass, 'binary_sensor.test_id', 'on',
                                     start, end, None, 'Test')

        sensor2 = HistoryStatsSensor(self.hass, 'unknown.id', 'on', start, end,
                                     None, 'Test')

        with patch(
                'homeassistant.components.history.'
                'state_changes_during_period',
                return_value=fake_states):
            with patch('homeassistant.components.history.get_state',
                       return_value=None):
                sensor1.update()
                sensor2.update()

        self.assertEqual(round(sensor1.value, 3), 0.5)
        self.assertEqual(round(sensor2.value, 3), 0)
        self.assertEqual(sensor1.device_state_attributes['ratio'], '50.0%')
def rewrite_common_legacy_to_modern_conf(
        entity_cfg: dict[str, Any],
        extra_legacy_fields: dict[str, str] = None) -> dict[str, Any]:
    """Rewrite legacy config."""
    entity_cfg = {**entity_cfg}
    if extra_legacy_fields is None:
        extra_legacy_fields = {}

    for from_key, to_key in itertools.chain(LEGACY_FIELDS.items(),
                                            extra_legacy_fields.items()):
        if from_key not in entity_cfg or to_key in entity_cfg:
            continue

        val = entity_cfg.pop(from_key)
        if isinstance(val, str):
            val = Template(val)
        entity_cfg[to_key] = val

    if CONF_NAME in entity_cfg and isinstance(entity_cfg[CONF_NAME], str):
        entity_cfg[CONF_NAME] = Template(entity_cfg[CONF_NAME])

    return entity_cfg
Esempio n. 31
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))