mirror of
https://github.com/esphome/esphome.git
synced 2025-09-30 17:12:20 +01:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -15,9 +15,11 @@ import argcomplete
|
||||
|
||||
from esphome import const, writer, yaml_util
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.mqtt import CONF_DISCOVER_IP
|
||||
from esphome.config import iter_component_configs, read_config, strip_default_ids
|
||||
from esphome.const import (
|
||||
ALLOWED_NAME_CHARS,
|
||||
CONF_API,
|
||||
CONF_BAUD_RATE,
|
||||
CONF_BROKER,
|
||||
CONF_DEASSERT_RTS_DTR,
|
||||
@@ -43,6 +45,7 @@ from esphome.const import (
|
||||
SECRETS_FILES,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, coroutine
|
||||
from esphome.enum import StrEnum
|
||||
from esphome.helpers import get_bool_env, indent, is_ip_address
|
||||
from esphome.log import AnsiFore, color, setup_log
|
||||
from esphome.types import ConfigType
|
||||
@@ -106,13 +109,15 @@ def choose_prompt(options, purpose: str = None):
|
||||
return options[opt - 1][1]
|
||||
|
||||
|
||||
class Purpose(StrEnum):
|
||||
UPLOADING = "uploading"
|
||||
LOGGING = "logging"
|
||||
|
||||
|
||||
def choose_upload_log_host(
|
||||
default: list[str] | str | None,
|
||||
check_default: str | None,
|
||||
show_ota: bool,
|
||||
show_mqtt: bool,
|
||||
show_api: bool,
|
||||
purpose: str | None = None,
|
||||
purpose: Purpose,
|
||||
) -> list[str]:
|
||||
# Convert to list for uniform handling
|
||||
defaults = [default] if isinstance(default, str) else default or []
|
||||
@@ -132,13 +137,30 @@ def choose_upload_log_host(
|
||||
]
|
||||
resolved.append(choose_prompt(options, purpose=purpose))
|
||||
elif device == "OTA":
|
||||
if CORE.address and (
|
||||
(show_ota and "ota" in CORE.config)
|
||||
or (show_api and "api" in CORE.config)
|
||||
# ensure IP adresses are used first
|
||||
if is_ip_address(CORE.address) and (
|
||||
(purpose == Purpose.LOGGING and has_api())
|
||||
or (purpose == Purpose.UPLOADING and has_ota())
|
||||
):
|
||||
resolved.append(CORE.address)
|
||||
elif show_mqtt and has_mqtt_logging():
|
||||
resolved.append("MQTT")
|
||||
|
||||
if purpose == Purpose.LOGGING:
|
||||
if has_api() and has_mqtt_ip_lookup():
|
||||
resolved.append("MQTTIP")
|
||||
|
||||
if has_mqtt_logging():
|
||||
resolved.append("MQTT")
|
||||
|
||||
if has_api() and has_non_ip_address():
|
||||
resolved.append(CORE.address)
|
||||
|
||||
elif purpose == Purpose.UPLOADING:
|
||||
if has_ota() and has_mqtt_ip_lookup():
|
||||
resolved.append("MQTTIP")
|
||||
|
||||
if has_ota() and has_non_ip_address():
|
||||
resolved.append(CORE.address)
|
||||
|
||||
else:
|
||||
resolved.append(device)
|
||||
if not resolved:
|
||||
@@ -149,39 +171,111 @@ def choose_upload_log_host(
|
||||
options = [
|
||||
(f"{port.path} ({port.description})", port.path) for port in get_serial_ports()
|
||||
]
|
||||
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if show_mqtt and has_mqtt_logging():
|
||||
mqtt_config = CORE.config[CONF_MQTT]
|
||||
options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT"))
|
||||
|
||||
if purpose == Purpose.LOGGING:
|
||||
if has_mqtt_logging():
|
||||
mqtt_config = CORE.config[CONF_MQTT]
|
||||
options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT"))
|
||||
|
||||
if has_api():
|
||||
if has_resolvable_address():
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if has_mqtt_ip_lookup():
|
||||
options.append(("Over The Air (MQTT IP lookup)", "MQTTIP"))
|
||||
|
||||
elif purpose == Purpose.UPLOADING and has_ota():
|
||||
if has_resolvable_address():
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if has_mqtt_ip_lookup():
|
||||
options.append(("Over The Air (MQTT IP lookup)", "MQTTIP"))
|
||||
|
||||
if check_default is not None and check_default in [opt[1] for opt in options]:
|
||||
return [check_default]
|
||||
return [choose_prompt(options, purpose=purpose)]
|
||||
|
||||
|
||||
def mqtt_logging_enabled(mqtt_config):
|
||||
def has_mqtt_logging() -> bool:
|
||||
"""Check if MQTT logging is available."""
|
||||
if CONF_MQTT not in CORE.config:
|
||||
return False
|
||||
|
||||
mqtt_config = CORE.config[CONF_MQTT]
|
||||
|
||||
# enabled by default
|
||||
if CONF_LOG_TOPIC not in mqtt_config:
|
||||
return True
|
||||
|
||||
log_topic = mqtt_config[CONF_LOG_TOPIC]
|
||||
if log_topic is None:
|
||||
return False
|
||||
|
||||
if CONF_TOPIC not in log_topic:
|
||||
return False
|
||||
return log_topic.get(CONF_LEVEL, None) != "NONE"
|
||||
|
||||
return log_topic[CONF_LEVEL] != "NONE"
|
||||
|
||||
|
||||
def has_mqtt_logging() -> bool:
|
||||
"""Check if MQTT logging is available."""
|
||||
return (mqtt_config := CORE.config.get(CONF_MQTT)) and mqtt_logging_enabled(
|
||||
mqtt_config
|
||||
)
|
||||
def has_mqtt() -> bool:
|
||||
"""Check if MQTT is available."""
|
||||
return CONF_MQTT in CORE.config
|
||||
|
||||
|
||||
def has_api() -> bool:
|
||||
"""Check if API is available."""
|
||||
return CONF_API in CORE.config
|
||||
|
||||
|
||||
def has_ota() -> bool:
|
||||
"""Check if OTA is available."""
|
||||
return CONF_OTA in CORE.config
|
||||
|
||||
|
||||
def has_mqtt_ip_lookup() -> bool:
|
||||
"""Check if MQTT is available and IP lookup is supported."""
|
||||
if CONF_MQTT not in CORE.config:
|
||||
return False
|
||||
# Default Enabled
|
||||
if CONF_DISCOVER_IP not in CORE.config[CONF_MQTT]:
|
||||
return True
|
||||
return CORE.config[CONF_MQTT][CONF_DISCOVER_IP]
|
||||
|
||||
|
||||
def has_mdns() -> bool:
|
||||
"""Check if MDNS is available."""
|
||||
return CONF_MDNS not in CORE.config or not CORE.config[CONF_MDNS][CONF_DISABLED]
|
||||
|
||||
|
||||
def has_non_ip_address() -> bool:
|
||||
"""Check if CORE.address is set and is not an IP address."""
|
||||
return CORE.address is not None and not is_ip_address(CORE.address)
|
||||
|
||||
|
||||
def has_ip_address() -> bool:
|
||||
"""Check if CORE.address is a valid IP address."""
|
||||
return CORE.address is not None and is_ip_address(CORE.address)
|
||||
|
||||
|
||||
def has_resolvable_address() -> bool:
|
||||
"""Check if CORE.address is resolvable (via mDNS or is an IP address)."""
|
||||
return has_mdns() or has_ip_address()
|
||||
|
||||
|
||||
def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str):
|
||||
from esphome import mqtt
|
||||
|
||||
return mqtt.get_esphome_device_ip(config, username, password, client_id)
|
||||
|
||||
|
||||
_PORT_TO_PORT_TYPE = {
|
||||
"MQTT": "MQTT",
|
||||
"MQTTIP": "MQTTIP",
|
||||
}
|
||||
|
||||
|
||||
def get_port_type(port: str) -> str:
|
||||
if port.startswith("/") or port.startswith("COM"):
|
||||
return "SERIAL"
|
||||
if port == "MQTT":
|
||||
return "MQTT"
|
||||
return "NETWORK"
|
||||
return _PORT_TO_PORT_TYPE.get(port, "NETWORK")
|
||||
|
||||
|
||||
def run_miniterm(config: ConfigType, port: str, args) -> int:
|
||||
@@ -226,7 +320,9 @@ def run_miniterm(config: ConfigType, port: str, args) -> int:
|
||||
.replace(b"\n", b"")
|
||||
.decode("utf8", "backslashreplace")
|
||||
)
|
||||
time_str = datetime.now().time().strftime("[%H:%M:%S]")
|
||||
time_ = datetime.now()
|
||||
nanoseconds = time_.microsecond // 1000
|
||||
time_str = f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{nanoseconds:03}]"
|
||||
safe_print(parser.parse_line(line, time_str))
|
||||
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
@@ -437,23 +533,9 @@ def upload_program(
|
||||
password = ota_conf.get(CONF_PASSWORD, "")
|
||||
binary = args.file if getattr(args, "file", None) is not None else CORE.firmware_bin
|
||||
|
||||
# Check if we should use MQTT for address resolution
|
||||
# This happens when no device was specified, or the current host is "MQTT"/"OTA"
|
||||
if (
|
||||
CONF_MQTT in config # pylint: disable=too-many-boolean-expressions
|
||||
and (not devices or host in ("MQTT", "OTA"))
|
||||
and (
|
||||
((config[CONF_MDNS][CONF_DISABLED]) and not is_ip_address(CORE.address))
|
||||
or get_port_type(host) == "MQTT"
|
||||
)
|
||||
):
|
||||
from esphome import mqtt
|
||||
|
||||
devices = [
|
||||
mqtt.get_esphome_device_ip(
|
||||
config, args.username, args.password, args.client_id
|
||||
)
|
||||
]
|
||||
# MQTT address resolution
|
||||
if get_port_type(host) in ("MQTT", "MQTTIP"):
|
||||
devices = mqtt_get_ip(config, args.username, args.password, args.client_id)
|
||||
|
||||
return espota2.run_ota(devices, remote_port, password, binary)
|
||||
|
||||
@@ -474,20 +556,28 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int
|
||||
if get_port_type(port) == "SERIAL":
|
||||
check_permissions(port)
|
||||
return run_miniterm(config, port, args)
|
||||
if get_port_type(port) == "NETWORK" and "api" in config:
|
||||
addresses_to_use = devices
|
||||
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
|
||||
from esphome import mqtt
|
||||
|
||||
mqtt_address = mqtt.get_esphome_device_ip(
|
||||
port_type = get_port_type(port)
|
||||
|
||||
# Check if we should use API for logging
|
||||
if has_api():
|
||||
addresses_to_use: list[str] | None = None
|
||||
|
||||
if port_type == "NETWORK" and (has_mdns() or is_ip_address(port)):
|
||||
addresses_to_use = devices
|
||||
elif port_type in ("NETWORK", "MQTT", "MQTTIP") and has_mqtt_ip_lookup():
|
||||
# Only use MQTT IP lookup if the first condition didn't match
|
||||
# (for MQTT/MQTTIP types, or for NETWORK when mdns/ip check fails)
|
||||
addresses_to_use = mqtt_get_ip(
|
||||
config, args.username, args.password, args.client_id
|
||||
)[0]
|
||||
addresses_to_use = [mqtt_address]
|
||||
)
|
||||
|
||||
from esphome.components.api.client import run_logs
|
||||
if addresses_to_use is not None:
|
||||
from esphome.components.api.client import run_logs
|
||||
|
||||
return run_logs(config, addresses_to_use)
|
||||
if get_port_type(port) in ("NETWORK", "MQTT") and "mqtt" in config:
|
||||
return run_logs(config, addresses_to_use)
|
||||
|
||||
if port_type in ("NETWORK", "MQTT") and has_mqtt_logging():
|
||||
from esphome import mqtt
|
||||
|
||||
return mqtt.show_logs(
|
||||
@@ -560,10 +650,7 @@ def command_upload(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
devices = choose_upload_log_host(
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
show_api=False,
|
||||
purpose="uploading",
|
||||
purpose=Purpose.UPLOADING,
|
||||
)
|
||||
|
||||
exit_code, _ = upload_program(config, args, devices)
|
||||
@@ -588,10 +675,7 @@ def command_logs(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
devices = choose_upload_log_host(
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
show_api=True,
|
||||
purpose="logging",
|
||||
purpose=Purpose.LOGGING,
|
||||
)
|
||||
return show_logs(config, args, devices)
|
||||
|
||||
@@ -617,10 +701,7 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
devices = choose_upload_log_host(
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
show_api=True,
|
||||
purpose="uploading",
|
||||
purpose=Purpose.UPLOADING,
|
||||
)
|
||||
|
||||
exit_code, successful_device = upload_program(config, args, devices)
|
||||
@@ -637,10 +718,7 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
devices = choose_upload_log_host(
|
||||
default=successful_device,
|
||||
check_default=successful_device,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
show_api=True,
|
||||
purpose="logging",
|
||||
purpose=Purpose.LOGGING,
|
||||
)
|
||||
return show_logs(config, args, devices)
|
||||
|
||||
|
@@ -11,15 +11,8 @@ from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
)
|
||||
from esphome.config_helpers import filter_source_files_from_platform
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ANALOG,
|
||||
CONF_INPUT,
|
||||
CONF_NUMBER,
|
||||
PLATFORM_ESP8266,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266
|
||||
from esphome.core import CORE
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
@@ -273,21 +266,3 @@ def validate_adc_pin(value):
|
||||
)(value)
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"adc_sensor_esp32.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
"adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO},
|
||||
"adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO},
|
||||
"adc_sensor_libretiny.cpp": {
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
},
|
||||
"adc_sensor_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR},
|
||||
}
|
||||
)
|
||||
|
@@ -9,6 +9,7 @@ from esphome.components.zephyr import (
|
||||
zephyr_add_prj_conf,
|
||||
zephyr_add_user,
|
||||
)
|
||||
from esphome.config_helpers import filter_source_files_from_platform
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ATTENUATION,
|
||||
@@ -20,6 +21,7 @@ from esphome.const import (
|
||||
PLATFORM_NRF52,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
@@ -174,3 +176,21 @@ async def to_code(config):
|
||||
}};
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"adc_sensor_esp32.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
"adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO},
|
||||
"adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO},
|
||||
"adc_sensor_libretiny.cpp": {
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
},
|
||||
"adc_sensor_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR},
|
||||
}
|
||||
)
|
||||
|
@@ -62,9 +62,11 @@ async def async_run_logs(config: dict[str, Any], addresses: list[str]) -> None:
|
||||
time_ = datetime.now()
|
||||
message: bytes = msg.message
|
||||
text = message.decode("utf8", "backslashreplace")
|
||||
for parsed_msg in parse_log_message(
|
||||
text, f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]"
|
||||
):
|
||||
nanoseconds = time_.microsecond // 1000
|
||||
timestamp = (
|
||||
f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{nanoseconds:03}]"
|
||||
)
|
||||
for parsed_msg in parse_log_message(text, timestamp):
|
||||
print(parsed_msg.replace("\033", "\\033") if dashboard else parsed_msg)
|
||||
|
||||
stop = await async_run(cli, on_log, name=name)
|
||||
|
@@ -353,6 +353,7 @@ SUPPORTED_PLATFORMIO_ESP_IDF_5X = [
|
||||
# pioarduino versions that don't require a release number
|
||||
# List based on https://github.com/pioarduino/esp-idf/releases
|
||||
SUPPORTED_PIOARDUINO_ESP_IDF_5X = [
|
||||
cv.Version(5, 5, 1),
|
||||
cv.Version(5, 5, 0),
|
||||
cv.Version(5, 4, 2),
|
||||
cv.Version(5, 4, 1),
|
||||
|
@@ -1,7 +1,13 @@
|
||||
#include "factory_reset_button.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_OPENTHREAD
|
||||
#include "esphome/components/openthread/openthread.h"
|
||||
#endif
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
@@ -13,9 +19,20 @@ void FactoryResetButton::press_action() {
|
||||
ESP_LOGI(TAG, "Resetting");
|
||||
// Let MQTT settle a bit
|
||||
delay(100); // NOLINT
|
||||
#ifdef USE_OPENTHREAD
|
||||
openthread::global_openthread_component->on_factory_reset(FactoryResetButton::factory_reset_callback);
|
||||
#else
|
||||
global_preferences->reset();
|
||||
App.safe_reboot();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_OPENTHREAD
|
||||
void FactoryResetButton::factory_reset_callback() {
|
||||
global_preferences->reset();
|
||||
App.safe_reboot();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace factory_reset
|
||||
} // namespace esphome
|
||||
|
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
@@ -9,6 +11,9 @@ namespace factory_reset {
|
||||
class FactoryResetButton : public button::Button, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
#ifdef USE_OPENTHREAD
|
||||
static void factory_reset_callback();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
|
@@ -1,7 +1,13 @@
|
||||
#include "factory_reset_switch.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_OPENTHREAD
|
||||
#include "esphome/components/openthread/openthread.h"
|
||||
#endif
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
@@ -17,10 +23,21 @@ void FactoryResetSwitch::write_state(bool state) {
|
||||
ESP_LOGI(TAG, "Resetting");
|
||||
// Let MQTT settle a bit
|
||||
delay(100); // NOLINT
|
||||
#ifdef USE_OPENTHREAD
|
||||
openthread::global_openthread_component->on_factory_reset(FactoryResetSwitch::factory_reset_callback);
|
||||
#else
|
||||
global_preferences->reset();
|
||||
App.safe_reboot();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_OPENTHREAD
|
||||
void FactoryResetSwitch::factory_reset_callback() {
|
||||
global_preferences->reset();
|
||||
App.safe_reboot();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace factory_reset
|
||||
} // namespace esphome
|
||||
|
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
@@ -9,6 +10,9 @@ namespace factory_reset {
|
||||
class FactoryResetSwitch : public switch_::Switch, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
#ifdef USE_OPENTHREAD
|
||||
static void factory_reset_callback();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
|
@@ -11,8 +11,6 @@
|
||||
#include <openthread/instance.h>
|
||||
#include <openthread/logging.h>
|
||||
#include <openthread/netdata.h>
|
||||
#include <openthread/srp_client.h>
|
||||
#include <openthread/srp_client_buffers.h>
|
||||
#include <openthread/tasklet.h>
|
||||
|
||||
#include <cstring>
|
||||
@@ -77,8 +75,14 @@ std::optional<otIp6Address> OpenThreadComponent::get_omr_address_(InstanceLock &
|
||||
return {};
|
||||
}
|
||||
|
||||
void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services,
|
||||
const otSrpClientService *removed_services, void *context) {
|
||||
void OpenThreadComponent::defer_factory_reset_external_callback() {
|
||||
ESP_LOGD(TAG, "Defer factory_reset_external_callback_");
|
||||
this->defer([this]() { this->factory_reset_external_callback_(); });
|
||||
}
|
||||
|
||||
void OpenThreadSrpComponent::srp_callback(otError err, const otSrpClientHostInfo *host_info,
|
||||
const otSrpClientService *services,
|
||||
const otSrpClientService *removed_services, void *context) {
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "SRP client reported an error: %s", otThreadErrorToString(err));
|
||||
for (const otSrpClientHostInfo *host = host_info; host; host = nullptr) {
|
||||
@@ -90,16 +94,30 @@ void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrp
|
||||
}
|
||||
}
|
||||
|
||||
void srp_start_callback(const otSockAddr *server_socket_address, void *context) {
|
||||
void OpenThreadSrpComponent::srp_start_callback(const otSockAddr *server_socket_address, void *context) {
|
||||
ESP_LOGI(TAG, "SRP client has started");
|
||||
}
|
||||
|
||||
void OpenThreadSrpComponent::srp_factory_reset_callback(otError err, const otSrpClientHostInfo *host_info,
|
||||
const otSrpClientService *services,
|
||||
const otSrpClientService *removed_services, void *context) {
|
||||
OpenThreadComponent *obj = (OpenThreadComponent *) context;
|
||||
if (err == OT_ERROR_NONE && removed_services != NULL && host_info != NULL &&
|
||||
host_info->mState == OT_SRP_CLIENT_ITEM_STATE_REMOVED) {
|
||||
ESP_LOGD(TAG, "Successful Removal SRP Host and Services");
|
||||
} else if (err != OT_ERROR_NONE) {
|
||||
// Handle other SRP client events or errors
|
||||
ESP_LOGW(TAG, "SRP client event/error: %s", otThreadErrorToString(err));
|
||||
}
|
||||
obj->defer_factory_reset_external_callback();
|
||||
}
|
||||
|
||||
void OpenThreadSrpComponent::setup() {
|
||||
otError error;
|
||||
InstanceLock lock = InstanceLock::acquire();
|
||||
otInstance *instance = lock.get_instance();
|
||||
|
||||
otSrpClientSetCallback(instance, srp_callback, nullptr);
|
||||
otSrpClientSetCallback(instance, OpenThreadSrpComponent::srp_callback, nullptr);
|
||||
|
||||
// set the host name
|
||||
uint16_t size;
|
||||
@@ -179,7 +197,8 @@ void OpenThreadSrpComponent::setup() {
|
||||
ESP_LOGD(TAG, "Added service: %s", full_service.c_str());
|
||||
}
|
||||
|
||||
otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr);
|
||||
otSrpClientEnableAutoStartMode(instance, OpenThreadSrpComponent::srp_start_callback, nullptr);
|
||||
ESP_LOGD(TAG, "Finished SRP setup");
|
||||
}
|
||||
|
||||
void *OpenThreadSrpComponent::pool_alloc_(size_t size) {
|
||||
@@ -217,6 +236,21 @@ bool OpenThreadComponent::teardown() {
|
||||
return this->teardown_complete_;
|
||||
}
|
||||
|
||||
void OpenThreadComponent::on_factory_reset(std::function<void()> callback) {
|
||||
factory_reset_external_callback_ = callback;
|
||||
ESP_LOGD(TAG, "Start Removal SRP Host and Services");
|
||||
otError error;
|
||||
InstanceLock lock = InstanceLock::acquire();
|
||||
otInstance *instance = lock.get_instance();
|
||||
otSrpClientSetCallback(instance, OpenThreadSrpComponent::srp_factory_reset_callback, this);
|
||||
error = otSrpClientRemoveHostAndServices(instance, true, true);
|
||||
if (error != OT_ERROR_NONE) {
|
||||
ESP_LOGW(TAG, "Failed to Remove SRP Host and Services");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Waiting on Confirmation Removal SRP Host and Services");
|
||||
}
|
||||
|
||||
} // namespace openthread
|
||||
} // namespace esphome
|
||||
|
||||
|
@@ -6,6 +6,8 @@
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include <openthread/srp_client.h>
|
||||
#include <openthread/srp_client_buffers.h>
|
||||
#include <openthread/thread.h>
|
||||
|
||||
#include <optional>
|
||||
@@ -28,11 +30,14 @@ class OpenThreadComponent : public Component {
|
||||
network::IPAddresses get_ip_addresses();
|
||||
std::optional<otIp6Address> get_omr_address();
|
||||
void ot_main();
|
||||
void on_factory_reset(std::function<void()> callback);
|
||||
void defer_factory_reset_external_callback();
|
||||
|
||||
protected:
|
||||
std::optional<otIp6Address> get_omr_address_(InstanceLock &lock);
|
||||
bool teardown_started_{false};
|
||||
bool teardown_complete_{false};
|
||||
std::function<void()> factory_reset_external_callback_;
|
||||
};
|
||||
|
||||
extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -43,6 +48,12 @@ class OpenThreadSrpComponent : public Component {
|
||||
// This has to run after the mdns component or else no services are available to advertise
|
||||
float get_setup_priority() const override { return this->mdns_->get_setup_priority() - 1.0; }
|
||||
void setup() override;
|
||||
static void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services,
|
||||
const otSrpClientService *removed_services, void *context);
|
||||
static void srp_start_callback(const otSockAddr *server_socket_address, void *context);
|
||||
static void srp_factory_reset_callback(otError err, const otSrpClientHostInfo *host_info,
|
||||
const otSrpClientService *services, const otSrpClientService *removed_services,
|
||||
void *context);
|
||||
|
||||
protected:
|
||||
esphome::mdns::MDNSComponent *mdns_{nullptr};
|
||||
|
@@ -270,6 +270,7 @@ void PacketTransport::add_binary_data_(uint8_t key, const char *id, bool data) {
|
||||
auto len = 1 + 1 + 1 + strlen(id);
|
||||
if (len + this->header_.size() + this->data_.size() > this->get_max_packet_size()) {
|
||||
this->flush_();
|
||||
this->init_data_();
|
||||
}
|
||||
add(this->data_, key);
|
||||
add(this->data_, (uint8_t) data);
|
||||
@@ -284,6 +285,7 @@ void PacketTransport::add_data_(uint8_t key, const char *id, uint32_t data) {
|
||||
auto len = 4 + 1 + 1 + strlen(id);
|
||||
if (len + this->header_.size() + this->data_.size() > this->get_max_packet_size()) {
|
||||
this->flush_();
|
||||
this->init_data_();
|
||||
}
|
||||
add(this->data_, key);
|
||||
add(this->data_, data);
|
||||
|
@@ -114,6 +114,7 @@ CONF_AND = "and"
|
||||
CONF_ANGLE = "angle"
|
||||
CONF_ANY = "any"
|
||||
CONF_AP = "ap"
|
||||
CONF_API = "api"
|
||||
CONF_APPARENT_POWER = "apparent_power"
|
||||
CONF_ARDUINO_VERSION = "arduino_version"
|
||||
CONF_AREA = "area"
|
||||
|
Reference in New Issue
Block a user