Import/Export config over MQTT

This commit is contained in:
iranl
2025-01-31 20:21:26 +01:00
parent 57456f42f9
commit 24bbe22e87
25 changed files with 1792 additions and 1206 deletions

View File

@@ -78,7 +78,7 @@ Devices ranked best-to-worst:
- ESP32 without PSRAM
- ...... <br>
(Devices below will not support more Nuki Hub functions)
- ......
- ......
- ESP32-C6
- ESP32-solo1
- ESP32-C3
@@ -292,7 +292,9 @@ In a browser navigate to the IP address assigned to the ESP32.
### Access Level Configuration
#### Nuki General Access Control
- Publish Nuki configuration information: Enable to publish information about the configuration of the connected Nuki device(s) through MQTT.
- Publish Nuki Hub configuration information: Publish Nuki Hub settings over MQTT, see "[Import and Export Nuki Hub settings over MQTT](#import-and-export-nuki-hub-settings-over-mqtt)"
- Modify Nuki Hub configuration over MQTT: Allow changing Nuki Hub settings using MQTT, see "[Import and Export Nuki Hub settings over MQTT](#import-and-export-nuki-hub-settings-over-mqtt)"
- Publish Nuki configuration information: Enable to publish information about the configuration of the connected Nuki device(s) through MQTT.
Note: All of the following requires the Nuki security code / PIN to be set, see "[Nuki Lock PIN / Nuki Opener PIN](#nuki-lock-pin--nuki-opener-pin)"
@@ -463,6 +465,9 @@ Note that the following options can break Nuki Hub and cause bootloops that will
- [lock/opener/]configuration/commandResult: Result of the last configuration change action as JSON data. See the "[Changing Nuki Lock/Opener Configuration](#changing-nuki-lockopener-configuration)" section of this README for possible values
- [lock/opener/]configuration/basicJson: The current basic configuration of the Nuki Lock/Opener as JSON data. See [Nuki Smart Lock API](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--set-config) and [Nuki Opener API](https://developer.nuki.io/page/nuki-opener-api-1/7/#heading--set-config) for available settings. Please note: Longitude and Latitude of the Lock/Opener are not published to MQTT by design. These values can still be changed though.
- [lock/opener/]configuration/advancedJson: The current advanced configuration of the Nuki Lock/Opener as JSON data. See [Nuki Smart Lock API](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--advanced-config) and [Nuki Opener API](https://developer.nuki.io/page/nuki-opener-api-1/7/#heading--advanced-config) for available settings.
- configuration/action: Allows importing and exporting configuration settings of Nuki Hub using a JSON formatted value. After receiving the action, the value is set to "--", see "[Import and Export Nuki Hub settings over MQTT](#import-and-export-nuki-hub-settings-over-mqtt)"
- configuration/commandResult: Result of the last Nuki Hub configuration import action as JSON data, see "[Import and Export Nuki Hub settings over MQTT](#import-and-export-nuki-hub-settings-over-mqtt)"
- configuration/json: Topic where you can export Nuki Hub configuration as JSON data to, see "[Import and Export Nuki Hub settings over MQTT](#import-and-export-nuki-hub-settings-over-mqtt)"
### Query
@@ -512,8 +517,51 @@ Note that the following options can break Nuki Hub and cause bootloops that will
- maintenance/wifiRssi: The Wi-Fi signal strength of the Wi-Fi Access Point as measured by the ESP32 and expressed by the RSSI Value in dBm.
- maintenance/log: If "Enable MQTT logging" is enabled in the web interface, this topic will be filled with debug log information.
- maintenance/freeHeap: Only available when debug mode is enabled. Set to the current size of free heap memory in bytes.
- maintenance/restartReasonNukiHub: Set to the last reason Nuki Hub was restarted. See [RestartReason.h](/RestartReason.h) for possible values
- maintenance/restartReasonNukiEsp: Set to the last reason the ESP was restarted. See [RestartReason.h](/RestartReason.h) for possible values
- maintenance/restartReasonNukiHub: Set to the last reason Nuki Hub was restarted. See [RestartReason.h](/src/RestartReason.h) for possible values
- maintenance/restartReasonNukiEsp: Set to the last reason the ESP was restarted. See [RestartReason.h](/src/RestartReason.h) for possible values
## Import and Export Nuki Hub settings over MQTT
Consider this when deciding if you want to enable the following functionality:
- Any application/actor that has read access to `nukihub/configuration/action` and `nukihub/configuration/json` can view your changes and exports.
- If you have not enabled the setting to require MFA when changing settings any application/actor that has write access to `nukihub/configuration/action` can change Nuki Hub settings (including pairing data and credentials)
### Export Nuki Hub settings over MQTT
To allow Nuki Hub to export configuration over MQTT first enable "Publish Nuki Hub configuration information" in "Access Level Configuration" and save the configuration.
You can export Nuki Hub settings in JSON format by sending the following JSON values to the `nukihub/configuration/action` topic:
- Export Nuki Hub settings without redacted settings and without pairing settings: `{"exportNH": 0}`
NOTE: The following settings can only be exported if you have setup a secure MQTT connection (MQTT over SSL)
- Export Nuki Hub settings with redacted settings and without pairing settings: `{"exportNH": 0, "redacted": 1}`
- Export Nuki Hub settings without redacted settings and with pairing settings: `{"exportNH": 0, "pairing": 1}`
- Export Nuki Hub settings with redacted settings and pairing settings: `{"exportNH": 0, "redacted": 1, "pairing": 1}`
- Export Nuki Hub MQTTS certificates and key: `{"exportMQTTS": 0}`
- Export Nuki Hub HTTPS certificate and key: `{"exportHTTPS": 0}`
The exported values will be available in the `nukihub/configuration/json` topic in JSON format.
A general explanation of the exported values can be found in the [PreferencesKeys.h](/src/PreferencesKeys.h) file
If you set the value of `exportNH`/`exportMQTTS`/`exportHTTPS` to an integer value > 0 the `nukihub/configuration/json` will be cleared after the given amount of seconds (e.g. `{"exportMQTTS": 30}` will clear the JSON topic after 30 seconds)
If you have enabled `Require Duo Push authentication for all sensitive Nuki Hub operations (changing/exporting settings)` you will first need to approve the Duo Push before the settings will be exported.
### Import/Change Nuki Hub settings over MQTT
To allow Nuki Hub to import/change configuration over MQTT first enable "Modify Nuki Hub configuration over MQTT" in "Access Level Configuration" and save the configuration.
You can import Nuki Hub settings in JSON format by sending the desired JSON values to be changed to the `nukihub/configuration/action` topic.
The expected values and format is the same as the JSON files/values that can be exported over MQTT or through the Web Configurator.
The result of the import will be available in the `nukihub/configuration/commandResult` topic in JSON format.
After the import is complete the ESP32 will reboot.
If you have enabled `Require Duo Push authentication for all sensitive Nuki Hub operations (changing/exporting settings)` you will first need to approve the Duo Push before the settings will be changed/imported.
Note: When importing settings using MQTT there are less/no checks on the values entered. These checks are only available when changing settings through the WebConfigurator.
Consider testing your configuration values by changing them in the Web Configurator before trying to use MQTT to change configuration.
A general explanation of the values that can be imported can be found in the [PreferencesKeys.h](/src/PreferencesKeys.h) file
## Changing Nuki Lock/Opener Configuration

View File

@@ -5,7 +5,7 @@
#define NUKI_HUB_VERSION "9.09"
#define NUKI_HUB_VERSION_INT (uint32_t)909
#define NUKI_HUB_BUILD "unknownbuildnr"
#define NUKI_HUB_DATE "2025-01-28"
#define NUKI_HUB_DATE "2025-02-04"
#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"

View File

@@ -223,7 +223,7 @@ void Gpio::loadPinConfiguration()
{
PinEntry entry;
entry.pin = serialized[i * 2];
Log->print(("Pin "));
Log->print("Pin ");
Log->println(entry.pin);
if(std::find(disabledPins.begin(), disabledPins.end(), entry.pin) == disabledPins.end())
@@ -234,14 +234,14 @@ void Gpio::loadPinConfiguration()
}
entry.role = (PinRole) serialized[(i * 2 + 1)];
Log->println("Not found in Ethernet disabled pins");
Log->print(("Role: "));
Log->print("Role: ");
Log->println(getRoleDescription(entry.role));
}
else
{
entry.role = PinRole::Ethernet;
Log->println("Found in Ethernet disabled pins");
Log->print(("Role: "));
Log->print("Role: ");
Log->println(getRoleDescription(entry.role));
}
if(entry.role != PinRole::Disabled)
@@ -359,7 +359,7 @@ const std::vector<int> Gpio::getDisabledPins() const
break;
}
Log->print(("GPIO Boot button and Ethernet disabled pins:"));
Log->print("GPIO Boot button and Ethernet disabled pins:");
for_each_n(disabledPins.begin(), disabledPins.size(),
[](int x)
{
@@ -382,7 +382,7 @@ void Gpio::savePinConfiguration(const std::vector<PinEntry> &pinConfiguration)
for(int i=0; i < len; i++)
{
const auto& entry = pinConfiguration[i];
Log->print(("Pin "));
Log->print("Pin ");
Log->println(entry.pin);
if(std::find(disabledPins.begin(), disabledPins.end(), entry.pin) != disabledPins.end())
@@ -390,7 +390,7 @@ void Gpio::savePinConfiguration(const std::vector<PinEntry> &pinConfiguration)
serialized[i * 2] = entry.pin;
serialized[i * 2 + 1] = (int8_t)PinRole::Ethernet;
Log->println("Found in Ethernet disabled pins");
Log->print(("Role: "));
Log->print("Role: ");
Log->println(getRoleDescription(PinRole::Ethernet));
}
@@ -401,7 +401,7 @@ void Gpio::savePinConfiguration(const std::vector<PinEntry> &pinConfiguration)
serialized[i * 2] = entry.pin;
serialized[i * 2 + 1] = (int8_t) entry.role;
Log->println("Not found in Ethernet disabled pins");
Log->print(("Role: "));
Log->print("Role: ");
Log->println(getRoleDescription(entry.role));
}
}

1005
src/ImportExport.cpp Normal file

File diff suppressed because it is too large Load Diff

45
src/ImportExport.h Normal file
View File

