add access level feature

This commit is contained in:
technyon
2023-05-06 11:39:18 +02:00
parent 190a757a4e
commit 5b8ed0a5b8
14 changed files with 168 additions and 24 deletions

8
AccessLevel.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
enum class AccessLevel
{
Full = 0,
LockOnly = 1,
ReadOnly = 2
};

View File

@@ -53,6 +53,8 @@ set(SRCFILES
networkDevices/ClientSyncW5500.cpp
networkDevices/espMqttClientW5500.cpp
networkDevices/IPConfiguration.cpp
AccessLevel.h
LockActionResult.h
QueryCommand.h
NukiWrapper.cpp
NukiOpenerWrapper.cpp

9
LockActionResult.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
enum class LockActionResult
{
Success,
UnknownAction,
AccessDenied,
Failed
};

View File

@@ -102,16 +102,36 @@ void NetworkLock::onMqttDataReceived(const char* topic, byte* payload, const uns
if(comparePrefixedPath(topic, mqtt_topic_lock_action))
{
if(strcmp(value, "") == 0 || strcmp(value, "--") == 0 || strcmp(value, "ack") == 0 || strcmp(value, "unknown_action") == 0) return;
if(strcmp(value, "") == 0 ||
strcmp(value, "--") == 0 ||
strcmp(value, "ack") == 0 ||
strcmp(value, "unknown_action") == 0 ||
strcmp(value, "denied") == 0 ||
strcmp(value, "error") == 0) return;
Log->print(F("Lock action received: "));
Log->println(value);
bool success = false;
LockActionResult lockActionResult = LockActionResult::Failed;
if(_lockActionReceivedCallback != NULL)
{
success = _lockActionReceivedCallback(value);
lockActionResult = _lockActionReceivedCallback(value);
}
switch(lockActionResult)
{
case LockActionResult::Success:
publishString(mqtt_topic_lock_action, "ack");
break;
case LockActionResult::UnknownAction:
publishString(mqtt_topic_lock_action, "unknown_action");
break;
case LockActionResult::AccessDenied:
publishString(mqtt_topic_lock_action, "denied");
break;
case LockActionResult::Failed:
publishString(mqtt_topic_lock_action, "error");
break;
}
publishString(mqtt_topic_lock_action, success ? "ack" : "unknown_action");
}
if(comparePrefixedPath(topic, mqtt_topic_keypad_command_action))
@@ -458,7 +478,7 @@ void NetworkLock::publishKeypadCommandResult(const char* result)
publishString(mqtt_topic_keypad_command_result, result);
}
void NetworkLock::setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char *))
void NetworkLock::setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char *))
{
_lockActionReceivedCallback = lockActionReceivedCallback;
}

View File

@@ -10,6 +10,7 @@
#include "NukiLockConstants.h"
#include "Network.h"
#include "QueryCommand.h"
#include "LockActionResult.h"
#define LOCK_LOG_JSON_BUFFER_SIZE 2048
@@ -38,7 +39,7 @@ public:
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
void publishKeypadCommandResult(const char* result);
void setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char* value));
void setLockActionReceivedCallback(LockActionResult (*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));
@@ -84,7 +85,7 @@ private:
char* _buffer;
size_t _bufferSize;
bool (*_lockActionReceivedCallback)(const char* value) = nullptr;
LockActionResult (*_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;
};

View File

@@ -93,16 +93,36 @@ void NetworkOpener::onMqttDataReceived(const char* topic, byte* payload, const u
if(comparePrefixedPath(topic, mqtt_topic_lock_action))
{
if(strcmp((char*)payload, "") == 0 || strcmp(value, "--") == 0 || strcmp(value, "ack") == 0 || strcmp(value, "unknown_action") == 0) return;
if(strcmp(value, "") == 0 ||
strcmp(value, "--") == 0 ||
strcmp(value, "ack") == 0 ||
strcmp(value, "unknown_action") == 0 ||
strcmp(value, "denied") == 0 ||
strcmp(value, "error") == 0) return;
Log->print(F("Opener lock action received: "));
Log->print(F("Lock action received: "));
Log->println(value);
bool success = false;
LockActionResult lockActionResult = LockActionResult::Failed;
if(_lockActionReceivedCallback != NULL)
{
success = _lockActionReceivedCallback(value);
lockActionResult = _lockActionReceivedCallback(value);
}
switch(lockActionResult)
{
case LockActionResult::Success:
publishString(mqtt_topic_lock_action, "ack");
break;
case LockActionResult::UnknownAction:
publishString(mqtt_topic_lock_action, "unknown_action");
break;
case LockActionResult::AccessDenied:
publishString(mqtt_topic_lock_action, "denied");
break;
case LockActionResult::Failed:
publishString(mqtt_topic_lock_action, "error");
break;
}
publishString(mqtt_topic_lock_action, success ? "ack" : "unknown_action");
}
if(comparePrefixedPath(topic, mqtt_topic_keypad_command_action))
@@ -508,7 +528,7 @@ void NetworkOpener::publishKeypadCommandResult(const char* result)
publishString(mqtt_topic_keypad_command_result, result);
}
void NetworkOpener::setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char *))
void NetworkOpener::setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char *))
{
_lockActionReceivedCallback = lockActionReceivedCallback;
}

