merge master
This commit is contained in:
@@ -50,8 +50,8 @@ file(GLOB SRCFILES
|
||||
networkDevices/NetworkDevice.h
|
||||
networkDevices/WifiDevice.cpp
|
||||
networkDevices/W5500Device.cpp
|
||||
networkDevices/ClientSyncEthernet.cpp
|
||||
networkDevices/espMqttClientEthernet.cpp
|
||||
networkDevices/ClientSyncW5500.cpp
|
||||
networkDevices/espMqttClientW5500.cpp
|
||||
NukiWrapper.cpp
|
||||
NukiOpenerWrapper.cpp
|
||||
MqttTopics.h
|
||||
@@ -62,6 +62,7 @@ file(GLOB SRCFILES
|
||||
PreferencesKeys.h
|
||||
Gpio.cpp
|
||||
Logger.cpp
|
||||
RestartReason.h
|
||||
# include/RTOS.h
|
||||
lib/WiFiManager/WiFiManager.cpp
|
||||
lib/WiFiManager/wm_consts_en.h
|
||||
@@ -122,15 +123,9 @@ target_link_arduino_libraries(${PROJECT_NAME}
|
||||
WiFi
|
||||
WiFiClientSecure
|
||||
Update
|
||||
# WebServer
|
||||
DNSServer
|
||||
Preferences
|
||||
SPIFFS
|
||||
SPI
|
||||
# Ethernet
|
||||
# esp32
|
||||
# Wire
|
||||
# FS
|
||||
)
|
||||
|
||||
target_link_arduino_libraries(DNSServer PUBLIC WiFi)
|
||||
|
||||
2
Config.h
2
Config.h
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#define NUKI_HUB_VERSION "8.3"
|
||||
#define NUKI_HUB_VERSION "8.8"
|
||||
|
||||
#define MQTT_QOS_LEVEL 1
|
||||
#define MQTT_CLEAN_SESSIONS false
|
||||
3
Gpio.cpp
3
Gpio.cpp
@@ -27,7 +27,6 @@ void Gpio::isrLock()
|
||||
if(millis() < _lockedTs) return;
|
||||
_nuki->lock();
|
||||
_lockedTs = millis() + _debounceTime;
|
||||
Log->println(F("Lock via GPIO"));;
|
||||
}
|
||||
|
||||
void Gpio::isrUnlock()
|
||||
@@ -35,7 +34,6 @@ void Gpio::isrUnlock()
|
||||
if(millis() < _lockedTs) return;
|
||||
_nuki->unlock();
|
||||
_lockedTs = millis() + _debounceTime;
|
||||
Log->println(F("Unlock via GPIO"));;
|
||||
}
|
||||
|
||||
void Gpio::isrUnlatch()
|
||||
@@ -43,5 +41,4 @@ void Gpio::isrUnlatch()
|
||||
if(millis() < _lockedTs) return;
|
||||
_nuki->unlatch();
|
||||
_lockedTs = millis() + _debounceTime;
|
||||
Log->println(F("Unlatch via GPIO"));;
|
||||
}
|
||||
|
||||
@@ -45,4 +45,6 @@
|
||||
#define mqtt_topic_uptime "/maintenance/uptime"
|
||||
#define mqtt_topic_wifi_rssi "/maintenance/wifiRssi"
|
||||
#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"
|
||||
33
Network.cpp
33
Network.cpp
@@ -6,6 +6,7 @@
|
||||
#include "Logger.h"
|
||||
#include "Config.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include "RestartReason.h"
|
||||
|
||||
Network* Network::_inst = nullptr;
|
||||
|
||||
@@ -65,8 +66,7 @@ void Network::setupDevice()
|
||||
Log->println(F(" for network device selection"));
|
||||
|
||||
pinMode(hardwareDetectGpio, INPUT_PULLUP);
|
||||
_networkDeviceType = NetworkDeviceType::W5500;
|
||||
// digitalRead(hardwareDetectGpio) == HIGH ? NetworkDeviceType::WiFi : NetworkDeviceType::W5500;
|
||||
_networkDeviceType = digitalRead(hardwareDetectGpio) == HIGH ? NetworkDeviceType::WiFi : NetworkDeviceType::W5500;
|
||||
}
|
||||
else if(hardwareDetect == 3)
|
||||
{
|
||||
@@ -173,6 +173,8 @@ void Network::initialize()
|
||||
_networkTimeout = -1;
|
||||
_preferences->putInt(preference_network_timeout, _networkTimeout);
|
||||
}
|
||||
|
||||
_publishDebugInfo = _preferences->getBool(preference_publish_debug_info);
|
||||
}
|
||||
|
||||
bool Network::update()
|
||||
@@ -185,7 +187,7 @@ bool Network::update()
|
||||
{
|
||||
if(_restartOnDisconnect && millis() > 60000)
|
||||
{
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::RestartOnDisconnectWatchdog);
|
||||
}
|
||||
|
||||
Log->println(F("Network not connected. Trying reconnect."));
|
||||
@@ -197,7 +199,7 @@ bool Network::update()
|
||||
strcpy(WiFi_fallbackDetect, "wifi_fallback");
|
||||
Log->println("Network device has a critical failure, enable fallback to Wifi and reboot.");
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::NetworkDeviceCriticalFailure);
|
||||
break;
|
||||
case ReconnectStatus::Success:
|
||||
memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect));
|
||||
@@ -216,7 +218,7 @@ bool Network::update()
|
||||
{
|
||||
Log->println("Network timeout has been reached, restarting ...");
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::NetworkTimeoutWatchdog);
|
||||
}
|
||||
|
||||
bool success = reconnect();
|
||||
@@ -254,7 +256,12 @@ bool Network::update()
|
||||
if(_lastMaintenanceTs == 0 || (ts - _lastMaintenanceTs) > 30000)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -367,6 +374,10 @@ bool Network::reconnect()
|
||||
}
|
||||
delay(1000);
|
||||
_mqttConnectionState = 2;
|
||||
for(const auto& callback : _reconnectedCallbacks)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -471,6 +482,11 @@ bool Network::encryptionSupported()
|
||||
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)
|
||||
{
|
||||
char str[30];
|
||||
@@ -924,3 +940,8 @@ void Network::setKeepAliveCallback(std::function<void()> reconnectTick)
|
||||
{
|
||||
_keepAliveCallback = reconnectTick;
|
||||
}
|
||||
|
||||
void Network::addReconnectedCallback(std::function<void()> reconnectedCallback)
|
||||
{
|
||||
_reconnectedCallbacks.push_back(reconnectedCallback);
|
||||
}
|
||||
|
||||
@@ -45,12 +45,14 @@ public:
|
||||
|
||||
int mqttConnectionState(); // 0 = not connected; 1 = connected; 2 = connected and mqtt processed
|
||||
bool encryptionSupported();
|
||||
const String networkDeviceName() const;
|
||||
|
||||
const NetworkDeviceType networkDeviceType();
|
||||
|
||||
uint16_t subscribe(const char* topic, uint8_t qos);
|
||||
|
||||
void setKeepAliveCallback(std::function<void()> reconnectTick);
|
||||
void addReconnectedCallback(std::function<void()> reconnectedCallback);
|
||||
|
||||
private:
|
||||
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;
|
||||
bool _restartOnDisconnect = false;
|
||||
bool _firstConnect = true;
|
||||
bool _publishDebugInfo = false;
|
||||
std::vector<String> _subscribedTopics;
|
||||
std::map<String, String> _initTopics;
|
||||
|
||||
@@ -89,7 +92,8 @@ private:
|
||||
unsigned long _lastMaintenanceTs = 0;
|
||||
unsigned long _lastRssiTs = 0;
|
||||
long _rssiPublishInterval = 0;
|
||||
std::function<void()> _keepAliveCallback;
|
||||
std::function<void()> _keepAliveCallback = nullptr;
|
||||
std::vector<std::function<void()>> _reconnectedCallbacks;
|
||||
|
||||
NetworkDeviceType _networkDeviceType = (NetworkDeviceType)-1;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "PreferencesKeys.h"
|
||||
#include "Pins.h"
|
||||
#include "Logger.h"
|
||||
#include "RestartReason.h"
|
||||
|
||||
NetworkLock::NetworkLock(Network* network, Preferences* preferences)
|
||||
: _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_enabled, "1");
|
||||
}
|
||||
|
||||
_network->addReconnectedCallback([&]()
|
||||
{
|
||||
_reconnected = true;
|
||||
});
|
||||
}
|
||||
|
||||
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."));
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::RequestedViaMqtt);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void NetworkLock::publishULong(const char *topic, const unsigned long value)
|
||||
{
|
||||
return _network->publishULong(_mqttPath, topic, value);
|
||||
@@ -557,3 +562,10 @@ String NetworkLock::concat(String a, String b)
|
||||
c.concat(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
bool NetworkLock::reconnected()
|
||||
{
|
||||
bool r = _reconnected;
|
||||
_reconnected = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,11 @@ public:
|
||||
|
||||
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
||||
|
||||
bool reconnected();
|
||||
|
||||
private:
|
||||
bool comparePrefixedPath(const char* fullPath, const char* subPath);
|
||||
|
||||
void publishFloat(const char* topic, const float value, const uint8_t precision = 2);
|
||||
void publishInt(const char* topic, const 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 char* value);
|
||||
void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry);
|
||||
bool comparePrefixedPath(const char* fullPath, const char* subPath);
|
||||
|
||||
String concat(String a, String b);
|
||||
|
||||
@@ -65,6 +68,7 @@ private:
|
||||
bool _firstTunerStatePublish = true;
|
||||
unsigned long _lastMaintenanceTs = 0;
|
||||
bool _haEnabled= false;
|
||||
bool _reconnected = false;
|
||||
|
||||
String _keypadCommandName = "";
|
||||
String _keypadCommandCode = "";
|
||||
|
||||
@@ -42,6 +42,25 @@ void NetworkOpener::initialize()
|
||||
{
|
||||
_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()
|
||||
@@ -76,6 +95,46 @@ void NetworkOpener::onMqttDataReceived(const char* topic, byte* payload, const u
|
||||
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)
|
||||
{
|
||||
if(comparePrefixedPath(topic, configTopic))
|
||||
@@ -399,6 +458,36 @@ void NetworkOpener::removeHASSConfig(char* 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 *))
|
||||
{
|
||||
_lockActionReceivedCallback = lockActionReceivedCallback;
|
||||
@@ -409,6 +498,11 @@ void NetworkOpener::setConfigUpdateReceivedCallback(void (*configUpdateReceivedC
|
||||
_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)
|
||||
{
|
||||
_network->publishFloat(_mqttPath, topic, value, precision);
|
||||
@@ -450,6 +544,24 @@ void NetworkOpener::publishString(const char* topic, const char* 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)
|
||||
{
|
||||
int offset = 0;
|
||||
@@ -486,3 +598,17 @@ bool NetworkOpener::comparePrefixedPath(const char *fullPath, const char *subPat
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -32,12 +32,17 @@ public:
|
||||
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 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 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;
|
||||
|
||||
bool reconnected();
|
||||
|
||||
private:
|
||||
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 std::string& 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 subscribe(const char* path);
|
||||
void logactionCompletionStatusToString(uint8_t value, char* out);
|
||||
|
||||
String concat(String a, String b);
|
||||
|
||||
Preferences* _preferences;
|
||||
|
||||
Network* _network = nullptr;
|
||||
@@ -64,8 +72,15 @@ private:
|
||||
|
||||
bool _firstTunerStatePublish = true;
|
||||
bool _haEnabled= false;
|
||||
bool _reconnected = false;
|
||||
|
||||
String _keypadCommandName = "";
|
||||
String _keypadCommandCode = "";
|
||||
uint _keypadCommandId = 0;
|
||||
int _keypadCommandEnabled = 1;
|
||||
unsigned long _resetLockStateTs = 0;
|
||||
|
||||
bool (*_lockActionReceivedCallback)(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;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "PreferencesKeys.h"
|
||||
#include "MqttTopics.h"
|
||||
#include "Logger.h"
|
||||
#include "RestartReason.h"
|
||||
#include <NukiOpenerUtils.h>
|
||||
|
||||
NukiOpenerWrapper* nukiOpenerInst;
|
||||
@@ -24,6 +25,7 @@ NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, uint32_t id,
|
||||
|
||||
network->setLockActionReceivedCallback(nukiOpenerInst->onLockActionReceivedCallback);
|
||||
network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback);
|
||||
network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,24 +41,56 @@ void NukiOpenerWrapper::initialize()
|
||||
_nukiOpener.registerBleScanner(_bleScanner);
|
||||
|
||||
_intervalLockstate = _preferences->getInt(preference_query_interval_lockstate);
|
||||
_intervalConfig = _preferences->getInt(preference_query_interval_configuration);
|
||||
_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);
|
||||
_maxKeypadCodeCount = _preferences->getUInt(preference_opener_max_keypad_code_count);
|
||||
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
||||
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
|
||||
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries);
|
||||
_retryDelay = _preferences->getInt(preference_command_retry_delay);
|
||||
_rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000;
|
||||
|
||||
if(_retryDelay <= 100)
|
||||
{
|
||||
_retryDelay = 100;
|
||||
_preferences->putInt(preference_command_retry_delay, _retryDelay);
|
||||
}
|
||||
|
||||
if(_intervalLockstate == 0)
|
||||
{
|
||||
_intervalLockstate = 60 * 5;
|
||||
_intervalLockstate = 60 * 30;
|
||||
_preferences->putInt(preference_query_interval_lockstate, _intervalLockstate);
|
||||
}
|
||||
if(_intervalConfig == 0)
|
||||
{
|
||||
_intervalConfig = 60 * 60;
|
||||
_preferences->putInt(preference_query_interval_configuration, _intervalConfig);
|
||||
}
|
||||
if(_intervalBattery == 0)
|
||||
{
|
||||
_intervalBattery = 60 * 30;
|
||||
_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);
|
||||
|
||||
@@ -108,7 +142,7 @@ void NukiOpenerWrapper::update()
|
||||
Log->print((millis() - _nukiOpener.getLastReceivedBeaconTs()) / 1000);
|
||||
Log->println(" seconds, restarting device.");
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::BLEBeaconWatchdog);
|
||||
}
|
||||
|
||||
_nukiOpener.updateConnectionState();
|
||||
@@ -128,12 +162,15 @@ void NukiOpenerWrapper::update()
|
||||
{
|
||||
_nextConfigUpdateTs = ts + _intervalConfig * 1000;
|
||||
updateConfig();
|
||||
if(_hassEnabled)
|
||||
if(_hassEnabled && !_hassSetupCompleted)
|
||||
{
|
||||
setupHASS();
|
||||
}
|
||||
}
|
||||
|
||||
if(_hassEnabled && _configRead && _network->reconnected())
|
||||
{
|
||||
setupHASS();
|
||||
}
|
||||
if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs))
|
||||
{
|
||||
_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)
|
||||
{
|
||||
Nuki::CmdResult cmdResult = _nukiOpener.lockAction(_nextLockAction, 0, 0);
|
||||
@@ -194,6 +237,7 @@ void NukiOpenerWrapper::update()
|
||||
_nextLockAction = (NukiOpener::LockAction) 0xff;
|
||||
}
|
||||
}
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
if(_clearAuthData)
|
||||
@@ -205,6 +249,11 @@ void NukiOpenerWrapper::update()
|
||||
memcpy(&_lastKeyTurnerState, &_keyTurnerState, sizeof(NukiOpener::OpenerState));
|
||||
}
|
||||
|
||||
bool NukiOpenerWrapper::isPinSet()
|
||||
{
|
||||
return _nukiOpener.getSecurityPincode() != 0;
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::setPin(const uint16_t pin)
|
||||
{
|
||||
_nukiOpener.saveSecurityPincode(pin);
|
||||
@@ -218,7 +267,18 @@ void NukiOpenerWrapper::unpair()
|
||||
|
||||
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 &&
|
||||
_keyTurnerState.lockState == NukiOpener::LockState::Locked &&
|
||||
@@ -249,20 +309,34 @@ void NukiOpenerWrapper::updateKeyTurnerState()
|
||||
{
|
||||
updateAuthData();
|
||||
}
|
||||
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::updateBatteryState()
|
||||
{
|
||||
_nukiOpener.requestBatteryReport(&_batteryReport);
|
||||
_network->publishBatteryReport(_batteryReport);
|
||||
Nuki::CmdResult result = _nukiOpener.requestBatteryReport(&_batteryReport);
|
||||
if(result == Nuki::CmdResult::Success)
|
||||
{
|
||||
_network->publishBatteryReport(_batteryReport);
|
||||
}
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::updateConfig()
|
||||
{
|
||||
readConfig();
|
||||
readAdvancedConfig();
|
||||
_network->publishConfig(_nukiConfig);
|
||||
_network->publishAdvancedConfig(_nukiAdvancedConfig);
|
||||
_configRead = true;
|
||||
_hasKeypad = _nukiConfig.hasKeypad > 0;
|
||||
if(_nukiConfigValid)
|
||||
{
|
||||
_network->publishConfig(_nukiConfig);
|
||||
}
|
||||
if(_nukiAdvancedConfigValid)
|
||||
{
|
||||
_network->publishAdvancedConfig(_nukiAdvancedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::updateAuthData()
|
||||
@@ -290,6 +364,41 @@ void NukiOpenerWrapper::updateAuthData()
|
||||
{
|
||||
_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)
|
||||
@@ -317,6 +426,10 @@ void NukiOpenerWrapper::onConfigUpdateReceivedCallback(const char *topic, const
|
||||
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)
|
||||
{
|
||||
@@ -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()
|
||||
{
|
||||
return _keyTurnerState;
|
||||
@@ -353,6 +577,11 @@ const bool NukiOpenerWrapper::isPaired()
|
||||
return _paired;
|
||||
}
|
||||
|
||||
const bool NukiOpenerWrapper::hasKeypad()
|
||||
{
|
||||
return _hasKeypad;
|
||||
}
|
||||
|
||||
const BLEAddress NukiOpenerWrapper::getBleAddress() const
|
||||
{
|
||||
return _nukiOpener.getBleAddress();
|
||||
@@ -379,6 +608,7 @@ void NukiOpenerWrapper::readConfig()
|
||||
char resultStr[20];
|
||||
NukiOpener::cmdResultToString(result, resultStr);
|
||||
Log->println(resultStr);
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::readAdvancedConfig()
|
||||
@@ -389,11 +619,12 @@ void NukiOpenerWrapper::readAdvancedConfig()
|
||||
char resultStr[20];
|
||||
NukiOpener::cmdResultToString(result, resultStr);
|
||||
Log->println(resultStr);
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::setupHASS()
|
||||
{
|
||||
if(!_nukiConfigValid || _hassSetupCompleted) return;
|
||||
if(!_nukiConfigValid) return;
|
||||
|
||||
String baseTopic = _preferences->getString(preference_mqtt_opener_path);
|
||||
char uidString[20];
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
void initialize();
|
||||
void update();
|
||||
|
||||
bool isPinSet();
|
||||
void setPin(const uint16_t pin);
|
||||
|
||||
void unpair();
|
||||
@@ -23,6 +24,7 @@ public:
|
||||
|
||||
const NukiOpener::OpenerState& keyTurnerState();
|
||||
const bool isPaired();
|
||||
const bool hasKeypad();
|
||||
const BLEAddress getBleAddress() const;
|
||||
|
||||
BleScanner::Scanner* bleScanner();
|
||||
@@ -32,12 +34,16 @@ public:
|
||||
private:
|
||||
static bool onLockActionReceivedCallback(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 onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||
|
||||
void updateKeyTurnerState();
|
||||
void updateBatteryState();
|
||||
void updateConfig();
|
||||
void updateAuthData();
|
||||
void updateKeypad();
|
||||
void postponeBleWatchdog();
|
||||
|
||||
void readConfig();
|
||||
void readAdvancedConfig();
|
||||
@@ -54,13 +60,16 @@ private:
|
||||
int _intervalLockstate = 0; // seconds
|
||||
int _intervalBattery = 0; // seconds
|
||||
int _intervalConfig = 60 * 60; // seconds
|
||||
int _intervalKeypad = 0; // seconds
|
||||
int _restartBeaconTimeout = 0; // seconds
|
||||
bool _publishAuthData = false;
|
||||
bool _clearAuthData = false;
|
||||
int _nrOfRetries = 0;
|
||||
int _retryDelay = 0;
|
||||
int _retryCount = 0;
|
||||
int _retryLockstateCount = 0;
|
||||
unsigned long _nextRetryTs = 0;
|
||||
std::vector<uint16_t> _keypadCodeIds;
|
||||
|
||||
NukiOpener::OpenerState _lastKeyTurnerState;
|
||||
NukiOpener::OpenerState _keyTurnerState;
|
||||
@@ -77,12 +86,18 @@ private:
|
||||
|
||||
bool _paired = false;
|
||||
bool _statusUpdated = false;
|
||||
bool _hasKeypad = false;
|
||||
bool _keypadEnabled = false;
|
||||
uint _maxKeypadCodeCount = 0;
|
||||
bool _configRead = false;
|
||||
long _rssiPublishInterval = 0;
|
||||
unsigned long _nextLockStateUpdateTs = 0;
|
||||
unsigned long _nextBatteryReportTs = 0;
|
||||
unsigned long _nextConfigUpdateTs = 0;
|
||||
unsigned long _nextKeypadUpdateTs = 0;
|
||||
unsigned long _nextPairTs = 0;
|
||||
long _nextRssiTs = 0;
|
||||
unsigned long _lastRssi = 0;
|
||||
unsigned long _disableBleWatchdogTs = 0;
|
||||
NukiOpener::LockAction _nextLockAction = (NukiOpener::LockAction)0xff;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "PreferencesKeys.h"
|
||||
#include "MqttTopics.h"
|
||||
#include "Logger.h"
|
||||
#include "RestartReason.h"
|
||||
#include <NukiLockUtils.h>
|
||||
|
||||
NukiWrapper* nukiInst;
|
||||
@@ -41,11 +42,12 @@ void NukiWrapper::initialize(const bool& firstStart)
|
||||
_nukiLock.registerBleScanner(_bleScanner);
|
||||
|
||||
_intervalLockstate = _preferences->getInt(preference_query_interval_lockstate);
|
||||
_intervalConfig = _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);
|
||||
_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);
|
||||
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
|
||||
_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_retry_delay, 1000);
|
||||
}
|
||||
|
||||
if(_retryDelay <= 100)
|
||||
{
|
||||
_retryDelay = 100;
|
||||
@@ -68,6 +71,11 @@ void NukiWrapper::initialize(const bool& firstStart)
|
||||
_intervalLockstate = 60 * 30;
|
||||
_preferences->putInt(preference_query_interval_lockstate, _intervalLockstate);
|
||||
}
|
||||
if(_intervalConfig == 0)
|
||||
{
|
||||
_intervalConfig = 60 * 60;
|
||||
_preferences->putInt(preference_query_interval_configuration, _intervalConfig);
|
||||
}
|
||||
if(_intervalBattery == 0)
|
||||
{
|
||||
_intervalBattery = 60 * 30;
|
||||
@@ -128,13 +136,14 @@ void NukiWrapper::update()
|
||||
if(_restartBeaconTimeout > 0 &&
|
||||
ts > 60000 &&
|
||||
lastReceivedBeaconTs > 0 &&
|
||||
_disableBleWatchdogTs < ts &&
|
||||
(ts - lastReceivedBeaconTs > _restartBeaconTimeout * 1000))
|
||||
{
|
||||
Log->print("No BLE beacon received from the lock for ");
|
||||
Log->print((millis() - _nukiLock.getLastReceivedBeaconTs()) / 1000);
|
||||
Log->println(" seconds, restarting device.");
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::BLEBeaconWatchdog);
|
||||
}
|
||||
|
||||
_nukiLock.updateConnectionState();
|
||||
@@ -154,11 +163,15 @@ void NukiWrapper::update()
|
||||
{
|
||||
_nextConfigUpdateTs = ts + _intervalConfig * 1000;
|
||||
updateConfig();
|
||||
if(_hassEnabled)
|
||||
if(_hassEnabled && !_hassSetupCompleted)
|
||||
{
|
||||
setupHASS();
|
||||
}
|
||||
}
|
||||
if(_hassEnabled && _configRead && _network->reconnected())
|
||||
{
|
||||
setupHASS();
|
||||
}
|
||||
if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs))
|
||||
{
|
||||
_nextRssiTs = ts + _rssiPublishInterval;
|
||||
@@ -225,6 +238,7 @@ void NukiWrapper::update()
|
||||
_nextLockAction = (NukiLock::LockAction) 0xff;
|
||||
}
|
||||
}
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
if(_clearAuthData)
|
||||
@@ -251,6 +265,11 @@ void NukiWrapper::unlatch()
|
||||
_nextLockAction = NukiLock::LockAction::Unlatch;
|
||||
}
|
||||
|
||||
bool NukiWrapper::isPinSet()
|
||||
{
|
||||
return _nukiLock.getSecurityPincode() != 0;
|
||||
}
|
||||
|
||||
void NukiWrapper::setPin(const uint16_t pin)
|
||||
{
|
||||
_nukiLock.saveSecurityPincode(pin);
|
||||
@@ -264,7 +283,19 @@ void NukiWrapper::unpair()
|
||||
|
||||
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);
|
||||
|
||||
if(_keyTurnerState.lockState != _lastKeyTurnerState.lockState)
|
||||
@@ -279,12 +310,18 @@ void NukiWrapper::updateKeyTurnerState()
|
||||
{
|
||||
updateAuthData();
|
||||
}
|
||||
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiWrapper::updateBatteryState()
|
||||
{
|
||||
_nukiLock.requestBatteryReport(&_batteryReport);
|
||||
_network->publishBatteryReport(_batteryReport);
|
||||
Nuki::CmdResult result = _nukiLock.requestBatteryReport(&_batteryReport);
|
||||
if(result == Nuki::CmdResult::Success)
|
||||
{
|
||||
_network->publishBatteryReport(_batteryReport);
|
||||
}
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiWrapper::updateConfig()
|
||||
@@ -293,8 +330,14 @@ void NukiWrapper::updateConfig()
|
||||
readAdvancedConfig();
|
||||
_configRead = true;
|
||||
_hasKeypad = _nukiConfig.hasKeypad > 0;
|
||||
_network->publishConfig(_nukiConfig);
|
||||
_network->publishAdvancedConfig(_nukiAdvancedConfig);
|
||||
if(_nukiConfigValid)
|
||||
{
|
||||
_network->publishConfig(_nukiConfig);
|
||||
}
|
||||
if(_nukiAdvancedConfigValid)
|
||||
{
|
||||
_network->publishAdvancedConfig(_nukiAdvancedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
void NukiWrapper::updateAuthData()
|
||||
@@ -322,6 +365,7 @@ void NukiWrapper::updateAuthData()
|
||||
{
|
||||
_network->publishAuthorizationInfo(log);
|
||||
}
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiWrapper::updateKeypad()
|
||||
@@ -338,7 +382,7 @@ void NukiWrapper::updateKeypad()
|
||||
if(keypadCount > _maxKeypadCodeCount)
|
||||
{
|
||||
_maxKeypadCodeCount = keypadCount;
|
||||
_preferences->putUInt(preference_max_keypad_code_count, _maxKeypadCodeCount);
|
||||
_preferences->putUInt(preference_lock_max_keypad_code_count, _maxKeypadCodeCount);
|
||||
}
|
||||
|
||||
_network->publishKeypad(entries, _maxKeypadCodeCount);
|
||||
@@ -350,6 +394,12 @@ void NukiWrapper::updateKeypad()
|
||||
_keypadCodeIds.push_back(entry.codeId);
|
||||
}
|
||||
}
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiWrapper::postponeBleWatchdog()
|
||||
{
|
||||
_disableBleWatchdogTs = millis() + 15000;
|
||||
}
|
||||
|
||||
NukiLock::LockAction NukiWrapper::lockActionToEnum(const char *str)
|
||||
@@ -378,13 +428,11 @@ void NukiWrapper::onConfigUpdateReceivedCallback(const char *topic, const char *
|
||||
nukiInst->onConfigUpdateReceived(topic, value);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value)
|
||||
{
|
||||
if(strcmp(topic, mqtt_topic_config_button_enabled) == 0)
|
||||
@@ -594,7 +642,7 @@ void NukiWrapper::readAdvancedConfig()
|
||||
|
||||
void NukiWrapper::setupHASS()
|
||||
{
|
||||
if(!_nukiConfigValid || _hassSetupCompleted) return;
|
||||
if(!_nukiConfigValid) return;
|
||||
|
||||
String baseTopic = _preferences->getString(preference_mqtt_lock_path);
|
||||
char uidString[20];
|
||||
|
||||
@@ -19,6 +19,7 @@ public:
|
||||
void unlock();
|
||||
void unlatch();
|
||||
|
||||
bool isPinSet();
|
||||
void setPin(const uint16_t pin);
|
||||
void unpair();
|
||||
|
||||
@@ -43,12 +44,12 @@ private:
|
||||
void updateConfig();
|
||||
void updateAuthData();
|
||||
void updateKeypad();
|
||||
void postponeBleWatchdog();
|
||||
|
||||
void readConfig();
|
||||
void readAdvancedConfig();
|
||||
|
||||
void setupHASS();
|
||||
|
||||
bool hasDoorSensor();
|
||||
|
||||
NukiLock::LockAction lockActionToEnum(const char* str); // char array at least 14 characters
|
||||
@@ -84,11 +85,12 @@ private:
|
||||
bool _statusUpdated = false;
|
||||
bool _hasKeypad = false;
|
||||
bool _keypadEnabled = false;
|
||||
bool _configRead = false;
|
||||
uint _maxKeypadCodeCount = 0;
|
||||
bool _configRead = false;
|
||||
int _nrOfRetries = 0;
|
||||
int _retryDelay = 0;
|
||||
int _retryCount = 0;
|
||||
int _retryLockstateCount = 0;
|
||||
long _rssiPublishInterval = 0;
|
||||
unsigned long _nextRetryTs = 0;
|
||||
unsigned long _nextLockStateUpdateTs = 0;
|
||||
@@ -97,5 +99,6 @@ private:
|
||||
unsigned long _nextKeypadUpdateTs = 0;
|
||||
unsigned long _nextRssiTs = 0;
|
||||
unsigned long _lastRssi = 0;
|
||||
NukiLock::LockAction _nextLockAction = (NukiLock::LockAction)0xff;
|
||||
unsigned long _disableBleWatchdogTs = 0;
|
||||
volatile NukiLock::LockAction _nextLockAction = (NukiLock::LockAction)0xff;
|
||||
};
|
||||
|
||||
3
Ota.cpp
3
Ota.cpp
@@ -1,6 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include "Ota.h"
|
||||
#include "Logger.h"
|
||||
#include "RestartReason.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)))
|
||||
{
|
||||
delay(2000);
|
||||
esp_restart();
|
||||
restartEsp(RestartReason::OTACompleted);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define preference_started_before "run"
|
||||
#define preference_deviceId "deviceId"
|
||||
#define preference_mqtt_broker "mqttbroker"
|
||||
@@ -11,7 +13,8 @@
|
||||
#define preference_mqtt_lock_path "mqttpath"
|
||||
#define preference_opener_enabled "openerena"
|
||||
#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_crt "mqttcrt"
|
||||
#define preference_mqtt_key "mqttkey"
|
||||
@@ -25,6 +28,7 @@
|
||||
#define preference_restart_timer "resttmr"
|
||||
#define preference_restart_ble_beacon_lost "rstbcn"
|
||||
#define preference_query_interval_lockstate "lockStInterval"
|
||||
#define preference_query_interval_configuration "configInterval"
|
||||
#define preference_query_interval_battery "batInterval"
|
||||
#define preference_query_interval_keypad "kpInterval"
|
||||
#define preference_keypad_control_enabled "kpEnabled"
|
||||
@@ -35,11 +39,183 @@
|
||||
#define preference_cred_password "crdpass"
|
||||
#define preference_publish_authdata "pubauth"
|
||||
#define preference_gpio_locking_enabled "gpiolck"
|
||||
#define preference_publish_debug_info "pubdbg"
|
||||
#define preference_presence_detection_timeout "prdtimeout"
|
||||
#define preference_has_mac_saved "hasmac"
|
||||
#define preference_has_mac_byte_0 "macb0"
|
||||
#define preference_has_mac_byte_1 "macb1"
|
||||
#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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ NUKI Smart Lock 2.0<br>
|
||||
NUKI Smart Lock 3.0<br>
|
||||
NUKI Smart Lock 3.0 Pro<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
|
||||
|
||||
@@ -124,6 +124,8 @@ NOTE: MQTT Discovery uses retained MQTT messages to store devices configurations
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
109
RestartReason.h
Normal file
109
RestartReason.h
Normal 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;
|
||||
}
|
||||
}
|
||||
192
WebCfgServer.cpp
192
WebCfgServer.cpp
@@ -4,6 +4,7 @@
|
||||
#include "hardware/WifiEthServer.h"
|
||||
#include "Logger.h"
|
||||
#include "Config.h"
|
||||
#include "RestartReason.h"
|
||||
#include <esp_task_wdt.h>
|
||||
|
||||
WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, Network* network, EthServer* ethServer, Preferences* preferences, bool allowRestartToPortal)
|
||||
@@ -119,8 +120,8 @@ void WebCfgServer::initialize()
|
||||
return _server.requestAuthentication();
|
||||
}
|
||||
String message = "";
|
||||
bool restartEsp = processArgs(message);
|
||||
if(restartEsp)
|
||||
bool restart = processArgs(message);
|
||||
if(restart)
|
||||
{
|
||||
String response = "";
|
||||
buildConfirmHtml(response, message);
|
||||
@@ -128,7 +129,7 @@ void WebCfgServer::initialize()
|
||||
Log->println(F("Restarting"));
|
||||
|
||||
waitAndProcess(true, 1000);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::ConfigurationUpdated);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -159,13 +160,43 @@ void WebCfgServer::initialize()
|
||||
|
||||
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();
|
||||
|
||||
_network->setKeepAliveCallback([&]()
|
||||
{
|
||||
update();
|
||||
});
|
||||
{
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
bool WebCfgServer::processArgs(String& message)
|
||||
@@ -301,6 +332,11 @@ bool WebCfgServer::processArgs(String& message)
|
||||
_preferences->putInt(preference_query_interval_lockstate, value.toInt());
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "CFGINT")
|
||||
{
|
||||
_preferences->putInt(preference_query_interval_configuration, value.toInt());
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "BATINT")
|
||||
{
|
||||
_preferences->putInt(preference_query_interval_battery, value.toInt());
|
||||
@@ -445,7 +481,7 @@ void WebCfgServer::update()
|
||||
{
|
||||
Log->println(F("OTA time out, restarting"));
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::OTATimeout);
|
||||
}
|
||||
|
||||
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 state", lockstateArr);
|
||||
}
|
||||
printParameter(response, "Firmware", version.c_str());
|
||||
printParameter(response, "Firmware", version.c_str(), "/info");
|
||||
response.concat("</table><br><br>");
|
||||
|
||||
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("<h3>Credentials</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30);
|
||||
printInputField(response, "CREDPASS", "Password (max 30 characters)", "*", 30, true);
|
||||
printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, false, true);
|
||||
printInputField(response, "CREDPASS", "Password", "*", 30, true, true);
|
||||
printInputField(response, "CREDPASSRE", "Retype password", "*", 30, true);
|
||||
response.concat("</table>");
|
||||
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, "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, "MQTTUSER", "MQTT User (# to clear)", _preferences->getString(preference_mqtt_user).c_str(), 30);
|
||||
printInputField(response, "MQTTPASS", "MQTT Password", "*", 30, true);
|
||||
printInputField(response, "MQTTUSER", "MQTT User (# to clear)", _preferences->getString(preference_mqtt_user).c_str(), 30, false, true);
|
||||
printInputField(response, "MQTTPASS", "MQTT Password", "*", 30, true, true);
|
||||
response.concat("</table><br>");
|
||||
|
||||
response.concat("<h3>Advanced MQTT and Network Configuration</h3>");
|
||||
response.concat("<table>");
|
||||
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, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", _preferences->getString(preference_mqtt_crt).c_str(), TLS_CERT_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());
|
||||
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(), true);
|
||||
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, "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);
|
||||
@@ -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));
|
||||
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);
|
||||
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);
|
||||
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>");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
String response = "";
|
||||
@@ -733,7 +824,7 @@ void WebCfgServer::processUnpair(bool opener)
|
||||
_nukiOpener->unpair();
|
||||
}
|
||||
waitAndProcess(false, 1000);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::DeviceUnpaired);
|
||||
}
|
||||
|
||||
void WebCfgServer::buildHtmlHeader(String &response)
|
||||
@@ -751,8 +842,9 @@ void WebCfgServer::printInputField(String& response,
|
||||
const char *token,
|
||||
const char *description,
|
||||
const char *value,
|
||||
const size_t maxLength,
|
||||
const bool isPassword)
|
||||
const size_t& maxLength,
|
||||
const bool& isPassword,
|
||||
const bool& showLengthRestriction)
|
||||
{
|
||||
char maxLengthStr[20];
|
||||
|
||||
@@ -760,6 +852,14 @@ void WebCfgServer::printInputField(String& response,
|
||||
|
||||
response.concat("<tr><td>");
|
||||
response.concat(description);
|
||||
|
||||
if(showLengthRestriction)
|
||||
{
|
||||
response.concat(" (Max. ");
|
||||
response.concat(maxLength);
|
||||
response.concat(" characters)");
|
||||
}
|
||||
|
||||
response.concat("</td><td>");
|
||||
response.concat("<INPUT TYPE=");
|
||||
response.concat(isPassword ? "PASSWORD" : "TEXT");
|
||||
@@ -806,8 +906,9 @@ void WebCfgServer::printTextarea(String& response,
|
||||
const char *token,
|
||||
const char *description,
|
||||
const char *value,
|
||||
const size_t maxLength,
|
||||
const bool enabled)
|
||||
const size_t& maxLength,
|
||||
const bool& enabled,
|
||||
const bool& showLengthRestriction)
|
||||
{
|
||||
char maxLengthStr[20];
|
||||
|
||||
@@ -815,6 +916,12 @@ void WebCfgServer::printTextarea(String& response,
|
||||
|
||||
response.concat("<tr><td>");
|
||||
response.concat(description);
|
||||
if(showLengthRestriction)
|
||||
{
|
||||
response.concat(" (Max. ");
|
||||
response.concat(maxLength);
|
||||
response.concat(" characters)");
|
||||
}
|
||||
response.concat("</td><td>");
|
||||
response.concat(" <TEXTAREA ");
|
||||
if(!enabled)
|
||||
@@ -872,14 +979,25 @@ void WebCfgServer::buildNavigationButton(String &response, const char *caption,
|
||||
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("<td>");
|
||||
response.concat(description);
|
||||
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("</tr>");
|
||||
|
||||
@@ -928,23 +1046,41 @@ void WebCfgServer::handleOtaUpload()
|
||||
return;
|
||||
}
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
if (upload.status == UPLOAD_FILE_START)
|
||||
{
|
||||
String filename = upload.filename;
|
||||
if (!filename.startsWith("/")) {
|
||||
if (!filename.startsWith("/"))
|
||||
{
|
||||
filename = "/" + filename;
|
||||
}
|
||||
_otaStartTs = millis();
|
||||
esp_task_wdt_init(30, false);
|
||||
_network->disableAutoRestarts();
|
||||
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;
|
||||
Log->println(_transferredSize);
|
||||
_ota.updateFirmware(upload.buf, upload.currentSize);
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
} else if (upload.status == UPLOAD_FILE_END)
|
||||
{
|
||||
Log->println();
|
||||
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()
|
||||
@@ -981,4 +1117,4 @@ const std::vector<std::pair<String, String>> WebCfgServer::getNetworkGpioOptions
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,22 +37,23 @@ private:
|
||||
void buildNukiConfigHtml(String& response);
|
||||
void buildConfirmHtml(String& response, const String &message, uint32_t redirectDelay = 5);
|
||||
void buildConfigureWifiHtml(String& response);
|
||||
void buildInfoHtml(String& response);
|
||||
void sendCss();
|
||||
void sendFavicon();
|
||||
void processUnpair(bool opener);
|
||||
|
||||
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 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 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>> 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();
|
||||
void waitAndProcess(const bool blocking, const uint32_t duration);
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
#include <libb64/cencode.h>
|
||||
#include "WiFiClient.h"
|
||||
#include "WebServer.h"
|
||||
#include "FS.h"
|
||||
#ifdef WEBSERVER_ENABLE_STATIC_CONTENT
|
||||
#include "FS.h"
|
||||
#endif
|
||||
#include "detail/RequestHandlersImpl.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) {
|
||||
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
|
||||
}
|
||||
#endif
|
||||
|
||||
void WebServer::handleClient() {
|
||||
if (_currentStatus == HC_NONE) {
|
||||
|
||||
@@ -90,7 +90,9 @@ public:
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
|
||||
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
|
||||
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 );
|
||||
#endif
|
||||
void onNotFound(THandlerFunction fn); //called when handler is not assigned
|
||||
void onFileUpload(THandlerFunction fn); //handle file uploads
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ protected:
|
||||
HTTPMethod _method;
|
||||
};
|
||||
|
||||
#ifdef WEBSERVER_ENABLE_STATIC_CONTENT
|
||||
class StaticRequestHandler : public RequestHandler {
|
||||
public:
|
||||
StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
|
||||
@@ -146,6 +147,6 @@ protected:
|
||||
bool _isFile;
|
||||
size_t _baseUriLength;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif //REQUESTHANDLERSIMPL_H
|
||||
|
||||
@@ -11,9 +11,15 @@ the LICENSE file.
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// Logging is en/disabled by Arduino framework macros
|
||||
#include <esp32-hal-log.h>
|
||||
#define emc_log_i(...) log_i(__VA_ARGS__)
|
||||
#define emc_log_e(...) log_e(__VA_ARGS__)
|
||||
#define emc_log_w(...) log_w(__VA_ARGS__)
|
||||
#if defined(DEBUG_ESP_MQTT_CLIENT)
|
||||
#define emc_log_i(...) log_i(__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)
|
||||
#if defined(DEBUG_ESP_PORT) && defined(DEBUG_ESP_MQTT_CLIENT)
|
||||
#include <Arduino.h>
|
||||
|
||||
37
main.cpp
37
main.cpp
@@ -12,6 +12,7 @@
|
||||
#include "Gpio.h"
|
||||
#include "Logger.h"
|
||||
#include "Config.h"
|
||||
#include "RestartReason.h"
|
||||
|
||||
Network* network = nullptr;
|
||||
NetworkLock* networkLock = nullptr;
|
||||
@@ -28,6 +29,9 @@ bool lockEnabled = false;
|
||||
bool openerEnabled = false;
|
||||
unsigned long restartTs = (2^32) - 5 * 60000;
|
||||
|
||||
RTC_NOINIT_ATTR int restartReason;
|
||||
RTC_NOINIT_ATTR uint64_t restartReasonValid;
|
||||
|
||||
void networkTask(void *pvParameters)
|
||||
{
|
||||
while(true)
|
||||
@@ -39,7 +43,17 @@ void networkTask(void *pvParameters)
|
||||
}
|
||||
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);
|
||||
|
||||
// Serial.println(uxTaskGetStackHighWaterMark(NULL));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,14 +79,10 @@ void nukiTask(void *pvParameters)
|
||||
{
|
||||
nukiOpener->update();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void webServerTask(void *pvParameters)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void presenceDetectionTask(void *pvParameters)
|
||||
{
|
||||
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()
|
||||
{
|
||||
@@ -105,7 +99,6 @@ void setupTasks()
|
||||
xTaskCreatePinnedToCore(networkTask, "ntw", 8192, NULL, 3, NULL, 1);
|
||||
xTaskCreatePinnedToCore(nukiTask, "nuki", 4096, NULL, 2, NULL, 1);
|
||||
xTaskCreatePinnedToCore(presenceDetectionTask, "prdet", 768, NULL, 5, NULL, 1);
|
||||
xTaskCreatePinnedToCore(checkMillisTask, "mlchk", 1024, NULL, 1, NULL, 1);
|
||||
}
|
||||
|
||||
uint32_t getRandomId()
|
||||
|
||||
@@ -8,17 +8,17 @@ the LICENSE file.
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
#include "ClientSyncEthernet.h"
|
||||
#include "ClientSyncW5500.h"
|
||||
#include <lwip/sockets.h> // socket options
|
||||
|
||||
namespace espMqttClientInternals {
|
||||
|
||||
ClientSyncEthernet::ClientSyncEthernet()
|
||||
ClientSyncW5500::ClientSyncW5500()
|
||||
: client() {
|
||||
// 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
|
||||
if (ret) {
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
@@ -34,7 +34,7 @@ namespace espMqttClientInternals {
|
||||
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
|
||||
if (ret) {
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
@@ -50,27 +50,27 @@ namespace espMqttClientInternals {
|
||||
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);
|
||||
}
|
||||
|
||||
int ClientSyncEthernet::available() {
|
||||
int ClientSyncW5500::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);
|
||||
}
|
||||
|
||||
void ClientSyncEthernet::stop() {
|
||||
void ClientSyncW5500::stop() {
|
||||
client.stop();
|
||||
}
|
||||
|
||||
bool ClientSyncEthernet::connected() {
|
||||
bool ClientSyncW5500::connected() {
|
||||
return client.connected();
|
||||
}
|
||||
|
||||
bool ClientSyncEthernet::disconnected() {
|
||||
bool ClientSyncW5500::disconnected() {
|
||||
return !client.connected();
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
namespace espMqttClientInternals {
|
||||
|
||||
class ClientSyncEthernet : public Transport {
|
||||
class ClientSyncW5500 : public Transport {
|
||||
public:
|
||||
ClientSyncEthernet();
|
||||
ClientSyncW5500();
|
||||
bool connect(IPAddress ip, uint16_t port) override;
|
||||
bool connect(const char* host, uint16_t port) override;
|
||||
size_t write(const uint8_t* buf, size_t size) override;
|
||||
@@ -17,6 +17,8 @@ public:
|
||||
: _hostname(hostname)
|
||||
{}
|
||||
|
||||
virtual const String deviceName() const = 0;
|
||||
|
||||
virtual void initialize() = 0;
|
||||
virtual ReconnectStatus reconnect() = 0;
|
||||
virtual void reconfigure() = 0;
|
||||
|
||||
@@ -32,6 +32,11 @@ W5500Device::W5500Device(const String &hostname, Preferences* preferences, int v
|
||||
W5500Device::~W5500Device()
|
||||
{}
|
||||
|
||||
const String W5500Device::deviceName() const
|
||||
{
|
||||
return "Wiznet W5500";
|
||||
}
|
||||
|
||||
void W5500Device::initialize()
|
||||
{
|
||||
WiFi.mode(WIFI_OFF);
|
||||
@@ -52,11 +57,10 @@ void W5500Device::initialize()
|
||||
|
||||
if(_preferences->getBool(preference_mqtt_log_enabled))
|
||||
{
|
||||
_path = new char[200];
|
||||
memset(_path, 0, sizeof(_path));
|
||||
|
||||
String pathStr = _preferences->getString(preference_mqtt_lock_path);
|
||||
pathStr.concat(mqtt_topic_log);
|
||||
_path = new char[pathStr.length() + 1];
|
||||
memset(_path, 0, sizeof(_path));
|
||||
strcpy(_path, pathStr.c_str());
|
||||
Log = new MqttLogger(this, _path, MqttLoggerMode::MqttAndSerial);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "NetworkDevice.h"
|
||||
#include "espMqttClient.h"
|
||||
#include "espMqttClientEthernet.h"
|
||||
#include "espMqttClientW5500.h"
|
||||
#include <Ethernet.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
@@ -18,6 +18,8 @@ public:
|
||||
explicit W5500Device(const String& hostname, Preferences* _preferences, int variant);
|
||||
~W5500Device();
|
||||
|
||||
const String deviceName() const override;
|
||||
|
||||
virtual void initialize();
|
||||
virtual ReconnectStatus reconnect();
|
||||
virtual void reconfigure();
|
||||
@@ -61,7 +63,7 @@ private:
|
||||
void resetDevice();
|
||||
void initializeMacAddress(byte* mac);
|
||||
|
||||
espMqttClientEthernet _mqttClient;
|
||||
espMqttClientW5500 _mqttClient;
|
||||
Preferences* _preferences = nullptr;
|
||||
|
||||
int _maintainResult = 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../Logger.h"
|
||||
#include "../MqttTopics.h"
|
||||
#include "espMqttClient.h"
|
||||
#include "../RestartReason.h"
|
||||
|
||||
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()
|
||||
{
|
||||
std::vector<const char *> wm_menu;
|
||||
@@ -80,7 +86,7 @@ void WifiDevice::initialize()
|
||||
if(!res) {
|
||||
Log->println(F("Failed to connect. Wait for ESP restart."));
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::WifiInitFailed);
|
||||
}
|
||||
else {
|
||||
Log->print(F("WiFi connected: "));
|
||||
@@ -103,7 +109,7 @@ void WifiDevice::reconfigure()
|
||||
{
|
||||
strcpy(WiFiDevice_reconfdetect, "reconfigure_wifi");
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::ReconfigureWifi);
|
||||
}
|
||||
|
||||
void WifiDevice::printError()
|
||||
@@ -143,7 +149,7 @@ void WifiDevice::onDisconnected()
|
||||
{
|
||||
if(millis() > 60000)
|
||||
{
|
||||
ESP.restart();
|
||||
restartEsp(RestartReason::RestartOnDisconnectWatchdog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ class WifiDevice : public NetworkDevice
|
||||
public:
|
||||
WifiDevice(const String& hostname, Preferences* _preferences);
|
||||
|
||||
const String deviceName() const override;
|
||||
|
||||
virtual void initialize();
|
||||
virtual void reconfigure();
|
||||
virtual ReconnectStatus reconnect();
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#include "espMqttClientEthernet.h"
|
||||
|
||||
espMqttClientEthernet::espMqttClientEthernet(uint8_t priority, uint8_t core)
|
||||
: MqttClientSetup(true, priority, core),
|
||||
_client()
|
||||
{
|
||||
_transport = &_client;
|
||||
}
|
||||
8
networkDevices/espMqttClientW5500.cpp
Normal file
8
networkDevices/espMqttClientW5500.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "espMqttClientW5500.h"
|
||||
|
||||
espMqttClientW5500::espMqttClientW5500(uint8_t priority, uint8_t core)
|
||||
: MqttClientSetup(true, priority, core),
|
||||
_client()
|
||||
{
|
||||
_transport = &_client;
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "MqttClientSetup.h"
|
||||
#include "ClientSyncEthernet.h"
|
||||
#include "ClientSyncW5500.h"
|
||||
|
||||
class espMqttClientEthernet : public MqttClientSetup<espMqttClientEthernet> {
|
||||
class espMqttClientW5500 : public MqttClientSetup<espMqttClientW5500> {
|
||||
public:
|
||||
#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
|
||||
espMqttClient();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||
espMqttClientInternals::ClientSyncEthernet _client;
|
||||
espMqttClientInternals::ClientSyncW5500 _client;
|
||||
#elif defined(__linux__)
|
||||
espMqttClientInternals::ClientPosix _client;
|
||||
#endif
|
||||
Binary file not shown.
Reference in New Issue
Block a user