merge master

This commit is contained in:
technyon
2023-02-14 18:56:36 +01:00
36 changed files with 1061 additions and 141 deletions

View File

@@ -50,8 +50,8 @@ file(GLOB SRCFILES
networkDevices/NetworkDevice.h networkDevices/NetworkDevice.h
networkDevices/WifiDevice.cpp networkDevices/WifiDevice.cpp
networkDevices/W5500Device.cpp networkDevices/W5500Device.cpp
networkDevices/ClientSyncEthernet.cpp networkDevices/ClientSyncW5500.cpp
networkDevices/espMqttClientEthernet.cpp networkDevices/espMqttClientW5500.cpp
NukiWrapper.cpp NukiWrapper.cpp
NukiOpenerWrapper.cpp NukiOpenerWrapper.cpp
MqttTopics.h MqttTopics.h
@@ -62,6 +62,7 @@ file(GLOB SRCFILES
PreferencesKeys.h PreferencesKeys.h
Gpio.cpp Gpio.cpp
Logger.cpp Logger.cpp
RestartReason.h
# include/RTOS.h # include/RTOS.h
lib/WiFiManager/WiFiManager.cpp lib/WiFiManager/WiFiManager.cpp
lib/WiFiManager/wm_consts_en.h lib/WiFiManager/wm_consts_en.h
@@ -122,15 +123,9 @@ target_link_arduino_libraries(${PROJECT_NAME}
WiFi WiFi
WiFiClientSecure WiFiClientSecure
Update Update
# WebServer
DNSServer DNSServer
Preferences Preferences
SPIFFS
SPI SPI
# Ethernet
# esp32
# Wire
# FS
) )
target_link_arduino_libraries(DNSServer PUBLIC WiFi) target_link_arduino_libraries(DNSServer PUBLIC WiFi)

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#define NUKI_HUB_VERSION "8.3" #define NUKI_HUB_VERSION "8.8"
#define MQTT_QOS_LEVEL 1 #define MQTT_QOS_LEVEL 1
#define MQTT_CLEAN_SESSIONS false #define MQTT_CLEAN_SESSIONS false

View File

@@ -27,7 +27,6 @@ void Gpio::isrLock()
if(millis() < _lockedTs) return; if(millis() < _lockedTs) return;
_nuki->lock(); _nuki->lock();
_lockedTs = millis() + _debounceTime; _lockedTs = millis() + _debounceTime;
Log->println(F("Lock via GPIO"));;
} }
void Gpio::isrUnlock() void Gpio::isrUnlock()
@@ -35,7 +34,6 @@ void Gpio::isrUnlock()
if(millis() < _lockedTs) return; if(millis() < _lockedTs) return;
_nuki->unlock(); _nuki->unlock();
_lockedTs = millis() + _debounceTime; _lockedTs = millis() + _debounceTime;
Log->println(F("Unlock via GPIO"));;
} }
void Gpio::isrUnlatch() void Gpio::isrUnlatch()
@@ -43,5 +41,4 @@ void Gpio::isrUnlatch()
if(millis() < _lockedTs) return; if(millis() < _lockedTs) return;
_nuki->unlatch(); _nuki->unlatch();
_lockedTs = millis() + _debounceTime; _lockedTs = millis() + _debounceTime;
Log->println(F("Unlatch via GPIO"));;
} }

View File

@@ -46,3 +46,5 @@
#define mqtt_topic_wifi_rssi "/maintenance/wifiRssi" #define mqtt_topic_wifi_rssi "/maintenance/wifiRssi"
#define mqtt_topic_log "/maintenance/log" #define mqtt_topic_log "/maintenance/log"
#define mqtt_topic_freeheap "/maintenance/freeHeap" #define mqtt_topic_freeheap "/maintenance/freeHeap"
#define mqtt_topic_restart_reason_fw "/maintenance/restartReasonNukiHub"
#define mqtt_topic_restart_reason_esp "/maintenance/restartReasonNukiEsp"

View File

@@ -6,6 +6,7 @@
#include "Logger.h" #include "Logger.h"
#include "Config.h" #include "Config.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "RestartReason.h"
Network* Network::_inst = nullptr; Network* Network::_inst = nullptr;
@@ -65,8 +66,7 @@ void Network::setupDevice()
Log->println(F(" for network device selection")); Log->println(F(" for network device selection"));
pinMode(hardwareDetectGpio, INPUT_PULLUP); pinMode(hardwareDetectGpio, INPUT_PULLUP);
_networkDeviceType = NetworkDeviceType::W5500; _networkDeviceType = digitalRead(hardwareDetectGpio) == HIGH ? NetworkDeviceType::WiFi : NetworkDeviceType::W5500;
// digitalRead(hardwareDetectGpio) == HIGH ? NetworkDeviceType::WiFi : NetworkDeviceType::W5500;
} }
else if(hardwareDetect == 3) else if(hardwareDetect == 3)
{ {
@@ -173,6 +173,8 @@ void Network::initialize()
_networkTimeout = -1; _networkTimeout = -1;
_preferences->putInt(preference_network_timeout, _networkTimeout); _preferences->putInt(preference_network_timeout, _networkTimeout);
} }
_publishDebugInfo = _preferences->getBool(preference_publish_debug_info);
} }
bool Network::update() bool Network::update()
@@ -185,7 +187,7 @@ bool Network::update()
{ {
if(_restartOnDisconnect && millis() > 60000) if(_restartOnDisconnect && millis() > 60000)
{ {
ESP.restart(); restartEsp(RestartReason::RestartOnDisconnectWatchdog);
} }
Log->println(F("Network not connected. Trying reconnect.")); Log->println(F("Network not connected. Trying reconnect."));
@@ -197,7 +199,7 @@ bool Network::update()
strcpy(WiFi_fallbackDetect, "wifi_fallback"); strcpy(WiFi_fallbackDetect, "wifi_fallback");
Log->println("Network device has a critical failure, enable fallback to Wifi and reboot."); Log->println("Network device has a critical failure, enable fallback to Wifi and reboot.");
delay(200); delay(200);
ESP.restart(); restartEsp(RestartReason::NetworkDeviceCriticalFailure);
break; break;
case ReconnectStatus::Success: case ReconnectStatus::Success:
memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect)); memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect));
@@ -216,7 +218,7 @@ bool Network::update()
{ {
Log->println("Network timeout has been reached, restarting ..."); Log->println("Network timeout has been reached, restarting ...");
delay(200); delay(200);
ESP.restart(); restartEsp(RestartReason::NetworkTimeoutWatchdog);
} }
bool success = reconnect(); bool success = reconnect();
@@ -254,7 +256,12 @@ bool Network::update()
if(_lastMaintenanceTs == 0 || (ts - _lastMaintenanceTs) > 30000) if(_lastMaintenanceTs == 0 || (ts - _lastMaintenanceTs) > 30000)
{ {
publishULong(_maintenancePathPrefix, mqtt_topic_uptime, ts / 1000 / 60); publishULong(_maintenancePathPrefix, mqtt_topic_uptime, ts / 1000 / 60);
// publishUInt(_maintenancePathPrefix, mqtt_topic_freeheap, esp_get_free_heap_size()); if(_publishDebugInfo)
{
publishUInt(_maintenancePathPrefix, mqtt_topic_freeheap, esp_get_free_heap_size());
publishString(_maintenancePathPrefix, mqtt_topic_restart_reason_fw, getRestartReason().c_str());
publishString(_maintenancePathPrefix, mqtt_topic_restart_reason_esp, getEspRestartReason().c_str());
}
_lastMaintenanceTs = ts; _lastMaintenanceTs = ts;
} }
@@ -367,6 +374,10 @@ bool Network::reconnect()
} }
delay(1000); delay(1000);
_mqttConnectionState = 2; _mqttConnectionState = 2;
for(const auto& callback : _reconnectedCallbacks)
{
callback();
}
} }
else else
{ {
@@ -471,6 +482,11 @@ bool Network::encryptionSupported()
return _device->supportsEncryption(); return _device->supportsEncryption();
} }
const String Network::networkDeviceName() const
{
return _device->deviceName();
}
void Network::publishFloat(const char* prefix, const char* topic, const float value, const uint8_t precision) void Network::publishFloat(const char* prefix, const char* topic, const float value, const uint8_t precision)
{ {
char str[30]; char str[30];
@@ -924,3 +940,8 @@ void Network::setKeepAliveCallback(std::function<void()> reconnectTick)
{ {
_keepAliveCallback = reconnectTick; _keepAliveCallback = reconnectTick;
} }
void Network::addReconnectedCallback(std::function<void()> reconnectedCallback)
{
_reconnectedCallbacks.push_back(reconnectedCallback);
}

View File

@@ -45,12 +45,14 @@ public:
int mqttConnectionState(); // 0 = not connected; 1 = connected; 2 = connected and mqtt processed int mqttConnectionState(); // 0 = not connected; 1 = connected; 2 = connected and mqtt processed
bool encryptionSupported(); bool encryptionSupported();
const String networkDeviceName() const;
const NetworkDeviceType networkDeviceType(); const NetworkDeviceType networkDeviceType();
uint16_t subscribe(const char* topic, uint8_t qos); uint16_t subscribe(const char* topic, uint8_t qos);
void setKeepAliveCallback(std::function<void()> reconnectTick); void setKeepAliveCallback(std::function<void()> reconnectTick);
void addReconnectedCallback(std::function<void()> reconnectedCallback);
private: private:
static void onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); static void onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
@@ -82,6 +84,7 @@ private:
char* _presenceCsv = nullptr; char* _presenceCsv = nullptr;
bool _restartOnDisconnect = false; bool _restartOnDisconnect = false;
bool _firstConnect = true; bool _firstConnect = true;
bool _publishDebugInfo = false;
std::vector<String> _subscribedTopics; std::vector<String> _subscribedTopics;
std::map<String, String> _initTopics; std::map<String, String> _initTopics;
@@ -89,7 +92,8 @@ private:
unsigned long _lastMaintenanceTs = 0; unsigned long _lastMaintenanceTs = 0;
unsigned long _lastRssiTs = 0; unsigned long _lastRssiTs = 0;
long _rssiPublishInterval = 0; long _rssiPublishInterval = 0;
std::function<void()> _keepAliveCallback; std::function<void()> _keepAliveCallback = nullptr;
std::vector<std::function<void()>> _reconnectedCallbacks;
NetworkDeviceType _networkDeviceType = (NetworkDeviceType)-1; NetworkDeviceType _networkDeviceType = (NetworkDeviceType)-1;

View File

@@ -5,6 +5,7 @@
#include "PreferencesKeys.h" #include "PreferencesKeys.h"
#include "Pins.h" #include "Pins.h"
#include "Logger.h" #include "Logger.h"
#include "RestartReason.h"
NetworkLock::NetworkLock(Network* network, Preferences* preferences) NetworkLock::NetworkLock(Network* network, Preferences* preferences)
: _network(network), : _network(network),
@@ -69,6 +70,11 @@ void NetworkLock::initialize()
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_code, "000000"); _network->initTopic(_mqttPath, mqtt_topic_keypad_command_code, "000000");
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_enabled, "1"); _network->initTopic(_mqttPath, mqtt_topic_keypad_command_enabled, "1");
} }
_network->addReconnectedCallback([&]()
{
_reconnected = true;
});
} }
void NetworkLock::onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) void NetworkLock::onMqttDataReceived(const char* topic, byte* payload, const unsigned int length)
@@ -81,7 +87,7 @@ void NetworkLock::onMqttDataReceived(const char* topic, byte* payload, const uns
{ {
Log->println(F("Restart requested via MQTT.")); Log->println(F("Restart requested via MQTT."));
delay(200); delay(200);
ESP.restart(); restartEsp(RestartReason::RequestedViaMqtt);
} }
if(processActions && comparePrefixedPath(topic, mqtt_topic_lock_action)) if(processActions && comparePrefixedPath(topic, mqtt_topic_lock_action))
@@ -545,7 +551,6 @@ void NetworkLock::publishKeypadEntry(const String topic, NukiLock::KeypadEntry e
publishInt(concat(topic, "/lockCount").c_str(), entry.lockCount); publishInt(concat(topic, "/lockCount").c_str(), entry.lockCount);
} }
void NetworkLock::publishULong(const char *topic, const unsigned long value) void NetworkLock::publishULong(const char *topic, const unsigned long value)
{ {
return _network->publishULong(_mqttPath, topic, value); return _network->publishULong(_mqttPath, topic, value);
@@ -557,3 +562,10 @@ String NetworkLock::concat(String a, String b)
c.concat(b); c.concat(b);
return c; return c;
} }
bool NetworkLock::reconnected()
{
bool r = _reconnected;
_reconnected = false;
return r;
}

