Пример #1
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":
                tag_id = service_data["tag_id"]
                hass.async_create_task(
                    hass.components.tag.async_scan_tag(tag_id, device_id)
                )
                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
                )
            )
Пример #2
0
def rewrite_common_legacy_to_modern_conf(
        entity_cfg: dict[str, Any],
        extra_legacy_fields: dict[str, str] = None) -> list[dict]:
    """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

    return entity_cfg
Пример #3
0
    def test_bad_template(self):
        """Test Exception when the template cannot be parsed."""
        bad = Template("{{ x - 12 }}", self.hass)  # x is undefined
        duration = "01:00"

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

        before_update1 = sensor1._period
        before_update2 = sensor2._period

        sensor1.update_period()
        sensor2.update_period()

        assert before_update1 == sensor1._period
        assert before_update2 == sensor2._period
    def test_template(self):
        """Test value template."""
        test_message = email.message.Message()
        test_message['From'] = '*****@*****.**'
        test_message['Subject'] = 'Test'
        test_message['Date'] = datetime.datetime(2016, 1, 1, 12, 44, 57)
        test_message.set_payload("Test Message")

        sensor = imap_email_content.EmailContentSensor(
            self.hass, FakeEMailReader(deque([test_message])),
            'test_emails_sensor', ['*****@*****.**'],
            Template("{{ subject }} from {{ from }} with message {{ body }}",
                     self.hass))

        sensor.entity_id = 'sensor.emailtest'
        sensor.schedule_update_ha_state(True)
        self.hass.block_till_done()
        self.assertEqual(
            "Test from [email protected] with message Test Message",
            sensor.state)
Пример #5
0
    def test_period_parsing(self, mock):
        """Test the conversion from templates to period."""
        now = datetime(2019, 1, 1, 23, 30, 0, tzinfo=dt_util.UTC)
        with patch("homeassistant.util.dt.now", return_value=now):
            today = Template(
                "{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}",
                self.hass,
            )
            duration = timedelta(hours=2, minutes=1)

            sensor1 = HistoryStatsSensor("test", "on", today, None, duration,
                                         "time", "test")
            sensor1.hass = self.hass
            sensor2 = HistoryStatsSensor("test", "on", None, today, duration,
                                         "time", "test")
            sensor2.hass = self.hass

            sensor1.update_period()
            sensor1_start, sensor1_end = sensor1._period
            sensor2.update_period()
            sensor2_start, sensor2_end = sensor2._period

        # Start = 00:00:00
        assert sensor1_start.hour == 0
        assert sensor1_start.minute == 0
        assert sensor1_start.second == 0

        # End = 02:01:00
        assert sensor1_end.hour == 2
        assert sensor1_end.minute == 1
        assert sensor1_end.second == 0

        # Start = 21:59:00
        assert sensor2_start.hour == 21
        assert sensor2_start.minute == 59
        assert sensor2_start.second == 0

        # End = 00:00:00
        assert sensor2_end.hour == 0
        assert sensor2_end.minute == 0
        assert sensor2_end.second == 0
Пример #6
0
async def test_template(hass):
    """Test value template."""
    test_message = email.message.Message()
    test_message["From"] = "*****@*****.**"
    test_message["Subject"] = "Test"
    test_message["Date"] = datetime.datetime(2016, 1, 1, 12, 44, 57)
    test_message.set_payload("Test Message")

    sensor = imap_email_content.EmailContentSensor(
        hass,
        FakeEMailReader(deque([test_message])),
        "test_emails_sensor",
        ["*****@*****.**"],
        Template("{{ subject }} from {{ from }} with message {{ body }}",
                 hass),
    )

    sensor.entity_id = "sensor.emailtest"
    sensor.async_schedule_update_ha_state(True)
    await hass.async_block_till_done()
    assert sensor.state == "Test from [email protected] with message Test Message"
Пример #7
0
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
    """Setup roller shutter controlled by shell commands."""
    rollershutters = config.get('rollershutters', {})
    devices = []

    for dev_name, properties in rollershutters.items():
        value_template = properties.get(CONF_VALUE_TEMPLATE)

        if value_template is not None:
            value_template = Template(value_template, hass)

        devices.append(
            CommandRollershutter(
                hass,
                properties.get('name', dev_name),
                properties.get('upcmd', 'true'),
                properties.get('downcmd', 'true'),
                properties.get('stopcmd', 'true'),
                properties.get('statecmd', False),
                value_template))
    add_devices_callback(devices)
Пример #8
0
    def __init__(self, hass, config):
        """Set all the config values if they exist and get initial state."""
        value_template = config.get(CONF_VALUE_TEMPLATE)

        if value_template is not None:
            value_template = Template(value_template, hass)

        self._hass = hass
        self._config = {
            CONF_NAME: config.get(CONF_NAME),
            CONF_HOST: config.get(CONF_HOST),
            CONF_PORT: config.get(CONF_PORT),
            CONF_TIMEOUT: config.get(CONF_TIMEOUT),
            CONF_PAYLOAD: config.get(CONF_PAYLOAD),
            CONF_UNIT_OF_MEASUREMENT: config.get(CONF_UNIT_OF_MEASUREMENT),
            CONF_VALUE_TEMPLATE: value_template,
            CONF_VALUE_ON: config.get(CONF_VALUE_ON),
            CONF_BUFFER_SIZE: config.get(CONF_BUFFER_SIZE),
        }
        self._state = None
        self.update()
Пример #9
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', '{}.{}'.format(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
Пример #10
0
    def test_period_parsing(self):
        """Test the conversion from templates to period."""
        now = datetime(2019, 1, 1, 23, 30, 0, tzinfo=pytz.utc)
        with patch.dict(template.ENV.globals, {'now': lambda: now}):
            today = Template(
                '{{ now().replace(hour=0).replace(minute=0)'
                '.replace(second=0) }}', self.hass)
            duration = timedelta(hours=2, minutes=1)

            sensor1 = HistoryStatsSensor(self.hass, 'test', 'on', today, None,
                                         duration, 'time', 'test')
            sensor2 = HistoryStatsSensor(self.hass, 'test', 'on', None, today,
                                         duration, 'time', 'test')

            sensor1.update_period()
            sensor1_start, sensor1_end = sensor1._period
            sensor2.update_period()
            sensor2_start, sensor2_end = sensor2._period

        # Start = 00:00:00
        assert sensor1_start.hour == 0
        assert sensor1_start.minute == 0
        assert sensor1_start.second == 0

        # End = 02:01:00
        assert sensor1_end.hour == 2
        assert sensor1_end.minute == 1
        assert sensor1_end.second == 0

        # Start = 21:59:00
        assert sensor2_start.hour == 21
        assert sensor2_start.minute == 59
        assert sensor2_start.second == 0

        # End = 00:00:00
        assert sensor2_end.hour == 0
        assert sensor2_end.minute == 0
        assert sensor2_end.second == 0
Пример #11
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry,
                            async_add_entities: AddEntitiesCallback) -> None:
    """Set up the Scrape sensor entry."""
    name: str = entry.options[CONF_NAME]
    resource: str = entry.options[CONF_RESOURCE]
    select: str | None = entry.options.get(CONF_SELECT)
    attr: str | None = entry.options.get(CONF_ATTRIBUTE)
    index: int = int(entry.options[CONF_INDEX])
    unit: str | None = entry.options.get(CONF_UNIT_OF_MEASUREMENT)
    device_class: str | None = entry.options.get(CONF_DEVICE_CLASS)
    state_class: str | None = entry.options.get(CONF_STATE_CLASS)
    value_template: str | None = entry.options.get(CONF_VALUE_TEMPLATE)
    entry_id: str = entry.entry_id

    val_template: Template | None = None
    if value_template is not None:
        val_template = Template(value_template, hass)

    rest = hass.data[DOMAIN][entry.entry_id]

    async_add_entities(
        [
            ScrapeSensor(
                rest,
                name,
                select,
                attr,
                index,
                val_template,
                unit,
                device_class,
                state_class,
                entry_id,
                resource,
            )
        ],
        True,
    )
Пример #12
0
    def test_period_parsing(self):
        """Test the conversion from templates to period."""
        today = Template(
            '{{ now().replace(hour=0).replace(minute=0)'
            '.replace(second=0) }}', self.hass)
        duration = timedelta(hours=2, minutes=1)

        sensor1 = HistoryStatsSensor(self.hass, 'test', 'on', today, None,
                                     duration, 'test')
        sensor2 = HistoryStatsSensor(self.hass, 'test', 'on', None, today,
                                     duration, 'test')

        sensor1.update_period()
        sensor2.update_period()

        self.assertEqual(sensor1.device_state_attributes['from'][-8:],
                         '00:00:00')
        self.assertEqual(sensor1.device_state_attributes['to'][-8:],
                         '02:01:00')
        self.assertEqual(sensor2.device_state_attributes['from'][-8:],
                         '21:59:00')
        self.assertEqual(sensor2.device_state_attributes['to'][-8:],
                         '00:00:00')
Пример #13
0
    def async_on_service_call(service: 'ServiceCall') -> 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: %s', ex)
                return

        hass.async_create_task(
            hass.services.async_call(domain,
                                     service_name,
                                     service_data,
                                     blocking=True))
    def _render_template(self, filename, variables):
        try:
            template = None
            with open(filename, encoding="utf-8") as file:
                template = Template(file.read(), self.hass)

            template.ensure_valid()

            rendered = template.render(
                variables={
                    **variables, "_global": self.config.get("variables", {})
                },
                limited=True,
            )
            stream = io.StringIO(rendered)
            stream.name = filename
            return stream
        except (FileNotFoundError, UnicodeDecodeError) as ex:
            _LOGGER.error("Unable to read file %s: %s", filename, ex)
            raise HomeAssistantError(ex) from ex
        except TemplateError as ex:
            _LOGGER.error("Unable to render file %s: %s", filename, ex)
            raise HomeAssistantError(ex) from ex
Пример #15
0
    def test_period_parsing(self):
        """Test the conversion from templates to period."""
        today = Template(
            '{{ now().replace(hour=0).replace(minute=0)'
            '.replace(second=0) }}', self.hass)
        duration = timedelta(hours=2, minutes=1)

        sensor1 = HistoryStatsSensor(self.hass, 'test', 'on', today, None,
                                     duration, 'time', 'test')
        sensor2 = HistoryStatsSensor(self.hass, 'test', 'on', None, today,
                                     duration, 'time', 'test')

        sensor1.update_period()
        sensor1_start, sensor1_end = sensor1._period
        sensor2.update_period()
        sensor2_start, sensor2_end = sensor2._period

        # Start = 00:00:00
        self.assertEqual(sensor1_start.hour, 0)
        self.assertEqual(sensor1_start.minute, 0)
        self.assertEqual(sensor1_start.second, 0)

        # End = 02:01:00
        self.assertEqual(sensor1_end.hour, 2)
        self.assertEqual(sensor1_end.minute, 1)
        self.assertEqual(sensor1_end.second, 0)

        # Start = 21:59:00
        self.assertEqual(sensor2_start.hour, 21)
        self.assertEqual(sensor2_start.minute, 59)
        self.assertEqual(sensor2_start.second, 0)

        # End = 00:00:00
        self.assertEqual(sensor2_end.hour, 0)
        self.assertEqual(sensor2_end.minute, 0)
        self.assertEqual(sensor2_end.second, 0)
Пример #16
0
async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Web scrape sensor."""
    _LOGGER.warning(
        # Config flow added in Home Assistant Core 2022.7, remove import flow in 2022.9
        "Loading Scrape via platform setup has been deprecated in Home Assistant 2022.7 "
        "Your configuration has been automatically imported and you can "
        "remove it from your configuration.yaml")

    if config.get(CONF_VALUE_TEMPLATE):
        template: Template = Template(config[CONF_VALUE_TEMPLATE])
        template.ensure_valid()
        config[CONF_VALUE_TEMPLATE] = template.template

    hass.async_create_task(
        hass.config_entries.flow.async_init(
            DOMAIN,
            context={"source": SOURCE_IMPORT},
            data=config,
        ))
