Granular Access Level Control
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
enum class AccessLevel
|
||||
{
|
||||
Full = 0,
|
||||
LockOnly = 1,
|
||||
ReadOnly = 2,
|
||||
LockAndUnlock = 3
|
||||
};
|
||||
@@ -57,7 +57,6 @@ set(SRCFILES
|
||||
networkDevices/ClientSyncW5500.cpp
|
||||
networkDevices/espMqttClientW5500.cpp
|
||||
networkDevices/IPConfiguration.cpp
|
||||
AccessLevel.h
|
||||
LockActionResult.h
|
||||
QueryCommand.h
|
||||
NukiWrapper.cpp
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <NukiOpenerUtils.h>
|
||||
|
||||
NukiOpenerWrapper* nukiOpenerInst;
|
||||
AccessLevel NukiOpenerWrapper::_accessLevel = AccessLevel::ReadOnly;
|
||||
Preferences* nukiOpenerPreferences = nullptr;
|
||||
|
||||
NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NetworkOpener* network, Gpio* gpio, Preferences* preferences)
|
||||
: _deviceName(deviceName),
|
||||
@@ -52,7 +52,7 @@ void NukiOpenerWrapper::initialize()
|
||||
_intervalConfig = _preferences->getInt(preference_query_interval_configuration);
|
||||
_intervalBattery = _preferences->getInt(preference_query_interval_battery);
|
||||
_intervalKeypad = _preferences->getInt(preference_query_interval_keypad);
|
||||
_keypadEnabled = _preferences->getBool(preference_keypad_control_enabled);
|
||||
_keypadEnabled = _preferences->getBool(preference_keypad_info_enabled);
|
||||
_publishAuthData = _preferences->getBool(preference_publish_authdata);
|
||||
_maxKeypadCodeCount = _preferences->getUInt(preference_opener_max_keypad_code_count);
|
||||
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
||||
@@ -60,7 +60,6 @@ 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)
|
||||
{
|
||||
@@ -404,8 +403,8 @@ void NukiOpenerWrapper::updateConfig()
|
||||
|
||||
void NukiOpenerWrapper::updateAuthData()
|
||||
{
|
||||
if(_nukiOpener.getSecurityPincode() == 0) return;
|
||||
|
||||
if(_nukiOpener.getSecurityPincode() == 0) return;
|
||||
|
||||
Nuki::CmdResult result = _nukiOpener.retrieveLogEntries(0, 0, 0, true);
|
||||
if(result != Nuki::CmdResult::Success)
|
||||
{
|
||||
@@ -434,6 +433,8 @@ void NukiOpenerWrapper::updateAuthData()
|
||||
|
||||
void NukiOpenerWrapper::updateKeypad()
|
||||
{
|
||||
if(_preferences->getBool(preference_keypad_info_enabled)) return;
|
||||
|
||||
Log->print(F("Querying opener keypad: "));
|
||||
Nuki::CmdResult result = _nukiOpener.retrieveKeypadEntries(0, 0xffff);
|
||||
printCommandResult(result);
|
||||
@@ -489,34 +490,18 @@ LockActionResult NukiOpenerWrapper::onLockActionReceivedCallback(const char *val
|
||||
{
|
||||
return LockActionResult::UnknownAction;
|
||||
}
|
||||
|
||||
switch(_accessLevel)
|
||||
|
||||
nukiOpenerPreferences = new Preferences();
|
||||
nukiOpenerPreferences->begin("nukihub", true);
|
||||
|
||||
if((action == NukiOpener::LockAction::ActivateRTO && nukiOpenerPreferences->getBool(preference_acl_act_rto)) || (action == NukiOpener::LockAction::DeactivateRTO && nukiOpenerPreferences->getBool(preference_acl_deact_rto)) || (action == NukiOpener::LockAction::ElectricStrikeActuation && nukiOpenerPreferences->getBool(preference_acl_act_esa)) || (action == NukiOpener::LockAction::ActivateCM && nukiOpenerPreferences->getBool(preference_acl_act_cm)) || (action == NukiOpener::LockAction::DeactivateCM && nukiOpenerPreferences->getBool(preference_acl_deact_cm)) || (action == NukiOpener::LockAction::FobAction1 && nukiOpenerPreferences->getBool(preference_acl_opn_fob1)) || (action == NukiOpener::LockAction::FobAction2 && nukiOpenerPreferences->getBool(preference_acl_opn_fob2)) || (action == NukiOpener::LockAction::FobAction3 && nukiOpenerPreferences->getBool(preference_acl_opn_fob3)))
|
||||
{
|
||||
case AccessLevel::Full:
|
||||
nukiOpenerInst->_nextLockAction = action;
|
||||
return LockActionResult::Success;
|
||||
break;
|
||||
case AccessLevel::LockAndUnlock:
|
||||
if(action == NukiOpener::LockAction::ActivateRTO || action == NukiOpener::LockAction::ActivateCM || action == NukiOpener::LockAction::DeactivateRTO || action == NukiOpener::LockAction::DeactivateCM)
|
||||
{
|
||||
nukiOpenerInst->_nextLockAction = action;
|
||||
return LockActionResult::Success;
|
||||
}
|
||||
return LockActionResult::AccessDenied;
|
||||
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;
|
||||
nukiOpenerPreferences->end();
|
||||
return LockActionResult::Success;
|
||||
}
|
||||
|
||||
nukiOpenerPreferences->end();
|
||||
return LockActionResult::AccessDenied;
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::onConfigUpdateReceivedCallback(const char *topic, const char *value)
|
||||
@@ -556,7 +541,7 @@ void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action, const int&
|
||||
|
||||
void NukiOpenerWrapper::onConfigUpdateReceived(const char *topic, const char *value)
|
||||
{
|
||||
if(_accessLevel != AccessLevel::Full) return;
|
||||
if(!_preferences->getBool(preference_admin_config_enabled)) return;
|
||||
|
||||
if(strcmp(topic, mqtt_topic_config_button_enabled) == 0)
|
||||
{
|
||||
@@ -583,7 +568,11 @@ 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(!_preferences->getBool(preference_keypad_control_enabled))
|
||||
{
|
||||
_network->publishKeypadCommandResult("KeypadControlDisabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_hasKeypad)
|
||||
{
|
||||
@@ -759,11 +748,11 @@ void NukiOpenerWrapper::setupHASS()
|
||||
|
||||
if (_preferences->getBool(preference_opener_continuous_mode))
|
||||
{
|
||||
_network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateCM","activateCM","electricStrikeActuation");
|
||||
_network->publishHASSConfig("Opener", baseTopic.c_str(), (char*)_nukiConfig.name, uidString, "deactivateCM", "activateCM", "electricStrikeActuation");
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishHASSConfig("Opener",baseTopic.c_str(),(char*)_nukiConfig.name,uidString, "deactivateRTO","activateRTO","electricStrikeActuation");
|
||||
_network->publishHASSConfig("Opener", baseTopic.c_str(), (char*)_nukiConfig.name, uidString, "deactivateRTO", "activateRTO", "electricStrikeActuation");
|
||||
}
|
||||
|
||||
_hassSetupCompleted = true;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "NukiDataTypes.h"
|
||||
#include "BleScanner.h"
|
||||
#include "Gpio.h"
|
||||
#include "AccessLevel.h"
|
||||
#include "NukiDeviceId.h"
|
||||
|
||||
class NukiOpenerWrapper : public NukiOpener::SmartlockEventHandler
|
||||
@@ -70,7 +69,7 @@ private:
|
||||
|
||||
void printCommandResult(Nuki::CmdResult result);
|
||||
|
||||
NukiOpener::LockAction lockActionToEnum(const char* str); // char array at least 14 characters
|
||||
NukiOpener::LockAction lockActionToEnum(const char* str); // char array at least 14 charactersz
|
||||
|
||||
std::string _deviceName;
|
||||
NukiDeviceId* _deviceId = nullptr;
|
||||
@@ -90,7 +89,6 @@ private:
|
||||
int _retryDelay = 0;
|
||||
int _retryCount = 0;
|
||||
int _retryLockstateCount = 0;
|
||||
static AccessLevel _accessLevel;
|
||||
unsigned long _nextRetryTs = 0;
|
||||
std::vector<uint16_t> _keypadCodeIds;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <NukiLockUtils.h>
|
||||
|
||||
NukiWrapper* nukiInst;
|
||||
AccessLevel NukiWrapper::_accessLevel = AccessLevel::ReadOnly;
|
||||
Preferences* nukiLockPreferences = nullptr;
|
||||
|
||||
NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, BleScanner::Scanner* scanner, NetworkLock* network, Gpio* gpio, Preferences* preferences)
|
||||
: _deviceName(deviceName),
|
||||
@@ -53,7 +53,7 @@ void NukiWrapper::initialize(const bool& firstStart)
|
||||
_intervalConfig = _preferences->getInt(preference_query_interval_configuration);
|
||||
_intervalBattery = _preferences->getInt(preference_query_interval_battery);
|
||||
_intervalKeypad = _preferences->getInt(preference_query_interval_keypad);
|
||||
_keypadEnabled = _preferences->getBool(preference_keypad_control_enabled);
|
||||
_keypadEnabled = _preferences->getBool(preference_keypad_info_enabled);
|
||||
_publishAuthData = _preferences->getBool(preference_publish_authdata);
|
||||
_maxKeypadCodeCount = _preferences->getUInt(preference_lock_max_keypad_code_count);
|
||||
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
||||
@@ -61,15 +61,32 @@ 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)
|
||||
{
|
||||
_preferences->putInt(preference_command_nr_of_retries, 3);
|
||||
_preferences->putInt(preference_command_retry_delay, 1000);
|
||||
_preferences->putInt(preference_restart_ble_beacon_lost, 60);
|
||||
_preferences->putBool(preference_admin_config_enabled, true);
|
||||
_preferences->putBool(preference_acl_lock, true);
|
||||
_preferences->putBool(preference_acl_unlock, true);
|
||||
_preferences->putBool(preference_acl_unlatch, true);
|
||||
_preferences->putBool(preference_acl_lockngo, true);
|
||||
_preferences->putBool(preference_acl_lockngo_unlatch, true);
|
||||
_preferences->putBool(preference_acl_fulllock, true);
|
||||
_preferences->putBool(preference_acl_lck_fob1, true);
|
||||
_preferences->putBool(preference_acl_lck_fob2, true);
|
||||
_preferences->putBool(preference_acl_lck_fob3, true);
|
||||
_preferences->putBool(preference_acl_act_rto, true);
|
||||
_preferences->putBool(preference_acl_deact_rto, true);
|
||||
_preferences->putBool(preference_acl_act_esa, true);
|
||||
_preferences->putBool(preference_acl_act_cm, true);
|
||||
_preferences->putBool(preference_acl_deact_cm, true);
|
||||
_preferences->putBool(preference_acl_opn_fob1, true);
|
||||
_preferences->putBool(preference_acl_opn_fob2, true);
|
||||
_preferences->putBool(preference_acl_opn_fob3, true);
|
||||
}
|
||||
|
||||
|
||||
if(_retryDelay <= 100)
|
||||
{
|
||||
_retryDelay = 100;
|
||||
@@ -324,7 +341,7 @@ void NukiWrapper::updateKeyTurnerState()
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_retryLockstateCount = 0;
|
||||
|
||||
if(_publishAuthData)
|
||||
@@ -375,7 +392,7 @@ void NukiWrapper::updateConfig()
|
||||
void NukiWrapper::updateAuthData()
|
||||
{
|
||||
if(_nukiLock.getSecurityPincode() == 0) return;
|
||||
|
||||
|
||||
Nuki::CmdResult result = _nukiLock.retrieveLogEntries(0, 0, 0, true);
|
||||
if(result != Nuki::CmdResult::Success)
|
||||
{
|
||||
@@ -404,6 +421,8 @@ void NukiWrapper::updateAuthData()
|
||||
|
||||
void NukiWrapper::updateKeypad()
|
||||
{
|
||||
if(_preferences->getBool(preference_keypad_info_enabled)) return;
|
||||
|
||||
Log->print(F("Querying lock keypad: "));
|
||||
Nuki::CmdResult result = _nukiLock.retrieveKeypadEntries(0, 0xffff);
|
||||
printCommandResult(result);
|
||||
@@ -462,33 +481,17 @@ LockActionResult NukiWrapper::onLockActionReceivedCallback(const char *value)
|
||||
return LockActionResult::UnknownAction;
|
||||
}
|
||||
|
||||
switch(_accessLevel)
|
||||
nukiLockPreferences = new Preferences();
|
||||
nukiLockPreferences->begin("nukihub", true);
|
||||
|
||||
if((action == NukiLock::LockAction::Lock && nukiLockPreferences->getBool(preference_acl_lock)) || (action == NukiLock::LockAction::Unlock && nukiLockPreferences->getBool(preference_acl_unlock)) || (action == NukiLock::LockAction::Unlatch && nukiLockPreferences->getBool(preference_acl_unlatch)) || (action == NukiLock::LockAction::LockNgo && nukiLockPreferences->getBool(preference_acl_lockngo)) || (action == NukiLock::LockAction::LockNgoUnlatch && nukiLockPreferences->getBool(preference_acl_lockngo_unlatch)) || (action == NukiLock::LockAction::FullLock && nukiLockPreferences->getBool(preference_acl_fulllock)) || (action == NukiLock::LockAction::FobAction1 && nukiLockPreferences->getBool(preference_acl_lck_fob1)) || (action == NukiLock::LockAction::FobAction2 && nukiLockPreferences->getBool(preference_acl_lck_fob2)) || (action == NukiLock::LockAction::FobAction3 && nukiLockPreferences->getBool(preference_acl_lck_fob3)))
|
||||
{
|
||||
case AccessLevel::Full:
|
||||
nukiInst->_nextLockAction = action;
|
||||
return LockActionResult::Success;
|
||||
break;
|
||||
case AccessLevel::LockAndUnlock:
|
||||
if(action == NukiLock::LockAction::Lock || action == NukiLock::LockAction::Unlock || action == NukiLock::LockAction::LockNgo || action == NukiLock::LockAction::FullLock)
|
||||
{
|
||||
nukiInst->_nextLockAction = action;
|
||||
return LockActionResult::Success;
|
||||
}
|
||||
return LockActionResult::AccessDenied;
|
||||
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;
|
||||
nukiLockPreferences->end();
|
||||
return LockActionResult::Success;
|
||||
}
|
||||
|
||||
nukiLockPreferences->end();
|
||||
return LockActionResult::AccessDenied;
|
||||
}
|
||||
|
||||
void NukiWrapper::onConfigUpdateReceivedCallback(const char *topic, const char *value)
|
||||
@@ -525,7 +528,7 @@ void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
||||
|
||||
void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value)
|
||||
{
|
||||
if(_accessLevel != AccessLevel::Full) return;
|
||||
if(!_preferences->getBool(preference_admin_config_enabled)) return;
|
||||
|
||||
if(strcmp(topic, mqtt_topic_config_button_enabled) == 0)
|
||||
{
|
||||
@@ -580,7 +583,11 @@ 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(!_preferences->getBool(preference_keypad_control_enabled))
|
||||
{
|
||||
_network->publishKeypadCommandResult("KeypadControlDisabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_hasKeypad)
|
||||
{
|
||||
@@ -742,7 +749,7 @@ void NukiWrapper::setupHASS()
|
||||
char uidString[20];
|
||||
itoa(_nukiConfig.nukiId, uidString, 16);
|
||||
|
||||
_network->publishHASSConfig("SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, hasDoorSensor(), _hasKeypad, _publishAuthData,"lock", "unlock", "unlatch");
|
||||
_network->publishHASSConfig("SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, hasDoorSensor(), _hasKeypad, _publishAuthData, "lock", "unlock", "unlatch");
|
||||
_hassSetupCompleted = true;
|
||||
|
||||
Log->println("HASS setup for lock completed.");
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "BleScanner.h"
|
||||
#include "NukiLock.h"
|
||||
#include "Gpio.h"
|
||||
#include "AccessLevel.h"
|
||||
#include "LockActionResult.h"
|
||||
#include "NukiDeviceId.h"
|
||||
|
||||
@@ -111,7 +110,6 @@ 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;
|
||||
|
||||
@@ -42,14 +42,33 @@
|
||||
#define preference_query_interval_configuration "configInterval"
|
||||
#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_admin_config_enabled "aclConfigEnabled"
|
||||
#define preference_keypad_info_enabled "kpInfoEnabled"
|
||||
#define preference_keypad_control_enabled "kpCntrlEnabled"
|
||||
#define preference_publish_authdata "pubAuth"
|
||||
#define preference_acl_lock "aclLckLck"
|
||||
#define preference_acl_unlock "aclLckUnlck"
|
||||
#define preference_acl_unlatch "aclLckUnltch"
|
||||
#define preference_acl_lockngo "aclLckLnG"
|
||||
#define preference_acl_lockngo_unlatch "aclLckLnGwU"
|
||||
#define preference_acl_fulllock "aclLckFlck"
|
||||
#define preference_acl_lck_fob1 "aclLckFob1"
|
||||
#define preference_acl_lck_fob2 "aclLckFob2"
|
||||
#define preference_acl_lck_fob3 "aclLckFob3"
|
||||
#define preference_acl_act_rto "aclOpnActRTO"
|
||||
#define preference_acl_deact_rto "aclOpnDeactRTO"
|
||||
#define preference_acl_act_esa "aclOpnActESA"
|
||||
#define preference_acl_act_cm "aclOpnActCM"
|
||||
#define preference_acl_deact_cm "aclOpnDeactCM"
|
||||
#define preference_acl_opn_fob1 "aclOpnFob1"
|
||||
#define preference_acl_opn_fob2 "aclOpnFob2"
|
||||
#define preference_acl_opn_fob3 "aclOpnFob3"
|
||||
#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"
|
||||
#define preference_cred_user "crdusr"
|
||||
#define preference_cred_password "crdpass"
|
||||
#define preference_publish_authdata "pubauth"
|
||||
#define preference_gpio_locking_enabled "gpiolck" // obsolete
|
||||
#define preference_gpio_configuration "gpiocfg"
|
||||
#define preference_publish_debug_info "pubdbg"
|
||||
@@ -75,8 +94,11 @@ 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_access_level,
|
||||
preference_register_as_app, preference_command_nr_of_retries,
|
||||
preference_keypad_control_enabled, preference_admin_config_enabled, preference_keypad_info_enabled, preference_acl_lock,
|
||||
preference_acl_unlock, preference_acl_unlatch, preference_acl_lockngo, preference_acl_lockngo_unlatch, preference_acl_fulllock,
|
||||
preference_acl_lck_fob1, preference_acl_lck_fob2, preference_acl_lck_fob3, preference_acl_act_rto, preference_acl_deact_rto,
|
||||
preference_acl_act_esa, preference_acl_act_cm, preference_acl_deact_cm, preference_acl_opn_fob1, preference_acl_opn_fob2, preference_acl_opn_fob3,
|
||||
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, preference_latest_version,
|
||||
@@ -90,7 +112,11 @@ private:
|
||||
std::vector<char*> _boolPrefs =
|
||||
{
|
||||
preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode,
|
||||
preference_restart_on_disconnect, preference_keypad_control_enabled, preference_register_as_app, preference_ip_dhcp_enabled,
|
||||
preference_restart_on_disconnect, preference_keypad_control_enabled, preference_admin_config_enabled, preference_keypad_info_enabled, preference_acl_lock,
|
||||
preference_acl_unlock, preference_acl_unlatch, preference_acl_lockngo, preference_acl_lockngo_unlatch, preference_acl_fulllock,
|
||||
preference_acl_lck_fob1, preference_acl_lck_fob2, preference_acl_lck_fob3, preference_acl_act_rto, preference_acl_deact_rto,
|
||||
preference_acl_act_esa, preference_acl_act_cm, preference_acl_deact_cm, preference_acl_opn_fob1, preference_acl_opn_fob2, preference_acl_opn_fob3,
|
||||
preference_register_as_app, preference_ip_dhcp_enabled,
|
||||
preference_publish_authdata, preference_has_mac_saved, preference_publish_debug_info, preference_network_wifi_fallback_disabled
|
||||
};
|
||||
|
||||
|
||||
14
README.md
14
README.md
@@ -136,16 +136,24 @@ In a browser navigate to the IP address assigned to the ESP32.
|
||||
- Query interval lock state: Set to a positive integer to set the maximum amount of seconds between actively querying the Nuki device for the current lock state, default 1800.
|
||||
- Query interval configuration: Set to a positive integer to set the maximum amount of seconds between actively querying the Nuki device for the current configuration, default 3600.
|
||||
- Query interval battery: Set to a positive integer to set the maximum amount of seconds between actively querying the Nuki device for the current battery state, default 1800.
|
||||
- Access level: Allows restricting the allowed lock actions through MQTT. Set to "Full" to enable all lock actions including unlatch/ESA, set to "Lock and unlock operation only" to disable unlatch/ESA, set to "Lock operation only" to disable unlatch/ESA and unlock and set to "Read only" to disable changing lock state completely. Note: GPIO control is not restricted through this setting.
|
||||
- Query interval keypad (Only available when a Keypad is detected): Set to a positive integer to set the maximum amount of seconds between actively querying the Nuki device for the current keypad state, default 1800.
|
||||
- Enable keypad control via MQTT (Only available when a Keypad is detected): Enable to allow configuration of keypad codes through MQTT, see the "Keypad control" section of this README
|
||||
- Number of retries if command failed: Set to a positive integer to define the amount of times the Nuki Hub retries sending commands to the Nuki Lock or Opener when commands are not acknowledged by the device, default 3.
|
||||
- Delay between retries: Set to the amount of milliseconds the Nuki Hub waits between resending not acknowledged commands, default 100.
|
||||
- Publish auth data: Enable to publish authorization date to the MQTT topic lock/log. Requires the Nuki security code / PIN to be set, see "Nuki Lock PIN / Nuki Opener PIN" below.
|
||||
- Nuki Bridge is running alongside Nuki Hub: Enable to allow Nuki Hub to co-exist with a Nuki Bridge by registering Nuki Hub as an (smartphone) app instead of a bridge. Changing this setting will require re-pairing. Enabling this setting is strongly discouraged as described in the "Pairing with a Nuki Lock or Opener" section of this README
|
||||
- Presence detection timeout: Set to a positive integer to set the amount of seconds between updates to the presence/devices MQTT topic with the list of detected bluetooth devices, set to -1 to disable presence detection, default 60.
|
||||
- Restart if bluetooth beacons not received: Set to a positive integer to restart the Nuki Hub after the set amount of seconds has passed without receiving a bluetooth beacon from the Nuki device, set to -1 to disable, default 60. Because the bluetooth stack of the ESP32 can silently fail it is not recommended to disable this setting.
|
||||
|
||||
### Access Level Configuration
|
||||
|
||||
#### Nuki General Access Control
|
||||
- Change Lock/Opener configuration: Allows changing the Nuki Lock/Opener configuration through MQTT.
|
||||
- Publish keypad codes information (Only available when a Keypad is detected): Enable to publish information about keypad codes through MQTT, see the "Keypad control" section of this README
|
||||
- Add, modify and delete keypad codes (Only available when a Keypad is detected): Enable to allow configuration of keypad codes through MQTT, see the "Keypad control" section of this README
|
||||
- Publish auth data: Enable to publish authorization data to the MQTT topic lock/log. Requires the Nuki security code / PIN to be set, see "Nuki Lock PIN / Nuki Opener PIN" below.
|
||||
|
||||
#### Nuki Lock/Opener Access Control
|
||||
- Enable or disable executing each available lock action for the Nuki Lock and Nuki Opener through MQTT. Note: GPIO control is not restricted through this setting.
|
||||
|
||||
### Credentials
|
||||
|
||||
#### Credentials
|
||||
|
||||
300
WebCfgServer.cpp
300
WebCfgServer.cpp
@@ -5,7 +5,6 @@
|
||||
#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)
|
||||
@@ -71,6 +70,14 @@ void WebCfgServer::initialize()
|
||||
}
|
||||
sendFavicon();
|
||||
});
|
||||
_server.on("/acclvl", [&]() {
|
||||
if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) {
|
||||
return _server.requestAuthentication();
|
||||
}
|
||||
String response = "";
|
||||
buildAccLvlHtml(response);
|
||||
_server.send(200, "text/html", response);
|
||||
});
|
||||
_server.on("/cred", [&]() {
|
||||
if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) {
|
||||
return _server.requestAuthentication();
|
||||
@@ -427,11 +434,6 @@ 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());
|
||||
@@ -467,6 +469,101 @@ bool WebCfgServer::processArgs(String& message)
|
||||
_preferences->putBool(preference_publish_authdata, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLCONFIG")
|
||||
{
|
||||
_preferences->putBool(preference_admin_config_enabled, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "KPPUB")
|
||||
{
|
||||
_preferences->putBool(preference_keypad_info_enabled, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKLCK")
|
||||
{
|
||||
_preferences->putBool(preference_acl_lock, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKUNLCK")
|
||||
{
|
||||
_preferences->putBool(preference_acl_unlock, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKUNLTCH")
|
||||
{
|
||||
_preferences->putBool(preference_acl_unlatch, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKLNG")
|
||||
{
|
||||
_preferences->putBool(preference_acl_lockngo, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKLNGU")
|
||||
{
|
||||
_preferences->putBool(preference_acl_lockngo_unlatch, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKFLLCK")
|
||||
{
|
||||
_preferences->putBool(preference_acl_fulllock, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKFOB1")
|
||||
{
|
||||
_preferences->putBool(preference_acl_lck_fob1, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKFOB2")
|
||||
{
|
||||
_preferences->putBool(preference_acl_lck_fob2, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLLCKFOB3")
|
||||
{
|
||||
_preferences->putBool(preference_acl_lck_fob3, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLOPNUNLCK")
|
||||
{
|
||||
_preferences->putBool(preference_acl_act_rto, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLOPNLCK")
|
||||
{
|
||||
_preferences->putBool(preference_acl_deact_rto, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLOPNUNLTCH")
|
||||
{
|
||||
_preferences->putBool(preference_acl_act_esa, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLOPNUNLCKCM")
|
||||
{
|
||||
_preferences->putBool(preference_acl_act_cm, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLOPNLCKCM")
|
||||
{
|
||||
_preferences->putBool(preference_acl_deact_cm, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLOPNFOB1")
|
||||
{
|
||||
_preferences->putBool(preference_acl_opn_fob1, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLOPNFOB2")
|
||||
{
|
||||
_preferences->putBool(preference_acl_opn_fob2, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "ACLOPNFOB3")
|
||||
{
|
||||
_preferences->putBool(preference_acl_opn_fob3, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "REGAPP")
|
||||
{
|
||||
_preferences->putBool(preference_register_as_app, (value == "1"));
|
||||
@@ -624,7 +721,7 @@ void WebCfgServer::buildHtml(String& response)
|
||||
char lockstateArr[20];
|
||||
NukiOpener::lockstateToString(_nukiOpener->keyTurnerState().lockState, lockstateArr);
|
||||
printParameter(response, "Nuki Opener paired", _nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No");
|
||||
|
||||
|
||||
if(_nukiOpener->keyTurnerState().nukiState == NukiOpener::State::ContinuousMode)
|
||||
{
|
||||
printParameter(response, "Nuki Opener state", "Open (Continuous Mode)");
|
||||
@@ -643,30 +740,29 @@ void WebCfgServer::buildHtml(String& response)
|
||||
//}
|
||||
}
|
||||
|
||||
response.concat("</table><br><br>");
|
||||
|
||||
response.concat("<h3>MQTT and Network Configuration</h3>");
|
||||
response.concat("</table><br><table id=\"tblnav\"><tbody>");
|
||||
response.concat("<tr><td><h3>MQTT and Network Configuration</h3></td><td class=\"tdbtn\">");
|
||||
buildNavigationButton(response, "Edit", "/mqttconfig", _brokerConfigured ? "" : "<font color=\"#f07000\"><em>(!) Please configure MQTT broker</em></font>");
|
||||
|
||||
response.concat("<BR><BR><h3>Nuki Configuration</h3>");
|
||||
response.concat("</td></tr><tr><td><h3>Nuki Configuration</h3></td><td class=\"tdbtn\">");
|
||||
buildNavigationButton(response, "Edit", "/nukicfg");
|
||||
|
||||
response.concat("<BR><BR><h3>Credentials</h3>");
|
||||
response.concat("</td></tr><tr><td><h3>Access Level Configuration</h3></td><td class=\"tdbtn\">");
|
||||
buildNavigationButton(response, "Edit", "/acclvl");
|
||||
response.concat("</td></tr><tr><td><h3>Credentials</h3></td><td class=\"tdbtn\">");
|
||||
buildNavigationButton(response, "Edit", "/cred", _pinsConfigured ? "" : "<font color=\"#f07000\"><em>(!) Please configure PIN</em></font>");
|
||||
|
||||
response.concat("<BR><BR><h3>GPIO Configuration</h3>");
|
||||
response.concat("</td></tr><tr><td><h3>GPIO Configuration</h3></td><td class=\"tdbtn\">");
|
||||
buildNavigationButton(response, "Edit", "/gpiocfg");
|
||||
|
||||
response.concat("<BR><BR><h3>Firmware update</h3>");
|
||||
response.concat("</td></tr><tr><td><h3>Firmware update</h3></td><td class=\"tdbtn\">");
|
||||
buildNavigationButton(response, "Open", "/ota");
|
||||
response.concat("</td></tr>");
|
||||
|
||||
if(_allowRestartToPortal)
|
||||
{
|
||||
response.concat("<br><br><h3>Wi-Fi</h3>");
|
||||
response.concat("<tr><td><h3>Wi-Fi</h3></td><td class=\"tdbtn\">");
|
||||
buildNavigationButton(response, "Restart and configure Wi-Fi", "/wifi");
|
||||
response.concat("</td></tr>");
|
||||
}
|
||||
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("</tbody></table></body></html>");
|
||||
}
|
||||
|
||||
|
||||
@@ -674,36 +770,36 @@ void WebCfgServer::buildCredHtml(String &response)
|
||||
{
|
||||
buildHtmlHeader(response);
|
||||
|
||||
response.concat("<FORM ACTION=savecfg method='POST'>");
|
||||
response.concat("<form method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Credentials</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, false, true);
|
||||
printInputField(response, "CREDPASS", "Password", "*", 30, true, true);
|
||||
printInputField(response, "CREDPASSRE", "Retype password", "*", 30, true);
|
||||
response.concat("</table>");
|
||||
response.concat("<br><INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Save\">");
|
||||
response.concat("</FORM>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
|
||||
if(_nuki != nullptr)
|
||||
{
|
||||
response.concat("<br><br><FORM method=\"post\" ACTION=savecfg >");
|
||||
response.concat("<br><br><form method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Nuki Lock PIN</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "NUKIPIN", "PIN Code (# to clear)", "*", 20, true);
|
||||
response.concat("</table>");
|
||||
response.concat("<br><INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Save\">");
|
||||
response.concat("</FORM>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
}
|
||||
|
||||
if(_nukiOpener != nullptr)
|
||||
{
|
||||
response.concat("<br><br><FORM method=\"posst\" ACTION=savecfg >");
|
||||
response.concat("<br><br><form method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Nuki Opener PIN</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "NUKIOPPIN", "PIN Code (# to clear)", "*", 20, true);
|
||||
response.concat("</table>");
|
||||
response.concat("<br><INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Save\">");
|
||||
response.concat("</FORM>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
}
|
||||
|
||||
_confirmCode = generateConfirmCode();
|
||||
@@ -732,7 +828,7 @@ void WebCfgServer::buildCredHtml(String &response)
|
||||
response.concat("</table>");
|
||||
response.concat("<br><button type=\"submit\">OK</button></form>");
|
||||
}
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildOtaHtml(String &response, bool errored)
|
||||
@@ -742,7 +838,7 @@ void WebCfgServer::buildOtaHtml(String &response, bool errored)
|
||||
if(millis() < 60000)
|
||||
{
|
||||
response.concat("OTA functionality not ready. Please wait a moment and reload.");
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("</body></html>");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -750,7 +846,7 @@ void WebCfgServer::buildOtaHtml(String &response, bool errored)
|
||||
response.concat("<div>Over-the-air update errored. Please check the logs for more info</div><br/>");
|
||||
}
|
||||
|
||||
response.concat("<form id=\"upform\" enctype=\"multipart/form-data\" action=\"/uploadota\" method=\"POST\"><input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"100000\" />Choose the updated nuki_hub.bin file to upload: <input name=\"uploadedfile\" type=\"file\" accept=\".bin\" /><br/>");
|
||||
response.concat("<form id=\"upform\" enctype=\"multipart/form-data\" action=\"/uploadota\" method=\"post\"><input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"100000\" />Choose the updated nuki_hub.bin file to upload: <input name=\"uploadedfile\" type=\"file\" accept=\".bin\" /><br/>");
|
||||
response.concat("<br><input id=\"submitbtn\" type=\"submit\" value=\"Upload File\" /></form>");
|
||||
|
||||
if(_preferences->getBool(preference_check_updates))
|
||||
@@ -771,12 +867,12 @@ void WebCfgServer::buildOtaHtml(String &response, bool errored)
|
||||
response.concat(" button.addEventListener('click',hideshow,false);");
|
||||
response.concat(" function hideshow() {");
|
||||
response.concat(" document.getElementById('upform').style.visibility = 'hidden';");
|
||||
response.concat(" document.getElementById('gitdiv').style.visibility = 'hidden';");
|
||||
response.concat(" document.getElementById('gitdiv').style.visibility = 'hidden';");
|
||||
response.concat(" document.getElementById('msgdiv').style.visibility = 'visible';");
|
||||
response.concat(" }");
|
||||
response.concat("});");
|
||||
response.concat("</script>");
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildOtaCompletedHtml(String &response)
|
||||
@@ -789,13 +885,13 @@ void WebCfgServer::buildOtaCompletedHtml(String &response)
|
||||
response.concat(" setTimeout(\"location.href = '/';\",10000);");
|
||||
response.concat("});");
|
||||
response.concat("</script>");
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildMqttConfigHtml(String &response)
|
||||
{
|
||||
buildHtmlHeader(response);
|
||||
response.concat("<FORM ACTION=savecfg method='POST'>");
|
||||
response.concat("<form method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Basic MQTT and Network Configuration</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100);
|
||||
@@ -832,17 +928,67 @@ void WebCfgServer::buildMqttConfigHtml(String &response)
|
||||
printInputField(response, "DNSSRV", "DNS Server", _preferences->getString(preference_ip_dns_server).c_str(), 15);
|
||||
response.concat("</table>");
|
||||
|
||||
response.concat("<br><INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Save\">");
|
||||
response.concat("</FORM>");
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildAccLvlHtml(String &response)
|
||||
{
|
||||
buildHtmlHeader(response);
|
||||
|
||||
response.concat("<form method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Nuki General Access Control</h3>");
|
||||
response.concat("<table><tr><th>Setting</th><th>Enabled</th></tr>");
|
||||
printCheckBox(response, "ACLCONFIG", "Change Lock/Opener configuration", _preferences->getBool(preference_admin_config_enabled));
|
||||
if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad()))
|
||||
{
|
||||
printCheckBox(response, "KPPUB", "Publish keypad codes information", _preferences->getBool(preference_keypad_info_enabled));
|
||||
printCheckBox(response, "KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled));
|
||||
}
|
||||
printCheckBox(response, "PUBAUTH", "Publish authorisation log (may reduce battery life)", _preferences->getBool(preference_publish_authdata));
|
||||
response.concat("</table><br>");
|
||||
if(_nuki != nullptr)
|
||||
{
|
||||
response.concat("<h3>Nuki Lock Access Control</h3>");
|
||||
response.concat("<table><tr><th>Action</th><th>Allowed</th></tr>");
|
||||
|
||||
printCheckBox(response, "ACLLCKLCK", "Lock", _preferences->getBool(preference_acl_lock));
|
||||
printCheckBox(response, "ACLLCKUNLCK", "Unlock", _preferences->getBool(preference_acl_unlock));
|
||||
printCheckBox(response, "ACLLCKUNLTCH", "Unlatch", _preferences->getBool(preference_acl_unlatch));
|
||||
printCheckBox(response, "ACLLCKLNG", "Lock N Go", _preferences->getBool(preference_acl_lockngo));
|
||||
printCheckBox(response, "ACLLCKLNGU", "Lock N Go Unlatch", _preferences->getBool(preference_acl_lockngo_unlatch));
|
||||
printCheckBox(response, "ACLLCKFLLCK", "Full Lock", _preferences->getBool(preference_acl_fulllock));
|
||||
printCheckBox(response, "ACLLCKFOB1", "Fob Action 1", _preferences->getBool(preference_acl_lck_fob1));
|
||||
printCheckBox(response, "ACLLCKFOB2", "Fob Action 2", _preferences->getBool(preference_acl_lck_fob2));
|
||||
printCheckBox(response, "ACLLCKFOB3", "Fob Action 3", _preferences->getBool(preference_acl_lck_fob3));
|
||||
response.concat("</table><br>");
|
||||
}
|
||||
if(_nukiOpener != nullptr)
|
||||
{
|
||||
response.concat("<h3>Nuki Opener Access Control</h3>");
|
||||
response.concat("<table><tr><th>Action</th><th>Allowed</th></tr>");
|
||||
|
||||
printCheckBox(response, "ACLOPNUNLCK", "Activate Ring-to-Open", _preferences->getBool(preference_acl_act_rto));
|
||||
printCheckBox(response, "ACLOPNLCK", "Deactivate Ring-to-Open", _preferences->getBool(preference_acl_deact_rto));
|
||||
printCheckBox(response, "ACLOPNUNLTCH", "Electric Strike Actuation", _preferences->getBool(preference_acl_act_esa));
|
||||
printCheckBox(response, "ACLOPNUNLCKCM", "Activate Continuous Mode", _preferences->getBool(preference_acl_act_cm));
|
||||
printCheckBox(response, "ACLOPNLCKCM", "Deactivate Continuous Mode", _preferences->getBool(preference_acl_deact_cm));
|
||||
printCheckBox(response, "ACLOPNFOB1", "Fob Action 1", _preferences->getBool(preference_acl_opn_fob1));
|
||||
printCheckBox(response, "ACLOPNFOB2", "Fob Action 2", _preferences->getBool(preference_acl_opn_fob2));
|
||||
printCheckBox(response, "ACLOPNFOB3", "Fob Action 3", _preferences->getBool(preference_acl_opn_fob3));
|
||||
response.concat("</table><br>");
|
||||
}
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildNukiConfigHtml(String &response)
|
||||
{
|
||||
buildHtmlHeader(response);
|
||||
|
||||
response.concat("<FORM ACTION=savecfg method='POST'>");
|
||||
response.concat("<form method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Basic Nuki Configuration</h3>");
|
||||
response.concat("<table>");
|
||||
printCheckBox(response, "LOCKENA", "Nuki Smartlock enabled", _preferences->getBool(preference_lock_enabled));
|
||||
@@ -863,30 +1009,26 @@ 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);
|
||||
printCheckBox(response, "KPENA", "Enable keypad control via MQTT", _preferences->getBool(preference_keypad_control_enabled));
|
||||
}
|
||||
printInputField(response, "NRTRY", "Number of retries if command failed", _preferences->getInt(preference_command_nr_of_retries), 10);
|
||||
printInputField(response, "TRYDLY", "Delay between retries (milliseconds)", _preferences->getInt(preference_command_retry_delay), 10);
|
||||
printCheckBox(response, "PUBAUTH", "Publish auth data (May reduce battery life)", _preferences->getBool(preference_publish_authdata));
|
||||
printCheckBox(response, "REGAPP", "Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_as_app));
|
||||
printInputField(response, "PRDTMO", "Presence detection timeout (seconds; -1 to disable)", _preferences->getInt(preference_presence_detection_timeout), 10);
|
||||
printInputField(response, "RSBC", "Restart if bluetooth beacons not received (seconds; -1 to disable)", _preferences->getInt(preference_restart_ble_beacon_lost), 10);
|
||||
response.concat("</table>");
|
||||
response.concat("<br><INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Save\">");
|
||||
response.concat("</FORM>");
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildGpioConfigHtml(String &response)
|
||||
{
|
||||
buildHtmlHeader(response);
|
||||
|
||||
response.concat("<FORM ACTION=savegpiocfg method='POST'>");
|
||||
response.concat("<form method=\"post\" action=\"savegpiocfg\">");
|
||||
response.concat("<h3>GPIO Configuration</h3>");
|
||||
response.concat("<table>");
|
||||
|
||||
@@ -900,26 +1042,26 @@ void WebCfgServer::buildGpioConfigHtml(String &response)
|
||||
}
|
||||
|
||||
response.concat("</table>");
|
||||
response.concat("<br><INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Save\">");
|
||||
response.concat("</FORM>");
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildConfirmHtml(String &response, const String &message, uint32_t redirectDelay)
|
||||
{
|
||||
String delay(redirectDelay);
|
||||
|
||||
response.concat("<HTML>\n");
|
||||
response.concat("<HEAD>\n");
|
||||
response.concat("<TITLE>Nuki Hub</TITLE>\n");
|
||||
response.concat("<html>\n");
|
||||
response.concat("<head>\n");
|
||||
response.concat("<title>Nuki Hub</title>\n");
|
||||
response.concat("<meta http-equiv=\"Refresh\" content=\"");
|
||||
response.concat(redirectDelay);
|
||||
response.concat("; url=/\" />");
|
||||
response.concat("\n</HEAD>\n");
|
||||
response.concat("<BODY>\n");
|
||||
response.concat("\n</head>\n");
|
||||
response.concat("<body>\n");
|
||||
response.concat(message);
|
||||
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildConfigureWifiHtml(String &response)
|
||||
@@ -930,7 +1072,7 @@ void WebCfgServer::buildConfigureWifiHtml(String &response)
|
||||
response.concat("Click confirm to restart ESP into Wi-Fi configuration mode. After restart, connect to ESP access point to reconfigure Wi-Fi.<br><br>");
|
||||
buildNavigationButton(response, "Confirm", "/wifimanager");
|
||||
|
||||
response.concat("</BODY></HTML>");
|
||||
response.concat("</body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildInfoHtml(String &response)
|
||||
@@ -1007,7 +1149,7 @@ void WebCfgServer::buildInfoHtml(String &response)
|
||||
response.concat(getEspRestartReason());
|
||||
response.concat("\n");
|
||||
|
||||
response.concat("</pre> </BODY></HTML>");
|
||||
response.concat("</pre> </body></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::processUnpair(bool opener)
|
||||
@@ -1050,13 +1192,13 @@ void WebCfgServer::processUnpair(bool opener)
|
||||
|
||||
void WebCfgServer::buildHtmlHeader(String &response)
|
||||
{
|
||||
response.concat("<HTML><HEAD>");
|
||||
response.concat("<html><head>");
|
||||
response.concat("<meta name='viewport' content='width=device-width, initial-scale=1'>");
|
||||
// response.concat("<style>");
|
||||
// response.concat(stylecss);
|
||||
// response.concat("</style>");
|
||||
response.concat("<link rel='stylesheet' href='/style.css'>");
|
||||
response.concat("<TITLE>Nuki Hub</TITLE></HEAD><BODY>");
|
||||
response.concat("<title>Nuki Hub</title></head><body>");
|
||||
|
||||
srand(millis());
|
||||
}
|
||||
@@ -1084,13 +1226,13 @@ void WebCfgServer::printInputField(String& response,
|
||||
}
|
||||
|
||||
response.concat("</td><td>");
|
||||
response.concat("<INPUT TYPE=");
|
||||
response.concat(isPassword ? "PASSWORD" : "TEXT");
|
||||
response.concat(" VALUE=\"");
|
||||
response.concat("<input type=");
|
||||
response.concat(isPassword ? "password" : "text");
|
||||
response.concat(" value=\"");
|
||||
response.concat(value);
|
||||
response.concat("\" NAME=\"");
|
||||
response.concat("\" name=\"");
|
||||
response.concat(token);
|
||||
response.concat("\" SIZE=\"25\" MAXLENGTH=\"");
|
||||
response.concat("\" size=\"25\" maxlength=\"");
|
||||
response.concat(maxLengthStr);
|
||||
response.concat("\"/>");
|
||||
response.concat("</td></tr>");
|
||||
@@ -1113,12 +1255,12 @@ void WebCfgServer::printCheckBox(String &response, const char *token, const char
|
||||
response.concat(description);
|
||||
response.concat("</td><td>");
|
||||
|
||||
response.concat("<INPUT TYPE=hidden NAME=\"");
|
||||
response.concat("<input type=hidden name=\"");
|
||||
response.concat(token);
|
||||
response.concat("\" value=\"0\"");
|
||||
response.concat("/>");
|
||||
|
||||
response.concat("<INPUT TYPE=checkbox NAME=\"");
|
||||
response.concat("<input type=checkbox name=\"");
|
||||
response.concat(token);
|
||||
response.concat("\" value=\"1\"");
|
||||
response.concat(value ? " checked=\"checked\"" : "");
|
||||
@@ -1146,18 +1288,18 @@ void WebCfgServer::printTextarea(String& response,
|
||||
response.concat(" characters)");
|
||||
}
|
||||
response.concat("</td><td>");
|
||||
response.concat(" <TEXTAREA ");
|
||||
response.concat(" <textarea ");
|
||||
if(!enabled)
|
||||
{
|
||||
response.concat("DISABLED");
|
||||
response.concat("disabled");
|
||||
}
|
||||
response.concat(" NAME=\"");
|
||||
response.concat(" name=\"");
|
||||
response.concat(token);
|
||||
response.concat("\" MAXLENGTH=\"");
|
||||
response.concat("\" maxlength=\"");
|
||||
response.concat(maxLengthStr);
|
||||
response.concat("\">");
|
||||
response.concat(value);
|
||||
response.concat("</TEXTAREA>");
|
||||
response.concat("</textarea>");
|
||||
response.concat("</td></tr>");
|
||||
}
|
||||
|
||||
@@ -1356,18 +1498,6 @@ 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::LockAndUnlock).c_str(), "Lock and unlock operation only"));
|
||||
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();
|
||||
|
||||
@@ -37,6 +37,7 @@ private:
|
||||
bool processArgs(String& message);
|
||||
void processGpioArgs();
|
||||
void buildHtml(String& response);
|
||||
void buildAccLvlHtml(String& response);
|
||||
void buildCredHtml(String& response);
|
||||
void buildOtaHtml(String& response, bool errored);
|
||||
void buildOtaCompletedHtml(String& response);
|
||||
@@ -60,7 +61,6 @@ 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 = "");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// escaped by https://www.cescaper.com/
|
||||
// source: https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css
|
||||
|
||||
const char stylecss[] = ":root{--nc-font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';--nc-font-mono:Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace;--nc-tx-1:#000000;--nc-tx-2:#1A1A1A;--nc-bg-1:#FFFFFF;--nc-bg-2:#F6F8FA;--nc-bg-3:#E5E7EB;--nc-lk-1:#0070F3;--nc-lk-2:#0366D6;--nc-lk-tx:#FFFFFF;--nc-ac-1:#79FFE1;--nc-ac-tx:#0C4047}@media (prefers-color-scheme:dark){:root{--nc-tx-1:#ffffff;--nc-tx-2:#eeeeee;--nc-bg-1:#000000;--nc-bg-2:#111111;--nc-bg-3:#222222;--nc-lk-1:#3291FF;--nc-lk-2:#0070F3;--nc-lk-tx:#FFFFFF;--nc-ac-1:#7928CA;--nc-ac-tx:#FFFFFF}}*{margin:0;padding:0}address,area,article,aside,audio,blockquote,datalist,details,dl,fieldset,figure,form,iframe,img,input,meter,nav,ol,optgroup,option,output,p,pre,progress,ruby,section,table,textarea,ul,video{margin-bottom:1rem}button,html,input,select{font-family:var(--nc-font-sans)}body{margin:0 auto;max-width:750px;padding:2rem;border-radius:6px;overflow-x:hidden;word-break:normal;overflow-wrap:anywhere;background:var(--nc-bg-1);color:var(--nc-tx-2);font-size:1.03rem;line-height:1.5}::selection{background:var(--nc-ac-1);color:var(--nc-ac-tx)}h1,h2,h3,h4,h5,h6{line-height:1;color:var(--nc-tx-1);padding-top:.875rem}h1,h2,h3{color:var(--nc-tx-1);padding-bottom:2px;margin-bottom:8px;border-bottom:1px solid var(--nc-bg-2)}h4,h5,h6{margin-bottom:.3rem}h1{font-size:2.25rem}h2{font-size:1.85rem}h3{font-size:1.55rem}h4{font-size:1.25rem}h5{font-size:1rem}h6{font-size:.875rem}a{color:var(--nc-lk-1)}a:hover{color:var(--nc-lk-2)}abbr:hover{cursor:help}blockquote{padding:1.5rem;background:var(--nc-bg-2);border-left:5px solid var(--nc-bg-3)}abbr{cursor:help}blockquote :last-child{padding-bottom:0;margin-bottom:0}header{background:var(--nc-bg-2);border-bottom:1px solid var(--nc-bg-3);padding:2rem 1.5rem;margin:-2rem calc(0px - (50vw - 50%)) 2rem;padding-left:calc(50vw - 50%);padding-right:calc(50vw - 50%)}header h1,header h2,header h3{padding-bottom:0;border-bottom:0}header>:first-child{margin-top:0;padding-top:0}header>:last-child{margin-bottom:0}a button,button,input[type=button],input[type=reset],input[type=submit]{font-size:1rem;display:inline-block;padding:6px 12px;text-align:center;text-decoration:none;white-space:nowrap;background:var(--nc-lk-1);color:var(--nc-lk-tx);border:0;border-radius:4px;box-sizing:border-box;cursor:pointer;color:var(--nc-lk-tx)}a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5;cursor:not-allowed}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background:var(--nc-lk-2)}code,kbd,pre,samp{font-family:var(--nc-font-mono)}code,kbd,pre,samp{background:var(--nc-bg-2);border:1px solid var(--nc-bg-3);border-radius:4px;padding:3px 6px;font-size:.9rem}kbd{border-bottom:3px solid var(--nc-bg-3)}pre{padding:1rem 1.4rem;max-width:100%;overflow:auto}pre code{background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}code pre{display:inline;background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}details{padding:.6rem 1rem;background:var(--nc-bg-2);border:1px solid var(--nc-bg-3);border-radius:4px}summary{cursor:pointer;font-weight:700}details[open]{padding-bottom:.75rem}details[open] summary{margin-bottom:6px}details[open]>:last-child{margin-bottom:0}dt{font-weight:700}dd::before{content:'→ '}hr{border:0;border-bottom:1px solid var(--nc-bg-3);margin:1rem auto}fieldset{margin-top:1rem;padding:2rem;border:1px solid var(--nc-bg-3);border-radius:4px}legend{padding:0.5rem}table{border-collapse:collapse;width:100%}td,th{border:1px solid var(--nc-bg-3);text-align:left;padding:.5rem}th{background:var(--nc-bg-2)}tr:nth-child(even){background:var(--nc-bg-2)}table caption{font-weight:700;margin-bottom:.5rem}textarea{max-width:100%}ol,ul{padding-left:2rem}li{margin-top:.4rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}mark{padding:3px 6px;background:var(--nc-ac-1);color:var(--nc-ac-tx)}input,select,textarea{padding:6px 12px;margin-bottom:.5rem;background:var(--nc-bg-2);color:var(--nc-tx-2);border:1px solid var(--nc-bg-3);border-radius:4px;box-shadow:none;box-sizing:border-box}img{max-width:100%}td>input{margin-top:0px;margin-bottom:0px}td>textarea{margin-top:0px;margin-bottom:0px}td>select{margin-top:0px;margin-bottom:0px}";
|
||||
const char stylecss[] = ":root{--nc-font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';--nc-font-mono:Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace;--nc-tx-1:#000000;--nc-tx-2:#1A1A1A;--nc-bg-1:#FFFFFF;--nc-bg-2:#F6F8FA;--nc-bg-3:#E5E7EB;--nc-lk-1:#0070F3;--nc-lk-2:#0366D6;--nc-lk-tx:#FFFFFF;--nc-ac-1:#79FFE1;--nc-ac-tx:#0C4047}@media (prefers-color-scheme:dark){:root{--nc-tx-1:#ffffff;--nc-tx-2:#eeeeee;--nc-bg-1:#000000;--nc-bg-2:#111111;--nc-bg-3:#222222;--nc-lk-1:#3291FF;--nc-lk-2:#0070F3;--nc-lk-tx:#FFFFFF;--nc-ac-1:#7928CA;--nc-ac-tx:#FFFFFF}}*{margin:0;padding:0}address,area,article,aside,audio,blockquote,datalist,details,dl,fieldset,figure,iframe,img,input,meter,nav,ol,optgroup,option,output,p,pre,progress,ruby,section,table,textarea,ul,video{margin-bottom:1rem}button,html,input,select{font-family:var(--nc-font-sans)}body{margin:0 auto;max-width:750px;padding:2rem;border-radius:6px;overflow-x:hidden;word-break:normal;overflow-wrap:anywhere;background:var(--nc-bg-1);color:var(--nc-tx-2);font-size:1.03rem;line-height:1.5}::selection{background:var(--nc-ac-1);color:var(--nc-ac-tx)}h1,h2,h3,h4,h5,h6{line-height:1;color:var(--nc-tx-1);padding-top:.875rem}h1,h2,h3{color:var(--nc-tx-1);padding-bottom:2px;margin-bottom:8px;border-bottom:1px solid var(--nc-bg-2)}h4,h5,h6{margin-bottom:.3rem}h1{font-size:2.25rem}h2{font-size:1.85rem}h3{font-size:1.55rem}h4{font-size:1.25rem}h5{font-size:1rem}h6{font-size:.875rem}a{color:var(--nc-lk-1)}a:hover{color:var(--nc-lk-2)}abbr:hover{cursor:help}blockquote{padding:1.5rem;background:var(--nc-bg-2);border-left:5px solid var(--nc-bg-3)}abbr{cursor:help}blockquote :last-child{padding-bottom:0;margin-bottom:0}header{background:var(--nc-bg-2);border-bottom:1px solid var(--nc-bg-3);padding:2rem 1.5rem;margin:-2rem calc(0px - (50vw - 50%)) 2rem;padding-left:calc(50vw - 50%);padding-right:calc(50vw - 50%)}header h1,header h2,header h3{padding-bottom:0;border-bottom:0}header>:first-child{margin-top:0;padding-top:0}header>:last-child{margin-bottom:0}a button,button,input[type=button],input[type=reset],input[type=submit]{font-size:1rem;display:inline-block;padding:6px 12px;text-align:center;text-decoration:none;white-space:nowrap;background:var(--nc-lk-1);color:var(--nc-lk-tx);border:0;border-radius:4px;box-sizing:border-box;cursor:pointer;color:var(--nc-lk-tx)}a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5;cursor:not-allowed}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background:var(--nc-lk-2)}code,kbd,pre,samp{font-family:var(--nc-font-mono)}code,kbd,pre,samp{background:var(--nc-bg-2);border:1px solid var(--nc-bg-3);border-radius:4px;padding:3px 6px;font-size:.9rem}kbd{border-bottom:3px solid var(--nc-bg-3)}pre{padding:1rem 1.4rem;max-width:100%;overflow:auto}pre code{background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}code pre{display:inline;background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}details{padding:.6rem 1rem;background:var(--nc-bg-2);border:1px solid var(--nc-bg-3);border-radius:4px}summary{cursor:pointer;font-weight:700}details[open]{padding-bottom:.75rem}details[open] summary{margin-bottom:6px}details[open]>:last-child{margin-bottom:0}dt{font-weight:700}dd::before{content:'→ '}hr{border:0;border-bottom:1px solid var(--nc-bg-3);margin:1rem auto}fieldset{margin-top:1rem;padding:2rem;border:1px solid var(--nc-bg-3);border-radius:4px}legend{padding:0.5rem}table{border-collapse:collapse;width:100%}td,th{border:1px solid var(--nc-bg-3);text-align:left;padding:.5rem}th{background:var(--nc-bg-2)}tr:nth-child(even){background:var(--nc-bg-2)}table caption{font-weight:700;margin-bottom:.5rem}textarea{max-width:100%}ol,ul{padding-left:2rem}li{margin-top:.4rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}mark{padding:3px 6px;background:var(--nc-ac-1);color:var(--nc-ac-tx)}input,select,textarea{padding:6px 12px;margin-bottom:.5rem;background:var(--nc-bg-2);color:var(--nc-tx-2);border:1px solid var(--nc-bg-3);border-radius:4px;box-shadow:none;box-sizing:border-box}img{max-width:100%}td>input{margin-top:0px;margin-bottom:0px}td>textarea{margin-top:0px;margin-bottom:0px}td>select{margin-top:0px;margin-bottom:0px}#tblnav td,th{border:0;border-bottom:1px solid;}.tdbtn{text-align:center;vertical-align: middle;}";
|
||||
|
||||
// converted to char array by https://notisrac.github.io/FileToCArray/
|
||||
const unsigned char favicon_32x32[] = {
|
||||
|
||||
83
main.cpp
83
main.cpp
@@ -153,11 +153,84 @@ bool initPreferences()
|
||||
|
||||
if(configVer < (atof(NUKI_HUB_VERSION) * 100))
|
||||
{
|
||||
//Example
|
||||
//if (configVer < 833)
|
||||
//{
|
||||
//MIGRATE SETTINGS
|
||||
//}
|
||||
if (configVer < 834)
|
||||
{
|
||||
if(preferences->getInt(preference_keypad_control_enabled))
|
||||
{
|
||||
preferences->putBool(preference_keypad_info_enabled, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
preferences->putBool(preference_keypad_info_enabled, false);
|
||||
}
|
||||
|
||||
switch(preferences->getInt(preference_access_level))
|
||||
{
|
||||
case 0:
|
||||
preferences->putBool(preference_keypad_control_enabled, true);
|
||||
preferences->putBool(preference_admin_config_enabled, true);
|
||||
preferences->putBool(preference_acl_lock, true);
|
||||
preferences->putBool(preference_acl_unlock, true);
|
||||
preferences->putBool(preference_acl_unlatch, true);
|
||||
preferences->putBool(preference_acl_lockngo, true);
|
||||
preferences->putBool(preference_acl_lockngo_unlatch, true);
|
||||
preferences->putBool(preference_acl_fulllock, true);
|
||||
preferences->putBool(preference_acl_lck_fob1, true);
|
||||
preferences->putBool(preference_acl_lck_fob2, true);
|
||||
preferences->putBool(preference_acl_lck_fob3, true);
|
||||
preferences->putBool(preference_acl_act_rto, true);
|
||||
preferences->putBool(preference_acl_deact_rto, true);
|
||||
preferences->putBool(preference_acl_act_esa, true);
|
||||
preferences->putBool(preference_acl_act_cm, true);
|
||||
preferences->putBool(preference_acl_deact_cm, true);
|
||||
preferences->putBool(preference_acl_opn_fob1, true);
|
||||
preferences->putBool(preference_acl_opn_fob2, true);
|
||||
preferences->putBool(preference_acl_opn_fob3, true);
|
||||
break;
|
||||
case 1:
|
||||
preferences->putBool(preference_keypad_control_enabled, false);
|
||||
preferences->putBool(preference_admin_config_enabled, false);
|
||||
preferences->putBool(preference_acl_lock, true);
|
||||
preferences->putBool(preference_acl_unlock, false);
|
||||
preferences->putBool(preference_acl_unlatch, false);
|
||||
preferences->putBool(preference_acl_lockngo, false);
|
||||
preferences->putBool(preference_acl_lockngo_unlatch, false);
|
||||
preferences->putBool(preference_acl_fulllock, true);
|
||||
preferences->putBool(preference_acl_lck_fob1, false);
|
||||
preferences->putBool(preference_acl_lck_fob2, false);
|
||||
preferences->putBool(preference_acl_lck_fob3, false);
|
||||
preferences->putBool(preference_acl_act_rto, false);
|
||||
preferences->putBool(preference_acl_deact_rto, true);
|
||||
preferences->putBool(preference_acl_act_esa, false);
|
||||
preferences->putBool(preference_acl_act_cm, false);
|
||||
preferences->putBool(preference_acl_deact_cm, true);
|
||||
preferences->putBool(preference_acl_opn_fob1, false);
|
||||
preferences->putBool(preference_acl_opn_fob2, false);
|
||||
preferences->putBool(preference_acl_opn_fob3, false);
|
||||
break;
|
||||
case 3:
|
||||
preferences->putBool(preference_keypad_control_enabled, false);
|
||||
preferences->putBool(preference_admin_config_enabled, false);
|
||||
preferences->putBool(preference_acl_lock, true);
|
||||
preferences->putBool(preference_acl_unlock, true);
|
||||
preferences->putBool(preference_acl_unlatch, false);
|
||||
preferences->putBool(preference_acl_lockngo, true);
|
||||
preferences->putBool(preference_acl_lockngo_unlatch, false);
|
||||
preferences->putBool(preference_acl_fulllock, true);
|
||||
preferences->putBool(preference_acl_lck_fob1, false);
|
||||
preferences->putBool(preference_acl_lck_fob2, false);
|
||||
preferences->putBool(preference_acl_lck_fob3, false);
|
||||
preferences->putBool(preference_acl_act_rto, true);
|
||||
preferences->putBool(preference_acl_deact_rto, true);
|
||||
preferences->putBool(preference_acl_act_esa, false);
|
||||
preferences->putBool(preference_acl_act_cm, true);
|
||||
preferences->putBool(preference_acl_deact_cm, true);
|
||||
preferences->putBool(preference_acl_opn_fob1, false);
|
||||
preferences->putBool(preference_acl_opn_fob2, false);
|
||||
preferences->putBool(preference_acl_opn_fob3, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
preferences->putInt(preference_config_version, atof(NUKI_HUB_VERSION) * 100);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user