View File

@@ -40,7 +40,11 @@ public:
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override; void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
bool reconnected();
private: private:
bool comparePrefixedPath(const char* fullPath, const char* subPath);
void publishFloat(const char* topic, const float value, const uint8_t precision = 2); void publishFloat(const char* topic, const float value, const uint8_t precision = 2);
void publishInt(const char* topic, const int value); void publishInt(const char* topic, const int value);
void publishUInt(const char* topic, const unsigned int value); void publishUInt(const char* topic, const unsigned int value);
@@ -50,7 +54,6 @@ private:
bool publishString(const char* topic, const std::string& value); bool publishString(const char* topic, const std::string& value);
bool publishString(const char* topic, const char* value); bool publishString(const char* topic, const char* value);
void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry); void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry);
bool comparePrefixedPath(const char* fullPath, const char* subPath);
String concat(String a, String b); String concat(String a, String b);
@@ -65,6 +68,7 @@ private:
bool _firstTunerStatePublish = true; bool _firstTunerStatePublish = true;
unsigned long _lastMaintenanceTs = 0; unsigned long _lastMaintenanceTs = 0;
bool _haEnabled= false; bool _haEnabled= false;
bool _reconnected = false;
String _keypadCommandName = ""; String _keypadCommandName = "";
String _keypadCommandCode = ""; String _keypadCommandCode = "";

View File

@@ -42,6 +42,25 @@ void NetworkOpener::initialize()
{ {
_network->subscribe(_mqttPath, topic); _network->subscribe(_mqttPath, topic);
} }
if(_preferences->getBool(preference_keypad_control_enabled))
{
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_action);
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_id);
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_name);
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_code);
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_enabled);
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_action, "--");
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_id, "0");
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_name, "--");
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_code, "000000");
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_enabled, "1");
}
_network->addReconnectedCallback([&]()
{
_reconnected = true;
});
} }
void NetworkOpener::update() void NetworkOpener::update()
@@ -76,6 +95,46 @@ void NetworkOpener::onMqttDataReceived(const char* topic, byte* payload, const u
publishString(mqtt_topic_lock_action, success ? "ack" : "unknown_action"); publishString(mqtt_topic_lock_action, success ? "ack" : "unknown_action");
} }
if(processActions && comparePrefixedPath(topic, mqtt_topic_keypad_command_action))
{
if(_keypadCommandReceivedReceivedCallback != nullptr)
{
if(strcmp(value, "--") == 0) return;
_keypadCommandReceivedReceivedCallback(value, _keypadCommandId, _keypadCommandName, _keypadCommandCode, _keypadCommandEnabled);
_keypadCommandId = 0;
_keypadCommandName = "--";
_keypadCommandCode = "000000";
_keypadCommandEnabled = 1;
if(strcmp(value, "--") != 0)
{
publishString(mqtt_topic_keypad_command_action, "--");
}
publishInt(mqtt_topic_keypad_command_id, _keypadCommandId);
publishString(mqtt_topic_keypad_command_name, _keypadCommandName);
publishString(mqtt_topic_keypad_command_code, _keypadCommandCode);
publishInt(mqtt_topic_keypad_command_enabled, _keypadCommandEnabled);
}
}
else if(comparePrefixedPath(topic, mqtt_topic_keypad_command_id))
{
_keypadCommandId = atoi(value);
}
else if(comparePrefixedPath(topic, mqtt_topic_keypad_command_name))
{
_keypadCommandName = value;
}
else if(comparePrefixedPath(topic, mqtt_topic_keypad_command_code))
{
_keypadCommandCode = value;
}
else if(comparePrefixedPath(topic, mqtt_topic_keypad_command_enabled))
{
_keypadCommandEnabled = atoi(value);
}
for(auto configTopic : _configTopics) for(auto configTopic : _configTopics)
{ {
if(comparePrefixedPath(topic, configTopic)) if(comparePrefixedPath(topic, configTopic))
@@ -399,6 +458,36 @@ void NetworkOpener::removeHASSConfig(char* uidString)
_network->removeHASSConfig(uidString); _network->removeHASSConfig(uidString);
} }
void NetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount)
{
uint index = 0;
for(const auto& entry : entries)
{
String basePath = mqtt_topic_keypad;
basePath.concat("/code_");
basePath.concat(std::to_string(index).c_str());
publishKeypadEntry(basePath, entry);
++index;
}
while(index < maxKeypadCodeCount)
{
NukiLock::KeypadEntry entry;
memset(&entry, 0, sizeof(entry));
String basePath = mqtt_topic_keypad;
basePath.concat("/code_");
basePath.concat(std::to_string(index).c_str());
publishKeypadEntry(basePath, entry);
++index;
}
}
void NetworkOpener::publishKeypadCommandResult(const char* result)
{
publishString(mqtt_topic_keypad_command_result, result);
}
void NetworkOpener::setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char *)) void NetworkOpener::setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char *))
{ {
_lockActionReceivedCallback = lockActionReceivedCallback; _lockActionReceivedCallback = lockActionReceivedCallback;
@@ -409,6 +498,11 @@ void NetworkOpener::setConfigUpdateReceivedCallback(void (*configUpdateReceivedC
_configUpdateReceivedCallback = configUpdateReceivedCallback; _configUpdateReceivedCallback = configUpdateReceivedCallback;
} }
void NetworkOpener::setKeypadCommandReceivedCallback(void (*keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled))
{
_keypadCommandReceivedReceivedCallback = keypadCommandReceivedReceivedCallback;
}
void NetworkOpener::publishFloat(const char *topic, const float value, const uint8_t precision) void NetworkOpener::publishFloat(const char *topic, const float value, const uint8_t precision)
{ {
_network->publishFloat(_mqttPath, topic, value, precision); _network->publishFloat(_mqttPath, topic, value, precision);
@@ -450,6 +544,24 @@ void NetworkOpener::publishString(const char* topic, const char* value)
_network->publishString(_mqttPath, topic, value); _network->publishString(_mqttPath, topic, value);
} }
void NetworkOpener::publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry)
{
char codeName[sizeof(entry.name) + 1];
memset(codeName, 0, sizeof(codeName));
memcpy(codeName, entry.name, sizeof(entry.name));
publishInt(concat(topic, "/id").c_str(), entry.codeId);
publishBool(concat(topic, "/enabled").c_str(), entry.enabled);
publishString(concat(topic, "/name").c_str(), codeName);
publishInt(concat(topic, "/createdYear").c_str(), entry.dateCreatedYear);
publishInt(concat(topic, "/createdMonth").c_str(), entry.dateCreatedMonth);
publishInt(concat(topic, "/createdDay").c_str(), entry.dateCreatedDay);
publishInt(concat(topic, "/createdHour").c_str(), entry.dateCreatedHour);
publishInt(concat(topic, "/createdMin").c_str(), entry.dateCreatedMin);
publishInt(concat(topic, "/createdSec").c_str(), entry.dateCreatedSec);
publishInt(concat(topic, "/lockCount").c_str(), entry.lockCount);
}
void NetworkOpener::buildMqttPath(const char* path, char* outPath) void NetworkOpener::buildMqttPath(const char* path, char* outPath)
{ {
int offset = 0; int offset = 0;
@@ -486,3 +598,17 @@ bool NetworkOpener::comparePrefixedPath(const char *fullPath, const char *subPat
return strcmp(fullPath, prefixedPath) == 0; return strcmp(fullPath, prefixedPath) == 0;
} }
String NetworkOpener::concat(String a, String b)
{
String c = a;
c.concat(b);
return c;
}
bool NetworkOpener::reconnected()
{
bool r = _reconnected;
_reconnected = false;
return r;
}

View File

@@ -32,12 +32,17 @@ public:
void publishBleAddress(const std::string& address); void publishBleAddress(const std::string& address);
void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState); void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState);
void removeHASSConfig(char* uidString); void removeHASSConfig(char* uidString);
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
void publishKeypadCommandResult(const char* result);
void setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char* value)); void setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char* value));
void setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char* path, const char* value)); void setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char* path, const char* value));
void setKeypadCommandReceivedCallback(void (*keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled));
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override; void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
bool reconnected();
private: private:
bool comparePrefixedPath(const char* fullPath, const char* subPath); bool comparePrefixedPath(const char* fullPath, const char* subPath);
@@ -48,11 +53,14 @@ private:
void publishString(const char* topic, const String& value); void publishString(const char* topic, const String& value);
void publishString(const char* topic, const std::string& value); void publishString(const char* topic, const std::string& value);
void publishString(const char* topic, const char* value); void publishString(const char* topic, const char* value);
void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry);
void buildMqttPath(const char* path, char* outPath); void buildMqttPath(const char* path, char* outPath);
void subscribe(const char* path); void subscribe(const char* path);
void logactionCompletionStatusToString(uint8_t value, char* out); void logactionCompletionStatusToString(uint8_t value, char* out);
String concat(String a, String b);
Preferences* _preferences; Preferences* _preferences;
Network* _network = nullptr; Network* _network = nullptr;
@@ -64,8 +72,15 @@ private:
bool _firstTunerStatePublish = true; bool _firstTunerStatePublish = true;
bool _haEnabled= false; bool _haEnabled= false;
bool _reconnected = false;
String _keypadCommandName = "";
String _keypadCommandCode = "";
uint _keypadCommandId = 0;
int _keypadCommandEnabled = 1;
unsigned long _resetLockStateTs = 0; unsigned long _resetLockStateTs = 0;
bool (*_lockActionReceivedCallback)(const char* value) = nullptr; bool (*_lockActionReceivedCallback)(const char* value) = nullptr;
void (*_configUpdateReceivedCallback)(const char* path, const char* value) = nullptr; void (*_configUpdateReceivedCallback)(const char* path, const char* value) = nullptr;
void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr;
}; };

View File

