diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index bfcb0ca7f8..6574037efb 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -52,6 +52,8 @@ class E131Component : public esphome::Component { if (!this->udp_.parsePacket()) return -1; return this->udp_.read(buf, len); +#else + return -1; #endif } bool packet_(const uint8_t *data, size_t len, int &universe, E131Packet &packet); diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 2b25cf243d..2de67542b2 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -280,5 +280,6 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform( PlatformFramework.RTL87XX_ARDUINO, PlatformFramework.LN882X_ARDUINO, }, + "mdns_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR}, } ) diff --git a/esphome/components/mdns/mdns_zephyr.cpp b/esphome/components/mdns/mdns_zephyr.cpp new file mode 100644 index 0000000000..0b2fd9e62b --- /dev/null +++ b/esphome/components/mdns/mdns_zephyr.cpp @@ -0,0 +1,17 @@ +#include "esphome/core/defines.h" +#if defined(USE_ZEPHYR) && defined(USE_MDNS) + +#include "mdns_component.h" +#include "esphome/core/log.h" + +namespace esphome::mdns { + +static const char *const TAG = "mdns.zephyr"; + +void MDNSComponent::setup() { ESP_LOGW(TAG, "mDNS is not implemented for Zephyr"); } + +void MDNSComponent::on_shutdown() {} + +} // namespace esphome::mdns + +#endif // USE_ZEPHYR && USE_MDNS diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 2818b8c93e..3bb14a05a7 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -4,6 +4,7 @@ import logging import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.psram import is_guaranteed as psram_is_guaranteed +from esphome.components.zephyr import zephyr_add_prj_conf import esphome.config_validation as cv from esphome.const import CONF_ENABLE_IPV6, CONF_ID, CONF_MIN_IPV6_ADDR_COUNT from esphome.core import CORE, CoroPriority, coroutine_with_priority @@ -117,6 +118,7 @@ CONFIG_SCHEMA = cv.Schema( esp8266=False, host=False, rp2040=False, + nrf52=True, ): cv.All( cv.boolean, cv.Any( @@ -127,6 +129,7 @@ CONFIG_SCHEMA = cv.Schema( esp8266_arduino=cv.Version(0, 0, 0), host=cv.Version(0, 0, 0), rp2040_arduino=cv.Version(0, 0, 0), + nrf52_zephyr=cv.Version(0, 0, 0), ), cv.boolean_false, ), @@ -205,6 +208,19 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_LWIP_TCP_RECVMBOX_SIZE", 64) add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 64) + if CORE.is_nrf52: + enable_ipv6 = config.get(CONF_ENABLE_IPV6, True) + if not enable_ipv6: + _LOGGER.warning( + "IPv6 cannot be disabled on nRF52 because the Zephyr IPAddress implementation is IPv6-only. " + "Forcing CONFIG_NET_IPV6=y." + ) + config[CONF_ENABLE_IPV6] = True + zephyr_add_prj_conf("NETWORKING", True) + zephyr_add_prj_conf("NET_IPV6", True) + zephyr_add_prj_conf("NET_TCP", True) + zephyr_add_prj_conf("NET_UDP", True) + if (enable_ipv6 := config.get(CONF_ENABLE_IPV6, None)) is not None: cg.add_define("USE_NETWORK_IPV6", enable_ipv6) if enable_ipv6: diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index c0e7b2886c..55bb2a1c89 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -5,13 +5,13 @@ #include #include #include +#include #include "esphome/core/helpers.h" #include "esphome/core/macros.h" #if defined(USE_ESP32) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0) #include #endif - #if USE_ARDUINO #include #include @@ -24,6 +24,14 @@ using ip4_addr_t = in_addr; #define ipaddr_aton(x, y) inet_aton((x), (y)) #endif +#ifdef USE_ZEPHYR +#include +#include +#include +using ip_addr_t = struct in6_addr; +static inline int ipaddr_aton(const char *cp, ip_addr_t *addr) { return inet_pton(AF_INET6, cp, addr) == 1 ? 1 : 0; } +#endif + #if USE_ESP32_FRAMEWORK_ARDUINO #define arduino_ns Arduino_h #elif USE_LIBRETINY @@ -33,7 +41,6 @@ using ip4_addr_t = in_addr; #endif #ifdef USE_ESP32 -#include #include #endif @@ -52,7 +59,36 @@ inline void lowercase_ip_str(char *buf) { struct IPAddress { public: -#ifdef USE_HOST +#ifdef USE_ZEPHYR + IPAddress() { memset(&ip_addr_, 0, sizeof(ip_addr_)); } + IPAddress(const std::string &in_address) : ip_addr_{} { ipaddr_aton(in_address.c_str(), &ip_addr_); } + IPAddress(const struct in6_addr *other_ip) { ip_addr_ = *other_ip; } + IPAddress(const struct sockaddr_in6 *addr) { ip_addr_ = addr->sin6_addr; } + + operator struct in6_addr() const { return ip_addr_; } + + bool is_set() const { return !net_ipv6_is_addr_unspecified(&ip_addr_); } + bool is_ip4() const { return false; } + bool is_ip6() const { return this->is_set(); } + bool is_multicast() const { return net_ipv6_is_addr_mcast(&ip_addr_); } + // Remove before 2026.8.0 + ESPDEPRECATED( + "str() is deprecated: use 'char buf[IP_ADDRESS_BUFFER_SIZE]; ip.str_to(buf);' instead. Removed in 2026.8.0", + "2026.2.0") + std::string str() const { + char buf[IP_ADDRESS_BUFFER_SIZE]; + this->str_to(buf); + return buf; + } + char *str_to(char *buf) const { + if (inet_ntop(AF_INET6, &ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE) == nullptr) + buf[0] = '\0'; + return buf; + } + bool operator==(const IPAddress &other) const { return net_ipv6_addr_cmp(&ip_addr_, &other.ip_addr_); } + bool operator!=(const IPAddress &other) const { return !net_ipv6_addr_cmp(&ip_addr_, &other.ip_addr_); } + +#elif defined(USE_HOST) IPAddress() { ip_addr_.s_addr = 0; } IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { this->ip_addr_.s_addr = htonl((first << 24) | (second << 16) | (third << 8) | fourth); diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 53326e9472..008081f586 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -1,6 +1,6 @@ #pragma once #include "esphome/core/defines.h" -#ifdef USE_NETWORK +#if defined(USE_NETWORK) && !defined(USE_ZEPHYR) #include #include @@ -219,4 +219,4 @@ class PrometheusHandler : public AsyncWebHandler, public Component { } // namespace esphome::prometheus -#endif +#endif // USE_NETWORK && !USE_ZEPHYR diff --git a/esphome/components/statsd/statsd.cpp b/esphome/components/statsd/statsd.cpp index 7086e462a7..2a56551255 100644 --- a/esphome/components/statsd/statsd.cpp +++ b/esphome/components/statsd/statsd.cpp @@ -2,7 +2,7 @@ #include "statsd.h" -#ifdef USE_NETWORK +#if defined(USE_NETWORK) && !defined(USE_ZEPHYR) namespace esphome::statsd { diff --git a/esphome/components/statsd/statsd.h b/esphome/components/statsd/statsd.h index 349bffe6fb..77f3d797c5 100644 --- a/esphome/components/statsd/statsd.h +++ b/esphome/components/statsd/statsd.h @@ -3,7 +3,7 @@ #include #include "esphome/core/defines.h" -#ifdef USE_NETWORK +#if defined(USE_NETWORK) && !defined(USE_ZEPHYR) #include "esphome/core/component.h" #include "esphome/components/socket/socket.h" #include "esphome/components/network/ip_address.h" @@ -83,4 +83,4 @@ class StatsdComponent : public PollingComponent { } // namespace esphome::statsd -#endif +#endif // USE_NETWORK && !USE_ZEPHYR diff --git a/esphome/components/sx1509/sx1509.h b/esphome/components/sx1509/sx1509.h index f645ede754..35883eed5b 100644 --- a/esphome/components/sx1509/sx1509.h +++ b/esphome/components/sx1509/sx1509.h @@ -51,7 +51,7 @@ class SX1509Component : public Component, this->cols_ = cols; this->has_keypad_ = true; }; - void set_keys(std::string keys) { this->keys_ = std::move(keys); }; + void set_keys(std::string keys) { this->keys_ = std::move(keys); }; // NOLINT(performance-unnecessary-value-param) void set_sleep_time(uint16_t sleep_time) { this->sleep_time_ = sleep_time; }; void set_scan_time(uint8_t scan_time) { this->scan_time_ = scan_time; }; void set_debounce_time(uint8_t debounce_time = 1) { this->debounce_time_ = debounce_time; }; @@ -62,10 +62,10 @@ class SX1509Component : public Component, void setup_led_driver(uint8_t pin); protected: - // Virtual methods from CachedGpioExpander - bool digital_read_hw(uint8_t pin) override; - bool digital_read_cache(uint8_t pin) override; - void digital_write_hw(uint8_t pin, bool value) override; + // Virtual methods from CachedGpioExpander — names come from base class + bool digital_read_hw(uint8_t pin) override; // NOLINT(readability-identifier-naming) + bool digital_read_cache(uint8_t pin) override; // NOLINT(readability-identifier-naming) + void digital_write_hw(uint8_t pin, bool value) override; // NOLINT(readability-identifier-naming) uint32_t clk_x_ = 2000000; uint8_t frequency_ = 0; diff --git a/esphome/components/tca9555/tca9555.h b/esphome/components/tca9555/tca9555.h index 7d37edad73..19773a0e93 100644 --- a/esphome/components/tca9555/tca9555.h +++ b/esphome/components/tca9555/tca9555.h @@ -27,9 +27,10 @@ class TCA9555Component : public Component, protected: static void IRAM_ATTR gpio_intr(TCA9555Component *arg); - bool digital_read_hw(uint8_t pin) override; - bool digital_read_cache(uint8_t pin) override; - void digital_write_hw(uint8_t pin, bool value) override; + // Virtual methods from GpioExpander base class — names come from base + bool digital_read_hw(uint8_t pin) override; // NOLINT(readability-identifier-naming) + bool digital_read_cache(uint8_t pin) override; // NOLINT(readability-identifier-naming) + void digital_write_hw(uint8_t pin, bool value) override; // NOLINT(readability-identifier-naming) /// Mask for the pin mode - 1 means output, 0 means input uint16_t mode_mask_{0x00}; diff --git a/esphome/components/wake_on_lan/wake_on_lan.cpp b/esphome/components/wake_on_lan/wake_on_lan.cpp index fee6377965..a514a55d80 100644 --- a/esphome/components/wake_on_lan/wake_on_lan.cpp +++ b/esphome/components/wake_on_lan/wake_on_lan.cpp @@ -1,5 +1,5 @@ #include "wake_on_lan.h" -#ifdef USE_NETWORK +#if defined(USE_NETWORK) && !defined(USE_ZEPHYR) #include "esphome/core/log.h" #include "esphome/components/network/ip_address.h" #include "esphome/components/network/util.h" diff --git a/esphome/components/wake_on_lan/wake_on_lan.h b/esphome/components/wake_on_lan/wake_on_lan.h index 48f8d00a66..84bc26e064 100644 --- a/esphome/components/wake_on_lan/wake_on_lan.h +++ b/esphome/components/wake_on_lan/wake_on_lan.h @@ -1,6 +1,6 @@ #pragma once #include "esphome/core/defines.h" -#ifdef USE_NETWORK +#if defined(USE_NETWORK) && !defined(USE_ZEPHYR) #include "esphome/components/button/button.h" #include "esphome/core/component.h" #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) @@ -32,4 +32,4 @@ class WakeOnLanButton : public button::Button, public Component { } // namespace esphome::wake_on_lan -#endif +#endif // USE_NETWORK && !USE_ZEPHYR diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 3e1baf34ba..ccfc04f674 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -1,5 +1,5 @@ #include "web_server_base.h" -#ifdef USE_NETWORK +#if defined(USE_NETWORK) && !defined(USE_ZEPHYR) namespace esphome::web_server_base { diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 2aa3ae215c..c7162c139a 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -1,6 +1,6 @@ #pragma once #include "esphome/core/defines.h" -#ifdef USE_NETWORK +#if defined(USE_NETWORK) && !defined(USE_ZEPHYR) #include #include @@ -145,4 +145,4 @@ class WebServerBase { }; } // namespace esphome::web_server_base -#endif +#endif // USE_NETWORK && !USE_ZEPHYR diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 0229bc14fa..f536467e2f 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -133,6 +133,7 @@ #define SNTP_SERVER_COUNT 3 #define USE_MEDIA_PLAYER #define USE_MEDIA_SOURCE +#define USE_NETWORK #define USE_NEXTION_COMMAND_SPACING #define USE_NEXTION_CONF_START_UP_PAGE #define USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START @@ -202,7 +203,6 @@ #define USE_SHA256 #define USE_MQTT #define USE_MQTT_COVER_JSON -#define USE_NETWORK #define USE_RTTTL_FINISHED_PLAYBACK_CALLBACK #define USE_RUNTIME_IMAGE_BMP #define USE_RUNTIME_IMAGE_PNG diff --git a/tests/components/network/test.nrf52-adafruit.yaml b/tests/components/network/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..61889b0361 --- /dev/null +++ b/tests/components/network/test.nrf52-adafruit.yaml @@ -0,0 +1 @@ +network: diff --git a/tests/components/network/test.nrf52-mcumgr.yaml b/tests/components/network/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..61889b0361 --- /dev/null +++ b/tests/components/network/test.nrf52-mcumgr.yaml @@ -0,0 +1 @@ +network: diff --git a/tests/components/network/test.nrf52-xiao-ble.yaml b/tests/components/network/test.nrf52-xiao-ble.yaml new file mode 100644 index 0000000000..61889b0361 --- /dev/null +++ b/tests/components/network/test.nrf52-xiao-ble.yaml @@ -0,0 +1 @@ +network: