diff --git a/README.md b/README.md index d7a29ee..020d0f3 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,6 @@ In a browser navigate to the IP address assigned to the ESP32. - MQTT Password : If using authentication on the MQTT broker set to the password belonging to a username with read/write rights on the MQTT broker, set to # to clear - MQTT NukiHub Path: Set to the preferred MQTT root topic for NukiHub, defaults to "nukihub". Make sure this topic is unique when using multiple ESP32 NukiHub devices - Enable Home Assistant auto discovery: Enable Home Assistant MQTT auto discovery. Will automatically create entities in Home Assistant for NukiHub and connected Nuki Lock and/or Opener when enabled. -- Use Home Assistant device based discovery: Use Home Assistant Device discovery instead of single component discovery. Recommended, but requires Home Assistant 2024.11 or newer. #### Advanced MQTT Configuration diff --git a/clion/CMakeLists.txt b/clion/CMakeLists.txt index 741aea4..0d93523 100644 --- a/clion/CMakeLists.txt +++ b/clion/CMakeLists.txt @@ -49,6 +49,7 @@ set(SRCFILES ../src/util/NetworkUtil.cpp ../src/enums/NetworkDeviceType.h ../src/util/NetworkDeviceInstantiator.cpp + ../src/HomeAssistantDiscovery.cpp ../src/NukiOfficial.cpp ../src/NukiPublisher.cpp ../src/EspMillis.h diff --git a/src/Config.h b/src/Config.h index fc4c53e..cf7ca8a 100644 --- a/src/Config.h +++ b/src/Config.h @@ -4,7 +4,7 @@ #define NUKI_HUB_VERSION "9.02" #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2024-11-06" +#define NUKI_HUB_DATE "2024-11-08" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" @@ -22,6 +22,7 @@ #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32c3.bin" #define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32c3.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32c3.bin" +#define NUKI_HUB_HW (char*)"ESP32-C3" #elif defined(CONFIG_IDF_TARGET_ESP32S3) #if defined(CONFIG_SPIRAM_MODE_OCT) #define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3oct.bin" @@ -36,6 +37,7 @@ #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32s3oct.bin" #define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32s3oct.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32s3oct.bin" +#define NUKI_HUB_HW (char*)"ESP32-S3 (Octal PSRAM)" #else #define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3.bin" @@ -49,6 +51,7 @@ #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32s3.bin" #define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32s3.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32s3.bin" +#define NUKI_HUB_HW (char*)"ESP32-S3" #endif #elif defined(CONFIG_IDF_TARGET_ESP32C6) #define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32c6.bin" @@ -63,6 +66,7 @@ #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32c6.bin" #define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32c6.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32c6.bin" +#define NUKI_HUB_HW (char*)"ESP32-C6" #elif defined(CONFIG_IDF_TARGET_ESP32H2) #define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32h2.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32h2.bin" @@ -76,6 +80,7 @@ #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32h2.bin" #define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32h2.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32h2.bin" +#define NUKI_HUB_HW (char*)"ESP32-H2" #else #if defined(CONFIG_FREERTOS_UNICORE) #define GITHUB_LATEST_RELEASE_BINARY_URL "https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32-solo1.bin" @@ -90,6 +95,7 @@ #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32-solo1.bin" #define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32-solo1.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32-solo1.bin" +#define NUKI_HUB_HW (char*)"ESP32-SOLO1" #else #define GITHUB_LATEST_RELEASE_BINARY_URL "https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32.bin" @@ -103,6 +109,7 @@ #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32.bin" #define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32.bin" +#define NUKI_HUB_HW (char*)"ESP32" #endif #endif diff --git a/src/HomeAssistantDiscovery.cpp b/src/HomeAssistantDiscovery.cpp index cd86bd3..ccfb3ac 100644 --- a/src/HomeAssistantDiscovery.cpp +++ b/src/HomeAssistantDiscovery.cpp @@ -1,139 +1,460 @@ -void NukiWrapper::setupHASS(int type) +#include "HomeAssistantDiscovery.h" +#include "Config.h" +#include "Logger.h" +#include "PreferencesKeys.h" +#include "MqttTopics.h" + +HomeAssistantDiscovery::HomeAssistantDiscovery(NetworkDevice* device, Preferences *preferences, char* buffer, size_t bufferSize) +: _device(device), + _preferences(preferences), + _buffer(buffer), + _bufferSize(bufferSize) { - if(_preferences->getUInt(preference_nuki_id_lock, 0) != _nukiConfig.nukiId) - { - return; - } - - String baseTopic = _preferences->getString(preference_mqtt_lock_path); - baseTopic.concat("/lock"); - char uidString[20]; - itoa(_nukiConfig.nukiId, uidString, 16); - - _network->publishHASSConfig((char*)"SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, _firmwareVersion.c_str(), _hardwareVersion.c_str(), hasDoorSensor(), _hasKeypad, _publishAuthData, (char*)"lock", (char*)"unlock", (char*)"unlatch"); - Log->println("HASS setup for lock completed."); + _discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery, ""); + _baseTopic = _preferences->getString(preference_mqtt_lock_path); + _offEnabled = _preferences->getBool(preference_official_hybrid_enabled, false); + _checkUpdates = _preferences->getBool(preference_check_updates, false); + _updateFromMQTT = _preferences->getBool(preference_update_from_mqtt, false); + _hostname = _preferences->getString(preference_hostname, ""); + sprintf(_nukiHubUidString, "%u", _preferences->getUInt(preference_device_id_lock, 0)); } -void NukiWrapper::disableHASS() +void HomeAssistantDiscovery::setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad) { char uidString[20]; - itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16); - _network->removeHASSConfig(uidString); + itoa(nukiId, uidString, 16); + bool publishAuthData = _preferences->getBool(preference_publish_authdata, false); + + if(type == 0) + { + publishHASSNukiHubConfig(); + Log->println("HASS setup for NukiHub completed."); + } + else if(type == 1) + { + if(_preferences->getUInt(preference_nuki_id_lock, 0) != nukiId) + { + return; + } + String lockTopic = _baseTopic; + lockTopic.concat("/lock"); + publishHASSConfig((char*)"SmartLock", lockTopic.c_str(), nukiName, uidString, firmwareVersion, hardwareVersion, hasDoorSensor, hasKeypad, publishAuthData, (char*)"lock", (char*)"unlock", (char*)"unlatch"); + Log->println("HASS setup for lock completed."); + } + else if(type == 2) + { + if(_preferences->getUInt(preference_nuki_id_opener, 0) != nukiId) + { + return; + } + String openerTopic = _baseTopic; + openerTopic.concat("/opener"); + if(_preferences->getBool(preference_opener_continuous_mode, false)) + { + publishHASSConfig((char*)"Opener", openerTopic.c_str(), nukiName, uidString, firmwareVersion, hardwareVersion, hasDoorSensor, hasKeypad, publishAuthData, (char*)"deactivateCM", (char*)"activateCM", (char*)"electricStrikeActuation"); + } + else + { + publishHASSConfig((char*)"Opener", openerTopic.c_str(), nukiName, uidString, firmwareVersion, hardwareVersion, hasDoorSensor, hasKeypad, publishAuthData, (char*)"deactivateRTO", (char*)"activateRTO", (char*)"electricStrikeActuation"); + } + + Log->println("HASS setup for opener completed."); + } } -void NukiOpenerWrapper::setupHASS() +void HomeAssistantDiscovery::disableHASS() { - if(_preferences->getUInt(preference_nuki_id_opener, 0) != _nukiConfig.nukiId) - { - return; - } + removeHASSConfig(_nukiHubUidString); - String baseTopic = _preferences->getString(preference_mqtt_lock_path); - baseTopic.concat("/opener"); char uidString[20]; - itoa(_nukiConfig.nukiId, uidString, 16); - if(_preferences->getBool(preference_opener_continuous_mode, false)) + if(_preferences->getUInt(preference_nuki_id_lock, 0) != 0) { - _network->publishHASSConfig((char*)"Opener", baseTopic.c_str(), (char*)_nukiConfig.name, uidString, _firmwareVersion.c_str(), _hardwareVersion.c_str(), _publishAuthData, _hasKeypad, (char*)"deactivateCM", (char*)"activateCM", (char*)"electricStrikeActuation"); + itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16); + removeHASSConfig(uidString); + } + if(_preferences->getUInt(preference_nuki_id_opener, 0) != 0) + { + itoa(_preferences->getUInt(preference_nuki_id_opener, 0), uidString, 16); + removeHASSConfig(uidString); + } +} + +void HomeAssistantDiscovery::publishHASSNukiHubConfig() +{ + JsonDocument json; + json.clear(); + JsonObject dev = json["dev"].to(); + JsonArray ids = dev["ids"].to(); + ids.add(String("nuki_") + _nukiHubUidString); + json["dev"]["mf"] = "Technyon"; + json["dev"]["mdl"] = "NukiHub"; + json["dev"]["name"] = _hostname.c_str(); + json["dev"]["sw"] = NUKI_HUB_VERSION; + json["dev"]["hw"] = NUKI_HUB_HW; + + String cuUrl = _preferences->getString(preference_mqtt_hass_cu_url, ""); + + if (cuUrl != "") + { + json["dev"]["cu"] = cuUrl; } else { - _network->publishHASSConfig((char*)"Opener", baseTopic.c_str(), (char*)_nukiConfig.name, uidString, _firmwareVersion.c_str(), _hardwareVersion.c_str(), _publishAuthData, _hasKeypad, (char*)"deactivateRTO", (char*)"activateRTO", (char*)"electricStrikeActuation"); + json["dev"]["cu"] = "http://" + _device->localIP(); } - _hassSetupCompleted = true; + json["~"] = _baseTopic; + json["name"] = "Restart Nuki Hub"; + json["unique_id"] = String(_nukiHubUidString) + "_reset"; + json["avty"][0]["t"] = String("~") + mqtt_topic_mqtt_connection_state; + json["opt"] = "false"; + json["stat_t"] = String("~") + mqtt_topic_reset; + json["ent_cat"] = "diagnostic"; + json["cmd_t"] = String("~") + mqtt_topic_reset; + json["ic"] = "mdi:restart"; + json["pl_on"] = "1"; + json["pl_off"] = "0"; + json["stat_on"] = "1"; + json["stat_off"] = "0"; - Log->println("HASS setup for opener completed."); -} + serializeJson(json, _buffer, _bufferSize); -void NukiOpenerWrapper::disableHASS() -{ - char uidString[20]; - itoa(_preferences->getUInt(preference_nuki_id_opener, 0), uidString, 16); - _network->removeHASSConfig(uidString); -} + String path = _preferences->getString(preference_mqtt_hass_discovery, "homeassistant"); + path.concat("/switch/"); + path.concat(_nukiHubUidString); + path.concat("/reset/config"); -void NukiNetworkOpener::publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const bool& publishAuthData, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction) -{ - String availabilityTopic = _preferences->getString(preference_mqtt_lock_path); - availabilityTopic.concat("/maintenance/mqttConnectionState"); - - _network->publishHASSConfig(deviceType, baseTopic, name, uidString, softwareVersion, hardwareVersion, availabilityTopic.c_str(), hasKeypad, lockAction, unlockAction, openAction); - _network->publishHASSConfigAdditionalOpenerEntities(deviceType, baseTopic, name, uidString); - if(publishAuthData) - { - _network->publishHASSConfigAccessLog(deviceType, baseTopic, name, uidString); - } - else - { - _network->removeHASSConfigTopic((char*)"sensor", (char*)"last_action_authorization", uidString); - _network->removeHASSConfigTopic((char*)"sensor", (char*)"rolling_log", uidString); - } - if(hasKeypad) - { - _network->publishHASSConfigKeypad(deviceType, baseTopic, name, uidString); - } - else - { - _network->removeHASSConfigTopic((char*)"sensor", (char*)"keypad_status", uidString); - _network->removeHASSConfigTopic((char*)"binary_sensor", (char*)"keypad_battery_low", uidString); - } -} - -void NukiNetworkOpener::removeHASSConfig(char* uidString) -{ - _network->removeHASSConfig(uidString); -} - -void NukiNetworkLock::publishHASSConfig(char *deviceType, const char *baseTopic, char *name, char *uidString, const char *softwareVersion, const char *hardwareVersion, const bool& hasDoorSensor, const bool& hasKeypad, const bool& publishAuthData, char *lockAction, - char *unlockAction, char *openAction) -{ - String availabilityTopic = _preferences->getString(preference_mqtt_lock_path); - availabilityTopic.concat("/maintenance/mqttConnectionState"); - _network->publishHASSConfig(deviceType, baseTopic, name, uidString, softwareVersion, hardwareVersion, availabilityTopic.c_str(), hasKeypad, lockAction, unlockAction, openAction); - _network->publishHASSConfigAdditionalLockEntities(deviceType, baseTopic, name, uidString); - - if(hasDoorSensor) - { - _network->publishHASSConfigDoorSensor(deviceType, baseTopic, name, uidString); - } - else - { - _network->removeHASSConfigTopic((char*)"binary_sensor", (char*)"door_sensor", uidString); - } + _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, _buffer); #ifndef CONFIG_IDF_TARGET_ESP32H2 - _network->publishHASSWifiRssiConfig(deviceType, baseTopic, name, uidString); + publishHassTopic("sensor", + "wifi_signal_strength", + _nukiHubUidString, + "_wifi_signal_strength", + "WIFI signal strength", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_wifi_rssi, + "NukiHub", + "signal_strength", + "measurement", + "diagnostic", + "", + { {(char*)"unit_of_meas", (char*)"dBm"} }); #endif + // MQTT Connected + publishHassTopic("binary_sensor", + "mqtt_connected", + _nukiHubUidString, + "_mqtt_connected", + "MQTT connected", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_mqtt_connection_state, + "NukiHub", + "", + "", + "diagnostic", + "", + { + {(char*)"pl_on", (char*)"online"}, + {(char*)"pl_off", (char*)"offline"}, + {(char*)"ic", (char*)"mdi:lan-connect"} + }); + + // Network device + publishHassTopic("sensor", + "network_device", + _nukiHubUidString, + "_network_device", + "Network device", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_network_device, + "NukiHub", + "", + "", + "diagnostic", + "", + { { (char*)"en", (char*)"true" }}); + + // Nuki Hub Webserver enabled + publishHassTopic("switch", + "webserver", + _nukiHubUidString, + "_webserver", + "Nuki Hub webserver enabled", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_webserver_state, + "NukiHub", + "", + "", + "diagnostic", + String("~") + mqtt_topic_webserver_action, + { + { (char*)"pl_on", (char*)"1" }, + { (char*)"pl_off", (char*)"0" }, + { (char*)"stat_on", (char*)"1" }, + { (char*)"stat_off", (char*)"0" } + }); + + // Uptime + publishHassTopic("sensor", + "uptime", + _nukiHubUidString, + "_uptime", + "Uptime", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_uptime, + "NukiHub", + "duration", + "", + "diagnostic", + "", + { + { (char*)"en", (char*)"true" }, + { (char*)"unit_of_meas", (char*)"min"} + }); + + if(_preferences->getBool(preference_mqtt_log_enabled, false)) + { + // MQTT Log + publishHassTopic("sensor", + "mqtt_log", + _nukiHubUidString, + "_mqtt_log", + "MQTT Log", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_log, + "NukiHub", + "", + "", + "diagnostic", + "", + { { (char*)"en", (char*)"true" }}); + } + else + { + removeHassTopic((char*)"sensor", (char*)"mqtt_log", _nukiHubUidString); + } + + // Nuki Hub version + publishHassTopic("sensor", + "nuki_hub_version", + _nukiHubUidString, + "_nuki_hub_version", + "Nuki Hub version", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_info_nuki_hub_version, + "NukiHub", + "", + "", + "diagnostic", + "", + { + { (char*)"en", (char*)"true" }, + {(char*)"ic", (char*)"mdi:counter"} + }); + + // Nuki Hub build + publishHassTopic("sensor", + "nuki_hub_build", + _nukiHubUidString, + "_nuki_hub_build", + "Nuki Hub build", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_info_nuki_hub_build, + "NukiHub", + "", + "", + "diagnostic", + "", + { + { (char*)"en", (char*)"true" }, + {(char*)"ic", (char*)"mdi:counter"} + }); + + // Nuki Hub restart reason + publishHassTopic("sensor", + "nuki_hub_restart_reason", + _nukiHubUidString, + "_nuki_hub_restart_reason", + "Nuki Hub restart reason", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_restart_reason_fw, + "NukiHub", + "", + "", + "diagnostic", + "", + { { (char*)"en", (char*)"true" }}); + + // Nuki Hub restart reason ESP + publishHassTopic("sensor", + "nuki_hub_restart_reason_esp", + _nukiHubUidString, + "_nuki_hub_restart_reason_esp", + "Nuki Hub restart reason ESP", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_restart_reason_esp, + "NukiHub", + "", + "", + "diagnostic", + "", + { { (char*)"en", (char*)"true" }}); + + if(_checkUpdates) + { + // NUKI Hub latest + publishHassTopic("sensor", + "nuki_hub_latest", + _nukiHubUidString, + "_nuki_hub_latest", + "NUKI Hub latest", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_info_nuki_hub_latest, + "NukiHub", + "", + "", + "diagnostic", + "", + { + { (char*)"en", (char*)"true" }, + {(char*)"ic", (char*)"mdi:counter"} + }); + + // NUKI Hub update + char latest_version_topic[250]; + _baseTopic.toCharArray(latest_version_topic,_baseTopic.length() + 1); + strcat(latest_version_topic, mqtt_topic_info_nuki_hub_latest); + + if(!_updateFromMQTT) + { + publishHassTopic("update", + "nuki_hub_update", + _nukiHubUidString, + "_nuki_hub_update", + "NUKI Hub firmware update", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_info_nuki_hub_version, + "NukiHub", + "firmware", + "", + "diagnostic", + "", + { + { (char*)"en", (char*)"true" }, + { (char*)"ent_pic", (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/master/icon/favicon-32x32.png" }, + { (char*)"rel_u", (char*)GITHUB_LATEST_RELEASE_URL }, + { (char*)"l_ver_t", (char*)latest_version_topic } + }); + } + else + { + publishHassTopic("update", + "nuki_hub_update", + _nukiHubUidString, + "_nuki_hub_update", + "NUKI Hub firmware update", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_info_nuki_hub_version, + "NukiHub", + "firmware", + "", + "diagnostic", + String("~") + mqtt_topic_update, + { + { (char*)"en", (char*)"true" }, + { (char*)"pl_inst", (char*)"1" }, + { (char*)"ent_pic", (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/master/icon/favicon-32x32.png" }, + { (char*)"rel_u", (char*)GITHUB_LATEST_RELEASE_URL }, + { (char*)"l_ver_t", (char*)latest_version_topic } + }); + } + } + else + { + removeHassTopic((char*)"sensor", (char*)"nuki_hub_latest", _nukiHubUidString); + removeHassTopic((char*)"update", (char*)"nuki_hub_update", _nukiHubUidString); + } + + // Nuki Hub IP Address + publishHassTopic("sensor", + "nuki_hub_ip", + _nukiHubUidString, + "_nuki_hub_ip", + "Nuki Hub IP", + _hostname.c_str(), + _baseTopic.c_str(), + String("~") + mqtt_topic_info_nuki_hub_ip, + "NukiHub", + "", + "", + "diagnostic", + "", + { + { (char*)"en", (char*)"true" }, + {(char*)"ic", (char*)"mdi:ip"} + }); +} + +void HomeAssistantDiscovery::publishHASSConfig(char *deviceType, const char *baseTopic, char *name, char *uidString, const char *softwareVersion, const char *hardwareVersion, const bool& hasDoorSensor, const bool& hasKeypad, const bool& publishAuthData, char *lockAction, char *unlockAction, char *openAction) +{ + String availabilityTopic = _baseTopic; + availabilityTopic.concat(mqtt_topic_mqtt_connection_state); + + publishHASSDeviceConfig(deviceType, baseTopic, name, uidString, softwareVersion, hardwareVersion, availabilityTopic.c_str(), hasKeypad, lockAction, unlockAction, openAction); + + if(strcmp(deviceType, "SmartLock") == 0) + { + publishHASSConfigAdditionalLockEntities(deviceType, baseTopic, name, uidString); + } + else + { + publishHASSConfigAdditionalOpenerEntities(deviceType, baseTopic, name, uidString); + } + if(hasDoorSensor) + { + publishHASSConfigDoorSensor(deviceType, baseTopic, name, uidString); + } + else + { + removeHASSConfigTopic((char*)"binary_sensor", (char*)"door_sensor", uidString); + } if(publishAuthData) { - _network->publishHASSConfigAccessLog(deviceType, baseTopic, name, uidString); + publishHASSConfigAccessLog(deviceType, baseTopic, name, uidString); } else { - _network->removeHASSConfigTopic((char*)"sensor", (char*)"last_action_authorization", uidString); - _network->removeHASSConfigTopic((char*)"sensor", (char*)"rolling_log", uidString); + removeHASSConfigTopic((char*)"sensor", (char*)"last_action_authorization", uidString); + removeHASSConfigTopic((char*)"sensor", (char*)"rolling_log", uidString); } - if(hasKeypad) { - _network->publishHASSConfigKeypad(deviceType, baseTopic, name, uidString); + publishHASSConfigKeypad(deviceType, baseTopic, name, uidString); } else { - _network->removeHASSConfigTopic((char*)"sensor", (char*)"keypad_status", uidString); - _network->removeHASSConfigTopic((char*)"binary_sensor", (char*)"keypad_battery_low", uidString); + removeHASSConfigTopic((char*)"sensor", (char*)"keypad_status", uidString); + removeHASSConfigTopic((char*)"binary_sensor", (char*)"keypad_battery_low", uidString); } } -void NukiNetworkLock::removeHASSConfig(char *uidString) -{ - _network->removeHASSConfig(uidString); -} - -void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction) +void HomeAssistantDiscovery::publishHASSDeviceConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction) { JsonDocument json; json.clear(); @@ -145,6 +466,7 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha json["dev"]["name"] = name; json["dev"]["sw"] = softwareVersion; json["dev"]["hw"] = hardwareVersion; + json["dev"]["via_device"] = String("nuki_") + _nukiHubUidString; String cuUrl = _preferences->getString(preference_mqtt_hass_cu_url, ""); @@ -192,6 +514,45 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, _buffer); + + // Firmware version + publishHassTopic("sensor", + "firmware_version", + uidString, + "_firmware_version", + "Firmware version", + name, + baseTopic, + String("~") + mqtt_topic_info_firmware_version, + deviceType, + "", + "", + "diagnostic", + "", + { + { (char*)"en", (char*)"true" }, + {(char*)"ic", (char*)"mdi:counter"} + }); + + // Hardware version + publishHassTopic("sensor", + "hardware_version", + uidString, + "_hardware_version", + "Hardware version", + name, + baseTopic, + String("~") + mqtt_topic_info_hardware_version, + deviceType, + "", + "", + "diagnostic", + "", + { + { (char*)"en", (char*)"true" }, + {(char*)"ic", (char*)"mdi:counter"} + }); + // Battery critical publishHassTopic("binary_sensor", "battery_low", @@ -247,133 +608,12 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha "", { { (char*)"en", (char*)"true" } }); - // MQTT Connected - publishHassTopic("binary_sensor", - "mqtt_connected", - uidString, - "_mqtt_connected", - "MQTT connected", - name, - baseTopic, - _lockPath + mqtt_topic_mqtt_connection_state, - deviceType, - "", - "", - "diagnostic", - "", - { - {(char*)"pl_on", (char*)"online"}, - {(char*)"pl_off", (char*)"offline"}, - {(char*)"ic", (char*)"mdi:lan-connect"} - }); - - // Reset - publishHassTopic("switch", - "reset", - uidString, - "_reset", - "Restart Nuki Hub", - name, - baseTopic, - String("~") + mqtt_topic_reset, - deviceType, - "", - "", - "diagnostic", - String("~") + mqtt_topic_reset, - { - { (char*)"ic", (char*)"mdi:restart" }, - { (char*)"pl_on", (char*)"1" }, - { (char*)"pl_off", (char*)"0" }, - { (char*)"stat_on", (char*)"1" }, - { (char*)"stat_off", (char*)"0" } - }); - - // Network device - publishHassTopic("sensor", - "network_device", - uidString, - "_network_device", - "Network device", - name, - baseTopic, - _lockPath + mqtt_topic_network_device, - deviceType, - "", - "", - "diagnostic", - "", - { { (char*)"en", (char*)"true" }}); - - // Nuki Hub Webserver enabled - publishHassTopic("switch", - "webserver", - uidString, - "_webserver", - "Nuki Hub webserver enabled", - name, - baseTopic, - _lockPath + mqtt_topic_webserver_state, - deviceType, - "", - "", - "diagnostic", - _lockPath + mqtt_topic_webserver_action, - { - { (char*)"pl_on", (char*)"1" }, - { (char*)"pl_off", (char*)"0" }, - { (char*)"stat_on", (char*)"1" }, - { (char*)"stat_off", (char*)"0" } - }); - - // Uptime - publishHassTopic("sensor", - "uptime", - uidString, - "_uptime", - "Uptime", - name, - baseTopic, - _lockPath + mqtt_topic_uptime, - deviceType, - "duration", - "", - "diagnostic", - "", - { - { (char*)"en", (char*)"true" }, - { (char*)"unit_of_meas", (char*)"min"} - }); - - if(_preferences->getBool(preference_mqtt_log_enabled, false)) - { - // MQTT Log - publishHassTopic("sensor", - "mqtt_log", - uidString, - "_mqtt_log", - "MQTT Log", - name, - baseTopic, - _lockPath + mqtt_topic_log, - deviceType, - "", - "", - "diagnostic", - "", - { { (char*)"en", (char*)"true" }}); - } - else - { - removeHassTopic((char*)"sensor", (char*)"mqtt_log", uidString); - } - if(_offEnabled) { // Hybrid connected - String hybridPath = _lockPath; + String hybridPath = _baseTopic; hybridPath.concat("/lock"); - hybridPath.concat(mqtt_hybrid_state); + hybridPath.concat(mqtt_topic_hybrid_state); publishHassTopic("binary_sensor", "hybrid_connected", uidString, @@ -398,211 +638,6 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha removeHassTopic((char*)"binary_sensor", (char*)"hybrid_connected", uidString); } - // Firmware version - publishHassTopic("sensor", - "firmware_version", - uidString, - "_firmware_version", - "Firmware version", - name, - baseTopic, - String("~") + mqtt_topic_info_firmware_version, - deviceType, - "", - "", - "diagnostic", - "", - { - { (char*)"en", (char*)"true" }, - {(char*)"ic", (char*)"mdi:counter"} - }); - - // Hardware version - publishHassTopic("sensor", - "hardware_version", - uidString, - "_hardware_version", - "Hardware version", - name, - baseTopic, - String("~") + mqtt_topic_info_hardware_version, - deviceType, - "", - "", - "diagnostic", - "", - { - { (char*)"en", (char*)"true" }, - {(char*)"ic", (char*)"mdi:counter"} - }); - - // Nuki Hub version - publishHassTopic("sensor", - "nuki_hub_version", - uidString, - "_nuki_hub_version", - "Nuki Hub version", - name, - baseTopic, - _lockPath + mqtt_topic_info_nuki_hub_version, - deviceType, - "", - "", - "diagnostic", - "", - { - { (char*)"en", (char*)"true" }, - {(char*)"ic", (char*)"mdi:counter"} - }); - - // Nuki Hub build - publishHassTopic("sensor", - "nuki_hub_build", - uidString, - "_nuki_hub_build", - "Nuki Hub build", - name, - baseTopic, - _lockPath + mqtt_topic_info_nuki_hub_build, - deviceType, - "", - "", - "diagnostic", - "", - { - { (char*)"en", (char*)"true" }, - {(char*)"ic", (char*)"mdi:counter"} - }); - - // Nuki Hub restart reason - publishHassTopic("sensor", - "nuki_hub_restart_reason", - uidString, - "_nuki_hub_restart_reason", - "Nuki Hub restart reason", - name, - baseTopic, - _lockPath + mqtt_topic_restart_reason_fw, - deviceType, - "", - "", - "diagnostic", - "", - { { (char*)"en", (char*)"true" }}); - - // Nuki Hub restart reason ESP - publishHassTopic("sensor", - "nuki_hub_restart_reason_esp", - uidString, - "_nuki_hub_restart_reason_esp", - "Nuki Hub restart reason ESP", - name, - baseTopic, - _lockPath + mqtt_topic_restart_reason_esp, - deviceType, - "", - "", - "diagnostic", - "", - { { (char*)"en", (char*)"true" }}); - - if(_checkUpdates) - { - // NUKI Hub latest - publishHassTopic("sensor", - "nuki_hub_latest", - uidString, - "_nuki_hub_latest", - "NUKI Hub latest", - name, - baseTopic, - _lockPath + mqtt_topic_info_nuki_hub_latest, - deviceType, - "", - "", - "diagnostic", - "", - { - { (char*)"en", (char*)"true" }, - {(char*)"ic", (char*)"mdi:counter"} - }); - - // NUKI Hub update - char latest_version_topic[250]; - _lockPath.toCharArray(latest_version_topic,_lockPath.length() + 1); - strcat(latest_version_topic, mqtt_topic_info_nuki_hub_latest); - - if(!_updateFromMQTT) - { - publishHassTopic("update", - "nuki_hub_update", - uidString, - "_nuki_hub_update", - "NUKI Hub firmware update", - name, - baseTopic, - _lockPath + mqtt_topic_info_nuki_hub_version, - deviceType, - "firmware", - "", - "diagnostic", - "", - { - { (char*)"en", (char*)"true" }, - { (char*)"ent_pic", (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/master/icon/favicon-32x32.png" }, - { (char*)"rel_u", (char*)GITHUB_LATEST_RELEASE_URL }, - { (char*)"l_ver_t", (char*)latest_version_topic } - }); - } - else - { - publishHassTopic("update", - "nuki_hub_update", - uidString, - "_nuki_hub_update", - "NUKI Hub firmware update", - name, - baseTopic, - _lockPath + mqtt_topic_info_nuki_hub_version, - deviceType, - "firmware", - "", - "diagnostic", - _lockPath + mqtt_topic_update, - { - { (char*)"en", (char*)"true" }, - { (char*)"pl_inst", (char*)"1" }, - { (char*)"ent_pic", (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/master/icon/favicon-32x32.png" }, - { (char*)"rel_u", (char*)GITHUB_LATEST_RELEASE_URL }, - { (char*)"l_ver_t", (char*)latest_version_topic } - }); - } - } - else - { - removeHassTopic((char*)"sensor", (char*)"nuki_hub_latest", uidString); - removeHassTopic((char*)"update", (char*)"nuki_hub_update", uidString); - } - - // Nuki Hub IP Address - publishHassTopic("sensor", - "nuki_hub_ip", - uidString, - "_nuki_hub_ip", - "Nuki Hub IP", - name, - baseTopic, - _lockPath + mqtt_topic_info_nuki_hub_ip, - deviceType, - "", - "", - "diagnostic", - "", - { - { (char*)"en", (char*)"true" }, - {(char*)"ic", (char*)"mdi:ip"} - }); - // Query Lock State publishHassTopic("button", "query_lockstate", @@ -676,7 +711,7 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha { {(char*)"unit_of_meas", (char*)"dBm"} }); } -void NukiNetwork::publishHASSConfigAdditionalLockEntities(char *deviceType, const char *baseTopic, char *name, char *uidString) +void HomeAssistantDiscovery::publishHASSConfigAdditionalLockEntities(char *deviceType, const char *baseTopic, char *name, char *uidString) { uint32_t aclPrefs[17]; _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); @@ -1790,7 +1825,7 @@ void NukiNetwork::publishHASSConfigAdditionalLockEntities(char *deviceType, cons } } -void NukiNetwork::publishHASSConfigDoorSensor(char *deviceType, const char *baseTopic, char *name, char *uidString) +void HomeAssistantDiscovery::publishHASSConfigDoorSensor(char *deviceType, const char *baseTopic, char *name, char *uidString) { publishHassTopic("binary_sensor", "door_sensor", @@ -1812,7 +1847,7 @@ void NukiNetwork::publishHASSConfigDoorSensor(char *deviceType, const char *base }); } -void NukiNetwork::publishHASSConfigAdditionalOpenerEntities(char *deviceType, const char *baseTopic, char *name, char *uidString) +void HomeAssistantDiscovery::publishHASSConfigAdditionalOpenerEntities(char *deviceType, const char *baseTopic, char *name, char *uidString) { uint32_t aclPrefs[17]; _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); @@ -1942,9 +1977,9 @@ void NukiNetwork::publishHASSConfigAdditionalOpenerEntities(char *deviceType, co { { (char*)"en", (char*)"true" }, { (char*)"ic", (char*)"mdi:led-variant-on" }, - { (char*)"pl_on", (char*)"{ \"ledEnabled\": \"1\"}" }, - { (char*)"pl_off", (char*)"{ \"ledEnabled\": \"0\"}" }, - { (char*)"val_tpl", (char*)"{{value_json.ledEnabled}}" }, + { (char*)"pl_on", (char*)"{ \"ledFlashEnabled\": \"1\"}" }, + { (char*)"pl_off", (char*)"{ \"ledFlashEnabled\": \"0\"}" }, + { (char*)"val_tpl", (char*)"{{value_json.ledFlashEnabled}}" }, { (char*)"stat_on", (char*)"1" }, { (char*)"stat_off", (char*)"0" } }); @@ -2709,7 +2744,7 @@ void NukiNetwork::publishHASSConfigAdditionalOpenerEntities(char *deviceType, co } } -void NukiNetwork::publishHASSConfigAccessLog(char *deviceType, const char *baseTopic, char *name, char *uidString) +void HomeAssistantDiscovery::publishHASSConfigAccessLog(char *deviceType, const char *baseTopic, char *name, char *uidString) { publishHassTopic("sensor", "last_action_authorization", @@ -2731,7 +2766,6 @@ void NukiNetwork::publishHASSConfigAccessLog(char *deviceType, const char *baseT String rollingSate = "~"; rollingSate.concat(mqtt_topic_lock_log_rolling); - const char *rollingStateChr = rollingSate.c_str(); publishHassTopic("sensor", "rolling_log", @@ -2748,12 +2782,12 @@ void NukiNetwork::publishHASSConfigAccessLog(char *deviceType, const char *baseT "", { { (char*)"ic", (char*)"mdi:format-list-bulleted" }, - { (char*)"json_attr_t", (char*)rollingStateChr }, + { (char*)"json_attr_t", (char*)rollingSate.c_str() }, { (char*)"val_tpl", (char*)"{{value_json.index}}" } }); } -void NukiNetwork::publishHASSConfigKeypad(char *deviceType, const char *baseTopic, char *name, char *uidString) +void HomeAssistantDiscovery::publishHASSConfigKeypad(char *deviceType, const char *baseTopic, char *name, char *uidString) { // Keypad battery critical publishHassTopic("binary_sensor", @@ -2813,25 +2847,7 @@ void NukiNetwork::publishHASSConfigKeypad(char *deviceType, const char *baseTopi }); } -void NukiNetwork::publishHASSWifiRssiConfig(char *deviceType, const char *baseTopic, char *name, char *uidString) -{ - publishHassTopic("sensor", - "wifi_signal_strength", - uidString, - "_wifi_signal_strength", - "WIFI signal strength", - name, - baseTopic, - _lockPath + mqtt_topic_wifi_rssi, - deviceType, - "signal_strength", - "measurement", - "diagnostic", - "", - { {(char*)"unit_of_meas", (char*)"dBm"} }); -} - -void NukiNetwork::publishHassTopic(const String& mqttDeviceType, +void HomeAssistantDiscovery::publishHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString, const String& uidStringPostfix, @@ -2857,7 +2873,7 @@ void NukiNetwork::publishHassTopic(const String& mqttDeviceType, } } -String NukiNetwork::createHassTopicPath(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString) +String HomeAssistantDiscovery::createHassTopicPath(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString) { String path = _discoveryTopic; path.concat("/"); @@ -2871,7 +2887,7 @@ String NukiNetwork::createHassTopicPath(const String& mqttDeviceType, const Stri return path; } -void NukiNetwork::removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString) +void HomeAssistantDiscovery::removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString) { if (_discoveryTopic != "") { @@ -2880,7 +2896,7 @@ void NukiNetwork::removeHassTopic(const String& mqttDeviceType, const String& mq } } -void NukiNetwork::removeHASSConfig(char* uidString) +void HomeAssistantDiscovery::removeHASSConfig(char* uidString) { removeHassTopic((char*)"lock", (char*)"smartlock", uidString); removeHassTopic((char*)"binary_sensor", (char*)"battery_low", uidString); @@ -2976,33 +2992,31 @@ void NukiNetwork::removeHASSConfig(char* uidString) removeHassTopic((char*)"sensor", (char*)"nuki_hub_restart_reason_esp", uidString); } -void NukiNetwork::removeHASSConfigTopic(char *deviceType, char *name, char *uidString) +void HomeAssistantDiscovery::removeHASSConfigTopic(char *deviceType, char *name, char *uidString) { removeHassTopic(deviceType, name, uidString); } -JsonDocument NukiNetwork::createHassJson(const String& uidString, - const String& uidStringPostfix, - const String& displayName, - const String& name, - const String& baseTopic, - const String& stateTopic, - const String& deviceType, - const String& deviceClass, - const String& stateClass, - const String& entityCat, - const String& commandTopic, - std::vector> additionalEntries - ) +JsonDocument HomeAssistantDiscovery::createHassJson(const String& uidString, + const String& uidStringPostfix, + const String& displayName, + const String& name, + const String& baseTopic, + const String& stateTopic, + const String& deviceType, + const String& deviceClass, + const String& stateClass, + const String& entityCat, + const String& commandTopic, + std::vector> additionalEntries + ) { JsonDocument json; json.clear(); JsonObject dev = json["dev"].to(); JsonArray ids = dev["ids"].to(); ids.add(String("nuki_") + uidString); - json["dev"]["mf"] = "Nuki"; - json["dev"]["mdl"] = deviceType; - json["dev"]["name"] = name; + json["~"] = baseTopic; json["name"] = displayName; json["unique_id"] = String(uidString) + uidStringPostfix; @@ -3032,7 +3046,7 @@ JsonDocument NukiNetwork::createHassJson(const String& uidString, json["cmd_t"] = commandTopic; } - json["avty"]["t"] = _lockPath + mqtt_topic_mqtt_connection_state; + json["avty"]["t"] = _baseTopic + mqtt_topic_mqtt_connection_state; for(const auto& entry : additionalEntries) { diff --git a/src/HomeAssistantDiscovery.h b/src/HomeAssistantDiscovery.h index aeb72c9..967de52 100644 --- a/src/HomeAssistantDiscovery.h +++ b/src/HomeAssistantDiscovery.h @@ -1,16 +1,15 @@ - void setupHASS(int type=0); +#pragma once +#include +#include +#include "networkDevices/NetworkDevice.h" + +class HomeAssistantDiscovery +{ +public: + explicit HomeAssistantDiscovery(NetworkDevice* device, Preferences* preferences, char* buffer, size_t bufferSize); + void setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad); void disableHASS(); - void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const bool& publishAuthData, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction); - void removeHASSConfig(char* uidString); - void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction); - void publishHASSConfigAdditionalLockEntities(char* deviceType, const char* baseTopic, char* name, char* uidString); - void publishHASSConfigDoorSensor(char* deviceType, const char* baseTopic, char* name, char* uidString); - void publishHASSConfigAdditionalOpenerEntities(char* deviceType, const char* baseTopic, char* name, char* uidString); - void publishHASSConfigAccessLog(char* deviceType, const char* baseTopic, char* name, char* uidString); - void publishHASSConfigKeypad(char* deviceType, const char* baseTopic, char* name, char* uidString); - void publishHASSWifiRssiConfig(char* deviceType, const char* baseTopic, char* name, char* uidString); - void removeHASSConfig(char* uidString); - void removeHASSConfigTopic(char* deviceType, char* name, char* uidString); + void removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString); void publishHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString, @@ -25,19 +24,51 @@ const String& entityCat = "", const String& commandTopic = "", std::vector> additionalEntries = {} - ); - void removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString); + ); +private: + void publishHASSConfig(char *deviceType, const char *baseTopic, char *name, char *uidString, const char *softwareVersion, const char *hardwareVersion, const bool& hasDoorSensor, const bool& hasKeypad, const bool& publishAuthData, char *lockAction, char *unlockAction, char *openAction); + void publishHASSDeviceConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction); + void publishHASSNukiHubConfig(); + + void publishHASSConfigAdditionalLockEntities(char* deviceType, const char* baseTopic, char* name, char* uidString); + void publishHASSConfigDoorSensor(char* deviceType, const char* baseTopic, char* name, char* uidString); + void publishHASSConfigAdditionalOpenerEntities(char* deviceType, const char* baseTopic, char* name, char* uidString); + void publishHASSConfigAccessLog(char* deviceType, const char* baseTopic, char* name, char* uidString); + void publishHASSConfigKeypad(char* deviceType, const char* baseTopic, char* name, char* uidString); + void publishHASSConfigWifiRssi(char* deviceType, const char* baseTopic, char* name, char* uidString); + + + void removeHASSConfig(char* uidString); + void removeHASSConfigTopic(char* deviceType, char* name, char* uidString); + String createHassTopicPath(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString); JsonDocument createHassJson(const String& uidString, - const String& uidStringPostfix, - const String& displayName, - const String& name, - const String& baseTopic, - const String& stateTopic, - const String& deviceType, - const String& deviceClass, - const String& stateClass = "", - const String& entityCat = "", - const String& commandTopic = "", - std::vector> additionalEntries = {} - ); \ No newline at end of file + const String& uidStringPostfix, + const String& displayName, + const String& name, + const String& baseTopic, + const String& stateTopic, + const String& deviceType, + const String& deviceClass, + const String& stateClass = "", + const String& entityCat = "", + const String& commandTopic = "", + std::vector> additionalEntries = {} + ); + + NetworkDevice* _device = nullptr; + Preferences* _preferences = nullptr; + + String _discoveryTopic; + String _baseTopic; + String _hostname; + + char _nukiHubUidString[20]; + + bool _offEnabled = false; + bool _checkUpdates = false; + bool _updateFromMQTT = false; + + char* _buffer; + const size_t _bufferSize; +}; \ No newline at end of file diff --git a/src/MqttReceiver.h b/src/MqttReceiver.h index 3db350d..e442919 100644 --- a/src/MqttReceiver.h +++ b/src/MqttReceiver.h @@ -1,7 +1,5 @@ #pragma once -#include - class MqttReceiver { public: diff --git a/src/MqttTopics.h b/src/MqttTopics.h index f842b13..1171ae0 100644 --- a/src/MqttTopics.h +++ b/src/MqttTopics.h @@ -110,7 +110,7 @@ #define mqtt_topic_restart_reason_esp "/maintenance/restartReasonNukiEsp" #define mqtt_topic_mqtt_connection_state "/maintenance/mqttConnectionState" #define mqtt_topic_network_device "/maintenance/networkDevice" -#define mqtt_hybrid_state "/hybridConnected" +#define mqtt_topic_hybrid_state "/hybridConnected" #define mqtt_topic_gpio_prefix "/gpio" #define mqtt_topic_gpio_pin "/pin_" diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index 10999d9..730cf04 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -11,12 +11,7 @@ #endif #include "networkDevices/EthernetDevice.h" -#ifndef NUKI_HUB_UPDATER -#include -#endif - NukiNetwork* NukiNetwork::_inst = nullptr; -HomeAssistantDiscovery* HomeAssistantDiscovery::_hadiscovery = nullptr; extern bool wifiFallback; extern bool disableNetwork; @@ -42,9 +37,7 @@ NukiNetwork::NukiNetwork(Preferences *preferences) } _inst = this; - _hadiscovery = new HomeAssistantDiscovery(_inst, _preferences); _webEnabled = _preferences->getBool(preference_webserver_enabled, true); - _updateFromMQTT = _preferences->getBool(preference_update_from_mqtt, false); #ifndef NUKI_HUB_UPDATER memset(_maintenancePathPrefix, 0, sizeof(_maintenancePathPrefix)); @@ -148,7 +141,10 @@ void NukiNetwork::setupDevice() { onMqttDisconnect(reason); }); + + _hadiscovery = new HomeAssistantDiscovery(_device, _preferences, _buffer, _bufferSize); #endif + } void NukiNetwork::reconfigureDevice() @@ -246,7 +242,7 @@ void NukiNetwork::initialize() { _nukiHubPath[i] = mqttPath.charAt(i); } - + _hostname = _preferences->getString(preference_hostname, ""); if(_hostname == "") @@ -339,8 +335,6 @@ void NukiNetwork::initialize() } } - _discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery, ""); - _offEnabled = _preferences->getBool(preference_official_hybrid_enabled, false); readSettings(); } } @@ -640,7 +634,7 @@ bool NukiNetwork::reconnect() _device->mqttSetCredentials(_mqttUser, _mqttPass); } - _device->setWill(_mqttConnectionStateTopic, 1, true, _lastWillPayload); + _device->mqttSetWill(_mqttConnectionStateTopic, 1, true, _lastWillPayload); _device->mqttSetServer(_mqttBrokerAddr, _mqttPort); _device->mqttConnect(); @@ -683,7 +677,12 @@ bool NukiNetwork::reconnect() publishString(_maintenancePathPrefix, mqtt_topic_network_device, _device->deviceName().c_str(), true); for(const auto& it : _initTopics) { - _device->mqttPublish(it.first.c_str(), MQTT_QOS_LEVEL, true, it.second.c_str()); + publish(it.first.c_str(), it.second.c_str(), true); + } + + if(_preferences->getBool(preference_mqtt_hass_enabled, false)) + { + setupHASS(0, 0, {0}, {0}, {0}, false, false); } } @@ -804,7 +803,7 @@ void NukiNetwork::onMqttDataReceived(const espMqttClientTypes::MessageProperties if(_mqttConnectedTs == -1 || (millis() - _mqttConnectedTs < 2000)) return; parseGpioTopics(properties, topic, payload, len, index, total); - + onMqttDataReceived(topic, (byte*)payload, index); for(auto receiver : _mqttReceivers) @@ -816,7 +815,7 @@ void NukiNetwork::onMqttDataReceived(const espMqttClientTypes::MessageProperties void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) { char* data = (char*)payload; - + if(comparePrefixedPath(topic, mqtt_topic_reset) && strcmp(data, "1") == 0) { Log->println(F("Restart requested via MQTT.")); @@ -960,7 +959,7 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns clearWifiFallback(); delay(200); restartEsp(RestartReason::ReconfigureWebServer); - } + } } void NukiNetwork::parseGpioTopics(const espMqttClientTypes::MessageProperties &properties, const char *topic, const uint8_t *payload, size_t& len, size_t& index, size_t& total) @@ -1027,7 +1026,7 @@ void NukiNetwork::publishFloat(const char* prefix, const char* topic, const floa dtostrf(value, 0, precision, str); char path[200] = {0}; buildMqttPath(path, { prefix, topic }); - _device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str); + publish(path, str, retain); } void NukiNetwork::publishInt(const char* prefix, const char *topic, const int value, bool retain) @@ -1036,7 +1035,7 @@ void NukiNetwork::publishInt(const char* prefix, const char *topic, const int va itoa(value, str, 10); char path[200] = {0}; buildMqttPath(path, { prefix, topic }); - _device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str); + publish(path, str, retain); } void NukiNetwork::publishUInt(const char* prefix, const char *topic, const unsigned int value, bool retain) @@ -1045,7 +1044,7 @@ void NukiNetwork::publishUInt(const char* prefix, const char *topic, const unsig utoa(value, str, 10); char path[200] = {0}; buildMqttPath(path, { prefix, topic }); - _device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str); + publish(path, str, retain); } void NukiNetwork::publishULong(const char* prefix, const char *topic, const unsigned long value, bool retain) @@ -1054,7 +1053,7 @@ void NukiNetwork::publishULong(const char* prefix, const char *topic, const unsi utoa(value, str, 10); char path[200] = {0}; buildMqttPath(path, { prefix, topic }); - _device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str); + publish(path, str, retain); } void NukiNetwork::publishLongLong(const char* prefix, const char *topic, int64_t value, bool retain) @@ -1075,7 +1074,7 @@ void NukiNetwork::publishLongLong(const char* prefix, const char *topic, int64_t } char path[200] = {0}; buildMqttPath(path, { prefix, topic }); - _device->mqttPublish(path, MQTT_QOS_LEVEL, retain, result); + publish(path, result, retain); } void NukiNetwork::publishBool(const char* prefix, const char *topic, const bool value, bool retain) @@ -1084,21 +1083,26 @@ void NukiNetwork::publishBool(const char* prefix, const char *topic, const bool str[0] = value ? '1' : '0'; char path[200] = {0}; buildMqttPath(path, { prefix, topic }); - _device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str); + publish(path, str, retain); } void NukiNetwork::publishString(const char* prefix, const char *topic, const char *value, bool retain) { char path[200] = {0}; buildMqttPath(path, { prefix, topic }); - _device->mqttPublish(path, MQTT_QOS_LEVEL, retain, value); + publish(path, value, retain); +} + +void NukiNetwork::publish(const char *topic, const char *value, bool retain) +{ + _device->mqttPublish(topic, MQTT_QOS_LEVEL, retain, value); } void NukiNetwork::removeTopic(const String& mqttPath, const String& mqttTopic) { String path = mqttPath; path.concat(mqttTopic); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); + publish(path.c_str(), "", true); #ifdef DEBUG_NUKIHUB Log->print(F("Removing MQTT topic: ")); @@ -1106,9 +1110,9 @@ void NukiNetwork::removeTopic(const String& mqttPath, const String& mqttTopic) #endif } -void NukiNetwork::setupHASS(int type) +void NukiNetwork::setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad) { - _hadiscovery->setupHASS(type); + _hadiscovery->setupHASS(type, nukiId, nukiName, firmwareVersion, hardwareVersion, hasDoorSensor, hasKeypad); } void NukiNetwork::disableHASS() @@ -1354,7 +1358,7 @@ void NukiNetwork::addReconnectedCallback(std::function reconnectedCallba void NukiNetwork::disableMqtt() { - _device->disableMqtt(); + _device->mqttDisable(); _mqttEnabled = false; } diff --git a/src/NukiNetwork.h b/src/NukiNetwork.h index 52c4069..ee40c97 100644 --- a/src/NukiNetwork.h +++ b/src/NukiNetwork.h @@ -15,6 +15,7 @@ #include "Gpio.h" #include #include "NukiConstants.h" +#include "HomeAssistantDiscovery.h" #endif class NukiNetwork @@ -56,11 +57,31 @@ public: void publishLongLong(const char* prefix, const char* topic, int64_t value, bool retain); void publishBool(const char* prefix, const char* topic, const bool value, bool retain); void publishString(const char* prefix, const char* topic, const char* value, bool retain); + void publish(const char *topic, const char *value, bool retain); void removeTopic(const String& mqttPath, const String& mqttTopic); void batteryTypeToString(const Nuki::BatteryType battype, char* str); void advertisingModeToString(const Nuki::AdvertisingMode advmode, char* str); void timeZoneIdToString(const Nuki::TimeZoneId timeZoneId, char* str); + void setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad); + void disableHASS(); + void publishHassTopic(const String& mqttDeviceType, + const String& mqttDeviceName, + const String& uidString, + const String& uidStringPostfix, + const String& displayName, + const String& name, + const String& baseTopic, + const String& stateTopic, + const String& deviceType, + const String& deviceClass, + const String& stateClass, + const String& entityCat, + const String& commandTopic, + std::vector> additionalEntries + ); + void removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString); + int mqttConnectionState(); // 0 = not connected; 1 = connected; 2 = connected and mqtt processed bool mqttRecentlyConnected(); bool pathEquals(const char* prefix, const char* path, const char* referencePath); @@ -87,8 +108,6 @@ private: NetworkDeviceType _networkDeviceType = (NetworkDeviceType)-1; bool _firstBootAfterDeviceChange = false; bool _webEnabled = true; - bool _updateFromMQTT = false; - bool _offEnabled = false; #ifndef NUKI_HUB_UPDATER static void onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); @@ -99,30 +118,16 @@ private: void parseGpioTopics(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t& len, size_t& index, size_t& total); void gpioActionCallback(const GpioAction& action, const int& pin); bool comparePrefixedPath(const char* fullPath, const char* subPath); - - String createHassTopicPath(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString); - JsonDocument createHassJson(const String& uidString, - const String& uidStringPostfix, - const String& displayName, - const String& name, - const String& baseTopic, - const String& stateTopic, - const String& deviceType, - const String& deviceClass, - const String& stateClass = "", - const String& entityCat = "", - const String& commandTopic = "", - std::vector> additionalEntries = {} - ); - void buildMqttPath(char* outPath, std::initializer_list paths); void buildMqttPath(const char *path, char *outPath); + void buildMqttPath(char* outPath, std::initializer_list paths); const char* _lastWillPayload = "offline"; char _mqttConnectionStateTopic[211] = {0}; String _lockPath; - String _discoveryTopic; String _brokerAddr; + HomeAssistantDiscovery* _hadiscovery = nullptr; + Gpio* _gpio; int _mqttConnectionState = 0; diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index 6f50275..9169e3a 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -1598,6 +1598,11 @@ uint8_t NukiNetworkLock::queryCommands() return qc; } +void NukiNetworkLock::setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad) +{ + _network->setupHASS(type, nukiId, nukiName, firmwareVersion, hardwareVersion, hasDoorSensor, hasKeypad); +} + void NukiNetworkLock::buttonPressActionToString(const NukiLock::ButtonPressAction btnPressAction, char* str) { switch (btnPressAction) diff --git a/src/NukiNetworkLock.h b/src/NukiNetworkLock.h index d172784..231ce5f 100644 --- a/src/NukiNetworkLock.h +++ b/src/NukiNetworkLock.h @@ -56,6 +56,7 @@ public: void setTimeControlCommandReceivedCallback(void (*timeControlCommandReceivedReceivedCallback)(const char* value)); void setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char* value)); void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override; + void setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad); void publishFloat(const char* topic, const float value, bool retain, const uint8_t precision = 2); void publishInt(const char* topic, const int value, bool retain); diff --git a/src/NukiNetworkOpener.cpp b/src/NukiNetworkOpener.cpp index ca286dc..08f0e0a 100644 --- a/src/NukiNetworkOpener.cpp +++ b/src/NukiNetworkOpener.cpp @@ -1556,6 +1556,11 @@ uint8_t NukiNetworkOpener::queryCommands() return qc; } +void NukiNetworkOpener::setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad) +{ + _network->setupHASS(type, nukiId, nukiName, firmwareVersion, hardwareVersion, hasDoorSensor, hasKeypad); +} + void NukiNetworkOpener::buttonPressActionToString(const NukiOpener::ButtonPressAction btnPressAction, char* str) { switch (btnPressAction) diff --git a/src/NukiNetworkOpener.h b/src/NukiNetworkOpener.h index 6db0a38..9ccc180 100644 --- a/src/NukiNetworkOpener.h +++ b/src/NukiNetworkOpener.h @@ -47,6 +47,7 @@ public: void setTimeControlCommandReceivedCallback(void (*timeControlCommandReceivedReceivedCallback)(const char* value)); void setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char* value)); void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override; + void setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad); int mqttConnectionState(); bool reconnected(); //SETBACK diff --git a/src/NukiOfficial.cpp b/src/NukiOfficial.cpp index 64960b3..d1bd966 100644 --- a/src/NukiOfficial.cpp +++ b/src/NukiOfficial.cpp @@ -13,7 +13,6 @@ NukiOfficial::NukiOfficial(Preferences *preferences) _disableNonJSON = preferences->getBool(preference_disable_non_json, false); } - void NukiOfficial::setUid(const uint32_t& uid) { char uidString[20]; @@ -43,7 +42,6 @@ void NukiOfficial::setPublisher(NukiPublisher *publisher) _publisher = publisher; } - const char *NukiOfficial::getMqttPath() const { return mqttPath; @@ -99,14 +97,14 @@ void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value Log->print(F("Connected: ")); Log->println((strcmp(value, "true") == 0 ? 1 : 0)); offConnected = (strcmp(value, "true") == 0 ? 1 : 0); - _publisher->publishBool(mqtt_hybrid_state, offConnected, true); + _publisher->publishBool(mqtt_topic_hybrid_state, offConnected, true); } else if(strcmp(topic, mqtt_topic_official_state) == 0) { offState = atoi(value); _statusUpdated = true; Log->println(F("Lock: Updating status on Hybrid state change")); - _publisher->publishBool(mqtt_hybrid_state, offConnected, true); + _publisher->publishBool(mqtt_topic_hybrid_state, offConnected, true); NukiLock::lockstateToString((NukiLock::LockState)offState, str); _publisher->publishString(mqtt_topic_lock_state, str, true); diff --git a/src/NukiOpenerWrapper.cpp b/src/NukiOpenerWrapper.cpp index ef548cc..2e70327 100644 --- a/src/NukiOpenerWrapper.cpp +++ b/src/NukiOpenerWrapper.cpp @@ -54,7 +54,7 @@ void NukiOpenerWrapper::initialize() _nukiOpener.setConnectTimeout(3); _nukiOpener.setDisconnectTimeout(5000); - _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; + _hassEnabled = _preferences->getBool(preference_mqtt_hass_enabled, false); readSettings(); } @@ -313,7 +313,7 @@ void NukiOpenerWrapper::update() } if(_hassEnabled && _nukiConfigValid && _nukiAdvancedConfigValid && !_hassSetupCompleted) { - _network->setupHASS(2); + _network->setupHASS(2, _nukiConfig.nukiId, (char*)_nukiConfig.name, _firmwareVersion.c_str(), _hardwareVersion.c_str(), false, _hasKeypad); _hassSetupCompleted = true; } if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs)) diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 36d03ce..c680edd 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -1,6 +1,3 @@ -#ifndef CONFIG_IDF_TARGET_ESP32H2 -#include "esp_wifi.h" -#endif #include "NukiWrapper.h" #include "PreferencesKeys.h" #include "MqttTopics.h" @@ -50,7 +47,7 @@ NukiWrapper::~NukiWrapper() } -void NukiWrapper::initialize(const bool& firstStart) +void NukiWrapper::initialize() { _nukiLock.initialize(); _nukiLock.registerBleScanner(_bleScanner); @@ -58,75 +55,7 @@ void NukiWrapper::initialize(const bool& firstStart) _nukiLock.setConnectTimeout(3); _nukiLock.setDisconnectTimeout(5000); - if(firstStart) - { - Log->println("First start, setting preference defaults"); - -#ifndef CONFIG_IDF_TARGET_ESP32H2 - wifi_config_t wifi_cfg; - if(esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) - { - Log->println("Failed to get Wi-Fi configuration in RAM"); - } - - if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) - { - Log->println("Failed to set storage Wi-Fi"); - } - - memset(wifi_cfg.sta.ssid, 0, sizeof(wifi_cfg.sta.ssid)); - memset(wifi_cfg.sta.password, 0, sizeof(wifi_cfg.sta.password)); - - if (esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) - { - Log->println("Failed to clear NVS Wi-Fi configuration"); - } -#endif - _preferences->putString(preference_mqtt_lock_path, "nukihub"); - - _preferences->putBool(preference_check_updates, true); - _preferences->putBool(preference_opener_continuous_mode, false); - _preferences->putBool(preference_official_hybrid_enabled, false); - _preferences->putBool(preference_official_hybrid_actions, false); - _preferences->putBool(preference_official_hybrid_retry, false); - _preferences->putBool(preference_disable_non_json, false); - _preferences->putBool(preference_update_from_mqtt, false); - _preferences->putBool(preference_ip_dhcp_enabled, true); - _preferences->putBool(preference_enable_bootloop_reset, false); - _preferences->putBool(preference_show_secrets, false); - - _preferences->putBool(preference_conf_info_enabled, true); - _preferences->putBool(preference_keypad_info_enabled, false); - _preferences->putBool(preference_keypad_topic_per_entry, false); - _preferences->putBool(preference_keypad_publish_code, false); - _preferences->putBool(preference_keypad_control_enabled, false); - _preferences->putBool(preference_timecontrol_info_enabled, false); - _preferences->putBool(preference_timecontrol_topic_per_entry, false); - _preferences->putBool(preference_timecontrol_control_enabled, false); - _preferences->putBool(preference_publish_authdata, false); - _preferences->putBool(preference_register_as_app, false); - _preferences->putBool(preference_register_opener_as_app, false); - - _preferences->putInt(preference_mqtt_broker_port, 1883); - _preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE); - _preferences->putInt(preference_task_size_network, NETWORK_TASK_SIZE); - _preferences->putInt(preference_task_size_nuki, NUKI_TASK_SIZE); - _preferences->putInt(preference_authlog_max_entries, MAX_AUTHLOG); - _preferences->putInt(preference_keypad_max_entries, MAX_KEYPAD); - _preferences->putInt(preference_timecontrol_max_entries, MAX_TIMECONTROL); - _preferences->putInt(preference_query_interval_hybrid_lockstate, 600); - _preferences->putInt(preference_rssi_publish_interval, 60); - _preferences->putInt(preference_network_timeout, 60); - _preferences->putInt(preference_command_nr_of_retries, 3); - _preferences->putInt(preference_command_retry_delay, 100); - _preferences->putInt(preference_restart_ble_beacon_lost, 60); - _preferences->putInt(preference_query_interval_lockstate, 1800); - _preferences->putInt(preference_query_interval_configuration, 3600); - _preferences->putInt(preference_query_interval_battery, 1800); - _preferences->putInt(preference_query_interval_keypad, 1800); - } - - _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; + _hassEnabled = _preferences->getBool(preference_mqtt_hass_enabled, false); readSettings(); } @@ -405,7 +334,7 @@ void NukiWrapper::update() } if(_hassEnabled && _nukiConfigValid && _nukiAdvancedConfigValid && !_hassSetupCompleted) { - _network->setupHASS(1); + _network->setupHASS(1, _nukiConfig.nukiId, (char*)_nukiConfig.name, _firmwareVersion.c_str(), _hardwareVersion.c_str(), hasDoorSensor(), _hasKeypad); _hassSetupCompleted = true; } if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs)) diff --git a/src/NukiWrapper.h b/src/NukiWrapper.h index 2212058..82484d5 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -17,7 +17,7 @@ public: NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, NukiOfficial* nukiOfficial, Gpio* gpio, Preferences* preferences); virtual ~NukiWrapper(); - void initialize(const bool& firstStart); + void initialize(); void readSettings(); void update(); diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index bcf1598..7272158 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -2,6 +2,9 @@ #include #include "Config.h" +#ifndef CONFIG_IDF_TARGET_ESP32H2 +#include "esp_wifi.h" +#endif //CHANGE REQUIRES REBOOT TO TAKE EFFECT #define preference_ip_dhcp_enabled (char*)"dhcpena" @@ -129,17 +132,17 @@ #define preference_presence_detection_timeout (char*)"prdtimeout" #define preference_network_wifi_fallback_disabled (char*)"nwwififb" -inline bool initPreferences(Preferences* preferences) +inline void initPreferences(Preferences* preferences) { #ifdef NUKI_HUB_UPDATER - bool firstStart = false; - return firstStart; + return; #else bool firstStart = !preferences->getBool(preference_started_before); - #endif if(firstStart) { + Serial.println("First start, setting preference defaults"); + preferences->putBool(preference_started_before, true); preferences->putBool(preference_lock_enabled, true); uint32_t aclPrefs[17] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; @@ -152,6 +155,69 @@ inline bool initPreferences(Preferences* preferences) preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); uint32_t advancedOpenerConfigAclPrefs[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); + +#ifndef CONFIG_IDF_TARGET_ESP32H2 + wifi_config_t wifi_cfg; + if(esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) + { + Serial.println("Failed to get Wi-Fi configuration in RAM"); + } + + if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) + { + Serial.println("Failed to set storage Wi-Fi"); + } + + memset(wifi_cfg.sta.ssid, 0, sizeof(wifi_cfg.sta.ssid)); + memset(wifi_cfg.sta.password, 0, sizeof(wifi_cfg.sta.password)); + + if (esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) + { + Serial.println("Failed to clear NVS Wi-Fi configuration"); + } +#endif + preferences->putString(preference_mqtt_lock_path, "nukihub"); + + preferences->putBool(preference_check_updates, true); + preferences->putBool(preference_opener_continuous_mode, false); + preferences->putBool(preference_official_hybrid_enabled, false); + preferences->putBool(preference_official_hybrid_actions, false); + preferences->putBool(preference_official_hybrid_retry, false); + preferences->putBool(preference_disable_non_json, false); + preferences->putBool(preference_update_from_mqtt, false); + preferences->putBool(preference_ip_dhcp_enabled, true); + preferences->putBool(preference_enable_bootloop_reset, false); + preferences->putBool(preference_show_secrets, false); + + preferences->putBool(preference_conf_info_enabled, true); + preferences->putBool(preference_keypad_info_enabled, false); + preferences->putBool(preference_keypad_topic_per_entry, false); + preferences->putBool(preference_keypad_publish_code, false); + preferences->putBool(preference_keypad_control_enabled, false); + preferences->putBool(preference_timecontrol_info_enabled, false); + preferences->putBool(preference_timecontrol_topic_per_entry, false); + preferences->putBool(preference_timecontrol_control_enabled, false); + preferences->putBool(preference_publish_authdata, false); + preferences->putBool(preference_register_as_app, false); + preferences->putBool(preference_register_opener_as_app, false); + + preferences->putInt(preference_mqtt_broker_port, 1883); + preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE); + preferences->putInt(preference_task_size_network, NETWORK_TASK_SIZE); + preferences->putInt(preference_task_size_nuki, NUKI_TASK_SIZE); + preferences->putInt(preference_authlog_max_entries, MAX_AUTHLOG); + preferences->putInt(preference_keypad_max_entries, MAX_KEYPAD); + preferences->putInt(preference_timecontrol_max_entries, MAX_TIMECONTROL); + preferences->putInt(preference_query_interval_hybrid_lockstate, 600); + preferences->putInt(preference_rssi_publish_interval, 60); + preferences->putInt(preference_network_timeout, 60); + preferences->putInt(preference_command_nr_of_retries, 3); + preferences->putInt(preference_command_retry_delay, 100); + preferences->putInt(preference_restart_ble_beacon_lost, 60); + preferences->putInt(preference_query_interval_lockstate, 1800); + preferences->putInt(preference_query_interval_configuration, 3600); + preferences->putInt(preference_query_interval_battery, 1800); + preferences->putInt(preference_query_interval_keypad, 1800); } else { @@ -266,8 +332,7 @@ inline bool initPreferences(Preferences* preferences) preferences->putInt(preference_config_version, atof(NUKI_HUB_VERSION) * 100); } } - - return firstStart; + #endif } class DebugPreferences diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 80302b4..051a1cc 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -1762,8 +1762,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, String& message) configChanged = true; } } - - HADEVDISC else if(key == "HASSDISCOVERY") { if(_preferences->getString(preference_mqtt_hass_discovery, "") != value) @@ -3539,12 +3537,12 @@ esp_err_t WebCfgServer::buildMqttConfigHtml(PsychicRequest *request) printInputField(&response, "MQTTPASS", "MQTT Password", "*", 30, "", true, true); printInputField(&response, "MQTTPATH", "MQTT NukiHub Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180, ""); printCheckBox(&response, "ENHADISC", "Enable Home Assistant auto discovery", _preferences->getBool(preference_mqtt_hass_enabled), "chkHass"); - printCheckBox(&response, "HADEVDISC", "Use Home Assistant device based discovery", _preferences->getBool(preference_hass_device_discovery), ""); response.print("
"); response.print("