@@ -3,6 +3,7 @@
#include "PreferencesKeys.h" #include "PreferencesKeys.h"
#include "MqttTopics.h" #include "MqttTopics.h"
#include "Logger.h" #include "Logger.h"
#include "RestartReason.h"
#include <NukiOpenerUtils.h> #include <NukiOpenerUtils.h>
NukiOpenerWrapper* nukiOpenerInst; NukiOpenerWrapper* nukiOpenerInst;
@@ -24,6 +25,7 @@ NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, uint32_t id,
network->setLockActionReceivedCallback(nukiOpenerInst->onLockActionReceivedCallback); network->setLockActionReceivedCallback(nukiOpenerInst->onLockActionReceivedCallback);
network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback); network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback);
network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback);
} }
@@ -39,24 +41,56 @@ void NukiOpenerWrapper::initialize()
_nukiOpener.registerBleScanner(_bleScanner); _nukiOpener.registerBleScanner(_bleScanner);
_intervalLockstate = _preferences->getInt(preference_query_interval_lockstate); _intervalLockstate = _preferences->getInt(preference_query_interval_lockstate);
_intervalConfig = _preferences->getInt(preference_query_interval_configuration);
_intervalBattery = _preferences->getInt(preference_query_interval_battery); _intervalBattery = _preferences->getInt(preference_query_interval_battery);
_intervalKeypad = _preferences->getInt(preference_query_interval_keypad);
_keypadEnabled = _preferences->getBool(preference_keypad_control_enabled);
_publishAuthData = _preferences->getBool(preference_publish_authdata); _publishAuthData = _preferences->getBool(preference_publish_authdata);
_maxKeypadCodeCount = _preferences->getUInt(preference_opener_max_keypad_code_count);
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost); _restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries); _nrOfRetries = _preferences->getInt(preference_command_nr_of_retries);
_retryDelay = _preferences->getInt(preference_command_retry_delay); _retryDelay = _preferences->getInt(preference_command_retry_delay);
_rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000;
if(_retryDelay <= 100)
{
_retryDelay = 100;
_preferences->putInt(preference_command_retry_delay, _retryDelay);
}
if(_intervalLockstate == 0) if(_intervalLockstate == 0)
{ {
_intervalLockstate = 60 * 5; _intervalLockstate = 60 * 30;
_preferences->putInt(preference_query_interval_lockstate, _intervalLockstate); _preferences->putInt(preference_query_interval_lockstate, _intervalLockstate);
} }
if(_intervalConfig == 0)
{
_intervalConfig = 60 * 60;
_preferences->putInt(preference_query_interval_configuration, _intervalConfig);
}
if(_intervalBattery == 0) if(_intervalBattery == 0)
{ {
_intervalBattery = 60 * 30; _intervalBattery = 60 * 30;
_preferences->putInt(preference_query_interval_battery, _intervalBattery); _preferences->putInt(preference_query_interval_battery, _intervalBattery);
} }
if(_intervalKeypad == 0)
{
_intervalKeypad = 60 * 30;
_preferences->putInt(preference_query_interval_keypad, _intervalKeypad);
}
/*
if(_intervalKeypad == 0)
{
_intervalKeypad = 60 * 30;
_preferences->putInt(preference_query_interval_keypad, _intervalKeypad);
}
*/
if(_restartBeaconTimeout < 10)
{
_restartBeaconTimeout = -1;
_preferences->putInt(preference_restart_ble_beacon_lost, _restartBeaconTimeout);
}
_nukiOpener.setEventHandler(this); _nukiOpener.setEventHandler(this);
@@ -108,7 +142,7 @@ void NukiOpenerWrapper::update()
Log->print((millis() - _nukiOpener.getLastReceivedBeaconTs()) / 1000); Log->print((millis() - _nukiOpener.getLastReceivedBeaconTs()) / 1000);
Log->println(" seconds, restarting device."); Log->println(" seconds, restarting device.");
delay(200); delay(200);
ESP.restart(); restartEsp(RestartReason::BLEBeaconWatchdog);
} }
_nukiOpener.updateConnectionState(); _nukiOpener.updateConnectionState();
@@ -128,12 +162,15 @@ void NukiOpenerWrapper::update()
{ {
_nextConfigUpdateTs = ts + _intervalConfig * 1000; _nextConfigUpdateTs = ts + _intervalConfig * 1000;
updateConfig(); updateConfig();
if(_hassEnabled) if(_hassEnabled && !_hassSetupCompleted)
{ {
setupHASS(); setupHASS();
} }
} }
if(_hassEnabled && _configRead && _network->reconnected())
{
setupHASS();
}
if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs)) if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs))
{ {
_nextRssiTs = ts + _rssiPublishInterval; _nextRssiTs = ts + _rssiPublishInterval;
@@ -146,6 +183,12 @@ void NukiOpenerWrapper::update()
} }
} }
if(_hasKeypad && _keypadEnabled && (_nextKeypadUpdateTs == 0 || ts > _nextKeypadUpdateTs))
{
_nextKeypadUpdateTs = ts + _intervalKeypad * 1000;
updateKeypad();
}
if(_nextLockAction != (NukiOpener::LockAction)0xff && ts > _nextRetryTs) if(_nextLockAction != (NukiOpener::LockAction)0xff && ts > _nextRetryTs)
{ {
Nuki::CmdResult cmdResult = _nukiOpener.lockAction(_nextLockAction, 0, 0); Nuki::CmdResult cmdResult = _nukiOpener.lockAction(_nextLockAction, 0, 0);
@@ -194,6 +237,7 @@ void NukiOpenerWrapper::update()
_nextLockAction = (NukiOpener::LockAction) 0xff; _nextLockAction = (NukiOpener::LockAction) 0xff;
} }
} }
postponeBleWatchdog();
} }
if(_clearAuthData) if(_clearAuthData)
@@ -205,6 +249,11 @@ void NukiOpenerWrapper::update()
memcpy(&_lastKeyTurnerState, &_keyTurnerState, sizeof(NukiOpener::OpenerState)); memcpy(&_lastKeyTurnerState, &_keyTurnerState, sizeof(NukiOpener::OpenerState));
} }
bool NukiOpenerWrapper::isPinSet()
{
return _nukiOpener.getSecurityPincode() != 0;
}
void NukiOpenerWrapper::setPin(const uint16_t pin) void NukiOpenerWrapper::setPin(const uint16_t pin)
{ {
_nukiOpener.saveSecurityPincode(pin); _nukiOpener.saveSecurityPincode(pin);
@@ -218,7 +267,18 @@ void NukiOpenerWrapper::unpair()
void NukiOpenerWrapper::updateKeyTurnerState() void NukiOpenerWrapper::updateKeyTurnerState()
{ {
_nukiOpener.requestOpenerState(&_keyTurnerState); Nuki::CmdResult result =_nukiOpener.requestOpenerState(&_keyTurnerState);
if(result != Nuki::CmdResult::Success)
{
_retryLockstateCount++;
postponeBleWatchdog();
if(_retryLockstateCount < _nrOfRetries)
{
_nextLockStateUpdateTs = millis() + _retryDelay;
}
return;
}
_retryLockstateCount = 0;
if(_statusUpdated && if(_statusUpdated &&
_keyTurnerState.lockState == NukiOpener::LockState::Locked && _keyTurnerState.lockState == NukiOpener::LockState::Locked &&
@@ -249,20 +309,34 @@ void NukiOpenerWrapper::updateKeyTurnerState()
{ {
updateAuthData(); updateAuthData();
} }
postponeBleWatchdog();
} }
void NukiOpenerWrapper::updateBatteryState() void NukiOpenerWrapper::updateBatteryState()
{ {
_nukiOpener.requestBatteryReport(&_batteryReport); Nuki::CmdResult result = _nukiOpener.requestBatteryReport(&_batteryReport);
_network->publishBatteryReport(_batteryReport); if(result == Nuki::CmdResult::Success)
{
_network->publishBatteryReport(_batteryReport);
}
postponeBleWatchdog();
} }
void NukiOpenerWrapper::updateConfig() void NukiOpenerWrapper::updateConfig()
{ {
readConfig(); readConfig();
readAdvancedConfig(); readAdvancedConfig();
_network->publishConfig(_nukiConfig); _configRead = true;
_network->publishAdvancedConfig(_nukiAdvancedConfig); _hasKeypad = _nukiConfig.hasKeypad > 0;
if(_nukiConfigValid)
{
_network->publishConfig(_nukiConfig);
}
if(_nukiAdvancedConfigValid)
{
_network->publishAdvancedConfig(_nukiAdvancedConfig);
}
} }
void NukiOpenerWrapper::updateAuthData() void NukiOpenerWrapper::updateAuthData()
@@ -290,6 +364,41 @@ void NukiOpenerWrapper::updateAuthData()
{ {
_network->publishAuthorizationInfo(log); _network->publishAuthorizationInfo(log);
} }
postponeBleWatchdog();
}
void NukiOpenerWrapper::updateKeypad()
{
Nuki::CmdResult result = _nukiOpener.retrieveKeypadEntries(0, 0xffff);
if(result == 1)
{
std::list<NukiLock::KeypadEntry> entries;
_nukiOpener.getKeypadEntries(&entries);
entries.sort([](const NukiLock::KeypadEntry& a, const NukiLock::KeypadEntry& b) { return a.codeId < b.codeId; });
uint keypadCount = entries.size();
if(keypadCount > _maxKeypadCodeCount)
{
_maxKeypadCodeCount = keypadCount;
_preferences->putUInt(preference_opener_max_keypad_code_count, _maxKeypadCodeCount);
}
_network->publishKeypad(entries, _maxKeypadCodeCount);
_keypadCodeIds.clear();
_keypadCodeIds.reserve(entries.size());
for(const auto& entry : entries)
{
_keypadCodeIds.push_back(entry.codeId);
}
}
postponeBleWatchdog();
}
void NukiOpenerWrapper::postponeBleWatchdog()
{
_disableBleWatchdogTs = millis() + 15000;
} }
NukiOpener::LockAction NukiOpenerWrapper::lockActionToEnum(const char *str) NukiOpener::LockAction NukiOpenerWrapper::lockActionToEnum(const char *str)
@@ -317,6 +426,10 @@ void NukiOpenerWrapper::onConfigUpdateReceivedCallback(const char *topic, const
nukiOpenerInst->onConfigUpdateReceived(topic, value); nukiOpenerInst->onConfigUpdateReceived(topic, value);
} }
void NukiOpenerWrapper::onKeypadCommandReceivedCallback(const char *command, const uint &id, const String &name, const String &code, const int& enabled)
{
nukiOpenerInst->onKeypadCommandReceived(command, id, name, code, enabled);
}
void NukiOpenerWrapper::onConfigUpdateReceived(const char *topic, const char *value) void NukiOpenerWrapper::onConfigUpdateReceived(const char *topic, const char *value)
{ {
@@ -343,6 +456,117 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *topic, const char *va
} }
} }
void NukiOpenerWrapper::onKeypadCommandReceived(const char *command, const uint &id, const String &name, const String &code, const int& enabled)
{
if(!_hasKeypad)
{
if(_configRead)
{
_network->publishKeypadCommandResult("KeypadNotAvailable");
}
return;
}
if(!_keypadEnabled)
{
return;
}
bool idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), id) != _keypadCodeIds.end();
int codeInt = code.toInt();
bool codeValid = codeInt > 100000 && codeInt < 1000000 && (code.indexOf('0') == -1);
NukiLock::CmdResult result = (NukiLock::CmdResult)-1;
if(strcmp(command, "add") == 0)
{
if(name == "" || name == "--")
{
_network->publishKeypadCommandResult("MissingParameterName");
return;
}
if(codeInt == 0)
{
_network->publishKeypadCommandResult("MissingParameterCode");
return;
}
if(!codeValid)
{
_network->publishKeypadCommandResult("CodeInvalid");
return;
}
NukiLock::NewKeypadEntry entry;
memset(&entry, 0, sizeof(entry));
size_t nameLen = name.length();
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
entry.code = codeInt;
result = _nukiOpener.addKeypadEntry(entry);
Log->print("Add keypad code: "); Log->println((int)result);
updateKeypad();
}
else if(strcmp(command, "delete") == 0)
{
if(!idExists)
{
_network->publishKeypadCommandResult("UnknownId");
return;
}
result = _nukiOpener.deleteKeypadEntry(id);
Log->print("Delete keypad code: "); Log->println((int)result);
updateKeypad();
}
else if(strcmp(command, "update") == 0)
{
if(name == "" || name == "--")
{
_network->publishKeypadCommandResult("MissingParameterName");
return;
}
if(codeInt == 0)
{
_network->publishKeypadCommandResult("MissingParameterCode");
return;
}
if(!codeValid)
{
_network->publishKeypadCommandResult("CodeInvalid");
return;
}
if(!idExists)
{
_network->publishKeypadCommandResult("UnknownId");
return;
}
NukiLock::UpdatedKeypadEntry entry;
memset(&entry, 0, sizeof(entry));
entry.codeId = id;
size_t nameLen = name.length();
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
entry.code = codeInt;
entry.enabled = enabled == 0 ? 0 : 1;
result = _nukiOpener.updateKeypadEntry(entry);
Log->print("Update keypad code: "); Log->println((int)result);
updateKeypad();
}
else if(command == "--")
{
return;
}
else
{
_network->publishKeypadCommandResult("UnknownCommand");
return;
}
if((int)result != -1)
{
char resultStr[15];
memset(&resultStr, 0, sizeof(resultStr));
NukiOpener::cmdResultToString(result, resultStr);
_network->publishKeypadCommandResult(resultStr);
}
}
const NukiOpener::OpenerState &NukiOpenerWrapper::keyTurnerState() const NukiOpener::OpenerState &NukiOpenerWrapper::keyTurnerState()
{ {
return _keyTurnerState; return _keyTurnerState;
@@ -353,6 +577,11 @@ const bool NukiOpenerWrapper::isPaired()
return _paired; return _paired;
} }
const bool NukiOpenerWrapper::hasKeypad()
{
return _hasKeypad;
}
const BLEAddress NukiOpenerWrapper::getBleAddress() const const BLEAddress NukiOpenerWrapper::getBleAddress() const
{ {
return _nukiOpener.getBleAddress(); return _nukiOpener.getBleAddress();
@@ -379,6 +608,7 @@ void NukiOpenerWrapper::readConfig()
char resultStr[20]; char resultStr[20];
NukiOpener::cmdResultToString(result, resultStr); NukiOpener::cmdResultToString(result, resultStr);
Log->println(resultStr); Log->println(resultStr);
postponeBleWatchdog();
} }
void NukiOpenerWrapper::readAdvancedConfig() void NukiOpenerWrapper::readAdvancedConfig()
@@ -389,11 +619,12 @@ void NukiOpenerWrapper::readAdvancedConfig()
char resultStr[20]; char resultStr[20];
NukiOpener::cmdResultToString(result, resultStr); NukiOpener::cmdResultToString(result, resultStr);
Log->println(resultStr); Log->println(resultStr);
postponeBleWatchdog();
} }
void NukiOpenerWrapper::setupHASS() void NukiOpenerWrapper::setupHASS()
{ {
if(!_nukiConfigValid || _hassSetupCompleted) return; if(!_nukiConfigValid) return;
String baseTopic = _preferences->getString(preference_mqtt_opener_path); String baseTopic = _preferences->getString(preference_mqtt_opener_path);
char uidString[20]; char uidString[20];

View File

@@ -15,6 +15,7 @@ public:
void initialize(); void initialize();
void update(); void update();
bool isPinSet();
void setPin(const uint16_t pin); void setPin(const uint16_t pin);
void unpair(); void unpair();
@@ -23,6 +24,7 @@ public:
const NukiOpener::OpenerState& keyTurnerState(); const NukiOpener::OpenerState& keyTurnerState();
const bool isPaired(); const bool isPaired();
const bool hasKeypad();
const BLEAddress getBleAddress() const; const BLEAddress getBleAddress() const;
BleScanner::Scanner* bleScanner(); BleScanner::Scanner* bleScanner();
@@ -32,12 +34,16 @@ public:
private: private:
static bool onLockActionReceivedCallback(const char* value); static bool onLockActionReceivedCallback(const char* value);
static void onConfigUpdateReceivedCallback(const char* topic, const char* value); static void onConfigUpdateReceivedCallback(const char* topic, const char* value);
static void onKeypadCommandReceivedCallback(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
void onConfigUpdateReceived(const char* topic, const char* value); void onConfigUpdateReceived(const char* topic, const char* value);
void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
void updateKeyTurnerState(); void updateKeyTurnerState();
void updateBatteryState(); void updateBatteryState();
void updateConfig(); void updateConfig();
void updateAuthData(); void updateAuthData();
void updateKeypad();
void postponeBleWatchdog();
void readConfig(); void readConfig();
void readAdvancedConfig(); void readAdvancedConfig();
@@ -54,13 +60,16 @@ private:
int _intervalLockstate = 0; // seconds int _intervalLockstate = 0; // seconds
int _intervalBattery = 0; // seconds int _intervalBattery = 0; // seconds
int _intervalConfig = 60 * 60; // seconds int _intervalConfig = 60 * 60; // seconds
int _intervalKeypad = 0; // seconds
int _restartBeaconTimeout = 0; // seconds int _restartBeaconTimeout = 0; // seconds
bool _publishAuthData = false; bool _publishAuthData = false;
bool _clearAuthData = false; bool _clearAuthData = false;
int _nrOfRetries = 0; int _nrOfRetries = 0;
int _retryDelay = 0; int _retryDelay = 0;
int _retryCount = 0; int _retryCount = 0;
int _retryLockstateCount = 0;
unsigned long _nextRetryTs = 0; unsigned long _nextRetryTs = 0;
std::vector<uint16_t> _keypadCodeIds;
NukiOpener::OpenerState _lastKeyTurnerState; NukiOpener::OpenerState _lastKeyTurnerState;
NukiOpener::OpenerState _keyTurnerState; NukiOpener::OpenerState _keyTurnerState;
@@ -77,12 +86,18 @@ private:
bool _paired = false; bool _paired = false;
bool _statusUpdated = false; bool _statusUpdated = false;
bool _hasKeypad = false;
bool _keypadEnabled = false;
uint _maxKeypadCodeCount = 0;
bool _configRead = false;
long _rssiPublishInterval = 0; long _rssiPublishInterval = 0;
unsigned long _nextLockStateUpdateTs = 0; unsigned long _nextLockStateUpdateTs = 0;
unsigned long _nextBatteryReportTs = 0; unsigned long _nextBatteryReportTs = 0;
unsigned long _nextConfigUpdateTs = 0; unsigned long _nextConfigUpdateTs = 0;
unsigned long _nextKeypadUpdateTs = 0;
unsigned long _nextPairTs = 0; unsigned long _nextPairTs = 0;
long _nextRssiTs = 0; long _nextRssiTs = 0;
unsigned long _lastRssi = 0; unsigned long _lastRssi = 0;
unsigned long _disableBleWatchdogTs = 0;
NukiOpener::LockAction _nextLockAction = (NukiOpener::LockAction)0xff; NukiOpener::LockAction _nextLockAction = (NukiOpener::LockAction)0xff;
}; };

View File

@@ -3,6 +3,7 @@
#include "PreferencesKeys.h" #include "PreferencesKeys.h"
#include "MqttTopics.h" #include "MqttTopics.h"
#include "Logger.h" #include "Logger.h"
#include "RestartReason.h"
#include <NukiLockUtils.h> #include <NukiLockUtils.h>
NukiWrapper* nukiInst; NukiWrapper* nukiInst;
@@ -41,11 +42,12 @@ void NukiWrapper::initialize(const bool& firstStart)
_nukiLock.registerBleScanner(_bleScanner); _nukiLock.registerBleScanner(_bleScanner);
_intervalLockstate = _preferences->getInt(preference_query_interval_lockstate); _intervalLockstate = _preferences->getInt(preference_query_interval_lockstate);
_intervalConfig = _preferences->getInt(preference_query_interval_battery);
_intervalBattery = _preferences->getInt(preference_query_interval_battery); _intervalBattery = _preferences->getInt(preference_query_interval_battery);
_intervalKeypad = _preferences->getInt(preference_query_interval_keypad); _intervalKeypad = _preferences->getInt(preference_query_interval_keypad);
_keypadEnabled = _preferences->getBool(preference_keypad_control_enabled); _keypadEnabled = _preferences->getBool(preference_keypad_control_enabled);
_publishAuthData = _preferences->getBool(preference_publish_authdata); _publishAuthData = _preferences->getBool(preference_publish_authdata);
_maxKeypadCodeCount = _preferences->getUInt(preference_max_keypad_code_count); _maxKeypadCodeCount = _preferences->getUInt(preference_lock_max_keypad_code_count);
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost); _restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries); _nrOfRetries = _preferences->getInt(preference_command_nr_of_retries);
@@ -57,6 +59,7 @@ void NukiWrapper::initialize(const bool& firstStart)
_preferences->putInt(preference_command_nr_of_retries, 3); _preferences->putInt(preference_command_nr_of_retries, 3);
_preferences->putInt(preference_command_retry_delay, 1000); _preferences->putInt(preference_command_retry_delay, 1000);
} }
if(_retryDelay <= 100) if(_retryDelay <= 100)
{ {
_retryDelay = 100; _retryDelay = 100;
@@ -68,6 +71,11 @@ void NukiWrapper::initialize(const bool& firstStart)
_intervalLockstate = 60 * 30; _intervalLockstate = 60 * 30;
_preferences->putInt(preference_query_interval_lockstate, _intervalLockstate); _preferences->putInt(preference_query_interval_lockstate, _intervalLockstate);
} }
if(_intervalConfig == 0)
{
_intervalConfig = 60 * 60;
_preferences->putInt(preference_query_interval_configuration, _intervalConfig);
}
if(_intervalBattery == 0) if(_intervalBattery == 0)
{ {
_intervalBattery = 60 * 30; _intervalBattery = 60 * 30;
@@ -128,13 +136,14 @@ void NukiWrapper::update()
if(_restartBeaconTimeout > 0 && if(_restartBeaconTimeout > 0 &&
ts > 60000 && ts > 60000 &&
lastReceivedBeaconTs > 0 && lastReceivedBeaconTs > 0 &&
_disableBleWatchdogTs < ts &&
(ts - lastReceivedBeaconTs > _restartBeaconTimeout * 1000)) (ts - lastReceivedBeaconTs > _restartBeaconTimeout * 1000))
{ {
Log->print("No BLE beacon received from the lock for "); Log->print("No BLE beacon received from the lock for ");
Log->print((millis() - _nukiLock.getLastReceivedBeaconTs()) / 1000); Log->print((millis() - _nukiLock.getLastReceivedBeaconTs()) / 1000);
Log->println(" seconds, restarting device."); Log->println(" seconds, restarting device.");
delay(200); delay(200);
ESP.restart(); restartEsp(RestartReason::BLEBeaconWatchdog);
} }
_nukiLock.updateConnectionState(); _nukiLock.updateConnectionState();
@@ -154,11 +163,15 @@ void NukiWrapper::update()
{ {
_nextConfigUpdateTs = ts + _intervalConfig * 1000; _nextConfigUpdateTs = ts + _intervalConfig * 1000;
updateConfig(); updateConfig();
if(_hassEnabled) if(_hassEnabled && !_hassSetupCompleted)
{ {
setupHASS(); setupHASS();
} }
} }
if(_hassEnabled && _configRead && _network->reconnected())
{
setupHASS();
}
if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs)) if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs))
{ {
_nextRssiTs = ts + _rssiPublishInterval; _nextRssiTs = ts + _rssiPublishInterval;
@@ -225,6 +238,7 @@ void NukiWrapper::update()
_nextLockAction = (NukiLock::LockAction) 0xff; _nextLockAction = (NukiLock::LockAction) 0xff;
} }
} }
postponeBleWatchdog();
} }
if(_clearAuthData) if(_clearAuthData)
@@ -251,6 +265,11 @@ void NukiWrapper::unlatch()
_nextLockAction = NukiLock::LockAction::Unlatch; _nextLockAction = NukiLock::LockAction::Unlatch;
} }
bool NukiWrapper::isPinSet()
{
return _nukiLock.getSecurityPincode() != 0;
}
void NukiWrapper::setPin(const uint16_t pin) void NukiWrapper::setPin(const uint16_t pin)
{ {
_nukiLock.saveSecurityPincode(pin); _nukiLock.saveSecurityPincode(pin);
@@ -264,7 +283,19 @@ void NukiWrapper::unpair()
void NukiWrapper::updateKeyTurnerState() void NukiWrapper::updateKeyTurnerState()
{ {
_nukiLock.requestKeyTurnerState(&_keyTurnerState); Nuki::CmdResult result =_nukiLock.requestKeyTurnerState(&_keyTurnerState);
if(result != Nuki::CmdResult::Success)
{
_retryLockstateCount++;
postponeBleWatchdog();
if(_retryLockstateCount < _nrOfRetries)
{
_nextLockStateUpdateTs = millis() + _retryDelay;
}
return;
}
_retryLockstateCount = 0;
_network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState); _network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState);
if(_keyTurnerState.lockState != _lastKeyTurnerState.lockState) if(_keyTurnerState.lockState != _lastKeyTurnerState.lockState)
@@ -279,12 +310,18 @@ void NukiWrapper::updateKeyTurnerState()
{ {
updateAuthData(); updateAuthData();
} }
postponeBleWatchdog();
} }
void NukiWrapper::updateBatteryState() void NukiWrapper::updateBatteryState()
{ {
_nukiLock.requestBatteryReport(&_batteryReport); Nuki::CmdResult result = _nukiLock.requestBatteryReport(&_batteryReport);
_network->publishBatteryReport(_batteryReport); if(result == Nuki::CmdResult::Success)
{
_network->publishBatteryReport(_batteryReport);
}
postponeBleWatchdog();
} }
void NukiWrapper::updateConfig() void NukiWrapper::updateConfig()
@@ -293,8 +330,14 @@ void NukiWrapper::updateConfig()
readAdvancedConfig(); readAdvancedConfig();
_configRead = true; _configRead = true;
_hasKeypad = _nukiConfig.hasKeypad > 0; _hasKeypad = _nukiConfig.hasKeypad > 0;
_network->publishConfig(_nukiConfig); if(_nukiConfigValid)
_network->publishAdvancedConfig(_nukiAdvancedConfig); {
_network->publishConfig(_nukiConfig);
}
if(_nukiAdvancedConfigValid)
{
_network->publishAdvancedConfig(_nukiAdvancedConfig);
}
} }
void NukiWrapper::updateAuthData() void NukiWrapper::updateAuthData()
@@ -322,6 +365,7 @@ void NukiWrapper::updateAuthData()
{ {
_network->publishAuthorizationInfo(log); _network->publishAuthorizationInfo(log);
} }
postponeBleWatchdog();
} }
void NukiWrapper::updateKeypad() void NukiWrapper::updateKeypad()
@@ -338,7 +382,7 @@ void NukiWrapper::updateKeypad()
if(keypadCount > _maxKeypadCodeCount) if(keypadCount > _maxKeypadCodeCount)
{ {
_maxKeypadCodeCount = keypadCount; _maxKeypadCodeCount = keypadCount;
_preferences->putUInt(preference_max_keypad_code_count, _maxKeypadCodeCount); _preferences->putUInt(preference_lock_max_keypad_code_count, _maxKeypadCodeCount);
} }
_network->publishKeypad(entries, _maxKeypadCodeCount); _network->publishKeypad(entries, _maxKeypadCodeCount);
@@ -350,6 +394,12 @@ void NukiWrapper::updateKeypad()
_keypadCodeIds.push_back(entry.codeId); _keypadCodeIds.push_back(entry.codeId);
} }
} }
postponeBleWatchdog();
}
void NukiWrapper::postponeBleWatchdog()
{
_disableBleWatchdogTs = millis() + 15000;
} }
NukiLock::LockAction NukiWrapper::lockActionToEnum(const char *str) NukiLock::LockAction NukiWrapper::lockActionToEnum(const char *str)
@@ -378,13 +428,11 @@ void NukiWrapper::onConfigUpdateReceivedCallback(const char *topic, const char *
nukiInst->onConfigUpdateReceived(topic, value); nukiInst->onConfigUpdateReceived(topic, value);
} }
void NukiWrapper::onKeypadCommandReceivedCallback(const char *command, const uint &id, const String &name, const String &code, const int& enabled) void NukiWrapper::onKeypadCommandReceivedCallback(const char *command, const uint &id, const String &name, const String &code, const int& enabled)
{ {
nukiInst->onKeypadCommandReceived(command, id, name, code, enabled); nukiInst->onKeypadCommandReceived(command, id, name, code, enabled);
} }
void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value) void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value)
{ {
if(strcmp(topic, mqtt_topic_config_button_enabled) == 0) if(strcmp(topic, mqtt_topic_config_button_enabled) == 0)
@@ -594,7 +642,7 @@ void NukiWrapper::readAdvancedConfig()
void NukiWrapper::setupHASS() void NukiWrapper::setupHASS()
{ {
if(!_nukiConfigValid || _hassSetupCompleted) return; if(!_nukiConfigValid) return;
String baseTopic = _preferences->getString(preference_mqtt_lock_path); String baseTopic = _preferences->getString(preference_mqtt_lock_path);
char uidString[20]; char uidString[20];

View File

@@ -19,6 +19,7 @@ public:
void unlock(); void unlock();
void unlatch(); void unlatch();
bool isPinSet();
void setPin(const uint16_t pin); void setPin(const uint16_t pin);
void unpair(); void unpair();
@@ -43,12 +44,12 @@ private:
void updateConfig(); void updateConfig();
void updateAuthData(); void updateAuthData();
void updateKeypad(); void updateKeypad();
void postponeBleWatchdog();
void readConfig(); void readConfig();
void readAdvancedConfig(); void readAdvancedConfig();
void setupHASS(); void setupHASS();
bool hasDoorSensor(); bool hasDoorSensor();
NukiLock::LockAction lockActionToEnum(const char* str); // char array at least 14 characters NukiLock::LockAction lockActionToEnum(const char* str); // char array at least 14 characters
@@ -84,11 +85,12 @@ private:
bool _statusUpdated = false; bool _statusUpdated = false;
bool _hasKeypad = false; bool _hasKeypad = false;
bool _keypadEnabled = false; bool _keypadEnabled = false;
bool _configRead = false;
uint _maxKeypadCodeCount = 0; uint _maxKeypadCodeCount = 0;
bool _configRead = false;
int _nrOfRetries = 0; int _nrOfRetries = 0;
int _retryDelay = 0; int _retryDelay = 0;
int _retryCount = 0; int _retryCount = 0;
int _retryLockstateCount = 0;
long _rssiPublishInterval = 0; long _rssiPublishInterval = 0;
unsigned long _nextRetryTs = 0; unsigned long _nextRetryTs = 0;
unsigned long _nextLockStateUpdateTs = 0; unsigned long _nextLockStateUpdateTs = 0;
@@ -97,5 +99,6 @@ private:
unsigned long _nextKeypadUpdateTs = 0; unsigned long _nextKeypadUpdateTs = 0;
unsigned long _nextRssiTs = 0; unsigned long _nextRssiTs = 0;
unsigned long _lastRssi = 0; unsigned long _lastRssi = 0;
NukiLock::LockAction _nextLockAction = (NukiLock::LockAction)0xff; unsigned long _disableBleWatchdogTs = 0;
volatile NukiLock::LockAction _nextLockAction = (NukiLock::LockAction)0xff;
}; };

View File

@@ -1,6 +1,7 @@
#include <Arduino.h> #include <Arduino.h>
#include "Ota.h" #include "Ota.h"
#include "Logger.h" #include "Logger.h"
#include "RestartReason.h"
#define FULL_PACKET 1436 // HTTP_UPLOAD_BUFLEN in WebServer,h #define FULL_PACKET 1436 // HTTP_UPLOAD_BUFLEN in WebServer,h
@@ -26,7 +27,7 @@ void Ota::updateFirmware(uint8_t* buf, size_t size)
if (ESP_OK == esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL))) if (ESP_OK == esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL)))
{ {
delay(2000); delay(2000);
esp_restart(); restartEsp(RestartReason::OTACompleted);
} }
else else
{ {

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <vector>
#define preference_started_before "run" #define preference_started_before "run"
#define preference_deviceId "deviceId" #define preference_deviceId "deviceId"
#define preference_mqtt_broker "mqttbroker" #define preference_mqtt_broker "mqttbroker"
@@ -11,7 +13,8 @@
#define preference_mqtt_lock_path "mqttpath" #define preference_mqtt_lock_path "mqttpath"
#define preference_opener_enabled "openerena" #define preference_opener_enabled "openerena"
#define preference_mqtt_opener_path "mqttoppath" #define preference_mqtt_opener_path "mqttoppath"
#define preference_max_keypad_code_count "maxkpad" #define preference_lock_max_keypad_code_count "maxkpad"
#define preference_opener_max_keypad_code_count "opmaxkpad"
#define preference_mqtt_ca "mqttca" #define preference_mqtt_ca "mqttca"
#define preference_mqtt_crt "mqttcrt" #define preference_mqtt_crt "mqttcrt"
#define preference_mqtt_key "mqttkey" #define preference_mqtt_key "mqttkey"
@@ -25,6 +28,7 @@
#define preference_restart_timer "resttmr" #define preference_restart_timer "resttmr"
#define preference_restart_ble_beacon_lost "rstbcn" #define preference_restart_ble_beacon_lost "rstbcn"
#define preference_query_interval_lockstate "lockStInterval" #define preference_query_interval_lockstate "lockStInterval"
#define preference_query_interval_configuration "configInterval"
#define preference_query_interval_battery "batInterval" #define preference_query_interval_battery "batInterval"
#define preference_query_interval_keypad "kpInterval" #define preference_query_interval_keypad "kpInterval"
#define preference_keypad_control_enabled "kpEnabled" #define preference_keypad_control_enabled "kpEnabled"
@@ -35,11 +39,183 @@
#define preference_cred_password "crdpass" #define preference_cred_password "crdpass"
#define preference_publish_authdata "pubauth" #define preference_publish_authdata "pubauth"
#define preference_gpio_locking_enabled "gpiolck" #define preference_gpio_locking_enabled "gpiolck"
#define preference_publish_debug_info "pubdbg"
#define preference_presence_detection_timeout "prdtimeout" #define preference_presence_detection_timeout "prdtimeout"
#define preference_has_mac_saved "hasmac" #define preference_has_mac_saved "hasmac"
#define preference_has_mac_byte_0 "macb0" #define preference_has_mac_byte_0 "macb0"
#define preference_has_mac_byte_1 "macb1" #define preference_has_mac_byte_1 "macb1"
#define preference_has_mac_byte_2 "macb2" #define preference_has_mac_byte_2 "macb2"
class DebugPreferences
{
private:
std::vector<char*> _keys =
{
preference_started_before, preference_deviceId, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_lock_enabled, preference_mqtt_lock_path, preference_opener_enabled, preference_mqtt_opener_path, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_network_hardware, preference_network_hardware_gpio, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, preference_restart_timer, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, preference_register_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user, preference_cred_password, preference_publish_authdata, preference_gpio_locking_enabled, preference_publish_debug_info, preference_presence_detection_timeout, preference_has_mac_saved, preference_has_mac_byte_0, preference_has_mac_byte_1, preference_has_mac_byte_2,
};
std::vector<char*> _redact =
{
preference_mqtt_user, preference_mqtt_password,
preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key,
preference_cred_user, preference_cred_password,
};
std::vector<char*> _boolPrefs =
{
preference_started_before, preference_mqtt_log_enabled, preference_lock_enabled, preference_opener_enabled,
preference_restart_on_disconnect, preference_keypad_control_enabled, preference_register_as_app,
preference_publish_authdata, preference_gpio_locking_enabled, preference_has_mac_saved, preference_publish_debug_info
};
const bool isRedacted(const char* key) const
{
return std::find(_redact.begin(), _redact.end(), key) != _redact.end();
}
const String redact(const String s) const
{
return s == "" ? "" : "***";
}
const String redact(const int i) const
{
return i == 0 ? "" : "***";
}
const String redact(const uint i) const
{
return i == 0 ? "" : "***";
}
const String redact(const int64_t i) const
{
return i == 0 ? "" : "***";
}
const String redact(const uint64_t i) const
{
return i == 0 ? "" : "***";
}
const void appendPreferenceInt8(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getChar(key)) : String(preferences->getChar(key)));
s.concat("\n");
}
const void appendPreferenceUInt8(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getUChar(key)) : String(preferences->getUChar(key)));
s.concat("\n");
}
const void appendPreferenceInt16(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getShort(key)) : String(preferences->getShort(key)));
s.concat("\n");
}
const void appendPreferenceUInt16(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getUShort(key)) : String(preferences->getUShort(key)));
s.concat("\n");
}
const void appendPreferenceInt32(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getInt(key)) : String(preferences->getInt(key)));
s.concat("\n");
}
const void appendPreferenceUInt32(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getUInt(key)) : String(preferences->getUInt(key)));
s.concat("\n");
}
const void appendPreferenceInt64(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getLong64(key)) : String(preferences->getLong64(key)));
s.concat("\n");
}
const void appendPreferenceUInt64(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getULong64(key)) : String(preferences->getULong64(key)));
s.concat("\n");
}
const void appendPreferenceBool(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(preferences->getBool(key) ? "true" : "false");
s.concat("\n");
}
const void appendPreferenceString(Preferences *preferences, String& s, const char* description, const char* key)
{
s.concat(description);
s.concat(": ");
s.concat(isRedacted(key) ? redact(preferences->getString(key)) : preferences->getString(key));
s.concat("\n");
}
const void appendPreference(Preferences *preferences, String& s, const char* key)
{
if(std::find(_boolPrefs.begin(), _boolPrefs.end(), key) != _boolPrefs.end())
{
appendPreferenceBool(preferences, s, key, key);
return;
}
switch(preferences->getType(key))
{
case PT_I8:
appendPreferenceInt8(preferences, s, key, key);
break;
case PT_I16:
appendPreferenceInt16(preferences, s, key, key);
break;
case PT_I32:
appendPreferenceInt32(preferences, s, key, key);
break;
case PT_I64:
appendPreferenceInt64(preferences, s, key, key);
break;
case PT_U8:
appendPreferenceUInt8(preferences, s, key, key);
break;
case PT_U16:
appendPreferenceUInt16(preferences, s, key, key);
break;
case PT_U32:
appendPreferenceUInt32(preferences, s, key, key);
break;
case PT_U64:
appendPreferenceUInt64(preferences, s, key, key);
break;
case PT_STR:
appendPreferenceString(preferences, s, key, key);
break;
default:
appendPreferenceString(preferences, s, key, key);
break;
}
}
public:
const String preferencesToString(Preferences *preferences)
{
String s = "";
for(const auto& key : _keys)
{
appendPreference(preferences, s, key);
}
return s;
}
};