Пример #17
0
    else:
        entity_id = async_generate_entity_id(ENTITY_ID_FORMAT,
                                             DEFAULT_OBJECT_ID,
                                             hass=hass)
        notification_id = entity_id.split(".")[1]

    warn = False

    attr: dict[str, str] = {}
    if title is not None:
        if is_template_string(title):
            warn = True
            try:
                title = cast(
                    str,
                    Template(title, hass).async_render(
                        parse_result=False)  # type: ignore[no-untyped-call]
                )
            except TemplateError as ex:
                _LOGGER.error("Error rendering title %s: %s", title, ex)

        attr[ATTR_TITLE] = title
        attr[ATTR_FRIENDLY_NAME] = title

    if is_template_string(message):
        warn = True
        try:
            message = Template(message, hass).async_render(
                parse_result=False)  # type: ignore[no-untyped-call]
        except TemplateError as ex:
            _LOGGER.error("Error rendering message %s: %s", message, ex)
Пример #18
0
async def addBJFsensor(hostname, add_devices, hass):
    _LOGGER.info("addBJFsensor querying %s", hostname)

    #config = doQuery(hostname, "/json/config", True)
    config = await async_doQuery(hostname, "/json/config", True)

    if config != None:

        sensorsToAdd = []
        mac = config["mac"]

        # built early, in case it's shared
        url = "http://" + config["ip"] + "/json/state"
        rest = BJFRestData(hass,"GET", url, None, None, None)


        friendlyName = (
            config["friendlyName"] if "friendlyName" in config else config["name"]
        )


        # and add a datacoordinator
        coord = DataUpdateCoordinator(hass,_LOGGER,name=friendlyName+"_DUC", update_method=rest.async_update,update_interval=timedelta(seconds=30))

        await coord.async_config_entry_first_refresh()

        #await hass.async_add_executor_job(coord.async_config_entry_first_refresh)

        # asyncio.run_coroutine_threadsafe(
        #     coordinator.async_config_entry_first_refresh(), hass.loop
        #     ).result()

        # add a bunch of rest sensors
        if "sensorConfig" in config:
            for eachSensor in config["sensorConfig"]:
                for element in eachSensor["elements"]:
                    deviceClass = element["type"]
                    potential = None

                    # special case - if there's an instant sensor - PIR generally
                    if "impl" in element:

                        sensorValue = Template(
                            '{{ value_json["sensorState"]['
                            + str(eachSensor["sensor"])
                            + "].state"
                            + " }}",
                            hass,
                        )

                        _LOGGER.debug(sensorValue)

                        _LOGGER.info("Potential BJFBinarySensor")

                        potential = BJFBinarySensor(
                            hass,
                            coord,
                            element["impl"],
                            mac,
                            hostname,
                            rest,
                            # entity name
                            friendlyName+" "+eachSensor["name"] + " " + deviceClass,
                            deviceClass,
                            None,
                            sensorValue,
                            eachSensor["sensor"],
                            deviceClass,
                            config,
                        )

                    else:
                        _LOGGER.info("Potential BJFRestSensor")

                        # uom, round

                        uom = element["uom"] if "uom" in element else None
                        numDP = element["round"] if "round" in element else "0"


                        # build the template string
                        sensorValue = Template(
                            '{{ value_json["sensorState"]['
                            + str(eachSensor["sensor"])
                            + "]."
                            + deviceClass
                            + " | round("
                            + numDP
                            + ") }}",
                            hass,
                        )

                        _LOGGER.debug(sensorValue)

                        potential = BJFRestSensor(
                            hass,
                            coord,
                            mac,
                            hostname,
                            rest,
                            # entity name
                            friendlyName+" "+eachSensor["name"] + " " + deviceClass,
                            deviceClass,
                            uom,
                            sensorValue,
                            eachSensor["sensor"],
                            deviceClass,
                            config,
                        )

                    if potential is not None:
                        _LOGGER.info("Adding sensor %s", potential._unique_id)
                        sensorsToAdd.append(potential)

            add_devices(sensorsToAdd)

        return True
    else:
        _LOGGER.error("Failed to query %s", hostname)

    return False
