1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-13 00:32:20 +01:00
Files
esphome/esphome/components/openthread/__init__.py
rwrozelle 4cdc804c17 OpenThread - add Device Type (#9272)
Co-authored-by: mc <mc@debian>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-07-03 00:16:28 +12:00

154 lines
5.2 KiB
Python

import esphome.codegen as cg
from esphome.components.esp32 import (
VARIANT_ESP32C6,
VARIANT_ESP32H2,
add_idf_sdkconfig_option,
only_on_variant,
)
from esphome.components.mdns import MDNSComponent
import esphome.config_validation as cv
from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID
import esphome.final_validate as fv
from .const import (
CONF_DEVICE_TYPE,
CONF_EXT_PAN_ID,
CONF_FORCE_DATASET,
CONF_MDNS_ID,
CONF_MESH_LOCAL_PREFIX,
CONF_NETWORK_KEY,
CONF_NETWORK_NAME,
CONF_PAN_ID,
CONF_PSKC,
CONF_SRP_ID,
CONF_TLV,
)
CODEOWNERS = ["@mrene"]
AUTO_LOAD = ["network"]
# Wi-fi / Bluetooth / Thread coexistence isn't implemented at this time
# TODO: Doesn't conflict with wifi if you're using another ESP as an RCP (radio coprocessor), but this isn't implemented yet
CONFLICTS_WITH = ["wifi"]
DEPENDENCIES = ["esp32"]
CONF_DEVICE_TYPES = [
"FTD",
"MTD",
]
def set_sdkconfig_options(config):
# and expose options for using SPI/UART RCPs
add_idf_sdkconfig_option("CONFIG_IEEE802154_ENABLED", True)
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_RADIO_NATIVE", True)
# There is a conflict if the logger's uart also uses the default UART, which is seen as a watchdog failure on "ot_cli"
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_CLI", False)
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_ENABLED", True)
if tlv := config.get(CONF_TLV):
cg.add_define("USE_OPENTHREAD_TLVS", tlv)
else:
if pan_id := config.get(CONF_PAN_ID):
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PANID", pan_id)
if channel := config.get(CONF_CHANNEL):
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", channel)
if network_key := config.get(CONF_NETWORK_KEY):
add_idf_sdkconfig_option(
"CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{network_key:X}".lower()
)
if network_name := config.get(CONF_NETWORK_NAME):
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_NAME", network_name)
if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None:
add_idf_sdkconfig_option(
"CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}".lower()
)
if (mesh_local_prefix := config.get(CONF_MESH_LOCAL_PREFIX)) is not None:
add_idf_sdkconfig_option(
"CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix}".lower()
)
if (pskc := config.get(CONF_PSKC)) is not None:
add_idf_sdkconfig_option(
"CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}".lower()
)
if force_dataset := config.get(CONF_FORCE_DATASET):
if force_dataset:
cg.add_define("USE_OPENTHREAD_FORCE_DATASET")
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_DNS64_CLIENT", True)
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT", True)
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT_MAX_SERVICES", 5)
# TODO: Add suport for sleepy end devices
add_idf_sdkconfig_option(f"CONFIG_OPENTHREAD_{config.get(CONF_DEVICE_TYPE)}", True)
openthread_ns = cg.esphome_ns.namespace("openthread")
OpenThreadComponent = openthread_ns.class_("OpenThreadComponent", cg.Component)
OpenThreadSrpComponent = openthread_ns.class_("OpenThreadSrpComponent", cg.Component)
_CONNECTION_SCHEMA = cv.Schema(
{
cv.Optional(CONF_PAN_ID): cv.hex_int,
cv.Optional(CONF_CHANNEL): cv.int_,
cv.Optional(CONF_NETWORK_KEY): cv.hex_int,
cv.Optional(CONF_EXT_PAN_ID): cv.hex_int,
cv.Optional(CONF_NETWORK_NAME): cv.string_strict,
cv.Optional(CONF_PSKC): cv.hex_int,
cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.ipv6network,
}
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(OpenThreadComponent),
cv.GenerateID(CONF_SRP_ID): cv.declare_id(OpenThreadSrpComponent),
cv.GenerateID(CONF_MDNS_ID): cv.use_id(MDNSComponent),
cv.Optional(CONF_DEVICE_TYPE, default="FTD"): cv.one_of(
*CONF_DEVICE_TYPES, upper=True
),
cv.Optional(CONF_FORCE_DATASET): cv.boolean,
cv.Optional(CONF_TLV): cv.string_strict,
}
).extend(_CONNECTION_SCHEMA),
cv.has_exactly_one_key(CONF_NETWORK_KEY, CONF_TLV),
cv.only_with_esp_idf,
only_on_variant(supported=[VARIANT_ESP32C6, VARIANT_ESP32H2]),
)
def _final_validate(_):
full_config = fv.full_config.get()
network_config = full_config.get("network", {})
if not network_config.get(CONF_ENABLE_IPV6, False):
raise cv.Invalid(
"OpenThread requires IPv6 to be enabled in the network component. "
"Please set `enable_ipv6: true` in the `network` configuration."
)
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config):
cg.add_define("USE_OPENTHREAD")
ot = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(ot, config)
srp = cg.new_Pvariable(config[CONF_SRP_ID])
mdns_component = await cg.get_variable(config[CONF_MDNS_ID])
cg.add(srp.set_mdns(mdns_component))
await cg.register_component(srp, config)
set_sdkconfig_options(config)