Skip to content

Commit

Permalink
schema dump idea
Browse files Browse the repository at this point in the history
accept boolean or anything default

accept null also for full dicts

added some common validators

more simple validators

support multi_conf

better handle automations

updates

updates

handle lists

removed not needed class

move to own folder

generalized for automations lists, etc

updates

updates

clean up

clean up

fix automations

made comment optional

basic docs support

added more docs

fixes docs handling

updates

updates

fix components parent

updates

updates

updates

Fix inkplate 6 registration

updates

Disable logging for vscode add on

better handle buses

keep extended order as in CONFIGs

updates

updates

updates

disable comments

moved to scripts/build_jsonschema

added configurable decorators

path handling

fix handle list_schema

fixes and cleanup

add jschema_extractor to maybe

updates

lint

no schema in git

add generated loggers list
  • Loading branch information
glmnet committed Mar 7, 2021
1 parent 6987992 commit 4a95e33
Show file tree
Hide file tree
Showing 11 changed files with 770 additions and 12 deletions.
1 change: 1 addition & 0 deletions esphome/__main__.py
Expand Up @@ -320,6 +320,7 @@ def command_config(args, config):
def command_vscode(args):
from esphome import vscode

logging.disable(logging.INFO)
CORE.config_path = args.configuration[0]
vscode.read_config(args)

Expand Down
12 changes: 12 additions & 0 deletions esphome/automation.py
Expand Up @@ -11,6 +11,7 @@
CONF_TIME,
)
from esphome.core import coroutine
from esphome.jsonschema import jschema_extractor
from esphome.util import Registry


Expand All @@ -21,7 +22,12 @@ def maybe_simple_id(*validators):
def maybe_conf(conf, *validators):
validator = cv.All(*validators)

@jschema_extractor('maybe')
def validate(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
return validator

if isinstance(value, dict):
return validator(value)
with cv.remove_prepend_path([conf]):
Expand Down Expand Up @@ -103,7 +109,13 @@ def validator_(value):
# This should only happen with invalid configs, but let's have a nice error message.
return [schema(value)]

@jschema_extractor('automation')
def validator(value):
# hack to get the schema
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
return schema

value = validator_(value)
if extra_validators is not None:
value = cv.Schema([extra_validators])(value)
Expand Down
12 changes: 9 additions & 3 deletions esphome/components/canbus/__init__.py
Expand Up @@ -12,7 +12,6 @@
CONF_CANBUS_ID = "canbus_id"
CONF_BIT_RATE = "bit_rate"
CONF_ON_FRAME = "on_frame"
CONF_CANBUS_SEND = "canbus.send"


def validate_id(id_value, id_ext):
Expand Down Expand Up @@ -59,7 +58,7 @@ def validate_raw_data(value):
"1000KBPS": CanSpeed.CAN_1000KBPS,
}

CONFIG_SCHEMA = cv.Schema(
CANBUS_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CanbusComponent),
cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
Expand All @@ -70,6 +69,13 @@ def validate_raw_data(value):
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
cv.Optional(CONF_ON_FRAME): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
}
),
}
),
}
Expand Down Expand Up @@ -104,7 +110,7 @@ def register_canbus(var, config):

# Actions
@automation.register_action(
CONF_CANBUS_SEND,
"canbus.send",
canbus_ns.class_("CanbusSendAction", automation.Action),
cv.maybe_simple_value(
{
Expand Down
4 changes: 3 additions & 1 deletion esphome/components/inkplate6/display.py
Expand Up @@ -87,7 +87,9 @@
CONF_DISPLAY_DATA_7_PIN, default=27
): pins.internal_gpio_output_pin_schema,
}
).extend(cv.polling_component_schema("5s").extend(i2c.i2c_device_schema(0x48))),
)
.extend(cv.polling_component_schema("5s"))
.extend(i2c.i2c_device_schema(0x48)),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
)

Expand Down
2 changes: 1 addition & 1 deletion esphome/components/mcp2515/canbus.py
Expand Up @@ -26,7 +26,7 @@
"LISTENONLY": McpMode.CANCTRL_REQOP_LISTENONLY,
}

CONFIG_SCHEMA = canbus.CONFIG_SCHEMA.extend(
CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(mcp2515),
cv.Optional(CONF_CLOCK, default="8MHZ"): cv.enum(CAN_CLOCK, upper=True),
Expand Down
20 changes: 14 additions & 6 deletions esphome/components/remote_base/__init__.py
Expand Up @@ -29,6 +29,7 @@
CONF_RC_CODE_2,
)
from esphome.core import coroutine
from esphome.jsonschema import jschema_extractor
from esphome.util import Registry, SimpleRegistry

AUTO_LOAD = ["binary_sensor"]
Expand Down Expand Up @@ -123,13 +124,16 @@ def validate_repeat(value):
return validate_repeat({CONF_TIMES: value})


BASE_REMOTE_TRANSMITTER_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase),
cv.Optional(CONF_REPEAT): validate_repeat,
}
)


