diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index ea7dd7765fcf..0cd55db150c8 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -108,14 +108,33 @@ def _setup_component(hass, domain, config): elif hasattr(component, 'PLATFORM_SCHEMA'): platforms = [] - for _, platform in config_per_platform(config, domain): + for p_name, p_config in config_per_platform(config, domain): + # Validate component specific platform schema try: - platforms.append(component.PLATFORM_SCHEMA(platform)) + p_validated = component.PLATFORM_SCHEMA(p_config) except vol.MultipleInvalid as ex: _LOGGER.error('Invalid platform config for [%s]: %s. %s', - domain, ex, platform) + domain, ex, p_config) return False + platform = prepare_setup_platform(hass, config, domain, + p_name) + + if platform is None: + return False + + # Validate platform specific schema + if hasattr(platform, 'PLATFORM_SCHEMA'): + try: + p_validated = platform.PLATFORM_SCHEMA(p_validated) + except vol.MultipleInvalid as ex: + _LOGGER.error( + 'Invalid platform config for [%s.%s]: %s. %s', + domain, p_name, ex, p_config) + return False + + platforms.append(p_validated) + # Create a copy of the configuration with all config for current # component removed and add validated config back in. filter_keys = extract_domain_configs(config, domain) diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 3a9f1c113d22..8f80a025c8d7 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -2,7 +2,7 @@ import voluptuous as vol from homeassistant.const import ( - CONF_PLATFORM, TEMP_CELCIUS, TEMP_FAHRENHEIT) + CONF_PLATFORM, CONF_SCAN_INTERVAL, TEMP_CELCIUS, TEMP_FAHRENHEIT) from homeassistant.helpers.entity import valid_entity_id import homeassistant.util.dt as dt_util @@ -10,6 +10,7 @@ import homeassistant.util.dt as dt_util PLATFORM_SCHEMA = vol.Schema({ vol.Required(CONF_PLATFORM): str, + CONF_SCAN_INTERVAL: vol.All(vol.Coerce(int), vol.Range(min=1)), }, extra=vol.ALLOW_EXTRA) byte = vol.All(vol.Coerce(int), vol.Range(min=0, max=255)) diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index b1a18b1de37f..0bcebd8dcdb8 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -79,24 +79,22 @@ class EntityComponent(object): platform = prepare_setup_platform( self.hass, self.config, self.domain, platform_type) - if platform is None: - return + # Config > Platform > Component + scan_interval = platform_config.get( + CONF_SCAN_INTERVAL, + getattr(platform, 'SCAN_INTERVAL', self.scan_interval)) try: - # Config > Platform > Component - scan_interval = platform_config.get( - CONF_SCAN_INTERVAL, - getattr(platform, 'SCAN_INTERVAL', self.scan_interval)) platform.setup_platform( self.hass, platform_config, EntityPlatform(self, scan_interval).add_entities, discovery_info) - platform_name = '{}.{}'.format(self.domain, platform_type) - self.hass.config.components.append(platform_name) + + self.hass.config.components.append( + '{}.{}'.format(self.domain, platform_type)) except Exception: # pylint: disable=broad-except self.logger.exception( 'Error while setting up platform %s', platform_type) - return def add_entity(self, entity): """Add entity to component.""" diff --git a/tests/common.py b/tests/common.py index 1774e6845b06..8f0c5543c1c1 100644 --- a/tests/common.py +++ b/tests/common.py @@ -143,12 +143,12 @@ class MockHTTP(object): class MockModule(object): """Representation of a fake module.""" - def __init__(self, domain=None, dependencies=[], setup=None, - requirements=[], config_schema=None, platform_schema=None): + def __init__(self, domain=None, dependencies=None, setup=None, + requirements=None, config_schema=None, platform_schema=None): """Initialize the mock module.""" self.DOMAIN = domain - self.DEPENDENCIES = dependencies - self.REQUIREMENTS = requirements + self.DEPENDENCIES = dependencies or [] + self.REQUIREMENTS = requirements or [] if config_schema is not None: self.CONFIG_SCHEMA = config_schema @@ -166,11 +166,15 @@ class MockModule(object): class MockPlatform(object): """Provide a fake platform.""" - def __init__(self, setup_platform=None, dependencies=[]): + def __init__(self, setup_platform=None, dependencies=None, + platform_schema=None): """Initialize the platform.""" - self.DEPENDENCIES = dependencies + self.DEPENDENCIES = dependencies or [] self._setup_platform = setup_platform + if platform_schema is not None: + self.PLATFORM_SCHEMA = platform_schema + def setup_platform(self, hass, config, add_devices, discovery_info=None): """Setup the platform.""" if self._setup_platform is not None: diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 4a50ae553930..d38c39685330 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -165,11 +165,14 @@ class TestBootstrap: """Test validating platform configuration.""" platform_schema = PLATFORM_SCHEMA.extend({ 'hello': str, - }, required=True) + }) loader.set_component( 'platform_conf', MockModule('platform_conf', platform_schema=platform_schema)) + loader.set_component( + 'platform_conf.whatever', MockPlatform('whatever')) + assert not bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': None }) @@ -196,6 +199,13 @@ class TestBootstrap: } }) + assert not bootstrap._setup_component(self.hass, 'platform_conf', { + 'platform_conf': { + 'platform': 'not_existing', + 'hello': 'world', + } + }) + assert bootstrap._setup_component(self.hass, 'platform_conf', { 'platform_conf': { 'platform': 'whatever', @@ -318,7 +328,7 @@ class TestBootstrap: loader.set_component('switch.platform_a', MockPlatform('comp_b', ['comp_a'])) - assert bootstrap.setup_component(self.hass, 'switch', { + bootstrap.setup_component(self.hass, 'switch', { 'comp_a': { 'valid': True }, @@ -327,3 +337,36 @@ class TestBootstrap: } }) assert 'comp_a' in self.hass.config.components + + def test_platform_specific_config_validation(self): + """Test platform that specifies config.""" + + platform_schema = PLATFORM_SCHEMA.extend({ + 'valid': True, + }, extra=vol.PREVENT_EXTRA) + + loader.set_component( + 'switch.platform_a', + MockPlatform('comp_b', platform_schema=platform_schema)) + + assert not bootstrap.setup_component(self.hass, 'switch', { + 'switch': { + 'platform': 'platform_a', + 'invalid': True + } + }) + + assert not bootstrap.setup_component(self.hass, 'switch', { + 'switch': { + 'platform': 'platform_a', + 'valid': True, + 'invalid_extra': True, + } + }) + + assert bootstrap.setup_component(self.hass, 'switch', { + 'switch': { + 'platform': 'platform_a', + 'valid': True + } + })