diff --git a/CMakeLists.txt b/CMakeLists.txt index 369c26d..d2f9357 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ include_directories(${PROJECT_NAME} file(GLOB SRCFILES Pins.h Network.cpp + NetworkOpener.cpp networkDevices/NetworkDevice.h networkDevices/WifiDevice.cpp networkDevices/W5500Device.cpp diff --git a/Network.cpp b/Network.cpp index 92dab7e..80541de 100644 --- a/Network.cpp +++ b/Network.cpp @@ -251,6 +251,11 @@ void Network::onMqttDataReceived(char *&topic, byte *&payload, unsigned int &len } } } + + if(_mqttTopicReceivedForwardCallback != nullptr) + { + _mqttTopicReceivedForwardCallback(topic, payload, length); + } } void Network::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurnerState, const NukiLock::KeyTurnerState& lastKeyTurnerState) @@ -345,6 +350,11 @@ void Network::setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallbac _configUpdateReceivedCallback = configUpdateReceivedCallback; } +void Network::setMqttDataReceivedForwardCallback(void (*callback)(char *, uint8_t *, unsigned int)) +{ + _mqttTopicReceivedForwardCallback = callback; +} + void Network::publishFloat(const char* topic, const float value, const uint8_t precision) { char str[30]; @@ -433,4 +443,9 @@ bool Network::comparePrefixedPath(const char *fullPath, const char *subPath) char prefixedPath[500]; buildMqttPath(subPath, prefixedPath); return strcmp(fullPath, prefixedPath) == 0; -} \ No newline at end of file +} + +NetworkDevice *Network::device() +{ + return _device; +} diff --git a/Network.h b/Network.h index bbc0284..3fea58b 100644 --- a/Network.h +++ b/Network.h @@ -39,9 +39,12 @@ public: void setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char* value)); void setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char* path, const char* value)); + void setMqttDataReceivedForwardCallback(void (*callback)(char*, uint8_t*, unsigned int)); void restartAndConfigureWifi(); + NetworkDevice* device(); + private: static void onMqttDataReceivedCallback(char* topic, byte* payload, unsigned int length); void onMqttDataReceived(char*& topic, byte*& payload, unsigned int& length); @@ -81,4 +84,5 @@ private: bool (*_lockActionReceivedCallback)(const char* value) = nullptr; void (*_configUpdateReceivedCallback)(const char* path, const char* value) = nullptr; + void (*_mqttTopicReceivedForwardCallback)(char*, uint8_t*, unsigned int) = nullptr; }; diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp new file mode 100644 index 0000000..e7d100d --- /dev/null +++ b/NetworkOpener.cpp @@ -0,0 +1,264 @@ +#include "NetworkOpener.h" +#include // https://github.com/tzapu/WiFiManager +#include "Arduino.h" +#include "MqttTopics.h" +#include "PreferencesKeys.h" +#include "Pins.h" + +NetworkOpener* nwInstOpener; + +NetworkOpener::NetworkOpener(Network* network, Preferences* preferences) + : _preferences(preferences), + _network(network) +{ + nwInstOpener = this; + + _configTopics.reserve(5); +// _configTopics.push_back(mqtt_topic_config_button_enabled); +// _configTopics.push_back(mqtt_topic_config_led_enabled); +// _configTopics.push_back(mqtt_topic_config_led_brightness); +// _configTopics.push_back(mqtt_topic_config_auto_unlock); +// _configTopics.push_back(mqtt_topic_config_auto_lock); +} + +void NetworkOpener::initialize() +{ + String mqttPath = _preferences->getString(preference_mqtt_opener_path); + if(mqttPath.length() > 0) + { + size_t len = mqttPath.length(); + for(int i=0; i < len; i++) + { + _mqttPath[i] = mqttPath.charAt(i); + } + } + else + { + strcpy(_mqttPath, "nukiopener"); + _preferences->putString(preference_mqtt_opener_path, _mqttPath); + } + + _network->setMqttDataReceivedForwardCallback(nwInstOpener->onMqttDataReceivedCallback); +} + +void NetworkOpener::update() +{ + bool connected = _network->device()->mqttClient()->connected(); + + if(!_isConnected && connected) + { + subscribe(mqtt_topic_lock_action); + } + + _isConnected = connected; + +// long ts = millis(); + +} + +void NetworkOpener::onMqttDataReceivedCallback(char *topic, byte *payload, unsigned int length) +{ + nwInstOpener->onMqttDataReceived(topic, payload, length); +} + +void NetworkOpener::onMqttDataReceived(char *&topic, byte *&payload, unsigned int &length) +{ + char value[50] = {0}; + size_t l = min(length, sizeof(value)-1); + + for(int i=0; i> 1; + bool critical = (keyTurnerState.criticalBatteryState & 0b00000001) > 0; + bool charging = (keyTurnerState.criticalBatteryState & 0b00000010) > 0; + publishInt(mqtt_topic_battery_level, level); // percent + publishBool(mqtt_topic_battery_critical, critical); + publishBool(mqtt_topic_battery_charging, charging); + } + + _firstTunerStatePublish = false; +} + +void NetworkOpener::publishAuthorizationInfo(const uint32_t authId, const char *authName) +{ + publishUInt(mqtt_topic_lock_auth_id, authId); + publishString(mqtt_topic_lock_auth_name, authName); +} + +void NetworkOpener::publishCommandResult(const char *resultStr) +{ + publishString(mqtt_topic_lock_action_command_result, resultStr); +} + +void NetworkOpener::publishBatteryReport(const NukiOpener::BatteryReport& batteryReport) +{ + publishFloat(mqtt_topic_battery_voltage, (float)batteryReport.batteryVoltage / 1000.0); + publishInt(mqtt_topic_battery_drain, batteryReport.batteryDrain); // milliwatt seconds + publishFloat(mqtt_topic_battery_max_turn_current, (float)batteryReport.maxTurnCurrent / 1000.0); + publishInt(mqtt_topic_battery_lock_distance, batteryReport.lockDistance); // degrees +} + +void NetworkOpener::publishConfig(const NukiOpener::Config &config) +{ + publishBool(mqtt_topic_config_button_enabled, config.buttonEnabled == 1); + publishBool(mqtt_topic_config_led_enabled, config.ledEnabled == 1); + publishInt(mqtt_topic_config_led_brightness, config.ledBrightness); +} + +void NetworkOpener::publishAdvancedConfig(const NukiOpener::AdvancedConfig &config) +{ + publishBool(mqtt_topic_config_auto_unlock, config.autoUnLockDisabled == 0); + publishBool(mqtt_topic_config_auto_lock, config.autoLockEnabled == 1); +} + +void NetworkOpener::setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char *)) +{ + _lockActionReceivedCallback = lockActionReceivedCallback; +} + +void NetworkOpener::setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char *, const char *)) +{ + _configUpdateReceivedCallback = configUpdateReceivedCallback; +} + +void NetworkOpener::publishFloat(const char* topic, const float value, const uint8_t precision) +{ + char str[30]; + dtostrf(value, 0, precision, str); + char path[200] = {0}; + buildMqttPath(topic, path); + _network->device()->mqttClient()->publish(path, str); +} + +void NetworkOpener::publishInt(const char *topic, const int value) +{ + char str[30]; + itoa(value, str, 10); + char path[200] = {0}; + buildMqttPath(topic, path); + _network->device()->mqttClient()->publish(path, str); +} + +void NetworkOpener::publishUInt(const char *topic, const unsigned int value) +{ + char str[30]; + utoa(value, str, 10); + char path[200] = {0}; + buildMqttPath(topic, path); + _network->device()->mqttClient()->publish(path, str); +} + +void NetworkOpener::publishBool(const char *topic, const bool value) +{ + char str[2] = {0}; + str[0] = value ? '1' : '0'; + char path[200] = {0}; + buildMqttPath(topic, path); + _network->device()->mqttClient()->publish(path, str); +} + +void NetworkOpener::publishString(const char *topic, const char *value) +{ + char path[200] = {0}; + buildMqttPath(topic, path); + _network->device()->mqttClient()->publish(path, value); +} + +void NetworkOpener::buildMqttPath(const char* path, char* outPath) +{ + int offset = 0; + for(const char& c : _mqttPath) + { + 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; +} + +void NetworkOpener::subscribe(const char *path) +{ + char prefixedPath[500]; + buildMqttPath(path, prefixedPath); + _network->device()->mqttClient()->subscribe(prefixedPath); +} + +bool NetworkOpener::comparePrefixedPath(const char *fullPath, const char *subPath) +{ + char prefixedPath[500]; + buildMqttPath(subPath, prefixedPath); + return strcmp(fullPath, prefixedPath) == 0; +} \ No newline at end of file diff --git a/NetworkOpener.h b/NetworkOpener.h new file mode 100644 index 0000000..2bcb1b1 --- /dev/null +++ b/NetworkOpener.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include "networkDevices/NetworkDevice.h" +#include "networkDevices/WifiDevice.h" +#include "networkDevices/W5500Device.h" +#include +#include +#include "NukiConstants.h" +#include "SpiffsCookie.h" +#include "NukiOpenerConstants.h" +#include "Network.h" + +class NetworkOpener +{ +public: + explicit NetworkOpener(Network* network, Preferences* preferences); + virtual ~NetworkOpener() = default; + + void initialize(); + void update(); + + void publishKeyTurnerState(const NukiOpener::KeyTurnerState& keyTurnerState, const NukiOpener::KeyTurnerState& lastKeyTurnerState); + void publishAuthorizationInfo(const uint32_t authId, const char* authName); + void publishCommandResult(const char* resultStr); + void publishBatteryReport(const NukiOpener::BatteryReport& batteryReport); + void publishConfig(const NukiOpener::Config& config); + void publishAdvancedConfig(const NukiOpener::AdvancedConfig& config); + + void setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char* value)); + void setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char* path, const char* value)); + +private: + static void onMqttDataReceivedCallback(char* topic, byte* payload, unsigned int length); + void onMqttDataReceived(char*& topic, byte*& payload, unsigned int& length); + 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); + void publishBool(const char* topic, const bool value); + void publishString(const char* topic, const char* value); + + void buildMqttPath(const char* path, char* outPath); + void subscribe(const char* path); + + Preferences* _preferences; + + Network* _network = nullptr; + + char _mqttPath[181] = {0}; + bool _isConnected = false; + + std::vector _configTopics; + + bool _firstTunerStatePublish = true; + + bool (*_lockActionReceivedCallback)(const char* value) = nullptr; + void (*_configUpdateReceivedCallback)(const char* path, const char* value) = nullptr; +}; diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index e3f1228..3f76e48 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -6,7 +6,7 @@ NukiOpenerWrapper* nukiOpenerInst; -NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, Network* network, Preferences* preferences) +NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, NetworkOpener* network, Preferences* preferences) : _deviceName(deviceName), _nukiOpener(deviceName, id), _bleScanner(scanner), @@ -115,7 +115,7 @@ void NukiOpenerWrapper::update() _network->publishCommandResult(resultStr); - Serial.print(F("Lock action result: ")); + Serial.print(F("Opener lock action result: ")); Serial.println(resultStr); _nextLockAction = (NukiOpener::LockAction)0xff; diff --git a/NukiOpenerWrapper.h b/NukiOpenerWrapper.h index 5358bb2..40d148e 100644 --- a/NukiOpenerWrapper.h +++ b/NukiOpenerWrapper.h @@ -1,7 +1,7 @@ #pragma once #include "NukiOpener.h" -#include "Network.h" +#include "NetworkOpener.h" #include "NukiOpenerConstants.h" #include "NukiDataTypes.h" #include "BleScanner.h" @@ -9,7 +9,7 @@ class NukiOpenerWrapper : public NukiOpener::SmartlockEventHandler { public: - NukiOpenerWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, Network* network, Preferences* preferences); + NukiOpenerWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, NetworkOpener* network, Preferences* preferences); virtual ~NukiOpenerWrapper(); void initialize(); @@ -44,7 +44,7 @@ private: std::string _deviceName; NukiOpener::NukiOpener _nukiOpener; BleScanner::Scanner* _bleScanner; - Network* _network; + NetworkOpener* _network; Preferences* _preferences; int _intervalLockstate = 0; // seconds int _intervalBattery = 0; // seconds diff --git a/main.cpp b/main.cpp index 1c01d20..9bde357 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,7 @@ #include "NukiOpenerWrapper.h" Network* network = nullptr; +NetworkOpener* networkOpener = nullptr; WebCfgServer* webCfgServer = nullptr; NukiWrapper* nuki = nullptr; NukiOpenerWrapper* nukiOpener = nullptr; @@ -23,6 +24,7 @@ void networkTask(void *pvParameters) while(true) { network->update(); + networkOpener->update(); webCfgServer->update(); vTaskDelay(200 / portTICK_PERIOD_MS); } @@ -112,6 +114,8 @@ void setup() preferences->begin("nukihub", false); network = new Network(networkDevice, preferences); network->initialize(); + networkOpener = new NetworkOpener(network, preferences); + networkOpener->initialize(); uint32_t deviceId = preferences->getUInt(preference_deviceId); if(deviceId == 0) @@ -125,7 +129,7 @@ void setup() nuki = new NukiWrapper("NukiHub", deviceId, network, preferences); nuki->initialize(); - nukiOpener = new NukiOpenerWrapper("NukiHub", deviceId, nuki->bleScanner(), network, preferences); + nukiOpener = new NukiOpenerWrapper("NukiHub", deviceId, nuki->bleScanner(), networkOpener, preferences); nukiOpener->initialize(); webCfgServer = new WebCfgServer(nuki, network, ethServer, preferences, networkDevice == NetworkDeviceType::WiFi);