Improve dualstack and IPv6 support (#5449)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Jimmy Hedman 2024-02-27 09:16:20 +01:00 committed by GitHub
parent 5e04914a11
commit f73518dbeb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 300 additions and 140 deletions

View file

@ -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")

View file

@ -141,9 +141,13 @@ void ESP32ImprovComponent::loop() {
std::vector<std::string> 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<uint8_t> data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls);
this->send_response_(data);

View file

@ -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;

View file

@ -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};

View file

@ -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<text_sensor::TextSensor *, 5> ip_sensors_;
};
} // namespace ethernet_info

View file

@ -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))

View file

@ -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;

View file

@ -155,9 +155,13 @@ std::vector<uint8_t> 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<uint8_t> data = improv::build_rpc_response(command, urls, false);
return data;

View file

@ -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

View file

@ -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(

View file

@ -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<IPAddress, 5>;
} // namespace network
} // namespace esphome

View file

@ -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 {};
}

View file

@ -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

View file

@ -10,15 +10,15 @@ namespace socket {
Socket::~Socket() {}
std::unique_ptr<Socket> 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

View file

@ -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);

View file

@ -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());

View file

@ -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<>()};

View file

@ -131,7 +131,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> 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<ManualIP> 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: {

View file

@ -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 <AddrList.h>
#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<ManualIP> 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());

View file

@ -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<ManualIP> 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;

View file

@ -81,7 +81,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> 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()};

View file

@ -6,6 +6,7 @@
#include "lwip/dns.h"
#include "lwip/err.h"
#include "lwip/netif.h"
#include <AddrList.h>
#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) {

View file

@ -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))

View file

@ -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 <array>
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<text_sensor::TextSensor *, 5> ip_sensors_;
};
class DNSAddressWifiInfo : public PollingComponent, public text_sensor::TextSensor {

View file

@ -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"

View file

@ -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)