ESPHome External Component development is like shooting rays into a Black Box to find atoms.
I’ve successfully created an external component for the Adafruit NeoTrellis keypad, at least the key part of it as that derived rather nicely using the internal matrix_keypad as an example. It exposes the ‘matrix_keypad’ component and a ‘binary_sensor’ (sub?) component for each key. Now I want to do the same for the light behind each key. Creating my light.neotrellis_keypad sub-component seemed a fairly straightforward matter of massaging the binary_sensor, this time deriving from light.rgb. The whole system with the configuration Python files is an undocumented Black Box. The ‘binary_sensor’ and files are in a subdirectory below the ‘neotrellis_keypad’ folder, which lies beneath the ‘my_components’ folder. Lacking any formal connection to these sub-components, I can only assume they are found by looking through the directory tree and their Python config files are discovered. This works for the binary_sensor sub-component, but not for the light. Using the internal components as examples, it seems that you can name the Python file as either __init__.py or, for example, light.py. But in trying to resolve the errors when installing my device YAML file I do get different results:
with __init__.py:
Platform not found: ‘light.neotrellis_keypad’.
with light.py:
Component light.neotrellis_keypad cannot be loaded via YAML (no CONFIG_SCHEMA).
Here’s the entire Python config file, which does define CONFIG_SCHEMA:
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import rgb, light, output
from esphome.const import CONF_ID, CONF_KEY, CONF_ROW, CONF_COL, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_OUTPUT_ID
from .. import NeoTrellisKeypad, neotrellis_keypad_ns, CONF_KEYPAD_ID
DEPENDENCIES = ["neotrellis_keypad"]
NeoTrellisKeypadLight = neotrellis_keypad_ns.class_(
"NeoTrellisKeypadLight", rgb.RGBLightOuput
)
def check_button(obj):
if CONF_ROW in obj or CONF_COL in obj:
if CONF_KEY in obj:
raise cv.Invalid("You can't provide both a key and a position")
if CONF_ROW not in obj:
raise cv.Invalid("Missing row")
if CONF_COL not in obj:
raise cv.Invalid("Missing col")
if obj[CONF_ROW] < 0 or obj[CONF_ROW] > 3:
raise cv.Invalid("row must be 0 to 3")
if obj[CONF_COL] < 0 or obj[CONF_COL] > 3:
raise cv.Invalid("col must be 0 to 3")
elif CONF_KEY not in obj:
raise cv.Invalid("Missing key or position")
elif len(obj[CONF_KEY]) != 1:
raise cv.Invalid("Key must be one character")
return obj
CONFIG_SCHEMA = cv.All(
light.RGB_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(NeoTrellisKeypadLight),
cv.Optional(CONF_RED): cv.use_id(output.FloatOutput),
cv.Optional(CONF_GREEN): cv.use_id(output.FloatOutput),
cv.Optional(CONF_BLUE): cv.use_id(output.FloatOutput),
cv.GenerateID(CONF_KEYPAD_ID): cv.use_id(NeoTrellisKeypad),
cv.Optional(CONF_ROW): cv.int_,
cv.Optional(CONF_COL): cv.int_,
cv.Optional(CONF_KEY): cv.string,
}
),
check_button,
)
async def to_code(config):
if CONF_KEY in config:
var = cg.new_Pvariable(config[CONF_ID], config[CONF_KEY][0])
else:
var = cg.new_Pvariable(config[CONF_ID], config[CONF_ROW], config[CONF_COL])
await light.register_light(var, config)
neotrellis_keypad = await cg.get_variable(config[CONF_KEYPAD_ID])
cg.add(var.set_pad(neotrellis_keypad))
if CONF_RED in config:
red = await cg.get_variable(config[CONF_RED])
cg.add(var.set_red(red))
if CONF_GREEN in config:
green = await cg.get_variable(config[CONF_GREEN])
cg.add(var.set_green(green))
if CONF_BLUE in config:
blue = await cg.get_variable(config[CONF_BLUE])
cg.add(var.set_blue(blue))
And here’s the part in the YAML that produces the error at the light platform line:
neotrellis_keypad:
id: mykeypad
keys: "PONMLKJIHGFEDCBA"
on_key:
- lambda: ESP_LOGI("KEY", "key %d pressed", x);
binary_sensor:
- platform: neotrellis_keypad
name: "KeyA"
id: keyA
key: A
light:
- platform: neotrellis_keypad
name: "LightA"
id: lightA
key: A
The device installs and works fine without the light component. There’s obviously something I’m not seeing keeping it from connecting to the light sub-component. Or perhaps an error in the Python code that I’m not seeing and is not being reported?
Consider this a cry for HELP!