mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 08:28:12 +01:00
Add more json schema generation features (#1690)
* some enums * extract enums, light effects remote_receiver etc * more pins schema * update to core changes
This commit is contained in:
parent
4f6982fbc5
commit
3d6dcc9eee
3 changed files with 60 additions and 9 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
from esphome.jsonschema import jschema_extractor
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
|
@ -452,7 +453,11 @@ def addressable_flicker_effect_to_code(config, effect_id):
|
||||||
|
|
||||||
|
|
||||||
def validate_effects(allowed_effects):
|
def validate_effects(allowed_effects):
|
||||||
|
@jschema_extractor("effects")
|
||||||
def validator(value):
|
def validator(value):
|
||||||
|
# pylint: disable=comparison-with-callable
|
||||||
|
if value == jschema_extractor:
|
||||||
|
return (allowed_effects, EFFECTS_REGISTRY)
|
||||||
value = cv.validate_registry("effect", EFFECTS_REGISTRY)(value)
|
value = cv.validate_registry("effect", EFFECTS_REGISTRY)(value)
|
||||||
errors = []
|
errors = []
|
||||||
names = set()
|
names = set()
|
||||||
|
|
|
@ -46,7 +46,13 @@ from esphome.core import (
|
||||||
TimePeriodMinutes,
|
TimePeriodMinutes,
|
||||||
)
|
)
|
||||||
from esphome.helpers import list_starts_with, add_class_to_obj
|
from esphome.helpers import list_starts_with, add_class_to_obj
|
||||||
from esphome.jsonschema import jschema_composite, jschema_registry, jschema_typed
|
from esphome.jsonschema import (
|
||||||
|
jschema_composite,
|
||||||
|
jschema_extractor,
|
||||||
|
jschema_registry,
|
||||||
|
jschema_typed,
|
||||||
|
)
|
||||||
|
|
||||||
from esphome.voluptuous_schema import _Schema
|
from esphome.voluptuous_schema import _Schema
|
||||||
from esphome.yaml_util import make_data_base
|
from esphome.yaml_util import make_data_base
|
||||||
|
|
||||||
|
@ -1121,7 +1127,12 @@ def one_of(*values, **kwargs):
|
||||||
if kwargs:
|
if kwargs:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
@jschema_extractor("one_of")
|
||||||
def validator(value):
|
def validator(value):
|
||||||
|
# pylint: disable=comparison-with-callable
|
||||||
|
if value == jschema_extractor:
|
||||||
|
return values
|
||||||
|
|
||||||
if string_:
|
if string_:
|
||||||
value = string(value)
|
value = string(value)
|
||||||
value = value.replace(" ", space)
|
value = value.replace(" ", space)
|
||||||
|
@ -1161,7 +1172,12 @@ def enum(mapping, **kwargs):
|
||||||
assert isinstance(mapping, dict)
|
assert isinstance(mapping, dict)
|
||||||
one_of_validator = one_of(*mapping, **kwargs)
|
one_of_validator = one_of(*mapping, **kwargs)
|
||||||
|
|
||||||
|
@jschema_extractor("enum")
|
||||||
def validator(value):
|
def validator(value):
|
||||||
|
# pylint: disable=comparison-with-callable
|
||||||
|
if value == jschema_extractor:
|
||||||
|
return mapping
|
||||||
|
|
||||||
value = one_of_validator(value)
|
value = one_of_validator(value)
|
||||||
value = add_class_to_obj(value, core.EnumValue)
|
value = add_class_to_obj(value, core.EnumValue)
|
||||||
value.enum_value = mapping[value]
|
value.enum_value = mapping[value]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from esphome.cpp_generator import MockObj
|
||||||
import json
|
import json
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
@ -54,7 +55,7 @@ def is_ref(jschema):
|
||||||
|
|
||||||
|
|
||||||
def unref(jschema):
|
def unref(jschema):
|
||||||
return definitions[jschema[JSC_REF][len("#/definitions/") :]]
|
return definitions.get(jschema[JSC_REF][len("#/definitions/") :])
|
||||||
|
|
||||||
|
|
||||||
def add_definition_array_or_single_object(ref):
|
def add_definition_array_or_single_object(ref):
|
||||||
|
@ -104,8 +105,11 @@ def add_registry(registry_name, registry):
|
||||||
for name in registry.keys():
|
for name in registry.keys():
|
||||||
schema = get_jschema(str(name), registry[name].schema, create_return_ref=False)
|
schema = get_jschema(str(name), registry[name].schema, create_return_ref=False)
|
||||||
if not schema:
|
if not schema:
|
||||||
schema = {"type": "string"}
|
schema = {"type": "null"}
|
||||||
o_schema = {"type": "object", JSC_PROPERTIES: {name: schema}}
|
o_schema = {"type": "object", JSC_PROPERTIES: {name: schema}}
|
||||||
|
o_schema = create_ref(
|
||||||
|
registry_name + "-" + name, str(registry[name].schema) + "x", o_schema
|
||||||
|
)
|
||||||
validators.append(o_schema)
|
validators.append(o_schema)
|
||||||
definitions[registry_name] = {JSC_ANYOF: validators}
|
definitions[registry_name] = {JSC_ANYOF: validators}
|
||||||
|
|
||||||
|
@ -134,7 +138,7 @@ def add_module_schemas(name, module):
|
||||||
|
|
||||||
|
|
||||||
def get_dirs():
|
def get_dirs():
|
||||||
from esphome.config import CORE_COMPONENTS_PATH
|
from esphome.loader import CORE_COMPONENTS_PATH
|
||||||
|
|
||||||
dir_names = [
|
dir_names = [
|
||||||
d
|
d
|
||||||
|
@ -146,7 +150,7 @@ def get_dirs():
|
||||||
|
|
||||||
|
|
||||||
def get_logger_tags():
|
def get_logger_tags():
|
||||||
from esphome.config import CORE_COMPONENTS_PATH
|
from esphome.loader import CORE_COMPONENTS_PATH
|
||||||
import glob
|
import glob
|
||||||
|
|
||||||
pattern = re.compile(r'^static const char(\*\s|\s\*)TAG = "(\w.*)";', re.MULTILINE)
|
pattern = re.compile(r'^static const char(\*\s|\s\*)TAG = "(\w.*)";', re.MULTILINE)
|
||||||
|
@ -241,7 +245,7 @@ def add_components():
|
||||||
|
|
||||||
elif c.config_schema is not None:
|
elif c.config_schema is not None:
|
||||||
# adds root components which are not platforms, e.g. api: logger:
|
# adds root components which are not platforms, e.g. api: logger:
|
||||||
if c.is_multi_conf:
|
if c.multi_conf:
|
||||||
schema = get_jschema(domain, c.config_schema)
|
schema = get_jschema(domain, c.config_schema)
|
||||||
schema = add_definition_array_or_single_object(schema)
|
schema = add_definition_array_or_single_object(schema)
|
||||||
else:
|
else:
|
||||||
|
@ -322,7 +326,6 @@ def get_entry(parent_key, vschema):
|
||||||
elif str(vschema) in ejs.list_schemas:
|
elif str(vschema) in ejs.list_schemas:
|
||||||
ref = get_jschema(parent_key, ejs.list_schemas[str(vschema)][0])
|
ref = get_jschema(parent_key, ejs.list_schemas[str(vschema)][0])
|
||||||
entry = {JSC_ANYOF: [ref, {"type": "array", "items": ref}]}
|
entry = {JSC_ANYOF: [ref, {"type": "array", "items": ref}]}
|
||||||
|
|
||||||
elif str(vschema) in ejs.typed_schemas:
|
elif str(vschema) in ejs.typed_schemas:
|
||||||
schema_types = [{"type": "object", "properties": {"type": {"type": "string"}}}]
|
schema_types = [{"type": "object", "properties": {"type": {"type": "string"}}}]
|
||||||
entry = {"allOf": schema_types}
|
entry = {"allOf": schema_types}
|
||||||
|
@ -342,6 +345,22 @@ def get_entry(parent_key, vschema):
|
||||||
entry = get_automation_schema(parent_key, inner_vschema)
|
entry = get_automation_schema(parent_key, inner_vschema)
|
||||||
elif type == "maybe":
|
elif type == "maybe":
|
||||||
entry = get_jschema(parent_key, inner_vschema)
|
entry = get_jschema(parent_key, inner_vschema)
|
||||||
|
elif type == "one_of":
|
||||||
|
entry = {"enum": list(inner_vschema)}
|
||||||
|
elif type == "enum":
|
||||||
|
entry = {"enum": list(inner_vschema.keys())}
|
||||||
|
elif type == "effects":
|
||||||
|
# Like list schema but subset from list.
|
||||||
|
subset_list = inner_vschema[0]
|
||||||
|
# get_jschema('strobex', registry['strobe'].schema)
|
||||||
|
registry_schemas = []
|
||||||
|
for name in subset_list:
|
||||||
|
registry_schemas.append(get_ref("light.EFFECTS_REGISTRY-" + name))
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
JSC_ANYOF: [{"type": "array", "items": {JSC_ANYOF: registry_schemas}}]
|
||||||
|
}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown extracted schema type")
|
raise ValueError("Unknown extracted schema type")
|
||||||
elif str(vschema).startswith("<function invalid."):
|
elif str(vschema).startswith("<function invalid."):
|
||||||
|
@ -374,7 +393,10 @@ def default_schema():
|
||||||
|
|
||||||
def is_default_schema(jschema):
|
def is_default_schema(jschema):
|
||||||
if is_ref(jschema):
|
if is_ref(jschema):
|
||||||
return is_default_schema(unref(jschema))
|
jschema = unref(jschema)
|
||||||
|
if not jschema:
|
||||||
|
return False
|
||||||
|
return is_default_schema(jschema)
|
||||||
return "type" in jschema and jschema["type"] == default_schema()["type"]
|
return "type" in jschema and jschema["type"] == default_schema()["type"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -512,6 +534,10 @@ def convert_schema(path, vschema, un_extend=True):
|
||||||
|
|
||||||
# When schema contains all, all also has a schema which points
|
# When schema contains all, all also has a schema which points
|
||||||
# back to the containing schema
|
# back to the containing schema
|
||||||
|
|
||||||
|
if isinstance(vschema, MockObj):
|
||||||
|
return output
|
||||||
|
|
||||||
while hasattr(vschema, "schema") and not hasattr(vschema, "validators"):
|
while hasattr(vschema, "schema") and not hasattr(vschema, "validators"):
|
||||||
vschema = vschema.schema
|
vschema = vschema.schema
|
||||||
|
|
||||||
|
@ -531,7 +557,6 @@ def convert_schema(path, vschema, un_extend=True):
|
||||||
output = val_schema
|
output = val_schema
|
||||||
else:
|
else:
|
||||||
output = {**output, **val_schema}
|
output = {**output, **val_schema}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
if not vschema:
|
if not vschema:
|
||||||
|
@ -679,6 +704,7 @@ def dump_schema():
|
||||||
pins.output_pin,
|
pins.output_pin,
|
||||||
pins.input_pin,
|
pins.input_pin,
|
||||||
pins.input_pullup_pin,
|
pins.input_pullup_pin,
|
||||||
|
cv.float_with_unit,
|
||||||
cv.subscribe_topic,
|
cv.subscribe_topic,
|
||||||
cv.publish_topic,
|
cv.publish_topic,
|
||||||
cv.mqtt_payload,
|
cv.mqtt_payload,
|
||||||
|
@ -698,9 +724,13 @@ def dump_schema():
|
||||||
|
|
||||||
for v in [pins.gpio_input_pin_schema, pins.gpio_input_pullup_pin_schema]:
|
for v in [pins.gpio_input_pin_schema, pins.gpio_input_pullup_pin_schema]:
|
||||||
schema_registry[v] = get_ref("PIN.GPIO_FULL_INPUT_PIN_SCHEMA")
|
schema_registry[v] = get_ref("PIN.GPIO_FULL_INPUT_PIN_SCHEMA")
|
||||||
|
for v in [pins.internal_gpio_input_pin_schema, pins.input_pin]:
|
||||||
|
schema_registry[v] = get_ref("PIN.INPUT_INTERNAL")
|
||||||
|
|
||||||
for v in [pins.gpio_output_pin_schema, pins.internal_gpio_output_pin_schema]:
|
for v in [pins.gpio_output_pin_schema, pins.internal_gpio_output_pin_schema]:
|
||||||
schema_registry[v] = get_ref("PIN.GPIO_FULL_OUTPUT_PIN_SCHEMA")
|
schema_registry[v] = get_ref("PIN.GPIO_FULL_OUTPUT_PIN_SCHEMA")
|
||||||
|
for v in [pins.internal_gpio_output_pin_schema, pins.output_pin]:
|
||||||
|
schema_registry[v] = get_ref("PIN.OUTPUT_INTERNAL")
|
||||||
|
|
||||||
add_module_schemas("CONFIG", cv)
|
add_module_schemas("CONFIG", cv)
|
||||||
get_jschema("POLLING_COMPONENT", cv.polling_component_schema("60s"))
|
get_jschema("POLLING_COMPONENT", cv.polling_component_schema("60s"))
|
||||||
|
|
Loading…
Reference in a new issue