2#ifdef USE_IMPROV_SERIAL
19static const char *
const TAG =
"improv_serial";
23#ifdef USE_IMPROV_SERIAL_UART
25#elif defined(USE_ESP32)
28#elif defined(USE_ARDUINO)
36 this->
state_ = improv::STATE_PROVISIONED;
50 ESP_LOGV(TAG,
"Timeout");
55 if (!
byte.has_value())
71 char ssid_buf[wifi::SSID_BUFFER_SIZE];
74 this->connecting_sta_.get_password());
91 const bool there_is_data = data !=
nullptr &&
size > 0;
93 const uint8_t header_checksum_len = there_is_data ? TX_BUFFER_SIZE - 3 : TX_BUFFER_SIZE - 2;
95 const uint8_t header_tx_len = there_is_data ? TX_BUFFER_SIZE - 3 : TX_BUFFER_SIZE;
98 for (uint8_t i = 0; i < header_checksum_len; i++) {
103 for (
size_t i = 0; i <
size; i++) {
109#ifdef USE_IMPROV_SERIAL_UART
115#elif defined(USE_ESP32)
119#if defined(USE_ESP32_VARIANT_ESP32)
128#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC)
130 esp_usb_console_write_buf((
const char *) this->
tx_header_, header_tx_len);
132 esp_usb_console_write_buf((
const char *) data,
size);
133 esp_usb_console_write_buf((
const char *) &this->tx_header_[TX_CHECKSUM_IDX],
138#ifdef USE_LOGGER_USB_SERIAL_JTAG
140 usb_serial_jtag_write_bytes((
const char *) this->tx_header_, header_tx_len, 20 / portTICK_PERIOD_MS);
142 usb_serial_jtag_write_bytes((
const char *) data,
size, 20 / portTICK_PERIOD_MS);
143 usb_serial_jtag_write_bytes((
const char *) &this->tx_header_[TX_CHECKSUM_IDX], 2,
144 20 / portTICK_PERIOD_MS);
151#elif defined(USE_ARDUINO)
168 for (
const auto &ip : addresses) {
169 if (!ip.is_ip4() || !ip.is_set())
171 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
174 char webserver_url[7 + network::IP_ADDRESS_BUFFER_SIZE + 1 + 5 + 1];
177 buf_append_printf(webserver_url,
sizeof(webserver_url), 0,
"http://%s:%u", ip_buf, USE_WEBSERVER_PORT);
178 if (!builder.add_string(webserver_url,
len)) {
179 ESP_LOGW(TAG,
"Response full; URL dropped");
186 const auto append_wifi_urls = [&append_urls]() {
198 if (modem::global_modem_component !=
nullptr)
199 append_urls(modem::global_modem_component->get_ip_addresses());
209 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
210 improv::RpcResponseBuilder builder(buf, command);
211#ifdef USE_IMPROV_SERIAL_NEXT_URL
224#ifdef ESPHOME_PROJECT_NAME
225 static constexpr size_t INFO_ENTRIES_LEN =
226 sizeof(ESPHOME_PROJECT_NAME) +
sizeof(ESPHOME_PROJECT_VERSION) +
sizeof(ESPHOME_VARIANT);
228 static constexpr size_t INFO_ENTRIES_LEN =
sizeof(
"ESPHome") +
sizeof(ESPHOME_VERSION) +
sizeof(ESPHOME_VARIANT);
230 static_assert(INFO_ENTRIES_LEN < MAX_SERIAL_PAYLOAD,
231 "esphome project name and version too long for the improv_serial device info frame");
232 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
233 improv::RpcResponseBuilder builder(buf, improv::GET_DEVICE_INFO);
237#define IMPROV_ADD_INFO(lit) \
239 static const char progmem_str[] PROGMEM = lit; \
240 char tmp[sizeof(lit)]; \
241 progmem_memcpy(tmp, progmem_str, sizeof(lit)); \
242 builder.add_string(tmp, sizeof(lit) - 1); \
246#define IMPROV_ADD_INFO(lit) builder.add_string(lit, sizeof(lit) - 1)
248#ifdef ESPHOME_PROJECT_NAME
249 IMPROV_ADD_INFO(ESPHOME_PROJECT_NAME);
250 IMPROV_ADD_INFO(ESPHOME_PROJECT_VERSION);
252 IMPROV_ADD_INFO(
"ESPHome");
253 IMPROV_ADD_INFO(ESPHOME_VERSION);
255 IMPROV_ADD_INFO(ESPHOME_VARIANT);
256#undef IMPROV_ADD_INFO
259 if (INFO_ENTRIES_LEN + 1 + name.size() <= MAX_SERIAL_PAYLOAD) {
260 builder.add_string(name.c_str(), name.size());
262 ESP_LOGW(TAG,
"Response full; device name dropped");
270 ESP_LOGV(TAG,
"Byte: 0x%02X",
byte);
273 return improv::parse_improv_serial_byte(
275 [
this](improv::Error error) ->
void {
276 ESP_LOGW(TAG,
"Error decoding payload");
282 switch (command.command) {
283 case improv::WIFI_SETTINGS: {
288 ESP_LOGW(TAG,
"Wi-Fi is disabled; cannot provision");
289 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
294 sta.set_password(command.password.c_str());
302 ESP_LOGD(TAG,
"Received settings: SSID=%s, password=" LOG_SECRET(
"%s"), command.ssid.c_str(),
303 command.password.c_str());
305 this->
set_timeout(
"wifi-connect-timeout", switching ? WIFI_SWITCH_TIMEOUT_MS : WIFI_CONNECT_TIMEOUT_MS,
309 ESP_LOGW(TAG,
"Wi-Fi not supported; cannot provision");
310 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
314 case improv::GET_CURRENT_STATE: {
327 if (this->
state_ == improv::STATE_PROVISIONED) {
335 case improv::GET_DEVICE_INFO: {
339 case improv::GET_WIFI_NETWORKS: {
341 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
344 for (
const auto &scan : results) {
345 bool with_auth =
false;
350 char *rssi_end =
int8_to_str(rssi_buf, scan.get_rssi());
352 improv::RpcResponseBuilder builder(buf, improv::GET_WIFI_NETWORKS);
354 const auto &ssid = scan.get_ssid();
355 builder.add_string(ssid.c_str(), ssid.size());
356 builder.add_string(rssi_buf, rssi_end - rssi_buf);
357 builder.add_string(YESNO(with_auth));
362 improv::RpcResponseBuilder builder(buf, improv::GET_WIFI_NETWORKS);
366 case improv::GET_NETWORK_STATE: {
372 flags |= improv::NETWORK_IS_ONLINE;
374 flags |= improv::NETWORK_SUPPORTS_WIFI;
377 flags |= improv::NETWORK_SUPPORTS_ETHERNET;
380 flags |= improv::NETWORK_SUPPORTS_THREAD;
383 flags |= improv::NETWORK_SUPPORTS_MODEM;
385 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
386 improv::RpcResponseBuilder builder(buf, improv::GET_NETWORK_STATE);
388 static_assert(improv::NETWORK_SUPPORTS_MODEM <= 0x7F,
"network flags no longer fit int8_to_str");
391 builder.add_string(flags_buf, flags_end - flags_buf);
395 if (
flags & improv::NETWORK_IS_ONLINE) {
396#if defined(USE_NETWORK_PRIMARY_INTERFACE_WIFI) && defined(USE_WIFI)
407 ESP_LOGW(TAG,
"Unknown payload");
433 if (response.size() > MAX_SERIAL_RESPONSE) {
434 ESP_LOGE(TAG,
"Response too long");
440 this->
write_data_(response.data(), response.size());
445 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
447 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.