Пример #19
0
     },
     'input_boolean': {
         'ct_switch': {
             'name': '启用/暂停',
             'initial': False,
             'icon': 'mdi:switch',
             'use_for': 'switch'
         }
     },
     'sensor': [{
         'platform': 'template',
         'entity_namespace': "common_timer",
         'sensors': {
             'ct_record_0': {
                 'friendly_name': "无定时任务",
                 'value_template': Template("-"),
                 'icon_template': Template("mdi:calendar-check")
             }
         }
     }]
 },
 # 'domains': ['light', 'switch', 'automation', 'script', 'input_boolean'],
 # 'exclude': [],
 # 'pattern': '[\u4e00-\u9fa5]+',
 # 'name': 'ct_control_panel',
 # 'friendly_name': '通用定时器',
 # 'info_panel': {
 #     'name': 'ct_info_panel',
 #     'friendly_name': '定时任务列表',
 #     'info_num_min': 1,
 #     'info_num_max': 10,
Пример #20
0
    def update_info(self):
        """update info and refresh info panel."""
        info_config = self._info_config
        if info_config is None:
            return
        _LOGGER.debug("↓↓↓↓↓_update_info()↓↓↓↓↓")
        running_tasks = self._get_running_tasks()
        self._running_tasks_ids = [
            entity['entity_id'] for entity in running_tasks
        ]
        info_row_num = len(running_tasks) if len(running_tasks) < info_config[
            CONF_INFO_NUM_MAX] else info_config[CONF_INFO_NUM_MAX]
        new_rows = []
        info_ui = []
        default_state = Template('-')
        default_state.hass = self._hass
        default_icon = Template('mdi:calendar-check')
        default_icon.hass = self._hass
        # refresh every row
        for row in range(0, info_config[CONF_INFO_NUM_MAX]):
            info_entity_id = 'sensor.ct_record_{}'.format(row)
            info_entity = self._hass.data['sensor'].get_entity(info_entity_id)
            # rows show record
            if row < info_row_num:
                _LOGGER.debug("info_entity:%s, row=%s", info_entity, row)
                # info1 = '{0:{2}<12}{1:{2}>20}'.format(running_tasks[row]['friendly_name'], running_tasks[row]['exec_time'].strftime("%Y-%m-%d %H:%M:%S"),chr(12288))  # for test
                info1 = '{}{}'.format(
                    align(running_tasks[row]['friendly_name'], 20),
                    align(
                        running_tasks[row]['exec_time'].strftime(
                            "%Y-%m-%d %H:%M:%S"),
                        20))  # name+time info template
                loop_flag = CONF_LOOP_FLAG if 'temporary' in running_tasks[
                    row]['operation'] else ''
                info2 = Template('{} {} → {}'.format(
                    loop_flag, self.get_state(running_tasks[row]['entity_id']),
                    running_tasks[row]
                    ['next_operation']))  # operation info template
                info2.hass = self._hass
                # info3 = Template('{{{{states.{}.{}.{}}}}}'.format(running_tasks[row]['entity_id'] ,'attributes' ,'icon'))  # for test
                info3 = Template(running_tasks[row]['icon'])  # icon template
                info3.hass = self._hass
                # row has record, update
                if info_entity is not None:
                    _LOGGER.debug("row%s, record exist. <info_entity_id= %s >",
                                  row, info_entity_id)
                    info_entity._name = info1
                    info_entity._template = info2
                    info_entity._icon_template = info3
                    info_entity.schedule_update_ha_state(
                        True
                    )  # force_refresh = True to call device_update to update sensor.template
                # row has record, add
                else:
                    _LOGGER.debug(
                        "row%s, no record. <info_entity_id = %s, state = %s>",
                        row, info_entity_id,
                        self._dic_operation.get(
                            running_tasks[row]['operation']))
                    object_id = 'ct_record_{}'.format(row)
                    sensor = SensorTemplate(hass=self._hass,
                                            device_id=object_id,
                                            friendly_name=info1,
                                            friendly_name_template=None,
                                            unit_of_measurement=None,
                                            state_template=info2,
                                            icon_template=info3,
                                            entity_picture_template=None,
                                            entity_ids=set(),
                                            device_class=None)
                    new_rows.append(sensor)
                info_ui.append(info_entity_id)
            # rows show blank or should be remove
            else:
                if not any([
                        info_row_num, row
                ]) or row < info_config[CONF_INFO_NUM_MIN] or info_config[
                        CONF_INFO_NUM_MAX] == info_config[CONF_INFO_NUM_MIN]:
                    info1 = '无定时任务'
                    info_entity._name = info1
                    info_entity._template = default_state
                    info_entity._icon_template = default_icon
                    info_entity.schedule_update_ha_state(
                        True
                    )  # force_refresh = True to call device_update to update sensor.template
                    info_ui.append(info_entity_id)
                else:
                    yield from self._hass.data['sensor'].async_remove_entity(
                        info_entity_id)
        if new_rows:
            yield from self._hass.data['sensor']._platforms[
                PLATFORM_KEY].async_add_entities(new_rows,
                                                 update_before_add=True)

        data = {
            ATTR_OBJECT_ID: info_config[CONF_NAME],
            ATTR_NAME: info_config[CONF_FRIENDLY_NAME],
            ATTR_ENTITIES: [entity_id for entity_id in info_ui]
        }
        yield from self._hass.services.async_call('group', SERVICE_SET, data)
        _LOGGER.debug("↑↑↑↑↑_update_info()↑↑↑↑↑")