Advanced MQTT Configuration

"); response.print(""); printInputField(&response, "HASSDISCOVERY", "Home Assistant discovery topic (usually \"homeassistant\")", _preferences->getString(preference_mqtt_hass_discovery).c_str(), 30, "class=\"chkHass\""); + //printCheckBox(&response, "HADEVDISC", "Use Home Assistant device based discovery (2024.11+)", _preferences->getBool(preference_hass_device_discovery), ""); if(_preferences->getBool(preference_opener_enabled, false)) { printCheckBox(&response, "OPENERCONT", "Set Nuki Opener Lock/Unlock action in Home Assistant to Continuous mode", _preferences->getBool(preference_opener_continuous_mode), ""); diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index 31e7ab7..db6dab0 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -2,7 +2,9 @@ #include #include +#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE #include +#endif #include "esp_ota_ops.h" #include "Config.h" diff --git a/src/main.cpp b/src/main.cpp index 013d3a1..0a86511 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -446,7 +446,7 @@ void setup() preferences = new Preferences(); preferences->begin("nukihub", false); - bool firstStart = initPreferences(preferences); + initPreferences(preferences); bool doOta = false; uint8_t partitionType = checkPartition(); @@ -468,13 +468,6 @@ void setup() doOta = true; } -#ifndef NUKI_HUB_UPDATER - if(preferences->getBool(preference_enable_bootloop_reset, false)) - { - bootloopDetection(); - } -#endif - #ifdef NUKI_HUB_UPDATER Log->print(F("Nuki Hub OTA version ")); Log->println(NUKI_HUB_VERSION); @@ -511,6 +504,11 @@ void setup() }); } #else + if(preferences->getBool(preference_enable_bootloop_reset, false)) + { + bootloopDetection(); + } + Log->print(F("Nuki Hub version ")); Log->println(NUKI_HUB_VERSION); Log->print(F("Nuki Hub build ")); @@ -571,7 +569,7 @@ void setup() } nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, nukiOfficial, gpio, preferences); - nuki->initialize(firstStart); + nuki->initialize(); } Log->println(openerEnabled ? F("Nuki Opener enabled") : F("Nuki Opener disabled")); diff --git a/src/networkDevices/EthernetDevice.cpp b/src/networkDevices/EthernetDevice.cpp index 4dec1a0..f4c5f93 100644 --- a/src/networkDevices/EthernetDevice.cpp +++ b/src/networkDevices/EthernetDevice.cpp @@ -2,6 +2,7 @@ #include "../PreferencesKeys.h" #include "../Logger.h" #include "../RestartReason.h" +#include "../EspMillis.h" extern bool ethCriticalFailure; extern bool wifiFallback; @@ -48,7 +49,9 @@ EthernetDevice::EthernetDevice(const String &hostname, _useSpi(true), _preferences(preferences) { +#ifndef NUKI_HUB_UPDATER NetworkDevice::init(); +#endif } const String EthernetDevice::deviceName() const diff --git a/src/networkDevices/EthernetDevice.h b/src/networkDevices/EthernetDevice.h index 6d5251d..fb0a737 100644 --- a/src/networkDevices/EthernetDevice.h +++ b/src/networkDevices/EthernetDevice.h @@ -11,9 +11,6 @@ #include #include #include "NetworkDevice.h" -#ifndef NUKI_HUB_UPDATER -#include "espMqttClient.h" -#endif class EthernetDevice : public NetworkDevice { @@ -65,16 +62,20 @@ private: void onNetworkEvent(arduino_event_id_t event, arduino_event_info_t info); bool _connected = false; - char* _path; bool _hardwareInitialized = false; + bool _useSpi = false; + + int64_t _checkIpTs = -1; const std::string _deviceName; uint8_t _phy_addr; + eth_phy_type_t _type; // LAN8720 int _power; int _mdc; int _mdio; + eth_clock_mode_t _clock_mode; // W55000 and DM9051 int _cs; @@ -83,10 +84,4 @@ private: int _spi_sck; int _spi_miso; int _spi_mosi; - - int64_t _checkIpTs = -1; - - eth_phy_type_t _type; - eth_clock_mode_t _clock_mode; - bool _useSpi = false; }; \ No newline at end of file diff --git a/src/networkDevices/LAN8720Definitions.h b/src/networkDevices/LAN8720Definitions.h index f45f5fc..6de25b1 100644 --- a/src/networkDevices/LAN8720Definitions.h +++ b/src/networkDevices/LAN8720Definitions.h @@ -2,19 +2,19 @@ #ifndef CONFIG_IDF_TARGET_ESP32 typedef enum { - ETH_CLOCK_GPIO0_IN = 0, - ETH_CLOCK_GPIO16_OUT = 2, - ETH_CLOCK_GPIO17_OUT = 3 - } eth_clock_mode_t; + ETH_CLOCK_GPIO0_IN = 0, + ETH_CLOCK_GPIO16_OUT = 2, + ETH_CLOCK_GPIO17_OUT = 3 +} eth_clock_mode_t; - #define ETH_PHY_TYPE_LAN8720 ETH_PHY_MAX +#define ETH_PHY_TYPE_LAN8720 ETH_PHY_MAX #else #define ETH_PHY_TYPE_LAN8720 ETH_PHY_LAN8720 #endif -#define ETH_CLK_MODE_LAN8720 ETH_CLOCK_GPIO0_IN -#define ETH_PHY_ADDR_LAN8720 0 -#define ETH_PHY_MDC_LAN8720 23 -#define ETH_PHY_MDIO_LAN8720 18 -#define ETH_PHY_POWER_LAN8720 -1 -#define ETH_RESET_PIN_LAN8720 1 \ No newline at end of file +#define ETH_CLK_MODE_LAN8720 ETH_CLOCK_GPIO0_IN +#define ETH_PHY_ADDR_LAN8720 0 +#define ETH_PHY_MDC_LAN8720 23 +#define ETH_PHY_MDIO_LAN8720 18 +#define ETH_PHY_POWER_LAN8720 -1 +#define ETH_RESET_PIN_LAN8720 1 \ No newline at end of file diff --git a/src/networkDevices/NetworkDevice.cpp b/src/networkDevices/NetworkDevice.cpp index 4ad1d84..091a12b 100644 --- a/src/networkDevices/NetworkDevice.cpp +++ b/src/networkDevices/NetworkDevice.cpp @@ -4,7 +4,7 @@ #ifndef NUKI_HUB_UPDATER #include "../MqttTopics.h" -#include "espMqttClient.h" +#include "PreferencesKeys.h" void NetworkDevice::init() { @@ -58,17 +58,38 @@ void NetworkDevice::update() void NetworkDevice::mqttSetClientId(const char *clientId) { - getMqttClient()->setClientId(clientId); + if (_useEncryption) + { + _mqttClientSecure->setClientId(clientId); + } + else + { + _mqttClient->setClientId(clientId); + } } void NetworkDevice::mqttSetCleanSession(bool cleanSession) { - getMqttClient()->setCleanSession(cleanSession); + if (_useEncryption) + { + _mqttClientSecure->setCleanSession(cleanSession); + } + else + { + _mqttClient->setCleanSession(cleanSession); + } } void NetworkDevice::mqttSetKeepAlive(uint16_t keepAlive) { - getMqttClient()->setKeepAlive(keepAlive); + if (_useEncryption) + { + _mqttClientSecure->setKeepAlive(keepAlive); + } + else + { + _mqttClient->setKeepAlive(keepAlive); + } } uint16_t NetworkDevice::mqttPublish(const char *topic, uint8_t qos, bool retain, const char *payload) @@ -88,7 +109,14 @@ bool NetworkDevice::mqttConnected() const void NetworkDevice::mqttSetServer(const char *host, uint16_t port) { - getMqttClient()->setServer(host, port); + if (_useEncryption) + { + _mqttClientSecure->setServer(host, port); + } + else + { + _mqttClient->setServer(host, port); + } } bool NetworkDevice::mqttConnect() @@ -101,29 +129,64 @@ bool NetworkDevice::mqttDisconnect(bool force) return getMqttClient()->disconnect(force); } -void NetworkDevice::setWill(const char *topic, uint8_t qos, bool retain, const char *payload) +void NetworkDevice::mqttSetWill(const char *topic, uint8_t qos, bool retain, const char *payload) { - getMqttClient()->setWill(topic, qos, retain, payload); + if (_useEncryption) + { + _mqttClientSecure->setWill(topic, qos, retain, payload); + } + else + { + _mqttClient->setWill(topic, qos, retain, payload); + } } void NetworkDevice::mqttSetCredentials(const char *username, const char *password) { - getMqttClient()->setCredentials(username, password); + if (_useEncryption) + { + _mqttClientSecure->setCredentials(username, password); + } + else + { + _mqttClient->setCredentials(username, password); + } } void NetworkDevice::mqttOnMessage(espMqttClientTypes::OnMessageCallback callback) { - getMqttClient()->onMessage(callback); + if (_useEncryption) + { + _mqttClientSecure->onMessage(callback); + } + else + { + _mqttClient->onMessage(callback); + } } void NetworkDevice::mqttOnConnect(espMqttClientTypes::OnConnectCallback callback) { - getMqttClient()->onConnect(callback); + if(_useEncryption) + { + _mqttClientSecure->onConnect(callback); + } + else + { + _mqttClient->onConnect(callback); + } } void NetworkDevice::mqttOnDisconnect(espMqttClientTypes::OnDisconnectCallback callback) { - getMqttClient()->onDisconnect(callback); + if (_useEncryption) + { + _mqttClientSecure->onDisconnect(callback); + } + else + { + _mqttClient->onDisconnect(callback); + } } uint16_t NetworkDevice::mqttSubscribe(const char *topic, uint8_t qos) @@ -131,7 +194,7 @@ uint16_t NetworkDevice::mqttSubscribe(const char *topic, uint8_t qos) return getMqttClient()->subscribe(topic, qos); } -void NetworkDevice::disableMqtt() +void NetworkDevice::mqttDisable() { getMqttClient()->disconnect(); _mqttEnabled = false; diff --git a/src/networkDevices/NetworkDevice.h b/src/networkDevices/NetworkDevice.h index 2705245..7e99eef 100644 --- a/src/networkDevices/NetworkDevice.h +++ b/src/networkDevices/NetworkDevice.h @@ -2,10 +2,8 @@ #ifndef NUKI_HUB_UPDATER #include "espMqttClient.h" -#include "MqttClientSetup.h" #endif #include "IPConfiguration.h" -#include "../EspMillis.h" class NetworkDevice { @@ -31,42 +29,45 @@ public: virtual String BSSIDstr() = 0; #ifndef NUKI_HUB_UPDATER + virtual bool mqttConnect(); + virtual bool mqttDisconnect(bool force); + virtual void mqttDisable(); + virtual bool mqttConnected() const; + + virtual uint16_t mqttPublish(const char* topic, uint8_t qos, bool retain, const char* payload); + virtual uint16_t mqttPublish(const char* topic, uint8_t qos, bool retain, const uint8_t* payload, size_t length); + virtual uint16_t mqttSubscribe(const char* topic, uint8_t qos); + + virtual void mqttSetServer(const char* host, uint16_t port); virtual void mqttSetClientId(const char* clientId); virtual void mqttSetCleanSession(bool cleanSession); virtual void mqttSetKeepAlive(uint16_t keepAlive); - virtual uint16_t mqttPublish(const char* topic, uint8_t qos, bool retain, const char* payload); - virtual uint16_t mqttPublish(const char* topic, uint8_t qos, bool retain, const uint8_t* payload, size_t length); - virtual bool mqttConnected() const; - virtual void mqttSetServer(const char* host, uint16_t port); - virtual bool mqttConnect(); - virtual bool mqttDisconnect(bool force); - virtual void setWill(const char* topic, uint8_t qos, bool retain, const char* payload); + virtual void mqttSetWill(const char* topic, uint8_t qos, bool retain, const char* payload); virtual void mqttSetCredentials(const char* username, const char* password); + virtual void mqttOnMessage(espMqttClientTypes::OnMessageCallback callback); virtual void mqttOnConnect(espMqttClientTypes::OnConnectCallback callback); virtual void mqttOnDisconnect(espMqttClientTypes::OnDisconnectCallback callback); - virtual void disableMqtt(); - - virtual uint16_t mqttSubscribe(const char* topic, uint8_t qos); #endif protected: + const IPConfiguration* _ipConfiguration = nullptr; + Preferences* _preferences = nullptr; #ifndef NUKI_HUB_UPDATER espMqttClient *_mqttClient = nullptr; espMqttClientSecure *_mqttClientSecure = nullptr; - bool _useEncryption = false; - bool _mqttEnabled = true; - void init(); MqttClient *getMqttClient() const; + bool _useEncryption = false; + bool _mqttEnabled = true; + char* _path; char _ca[TLS_CA_MAX_SIZE] = {0}; char _cert[TLS_CERT_MAX_SIZE] = {0}; char _key[TLS_KEY_MAX_SIZE] = {0}; #endif const String _hostname; - const IPConfiguration* _ipConfiguration = nullptr; }; \ No newline at end of file diff --git a/src/networkDevices/WifiDevice.cpp b/src/networkDevices/WifiDevice.cpp index 460e566..0109e7f 100644 --- a/src/networkDevices/WifiDevice.cpp +++ b/src/networkDevices/WifiDevice.cpp @@ -1,9 +1,10 @@ +#include "WifiDevice.h" #include "esp_wifi.h" #include -#include "WifiDevice.h" #include "../PreferencesKeys.h" #include "../Logger.h" #include "../RestartReason.h" +#include "../EspMillis.h" WifiDevice::WifiDevice(const String& hostname, Preferences* preferences, const IPConfiguration* ipConfiguration) : NetworkDevice(hostname, preferences, ipConfiguration), diff --git a/src/networkDevices/WifiDevice.h b/src/networkDevices/WifiDevice.h index b08f6f6..ef1915b 100644 --- a/src/networkDevices/WifiDevice.h +++ b/src/networkDevices/WifiDevice.h @@ -1,7 +1,5 @@ #pragma once -#include -#include #include #include "NetworkDevice.h" #include "IPConfiguration.h" @@ -29,10 +27,10 @@ private: void onDisconnected(); void onConnected(); bool connect(); - char* _path; Preferences* _preferences = nullptr; + char* _path; int _foundNetworks = 0; int _disconnectCount = 0; bool _connectOnScanDone = false; @@ -45,4 +43,4 @@ private: uint8_t _connectedChannel = 0; uint8_t* _connectedBSSID; int64_t _disconnectTs = 0; -}; +}; \ No newline at end of file