@@ -0,0 +1,45 @@
#pragma once
#include <Preferences.h>
#include "ArduinoJson.h"
#include <PsychicHttp.h>
class ImportExport
{
public:
explicit ImportExport(Preferences* preferences);
void exportHttpsJson(JsonDocument &json);
void exportMqttsJson(JsonDocument &json);
void exportNukiHubJson(JsonDocument &json, bool redacted = false, bool pairing = false, bool nuki = false, bool nukiOpener = false);
JsonDocument importJson(JsonDocument &doc);
int checkDuoAuth(PsychicRequest *request);
int checkDuoApprove();
bool startDuoAuth(char* pushType = (char*)"");
bool getDuoEnabled();
bool getBypassGPIOEnabled();
int getBypassGPIOHigh();
int getBypassGPIOLow();
void readSettings();
void setDuoCheckIP(String duoCheckIP);
void setDuoCheckId(String duoCheckId);
JsonDocument _duoSessions;
JsonDocument _sessionsOpts;
private:
void saveSessions();
Preferences* _preferences;
struct tm timeinfo;
bool _duoActiveRequest;
bool _duoEnabled = false;
bool _bypassGPIO = false;
int _bypassGPIOHigh = -1;
int _bypassGPIOLow = -1;
int64_t _duoRequestTS = 0;
String _duoTransactionId;
String _duoHost;
String _duoSkey;
String _duoIkey;
String _duoUser;
String _duoCheckId;
String _duoCheckIP;
};

View File

@@ -113,6 +113,11 @@
#define mqtt_topic_restart_reason_esp (char*)"/maintenance/restartReasonNukiEsp"
#define mqtt_topic_mqtt_connection_state (char*)"/maintenance/mqttConnectionState"
#define mqtt_topic_network_device (char*)"/maintenance/networkDevice"
#define mqtt_topic_nuki_hub_config_action (char*)"/configuration/action"
#define mqtt_topic_nuki_hub_config_action_command_result (char*)"/configuration/commandResult"
#define mqtt_topic_nuki_hub_config_json (char*)"/configuration/json"
#define mqtt_topic_hybrid_state (char*)"/hybridConnected"
#define mqtt_topic_gpio_prefix (char*)"/gpio"

View File

