mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Add initial config validation
This commit is contained in:
parent
0549bc0290
commit
5baa98b79f
@ -244,6 +244,9 @@ def setup_and_run_hass(config_dir, args, top_process=False):
|
||||
config_file, daemon=args.daemon, verbose=args.verbose,
|
||||
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days)
|
||||
|
||||
if hass is None:
|
||||
return
|
||||
|
||||
if args.open_ui:
|
||||
def open_browser(event):
|
||||
"""Open the webinterface in a browser."""
|
||||
|
@ -8,6 +8,8 @@ import sys
|
||||
from collections import defaultdict
|
||||
from threading import RLock
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components as core_components
|
||||
import homeassistant.components.group as group
|
||||
import homeassistant.config as config_util
|
||||
@ -20,7 +22,8 @@ from homeassistant.const import (
|
||||
CONF_CUSTOMIZE, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME,
|
||||
CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, EVENT_COMPONENT_LOADED,
|
||||
TEMP_CELCIUS, TEMP_FAHRENHEIT, __version__)
|
||||
from homeassistant.helpers import event_decorators, service
|
||||
from homeassistant.helpers import (
|
||||
event_decorators, service, config_per_platform)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -72,7 +75,7 @@ def _handle_requirements(hass, component, name):
|
||||
|
||||
def _setup_component(hass, domain, config):
|
||||
"""Setup a component for Home Assistant."""
|
||||
# pylint: disable=too-many-return-statements
|
||||
# pylint: disable=too-many-return-statements,too-many-branches
|
||||
if domain in hass.config.components:
|
||||
return True
|
||||
|
||||
@ -96,6 +99,25 @@ def _setup_component(hass, domain, config):
|
||||
domain, ", ".join(missing_deps))
|
||||
return False
|
||||
|
||||
if hasattr(component, 'CONFIG_SCHEMA'):
|
||||
try:
|
||||
config = component.CONFIG_SCHEMA(config)
|
||||
except vol.MultipleInvalid as ex:
|
||||
_LOGGER.error('Invalid config for [%s]: %s', domain, ex)
|
||||
return False
|
||||
|
||||
if hasattr(component, 'PLATFORM_SCHEMA'):
|
||||
platforms = []
|
||||
for _, platform in config_per_platform(config, domain):
|
||||
try:
|
||||
platforms.append(component.PLATFORM_SCHEMA(platform))
|
||||
except vol.MultipleInvalid as ex:
|
||||
_LOGGER.error('Invalid platform config for [%s]: %s. %s',
|
||||
domain, ex, platform)
|
||||
return False
|
||||
|
||||
config = {domain: platforms}
|
||||
|
||||
if not _handle_requirements(hass, component, domain):
|
||||
return False
|
||||
|
||||
@ -176,8 +198,14 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
||||
hass.config.config_dir = config_dir
|
||||
mount_local_lib_path(config_dir)
|
||||
|
||||
try:
|
||||
process_ha_core_config(hass, config_util.CORE_CONFIG_SCHEMA(
|
||||
config.get(core.DOMAIN, {})))
|
||||
except vol.MultipleInvalid as ex:
|
||||
_LOGGER.error('Invalid config for [homeassistant]: %s', ex)
|
||||
return None
|
||||
|
||||
process_ha_config_upgrade(hass)
|
||||
process_ha_core_config(hass, config.get(core.DOMAIN, {}))
|
||||
|
||||
if enable_log:
|
||||
enable_logging(hass, verbose, daemon, log_rotate_days)
|
||||
@ -336,40 +364,28 @@ def process_ha_core_config(hass, config):
|
||||
else:
|
||||
_LOGGER.error('Received invalid time zone %s', time_zone_str)
|
||||
|
||||
for key, attr, typ in ((CONF_LATITUDE, 'latitude', float),
|
||||
(CONF_LONGITUDE, 'longitude', float),
|
||||
(CONF_NAME, 'location_name', str)):
|
||||
for key, attr in ((CONF_LATITUDE, 'latitude'),
|
||||
(CONF_LONGITUDE, 'longitude'),
|
||||
(CONF_NAME, 'location_name')):
|
||||
if key in config:
|
||||
try:
|
||||
setattr(hac, attr, typ(config[key]))
|
||||
except ValueError:
|
||||
_LOGGER.error('Received invalid %s value for %s: %s',
|
||||
typ.__name__, key, attr)
|
||||
setattr(hac, attr, config[key])
|
||||
|
||||
set_time_zone(config.get(CONF_TIME_ZONE))
|
||||
if CONF_TIME_ZONE in config:
|
||||
set_time_zone(config.get(CONF_TIME_ZONE))
|
||||
|
||||
customize = config.get(CONF_CUSTOMIZE)
|
||||
|
||||
if isinstance(customize, dict):
|
||||
for entity_id, attrs in config.get(CONF_CUSTOMIZE, {}).items():
|
||||
if not isinstance(attrs, dict):
|
||||
continue
|
||||
Entity.overwrite_attribute(entity_id, attrs.keys(), attrs.values())
|
||||
for entity_id, attrs in config.get(CONF_CUSTOMIZE).items():
|
||||
Entity.overwrite_attribute(entity_id, attrs.keys(), attrs.values())
|
||||
|
||||
if CONF_TEMPERATURE_UNIT in config:
|
||||
unit = config[CONF_TEMPERATURE_UNIT]
|
||||
|
||||
if unit == 'C':
|
||||
hac.temperature_unit = TEMP_CELCIUS
|
||||
elif unit == 'F':
|
||||
hac.temperature_unit = TEMP_FAHRENHEIT
|
||||
hac.temperature_unit = config[CONF_TEMPERATURE_UNIT]
|
||||
|
||||
# If we miss some of the needed values, auto detect them
|
||||
if None not in (
|
||||
hac.latitude, hac.longitude, hac.temperature_unit, hac.time_zone):
|
||||
return
|
||||
|
||||
_LOGGER.info('Auto detecting location and temperature unit')
|
||||
_LOGGER.warning('Incomplete core config. Auto detecting location and '
|
||||
'temperature unit')
|
||||
|
||||
info = loc_util.detect_location_info()
|
||||
|
||||
|
@ -11,6 +11,7 @@ from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import (STATE_ON, STATE_OFF)
|
||||
from homeassistant.components import (
|
||||
bloomsky, mysensors, zwave, vera, wemo, wink)
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
|
||||
DOMAIN = 'binary_sensor'
|
||||
SCAN_INTERVAL = 30
|
||||
|
@ -15,6 +15,7 @@ from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.components import bloomsky
|
||||
from homeassistant.const import HTTP_OK, HTTP_NOT_FOUND, ATTR_ENTITY_ID
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
|
||||
|
||||
DOMAIN = 'camera'
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_per_platform
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
import homeassistant.util as util
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
@ -129,8 +130,7 @@ def setup(hass, config):
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception('Error setting up platform %s', p_type)
|
||||
|
||||
for p_type, p_config in \
|
||||
config_per_platform(config, DOMAIN, _LOGGER):
|
||||
for p_type, p_config in config_per_platform(config, DOMAIN):
|
||||
setup_platform(p_type, p_config)
|
||||
|
||||
def device_tracker_discovered(service, info):
|
||||
|
@ -10,7 +10,7 @@ import os
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
from homeassistant.const import (
|
||||
STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN, SERVICE_CLOSE, SERVICE_OPEN,
|
||||
ATTR_ENTITY_ID)
|
||||
|
@ -6,6 +6,8 @@ https://home-assistant.io/components/group/
|
||||
"""
|
||||
import threading
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.core as ha
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_CLOSED, STATE_HOME,
|
||||
@ -14,6 +16,7 @@ from homeassistant.const import (
|
||||
from homeassistant.helpers.entity import (
|
||||
Entity, generate_entity_id, split_entity_id)
|
||||
from homeassistant.helpers.event import track_state_change
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
DOMAIN = 'group'
|
||||
|
||||
@ -26,6 +29,38 @@ ATTR_AUTO = 'auto'
|
||||
ATTR_ORDER = 'order'
|
||||
ATTR_VIEW = 'view'
|
||||
|
||||
|
||||
def _conf_preprocess(value):
|
||||
"""Preprocess alternative configuration formats."""
|
||||
if isinstance(value, (str, list)):
|
||||
value = {CONF_ENTITIES: value}
|
||||
|
||||
return value
|
||||
|
||||
_SINGLE_GROUP_CONFIG = vol.Schema(vol.All(_conf_preprocess, {
|
||||
vol.Required(CONF_ENTITIES): cv.entity_ids,
|
||||
CONF_VIEW: bool,
|
||||
CONF_NAME: str,
|
||||
CONF_ICON: cv.icon,
|
||||
}))
|
||||
|
||||
|
||||
def _group_dict(value):
|
||||
"""Validate a dictionary of group definitions."""
|
||||
config = {}
|
||||
for key, group in value.items():
|
||||
try:
|
||||
config[key] = _SINGLE_GROUP_CONFIG(group)
|
||||
except vol.MultipleInvalid as ex:
|
||||
raise vol.Invalid('Group {} is invalid: {}'.format(key, ex))
|
||||
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.All(dict, _group_dict)
|
||||
}, extra=True)
|
||||
|
||||
# List of ON/OFF state tuples for groupable states
|
||||
_GROUP_TYPES = [(STATE_ON, STATE_OFF), (STATE_HOME, STATE_NOT_HOME),
|
||||
(STATE_OPEN, STATE_CLOSED)]
|
||||
@ -108,17 +143,11 @@ def get_entity_ids(hass, entity_id, domain_filter=None):
|
||||
def setup(hass, config):
|
||||
"""Setup all groups found definded in the configuration."""
|
||||
for object_id, conf in config.get(DOMAIN, {}).items():
|
||||
if not isinstance(conf, dict):
|
||||
conf = {CONF_ENTITIES: conf}
|
||||
|
||||
name = conf.get(CONF_NAME, object_id)
|
||||
entity_ids = conf.get(CONF_ENTITIES)
|
||||
entity_ids = conf[CONF_ENTITIES]
|
||||
icon = conf.get(CONF_ICON)
|
||||
view = conf.get(CONF_VIEW)
|
||||
|
||||
if isinstance(entity_ids, str):
|
||||
entity_ids = [ent.strip() for ent in entity_ids.split(",")]
|
||||
|
||||
Group(hass, name, entity_ids, icon=icon, view=view,
|
||||
object_id=object_id)
|
||||
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.const import (
|
||||
ATTR_ENTITY_ID)
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
import homeassistant.util as util
|
||||
import homeassistant.util.color as color_util
|
||||
|
||||
|
@ -11,7 +11,7 @@ import os
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED,
|
||||
STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK)
|
||||
|
@ -11,6 +11,7 @@ from homeassistant.components import discovery
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
from homeassistant.const import (
|
||||
STATE_OFF, STATE_UNKNOWN, STATE_PLAYING, STATE_IDLE,
|
||||
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON,
|
||||
|
@ -10,8 +10,8 @@ import os
|
||||
|
||||
import homeassistant.bootstrap as bootstrap
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.helpers import config_per_platform
|
||||
from homeassistant.helpers import template
|
||||
from homeassistant.helpers import config_per_platform, template
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
|
||||
from homeassistant.const import CONF_NAME
|
||||
|
||||
@ -51,7 +51,7 @@ def setup(hass, config):
|
||||
descriptions = load_yaml_config_file(
|
||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
||||
|
||||
for platform, p_config in config_per_platform(config, DOMAIN, _LOGGER):
|
||||
for platform, p_config in config_per_platform(config, DOMAIN):
|
||||
notify_implementation = bootstrap.prepare_setup_platform(
|
||||
hass, config, DOMAIN, platform)
|
||||
|
||||
|
@ -10,6 +10,7 @@ import logging
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
from homeassistant.components import group
|
||||
from homeassistant.const import (
|
||||
SERVICE_MOVE_UP, SERVICE_MOVE_DOWN, SERVICE_STOP,
|
||||
|
@ -7,6 +7,7 @@ https://home-assistant.io/components/sensor/
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
from homeassistant.components import (
|
||||
wink, zwave, isy994, verisure, ecobee, tellduslive, mysensors,
|
||||
bloomsky, vera)
|
||||
|
@ -11,7 +11,7 @@ import os
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
from homeassistant.const import (
|
||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
|
||||
ATTR_ENTITY_ID)
|
||||
|
@ -13,6 +13,7 @@ from homeassistant.config import load_yaml_config_file
|
||||
import homeassistant.util as util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.temperature import convert
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
from homeassistant.components import ecobee
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF, STATE_UNKNOWN,
|
||||
|
@ -1,13 +1,18 @@
|
||||
"""Module to help with parsing and generating configuration files."""
|
||||
import logging
|
||||
import os
|
||||
from types import MappingProxyType
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.util.location as loc_util
|
||||
from homeassistant.const import (
|
||||
CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_TEMPERATURE_UNIT,
|
||||
CONF_TIME_ZONE)
|
||||
CONF_TIME_ZONE, CONF_CUSTOMIZE)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util.yaml import load_yaml
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import valid_entity_id
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -37,6 +42,31 @@ DEFAULT_COMPONENTS = {
|
||||
}
|
||||
|
||||
|
||||
def _valid_customize(value):
|
||||
"""Config validator for customize."""
|
||||
if not isinstance(value, dict):
|
||||
raise vol.Invalid('Expected dictionary')
|
||||
|
||||
for key, val in value.items():
|
||||
if not valid_entity_id(key):
|
||||
raise vol.Invalid('Invalid entity ID: {}'.format(key))
|
||||
|
||||
if not isinstance(val, dict):
|
||||
raise vol.Invalid('Value of {} is not a dictionary'.format(key))
|
||||
|
||||
return value
|
||||
|
||||
CORE_CONFIG_SCHEMA = vol.Schema({
|
||||
CONF_NAME: vol.Coerce(str),
|
||||
CONF_LATITUDE: cv.latitude,
|
||||
CONF_LONGITUDE: cv.longitude,
|
||||
CONF_TEMPERATURE_UNIT: cv.temperature_unit,
|
||||
CONF_TIME_ZONE: cv.time_zone,
|
||||
vol.Required(CONF_CUSTOMIZE,
|
||||
default=MappingProxyType({})): _valid_customize,
|
||||
})
|
||||
|
||||
|
||||
def get_default_config_dir():
|
||||
"""Put together the default configuration directory based on OS."""
|
||||
data_dir = os.getenv('APPDATA') if os.name == "nt" \
|
||||
|
@ -29,30 +29,18 @@ def validate_config(config, items, logger):
|
||||
return not errors_found
|
||||
|
||||
|
||||
def config_per_platform(config, domain, logger):
|
||||
def config_per_platform(config, domain):
|
||||
"""Generator to break a component config into different platforms.
|
||||
|
||||
For example, will find 'switch', 'switch 2', 'switch 3', .. etc
|
||||
"""
|
||||
config_key = domain
|
||||
found = 1
|
||||
|
||||
for config_key in extract_domain_configs(config, domain):
|
||||
platform_config = config[config_key]
|
||||
if not isinstance(platform_config, list):
|
||||
platform_config = [platform_config]
|
||||
|
||||
for item in platform_config:
|
||||
platform_type = item.get(CONF_PLATFORM)
|
||||
|
||||
if platform_type is None:
|
||||
logger.warning('No platform specified for %s', config_key)
|
||||
continue
|
||||
|
||||
yield platform_type, item
|
||||
|
||||
found += 1
|
||||
config_key = "{} {}".format(domain, found)
|
||||
yield item.get(CONF_PLATFORM), item
|
||||
|
||||
|
||||
def extract_domain_configs(config, domain):
|
||||
|
65
homeassistant/helpers/config_validation.py
Normal file
65
homeassistant/helpers/config_validation.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""Helpers for config validation using voluptuous."""
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_PLATFORM, TEMP_CELCIUS, TEMP_FAHRENHEIT)
|
||||
from homeassistant.helpers.entity import valid_entity_id
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
PLATFORM_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_PLATFORM): str,
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
latitude = vol.All(vol.Coerce(float), vol.Range(min=-90, max=90))
|
||||
longitude = vol.All(vol.Coerce(float), vol.Range(min=-180, max=180))
|
||||
|
||||
|
||||
def entity_id(value):
|
||||
"""Validate Entity ID."""
|
||||
if valid_entity_id(value):
|
||||
return value
|
||||
raise vol.Invalid('Entity ID {} does not match format <domain>.<object_id>'
|
||||
.format(value))
|
||||
|
||||
|
||||
def entity_ids(value):
|
||||
"""Validate Entity IDs."""
|
||||
if isinstance(value, str):
|
||||
value = [ent_id.strip() for ent_id in value.split(',')]
|
||||
|
||||
for ent_id in value:
|
||||
entity_id(ent_id)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def icon(value):
|
||||
"""Validate icon."""
|
||||
value = str(value)
|
||||
|
||||
if value.startswith('mdi:'):
|
||||
return value
|
||||
|
||||
raise vol.Invalid('Icons should start with prefix "mdi:"')
|
||||
|
||||
|
||||
def temperature_unit(value):
|
||||
"""Validate and transform temperature unit."""
|
||||
if isinstance(value, str):
|
||||
value = value.upper()
|
||||
if value == 'C':
|
||||
return TEMP_CELCIUS
|
||||
elif value == 'F':
|
||||
return TEMP_FAHRENHEIT
|
||||
raise vol.Invalid('Invalid temperature unit. Expected: C or F')
|
||||
|
||||
|
||||
def time_zone(value):
|
||||
"""Validate timezone."""
|
||||
if dt_util.get_time_zone(value) is not None:
|
||||
return value
|
||||
raise vol.Invalid(
|
||||
'Invalid time zone passed in. Valid options can be found here: '
|
||||
'http://en.wikipedia.org/wiki/List_of_tz_database_time_zones')
|
@ -49,9 +49,7 @@ class EntityComponent(object):
|
||||
self.config = config
|
||||
|
||||
# Look in config for Domain, Domain 2, Domain 3 etc and load them
|
||||
for p_type, p_config in \
|
||||
config_per_platform(config, self.domain, self.logger):
|
||||
|
||||
for p_type, p_config in config_per_platform(config, self.domain):
|
||||
self._setup_platform(p_type, p_config)
|
||||
|
||||
if self.discovery_platforms:
|
||||
|
@ -5,6 +5,7 @@ pytz>=2015.4
|
||||
pip>=7.0.0
|
||||
vincenty==0.1.3
|
||||
jinja2>=2.8
|
||||
voluptuous==0.8.9
|
||||
|
||||
# homeassistant.components.isy994
|
||||
PyISY==1.0.5
|
||||
|
1
setup.py
1
setup.py
@ -17,6 +17,7 @@ REQUIRES = [
|
||||
'pip>=7.0.0',
|
||||
'vincenty==0.1.3',
|
||||
'jinja2>=2.8',
|
||||
'voluptuous==0.8.9',
|
||||
]
|
||||
|
||||
setup(
|
||||
|
@ -2,6 +2,7 @@
|
||||
# pylint: disable=protected-access,too-many-public-methods
|
||||
import unittest
|
||||
|
||||
from homeassistant.bootstrap import _setup_component
|
||||
from homeassistant.const import (
|
||||
STATE_ON, STATE_OFF, STATE_HOME, STATE_UNKNOWN, ATTR_ICON, ATTR_HIDDEN,
|
||||
ATTR_ASSUMED_STATE, )
|
||||
@ -218,19 +219,15 @@ class TestComponentsGroup(unittest.TestCase):
|
||||
test_group = group.Group(
|
||||
self.hass, 'init_group', ['light.Bowl', 'light.Ceiling'], False)
|
||||
|
||||
self.assertTrue(
|
||||
group.setup(
|
||||
self.hass,
|
||||
{
|
||||
group.DOMAIN: {
|
||||
'second_group': {
|
||||
'entities': 'light.Bowl, ' + test_group.entity_id,
|
||||
'icon': 'mdi:work',
|
||||
'view': True,
|
||||
},
|
||||
'test_group': 'hello.world,sensor.happy',
|
||||
}
|
||||
}))
|
||||
_setup_component(self.hass, 'group', {'group': {
|
||||
'second_group': {
|
||||
'entities': 'light.Bowl, ' + test_group.entity_id,
|
||||
'icon': 'mdi:work',
|
||||
'view': True,
|
||||
},
|
||||
'test_group': 'hello.world,sensor.happy',
|
||||
}
|
||||
})
|
||||
|
||||
group_state = self.hass.states.get(
|
||||
group.ENTITY_ID_FORMAT.format('second_group'))
|
||||
|
114
tests/helpers/test_config_validation.py
Normal file
114
tests/helpers/test_config_validation.py
Normal file
@ -0,0 +1,114 @@
|
||||
import pytest
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
|
||||
def test_latitude():
|
||||
"""Test latitude validation."""
|
||||
schema = vol.Schema(cv.latitude)
|
||||
|
||||
for value in ('invalid', None, -91, 91, '-91', '91', '123.01A'):
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
schema(value)
|
||||
|
||||
for value in ('-89', 89, '12.34'):
|
||||
schema(value)
|
||||
|
||||
|
||||
def test_longitude():
|
||||
"""Test longitude validation."""
|
||||
schema = vol.Schema(cv.longitude)
|
||||
|
||||
for value in ('invalid', None, -181, 181, '-181', '181', '123.01A'):
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
schema(value)
|
||||
|
||||
for value in ('-179', 179, '12.34'):
|
||||
schema(value)
|
||||
|
||||
|
||||
def test_icon():
|
||||
"""Test icon validation."""
|
||||
schema = vol.Schema(cv.icon)
|
||||
|
||||
for value in (False, 'work', 'icon:work'):
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
schema(value)
|
||||
|
||||
schema('mdi:work')
|
||||
|
||||
|
||||
def test_platform_config():
|
||||
"""Test platform config validation."""
|
||||
for value in (
|
||||
{'platform': 1},
|
||||
{},
|
||||
{'hello': 'world'},
|
||||
):
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
cv.PLATFORM_SCHEMA(value)
|
||||
|
||||
for value in (
|
||||
{'platform': 'mqtt'},
|
||||
{'platform': 'mqtt', 'beer': 'yes'},
|
||||
):
|
||||
cv.PLATFORM_SCHEMA(value)
|
||||
|
||||
|
||||
def test_entity_id():
|
||||
"""Test entity ID validation."""
|
||||
schema = vol.Schema(cv.entity_id)
|
||||
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
schema('invalid_entity')
|
||||
|
||||
schema('sensor.light')
|
||||
|
||||
|
||||
def test_entity_ids():
|
||||
"""Test entity ID validation."""
|
||||
schema = vol.Schema(cv.entity_ids)
|
||||
|
||||
for value in (
|
||||
'invalid_entity',
|
||||
'sensor.light,sensor_invalid',
|
||||
['invalid_entity'],
|
||||
['sensor.light', 'sensor_invalid'],
|
||||
['sensor.light,sensor_invalid'],
|
||||
):
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
schema(value)
|
||||
|
||||
for value in (
|
||||
[],
|
||||
['sensor.light'],
|
||||
'sensor.light'
|
||||
):
|
||||
schema(value)
|
||||
|
||||
assert schema('sensor.light, light.kitchen ') == [
|
||||
'sensor.light', 'light.kitchen'
|
||||
]
|
||||
|
||||
|
||||
def test_temperature_unit():
|
||||
"""Test temperature unit validation."""
|
||||
schema = vol.Schema(cv.temperature_unit)
|
||||
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
schema('K')
|
||||
|
||||
schema('C')
|
||||
schema('F')
|
||||
|
||||
|
||||
def test_time_zone():
|
||||
"""Test time zone validation."""
|
||||
schema = vol.Schema(cv.time_zone)
|
||||
|
||||
with pytest.raises(vol.MultipleInvalid):
|
||||
schema('America/Do_Not_Exist')
|
||||
|
||||
schema('America/Los_Angeles')
|
||||
schema('UTC')
|
@ -4,6 +4,9 @@ import unittest
|
||||
import unittest.mock as mock
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from voluptuous import MultipleInvalid
|
||||
|
||||
from homeassistant.core import DOMAIN, HomeAssistantError
|
||||
import homeassistant.config as config_util
|
||||
from homeassistant.const import (
|
||||
@ -138,3 +141,28 @@ class TestConfig(unittest.TestCase):
|
||||
config_util.create_default_config(
|
||||
os.path.join(CONFIG_DIR, 'non_existing_dir/'), False))
|
||||
self.assertTrue(mock_print.called)
|
||||
|
||||
def test_core_config_schema(self):
|
||||
for value in (
|
||||
{'temperature_unit': 'K'},
|
||||
{'time_zone': 'non-exist'},
|
||||
{'latitude': '91'},
|
||||
{'longitude': -181},
|
||||
{'customize': 'bla'},
|
||||
{'customize': {'invalid_entity_id': {}}},
|
||||
{'customize': {'light.sensor': 100}},
|
||||
):
|
||||
with pytest.raises(MultipleInvalid):
|
||||
config_util.CORE_CONFIG_SCHEMA(value)
|
||||
|
||||
config_util.CORE_CONFIG_SCHEMA({
|
||||
'name': 'Test name',
|
||||
'latitude': '-23.45',
|
||||
'longitude': '123.45',
|
||||
'temperature_unit': 'c',
|
||||
'customize': {
|
||||
'sensor.temperature': {
|
||||
'hidden': True,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user