schema-filters (#1052)

* pin schema and other fixes

* schema filters and registries
This commit is contained in:
Guillermo Ruffino 2021-03-11 15:31:31 -03:00 committed by GitHub
parent 979e0a2d57
commit f6c277c4f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 284 additions and 205 deletions

View File

@ -85,23 +85,40 @@ of these entries matters!)
return {};
}
Supported filters:
``invert``
**********
- **invert**: Simple filter that just inverts every value from the binary sensor.
- **delayed_on**: When a signal ON is received, wait for the specified time period until publishing
an ON state. If an OFF value is received while waiting, the ON action is discarded. Or in other words:
Only send an ON value if the binary sensor has stayed ON for at least the specified time period.
**Useful for debouncing push buttons**.
- **delayed_off**: When a signal OFF is received, wait for the specified time period until publishing
an OFF state. If an ON value is received while waiting, the OFF action is discarded. Or in other words:
Only send an OFF value if the binary sensor has stayed OFF for at least the specified time period.
**Useful for debouncing push buttons**.
- **delayed_on_off**: Only send an ON or OFF value if the binary sensor has stayed in the same state
for at least the specified time period.
**Useful for debouncing binary switches**.
- **lambda**: Specify any :ref:`lambda <config-lambda>` for more complex filters. The input value from
the binary sensor is ``x`` and you can return ``true`` for ON, ``false`` for OFF, and ``{}`` to stop
the filter chain.
Simple filter that just inverts every value from the binary sensor.
``delayed_on``
**************
(**Required**, :ref:`config-time`): When a signal ON is received, wait for the specified time period until publishing
an ON state. If an OFF value is received while waiting, the ON action is discarded. Or in other words:
Only send an ON value if the binary sensor has stayed ON for at least the specified time period.
**Useful for debouncing push buttons**.
``delayed_off``
***************
(**Required**, :ref:`config-time`): When a signal OFF is received, wait for the specified time period until publishing
an OFF state. If an ON value is received while waiting, the OFF action is discarded. Or in other words:
Only send an OFF value if the binary sensor has stayed OFF for at least the specified time period.
**Useful for debouncing push buttons**.
``delayed_on_off``
******************
(**Required**, :ref:`config-time`): Only send an ON or OFF value if the binary sensor has stayed in the same state
for at least the specified time period.
**Useful for debouncing binary switches**.
``lambda``
**********
Specify any :ref:`lambda <config-lambda>` for more complex filters. The input value from
the binary sensor is ``x`` and you can return ``true`` for ON, ``false`` for OFF, and ``{}`` to stop
the filter chain.
Binary Sensor Automation
------------------------

View File

@ -42,7 +42,7 @@ There are numerous board types out there. Some initialize differently as well. T
update_interval: 5s
Configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~
************************
- **model** (**Required**, "See Models Below"): This the model to use. INITR_BLACKTAB is the default
- **cs_pin** (**Required**, :ref:`Pin Schema <config-pin_schema>`): The CS pin.
@ -55,7 +55,7 @@ Configuration variables:
- **reset_pin** (*Optional*, :ref:`Pin Schema <config-pin_schema>`): The RESET pin.
Memory notes:
~~~~~~~~~~~~~
*************
- 8Bit color saves 50% of the buffer required.
- eightbitcolor: True 160x128 = 20480 *Important for memory constrained devices*
@ -63,7 +63,7 @@ Memory notes:
Models:
~~~~~~~
*******
- INITR_GREENTAB
- INITR_REDTAB

View File

