ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
openthread.cpp
Go to the documentation of this file.
2#ifdef USE_OPENTHREAD
3#include "openthread.h"
4#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
5#include "esp_openthread.h"
6#endif
7
8#include <freertos/portmacro.h>
9
10#include <openthread/cli.h>
11#include <openthread/instance.h>
12#include <openthread/logging.h>
13#include <openthread/netdata.h>
14#include <openthread/srp_client.h>
15#include <openthread/srp_client_buffers.h>
16#include <openthread/tasklet.h>
17
18#include <cstring>
19
22#include "esphome/core/log.h"
23
24static const char *const TAG = "openthread";
25
26namespace esphome {
27namespace openthread {
28
29OpenThreadComponent *global_openthread_component = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
30 nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
31
33
35 auto lock = InstanceLock::try_acquire(100);
36 if (!lock) {
37 ESP_LOGW(TAG, "Failed to acquire OpenThread lock in is_connected");
38 return false;
39 }
40
41 otInstance *instance = lock->get_instance();
42 if (instance == nullptr) {
43 return false;
44 }
45
46 otDeviceRole role = otThreadGetDeviceRole(instance);
47
48 // TODO: If we're a leader, check that there is at least 1 known peer
49 return role >= OT_DEVICE_ROLE_CHILD;
50}
51
52// Gets the off-mesh routable address
53std::optional<otIp6Address> OpenThreadComponent::get_omr_address() {
55 return this->get_omr_address_(lock);
56}
57
58std::optional<otIp6Address> OpenThreadComponent::get_omr_address_(InstanceLock &lock) {
59 otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
60 otInstance *instance = nullptr;
61
62 instance = lock.get_instance();
63
64 otBorderRouterConfig config;
65 if (otNetDataGetNextOnMeshPrefix(instance, &iterator, &config) != OT_ERROR_NONE) {
66 return std::nullopt;
67 }
68
69 const otIp6Prefix *omr_prefix = &config.mPrefix;
70 const otNetifAddress *unicast_addresses = otIp6GetUnicastAddresses(instance);
71 for (const otNetifAddress *addr = unicast_addresses; addr; addr = addr->mNext) {
72 const otIp6Address *local_ip = &addr->mAddress;
73 if (otIp6PrefixMatch(&omr_prefix->mPrefix, local_ip)) {
74 return *local_ip;
75 }
76 }
77 return {};
78}
79
80void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services,
81 const otSrpClientService *removed_services, void *context) {
82 if (err != 0) {
83 ESP_LOGW(TAG, "SRP client reported an error: %s", otThreadErrorToString(err));
84 for (const otSrpClientHostInfo *host = host_info; host; host = nullptr) {
85 ESP_LOGW(TAG, " Host: %s", host->mName);
86 }
87 for (const otSrpClientService *service = services; service; service = service->mNext) {
88 ESP_LOGW(TAG, " Service: %s", service->mName);
89 }
90 }
91}
92
93void srp_start_callback(const otSockAddr *server_socket_address, void *context) {
94 ESP_LOGI(TAG, "SRP client has started");
95}
96
98 otError error;
100 otInstance *instance = lock.get_instance();
101
102 otSrpClientSetCallback(instance, srp_callback, nullptr);
103
104 // set the host name
105 uint16_t size;
106 char *existing_host_name = otSrpClientBuffersGetHostNameString(instance, &size);
107 const std::string &host_name = App.get_name();
108 uint16_t host_name_len = host_name.size();
109 if (host_name_len > size) {
110 ESP_LOGW(TAG, "Hostname is too long, choose a shorter project name");
111 return;
112 }
113 memset(existing_host_name, 0, size);
114 memcpy(existing_host_name, host_name.c_str(), host_name_len);
115
116 error = otSrpClientSetHostName(instance, existing_host_name);
117 if (error != 0) {
118 ESP_LOGW(TAG, "Could not set host name");
119 return;
120 }
121
122 error = otSrpClientEnableAutoHostAddress(instance);
123 if (error != 0) {
124 ESP_LOGW(TAG, "Could not enable auto host address");
125 return;
126 }
127
128 // Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this
129 // component
130 this->mdns_services_ = this->mdns_->get_services();
131 ESP_LOGD(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size());
132 for (const auto &service : this->mdns_services_) {
133 otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
134 if (!entry) {
135 ESP_LOGW(TAG, "Failed to allocate service entry");
136 continue;
137 }
138
139 // Set service name
140 char *string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size);
141 std::string full_service = service.service_type + "." + service.proto;
142 if (full_service.size() > size) {
143 ESP_LOGW(TAG, "Service name too long: %s", full_service.c_str());
144 continue;
145 }
146 memcpy(string, full_service.c_str(), full_service.size() + 1);
147
148 // Set instance name (using host_name)
149 string = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size);
150 if (host_name_len > size) {
151 ESP_LOGW(TAG, "Instance name too long: %s", host_name.c_str());
152 continue;
153 }
154 memset(string, 0, size);
155 memcpy(string, host_name.c_str(), host_name_len);
156
157 // Set port
158 entry->mService.mPort = const_cast<TemplatableValue<uint16_t> &>(service.port).value();
159
160 otDnsTxtEntry *txt_entries =
161 reinterpret_cast<otDnsTxtEntry *>(this->pool_alloc_(sizeof(otDnsTxtEntry) * service.txt_records.size()));
162 // Set TXT records
163 entry->mService.mNumTxtEntries = service.txt_records.size();
164 for (size_t i = 0; i < service.txt_records.size(); i++) {
165 const auto &txt = service.txt_records[i];
166 auto value = const_cast<TemplatableValue<std::string> &>(txt.value).value();
167 txt_entries[i].mKey = strdup(txt.key.c_str());
168 txt_entries[i].mValue = reinterpret_cast<const uint8_t *>(strdup(value.c_str()));
169 txt_entries[i].mValueLength = value.size();
170 }
171 entry->mService.mTxtEntries = txt_entries;
172 entry->mService.mNumTxtEntries = service.txt_records.size();
173
174 // Add service
175 error = otSrpClientAddService(instance, &entry->mService);
176 if (error != OT_ERROR_NONE) {
177 ESP_LOGW(TAG, "Failed to add service: %s", otThreadErrorToString(error));
178 }
179 ESP_LOGD(TAG, "Added service: %s", full_service.c_str());
180 }
181
182 otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr);
183}
184
186 uint8_t *ptr = new uint8_t[size];
187 this->memory_pool_.emplace_back(std::unique_ptr<uint8_t[]>(ptr));
188 return ptr;
189}
190
192
194 if (!this->teardown_started_) {
195 this->teardown_started_ = true;
196 ESP_LOGD(TAG, "Clear Srp");
197 auto lock = InstanceLock::try_acquire(100);
198 if (!lock) {
199 ESP_LOGW(TAG, "Failed to acquire OpenThread lock during teardown, leaking memory");
200 return true;
201 }
202 otInstance *instance = lock->get_instance();
203 otSrpClientClearHostAndServices(instance);
204 otSrpClientBuffersFreeAllServices(instance);
206#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
207 ESP_LOGD(TAG, "Exit main loop ");
208 int error = esp_openthread_mainloop_exit();
209 if (error != ESP_OK) {
210 ESP_LOGW(TAG, "Failed attempt to stop main loop %d", error);
211 this->teardown_complete_ = true;
212 }
213#else
214 this->teardown_complete_ = true;
215#endif
216 }
217 return this->teardown_complete_;
218}
219
220} // namespace openthread
221} // namespace esphome
222
223#endif
const std::string & get_name() const
Get the name of this Application set by pre_setup().
std::vector< MDNSService > get_services()
static std::optional< InstanceLock > try_acquire(int delay)
std::optional< otIp6Address > get_omr_address_(InstanceLock &lock)
std::optional< otIp6Address > get_omr_address()
std::vector< std::unique_ptr< uint8_t[]> > memory_pool_
Definition openthread.h:50
void set_mdns(esphome::mdns::MDNSComponent *mdns)
std::vector< esphome::mdns::MDNSService > mdns_services_
Definition openthread.h:49
esphome::mdns::MDNSComponent * mdns_
Definition openthread.h:48
OpenThreadComponent * global_openthread_component
void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services, const otSrpClientService *removed_services, void *context)
void srp_start_callback(const otSockAddr *server_socket_address, void *context)
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
Application App
Global storage of Application pointer - only one Application can exist.