Add default config if not there (#43321)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Paulus Schoutsen 2020-11-25 15:10:04 +01:00 committed by GitHub
parent 0cf3736162
commit b3be708db6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 432 additions and 21 deletions

View file

@ -166,7 +166,8 @@ async def async_setup(hass, config):
# To register the automation blueprints # To register the automation blueprints
async_get_blueprints(hass) async_get_blueprints(hass)
await _async_process_config(hass, config, component) if not await _async_process_config(hass, config, component):
await async_get_blueprints(hass).async_populate()
async def trigger_service_handler(entity, service_call): async def trigger_service_handler(entity, service_call):
"""Handle automation triggers.""" """Handle automation triggers."""
@ -487,12 +488,13 @@ async def _async_process_config(
hass: HomeAssistant, hass: HomeAssistant,
config: Dict[str, Any], config: Dict[str, Any],
component: EntityComponent, component: EntityComponent,
) -> None: ) -> bool:
"""Process config and add automations. """Process config and add automations.
This method is a coroutine. Returns if blueprints were used.
""" """
entities = [] entities = []
blueprints_used = False
for config_key in extract_domain_configs(config, DOMAIN): for config_key in extract_domain_configs(config, DOMAIN):
conf: List[Union[Dict[str, Any], blueprint.BlueprintInputs]] = config[ # type: ignore conf: List[Union[Dict[str, Any], blueprint.BlueprintInputs]] = config[ # type: ignore
@ -501,6 +503,7 @@ async def _async_process_config(
for list_no, config_block in enumerate(conf): for list_no, config_block in enumerate(conf):
if isinstance(config_block, blueprint.BlueprintInputs): # type: ignore if isinstance(config_block, blueprint.BlueprintInputs): # type: ignore
blueprints_used = True
blueprint_inputs = config_block blueprint_inputs = config_block
try: try:
@ -562,6 +565,8 @@ async def _async_process_config(
if entities: if entities:
await component.async_add_entities(entities) await component.async_add_entities(entities)
return blueprints_used
async def _async_process_if(hass, config, p_config): async def _async_process_if(hass, config, p_config):
"""Process if checks.""" """Process if checks."""

View file

@ -0,0 +1,39 @@
blueprint:
name: Motion-activated Light
domain: automation
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml
input:
motion_entity:
name: Motion Sensor
selector:
entity:
domain: binary_sensor
device_class: motion
light_entity:
name: Light
selector:
entity:
domain: light
# If motion is detected within the 120s delay,
# we restart the script.
mode: restart
max_exceeded: silent
trigger:
platform: state
entity_id: !placeholder motion_entity
from: "off"
to: "on"
action:
- service: homeassistant.turn_on
entity_id: !placeholder light_entity
- wait_for_trigger:
platform: state
entity_id: !placeholder motion_entity
from: "on"
to: "off"
- delay: 120
- service: homeassistant.turn_off
entity_id: !placeholder light_entity

View file

@ -0,0 +1,34 @@
blueprint:
name: Send notification when a person leaves a zone
domain: automation
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/notify_leaving_zone.yaml
input:
person_entity:
name: Person
selector:
entity:
domain: person
zone_entity:
name: Zone
selector:
entity:
domain: zone
notify_service:
name: The notify service to use
trigger:
platform: state
entity_id: !placeholder person_entity
variables:
zone_entity: !placeholder zone_entity
zone_state: "{{ states[zone_entity].name }}"
condition:
condition: template
value_template: "{{ trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}"
action:
- service: !placeholder notify_service
data:
message: "{{ trigger.to_state.name }} has left {{ zone_state }}"

View file

@ -2,14 +2,16 @@
import asyncio import asyncio
import logging import logging
import pathlib import pathlib
import shutil
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
from pkg_resources import parse_version from pkg_resources import parse_version
import voluptuous as vol import voluptuous as vol
from voluptuous.humanize import humanize_error from voluptuous.humanize import humanize_error
from homeassistant import loader
from homeassistant.const import CONF_DOMAIN, CONF_NAME, CONF_PATH, __version__ from homeassistant.const import CONF_DOMAIN, CONF_NAME, CONF_PATH, __version__
from homeassistant.core import HomeAssistant, callback from homeassistant.core import DOMAIN as HA_DOMAIN, HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import placeholder from homeassistant.helpers import placeholder
from homeassistant.util import yaml from homeassistant.util import yaml
@ -169,6 +171,11 @@ class DomainBlueprints:
hass.data.setdefault(DOMAIN, {})[domain] = self hass.data.setdefault(DOMAIN, {})[domain] = self
@property
def blueprint_folder(self) -> pathlib.Path:
"""Return the blueprint folder."""
return pathlib.Path(self.hass.config.path(BLUEPRINT_FOLDER, self.domain))
@callback @callback
def async_reset_cache(self) -> None: def async_reset_cache(self) -> None:
"""Reset the blueprint cache.""" """Reset the blueprint cache."""
@ -177,9 +184,7 @@ class DomainBlueprints:
def _load_blueprint(self, blueprint_path) -> Blueprint: def _load_blueprint(self, blueprint_path) -> Blueprint:
"""Load a blueprint.""" """Load a blueprint."""
try: try:
blueprint_data = yaml.load_yaml( blueprint_data = yaml.load_yaml(self.blueprint_folder / blueprint_path)
self.hass.config.path(BLUEPRINT_FOLDER, self.domain, blueprint_path)
)
except (HomeAssistantError, FileNotFoundError) as err: except (HomeAssistantError, FileNotFoundError) as err:
raise FailedToLoad(self.domain, blueprint_path, err) from err raise FailedToLoad(self.domain, blueprint_path, err) from err
@ -257,10 +262,7 @@ class DomainBlueprints:
async def async_remove_blueprint(self, blueprint_path: str) -> None: async def async_remove_blueprint(self, blueprint_path: str) -> None:
"""Remove a blueprint file.""" """Remove a blueprint file."""
path = pathlib.Path( path = self.blueprint_folder / blueprint_path
self.hass.config.path(BLUEPRINT_FOLDER, self.domain, blueprint_path)
)
await self.hass.async_add_executor_job(path.unlink) await self.hass.async_add_executor_job(path.unlink)
self._blueprints[blueprint_path] = None self._blueprints[blueprint_path] = None
@ -288,3 +290,18 @@ class DomainBlueprints:
) )
self._blueprints[blueprint_path] = blueprint self._blueprints[blueprint_path] = blueprint
async def async_populate(self) -> None:
"""Create folder if it doesn't exist and populate with examples."""
integration = await loader.async_get_integration(self.hass, self.domain)
def populate():
if self.blueprint_folder.exists():
return
shutil.copytree(
integration.file_path / BLUEPRINT_FOLDER,
self.blueprint_folder / HA_DOMAIN,
)
await self.hass.async_add_executor_job(populate)

View file

@ -63,6 +63,7 @@ BLUEPRINT_SCHEMA = vol.Schema(
vol.Required(CONF_BLUEPRINT): vol.Schema( vol.Required(CONF_BLUEPRINT): vol.Schema(
{ {
vol.Required(CONF_NAME): str, vol.Required(CONF_NAME): str,
vol.Optional(CONF_DESCRIPTION): str,
vol.Required(CONF_DOMAIN): str, vol.Required(CONF_DOMAIN): str,
vol.Optional(CONF_SOURCE_URL): cv.url, vol.Optional(CONF_SOURCE_URL): cv.url,
vol.Optional(CONF_HOMEASSISTANT): { vol.Optional(CONF_HOMEASSISTANT): {

View file

@ -453,18 +453,12 @@ def async_template(
) -> bool: ) -> bool:
"""Test if template condition matches.""" """Test if template condition matches."""
try: try:
value = value_template.async_render(variables) value: str = value_template.async_render(variables, parse_result=False)
except TemplateError as ex: except TemplateError as ex:
_LOGGER.error("Error during template condition: %s", ex) _LOGGER.error("Error during template condition: %s", ex)
return False return False
if isinstance(value, bool): return value.lower() == "true"
return value
if isinstance(value, str):
return value.lower() == "true"
return False
def async_template_from_config( def async_template_from_config(

View file

@ -23,6 +23,10 @@ def validate_selector(config: Any) -> Dict:
if selector_class is None: if selector_class is None:
raise vol.Invalid(f"Unknown selector type {selector_type} found") raise vol.Invalid(f"Unknown selector type {selector_type} found")
# Seletors can be empty
if config[selector_type] is None:
return {selector_type: {}}
return { return {
selector_type: cast(Dict, selector_class.CONFIG_SCHEMA(config[selector_type])) selector_type: cast(Dict, selector_class.CONFIG_SCHEMA(config[selector_type]))
} }
@ -44,6 +48,8 @@ class EntitySelector(Selector):
vol.Optional("integration"): str, vol.Optional("integration"): str,
# Domain the entity belongs to # Domain the entity belongs to
vol.Optional("domain"): str, vol.Optional("domain"): str,
# Device class of the entity
vol.Optional("device_class"): str,
} }
) )

View file

@ -9,6 +9,7 @@ from io import StringIO
import json import json
import logging import logging
import os import os
import pathlib
import threading import threading
import time import time
import uuid import uuid
@ -704,6 +705,9 @@ def patch_yaml_files(files_dict, endswith=True):
def mock_open_f(fname, **_): def mock_open_f(fname, **_):
"""Mock open() in the yaml module, used by load_yaml.""" """Mock open() in the yaml module, used by load_yaml."""
# Return the mocked file on full match # Return the mocked file on full match
if isinstance(fname, pathlib.Path):
fname = str(fname)
if fname in files_dict: if fname in files_dict:
_LOGGER.debug("patch_yaml_files match %s", fname) _LOGGER.debug("patch_yaml_files match %s", fname)
res = StringIO(files_dict[fname]) res = StringIO(files_dict[fname])

View file

@ -23,6 +23,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -22,6 +22,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -22,6 +22,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -13,6 +13,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -0,0 +1,3 @@
"""Conftest for automation tests."""
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa

View file

@ -0,0 +1,185 @@
"""Test built-in blueprints."""
import asyncio
import contextlib
from datetime import timedelta
import pathlib
from homeassistant.components import automation
from homeassistant.components.blueprint import models
from homeassistant.core import callback
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util, yaml
from tests.async_mock import patch
from tests.common import async_fire_time_changed, async_mock_service
BUILTIN_BLUEPRINT_FOLDER = pathlib.Path(automation.__file__).parent / "blueprints"
@contextlib.contextmanager
def patch_blueprint(blueprint_path: str, data_path):
"""Patch blueprint loading from a different source."""
orig_load = models.DomainBlueprints._load_blueprint
@callback
def mock_load_blueprint(self, path):
if path != blueprint_path:
assert False, f"Unexpected blueprint {path}"
return orig_load(self, path)
return models.Blueprint(
yaml.load_yaml(data_path), expected_domain=self.domain, path=path
)
with patch(
"homeassistant.components.blueprint.models.DomainBlueprints._load_blueprint",
mock_load_blueprint,
):
yield
async def test_notify_leaving_zone(hass):
"""Test notifying leaving a zone blueprint."""
def set_person_state(state, extra={}):
hass.states.async_set(
"person.test_person", state, {"friendly_name": "Paulus", **extra}
)
set_person_state("School")
assert await async_setup_component(
hass, "zone", {"zone": {"name": "School", "latitude": 1, "longitude": 2}}
)
with patch_blueprint(
"notify_leaving_zone.yaml",
BUILTIN_BLUEPRINT_FOLDER / "notify_leaving_zone.yaml",
):
assert await async_setup_component(
hass,
"automation",
{
"automation": {
"use_blueprint": {
"path": "notify_leaving_zone.yaml",
"input": {
"person_entity": "person.test_person",
"zone_entity": "zone.school",
"notify_service": "notify.test_service",
},
}
}
},
)
calls = async_mock_service(hass, "notify", "test_service")
# Leaving zone to no zone
set_person_state("not_home")
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[0].data["message"] == "Paulus has left School"
# Should not increase when we go to another zone
set_person_state("bla")
await hass.async_block_till_done()
assert len(calls) == 1
# Should not increase when we go into the zone
set_person_state("School")
await hass.async_block_till_done()
assert len(calls) == 1
# Should not increase when we move in the zone
set_person_state("School", {"extra_key": "triggers change with same state"})
await hass.async_block_till_done()
assert len(calls) == 1
# Should increase when leaving zone for another zone
set_person_state("Just Outside School")
await hass.async_block_till_done()
assert len(calls) == 2
async def test_motion_light(hass):
"""Test motion light blueprint."""
hass.states.async_set("binary_sensor.kitchen", "off")
with patch_blueprint(
"motion_light.yaml",
BUILTIN_BLUEPRINT_FOLDER / "motion_light.yaml",
):
assert await async_setup_component(
hass,
"automation",
{
"automation": {
"use_blueprint": {
"path": "motion_light.yaml",
"input": {
"light_entity": "light.kitchen",
"motion_entity": "binary_sensor.kitchen",
},
}
}
},
)
turn_on_calls = async_mock_service(hass, "homeassistant", "turn_on")
turn_off_calls = async_mock_service(hass, "homeassistant", "turn_off")
# Turn on motion
hass.states.async_set("binary_sensor.kitchen", "on")
# Can't block till done because delay is active
# So wait 5 event loop iterations to process script
for _ in range(5):
await asyncio.sleep(0)
assert len(turn_on_calls) == 1
# Test light doesn't turn off if motion stays
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
for _ in range(5):
await asyncio.sleep(0)
assert len(turn_off_calls) == 0
# Test light turns off off 120s after last motion
hass.states.async_set("binary_sensor.kitchen", "off")
for _ in range(5):
await asyncio.sleep(0)
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=120))
await hass.async_block_till_done()
assert len(turn_off_calls) == 1
# Test restarting the script
hass.states.async_set("binary_sensor.kitchen", "on")
for _ in range(5):
await asyncio.sleep(0)
assert len(turn_on_calls) == 2
assert len(turn_off_calls) == 1
hass.states.async_set("binary_sensor.kitchen", "off")
for _ in range(5):
await asyncio.sleep(0)
hass.states.async_set("binary_sensor.kitchen", "on")
for _ in range(15):
await asyncio.sleep(0)
assert len(turn_on_calls) == 3
assert len(turn_off_calls) == 1

View file

@ -20,6 +20,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -20,6 +20,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -0,0 +1,14 @@
"""Blueprints conftest."""
import pytest
from tests.async_mock import patch
@pytest.fixture(autouse=True)
def stub_blueprint_populate():
"""Stub copying the blueprint automations to the config folder."""
with patch(
"homeassistant.components.blueprint.models.DomainBlueprints.async_populate"
):
yield

View file

@ -0,0 +1,28 @@
"""Test default blueprints."""
import importlib
import logging
import pathlib
import pytest
from homeassistant.components.blueprint import models
from homeassistant.components.blueprint.const import BLUEPRINT_FOLDER
from homeassistant.util import yaml
DOMAINS = ["automation"]
LOGGER = logging.getLogger(__name__)
@pytest.mark.parametrize("domain", DOMAINS)
def test_default_blueprints(domain: str):
"""Validate a folder of blueprints."""
integration = importlib.import_module(f"homeassistant.components.{domain}")
blueprint_folder = pathlib.Path(integration.__file__).parent / BLUEPRINT_FOLDER
items = list(blueprint_folder.glob("*"))
assert len(items) > 0, "Folder cannot be empty"
for fil in items:
LOGGER.info("Processing %s", fil)
assert fil.name.endswith(".yaml")
data = yaml.load_yaml(fil)
models.Blueprint(data, expected_domain=domain)

View file

@ -15,6 +15,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -15,6 +15,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -5,6 +5,7 @@ from homeassistant.bootstrap import async_setup_component
from homeassistant.components import config from homeassistant.components import config
from tests.async_mock import patch from tests.async_mock import patch
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
async def test_get_device_config(hass, hass_client): async def test_get_device_config(hass, hass_client):

View file

@ -4,6 +4,7 @@ import pytest
from homeassistant.components.config import device_registry from homeassistant.components.config import device_registry
from tests.common import mock_device_registry from tests.common import mock_device_registry
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -22,6 +22,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -22,6 +22,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -19,6 +19,7 @@ from homeassistant.const import (
from .test_gateway import DECONZ_WEB_REQUEST, setup_deconz_integration from .test_gateway import DECONZ_WEB_REQUEST, setup_deconz_integration
from tests.common import assert_lists_same, async_get_device_automations from tests.common import assert_lists_same, async_get_device_automations
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
SENSORS = { SENSORS = {
"1": { "1": {

View file

@ -4,6 +4,7 @@ import pytest
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.async_mock import patch from tests.async_mock import patch
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)

View file

@ -13,6 +13,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -15,6 +15,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
AWAY_LATITUDE = 32.881011 AWAY_LATITUDE = 32.881011
AWAY_LONGITUDE = -117.234758 AWAY_LONGITUDE = -117.234758

View file

@ -14,6 +14,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -15,6 +15,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -15,6 +15,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -7,6 +7,7 @@ from homeassistant.core import Context
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import async_mock_service, mock_component from tests.common import async_mock_service, mock_component
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -0,0 +1,3 @@
"""Conftest for HA triggers."""
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa

View file

@ -12,6 +12,7 @@ from tests.common import (
async_get_device_automations, async_get_device_automations,
async_mock_service, async_mock_service,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
from tests.components.homekit_controller.common import setup_test_component from tests.components.homekit_controller.common import setup_test_component

View file

@ -15,6 +15,7 @@ from tests.common import (
async_mock_service, async_mock_service,
mock_device_registry, mock_device_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
REMOTES_RESPONSE = {"7": HUE_TAP_REMOTE_1, "8": HUE_DIMMER_REMOTE_1} REMOTES_RESPONSE = {"7": HUE_TAP_REMOTE_1, "8": HUE_DIMMER_REMOTE_1}

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -17,6 +17,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -20,6 +20,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -21,6 +21,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -19,6 +19,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -19,6 +19,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -11,6 +11,7 @@ import homeassistant.components.automation as automation
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from tests.common import async_fire_time_changed, async_mock_service from tests.common import async_fire_time_changed, async_mock_service
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View file

@ -15,6 +15,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -15,6 +15,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -15,6 +15,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -21,6 +21,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -7,6 +7,7 @@ from homeassistant.setup import async_setup_component
from tests.async_mock import ANY from tests.async_mock import ANY
from tests.common import async_fire_mqtt_message, async_mock_service, mock_component from tests.common import async_fire_mqtt_message, async_mock_service, mock_component
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -19,6 +19,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -19,6 +19,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -3,6 +3,7 @@ from homeassistant.components import search
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
async def test_search(hass): async def test_search(hass):

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
from tests.testing_config.custom_components.test.sensor import DEVICE_CLASSES from tests.testing_config.custom_components.test.sensor import DEVICE_CLASSES

View file

@ -20,6 +20,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
from tests.testing_config.custom_components.test.sensor import DEVICE_CLASSES from tests.testing_config.custom_components.test.sensor import DEVICE_CLASSES

View file

@ -18,6 +18,7 @@ import homeassistant.util.dt as dt_util
from tests.async_mock import patch from tests.async_mock import patch
from tests.common import async_fire_time_changed, async_mock_service, mock_component from tests.common import async_fire_time_changed, async_mock_service, mock_component
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
ORIG_TIME_ZONE = dt_util.DEFAULT_TIME_ZONE ORIG_TIME_ZONE = dt_util.DEFAULT_TIME_ZONE

View file

@ -16,6 +16,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -19,6 +19,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -19,6 +19,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -8,6 +8,7 @@ from homeassistant.components.tag.const import DOMAIN, TAG_ID
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import async_mock_service from tests.common import async_mock_service
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -18,6 +18,7 @@ from tests.common import (
async_fire_mqtt_message, async_fire_mqtt_message,
async_get_device_automations, async_get_device_automations,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
async def test_get_triggers(hass, device_reg, entity_reg, mqtt_mock, setup_tasmota): async def test_get_triggers(hass, device_reg, entity_reg, mqtt_mock, setup_tasmota):

View file

@ -17,6 +17,7 @@ from tests.common import (
async_mock_service, async_mock_service,
mock_component, mock_component,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -14,6 +14,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -19,6 +19,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -14,6 +14,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -14,6 +14,7 @@ from tests.common import (
mock_device_registry, mock_device_registry,
mock_registry, mock_registry,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -5,6 +5,7 @@ from homeassistant.core import callback
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.async_mock import patch from tests.async_mock import patch
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)

View file

@ -16,6 +16,7 @@ from homeassistant.helpers.device_registry import async_get_registry
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import async_mock_service, mock_coro from tests.common import async_mock_service, mock_coro
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
SHORT_PRESS = "remote_button_short_press" SHORT_PRESS = "remote_button_short_press"
COMMAND = "command" COMMAND = "command"

View file

@ -19,6 +19,7 @@ from tests.common import (
async_get_device_automations, async_get_device_automations,
async_mock_service, async_mock_service,
) )
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
ON = 1 ON = 1
OFF = 0 OFF = 0

View file

@ -7,6 +7,7 @@ from homeassistant.core import Context
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import async_mock_service, mock_component from tests.common import async_mock_service, mock_component
from tests.components.blueprint.conftest import stub_blueprint_populate # noqa
@pytest.fixture @pytest.fixture

View file

@ -165,4 +165,4 @@ action:
} }
with patch("os.path.isfile", return_value=True), patch_yaml_files(files): with patch("os.path.isfile", return_value=True), patch_yaml_files(files):
res = await async_check_ha_config_file(hass) res = await async_check_ha_config_file(hass)
assert len(res["automation"]) == 1 assert len(res.get("automation", [])) == 1

View file

@ -6,7 +6,25 @@ from homeassistant.helpers import selector
@pytest.mark.parametrize( @pytest.mark.parametrize(
"schema", ({}, {"non_existing": {}}, {"device": {}, "entity": {}}) "schema",
(
{"device": None},
{"entity": None},
),
)
def test_valid_base_schema(schema):
"""Test base schema validation."""
selector.validate_selector(schema)
@pytest.mark.parametrize(
"schema",
(
{},
{"non_existing": {}},
# Two keys
{"device": {}, "entity": {}},
),
) )
def test_invalid_base_schema(schema): def test_invalid_base_schema(schema):
"""Test base schema validation.""" """Test base schema validation."""