@ -52,7 +52,7 @@ The MCP23008 component (`datasheet <http://ww1.microchip.com/downloads/en/device
inverted: False
Configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~
************************
- **id** (**Required**, :ref:`config-id`): The id to use for this MCP23008 component.
- **address** (*Optional*, int): The I²C address of the driver.
@ -62,7 +62,7 @@ Configuration variables:
will require a pull-up resistor (to 3.3 volts) when this mode is enabled.
Pin configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****************************
- **mcp23xxx** (**Required**, :ref:`config-id`): The id of the MCP23008 component.
- **interrupt** (*Optional*): Set this pin to trigger the INT pin on the component. Can be one of ``CHANGE``, ``RISING``, ``FALLING``.
@ -114,14 +114,14 @@ has 16 GPIOs and can be configured the same way than the other variants.
Configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~
************************
- **id** (**Required**, :ref:`config-id`): The id to use for this MCP23016 component.
- **address** (*Optional*, int): The I²C address of the driver.
Defaults to ``0x20``.
Pin configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****************************
- **mcp23xxx** (**Required**, :ref:`config-id`): The id of the MCP23016 component.
- All other options from :ref:`Pin Schema <config-pin_schema>`
@ -173,7 +173,7 @@ binary sensor or GPIO switch.
inverted: False
Configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~
************************
- **id** (**Required**, :ref:`config-id`): The id to use for this MCP23017 component.
- **address** (*Optional*, int): The I²C address of the driver.
@ -183,7 +183,7 @@ Configuration variables:
will require pull-up resistors (to 3.3 volts) when this mode is enabled.
Pin configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****************************
- **mcp23xxx** (**Required**, :ref:`config-id`): The id of the MCP23017 component.
- **interrupt** (*Optional*): Set this pin to trigger the port INT pin on the component. Can be one of ``CHANGE``, ``RISING``, ``FALLING``.

View File

@ -54,7 +54,7 @@ The MCP23S08 component (`datasheet <http://ww1.microchip.com/downloads/en/Device
inverted: False
Configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~
************************
- **id** (**Required**, :ref:`config-id`): The id to use for this MCP23S08 component.
- **cs_pin** (**Required**, int): The SPI chip select pin to use
@ -65,7 +65,7 @@ Configuration variables:
will require pull-up resistors (to 3.3 volts) when this mode is enabled.
Pin Configuration Variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****************************
- **mcp23xxx** (**Required**, :ref:`config-id`): The id of the MCP23S08 component.
- **interrupt** (*Optional*): Set this pin to trigger the INT pin on the component. Can be one of ``CHANGE``, ``RISING``, ``FALLING``.
@ -119,7 +119,7 @@ binary sensor or GPIO switch.
inverted: False
Configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~
************************
- **id** (**Required**, :ref:`config-id`): The id to use for this MCP23S17 component.
- **cs_pin** (**Required**, int): The SPI chip select pin to use.
@ -130,7 +130,7 @@ Configuration variables:
will require pull-up resistors (to 3.3 volts) when this mode is enabled.
Pin Configuration Variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****************************
- **mcp23xxx** (**Required**, :ref:`config-id`): The id of the MCP23S17 component.
- **interrupt** (*Optional*): Set this pin to trigger the port INT pin on the component. Can be one of ``CHANGE``, ``RISING``, ``FALLING``.

View File

@ -132,8 +132,10 @@ for platforms with multiple sensors)
- delta: 5.0
- lambda: return x * (9.0/5.0) + 32.0;
``offset`` / ``multiply``
*************************
``offset``
**********
Adds a constant value to each sensor value.
.. code-block:: yaml
@ -144,8 +146,10 @@ for platforms with multiple sensors)
- offset: 2.0
- multiply: 1.2
Offset adds a constant value to each sensor value. Multiply multiplies each value
by a constant value.
``multiply``
************
Multiplies each value by a constant value.
.. _sensor-filter-calibrate_linear:
@ -205,7 +209,7 @@ degree with a least squares solver.
``filter_out``
**************
Filter out specific values to be displayed. For example to filter out the value ``85.0``
(**Required**, number): Filter out specific values to be displayed. For example to filter out the value ``85.0``
.. code-block:: yaml
@ -218,8 +222,9 @@ Filter out specific values to be displayed. For example to filter out the value
``median``
**********
Calculate moving median over the data. This can be used to filter outliers from the received
sensor data. A large window size will make the filter slow to react to input changes.
A `simple moving median <https://en.wikipedia.org/wiki/Median_filter#Worked_1D_example>`__
over the last few values. This can be used to filter outliers from the received sensor data. A large
window size will make the filter slow to react to input changes.
.. code-block:: yaml
@ -232,27 +237,25 @@ sensor data. A large window size will make the filter slow to react to input cha
send_every: 4
send_first_at: 3
- **median**: A `simple moving median
<https://en.wikipedia.org/wiki/Median_filter#Worked_1D_example>`__
over the last few values.
Configuration variables:
- **window_size**: The number of values over which to calculate the median
when pushing out a value. This number should
be odd if you want an actual received value pushed out.
Defaults to ``5``.
- **send_every**: How often a sensor value should be pushed out. For
example, in above configuration the median is calculated after every 4th
received sensor value, over the last 7 received values.
Defaults to ``5``.
- **send_first_at**: By default, the very first raw value on boot is immediately
published. With this parameter you can specify when the very first value is to be sent.
Must be smaller than or equal to ``send_every``
Defaults to ``1``.
- **window_size** (*Optional*, integer): The number of values over which to calculate the median
when pushing out a value. This number should
be odd if you want an actual received value pushed out.
Defaults to ``5``.
- **send_every** (*Optional*, integer): How often a sensor value should be pushed out. For
example, in above configuration the median is calculated after every 4th
received sensor value, over the last 7 received values.
Defaults to ``5``.
- **send_first_at** (*Optional*, integer): By default, the very first raw value on boot is immediately
published. With this parameter you can specify when the very first value is to be sent.
Must be smaller than or equal to ``send_every``
Defaults to ``1``.
``min`` / ``max``
*****************
``min``
*******
Calculate min/max over the data. A large window size will make the filter slow to
A moving minimum over the last few values. A large window size will make the filter slow to
react to input changes.
.. code-block:: yaml
@ -266,29 +269,46 @@ react to input changes.
send_every: 4
send_first_at: 3
- **min**: A moving minimum over the last few values.
Configuration variables:
- **max**: A moving maximum over the last few values.
- **window_size** (*Optional*, integer): The number of values over which to calculate the min/max when pushing out a
value. Defaults to ``5``.
- **send_every** (*Optional*, integer): How often a sensor value should be pushed out. For
example, in above configuration the min is calculated after every 4th
received sensor value, over the last 7 received values.
Defaults to ``5``.
- **send_first_at** (*Optional*, integer): By default, the very first raw value on boot is immediately
published. With this parameter you can specify when the very first value is to be sent.
Must be smaller than or equal to ``send_every``
Defaults to ``1``.
- Both accept the following parameters:
``max``
*******
- **window_size**: The number of values over which to calculate the min/max
when pushing out a value.
Defaults to ``5``.
- **send_every**: How often a sensor value should be pushed out. For
example, in above configuration the min is calculated after every 4th
received sensor value, over the last 7 received values.
Defaults to ``5``.
- **send_first_at**: By default, the very first raw value on boot is immediately
published. With this parameter you can specify when the very first value is to be sent.
Must be smaller than or equal to ``send_every``
Defaults to ``1``.
A moving maximum over the last few values. A large window size will make the filter slow to
react to input changes.
``sliding_window_moving_average`` / ``exponential_moving_average``
******************************************************************
Configuration variables:
Two simple moving averages over the data. These can be used to have a short update interval
on the sensor but only push out an average on a specific interval (thus increasing resolution).
- **window_size** (*Optional*, integer): The number of values over which to calculate the min/max
when pushing out a value.
Defaults to ``5``.
- **send_every** (*Optional*, integer): How often a sensor value should be pushed out. For
example, in above configuration the min is calculated after every 4th
received sensor value, over the last 7 received values.
Defaults to ``5``.
- **send_first_at** (*Optional*, integer): By default, the very first raw value on boot is immediately
published. With this parameter you can specify when the very first value is to be sent.
Must be smaller than or equal to ``send_every``
Defaults to ``1``.
``sliding_window_moving_average``
*********************************
A `simple moving average <https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average>`__
over the last few values. It can be used to have a short update interval on the sensor but only push
out an average on a specific interval (thus increasing resolution).
.. code-block:: yaml
@ -300,28 +320,36 @@ on the sensor but only push out an average on a specific interval (thus increasi
window_size: 15
send_every: 15
- **sliding_window_moving_average**: A `simple moving
average <https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average>`__
over the last few values.
Configuration variables:
- **window_size**: The number of values over which to perform an
average when pushing out a value.
- **send_every**: How often a sensor value should be pushed out. For
example, in above configuration the weighted average is only
pushed out on every 15th received sensor value.
- **send_first_at**: By default, the very first raw value on boot is immediately
published. With this parameter you can specify when the very first value is to be sent.
Defaults to ``1``.
- **window_size** (*Optional*, integer): The number of values over which to perform an
average when pushing out a value.
- **send_every** (*Optional*, integer): How often a sensor value should be pushed out. For
example, in above configuration the weighted average is only
pushed out on every 15th received sensor value.
- **send_first_at** (*Optional*, integer): By default, the very first raw value on boot is immediately
published. With this parameter you can specify when the very first value is to be sent.
Defaults to ``1``.
- **exponential_moving_average**: A simple `exponential moving
average <https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average>`__
over the last few values.
``exponential_moving_average``
******************************
- **alpha**: The forget factor/alpha value of the filter.
- **send_every**: How often a sensor value should be pushed out.
A simple `exponential moving average
<https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average>`__ over the last few
values. It can be used to have a short update interval on the sensor but only push
out an average on a specific interval (thus increasing resolution).
``throttle`` / ``heartbeat`` / ``debounce`` / ``delta``
*******************************************************
Configuration variables:
- **alpha** (*Optional*, float): The forget factor/alpha value of the filter. Defaults to ``0.1``.
- **send_every** (*Optional*, integer): How often a sensor value should be pushed out. Defaults to ``15``.
``throttle``
************
Throttle the incoming values. When this filter gets an incoming value,
it checks if the last incoming value is at least ``specified time period`` old.
If it is not older than the configured value, the value is not passed forward.
.. code-block:: yaml
@ -333,28 +361,38 @@ on the sensor but only push out an average on a specific interval (thus increasi
- delta: 5.0
- lambda: return x * (9.0/5.0) + 32.0;
- **throttle**: Throttle the incoming values. When this filter gets an incoming value,
it checks if the last incoming value is at least ``specified time period`` old.
If it is not older than the configured value, the value is not passed forward.
- **heartbeat**: Send the last value that this sensor in the specified time interval.
So a value of ``10s`` will cause the filter to output values every 10s regardless
of the input values.
- **debounce**: Only send values if the last incoming value is at least ``specified time period``
old. For example if two values come in at almost the same time, this filter will only output
the last value and only after the specified time period has passed without any new incoming
values.
- **delta**: This filter stores the last value passed through this filter and only
passes incoming values through if the absolute difference is greater than the configured
value. For example if a value of 1.0 first comes in, it's passed on. If the delta filter
is configured with a value of 5, it will now not pass on an incoming value of 2.0, only values
that are at least 6.0 big or -4.0.
``or`` Filter
``heartbeat``
*************
Send the last value that this sensor in the specified time interval.
So a value of ``10s`` will cause the filter to output values every 10s regardless
of the input values.
``debounce``
************
Only send values if the last incoming value is at least ``specified time period``
old. For example if two values come in at almost the same time, this filter will only output
the last value and only after the specified time period has passed without any new incoming
values.
``delta``
*********
This filter stores the last value passed through this filter and only
passes incoming values through if the absolute difference is greater than the configured
value. For example if a value of 1.0 first comes in, it's passed on. If the delta filter
is configured with a value of 5, it will now not pass on an incoming value of 2.0, only values
that are at least 6.0 big or -4.0.
``or``
******
Pass forward a value with the first child filter that returns. Above example
will only pass forward values that are *either* at least 1s old or are if the absolute
difference is at least 5.0.
.. code-block:: yaml
# Example filters:
@ -363,22 +401,18 @@ on the sensor but only push out an average on a specific interval (thus increasi
- throttle: 1s
- delta: 5.0
- **or**: Pass forward a value with the first child filter that returns. Above example
will only pass forward values that are *either* at least 1s old or are if the absolute
difference is at least 5.0.
``lambda``
**********
``lambda`` Filter
*****************
Perform a simple mathematical operation over the sensor values. The input value is ``x`` and
the result of the lambda is used as the output (use ``return``).
.. code-block:: yaml
filters:
- lambda: return x * (9.0/5.0) + 32.0;
**lambda**: Perform a simple mathematical operation over the sensor
values. The input value is ``x`` and the result of the lambda is used
as the output (use ``return``).
Make sure to add ``.0`` to all values in the lambda, otherwise divisions of integers will
result in integers (not floating point values).

View File

@ -43,7 +43,7 @@ Use of the OE pin is optional. If used, the pin should be pulled up externally.
inverted: False
Configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~
************************
- **id** (**Required**, :ref:`config-id`): The id to use for this SN74HC595 component.
- **data_pin** (**Required**, :ref:`Pin Schema <config-pin_schema>`): Pin connected to SN74HC595 SER input.
@ -54,7 +54,7 @@ Configuration variables:
Pin configuration variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****************************
- **sn74hc595** (**Required**, :ref:`config-id`): The id of the SN74HC595 component of the pin.
- **number** (**Required**, integer): The pin number.

View File

@ -31,9 +31,10 @@ The ESPHome documentation is built using `sphinx <http://www.sphinx-doc.org/>`__
Syntax
******
In my opinion, Markdown would have been the much better choice in hindsight, but at the time
I was setting up the documentation good Doxygen integration was key to me. Anyway, here's a quick
RST primer:
Here's a quick RST primer:
Title hierarchy is based on order of occurence, not on type of character used to underline it. This
documents establish the following character order for better consistency.
- **Headers**: You can write titles like this:
@ -547,8 +548,8 @@ loader. These are:
has one of them in the config, a validation error will be generated.
- ``ESP_PLATFORMS``: Provide a list of allowed ESP types this integration works with.
- ``CODEOWNERS``: GitHub usernames or team names of people that are responsible for this integration.
You should add at least your GitHub username here, as well as anyone who helped you to write code
- ``CODEOWNERS``: GitHub usernames or team names of people that are responsible for this integration.
You should add at least your GitHub username here, as well as anyone who helped you to write code
that is being included.
Codebase Standards

View File

@ -6,7 +6,7 @@ from typing import MutableMapping
from sphinx.util import logging
from docutils import nodes
SCHEMA_PATH = "../esphome_devices/schema.json"
SCHEMA_PATH = "schema.json"
CONFIGURATION_VARIABLES = "Configuration variables:"
PIN_CONFIGURATION_VARIABLES = "Pin configuration variables:"
COMPONENT_HUB = "Component/Hub"
@ -75,7 +75,9 @@ PLATFORMS_TITLES = {
}
CUSTOM_DOCS = {
"guides/automations": {"Global Variables": "properties/globals"},
"guides/automations": {
"Global Variables": "properties/globals",
},
"guides/configuration-types": {
"Color": "properties/color",
"Pin Schema": [
@ -83,6 +85,9 @@ CUSTOM_DOCS = {
"definitions/PIN.OUTPUT_INTERNAL",
],
},
"components/binary_sensor/index": {
"Binary Sensor Filters": "binary_sensor.FILTER_REGISTRY",
},
"components/climate/ir_climate": {
"IR Remote Climate": [
"properties/climate/coolix",
@ -109,7 +114,8 @@ CUSTOM_DOCS = {
"definitions/light.BINARY_LIGHT_SCHEMA",
"definitions/light.BRIGHTNESS_ONLY_LIGHT_SCHEMA",
"definitions/light.LIGHT_SCHEMA",
]
],
"Light Effects": "light.EFFECTS_REGISTRY",
},
"components/light/fastled": {
"Clockless": "properties/light/fastled_clockless",
@ -125,10 +131,13 @@ CUSTOM_DOCS = {
"MQTT Component Base Configuration": "definitions/CONFIG.MQTT_COMMAND_COMPONENT_SCHEMA",
},
"components/output/index": {
"Base Output Configuration": "definitions/output.FLOAT_OUTPUT_SCHEMA"
"Base Output Configuration": "definitions/output.FLOAT_OUTPUT_SCHEMA",
},
"components/remote_transmitter": {
"Remote Transmitter Actions": "definitions/REMOTE_BASE.BASE_REMOTE_TRANSMITTER_SCHEMA"
"Remote Transmitter Actions": "definitions/REMOTE_BASE.BASE_REMOTE_TRANSMITTER_SCHEMA",
},
"components/sensor/index": {
"Sensor Filters": "sensor.FILTER_REGISTRY",
},
"components/time": {
"Home Assistant Time Source": "properties/time/homeassistant",
@ -146,6 +155,10 @@ CUSTOM_DOCS = {
}
def get_node_title(node):
return list(node.traverse(nodes.title))[0].astext()
class SchemaGeneratorVisitor(nodes.NodeVisitor):
def __init__(self, app, doctree, docname):
nodes.NodeVisitor.__init__(self, doctree)
@ -160,6 +173,8 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
self.json_base_config = None
self.title_id = None
self.props_section_title = None
self.find_registry = None
self.section_level = 0
if self.path[0] == "components":
if len(self.path) == 2: # root component, e.g. dfplayer, logger
component = docname[11:]
@ -230,7 +245,7 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
# this is empty, not much to do
raise nodes.SkipChildren
self.props_section_title = list(node.traverse(nodes.title))[0].astext()
self.props_section_title = get_node_title(node)
# Document first paragraph is description of this thing
description = self.getMarkdownParagraph(node)
@ -266,23 +281,23 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
def depart_document(self, node):
pass
def visit_SEONode(self, node):
pass
def depart_SEONode(self, node):
pass
def visit_literal_block(self, node):
pass
def depart_literal_block(self, node):
pass
def visit_section(self, node):
pass
self.section_level += 1
section_title = get_node_title(node)
if self.custom_doc and section_title in self.custom_doc:
r = self.custom_doc[section_title]
if (
isinstance(r, list)
or r.startswith("properties")
or r.startswith("definitions")
):
return
self.find_registry = r
def depart_section(self, node):
pass
self.section_level -= 1
if self.section_level == 1:
self.find_registry = None
def unknown_visit(self, node):
pass
@ -303,7 +318,11 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
# TODO: add same markdown description to each?
return
json_component = self.find_component(self.custom_doc[title_text])
if not json_component:
return
json_component["markdownDescription"] = self.getMarkdownParagraph(
node.parent
)
@ -450,22 +469,6 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
)
return
if title_text.endswith("Action") or title_text.endswith("Condition"):
# Document first paragraph is description of this thing
description = self.getMarkdownParagraph(node.parent)
split_text = title_text.split(" ")
if len(split_text) != 2:
return
key = split_text[0]
registry_name = f"automation.{split_text[1].upper()}_REGISTRY"
registry = self.app.jschema["definitions"][registry_name]["anyOf"]
for action in registry:
if key in action["properties"]:
action["properties"][key]["markdownDescription"] = description
self.props = self.find_props(action["properties"][key])
break
self.props_section_title = title_text
if title_text.endswith("Trigger"):
# Document first paragraph is description of this thing
description = self.getMarkdownParagraph(node.parent)
@ -481,28 +484,50 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
self.props = self.find_props(trigger_schema)
self.props_section_title = title_text
if self.docname == "components/light/index" and title_text.endswith("Effect"):
# Document first paragraph is description of this thing
if title_text == PIN_CONFIGURATION_VARIABLES:
self.multi_component = []
if self.app.jschema["definitions"].get(f"PIN.INPUT_{self.path[-1]}"):
self.multi_component.append(f"definitions/PIN.INPUT_{self.path[-1]}")
if self.app.jschema["definitions"].get(f"PIN.OUTPUT_{self.path[-1]}"):
self.multi_component.append(f"definitions/PIN.OUTPUT_{self.path[-1]}")
self.accept_props = True
self.filled_props = False
self.props = None
if len(self.multi_component) == 0:
raise ValueError(
f'Found a "{PIN_CONFIGURATION_VARIABLES}" entry but could not find pin schema'
)
if title_text.endswith("Action") or title_text.endswith("Condition"):
# Document first paragraph is description of this thing
description = self.getMarkdownParagraph(node.parent)
name = title_text[: -len(" Effect")]
# accept Light Effect as ending (Automation Light Effect)
split_text = title_text.split(" ")
if len(split_text) != 2:
return
key = split_text[0]
if self.props:
ref = self.props.get(key)
if ref:
ref = self.get_ref(ref)
if ref:
self.props = self.find_props(ref)
return
registry_name = f"automation.{split_text[1].upper()}_REGISTRY"
self.find_registry_prop(registry_name, key, description)
if self.section_level == 3 and self.find_registry:
name = title_text
if name.endswith(" Effect"):
name = title_text[: -len(" Effect")]
if name.endswith(" Light"):
name = name[: -len(" Light")]
key = name.replace(" ", "_").replace(".", "").lower()
registry = self.app.jschema["definitions"]["light.EFFECTS_REGISTRY"][
"anyOf"
]
description = self.getMarkdownParagraph(node.parent)
self.find_registry_prop(self.find_registry, key, description)
self.props_section_title = title_text
for effect in registry:
if key in effect["properties"]:
effect["properties"][key]["markdownDescription"] = description
self.props = self.find_props(effect["properties"][key])
return
raise ValueError("Cannot find Effect " + title_text)
if title_text == PIN_CONFIGURATION_VARIABLES:
self.multi_component = []
if self.app.jschema["definitions"].get(f"PIN.INPUT_{self.path[-1]}"):
@ -517,6 +542,16 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
f'Found a "{PIN_CONFIGURATION_VARIABLES}" entry but could not find pin schema'
)
def find_registry_prop(self, registry_name, key, description):
registry = self.app.jschema["definitions"][registry_name]["anyOf"]
for item in registry:
if key in item["properties"]:
item["properties"][key]["markdownDescription"] = description
self.props = self.find_props(item["properties"][key])
return
raise ValueError(f"Cannot find {registry_name} {key}")
def depart_title(self, node):
if self.filled_props:
self.filled_props = False
@ -663,6 +698,25 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
paragraph = list(node.traverse(nodes.paragraph))[0]
markdown = self.getMarkdown(paragraph)
param_type = None
# Check if there is type information for this item
try:
name_type = markdown[: markdown.index(": ") + 2]
ntr = re.search(
r"(\(((\*\*Required\*\*)|(\*Optional\*))(,\s(.*))*)\):\s",
name_type,
re.IGNORECASE,
)
if ntr:
param_type = ntr.group(6)
if param_type:
markdown = (
f"**{param_type}**: {markdown[markdown.index(': ') + 2 :]}"
)
except ValueError:
# ': ' not found
pass
title = list(node.traverse(nodes.title))[0]
if len(title) > 0:
url = urllib.parse.urljoin(
@ -696,9 +750,6 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
# **name** (*Optional*, string): Long Description... Defaults to ``value``.
# **name** (*Optional*): Long Description... Defaults to ``value``.
if "ads111" in self.docname:
self.docname = self.docname
ntr = re.search(
r"\* \*\*(\w*)\*\*\s(\(((\*\*Required\*\*)|(\*Optional\*))(,\s(.*))*)\):\s",
name_type,
@ -765,7 +816,7 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
desc = markdown[markdown.index(": ") + 2 :].strip()
if param_type:
desc = param_type + ": " + desc
desc = "**" + param_type + "**: " + desc
jprop["markdownDescription"] = desc
global props_documented
@ -780,6 +831,8 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
def find_component(self, component_path):
path = component_path.split("/")
if path[0] not in ("properties", "definitions"):
return None
json_component = self.app.jschema[path[0]][path[1]]
if len(path) > 2:
@ -897,32 +950,6 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor):
self.current_prop = None
return props
return
# find properties
if "then" in component:
component = component["then"]
props = component.get("properties")
ref = None
if not props:
arr = component.get("anyOf", component.get("allOf"))
if not arr:
if "$ref" in component:
return self.find_props(self.get_ref(component))
return None
for x in arr:
props = x.get("properties")
if not ref:
ref = self.get_ref(x)
if props:
break
if not props and ref:
props = self.find_props(ref)
if props:
self.filled_props = False
self.accept_props = False
self.current_prop = None
return props
def handle_component(app, doctree, docname):