diff --git a/clion/CMakeLists.txt b/clion/CMakeLists.txt index 83721a7..6d9e78a 100644 --- a/clion/CMakeLists.txt +++ b/clion/CMakeLists.txt @@ -3,6 +3,7 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(nukihub) add_compile_definitions(CONFIG_IDF_TARGET_ESP32) +add_compile_definitions(NUKI_64BIT_TIME) set(SRCFILES ../src/Config.h @@ -55,6 +56,8 @@ set(SRCFILES ../src/util/NetworkUtil.cpp ../src/enums/NetworkDeviceType.h ../src/util/NetworkDeviceInstantiator.cpp + ../src/NukiOfficial.cpp + ../src/NukiPublisher.cpp ) file(GLOB_RECURSE SRCFILESREC diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index aa4b541..cdff697 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -309,7 +309,7 @@ void NukiNetwork::initialize() } _discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery, ""); - _offEnabled = _preferences->getBool(preference_official_hybrid, false); + _offEnabled = _preferences->getBool(preference_official_hybrid_enabled, false); readSettings(); } diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index 2c8e84f..9a65f18 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -14,28 +14,19 @@ extern bool forceEnableWebServer; extern const uint8_t x509_crt_imported_bundle_bin_start[] asm("_binary_x509_crt_bundle_start"); extern const uint8_t x509_crt_imported_bundle_bin_end[] asm("_binary_x509_crt_bundle_end"); -NukiNetworkLock::NukiNetworkLock(NukiNetwork* network, Preferences* preferences, char* buffer, size_t bufferSize) +NukiNetworkLock::NukiNetworkLock(NukiNetwork* network, NukiOfficial* nukiOfficial, Preferences* preferences, char* buffer, size_t bufferSize) : _network(network), + _nukiOfficial(nukiOfficial), _preferences(preferences), _buffer(buffer), _bufferSize(bufferSize) { + _nukiPublisher = new NukiPublisher(network, _mqttPath); + _nukiOfficial->setPublisher(_nukiPublisher); + memset(_authName, 0, sizeof(_authName)); _authName[0] = '\0'; - _offTopics.reserve(10); - //_offTopics.push_back(mqtt_topic_official_mode); - _offTopics.push_back((char*)mqtt_topic_official_state); - _offTopics.push_back((char*)mqtt_topic_official_batteryCritical); - _offTopics.push_back((char*)mqtt_topic_official_batteryChargeState); - _offTopics.push_back((char*)mqtt_topic_official_batteryCharging); - _offTopics.push_back((char*)mqtt_topic_official_keypadBatteryCritical); - _offTopics.push_back((char*)mqtt_topic_official_doorsensorState); - _offTopics.push_back((char*)mqtt_topic_official_doorsensorBatteryCritical); - _offTopics.push_back((char*)mqtt_topic_official_connected); - _offTopics.push_back((char*)mqtt_topic_official_commandResponse); - _offTopics.push_back((char*)mqtt_topic_official_lockActionEvent); - _network->registerMqttReceiver(this); } @@ -62,7 +53,6 @@ void NukiNetworkLock::initialize() _haEnabled = _preferences->getString(preference_mqtt_hass_discovery, "") != ""; _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); - _offEnabled = _preferences->getBool(preference_official_hybrid, false); _network->initTopic(_mqttPath, mqtt_topic_lock_action, "--"); _network->subscribe(_mqttPath, mqtt_topic_lock_action); @@ -159,17 +149,13 @@ void NukiNetworkLock::initialize() _network->initTopic(_mqttPath, mqtt_topic_auth_action, "--"); } - if(_offEnabled) + if(_nukiOfficial->getOffEnabled()) { - char uidString[20]; - itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16); - for(char* c=uidString; *c=toupper(*c); ++c); - strcpy(_offMqttPath, "nuki/"); - strcat(_offMqttPath,uidString); + _nukiOfficial->setUid(_preferences->getUInt(preference_nuki_id_lock, 0)); - for(const auto& offTopic : _offTopics) + for(const auto& offTopic : _nukiOfficial->getOffTopics()) { - _network->subscribe(_offMqttPath, offTopic); + _network->subscribe(_nukiOfficial->getMqttPath(), offTopic); } } @@ -184,6 +170,14 @@ void NukiNetworkLock::initialize() }); } +void NukiNetworkLock::update() +{ + if(_nukiOfficial->hasOffStateToPublish()) + { + publishState(_nukiOfficial->getOffStateToPublish()); + } +} + void NukiNetworkLock::onMqttDataReceived(char* topic, int topic_len, char* data, int data_len) { if(_network->mqttRecentlyConnected() && _network->pathEquals(_mqttPath, mqtt_topic_lock_action, topic)) @@ -331,11 +325,11 @@ void NukiNetworkLock::onMqttDataReceived(char* topic, int topic_len, char* data, if(atoi(data) > 0 && atoi(data) > _lastRollingLog) _lastRollingLog = atoi(data); } - if(_offEnabled) + if(_nukiOfficial->getOffEnabled()) { - for(auto offTopic : _offTopics) + for(auto offTopic : _nukiOfficial->getOffTopics()) { - if(comparePrefixedPath(topic, offTopic, true)) + if(_nukiOfficial->comparePrefixedPath(topic, offTopic)) { if(_officialUpdateReceivedCallback != nullptr) { @@ -500,7 +494,7 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT JsonDocument json; JsonDocument jsonBattery; - if(!_offConnected) + if(!_nukiOfficial->getOffConnected()) { lockstateToString(keyTurnerState.lockState, str); @@ -519,7 +513,7 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT } else { - lockstateToString((NukiLock::LockState)_offState, str); + lockstateToString((NukiLock::LockState)_nukiOfficial->getOffState(), str); json["lock_state"] = str; } @@ -527,7 +521,7 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT memset(&str, 0, sizeof(str)); - if(!_offConnected) + if(!_nukiOfficial->getOffConnected()) { triggerToString(keyTurnerState.trigger, str); @@ -540,7 +534,7 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT } else { - triggerToString((NukiLock::Trigger)_offTrigger, str); + triggerToString((NukiLock::Trigger)_nukiOfficial->getOffTrigger(), str); json["trigger"] = str; } @@ -552,7 +546,7 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT memset(&str, 0, sizeof(str)); - if(!_offConnected) + if(!_nukiOfficial->getOffConnected()) { lockactionToString(keyTurnerState.lastLockAction, str); @@ -565,7 +559,7 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT } else { - lockactionToString((NukiLock::LockAction)_offLockAction, str); + lockactionToString((NukiLock::LockAction)_nukiOfficial->getOffLockAction(), str); json["last_lock_action"] = str; } @@ -584,7 +578,7 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT json["lock_completion_status"] = str; memset(&str, 0, sizeof(str)); - if(!_offConnected) + if(!_nukiOfficial->getOffConnected()) { NukiLock::doorSensorStateToString(keyTurnerState.doorSensorState, str); @@ -622,11 +616,11 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT } else { - NukiLock::doorSensorStateToString((NukiLock::DoorSensorState)_offDoorsensorState, str); + NukiLock::doorSensorStateToString((NukiLock::DoorSensorState)_nukiOfficial->getOffDoorsensorState(), str); json["door_sensor_state"] = str; } - json["auth_id"] = _authId; + json["auth_id"] = getAuthId(); json["auth_name"] = _authName; serializeJson(json, _buffer, _bufferSize); @@ -698,13 +692,14 @@ void NukiNetworkLock::publishAuthorizationInfo(const std::listclearAuthId(); memset(_authName, 0, sizeof(_authName)); memcpy(_authName, authName, sizeof(authName)); - if(authName[sizeName - 1] != '\0' && _authEntries.count(_authId) > 0) + if(authName[sizeName - 1] != '\0' && _authEntries.count(getAuthId()) > 0) { memset(_authName, 0, sizeof(_authName)); - memcpy(_authName, _authEntries[_authId].c_str(), sizeof(_authEntries[_authId].c_str())); + memcpy(_authName, _authEntries[getAuthId()].c_str(), sizeof(_authEntries[getAuthId()].c_str())); } } } @@ -814,7 +809,7 @@ void NukiNetworkLock::publishAuthorizationInfo(const std::list 0) { - publishUInt(mqtt_topic_lock_auth_id, _authId, true); + publishUInt(mqtt_topic_lock_auth_id, getAuthId(), true); publishString(mqtt_topic_lock_auth_name, _authName, true); } } @@ -1575,13 +1570,12 @@ void NukiNetworkLock::setAuthCommandReceivedCallback(void (*authCommandReceivedR _authCommandReceivedReceivedCallback = authCommandReceivedReceivedCallback; } -void NukiNetworkLock::buildMqttPath(const char* path, char* outPath, bool offPath) +void NukiNetworkLock::buildMqttPath(const char* path, char* outPath) { int offset = 0; char inPath[181] = {0}; - if(offPath) memcpy(inPath, _offMqttPath, sizeof(_offMqttPath)); - else memcpy(inPath, _mqttPath, sizeof(_mqttPath)); + memcpy(inPath, _mqttPath, sizeof(_mqttPath)); for(const char& c : inPath) { @@ -1602,10 +1596,10 @@ void NukiNetworkLock::buildMqttPath(const char* path, char* outPath, bool offPat outPath[i+1] = 0x00; } -bool NukiNetworkLock::comparePrefixedPath(const char *fullPath, const char *subPath, bool offPath) +bool NukiNetworkLock::comparePrefixedPath(const char *fullPath, const char *subPath) { char prefixedPath[500]; - buildMqttPath(subPath, prefixedPath, offPath); + buildMqttPath(subPath, prefixedPath); return strcmp(fullPath, prefixedPath) == 0; } @@ -1651,63 +1645,57 @@ void NukiNetworkLock::publishHASSConfig(char *deviceType, const char *baseTopic, void NukiNetworkLock::removeHASSConfig(char *uidString) { - _network->removeHASSConfig(uidString); + return _network->removeHASSConfig(uidString); } void NukiNetworkLock::publishOffAction(const int value) { - _network->publishInt(_offMqttPath, mqtt_topic_official_lock_action, value, false); + return _network->publishInt(_nukiOfficial->getMqttPath(), mqtt_topic_official_lock_action, value, false); } void NukiNetworkLock::publishFloat(const char *topic, const float value, bool retain, const uint8_t precision) { - _network->publishFloat(_mqttPath, topic, value, retain, precision); + return _nukiPublisher->publishFloat(topic, value, retain, precision); } void NukiNetworkLock::publishInt(const char *topic, const int value, bool retain) { - _network->publishInt(_mqttPath, topic, value, retain); + return _nukiPublisher->publishInt(topic, value, retain); } void NukiNetworkLock::publishUInt(const char *topic, const unsigned int value, bool retain) { - _network->publishUInt(_mqttPath, topic, value, retain); + return _nukiPublisher->publishUInt(topic, value, retain); } void NukiNetworkLock::publishBool(const char *topic, const bool value, bool retain) { - _network->publishBool(_mqttPath, topic, value, retain); + return _nukiPublisher->publishBool(topic, value, retain); } bool NukiNetworkLock::publishString(const char *topic, const String &value, bool retain) { - char str[value.length() + 1]; - memset(str, 0, sizeof(str)); - memcpy(str, value.begin(), value.length()); - return publishString(topic, str, retain); + return _nukiPublisher->publishString(topic, value, retain); } bool NukiNetworkLock::publishString(const char *topic, const std::string &value, bool retain) { - char str[value.size() + 1]; - memset(str, 0, sizeof(str)); - memcpy(str, value.data(), value.length()); - return publishString(topic, str, retain); + return _nukiPublisher->publishString(topic, value, retain); } bool NukiNetworkLock::publishString(const char *topic, const char *value, bool retain) { - return _network->publishString(_mqttPath, topic, value, retain); + return _nukiPublisher->publishString(topic, value, retain); } void NukiNetworkLock::publishULong(const char *topic, const unsigned long value, bool retain) { - return _network->publishULong(_mqttPath, topic, value, retain); + return _nukiPublisher->publishULong(topic, value, retain); } void NukiNetworkLock::publishLongLong(const char *topic, int64_t value, bool retain) { - return _network->publishLongLong(_mqttPath, topic, value, retain); + return _nukiPublisher->publishLongLong(topic, value, retain); } String NukiNetworkLock::concat(String a, String b) @@ -1802,3 +1790,12 @@ void NukiNetworkLock::fobActionToString(const int fobact, char* str) { break; } } + +const uint32_t NukiNetworkLock::getAuthId() const +{ + if(_nukiOfficial->hasAuthId()) + { + return _nukiOfficial->getAuthId(); + } + return _authId; +} diff --git a/src/NukiNetworkLock.h b/src/NukiNetworkLock.h index 34a8cbe..505d8b7 100644 --- a/src/NukiNetworkLock.h +++ b/src/NukiNetworkLock.h @@ -12,14 +12,17 @@ #include "NukiNetwork.h" #include "QueryCommand.h" #include "LockActionResult.h" +#include "NukiOfficial.h" +#include "NukiPublisher.h" class NukiNetworkLock : public MqttReceiver { public: - explicit NukiNetworkLock(NukiNetwork* network, Preferences* preferences, char* buffer, size_t bufferSize); + explicit NukiNetworkLock(NukiNetwork* network, NukiOfficial* nukiOfficial, Preferences* preferences, char* buffer, size_t bufferSize); virtual ~NukiNetworkLock(); void initialize(); + void update(); void publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurnerState, const NukiLock::KeyTurnerState& lastKeyTurnerState); void publishState(NukiLock::LockState lockState); @@ -65,55 +68,38 @@ public: bool publishString(const char* topic, const std::string& value, bool retain); bool publishString(const char* topic, const char* value, bool retain); + const uint32_t getAuthId() const; + bool reconnected(); uint8_t queryCommands(); - //uint8_t _offMode = 0; - uint8_t _offState = 0; - bool _offCritical = false; - uint8_t _offChargeState = 100; - bool _offCharging = false; - bool _offKeypadCritical = false; - uint8_t _offDoorsensorState = 0; - bool _offDoorsensorCritical = false; - bool _offConnected = false; - uint8_t _offCommandResponse = 0; - char* _offLockActionEvent; - uint8_t _offLockAction = 0; - uint8_t _offTrigger = 0; - uint32_t _offAuthId = 0; - uint32_t _offCodeId = 0; - uint8_t _offContext = 0; - uint32_t _authId = 0; - int64_t _offCommandExecutedTs = 0; - NukiLock::LockAction _offCommand = (NukiLock::LockAction)0xff; - private: - bool comparePrefixedPath(const char* fullPath, const char* subPath, bool offPath = false); + bool comparePrefixedPath(const char* fullPath, const char* subPath); void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry); void buttonPressActionToString(const NukiLock::ButtonPressAction btnPressAction, char* str); void homeKitStatusToString(const int hkstatus, char* str); void fobActionToString(const int fobact, char* str); + void (*_officialUpdateReceivedCallback)(const char* path, const char* value) = nullptr; + String concat(String a, String b); - void buildMqttPath(const char* path, char* outPath, bool offPath = false); + void buildMqttPath(const char* path, char* outPath); - NukiNetwork* _network; - Preferences* _preferences; + NukiNetwork* _network = nullptr; + NukiPublisher* _nukiPublisher = nullptr; + NukiOfficial* _nukiOfficial = nullptr; + Preferences* _preferences = nullptr; std::map _authEntries; - std::vector _offTopics; char _mqttPath[181] = {0}; - char _offMqttPath[181] = {0}; bool _firstTunerStatePublish = true; int64_t _lastMaintenanceTs = 0; bool _haEnabled = false; bool _reconnected = false; bool _disableNonJSON = false; - bool _offEnabled = false; String _keypadCommandName = ""; String _keypadCommandCode = ""; @@ -121,6 +107,7 @@ private: int _keypadCommandEnabled = 1; uint8_t _queryCommands = 0; uint32_t _lastRollingLog = 0; + uint32_t _authId = 0; char _nukiName[33]; char _authName[33]; @@ -129,7 +116,6 @@ private: size_t _bufferSize; LockActionResult (*_lockActionReceivedCallback)(const char* value) = nullptr; - void (*_officialUpdateReceivedCallback)(const char* path, const char* value) = nullptr; void (*_configUpdateReceivedCallback)(const char* value) = nullptr; void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr; void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr; diff --git a/src/NukiNetworkOpener.cpp b/src/NukiNetworkOpener.cpp index 337068f..537d281 100644 --- a/src/NukiNetworkOpener.cpp +++ b/src/NukiNetworkOpener.cpp @@ -12,6 +12,8 @@ NukiNetworkOpener::NukiNetworkOpener(NukiNetwork* network, Preferences* preferen _buffer(buffer), _bufferSize(bufferSize) { + _nukiPublisher = new NukiPublisher(network, _mqttPath); + memset(_authName, 0, sizeof(_authName)); _authName[0] = '\0'; @@ -1380,22 +1382,22 @@ void NukiNetworkOpener::setAuthCommandReceivedCallback(void (*authCommandReceive void NukiNetworkOpener::publishFloat(const char *topic, const float value, bool retain, const uint8_t precision) { - _network->publishFloat(_mqttPath, topic, value, retain, precision); + _nukiPublisher->publishFloat(topic, value, retain, precision); } void NukiNetworkOpener::publishInt(const char *topic, const int value, bool retain) { - _network->publishInt(_mqttPath, topic, value, retain); + _nukiPublisher->publishInt(topic, value, retain); } void NukiNetworkOpener::publishUInt(const char *topic, const unsigned int value, bool retain) { - _network->publishUInt(_mqttPath, topic, value, retain); + _nukiPublisher->publishUInt(topic, value, retain); } void NukiNetworkOpener::publishBool(const char *topic, const bool value, bool retain) { - _network->publishBool(_mqttPath, topic, value, retain); + _nukiPublisher->publishBool(topic, value, retain); } void NukiNetworkOpener::publishString(const char *topic, const String &value, bool retain) @@ -1416,7 +1418,7 @@ void NukiNetworkOpener::publishString(const char *topic, const std::string &valu void NukiNetworkOpener::publishString(const char* topic, const char* value, bool retain) { - _network->publishString(_mqttPath, topic, value, retain); + _nukiPublisher->publishString(topic, value, retain); } void NukiNetworkOpener::publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry) diff --git a/src/NukiNetworkOpener.h b/src/NukiNetworkOpener.h index df36893..5b9e394 100644 --- a/src/NukiNetworkOpener.h +++ b/src/NukiNetworkOpener.h @@ -76,9 +76,10 @@ private: String concat(String a, String b); - Preferences* _preferences; + Preferences* _preferences = nullptr; NukiNetwork* _network = nullptr; + NukiPublisher* _nukiPublisher = nullptr; std::map _authEntries; char _mqttPath[181] = {0}; diff --git a/src/NukiOfficial.cpp b/src/NukiOfficial.cpp new file mode 100644 index 0000000..9eaef7d --- /dev/null +++ b/src/NukiOfficial.cpp @@ -0,0 +1,325 @@ +#include +#include "NukiOfficial.h" +#include "Logger.h" +#include "PreferencesKeys.h" +#include "../lib/nuki_ble/src/NukiLockUtils.h" +#include +#include + + +NukiOfficial::NukiOfficial(Preferences *preferences) +{ + offEnabled = preferences->getBool(preference_official_hybrid_enabled, false); + _disableNonJSON = preferences->getBool(preference_disable_non_json, false); +} + + +void NukiOfficial::setUid(const uint32_t& uid) +{ + char uidString[20]; + itoa(uid, uidString, 16); + + for(char* c=uidString; *c=toupper(*c); ++c); + + strcpy(mqttPath, "nuki/"); + strcat(mqttPath, uidString); + + offTopics.reserve(10); + //_offTopics.push_back(mqtt_topic_official_mode); + offTopics.push_back((char*)mqtt_topic_official_state); + offTopics.push_back((char*)mqtt_topic_official_batteryCritical); + offTopics.push_back((char*)mqtt_topic_official_batteryChargeState); + offTopics.push_back((char*)mqtt_topic_official_batteryCharging); + offTopics.push_back((char*)mqtt_topic_official_keypadBatteryCritical); + offTopics.push_back((char*)mqtt_topic_official_doorsensorState); + offTopics.push_back((char*)mqtt_topic_official_doorsensorBatteryCritical); + offTopics.push_back((char*)mqtt_topic_official_connected); + offTopics.push_back((char*)mqtt_topic_official_commandResponse); + offTopics.push_back((char*)mqtt_topic_official_lockActionEvent); +} + +void NukiOfficial::setPublisher(NukiPublisher *publisher) +{ + _publisher = publisher; +} + + +const char *NukiOfficial::getMqttPath() const +{ + return mqttPath; +} + +void NukiOfficial::buildMqttPath(const char *path, char *outPath) +{ + int offset = 0; + char inPath[181] = {0}; + + memcpy(inPath, mqttPath, sizeof(mqttPath)); + + for(const char& c : inPath) + { + if(c == 0x00) + { + break; + } + outPath[offset] = c; + ++offset; + } + int i=0; + while(outPath[i] != 0x00) + { + outPath[offset] = path[i]; + ++i; + ++offset; + } + outPath[i+1] = 0x00; +} + +bool NukiOfficial::comparePrefixedPath(const char *fullPath, const char *subPath) +{ + char prefixedPath[500]; + buildMqttPath(subPath, prefixedPath); + return strcmp(fullPath, prefixedPath) == 0; +} + +void NukiOfficial::onOfficialUpdateReceived(const char *topic, const char *value) +{ + char str[50]; + bool publishBatteryJson = false; + memset(&str, 0, sizeof(str)); + + Log->println("Official Nuki change recieved"); + Log->print(F("Topic: ")); + Log->println(topic); + Log->print(F("Value: ")); + Log->println(value); + + if(strcmp(topic, mqtt_topic_official_connected) == 0) + { + Log->print(F("Connected: ")); + Log->println((strcmp(value, "true") == 0 ? 1 : 0)); + offConnected = (strcmp(value, "true") == 0 ? 1 : 0); + _publisher->publishBool(mqtt_hybrid_state, offConnected, true); + } + else if(strcmp(topic, mqtt_topic_official_state) == 0) + { + offState = atoi(value); + _statusUpdated = true; + Log->println(F("Lock: Updating status on Hybrid state change")); + _publisher->publishBool(mqtt_hybrid_state, offConnected, true); + NukiLock::lockstateToString((NukiLock::LockState)offState, str); + _publisher->publishString(mqtt_topic_lock_state, str, true); + + Log->print(F("Lockstate: ")); + Log->println(str); + + _offStateToPublish = (NukiLock::LockState)offState; + _hasOffStateToPublish = true; + } + else if(strcmp(topic, mqtt_topic_official_doorsensorState) == 0) + { + offDoorsensorState = atoi(value); + _statusUpdated = true; + Log->println(F("Lock: Updating status on Hybrid door sensor state change")); + _publisher->publishBool(mqtt_topic_lock_status_updated, _statusUpdated, true); + NukiLock::doorSensorStateToString((NukiLock::DoorSensorState)offDoorsensorState, str); + + Log->print(F("Doorsensor state: ")); + Log->println(str); + + _publisher->publishString(mqtt_topic_lock_door_sensor_state, str, true); + } + else if(strcmp(topic, mqtt_topic_official_batteryCritical) == 0) + { + offCritical = (strcmp(value, "true") == 0 ? 1 : 0); + + Log->print(F("Battery critical: ")); + Log->println(offCritical); + + if(!_disableNonJSON) _publisher->publishBool(mqtt_topic_battery_critical, offCritical, true); + publishBatteryJson = true; + } + else if(strcmp(topic, mqtt_topic_official_batteryCharging) == 0) + { + offCharging = (strcmp(value, "true") == 0 ? 1 : 0); + + Log->print(F("Battery charging: ")); + Log->println(offCharging); + + if(!_disableNonJSON) _publisher->publishBool(mqtt_topic_battery_charging, offCharging, true); + publishBatteryJson = true; + } + else if(strcmp(topic, mqtt_topic_official_batteryChargeState) == 0) + { + offChargeState = atoi(value); + + Log->print(F("Battery level: ")); + Log->println(offChargeState); + + if(!_disableNonJSON) _publisher->publishInt(mqtt_topic_battery_level, offChargeState, true); + publishBatteryJson = true; + } + else if(strcmp(topic, mqtt_topic_official_keypadBatteryCritical) == 0) + { + offKeypadCritical = (strcmp(value, "true") == 0 ? 1 : 0); + if(!_disableNonJSON) _publisher->publishBool(mqtt_topic_battery_keypad_critical, offKeypadCritical, true); + publishBatteryJson = true; + } + else if(strcmp(topic, mqtt_topic_official_doorsensorBatteryCritical) == 0) + { + offDoorsensorCritical = (strcmp(value, "true") == 0 ? 1 : 0); + if(!_disableNonJSON) _publisher->publishBool(mqtt_topic_battery_doorsensor_critical, offDoorsensorCritical, true); + publishBatteryJson = true; + } + else if(strcmp(topic, mqtt_topic_official_commandResponse) == 0) + { + offCommandResponse = atoi(value); + if(offCommandResponse == 0) + { + clearOffCommandExecutedTs(); + } + char resultStr[15] = {0}; + NukiLock::cmdResultToString((Nuki::CmdResult)offCommandResponse, resultStr); + _publisher->publishString(mqtt_topic_lock_action_command_result, resultStr, true); + } + else if(strcmp(topic, mqtt_topic_official_lockActionEvent) == 0) + { + clearOffCommandExecutedTs(); + offLockActionEvent = (char*)value; + String LockActionEvent = offLockActionEvent; + const int ind1 = LockActionEvent.indexOf(','); + const int ind2 = LockActionEvent.indexOf(',', ind1+1); + const int ind3 = LockActionEvent.indexOf(',', ind2+1); + const int ind4 = LockActionEvent.indexOf(',', ind3+1); + const int ind5 = LockActionEvent.indexOf(',', ind4+1); + + offLockAction = atoi(LockActionEvent.substring(0, ind1).c_str()); + offTrigger = atoi(LockActionEvent.substring(ind1 + 1, ind2 + 1).c_str()); + offAuthId = atoi(LockActionEvent.substring(ind2 + 1, ind3 + 1).c_str()); + offCodeId = atoi(LockActionEvent.substring(ind3 + 1, ind4 + 1).c_str()); +// offContext = atoi(LockActionEvent.substring(ind4 + 1, ind5 + 1).c_str()); + + memset(&str, 0, sizeof(str)); + lockactionToString((NukiLock::LockAction)offLockAction, str); + _publisher->publishString(mqtt_topic_lock_last_lock_action, str, true); + + memset(&str, 0, sizeof(str)); + triggerToString((NukiLock::Trigger)offTrigger, str); + _publisher->publishString(mqtt_topic_lock_trigger, str, true); + + if(offAuthId > 0 || offCodeId > 0) + { + if(offCodeId > 0) + { + _authId = offCodeId; + } + else + { + _authId = offAuthId; + } + _hasAuthId = true; + + /* + _network->_authName = RETRIEVE FROM VECTOR AFTER AUTHORIZATION ENTRIES ARE IMPLEMENTED; + _offContext = BASE ON CONTEXT OF TRIGGER AND PUBLISH TO MQTT; + */ + } + } + + if(publishBatteryJson) + { + JsonDocument jsonBattery; + char _resbuf[2048]; + jsonBattery["critical"] = offCritical ? "1" : "0"; + jsonBattery["charging"] = offCharging ? "1" : "0"; + jsonBattery["level"] = offChargeState; + jsonBattery["keypadCritical"] = offKeypadCritical ? "1" : "0"; + jsonBattery["doorSensorCritical"] = offDoorsensorCritical ? "1" : "0"; + serializeJson(jsonBattery, _resbuf, sizeof(_resbuf)); + _publisher->publishString(mqtt_topic_battery_basic_json, _resbuf, true); + } +} + +const bool NukiOfficial::getStatusUpdated() +{ + bool stu = _statusUpdated; + _statusUpdated = false; + return stu; +} + +const bool NukiOfficial::hasOffStateToPublish() +{ + bool hasOff = _hasOffStateToPublish; + _hasOffStateToPublish = false; + return hasOff; +} + +const NukiLock::LockState NukiOfficial::getOffStateToPublish() const +{ + return _offStateToPublish; +} + +const uint32_t NukiOfficial::getAuthId() const +{ + return _authId; +} + +const bool NukiOfficial::hasAuthId() const +{ + return _hasAuthId; +} + +void NukiOfficial::clearAuthId() +{ + _hasAuthId = false; +} + +const bool NukiOfficial::getOffConnected() const +{ + return offConnected; +} + +const bool NukiOfficial::getOffEnabled() const +{ + return offEnabled; +} + +const uint8_t NukiOfficial::getOffDoorsensorState() const +{ + return offDoorsensorState; +} + +const uint8_t NukiOfficial::getOffState() const +{ + return offState; +} + +const uint8_t NukiOfficial::getOffLockAction() const +{ + return offLockAction; +} + +const uint8_t NukiOfficial::getOffTrigger() const +{ + return offTrigger; +} + +const int64_t NukiOfficial::getOffCommandExecutedTs() const +{ + return offCommandExecutedTs; +} + +void NukiOfficial::setOffCommandExecutedTs(const int64_t &value) +{ + offCommandExecutedTs = value; +} + +void NukiOfficial::clearOffCommandExecutedTs() +{ + offCommandExecutedTs = 0; +} + +const std::vector NukiOfficial::getOffTopics() const +{ + return offTopics; +} diff --git a/src/NukiOfficial.h b/src/NukiOfficial.h new file mode 100644 index 0000000..366b555 --- /dev/null +++ b/src/NukiOfficial.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include "../lib/nuki_ble/src/NukiLockConstants.h" +#include "NukiPublisher.h" + +class NukiOfficial +{ +public: + explicit NukiOfficial(Preferences* preferences); + void setPublisher(NukiPublisher* publisher); + + void setUid(const uint32_t& uid); + + const char* getMqttPath() const; + const bool getStatusUpdated(); + + const bool hasOffStateToPublish(); + const NukiLock::LockState getOffStateToPublish() const; + + const uint32_t getAuthId() const; + const bool hasAuthId() const; + void clearAuthId(); + + void buildMqttPath(const char* path, char* outPath); + bool comparePrefixedPath(const char *fullPath, const char *subPath); + + void onOfficialUpdateReceived(const char* topic, const char* value); + + const bool getOffConnected() const; + const bool getOffEnabled() const; + const uint8_t getOffDoorsensorState() const; + const uint8_t getOffState() const; + const uint8_t getOffLockAction() const; + const uint8_t getOffTrigger() const; + const std::vector getOffTopics() const; + + const int64_t getOffCommandExecutedTs() const; + void setOffCommandExecutedTs(const int64_t& value); + void clearOffCommandExecutedTs(); + +private: + char mqttPath[181] = {0}; + std::vector offTopics; + + NukiPublisher* _publisher = nullptr; + bool _statusUpdated = false; + bool _hasOffStateToPublish = false; + NukiLock::LockState _offStateToPublish = (NukiLock::LockState)0; + uint32_t _authId = 0; + bool _hasAuthId = false; + bool _disableNonJSON = false; + + int64_t offCommandExecutedTs = 0; + + //uint8_t _offMode = 0; + uint8_t offState = 0; + bool offCritical = false; + uint8_t offChargeState = 100; + bool offCharging = false; + bool offKeypadCritical = false; + uint8_t offDoorsensorState = 0; + bool offDoorsensorCritical = false; + bool offConnected = false; + uint8_t offCommandResponse = 0; + char* offLockActionEvent; + uint8_t offLockAction = 0; + uint8_t offTrigger = 0; + uint32_t offAuthId = 0; + uint32_t offCodeId = 0; +// uint8_t offContext = 0; + bool offEnabled = false; +}; + diff --git a/src/NukiPublisher.cpp b/src/NukiPublisher.cpp new file mode 100644 index 0000000..78376ec --- /dev/null +++ b/src/NukiPublisher.cpp @@ -0,0 +1,59 @@ +#include "NukiPublisher.h" + + +NukiPublisher::NukiPublisher(NukiNetwork *network, const char* mqttPath) +: _network(network), + _mqttPath(mqttPath) +{ +} + +void NukiPublisher::publishFloat(const char *topic, const float value, bool retain, const uint8_t precision) +{ + _network->publishFloat(_mqttPath, topic, value, retain, precision); +} + +void NukiPublisher::publishInt(const char *topic, const int value, bool retain) +{ + _network->publishInt(_mqttPath, topic, value, retain); +} + +void NukiPublisher::publishUInt(const char *topic, const unsigned int value, bool retain) +{ + _network->publishUInt(_mqttPath, topic, value, retain); +} + +void NukiPublisher::publishBool(const char *topic, const bool value, bool retain) +{ + _network->publishBool(_mqttPath, topic, value, retain); +} + +bool NukiPublisher::publishString(const char *topic, const String &value, bool retain) +{ + char str[value.length() + 1]; + memset(str, 0, sizeof(str)); + memcpy(str, value.begin(), value.length()); + return publishString(topic, str, retain); +} + +bool NukiPublisher::publishString(const char *topic, const std::string &value, bool retain) +{ + char str[value.size() + 1]; + memset(str, 0, sizeof(str)); + memcpy(str, value.data(), value.length()); + return publishString(topic, str, retain); +} + +bool NukiPublisher::publishString(const char *topic, const char *value, bool retain) +{ + return _network->publishString(_mqttPath, topic, value, retain); +} + +void NukiPublisher::publishULong(const char *topic, const unsigned long value, bool retain) +{ + return _network->publishULong(_mqttPath, topic, value, retain); +} + +void NukiPublisher::publishLongLong(const char *topic, int64_t value, bool retain) +{ + return _network->publishLongLong(_mqttPath, topic, value, retain); +} diff --git a/src/NukiPublisher.h b/src/NukiPublisher.h new file mode 100644 index 0000000..d59a839 --- /dev/null +++ b/src/NukiPublisher.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "NukiNetwork.h" + +class NukiPublisher +{ +public: + NukiPublisher(NukiNetwork* _network, const char* mqttPath); + + void publishFloat(const char* topic, const float value, bool retain, const uint8_t precision = 2); + void publishInt(const char* topic, const int value, bool retain); + void publishUInt(const char* topic, const unsigned int value, bool retain); + void publishULong(const char* topic, const unsigned long value, bool retain); + void publishLongLong(const char* topic, int64_t value, bool retain); + void publishBool(const char* topic, const bool value, bool retain); + bool publishString(const char* topic, const String& value, bool retain); + bool publishString(const char* topic, const std::string& value, bool retain); + bool publishString(const char* topic, const char* value, bool retain); + +private: + NukiNetwork* _network; + const char* _mqttPath; + +}; diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 6980df0..7cc1092 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -6,16 +6,15 @@ #include #include "Config.h" -NukiWrapper* nukiInst; -NukiNetworkLock* networkInst; -Preferences* nukiLockPreferences = nullptr; +NukiWrapper* nukiInst = nullptr; -NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, Gpio* gpio, Preferences* preferences) +NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, NukiOfficial* nukiOfficial, Gpio* gpio, Preferences* preferences) : _deviceName(deviceName), _deviceId(deviceId), _bleScanner(scanner), _nukiLock(deviceName, _deviceId->get()), _network(network), + _nukiOfficial(nukiOfficial), _gpio(gpio), _preferences(preferences) { @@ -23,7 +22,6 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, Log->println(_deviceId->get()); nukiInst = this; - networkInst = _network; memset(&_lastKeyTurnerState, sizeof(NukiLock::KeyTurnerState), 0); memset(&_lastBatteryReport, sizeof(NukiLock::BatteryReport), 0); @@ -64,7 +62,7 @@ void NukiWrapper::initialize(const bool& firstStart) _preferences->putBool(preference_find_best_rssi, false); _preferences->putBool(preference_check_updates, true); _preferences->putBool(preference_opener_continuous_mode, false); - _preferences->putBool(preference_official_hybrid, false); + _preferences->putBool(preference_official_hybrid_enabled, false); _preferences->putBool(preference_official_hybrid_actions, false); _preferences->putBool(preference_official_hybrid_retry, false); _preferences->putBool(preference_disable_non_json, false); @@ -105,7 +103,6 @@ void NukiWrapper::initialize(const bool& firstStart) } _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; - _offEnabled = _preferences->getBool(preference_official_hybrid, false); readSettings(); } @@ -250,10 +247,10 @@ void NukiWrapper::update() _nukiLock.updateConnectionState(); - if(networkInst->_offCommandExecutedTs>0 && ts >= networkInst->_offCommandExecutedTs) + if(_nukiOfficial->getOffCommandExecutedTs() > 0 && ts >= _nukiOfficial->getOffCommandExecutedTs()) { - nukiInst->_nextLockAction = networkInst->_offCommand; - networkInst->_offCommandExecutedTs = 0; + nukiInst->_nextLockAction = _offCommand; + _nukiOfficial->clearOffCommandExecutedTs(); } if(_nextLockAction != (NukiLock::LockAction)0xff) { @@ -293,7 +290,7 @@ void NukiWrapper::update() _nextLockAction = (NukiLock::LockAction) 0xff; _network->publishRetry("--"); retryCount = 0; - if(!_network->_offConnected) _statusUpdated = true; Log->println(F("Lock: updating status after action")); + if(!_nukiOfficial->getOffConnected()) _statusUpdated = true; Log->println(F("Lock: updating status after action")); _statusUpdatedTs = ts; if(_intervalLockstate > 10) _nextLockStateUpdateTs = ts + 10 * 1000; } @@ -305,7 +302,7 @@ void NukiWrapper::update() _nextLockAction = (NukiLock::LockAction) 0xff; } } - if(_statusUpdated || _nextLockStateUpdateTs == 0 || ts >= _nextLockStateUpdateTs || (queryCommands & QUERY_COMMAND_LOCKSTATE) > 0) + if(_nukiOfficial->getStatusUpdated() || _statusUpdated || _nextLockStateUpdateTs == 0 || ts >= _nextLockStateUpdateTs || (queryCommands & QUERY_COMMAND_LOCKSTATE) > 0) { Log->println("Updating Lock state based on status, timer or query"); _statusUpdated = false; @@ -497,7 +494,7 @@ void NukiWrapper::updateKeyTurnerState() updateGpioOutputs(); } - else if(!_network->_offConnected && (esp_timer_get_time() / 1000) < _statusUpdatedTs + 10000) + else if(!_nukiOfficial->getOffConnected() && (esp_timer_get_time() / 1000) < _statusUpdatedTs + 10000) { _statusUpdated = true; Log->println(F("Lock: Keep updating status on intermediate lock state")); @@ -933,6 +930,11 @@ NukiLock::LockAction NukiWrapper::lockActionToEnum(const char *str) } LockActionResult NukiWrapper::onLockActionReceivedCallback(const char *value) +{ + return nukiInst->onLockActionReceived(value); +} + +LockActionResult NukiWrapper::onLockActionReceived(const char *value) { NukiLock::LockAction action; @@ -947,33 +949,28 @@ LockActionResult NukiWrapper::onLockActionReceivedCallback(const char *value) } else return LockActionResult::UnknownAction; - nukiLockPreferences = new Preferences(); - nukiLockPreferences->begin("nukihub", true); uint32_t aclPrefs[17]; - nukiLockPreferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); + _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); if((action == NukiLock::LockAction::Lock && (int)aclPrefs[0] == 1) || (action == NukiLock::LockAction::Unlock && (int)aclPrefs[1] == 1) || (action == NukiLock::LockAction::Unlatch && (int)aclPrefs[2] == 1) || (action == NukiLock::LockAction::LockNgo && (int)aclPrefs[3] == 1) || (action == NukiLock::LockAction::LockNgoUnlatch && (int)aclPrefs[4] == 1) || (action == NukiLock::LockAction::FullLock && (int)aclPrefs[5] == 1) || (action == NukiLock::LockAction::FobAction1 && (int)aclPrefs[6] == 1) || (action == NukiLock::LockAction::FobAction2 && (int)aclPrefs[7] == 1) || (action == NukiLock::LockAction::FobAction3 && (int)aclPrefs[8] == 1)) { - if(!networkInst->_offConnected) nukiInst->_nextLockAction = action; + if(!_nukiOfficial->getOffConnected()) nukiInst->_nextLockAction = action; else { - if(nukiLockPreferences->getBool(preference_official_hybrid_actions, false)) + if(_preferences->getBool(preference_official_hybrid_actions, false)) { - networkInst->_offCommandExecutedTs = (esp_timer_get_time() / 1000) + 2000; - networkInst->_offCommand = action; - networkInst->publishOffAction((int)action); + _nukiOfficial->setOffCommandExecutedTs((esp_timer_get_time() / 1000) + 2000); + _offCommand = action; + _network->publishOffAction((int)action); } else { nukiInst->_nextLockAction = action; } } - nukiLockPreferences->end(); return LockActionResult::Success; } - nukiLockPreferences->end(); - return LockActionResult::AccessDenied; } @@ -989,7 +986,7 @@ void NukiWrapper::onConfigUpdateReceivedCallback(const char *value) bool NukiWrapper::offConnected() { - return _network->_offConnected; + return _nukiOfficial->getOffConnected(); } Nuki::AdvertisingMode NukiWrapper::advertisingModeToEnum(const char *str) @@ -1085,152 +1082,7 @@ Nuki::BatteryType NukiWrapper::batteryTypeToEnum(const char* str) void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value) { - char str[50]; - bool publishBatteryJson = false; - memset(&str, 0, sizeof(str)); - - Log->println("Official Nuki change recieved"); - Log->print(F("Topic: ")); - Log->println(topic); - Log->print(F("Value: ")); - Log->println(value); - - if(strcmp(topic, mqtt_topic_official_connected) == 0) - { - Log->print(F("Connected: ")); - Log->println((strcmp(value, "true") == 0 ? 1 : 0)); - _network->_offConnected = (strcmp(value, "true") == 0 ? 1 : 0); - _network->publishBool(mqtt_hybrid_state, _network->_offConnected, true); - - if(!_network->_offConnected) _nextHybridLockStateUpdateTs = (esp_timer_get_time() / 1000) + _intervalHybridLockstate * 1000; - else _nextHybridLockStateUpdateTs = 0; - } - else if(strcmp(topic, mqtt_topic_official_state) == 0) - { - _network->_offState = atoi(value); - _statusUpdated = true; - Log->println(F("Lock: Updating status on Hybrid state change")); - _network->publishStatusUpdated(_statusUpdated); - NukiLock::lockstateToString((NukiLock::LockState)_network->_offState, str); - _network->publishString(mqtt_topic_lock_state, str, true); - - Log->print(F("Lockstate: ")); - Log->println(str); - - _network->publishState((NukiLock::LockState)_network->_offState); - } - else if(strcmp(topic, mqtt_topic_official_doorsensorState) == 0) - { - _network->_offDoorsensorState = atoi(value); - _statusUpdated = true; - Log->println(F("Lock: Updating status on Hybrid door sensor state change")); - _network->publishStatusUpdated(_statusUpdated); - NukiLock::doorSensorStateToString((NukiLock::DoorSensorState)_network->_offDoorsensorState, str); - - Log->print(F("Doorsensor state: ")); - Log->println(str); - - _network->publishString(mqtt_topic_lock_door_sensor_state, str, true); - } - else if(strcmp(topic, mqtt_topic_official_batteryCritical) == 0) - { - _network->_offCritical = (strcmp(value, "true") == 0 ? 1 : 0); - - Log->print(F("Battery critical: ")); - Log->println(_network->_offCritical); - - if(!_disableNonJSON) _network->publishBool(mqtt_topic_battery_critical, _network->_offCritical, true); - publishBatteryJson = true; - } - else if(strcmp(topic, mqtt_topic_official_batteryCharging) == 0) - { - _network->_offCharging = (strcmp(value, "true") == 0 ? 1 : 0); - - Log->print(F("Battery charging: ")); - Log->println(_network->_offCharging); - - if(!_disableNonJSON) _network->publishBool(mqtt_topic_battery_charging, _network->_offCharging, true); - publishBatteryJson = true; - } - else if(strcmp(topic, mqtt_topic_official_batteryChargeState) == 0) - { - _network->_offChargeState = atoi(value); - - Log->print(F("Battery level: ")); - Log->println(_network->_offChargeState); - - if(!_disableNonJSON) _network->publishInt(mqtt_topic_battery_level, _network->_offChargeState, true); - publishBatteryJson = true; - } - else if(strcmp(topic, mqtt_topic_official_keypadBatteryCritical) == 0) - { - _network->_offKeypadCritical = (strcmp(value, "true") == 0 ? 1 : 0); - if(!_disableNonJSON) _network->publishBool(mqtt_topic_battery_keypad_critical, _network->_offKeypadCritical, true); - publishBatteryJson = true; - } - else if(strcmp(topic, mqtt_topic_official_doorsensorBatteryCritical) == 0) - { - _network->_offDoorsensorCritical = (strcmp(value, "true") == 0 ? 1 : 0); - if(!_disableNonJSON) _network->publishBool(mqtt_topic_battery_doorsensor_critical, _network->_offDoorsensorCritical, true); - publishBatteryJson = true; - } - else if(strcmp(topic, mqtt_topic_official_commandResponse) == 0) - { - _network->_offCommandResponse = atoi(value); - if(_network->_offCommandResponse == 0) networkInst->_offCommandExecutedTs = 0; - char resultStr[15] = {0}; - NukiLock::cmdResultToString((Nuki::CmdResult)_network->_offCommandResponse, resultStr); - _network->publishCommandResult(resultStr); - } - else if(strcmp(topic, mqtt_topic_official_lockActionEvent) == 0) - { - networkInst->_offCommandExecutedTs = 0; - _network->_offLockActionEvent = (char*)value; - String LockActionEvent = _network->_offLockActionEvent; - const int ind1 = LockActionEvent.indexOf(','); - const int ind2 = LockActionEvent.indexOf(',', ind1+1); - const int ind3 = LockActionEvent.indexOf(',', ind2+1); - const int ind4 = LockActionEvent.indexOf(',', ind3+1); - const int ind5 = LockActionEvent.indexOf(',', ind4+1); - - _network->_offLockAction = atoi(LockActionEvent.substring(0, ind1).c_str()); - _network->_offTrigger = atoi(LockActionEvent.substring(ind1+1, ind2+1).c_str()); - _network->_offAuthId = atoi(LockActionEvent.substring(ind2+1, ind3+1).c_str()); - _network->_offCodeId = atoi(LockActionEvent.substring(ind3+1, ind4+1).c_str()); - _network->_offContext = atoi(LockActionEvent.substring(ind4+1, ind5+1).c_str()); - - memset(&str, 0, sizeof(str)); - lockactionToString((NukiLock::LockAction)_network->_offLockAction, str); - _network->publishString(mqtt_topic_lock_last_lock_action, str, true); - - memset(&str, 0, sizeof(str)); - triggerToString((NukiLock::Trigger)_network->_offTrigger, str); - _network->publishString(mqtt_topic_lock_trigger, str, true); - - if(_network->_offAuthId > 0 || _network->_offCodeId > 0) - { - if(_network->_offCodeId > 0) _network->_authId = _network->_offCodeId; - else _network->_authId = _network->_offAuthId; - - /* - _network->_authName = RETRIEVE FROM VECTOR AFTER AUTHORIZATION ENTRIES ARE IMPLEMENTED; - _network->_offContext = BASE ON CONTEXT OF TRIGGER AND PUBLISH TO MQTT; - */ - } - } - - if(publishBatteryJson) - { - JsonDocument jsonBattery; - char _resbuf[2048]; - jsonBattery["critical"] = _network->_offCritical ? "1" : "0"; - jsonBattery["charging"] = _network->_offCharging ? "1" : "0"; - jsonBattery["level"] = _network->_offChargeState; - jsonBattery["keypadCritical"] = _network->_offKeypadCritical ? "1" : "0"; - jsonBattery["doorSensorCritical"] = _network->_offDoorsensorCritical ? "1" : "0"; - serializeJson(jsonBattery, _resbuf, sizeof(_resbuf)); - _network->publishString(mqtt_topic_battery_basic_json, _resbuf, true); - } + _nukiOfficial->onOfficialUpdateReceived(topic, value); } void NukiWrapper::onConfigUpdateReceived(const char *value) @@ -1798,53 +1650,59 @@ void NukiWrapper::onAuthCommandReceivedCallback(const char *value) nukiInst->onAuthCommandReceived(value); } + void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin) +{ + nukiInst->onGpioActionReceived(action, pin); +} + +void NukiWrapper::onGpioActionReceived(const GpioAction &action, const int &pin) { switch(action) { case GpioAction::Lock: - if(!networkInst->_offConnected) nukiInst->lock(); + if(!_nukiOfficial->getOffConnected()) nukiInst->lock(); else { - networkInst->_offCommandExecutedTs = (esp_timer_get_time() / 1000) + 2000; - networkInst->_offCommand = NukiLock::LockAction::Lock; - networkInst->publishOffAction(2); + _nukiOfficial->setOffCommandExecutedTs((esp_timer_get_time() / 1000) + 2000); + _offCommand = NukiLock::LockAction::Lock; + _network->publishOffAction(2); } break; case GpioAction::Unlock: - if(!networkInst->_offConnected) nukiInst->unlock(); + if(!_nukiOfficial->getOffConnected()) nukiInst->unlock(); else { - networkInst->_offCommandExecutedTs = (esp_timer_get_time() / 1000) + 2000; - networkInst->_offCommand = NukiLock::LockAction::Unlock; - networkInst->publishOffAction(1); + _nukiOfficial->setOffCommandExecutedTs((esp_timer_get_time() / 1000) + 2000); + _offCommand = NukiLock::LockAction::Unlock; + _network->publishOffAction(1); } break; case GpioAction::Unlatch: - if(!networkInst->_offConnected) nukiInst->unlatch(); + if(!_nukiOfficial->getOffConnected()) nukiInst->unlatch(); else { - networkInst->_offCommandExecutedTs = (esp_timer_get_time() / 1000) + 2000; - networkInst->_offCommand = NukiLock::LockAction::Unlatch; - networkInst->publishOffAction(3); + _nukiOfficial->setOffCommandExecutedTs((esp_timer_get_time() / 1000) + 2000); + _offCommand = NukiLock::LockAction::Unlatch; + _network->publishOffAction(3); } break; case GpioAction::LockNgo: - if(!networkInst->_offConnected) nukiInst->lockngo(); + if(!_nukiOfficial->getOffConnected()) nukiInst->lockngo(); else { - networkInst->_offCommandExecutedTs = (esp_timer_get_time() / 1000) + 2000; - networkInst->_offCommand = NukiLock::LockAction::LockNgo; - networkInst->publishOffAction(4); + _nukiOfficial->setOffCommandExecutedTs((esp_timer_get_time() / 1000) + 2000); + _offCommand = NukiLock::LockAction::LockNgo; + _network->publishOffAction(4); } break; case GpioAction::LockNgoUnlatch: - if(!networkInst->_offConnected) nukiInst->lockngounlatch(); + if(!_nukiOfficial->getOffConnected()) nukiInst->lockngounlatch(); else { - networkInst->_offCommandExecutedTs = (esp_timer_get_time() / 1000) + 2000; - networkInst->_offCommand = NukiLock::LockAction::LockNgoUnlatch; - networkInst->publishOffAction(5); + _nukiOfficial->setOffCommandExecutedTs((esp_timer_get_time() / 1000) + 2000); + _offCommand = NukiLock::LockAction::LockNgoUnlatch; + _network->publishOffAction(5); } break; } @@ -3142,13 +3000,12 @@ const bool NukiWrapper::hasKeypad() const void NukiWrapper::notify(Nuki::EventType eventType) { - if(!_network->_offConnected) + if(!_nukiOfficial->getOffConnected()) { - if(_offEnabled && _intervalHybridLockstate > 0 && (esp_timer_get_time() / 1000) > (_intervalHybridLockstate * 1000)) + if(_nukiOfficial->getOffEnabled() && _intervalHybridLockstate > 0 && (esp_timer_get_time() / 1000) > (_intervalHybridLockstate * 1000)) { Log->println("OffKeyTurnerStatusUpdated"); _statusUpdated = true; - _nextHybridLockStateUpdateTs = (esp_timer_get_time() / 1000) + _intervalHybridLockstate * 1000; } else { @@ -3290,4 +3147,4 @@ void NukiWrapper::updateGpioOutputs() break; } } -} \ No newline at end of file +} diff --git a/src/NukiWrapper.h b/src/NukiWrapper.h index a510b43..ee695f9 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -8,11 +8,12 @@ #include "Gpio.h" #include "LockActionResult.h" #include "NukiDeviceId.h" +#include "NukiOfficial.h" class NukiWrapper : public Nuki::SmartlockEventHandler { public: - NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, Gpio* gpio, Preferences* preferences); + NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NukiNetworkLock* network, NukiOfficial* nukiOfficial, Gpio* gpio, Preferences* preferences); virtual ~NukiWrapper(); void initialize(const bool& firstStart); @@ -56,12 +57,14 @@ private: static void onTimeControlCommandReceivedCallback(const char* value); static void onAuthCommandReceivedCallback(const char* value); static void gpioActionCallback(const GpioAction& action, const int& pin); + LockActionResult onLockActionReceived(const char* value); void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled); void onOfficialUpdateReceived(const char* topic, const char* value); void onConfigUpdateReceived(const char* value); void onKeypadJsonCommandReceived(const char* value); void onTimeControlCommandReceived(const char* value); void onAuthCommandReceived(const char* value); + void onGpioActionReceived(const GpioAction& action, const int& pin); void updateKeyTurnerState(); void updateBatteryState(); @@ -93,6 +96,7 @@ private: NukiLock::NukiLock _nukiLock; BleScanner::Scanner* _bleScanner = nullptr; NukiNetworkLock* _network = nullptr; + NukiOfficial* _nukiOfficial = nullptr; Gpio* _gpio = nullptr; Preferences* _preferences; int _intervalLockstate = 0; // seconds @@ -113,13 +117,14 @@ private: NukiLock::BatteryReport _batteryReport; NukiLock::BatteryReport _lastBatteryReport; + NukiLock::LockAction _offCommand = (NukiLock::LockAction)0xff; + NukiLock::Config _nukiConfig = {0}; NukiLock::AdvancedConfig _nukiAdvancedConfig = {0}; bool _nukiConfigValid = false; bool _nukiAdvancedConfigValid = false; bool _hassEnabled = false; bool _hassSetupCompleted = false; - bool _offEnabled = false; bool _disableNonJSON = false; bool _paired = false; bool _statusUpdated = false; @@ -136,7 +141,6 @@ private: int64_t _statusUpdatedTs = 0; int64_t _nextRetryTs = 0; int64_t _nextLockStateUpdateTs = 0; - int64_t _nextHybridLockStateUpdateTs = 0; int64_t _nextBatteryReportTs = 0; int64_t _nextConfigUpdateTs = 0; int64_t _waitAuthLogUpdateTs = 0; diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index 858d15c..fc0cb16 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -51,7 +51,7 @@ #define preference_webserver_enabled (char*)"websrvena" #define preference_update_from_mqtt (char*)"updMqtt" #define preference_disable_non_json (char*)"disnonjson" -#define preference_official_hybrid (char*)"offHybrid" +#define preference_official_hybrid_enabled (char*)"offHybrid" // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT #define preference_find_best_rssi (char*)"nwbestrssi" @@ -278,7 +278,7 @@ private: preference_keypad_info_enabled, preference_keypad_publish_code, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_conf_info_enabled, preference_register_as_app, preference_register_opener_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user, preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, - preference_official_hybrid, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, + preference_official_hybrid_enabled, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries, preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq, @@ -297,7 +297,7 @@ private: preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_enable_bootloop_reset, preference_webserver_enabled, preference_find_best_rssi, preference_restart_on_disconnect, preference_keypad_control_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, preference_show_secrets, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_register_as_app, preference_register_opener_as_app, preference_ip_dhcp_enabled, - preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid, + preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid_enabled, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt, preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, preference_ntw_reconfigure diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index da0b7c2..b51a608 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -1254,9 +1254,9 @@ bool WebCfgServer::processArgs(PsychicRequest *request, String& message) } else if(key == "OFFHYBRID") { - if(_preferences->getBool(preference_official_hybrid, false) != (value == "1")) + if(_preferences->getBool(preference_official_hybrid_enabled, false) != (value == "1")) { - _preferences->putBool(preference_official_hybrid, (value == "1")); + _preferences->putBool(preference_official_hybrid_enabled, (value == "1")); if((value == "1")) _preferences->putBool(preference_register_as_app, true); Log->print(F("Setting changed: ")); Log->println(key); @@ -2626,7 +2626,7 @@ esp_err_t WebCfgServer::buildHtml(PsychicRequest *request) String lockState = pinStateToString(_preferences->getInt(preference_lock_pin_status, 4)); printParameter(&response, "Nuki Lock PIN status", lockState.c_str(), "", "lockPin"); - if(_preferences->getBool(preference_official_hybrid, false)) + if(_preferences->getBool(preference_official_hybrid_enabled, false)) { String offConnected = _nuki->offConnected() ? "Yes": "No"; printParameter(&response, "Nuki Lock hybrid mode connected", offConnected.c_str(), "", "lockHybrid"); @@ -2811,7 +2811,6 @@ esp_err_t WebCfgServer::buildMqttConfigHtml(PsychicRequest *request) // printCheckBox(&response, "HYBRIDRETRY", "Retry command sent using official MQTT over BLE if failed", _preferences->getBool(preference_official_hybrid_retry), ""); // NOT IMPLEMENTED (YET?) response.print(""); response.print("* If no encryption is configured for the MQTT broker, leave empty.

"); - response.print("

IP Address assignment

"); response.print(""); printCheckBox(&response, "DHCPENA", "Enable DHCP", _preferences->getBool(preference_ip_dhcp_enabled), ""); diff --git a/src/main.cpp b/src/main.cpp index a6ca969..ab5702a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,7 @@ NukiNetworkLock* networkLock = nullptr; NukiNetworkOpener* networkOpener = nullptr; BleScanner::Scanner* bleScanner = nullptr; NukiWrapper* nuki = nullptr; +NukiOfficial* nukiOfficial = nullptr; NukiOpenerWrapper* nukiOpener = nullptr; NukiDeviceId* deviceIdLock = nullptr; NukiDeviceId* deviceIdOpener = nullptr; @@ -139,16 +140,21 @@ void networkTask(void *pvParameters) bool connected = network->update(); - #ifndef NUKI_HUB_UPDATER - #ifdef DEBUG_NUKIHUB +#ifndef NUKI_HUB_UPDATER + if(connected && networkLock != nullptr) + { + networkLock->update(); + } + +#ifdef DEBUG_NUKIHUB if(connected && reroute) { reroute = false; setReroute(); } - #endif +#endif if(connected && openerEnabled) networkOpener->update(); - #endif +#endif if((esp_timer_get_time() / 1000) - networkLoopTs > 120000) { @@ -466,10 +472,12 @@ void setup() const String mqttLockPath = preferences->getString(preference_mqtt_lock_path); + nukiOfficial = new NukiOfficial(preferences); + network = new NukiNetwork(preferences, gpio, mqttLockPath, CharBuffer::get(), buffer_size); network->initialize(); - networkLock = new NukiNetworkLock(network, preferences, CharBuffer::get(), buffer_size); + networkLock = new NukiNetworkLock(network, nukiOfficial, preferences, CharBuffer::get(), buffer_size); networkLock->initialize(); if(openerEnabled) @@ -481,7 +489,7 @@ void setup() Log->println(lockEnabled ? F("Nuki Lock enabled") : F("Nuki Lock disabled")); if(lockEnabled) { - nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, gpio, preferences); + nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, nukiOfficial, gpio, preferences); nuki->initialize(firstStart); }