diff --git a/CMakeLists.txt b/CMakeLists.txt index 61f98e4..51c7871 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ include_directories(${PROJECT_NAME} set(SRCFILES Pins.h Config.h + CharBuffer.cpp Network.cpp MqttReceiver.h NetworkLock.cpp diff --git a/CharBuffer.cpp b/CharBuffer.cpp new file mode 100644 index 0000000..62b47b7 --- /dev/null +++ b/CharBuffer.cpp @@ -0,0 +1,13 @@ +#include "CharBuffer.h" + +void CharBuffer::initialize() +{ + _buffer = new char[CHAR_BUFFER_SIZE]; +} + +char *CharBuffer::get() +{ + return _buffer; +} + +char* CharBuffer::_buffer; diff --git a/CharBuffer.h b/CharBuffer.h new file mode 100644 index 0000000..4abaf87 --- /dev/null +++ b/CharBuffer.h @@ -0,0 +1,13 @@ +#pragma once + +#define CHAR_BUFFER_SIZE 4096 + +class CharBuffer +{ +public: + static void initialize(); + static char* get(); + +private: + static char* _buffer; +}; \ No newline at end of file diff --git a/Network.cpp b/Network.cpp index 8453f77..7c7fb7c 100644 --- a/Network.cpp +++ b/Network.cpp @@ -890,8 +890,28 @@ void Network::publishHASSConfigAccessLog(char *deviceType, const char *baseTopic "", "diagnostic", "", - { { "value_template", "{{ (value_json|selectattr('type', 'eq', 'LockAction')|selectattr('action', 'in', ['Lock', 'Unlock', 'Unlatch'])|first).authorizationName }}" }});} + { { "ic", "mdi:format-list-bulleted" }, + { "value_template", "{{ (value_json|selectattr('type', 'eq', 'LockAction')|selectattr('action', 'in', ['Lock', 'Unlock', 'Unlatch'])|first).authorizationName }}" }}); +} +void Network::publishHASSConfigKeypadAttemptInfo(char *deviceType, const char *baseTopic, char *name, char *uidString) +{ + publishHassTopic("sensor", + "keypad_status", + uidString, + "_keypad_stats", + "Keypad status", + name, + baseTopic, + mqtt_topic_lock_log, + deviceType, + "", + "", + "diagnostic", + "", + { { "ic", "mdi:drag-vertical" }, + { "value_template", "{% for state in value_json %} {% if state.type == 'KeypadAction' %} {{ state.completionStatus }} {% endif %} {% endfor %}" }}); +} void Network::publishHASSWifiRssiConfig(char *deviceType, const char *baseTopic, char *name, char *uidString) { diff --git a/Network.h b/Network.h index 9b1e839..2df975e 100644 --- a/Network.h +++ b/Network.h @@ -47,6 +47,7 @@ public: void publishHASSConfigLedBrightness(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigSoundLevel(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSConfigAccessLog(char* deviceType, const char* baseTopic, char* name, char* uidString); + void publishHASSConfigKeypadAttemptInfo(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSWifiRssiConfig(char* deviceType, const char* baseTopic, char* name, char* uidString); void publishHASSBleRssiConfig(char* deviceType, const char* baseTopic, char* name, char* uidString); void removeHASSConfig(char* uidString); diff --git a/NetworkLock.cpp b/NetworkLock.cpp index 5669aa8..dbd458c 100644 --- a/NetworkLock.cpp +++ b/NetworkLock.cpp @@ -5,10 +5,14 @@ #include "PreferencesKeys.h" #include "Logger.h" #include "RestartReason.h" +#include "CharBuffer.h" +#include -NetworkLock::NetworkLock(Network* network, Preferences* preferences) +NetworkLock::NetworkLock(Network* network, Preferences* preferences, char* buffer, size_t bufferSize) : _network(network), - _preferences(preferences) + _preferences(preferences), + _buffer(buffer), + _bufferSize(bufferSize) { _configTopics.reserve(5); _configTopics.push_back(mqtt_topic_config_button_enabled); @@ -273,7 +277,6 @@ void NetworkLock::publishBinaryState(NukiLock::LockState lockState) } } - void NetworkLock::publishAuthorizationInfo(const std::list& logEntries) { char str[50]; @@ -283,7 +286,7 @@ void NetworkLock::publishAuthorizationInfo(const std::list& char authName[33]; memset(authName, 0, sizeof(authName)); - String json = "[\n"; + DynamicJsonDocument json(_bufferSize); for(const auto& log : logEntries) { @@ -294,90 +297,75 @@ void NetworkLock::publishAuthorizationInfo(const std::list& memcpy(authName, log.name, sizeof(log.name)); } - json.concat("{\n"); + auto entry = json.add(); - json.concat("\"index\": "); json.concat(log.index); json.concat(",\n"); - json.concat("\"authorizationId\": "); json.concat(log.authId); json.concat(",\n"); - - memset(str, 0, sizeof(str)); - memcpy(str, log.name, sizeof(log.name)); - json.concat("\"authorizationName\": \""); json.concat(str); json.concat("\",\n"); - - json.concat("\"timeYear\": "); json.concat(log.timeStampYear); json.concat(",\n"); - json.concat("\"timeMonth\": "); json.concat(log.timeStampMonth); json.concat(",\n"); - json.concat("\"timeDay\": "); json.concat(log.timeStampDay); json.concat(",\n"); - json.concat("\"timeHour\": "); json.concat(log.timeStampHour); json.concat(",\n"); - json.concat("\"timeMinute\": "); json.concat(log.timeStampMinute); json.concat(",\n"); - json.concat("\"timeSecond\": "); json.concat(log.timeStampSecond); json.concat(",\n"); + entry["index"] = log.index; + entry["authorizationId"] = log.authId; + entry["authorizationName"] = log.name; + entry["timeYear"] = log.timeStampYear; + entry["timeMonth"] = log.timeStampMonth; + entry["timeDay"] = log.timeStampDay; + entry["timeHour"] = log.timeStampHour; + entry["timeMinute"] = log.timeStampMinute; + entry["timeSecond"] = log.timeStampSecond; memset(str, 0, sizeof(str)); loggingTypeToString(log.loggingType, str); - json.concat("\"type\": \""); json.concat(str); json.concat("\",\n"); + entry["type"] = str; switch(log.loggingType) { case NukiLock::LoggingType::LockAction: memset(str, 0, sizeof(str)); NukiLock::lockactionToString((NukiLock::LockAction)log.data[0], str); - json.concat("\"action\": \""); json.concat(str); json.concat("\",\n"); + entry["action"] = str; memset(str, 0, sizeof(str)); NukiLock::triggerToString((NukiLock::Trigger)log.data[1], str); - json.concat("\"trigger\": \""); json.concat(str); json.concat("\",\n"); + entry["trigger"] = str; memset(str, 0, sizeof(str)); NukiLock::completionStatusToString((NukiLock::CompletionStatus)log.data[3], str); - json.concat("\"completionStatus\": \""); json.concat(str); json.concat("\"\n"); + entry["completionStatus"] = str; break; case NukiLock::LoggingType::KeypadAction: memset(str, 0, sizeof(str)); NukiLock::lockactionToString((NukiLock::LockAction)log.data[0], str); - json.concat("\"action\": \""); json.concat(str); json.concat("\",\n"); + entry["action"] = str; memset(str, 0, sizeof(str)); NukiLock::completionStatusToString((NukiLock::CompletionStatus)log.data[2], str); - json.concat("\"completionStatus\": \""); json.concat(str); json.concat("\"\n"); + entry["completionStatus"] = str; break; case NukiLock::LoggingType::DoorSensor: memset(str, 0, sizeof(str)); NukiLock::lockactionToString((NukiLock::LockAction)log.data[0], str); - json.concat("\"action\": \""); + switch(log.data[0]) { case 0: - json.concat("DoorOpened"); + entry["action"] = "DoorOpened"; break; case 1: - json.concat("DoorClosed"); + entry["action"] = "DoorClosed"; break; case 2: - json.concat("SensorJammed"); + entry["action"] = "SensorJammed"; break; default: - json.concat("Unknown"); + entry["action"] = "Unknown"; break; } - json.concat("\",\n"); memset(str, 0, sizeof(str)); NukiLock::completionStatusToString((NukiLock::CompletionStatus)log.data[2], str); - json.concat("\"completionStatus\": \""); json.concat(str); json.concat("\"\n"); + entry["completionStatus"] = str; break; } - - json.concat("}"); - if(&log == &logEntries.back()) - { - json.concat("\n"); - } - else - { - json.concat(",\n"); - } } - json.concat("]"); - publishString(mqtt_topic_lock_log, json); + serializeJson(json, reinterpret_cast(*_buffer)); + publishString(mqtt_topic_lock_log, _buffer); if(authFound) { @@ -540,6 +528,15 @@ void NetworkLock::publishHASSConfig(char *deviceType, const char *baseTopic, cha { _network->removeHASSConfigTopic("sensor", "last_action_authorization", uidString); } + + if(hasKeypad) + { + _network->publishHASSConfigKeypadAttemptInfo(deviceType, baseTopic, name, uidString); + } + else + { + _network->removeHASSConfigTopic("sensor", "keypad_status", uidString); + } } void NetworkLock::removeHASSConfig(char *uidString) diff --git a/NetworkLock.h b/NetworkLock.h index d529fc5..47fb12f 100644 --- a/NetworkLock.h +++ b/NetworkLock.h @@ -11,10 +11,12 @@ #include "Network.h" #include "QueryCommand.h" +#define LOCK_LOG_JSON_BUFFER_SIZE 2048 + class NetworkLock : public MqttReceiver { public: - explicit NetworkLock(Network* network, Preferences* preferences); + explicit NetworkLock(Network* network, Preferences* preferences, char* buffer, size_t bufferSize); virtual ~NetworkLock(); void initialize(); @@ -79,6 +81,9 @@ private: int _keypadCommandEnabled = 1; uint8_t _queryCommands = 0; + char* _buffer; + size_t _bufferSize; + 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; diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp index 0650a5d..72ec80c 100644 --- a/NetworkOpener.cpp +++ b/NetworkOpener.cpp @@ -4,6 +4,7 @@ #include "PreferencesKeys.h" #include "Logger.h" #include "Config.h" +#include NetworkOpener::NetworkOpener(Network* network, Preferences* preferences) : _preferences(preferences), @@ -268,130 +269,114 @@ void NetworkOpener::publishAuthorizationInfo(const std::list(*jsonOut)); + publishString(mqtt_topic_lock_log, jsonOut); + + delete jsonOut; if(authFound) { diff --git a/NetworkOpener.h b/NetworkOpener.h index a868dcf..9c1c2e4 100644 --- a/NetworkOpener.h +++ b/NetworkOpener.h @@ -9,6 +9,8 @@ #include "NukiOpenerConstants.h" #include "NetworkLock.h" +#define OPENER_LOG_JSON_BUFFER_SIZE 2048 + class NetworkOpener : public MqttReceiver { public: diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index e7ade08..cc33c25 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -646,7 +646,7 @@ void NukiOpenerWrapper::setupHASS() String baseTopic = _preferences->getString(preference_mqtt_opener_path); char uidString[20]; itoa(_nukiConfig.nukiId, uidString, 16); - _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, _publishAuthData, "deactivateRTO","activateRTO","electricStrikeActuation","locked","unlocked"); + _network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateRTO","activateRTO","electricStrikeActuation","locked","unlocked"); _hassSetupCompleted = true; Log->println("HASS setup for opener completed."); diff --git a/PresenceDetection.cpp b/PresenceDetection.cpp index 786f4cb..45a8702 100644 --- a/PresenceDetection.cpp +++ b/PresenceDetection.cpp @@ -1,14 +1,15 @@ #include "PresenceDetection.h" #include "PreferencesKeys.h" #include "Logger.h" +#include "CharBuffer.h" -PresenceDetection::PresenceDetection(Preferences* preferences, BleScanner::Scanner *bleScanner, Network* network) +PresenceDetection::PresenceDetection(Preferences* preferences, BleScanner::Scanner *bleScanner, Network* network, char* buffer, size_t bufferSize) : _preferences(preferences), _bleScanner(bleScanner), - _network(network) + _network(network), + _csv(buffer), + _bufferSize(bufferSize) { - _csv = new char[presence_detection_buffer_size]; - _timeout = _preferences->getInt(preference_presence_detection_timeout) * 1000; if(_timeout == 0) { @@ -41,7 +42,7 @@ void PresenceDetection::update() delay(3000); if(_timeout < 0) return; - memset(_csv, 0, presence_detection_buffer_size); + memset(_csv, 0, _bufferSize); if(_devices.size() == 0) { @@ -60,7 +61,7 @@ void PresenceDetection::update() } // Prevent csv buffer overflow - if(_csvIndex > presence_detection_buffer_size - (sizeof(it.second.name) + sizeof(it.second.address) + 10)) + if(_csvIndex > _bufferSize - (sizeof(it.second.name) + sizeof(it.second.address) + 10)) { break; } diff --git a/PresenceDetection.h b/PresenceDetection.h index 6cc210c..049af9f 100644 --- a/PresenceDetection.h +++ b/PresenceDetection.h @@ -13,12 +13,10 @@ struct PdDevice bool hasRssi = false; }; -#define presence_detection_buffer_size 4096 - class PresenceDetection : public BleScanner::Subscriber { public: - PresenceDetection(Preferences* preferences, BleScanner::Scanner* bleScanner, Network* network); + PresenceDetection(Preferences* preferences, BleScanner::Scanner* bleScanner, Network* network, char* buffer, size_t bufferSize); virtual ~PresenceDetection(); void initialize(); @@ -33,6 +31,7 @@ private: BleScanner::Scanner* _bleScanner; Network* _network; char* _csv = {0}; + size_t _bufferSize = 0; std::map _devices; int _timeout = 20000; int _csvIndex = 0; diff --git a/main.cpp b/main.cpp index b6ffd5b..ea7725e 100644 --- a/main.cpp +++ b/main.cpp @@ -13,6 +13,7 @@ #include "Logger.h" #include "Config.h" #include "RestartReason.h" +#include "CharBuffer.h" Network* network = nullptr; NetworkLock* networkLock = nullptr; @@ -170,6 +171,8 @@ void setup() bool firstStart = initPreferences(); initializeRestartReason(); + CharBuffer::initialize(); + if(preferences->getInt(preference_restart_timer) > 0) { restartTs = preferences->getInt(preference_restart_timer) * 60 * 1000; @@ -182,7 +185,7 @@ void setup() network = new Network(preferences, mqttLockPath); network->initialize(); - networkLock = new NetworkLock(network, preferences); + networkLock = new NetworkLock(network, preferences, CharBuffer::get(), CHAR_BUFFER_SIZE); networkLock->initialize(); if(openerEnabled) @@ -226,7 +229,7 @@ void setup() webCfgServer = new WebCfgServer(nuki, nukiOpener, network, ethServer, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi); webCfgServer->initialize(); - presenceDetection = new PresenceDetection(preferences, bleScanner, network); + presenceDetection = new PresenceDetection(preferences, bleScanner, network, CharBuffer::get(), CHAR_BUFFER_SIZE); presenceDetection->initialize(); setupTasks();