Пример #21
0
def async_setup(hass, config):
    _LOGGER.debug("-------%s--------", config[DOMAIN])
    """ setup up common_timer component """
    ui = {}  #save params of input components for getting input

    VALIDATED_CONF = BUILT_IN_CONFIG[CONF_UI]
    info_ui = []
    #remove CONF_USE_FOR from BUILT_IN_CONFIG, otherwise raise a validate failure
    for domain in VALIDATED_CONF:
        if isinstance(VALIDATED_CONF[domain], list):
            for object_id in VALIDATED_CONF[domain][0]['sensors']:
                info_ui.append('{}.{}'.format(domain, object_id))
        else:
            for object_id in VALIDATED_CONF[domain]:
                if CONF_USE_FOR in VALIDATED_CONF[domain][object_id]:
                    user_for = VALIDATED_CONF[domain][object_id][CONF_USE_FOR]
                    ui[user_for] = '{}.{}'.format(domain, object_id)
                    VALIDATED_CONF[domain][object_id].pop(CONF_USE_FOR)

    components = set(key.split(' ')[0] for key in config.keys())
    for setup_domain in [
            'input_select', 'input_text', 'input_boolean', 'sensor'
    ]:
        #config file contains info, let HA initailize
        if setup_domain in components:
            _LOGGER.debug(
                'initialize component[%s]: config has this component',
                setup_domain)
            #wait for HA initialize component
            #maybe it can use discovery.discover(hass, service='load_component.{}'.format(setup_domain), discovered={}, component=setup_domain, hass_config=config) instead
            while setup_domain not in hass.config.components:
                yield from asyncio.sleep(1)
                _LOGGER.debug(
                    "initialize component[%s]: wait for HA initialization.",
                    setup_domain)

            if setup_domain in ['input_select', 'input_text',
                                'input_boolean']:  #entity belongs to component
                _LOGGER.debug(
                    "initialize component[%s]: component is ready, use component's method.",
                    setup_domain)
                #add entity in component
                entities = []
                for object_id, conf in VALIDATED_CONF.get(setup_domain,
                                                          {}).items():
                    # _LOGGER.debug("setup %s.%s", setup_domain, object_id)
                    if setup_domain == 'input_select':
                        # InputSelect(object_id, name, initial, options, icon)
                        entity = InputSelect(object_id,
                                             conf.get(CONF_NAME, object_id),
                                             conf.get(CONF_INITIAL),
                                             conf.get(CONF_OPTIONS) or [],
                                             conf.get(CONF_ICON))
                    elif setup_domain == 'input_text':
                        # InputText(object_id, name, initial, minimum, maximum, icon, unit, pattern, mode)
                        entity = InputText(object_id,
                                           conf.get(CONF_NAME, object_id),
                                           conf.get(CONF_INITIAL),
                                           conf.get(CONF_MIN),
                                           conf.get(CONF_MAX),
                                           conf.get(CONF_ICON),
                                           conf.get(ATTR_UNIT_OF_MEASUREMENT),
                                           conf.get(CONF_PATTERN),
                                           conf.get(CONF_MODE))
                    elif setup_domain == 'input_boolean':
                        # InputBoolean(object_id, name, initial, icon)
                        entity = InputBoolean(object_id, conf.get(CONF_NAME),
                                              conf.get(CONF_INITIAL),
                                              conf.get(CONF_ICON))
                        _LOGGER.debug("input_boolean.timer_button:%s,%s,%s,%s",
                                      object_id, conf.get(CONF_NAME),
                                      conf.get(CONF_INITIAL),
                                      conf.get(CONF_ICON))
                    else:
                        pass
                        # _LOGGER.debug("illegal component:%s", object_id, conf.get(CONF_NAME), conf.get(CONF_INITIAL), conf.get(CONF_ICON))
                    entities.append(entity)
                # _LOGGER.debug("entities:%s", entities)
                yield from hass.data[setup_domain].async_add_entities(entities)
                _LOGGER.debug('initialize component[%s]: entities added.',
                              setup_domain)
            elif setup_domain in ['sensor'
                                  ]:  #entity belongs to component.platform
                _LOGGER.debug(
                    "initialize component.platform[%s]: component is ready, use EntityComponent's method to initialize entity.",
                    setup_domain)
                # should set a unique namespace to ensure it's a new platform and don't affect other entities using template platform which have been initialized.
                yield from hass.data[setup_domain].async_setup(
                    {setup_domain: VALIDATED_CONF.get(setup_domain, {})})
            else:
                _LOGGER.debug(
                    "initialize component[%s]: undefined initialize method.",
                    setup_domain)

        #add config for HA to initailize
        else:
            _LOGGER.debug(
                'initialize component[%s]: config hasn\'t this componet , use HA\'s setup method to initialize entity.',
                setup_domain)
            hass.async_create_task(
                setup.async_setup_component(hass, setup_domain,
                                            VALIDATED_CONF))

    #add group through service since HA initialize group by defalut
    data = {
        ATTR_OBJECT_ID: config[DOMAIN][CONF_NAME],
        ATTR_NAME: config[DOMAIN][CONF_FRIENDLY_NAME],
        ATTR_ENTITIES: [entity_id for param, entity_id in ui.items()]
    }
    # data[ATTR_ENTITIES].append('timer.laundry')
    yield from hass.services.async_call('group', SERVICE_SET, data)
    # hass.async_add_job(hass.services.async_call('group', SERVICE_SET, data))
    _LOGGER.debug('---control planel initialized---')

    #info panel inital
    info_config = config[DOMAIN].get(CONF_INFO_PANEL)
    if info_config:
        entities = []
        for num in range(1, info_config[CONF_INFO_NUM_MIN]):
            object_id = 'ct_record_{}'.format(num)
            state_template = Template('-')
            state_template.hass = hass
            icon_template = Template('mdi:calendar-check')
            icon_template.hass = hass
            entity = SensorTemplate(hass=hass,
                                    device_id=object_id,
                                    friendly_name='无定时任务',
                                    friendly_name_template=None,
                                    unit_of_measurement=None,
                                    state_template=state_template,
                                    icon_template=icon_template,
                                    entity_picture_template=None,
                                    entity_ids=set(),
                                    device_class=None)

            entities.append(entity)
            info_ui.append(entity.entity_id)
        yield from hass.data['sensor']._platforms[
            PLATFORM_KEY].async_add_entities(entities)
        data = {
            ATTR_OBJECT_ID: info_config[CONF_NAME],
            ATTR_NAME: info_config[CONF_FRIENDLY_NAME],
            ATTR_ENTITIES: [entity_id for entity_id in info_ui]
        }
        yield from hass.services.async_call('group', SERVICE_SET, data)
        _LOGGER.debug('---info planel initialized---')

    domains = config[DOMAIN].get(CONF_DOMAINS)
    exclude = config[DOMAIN].get(CONF_EXCLUDE)
    pattern = config[DOMAIN].get(CONF_PATTERN)
    ratio = config[DOMAIN].get(CONF_RATIO)
    exclude.append(ui['switch'])  # ignore ui input_boolean

    @callback
    def start_common_timer(event):
        """ initialize common_timer. """
        _LOGGER.debug('start initialize common_timer.')
        common_timer = CommonTimer(domains, exclude, pattern, ratio, ui, hass,
                                   info_config)

        @callback
        def common_timer_handle(event):
            """Listen for state changed events and refresh ui. """
            if event.data[ATTR_ENTITY_ID] == ui[UI_INPUT_DOMAIN]:
                # _LOGGER.debug('set domain from %s to %s',event.data['old_state'].as_dict()['state'] ,event.data['new_state'].as_dict()['state'])
                common_timer.choose_domain(
                    event.data['new_state'].as_dict()['state'])
            elif event.data[ATTR_ENTITY_ID] == ui[UI_INPUT_ENTITY]:
                # _LOGGER.debug('set entity')
                common_timer.choose_entity(
                    event.data['new_state'].as_dict()['state'])
            elif event.data[ATTR_ENTITY_ID] == ui[UI_INPUT_OPERATION]:
                # _LOGGER.debug('set operation')
                common_timer.choose_operation(
                    event.data['new_state'].as_dict()['state'])
            elif event.data[ATTR_ENTITY_ID] == ui[UI_INPUT_DURATION]:
                pass
                # _LOGGER.debug('set time')
                # common_timer.input_duration(event.data['new_state'].as_dict()['state'])
            elif event.data[ATTR_ENTITY_ID] == ui[UI_SWITCH]:
                # _LOGGER.debug('start/stop')
                common_timer.switch(event.data['new_state'].as_dict()['state'])
            else:
                # _LOGGER.debug('start/stop')
                if common_timer.stop_loop_task(event.data[ATTR_ENTITY_ID],
                                               context=event.context):
                    hass.async_add_job(common_timer.update_info)

        hass.bus.async_listen(EVENT_STATE_CHANGED, common_timer_handle)

        @asyncio.coroutine
        def async_handler_service(service):
            """ Handle calls to the common timer services. """
            entity_id = service.data[ATTR_ENTITY_ID]
            duration = str(service.data[ATTR_DURATION])
            operation = service.data[ATTR_OPERATION]
            is_loop = service.data[ATTR_IS_LOOP]
            if service.service == SERVICE_SET:
                common_timer.set_task(entity_id, operation, duration, is_loop)
                pass
            elif service.service == SERVICE_CANCEL:
                common_timer.cancel_task(entity_id)
                pass

        hass.services.async_register(DOMAIN,
                                     SERVICE_SET,
                                     async_handler_service,
                                     schema=COMMON_TIMER_SERVICE_SCHEMA)
        hass.services.async_register(DOMAIN,
                                     SERVICE_CANCEL,
                                     async_handler_service,
                                     schema=COMMON_TIMER_SERVICE_SCHEMA)

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_common_timer)

    # for test
    # @callback
    # def service_executed(event):
    #     if event.context == CONTEXT:
    #         _LOGGER.debug("-----common_timer调用服务完毕!-----context = %s", CONTEXT)
    # hass.bus.async_listen(EVENT_SERVICE_EXECUTED, service_executed)

    return True