View File

@@ -36,7 +36,7 @@ public:
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
void publishKeypadCommandResult(const char* result);
void setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char* value));
void setLockActionReceivedCallback(LockActionResult (*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));
@@ -86,7 +86,7 @@ private:
char* _buffer;
const size_t _bufferSize;
bool (*_lockActionReceivedCallback)(const char* value) = nullptr;
LockActionResult (*_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;
};

View File

@@ -7,6 +7,7 @@
#include <NukiOpenerUtils.h>
NukiOpenerWrapper* nukiOpenerInst;
AccessLevel NukiOpenerWrapper::_accessLevel = AccessLevel::ReadOnly;
NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, NetworkOpener* network, Gpio* gpio, Preferences* preferences)
: _deviceName(deviceName),
@@ -55,6 +56,7 @@ void NukiOpenerWrapper::initialize()
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries);
_retryDelay = _preferences->getInt(preference_command_retry_delay);
_rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000;
_accessLevel = (AccessLevel)_preferences->getInt(preference_access_level);
if(_retryDelay <= 100)
{
@@ -465,11 +467,33 @@ NukiOpener::LockAction NukiOpenerWrapper::lockActionToEnum(const char *str)
return (NukiOpener::LockAction)0xff;
}
bool NukiOpenerWrapper::onLockActionReceivedCallback(const char *value)
LockActionResult NukiOpenerWrapper::onLockActionReceivedCallback(const char *value)
{
NukiOpener::LockAction action = nukiOpenerInst->lockActionToEnum(value);
nukiOpenerInst->_nextLockAction = action;
return (int)action != 0xff;
if((int)action == 0xff)
{
return LockActionResult::UnknownAction;
}
switch(_accessLevel)
{
case AccessLevel::Full:
nukiOpenerInst->_nextLockAction = action;
return LockActionResult::Success;
break;
case AccessLevel::LockOnly:
if(action == NukiOpener::LockAction::DeactivateRTO || action == NukiOpener::LockAction::DeactivateCM)
{
nukiOpenerInst->_nextLockAction = action;
return LockActionResult::Success;
}
return LockActionResult::AccessDenied;
break;
case AccessLevel::ReadOnly:
default:
return LockActionResult::AccessDenied;
break;
}
}
void NukiOpenerWrapper::onConfigUpdateReceivedCallback(const char *topic, const char *value)
@@ -503,6 +527,8 @@ void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action)
void NukiOpenerWrapper::onConfigUpdateReceived(const char *topic, const char *value)
{
if(_accessLevel != AccessLevel::Full) return;
if(strcmp(topic, mqtt_topic_config_button_enabled) == 0)
{
bool newValue = atoi(value) > 0;
@@ -528,6 +554,8 @@ 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(_accessLevel != AccessLevel::Full) return;
if(!_hasKeypad)
{
if(_configRead)

View File

@@ -6,6 +6,7 @@
#include "NukiDataTypes.h"
#include "BleScanner.h"
#include "Gpio.h"
#include "AccessLevel.h"
class NukiOpenerWrapper : public NukiOpener::SmartlockEventHandler
{
@@ -43,7 +44,7 @@ public:
void notify(NukiOpener::EventType eventType) override;
private:
static bool onLockActionReceivedCallback(const char* value);
static LockActionResult 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);
static void gpioActionCallback(const GpioAction& action);
@@ -85,6 +86,7 @@ private:
int _retryDelay = 0;
int _retryCount = 0;
int _retryLockstateCount = 0;
static AccessLevel _accessLevel;
unsigned long _nextRetryTs = 0;
std::vector<uint16_t> _keypadCodeIds;

View File

@@ -7,6 +7,7 @@
#include <NukiLockUtils.h>
NukiWrapper* nukiInst;
AccessLevel NukiWrapper::_accessLevel = AccessLevel::ReadOnly;
NukiWrapper::NukiWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, NetworkLock* network, Gpio* gpio, Preferences* preferences)
: _deviceName(deviceName),
@@ -56,6 +57,7 @@ void NukiWrapper::initialize(const bool& firstStart)
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries);
_retryDelay = _preferences->getInt(preference_command_retry_delay);
_rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000;
_accessLevel = (AccessLevel)_preferences->getInt(preference_access_level);
if(firstStart)
{
@@ -433,11 +435,34 @@ NukiLock::LockAction NukiWrapper::lockActionToEnum(const char *str)
return (NukiLock::LockAction)0xff;
}
bool NukiWrapper::onLockActionReceivedCallback(const char *value)
LockActionResult NukiWrapper::onLockActionReceivedCallback(const char *value)
{
NukiLock::LockAction action = nukiInst->lockActionToEnum(value);
nukiInst->_nextLockAction = action;
return (int)action != 0xff;
if((int)action == 0xff)
{
return LockActionResult::UnknownAction;
}
switch(_accessLevel)
{
case AccessLevel::Full:
nukiInst->_nextLockAction = action;
return LockActionResult::Success;
break;
case AccessLevel::LockOnly:
if(action == NukiLock::LockAction::Lock)
{
nukiInst->_nextLockAction = action;
return LockActionResult::Success;
}
return LockActionResult::AccessDenied;
break;
case AccessLevel::ReadOnly:
default:
return LockActionResult::AccessDenied;
break;
}
}
void NukiWrapper::onConfigUpdateReceivedCallback(const char *topic, const char *value)
@@ -468,6 +493,8 @@ void NukiWrapper::gpioActionCallback(const GpioAction &action)
void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value)
{
if(_accessLevel != AccessLevel::Full) return;
if(strcmp(topic, mqtt_topic_config_button_enabled) == 0)
{
bool newValue = atoi(value) > 0;
@@ -521,6 +548,8 @@ void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value)
void NukiWrapper::onKeypadCommandReceived(const char *command, const uint &id, const String &name, const String &code, const int& enabled)
{
if(_accessLevel != AccessLevel::Full) return;
if(!_hasKeypad)
{
if(_configRead)

View File

@@ -6,6 +6,8 @@
#include "BleScanner.h"
#include "NukiLock.h"
#include "Gpio.h"
#include "AccessLevel.h"
#include "LockActionResult.h"
class NukiWrapper : public Nuki::SmartlockEventHandler
{
@@ -40,7 +42,7 @@ public:
void notify(Nuki::EventType eventType) override;
private:
static bool onLockActionReceivedCallback(const char* value);
static LockActionResult 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);
static void gpioActionCallback(const GpioAction& action);
@@ -105,6 +107,7 @@ private:
int _retryCount = 0;
int _retryLockstateCount = 0;
long _rssiPublishInterval = 0;
static AccessLevel _accessLevel;
unsigned long _nextRetryTs = 0;
unsigned long _nextLockStateUpdateTs = 0;
unsigned long _nextBatteryReportTs = 0;

View File

@@ -37,6 +37,7 @@
#define preference_query_interval_battery "batInterval"
#define preference_query_interval_keypad "kpInterval"
#define preference_keypad_control_enabled "kpEnabled"
#define preference_access_level "accLvl"
#define preference_register_as_app "regAsApp" // true = register as hub; false = register as app
#define preference_command_nr_of_retries "nrRetry"
#define preference_command_retry_delay "rtryDelay"
@@ -67,7 +68,8 @@ private:
preference_hostname, preference_network_timeout, preference_restart_on_disconnect,
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_keypad_control_enabled, preference_access_level,
preference_register_as_app, preference_command_nr_of_retries,
preference_command_retry_delay, preference_cred_user, preference_cred_password, preference_publish_authdata,
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,

View File

@@ -5,6 +5,7 @@
#include "Logger.h"
#include "Config.h"
#include "RestartReason.h"
#include "AccessLevel.h"
#include <esp_task_wdt.h>
WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, Network* network, Gpio* gpio, EthServer* ethServer, Preferences* preferences, bool allowRestartToPortal)
@@ -406,6 +407,11 @@ bool WebCfgServer::processArgs(String& message)
_preferences->putInt(preference_query_interval_battery, value.toInt());
configChanged = true;
}
else if(key == "ACCLVL")
{
_preferences->putInt(preference_access_level, value.toInt());
configChanged = true;
}
else if(key == "KPINT")
{
_preferences->putInt(preference_query_interval_keypad, value.toInt());
@@ -801,6 +807,8 @@ void WebCfgServer::buildNukiConfigHtml(String &response)
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);
printDropDown(response, "ACCLVL", "Access level", String(_preferences->getInt(preference_access_level)), getAccessLevelOptions());
if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad()))
{
printInputField(response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10);
@@ -1289,6 +1297,17 @@ const std::vector<std::pair<String, String>> WebCfgServer::getGpioOptions() cons
return options;
}
const std::vector<std::pair<String, String>> WebCfgServer::getAccessLevelOptions() const
{
std::vector<std::pair<String, String>> options;
options.push_back(std::make_pair(std::to_string((int)AccessLevel::Full).c_str(), "Full"));
options.push_back(std::make_pair(std::to_string((int)AccessLevel::LockOnly).c_str(), "Lock operation only"));
options.push_back(std::make_pair(std::to_string((int)AccessLevel::ReadOnly).c_str(), "Read only"));
return options;
}
String WebCfgServer::getPreselectionForGpio(const uint8_t &pin)
{
const std::vector<PinEntry>& pinConfiguration = _gpio->pinConfiguration();

View File

@@ -60,6 +60,7 @@ private:
const std::vector<std::pair<String, String>> getNetworkDetectionOptions() const;
const std::vector<std::pair<String, String>> getGpioOptions() const;
const std::vector<std::pair<String, String>> getAccessLevelOptions() const;
String getPreselectionForGpio(const uint8_t& pin);
void printParameter(String& response, const char* description, const char* value, const char *link = "");