From 5ba66d80c3d6168ff1fdecefe8373b337e60529f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 21 Jun 2026 17:43:52 +0200 Subject: [PATCH 1/2] Add command `BSSid[1|2] [0|1|11:22:33:44:55:66]` to select fixed WiFi Access Point disabling `SetOption56` and `SetOption57` (#24394) --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/include/i18n.h | 1 + tasmota/include/tasmota.h | 1 + tasmota/my_user_config.h | 6 +++ tasmota/tasmota_support/support_command.ino | 20 ++++++++- tasmota/tasmota_support/support_wifi.ino | 45 +++++++++++++++++---- 7 files changed, 65 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4570a6cc..30fa5a80a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Serial console support for Backspace when enabling `#define USE_SERIAL_BACKSPACE` (#24830) - Build copy firmware artifacts with ELF-extracted build timestamp (opt-in) (#24794) - Enhance Matter plugin functionality for On/Off control and add support for Global Scene Control and related commands (#24854) +- Command `BSSid[1|2] [0|1|11:22:33:44:55:66]` to select fixed WiFi Access Point disabling `SetOption56` and `SetOption57` (#24394) ### Breaking Changed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c776b6920..68deae39c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -117,6 +117,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Build copy firmware artifacts with ELF-extracted build timestamp (opt-in) [#24794](https://github.com/arendst/Tasmota/issues/24794) - Command `SetOption [0..2]` to display SetOption values - Command `WcResolution 0..24` increasing camera max resolution from 14 to 24 +- Command `BSSid[1|2] [0|1|11:22:33:44:55:66]` to select fixed WiFi Access Point disabling `SetOption56` and `SetOption57` [#24394](https://github.com/arendst/Tasmota/issues/24394) - Support for hostname generation using single-specifier Format() patterns [#24731](https://github.com/arendst/Tasmota/issues/24731) - Support for Modbus RX Enable GPIO [#24726](https://github.com/arendst/Tasmota/issues/24726) - Support for multi-byte chars like emojis (💡) in light device toggle buttons [#24482](https://github.com/arendst/Tasmota/issues/24482) diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index c7da81d47..5a7f9e576 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -346,6 +346,7 @@ #define D_CMND_IPADDRESS "IPAddress" #define D_CMND_NTPSERVER "NtpServer" #define D_CMND_AP "Ap" +#define D_CMND_BSSID "BSSId" #define D_CMND_SSID "SSId" #define D_CMND_PASSWORD "Password" #define D_CMND_HOSTNAME "Hostname" diff --git a/tasmota/include/tasmota.h b/tasmota/include/tasmota.h index 8158eff08..d6225b89c 100644 --- a/tasmota/include/tasmota.h +++ b/tasmota/include/tasmota.h @@ -508,6 +508,7 @@ enum SettingsTextIndex { SET_OTAURL, SET_INFLUXDB_HOST, SET_INFLUXDB_PORT, SET_INFLUXDB_ORG, SET_INFLUXDB_TOKEN, SET_INFLUXDB_BUCKET, SET_INFLUXDB_RP, SET_CANVAS, SET_TELEGRAM_FINGERPRINT, + SET_APBSSID1, SET_APBSSID2, // MAX_SSIDS SET_MAX, // limit of texts stored in Settings // Index above are not stored in Settings and should be handled specifically in SettingText() SET_BUTTON17, SET_BUTTON18, SET_BUTTON19, SET_BUTTON20, SET_BUTTON21, SET_BUTTON22, SET_BUTTON23, SET_BUTTON24, diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index f99c6e8af..c8bd340ee 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -78,12 +78,18 @@ #ifndef STA_PASS1 #define STA_PASS1 "" // [Password1] Wi-Fi password #endif +#ifndef AP_BSSID1 +#define AP_BSSID1 "" // [BSsid1] Wi-Fi desired AP BSSID to connect to +#endif #ifndef STA_SSID2 #define STA_SSID2 "" // [Ssid2] Optional alternate AP Wi-Fi SSID #endif #ifndef STA_PASS2 #define STA_PASS2 "" // [Password2] Optional alternate AP Wi-Fi password #endif +#ifndef AP_BSSID2 +#define AP_BSSID2 "" // [BSsid2] Wi-Fi desired AP BSSID to connect to +#endif #ifndef WIFI_AP_PASSPHRASE #define WIFI_AP_PASSPHRASE "" // AccessPoint passphrase. For WPA2 min 8 char, for open use "" (max 63 char). #endif diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 599ae145e..ddb8a0c0d 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -30,7 +30,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOREAD "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALBUFFER "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" D_CMND_SERIALDELIMITER "|" - D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_WIFI "|" D_CMND_DNSTIMEOUT "|" + D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_BSSID "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_WIFI "|" D_CMND_DNSTIMEOUT "|" D_CMND_DEVICENAME "|" D_CMND_FN "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|" @@ -79,7 +79,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { &CmndModule, &CmndModules, &CmndGpio, &CmndGpioRead, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialBuffer, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, &CmndSerialDelimiter, - &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, &CmndDnsTimeout, + &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndBSsid, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, &CmndDnsTimeout, &CmndDevicename, &CmndFriendlyname, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode, @@ -2475,6 +2475,22 @@ void CmndAp(void) Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings->sta_active +1, EscapeJSONString(SettingsText(SET_STASSID1 + Settings->sta_active)).c_str()); } +void CmndBSsid(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SSIDS)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_APBSSID1, MAX_SSIDS); + } else { + if ((Shortcut() < 2) || (17 == XdrvMailbox.data_len)) { // Valid BSSID = 18:E8:29:CA:17:C1 + SettingsUpdateText(SET_APBSSID1 + XdrvMailbox.index -1, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? AP_BSSID1 : AP_BSSID2 : XdrvMailbox.data); + Settings->sta_active = XdrvMailbox.index -1; + TasmotaGlobal.restart_flag = 2; + } + ResponseCmndIdxChar(SettingsText(SET_APBSSID1 + XdrvMailbox.index -1)); + } + } +} + void CmndSsid(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SSIDS)) { diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 767632cbd..82a37ac25 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -410,22 +410,49 @@ void WifiBegin(uint8_t flag, uint8_t channel) { } if (Settings->ipv4_address[0]) { // AddLog(LOG_LEVEL_INFO, ">>>1: Wifi Config DNS %_I %_I", Settings->ipv4_address[3], Settings->ipv4_address[4]); - WiFi.config(Settings->ipv4_address[0], Settings->ipv4_address[1], Settings->ipv4_address[2], Settings->ipv4_address[3], Settings->ipv4_address[4]); // Set static IP + WiFi.config(Settings->ipv4_address[0], + Settings->ipv4_address[1], + Settings->ipv4_address[2], + Settings->ipv4_address[3], + Settings->ipv4_address[4]); // Set static IP } WiFi.hostname(TasmotaGlobal.hostname); // ESP8266 needs this here (after WiFi.mode) - char stemp[40] = { 0 }; + char stemp[50] = { 0 }; if (channel) { - WiFiHelper::begin(SettingsText(SET_STASSID1 + Settings->sta_active), SettingsText(SET_STAPWD1 + Settings->sta_active), channel, Wifi.bssid); + char bssid_user[16] = " (LastUsed)"; + if (strlen(SettingsText(SET_APBSSID1 + Settings->sta_active))) { // Valid BSSID = 18:E8:29:CA:17:C1 + char* mac_part = SettingsText(SET_APBSSID1 + Settings->sta_active); + for (uint32_t i = 0; i < 6; i++) { + Wifi.bssid[i] = strtol(mac_part, &mac_part, 16); + mac_part++; // Skip ":" + } + snprintf_P(bssid_user, sizeof(bssid_user), PSTR(" (User)")); + } + else if (Settings->flag3.use_wifi_scan) { // SetOption56 - Scan wifi network at restart for configured AP's + snprintf_P(bssid_user, sizeof(bssid_user), PSTR(" (Strongest)")); + } + WiFiHelper::begin(SettingsText(SET_STASSID1 + Settings->sta_active), + SettingsText(SET_STAPWD1 + Settings->sta_active), + channel, + Wifi.bssid); // Add connected BSSID and channel for multi-AP installations char hex_char[18]; - snprintf_P(stemp, sizeof(stemp), PSTR(" Channel %d BSSId %s"), channel, ToHex_P((unsigned char*)Wifi.bssid, 6, hex_char, sizeof(hex_char), ':')); + snprintf_P(stemp, sizeof(stemp), PSTR(" Channel %d BSSId %s%s"), + channel, + ToHex_P((unsigned char*)Wifi.bssid, 6, hex_char, sizeof(hex_char), ':'), + bssid_user); } else { - WiFiHelper::begin(SettingsText(SET_STASSID1 + Settings->sta_active), SettingsText(SET_STAPWD1 + Settings->sta_active)); + WiFiHelper::begin(SettingsText(SET_STASSID1 + Settings->sta_active), + SettingsText(SET_STAPWD1 + Settings->sta_active)); } delay(500); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " %s " D_AS " %s..."), - Settings->sta_active +1, SettingsText(SET_STASSID1 + Settings->sta_active), stemp, WifiGetPhyMode().c_str(), TasmotaGlobal.hostname); + Settings->sta_active +1, + SettingsText(SET_STASSID1 + Settings->sta_active), + stemp, + WifiGetPhyMode().c_str(), + TasmotaGlobal.hostname); if (Settings->flag5.wait_for_wifi_result) { // SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1) WiFi.waitForConnectResult(1000); // https://github.com/arendst/Tasmota/issues/14985 @@ -1235,7 +1262,8 @@ void WifiCheckIp(void) { } } if (Wifi.retry) { - if (Settings->flag3.use_wifi_scan) { // SetOption56 - Scan wifi network at restart for configured AP's + if ((Settings->flag3.use_wifi_scan) && // SetOption56 - Scan wifi network at restart for configured AP's + (!strlen(SettingsText(SET_APBSSID1 + Settings->sta_active)))) { // No BSSID if (Wifi.retry_init == Wifi.retry) { Wifi.scan_state = 1; // Select scanned SSID } @@ -1300,7 +1328,8 @@ void WifiCheck(uint8_t param) } if ((WL_CONNECTED == WiFi.status()) && WifiHasIP() && !Wifi.config_type) { WifiSetState(1); - if (Settings->flag3.use_wifi_rescan) { // SetOption57 - Scan wifi network every 44 minutes for configured AP's + if ((Settings->flag3.use_wifi_rescan) && // SetOption57 - Scan wifi network every 44 minutes for configured AP's + (!strlen(SettingsText(SET_APBSSID1 + Settings->sta_active)))) { // No BSSID if (!(TasmotaGlobal.uptime % (60 * WIFI_RESCAN_MINUTES))) { if (!Wifi.scan_state) { Wifi.scan_state = 2; } // If wifi scan routine is free, use it. Otherwise, wait for next RESCAN TIME } From ee435ea968d900ccb20299eac4eceaf022fd7231 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:24:06 +0200 Subject: [PATCH 2/2] Fix possible buffer overflow --- lib/lib_div/ESPFtpServer/ESPFtpServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 lib/lib_div/ESPFtpServer/ESPFtpServer.cpp diff --git a/lib/lib_div/ESPFtpServer/ESPFtpServer.cpp b/lib/lib_div/ESPFtpServer/ESPFtpServer.cpp old mode 100755 new mode 100644 index 84e9b3656..360a196e9 --- a/lib/lib_div/ESPFtpServer/ESPFtpServer.cpp +++ b/lib/lib_div/ESPFtpServer/ESPFtpServer.cpp @@ -264,7 +264,7 @@ boolean FtpServer::processCommand () { else if (!strcmp (command, "CWD")) { char path[FTP_CWD_SIZE]; if (haveParameter () && makeExistsPath (path)) { - strcpy (cwdName, path); + strlcpy (cwdName, path, sizeof(cwdName)); client.println (F("250 Ok. Current directory is ") + String (cwdName)); } }