View File

@@ -12,7 +12,7 @@ NUKI Smart Lock 2.0<br>
NUKI Smart Lock 3.0<br> NUKI Smart Lock 3.0<br>
NUKI Smart Lock 3.0 Pro<br> NUKI Smart Lock 3.0 Pro<br>
NUKI Opener<br> NUKI Opener<br>
NUKI Keypad 1.0 NUKI Keypad 1.0 (2.0 not supported yet due to current [NUKI limitations](https://developer.nuki.io/t/keypad-2-not-setting-has-keypad-flag-in-config-of-nuki-lock/19539))
## Installation ## Installation
@@ -124,6 +124,8 @@ NOTE: MQTT Discovery uses retained MQTT messages to store devices configurations
## Keypad control (optional) ## Keypad control (optional)
Note: Only devices listed in the [About section](#about) are currently supported.
If a keypad is connected to the lock, keypad codes can be added, updated and removed. If a keypad is connected to the lock, keypad codes can be added, updated and removed.
This has to enabled first in the configuration portal. Check "Enabled keypad control via MQTT" and save the configuration. This has to enabled first in the configuration portal. Check "Enabled keypad control via MQTT" and save the configuration.
After enabling keypad control, information about codes is published under "keypad/code_x", x starting from 0 up the number of configured codes. After enabling keypad control, information about codes is published under "keypad/code_x", x starting from 0 up the number of configured codes.

109
RestartReason.h Normal file
View File

@@ -0,0 +1,109 @@
#pragma once
enum class RestartReason
{
RequestedViaMqtt,
BLEBeaconWatchdog,
RestartOnDisconnectWatchdog,
RestartIntervalWatchdog,
NetworkTimeoutWatchdog,
WifiInitFailed,
ReconfigureWifi,
NetworkDeviceCriticalFailure,
ConfigurationUpdated,
RestartTimer,
OTACompleted,
OTATimeout,
OTAAborted,
OTAUnknownState,
DeviceUnpaired,
};
#define RESTART_REASON_VALID_DETECT 0xa00ab00bc00bd00d;
extern int restartReason;
extern uint64_t restartReasonValid;
inline static void restartEsp(RestartReason reason)
{
restartReason = (int)reason;
restartReasonValid = RESTART_REASON_VALID_DETECT;
ESP.restart();
}
inline static String getRestartReason()
{
uint64_t cmp = RESTART_REASON_VALID_DETECT;
if(restartReasonValid != cmp)
{
return "UnknownNoRestartRegistered";
}
switch((RestartReason)restartReason)
{
case RestartReason::RequestedViaMqtt:
return "RequestedViaMqtt";
case RestartReason::BLEBeaconWatchdog:
return "BLEBeaconWatchdog";
case RestartReason::RestartOnDisconnectWatchdog:
return "RestartOnDisconnectWatchdog";
case RestartReason::RestartIntervalWatchdog:
return "RestartIntervalWatchdog";
case RestartReason::NetworkTimeoutWatchdog:
return "NetworkTimeoutWatchdog";
case RestartReason::WifiInitFailed:
return "WifiInitFailed";
case RestartReason::ReconfigureWifi:
return "ReconfigureWifi";
case RestartReason::NetworkDeviceCriticalFailure:
return "NetworkDeviceCriticalFailure";
case RestartReason::ConfigurationUpdated:
return "ConfigurationUpdated";
case RestartReason::RestartTimer:
return "RestartTimer";
case RestartReason::OTACompleted:
return "OTACompleted";
case RestartReason::OTATimeout:
return "OTATimeout";
case RestartReason::OTAAborted:
return "OTAAborted";
case RestartReason::OTAUnknownState:
return "OTAUnknownState";
case RestartReason::DeviceUnpaired:
return "DeviceUnpaired";
default:
return "Unknown: " + restartReason;
}
}
inline static String getEspRestartReason()
{
esp_reset_reason_t reason = esp_reset_reason();
switch(reason)
{
case esp_reset_reason_t::ESP_RST_UNKNOWN:
return "ESP_RST_UNKNOWN: Reset reason can not be determined.";
case esp_reset_reason_t::ESP_RST_POWERON:
return "ESP_RST_POWERON: Reset due to power-on event.";
case esp_reset_reason_t::ESP_RST_EXT:
return "ESP_RST_EXT: Reset by external pin";
case esp_reset_reason_t::ESP_RST_SW:
return "ESP_RST_SW: Software reset via esp_restart.";
case esp_reset_reason_t::ESP_RST_PANIC:
return "ESP_RST_PANIC: Software reset due to exception/panic.";
case esp_reset_reason_t::ESP_RST_INT_WDT:
return "ESP_RST_INT_WDT: Reset (software or hardware) due to interrupt watchdog";
case esp_reset_reason_t::ESP_RST_TASK_WDT:
return "ESP_RST_TASK_WDT: Reset due to task watchdog.";
case esp_reset_reason_t::ESP_RST_WDT:
return "ESP_RST_WDT: Reset due to other watchdogs.";
case esp_reset_reason_t::ESP_RST_DEEPSLEEP:
return "ESP_RST_DEEPSLEEP: Reset after exiting deep sleep mode.";
case esp_reset_reason_t::ESP_RST_BROWNOUT:
return "ESP_RST_BROWNOUT: Brownout reset (software or hardware)";
case esp_reset_reason_t::ESP_RST_SDIO:
return "ESP_RST_SDIO: Reset over SDIO.";
default:
return "Unknown: " + (int)reason;
}
}

View File

@@ -4,6 +4,7 @@
#include "hardware/WifiEthServer.h" #include "hardware/WifiEthServer.h"
#include "Logger.h" #include "Logger.h"
#include "Config.h" #include "Config.h"
#include "RestartReason.h"
#include <esp_task_wdt.h> #include <esp_task_wdt.h>
WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, Network* network, EthServer* ethServer, Preferences* preferences, bool allowRestartToPortal) WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, Network* network, EthServer* ethServer, Preferences* preferences, bool allowRestartToPortal)
@@ -119,8 +120,8 @@ void WebCfgServer::initialize()
return _server.requestAuthentication(); return _server.requestAuthentication();
} }
String message = ""; String message = "";
bool restartEsp = processArgs(message); bool restart = processArgs(message);
if(restartEsp) if(restart)
{ {
String response = ""; String response = "";
buildConfirmHtml(response, message); buildConfirmHtml(response, message);
@@ -128,7 +129,7 @@ void WebCfgServer::initialize()
Log->println(F("Restarting")); Log->println(F("Restarting"));
waitAndProcess(true, 1000); waitAndProcess(true, 1000);
ESP.restart(); restartEsp(RestartReason::ConfigurationUpdated);
} }
else else
{ {
@@ -159,13 +160,43 @@ void WebCfgServer::initialize()
handleOtaUpload(); handleOtaUpload();
}); });
_server.on("/info", [&]() {
if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) {
return _server.requestAuthentication();
}
String response = "";
buildInfoHtml(response);
_server.send(200, "text/html", response);
});
_server.on("/debugon", [&]() {
_preferences->putBool(preference_publish_debug_info, true);
String response = "";
buildConfirmHtml(response, "OK");
_server.send(200, "text/html", response);
Log->println(F("Restarting"));
waitAndProcess(true, 1000);
restartEsp(RestartReason::ConfigurationUpdated);
});
_server.on("/debugoff", [&]() {
_preferences->putBool(preference_publish_debug_info, false);
String response = "";
buildConfirmHtml(response, "OK");
_server.send(200, "text/html", response);
Log->println(F("Restarting"));
waitAndProcess(true, 1000);
restartEsp(RestartReason::ConfigurationUpdated);
});
_server.begin(); _server.begin();
_network->setKeepAliveCallback([&]() _network->setKeepAliveCallback([&]()
{ {
update(); update();
}); });
} }
bool WebCfgServer::processArgs(String& message) bool WebCfgServer::processArgs(String& message)
@@ -301,6 +332,11 @@ bool WebCfgServer::processArgs(String& message)
_preferences->putInt(preference_query_interval_lockstate, value.toInt()); _preferences->putInt(preference_query_interval_lockstate, value.toInt());
configChanged = true; configChanged = true;
} }
else if(key == "CFGINT")
{
_preferences->putInt(preference_query_interval_configuration, value.toInt());
configChanged = true;
}
else if(key == "BATINT") else if(key == "BATINT")
{ {
_preferences->putInt(preference_query_interval_battery, value.toInt()); _preferences->putInt(preference_query_interval_battery, value.toInt());
@@ -445,7 +481,7 @@ void WebCfgServer::update()
{ {
Log->println(F("OTA time out, restarting")); Log->println(F("OTA time out, restarting"));
delay(200); delay(200);
ESP.restart(); restartEsp(RestartReason::OTATimeout);
} }
if(!_enabled) return; if(!_enabled) return;
@@ -479,7 +515,7 @@ void WebCfgServer::buildHtml(String& response)
printParameter(response, "NUKI Opener paired", _nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No"); printParameter(response, "NUKI Opener paired", _nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No");
printParameter(response, "NUKI Opener state", lockstateArr); printParameter(response, "NUKI Opener state", lockstateArr);
} }
printParameter(response, "Firmware", version.c_str()); printParameter(response, "Firmware", version.c_str(), "/info");
response.concat("</table><br><br>"); response.concat("</table><br><br>");
response.concat("<h3>MQTT and Network Configuration</h3>"); response.concat("<h3>MQTT and Network Configuration</h3>");
@@ -511,8 +547,8 @@ void WebCfgServer::buildCredHtml(String &response)
response.concat("<FORM ACTION=method=get >"); response.concat("<FORM ACTION=method=get >");
response.concat("<h3>Credentials</h3>"); response.concat("<h3>Credentials</h3>");
response.concat("<table>"); response.concat("<table>");
printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30); printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, false, true);
printInputField(response, "CREDPASS", "Password (max 30 characters)", "*", 30, true); printInputField(response, "CREDPASS", "Password", "*", 30, true, true);
printInputField(response, "CREDPASSRE", "Retype password", "*", 30, true); printInputField(response, "CREDPASSRE", "Retype password", "*", 30, true);
response.concat("</table>"); response.concat("</table>");
response.concat("<br><INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Save\">"); response.concat("<br><INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Save\">");
@@ -602,16 +638,16 @@ void WebCfgServer::buildMqttConfigHtml(String &response)
printInputField(response, "HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100); printInputField(response, "HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100);
printInputField(response, "MQTTSERVER", "MQTT Broker", _preferences->getString(preference_mqtt_broker).c_str(), 100); printInputField(response, "MQTTSERVER", "MQTT Broker", _preferences->getString(preference_mqtt_broker).c_str(), 100);
printInputField(response, "MQTTPORT", "MQTT Broker port", _preferences->getInt(preference_mqtt_broker_port), 5); printInputField(response, "MQTTPORT", "MQTT Broker port", _preferences->getInt(preference_mqtt_broker_port), 5);
printInputField(response, "MQTTUSER", "MQTT User (# to clear)", _preferences->getString(preference_mqtt_user).c_str(), 30); printInputField(response, "MQTTUSER", "MQTT User (# to clear)", _preferences->getString(preference_mqtt_user).c_str(), 30, false, true);
printInputField(response, "MQTTPASS", "MQTT Password", "*", 30, true); printInputField(response, "MQTTPASS", "MQTT Password", "*", 30, true, true);
response.concat("</table><br>"); response.concat("</table><br>");
response.concat("<h3>Advanced MQTT and Network Configuration</h3>"); response.concat("<h3>Advanced MQTT and Network Configuration</h3>");
response.concat("<table>"); response.concat("<table>");
printInputField(response, "HASSDISCOVERY", "Home Assistant discovery topic (empty to disable; usually homeassistant)", _preferences->getString(preference_mqtt_hass_discovery).c_str(), 30); printInputField(response, "HASSDISCOVERY", "Home Assistant discovery topic (empty to disable; usually homeassistant)", _preferences->getString(preference_mqtt_hass_discovery).c_str(), 30);
printTextarea(response, "MQTTCA", "MQTT SSL CA Certificate (*, optional)", _preferences->getString(preference_mqtt_ca).c_str(), TLS_CA_MAX_SIZE, _network->encryptionSupported()); printTextarea(response, "MQTTCA", "MQTT SSL CA Certificate (*, optional)", _preferences->getString(preference_mqtt_ca).c_str(), TLS_CA_MAX_SIZE, _network->encryptionSupported(), true);
printTextarea(response, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", _preferences->getString(preference_mqtt_crt).c_str(), TLS_CERT_MAX_SIZE, _network->encryptionSupported()); printTextarea(response, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", _preferences->getString(preference_mqtt_crt).c_str(), TLS_CERT_MAX_SIZE, _network->encryptionSupported(), true);
printTextarea(response, "MQTTKEY", "MQTT SSL Client Key (*, optional)", _preferences->getString(preference_mqtt_key).c_str(), TLS_KEY_MAX_SIZE, _network->encryptionSupported()); printTextarea(response, "MQTTKEY", "MQTT SSL Client Key (*, optional)", _preferences->getString(preference_mqtt_key).c_str(), TLS_KEY_MAX_SIZE, _network->encryptionSupported(), true);
printDropDown(response, "NWHW", "Network hardware", String(_preferences->getInt(preference_network_hardware)), getNetworkDetectionOptions()); printDropDown(response, "NWHW", "Network hardware", String(_preferences->getInt(preference_network_hardware)), getNetworkDetectionOptions());
printDropDown(response, "NWHWDT", "Network hardware detection", String(_preferences->getInt(preference_network_hardware_gpio)), getNetworkGpioOptions()); printDropDown(response, "NWHWDT", "Network hardware detection", String(_preferences->getInt(preference_network_hardware_gpio)), getNetworkGpioOptions());
printInputField(response, "RSSI", "RSSI Publish interval (seconds; -1 to disable)", _preferences->getInt(preference_rssi_publish_interval), 6); printInputField(response, "RSSI", "RSSI Publish interval (seconds; -1 to disable)", _preferences->getInt(preference_rssi_publish_interval), 6);
@@ -652,8 +688,9 @@ void WebCfgServer::buildNukiConfigHtml(String &response)
printCheckBox(response, "REGAPP", "Register as app (on: register as app, off: register as bridge; needs re-pairing if changed)", _preferences->getBool(preference_register_as_app)); printCheckBox(response, "REGAPP", "Register as app (on: register as app, off: register as bridge; needs re-pairing if changed)", _preferences->getBool(preference_register_as_app));
printInputField(response, "LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10); printInputField(response, "LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10);
printInputField(response, "CFGINT", "Query interval configuration (seconds)", _preferences->getInt(preference_query_interval_configuration), 10);
printInputField(response, "BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10); printInputField(response, "BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10);
if(_nuki != nullptr && _nuki->hasKeypad()) if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad()))
{ {
printInputField(response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10); printInputField(response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10);
printCheckBox(response, "KPENA", "Enabled keypad control via MQTT", _preferences->getBool(preference_keypad_control_enabled)); printCheckBox(response, "KPENA", "Enabled keypad control via MQTT", _preferences->getBool(preference_keypad_control_enabled));
@@ -698,6 +735,60 @@ void WebCfgServer::buildConfigureWifiHtml(String &response)
response.concat("</BODY></HTML>"); response.concat("</BODY></HTML>");
} }
void WebCfgServer::buildInfoHtml(String &response)
{
DebugPreferences debugPreferences;
buildHtmlHeader(response);
response.concat("<h3>System Information</h3> <pre>");
response.concat("Firmware version: ");
response.concat(NUKI_HUB_VERSION);
response.concat("\n");
response.concat(debugPreferences.preferencesToString(_preferences));
response.concat("MQTT connected: ");
response.concat(_network->mqttConnectionState() > 0 ? "Yes\n" : "No\n");
if(_nuki != nullptr)
{
response.concat("Lock paired: ");
response.concat(_nuki->isPaired() ? "Yes\n" : "No\n");
response.concat("Lock PIN set: ");
response.concat(_nuki->isPaired() ? _nuki->isPinSet() ? "Yes\n" : "No\n" : "-\n");
}
if(_nukiOpener != nullptr)
{
response.concat("Opener paired: ");
response.concat(_nukiOpener->isPaired() ? "Yes\n" : "No\n");
response.concat("Opener PIN set: ");
response.concat(_nukiOpener->isPaired() ? _nukiOpener->isPinSet() ? "Yes\n" : "No\n" : "-\n");
}
response.concat("Network device: ");
response.concat(_network->networkDeviceName());
response.concat("\n");
response.concat("Uptime: ");
response.concat(millis() / 1000 / 60);
response.concat(" minutes\n");
response.concat("Heap: ");
response.concat(esp_get_free_heap_size());
response.concat("\n");
response.concat("Restart reason FW: ");
response.concat(getRestartReason());
response.concat("\n");
response.concat("Restart reason ESP: ");
response.concat(getEspRestartReason());
response.concat("\n");
response.concat("</pre> </BODY></HTML>");
}
void WebCfgServer::processUnpair(bool opener) void WebCfgServer::processUnpair(bool opener)
{ {
String response = ""; String response = "";
@@ -733,7 +824,7 @@ void WebCfgServer::processUnpair(bool opener)
_nukiOpener->unpair(); _nukiOpener->unpair();
} }
waitAndProcess(false, 1000); waitAndProcess(false, 1000);
ESP.restart(); restartEsp(RestartReason::DeviceUnpaired);
} }
void WebCfgServer::buildHtmlHeader(String &response) void WebCfgServer::buildHtmlHeader(String &response)
@@ -751,8 +842,9 @@ void WebCfgServer::printInputField(String& response,
const char *token, const char *token,
const char *description, const char *description,
const char *value, const char *value,
const size_t maxLength, const size_t& maxLength,
const bool isPassword) const bool& isPassword,
const bool& showLengthRestriction)
{ {
char maxLengthStr[20]; char maxLengthStr[20];
@@ -760,6 +852,14 @@ void WebCfgServer::printInputField(String& response,
response.concat("<tr><td>"); response.concat("<tr><td>");
response.concat(description); response.concat(description);
if(showLengthRestriction)
{
response.concat(" (Max. ");
response.concat(maxLength);
response.concat(" characters)");
}
response.concat("</td><td>"); response.concat("</td><td>");
response.concat("<INPUT TYPE="); response.concat("<INPUT TYPE=");
response.concat(isPassword ? "PASSWORD" : "TEXT"); response.concat(isPassword ? "PASSWORD" : "TEXT");
@@ -806,8 +906,9 @@ void WebCfgServer::printTextarea(String& response,
const char *token, const char *token,
const char *description, const char *description,
const char *value, const char *value,
const size_t maxLength, const size_t& maxLength,
const bool enabled) const bool& enabled,
const bool& showLengthRestriction)
{ {
char maxLengthStr[20]; char maxLengthStr[20];
@@ -815,6 +916,12 @@ void WebCfgServer::printTextarea(String& response,
response.concat("<tr><td>"); response.concat("<tr><td>");
response.concat(description); response.concat(description);
if(showLengthRestriction)
{
response.concat(" (Max. ");
response.concat(maxLength);
response.concat(" characters)");
}
response.concat("</td><td>"); response.concat("</td><td>");
response.concat(" <TEXTAREA "); response.concat(" <TEXTAREA ");
if(!enabled) if(!enabled)
@@ -872,14 +979,25 @@ void WebCfgServer::buildNavigationButton(String &response, const char *caption,
response.concat("</form>"); response.concat("</form>");
} }
void WebCfgServer::printParameter(String& response, const char *description, const char *value) void WebCfgServer::printParameter(String& response, const char *description, const char *value, const char *link)
{ {
response.concat("<tr>"); response.concat("<tr>");
response.concat("<td>"); response.concat("<td>");
response.concat(description); response.concat(description);
response.concat("</td>"); response.concat("</td>");
response.concat("<td>"); response.concat("<td>");
response.concat(value); if(strcmp(link, "") == 0)
{
response.concat(value);
}
else
{
response.concat("<a href=\"");
response.concat(link);
response.concat("\"> ");
response.concat(value);
response.concat("</a>");
}
response.concat("</td>"); response.concat("</td>");
response.concat("</tr>"); response.concat("</tr>");
@@ -928,23 +1046,41 @@ void WebCfgServer::handleOtaUpload()
return; return;
} }
if (upload.status == UPLOAD_FILE_START) { if (upload.status == UPLOAD_FILE_START)
{
String filename = upload.filename; String filename = upload.filename;
if (!filename.startsWith("/")) { if (!filename.startsWith("/"))
{
filename = "/" + filename; filename = "/" + filename;
} }
_otaStartTs = millis(); _otaStartTs = millis();
esp_task_wdt_init(30, false); esp_task_wdt_init(30, false);
_network->disableAutoRestarts(); _network->disableAutoRestarts();
Log->print("handleFileUpload Name: "); Log->println(filename); Log->print("handleFileUpload Name: "); Log->println(filename);
} else if (upload.status == UPLOAD_FILE_WRITE) { }
else if (upload.status == UPLOAD_FILE_WRITE)
{
_transferredSize = _transferredSize + upload.currentSize; _transferredSize = _transferredSize + upload.currentSize;
Log->println(_transferredSize); Log->println(_transferredSize);
_ota.updateFirmware(upload.buf, upload.currentSize); _ota.updateFirmware(upload.buf, upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) { } else if (upload.status == UPLOAD_FILE_END)
{
Log->println(); Log->println();
Log->print("handleFileUpload Size: "); Log->println(upload.totalSize); Log->print("handleFileUpload Size: "); Log->println(upload.totalSize);
} }
else if(upload.status == UPLOAD_FILE_ABORTED)
{
Log->println();
Log->println("OTA aborted, restarting ESP.");
restartEsp(RestartReason::OTAAborted);
}
else
{
Log->println();
Log->print("OTA unknown state: ");
Log->println((int)upload.status);
restartEsp(RestartReason::OTAUnknownState);
}
} }
void WebCfgServer::sendCss() void WebCfgServer::sendCss()

View File

@@ -37,22 +37,23 @@ private:
void buildNukiConfigHtml(String& response); void buildNukiConfigHtml(String& response);
void buildConfirmHtml(String& response, const String &message, uint32_t redirectDelay = 5); void buildConfirmHtml(String& response, const String &message, uint32_t redirectDelay = 5);
void buildConfigureWifiHtml(String& response); void buildConfigureWifiHtml(String& response);
void buildInfoHtml(String& response);
void sendCss(); void sendCss();
void sendFavicon(); void sendFavicon();
void processUnpair(bool opener); void processUnpair(bool opener);
void buildHtmlHeader(String& response); void buildHtmlHeader(String& response);
void printInputField(String& response, const char* token, const char* description, const char* value, const size_t maxLength, const bool isPassword = false); void printInputField(String& response, const char* token, const char* description, const char* value, const size_t& maxLength, const bool& isPassword = false, const bool& showLengthRestriction = false);
void printInputField(String& response, const char* token, const char* description, const int value, size_t maxLength); void printInputField(String& response, const char* token, const char* description, const int value, size_t maxLength);
void printCheckBox(String& response, const char* token, const char* description, const bool value); void printCheckBox(String& response, const char* token, const char* description, const bool value);
void printTextarea(String& response, const char *token, const char *description, const char *value, const size_t maxLength, const bool enabled = true); void printTextarea(String& response, const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false);
void printDropDown(String &response, const char *token, const char *description, const String preselectedValue, std::vector<std::pair<String, String>> options); void printDropDown(String &response, const char *token, const char *description, const String preselectedValue, std::vector<std::pair<String, String>> options);
void buildNavigationButton(String& response, const char* caption, const char* targetPath); void buildNavigationButton(String& response, const char* caption, const char* targetPath);
const std::vector<std::pair<String, String>> getNetworkDetectionOptions() const; const std::vector<std::pair<String, String>> getNetworkDetectionOptions() const;
const std::vector<std::pair<String, String>> getNetworkGpioOptions() const; const std::vector<std::pair<String, String>> getNetworkGpioOptions() const;
void printParameter(String& response, const char* description, const char* value); void printParameter(String& response, const char* description, const char* value, const char *link = "");
String generateConfirmCode(); String generateConfirmCode();
void waitAndProcess(const bool blocking, const uint32_t duration); void waitAndProcess(const bool blocking, const uint32_t duration);

View File

@@ -26,7 +26,9 @@
#include <libb64/cencode.h> #include <libb64/cencode.h>
#include "WiFiClient.h" #include "WiFiClient.h"
#include "WebServer.h" #include "WebServer.h"
#include "FS.h" #ifdef WEBSERVER_ENABLE_STATIC_CONTENT
#include "FS.h"
#endif
#include "detail/RequestHandlersImpl.h" #include "detail/RequestHandlersImpl.h"
#include "mbedtls/md5.h" #include "mbedtls/md5.h"
@@ -258,9 +260,11 @@ void WebServer::_addRequestHandler(RequestHandler* handler) {
} }
} }
#ifdef WEBSERVER_ENABLE_STATIC_CONTENT
void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
} }
#endif
void WebServer::handleClient() { void WebServer::handleClient() {
if (_currentStatus == HC_NONE) { if (_currentStatus == HC_NONE) {

View File

@@ -90,7 +90,9 @@ public:
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
void addHandler(RequestHandler* handler); void addHandler(RequestHandler* handler);
#ifdef WEBSERVER_ENABLE_STATIC_CONTENT
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
#endif
void onNotFound(THandlerFunction fn); //called when handler is not assigned void onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction fn); //handle file uploads void onFileUpload(THandlerFunction fn); //handle file uploads

View File

@@ -60,6 +60,7 @@ protected:
HTTPMethod _method; HTTPMethod _method;
}; };
#ifdef WEBSERVER_ENABLE_STATIC_CONTENT
class StaticRequestHandler : public RequestHandler { class StaticRequestHandler : public RequestHandler {
public: public:
StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header) StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
@@ -146,6 +147,6 @@ protected:
bool _isFile; bool _isFile;
size_t _baseUriLength; size_t _baseUriLength;
}; };
#endif
#endif //REQUESTHANDLERSIMPL_H #endif //REQUESTHANDLERSIMPL_H

View File

@@ -11,9 +11,15 @@ the LICENSE file.
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
// Logging is en/disabled by Arduino framework macros // Logging is en/disabled by Arduino framework macros
#include <esp32-hal-log.h> #include <esp32-hal-log.h>
#define emc_log_i(...) log_i(__VA_ARGS__) #if defined(DEBUG_ESP_MQTT_CLIENT)
#define emc_log_e(...) log_e(__VA_ARGS__) #define emc_log_i(...) log_i(__VA_ARGS__)
#define emc_log_w(...) log_w(__VA_ARGS__) #define emc_log_e(...) log_e(__VA_ARGS__)
#define emc_log_w(...) log_w(__VA_ARGS__)
#else
#define emc_log_i(...)
#define emc_log_e(...)
#define emc_log_w(...)
#endif
#elif defined(ARDUINO_ARCH_ESP8266) #elif defined(ARDUINO_ARCH_ESP8266)
#if defined(DEBUG_ESP_PORT) && defined(DEBUG_ESP_MQTT_CLIENT) #if defined(DEBUG_ESP_PORT) && defined(DEBUG_ESP_MQTT_CLIENT)
#include <Arduino.h> #include <Arduino.h>

View File

@@ -12,6 +12,7 @@
#include "Gpio.h" #include "Gpio.h"
#include "Logger.h" #include "Logger.h"
#include "Config.h" #include "Config.h"
#include "RestartReason.h"
Network* network = nullptr; Network* network = nullptr;
NetworkLock* networkLock = nullptr; NetworkLock* networkLock = nullptr;
@@ -28,6 +29,9 @@ bool lockEnabled = false;
bool openerEnabled = false; bool openerEnabled = false;
unsigned long restartTs = (2^32) - 5 * 60000; unsigned long restartTs = (2^32) - 5 * 60000;
RTC_NOINIT_ATTR int restartReason;
RTC_NOINIT_ATTR uint64_t restartReasonValid;
void networkTask(void *pvParameters) void networkTask(void *pvParameters)
{ {
while(true) while(true)
@@ -39,7 +43,17 @@ void networkTask(void *pvParameters)
} }
webCfgServer->update(); webCfgServer->update();
// millis() is about to overflow. Restart device to prevent problems with overflow
if(millis() > restartTs)
{
Log->println(F("Restart timer expired, restarting device."));
delay(200);
restartEsp(RestartReason::RestartTimer);
}
delay(200); delay(200);
// Serial.println(uxTaskGetStackHighWaterMark(NULL));
} }
} }
@@ -65,14 +79,10 @@ void nukiTask(void *pvParameters)
{ {
nukiOpener->update(); nukiOpener->update();
} }
} }
} }
void webServerTask(void *pvParameters)
{
}
void presenceDetectionTask(void *pvParameters) void presenceDetectionTask(void *pvParameters)
{ {
while(true) while(true)
@@ -81,22 +91,6 @@ void presenceDetectionTask(void *pvParameters)
} }
} }
void checkMillisTask(void *pvParameters)
{
while(true)
{
delay(30000);
// millis() is about to overflow. Restart device to prevent problems with overflow
if(millis() > restartTs)
{
Log->println(F("Restart timer expired, restarting device."));
delay(200);
ESP.restart();
}
}
}
void setupTasks() void setupTasks()
{ {
@@ -105,7 +99,6 @@ void setupTasks()
xTaskCreatePinnedToCore(networkTask, "ntw", 8192, NULL, 3, NULL, 1); xTaskCreatePinnedToCore(networkTask, "ntw", 8192, NULL, 3, NULL, 1);
xTaskCreatePinnedToCore(nukiTask, "nuki", 4096, NULL, 2, NULL, 1); xTaskCreatePinnedToCore(nukiTask, "nuki", 4096, NULL, 2, NULL, 1);
xTaskCreatePinnedToCore(presenceDetectionTask, "prdet", 768, NULL, 5, NULL, 1); xTaskCreatePinnedToCore(presenceDetectionTask, "prdet", 768, NULL, 5, NULL, 1);
xTaskCreatePinnedToCore(checkMillisTask, "mlchk", 1024, NULL, 1, NULL, 1);
} }
uint32_t getRandomId() uint32_t getRandomId()

