From f73518dbeb3bf6b6db94e5c96adf63e8834f374c Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Tue, 27 Feb 2024 09:16:20 +0100 Subject: [PATCH] Improve dualstack and IPv6 support (#5449) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/async_tcp/__init__.py | 2 +- .../esp32_improv/esp32_improv_component.cpp | 10 +- .../ethernet/ethernet_component.cpp | 92 +++++++++++-------- .../components/ethernet/ethernet_component.h | 4 +- .../ethernet_info/ethernet_info_text_sensor.h | 21 ++++- .../components/ethernet_info/text_sensor.py | 26 ++++-- .../components/improv_base/improv_base.cpp | 9 +- .../improv_serial/improv_serial_component.cpp | 10 +- esphome/components/mqtt/mqtt_client.cpp | 20 ++-- esphome/components/network/__init__.py | 5 +- esphome/components/network/ip_address.h | 9 ++ esphome/components/network/util.cpp | 6 +- esphome/components/network/util.h | 2 +- esphome/components/socket/socket.cpp | 12 +-- .../components/wake_on_lan/wake_on_lan.cpp | 8 +- esphome/components/wifi/wifi_component.cpp | 13 ++- esphome/components/wifi/wifi_component.h | 8 +- .../wifi/wifi_component_esp32_arduino.cpp | 48 ++++++++-- .../wifi/wifi_component_esp8266.cpp | 26 +++--- .../wifi/wifi_component_esp_idf.cpp | 53 +++++++---- .../wifi/wifi_component_libretiny.cpp | 2 +- .../components/wifi/wifi_component_pico_w.cpp | 10 +- esphome/components/wifi_info/text_sensor.py | 19 +++- .../wifi_info/wifi_info_text_sensor.h | 22 ++++- esphome/const.py | 1 + platformio.ini | 2 +- 26 files changed, 300 insertions(+), 140 deletions(-) diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 0a69943a25..eae8c0e2df 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -22,7 +22,7 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): if CORE.is_esp32 or CORE.is_libretiny: # https://github.com/esphome/AsyncTCP/blob/master/library.json - cg.add_library("esphome/AsyncTCP-esphome", "2.0.1") + cg.add_library("esphome/AsyncTCP-esphome", "2.1.3") elif CORE.is_esp8266: # https://github.com/esphome/ESPAsyncTCP cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0") diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 90e69e1cfa..a6037a773c 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -141,9 +141,13 @@ void ESP32ImprovComponent::loop() { std::vector urls = {ESPHOME_MY_LINK}; #ifdef USE_WEBSERVER - auto ip = wifi::global_wifi_component->wifi_sta_ip(); - std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); - urls.push_back(webserver_url); + for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { + if (ip.is_ip4()) { + std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); + urls.push_back(webserver_url); + break; + } + } #endif std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls); this->send_response_(data); diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 50d5855e6a..8071e9c97a 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -119,10 +119,10 @@ void EthernetComponent::setup() { ESPHL_ERROR_CHECK(err, "ETH event handler register error"); err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &EthernetComponent::got_ip_event_handler, nullptr); ESPHL_ERROR_CHECK(err, "GOT IP event handler register error"); -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 err = esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &EthernetComponent::got_ip6_event_handler, nullptr); - ESPHL_ERROR_CHECK(err, "GOT IP6 event handler register error"); -#endif /* ENABLE_IPV6 */ + ESPHL_ERROR_CHECK(err, "GOT IPv6 event handler register error"); +#endif /* USE_NETWORK_IPV6 */ /* start Ethernet driver state machine */ err = esp_eth_start(this->eth_handle_); @@ -165,20 +165,6 @@ void EthernetComponent::loop() { this->state_ = EthernetComponentState::CONNECTING; this->start_connect_(); } -#if ENABLE_IPV6 - else if (this->got_ipv6_) { - esp_ip6_addr_t ip6_addr; - if (esp_netif_get_ip6_global(this->eth_netif_, &ip6_addr) == 0 && - esp_netif_ip6_get_addr_type(&ip6_addr) == ESP_IP6_ADDR_IS_GLOBAL) { - ESP_LOGCONFIG(TAG, "IPv6 Addr (Global): " IPV6STR, IPV62STR(ip6_addr)); - } else { - esp_netif_get_ip6_linklocal(this->eth_netif_, &ip6_addr); - ESP_LOGCONFIG(TAG, " IPv6: " IPV6STR, IPV62STR(ip6_addr)); - } - - this->got_ipv6_ = false; - } -#endif /* ENABLE_IPV6 */ break; } } @@ -234,10 +220,28 @@ float EthernetComponent::get_setup_priority() const { return setup_priority::WIF bool EthernetComponent::can_proceed() { return this->is_connected(); } -network::IPAddress EthernetComponent::get_ip_address() { +network::IPAddresses EthernetComponent::get_ip_addresses() { + network::IPAddresses addresses; esp_netif_ip_info_t ip; - esp_netif_get_ip_info(this->eth_netif_, &ip); - return network::IPAddress(&ip.ip); + esp_err_t err = esp_netif_get_ip_info(this->eth_netif_, &ip); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); + // TODO: do something smarter + // return false; + } else { + addresses[0] = network::IPAddress(&ip.ip); + } +#if USE_NETWORK_IPV6 + struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES]; + uint8_t count = 0; + count = esp_netif_get_all_ip6(this->eth_netif_, if_ip6s); + assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES); + for (int i = 0; i < count; i++) { + addresses[i + 1] = network::IPAddress(&if_ip6s[i]); + } +#endif /* USE_NETWORK_IPV6 */ + + return addresses; } void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) { @@ -269,20 +273,33 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + ESP_LOGV(TAG, "[Ethernet event] ETH Got IP " IPSTR, IP2STR(&ip_info->ip)); + global_eth_component->got_ipv4_address_ = true; +#if USE_NETWORK_IPV6 + global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT; +#else global_eth_component->connected_ = true; - ESP_LOGV(TAG, "[Ethernet event] ETH Got IP (num=%" PRId32 ")", event_id); +#endif /* USE_NETWORK_IPV6 */ } -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { - ESP_LOGV(TAG, "[Ethernet event] ETH Got IP6 (num=%" PRId32 ")", event_id); - global_eth_component->got_ipv6_ = true; + ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data; + ESP_LOGV(TAG, "[Ethernet event] ETH Got IPv6: " IPV6STR, IPV62STR(event->ip6_info.ip)); global_eth_component->ipv6_count_ += 1; + global_eth_component->connected_ = + global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); } -#endif /* ENABLE_IPV6 */ +#endif /* USE_NETWORK_IPV6 */ void EthernetComponent::start_connect_() { + global_eth_component->got_ipv4_address_ = false; +#if USE_NETWORK_IPV6 + global_eth_component->ipv6_count_ = 0; +#endif /* USE_NETWORK_IPV6 */ this->connect_begin_ = millis(); this->status_set_warning(); @@ -334,12 +351,12 @@ void EthernetComponent::start_connect_() { if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) { ESPHL_ERROR_CHECK(err, "DHCPC start error"); } -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 err = esp_netif_create_ip6_linklocal(this->eth_netif_); if (err != ESP_OK) { - ESPHL_ERROR_CHECK(err, "IPv6 local failed"); + ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed"); } -#endif /* ENABLE_IPV6 */ +#endif /* USE_NETWORK_IPV6 */ } this->connect_begin_ = millis(); @@ -362,18 +379,15 @@ void EthernetComponent::dump_connect_params_() { ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1).str().c_str()); ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2).str().c_str()); -#if ENABLE_IPV6 - if (this->ipv6_count_ > 0) { - esp_ip6_addr_t ip6_addr; - esp_netif_get_ip6_linklocal(this->eth_netif_, &ip6_addr); - ESP_LOGCONFIG(TAG, " IPv6: " IPV6STR, IPV62STR(ip6_addr)); - - if (esp_netif_get_ip6_global(this->eth_netif_, &ip6_addr) == 0 && - esp_netif_ip6_get_addr_type(&ip6_addr) == ESP_IP6_ADDR_IS_GLOBAL) { - ESP_LOGCONFIG(TAG, "IPv6 Addr (Global): " IPV6STR, IPV62STR(ip6_addr)); - } +#if USE_NETWORK_IPV6 + struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES]; + uint8_t count = 0; + count = esp_netif_get_all_ip6(this->eth_netif_, if_ip6s); + assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES); + for (int i = 0; i < count; i++) { + ESP_LOGCONFIG(TAG, " IPv6: " IPV6STR, IPV62STR(if_ip6s[i])); } -#endif /* ENABLE_IPV6 */ +#endif /* USE_NETWORK_IPV6 */ esp_err_t err; diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 11f50af966..bfd0944af7 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -58,7 +58,7 @@ class EthernetComponent : public Component { void set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio); void set_manual_ip(const ManualIP &manual_ip); - network::IPAddress get_ip_address(); + network::IPAddresses get_ip_addresses(); std::string get_use_address() const; void set_use_address(const std::string &use_address); bool powerdown(); @@ -87,8 +87,8 @@ class EthernetComponent : public Component { bool started_{false}; bool connected_{false}; + bool got_ipv4_address_{false}; #if LWIP_IPV6 - bool got_ipv6_{false}; uint8_t ipv6_count_{0}; #endif /* LWIP_IPV6 */ EthernetComponentState state_{EthernetComponentState::STOPPED}; diff --git a/esphome/components/ethernet_info/ethernet_info_text_sensor.h b/esphome/components/ethernet_info/ethernet_info_text_sensor.h index 2d46fe18eb..62c5781f66 100644 --- a/esphome/components/ethernet_info/ethernet_info_text_sensor.h +++ b/esphome/components/ethernet_info/ethernet_info_text_sensor.h @@ -12,19 +12,30 @@ namespace ethernet_info { class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextSensor { public: void update() override { - auto ip = ethernet::global_eth_component->get_ip_address(); - if (ip != this->last_ip_) { - this->last_ip_ = ip; - this->publish_state(network::IPAddress(ip).str()); + auto ips = ethernet::global_eth_component->get_ip_addresses(); + if (ips != this->last_ips_) { + this->last_ips_ = ips; + this->publish_state(ips[0].str()); + uint8_t sensor = 0; + for (auto &ip : ips) { + if (ip.is_set()) { + if (this->ip_sensors_[sensor] != nullptr) { + this->ip_sensors_[sensor]->publish_state(ip.str()); + sensor++; + } + } + } } } float get_setup_priority() const override { return setup_priority::ETHERNET; } std::string unique_id() override { return get_mac_address() + "-ethernetinfo"; } void dump_config() override; + void add_ip_sensors(uint8_t index, text_sensor::TextSensor *s) { this->ip_sensors_[index] = s; } protected: - network::IPAddress last_ip_; + network::IPAddresses last_ips_; + std::array ip_sensors_; }; } // namespace ethernet_info diff --git a/esphome/components/ethernet_info/text_sensor.py b/esphome/components/ethernet_info/text_sensor.py index 7cb9944c92..b802c427e8 100644 --- a/esphome/components/ethernet_info/text_sensor.py +++ b/esphome/components/ethernet_info/text_sensor.py @@ -18,17 +18,25 @@ CONFIG_SCHEMA = cv.Schema( { cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema( IPAddressEsthernetInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC - ).extend(cv.polling_component_schema("1s")) + ) + .extend(cv.polling_component_schema("1s")) + .extend( + { + cv.Optional(f"address_{x}"): text_sensor.text_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ) + for x in range(5) + } + ) } ) -async def setup_conf(config, key): - if key in config: - conf = config[key] - var = await text_sensor.new_text_sensor(conf) - await cg.register_component(var, conf) - - async def to_code(config): - await setup_conf(config, CONF_IP_ADDRESS) + if conf := config.get(CONF_IP_ADDRESS): + ip_info = await text_sensor.new_text_sensor(config[CONF_IP_ADDRESS]) + await cg.register_component(ip_info, config[CONF_IP_ADDRESS]) + for x in range(5): + if sensor_conf := conf.get(f"address_{x}"): + sens = await text_sensor.new_text_sensor(sensor_conf) + cg.add(ip_info.add_ip_sensors(x, sens)) diff --git a/esphome/components/improv_base/improv_base.cpp b/esphome/components/improv_base/improv_base.cpp index f18a1061fb..e890187d1a 100644 --- a/esphome/components/improv_base/improv_base.cpp +++ b/esphome/components/improv_base/improv_base.cpp @@ -21,8 +21,13 @@ std::string ImprovBase::get_formatted_next_url_() { // Ip address pos = this->next_url_.find("{{ip_address}}"); if (pos != std::string::npos) { - std::string ip = network::get_ip_address().str(); - copy.replace(pos, 14, ip); + for (auto &ip : network::get_ip_addresses()) { + if (ip.is_ip4()) { + std::string ipa = ip.str(); + copy.replace(pos, 14, ipa); + break; + } + } } return copy; diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 2318fd43cb..0325bad4df 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -155,9 +155,13 @@ std::vector ImprovSerialComponent::build_rpc_settings_response_(improv: urls.push_back(this->get_formatted_next_url_()); } #ifdef USE_WEBSERVER - auto ip = wifi::global_wifi_component->wifi_sta_ip(); - std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); - urls.push_back(webserver_url); + for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { + if (ip.is_ip4()) { + std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); + urls.push_back(webserver_url); + break; + } + } #endif std::vector data = improv::build_rpc_response(command, urls, false); return data; diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 923762aea4..0b18413928 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -91,8 +91,13 @@ void MQTTClientComponent::send_device_info_() { this->publish_json( topic, [](JsonObject root) { - auto ip = network::get_ip_address(); - root["ip"] = ip.str(); + uint8_t index = 0; + for (auto &ip : network::get_ip_addresses()) { + if (ip.is_set()) { + root["ip" + (index == 0 ? "" : esphome::to_string(index))] = ip.str(); + index++; + } + } root["name"] = App.get_name(); #ifdef USE_API root["port"] = api::global_api_server->get_port(); @@ -159,14 +164,13 @@ void MQTTClientComponent::start_dnslookup_() { this->dns_resolve_error_ = false; this->dns_resolved_ = false; ip_addr_t addr; -#if defined(USE_ESP32) || defined(USE_LIBRETINY) +#if USE_NETWORK_IPV6 + err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, + MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV6_IPV4); +#else err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4); -#endif -#ifdef USE_ESP8266 - err_t err = dns_gethostbyname(this->credentials_.address.c_str(), &addr, - esphome::mqtt::MQTTClientComponent::dns_found_callback, this); -#endif +#endif /* USE_NETWORK_IPV6 */ switch (err) { case ERR_OK: { // Got IP immediately diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index dd1353f86f..4e87ff1c12 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -5,6 +5,7 @@ from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.const import ( CONF_ENABLE_IPV6, + CONF_MIN_IPV6_ADDR_COUNT, ) CODEOWNERS = ["@esphome/core"] @@ -16,12 +17,14 @@ IPAddress = network_ns.class_("IPAddress") CONFIG_SCHEMA = cv.Schema( { cv.Optional(CONF_ENABLE_IPV6, default=False): cv.boolean, + cv.Optional(CONF_MIN_IPV6_ADDR_COUNT, default=0): cv.positive_int, } ) async def to_code(config): - cg.add_define("ENABLE_IPV6", config[CONF_ENABLE_IPV6]) + cg.add_define("USE_NETWORK_IPV6", config[CONF_ENABLE_IPV6]) + cg.add_define("USE_NETWORK_MIN_IPV6_ADDR_COUNT", config[CONF_MIN_IPV6_ADDR_COUNT]) if CORE.using_esp_idf: add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", config[CONF_ENABLE_IPV6]) add_idf_sdkconfig_option( diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index 02e71790a7..b02c358a77 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -77,6 +77,13 @@ struct IPAddress { } #endif /* LWIP_IPV6 */ IPAddress(esp_ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(esp_ip4_addr_t)); } + IPAddress(esp_ip_addr_t *other_ip) { +#if LWIP_IPV6 + memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip_addr_)); +#else + memcpy((void *) &ip_addr_, (void *) &other_ip->u_addr.ip4, sizeof(ip_addr_)); +#endif + } operator esp_ip_addr_t() const { esp_ip_addr_t tmp; #if LWIP_IPV6 @@ -128,5 +135,7 @@ struct IPAddress { ip_addr_t ip_addr_; }; +using IPAddresses = std::array; + } // namespace network } // namespace esphome diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index 109c2947ce..445485b644 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -37,14 +37,14 @@ bool is_disabled() { return false; } -network::IPAddress get_ip_address() { +network::IPAddresses get_ip_addresses() { #ifdef USE_ETHERNET if (ethernet::global_eth_component != nullptr) - return ethernet::global_eth_component->get_ip_address(); + return ethernet::global_eth_component->get_ip_addresses(); #endif #ifdef USE_WIFI if (wifi::global_wifi_component != nullptr) - return wifi::global_wifi_component->get_ip_address(); + return wifi::global_wifi_component->get_ip_addresses(); #endif return {}; } diff --git a/esphome/components/network/util.h b/esphome/components/network/util.h index 0322f19215..5377d44f2f 100644 --- a/esphome/components/network/util.h +++ b/esphome/components/network/util.h @@ -12,7 +12,7 @@ bool is_connected(); bool is_disabled(); /// Get the active network hostname std::string get_use_address(); -IPAddress get_ip_address(); +IPAddresses get_ip_addresses(); } // namespace network } // namespace esphome diff --git a/esphome/components/socket/socket.cpp b/esphome/components/socket/socket.cpp index d0fce9198f..b200046d7f 100644 --- a/esphome/components/socket/socket.cpp +++ b/esphome/components/socket/socket.cpp @@ -10,15 +10,15 @@ namespace socket { Socket::~Socket() {} std::unique_ptr socket_ip(int type, int protocol) { -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 return socket(AF_INET6, type, protocol); #else return socket(AF_INET, type, protocol); -#endif +#endif /* USE_NETWORK_IPV6 */ } socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) { -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 if (addrlen < sizeof(sockaddr_in6)) { errno = EINVAL; return 0; @@ -47,11 +47,11 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::stri server->sin_addr.s_addr = inet_addr(ip_address.c_str()); server->sin_port = htons(port); return sizeof(sockaddr_in); -#endif +#endif /* USE_NETWORK_IPV6 */ } socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port) { -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 if (addrlen < sizeof(sockaddr_in6)) { errno = EINVAL; return 0; @@ -73,7 +73,7 @@ socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t po server->sin_addr.s_addr = ESPHOME_INADDR_ANY; server->sin_port = htons(port); return sizeof(sockaddr_in); -#endif +#endif /* USE_NETWORK_IPV6 */ } } // namespace socket } // namespace esphome diff --git a/esphome/components/wake_on_lan/wake_on_lan.cpp b/esphome/components/wake_on_lan/wake_on_lan.cpp index a4dd0f3b6f..f414bf6c71 100644 --- a/esphome/components/wake_on_lan/wake_on_lan.cpp +++ b/esphome/components/wake_on_lan/wake_on_lan.cpp @@ -32,8 +32,12 @@ void WakeOnLanButton::press_action() { bool end_status = false; IPAddress broadcast = IPAddress(255, 255, 255, 255); #ifdef USE_ESP8266 - begin_status = this->udp_client_.beginPacketMulticast(broadcast, 9, - IPAddress((ip_addr_t) esphome::network::get_ip_address()), 128); + for (auto ip : esphome::network::get_ip_addresses()) { + if (ip.is_ip4()) { + begin_status = this->udp_client_.beginPacketMulticast(broadcast, 9, ip, 128); + break; + } + } #endif #ifdef USE_ESP32 begin_status = this->udp_client_.beginPacket(broadcast, 9); diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 05938d87a2..15a994d8eb 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -207,14 +207,13 @@ void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ = void WiFiComponent::set_btm(bool btm) { this->btm_ = btm; } void WiFiComponent::set_rrm(bool rrm) { this->rrm_ = rrm; } #endif - -network::IPAddress WiFiComponent::get_ip_address() { +network::IPAddresses WiFiComponent::get_ip_addresses() { if (this->has_sta()) - return this->wifi_sta_ip(); + return this->wifi_sta_ip_addresses(); #ifdef USE_WIFI_AP if (this->has_ap()) - return this->wifi_soft_ap_ip(); + return {this->wifi_soft_ap_ip()}; #endif // USE_WIFI_AP return {}; @@ -412,7 +411,11 @@ void WiFiComponent::print_connect_params_() { return; } ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str()); - ESP_LOGCONFIG(TAG, " IP Address: %s", wifi_sta_ip().str().c_str()); + for (auto &ip : wifi_sta_ip_addresses()) { + if (ip.is_set()) { + ESP_LOGCONFIG(TAG, " IP Address: %s", ip.str().c_str()); + } + } ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index be5095105c..d9cb6a9ae2 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -258,7 +258,7 @@ class WiFiComponent : public Component { #endif network::IPAddress get_dns_address(int num); - network::IPAddress get_ip_address(); + network::IPAddresses get_ip_addresses(); std::string get_use_address() const; void set_use_address(const std::string &use_address); @@ -293,7 +293,7 @@ class WiFiComponent : public Component { }); } - network::IPAddress wifi_sta_ip(); + network::IPAddresses wifi_sta_ip_addresses(); std::string wifi_ssid(); bssid_t wifi_bssid(); @@ -396,6 +396,10 @@ class WiFiComponent : public Component { bool rrm_{false}; #endif bool enable_on_boot_; + bool got_ipv4_address_{false}; +#if USE_NETWORK_IPV6 + uint8_t num_ipv6_addresses_{0}; +#endif /* USE_NETWORK_IPV6 */ Trigger<> *connect_trigger_{new Trigger<>()}; Trigger<> *disconnect_trigger_{new Trigger<>()}; diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 5d8aa7f749..d7241fb66c 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -131,7 +131,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { // TODO: is this needed? #if LWIP_IPV6 dns.type = IPADDR_TYPE_V4; -#endif +#endif /* LWIP_IPV6 */ if (manual_ip->dns1.is_set()) { dns = manual_ip->dns1; dns_setserver(0, &dns); @@ -144,12 +144,36 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return true; } -network::IPAddress WiFiComponent::wifi_sta_ip() { +network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { if (!this->has_sta()) return {}; + network::IPAddresses addresses; tcpip_adapter_ip_info_t ip; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); - return network::IPAddress(&ip.ip); + esp_err_t err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); + // TODO: do something smarter + // return false; + } else { + addresses[0] = network::IPAddress(&ip.ip); + } +#if LWIP_IPV6 + ip6_addr_t ipv6; + err = tcpip_adapter_get_ip6_global(TCPIP_ADAPTER_IF_STA, &ipv6); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_netif_get_ip6_gobal failed: %s", esp_err_to_name(err)); + } else { + addresses[1] = network::IPAddress(&ipv6); + } + err = tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_STA, &ipv6); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_netif_get_ip6_linklocal failed: %s", esp_err_to_name(err)); + } else { + addresses[2] = network::IPAddress(&ipv6); + } +#endif /* LWIP_IPV6 */ + + return addresses; } bool WiFiComponent::wifi_apply_hostname_() { @@ -440,9 +464,9 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ buf[it.ssid_len] = '\0'; ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 this->set_timeout(100, [] { WiFi.enableIpV6(); }); -#endif /* ENABLE_IPV6 */ +#endif /* USE_NETWORK_IPV6 */ break; } @@ -494,18 +518,26 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ auto it = info.got_ip.ip_info; ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(), format_ip4_addr(it.gw).c_str()); + this->got_ipv4_address_ = true; +#if USE_NETWORK_IPV6 + s_sta_connecting = this->num_ipv6_addresses_ < USE_NETWORK_MIN_IPV6_ADDR_COUNT; +#else s_sta_connecting = false; +#endif /* USE_NETWORK_IPV6 */ break; } -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6: { auto it = info.got_ip6.ip6_info; ESP_LOGV(TAG, "Got IPv6 address=" IPV6STR, IPV62STR(it.ip)); + this->num_ipv6_addresses_++; + s_sta_connecting = !(this->got_ipv4_address_ & (this->num_ipv6_addresses_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT)); break; } -#endif /* ENABLE_IPV6 */ +#endif /* USE_NETWORK_IPV6 */ case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: { ESP_LOGV(TAG, "Event: Lost IP"); + this->got_ipv4_address_ = false; break; } case ESPHOME_EVENT_ID_WIFI_AP_START: { diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 15b0c65641..f274e37a9f 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -17,10 +17,8 @@ extern "C" { #include "lwip/dhcp.h" #include "lwip/init.h" // LWIP_VERSION_ #include "lwip/apps/sntp.h" -#if LWIP_IPV6 #include "lwip/netif.h" // struct netif #include -#endif #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0) #include "LwipDhcpServer.h" #define wifi_softap_set_dhcps_lease(lease) dhcpSoftAP.set_dhcps_lease(lease) @@ -185,12 +183,15 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return ret; } -network::IPAddress WiFiComponent::wifi_sta_ip() { +network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { if (!this->has_sta()) return {}; - struct ip_info ip {}; - wifi_get_ip_info(STATION_IF, &ip); - return network::IPAddress(&ip.ip); + network::IPAddresses addresses; + uint8_t index = 0; + for (auto &addr : addrList) { + addresses[index++] = addr.ipFromNetifNum(); + } + return addresses; } bool WiFiComponent::wifi_apply_hostname_() { const std::string &hostname = App.get_name(); @@ -327,17 +328,20 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { return false; } -#if ENABLE_IPV6 - for (bool configured = false; !configured;) { +#if USE_NETWORK_IPV6 + bool connected = false; + while (!connected) { + uint8_t ipv6_addr_count = 0; for (auto addr : addrList) { ESP_LOGV(TAG, "Address %s", addr.toString().c_str()); - if ((configured = !addr.isLocal() && addr.isV6())) { - break; + if (addr.isV6()) { + ipv6_addr_count++; } } delay(500); // NOLINT + connected = (ipv6_addr_count >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); } -#endif +#endif /* USE_NETWORK_IPV6 */ if (ap.get_channel().has_value()) { ret = wifi_set_channel(*ap.get_channel()); diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 0035733553..ebb2fb92ea 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -39,14 +39,11 @@ static const char *const TAG = "wifi_esp32"; static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static QueueHandle_t s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - #ifdef USE_WIFI_AP -static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -#endif // USE_WIFI_AP - +static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +#endif // USE_WIFI_AP static bool s_sta_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static bool s_ap_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -66,9 +63,9 @@ struct IDFWiFiEvent { wifi_event_ap_probe_req_rx_t ap_probe_req_rx; wifi_event_bss_rssi_low_t bss_rssi_low; ip_event_got_ip_t ip_got_ip; -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 ip_event_got_ip6_t ip_got_ip6; -#endif /* ENABLE_IPV6 */ +#endif /* USE_NETWORK_IPV6 */ ip_event_ap_staipassigned_t ip_ap_staipassigned; } data; }; @@ -92,7 +89,7 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi memcpy(&event.data.sta_disconnected, event_data, sizeof(wifi_event_sta_disconnected_t)); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { memcpy(&event.data.ip_got_ip, event_data, sizeof(ip_event_got_ip_t)); -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 } else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) { memcpy(&event.data.ip_got_ip6, event_data, sizeof(ip_event_got_ip6_t)); #endif @@ -411,7 +408,6 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // may be called from wifi_station_connect s_sta_connecting = true; s_sta_connected = false; - s_sta_got_ip = false; s_sta_connect_error = false; s_sta_connect_not_found = false; @@ -476,17 +472,29 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return true; } -network::IPAddress WiFiComponent::wifi_sta_ip() { +network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { if (!this->has_sta()) return {}; + network::IPAddresses addresses; esp_netif_ip_info_t ip; esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip); if (err != ESP_OK) { ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); // TODO: do something smarter // return false; + } else { + addresses[0] = network::IPAddress(&ip.ip); } - return network::IPAddress(&ip.ip); +#if USE_NETWORK_IPV6 + struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES]; + uint8_t count = 0; + count = esp_netif_get_all_ip6(s_sta_netif, if_ip6s); + assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES); + for (int i = 0; i < count; i++) { + addresses[i + 1] = network::IPAddress(&if_ip6s[i]); + } +#endif /* USE_NETWORK_IPV6 */ + return addresses; } bool WiFiComponent::wifi_apply_hostname_() { @@ -521,7 +529,7 @@ const char *get_auth_mode_str(uint8_t mode) { std::string format_ip4_addr(const esp_ip4_addr_t &ip) { return str_snprintf(IPSTR, 15, IP2STR(&ip)); } #if LWIP_IPV6 std::string format_ip6_addr(const esp_ip6_addr_t &ip) { return str_snprintf(IPV6STR, 39, IPV62STR(ip)); } -#endif +#endif /* LWIP_IPV6 */ const char *get_disconnect_reason_str(uint8_t reason) { switch (reason) { case WIFI_REASON_AUTH_EXPIRE: @@ -658,22 +666,23 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) { const auto &it = data->data.ip_got_ip; -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 esp_netif_create_ip6_linklocal(s_sta_netif); -#endif /* ENABLE_IPV6 */ +#endif /* USE_NETWORK_IPV6 */ ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(), format_ip4_addr(it.ip_info.gw).c_str()); - s_sta_got_ip = true; + this->got_ipv4_address_ = true; -#if ENABLE_IPV6 +#if USE_NETWORK_IPV6 } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_GOT_IP6) { const auto &it = data->data.ip_got_ip6; ESP_LOGV(TAG, "Event: Got IPv6 address=%s", format_ip6_addr(it.ip6_info.ip).c_str()); -#endif /* ENABLE_IPV6 */ + this->num_ipv6_addresses_++; +#endif /* USE_NETWORK_IPV6 */ } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_LOST_IP) { ESP_LOGV(TAG, "Event: Lost IP"); - s_sta_got_ip = false; + this->got_ipv4_address_ = false; } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_SCAN_DONE) { const auto &it = data->data.sta_scan_done; @@ -737,8 +746,14 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { } WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { - if (s_sta_connected && s_sta_got_ip) { + if (s_sta_connected && this->got_ipv4_address_) { +#if USE_NETWORK_IPV6 + if (this->num_ipv6_addresses_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT) { + return WiFiSTAConnectStatus::CONNECTED; + } +#else return WiFiSTAConnectStatus::CONNECTED; +#endif /* USE_NETWORK_IPV6 */ } if (s_sta_connect_error) { return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index 29c6ce64d0..f6b0fb2699 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -81,7 +81,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return true; } -network::IPAddress WiFiComponent::wifi_sta_ip() { +network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { if (!this->has_sta()) return {}; return {WiFi.localIP()}; diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index c71203a877..2bb1af5489 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -6,6 +6,7 @@ #include "lwip/dns.h" #include "lwip/err.h" #include "lwip/netif.h" +#include #include "esphome/core/application.h" #include "esphome/core/hal.h" @@ -173,7 +174,14 @@ std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } -network::IPAddress WiFiComponent::wifi_sta_ip() { return {(const ip_addr_t *) WiFi.localIP()}; } +network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { + network::IPAddresses addresses; + uint8_t index = 0; + for (auto addr : addrList) { + addresses[index++] = addr.ipFromNetifNum(); + } + return addresses; +} network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; } network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; } network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { diff --git a/esphome/components/wifi_info/text_sensor.py b/esphome/components/wifi_info/text_sensor.py index 659fd08db1..75513712dd 100644 --- a/esphome/components/wifi_info/text_sensor.py +++ b/esphome/components/wifi_info/text_sensor.py @@ -37,7 +37,16 @@ CONFIG_SCHEMA = cv.Schema( { cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema( IPAddressWiFiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC - ).extend(cv.polling_component_schema("1s")), + ) + .extend(cv.polling_component_schema("1s")) + .extend( + { + cv.Optional(f"address_{x}"): text_sensor.text_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ) + for x in range(5) + } + ), cv.Optional(CONF_SCAN_RESULTS): text_sensor.text_sensor_schema( ScanResultsWiFiInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC ).extend(cv.polling_component_schema("60s")), @@ -65,9 +74,15 @@ async def setup_conf(config, key): async def to_code(config): - await setup_conf(config, CONF_IP_ADDRESS) await setup_conf(config, CONF_SSID) await setup_conf(config, CONF_BSSID) await setup_conf(config, CONF_MAC_ADDRESS) await setup_conf(config, CONF_SCAN_RESULTS) await setup_conf(config, CONF_DNS_ADDRESS) + if conf := config.get(CONF_IP_ADDRESS): + wifi_info = await text_sensor.new_text_sensor(config[CONF_IP_ADDRESS]) + await cg.register_component(wifi_info, config[CONF_IP_ADDRESS]) + for x in range(5): + if sensor_conf := conf.get(f"address_{x}"): + sens = await text_sensor.new_text_sensor(sensor_conf) + cg.add(wifi_info.add_ip_sensors(x, sens)) diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index 35ce108c86..9ffbd3f418 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/wifi/wifi_component.h" +#include namespace esphome { namespace wifi_info { @@ -10,18 +11,29 @@ namespace wifi_info { class IPAddressWiFiInfo : public PollingComponent, public text_sensor::TextSensor { public: void update() override { - auto ip = wifi::global_wifi_component->wifi_sta_ip(); - if (ip != this->last_ip_) { - this->last_ip_ = ip; - this->publish_state(ip.str()); + auto ips = wifi::global_wifi_component->wifi_sta_ip_addresses(); + if (ips != this->last_ips_) { + this->last_ips_ = ips; + this->publish_state(ips[0].str()); + uint8_t sensor = 0; + for (auto &ip : ips) { + if (ip.is_set()) { + if (this->ip_sensors_[sensor] != nullptr) { + this->ip_sensors_[sensor]->publish_state(ip.str()); + sensor++; + } + } + } } } float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } std::string unique_id() override { return get_mac_address() + "-wifiinfo-ip"; } void dump_config() override; + void add_ip_sensors(uint8_t index, text_sensor::TextSensor *s) { this->ip_sensors_[index] = s; } protected: - network::IPAddress last_ip_; + network::IPAddresses last_ips_; + std::array ip_sensors_; }; class DNSAddressWifiInfo : public PollingComponent, public text_sensor::TextSensor { diff --git a/esphome/const.py b/esphome/const.py index 1d7f43d841..044465de7b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -449,6 +449,7 @@ CONF_MIN_FANNING_RUN_TIME = "min_fanning_run_time" CONF_MIN_HEATING_OFF_TIME = "min_heating_off_time" CONF_MIN_HEATING_RUN_TIME = "min_heating_run_time" CONF_MIN_IDLE_TIME = "min_idle_time" +CONF_MIN_IPV6_ADDR_COUNT = "min_ipv6_addr_count" CONF_MIN_LENGTH = "min_length" CONF_MIN_LEVEL = "min_level" CONF_MIN_POWER = "min_power" diff --git a/platformio.ini b/platformio.ini index 8ab2fb6f48..8ba8b8a2cf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -116,7 +116,7 @@ lib_deps = WiFi ; wifi,web_server_base,ethernet (Arduino built-in) Update ; ota,web_server_base (Arduino built-in) ${common:arduino.lib_deps} - esphome/AsyncTCP-esphome@1.2.2 ; async_tcp + esphome/AsyncTCP-esphome@2.1.3 ; async_tcp WiFiClientSecure ; http_request,nextion (Arduino built-in) HTTPClient ; http_request,nextion (Arduino built-in) ESPmDNS ; mdns (Arduino built-in)