Пример #22
0
from tests.common import (get_test_home_assistant, assert_setup_component)
from homeassistant.bootstrap import setup_component
from homeassistant.components.sensor import tcp
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.template import Template

TEST_CONFIG = {
    'sensor': {
        'platform': 'tcp',
        tcp.CONF_NAME: 'test_name',
        tcp.CONF_HOST: 'test_host',
        tcp.CONF_PORT: 12345,
        tcp.CONF_TIMEOUT: tcp.DEFAULT_TIMEOUT + 1,
        tcp.CONF_PAYLOAD: 'test_payload',
        tcp.CONF_UNIT_OF_MEASUREMENT: 'test_unit',
        tcp.CONF_VALUE_TEMPLATE: Template('test_template'),
        tcp.CONF_VALUE_ON: 'test_on',
        tcp.CONF_BUFFER_SIZE: tcp.DEFAULT_BUFFER_SIZE + 1
    },
}

KEYS_AND_DEFAULTS = {
    tcp.CONF_TIMEOUT: tcp.DEFAULT_TIMEOUT,
    tcp.CONF_UNIT_OF_MEASUREMENT: None,
    tcp.CONF_VALUE_TEMPLATE: None,
    tcp.CONF_VALUE_ON: None,
    tcp.CONF_BUFFER_SIZE: tcp.DEFAULT_BUFFER_SIZE
}


