I have an ESP32 (esp32-c3-devkitm-1 with esp-idf framework) connected to an OLED display. The device is not meant to be used with Home Assistant.
I was trying to achieve displaying different information (and icon) when
WiFi did not find the configured SSID and is basically not working
WiFI is in AP mode (SSID timed out or no configuration)
WiFI is properly connected to the configured SSID
I’m struggling to sense the latest, when the WiFi is simply connected to the SSID. I’m expecting that id(wifi_id).state or wifi::global_wifi_component->is_connected() would tell whether the WiFi is connected to the network, but even when it is properly connected, those return false.
Only if a client is connected to the ESP32 server (API for example, using esphome run or esphome logs) would those sensors/lambda return true. As soon as the TCP connection is lost, the WiFi is reported as disconnected.
How can I really sense the connection at the WiFi level (PHY/STA)? It seems that connected_ is aggregating several other state members, but those are all protected and cannot be checked separately.
Secondly, ESPHome has had some issues with Wi-Fi - but in the last month ESPHome Wi-Fi received updates, so I suggest checking that you are using the latest version.
As for the rest, I’m not sure what you are meaning. Posting your full yaml code (formatted for the forum) and log including the particular error would make it much easier for people to suggest the right answer.
FWIW, I had issues with my own wi-fi connections a while back, and added a fair bit of debugging code … which I have included below. Note that a lot happens before wi-fi connects; to debug this you will need to connect the ESP by serial/USB to a serial terminal emulator (eg minicom) running on a PC while the ESP boots up.
Note that my main device yaml includes a substitution for deviceIP so all ESPHome devices have static IPs to speed up joining the WLAN. Also, Ponder is the name of my desktop PC.
###########################################################
#
# Start with the Wi-fi connection
#
# Sept 2025 Added more sensors, esp for debugging which WAP
# a device has connected to. prefix all with wifi-
#
# As at Sept 2025 ESPHome still DOES NOT SUPPORT WI-FI ROAMING,
# and will sometimes connect to a WAP with lower signal strength.
#
# When an ESPHome device boots up it scans for the allowable
# network (SSID) with highest signal strength. Having made
# the connection it does not check for a stronger signal,
# even though a stronger signal may later become available.
# Even worse, if the scan finds multiple WAPs on the same WLAN
# it connects to the WAS (BSSID) with lowest channel number
# - not the one with strongest signal strength !
#
###########################################################
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.1.${deviceIP}
gateway: 192.168.1.1
subnet: 255.255.255.0
fast_connect: True
output_power: 10.5 # 8.5-20.5 reduce output power MAY improve Wi-fi in study
min_auth_mode: WPA2
##### add some debugging when compiled on Ponder - will only be seen if USB debugging
on_connect:
then:
lambda: |-
ESP_LOGI("TEST", "##### >>>>>>>>>>> WIFI CONNECT");
on_disconnect:
then:
lambda: |-
ESP_LOGI("TEST", "##### >>>>>>>>>>> WIFI DISCONNECT");
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "$devicename Fallback"
password: !secret wifi_ap_password
ota:
platform: esphome
password: !secret esphome_ota_password
captive_portal:
# this displays the device's status at http:IP_Address
web_server:
port: 80
# Enable Home Assistant API
api:
encryption:
key: !secret esphome_api_encryption
##### add some debugging when compiled on Ponder
on_client_connected:
- logger.log:
format: "##### >>>>>>>>>> API Client '%s' connected with IP %s"
args: ["client_info.c_str()", "client_address.c_str()"]
on_client_disconnected:
- logger.log: "##### >>>>>>>>>> API client disconnected!"
###########################################################
#
# Add some common sensors - wi-fi signal strength,
# uptime, ESPHome version & compile date/time
#
###########################################################
sensor:
- platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
name: WiFi Signal dB
id: wifi_signal_db
update_interval: $update_interval_network
entity_category: "diagnostic"
- platform: template
id: wifi_channel
name: Wifi channel
entity_category: "diagnostic"
# human readable uptime sensor output to the text sensor
- platform: uptime
id: uptime_sensor
name: Uptime
type: seconds
update_interval: $update_interval_network
text_sensor:
- platform: version
name: ESPHome Version
hide_timestamp: False
- platform: template
id: wifi_node
name: WLAN node
update_interval: $update_interval_network
entity_category: "diagnostic"
- platform: wifi_info
ip_address:
name: Wifi IP Address
update_interval: 60s # We use static IPs everywhere, but using 'never' results in the sensor having no value
mac_address:
name: Wifi Mac Address
ssid:
name: Wifi Connected SSID
scan_results:
name: Wifi Latest Scan Results
update_interval: $update_interval_network
dns_address:
name: Wifi DNS Address
bssid:
id: esp_connected_bssid
name: Wifi Connected BSSID
on_value:
then:
# lookup friendly names indicating which WAP we connected to
- lambda: |-
if ( x == "A0:36:BC:0E:29:38") {
id(wifi_node).publish_state("LivingRoom");
id(wifi_channel).publish_state(8);
} else if ( x == "30:5A:3A:C5:B4:20") {
id(wifi_node).publish_state("Laundry");
id(wifi_channel).publish_state(3);
} else if ( x == "5C:E9:31:1C:C1:69") {
id(wifi_node).publish_state("Bedroom");
id(wifi_channel).publish_state(11);
} else if ( x == "64:66:B3:ED:08:C4") {
id(wifi_node).publish_state("Study");
id(wifi_channel).publish_state(13);
} else {
id(wifi_node).publish_state("Unknown");
id(wifi_channel).publish_state(-99);
}
# get the current time from the Home Assistant server
time:
- platform: homeassistant
id: homeassistant_time
update_interval: "12h" # update from Home Assistant every 12 hours
binary_sensor:
- platform: status
name: Wifi "Status"
Thanks, it seems I got really confused. As I had to test many different things, I was using a binary_sensor with template and a lambda containing exactly that.
Then I was doing id(binsens_wifi_connected).state thinking that it was equivalent, but actually it isn’t.
I also tested this hack:
// Detect WiFi connection by checking if we have an IP address assigned
auto ips = wifi::global_wifi_component->get_ip_addresses();
bool has_ip = false;
for (const auto& ip : ips) {
if (ip.is_set()) {
has_ip = true;
break;
}
}
which worked but is not very idiomatic.
I’ll stick with your solution.