mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Add multicast support to udp component (#8051)
This commit is contained in:
		| @@ -49,6 +49,7 @@ struct IPAddress { | ||||
|   } | ||||
|   IPAddress(const std::string &in_address) { inet_aton(in_address.c_str(), &ip_addr_); } | ||||
|   IPAddress(const ip_addr_t *other_ip) { ip_addr_ = *other_ip; } | ||||
|   std::string str() const { return str_lower_case(inet_ntoa(ip_addr_)); } | ||||
| #else | ||||
|   IPAddress() { ip_addr_set_zero(&ip_addr_); } | ||||
|   IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { | ||||
| @@ -119,6 +120,7 @@ struct IPAddress { | ||||
|   bool is_set() { return !ip_addr_isany(&ip_addr_); }  // NOLINT(readability-simplify-boolean-expr) | ||||
|   bool is_ip4() { return IP_IS_V4(&ip_addr_); } | ||||
|   bool is_ip6() { return IP_IS_V6(&ip_addr_); } | ||||
|   bool is_multicast() { return ip_addr_ismulticast(&ip_addr_); } | ||||
|   std::string str() const { return str_lower_case(ipaddr_ntoa(&ip_addr_)); } | ||||
|   bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); } | ||||
|   bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); } | ||||
|   | ||||
| @@ -27,6 +27,7 @@ UDPComponent = udp_ns.class_("UDPComponent", cg.PollingComponent) | ||||
| CONF_BROADCAST = "broadcast" | ||||
| CONF_BROADCAST_ID = "broadcast_id" | ||||
| CONF_ADDRESSES = "addresses" | ||||
| CONF_LISTEN_ADDRESS = "listen_address" | ||||
| CONF_PROVIDER = "provider" | ||||
| CONF_PROVIDERS = "providers" | ||||
| CONF_REMOTE_ID = "remote_id" | ||||
| @@ -84,6 +85,9 @@ CONFIG_SCHEMA = cv.All( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(UDPComponent), | ||||
|             cv.Optional(CONF_PORT, default=18511): cv.port, | ||||
|             cv.Optional( | ||||
|                 CONF_LISTEN_ADDRESS, default="255.255.255.255" | ||||
|             ): cv.ipv4address_multi_broadcast, | ||||
|             cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list( | ||||
|                 cv.ipv4address, | ||||
|             ), | ||||
| @@ -154,5 +158,7 @@ async def to_code(config): | ||||
|     for provider in config.get(CONF_PROVIDERS, ()): | ||||
|         name = provider[CONF_NAME] | ||||
|         cg.add(var.add_provider(name)) | ||||
|         if (listen_address := str(config[CONF_LISTEN_ADDRESS])) != "255.255.255.255": | ||||
|             cg.add(var.set_listen_address(listen_address)) | ||||
|         if encryption := provider.get(CONF_ENCRYPTION): | ||||
|             cg.add(var.set_provider_encryption(name, hash_encryption_key(encryption))) | ||||
|   | ||||
| @@ -249,6 +249,21 @@ void UDPComponent::setup() { | ||||
|     server.sin_addr.s_addr = ESPHOME_INADDR_ANY; | ||||
|     server.sin_port = htons(this->port_); | ||||
|  | ||||
|     if (this->listen_address_.has_value()) { | ||||
|       struct ip_mreq imreq = {}; | ||||
|       imreq.imr_interface.s_addr = ESPHOME_INADDR_ANY; | ||||
|       inet_aton(this->listen_address_.value().str().c_str(), &imreq.imr_multiaddr); | ||||
|       server.sin_addr.s_addr = imreq.imr_multiaddr.s_addr; | ||||
|       ESP_LOGV(TAG, "Join multicast %s", this->listen_address_.value().str().c_str()); | ||||
|       err = this->listen_socket_->setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(imreq)); | ||||
|       if (err < 0) { | ||||
|         ESP_LOGE(TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno); | ||||
|         this->mark_failed(); | ||||
|         this->status_set_error("Failed to set IP_ADD_MEMBERSHIP"); | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server)); | ||||
|     if (err != 0) { | ||||
|       ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); | ||||
| @@ -565,6 +580,9 @@ void UDPComponent::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "  Ping-pong: %s", YESNO(this->ping_pong_enable_)); | ||||
|   for (const auto &address : this->addresses_) | ||||
|     ESP_LOGCONFIG(TAG, "  Address: %s", address.c_str()); | ||||
|   if (this->listen_address_.has_value()) { | ||||
|     ESP_LOGCONFIG(TAG, "  Listen address: %s", this->listen_address_.value().str().c_str()); | ||||
|   } | ||||
| #ifdef USE_SENSOR | ||||
|   for (auto sensor : this->sensors_) | ||||
|     ESP_LOGCONFIG(TAG, "  Sensor: %s", sensor.id); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/network/ip_address.h" | ||||
| #ifdef USE_SENSOR | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #endif | ||||
| @@ -69,6 +70,7 @@ class UDPComponent : public PollingComponent { | ||||
|   } | ||||
| #endif | ||||
|   void add_address(const char *addr) { this->addresses_.emplace_back(addr); } | ||||
|   void set_listen_address(const char *listen_addr) { this->listen_address_ = network::IPAddress(listen_addr); } | ||||
|   void set_port(uint16_t port) { this->port_ = port; } | ||||
|   float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } | ||||
|  | ||||
| @@ -143,6 +145,7 @@ class UDPComponent : public PollingComponent { | ||||
|   std::map<std::string, std::map<std::string, binary_sensor::BinarySensor *>> remote_binary_sensors_{}; | ||||
| #endif | ||||
|  | ||||
|   optional<network::IPAddress> listen_address_{}; | ||||
|   std::map<std::string, Provider> providers_{}; | ||||
|   std::vector<uint8_t> ping_header_{}; | ||||
|   std::vector<uint8_t> header_{}; | ||||
|   | ||||
| @@ -1168,6 +1168,15 @@ def ipv4address(value): | ||||
|     return address | ||||
|  | ||||
|  | ||||
| def ipv4address_multi_broadcast(value): | ||||
|     address = ipv4address(value) | ||||
|     if not (address.is_multicast or (address == IPv4Address("255.255.255.255"))): | ||||
|         raise Invalid( | ||||
|             f"{value} is not a multicasst address nor local broadcast address" | ||||
|         ) | ||||
|     return address | ||||
|  | ||||
|  | ||||
| def ipaddress(value): | ||||
|     try: | ||||
|         address = ip_address(value) | ||||
|   | ||||
| @@ -7,6 +7,7 @@ udp: | ||||
|   encryption: "our key goes here" | ||||
|   rolling_code_enable: true | ||||
|   ping_pong_enable: true | ||||
|   listen_address: 239.0.60.53 | ||||
|   binary_sensors: | ||||
|     - binary_sensor_id1 | ||||
|     - id: binary_sensor_id1 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user