class TestTCPSensor(unittest.TestCase):
Пример #23
0
    async def async_play_media(self,
                               media_type: str,
                               media_id: str,
                               extra: dict = None,
                               **kwargs):
        # backward support Hass lower than v2022.3
        if '/api/tts_proxy/' in media_id:
            session = async_get_clientsession(self.hass)
            media_id = await utils.get_tts_message(session, media_id)
            media_type = 'tts'

        if media_id.startswith("media-source://tts/"):
            query = utils.decode_media_source(media_id)
            if query.get("template"):
                template = Template(query.pop("template"), self.hass)
                media_id = template.async_render(query)
            else:
                media_id = query["message"]
            if query.get("volume_level"):
                extra.setdefault("volume_level", float(query["volume_level"]))
            # provider, music - from 3rd party TTS (ex google)
            if media_type in ("provider", "music"):
                media_type = "text"

        if not media_id:
            _LOGGER.warning(f"Получено пустое media_id")
            return

        # tts for backward compatibility
        if media_type == "tts":
            media_type = "text"
        elif media_type == 'brightness':
            await self._set_brightness(media_id)
            return
        elif media_type == 'beta':
            await self._set_beta(media_id)
            return
        elif media_type == 'settings':
            await self._set_settings(media_id)
            return

        if self.local_state:
            if 'https://' in media_id or 'http://' in media_id:
                session = async_get_clientsession(self.hass)
                payload = await utils.get_media_payload(media_id, session)
                if not payload:
                    _LOGGER.warning(f"Unsupported url: {media_id}")
                    return

            elif media_type.startswith(("text:", "dialog:")):
                payload = {
                    'command': 'sendText',
                    'text': self.yandex_dialog(media_type, media_id)
                }

            elif media_type == 'text':
                # даже в локальном режиме делам TTS через облако, чтоб колонка
                # не продолжала слушать
                force_local: bool = extra and extra.get("force_local")
                if self.quasar.session.x_token and not force_local:
                    media_id = utils.fix_cloud_text(media_id)
                    if extra and extra.get("volume_level") is not None:
                        self._check_set_alice_volume(extra["volume_level"])
                    await self.quasar.send(self.device, media_id, is_tts=True)
                    return

                else:
                    payload = {
                        'command': 'sendText',
                        'text': f"Повтори за мной '{media_id}'"
                    }

            elif media_type == 'command':
                payload = {'command': 'sendText', 'text': media_id}

            elif media_type == 'dialog':
                if extra and extra.get("volume_level") is not None:
                    self._check_set_alice_volume(extra["volume_level"])
                payload = utils.update_form(
                    'personal_assistant.scenarios.repeat_after_me',
                    request=media_id)

            elif media_type == 'json':
                payload = json.loads(media_id)

            elif RE_MUSIC_ID.match(media_id):
                payload = {
                    'command': 'playMusic',
                    'id': media_id,
                    'type': media_type
                }

            elif media_type == 'shopping_list':
                await self._shopping_list()
                return

            elif media_type.startswith('question'):
                request_id = (media_type.split(':', 1)[1]
                              if ':' in media_type else None)
                card = await self.glagol.send({
                    'command': 'sendText',
                    'text': media_id
                })
                await self.response(card, request_id)
                return

            else:
                _LOGGER.warning(f"Unsupported local media: {media_id}")
                return

            await self.glagol.send(payload)

        else:
            if media_type.startswith(("text:", "dialog:")):
                media_id = self.yandex_dialog(media_type, media_id)
                await self.quasar.send(self.device, media_id)

            elif media_type == 'text':
                media_id = utils.fix_cloud_text(media_id)
                await self.quasar.send(self.device, media_id, is_tts=True)

            elif media_type == 'command':
                media_id = utils.fix_cloud_text(media_id)
                await self.quasar.send(self.device, media_id)

            elif media_type == 'brightness':
                await self._set_brightness(media_id)
                return

            else:
                _LOGGER.warning(f"Unsupported cloud media: {media_type}")
                return