@@ -14,6 +14,7 @@
NukiNetwork* NukiNetwork::_inst = nullptr;
extern bool timeSynced;
extern bool wifiFallback;
extern bool disableNetwork;
extern bool forceEnableWebServer;
@@ -21,11 +22,12 @@ extern const uint8_t x509_crt_imported_bundle_bin_start[] asm("_binary_x509_crt_
extern const uint8_t x509_crt_imported_bundle_bin_end[] asm("_binary_x509_crt_bundle_end");
#ifndef NUKI_HUB_UPDATER
NukiNetwork::NukiNetwork(Preferences *preferences, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize)
NukiNetwork::NukiNetwork(Preferences *preferences, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize, ImportExport* importExport)
: _preferences(preferences),
_gpio(gpio),
_buffer(buffer),
_bufferSize(bufferSize)
_bufferSize(bufferSize),
_importExport(importExport)
#else
NukiNetwork::NukiNetwork(Preferences *preferences)
: _preferences(preferences)
@@ -66,7 +68,7 @@ void NukiNetwork::setupDevice()
{
_ipConfiguration = new IPConfiguration(_preferences);
int hardwareDetect = _preferences->getInt(preference_network_hardware, 0);
Log->print(("Hardware detect: "));
Log->print("Hardware detect: ");
Log->println(hardwareDetect);
_firstBootAfterDeviceChange = _preferences->getBool(preference_ntw_reconfigure, false);
@@ -94,13 +96,13 @@ void NukiNetwork::setupDevice()
#ifndef CONFIG_IDF_TARGET_ESP32H2
if(!_firstBootAfterDeviceChange)
{
Log->println(("Failed to connect to network. Wi-Fi fallback is disabled, rebooting."));
Log->println("Failed to connect to network. Wi-Fi fallback is disabled, rebooting.");
wifiFallback = false;
sleep(5);
restartEsp(RestartReason::NetworkDeviceCriticalFailureNoWifiFallback);
}
Log->println(("Switching to Wi-Fi device as fallback."));
Log->println("Switching to Wi-Fi device as fallback.");
_networkDeviceType = NetworkDeviceType::WiFi;
#else
int custEth = _preferences->getInt(preference_network_custom_phy, 0);
@@ -124,7 +126,7 @@ void NukiNetwork::setupDevice()
_device = NetworkDeviceInstantiator::Create(_networkDeviceType, _hostname, _preferences, _ipConfiguration);
Log->print(("Network device: "));
Log->print("Network device: ");
Log->println(_device->deviceName());
#ifndef NUKI_HUB_UPDATER
@@ -223,7 +225,7 @@ void NukiNetwork::initialize()
strcpy(_hostnameArr, _hostname.c_str());
_device->initialize();
Log->print(("Host name: "));
Log->print("Host name: ");
Log->println(_hostname);
}
@@ -273,7 +275,7 @@ void NukiNetwork::initialize()
strcpy(_hostnameArr, _hostname.c_str());
_device->initialize();
Log->print(("Host name: "));
Log->print("Host name: ");
Log->println(_hostname);
String brokerAddr = _preferences->getString(preference_mqtt_broker);
@@ -300,9 +302,9 @@ void NukiNetwork::initialize()
}
}
Log->print(("MQTT Broker: "));
Log->print("MQTT Broker: ");
Log->print(_mqttBrokerAddr);
Log->print((":"));
Log->print(":");
Log->println(_mqttPort);
_device->mqttSetClientId(_hostnameArr);
@@ -314,7 +316,7 @@ void NukiNetwork::initialize()
if(rebGpio)
{
Log->println(("Rebuild MQTT GPIO structure"));
Log->println("Rebuild MQTT GPIO structure");
}
for (const auto &pinEntry: _gpio->pinConfiguration())
{
@@ -412,7 +414,7 @@ bool NukiNetwork::update()
if(_logIp && _device->isConnected() && !_device->localIP().equals("0.0.0.0"))
{
_logIp = false;
Log->print(("IP: "));
Log->print("IP: ");
Log->println(_device->localIP());
_firstDisconnected = true;
}
@@ -470,6 +472,12 @@ bool NukiNetwork::update()
_lastRssi = rssi;
}
}
if(_overwriteNukiHubConfigTS > 0 && espMillis() > _overwriteNukiHubConfigTS)
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_json, "--", true);
_overwriteNukiHubConfigTS = -1;
}
if(_lastMaintenanceTs == 0 || (ts - _lastMaintenanceTs) > 30000)
{
@@ -575,9 +583,9 @@ bool NukiNetwork::update()
buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_pin + std::to_string(pin)).c_str(), mqtt_topic_gpio_state});
publishInt(_lockPath.c_str(), gpioPath, pinState, _retainGpio);
Log->print(("GPIO "));
Log->print("GPIO ");
Log->print(pin);
Log->print((" (Input) --> "));
Log->print(" (Input) --> ");
Log->println(pinState);
}
}
@@ -597,31 +605,31 @@ void NukiNetwork::onMqttDisconnect(const espMqttClientTypes::DisconnectReason &r
switch(reason)
{
case espMqttClientTypes::DisconnectReason::USER_OK:
Log->println(("USER_OK"));
Log->println("USER_OK");
break;
case espMqttClientTypes::DisconnectReason::MQTT_UNACCEPTABLE_PROTOCOL_VERSION:
Log->println(("MQTT_UNACCEPTABLE_PROTOCOL_VERSION"));
Log->println("MQTT_UNACCEPTABLE_PROTOCOL_VERSION");
break;
case espMqttClientTypes::DisconnectReason::MQTT_IDENTIFIER_REJECTED:
Log->println(("MQTT_IDENTIFIER_REJECTED"));
Log->println("MQTT_IDENTIFIER_REJECTED");
break;
case espMqttClientTypes::DisconnectReason::MQTT_SERVER_UNAVAILABLE:
Log->println(("MQTT_SERVER_UNAVAILABLE"));
Log->println("MQTT_SERVER_UNAVAILABLE");
break;
case espMqttClientTypes::DisconnectReason::MQTT_MALFORMED_CREDENTIALS:
Log->println(("MQTT_MALFORMED_CREDENTIALS"));
Log->println("MQTT_MALFORMED_CREDENTIALS");
break;
case espMqttClientTypes::DisconnectReason::MQTT_NOT_AUTHORIZED:
Log->println(("MQTT_NOT_AUTHORIZED"));
Log->println("MQTT_NOT_AUTHORIZED");
break;
case espMqttClientTypes::DisconnectReason::TLS_BAD_FINGERPRINT:
Log->println(("TLS_BAD_FINGERPRINT"));
Log->println("TLS_BAD_FINGERPRINT");
break;
case espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED:
Log->println(("TCP_DISCONNECTED"));
Log->println("TCP_DISCONNECTED");
break;
default:
Log->println(("Unknown"));
Log->println("Unknown");
break;
}
}
@@ -634,7 +642,7 @@ bool NukiNetwork::reconnect()
{
if(strcmp(_mqttBrokerAddr, "") == 0)
{
Log->println(("MQTT Broker not configured, aborting connection attempt."));
Log->println("MQTT Broker not configured, aborting connection attempt.");
_nextReconnect = espMillis() + 5000;
if(_device->isConnected())
@@ -644,17 +652,17 @@ bool NukiNetwork::reconnect()
return false;
}
Log->println(("Attempting MQTT connection"));
Log->println("Attempting MQTT connection");
_connectReplyReceived = false;
if(strlen(_mqttUser) == 0)
{
Log->println(("MQTT: Connecting without credentials"));
Log->println("MQTT: Connecting without credentials");
}
else
{
Log->print(("MQTT: Connecting with user: "));
Log->print("MQTT: Connecting with user: ");
Log->println(_mqttUser);
_device->mqttSetCredentials(_mqttUser, _mqttPass);
}
@@ -677,7 +685,7 @@ bool NukiNetwork::reconnect()
if (_device->mqttConnected())
{
Log->println(("MQTT connected"));
Log->println("MQTT connected");
_mqttConnectedTs = millis();
_mqttConnectionState = 1;
delay(100);
@@ -766,6 +774,18 @@ bool NukiNetwork::reconnect()
subscribe(_maintenancePathPrefix, mqtt_topic_update);
}
if(_preferences->getBool(preference_publish_config, false))
{
initTopic(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_json, "--");
}
if(_preferences->getBool(preference_config_from_mqtt, false) || _preferences->getBool(preference_publish_config, false))
{
initTopic(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--");
subscribe(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action);
initTopic(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "--");
}
initTopic(_maintenancePathPrefix, mqtt_topic_webserver_action, "--");
subscribe(_maintenancePathPrefix, mqtt_topic_webserver_action);
initTopic(_maintenancePathPrefix, mqtt_topic_webserver_state, (_preferences->getBool(preference_webserver_enabled, true) || forceEnableWebServer ? "1" : "0"));
@@ -792,7 +812,7 @@ bool NukiNetwork::reconnect()
}
else
{
Log->print(("MQTT connect failed"));
Log->print("MQTT connect failed");
_mqttConnectionState = 0;
_nextReconnect = espMillis() + 5000;
//_device->mqttDisconnect(true);
@@ -911,14 +931,14 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
if(comparePrefixedPath(topic, mqtt_topic_reset) && strcmp(data, "1") == 0 && !mqttRecentlyConnected())
{
Log->println(("Restart requested via MQTT."));
Log->println("Restart requested via MQTT.");
clearWifiFallback();
delay(200);
restartEsp(RestartReason::RequestedViaMqtt);
}
else if(comparePrefixedPath(topic, mqtt_topic_update) && strcmp(data, "1") == 0 && _preferences->getBool(preference_update_from_mqtt, false) && !mqttRecentlyConnected())
{
Log->println(("Update requested via MQTT."));
Log->println("Update requested via MQTT.");
bool otaManifestSuccess = false;
JsonDocument doc;
@@ -959,13 +979,13 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
{
if(strcmp(NUKI_HUB_VERSION, doc["release"]["fullversion"].as<const char*>()) == 0 && strcmp(NUKI_HUB_BUILD, doc["release"]["build"].as<const char*>()) == 0 && strcmp(NUKI_HUB_DATE, doc["release"]["time"].as<const char*>()) == 0)
{
Log->println(("Nuki Hub is already on the latest release version, OTA update aborted."));
Log->println("Nuki Hub is already on the latest release version, OTA update aborted.");
}
else
{
_preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL);
_preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL);
Log->println(("Updating to latest release version."));
Log->println("Updating to latest release version.");
delay(200);
restartEsp(RestartReason::OTAReboot);
}
@@ -974,13 +994,13 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
{
if(strcmp(NUKI_HUB_VERSION, doc["beta"]["fullversion"].as<const char*>()) == 0 && strcmp(NUKI_HUB_BUILD, doc["beta"]["build"].as<const char*>()) == 0 && strcmp(NUKI_HUB_DATE, doc["beta"]["time"].as<const char*>()) == 0)
{
Log->println(("Nuki Hub is already on the latest beta version, OTA update aborted."));
Log->println("Nuki Hub is already on the latest beta version, OTA update aborted.");
}
else
{
_preferences->putString(preference_ota_updater_url, GITHUB_BETA_UPDATER_BINARY_URL);
_preferences->putString(preference_ota_main_url, GITHUB_BETA_RELEASE_BINARY_URL);
Log->println(("Updating to latest beta version."));
Log->println("Updating to latest beta version.");
delay(200);
restartEsp(RestartReason::OTAReboot);
}
@@ -989,13 +1009,13 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
{
if(strcmp(NUKI_HUB_VERSION, doc["master"]["fullversion"].as<const char*>()) == 0 && strcmp(NUKI_HUB_BUILD, doc["master"]["build"].as<const char*>()) == 0 && strcmp(NUKI_HUB_DATE, doc["master"]["time"].as<const char*>()) == 0)
{
Log->println(("Nuki Hub is already on the latest development version, OTA update aborted."));
Log->println("Nuki Hub is already on the latest development version, OTA update aborted.");
}
else
{
_preferences->putString(preference_ota_updater_url, GITHUB_MASTER_UPDATER_BINARY_URL);
_preferences->putString(preference_ota_main_url, GITHUB_MASTER_RELEASE_BINARY_URL);
Log->println(("Updating to latest developmemt version."));
Log->println("Updating to latest developmemt version.");
delay(200);
restartEsp(RestartReason::OTAReboot);
}
@@ -1004,13 +1024,13 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
{
if(strcmp(NUKI_HUB_VERSION, doc["release"]["fullversion"].as<const char*>()) == 0 && strcmp(NUKI_HUB_BUILD, doc["release"]["build"].as<const char*>()) == 0 && strcmp(NUKI_HUB_DATE, doc["release"]["time"].as<const char*>()) == 0)
{
Log->println(("Nuki Hub is already on the latest release version, OTA update aborted."));
Log->println("Nuki Hub is already on the latest release version, OTA update aborted.");
}
else
{
_preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL);
_preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL);
Log->println(("Updating to latest release version."));
Log->println("Updating to latest release version.");
delay(200);
restartEsp(RestartReason::OTAReboot);
}
@@ -1018,7 +1038,7 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
}
else
{
Log->println(("Failed to retrieve OTA manifest, OTA update aborted."));
Log->println("Failed to retrieve OTA manifest, OTA update aborted.");
}
}
else if(comparePrefixedPath(topic, mqtt_topic_webserver_action) && !mqttRecentlyConnected())
@@ -1035,7 +1055,7 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
{
return;
}
Log->println(("Webserver enabled, restarting."));
Log->println("Webserver enabled, restarting.");
_preferences->putBool(preference_webserver_enabled, true);
}
else if (strcmp(data, "0") == 0)
@@ -1044,20 +1064,184 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns
{
return;
}
Log->println(("Webserver disabled, restarting."));
Log->println("Webserver disabled, restarting.");
_preferences->putBool(preference_webserver_enabled, false);
}
clearWifiFallback();
delay(200);
restartEsp(RestartReason::ReconfigureWebServer);
}
else if(comparePrefixedPath(topic, mqtt_topic_nuki_hub_config_action) && !mqttRecentlyConnected())
{
if(strcmp(data, "") == 0 || strcmp(data, "--") == 0)
{
return;
}
else
{
Log->println("JSON config update received");
JsonDocument doc;
DeserializationError error = deserializeJson(doc, data);
if (error)
{
Log->println("Invalid JSON for import/export");
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"jsonInvalid\"}", false);
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--", true);
}
else
{
if(_preferences->getBool(preference_cred_duo_approval, false))
{
if (!timeSynced)
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"duoTimeNotSynced\"}", false);
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--", true);
return;
}
else if (_importExport->startDuoAuth((char*)"Approve Nuki Hub setting change"))
{
int duoResult = 2;
while (duoResult == 2)
{
duoResult = _importExport->checkDuoApprove();
delay(2000);
esp_task_wdt_reset();
}
if (duoResult != 1)
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"duoApprovalFailed\"}", false);
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--", true);
return;
}
}
}
if(!doc["exportHTTPS"].isNull() && _device->isEncrypted())
{
if(_preferences->getBool(preference_publish_config, false))
{
if(_device->isEncrypted())
{
JsonDocument json;
_importExport->exportHttpsJson(json);
serializeJson(json, _buffer, _bufferSize);
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_json, _buffer, false);
if (doc["exportHTTPS"].as<int>() > 0)
{
_overwriteNukiHubConfigTS = espMillis() + (doc["exportHTTPS"].as<int>() * 1000);
}
}
else
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"mqttExportNotEncrypted\"}", false);
}
}
else
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"mqttExportNotEnabled\"}", false);
}
}
else if(!doc["exportMQTTS"].isNull())
{
if(_preferences->getBool(preference_publish_config, false))
{
if(_device->isEncrypted())
{
JsonDocument json;
_importExport->exportMqttsJson(json);
serializeJson(json, _buffer, _bufferSize);
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_json, _buffer, false);
if (doc["exportMQTTS"].as<int>() > 0)
{
_overwriteNukiHubConfigTS = espMillis() + (doc["exportMQTTS"].as<int>() * 1000);
}
}
else
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"mqttExportNotEncrypted\"}", false);
}
}
else
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"mqttExportNotEnabled\"}", false);
}
}
else if(!doc["exportNH"].isNull())
{
if(_preferences->getBool(preference_publish_config, false))
{
bool redacted = false;
if(!doc["redacted"].isNull())
{
if(_device->isEncrypted())
{
redacted = true;
}
else
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"mqttExportNotEncrypted\"}", false);
}
}
bool pairing = false;
if(!doc["pairing"].isNull())
{
if(_device->isEncrypted())
{
pairing = true;
}
else
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"mqttExportNotEncrypted\"}", false);
}
}
JsonDocument json;
_importExport->exportNukiHubJson(json, redacted, pairing, _preferences->getBool(preference_lock_enabled, true), _preferences->getBool(preference_opener_enabled, false));
serializeJson(json, _buffer, _bufferSize);
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_json, _buffer, false);
if (doc["exportNH"].as<int>() > 0)
{
_overwriteNukiHubConfigTS = espMillis() + (doc["exportNH"].as<int>() * 1000);
}
}
else
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"mqttExportNotEnabled\"}", false);
}
}
else
{
if(_preferences->getBool(preference_config_from_mqtt, false))
{
JsonDocument json;
json = _importExport->importJson(doc);
serializeJson(json, _buffer, _bufferSize);
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_json, _buffer, false);
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--", true);
delay(200);
restartEsp(RestartReason::ConfigurationUpdated);
}
else
{
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action_command_result, "{\"error\": \"mqttImportNotEnabled\"}", false);
}
}
publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--", true);
}
}
}
}
void NukiNetwork::parseGpioTopics(const espMqttClientTypes::MessageProperties &properties, const char *topic, const uint8_t *payload, size_t& len, size_t& index, size_t& total)
{
char gpioPath[250];
buildMqttPath(gpioPath, {_lockPath.c_str(), mqtt_topic_gpio_prefix, mqtt_topic_gpio_pin});
// /nuki_t/gpio/pin_17/state
size_t gpioLen = strlen(gpioPath);
if(strncmp(gpioPath, topic, gpioLen) == 0)
{
@@ -1073,9 +1257,9 @@ void NukiNetwork::parseGpioTopics(const espMqttClientTypes::MessageProperties &p
if(_gpio->getPinRole(pin) == PinRole::GeneralOutput)
{
const uint8_t pinState = strcmp((const char*)payload, "1") == 0 ? HIGH : LOW;
Log->print(("GPIO "));
Log->print("GPIO ");
Log->print(pin);
Log->print((" (Output) --> "));
Log->print(" (Output) --> ");
Log->println(pinState);
digitalWrite(pin, pinState);
}
@@ -1177,7 +1361,7 @@ void NukiNetwork::removeTopic(const String& mqttPath, const String& mqttTopic)
publish(path.c_str(), "", true);
#ifdef DEBUG_NUKIHUB
Log->print(("Removing MQTT topic: "));
Log->print("Removing MQTT topic: ");
Log->println(path.c_str());
#endif
}

View File

@@ -16,6 +16,7 @@
#include <ArduinoJson.h>
#include "NukiConstants.h"
#include "HomeAssistantDiscovery.h"
#include "ImportExport.h"
#endif
class NukiNetwork
@@ -42,7 +43,7 @@ public:
#ifdef NUKI_HUB_UPDATER
explicit NukiNetwork(Preferences* preferences);
#else
explicit NukiNetwork(Preferences* preferences, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize);
explicit NukiNetwork(Preferences* preferences, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize, ImportExport* importExport);
void registerMqttReceiver(MqttReceiver* receiver);
void disableAutoRestarts(); // disable on OTA start
@@ -128,13 +129,14 @@ private:
String _lockPath;
HomeAssistantDiscovery* _hadiscovery = nullptr;
ImportExport* _importExport;
Gpio* _gpio;
int _mqttConnectionState = 0;
int _mqttConnectCounter = 0;
int _mqttPort = 1883;
long _mqttConnectedTs = -1;
long _overwriteNukiHubConfigTS = -1;
bool _connectReplyReceived = false;
bool _firstDisconnected = true;

View File

@@ -237,7 +237,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const
return;
}
Log->print(("Lock action received: "));
Log->print("Lock action received: ");
Log->println(data);
LockActionResult lockActionResult = LockActionResult::Failed;
if(_lockActionReceivedCallback != NULL)

View File

@@ -175,7 +175,7 @@ void NukiNetworkOpener::onMqttDataReceived(const char* topic, byte* payload, con
return;
}
Log->print(("Opener action received: "));
Log->print("Opener action received: ");
Log->println(data);
LockActionResult lockActionResult = LockActionResult::Failed;
if(_lockActionReceivedCallback != NULL)
@@ -667,12 +667,12 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::Log
{
if((log.data[0] & 3) == 0)
{
Log->println(("Nuki opener: Ring detected (Locked)"));
Log->println("Nuki opener: Ring detected (Locked)");
publishRing(true);
}
else
{
Log->println(("Nuki opener: Ring detected (Open)"));
Log->println("Nuki opener: Ring detected (Open)");
publishRing(false);
}
}

View File

@@ -86,14 +86,14 @@ void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value
memset(&str, 0, sizeof(str));
Log->println("Official Nuki change received");
Log->print(("Topic: "));
Log->print("Topic: ");
Log->println(topic);
Log->print(("Value: "));
Log->print("Value: ");
Log->println(value);
if(strcmp(topic, mqtt_topic_official_connected) == 0)
{
Log->print(("Connected: "));
Log->print("Connected: ");
Log->println((strcmp(value, "true") == 0 ? 1 : 0));
offConnected = (strcmp(value, "true") == 0 ? 1 : 0);
_publisher->publishBool(mqtt_topic_hybrid_state, offConnected, true);
@@ -102,12 +102,12 @@ void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value
{
offState = atoi(value);
_statusUpdated = true;
Log->println(("Lock: Updating status on Hybrid state change"));
Log->println("Lock: Updating status on Hybrid state change");
_publisher->publishBool(mqtt_topic_hybrid_state, offConnected, true);
NukiLock::lockstateToString((NukiLock::LockState)offState, str);
_publisher->publishString(mqtt_topic_lock_state, str, true);
Log->print(("Lockstate: "));
Log->print("Lockstate: ");
Log->println(str);
_offStateToPublish = (NukiLock::LockState)offState;
@@ -117,11 +117,11 @@ void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value
{
offDoorsensorState = atoi(value);
_statusUpdated = true;
Log->println(("Lock: Updating status on Hybrid door sensor state change"));
Log->println("Lock: Updating status on Hybrid door sensor state change");
_publisher->publishBool(mqtt_topic_lock_status_updated, _statusUpdated, true);
NukiLock::doorSensorStateToString((NukiLock::DoorSensorState)offDoorsensorState, str);
Log->print(("Doorsensor state: "));
Log->print("Doorsensor state: ");
Log->println(str);
_publisher->publishString(mqtt_topic_lock_door_sensor_state, str, true);
@@ -130,7 +130,7 @@ void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value
{
offCritical = (strcmp(value, "true") == 0 ? 1 : 0);
Log->print(("Battery critical: "));
Log->print("Battery critical: ");
Log->println(offCritical);
if(!_disableNonJSON)
@@ -143,7 +143,7 @@ void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value
{
offCharging = (strcmp(value, "true") == 0 ? 1 : 0);
Log->print(("Battery charging: "));
Log->print("Battery charging: ");
Log->println(offCharging);
if(!_disableNonJSON)
@@ -156,7 +156,7 @@ void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value
{
offChargeState = atoi(value);
Log->print(("Battery level: "));
Log->print("Battery level: ");
Log->println(offChargeState);
if(!_disableNonJSON)

View File

@@ -12,14 +12,16 @@
NukiOpenerWrapper* nukiOpenerInst;
Preferences* nukiOpenerPreferences = nullptr;
NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkOpener* network, Gpio* gpio, Preferences* preferences)
NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkOpener* network, Gpio* gpio, Preferences* preferences, char* buffer, size_t bufferSize)
: _deviceName(deviceName),
_deviceId(deviceId),
_nukiOpener(deviceName, _deviceId->get()),
_bleScanner(scanner),
_network(network),
_gpio(gpio),
_preferences(preferences)
_preferences(preferences),
_buffer(buffer),
_bufferSize(bufferSize)
{
Log->print("Device id opener: ");
Log->println(_deviceId->get());
@@ -81,7 +83,7 @@ void NukiOpenerWrapper::readSettings()
#else
if(pwrLvl >= 20)
{
powerLevel = ESP_PWR_LVL_P20;
powerLevel = ESP_PWR_LVL_P20;
}
else if(pwrLvl >= 18)
{
@@ -197,11 +199,11 @@ void NukiOpenerWrapper::readSettings()
_preferences->putInt(preference_restart_ble_beacon_lost, _restartBeaconTimeout);
}
Log->print(("Opener state interval: "));
Log->print("Opener state interval: ");
Log->print(_intervalLockstate);
Log->print((" | Battery interval: "));
Log->print(" | Battery interval: ");
Log->print(_intervalBattery);
Log->print((" | Publish auth data: "));
Log->print(" | Publish auth data: ");
Log->println(_publishAuthData ? "yes" : "no");
if(!_publishAuthData)
@@ -218,7 +220,7 @@ void NukiOpenerWrapper::update()
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
if(!_paired)
{
Log->println(("Nuki opener start pairing"));
Log->println("Nuki opener start pairing");
_network->publishBleAddress("");
Nuki::AuthorizationIdType idType = _preferences->getBool(preference_register_opener_as_app) ?
@@ -227,7 +229,7 @@ void NukiOpenerWrapper::update()
if(_nukiOpener.pairNuki(idType) == NukiOpener::PairingResult::Success)
{
Log->println(("Nuki opener paired"));
Log->println("Nuki opener paired");
_paired = true;
_network->publishBleAddress(_nukiOpener.getBleAddress().toString());
}
@@ -270,14 +272,14 @@ void NukiOpenerWrapper::update()
_network->publishCommandResult(resultStr);
Log->print(("Opener action result: "));
Log->print("Opener action result: ");
Log->println(resultStr);
if(cmdResult != Nuki::CmdResult::Success)
{
Log->print(("Opener: Last command failed, retrying after "));
Log->print("Opener: Last command failed, retrying after ");
Log->print(_retryDelay);
Log->print((" milliseconds. Retry "));
Log->print(" milliseconds. Retry ");
Log->print(retryCount + 1);
Log->print(" of ");
Log->println(_nrOfRetries);
@@ -297,7 +299,7 @@ void NukiOpenerWrapper::update()
_network->publishRetry("--");
retryCount = 0;
_statusUpdated = true;
Log->println(("Opener: updating status after action"));
Log->println("Opener: updating status after action");
_statusUpdatedTs = ts;
if(_intervalLockstate > 10)
{
@@ -306,7 +308,7 @@ void NukiOpenerWrapper::update()
}
else
{
Log->println(("Opener: Maximum number of retries exceeded, aborting."));
Log->println("Opener: Maximum number of retries exceeded, aborting.");
_network->publishRetry("failed");
retryCount = 0;
_nextLockAction = (NukiOpener::LockAction) 0xff;
@@ -432,11 +434,6 @@ void NukiOpenerWrapper::deactivateCM()
_nextLockAction = NukiOpener::LockAction::DeactivateCM;
}
bool NukiOpenerWrapper::isPinSet()
{
return _nukiOpener.getSecurityPincode() != 0;
}
bool NukiOpenerWrapper::isPinValid()
{
return _preferences->getInt(preference_opener_pin_status, 4) == 1;
@@ -475,7 +472,7 @@ bool NukiOpenerWrapper::updateKeyTurnerState()
while(result != Nuki::CmdResult::Success && retryCount < _nrOfRetries + 1)
{
Log->print(("Result (attempt "));
Log->print("Result (attempt ");
Log->print(retryCount + 1);
Log->print("): ");
result =_nukiOpener.requestOpenerState(&_keyTurnerState);
@@ -494,7 +491,7 @@ bool NukiOpenerWrapper::updateKeyTurnerState()
postponeBleWatchdog();
if(_retryLockstateCount < _nrOfRetries + 1)
{
Log->print(("Query opener state retrying in "));
Log->print("Query opener state retrying in ");
Log->print(_retryDelay);
Log->println("ms");
_nextLockStateUpdateTs = espMillis() + _retryDelay;
@@ -517,7 +514,7 @@ bool NukiOpenerWrapper::updateKeyTurnerState()
_lastKeyTurnerState.lockState == NukiOpener::LockState::Locked &&
_lastKeyTurnerState.nukiState == _keyTurnerState.nukiState)
{
Log->println(("Nuki opener: Ring detected (Locked)"));
Log->println("Nuki opener: Ring detected (Locked)");
_network->publishRing(true);
}
else
@@ -527,17 +524,17 @@ bool NukiOpenerWrapper::updateKeyTurnerState()
_keyTurnerState.lockState == NukiOpener::LockState::Open &&
_keyTurnerState.trigger == NukiOpener::Trigger::Manual)
{
Log->println(("Nuki opener: Ring detected (Open)"));
Log->println("Nuki opener: Ring detected (Open)");
_network->publishRing(false);
}
if(_publishAuthData)
{
Log->println(("Publishing auth data"));
Log->println("Publishing auth data");
updateAuthData(false);
Log->println(("Done publishing auth data"));
Log->println("Done publishing auth data");
}
if(_keyTurnerState.lockState == NukiOpener::LockState::Undefined)
{
if (_nextLockStateUpdateTs > espMillis() + 60000)
@@ -552,12 +549,12 @@ bool NukiOpenerWrapper::updateKeyTurnerState()
if((_keyTurnerState.lockState == NukiOpener::LockState::Open || _keyTurnerState.lockState == NukiOpener::LockState::Opening) && espMillis() < _statusUpdatedTs + 10000)
{
updateStatus = true;
Log->println(("Opener: Keep updating status on intermediate lock state"));
Log->println("Opener: Keep updating status on intermediate lock state");
}
if(_keyTurnerState.nukiState == NukiOpener::State::ContinuousMode)
{
Log->println(("Continuous Mode"));
Log->println("Continuous Mode");
}
char lockStateStr[20];
@@ -566,7 +563,7 @@ bool NukiOpenerWrapper::updateKeyTurnerState()
}
postponeBleWatchdog();
Log->println(("Done querying opener state"));
Log->println("Done querying opener state");
return updateStatus;
}
@@ -577,7 +574,7 @@ void NukiOpenerWrapper::updateBatteryState()
while(retryCount < _nrOfRetries + 1)
{
Log->print(("Querying opener battery state: "));
Log->print("Querying opener battery state: ");
result = _nukiOpener.requestBatteryReport(&_batteryReport);
delay(250);
if(result != Nuki::CmdResult::Success)
@@ -596,7 +593,7 @@ void NukiOpenerWrapper::updateBatteryState()
_network->publishBatteryReport(_batteryReport);
}
postponeBleWatchdog();
Log->println(("Done querying opener battery state"));
Log->println("Done querying opener battery state");
}
void NukiOpenerWrapper::updateConfig()
@@ -611,7 +608,7 @@ void NukiOpenerWrapper::updateConfig()
{
char uidString[20];
itoa(_nukiConfig.nukiId, uidString, 16);
Log->print(("Saving Opener Nuki ID to preferences ("));
Log->print("Saving Opener Nuki ID to preferences (");
Log->print(_nukiConfig.nukiId);
Log->print(" / ");
Log->print(uidString);
@@ -640,61 +637,49 @@ void NukiOpenerWrapper::updateConfig()
const int pinStatus = _preferences->getInt(preference_opener_pin_status, 4);
if(isPinSet())
Nuki::CmdResult result = (Nuki::CmdResult)-1;
int retryCount = 0;
while(retryCount < _nrOfRetries + 1)
{
Nuki::CmdResult result = (Nuki::CmdResult)-1;
int retryCount = 0;
Log->println(("Nuki opener PIN is set"));
while(retryCount < _nrOfRetries + 1)
{
result = _nukiOpener.verifySecurityPin();
if(result != Nuki::CmdResult::Success)
{
++retryCount;
}
else
{
break;
}
}
result = _nukiOpener.verifySecurityPin();
if(result != Nuki::CmdResult::Success)
{
Log->println(("Nuki opener PIN is invalid"));
if(pinStatus != 2)
{
_preferences->putInt(preference_opener_pin_status, 2);
}
++retryCount;
}
else
{
Log->println(("Nuki opener PIN is valid"));
if(pinStatus != 1)
{
_preferences->putInt(preference_opener_pin_status, 1);
}
break;
}
}
if(result != Nuki::CmdResult::Success)
{
Log->println("Nuki opener PIN is invalid or not set");
if(pinStatus != 2)
{
_preferences->putInt(preference_opener_pin_status, 2);
}
}
else
{
Log->println(("Nuki opener PIN is not set"));
if(pinStatus != 0)
Log->println("Nuki opener PIN is valid");
if(pinStatus != 1)
{
_preferences->putInt(preference_opener_pin_status, 0);
_preferences->putInt(preference_opener_pin_status, 1);
}
}
}
else
{
Log->println(("Invalid/Unexpected opener config received, ID does not matched saved ID"));
Log->println("Invalid/Unexpected opener config received, ID does not matched saved ID");
expectedConfig = false;
}
}
else
{
Log->println(("Invalid/Unexpected opener config received, Config is not valid"));
Log->println("Invalid/Unexpected opener config received, Config is not valid");
expectedConfig = false;
}
@@ -711,7 +696,7 @@ void NukiOpenerWrapper::updateConfig()
}
else
{
Log->println(("Invalid/Unexpected opener advanced config received, Advanced config is not valid"));
Log->println("Invalid/Unexpected opener advanced config received, Advanced config is not valid");
expectedConfig = false;
}
}
@@ -719,12 +704,12 @@ void NukiOpenerWrapper::updateConfig()
if(expectedConfig && _nukiConfigValid && _nukiAdvancedConfigValid)
{
_retryConfigCount = 0;
Log->println(("Done retrieving opener config and advanced config"));
Log->println("Done retrieving opener config and advanced config");
}
else
{
++_retryConfigCount;
Log->println(("Invalid/Unexpected opener config and/or advanced config received, retrying in 10 seconds"));
Log->println("Invalid/Unexpected opener config and/or advanced config received, retrying in 10 seconds");
int64_t ts = espMillis();
_nextConfigUpdateTs = ts + 10000;
}
@@ -734,7 +719,7 @@ void NukiOpenerWrapper::updateAuthData(bool retrieved)
{
if(!isPinValid())
{
Log->println(("No valid PIN set"));
Log->println("No valid PIN set");
return;
}
@@ -820,7 +805,7 @@ void NukiOpenerWrapper::updateKeypad(bool retrieved)
if(!isPinValid())
{
Log->println(("No valid Nuki Opener PIN set"));
Log->println("No valid Nuki Opener PIN set");
return;
}
@@ -900,7 +885,7 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
if(!isPinValid())
{
Log->println(("No valid Nuki Opener PIN set"));
Log->println("No valid Nuki Opener PIN set");
return;
}
@@ -972,7 +957,7 @@ void NukiOpenerWrapper::updateAuth(bool retrieved)
{
if(!isPinValid())
{
Log->println(("No valid Nuki Lock PIN set"));
Log->println("No valid Nuki Lock PIN set");
return;
}
@@ -1558,23 +1543,21 @@ Nuki::BatteryType NukiOpenerWrapper::batteryTypeToEnum(const char* str)
void NukiOpenerWrapper::onConfigUpdateReceived(const char *value)
{
JsonDocument jsonResult;
char _resbuf[2048];
if(!_nukiConfigValid)
{
jsonResult["general"] = "configNotReady";
serializeJson(jsonResult, _resbuf, sizeof(_resbuf));
_network->publishConfigCommandResult(_resbuf);
serializeJson(jsonResult, _buffer, _bufferSize);
_network->publishConfigCommandResult(_buffer);
return;
}
if(!isPinValid())
{
jsonResult["general"] = "noValidPinSet";
serializeJson(jsonResult, _resbuf, sizeof(_resbuf));
_network->publishConfigCommandResult(_resbuf);
serializeJson(jsonResult, _buffer, _bufferSize);
_network->publishConfigCommandResult(_buffer);
return;
}
@@ -1584,8 +1567,8 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value)
if(jsonError)
{
jsonResult["general"] = "invalidJson";
serializeJson(jsonResult, _resbuf, sizeof(_resbuf));
_network->publishConfigCommandResult(_resbuf);
serializeJson(jsonResult, _buffer, _bufferSize);
_network->publishConfigCommandResult(_buffer);
return;
}
@@ -2437,8 +2420,8 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value)
_nextConfigUpdateTs = espMillis() + 300;
serializeJson(jsonResult, _resbuf, sizeof(_resbuf));
_network->publishConfigCommandResult(_resbuf);
serializeJson(jsonResult, _buffer, _bufferSize);
_network->publishConfigCommandResult(_buffer);
return;
}
@@ -4198,7 +4181,7 @@ void NukiOpenerWrapper::updateTime()
{
if(!isPinValid())
{
Log->println(("No valid PIN set"));
Log->println("No valid PIN set");
return;
}
@@ -4209,7 +4192,7 @@ void NukiOpenerWrapper::updateTime()
if (int(tm.tm_year + 1900) < int(2025))
{
Log->println(("NTP Time not valid, not updating Nuki device"));
Log->println("NTP Time not valid, not updating Nuki device");
return;
}

View File

@@ -11,7 +11,7 @@
class NukiOpenerWrapper : public NukiOpener::SmartlockEventHandler
{
public:
NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkOpener* network, Gpio* gpio, Preferences* preferences);
NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkOpener* network, Gpio* gpio, Preferences* preferences, char* buffer, size_t bufferSize);
virtual ~NukiOpenerWrapper();
void initialize();
@@ -25,7 +25,6 @@ public:
void deactivateRTO();
void deactivateCM();
bool isPinSet();
bool isPinValid();
void setPin(const uint16_t pin);
uint16_t getPin();
@@ -159,4 +158,7 @@ private:
std::string _firmwareVersion = "";
std::string _hardwareVersion = "";
NukiOpener::LockAction _nextLockAction = (NukiOpener::LockAction)0xff;
char* _buffer;
const size_t _bufferSize;
};

View File

@@ -11,7 +11,7 @@
NukiWrapper* nukiInst = nullptr;
NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, NukiOfficial* nukiOfficial, Gpio* gpio, Preferences* preferences)
NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, NukiOfficial* nukiOfficial, Gpio* gpio, Preferences* preferences, char* buffer, size_t bufferSize)
: _deviceName(deviceName),
_deviceId(deviceId),
_bleScanner(scanner),
@@ -19,7 +19,9 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId,
_network(network),
_nukiOfficial(nukiOfficial),
_gpio(gpio),
_preferences(preferences)
_preferences(preferences),
_buffer(buffer),
_bufferSize(bufferSize)
{
Log->print("Device id lock: ");
@@ -233,8 +235,8 @@ void NukiWrapper::update(bool reboot)
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
if(!_paired)
{
Log->println(("Nuki lock start pairing"));
_preferences->getBool(preference_register_as_app) ? Log->println(("Pairing as app")) : Log->println(("Pairing as bridge"));
Log->println("Nuki lock start pairing");
_preferences->getBool(preference_register_as_app) ? Log->println("Pairing as app") : Log->println("Pairing as bridge");
_network->publishBleAddress("");
Nuki::AuthorizationIdType idType = _preferences->getBool(preference_register_as_app) ?
@@ -243,7 +245,7 @@ void NukiWrapper::update(bool reboot)
if(_nukiLock.pairNuki(idType) == Nuki::PairingResult::Success)
{
Log->println(("Nuki paired"));
Log->println("Nuki paired");
_paired = true;
_network->publishBleAddress(_nukiLock.getBleAddress().toString());
}
@@ -295,9 +297,9 @@ void NukiWrapper::update(bool reboot)
if(cmdResult != Nuki::CmdResult::Success)
{
Log->print(("Lock: Last command failed, retrying after "));
Log->print("Lock: Last command failed, retrying after ");
Log->print(_retryDelay);
Log->print((" milliseconds. Retry "));
Log->print(" milliseconds. Retry ");
Log->print(retryCount + 1);
Log->print(" of ");
Log->println(_nrOfRetries);
@@ -320,7 +322,7 @@ void NukiWrapper::update(bool reboot)
{
_statusUpdated = true;
}
Log->println(("Lock: updating status after action"));
Log->println("Lock: updating status after action");
_statusUpdatedTs = ts;
if(_intervalLockstate > 10)
{
@@ -329,7 +331,7 @@ void NukiWrapper::update(bool reboot)
}
else
{
Log->println(("Lock: Maximum number of retries exceeded, aborting."));
Log->println("Lock: Maximum number of retries exceeded, aborting.");
_network->publishRetry("failed");
retryCount = 0;
_nextLockAction = (NukiLock::LockAction) 0xff;
@@ -450,18 +452,6 @@ void NukiWrapper::lockngounlatch()
_nextLockAction = NukiLock::LockAction::LockNgoUnlatch;
}
bool NukiWrapper::isPinSet()
{
if (_isUltra)
{
return _nukiLock.getUltraPincode() != 0;
}
else
{
return _nukiLock.getSecurityPincode() != 0;
}
}
bool NukiWrapper::isPinValid()
{
return _preferences->getInt(preference_lock_pin_status, 4) == 1;
@@ -508,11 +498,11 @@ bool NukiWrapper::updateKeyTurnerState()
Nuki::CmdResult result = (Nuki::CmdResult)-1;
int retryCount = 0;
Log->println(("Querying lock state"));
Log->println("Querying lock state");
while(result != Nuki::CmdResult::Success && retryCount < _nrOfRetries + 1)
{
Log->print(("Result (attempt "));
Log->print("Result (attempt ");
Log->print(retryCount + 1);
Log->print(("): "));
result =_nukiLock.requestKeyTurnerState(&_keyTurnerState);
@@ -531,7 +521,7 @@ bool NukiWrapper::updateKeyTurnerState()
postponeBleWatchdog();
if(_retryLockstateCount < _nrOfRetries + 1)
{
Log->print(("Query lock state retrying in "));
Log->print("Query lock state retrying in ");
Log->print(_retryDelay);
Log->println("ms");
_nextLockStateUpdateTs = espMillis() + _retryDelay;
@@ -557,9 +547,9 @@ bool NukiWrapper::updateKeyTurnerState()
{
if(_publishAuthData && (lockState == NukiLock::LockState::Locked || lockState == NukiLock::LockState::Unlocked))
{
Log->println(("Publishing auth data"));
Log->println("Publishing auth data");
updateAuthData(false);
Log->println(("Done publishing auth data"));
Log->println("Done publishing auth data");
}
updateGpioOutputs();
@@ -567,7 +557,7 @@ bool NukiWrapper::updateKeyTurnerState()
else if(!_nukiOfficial->getOffConnected() && espMillis() < _statusUpdatedTs + 10000)
{
updateStatus = true;
Log->println(("Lock: Keep updating status on intermediate lock state"));
Log->println("Lock: Keep updating status on intermediate lock state");
}
else if(lockState == NukiLock::LockState::Undefined)
{
@@ -583,7 +573,7 @@ bool NukiWrapper::updateKeyTurnerState()
Log->println(lockStateStr);
postponeBleWatchdog();
Log->println(("Done querying lock state"));
Log->println("Done querying lock state");
return updateStatus;
}
@@ -617,7 +607,7 @@ void NukiWrapper::updateBatteryState()
_network->publishBatteryReport(_batteryReport);
}
postponeBleWatchdog();
Log->println(("Done querying lock battery state"));
Log->println("Done querying lock battery state");
}
void NukiWrapper::updateConfig()
@@ -632,7 +622,7 @@ void NukiWrapper::updateConfig()
{
char uidString[20];
itoa(_nukiConfig.nukiId, uidString, 16);
Log->print(("Saving Lock Nuki ID to preferences ("));
Log->print("Saving Lock Nuki ID to preferences (");
Log->print(_nukiConfig.nukiId);
Log->print(" / ");
Log->print(uidString);
@@ -660,60 +650,48 @@ void NukiWrapper::updateConfig()
const int pinStatus = _preferences->getInt(preference_lock_pin_status, 4);
if(isPinSet())
Nuki::CmdResult result = (Nuki::CmdResult)-1;
int retryCount = 0;
while(retryCount < _nrOfRetries + 1)
{
Nuki::CmdResult result = (Nuki::CmdResult)-1;
int retryCount = 0;
Log->println(("Nuki Lock PIN is set"));
while(retryCount < _nrOfRetries + 1)
{
result = _nukiLock.verifySecurityPin();
if(result != Nuki::CmdResult::Success)
{
++retryCount;
}
else
{
break;
}
}
result = _nukiLock.verifySecurityPin();
if(result != Nuki::CmdResult::Success)
{
Log->println(("Nuki Lock PIN is invalid"));
if(pinStatus != 2)
{
_preferences->putInt(preference_lock_pin_status, 2);
}
++retryCount;
}
else
{
Log->println(("Nuki Lock PIN is valid"));
if(pinStatus != 1)
{
_preferences->putInt(preference_lock_pin_status, 1);
}
break;
}
}
if(result != Nuki::CmdResult::Success)
{
Log->println("Nuki Lock PIN is invalid or not set");
if(pinStatus != 2)
{
_preferences->putInt(preference_lock_pin_status, 2);
}
}
else
{
Log->println(("Nuki Lock PIN is not set"));
if(pinStatus != 0)
Log->println("Nuki Lock PIN is valid");
if(pinStatus != 1)
{
_preferences->putInt(preference_lock_pin_status, 0);
_preferences->putInt(preference_lock_pin_status, 1);
}
}
}
else
{
Log->println(("Invalid/Unexpected lock config received, ID does not matched saved ID"));
Log->println("Invalid/Unexpected lock config received, ID does not matched saved ID");
expectedConfig = false;
}
}
else
{
Log->println(("Invalid/Unexpected lock config received, Config is not valid"));
Log->println("Invalid/Unexpected lock config received, Config is not valid");
expectedConfig = false;
}
@@ -730,7 +708,7 @@ void NukiWrapper::updateConfig()
}
else
{
Log->println(("Invalid/Unexpected lock advanced config received, Advanced config is not valid"));
Log->println("Invalid/Unexpected lock advanced config received, Advanced config is not valid");
expectedConfig = false;
}
}
@@ -738,12 +716,12 @@ void NukiWrapper::updateConfig()
if(expectedConfig && _nukiConfigValid && _nukiAdvancedConfigValid)
{
_retryConfigCount = 0;
Log->println(("Done retrieving lock config and advanced config"));
Log->println("Done retrieving lock config and advanced config");
}
else
{
++_retryConfigCount;
Log->println(("Invalid/Unexpected lock config and/or advanced config received, retrying in 10 seconds"));
Log->println("Invalid/Unexpected lock config and/or advanced config received, retrying in 10 seconds");
int64_t ts = espMillis();
_nextConfigUpdateTs = ts + 10000;
}
@@ -753,7 +731,7 @@ void NukiWrapper::updateAuthData(bool retrieved)
{
if(!isPinValid())
{
Log->println(("No valid Nuki Lock PIN set"));
Log->println("No valid Nuki Lock PIN set");
return;
}
@@ -837,7 +815,7 @@ void NukiWrapper::updateKeypad(bool retrieved)
if(!isPinValid())
{
Log->println(("No valid Nuki Lock PIN set"));
Log->println("No valid Nuki Lock PIN set");
return;
}
@@ -916,7 +894,7 @@ void NukiWrapper::updateTimeControl(bool retrieved)
if(!isPinValid())
{
Log->println(("No valid Nuki Lock PIN set"));
Log->println("No valid Nuki Lock PIN set");
return;
}
@@ -987,7 +965,7 @@ void NukiWrapper::updateAuth(bool retrieved)
{
if(!isPinValid())
{
Log->println(("No valid Nuki Lock PIN set"));
Log->println("No valid Nuki Lock PIN set");
return;
}
@@ -1495,21 +1473,20 @@ void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value)
void NukiWrapper::onConfigUpdateReceived(const char *value)
{
JsonDocument jsonResult;
char _resbuf[2048];
if(!_nukiConfigValid)
{
jsonResult["general"] = "configNotReady";
serializeJson(jsonResult, _resbuf, sizeof(_resbuf));
_network->publishConfigCommandResult(_resbuf);
serializeJson(jsonResult, _buffer, _bufferSize);
_network->publishConfigCommandResult(_buffer);
return;
}
if(!isPinValid())
{
jsonResult["general"] = "noValidPinSet";
serializeJson(jsonResult, _resbuf, sizeof(_resbuf));
_network->publishConfigCommandResult(_resbuf);
serializeJson(jsonResult, _buffer, _bufferSize);
_network->publishConfigCommandResult(_buffer);
return;
}
@@ -1519,15 +1496,11 @@ void NukiWrapper::onConfigUpdateReceived(const char *value)
if(jsonError)
{
jsonResult["general"] = "invalidJson";
serializeJson(jsonResult, _resbuf, sizeof(_resbuf));
_network->publishConfigCommandResult(_resbuf);
serializeJson(jsonResult, _buffer, _bufferSize);
_network->publishConfigCommandResult(_buffer);
return;
}
Log->println(value);
serializeJson(json, _resbuf, sizeof(_resbuf));
Log->println(_resbuf);
Nuki::CmdResult cmdResult;
const char *basicKeys[16] = {"name", "latitude", "longitude", "autoUnlatch", "pairingEnabled", "buttonEnabled", "ledEnabled", "ledBrightness", "timeZoneOffset", "dstMode", "fobAction1", "fobAction2", "fobAction3", "singleLock", "advertisingMode", "timeZone"};
const char *advancedKeys[25] = {"unlockedPositionOffsetDegrees", "lockedPositionOffsetDegrees", "singleLockedPositionOffsetDegrees", "unlockedToLockedTransitionOffsetDegrees", "lockNgoTimeout", "singleButtonPressAction", "doubleButtonPressAction", "detachedCylinder", "batteryType", "automaticBatteryTypeDetection", "unlatchDuration", "autoLockTimeOut", "autoUnLockDisabled", "nightModeEnabled", "nightModeStartTime", "nightModeEndTime", "nightModeAutoLockEnabled", "nightModeAutoUnlockDisabled", "nightModeImmediateLockOnStart", "autoLockEnabled", "immediateAutoLockEnabled", "autoUpdateEnabled", "rebootNuki", "motorSpeed", "enableSlowSpeedDuringNightMode"};
@@ -2500,8 +2473,8 @@ void NukiWrapper::onConfigUpdateReceived(const char *value)
_nextConfigUpdateTs = espMillis() + 300;
serializeJson(jsonResult, _resbuf, sizeof(_resbuf));
_network->publishConfigCommandResult(_resbuf);
serializeJson(jsonResult, _buffer, _bufferSize);
_network->publishConfigCommandResult(_buffer);
return;
}
@@ -4325,7 +4298,7 @@ void NukiWrapper::updateTime()
{
if(!isPinValid())
{
Log->println(("No valid PIN set"));
Log->println("No valid PIN set");
return;
}
@@ -4336,7 +4309,7 @@ void NukiWrapper::updateTime()
if (int(tm.tm_year + 1900) < int(2025))
{
Log->println(("NTP Time not valid, not updating Nuki device"));
Log->println("NTP Time not valid, not updating Nuki device");
return;
}

View File

@@ -14,7 +14,7 @@
class NukiWrapper : public Nuki::SmartlockEventHandler
{
public:
NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, NukiOfficial* nukiOfficial, Gpio* gpio, Preferences* preferences);
NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, NukiOfficial* nukiOfficial, Gpio* gpio, Preferences* preferences, char* buffer, size_t bufferSize);
virtual ~NukiWrapper();
void initialize();
@@ -27,7 +27,6 @@ public:
void lockngo();
void lockngounlatch();
bool isPinSet();
bool isPinValid();
void setPin(const uint16_t pin);
void setUltraPin(const uint32_t pin);
@@ -168,4 +167,7 @@ private:
std::string _firmwareVersion = "";
std::string _hardwareVersion = "";
volatile NukiLock::LockAction _nextLockAction = (NukiLock::LockAction)0xff;
char* _buffer;
const size_t _bufferSize;
};

View File

@@ -91,6 +91,8 @@
#define preference_cred_bypass_boot_btn_enabled (char*)"bypassBtBtn"
#define preference_cred_bypass_gpio_high (char*)"bypassHigh"
#define preference_cred_bypass_gpio_low (char*)"bypassLow"
#define preference_publish_config (char*)"nhPubConfig"
#define preference_config_from_mqtt (char*)"nhCntrlEnabled"
// CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT
#define preference_find_best_rssi (char*)"nwbestrssi"
@@ -241,6 +243,8 @@ inline void initPreferences(Preferences* preferences)
preferences->putBool(preference_cred_duo_enabled, false);
preferences->putBool(preference_cred_duo_approval, false);
preferences->putBool(preference_cred_bypass_boot_btn_enabled, false);
preferences->putBool(preference_publish_config, false);
preferences->putBool(preference_config_from_mqtt, false);
preferences->putInt(preference_mqtt_broker_port, 1883);
preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE);
@@ -504,8 +508,8 @@ private:
preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates,
preference_webserver_enabled, preference_lock_enabled, preference_lock_pin_status, preference_mqtt_lock_path, preference_opener_enabled, preference_opener_pin_status,
preference_opener_continuous_mode, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_update_time, preference_time_server,
preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt,
preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address,
preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset,
preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address,
preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_http_auth_type, preference_lock_gemini_pin,
preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, preference_hybrid_reboot_on_disconnect,
preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry,
@@ -525,7 +529,8 @@ private:
preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_nukihub_id,
preference_cred_duo_host, preference_cred_duo_ikey, preference_cred_duo_skey, preference_cred_duo_user, preference_cred_duo_enabled, preference_https_fqdn, preference_bypass_proxy,
preference_cred_session_lifetime, preference_cred_session_lifetime_remember, preference_cred_session_lifetime_duo, preference_cred_session_lifetime_duo_remember,
preference_cred_duo_approval, preference_cred_bypass_boot_btn_enabled, preference_cred_bypass_gpio_high, preference_cred_bypass_gpio_low
preference_cred_duo_approval, preference_cred_bypass_boot_btn_enabled, preference_cred_bypass_gpio_high, preference_cred_bypass_gpio_low, preference_publish_config,
preference_config_from_mqtt
};
std::vector<char*> _redact =
{
@@ -544,7 +549,8 @@ private:
preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi, preference_cred_bypass_boot_btn_enabled,
preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode,
preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_mqtt_ssl_enabled,
preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled, preference_enable_debug_mode, preference_cred_duo_enabled, preference_cred_duo_approval
preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled, preference_enable_debug_mode, preference_cred_duo_enabled, preference_cred_duo_approval,
preference_publish_config, preference_config_from_mqtt
};
std::vector<char*> _bytePrefs =
{

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,7 @@
#include "NukiNetworkLock.h"
#include "NukiOpenerWrapper.h"
#include "Gpio.h"
#include "ImportExport.h"
extern TaskHandle_t nukiTaskHandle;
@@ -30,6 +31,7 @@ enum class TokenType
#else
#include "NukiNetwork.h"
#include "ImportExport.h"
#endif
extern TaskHandle_t networkTaskHandle;
@@ -38,9 +40,9 @@ class WebCfgServer
{
public:
#ifndef NUKI_HUB_UPDATER
WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, NukiNetwork* network, Gpio* gpio, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer);
WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, NukiNetwork* network, Gpio* gpio, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer, ImportExport* importExport);
#else
WebCfgServer(NukiNetwork* network, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer);
WebCfgServer(NukiNetwork* network, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer, ImportExport* importExport);
#endif
~WebCfgServer() = default;
@@ -91,7 +93,6 @@ private:
NukiWrapper* _nuki = nullptr;
NukiOpenerWrapper* _nukiOpener = nullptr;
Gpio* _gpio = nullptr;
bool _pinsConfigured = false;
bool _brokerConfigured = false;
bool _rebootRequired = false;
#endif
@@ -101,9 +102,6 @@ private:
String generateConfirmCode();
String _confirmCode = "----";
int checkDuoAuth(PsychicRequest *request);
int checkDuoApprove();
bool startDuoAuth(char* pushType = (char*)"");
void saveSessions(bool duo = false);
void loadSessions(bool duo = false);
void clearSessions();
@@ -136,6 +134,7 @@ private:
PsychicHttpServer* _psychicServer = nullptr;
NukiNetwork* _network = nullptr;
Preferences* _preferences = nullptr;
ImportExport* _importExport;
char _credUser[31] = {0};
char _credPassword[31] = {0};
@@ -145,20 +144,8 @@ private:
size_t _otaContentLen = 0;
String _hostname;
JsonDocument _httpSessions;
JsonDocument _duoSessions;
JsonDocument _sessionsOpts;
struct tm timeinfo;
bool _duoActiveRequest;
bool _duoEnabled = false;
bool _bypassGPIO = false;
int _bypassGPIOHigh = -1;
int _bypassGPIOLow = -1;
int64_t _duoRequestTS = 0;
String _duoTransactionId;
String _duoHost;
String _duoSkey;
String _duoIkey;
String _duoUser;
String _duoCheckId;
String _duoCheckIP;
};

