mirror of
https://github.com/esphome/esphome.git
synced 2024-12-23 16:47:57 +01:00
parent
31d964c16a
commit
b7dff4bbab
@ -18,12 +18,12 @@ from esphome.components.substitutions import CONF_SUBSTITUTIONS
|
|||||||
from esphome.const import CONF_ESPHOME, CONF_PLATFORM, ESP_PLATFORMS
|
from esphome.const import CONF_ESPHOME, CONF_PLATFORM, ESP_PLATFORMS
|
||||||
from esphome.core import CORE, EsphomeError # noqa
|
from esphome.core import CORE, EsphomeError # noqa
|
||||||
from esphome.helpers import color, indent
|
from esphome.helpers import color, indent
|
||||||
from esphome.py_compat import text_type, IS_PY2, decode_text
|
from esphome.py_compat import text_type, IS_PY2, decode_text, string_types
|
||||||
from esphome.util import safe_print, OrderedDict
|
from esphome.util import safe_print, OrderedDict
|
||||||
|
|
||||||
from typing import List, Optional, Tuple, Union # noqa
|
from typing import List, Optional, Tuple, Union # noqa
|
||||||
from esphome.core import ConfigType # noqa
|
from esphome.core import ConfigType # noqa
|
||||||
from esphome.yaml_util import is_secret, ESPHomeDataBase
|
from esphome.yaml_util import is_secret, ESPHomeDataBase, ESPForceValue
|
||||||
from esphome.voluptuous_schema import ExtraKeysInvalid
|
from esphome.voluptuous_schema import ExtraKeysInvalid
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -380,6 +380,24 @@ def do_id_pass(result): # type: (Config) -> None
|
|||||||
result.add_str_error("Couldn't resolve ID for type '{}'".format(id.type), path)
|
result.add_str_error("Couldn't resolve ID for type '{}'".format(id.type), path)
|
||||||
|
|
||||||
|
|
||||||
|
def recursive_check_replaceme(value):
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
|
if isinstance(value, list):
|
||||||
|
return cv.Schema([recursive_check_replaceme])(value)
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return cv.Schema({cv.valid: recursive_check_replaceme})(value)
|
||||||
|
if isinstance(value, ESPForceValue):
|
||||||
|
pass
|
||||||
|
if isinstance(value, string_types) and value == 'REPLACEME':
|
||||||
|
raise cv.Invalid("Found 'REPLACEME' in configuration, this is most likely an error. "
|
||||||
|
"Please make sure you have replaced all fields from the sample "
|
||||||
|
"configuration.\n"
|
||||||
|
"If you want to use the literal REPLACEME string, "
|
||||||
|
"please use \"!force REPLACEME\"")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def validate_config(config):
|
def validate_config(config):
|
||||||
result = Config()
|
result = Config()
|
||||||
|
|
||||||
@ -393,6 +411,12 @@ def validate_config(config):
|
|||||||
result.add_error(err)
|
result.add_error(err)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
# 1.1. Check for REPLACEME special value
|
||||||
|
try:
|
||||||
|
recursive_check_replaceme(config)
|
||||||
|
except vol.Invalid as err:
|
||||||
|
result.add_error(err)
|
||||||
|
|
||||||
if 'esphomeyaml' in config:
|
if 'esphomeyaml' in config:
|
||||||
_LOGGER.warning("The esphomeyaml section has been renamed to esphome in 1.11.0. "
|
_LOGGER.warning("The esphomeyaml section has been renamed to esphome in 1.11.0. "
|
||||||
"Please replace 'esphomeyaml:' in your configuration with 'esphome:'.")
|
"Please replace 'esphomeyaml:' in your configuration with 'esphome:'.")
|
||||||
@ -588,7 +612,7 @@ def _nested_getitem(data, path):
|
|||||||
|
|
||||||
def humanize_error(config, validation_error):
|
def humanize_error(config, validation_error):
|
||||||
validation_error = text_type(validation_error)
|
validation_error = text_type(validation_error)
|
||||||
m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error)
|
m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error, re.DOTALL)
|
||||||
if m is not None:
|
if m is not None:
|
||||||
validation_error = m.group(1)
|
validation_error = m.group(1)
|
||||||
validation_error = validation_error.strip()
|
validation_error = validation_error.strip()
|
||||||
|
@ -19,7 +19,7 @@ from esphome.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY,
|
|||||||
CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, CONF_TYPE
|
CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, CONF_TYPE
|
||||||
from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
|
from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
|
||||||
TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes
|
TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes
|
||||||
from esphome.helpers import list_starts_with
|
from esphome.helpers import list_starts_with, add_class_to_obj
|
||||||
from esphome.py_compat import integer_types, string_types, text_type, IS_PY2, decode_text
|
from esphome.py_compat import integer_types, string_types, text_type, IS_PY2, decode_text
|
||||||
from esphome.voluptuous_schema import _Schema
|
from esphome.voluptuous_schema import _Schema
|
||||||
|
|
||||||
@ -964,11 +964,8 @@ def enum(mapping, **kwargs):
|
|||||||
one_of_validator = one_of(*mapping, **kwargs)
|
one_of_validator = one_of(*mapping, **kwargs)
|
||||||
|
|
||||||
def validator(value):
|
def validator(value):
|
||||||
from esphome.yaml_util import make_data_base
|
value = one_of_validator(value)
|
||||||
|
value = add_class_to_obj(value, core.EnumValue)
|
||||||
value = make_data_base(one_of_validator(value))
|
|
||||||
cls = value.__class__
|
|
||||||
value.__class__ = cls.__class__(cls.__name__ + "Enum", (cls, core.EnumValue), {})
|
|
||||||
value.enum_value = mapping[value]
|
value.enum_value = mapping[value]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -261,3 +261,51 @@ def file_compare(path1, path2):
|
|||||||
if not blob1:
|
if not blob1:
|
||||||
# Reached end
|
# Reached end
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# A dict of types that need to be converted to heaptypes before a class can be added
|
||||||
|
# to the object
|
||||||
|
_TYPE_OVERLOADS = {
|
||||||
|
int: type('int', (int,), dict()),
|
||||||
|
float: type('float', (float,), dict()),
|
||||||
|
str: type('str', (str,), dict()),
|
||||||
|
dict: type('dict', (str,), dict()),
|
||||||
|
list: type('list', (list,), dict()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if IS_PY2:
|
||||||
|
_TYPE_OVERLOADS[long] = type('long', (long,), dict())
|
||||||
|
_TYPE_OVERLOADS[unicode] = type('unicode', (unicode,), dict())
|
||||||
|
|
||||||
|
# cache created classes here
|
||||||
|
_CLASS_LOOKUP = {}
|
||||||
|
|
||||||
|
|
||||||
|
def add_class_to_obj(value, cls):
|
||||||
|
"""Add a class to a python type.
|
||||||
|
|
||||||
|
This function modifies value so that it has cls as a basetype.
|
||||||
|
The value itself may be modified by this action! You must use the return
|
||||||
|
value of this function however, since some types need to be copied first (heaptypes).
|
||||||
|
"""
|
||||||
|
if isinstance(value, cls):
|
||||||
|
# If already is instance, do not add
|
||||||
|
return value
|
||||||
|
|
||||||
|
try:
|
||||||
|
orig_cls = value.__class__
|
||||||
|
key = (orig_cls, cls)
|
||||||
|
new_cls = _CLASS_LOOKUP.get(key)
|
||||||
|
if new_cls is None:
|
||||||
|
new_cls = orig_cls.__class__(orig_cls.__name__, (orig_cls, cls), {})
|
||||||
|
_CLASS_LOOKUP[key] = new_cls
|
||||||
|
value.__class__ = new_cls
|
||||||
|
return value
|
||||||
|
except TypeError:
|
||||||
|
# Non heap type, look in overloads dict
|
||||||
|
for type_, func in _TYPE_OVERLOADS.items():
|
||||||
|
# Use type() here, we only need to trigger if it's the exact type,
|
||||||
|
# as otherwise we don't need to overload the class
|
||||||
|
if type(value) is type_: # pylint: disable=unidiomatic-typecheck
|
||||||
|
return add_class_to_obj(func(value), cls)
|
||||||
|
raise
|
||||||
|
@ -2,6 +2,7 @@ from __future__ import print_function
|
|||||||
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import functools
|
import functools
|
||||||
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
@ -13,6 +14,7 @@ import yaml.constructor
|
|||||||
from esphome import core
|
from esphome import core
|
||||||
from esphome.config_helpers import read_config_file
|
from esphome.config_helpers import read_config_file
|
||||||
from esphome.core import EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, DocumentRange
|
from esphome.core import EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, DocumentRange
|
||||||
|
from esphome.helpers import add_class_to_obj
|
||||||
from esphome.py_compat import text_type, IS_PY2
|
from esphome.py_compat import text_type, IS_PY2
|
||||||
from esphome.util import OrderedDict, filter_yaml_files
|
from esphome.util import OrderedDict, filter_yaml_files
|
||||||
|
|
||||||
@ -26,14 +28,6 @@ _SECRET_CACHE = {}
|
|||||||
_SECRET_VALUES = {}
|
_SECRET_VALUES = {}
|
||||||
|
|
||||||
|
|
||||||
class NodeListClass(list):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NodeStrClass(text_type):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ESPHomeDataBase(object):
|
class ESPHomeDataBase(object):
|
||||||
@property
|
@property
|
||||||
def esp_range(self):
|
def esp_range(self):
|
||||||
@ -44,56 +38,25 @@ class ESPHomeDataBase(object):
|
|||||||
self._esp_range = DocumentRange.from_marks(node.start_mark, node.end_mark)
|
self._esp_range = DocumentRange.from_marks(node.start_mark, node.end_mark)
|
||||||
|
|
||||||
|
|
||||||
class ESPInt(int, ESPHomeDataBase):
|
class ESPForceValue(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ESPFloat(float, ESPHomeDataBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ESPStr(str, ESPHomeDataBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ESPDict(OrderedDict, ESPHomeDataBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ESPList(list, ESPHomeDataBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ESPLambda(Lambda, ESPHomeDataBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
ESP_TYPES = {
|
|
||||||
int: ESPInt,
|
|
||||||
float: ESPFloat,
|
|
||||||
str: ESPStr,
|
|
||||||
dict: ESPDict,
|
|
||||||
list: ESPList,
|
|
||||||
Lambda: ESPLambda,
|
|
||||||
}
|
|
||||||
if IS_PY2:
|
|
||||||
class ESPUnicode(unicode, ESPHomeDataBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
ESP_TYPES[unicode] = ESPUnicode
|
|
||||||
|
|
||||||
|
|
||||||
def make_data_base(value):
|
def make_data_base(value):
|
||||||
for typ, cons in ESP_TYPES.items():
|
return add_class_to_obj(value, ESPHomeDataBase)
|
||||||
if isinstance(value, typ):
|
|
||||||
return cons(value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def _add_data_ref(fn):
|
def _add_data_ref(fn):
|
||||||
@functools.wraps(fn)
|
@functools.wraps(fn)
|
||||||
def wrapped(loader, node):
|
def wrapped(loader, node):
|
||||||
res = fn(loader, node)
|
res = fn(loader, node)
|
||||||
|
# newer PyYAML versions use generators, resolve them
|
||||||
|
if inspect.isgenerator(res):
|
||||||
|
generator = res
|
||||||
|
res = next(generator)
|
||||||
|
# Let generator finish
|
||||||
|
for _ in generator:
|
||||||
|
pass
|
||||||
res = make_data_base(res)
|
res = make_data_base(res)
|
||||||
if isinstance(res, ESPHomeDataBase):
|
if isinstance(res, ESPHomeDataBase):
|
||||||
res.from_node(node)
|
res.from_node(node)
|
||||||
@ -296,6 +259,11 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
|
|||||||
def construct_lambda(self, node):
|
def construct_lambda(self, node):
|
||||||
return Lambda(text_type(node.value))
|
return Lambda(text_type(node.value))
|
||||||
|
|
||||||
|
@_add_data_ref
|
||||||
|
def construct_force(self, node):
|
||||||
|
obj = self.construct_scalar(node)
|
||||||
|
return add_class_to_obj(obj, ESPForceValue)
|
||||||
|
|
||||||
|
|
||||||
ESPHomeLoader.add_constructor(u'tag:yaml.org,2002:int', ESPHomeLoader.construct_yaml_int)
|
ESPHomeLoader.add_constructor(u'tag:yaml.org,2002:int', ESPHomeLoader.construct_yaml_int)
|
||||||
ESPHomeLoader.add_constructor(u'tag:yaml.org,2002:float', ESPHomeLoader.construct_yaml_float)
|
ESPHomeLoader.add_constructor(u'tag:yaml.org,2002:float', ESPHomeLoader.construct_yaml_float)
|
||||||
@ -314,6 +282,7 @@ ESPHomeLoader.add_constructor('!include_dir_named', ESPHomeLoader.construct_incl
|
|||||||
ESPHomeLoader.add_constructor('!include_dir_merge_named',
|
ESPHomeLoader.add_constructor('!include_dir_merge_named',
|
||||||
ESPHomeLoader.construct_include_dir_merge_named)
|
ESPHomeLoader.construct_include_dir_merge_named)
|
||||||
ESPHomeLoader.add_constructor('!lambda', ESPHomeLoader.construct_lambda)
|
ESPHomeLoader.add_constructor('!lambda', ESPHomeLoader.construct_lambda)
|
||||||
|
ESPHomeLoader.add_constructor('!force', ESPHomeLoader.construct_force)
|
||||||
|
|
||||||
|
|
||||||
def load_yaml(fname):
|
def load_yaml(fname):
|
||||||
|
Loading…
Reference in New Issue
Block a user