Пример #24
0
from homeassistant.helpers.template import Template
from homeassistant.setup import setup_component

from tests.async_mock import Mock, patch
from tests.common import assert_setup_component, get_test_home_assistant

TEST_CONFIG = {
    "sensor": {
        "platform": "tcp",
        tcp.CONF_NAME: "test_name",
        tcp.CONF_HOST: "test_host",
        tcp.CONF_PORT: 12345,
        tcp.CONF_TIMEOUT: tcp.DEFAULT_TIMEOUT + 1,
        tcp.CONF_PAYLOAD: "test_payload",
        tcp.CONF_UNIT_OF_MEASUREMENT: "test_unit",
        tcp.CONF_VALUE_TEMPLATE: Template("test_template"),
        tcp.CONF_VALUE_ON: "test_on",
        tcp.CONF_BUFFER_SIZE: tcp.DEFAULT_BUFFER_SIZE + 1,
    }
}

KEYS_AND_DEFAULTS = {
    tcp.CONF_TIMEOUT: tcp.DEFAULT_TIMEOUT,
    tcp.CONF_UNIT_OF_MEASUREMENT: None,
    tcp.CONF_VALUE_TEMPLATE: None,
    tcp.CONF_VALUE_ON: None,
    tcp.CONF_BUFFER_SIZE: tcp.DEFAULT_BUFFER_SIZE,
}


class TestTCPSensor(unittest.TestCase):
Пример #25
0
    def test_measure_multiple(self):
        """Test the history statistics sensor measure for multiple states."""
        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--|
        # |---------|--orange-|-default-|---blue--|

        fake_states = {
            "input_select.test_id": [
                ha.State("input_select.test_id", "orange", last_changed=t0),
                ha.State("input_select.test_id", "default", last_changed=t1),
                ha.State("input_select.test_id", "blue", last_changed=t2),
            ]
        }

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

        sensor1 = HistoryStatsSensor(
            self.hass,
            "input_select.test_id",
            ["orange", "blue"],
            start,
            end,
            None,
            "time",
            "Test",
        )

        sensor2 = HistoryStatsSensor(
            self.hass,
            "unknown.id",
            ["orange", "blue"],
            start,
            end,
            None,
            "time",
            "Test",
        )

        sensor3 = HistoryStatsSensor(
            self.hass,
            "input_select.test_id",
            ["orange", "blue"],
            start,
            end,
            None,
            "count",
            "test",
        )

        sensor4 = HistoryStatsSensor(
            self.hass,
            "input_select.test_id",
            ["orange", "blue"],
            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,
        ), 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
