From 5b671ca0be28ffed629654e746e363c6137ff753 Mon Sep 17 00:00:00 2001 From: Moirtz Wagner Date: Thu, 19 Feb 2026 17:31:26 +0100 Subject: [PATCH] Add homeassisstant compatibility and choose strongest AP on wifi connection. --- Raumtermostat/lib/WiFiManager/WiFiManager.cpp | 70 ++++++++++++++++++- Raumtermostat/lib/WiFiManager/WiFiManager.h | 2 + Raumtermostat/platformio.ini | 2 +- Raumtermostat/src/MQTT.cpp | 47 ++++++++++++- Raumtermostat/src/MQTT.h | 7 +- 5 files changed, 116 insertions(+), 12 deletions(-) diff --git a/Raumtermostat/lib/WiFiManager/WiFiManager.cpp b/Raumtermostat/lib/WiFiManager/WiFiManager.cpp index f44d261..73c8f40 100644 --- a/Raumtermostat/lib/WiFiManager/WiFiManager.cpp +++ b/Raumtermostat/lib/WiFiManager/WiFiManager.cpp @@ -1059,7 +1059,12 @@ uint8_t WiFiManager::connectWifi(String ssid, String pass, bool connect) { else { // connect using saved ssid if there is one if (WiFi_hasAutoConnect()) { - wifiConnectDefault(); + if (_findBestRSSI) { + wifiConnectNew(WiFi_SSID(), WiFi_psk(), connect); + } else { + // connect to saved ssid + wifiConnectDefault(); + } connRes = waitForConnectResult(); } else { @@ -1110,7 +1115,62 @@ bool WiFiManager::wifiConnectNew(String ssid, String pass,bool connect){ #endif WiFi_enableSTA(true,storeSTAmode); // storeSTAmode will also toggle STA on in default opmode (persistent) if true (default) WiFi.persistent(true); - ret = WiFi.begin(ssid.c_str(), pass.c_str(), 0, NULL, connect); + + + if (_findBestRSSI) { + if (!_numNetworks) + WiFi_scanNetworks(); // scan in case this gets called before any scans + + int n = _numNetworks; + if (n == 0) { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(F("No networks found")); +#endif + } else { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(n, F("networks found")); +#endif + int bestConnection = -1; + // Find best RSSI AP for given SSID + for (int i = 0; i < n; i++) { + if (ssid == WiFi.SSID(i)) { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(String(F("SSID ")) + ssid + String(F(" found with RSSI: ")) + + String(WiFi.RSSI(i)) + String(F("(")) + + String(constrain((100.0 + WiFi.RSSI(i)) * 2, 0, 100)) + + String(F(" %) and BSSID: ")) + WiFi.BSSIDstr(i) + + String(F(" and channel: ")) + String(WiFi.channel(i))); +#endif + if (bestConnection == -1) { + bestConnection = i; + } else { + if (WiFi.RSSI(i) > WiFi.RSSI(bestConnection)) { + bestConnection = i; + } + } + } + } + if (bestConnection == -1) { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(F("No network found with SSID: "), ssid); +#endif + } else { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(String(F("Trying to connect to SSID ")) + ssid + String(F(" found with RSSI: ")) + + String(WiFi.RSSI(bestConnection)) + String(F("(")) + + String(constrain((100.0 + WiFi.RSSI(bestConnection)) * 2, 0, 100)) + + String(F(" %) and BSSID: ")) + WiFi.BSSIDstr(bestConnection) + + String(F(" and channel: ")) + String(WiFi.channel(bestConnection))); +#endif + ret = WiFi.begin(ssid.c_str(), pass.c_str(), 0, + WiFi.BSSID(bestConnection), connect); + } + } + } else { + ret = WiFi.begin(ssid.c_str(), pass.c_str(), 0, NULL, connect); + } + + WiFi.persistent(false); #ifdef WM_DEBUG_LEVEL if(!ret) DEBUG_WM(WM_DEBUG_ERROR,F("[ERROR] wifi begin failed")); @@ -3138,6 +3198,12 @@ String WiFiManager::getWiFiHostname(){ #endif } +/** + * toggle showing find best RSSID + * @param boolean enabled + */ +void WiFiManager::setFindBestRSSI(boolean enabled) { _findBestRSSI = enabled; } + /** * [setTitle description] * @param String title, set app title diff --git a/Raumtermostat/lib/WiFiManager/WiFiManager.h b/Raumtermostat/lib/WiFiManager/WiFiManager.h index 3ab1983..3764564 100644 --- a/Raumtermostat/lib/WiFiManager/WiFiManager.h +++ b/Raumtermostat/lib/WiFiManager/WiFiManager.h @@ -431,6 +431,7 @@ class WiFiManager // clean connect, always disconnect before connecting void setCleanConnect(bool enable); // default false + void setFindBestRSSI(boolean enabled); // set custom menu items and order, vector or arr // see _menutokens for ids void setMenu(std::vector& menu); @@ -531,6 +532,7 @@ class WiFiManager unsigned long _lastscan = 0; // ms for timing wifi scans unsigned long _startscan = 0; // ms for timing wifi scans unsigned long _startconn = 0; // ms for timing wifi connects + boolean _findBestRSSI = true; // find best rssi ap in wifiscan // defaults const byte DNS_PORT = 53; diff --git a/Raumtermostat/platformio.ini b/Raumtermostat/platformio.ini index dc46024..f422dfb 100644 --- a/Raumtermostat/platformio.ini +++ b/Raumtermostat/platformio.ini @@ -15,7 +15,7 @@ monitor_speed = 115200 framework = arduino board_build.partitions = min_spiffs.csv upload_protocol = espota -upload_port = TMP-EG-Bad.fritz.box +upload_port = TMP-EG-WoZi.fritz.box lib_deps = moononournation/GFX Library for Arduino@^1.5.3 lvgl/lvgl@^9.4.0 diff --git a/Raumtermostat/src/MQTT.cpp b/Raumtermostat/src/MQTT.cpp index 5fd9a8d..5f9177b 100644 --- a/Raumtermostat/src/MQTT.cpp +++ b/Raumtermostat/src/MQTT.cpp @@ -36,7 +36,15 @@ void MQTT::begin(){ wm.setConnectRetries(5); wm.setWiFiAutoReconnect(true); wm.setMinimumSignalQuality(20); - WiFi.persistent(true); + WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); + WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); + WiFi.persistent(false); + WiFi.mode(WIFI_OFF); + WiFi.mode(WIFI_STA); + WiFi.disconnect(true); // delete old config + WiFi.begin(); + delay(500); // 500ms seems to work in most cases, may depend on AP + WiFi.disconnect(true); // delete old config WiFi.setAutoConnect(true); WiFi.setAutoReconnect(true); wm.addParameter(&custom_hostname); @@ -47,6 +55,7 @@ void MQTT::begin(){ wm.addParameter(&custom_enbuff); wm.addParameter(&custom_ntpServer); psclient.setClient(client); + psclient.setBufferSize(1024); psclient.setServer(Settings::prefs.mqtt_server,Settings::prefs.mqtt_port); psclient.setCallback(receiveTopic); WiFi.setHostname(custom_hostname.getValue()); //define hostname @@ -154,7 +163,8 @@ void MQTT::receiveTopic(char* topic, byte* payload, unsigned int length) { remoteLog("MQTT: ON mode received", strVal.c_str()); forceStatusUpdate = true; } - } +} + void MQTT::reconnect(void) { // Loop until we're reconnected @@ -170,7 +180,7 @@ void MQTT::reconnect(void) { strcat(newtopic,"/changeMode"); psclient.subscribe(newtopic); Serial.println("connected"); - + sendHAautodiscovery(); } else { Serial.print("failed, rc="); Serial.print(psclient.state()); @@ -392,7 +402,38 @@ void MQTT::sendStatus(void){ }else{ publish_sub("mode","Unknown",true); } +} +void MQTT::sendHAautodiscovery(void){ + char buffer[128]; + char payload[1024]; + String devName = Settings::prefs.mqtt_topic; + devName.replace("Raumtemp/","Thermostat "); + devName.replace("/"," "); + snprintf(buffer,sizeof(buffer),"homeassistant/climate/%s/thermostat/config",Settings::prefs.hostname); + snprintf(payload, sizeof(payload),"{\"name\":\"%s\",\"unique_id\":\"%s_climate\",\"current_temperature_topic\":\"%s/Temp[degC]\",\"temperature_state_topic\":\"%s/Set Temp[degC]\",\"temperature_command_topic\":\"%s/changeSetTemp\",\"min_temp\":5,\"max_temp\":30,\"mode_state_topic\":\"%s/mode\",\"mode_command_topic\":\"%s/changeMode\",\"modes\":[\"normal\",\"frost-prevention\",\"overheating\"],\"device\":{\"identifiers\":[\"%s\"],\"name\":\"%s\",\"model\":\"ESP32 Thermostat\",\"manufacturer\":\"DIY\"}}", devName.c_str(),Settings::prefs.hostname,Settings::prefs.mqtt_topic,Settings::prefs.mqtt_topic,Settings::prefs.mqtt_topic,Settings::prefs.mqtt_topic,Settings::prefs.mqtt_topic,Settings::prefs.hostname, devName.c_str()); + psclient.publish(buffer,payload,true); + psclient.loop(); + snprintf(buffer,sizeof(buffer),"homeassistant/binary_sensor/%s/heating/config",Settings::prefs.hostname); + snprintf(payload, sizeof(payload),"{\"name\":\"Heizung aktiv\",\"unique_id\":\"%s_heating\",\"state_topic\":\"%s/Heating\",\"payload_on\":\"true\",\"payload_off\":\"false\",\"device_class\":\"heat\",\"device\":{\"identifiers\":[\"%s\"],\"name\":\"%s\"}}",Settings::prefs.hostname,Settings::prefs.mqtt_topic,Settings::prefs.hostname, devName.c_str()); + psclient.publish(buffer,payload,true); + psclient.loop(); + snprintf(buffer,sizeof(buffer),"homeassistant/sensor/%s/temperature/config",Settings::prefs.hostname); + snprintf(payload, sizeof(payload),"{\"name\":\"Temperatur\",\"unique_id\":\"%s_temp\",\"state_topic\":\"%s/Temp[degC]\",\"unit_of_measurement\": \"°C\",\"device_class\": \"temperature\",\"state_class\": \"measurement\",\"device\":{\"identifiers\":[\"%s\"],\"name\":\"%s\"}}",Settings::prefs.hostname,Settings::prefs.mqtt_topic,Settings::prefs.hostname, devName.c_str()); + psclient.publish(buffer,payload,true); + psclient.loop(); + snprintf(buffer,sizeof(buffer),"homeassistant/sensor/%s/humidity/config",Settings::prefs.hostname); + snprintf(payload, sizeof(payload),"{\"name\":\"Luftfeuchte\",\"unique_id\":\"%s_hum\",\"state_topic\":\"%s/rHum[%%]\",\"unit_of_measurement\": \"%%\",\"device_class\": \"humidity\",\"state_class\": \"measurement\",\"device\":{\"identifiers\":[\"%s\"],\"name\":\"%s\"}}",Settings::prefs.hostname,Settings::prefs.mqtt_topic,Settings::prefs.hostname, devName.c_str()); + psclient.publish(buffer,payload,true); + psclient.loop(); + snprintf(buffer,sizeof(buffer),"homeassistant/sensor/%s/pressure/config",Settings::prefs.hostname); + snprintf(payload, sizeof(payload),"{\"name\":\"Luftdruck\",\"unique_id\":\"%s_pres\",\"state_topic\":\"%s/pressure[hPa]\",\"unit_of_measurement\": \"hPa\",\"device_class\": \"pressure\",\"state_class\": \"measurement\",\"device\":{\"identifiers\":[\"%s\"],\"name\":\"%s\"}}",Settings::prefs.hostname,Settings::prefs.mqtt_topic,Settings::prefs.hostname, devName.c_str()); + psclient.publish(buffer,payload,true); + psclient.loop(); + snprintf(buffer,sizeof(buffer),"homeassistant/sensor/%s/hostname/config",Settings::prefs.hostname); + snprintf(payload, sizeof(payload),"{\"name\":\"Luftdruck\",\"unique_id\":\"%s_host\",\"state_topic\":\"%s/hostname\",\"entity_category\": \"diagnostic\",\"device\":{\"identifiers\":[\"%s\"],\"name\":\"%s\"}}",Settings::prefs.hostname,Settings::prefs.mqtt_topic,Settings::prefs.hostname, devName.c_str()); + psclient.publish(buffer,payload,true); + psclient.loop(); } void MQTT::remoteLog(const char* str, int32_t num){ diff --git a/Raumtermostat/src/MQTT.h b/Raumtermostat/src/MQTT.h index 7746942..cdf9dac 100644 --- a/Raumtermostat/src/MQTT.h +++ b/Raumtermostat/src/MQTT.h @@ -36,6 +36,7 @@ public: WiFiManager wm; static WiFiUDP udp; void sendStatus(void); + void sendHAautodiscovery(void); char* getMqttServer(void); uint16_t getMqttPort(void); char* getHostname(void); @@ -45,10 +46,8 @@ public: void begin(); void loop(void); void publish(const char* msg); - void publish(String msg); void publish(const char* msg, unsigned int len); void publish(const char* msg, bool retained); - void publish(const char* msg, unsigned int len, bool retained); void publish_sub(const char* subtopic, const char* msg, bool retained = false); static void remoteLog(const char* str,const char* str2); static void remoteLog(const char* str); @@ -66,10 +65,6 @@ private: //static void serveSetValve(AsyncWebServerRequest *request); // static void handleWebSocket(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len); // static void handleWebSocketMessage(void *arg, uint8_t *data, size_t len); - static String processor(const String& var); - static String makeStatusJSON(void); - static String makeTimerJSON(void); - void sendMQTTstatus(void); void saveConfigToFlash(void); static void saveConfigCallback(void); unsigned long lastReconnectTry{0};