2#ifdef USE_IMPROV_SERIAL
19static const char *
const TAG =
"improv_serial";
21static constexpr uint32_t WIFI_CONNECT_TIMEOUT_ID = 0;
25#ifdef USE_IMPROV_SERIAL_UART
27#elif defined(USE_ESP32)
30#elif defined(USE_ARDUINO)
38 this->
state_ = improv::STATE_PROVISIONED;
52 ESP_LOGV(TAG,
"Timeout");
57 if (!
byte.has_value())
73 char ssid_buf[wifi::SSID_BUFFER_SIZE];
76 this->connecting_sta_.get_password());
93 const bool there_is_data = data !=
nullptr &&
size > 0;
95 const uint8_t header_checksum_len = there_is_data ? TX_BUFFER_SIZE - 3 : TX_BUFFER_SIZE - 2;
97 const uint8_t header_tx_len = there_is_data ? TX_BUFFER_SIZE - 3 : TX_BUFFER_SIZE;
100 for (uint8_t i = 0; i < header_checksum_len; i++) {
105 for (
size_t i = 0; i <
size; i++) {
111#ifdef USE_IMPROV_SERIAL_UART
117#elif defined(USE_ESP32)
121#if defined(USE_ESP32_VARIANT_ESP32)
130#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC)
132 esp_usb_console_write_buf((
const char *) this->
tx_header_, header_tx_len);
134 esp_usb_console_write_buf((
const char *) data,
size);
135 esp_usb_console_write_buf((
const char *) &this->tx_header_[TX_CHECKSUM_IDX],
140#ifdef USE_LOGGER_USB_SERIAL_JTAG
142 usb_serial_jtag_write_bytes((
const char *) this->tx_header_, header_tx_len, 20 / portTICK_PERIOD_MS);
144 usb_serial_jtag_write_bytes((
const char *) data,
size, 20 / portTICK_PERIOD_MS);
145 usb_serial_jtag_write_bytes((
const char *) &this->tx_header_[TX_CHECKSUM_IDX], 2,
146 20 / portTICK_PERIOD_MS);
153#elif defined(USE_ARDUINO)
170 for (
const auto &ip : addresses) {
171 if (!ip.is_ip4() || !ip.is_set())
173 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
176 char webserver_url[7 + network::IP_ADDRESS_BUFFER_SIZE + 1 + 5 + 1];
179 buf_append_printf(webserver_url,
sizeof(webserver_url), 0,
"http://%s:%u", ip_buf, USE_WEBSERVER_PORT);
180 if (!builder.add_string(webserver_url,
len)) {
181 ESP_LOGW(TAG,
"Response full; URL dropped");
188 const auto append_wifi_urls = [&append_urls]() {
200 if (modem::global_modem_component !=
nullptr)
201 append_urls(modem::global_modem_component->get_ip_addresses());
211 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
212 improv::RpcResponseBuilder builder(buf, command);
213#ifdef USE_IMPROV_NEXT_URL
226#ifdef ESPHOME_PROJECT_NAME
227 static constexpr size_t INFO_ENTRIES_LEN =
228 sizeof(ESPHOME_PROJECT_NAME) +
sizeof(ESPHOME_PROJECT_VERSION) +
sizeof(ESPHOME_VARIANT);
230 static constexpr size_t INFO_ENTRIES_LEN =
sizeof(
"ESPHome") +
sizeof(ESPHOME_VERSION) +
sizeof(ESPHOME_VARIANT);
232 static_assert(INFO_ENTRIES_LEN < MAX_SERIAL_PAYLOAD,
233 "esphome project name and version too long for the improv_serial device info frame");
234 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
235 improv::RpcResponseBuilder builder(buf, improv::GET_DEVICE_INFO);
239#define IMPROV_ADD_INFO(lit) \
241 static const char progmem_str[] PROGMEM = lit; \
242 char tmp[sizeof(lit)]; \
243 progmem_memcpy(tmp, progmem_str, sizeof(lit)); \
244 builder.add_string(tmp, sizeof(lit) - 1); \
248#define IMPROV_ADD_INFO(lit) builder.add_string(lit, sizeof(lit) - 1)
250#ifdef ESPHOME_PROJECT_NAME
251 IMPROV_ADD_INFO(ESPHOME_PROJECT_NAME);
252 IMPROV_ADD_INFO(ESPHOME_PROJECT_VERSION);
254 IMPROV_ADD_INFO(
"ESPHome");
255 IMPROV_ADD_INFO(ESPHOME_VERSION);
257 IMPROV_ADD_INFO(ESPHOME_VARIANT);
258#undef IMPROV_ADD_INFO
261 if (INFO_ENTRIES_LEN + 1 + name.size() <= MAX_SERIAL_PAYLOAD) {
262 builder.add_string(name.c_str(), name.size());
264 ESP_LOGW(TAG,
"Response full; device name dropped");
272 ESP_LOGV(TAG,
"Byte: 0x%02X",
byte);
275 return improv::parse_improv_serial_byte(
277 [
this](improv::Error error) ->
void {
278 ESP_LOGW(TAG,
"Error decoding payload");
284 switch (command.command) {
285 case improv::WIFI_SETTINGS: {
290 ESP_LOGW(TAG,
"Wi-Fi is disabled; cannot provision");
291 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
296 sta.set_password(command.password.c_str());
304 ESP_LOGD(TAG,
"Received settings: SSID=%s, password=" LOG_SECRET(
"%s"), command.ssid.c_str(),
305 command.password.c_str());
307 this->
set_timeout(WIFI_CONNECT_TIMEOUT_ID, switching ? WIFI_SWITCH_TIMEOUT_MS : WIFI_CONNECT_TIMEOUT_MS,
311 ESP_LOGW(TAG,
"Wi-Fi not supported; cannot provision");
312 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
316 case improv::GET_CURRENT_STATE: {
329 if (this->
state_ == improv::STATE_PROVISIONED) {
337 case improv::GET_DEVICE_INFO: {
341 case improv::GET_WIFI_NETWORKS: {
343 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
346 for (
const auto &scan : results) {
347 bool with_auth =
false;
352 char *rssi_end =
int8_to_str(rssi_buf, scan.get_rssi());
354 improv::RpcResponseBuilder builder(buf, improv::GET_WIFI_NETWORKS);
356 const auto &ssid = scan.get_ssid();
357 builder.add_string(ssid.c_str(), ssid.size());
358 builder.add_string(rssi_buf, rssi_end - rssi_buf);
359 builder.add_string(YESNO(with_auth));
364 improv::RpcResponseBuilder builder(buf, improv::GET_WIFI_NETWORKS);
368 case improv::GET_NETWORK_STATE: {
374 flags |= improv::NETWORK_IS_ONLINE;
376 flags |= improv::NETWORK_SUPPORTS_WIFI;
379 flags |= improv::NETWORK_SUPPORTS_ETHERNET;
382 flags |= improv::NETWORK_SUPPORTS_THREAD;
385 flags |= improv::NETWORK_SUPPORTS_MODEM;
387 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
388 improv::RpcResponseBuilder builder(buf, improv::GET_NETWORK_STATE);
390 static_assert(improv::NETWORK_SUPPORTS_MODEM <= 0x7F,
"network flags no longer fit int8_to_str");
393 builder.add_string(flags_buf, flags_end - flags_buf);
397 if (
flags & improv::NETWORK_IS_ONLINE) {
398#if defined(USE_NETWORK_PRIMARY_INTERFACE_WIFI) && defined(USE_WIFI)
409 ESP_LOGW(TAG,
"Unknown payload");
435 if (response.size() > MAX_SERIAL_RESPONSE) {
436 ESP_LOGE(TAG,
"Response too long");
442 this->
write_data_(response.data(), response.size());
447 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
449 ESP_LOGW(TAG,
"Timed out while connecting to Wi-Fi network");
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
bool cancel_timeout(const char *name)
Cancel a timeout function.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
constexpr const char * c_str() const
void add_next_url_(improv::RpcResponseBuilder &builder, size_t max_len)
Append the formatted next_url to the RPC response, warning if it does not fit.
std::vector< uint8_t > rx_buffer_
void send_version_info_()
void set_state_(improv::State state)
wifi::WiFiAP connecting_sta_
void on_wifi_connect_timeout_()
void send_settings_response_(improv::Command command)
void write_data_(const uint8_t *data=nullptr, size_t size=0)
void send_response_(std::span< const uint8_t > response)
void dump_config() override
ESPHOME_ALWAYS_INLINE optional< uint8_t > read_byte_()
bool parse_improv_serial_byte_(uint8_t byte)
logger::UARTSelection uart_selection_
void send_current_state_(improv::State state)
void add_webserver_urls_(improv::RpcResponseBuilder &builder, bool wifi_first)
Append one web server URL per interface that has a usable IPv4.
uint8_t tx_header_[TX_BUFFER_SIZE]
bool parse_improv_payload_(improv::ImprovCommand &command)
void set_error_(improv::Error error)
uart::UARTComponent * uart_
Stream * get_hw_serial() const
UARTSelection get_uart() const
Get the UART used by the logger.
uart_port_t get_uart_num() const
void write_array(const std::vector< uint8_t > &data)
StringRef get_ssid() const
void set_ssid(const std::string &ssid)
void set_sta(const WiFiAP &ap)
void save_wifi_sta(const std::string &ssid, const std::string &password)
void start_connecting(const WiFiAP &ap)
bool is_connected() const
const wifi_scan_vector_t< WiFiScanResult > & get_scan_result() const
Main-loop callers may read this directly.
EthernetComponent * global_eth_component
ImprovSerialComponent * global_improv_serial_component
@ UART_SELECTION_USB_SERIAL_JTAG
std::array< IPAddress, 5 > IPAddresses
ESPHOME_ALWAYS_INLINE bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
bool should_show_scan_entry(const Results &results, const Entry &scan, bool &with_auth)
WiFiComponent * global_wifi_component
char * int8_to_str(char *buf, int8_t val)
Write int8 value to buffer without modulo operations.
Application App
Global storage of Application pointer - only one Application can exist.