Пример #26
0
    """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():
        if (state := hass.states.get(entity_id)) is None:
            _LOGGER.debug("Entity not found: %s", entity_id)
            continue

        if entity := entity_registry.async_get(entity_id):
            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
        else:
            _LOGGER.debug("No power value defined for: %s", entity_id)


def get_system_unique_id(entity: RegistryEntry):
Пример #27
0
import aiohttp
import async_timeout
import voluptuous as vol

from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_NAME, CONF_RESOURCE, CONF_TIMEOUT)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.template import Template

CONF_BODY_OFF = 'body_off'
CONF_BODY_ON = 'body_on'
CONF_IS_ON_TEMPLATE = 'is_on_template'

DEFAULT_BODY_OFF = Template('OFF')
DEFAULT_BODY_ON = Template('ON')
DEFAULT_NAME = 'REST Switch'
DEFAULT_TIMEOUT = 10

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_RESOURCE): cv.url,
    vol.Optional(CONF_BODY_OFF, default=DEFAULT_BODY_OFF): cv.template,
    vol.Optional(CONF_BODY_ON, default=DEFAULT_BODY_ON): cv.template,
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Optional(CONF_IS_ON_TEMPLATE): cv.template,
    vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
})

_LOGGER = logging.getLogger(__name__)
Пример #28
0
async def test_track_template(hass):
    """Test tracking template."""
    specific_runs = []
    wildcard_runs = []
    wildercard_runs = []

    template_condition = Template("{{states.switch.test.state == 'on'}}", hass)
    template_condition_var = Template(
        "{{states.switch.test.state == 'on' and test == 5}}", hass)

    hass.states.async_set("switch.test", "off")

    def specific_run_callback(entity_id, old_state, new_state):
        specific_runs.append(1)

    async_track_template(hass, template_condition, specific_run_callback)

    @ha.callback
    def wildcard_run_callback(entity_id, old_state, new_state):
        wildcard_runs.append((old_state, new_state))

    async_track_template(hass, template_condition, wildcard_run_callback)

    @asyncio.coroutine
    def wildercard_run_callback(entity_id, old_state, new_state):
        wildercard_runs.append((old_state, new_state))

    async_track_template(hass, template_condition_var, wildercard_run_callback,
                         {"test": 5})

    hass.states.async_set("switch.test", "on")
    await hass.async_block_till_done()

    assert len(specific_runs) == 1
    assert len(wildcard_runs) == 1
    assert len(wildercard_runs) == 1

    hass.states.async_set("switch.test", "on")
    await hass.async_block_till_done()

    assert len(specific_runs) == 1
    assert len(wildcard_runs) == 1
    assert len(wildercard_runs) == 1

    hass.states.async_set("switch.test", "off")
    await hass.async_block_till_done()

    assert len(specific_runs) == 1
    assert len(wildcard_runs) == 1
    assert len(wildercard_runs) == 1

    hass.states.async_set("switch.test", "off")
    await hass.async_block_till_done()

    assert len(specific_runs) == 1
    assert len(wildcard_runs) == 1
    assert len(wildercard_runs) == 1

    hass.states.async_set("switch.test", "on")
    await hass.async_block_till_done()

    assert len(specific_runs) == 2
    assert len(wildcard_runs) == 2
    assert len(wildercard_runs) == 2
Пример #29
0
async def test_extract_entities():
    """Test extracting entities."""
    assert condition.async_extract_entities(
        {
            "condition": "and",
            "conditions": [
                {
                    "condition": "state",
                    "entity_id": "sensor.temperature",
                    "state": "100",
                },
                {
                    "condition": "numeric_state",
                    "entity_id": "sensor.temperature_2",
                    "below": 110,
                },
                {
                    "condition": "not",
                    "conditions": [
                        {
                            "condition": "state",
                            "entity_id": "sensor.temperature_3",
                            "state": "100",
                        },
                        {
                            "condition": "numeric_state",
                            "entity_id": "sensor.temperature_4",
                            "below": 110,
                        },
                    ],
                },
                {
                    "condition": "or",
                    "conditions": [
                        {
                            "condition": "state",
                            "entity_id": "sensor.temperature_5",
                            "state": "100",
                        },
                        {
                            "condition": "numeric_state",
                            "entity_id": "sensor.temperature_6",
                            "below": 110,
                        },
                    ],
                },
                {
                    "condition": "state",
                    "entity_id": ["sensor.temperature_7", "sensor.temperature_8"],
                    "state": "100",
                },
                {
                    "condition": "numeric_state",
                    "entity_id": ["sensor.temperature_9", "sensor.temperature_10"],
                    "below": 110,
                },
                Template("{{ is_state('light.example', 'on') }}"),
            ],
        }
    ) == {
        "sensor.temperature",
        "sensor.temperature_2",
        "sensor.temperature_3",
        "sensor.temperature_4",
        "sensor.temperature_5",
        "sensor.temperature_6",
        "sensor.temperature_7",
        "sensor.temperature_8",
        "sensor.temperature_9",
        "sensor.temperature_10",
    }
    def test_track_template(self):
        """Test tracking template."""
        specific_runs = []
        wildcard_runs = []
        wildercard_runs = []

        template_condition = Template(
            "{{states.switch.test.state == 'on'}}",
            self.hass
        )
        template_condition_var = Template(
            "{{states.switch.test.state == 'on' and test == 5}}",
            self.hass
        )

        self.hass.states.set('switch.test', 'off')

        def specific_run_callback(entity_id, old_state, new_state):
            specific_runs.append(1)

        track_template(self.hass, template_condition, specific_run_callback)

        @ha.callback
        def wildcard_run_callback(entity_id, old_state, new_state):
            wildcard_runs.append((old_state, new_state))

        track_template(self.hass, template_condition, wildcard_run_callback)

        @asyncio.coroutine
        def wildercard_run_callback(entity_id, old_state, new_state):
            wildercard_runs.append((old_state, new_state))

        track_template(
            self.hass, template_condition_var, wildercard_run_callback,
            {'test': 5})

        self.hass.states.set('switch.test', 'on')
        self.hass.block_till_done()

        self.assertEqual(1, len(specific_runs))
        self.assertEqual(1, len(wildcard_runs))
        self.assertEqual(1, len(wildercard_runs))

        self.hass.states.set('switch.test', 'on')
        self.hass.block_till_done()

        self.assertEqual(1, len(specific_runs))
        self.assertEqual(1, len(wildcard_runs))
        self.assertEqual(1, len(wildercard_runs))

        self.hass.states.set('switch.test', 'off')
        self.hass.block_till_done()

        self.assertEqual(1, len(specific_runs))
        self.assertEqual(1, len(wildcard_runs))
        self.assertEqual(1, len(wildercard_runs))

        self.hass.states.set('switch.test', 'off')
        self.hass.block_till_done()

        self.assertEqual(1, len(specific_runs))
        self.assertEqual(1, len(wildcard_runs))
        self.assertEqual(1, len(wildercard_runs))

        self.hass.states.set('switch.test', 'on')
        self.hass.block_till_done()

        self.assertEqual(2, len(specific_runs))
        self.assertEqual(2, len(wildcard_runs))
        self.assertEqual(2, len(wildercard_runs))