View File

@@ -8,17 +8,17 @@ the LICENSE file.
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
#include "ClientSyncEthernet.h" #include "ClientSyncW5500.h"
#include <lwip/sockets.h> // socket options #include <lwip/sockets.h> // socket options
namespace espMqttClientInternals { namespace espMqttClientInternals {
ClientSyncEthernet::ClientSyncEthernet() ClientSyncW5500::ClientSyncW5500()
: client() { : client() {
// empty // empty
} }
bool ClientSyncEthernet::connect(IPAddress ip, uint16_t port) { bool ClientSyncW5500::connect(IPAddress ip, uint16_t port) {
bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool
if (ret) { if (ret) {
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
@@ -34,7 +34,7 @@ namespace espMqttClientInternals {
return ret; return ret;
} }
bool ClientSyncEthernet::connect(const char* host, uint16_t port) { bool ClientSyncW5500::connect(const char* host, uint16_t port) {
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
if (ret) { if (ret) {
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
@@ -50,27 +50,27 @@ namespace espMqttClientInternals {
return ret; return ret;
} }
size_t ClientSyncEthernet::write(const uint8_t* buf, size_t size) { size_t ClientSyncW5500::write(const uint8_t* buf, size_t size) {
return client.write(buf, size); return client.write(buf, size);
} }
int ClientSyncEthernet::available() { int ClientSyncW5500::available() {
return client.available(); return client.available();
} }
int ClientSyncEthernet::read(uint8_t* buf, size_t size) { int ClientSyncW5500::read(uint8_t* buf, size_t size) {
return client.read(buf, size); return client.read(buf, size);
} }
void ClientSyncEthernet::stop() { void ClientSyncW5500::stop() {
client.stop(); client.stop();
} }
bool ClientSyncEthernet::connected() { bool ClientSyncW5500::connected() {
return client.connected(); return client.connected();
} }
bool ClientSyncEthernet::disconnected() { bool ClientSyncW5500::disconnected() {
return !client.connected(); return !client.connected();
} }

View File

@@ -7,9 +7,9 @@
namespace espMqttClientInternals { namespace espMqttClientInternals {
class ClientSyncEthernet : public Transport { class ClientSyncW5500 : public Transport {
public: public:
ClientSyncEthernet(); ClientSyncW5500();
bool connect(IPAddress ip, uint16_t port) override; bool connect(IPAddress ip, uint16_t port) override;
bool connect(const char* host, uint16_t port) override; bool connect(const char* host, uint16_t port) override;
size_t write(const uint8_t* buf, size_t size) override; size_t write(const uint8_t* buf, size_t size) override;

View File

@@ -17,6 +17,8 @@ public:
: _hostname(hostname) : _hostname(hostname)
{} {}
virtual const String deviceName() const = 0;
virtual void initialize() = 0; virtual void initialize() = 0;
virtual ReconnectStatus reconnect() = 0; virtual ReconnectStatus reconnect() = 0;
virtual void reconfigure() = 0; virtual void reconfigure() = 0;

View File

@@ -32,6 +32,11 @@ W5500Device::W5500Device(const String &hostname, Preferences* preferences, int v
W5500Device::~W5500Device() W5500Device::~W5500Device()
{} {}
const String W5500Device::deviceName() const
{
return "Wiznet W5500";
}
void W5500Device::initialize() void W5500Device::initialize()
{ {
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
@@ -52,11 +57,10 @@ void W5500Device::initialize()
if(_preferences->getBool(preference_mqtt_log_enabled)) if(_preferences->getBool(preference_mqtt_log_enabled))
{ {
_path = new char[200];
memset(_path, 0, sizeof(_path));
String pathStr = _preferences->getString(preference_mqtt_lock_path); String pathStr = _preferences->getString(preference_mqtt_lock_path);
pathStr.concat(mqtt_topic_log); pathStr.concat(mqtt_topic_log);
_path = new char[pathStr.length() + 1];
memset(_path, 0, sizeof(_path));
strcpy(_path, pathStr.c_str()); strcpy(_path, pathStr.c_str());
Log = new MqttLogger(this, _path, MqttLoggerMode::MqttAndSerial); Log = new MqttLogger(this, _path, MqttLoggerMode::MqttAndSerial);
} }

View File

@@ -2,7 +2,7 @@
#include "NetworkDevice.h" #include "NetworkDevice.h"
#include "espMqttClient.h" #include "espMqttClient.h"
#include "espMqttClientEthernet.h" #include "espMqttClientW5500.h"
#include <Ethernet.h> #include <Ethernet.h>
#include <Preferences.h> #include <Preferences.h>
@@ -18,6 +18,8 @@ public:
explicit W5500Device(const String& hostname, Preferences* _preferences, int variant); explicit W5500Device(const String& hostname, Preferences* _preferences, int variant);
~W5500Device(); ~W5500Device();
const String deviceName() const override;
virtual void initialize(); virtual void initialize();
virtual ReconnectStatus reconnect(); virtual ReconnectStatus reconnect();
virtual void reconfigure(); virtual void reconfigure();
@@ -61,7 +63,7 @@ private:
void resetDevice(); void resetDevice();
void initializeMacAddress(byte* mac); void initializeMacAddress(byte* mac);
espMqttClientEthernet _mqttClient; espMqttClientW5500 _mqttClient;
Preferences* _preferences = nullptr; Preferences* _preferences = nullptr;
int _maintainResult = 0; int _maintainResult = 0;

View File

@@ -4,6 +4,7 @@
#include "../Logger.h" #include "../Logger.h"
#include "../MqttTopics.h" #include "../MqttTopics.h"
#include "espMqttClient.h" #include "espMqttClient.h"
#include "../RestartReason.h"
RTC_NOINIT_ATTR char WiFiDevice_reconfdetect[17]; RTC_NOINIT_ATTR char WiFiDevice_reconfdetect[17];
@@ -52,6 +53,11 @@ WifiDevice::WifiDevice(const String& hostname, Preferences* _preferences)
} }
} }
const String WifiDevice::deviceName() const
{
return "Built-in Wifi";
}
void WifiDevice::initialize() void WifiDevice::initialize()
{ {
std::vector<const char *> wm_menu; std::vector<const char *> wm_menu;
@@ -80,7 +86,7 @@ void WifiDevice::initialize()
if(!res) { if(!res) {
Log->println(F("Failed to connect. Wait for ESP restart.")); Log->println(F("Failed to connect. Wait for ESP restart."));
delay(1000); delay(1000);
ESP.restart(); restartEsp(RestartReason::WifiInitFailed);
} }
else { else {
Log->print(F("WiFi connected: ")); Log->print(F("WiFi connected: "));
@@ -103,7 +109,7 @@ void WifiDevice::reconfigure()
{ {
strcpy(WiFiDevice_reconfdetect, "reconfigure_wifi"); strcpy(WiFiDevice_reconfdetect, "reconfigure_wifi");
delay(200); delay(200);
ESP.restart(); restartEsp(RestartReason::ReconfigureWifi);
} }
void WifiDevice::printError() void WifiDevice::printError()
@@ -143,7 +149,7 @@ void WifiDevice::onDisconnected()
{ {
if(millis() > 60000) if(millis() > 60000)
{ {
ESP.restart(); restartEsp(RestartReason::RestartOnDisconnectWatchdog);
} }
} }

View File

@@ -12,6 +12,8 @@ class WifiDevice : public NetworkDevice
public: public:
WifiDevice(const String& hostname, Preferences* _preferences); WifiDevice(const String& hostname, Preferences* _preferences);
const String deviceName() const override;
virtual void initialize(); virtual void initialize();
virtual void reconfigure(); virtual void reconfigure();
virtual ReconnectStatus reconnect(); virtual ReconnectStatus reconnect();

View File

@@ -1,8 +0,0 @@
#include "espMqttClientEthernet.h"
espMqttClientEthernet::espMqttClientEthernet(uint8_t priority, uint8_t core)
: MqttClientSetup(true, priority, core),
_client()
{
_transport = &_client;
}

View File

@@ -0,0 +1,8 @@
#include "espMqttClientW5500.h"
espMqttClientW5500::espMqttClientW5500(uint8_t priority, uint8_t core)
: MqttClientSetup(true, priority, core),
_client()
{
_transport = &_client;
}

View File

@@ -1,19 +1,19 @@
#pragma once #pragma once
#include "MqttClientSetup.h" #include "MqttClientSetup.h"
#include "ClientSyncEthernet.h" #include "ClientSyncW5500.h"
class espMqttClientEthernet : public MqttClientSetup<espMqttClientEthernet> { class espMqttClientW5500 : public MqttClientSetup<espMqttClientW5500> {
public: public:
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
explicit espMqttClientEthernet(uint8_t priority = 1, uint8_t core = 1); explicit espMqttClientW5500(uint8_t priority = 1, uint8_t core = 1);
#else #else
espMqttClient(); espMqttClient();
#endif #endif
protected: protected:
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
espMqttClientInternals::ClientSyncEthernet _client; espMqttClientInternals::ClientSyncW5500 _client;
#elif defined(__linux__) #elif defined(__linux__)
espMqttClientInternals::ClientPosix _client; espMqttClientInternals::ClientPosix _client;
#endif #endif

Binary file not shown.