View File

@@ -31,6 +31,7 @@
#include "RestartReason.h"
#include "EspMillis.h"
#include "NimBLEDevice.h"
#include "ImportExport.h"
/*
#ifdef DEBUG_NUKIHUB
@@ -65,6 +66,7 @@ int64_t restartTs = (pow(2,63) - (5 * 1000 * 60000)) / 1000;
#include "../../src/RestartReason.h"
#include "../../src/NukiNetwork.h"
#include "../../src/EspMillis.h"
#include "../../src/ImportExport.h"
int64_t restartTs = 10 * 60 * 1000;
@@ -78,6 +80,7 @@ NukiNetwork* network = nullptr;
WebCfgServer* webCfgServer = nullptr;
WebCfgServer* webCfgServerSSL = nullptr;
Preferences* preferences = nullptr;
ImportExport* importExport = nullptr;
RTC_NOINIT_ATTR int espRunning;
RTC_NOINIT_ATTR int restartReason;
@@ -163,9 +166,9 @@ void setReroute()
uint8_t checkPartition()
{
const esp_partition_t* running_partition = esp_ota_get_running_partition();
Log->print(("Partition size: "));
Log->print("Partition size: ");
Log->println(running_partition->size);
Log->print(("Partition subtype: "));
Log->print("Partition subtype: ");
Log->println(running_partition->subtype);
if(running_partition->size == 1966080)
@@ -203,7 +206,7 @@ void networkTask(void *pvParameters)
if(bootloopCounter > 0)
{
bootloopCounter = (int8_t)0;
Log->println(("Bootloop counter reset"));
Log->println("Bootloop counter reset");
}
}
@@ -339,12 +342,12 @@ void bootloopDetection()
esp_reset_reason() == esp_reset_reason_t::ESP_RST_WDT)
{
bootloopCounter++;
Log->print(("Bootloop counter incremented: "));
Log->print("Bootloop counter incremented: ");
Log->println(bootloopCounter);
if(bootloopCounter == 10)
{
Log->print(("Bootloop detected."));
Log->print("Bootloop detected.");
preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE);
preferences->putInt(preference_task_size_network, NETWORK_TASK_SIZE);
@@ -440,7 +443,7 @@ void otaTask(void *pvParameter)
{
.http_config = &config,
};
Log->print(("Attempting to download update from "));
Log->print("Attempting to download update from ");
Log->println(config.url);
int retryMax = 3;
@@ -649,9 +652,9 @@ void setup()
}
#ifdef NUKI_HUB_UPDATER
Log->print(("Nuki Hub OTA version "));
Log->print("Nuki Hub OTA version ");
Log->println(NUKI_HUB_VERSION);
Log->print(("Nuki Hub OTA build "));
Log->print("Nuki Hub OTA build ");
Log->println();
if(preferences->getString(preference_updater_version, "") != NUKI_HUB_VERSION)
@@ -666,6 +669,8 @@ void setup()
{
preferences->putString(preference_updater_date, NUKI_HUB_DATE);
}
importExport = new ImportExport(preferences);
network = new NukiNetwork(preferences);
network->initialize();
@@ -735,7 +740,7 @@ void setup()
psychicSSLServer->ssl_config.httpd.max_open_sockets = 8;
psychicSSLServer->setCertificate(cert, key);
psychicSSLServer->config.stack_size = HTTPD_TASK_SIZE;
webCfgServerSSL = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicSSLServer);
webCfgServerSSL = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicSSLServer, importExport);
webCfgServerSSL->initialize();
psychicSSLServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
return response->redirect("/");
@@ -751,7 +756,7 @@ void setup()
#endif
psychicServer = new PsychicHttpServer;
psychicServer->config.stack_size = HTTPD_TASK_SIZE;
webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer);
webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer, importExport);
webCfgServer->initialize();
psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
return response->redirect("/");
@@ -767,9 +772,9 @@ void setup()
bootloopDetection();
}
Log->print(("Nuki Hub version "));
Log->print("Nuki Hub version ");
Log->println(NUKI_HUB_VERSION);
Log->print(("Nuki Hub build "));
Log->print("Nuki Hub build ");
Log->println(NUKI_HUB_BUILD);
uint32_t devIdOpener = preferences->getUInt(preference_device_id_opener);
@@ -791,8 +796,10 @@ void setup()
Log->print(gpioDesc.c_str());
const String mqttLockPath = preferences->getString(preference_mqtt_lock_path);
importExport = new ImportExport(preferences);
network = new NukiNetwork(preferences, gpio, mqttLockPath, CharBuffer::get(), buffer_size);
network = new NukiNetwork(preferences, gpio, mqttLockPath, CharBuffer::get(), buffer_size, importExport);
network->initialize();
lockEnabled = preferences->getBool(preference_lock_enabled);
@@ -826,7 +833,7 @@ void setup()
networkLock->initialize();
}
nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, nukiOfficial, gpio, preferences);
nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, nukiOfficial, gpio, preferences, CharBuffer::get(), buffer_size);
nuki->initialize();
}
@@ -840,7 +847,7 @@ void setup()
networkOpener->initialize();
}
nukiOpener = new NukiOpenerWrapper("NukiHub", deviceIdOpener, bleScanner, networkOpener, gpio, preferences);
nukiOpener = new NukiOpenerWrapper("NukiHub", deviceIdOpener, bleScanner, networkOpener, gpio, preferences, CharBuffer::get(), buffer_size);
nukiOpener->initialize();
}
@@ -911,7 +918,7 @@ void setup()
psychicSSLServer->ssl_config.httpd.max_open_sockets = 8;
psychicSSLServer->setCertificate(cert, key);
psychicSSLServer->config.stack_size = HTTPD_TASK_SIZE;
webCfgServerSSL = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicSSLServer);
webCfgServerSSL = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicSSLServer, importExport);
webCfgServerSSL->initialize();
psychicSSLServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
return response->redirect("/");
@@ -927,7 +934,7 @@ void setup()
#endif
psychicServer = new PsychicHttpServer;
psychicServer->config.stack_size = HTTPD_TASK_SIZE;
webCfgServer = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer);
webCfgServer = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer, importExport);
webCfgServer->initialize();
psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
return response->redirect("/");
@@ -949,27 +956,27 @@ void setup()
}
#endif
*/
String timeserver = preferences->getString(preference_time_server, "pool.ntp.org");
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(timeserver.c_str());
config.start = false;
config.server_from_dhcp = true;
config.renew_servers_after_new_IP = true;
config.index_of_first_server = 1;
if (network->networkDeviceType() == NetworkDeviceType::WiFi)
{
config.ip_event_to_renew = IP_EVENT_STA_GOT_IP;
}
else
{
config.ip_event_to_renew = IP_EVENT_ETH_GOT_IP;
}
config.sync_cb = cbSyncTime;
esp_netif_sntp_init(&config);
}
#endif
String timeserver = preferences->getString(preference_time_server, "pool.ntp.org");
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(timeserver.c_str());
config.start = false;
config.server_from_dhcp = true;
config.renew_servers_after_new_IP = true;
config.index_of_first_server = 1;
if (network->networkDeviceType() == NetworkDeviceType::WiFi)
{
config.ip_event_to_renew = IP_EVENT_STA_GOT_IP;
}
else
{
config.ip_event_to_renew = IP_EVENT_ETH_GOT_IP;
}
config.sync_cb = cbSyncTime;
esp_netif_sntp_init(&config);
if(doOta)
{
setupTasks(true);

View File

@@ -72,7 +72,7 @@ void EthernetDevice::initialize()
if(ethCriticalFailure)
{
ethCriticalFailure = false;
Log->println(("Failed to initialize ethernet hardware"));
Log->println("Failed to initialize ethernet hardware");
Log->println("Network device has a critical failure, enable fallback to Wi-Fi and reboot.");
wifiFallback = true;
delay(200);
@@ -80,11 +80,11 @@ void EthernetDevice::initialize()
return;
}
Log->println(("Init Ethernet"));
Log->println("Init Ethernet");
if(_useSpi)
{
Log->println(("Use SPI"));
Log->println("Use SPI");
ethCriticalFailure = true;
SPI.begin(_spi_sck, _spi_miso, _spi_mosi);
_hardwareInitialized = ETH.begin(_type, _phy_addr, _cs, _irq, _rst, SPI);
@@ -93,7 +93,7 @@ void EthernetDevice::initialize()
#ifdef CONFIG_IDF_TARGET_ESP32
else
{
Log->println(("Use RMII"));
Log->println("Use RMII");
// Workaround for failing RMII initialization with pioarduino 3.1.0
// Revoke all GPIO's some of them set by init PSRAM in IDF
@@ -118,7 +118,7 @@ void EthernetDevice::initialize()
if(_hardwareInitialized)
{
Log->println(("Ethernet hardware Initialized"));
Log->println("Ethernet hardware Initialized");
wifiFallback = false;
if(_useSpi && !_ipConfiguration->dhcpEnabled())
@@ -133,7 +133,7 @@ void EthernetDevice::initialize()
}
else
{
Log->println(("Failed to initialize ethernet hardware"));
Log->println("Failed to initialize ethernet hardware");
Log->println("Network device has a critical failure, enable fallback to Wi-Fi and reboot.");
wifiFallback = true;
delay(200);
@@ -150,7 +150,7 @@ void EthernetDevice::update()
{
if(_ipConfiguration->ipAddress() != ETH.localIP())
{
Log->println(("ETH Set static IP"));
Log->println("ETH Set static IP");
ETH.config(_ipConfiguration->ipAddress(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet(), _ipConfiguration->dnsServer());
_checkIpTs = espMillis() + 5000;
return;

View File

@@ -16,20 +16,20 @@ IPConfiguration::IPConfiguration(Preferences *preferences)
_gateway.fromString(_preferences->getString(preference_ip_gateway, ""));
_dnsServer.fromString(_preferences->getString(preference_ip_dns_server, ""));
Log->print(("IP configuration: "));
Log->print("IP configuration: ");
if(dhcpEnabled())
{
Log->println(("DHCP"));
Log->println("DHCP");
}
else
{
Log->print(("IP address: "));
Log->print("IP address: ");
Log->print(ipAddress());
Log->print((", Subnet: "));
Log->print(", Subnet: ");
Log->print(subnet());
Log->print((", Gateway: "));
Log->print(", Gateway: ");
Log->print(defaultGateway());
Log->print((", DNS: "));
Log->print(", DNS: ");
Log->println(dnsServer());
}
}

View File

@@ -32,7 +32,7 @@ void NetworkDevice::init()
if(ca_cert.length() > 1)
{
_useEncryption = true;
Log->println(("MQTT over TLS."));
Log->println("MQTT over TLS.");
_mqttClientSecure = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
_mqttClientSecure->setCACert(caDest);
@@ -57,7 +57,7 @@ void NetworkDevice::init()
if(cert.length() > 1 && key.length() > 1)
{
Log->println(("MQTT with client certificate."));
Log->println("MQTT with client certificate.");
_mqttClientSecure->setCertificate(certDest);
_mqttClientSecure->setPrivateKey(keyDest);
}
@@ -69,7 +69,7 @@ void NetworkDevice::init()
if (!_useEncryption)
{
Log->println(("MQTT without TLS."));
Log->println("MQTT without TLS.");
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO);
}
@@ -251,6 +251,11 @@ void NetworkDevice::mqttDisable()
_mqttEnabled = false;
}
bool NetworkDevice::isEncrypted()
{
return _useEncryption;
}
MqttClient *NetworkDevice::getMqttClient() const
{
if (_useEncryption)

View File

@@ -29,6 +29,7 @@ public:
virtual String BSSIDstr() = 0;
#ifndef NUKI_HUB_UPDATER
virtual bool isEncrypted();
virtual bool mqttConnect();
virtual bool mqttDisconnect(bool force);
virtual void mqttDisable();

View File

@@ -50,7 +50,7 @@ NetworkDeviceType NetworkUtil::GetDeviceTypeFromPreference(int hardwareDetect, i
return NetworkDeviceType::LilyGO_T_ETH_ELite;
break;
default:
Log->println(("Unknown hardware selected, falling back to Wi-Fi."));
Log->println("Unknown hardware selected, falling back to Wi-Fi.");
return NetworkDeviceType::WiFi;
break;
}

View File

@@ -6,10 +6,12 @@ list(APPEND app_sources ../../src/PreferencesKeys.h)
list(APPEND app_sources ../../src/RestartReason.h)
list(APPEND app_sources ../../src/WebCfgServer.h)
list(APPEND app_sources ../../src/WebCfgServerConstants.h)
list(APPEND app_sources ../../src/ImportExport.h)
list(APPEND app_sources ../../src/Logger.cpp)
list(APPEND app_sources ../../src/NukiNetwork.cpp)
list(APPEND app_sources ../../src/WebCfgServer.cpp)
list(APPEND app_sources ../../src/ImportExport.cpp)
list(APPEND app_sources ../../src/enums/NetworkDeviceType.h)
list(APPEND app_sources ../../src/networkDevices/EthernetDevice.h)