Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion homeassistant/components/alarm_control_panel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY,
SERVICE_ALARM_ARM_NIGHT, SERVICE_ALARM_ARM_CUSTOM_BYPASS)
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.helpers.config_validation import ( # noqa
PLATFORM_SCHEMA_BASE, PLATFORM_SCHEMA_2 as PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
Expand Down
12 changes: 9 additions & 3 deletions homeassistant/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,13 +743,19 @@ def async_process_component_config(
async_log_exception(ex, domain, config, hass)
return None

elif hasattr(component, 'PLATFORM_SCHEMA'):
elif (hasattr(component, 'PLATFORM_SCHEMA') or
hasattr(component, 'PLATFORM_SCHEMA_BASE')):
platforms = []
for p_name, p_config in config_per_platform(config, domain):
# Validate component specific platform schema
try:
p_validated = component.PLATFORM_SCHEMA( # type: ignore
p_config)
if hasattr(component, 'PLATFORM_SCHEMA_BASE'):
p_validated = \
component.PLATFORM_SCHEMA_BASE( # type: ignore
p_config)
else:
p_validated = component.PLATFORM_SCHEMA( # type: ignore
p_config)
except vol.Invalid as ex:
async_log_exception(ex, domain, config, hass)
continue
Expand Down
9 changes: 9 additions & 0 deletions homeassistant/helpers/config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,15 @@ def validator(value):
vol.Optional(CONF_SCAN_INTERVAL): time_period
}, extra=vol.ALLOW_EXTRA)

# This will replace PLATFORM_SCHEMA once all base components are updated
PLATFORM_SCHEMA_2 = vol.Schema({
vol.Required(CONF_PLATFORM): string,
vol.Optional(CONF_SCAN_INTERVAL): time_period
})

PLATFORM_SCHEMA_BASE = PLATFORM_SCHEMA_2.extend({
}, extra=vol.ALLOW_EXTRA)

EVENT_SCHEMA = vol.Schema({
vol.Optional(CONF_ALIAS): string,
vol.Required('event'): string,
Expand Down
7 changes: 5 additions & 2 deletions tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,8 @@ class MockModule:
# pylint: disable=invalid-name
def __init__(self, domain=None, dependencies=None, setup=None,
requirements=None, config_schema=None, platform_schema=None,
async_setup=None, async_setup_entry=None,
async_unload_entry=None):
platform_schema_base=None, async_setup=None,
async_setup_entry=None, async_unload_entry=None):
"""Initialize the mock module."""
self.DOMAIN = domain
self.DEPENDENCIES = dependencies or []
Expand All @@ -448,6 +448,9 @@ def __init__(self, domain=None, dependencies=None, setup=None,
if platform_schema is not None:
self.PLATFORM_SCHEMA = platform_schema

if platform_schema_base is not None:
self.PLATFORM_SCHEMA_BASE = platform_schema_base

if setup is not None:
# We run this in executor, wrap it in function
self.setup = lambda *args: setup(*args)
Expand Down
115 changes: 112 additions & 3 deletions tests/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
import homeassistant.config as config_util
from homeassistant import setup, loader
import homeassistant.util.dt as dt_util
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
from homeassistant.helpers.config_validation import (
PLATFORM_SCHEMA_2 as PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE)
from homeassistant.helpers import discovery

from tests.common import \
Expand Down Expand Up @@ -94,18 +95,24 @@ def test_validate_platform_config(self):
platform_schema = PLATFORM_SCHEMA.extend({
'hello': str,
})
platform_schema_base = PLATFORM_SCHEMA_BASE.extend({
})
loader.set_component(
self.hass,
'platform_conf',
MockModule('platform_conf', platform_schema=platform_schema))
MockModule('platform_conf',
platform_schema_base=platform_schema_base))

loader.set_component(
self.hass,
'platform_conf.whatever', MockPlatform('whatever'))
'platform_conf.whatever',
MockPlatform('whatever',
platform_schema=platform_schema))

with assert_setup_component(0):
assert setup.setup_component(self.hass, 'platform_conf', {
'platform_conf': {
'platform': 'whatever',
'hello': 'world',
'invalid': 'extra',
}
Expand All @@ -121,6 +128,7 @@ def test_validate_platform_config(self):
'hello': 'world',
},
'platform_conf 2': {
'platform': 'whatever',
'invalid': True
}
})
Expand Down Expand Up @@ -175,6 +183,107 @@ def test_validate_platform_config(self):
assert 'platform_conf' in self.hass.config.components
assert not config['platform_conf'] # empty

def test_validate_platform_config_2(self):
"""Test component PLATFORM_SCHEMA_BASE prio over PLATFORM_SCHEMA."""
platform_schema = PLATFORM_SCHEMA.extend({
'hello': str,
})
platform_schema_base = PLATFORM_SCHEMA_BASE.extend({
'hello': 'world',
})
loader.set_component(
self.hass,
'platform_conf',
MockModule('platform_conf',
platform_schema=platform_schema,
platform_schema_base=platform_schema_base))

loader.set_component(
self.hass,
'platform_conf.whatever',
MockPlatform('whatever',
platform_schema=platform_schema))

with assert_setup_component(0):
assert setup.setup_component(self.hass, 'platform_conf', {
# fail: no extra keys allowed in platform schema
'platform_conf': {
'platform': 'whatever',
'hello': 'world',
'invalid': 'extra',
}
})

self.hass.data.pop(setup.DATA_SETUP)
self.hass.config.components.remove('platform_conf')

with assert_setup_component(1):
assert setup.setup_component(self.hass, 'platform_conf', {
# pass
'platform_conf': {
'platform': 'whatever',
'hello': 'world',
},
# fail: key hello violates component platform_schema_base
'platform_conf 2': {
'platform': 'whatever',
'hello': 'there'
}
})

self.hass.data.pop(setup.DATA_SETUP)
self.hass.config.components.remove('platform_conf')

def test_validate_platform_config_3(self):
"""Test fallback to component PLATFORM_SCHEMA."""
Comment thread
emontnemery marked this conversation as resolved.
component_schema = PLATFORM_SCHEMA_BASE.extend({
'hello': str,
})
platform_schema = PLATFORM_SCHEMA.extend({
'cheers': str,
'hello': 'world',
})
loader.set_component(
self.hass,
'platform_conf',
MockModule('platform_conf',
platform_schema=component_schema))

loader.set_component(
self.hass,
'platform_conf.whatever',
MockPlatform('whatever',
platform_schema=platform_schema))

with assert_setup_component(0):
assert setup.setup_component(self.hass, 'platform_conf', {
'platform_conf': {
# fail: no extra keys allowed
'hello': 'world',
'invalid': 'extra',
}
})

self.hass.data.pop(setup.DATA_SETUP)
self.hass.config.components.remove('platform_conf')

with assert_setup_component(1):
assert setup.setup_component(self.hass, 'platform_conf', {
# pass
'platform_conf': {
'platform': 'whatever',
'hello': 'world',
},
# fail: key hello violates component platform_schema
'platform_conf 2': {
'platform': 'whatever',
'hello': 'there'
}
})

self.hass.data.pop(setup.DATA_SETUP)
self.hass.config.components.remove('platform_conf')

def test_component_not_found(self):
"""setup_component should not crash if component doesn't exist."""
assert not setup.setup_component(self.hass, 'non_existing')
Expand Down