Add homeassisstant compatibility and choose strongest AP on wifi connection.

This commit is contained in:
Moirtz Wagner 2026-02-19 17:31:26 +01:00
parent bfcb43d22b
commit 5b671ca0be
5 changed files with 116 additions and 12 deletions

View File

@ -1059,7 +1059,12 @@ uint8_t WiFiManager::connectWifi(String ssid, String pass, bool connect) {
else { else {
// connect using saved ssid if there is one // connect using saved ssid if there is one
if (WiFi_hasAutoConnect()) { if (WiFi_hasAutoConnect()) {
wifiConnectDefault(); if (_findBestRSSI) {
wifiConnectNew(WiFi_SSID(), WiFi_psk(), connect);
} else {
// connect to saved ssid
wifiConnectDefault();
}
connRes = waitForConnectResult(); connRes = waitForConnectResult();
} }
else { else {
@ -1110,7 +1115,62 @@ bool WiFiManager::wifiConnectNew(String ssid, String pass,bool connect){
#endif #endif
WiFi_enableSTA(true,storeSTAmode); // storeSTAmode will also toggle STA on in default opmode (persistent) if true (default) WiFi_enableSTA(true,storeSTAmode); // storeSTAmode will also toggle STA on in default opmode (persistent) if true (default)
WiFi.persistent(true); 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); WiFi.persistent(false);
#ifdef WM_DEBUG_LEVEL #ifdef WM_DEBUG_LEVEL
if(!ret) DEBUG_WM(WM_DEBUG_ERROR,F("[ERROR] wifi begin failed")); if(!ret) DEBUG_WM(WM_DEBUG_ERROR,F("[ERROR] wifi begin failed"));
@ -3138,6 +3198,12 @@ String WiFiManager::getWiFiHostname(){
#endif #endif
} }
/**
* toggle showing find best RSSID
* @param boolean enabled
*/
void WiFiManager::setFindBestRSSI(boolean enabled) { _findBestRSSI = enabled; }
/** /**
* [setTitle description] * [setTitle description]
* @param String title, set app title * @param String title, set app title

View File

@ -431,6 +431,7 @@ class WiFiManager
// clean connect, always disconnect before connecting // clean connect, always disconnect before connecting
void setCleanConnect(bool enable); // default false void setCleanConnect(bool enable); // default false
void setFindBestRSSI(boolean enabled);
// set custom menu items and order, vector or arr // set custom menu items and order, vector or arr
// see _menutokens for ids // see _menutokens for ids
void setMenu(std::vector<const char*>& menu); void setMenu(std::vector<const char*>& menu);
@ -531,6 +532,7 @@ class WiFiManager
unsigned long _lastscan = 0; // ms for timing wifi scans unsigned long _lastscan = 0; // ms for timing wifi scans
unsigned long _startscan = 0; // ms for timing wifi scans unsigned long _startscan = 0; // ms for timing wifi scans
unsigned long _startconn = 0; // ms for timing wifi connects unsigned long _startconn = 0; // ms for timing wifi connects
boolean _findBestRSSI = true; // find best rssi ap in wifiscan
// defaults // defaults
const byte DNS_PORT = 53; const byte DNS_PORT = 53;

View File

@ -15,7 +15,7 @@ monitor_speed = 115200
framework = arduino framework = arduino
board_build.partitions = min_spiffs.csv board_build.partitions = min_spiffs.csv
upload_protocol = espota upload_protocol = espota
upload_port = TMP-EG-Bad.fritz.box upload_port = TMP-EG-WoZi.fritz.box
lib_deps = lib_deps =
moononournation/GFX Library for Arduino@^1.5.3 moononournation/GFX Library for Arduino@^1.5.3
lvgl/lvgl@^9.4.0 lvgl/lvgl@^9.4.0

View File

@ -36,7 +36,15 @@ void MQTT::begin(){
wm.setConnectRetries(5); wm.setConnectRetries(5);
wm.setWiFiAutoReconnect(true); wm.setWiFiAutoReconnect(true);
wm.setMinimumSignalQuality(20); 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.setAutoConnect(true);
WiFi.setAutoReconnect(true); WiFi.setAutoReconnect(true);
wm.addParameter(&custom_hostname); wm.addParameter(&custom_hostname);
@ -47,6 +55,7 @@ void MQTT::begin(){
wm.addParameter(&custom_enbuff); wm.addParameter(&custom_enbuff);
wm.addParameter(&custom_ntpServer); wm.addParameter(&custom_ntpServer);
psclient.setClient(client); psclient.setClient(client);
psclient.setBufferSize(1024);
psclient.setServer(Settings::prefs.mqtt_server,Settings::prefs.mqtt_port); psclient.setServer(Settings::prefs.mqtt_server,Settings::prefs.mqtt_port);
psclient.setCallback(receiveTopic); psclient.setCallback(receiveTopic);
WiFi.setHostname(custom_hostname.getValue()); //define hostname 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()); remoteLog("MQTT: ON mode received", strVal.c_str());
forceStatusUpdate = true; forceStatusUpdate = true;
} }
} }
void MQTT::reconnect(void) { void MQTT::reconnect(void) {
// Loop until we're reconnected // Loop until we're reconnected
@ -170,7 +180,7 @@ void MQTT::reconnect(void) {
strcat(newtopic,"/changeMode"); strcat(newtopic,"/changeMode");
psclient.subscribe(newtopic); psclient.subscribe(newtopic);
Serial.println("connected"); Serial.println("connected");
sendHAautodiscovery();
} else { } else {
Serial.print("failed, rc="); Serial.print("failed, rc=");
Serial.print(psclient.state()); Serial.print(psclient.state());
@ -392,7 +402,38 @@ void MQTT::sendStatus(void){
}else{ }else{
publish_sub("mode","Unknown",true); 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){ void MQTT::remoteLog(const char* str, int32_t num){

View File

@ -36,6 +36,7 @@ public:
WiFiManager wm; WiFiManager wm;
static WiFiUDP udp; static WiFiUDP udp;
void sendStatus(void); void sendStatus(void);
void sendHAautodiscovery(void);
char* getMqttServer(void); char* getMqttServer(void);
uint16_t getMqttPort(void); uint16_t getMqttPort(void);
char* getHostname(void); char* getHostname(void);
@ -45,10 +46,8 @@ public:
void begin(); void begin();
void loop(void); void loop(void);
void publish(const char* msg); void publish(const char* msg);
void publish(String msg);
void publish(const char* msg, unsigned int len); void publish(const char* msg, unsigned int len);
void publish(const char* msg, bool retained); 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); 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,const char* str2);
static void remoteLog(const char* str); static void remoteLog(const char* str);
@ -66,10 +65,6 @@ private:
//static void serveSetValve(AsyncWebServerRequest *request); //static void serveSetValve(AsyncWebServerRequest *request);
// static void handleWebSocket(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len); // 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 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); void saveConfigToFlash(void);
static void saveConfigCallback(void); static void saveConfigCallback(void);
unsigned long lastReconnectTry{0}; unsigned long lastReconnectTry{0};