def register_action(name, type_, schema):
validator = templatize(schema).extend(
{
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase),
cv.Optional(CONF_REPEAT): validate_repeat,
}
)
validator = templatize(schema).extend(BASE_REMOTE_TRANSMITTER_SCHEMA)
registerer = automation.register_action(
f"remote_transmitter.transmit_{name}", type_, validator
)
Expand Down Expand Up @@ -190,11 +194,15 @@ def validate_dumpers(value):
def validate_triggers(base_schema):
assert isinstance(base_schema, cv.Schema)

@jschema_extractor("triggers")
def validator(config):
added_keys = {}
for key, (_, valid) in TRIGGER_REGISTRY.items():
added_keys[cv.Optional(key)] = valid
new_schema = base_schema.extend(added_keys)
# pylint: disable=comparison-with-callable
if config == jschema_extractor:
return new_schema
return new_schema(config)

return validator
Expand Down
5 changes: 5 additions & 0 deletions esphome/config_validation.py
Expand Up @@ -46,6 +46,7 @@
TimePeriodMinutes,
)
from esphome.helpers import list_starts_with, add_class_to_obj
from esphome.jsonschema import jschema_composite, jschema_registry, jschema_typed
from esphome.voluptuous_schema import _Schema
from esphome.yaml_util import make_data_base

Expand Down Expand Up @@ -306,6 +307,7 @@ def boolean(value):
)


@jschema_composite
def ensure_list(*validators):
"""Validate this configuration option to be a list.
Expand Down Expand Up @@ -1341,6 +1343,7 @@ def extract_keys(schema):
return keys


@jschema_typed
def typed_schema(schemas, **kwargs):
"""Create a schema that has a key to distinguish between schemas"""
key = kwargs.pop("key", CONF_TYPE)
Expand Down Expand Up @@ -1442,6 +1445,7 @@ def validate_registry_entry(name, registry):
)
ignore_keys = extract_keys(base_schema)

@jschema_registry(registry)
def validator(value):
if isinstance(value, str):
value = {value: {}}
Expand Down Expand Up @@ -1488,6 +1492,7 @@ def validate_registry(name, registry):
return ensure_list(validate_registry_entry(name, registry))


@jschema_composite
def maybe_simple_value(*validators, **kwargs):
key = kwargs.pop("key", CONF_VALUE)
validator = All(*validators)
Expand Down
1 change: 0 additions & 1 deletion esphome/const.py
Expand Up @@ -180,7 +180,6 @@
CONF_ENTITY_ID = "entity_id"
CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash"
CONF_ESPHOME = "esphome"
CONF_ESPHOME_CORE_VERSION = "esphome_core_version"
CONF_EVENT = "event"
CONF_EXPIRE_AFTER = "expire_after"
CONF_EXTERNAL_VCC = "external_vcc"
Expand Down
78 changes: 78 additions & 0 deletions esphome/jsonschema.py
@@ -0,0 +1,78 @@
# These are a helper decorators to help get schema from some
# components which uses volutuous in a way where validation
# is hidden in local functions

# These decorators should not modify at all what the functions
# originally do.
#
# However there is a property to further disable decorator
# impat.
#
# This is set to true by script/build_jsonschema.py
# only, so data is collected (again functionality is not modified)
EnableJsonSchemaCollect = False

extended_schemas = {}
list_schemas = {}
registry_schemas = {}
hidden_schemas = {}
typed_schemas = {}


def jschema_extractor(validator_name):
if EnableJsonSchemaCollect:
def decorator(func):
hidden_schemas[str(func)] = validator_name
return func
return decorator

def dummy(f):
return f
return dummy


def jschema_extended(func):
if EnableJsonSchemaCollect:
def decorate(*args, **kwargs):
ret = func(*args, **kwargs)
assert len(args) == 2
extended_schemas[str(ret)] = args
return ret
return decorate

return func


def jschema_composite(func):
if EnableJsonSchemaCollect:
def decorate(*args, **kwargs):
ret = func(*args, **kwargs)
# args length might be 2, but 2nd is always validator
list_schemas[str(ret)] = args
return ret
return decorate

return func


def jschema_registry(registry):
if EnableJsonSchemaCollect:
def decorator(func):
registry_schemas[str(func)] = registry
return func
return decorator

def dummy(f):
return f
return dummy


def jschema_typed(func):
if EnableJsonSchemaCollect:
def decorate(*args, **kwargs):
ret = func(*args, **kwargs)
typed_schemas[str(ret)] = (args, kwargs)
return ret
return decorate

return func
2 changes: 2 additions & 0 deletions esphome/voluptuous_schema.py
Expand Up @@ -2,6 +2,7 @@
import itertools

import voluptuous as vol
from esphome.jsonschema import jschema_extended


class ExtraKeysInvalid(vol.Invalid):
Expand Down Expand Up @@ -202,6 +203,7 @@ def add_extra(self, validator):
self._extra_schemas.append(validator)
return self

@jschema_extended
# pylint: disable=signature-differs
def extend(self, *schemas, **kwargs):
extra = kwargs.pop("extra", None)
Expand Down

0 comments on commit 4a95e33

Please sign in to comment.