ESPHome  2024.12.2
udp_component.cpp
Go to the documentation of this file.
1 #include "esphome/core/log.h"
4 #include "udp_component.h"
5 
6 namespace esphome {
7 namespace udp {
8 
48 static const char *const TAG = "udp";
49 
54 static const uint32_t DELTA = 0x9e3779b9;
55 #define MX ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p ^ e) & 7] ^ z)))
56 
61 static void xxtea_encrypt(uint32_t *v, size_t n, const uint32_t *k) {
62  uint32_t z, y, sum, e;
63  size_t p;
64  size_t q = 6 + 52 / n;
65  sum = 0;
66  z = v[n - 1];
67  while (q-- != 0) {
68  sum += DELTA;
69  e = (sum >> 2);
70  for (p = 0; p != n - 1; p++) {
71  y = v[p + 1];
72  z = v[p] += MX;
73  }
74  y = v[0];
75  z = v[n - 1] += MX;
76  }
77 }
78 
79 static void xxtea_decrypt(uint32_t *v, size_t n, const uint32_t *k) {
80  uint32_t z, y, sum, e;
81  size_t p;
82  size_t q = 6 + 52 / n;
83  sum = q * DELTA;
84  y = v[0];
85  while (q-- != 0) {
86  e = (sum >> 2);
87  for (p = n - 1; p != 0; p--) {
88  z = v[p - 1];
89  y = v[p] -= MX;
90  }
91  z = v[n - 1];
92  y = v[0] -= MX;
93  sum -= DELTA;
94  }
95 }
96 
97 inline static size_t round4(size_t value) { return (value + 3) & ~3; }
98 
99 union FuData {
100  uint32_t u32;
101  float f32;
102 };
103 
104 static const size_t MAX_PACKET_SIZE = 508;
105 static const uint16_t MAGIC_NUMBER = 0x4553;
106 static const uint16_t MAGIC_PING = 0x5048;
107 static const uint32_t PREF_HASH = 0x45535043;
108 enum DataKey {
115 };
116 
117 static const size_t MAX_PING_KEYS = 4;
118 
119 static inline void add(std::vector<uint8_t> &vec, uint32_t data) {
120  vec.push_back(data & 0xFF);
121  vec.push_back((data >> 8) & 0xFF);
122  vec.push_back((data >> 16) & 0xFF);
123  vec.push_back((data >> 24) & 0xFF);
124 }
125 
126 static inline uint32_t get_uint32(uint8_t *&buf) {
127  uint32_t data = *buf++;
128  data += *buf++ << 8;
129  data += *buf++ << 16;
130  data += *buf++ << 24;
131  return data;
132 }
133 
134 static inline uint16_t get_uint16(uint8_t *&buf) {
135  uint16_t data = *buf++;
136  data += *buf++ << 8;
137  return data;
138 }
139 
140 static inline void add(std::vector<uint8_t> &vec, uint8_t data) { vec.push_back(data); }
141 static inline void add(std::vector<uint8_t> &vec, uint16_t data) {
142  vec.push_back((uint8_t) data);
143  vec.push_back((uint8_t) (data >> 8));
144 }
145 static inline void add(std::vector<uint8_t> &vec, DataKey data) { vec.push_back(data); }
146 static void add(std::vector<uint8_t> &vec, const char *str) {
147  auto len = strlen(str);
148  vec.push_back(len);
149  for (size_t i = 0; i != len; i++) {
150  vec.push_back(*str++);
151  }
152 }
153 
155  this->name_ = App.get_name().c_str();
156  if (strlen(this->name_) > 255) {
157  this->mark_failed();
158  this->status_set_error("Device name exceeds 255 chars");
159  return;
160  }
161  this->resend_ping_key_ = this->ping_pong_enable_;
162  // restore the upper 32 bits of the rolling code, increment and save.
163  this->pref_ = global_preferences->make_preference<uint32_t>(PREF_HASH, true);
164  this->pref_.load(&this->rolling_code_[1]);
165  this->rolling_code_[1]++;
166  this->pref_.save(&this->rolling_code_[1]);
167  this->ping_key_ = random_uint32();
168  ESP_LOGV(TAG, "Rolling code incremented, upper part now %u", (unsigned) this->rolling_code_[1]);
169 #ifdef USE_SENSOR
170  for (auto &sensor : this->sensors_) {
171  sensor.sensor->add_on_state_callback([this, &sensor](float x) {
172  this->updated_ = true;
173  sensor.updated = true;
174  });
175  }
176 #endif
177 #ifdef USE_BINARY_SENSOR
178  for (auto &sensor : this->binary_sensors_) {
179  sensor.sensor->add_on_state_callback([this, &sensor](bool value) {
180  this->updated_ = true;
181  sensor.updated = true;
182  });
183  }
184 #endif
185  this->should_send_ = this->ping_pong_enable_;
186 #ifdef USE_SENSOR
187  this->should_send_ |= !this->sensors_.empty();
188 #endif
189 #ifdef USE_BINARY_SENSOR
190  this->should_send_ |= !this->binary_sensors_.empty();
191 #endif
192  this->should_listen_ = !this->providers_.empty() || this->is_encrypted_();
193  // initialise the header. This is invariant.
194  add(this->header_, MAGIC_NUMBER);
195  add(this->header_, this->name_);
196  // pad to a multiple of 4 bytes
197  while (this->header_.size() & 0x3)
198  this->header_.push_back(0);
199 #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
200  for (const auto &address : this->addresses_) {
201  struct sockaddr saddr {};
202  socket::set_sockaddr(&saddr, sizeof(saddr), address, this->port_);
203  this->sockaddrs_.push_back(saddr);
204  }
205  // set up broadcast socket
206  if (this->should_send_) {
207  this->broadcast_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
208  if (this->broadcast_socket_ == nullptr) {
209  this->mark_failed();
210  this->status_set_error("Could not create socket");
211  return;
212  }
213  int enable = 1;
214  auto err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
215  if (err != 0) {
216  this->status_set_warning("Socket unable to set reuseaddr");
217  // we can still continue
218  }
219  err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
220  if (err != 0) {
221  this->status_set_warning("Socket unable to set broadcast");
222  }
223  }
224  // create listening socket if we either want to subscribe to providers, or need to listen
225  // for ping key broadcasts.
226  if (this->should_listen_) {
227  this->listen_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
228  if (this->listen_socket_ == nullptr) {
229  this->mark_failed();
230  this->status_set_error("Could not create socket");
231  return;
232  }
233  auto err = this->listen_socket_->setblocking(false);
234  if (err < 0) {
235  ESP_LOGE(TAG, "Unable to set nonblocking: errno %d", errno);
236  this->mark_failed();
237  this->status_set_error("Unable to set nonblocking");
238  return;
239  }
240  int enable = 1;
241  err = this->listen_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
242  if (err != 0) {
243  this->status_set_warning("Socket unable to set reuseaddr");
244  // we can still continue
245  }
246  struct sockaddr_in server {};
247 
248  socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
249  if (sl == 0) {
250  ESP_LOGE(TAG, "Socket unable to set sockaddr: errno %d", errno);
251  this->mark_failed();
252  this->status_set_error("Unable to set sockaddr");
253  return;
254  }
255 
256  err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server));
257  if (err != 0) {
258  ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
259  this->mark_failed();
260  this->status_set_error("Unable to bind socket");
261  return;
262  }
263  }
264 #endif
265 #ifdef USE_SOCKET_IMPL_LWIP_TCP
266  // 8266 and RP2040 `Duino
267  for (const auto &address : this->addresses_) {
268  auto ipaddr = IPAddress();
269  ipaddr.fromString(address.c_str());
270  this->ipaddrs_.push_back(ipaddr);
271  }
272  if (this->should_listen_)
273  this->udp_client_.begin(this->port_);
274 #endif
275 }
276 
278  this->data_.clear();
279  if (this->rolling_code_enable_) {
280  add(this->data_, ROLLING_CODE_KEY);
281  add(this->data_, this->rolling_code_[0]);
282  add(this->data_, this->rolling_code_[1]);
283  this->increment_code_();
284  } else {
285  add(this->data_, DATA_KEY);
286  }
287  for (auto pkey : this->ping_keys_) {
288  add(this->data_, PING_KEY);
289  add(this->data_, pkey.second);
290  }
291 }
292 
294  if (!network::is_connected() || this->data_.empty())
295  return;
296  uint32_t buffer[MAX_PACKET_SIZE / 4];
297  memset(buffer, 0, sizeof buffer);
298  // len must be a multiple of 4
299  auto header_len = round4(this->header_.size()) / 4;
300  auto len = round4(data_.size()) / 4;
301  memcpy(buffer, this->header_.data(), this->header_.size());
302  memcpy(buffer + header_len, this->data_.data(), this->data_.size());
303  if (this->is_encrypted_()) {
304  xxtea_encrypt(buffer + header_len, len, (uint32_t *) this->encryption_key_.data());
305  }
306  auto total_len = (header_len + len) * 4;
307  this->send_packet_(buffer, total_len);
308 }
309 
310 void UDPComponent::add_binary_data_(uint8_t key, const char *id, bool data) {
311  auto len = 1 + 1 + 1 + strlen(id);
312  if (len + this->header_.size() + this->data_.size() > MAX_PACKET_SIZE) {
313  this->flush_();
314  }
315  add(this->data_, key);
316  add(this->data_, (uint8_t) data);
317  add(this->data_, id);
318 }
319 void UDPComponent::add_data_(uint8_t key, const char *id, float data) {
320  FuData udata{.f32 = data};
321  this->add_data_(key, id, udata.u32);
322 }
323 
324 void UDPComponent::add_data_(uint8_t key, const char *id, uint32_t data) {
325  auto len = 4 + 1 + 1 + strlen(id);
326  if (len + this->header_.size() + this->data_.size() > MAX_PACKET_SIZE) {
327  this->flush_();
328  }
329  add(this->data_, key);
330  add(this->data_, data);
331  add(this->data_, id);
332 }
333 void UDPComponent::send_data_(bool all) {
334  if (!this->should_send_ || !network::is_connected())
335  return;
336  this->init_data_();
337 #ifdef USE_SENSOR
338  for (auto &sensor : this->sensors_) {
339  if (all || sensor.updated) {
340  sensor.updated = false;
341  this->add_data_(SENSOR_KEY, sensor.id, sensor.sensor->get_state());
342  }
343  }
344 #endif
345 #ifdef USE_BINARY_SENSOR
346  for (auto &sensor : this->binary_sensors_) {
347  if (all || sensor.updated) {
348  sensor.updated = false;
349  this->add_binary_data_(BINARY_SENSOR_KEY, sensor.id, sensor.sensor->state);
350  }
351  }
352 #endif
353  this->flush_();
354  this->updated_ = false;
355  this->resend_data_ = false;
356 }
357 
359  this->updated_ = true;
360  this->resend_data_ = this->should_send_;
361  auto now = millis() / 1000;
362  if (this->last_key_time_ + this->ping_pong_recyle_time_ < now) {
363  this->resend_ping_key_ = this->ping_pong_enable_;
364  this->last_key_time_ = now;
365  }
366 }
367 
369  uint8_t buf[MAX_PACKET_SIZE];
370  if (this->should_listen_) {
371  for (;;) {
372 #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
373  auto len = this->listen_socket_->read(buf, sizeof(buf));
374 #endif
375 #ifdef USE_SOCKET_IMPL_LWIP_TCP
376  auto len = this->udp_client_.parsePacket();
377  if (len > 0)
378  len = this->udp_client_.read(buf, sizeof(buf));
379 #endif
380  if (len > 0) {
381  this->process_(buf, len);
382  continue;
383  }
384  break;
385  }
386  }
387  if (this->resend_ping_key_)
388  this->send_ping_pong_request_();
389  if (this->updated_) {
390  this->send_data_(this->resend_data_);
391  }
392 }
393 
394 void UDPComponent::add_key_(const char *name, uint32_t key) {
395  if (!this->is_encrypted_())
396  return;
397  if (this->ping_keys_.count(name) == 0 && this->ping_keys_.size() == MAX_PING_KEYS) {
398  ESP_LOGW(TAG, "Ping key from %s discarded", name);
399  return;
400  }
401  this->ping_keys_[name] = key;
402  this->resend_data_ = true;
403  ESP_LOGV(TAG, "Ping key from %s now %X", name, (unsigned) key);
404 }
405 
406 void UDPComponent::process_ping_request_(const char *name, uint8_t *ptr, size_t len) {
407  if (len != 4) {
408  ESP_LOGW(TAG, "Bad ping request");
409  return;
410  }
411  auto key = get_uint32(ptr);
412  this->add_key_(name, key);
413  ESP_LOGV(TAG, "Updated ping key for %s to %08X", name, (unsigned) key);
414 }
415 
416 static bool process_rolling_code(Provider &provider, uint8_t *&buf, const uint8_t *end) {
417  if (end - buf < 8)
418  return false;
419  auto code0 = get_uint32(buf);
420  auto code1 = get_uint32(buf);
421  if (code1 < provider.last_code[1] || (code1 == provider.last_code[1] && code0 <= provider.last_code[0])) {
422  ESP_LOGW(TAG, "Rolling code for %s %08lX:%08lX is old", provider.name, (unsigned long) code1,
423  (unsigned long) code0);
424  return false;
425  }
426  provider.last_code[0] = code0;
427  provider.last_code[1] = code1;
428  return true;
429 }
430 
434 void UDPComponent::process_(uint8_t *buf, const size_t len) {
435  auto ping_key_seen = !this->ping_pong_enable_;
436  if (len < 8) {
437  ESP_LOGV(TAG, "Bad length %zu", len);
438  return;
439  }
440  char namebuf[256]{};
441  uint8_t byte;
442  uint8_t *start_ptr = buf;
443  const uint8_t *end = buf + len;
444  FuData rdata{};
445  auto magic = get_uint16(buf);
446  if (magic != MAGIC_NUMBER && magic != MAGIC_PING) {
447  ESP_LOGV(TAG, "Bad magic %X", magic);
448  return;
449  }
450 
451  auto hlen = *buf++;
452  if (hlen > len - 3) {
453  ESP_LOGV(TAG, "Bad hostname length %u > %zu", hlen, len - 3);
454  return;
455  }
456  memcpy(namebuf, buf, hlen);
457  if (strcmp(this->name_, namebuf) == 0) {
458  ESP_LOGV(TAG, "Ignoring our own data");
459  return;
460  }
461  buf += hlen;
462  if (magic == MAGIC_PING) {
463  this->process_ping_request_(namebuf, buf, end - buf);
464  return;
465  }
466  if (round4(len) != len) {
467  ESP_LOGW(TAG, "Bad length %zu", len);
468  return;
469  }
470  hlen = round4(hlen + 3);
471  buf = start_ptr + hlen;
472  if (buf == end) {
473  ESP_LOGV(TAG, "No data after header");
474  return;
475  }
476 
477  if (this->providers_.count(namebuf) == 0) {
478  ESP_LOGVV(TAG, "Unknown hostname %s", namebuf);
479  return;
480  }
481  auto &provider = this->providers_[namebuf];
482  // if encryption not used with this host, ping check is pointless since it would be easily spoofed.
483  if (provider.encryption_key.empty())
484  ping_key_seen = true;
485 
486  ESP_LOGV(TAG, "Found hostname %s", namebuf);
487 #ifdef USE_SENSOR
488  auto &sensors = this->remote_sensors_[namebuf];
489 #endif
490 #ifdef USE_BINARY_SENSOR
491  auto &binary_sensors = this->remote_binary_sensors_[namebuf];
492 #endif
493 
494  if (!provider.encryption_key.empty()) {
495  xxtea_decrypt((uint32_t *) buf, (end - buf) / 4, (uint32_t *) provider.encryption_key.data());
496  }
497  byte = *buf++;
498  if (byte == ROLLING_CODE_KEY) {
499  if (!process_rolling_code(provider, buf, end))
500  return;
501  } else if (byte != DATA_KEY) {
502  ESP_LOGV(TAG, "Expected rolling_key or data_key, got %X", byte);
503  return;
504  }
505  while (buf < end) {
506  byte = *buf++;
507  if (byte == ZERO_FILL_KEY)
508  continue;
509  if (byte == PING_KEY) {
510  if (end - buf < 4) {
511  ESP_LOGV(TAG, "PING_KEY requires 4 more bytes");
512  return;
513  }
514  auto key = get_uint32(buf);
515  if (key == this->ping_key_) {
516  ping_key_seen = true;
517  ESP_LOGV(TAG, "Found good ping key %X", (unsigned) key);
518  } else {
519  ESP_LOGV(TAG, "Unknown ping key %X", (unsigned) key);
520  }
521  continue;
522  }
523  if (!ping_key_seen) {
524  ESP_LOGW(TAG, "Ping key not seen");
525  this->resend_ping_key_ = true;
526  break;
527  }
528  if (byte == BINARY_SENSOR_KEY) {
529  if (end - buf < 3) {
530  ESP_LOGV(TAG, "Binary sensor key requires at least 3 more bytes");
531  return;
532  }
533  rdata.u32 = *buf++;
534  } else if (byte == SENSOR_KEY) {
535  if (end - buf < 6) {
536  ESP_LOGV(TAG, "Sensor key requires at least 6 more bytes");
537  return;
538  }
539  rdata.u32 = get_uint32(buf);
540  } else {
541  ESP_LOGW(TAG, "Unknown key byte %X", byte);
542  return;
543  }
544 
545  hlen = *buf++;
546  if (end - buf < hlen) {
547  ESP_LOGV(TAG, "Name length of %u not available", hlen);
548  return;
549  }
550  memset(namebuf, 0, sizeof namebuf);
551  memcpy(namebuf, buf, hlen);
552  ESP_LOGV(TAG, "Found sensor key %d, id %s, data %lX", byte, namebuf, (unsigned long) rdata.u32);
553  buf += hlen;
554 #ifdef USE_SENSOR
555  if (byte == SENSOR_KEY && sensors.count(namebuf) != 0)
556  sensors[namebuf]->publish_state(rdata.f32);
557 #endif
558 #ifdef USE_BINARY_SENSOR
559  if (byte == BINARY_SENSOR_KEY && binary_sensors.count(namebuf) != 0)
560  binary_sensors[namebuf]->publish_state(rdata.u32 != 0);
561 #endif
562  }
563 }
564 
566  ESP_LOGCONFIG(TAG, "UDP:");
567  ESP_LOGCONFIG(TAG, " Port: %u", this->port_);
568  ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(this->is_encrypted_()));
569  ESP_LOGCONFIG(TAG, " Ping-pong: %s", YESNO(this->ping_pong_enable_));
570  for (const auto &address : this->addresses_)
571  ESP_LOGCONFIG(TAG, " Address: %s", address.c_str());
572 #ifdef USE_SENSOR
573  for (auto sensor : this->sensors_)
574  ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.id);
575 #endif
576 #ifdef USE_BINARY_SENSOR
577  for (auto sensor : this->binary_sensors_)
578  ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.id);
579 #endif
580  for (const auto &host : this->providers_) {
581  ESP_LOGCONFIG(TAG, " Remote host: %s", host.first.c_str());
582  ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(!host.second.encryption_key.empty()));
583 #ifdef USE_SENSOR
584  for (const auto &sensor : this->remote_sensors_[host.first.c_str()])
585  ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.first.c_str());
586 #endif
587 #ifdef USE_BINARY_SENSOR
588  for (const auto &sensor : this->remote_binary_sensors_[host.first.c_str()])
589  ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.first.c_str());
590 #endif
591  }
592 }
594  if (this->rolling_code_enable_) {
595  if (++this->rolling_code_[0] == 0) {
596  this->rolling_code_[1]++;
597  this->pref_.save(&this->rolling_code_[1]);
598  }
599  }
600 }
601 void UDPComponent::send_packet_(void *data, size_t len) {
602 #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
603  for (const auto &saddr : this->sockaddrs_) {
604  auto result = this->broadcast_socket_->sendto(data, len, 0, &saddr, sizeof(saddr));
605  if (result < 0)
606  ESP_LOGW(TAG, "sendto() error %d", errno);
607  }
608 #endif
609 #ifdef USE_SOCKET_IMPL_LWIP_TCP
610  auto iface = IPAddress(0, 0, 0, 0);
611  for (const auto &saddr : this->ipaddrs_) {
612  if (this->udp_client_.beginPacketMulticast(saddr, this->port_, iface, 128) != 0) {
613  this->udp_client_.write((const uint8_t *) data, len);
614  auto result = this->udp_client_.endPacket();
615  if (result == 0)
616  ESP_LOGW(TAG, "udp.write() error");
617  }
618  }
619 #endif
620 }
621 
623  if (!this->ping_pong_enable_ || !network::is_connected())
624  return;
625  this->ping_key_ = random_uint32();
626  this->ping_header_.clear();
627  add(this->ping_header_, MAGIC_PING);
628  add(this->ping_header_, this->name_);
629  add(this->ping_header_, this->ping_key_);
630  this->send_packet_(this->ping_header_.data(), this->ping_header_.size());
631  this->resend_ping_key_ = false;
632  ESP_LOGV(TAG, "Sent new ping request %08X", (unsigned) this->ping_key_);
633 }
634 } // namespace udp
635 } // namespace esphome
const char * name
Definition: stm32flash.h:78
void add_on_state_callback(std::function< void(float)> &&callback)
Add a callback that will be called every time a filtered value arrives.
Definition: sensor.cpp:52
std::vector< uint8_t > encryption_key
Definition: udp_component.h:23
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port)
Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
Definition: socket.cpp:51
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition: helpers.cpp:193
void add_binary_data_(uint8_t key, const char *id, bool data)
uint16_t x
Definition: tt21100.cpp:17
uint32_t socklen_t
Definition: headers.h:97
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition: util.cpp:15
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
uint16_t y
Definition: tt21100.cpp:18
float state
This member variable stores the last state that has passed through all filters.
Definition: sensor.h:131
void process_(uint8_t *buf, size_t len)
Process a received packet.
ESPPreferences * global_preferences
void add_key_(const char *name, uint32_t key)
Application App
Global storage of Application pointer - only one Application can exist.
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:202
void send_packet_(void *data, size_t len)
float get_state() const
Getter-syntax for .state.
Definition: sensor.cpp:86
std::string size_t len
Definition: helpers.h:293
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
uint8_t address
Definition: bl0906.h:211
void add_data_(uint8_t key, const char *id, float data)
uint8_t end[39]
Definition: sun_gtil2.cpp:31
void process_ping_request_(const char *name, uint8_t *ptr, size_t len)
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port)
Set a sockaddr to the specified address and port for the IP version used by socket_ip().
Definition: socket.cpp:21
esphome::sensor::Sensor * sensor
Definition: statsd.h:38
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.