Extend keypad
This commit is contained in:
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
cd ~/arduino*
|
||||
./install.sh
|
||||
./arduino --pref "boardsmanager.additional.urls=https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json" --save-prefs
|
||||
./arduino --install-boards esp32:esp32:2.0.14
|
||||
./arduino --install-boards esp32:esp32:2.0.15
|
||||
- name: Install Arduino CMake Toolchain
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
|
||||
@@ -10,7 +10,7 @@ RUN tar -xf /tmp/arduino-ide.tar.xz --directory ~/
|
||||
RUN cd ~/arduino* && \
|
||||
./install.sh && \
|
||||
./arduino --pref "boardsmanager.additional.urls=https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json" --save-prefs && \
|
||||
./arduino --install-boards esp32:esp32:2.0.14
|
||||
./arduino --install-boards esp32:esp32:2.0.15
|
||||
|
||||
RUN git clone --recurse-submodules https://github.com/technyon/Arduino-CMake-Toolchain.git ~/Arduino-CMake-Toolchain
|
||||
|
||||
@@ -44,6 +44,6 @@ FROM builder AS runtime
|
||||
|
||||
COPY --from=builder /usr/src/nuki_hub/build/nuki_hub.bin /usr/src/nuki_hub/build/release/nuki_hub.bin
|
||||
COPY --from=builder /usr/src/nuki_hub/build/nuki_hub.partitions.bin /usr/src/nuki_hub/build/release/nuki_hub.partitions.bin
|
||||
COPY --from=builder /root/.arduino15/packages/esp32/hardware/esp32/2.0.14/tools/partitions/boot_app0.bin /usr/src/nuki_hub/build/release/boot_app0.bin
|
||||
COPY --from=builder /root/.arduino15/packages/esp32/hardware/esp32/2.0.15/tools/partitions/boot_app0.bin /usr/src/nuki_hub/build/release/boot_app0.bin
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
@@ -51,6 +51,8 @@
|
||||
#define mqtt_topic_keypad_command_enabled "/keypad/command/enabled"
|
||||
#define mqtt_topic_keypad_command_result "/keypad/command/commandResult"
|
||||
#define mqtt_topic_keypad_json "/keypad/json"
|
||||
#define mqtt_topic_keypad_json_action "/keypad/actionJson"
|
||||
#define mqtt_topic_keypad_json_command_result "/keypad/commandResultJson"
|
||||
|
||||
#define mqtt_topic_info_hardware_version "/info/hardwareVersion"
|
||||
#define mqtt_topic_info_firmware_version "/info/firmwareVersion"
|
||||
|
||||
@@ -592,7 +592,7 @@ void Network::registerMqttReceiver(MqttReceiver* receiver)
|
||||
|
||||
void Network::onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total)
|
||||
{
|
||||
uint8_t value[50] = {0};
|
||||
uint8_t value[800] = {0};
|
||||
size_t l = min(len, sizeof(value)-1);
|
||||
|
||||
for(int i=0; i<l; i++)
|
||||
|
||||
104
NetworkLock.cpp
104
NetworkLock.cpp
@@ -74,12 +74,14 @@ void NetworkLock::initialize()
|
||||
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_code);
|
||||
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_enabled);
|
||||
_network->subscribe(_mqttPath, mqtt_topic_query_keypad);
|
||||
_network->subscribe(_mqttPath, mqtt_topic_keypad_json_action);
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_action, "--");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_id, "0");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_name, "--");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_code, "000000");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_enabled, "1");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_query_keypad, "0");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_json_action, "--");
|
||||
}
|
||||
|
||||
_network->addReconnectedCallback([&]()
|
||||
@@ -204,6 +206,18 @@ void NetworkLock::onMqttDataReceived(const char* topic, byte* payload, const uns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(comparePrefixedPath(topic, mqtt_topic_keypad_json_action))
|
||||
{
|
||||
if(strcmp(value, "") == 0 || strcmp(value, "--") == 0) return;
|
||||
|
||||
if(_keypadJsonCommandReceivedReceivedCallback != NULL)
|
||||
{
|
||||
_keypadJsonCommandReceivedReceivedCallback(value);
|
||||
}
|
||||
|
||||
publishString(mqtt_topic_keypad_json_action, "--");
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurnerState, const NukiLock::KeyTurnerState& lastKeyTurnerState)
|
||||
@@ -502,20 +516,82 @@ void NetworkLock::publishKeypad(const std::list<NukiLock::KeypadEntry>& entries,
|
||||
basePath.concat("/code_");
|
||||
basePath.concat(std::to_string(index).c_str());
|
||||
publishKeypadEntry(basePath, entry);
|
||||
|
||||
|
||||
auto jsonEntry = json.add();
|
||||
|
||||
jsonEntry["id"] = entry.codeId;
|
||||
jsonEntry["codeId"] = entry.codeId;
|
||||
jsonEntry["enabled"] = entry.enabled;
|
||||
jsonEntry["name"] = entry.name;
|
||||
jsonEntry["createdYear"] = entry.dateCreatedYear;
|
||||
jsonEntry["createdMonth"] = entry.dateCreatedMonth;
|
||||
jsonEntry["createdDay"] = entry.dateCreatedDay;
|
||||
jsonEntry["createdHour"] = entry.dateCreatedHour;
|
||||
jsonEntry["createdMin"] = entry.dateCreatedMin;
|
||||
jsonEntry["createdSec"] = entry.dateCreatedSec;
|
||||
char createdDT[20];
|
||||
sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.dateCreatedYear, entry.dateCreatedMonth, entry.dateCreatedDay, entry.dateCreatedHour, entry.dateCreatedMin, entry.dateCreatedSec);
|
||||
jsonEntry["dateCreated"] = createdDT;
|
||||
jsonEntry["lockCount"] = entry.lockCount;
|
||||
|
||||
char lastActiveDT[20];
|
||||
sprintf(lastActiveDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.dateLastActiveYear, entry.dateLastActiveMonth, entry.dateLastActiveDay, entry.dateLastActiveHour, entry.dateLastActiveMin, entry.dateLastActiveSec);
|
||||
jsonEntry["dateLastActive"] = lastActiveDT;
|
||||
jsonEntry["timeLimited"] = entry.timeLimited;
|
||||
char allowedFromDT[20];
|
||||
sprintf(allowedFromDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedFromYear, entry.allowedFromMonth, entry.allowedFromDay, entry.allowedFromHour, entry.allowedFromMin, entry.allowedFromSec);
|
||||
jsonEntry["allowedFrom"] = allowedFromDT;
|
||||
char allowedUntilDT[20];
|
||||
sprintf(allowedUntilDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedUntilYear, entry.allowedUntilMonth, entry.allowedUntilDay, entry.allowedUntilHour, entry.allowedUntilMin, entry.allowedUntilSec);
|
||||
jsonEntry["allowedUntil"] = allowedUntilDT;
|
||||
|
||||
uint8_t allowedWeekdaysInt = entry.allowedWeekdays;
|
||||
JsonArray weekdays = jsonEntry.createNestedArray("allowedWeekdays");
|
||||
|
||||
while(allowedWeekdaysInt > 0) {
|
||||
if(allowedWeekdaysInt >= 64)
|
||||
{
|
||||
weekdays.add("mon");
|
||||
allowedWeekdaysInt -= 64;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 32)
|
||||
{
|
||||
weekdays.add("tue");
|
||||
allowedWeekdaysInt -= 32;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 16)
|
||||
{
|
||||
weekdays.add("wed");
|
||||
allowedWeekdaysInt -= 16;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 8)
|
||||
{
|
||||
weekdays.add("thu");
|
||||
allowedWeekdaysInt -= 8;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 4)
|
||||
{
|
||||
weekdays.add("fri");
|
||||
allowedWeekdaysInt -= 4;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 2)
|
||||
{
|
||||
weekdays.add("sat");
|
||||
allowedWeekdaysInt -= 2;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 1)
|
||||
{
|
||||
weekdays.add("sun");
|
||||
allowedWeekdaysInt -= 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
char allowedFromTimeT[5];
|
||||
sprintf(allowedFromTimeT, "%02d:%02d", entry.allowedFromTimeHour, entry.allowedFromTimeMin);
|
||||
jsonEntry["allowedFromTime"] = allowedFromTimeT;
|
||||
char allowedUntilTimeT[5];
|
||||
sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin);
|
||||
jsonEntry["allowedUntilTime"] = allowedUntilTimeT;
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
@@ -540,6 +616,11 @@ void NetworkLock::publishKeypadCommandResult(const char* result)
|
||||
publishString(mqtt_topic_keypad_command_result, result);
|
||||
}
|
||||
|
||||
void NetworkLock::publishKeypadJsonCommandResult(const char* result)
|
||||
{
|
||||
publishString(mqtt_topic_keypad_json_command_result, result);
|
||||
}
|
||||
|
||||
void NetworkLock::setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char *))
|
||||
{
|
||||
_lockActionReceivedCallback = lockActionReceivedCallback;
|
||||
@@ -555,6 +636,11 @@ void NetworkLock::setKeypadCommandReceivedCallback(void (*keypadCommandReceivedR
|
||||
_keypadCommandReceivedReceivedCallback = keypadCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NetworkLock::setKeypadJsonCommandReceivedCallback(void (*keypadJsonCommandReceivedReceivedCallback)(const char *))
|
||||
{
|
||||
_keypadJsonCommandReceivedReceivedCallback = keypadJsonCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NetworkLock::buildMqttPath(const char* path, char* outPath)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
@@ -38,11 +38,13 @@ public:
|
||||
void removeHASSConfig(char* uidString);
|
||||
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
||||
void publishKeypadCommandResult(const char* result);
|
||||
void publishKeypadJsonCommandResult(const char* result);
|
||||
|
||||
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));
|
||||
|
||||
void setKeypadJsonCommandReceivedCallback(void (*keypadJsonCommandReceivedReceivedCallback)(const char* value));
|
||||
|
||||
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
||||
|
||||
bool reconnected();
|
||||
@@ -90,4 +92,5 @@ private:
|
||||
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;
|
||||
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
};
|
||||
|
||||
@@ -62,12 +62,14 @@ void NetworkOpener::initialize()
|
||||
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_code);
|
||||
_network->subscribe(_mqttPath, mqtt_topic_keypad_command_enabled);
|
||||
_network->subscribe(_mqttPath, mqtt_topic_query_keypad);
|
||||
_network->subscribe(_mqttPath, mqtt_topic_keypad_json_action);
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_action, "--");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_id, "0");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_name, "--");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_code, "000000");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_command_enabled, "1");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_query_keypad, "0");
|
||||
_network->initTopic(_mqttPath, mqtt_topic_keypad_json_action, "--");
|
||||
}
|
||||
|
||||
_network->addReconnectedCallback([&]()
|
||||
@@ -193,6 +195,18 @@ void NetworkOpener::onMqttDataReceived(const char* topic, byte* payload, const u
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(comparePrefixedPath(topic, mqtt_topic_keypad_json_action))
|
||||
{
|
||||
if(strcmp(value, "") == 0 || strcmp(value, "--") == 0) return;
|
||||
|
||||
if(_keypadJsonCommandReceivedReceivedCallback != NULL)
|
||||
{
|
||||
_keypadJsonCommandReceivedReceivedCallback(value);
|
||||
}
|
||||
|
||||
publishString(mqtt_topic_keypad_json_action, "--");
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkOpener::publishKeyTurnerState(const NukiOpener::OpenerState& keyTurnerState, const NukiOpener::OpenerState& lastKeyTurnerState)
|
||||
@@ -282,7 +296,7 @@ void NetworkOpener::publishRing(const bool locked)
|
||||
{
|
||||
publishString(mqtt_topic_lock_ring, "ring");
|
||||
}
|
||||
|
||||
|
||||
publishString(mqtt_topic_lock_binary_ring, "ring");
|
||||
_resetRingStateTs = millis() + 2000;
|
||||
}
|
||||
@@ -561,16 +575,78 @@ void NetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& entrie
|
||||
|
||||
auto jsonEntry = json.add();
|
||||
|
||||
jsonEntry["id"] = entry.codeId;
|
||||
jsonEntry["codeId"] = entry.codeId;
|
||||
jsonEntry["enabled"] = entry.enabled;
|
||||
jsonEntry["name"] = entry.name;
|
||||
jsonEntry["createdYear"] = entry.dateCreatedYear;
|
||||
jsonEntry["createdMonth"] = entry.dateCreatedMonth;
|
||||
jsonEntry["createdDay"] = entry.dateCreatedDay;
|
||||
jsonEntry["createdHour"] = entry.dateCreatedHour;
|
||||
jsonEntry["createdMin"] = entry.dateCreatedMin;
|
||||
jsonEntry["createdSec"] = entry.dateCreatedSec;
|
||||
char createdDT[20];
|
||||
sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.dateCreatedYear, entry.dateCreatedMonth, entry.dateCreatedDay, entry.dateCreatedHour, entry.dateCreatedMin, entry.dateCreatedSec);
|
||||
jsonEntry["dateCreated"] = createdDT;
|
||||
jsonEntry["lockCount"] = entry.lockCount;
|
||||
char lastActiveDT[20];
|
||||
sprintf(lastActiveDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.dateLastActiveYear, entry.dateLastActiveMonth, entry.dateLastActiveDay, entry.dateLastActiveHour, entry.dateLastActiveMin, entry.dateLastActiveSec);
|
||||
jsonEntry["dateLastActive"] = lastActiveDT;
|
||||
jsonEntry["timeLimited"] = entry.timeLimited;
|
||||
char allowedFromDT[20];
|
||||
sprintf(allowedFromDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedFromYear, entry.allowedFromMonth, entry.allowedFromDay, entry.allowedFromHour, entry.allowedFromMin, entry.allowedFromSec);
|
||||
jsonEntry["allowedFrom"] = allowedFromDT;
|
||||
char allowedUntilDT[20];
|
||||
sprintf(allowedUntilDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedUntilYear, entry.allowedUntilMonth, entry.allowedUntilDay, entry.allowedUntilHour, entry.allowedUntilMin, entry.allowedUntilSec);
|
||||
jsonEntry["allowedUntil"] = allowedUntilDT;
|
||||
|
||||
uint8_t allowedWeekdaysInt = entry.allowedWeekdays;
|
||||
JsonArray weekdays = jsonEntry.createNestedArray("allowedWeekdays");
|
||||
|
||||
while(allowedWeekdaysInt > 0) {
|
||||
if(allowedWeekdaysInt >= 64)
|
||||
{
|
||||
weekdays.add("mon");
|
||||
allowedWeekdaysInt -= 64;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 32)
|
||||
{
|
||||
weekdays.add("tue");
|
||||
allowedWeekdaysInt -= 32;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 16)
|
||||
{
|
||||
weekdays.add("wed");
|
||||
allowedWeekdaysInt -= 16;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 8)
|
||||
{
|
||||
weekdays.add("thu");
|
||||
allowedWeekdaysInt -= 8;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 4)
|
||||
{
|
||||
weekdays.add("fri");
|
||||
allowedWeekdaysInt -= 4;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 2)
|
||||
{
|
||||
weekdays.add("sat");
|
||||
allowedWeekdaysInt -= 2;
|
||||
continue;
|
||||
}
|
||||
if(allowedWeekdaysInt >= 1)
|
||||
{
|
||||
weekdays.add("sun");
|
||||
allowedWeekdaysInt -= 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
char allowedFromTimeT[5];
|
||||
sprintf(allowedFromTimeT, "%02d:%02d", entry.allowedFromTimeHour, entry.allowedFromTimeMin);
|
||||
jsonEntry["allowedFromTime"] = allowedFromTimeT;
|
||||
char allowedUntilTimeT[5];
|
||||
sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin);
|
||||
jsonEntry["allowedUntilTime"] = allowedUntilTimeT;
|
||||
|
||||
++index;
|
||||
}
|
||||
@@ -596,6 +672,11 @@ void NetworkOpener::publishKeypadCommandResult(const char* result)
|
||||
publishString(mqtt_topic_keypad_command_result, result);
|
||||
}
|
||||
|
||||
void NetworkOpener::publishKeypadJsonCommandResult(const char* result)
|
||||
{
|
||||
publishString(mqtt_topic_keypad_json_command_result, result);
|
||||
}
|
||||
|
||||
void NetworkOpener::setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char *))
|
||||
{
|
||||
_lockActionReceivedCallback = lockActionReceivedCallback;
|
||||
@@ -611,6 +692,11 @@ void NetworkOpener::setKeypadCommandReceivedCallback(void (*keypadCommandReceive
|
||||
_keypadCommandReceivedReceivedCallback = keypadCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NetworkOpener::setKeypadJsonCommandReceivedCallback(void (*keypadJsonCommandReceivedReceivedCallback)(const char *))
|
||||
{
|
||||
_keypadJsonCommandReceivedReceivedCallback = keypadJsonCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NetworkOpener::publishFloat(const char *topic, const float value, const uint8_t precision)
|
||||
{
|
||||
_network->publishFloat(_mqttPath, topic, value, precision);
|
||||
|
||||
@@ -35,11 +35,13 @@ public:
|
||||
void removeHASSConfig(char* uidString);
|
||||
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
||||
void publishKeypadCommandResult(const char* result);
|
||||
void publishKeypadJsonCommandResult(const char* result);
|
||||
|
||||
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));
|
||||
|
||||
void setKeypadJsonCommandReceivedCallback(void (*keypadJsonCommandReceivedReceivedCallback)(const char* value));
|
||||
|
||||
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
||||
|
||||
bool reconnected();
|
||||
@@ -83,8 +85,8 @@ private:
|
||||
unsigned long _resetRingStateTs = 0;
|
||||
uint8_t _queryCommands = 0;
|
||||
uint32_t authId = 0;
|
||||
char authName[33];
|
||||
|
||||
char authName[33];
|
||||
|
||||
NukiOpener::LockState _currentLockState = NukiOpener::LockState::Undefined;
|
||||
|
||||
char* _buffer;
|
||||
@@ -93,4 +95,5 @@ private:
|
||||
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;
|
||||
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@ NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId
|
||||
network->setLockActionReceivedCallback(nukiOpenerInst->onLockActionReceivedCallback);
|
||||
network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback);
|
||||
network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback);
|
||||
network->setKeypadJsonCommandReceivedCallback(nukiOpenerInst->onKeypadJsonCommandReceivedCallback);
|
||||
|
||||
_gpio->addCallback(NukiOpenerWrapper::gpioActionCallback);
|
||||
}
|
||||
@@ -87,13 +88,6 @@ void NukiOpenerWrapper::initialize()
|
||||
_intervalKeypad = 60 * 30;
|
||||
_preferences->putInt(preference_query_interval_keypad, _intervalKeypad);
|
||||
}
|
||||
/*
|
||||
if(_intervalKeypad == 0)
|
||||
{
|
||||
_intervalKeypad = 60 * 30;
|
||||
_preferences->putInt(preference_query_interval_keypad, _intervalKeypad);
|
||||
}
|
||||
*/
|
||||
if(_restartBeaconTimeout < 10)
|
||||
{
|
||||
_restartBeaconTimeout = -1;
|
||||
@@ -407,7 +401,7 @@ void NukiOpenerWrapper::updateConfig()
|
||||
|
||||
if(_preferences->getUInt(preference_nuki_id_opener, 0) == _nukiConfig.nukiId)
|
||||
{
|
||||
_hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2;
|
||||
_hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2 > 0;
|
||||
_firmwareVersion = std::to_string(_nukiConfig.firmwareVersion[0]) + "." + std::to_string(_nukiConfig.firmwareVersion[1]) + "." + std::to_string(_nukiConfig.firmwareVersion[2]);
|
||||
_hardwareVersion = std::to_string(_nukiConfig.hardwareRevision[0]) + "." + std::to_string(_nukiConfig.hardwareRevision[1]);
|
||||
_network->publishConfig(_nukiConfig);
|
||||
@@ -473,7 +467,7 @@ void NukiOpenerWrapper::updateAuthData()
|
||||
|
||||
void NukiOpenerWrapper::updateKeypad()
|
||||
{
|
||||
if(_preferences->getBool(preference_keypad_info_enabled)) return;
|
||||
if(!_preferences->getBool(preference_keypad_info_enabled)) return;
|
||||
|
||||
Log->print(F("Querying opener keypad: "));
|
||||
Nuki::CmdResult result = _nukiOpener.retrieveKeypadEntries(0, 0xffff);
|
||||
@@ -557,6 +551,11 @@ void NukiOpenerWrapper::onKeypadCommandReceivedCallback(const char *command, con
|
||||
nukiOpenerInst->onKeypadCommandReceived(command, id, name, code, enabled);
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::onKeypadJsonCommandReceivedCallback(const char *value)
|
||||
{
|
||||
nukiOpenerInst->onKeypadJsonCommandReceived(value);
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
||||
{
|
||||
switch(action)
|
||||
@@ -726,6 +725,350 @@ void NukiOpenerWrapper::onKeypadCommandReceived(const char *command, const uint
|
||||
}
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value)
|
||||
{
|
||||
if(_nukiOpener.getSecurityPincode() == 0)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noPinSet");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_preferences->getBool(preference_keypad_control_enabled))
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("keypadControlDisabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_hasKeypad)
|
||||
{
|
||||
if(_configRead && _nukiConfigValid)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("keypadNotAvailable");
|
||||
return;
|
||||
}
|
||||
|
||||
updateConfig();
|
||||
|
||||
while (!_nukiConfigValid && _retryConfigCount < 11)
|
||||
{
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
if(_configRead && _nukiConfigValid)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("keypadNotAvailable");
|
||||
return;
|
||||
}
|
||||
|
||||
_network->publishKeypadJsonCommandResult("invalidConfig");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_keypadEnabled)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("keypadDisabled");
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument json(2048);
|
||||
DeserializationError jsonError = deserializeJson(json, value);
|
||||
|
||||
if(jsonError)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidJson");
|
||||
return;
|
||||
}
|
||||
|
||||
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||
|
||||
const char *action = json["action"].as<const char*>();
|
||||
uint16_t codeId = json["codeId"].as<unsigned int>();
|
||||
uint32_t code = json["code"].as<unsigned int>();
|
||||
String codeStr = json["code"].as<String>();
|
||||
const char *name = json["name"].as<const char*>();
|
||||
uint8_t enabled = json["enabled"].as<unsigned int>();
|
||||
uint8_t timeLimited = json["timeLimited"].as<unsigned int>();
|
||||
const char *allowedFrom = json["allowedFrom"].as<const char*>();
|
||||
const char *allowedUntil = json["allowedUntil"].as<const char*>();
|
||||
String allowedWeekdays = json["allowedWeekdays"].as<String>();
|
||||
const char *allowedFromTime = json["allowedFromTime"].as<const char*>();
|
||||
const char *allowedUntilTime = json["allowedUntilTime"].as<const char*>();
|
||||
|
||||
if(action)
|
||||
{
|
||||
bool idExists = false;
|
||||
|
||||
if(codeId)
|
||||
{
|
||||
idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId) != _keypadCodeIds.end();
|
||||
}
|
||||
|
||||
if(strcmp(action, "delete") == 0) {
|
||||
if(idExists)
|
||||
{
|
||||
result = _nukiOpener.deleteKeypadEntry(codeId);
|
||||
Log->print("Delete keypad code: ");
|
||||
Log->println((int)result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noExistingCodeIdSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
|
||||
{
|
||||
if(!name)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noNameSet");
|
||||
return;
|
||||
}
|
||||
|
||||
if(code)
|
||||
{
|
||||
bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1);
|
||||
|
||||
if (!codeValid)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noValidCodeSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noCodeSet");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int allowedFromAr[6];
|
||||
unsigned int allowedUntilAr[6];
|
||||
unsigned int allowedFromTimeAr[2];
|
||||
unsigned int allowedUntilTimeAr[2];
|
||||
uint8_t allowedWeekdaysInt = 0;
|
||||
|
||||
if(timeLimited == 1 && enabled != 0)
|
||||
{
|
||||
if(allowedFrom)
|
||||
{
|
||||
if(strlen(allowedFrom) == 19)
|
||||
{
|
||||
String allowedFromStr = allowedFrom;
|
||||
allowedFromAr[0] = (uint16_t)allowedFromStr.substring(0, 4).toInt();
|
||||
allowedFromAr[1] = (uint8_t)allowedFromStr.substring(5, 7).toInt();
|
||||
allowedFromAr[2] = (uint8_t)allowedFromStr.substring(8, 10).toInt();
|
||||
allowedFromAr[3] = (uint8_t)allowedFromStr.substring(11, 13).toInt();
|
||||
allowedFromAr[4] = (uint8_t)allowedFromStr.substring(14, 16).toInt();
|
||||
allowedFromAr[5] = (uint8_t)allowedFromStr.substring(17, 19).toInt();
|
||||
|
||||
if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedFrom");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedFrom");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedUntil)
|
||||
{
|
||||
if(strlen(allowedUntil) == 19)
|
||||
{
|
||||
String allowedUntilStr = allowedUntil;
|
||||
allowedUntilAr[0] = (uint16_t)allowedUntilStr.substring(0, 4).toInt();
|
||||
allowedUntilAr[1] = (uint8_t)allowedUntilStr.substring(5, 7).toInt();
|
||||
allowedUntilAr[2] = (uint8_t)allowedUntilStr.substring(8, 10).toInt();
|
||||
allowedUntilAr[3] = (uint8_t)allowedUntilStr.substring(11, 13).toInt();
|
||||
allowedUntilAr[4] = (uint8_t)allowedUntilStr.substring(14, 16).toInt();
|
||||
allowedUntilAr[5] = (uint8_t)allowedUntilStr.substring(17, 19).toInt();
|
||||
|
||||
if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedUntil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedUntil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedFromTime)
|
||||
{
|
||||
if(strlen(allowedFromTime) == 5)
|
||||
{
|
||||
String allowedFromTimeStr = allowedFromTime;
|
||||
allowedFromTimeAr[0] = (uint8_t)allowedFromTimeStr.substring(0, 2).toInt();
|
||||
allowedFromTimeAr[1] = (uint8_t)allowedFromTimeStr.substring(3, 5).toInt();
|
||||
|
||||
if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedUntilTime)
|
||||
{
|
||||
if(strlen(allowedUntilTime) == 5)
|
||||
{
|
||||
String allowedUntilTimeStr = allowedUntilTime;
|
||||
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTimeStr.substring(0, 2).toInt();
|
||||
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTimeStr.substring(3, 5).toInt();
|
||||
|
||||
if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64;
|
||||
if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32;
|
||||
if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16;
|
||||
if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8;
|
||||
if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4;
|
||||
if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2;
|
||||
if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1;
|
||||
}
|
||||
|
||||
if(strcmp(action, "add") == 0)
|
||||
{
|
||||
NukiOpener::NewKeypadEntry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
size_t nameLen = strlen(name);
|
||||
memcpy(&entry.name, name, nameLen > 20 ? 20 : nameLen);
|
||||
entry.code = code;
|
||||
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||
|
||||
if(allowedFrom)
|
||||
{
|
||||
entry.allowedFromYear = allowedFromAr[0];
|
||||
entry.allowedFromMonth = allowedFromAr[1];
|
||||
entry.allowedFromDay = allowedFromAr[2];
|
||||
entry.allowedFromHour = allowedFromAr[3];
|
||||
entry.allowedFromMin = allowedFromAr[4];
|
||||
entry.allowedFromSec = allowedFromAr[5];
|
||||
}
|
||||
|
||||
if(allowedUntil)
|
||||
{
|
||||
entry.allowedUntilYear = allowedUntilAr[0];
|
||||
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||
entry.allowedUntilDay = allowedUntilAr[2];
|
||||
entry.allowedUntilHour = allowedUntilAr[3];
|
||||
entry.allowedUntilMin = allowedUntilAr[4];
|
||||
entry.allowedUntilSec = allowedUntilAr[5];
|
||||
}
|
||||
|
||||
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||
|
||||
if(allowedFromTime)
|
||||
{
|
||||
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||
}
|
||||
|
||||
if(allowedUntilTime)
|
||||
{
|
||||
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||
}
|
||||
|
||||
result = _nukiOpener.addKeypadEntry(entry);
|
||||
Log->print("Add keypad code: ");
|
||||
Log->println((int)result);
|
||||
}
|
||||
else if (strcmp(action, "update") == 0)
|
||||
{
|
||||
NukiOpener::UpdatedKeypadEntry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.codeId = codeId;
|
||||
size_t nameLen = strlen(name);
|
||||
memcpy(&entry.name, name, nameLen > 20 ? 20 : nameLen);
|
||||
entry.code = code;
|
||||
entry.enabled = enabled == 0 ? 0 : 1;
|
||||
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||
|
||||
if(allowedFrom)
|
||||
{
|
||||
entry.allowedFromYear = allowedFromAr[0];
|
||||
entry.allowedFromMonth = allowedFromAr[1];
|
||||
entry.allowedFromDay = allowedFromAr[2];
|
||||
entry.allowedFromHour = allowedFromAr[3];
|
||||
entry.allowedFromMin = allowedFromAr[4];
|
||||
entry.allowedFromSec = allowedFromAr[5];
|
||||
}
|
||||
|
||||
if(allowedUntil)
|
||||
{
|
||||
entry.allowedUntilYear = allowedUntilAr[0];
|
||||
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||
entry.allowedUntilDay = allowedUntilAr[2];
|
||||
entry.allowedUntilHour = allowedUntilAr[3];
|
||||
entry.allowedUntilMin = allowedUntilAr[4];
|
||||
entry.allowedUntilSec = allowedUntilAr[5];
|
||||
}
|
||||
|
||||
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||
|
||||
if(allowedFromTime)
|
||||
{
|
||||
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||
}
|
||||
|
||||
if(allowedUntilTime)
|
||||
{
|
||||
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||
}
|
||||
|
||||
result = _nukiOpener.updateKeypadEntry(entry);
|
||||
Log->print("Update keypad code: ");
|
||||
Log->println((int)result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAction");
|
||||
return;
|
||||
}
|
||||
|
||||
updateKeypad();
|
||||
|
||||
if((int)result != -1)
|
||||
{
|
||||
char resultStr[15];
|
||||
memset(&resultStr, 0, sizeof(resultStr));
|
||||
NukiOpener::cmdResultToString(result, resultStr);
|
||||
_network->publishKeypadJsonCommandResult(resultStr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noActionSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const NukiOpener::OpenerState &NukiOpenerWrapper::keyTurnerState()
|
||||
{
|
||||
return _keyTurnerState;
|
||||
|
||||
@@ -49,9 +49,11 @@ private:
|
||||
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 onKeypadJsonCommandReceivedCallback(const char* value);
|
||||
static void gpioActionCallback(const GpioAction& action, const int& pin);
|
||||
void onConfigUpdateReceived(const char* topic, const char* value);
|
||||
void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||
void onKeypadJsonCommandReceived(const char* value);
|
||||
|
||||
void updateKeyTurnerState();
|
||||
void updateBatteryState();
|
||||
|
||||
354
NukiWrapper.cpp
354
NukiWrapper.cpp
@@ -32,6 +32,7 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId,
|
||||
network->setLockActionReceivedCallback(nukiInst->onLockActionReceivedCallback);
|
||||
network->setConfigUpdateReceivedCallback(nukiInst->onConfigUpdateReceivedCallback);
|
||||
network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback);
|
||||
network->setKeypadJsonCommandReceivedCallback(nukiInst->onKeypadJsonCommandReceivedCallback);
|
||||
|
||||
_gpio->addCallback(NukiWrapper::gpioActionCallback);
|
||||
}
|
||||
@@ -373,7 +374,7 @@ void NukiWrapper::updateConfig()
|
||||
|
||||
if(_preferences->getUInt(preference_nuki_id_lock, 0) == _nukiConfig.nukiId)
|
||||
{
|
||||
_hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2;
|
||||
_hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2 > 0;
|
||||
_firmwareVersion = std::to_string(_nukiConfig.firmwareVersion[0]) + "." + std::to_string(_nukiConfig.firmwareVersion[1]) + "." + std::to_string(_nukiConfig.firmwareVersion[2]);
|
||||
_hardwareVersion = std::to_string(_nukiConfig.hardwareRevision[0]) + "." + std::to_string(_nukiConfig.hardwareRevision[1]);
|
||||
_network->publishConfig(_nukiConfig);
|
||||
@@ -439,7 +440,7 @@ void NukiWrapper::updateAuthData()
|
||||
|
||||
void NukiWrapper::updateKeypad()
|
||||
{
|
||||
if(_preferences->getBool(preference_keypad_info_enabled)) return;
|
||||
if(!_preferences->getBool(preference_keypad_info_enabled)) return;
|
||||
|
||||
Log->print(F("Querying lock keypad: "));
|
||||
Nuki::CmdResult result = _nukiLock.retrieveKeypadEntries(0, 0xffff);
|
||||
@@ -525,6 +526,11 @@ void NukiWrapper::onKeypadCommandReceivedCallback(const char *command, const uin
|
||||
nukiInst->onKeypadCommandReceived(command, id, name, code, enabled);
|
||||
}
|
||||
|
||||
void NukiWrapper::onKeypadJsonCommandReceivedCallback(const char *value)
|
||||
{
|
||||
nukiInst->onKeypadJsonCommandReceived(value);
|
||||
}
|
||||
|
||||
void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
||||
{
|
||||
switch(action)
|
||||
@@ -712,6 +718,350 @@ void NukiWrapper::onKeypadCommandReceived(const char *command, const uint &id, c
|
||||
}
|
||||
}
|
||||
|
||||
void NukiWrapper::onKeypadJsonCommandReceived(const char *value)
|
||||
{
|
||||
if(_nukiLock.getSecurityPincode() == 0)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noPinSet");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_preferences->getBool(preference_keypad_control_enabled))
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("keypadControlDisabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_hasKeypad)
|
||||
{
|
||||
if(_configRead && _nukiConfigValid)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("keypadNotAvailable");
|
||||
return;
|
||||
}
|
||||
|
||||
updateConfig();
|
||||
|
||||
while (!_nukiConfigValid && _retryConfigCount < 11)
|
||||
{
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
if(_configRead && _nukiConfigValid)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("keypadNotAvailable");
|
||||
return;
|
||||
}
|
||||
|
||||
_network->publishKeypadJsonCommandResult("invalidConfig");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_keypadEnabled)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("keypadDisabled");
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument json(2048);
|
||||
DeserializationError jsonError = deserializeJson(json, value);
|
||||
|
||||
if(jsonError)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidJson");
|
||||
return;
|
||||
}
|
||||
|
||||
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||
|
||||
const char *action = json["action"].as<const char*>();
|
||||
uint16_t codeId = json["codeId"].as<unsigned int>();
|
||||
uint32_t code = json["code"].as<unsigned int>();
|
||||
String codeStr = json["code"].as<String>();
|
||||
const char *name = json["name"].as<const char*>();
|
||||
uint8_t enabled = json["enabled"].as<unsigned int>();
|
||||
uint8_t timeLimited = json["timeLimited"].as<unsigned int>();
|
||||
const char *allowedFrom = json["allowedFrom"].as<const char*>();
|
||||
const char *allowedUntil = json["allowedUntil"].as<const char*>();
|
||||
String allowedWeekdays = json["allowedWeekdays"].as<String>();
|
||||
const char *allowedFromTime = json["allowedFromTime"].as<const char*>();
|
||||
const char *allowedUntilTime = json["allowedUntilTime"].as<const char*>();
|
||||
|
||||
if(action)
|
||||
{
|
||||
bool idExists = false;
|
||||
|
||||
if(codeId)
|
||||
{
|
||||
idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId) != _keypadCodeIds.end();
|
||||
}
|
||||
|
||||
if(strcmp(action, "delete") == 0) {
|
||||
if(idExists)
|
||||
{
|
||||
result = _nukiLock.deleteKeypadEntry(codeId);
|
||||
Log->print("Delete keypad code: ");
|
||||
Log->println((int)result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noExistingCodeIdSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
|
||||
{
|
||||
if(!name)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noNameSet");
|
||||
return;
|
||||
}
|
||||
|
||||
if(code)
|
||||
{
|
||||
bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1);
|
||||
|
||||
if (!codeValid)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noValidCodeSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noCodeSet");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int allowedFromAr[6];
|
||||
unsigned int allowedUntilAr[6];
|
||||
unsigned int allowedFromTimeAr[2];
|
||||
unsigned int allowedUntilTimeAr[2];
|
||||
uint8_t allowedWeekdaysInt = 0;
|
||||
|
||||
if(timeLimited == 1 && enabled != 0)
|
||||
{
|
||||
if(allowedFrom)
|
||||
{
|
||||
if(strlen(allowedFrom) == 19)
|
||||
{
|
||||
String allowedFromStr = allowedFrom;
|
||||
allowedFromAr[0] = (uint16_t)allowedFromStr.substring(0, 4).toInt();
|
||||
allowedFromAr[1] = (uint8_t)allowedFromStr.substring(5, 7).toInt();
|
||||
allowedFromAr[2] = (uint8_t)allowedFromStr.substring(8, 10).toInt();
|
||||
allowedFromAr[3] = (uint8_t)allowedFromStr.substring(11, 13).toInt();
|
||||
allowedFromAr[4] = (uint8_t)allowedFromStr.substring(14, 16).toInt();
|
||||
allowedFromAr[5] = (uint8_t)allowedFromStr.substring(17, 19).toInt();
|
||||
|
||||
if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedFrom");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedFrom");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedUntil)
|
||||
{
|
||||
if(strlen(allowedUntil) == 19)
|
||||
{
|
||||
String allowedUntilStr = allowedUntil;
|
||||
allowedUntilAr[0] = (uint16_t)allowedUntilStr.substring(0, 4).toInt();
|
||||
allowedUntilAr[1] = (uint8_t)allowedUntilStr.substring(5, 7).toInt();
|
||||
allowedUntilAr[2] = (uint8_t)allowedUntilStr.substring(8, 10).toInt();
|
||||
allowedUntilAr[3] = (uint8_t)allowedUntilStr.substring(11, 13).toInt();
|
||||
allowedUntilAr[4] = (uint8_t)allowedUntilStr.substring(14, 16).toInt();
|
||||
allowedUntilAr[5] = (uint8_t)allowedUntilStr.substring(17, 19).toInt();
|
||||
|
||||
if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedUntil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedUntil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedFromTime)
|
||||
{
|
||||
if(strlen(allowedFromTime) == 5)
|
||||
{
|
||||
String allowedFromTimeStr = allowedFromTime;
|
||||
allowedFromTimeAr[0] = (uint8_t)allowedFromTimeStr.substring(0, 2).toInt();
|
||||
allowedFromTimeAr[1] = (uint8_t)allowedFromTimeStr.substring(3, 5).toInt();
|
||||
|
||||
if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedUntilTime)
|
||||
{
|
||||
if(strlen(allowedUntilTime) == 5)
|
||||
{
|
||||
String allowedUntilTimeStr = allowedUntilTime;
|
||||
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTimeStr.substring(0, 2).toInt();
|
||||
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTimeStr.substring(3, 5).toInt();
|
||||
|
||||
if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64;
|
||||
if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32;
|
||||
if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16;
|
||||
if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8;
|
||||
if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4;
|
||||
if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2;
|
||||
if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1;
|
||||
}
|
||||
|
||||
if(strcmp(action, "add") == 0)
|
||||
{
|
||||
NukiLock::NewKeypadEntry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
size_t nameLen = strlen(name);
|
||||
memcpy(&entry.name, name, nameLen > 20 ? 20 : nameLen);
|
||||
entry.code = code;
|
||||
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||
|
||||
if(allowedFrom)
|
||||
{
|
||||
entry.allowedFromYear = allowedFromAr[0];
|
||||
entry.allowedFromMonth = allowedFromAr[1];
|
||||
entry.allowedFromDay = allowedFromAr[2];
|
||||
entry.allowedFromHour = allowedFromAr[3];
|
||||
entry.allowedFromMin = allowedFromAr[4];
|
||||
entry.allowedFromSec = allowedFromAr[5];
|
||||
}
|
||||
|
||||
if(allowedUntil)
|
||||
{
|
||||
entry.allowedUntilYear = allowedUntilAr[0];
|
||||
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||
entry.allowedUntilDay = allowedUntilAr[2];
|
||||
entry.allowedUntilHour = allowedUntilAr[3];
|
||||
entry.allowedUntilMin = allowedUntilAr[4];
|
||||
entry.allowedUntilSec = allowedUntilAr[5];
|
||||
}
|
||||
|
||||
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||
|
||||
if(allowedFromTime)
|
||||
{
|
||||
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||
}
|
||||
|
||||
if(allowedUntilTime)
|
||||
{
|
||||
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||
}
|
||||
|
||||
result = _nukiLock.addKeypadEntry(entry);
|
||||
Log->print("Add keypad code: ");
|
||||
Log->println((int)result);
|
||||
}
|
||||
else if (strcmp(action, "update") == 0)
|
||||
{
|
||||
NukiLock::UpdatedKeypadEntry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.codeId = codeId;
|
||||
size_t nameLen = strlen(name);
|
||||
memcpy(&entry.name, name, nameLen > 20 ? 20 : nameLen);
|
||||
entry.code = code;
|
||||
entry.enabled = enabled == 0 ? 0 : 1;
|
||||
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||
|
||||
if(allowedFrom)
|
||||
{
|
||||
entry.allowedFromYear = allowedFromAr[0];
|
||||
entry.allowedFromMonth = allowedFromAr[1];
|
||||
entry.allowedFromDay = allowedFromAr[2];
|
||||
entry.allowedFromHour = allowedFromAr[3];
|
||||
entry.allowedFromMin = allowedFromAr[4];
|
||||
entry.allowedFromSec = allowedFromAr[5];
|
||||
}
|
||||
|
||||
if(allowedUntil)
|
||||
{
|
||||
entry.allowedUntilYear = allowedUntilAr[0];
|
||||
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||
entry.allowedUntilDay = allowedUntilAr[2];
|
||||
entry.allowedUntilHour = allowedUntilAr[3];
|
||||
entry.allowedUntilMin = allowedUntilAr[4];
|
||||
entry.allowedUntilSec = allowedUntilAr[5];
|
||||
}
|
||||
|
||||
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||
|
||||
if(allowedFromTime)
|
||||
{
|
||||
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||
}
|
||||
|
||||
if(allowedUntilTime)
|
||||
{
|
||||
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||
}
|
||||
|
||||
result = _nukiLock.updateKeypadEntry(entry);
|
||||
Log->print("Update keypad code: ");
|
||||
Log->println((int)result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("invalidAction");
|
||||
return;
|
||||
}
|
||||
|
||||
updateKeypad();
|
||||
|
||||
if((int)result != -1)
|
||||
{
|
||||
char resultStr[15];
|
||||
memset(&resultStr, 0, sizeof(resultStr));
|
||||
NukiLock::cmdResultToString(result, resultStr);
|
||||
_network->publishKeypadJsonCommandResult(resultStr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishKeypadJsonCommandResult("noActionSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const NukiLock::KeyTurnerState &NukiWrapper::keyTurnerState()
|
||||
{
|
||||
return _keyTurnerState;
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
bool isPinSet();
|
||||
void setPin(const uint16_t pin);
|
||||
void unpair();
|
||||
|
||||
|
||||
void disableHASS();
|
||||
|
||||
void disableWatchdog();
|
||||
@@ -47,10 +47,12 @@ private:
|
||||
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 onKeypadJsonCommandReceivedCallback(const char* value);
|
||||
static void gpioActionCallback(const GpioAction& action, const int& pin);
|
||||
|
||||
void onConfigUpdateReceived(const char* topic, const char* value);
|
||||
void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||
void onKeypadJsonCommandReceived(const char* value);
|
||||
|
||||
void updateKeyTurnerState();
|
||||
void updateBatteryState();
|
||||
@@ -63,7 +65,7 @@ private:
|
||||
|
||||
void readConfig();
|
||||
void readAdvancedConfig();
|
||||
|
||||
|
||||
void setupHASS();
|
||||
|
||||
void printCommandResult(Nuki::CmdResult result);
|
||||
|
||||
38
README.md
38
README.md
@@ -15,9 +15,9 @@ Feel free to join us on Discord: https://discord.gg/feB9FnMY
|
||||
## Supported devices
|
||||
|
||||
<b>Supported ESP32 devices:</b>
|
||||
- All dual-core ESP32 models with WIFI and BLE which are supported by Arduino Core 2.0.14 should work, but builds are currently only provided for the ESP32 and not for the ESP32-S3 or ESP32-C3.
|
||||
- All dual-core ESP32 models with WIFI and BLE which are supported by Arduino Core 2.0.15 should work, but builds are currently only provided for the ESP32 and not for the ESP32-S3 or ESP32-C3.
|
||||
- The ESP32-S2 has no BLE and as such can't run Nuki Hub.
|
||||
- The ESP32-C6 and ESP32-H2 are not supported by Arduino Core 2.0.14 as such can't run Nuki Hub (at this time).
|
||||
- The ESP32-C6 and ESP32-H2 are not supported by Arduino Core 2.0.15 as such can't run Nuki Hub (at this time).
|
||||
|
||||
<b>Supported Nuki devices:</b>
|
||||
- Nuki Smart Lock 1.0
|
||||
@@ -309,12 +309,42 @@ The following mapping between Home Assistant services and Nuki commands is setup
|
||||
NOTE: MQTT Discovery uses retained MQTT messages to store devices configurations. In order to avoid orphan configurations on your broker please disable autodiscovery first if you no longer want to use this SW. Retained messages are automatically cleared when unpairing and when changing/disabling autodiscovery topic in MQTT Configuration page.<br>
|
||||
NOTE2: Home Assistant can be setup manually using the [MQTT Lock integration](https://www.home-assistant.io/integrations/lock.mqtt/), but this is not recommended
|
||||
|
||||
## Keypad control (optional)
|
||||
## Keypad control using JSON (optional)
|
||||
|
||||
To change Nuki Lock/Opener keypad settings set the `keypad/actionJson` topic to a JSON formatted value containing the following nodes.
|
||||
|
||||
| Node | Delete | Add | Update | Usage | Possible values |
|
||||
|------------------|----------|----------|----------|------------------------------------------------------------------------------------------|----------------------------------------------------------------|
|
||||
| action | Required | Required | Required | The action to execute | "delete", "add", "update" |
|
||||
| codeId | Required | Not used | Required | The code ID of the existing code to delete or update | Integer |
|
||||
| code | Not used | Required | Required | The code to create or update | 6-digit Integer without zero's |
|
||||
| enabled | Not used | Not used | Optional | Enable or disable the code, enabled if not set | 1 = enabled, 0 = disabled |
|
||||
| name | Not used | Required | Required | The name of the code to create or update | String, max 20 chars |
|
||||
| timeLimited | Not used | Optional | Optional | If this authorization is restricted to access only at certain times, disabled if not set | 1 = enabled, 0 = disabled |
|
||||
| allowedFrom | Not used | Optional | Optional | The start timestamp from which access should be allowed (requires timeLimited = 1) | "YYYY-MM-DD HH:MM:SS" |
|
||||
| allowedUntil | Not used | Optional | Optional | The end timestamp until access should be allowed (requires timeLimited = 1) | "YYYY-MM-DD HH:MM:SS" |
|
||||
| allowedWeekdays | Not used | Optional | Optional | Allowed weekdays on which access should be allowed (requires timeLimited = 1) | Array of days: "mon", "tue", "wed", "thu" , "fri" "sat", "sun" |
|
||||
| allowedFromTime | Not used | Optional | Optional | The start time per day from which access should be allowed (requires timeLimited = 1) | "HH:MM" |
|
||||
| allowedUntilTime | Not used | Optional | Optional | The end time per day until access should be allowed (requires timeLimited = 1) | "HH:MM" |
|
||||
|
||||
Example usage:<br>
|
||||
Examples:
|
||||
-Delete: `{ "action": "delete", "codeId": "1234" }`
|
||||
-Add: `{ "action": "add", "code": "589472", "name": "Test", "timeLimited": "1", "allowedFrom": "2024-04-12 10:00:00", "allowedUntil": "2034-04-12 10:00:00", "allowedWeekdays": [ "wed", "thu", "fri" ], "allowedFromTime": "08:00", "allowedUntilTime": "16:00" }`
|
||||
-Update: `{ "action": "update", "codeId": "1234", "code": "589472", "enabled": "1", "name": "Test", "timeLimited": "1", "allowedFrom": "2024-04-12 10:00:00", "allowedUntil": "2034-04-12 10:00:00", "allowedWeekdays": [ "mon", "tue", "sat", "sun" ],
|
||||
"allowedFromTime": "08:00", "allowedUntilTime": "16:00" }`
|
||||
|
||||
### Result of attempted keypad code changes
|
||||
|
||||
The result of the last configuration change action will be published to the `configuration/commandResultJson` MQTT topic.<br>
|
||||
Possible values are "noPinSet", "keypadControlDisabled", "keypadNotAvailable", "keypadDisabled", "invalidConfig", "invalidJson", "noActionSet", "invalidAction", "noExistingCodeIdSet", "noNameSet", "noValidCodeSet", "noCodeSet", "invalidAllowedFrom", "invalidAllowedUntil", "invalidAllowedFromTime", "invalidAllowedUntilTime", "success", "failed", "timeOut", "working", "notPaired", "error" and "undefined".<br>
|
||||
|
||||
## Keypad control (alternative, optional)
|
||||
|
||||
If a keypad is connected to the lock, keypad codes can be added, updated and removed.
|
||||
This has to enabled first in the configuration portal. Check "Enable keypad control via MQTT" and save the configuration.
|
||||
After enabling keypad control, information about codes is published under "keypad/code_x", x starting from 0 up the number of configured codes.
|
||||
The same data is also published as JSON data to keypad/json.
|
||||
The same data is also published as JSON data to the "keypad/json" MQTT topic.
|
||||
<br>
|
||||
For security reasons, the code itself is not published. To modify keypad codes, a command
|
||||
structure is setup under keypad/command:
|
||||
|
||||
Reference in New Issue
Block a user