diff --git a/Config.h b/Config.h index 9455054..2230977 100644 --- a/Config.h +++ b/Config.h @@ -1,6 +1,6 @@ #pragma once -#define NUKI_HUB_VERSION "8.33" +#define NUKI_HUB_VERSION "8.34-pre-2" #define GITHUB_LATEST_RELEASE_URL "https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_LATEST_RELEASE_API_URL "https://api.github.com/repos/technyon/nuki_hub/releases/latest" diff --git a/Network.cpp b/Network.cpp index f1c8071..f188bd7 100644 --- a/Network.cpp +++ b/Network.cpp @@ -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; igetBool(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); @@ -658,6 +658,15 @@ uint8_t NukiOpenerWrapper::doorbellSuppressionToInt(const char *str) return 99; } +uint8_t NukiOpenerWrapper::soundToInt(const char *str) +{ + if(strcmp(str, "No Sound") == 0) return 0; + else if(strcmp(str, "Sound 1") == 0) return 1; + else if(strcmp(str, "Sound 2") == 0) return 2; + else if(strcmp(str, "Sound 3") == 0) return 3; + return 99; +} + NukiOpener::ButtonPressAction NukiOpenerWrapper::buttonPressActionToEnum(const char* str) { if(strcmp(str, "No Action") == 0) return NukiOpener::ButtonPressAction::NoAction; @@ -684,12 +693,20 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) DynamicJsonDocument jsonResult(2048); char _resbuf[2048]; + if(_nukiOpener.getSecurityPincode() == 0) + { + jsonResult["general"] = "noPinSet"; + serializeJson(jsonResult, _resbuf, sizeof(_resbuf)); + _network->publishConfigCommandResult(_resbuf); + return; + } + DynamicJsonDocument json(2048); DeserializationError jsonError = deserializeJson(json, value); if(jsonError) { - jsonResult["general"] = "invalidjson"; + jsonResult["general"] = "invalidJson"; serializeJson(jsonResult, _resbuf, sizeof(_resbuf)); _network->publishConfigCommandResult(_resbuf); return; @@ -697,9 +714,14 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) updateConfig(); + while ((!_nukiConfigValid || !_nukiAdvancedConfigValid) && _retryConfigCount < 11) + { + updateConfig(); + } + if(!_nukiConfigValid || !_nukiAdvancedConfigValid) { - jsonResult["general"] = "invalidconfig"; + jsonResult["general"] = "invalidConfig"; serializeJson(jsonResult, _resbuf, sizeof(_resbuf)); _network->publishConfigCommandResult(_resbuf); return; @@ -721,164 +743,164 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) for(int i=0; i < 16; i++) { if(json[basicKeys[i]]) - { + { + const char *jsonchar = json[basicKeys[i]].as(); + if((int)basicOpenerConfigAclPrefs[i] == 1) { cmdResult = Nuki::CmdResult::Error; if(strcmp(basicKeys[i], "name") == 0) { - const char* keyvalue = json[basicKeys[i]]; - - if(strlen(keyvalue) <= 32) + if(strlen(jsonchar) <= 32) { - if(strcmp((const char*)_nukiConfig.name, keyvalue) == 0) jsonResult[basicKeys[i]] = "unchanged"; - else cmdResult = _nukiOpener.setName(std::string(keyvalue)); + if(strcmp((const char*)_nukiConfig.name, jsonchar) == 0) jsonResult[basicKeys[i]] = "unchanged"; + else cmdResult = _nukiOpener.setName(std::string(jsonchar)); } - else jsonResult[basicKeys[i]] = "valuetoolong"; + else jsonResult[basicKeys[i]] = "valueTooLong"; } else if(strcmp(basicKeys[i], "latitude") == 0) { - const float keyvalue = atof(json[basicKeys[i]]); + const float keyvalue = atof(jsonchar); if(keyvalue > 0) { if(_nukiConfig.latitude == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setLatitude(keyvalue); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "longitude") == 0) { - const float keyvalue = atof(json[basicKeys[i]]); + const float keyvalue = atof(jsonchar); if(keyvalue > 0) { if(_nukiConfig.longitude == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setLongitude(keyvalue); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "pairingEnabled") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.pairingEnabled == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.enablePairing((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "buttonEnabled") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.buttonEnabled == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.enableButton((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "ledFlashEnabled") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.ledFlashEnabled == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.enableLedFlash((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "timeZoneOffset") == 0) { - const int16_t keyvalue = atoi(json[basicKeys[i]]); + const int16_t keyvalue = atoi(jsonchar); if(keyvalue >= 0 && keyvalue <= 60) { if(_nukiConfig.timeZoneOffset == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setTimeZoneOffset(keyvalue); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "dstMode") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.dstMode == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.enableDst((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "fobAction1") == 0) { - const uint8_t fobAct1 = nukiOpenerInst->fobActionToInt(json[basicKeys[i]]); + const uint8_t fobAct1 = nukiOpenerInst->fobActionToInt(jsonchar); if(fobAct1 != 99) { if(_nukiConfig.fobAction1 == fobAct1) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setFobAction(1, fobAct1); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "fobAction2") == 0) { - const uint8_t fobAct2 = nukiOpenerInst->fobActionToInt(json[basicKeys[i]]); + const uint8_t fobAct2 = nukiOpenerInst->fobActionToInt(jsonchar); if(fobAct2 != 99) { if(_nukiConfig.fobAction2 == fobAct2) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setFobAction(2, fobAct2); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "fobAction3") == 0) { - const uint8_t fobAct3 = nukiOpenerInst->fobActionToInt(json[basicKeys[i]]); + const uint8_t fobAct3 = nukiOpenerInst->fobActionToInt(jsonchar); if(fobAct3 != 99) { if(_nukiConfig.fobAction3 == fobAct3) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setFobAction(3, fobAct3); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "operatingMode") == 0) { - const uint8_t opmode = nukiOpenerInst->operatingModeToInt(json[basicKeys[i]]); + const uint8_t opmode = nukiOpenerInst->operatingModeToInt(jsonchar); if(opmode != 99) { if(_nukiConfig.operatingMode == opmode) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setOperatingMode(opmode); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "advertisingMode") == 0) { - Nuki::AdvertisingMode advmode = nukiOpenerInst->advertisingModeToEnum(json[basicKeys[i]]); + Nuki::AdvertisingMode advmode = nukiOpenerInst->advertisingModeToEnum(jsonchar); if(!(int)advmode == 0xff) { if(_nukiConfig.advertisingMode == advmode) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setAdvertisingMode(advmode); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "timeZone") == 0) { - Nuki::TimeZoneId tzid = nukiOpenerInst->timeZoneToEnum(json[basicKeys[i]]); + Nuki::TimeZoneId tzid = nukiOpenerInst->timeZoneToEnum(jsonchar); if(!(int)tzid == 0xff) { if(_nukiConfig.timeZoneId == tzid) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setTimeZoneId(tzid); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } if(cmdResult == Nuki::CmdResult::Success) basicUpdated = true; @@ -889,7 +911,7 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) jsonResult[basicKeys[i]] = resultStr; } } - else jsonResult[basicKeys[i]] = "accessdenied"; + else jsonResult[basicKeys[i]] = "accessDenied"; } } @@ -897,229 +919,231 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) { if(json[advancedKeys[i]]) { + const char *jsonchar = json[advancedKeys[i]].as(); + if((int)advancedOpenerConfigAclPrefs[i] == 1) { cmdResult = Nuki::CmdResult::Error; if(strcmp(advancedKeys[i], "intercomID") == 0) { - const uint16_t keyvalue = atoi(json[advancedKeys[i]]); + const uint16_t keyvalue = atoi(jsonchar); if(keyvalue >= 0) { if(_nukiAdvancedConfig.intercomID == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setIntercomID(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "busModeSwitch") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.busModeSwitch == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setBusModeSwitch((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "shortCircuitDuration") == 0) { - const uint16_t keyvalue = atoi(json[advancedKeys[i]]); + const uint16_t keyvalue = atoi(jsonchar); if(keyvalue >= 0) { if(_nukiAdvancedConfig.shortCircuitDuration == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setShortCircuitDuration(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "electricStrikeDelay") == 0) { - const uint16_t keyvalue = atoi(json[advancedKeys[i]]); + const uint16_t keyvalue = atoi(jsonchar); if(keyvalue >= 0 && keyvalue <= 30000) { if(_nukiAdvancedConfig.electricStrikeDelay == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setElectricStrikeDelay(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "randomElectricStrikeDelay") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.randomElectricStrikeDelay == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.enableRandomElectricStrikeDelay((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "electricStrikeDuration") == 0) { - const uint16_t keyvalue = atoi(json[advancedKeys[i]]); + const uint16_t keyvalue = atoi(jsonchar); if(keyvalue >= 1000 && keyvalue <= 30000) { if(_nukiAdvancedConfig.electricStrikeDuration == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setElectricStrikeDuration(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "disableRtoAfterRing") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.disableRtoAfterRing == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.disableRtoAfterRing((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "rtoTimeout") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue >= 5 && keyvalue <= 60) { if(_nukiAdvancedConfig.rtoTimeout == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setRtoTimeout(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "doorbellSuppression") == 0) { - const uint8_t dbsupr = nukiOpenerInst->doorbellSuppressionToInt(json[advancedKeys[i]]); + const uint8_t dbsupr = nukiOpenerInst->doorbellSuppressionToInt(jsonchar); if(dbsupr != 99) { if(_nukiAdvancedConfig.doorbellSuppression == dbsupr) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setDoorbellSuppression(dbsupr); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "doorbellSuppressionDuration") == 0) { - const uint16_t keyvalue = atoi(json[advancedKeys[i]]); + const uint16_t keyvalue = atoi(jsonchar); if(keyvalue >= 500 && keyvalue <= 10000) { if(_nukiAdvancedConfig.doorbellSuppressionDuration == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setDoorbellSuppressionDuration(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "soundRing") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t sound = nukiOpenerInst->soundToInt(jsonchar); - if(keyvalue >= 0 && keyvalue <= 3) + if(sound != 99) { - if(_nukiAdvancedConfig.soundRing == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; - else cmdResult = _nukiOpener.setSoundRing(keyvalue); + if(_nukiAdvancedConfig.soundRing == sound) jsonResult[advancedKeys[i]] = "unchanged"; + else cmdResult = _nukiOpener.setSoundRing(sound); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "soundOpen") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t sound = nukiOpenerInst->soundToInt(jsonchar); - if(keyvalue >= 0 && keyvalue <= 3) + if(sound != 99) { - if(_nukiAdvancedConfig.soundOpen == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; - else cmdResult = _nukiOpener.setSoundOpen(keyvalue); + if(_nukiAdvancedConfig.soundOpen == sound) jsonResult[advancedKeys[i]] = "unchanged"; + else cmdResult = _nukiOpener.setSoundOpen(sound); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "soundRto") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t sound = nukiOpenerInst->soundToInt(jsonchar); - if(keyvalue >= 0 && keyvalue <= 3) + if(sound != 99) { - if(_nukiAdvancedConfig.soundRto == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; - else cmdResult = _nukiOpener.setSoundRto(keyvalue); + if(_nukiAdvancedConfig.soundRto == sound) jsonResult[advancedKeys[i]] = "unchanged"; + else cmdResult = _nukiOpener.setSoundRto(sound); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "soundCm") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t sound = nukiOpenerInst->soundToInt(jsonchar); - if(keyvalue >= 0 && keyvalue <= 3) + if(sound != 99) { - if(_nukiAdvancedConfig.soundCm == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; - else cmdResult = _nukiOpener.setSoundCm(keyvalue); + if(_nukiAdvancedConfig.soundCm == sound) jsonResult[advancedKeys[i]] = "unchanged"; + else cmdResult = _nukiOpener.setSoundCm(sound); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "soundConfirmation") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.soundConfirmation == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.enableSoundConfirmation((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "soundLevel") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); - if(keyvalue >= 0 && keyvalue <= 5) + if(keyvalue >= 0 && keyvalue <= 255) { if(_nukiAdvancedConfig.soundLevel == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setSoundLevel(keyvalue); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "singleButtonPressAction") == 0) { - NukiOpener::ButtonPressAction sbpa = nukiOpenerInst->buttonPressActionToEnum(json[advancedKeys[i]]); + NukiOpener::ButtonPressAction sbpa = nukiOpenerInst->buttonPressActionToEnum(jsonchar); if(!(int)sbpa == 0xff) { if(_nukiAdvancedConfig.singleButtonPressAction == sbpa) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setSingleButtonPressAction(sbpa); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "doubleButtonPressAction") == 0) { - NukiOpener::ButtonPressAction dbpa = nukiOpenerInst->buttonPressActionToEnum(json[advancedKeys[i]]); + NukiOpener::ButtonPressAction dbpa = nukiOpenerInst->buttonPressActionToEnum(jsonchar); if(!(int)dbpa == 0xff) { if(_nukiAdvancedConfig.doubleButtonPressAction == dbpa) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setDoubleButtonPressAction(dbpa); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "batteryType") == 0) { - Nuki::BatteryType battype = nukiOpenerInst->batteryTypeToEnum(json[advancedKeys[i]]); + Nuki::BatteryType battype = nukiOpenerInst->batteryTypeToEnum(jsonchar); if(!(int)battype == 0xff) { if(_nukiAdvancedConfig.batteryType == battype) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.setBatteryType(battype); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "automaticBatteryTypeDetection") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.automaticBatteryTypeDetection == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiOpener.enableAutoBatteryTypeDetection((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } if(cmdResult == Nuki::CmdResult::Success) advancedUpdated = true; @@ -1130,7 +1154,7 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) jsonResult[advancedKeys[i]] = resultStr; } } - else jsonResult[advancedKeys[i]] = "accessdenied"; + else jsonResult[advancedKeys[i]] = "accessDenied"; } } @@ -1138,10 +1162,11 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) if(basicUpdated || advancedUpdated) { - _nextConfigUpdateTs = millis() + 300; jsonResult["general"] = "success"; } - else jsonResult["general"] = "nochange"; + else jsonResult["general"] = "noChange"; + + _nextConfigUpdateTs = millis() + 300; serializeJson(jsonResult, _resbuf, sizeof(_resbuf)); _network->publishConfigCommandResult(_resbuf); diff --git a/NukiOpenerWrapper.h b/NukiOpenerWrapper.h index 108dd68..5b62a1a 100644 --- a/NukiOpenerWrapper.h +++ b/NukiOpenerWrapper.h @@ -75,6 +75,7 @@ private: uint8_t fobActionToInt(const char *str); uint8_t operatingModeToInt(const char *str); uint8_t doorbellSuppressionToInt(const char *str); + uint8_t soundToInt(const char *str); NukiOpener::ButtonPressAction buttonPressActionToEnum(const char* str); Nuki::BatteryType batteryTypeToEnum(const char* str); diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index 19b067e..a166ce8 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -446,7 +446,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); @@ -623,12 +623,20 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) DynamicJsonDocument jsonResult(2048); char _resbuf[2048]; + if(_nukiLock.getSecurityPincode() == 0) + { + jsonResult["general"] = "noPinSet"; + serializeJson(jsonResult, _resbuf, sizeof(_resbuf)); + _network->publishConfigCommandResult(_resbuf); + return; + } + DynamicJsonDocument json(2048); DeserializationError jsonError = deserializeJson(json, value); if(jsonError) { - jsonResult["general"] = "invalidjson"; + jsonResult["general"] = "invalidJson"; serializeJson(jsonResult, _resbuf, sizeof(_resbuf)); _network->publishConfigCommandResult(_resbuf); return; @@ -636,9 +644,14 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) updateConfig(); + while ((!_nukiConfigValid || !_nukiAdvancedConfigValid) && _retryConfigCount < 11) + { + updateConfig(); + } + if(!_nukiConfigValid || !_nukiAdvancedConfigValid) { - jsonResult["general"] = "invalidconfig"; + jsonResult["general"] = "invalidConfig"; serializeJson(jsonResult, _resbuf, sizeof(_resbuf)); _network->publishConfigCommandResult(_resbuf); return; @@ -661,185 +674,185 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) { if(json[basicKeys[i]]) { + const char *jsonchar = json[basicKeys[i]].as(); + if((int)basicLockConfigAclPrefs[i] == 1) { cmdResult = Nuki::CmdResult::Error; if(strcmp(basicKeys[i], "name") == 0) { - const char* keyvalue = json[basicKeys[i]]; - - if(strlen(keyvalue) <= 32) + if(strlen(jsonchar) <= 32) { - if(strcmp((const char*)_nukiConfig.name, keyvalue) == 0) jsonResult[basicKeys[i]] = "unchanged"; - else cmdResult = _nukiLock.setName(std::string(keyvalue)); + if(strcmp((const char*)_nukiConfig.name, jsonchar) == 0) jsonResult[basicKeys[i]] = "unchanged"; + else cmdResult = _nukiLock.setName(std::string(jsonchar)); } - else jsonResult[basicKeys[i]] = "valuetoolong"; + else jsonResult[basicKeys[i]] = "valueTooLong"; } else if(strcmp(basicKeys[i], "latitude") == 0) { - const float keyvalue = atof(json[basicKeys[i]]); + const float keyvalue = atof(jsonchar); if(keyvalue > 0) { if(_nukiConfig.latitude == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setLatitude(keyvalue); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "longitude") == 0) { - const float keyvalue = atof(json[basicKeys[i]]); + const float keyvalue = atof(jsonchar); if(keyvalue > 0) { if(_nukiConfig.longitude == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setLongitude(keyvalue); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "autoUnlatch") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.autoUnlatch == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableAutoUnlatch((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "pairingEnabled") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.pairingEnabled == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enablePairing((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "buttonEnabled") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.buttonEnabled == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableButton((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "ledEnabled") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.ledEnabled == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableLedFlash((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "ledBrightness") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue >= 0 && keyvalue <= 5) { if(_nukiConfig.ledBrightness == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setLedBrightness(keyvalue); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "timeZoneOffset") == 0) { - const int16_t keyvalue = atoi(json[basicKeys[i]]); + const int16_t keyvalue = atoi(jsonchar); if(keyvalue >= 0 && keyvalue <= 60) { if(_nukiConfig.timeZoneOffset == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setTimeZoneOffset(keyvalue); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "dstMode") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.dstMode == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableDst((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "fobAction1") == 0) { - const uint8_t fobAct1 = nukiInst->fobActionToInt(json[basicKeys[i]]); + const uint8_t fobAct1 = nukiInst->fobActionToInt(jsonchar); if(fobAct1 != 99) { if(_nukiConfig.fobAction1 == fobAct1) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setFobAction(1, fobAct1); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "fobAction2") == 0) { - const uint8_t fobAct2 = nukiInst->fobActionToInt(json[basicKeys[i]]); + const uint8_t fobAct2 = nukiInst->fobActionToInt(jsonchar); if(fobAct2 != 99) { if(_nukiConfig.fobAction2 == fobAct2) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setFobAction(2, fobAct2); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "fobAction3") == 0) { - const uint8_t fobAct3 = nukiInst->fobActionToInt(json[basicKeys[i]]); + const uint8_t fobAct3 = nukiInst->fobActionToInt(jsonchar); if(fobAct3 != 99) { if(_nukiConfig.fobAction3 == fobAct3) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setFobAction(3, fobAct3); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "singleLock") == 0) { - const uint8_t keyvalue = atoi(json[basicKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiConfig.singleLock == keyvalue) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableSingleLock((keyvalue > 0)); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "advertisingMode") == 0) { - Nuki::AdvertisingMode advmode = nukiInst->advertisingModeToEnum(json[basicKeys[i]]); + Nuki::AdvertisingMode advmode = nukiInst->advertisingModeToEnum(jsonchar); if((int)advmode != 0xff) { if(_nukiConfig.advertisingMode == advmode) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setAdvertisingMode(advmode); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } else if(strcmp(basicKeys[i], "timeZone") == 0) { - Nuki::TimeZoneId tzid = nukiInst->timeZoneToEnum(json[basicKeys[i]]); + Nuki::TimeZoneId tzid = nukiInst->timeZoneToEnum(jsonchar); if((int)tzid != 0xff) { if(_nukiConfig.timeZoneId == tzid) jsonResult[basicKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setTimeZoneId(tzid); } - else jsonResult[basicKeys[i]] = "invalidvalue"; + else jsonResult[basicKeys[i]] = "invalidValue"; } if(cmdResult == Nuki::CmdResult::Success) basicUpdated = true; @@ -850,7 +863,7 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) jsonResult[basicKeys[i]] = resultStr; } } - else jsonResult[basicKeys[i]] = "accessdenied"; + else jsonResult[basicKeys[i]] = "accessDenied"; } } @@ -858,255 +871,257 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) { if(json[advancedKeys[i]]) { + const char *jsonchar = json[advancedKeys[i]].as(); + if((int)advancedLockConfigAclPrefs[i] == 1) { cmdResult = Nuki::CmdResult::Error; if(strcmp(advancedKeys[i], "unlockedPositionOffsetDegrees") == 0) { - const int16_t keyvalue = atoi(json[advancedKeys[i]]); + const int16_t keyvalue = atoi(jsonchar); if(keyvalue >= -90 && keyvalue <= 180) { if(_nukiAdvancedConfig.unlockedPositionOffsetDegrees == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setUnlockedPositionOffsetDegrees(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "lockedPositionOffsetDegrees") == 0) { - const int16_t keyvalue = atoi(json[advancedKeys[i]]); + const int16_t keyvalue = atoi(jsonchar); if(keyvalue >= -180 && keyvalue <= 90) { if(_nukiAdvancedConfig.lockedPositionOffsetDegrees == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setLockedPositionOffsetDegrees(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "singleLockedPositionOffsetDegrees") == 0) { - const int16_t keyvalue = atoi(json[advancedKeys[i]]); + const int16_t keyvalue = atoi(jsonchar); if(keyvalue >= -180 && keyvalue <= 180) { if(_nukiAdvancedConfig.singleLockedPositionOffsetDegrees == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setSingleLockedPositionOffsetDegrees(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "unlockedToLockedTransitionOffsetDegrees") == 0) { - const int16_t keyvalue = atoi(json[advancedKeys[i]]); + const int16_t keyvalue = atoi(jsonchar); if(keyvalue >= -180 && keyvalue <= 180) { if(_nukiAdvancedConfig.unlockedToLockedTransitionOffsetDegrees == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setUnlockedToLockedTransitionOffsetDegrees(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "lockNgoTimeout") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue >= 5 && keyvalue <= 60) { if(_nukiAdvancedConfig.lockNgoTimeout == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setLockNgoTimeout(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "singleButtonPressAction") == 0) { - NukiLock::ButtonPressAction sbpa = nukiInst->buttonPressActionToEnum(json[advancedKeys[i]]); + NukiLock::ButtonPressAction sbpa = nukiInst->buttonPressActionToEnum(jsonchar); if((int)sbpa != 0xff) { if(_nukiAdvancedConfig.singleButtonPressAction == sbpa) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setSingleButtonPressAction(sbpa); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "doubleButtonPressAction") == 0) { - NukiLock::ButtonPressAction dbpa = nukiInst->buttonPressActionToEnum(json[advancedKeys[i]]); + NukiLock::ButtonPressAction dbpa = nukiInst->buttonPressActionToEnum(jsonchar); if((int)dbpa != 0xff) { if(_nukiAdvancedConfig.doubleButtonPressAction == dbpa) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setDoubleButtonPressAction(dbpa); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "detachedCylinder") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.detachedCylinder == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableDetachedCylinder((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "batteryType") == 0) { - Nuki::BatteryType battype = nukiInst->batteryTypeToEnum(json[advancedKeys[i]]); + Nuki::BatteryType battype = nukiInst->batteryTypeToEnum(jsonchar); if((int)battype != 0xff) { if(_nukiAdvancedConfig.batteryType == battype) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setBatteryType(battype); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "automaticBatteryTypeDetection") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.automaticBatteryTypeDetection == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableAutoBatteryTypeDetection((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "unlatchDuration") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue >= 1 && keyvalue <= 30) { if(_nukiAdvancedConfig.unlatchDuration == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setUnlatchDuration(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "autoLockTimeOut") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue >= 30 && keyvalue <= 180) { if(_nukiAdvancedConfig.autoLockTimeOut == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setAutoLockTimeOut(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "autoUnLockDisabled") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.autoUnLockDisabled == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.disableAutoUnlock((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "nightModeEnabled") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.nightModeEnabled == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableNightMode((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "nightModeStartTime") == 0) { - String keystr = json[advancedKeys[i]]; + String keystr = jsonchar; unsigned char keyvalue[2]; - keyvalue[0] = (uint8_t)atoi(keystr.substring(0, 2).c_str()); - keyvalue[1] = (uint8_t)atoi(keystr.substring(3, 5).c_str()); + keyvalue[0] = (uint8_t)keystr.substring(0, 2).toInt(); + keyvalue[1] = (uint8_t)keystr.substring(3, 5).toInt(); if(keyvalue[0] >= 0 && keyvalue[0] <= 23 && keyvalue[1] >= 0 && keyvalue[1] <= 59) { if(_nukiAdvancedConfig.nightModeStartTime == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setNightModeStartTime(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "nightModeEndTime") == 0) { - String keystr = json[advancedKeys[i]]; + String keystr = jsonchar; unsigned char keyvalue[2]; - keyvalue[0] = (uint8_t)atoi(keystr.substring(0, 2).c_str()); - keyvalue[1] = (uint8_t)atoi(keystr.substring(3, 5).c_str()); + keyvalue[0] = (uint8_t)keystr.substring(0, 2).toInt(); + keyvalue[1] = (uint8_t)keystr.substring(3, 5).toInt(); if(keyvalue[0] >= 0 && keyvalue[0] <= 23 && keyvalue[1] >= 0 && keyvalue[1] <= 59) { if(_nukiAdvancedConfig.nightModeEndTime == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.setNightModeEndTime(keyvalue); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "nightModeAutoLockEnabled") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.nightModeAutoLockEnabled == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableNightModeAutoLock((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "nightModeAutoUnlockDisabled") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.nightModeAutoUnlockDisabled == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.disableNightModeAutoUnlock((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "nightModeImmediateLockOnStart") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.nightModeImmediateLockOnStart == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableNightModeImmediateLockOnStart((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "autoLockEnabled") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.autoLockEnabled == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableAutoLock((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "immediateAutoLockEnabled") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.immediateAutoLockEnabled == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableImmediateAutoLock((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } else if(strcmp(advancedKeys[i], "autoUpdateEnabled") == 0) { - const uint8_t keyvalue = atoi(json[advancedKeys[i]]); + const uint8_t keyvalue = atoi(jsonchar); if(keyvalue == 0 || keyvalue == 1) { if(_nukiAdvancedConfig.autoUpdateEnabled == keyvalue) jsonResult[advancedKeys[i]] = "unchanged"; else cmdResult = _nukiLock.enableAutoUpdate((keyvalue > 0)); } - else jsonResult[advancedKeys[i]] = "invalidvalue"; + else jsonResult[advancedKeys[i]] = "invalidValue"; } if(cmdResult == Nuki::CmdResult::Success) advancedUpdated = true; @@ -1117,18 +1132,19 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) jsonResult[advancedKeys[i]] = resultStr; } } - else jsonResult[advancedKeys[i]] = "accessdenied"; + else jsonResult[advancedKeys[i]] = "accessDenied"; } } nukiLockPreferences->end(); if(basicUpdated || advancedUpdated) - { - _nextConfigUpdateTs = millis() + 300; + { jsonResult["general"] = "success"; } - else jsonResult["general"] = "nochange"; + else jsonResult["general"] = "noChange"; + + _nextConfigUpdateTs = millis() + 300; serializeJson(jsonResult, _resbuf, sizeof(_resbuf)); _network->publishConfigCommandResult(_resbuf); diff --git a/README.md b/README.md index 890f16b..ab1aa60 100644 --- a/README.md +++ b/README.md @@ -229,9 +229,9 @@ In a browser navigate to the IP address assigned to the ESP32. - configuration/autoLock: 1 if the Nuki Lock is set to Auto Lock, otherwise 0 (Lock only). - configuration/autoUnlock: 1 if the Nuki Lock is set to Auto Unlock, otherwise 0 (Lock only). - configuration/soundLevel: Set to the volume for sounds the Nuki Opener plays (0 = min; 255 = max) (Opener only). -- configuration/action: Allows changing configuration settings of the Nuki Lock/Opener using a JSON formatted value. After receiving the action, the value is set to "--". See the "Changing Nuki Lock/Opener Configuration" section of this readme for possible actions/values -- configuration/commandResult: Result of the last configuration change action as JSON data. See the "Changing Nuki Lock/Opener Configuration" section of this readme for possible values -- configuration/basicJson: The current basic configuration of the Nuki Lock/Opener as JSON data. See [Nuki Smart Lock API](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--set-config) and [Nuki Opener API](https://developer.nuki.io/page/nuki-opener-api-1/7/#heading--set-config) for available settings. +- configuration/action: Allows changing configuration settings of the Nuki Lock/Opener using a JSON formatted value. After receiving the action, the value is set to "--". See the "[Changing Nuki Lock/Opener Configuration](#changing-nuki-lockopener-configuration)" section of this README for possible actions/values +- configuration/commandResult: Result of the last configuration change action as JSON data. See the "[Changing Nuki Lock/Opener Configuration](#changing-nuki-lockopener-configuration)" section of this README for possible values +- configuration/basicJson: The current basic configuration of the Nuki Lock/Opener as JSON data. See [Nuki Smart Lock API](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--set-config) and [Nuki Opener API](https://developer.nuki.io/page/nuki-opener-api-1/7/#heading--set-config) for available settings. Please note: Longitude and Latitude of the Lock/Opener are not published to MQTT by design. These values can still be changed though. - configuration/advancedJson: The current advanced configuration of the Nuki Lock/Opener as JSON data. See [Nuki Smart Lock API](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--advanced-config) and [Nuki Opener API](https://developer.nuki.io/page/nuki-opener-api-1/7/#heading--advanced-config) for available settings. ### Query @@ -283,13 +283,108 @@ In a browser navigate to the IP address assigned to the ESP32. ## Changing Nuki Lock/Opener Configuration -To change Nuki Lock/Opener settings set configuration/action topic to a JSON formatted value with any of the following settings. Multiple settings can be changed at once. +To change Nuki Lock/Opener settings set the `configuration/action` topic to a JSON formatted value with any of the following settings. Multiple settings can be changed at once. See [Nuki Smart Lock API Basic Config](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--set-config), [Nuki Smart Lock API Advanced Config](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--advanced-config), [Nuki Opener API Basic Config](https://developer.nuki.io/page/nuki-opener-api-1/7/#heading--set-config) and [Nuki Opener API Advanced Config](https://developer.nuki.io/page/nuki-opener-api-1/7/#heading--advanced-config) for more information on the available settings.
-|Setting | Possible values | -|-------------|-------------------------------------------------------------------| -| name | | -| latitude | | -| longitude | | +### Nuki Lock Configuration + +| Setting | Usage | Possible values | Example | +|-----------------------------------------|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|------------------------------------| +| name | The name of the Smart Lock. | Alphanumeric string, max length 32 chars |`{ "name": "Frontdoor" }` | +| latitude | The latitude of the Smart Locks geoposition. | Float |`{ "latitude": "48.858093" }` | +| longitude | The longitude of the Smart Locks geoposition | Float |`{ "longitude": "2.294694" }` | +| autoUnlatch | Whether or not the door shall be unlatched by manually operating a door handle from the outside. | 1 = enabled, 0 = disabled |`{ "autoUnlatch": "1" }` | +| pairingEnabled | Whether or not activating the pairing mode via button should be enabled. | 1 = enabled, 0 = disabled |`{ "pairingEnabled": "0" }` | +| buttonEnabled | Whether or not the button should be enabled. | 1 = enabled, 0 = disabled |`{ "buttonEnabled": "1" }` | +| ledEnabled | Whether or not the flashing LED should be enabled to signal an unlocked door. | 1 = enabled, 0 = disabled |`{ "ledEnabled": "1" }` | +| ledBrightness | The LED brightness level | 0 = off, …, 5 = max |`{ "ledBrightness": "2" }` | +| timeZoneOffset | The timezone offset (UTC) in minutes | Integer between 0 and 60 |`{ "timeZoneOffset": "0" }` | +| dstMode | The desired daylight saving time mode. | 0 = disabled, 1 = European |`{ "dstMode": "0" }` | +| fobAction1 | The desired action, if a Nuki Fob is pressed once. | "No Action", "Unlock", "Lock", "Lock n Go", "Intelligent" |`{ "fobAction1": "Lock n Go" }` | +| fobAction2 | The desired action, if a Nuki Fob is pressed twice. | "No Action", "Unlock", "Lock", "Lock n Go", "Intelligent" |`{ "fobAction2": "Intelligent" }` | +| fobAction3 | The desired action, if a Nuki Fob is pressed three times. | "No Action", "Unlock", "Lock", "Lock n Go", "Intelligent" |`{ "fobAction3": "Unlock" }` | +| singleLock | Whether only a single lock or double lock should be performed | 0 = double lock, 1 = single lock |`{ "singleLock": "0" }` | +| advertisingMode | The desired advertising mode. | "Automatic", "Normal", "Slow", "Slowest" |`{ "advertisingMode": "Normal" }` | +| timeZone | The current timezone or "None" if timezones are not supported | "None" or one of the timezones from [Nuki Timezones](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--list-of-timezone-ids) |`{ "timeZone": "Europe/Berlin" }` | +| unlockedPositionOffsetDegrees | Offset that alters the unlocked position in degrees. | Integer between -90 and 180 |`{ "unlockedPositionOffsetDegrees": "-90" }` | +| lockedPositionOffsetDegrees | Offset that alters the locked position in degrees. | Integer between -180 and 90 |`{ "lockedPositionOffsetDegrees": "80" }` | +| singleLockedPositionOffsetDegrees | Offset that alters the single locked position in degrees. | Integer between -180 and 180 |`{ "singleLockedPositionOffsetDegrees": "120" }` | +| unlockedToLockedTransitionOffsetDegrees | Offset that alters the position where transition from unlocked to locked happens in degrees. | Integer between -180 and 180 |`{ "unlockedToLockedTransitionOffsetDegrees": "180" }` | +| lockNgoTimeout | Timeout for lock ā€˜n’ go in seconds | Integer between 5 and 60 |`{ "lockNgoTimeout": "60" }` | +| singleButtonPressAction | The desired action, if the button is pressed once. | "No Action", "Intelligent", "Unlock", "Lock", "Unlatch", "Lock n Go", "Show Status" |`{ "singleButtonPressAction": "Lock n Go" }` | +| doubleButtonPressAction | The desired action, if the button is pressed twice. | "No Action", "Intelligent", "Unlock", "Lock", "Unlatch", "Lock n Go", "Show Status" |`{ "doubleButtonPressAction": "Show Status" }` | +| detachedCylinder | Wheter the inner side of the used cylinder is detached from the outer side. | 0 = not detached, 1 = detached |`{ "detachedCylinder": "1" }` | +| batteryType | The type of the batteries present in the smart lock. | "Alkali", "Accumulators", "Lithium" |`{ "batteryType": "Accumulators" }` | +| automaticBatteryTypeDetection | Whether the automatic detection of the battery type is enabled. | 1 = enabled, 0 = disabled |`{ "automaticBatteryTypeDetection": "Lock n Go" }` | +| unlatchDuration | Duration in seconds for holding the latch in unlatched position. | Integer between 1 and 30 |`{ "unlatchDuration": "3" }` | +| autoLockTimeOut | Seconds until the smart lock relocks itself after it has been unlocked. | Integer between 30 and 180 |`{ "autoLockTimeOut": "60" }` | +| autoUnLockDisabled | Whether auto unlock should be disabled in general. | 1 = auto unlock disabled, 0 = auto unlock enabled |`{ "autoUnLockDisabled": "1" }` | +| nightModeEnabled | Whether nightmode is enabled. | 1 = enabled, 0 = disabled |`{ "nightModeEnabled": "1" }` | +| nightModeStartTime | Start time for nightmode if enabled. | Time in "HH:MM" format |`{ "nightModeStartTime": "22:00" }` | +| nightModeEndTime | End time for nightmode if enabled. | Time in "HH:MM" format |`{ "nightModeEndTime": "07:00" }` | +| nightModeAutoLockEnabled | Whether auto lock should be enabled during nightmode. | 1 = enabled, 0 = disabled |`{ "nightModeAutoLockEnabled": "1" }`| +| nightModeAutoUnlockDisabled | Whether auto unlock should be disabled during nightmode. | 1 = auto unlock disabled, 0 = auto unlock enabled |`{ "nightModeAutoUnlockDisabled": "1" }`| +| nightModeImmediateLockOnStart | Whether the door should be immediately locked on nightmode start. | 1 = enabled, 0 = disabled |`{ "nightModeImmediateLockOnStart": "1" }`| +| autoLockEnabled | Whether auto lock is enabled. | 1 = enabled, 0 = disabled |`{ "autoLockEnabled": "1" }` | +| immediateAutoLockEnabled | Whether auto lock should be performed immediately after the door has been closed. | 1 = enabled, 0 = disabled |`{ "immediateAutoLockEnabled": "1" }`| +| autoUpdateEnabled | Whether automatic firmware updates should be enabled. | 1 = enabled, 0 = disabled |`{ "autoUpdateEnabled": "1" }` | + +### Nuki Opener Configuration + +| Setting | Usage | Possible values | Example | +|-----------------------------------------|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|------------------------------------| +| name | The name of the Opener. | Alphanumeric string, max length 32 chars |`{ "name": "Frontdoor" }` | +| latitude | The latitude of the Openers geoposition. | Float |`{ "latitude": "48.858093" }` | +| longitude | The longitude of the Openers geoposition | Float |`{ "longitude": "2.294694" }` | +| pairingEnabled | Whether or not activating the pairing mode via button should be enabled. | 1 = enabled, 0 = disabled |`{ "pairingEnabled": "0" }` | +| buttonEnabled | Whether or not the button should be enabled. | 1 = enabled, 0 = disabled |`{ "buttonEnabled": "1" }` | +| ledFlashEnabled | Whether or not the flashing LED should be enabled to signal CM or RTO. | 1 = enabled, 0 = disabled |`{ "ledFlashEnabled": "1" }` | +| timeZoneOffset | The timezone offset (UTC) in minutes | Integer between 0 and 60 |`{ "timeZoneOffset": "0" }` | +| dstMode | The desired daylight saving time mode. | 0 = disabled, 1 = European |`{ "dstMode": "0" }` | +| fobAction1 | The desired action, if a Nuki Fob is pressed once. | "No Action", "Toggle RTO", "Activate RTO", "Deactivate RTO", "Open", "Ring" |`{ "fobAction1": "Toggle RTO" }` | +| fobAction2 | The desired action, if a Nuki Fob is pressed twice. | "No Action", "Toggle RTO", "Activate RTO", "Deactivate RTO", "Open", "Ring" |`{ "fobAction2": "Open" }` | +| fobAction3 | The desired action, if a Nuki Fob is pressed three times. | "No Action", "Toggle RTO", "Activate RTO", "Deactivate RTO", "Open", "Ring" |`{ "fobAction3": "Ring" }` | +| operatingMode | The desired operating mode | "Generic door opener", "Analogue intercom", "Digital intercom", "Siedle", "TCS", "Bticino", "Siedle HTS", "STR", "Ritto", "Fermax", "Comelit", "Urmet BiBus", "Urmet 2Voice", "Golmar", "SKS", "Spare" |`{ "operatingMode": "TCS" }` | +| advertisingMode | The desired advertising mode. | "Automatic", "Normal", "Slow", "Slowest" |`{ "advertisingMode": "Normal" }` | +| timeZone | The current timezone or "None" if timezones are not supported | "None" or one of the timezones from [Nuki Timezones](https://developer.nuki.io/page/nuki-smart-lock-api-2/2/#heading--list-of-timezone-ids) |`{ "timeZone": "Europe/Berlin" }` | +| intercomID | Database ID of the connected intercom. | Integer |`{ "intercomID": "1" }` | +| busModeSwitch | Method to switch between data and analogue mode | 0 = none, 1 =vshort circuit |`{ "busModeSwitch": "0" }` | +| shortCircuitDuration | Duration of the short circuit for BUS mode switching in ms. | Integer |`{ "shortCircuitDuration": "250" }` | +| electricStrikeDelay | Delay in ms of electric strike activation in case of an electric strike actuation by RTO | Integer between 0 and 30000 |`{ "electricStrikeDelay": "2080" }` | +| randomElectricStrikeDelay | Random delay (3-7s) in order to simulate a person inside actuating the electric strike. | 1 = enabled, 0 = disabled |`{ "randomElectricStrikeDelay": "1" }`| +| electricStrikeDuration | Duration in ms of electric strike actuation. . | Integer between 1000 and 30000 |`{ "electricStrikeDuration": "5000" }` | +| disableRtoAfterRing | Whether to disable RTO after ring. | 1 = disable RTO after ring, 0 = Don't disable RTO after ring |`{ "disableRtoAfterRing": "0" }` | +| rtoTimeout | After this period of time in minutes, RTO gets deactivated automatically | Integer between 5 and 60 |`{ "rtoTimeout": "60" }` | +| doorbellSuppression | Whether the doorbell is suppressed when Ring, CM and/or RTO are active | "Off", "CM", "RTO", "CM & RTO", "Ring", "CM & Ring", "RTO & Ring", "CM & RTO & Ring"|`{ "doorbellSuppression": "CM & Ring" }`| +| doorbellSuppressionDuration | Duration in ms of doorbell suppression. | Integer between 500 and 10000 |`{ "doorbellSuppressionDuration": "2000" }` | +| soundRing | The Ring sound | "No Sound", "Sound 1", "Sound 2", "Sound 3" |`{ "soundRing": "No Sound" }` | +| soundOpen | The Open sound. | "No Sound", "Sound 1", "Sound 2", "Sound 3" |`{ "soundOpen": "Sound 1" }` | +| soundRto | The RTO sound. | "No Sound", "Sound 1", "Sound 2", "Sound 3" |`{ "soundRto": "Sound 2" }` | +| soundCm | The CM sound. | "No Sound", "Sound 1", "Sound 2", "Sound 3" |`{ "soundCm": "Sound 3" }` | +| soundConfirmation | Sound confirmation | 0 = no sound, 1 = sound |`{ "soundConfirmation": "1" }` | +| soundLevel | The sound level for the opener | Integer between 0 and 255 |`{ "soundLevel": "200" }` | +| singleButtonPressAction | The desired action, if the button is pressed once. | "No Action", "Toggle RTO", "Activate RTO", "Deactivate RTO", "Toggle CM", "Activate CM", "Deactivate CM", "Open" |`{ "singleButtonPressAction": "Open" }` | +| doubleButtonPressAction | The desired action, if the button is pressed twice. | "No Action", "Toggle RTO", "Activate RTO", "Deactivate RTO", "Toggle CM", "Activate CM", "Deactivate CM", "Open" |`{ "doubleButtonPressAction": "No Action" }` | +| batteryType | The type of the batteries present in the smart lock. | "Alkali", "Accumulators", "Lithium" |`{ "batteryType": "Accumulators" }` | +| automaticBatteryTypeDetection | Whether the automatic detection of the battery type is enabled. | 1 = enabled, 0 = disabled |`{ "automaticBatteryTypeDetection": "1" }` | + +Example usage for changing multiple settings at once:
+- `{ "buttonEnabled": "1", "lockngoTimeout": "60", "automaticBatteryTypeDetection": "1" }` +- `{ "fobAction1": "Unlock", "fobAction2": "Intelligent", "nightModeImmediateLockOnStart": "1" }` + +### Result of attempted configuration changes + +The result of the last configuration change action will be published to the `configuration/commandResult` MQTT topic as JSON data.
+
+The JSON data will include a node called "general" and a node for every setting that Nuki Hub detected in the action.
+Possible values for the "general" node are "noPinSet", "invalidJson", "invalidConfig", "success" and "noChange".
+Possible values for the node per setting are "unchanged", "invalidValue", "valueTooLong", "accessDenied", "success", "failed", "timeOut", "working", "notPaired", "error" and "undefined"
+
+Example: +- `{"advertisingMode":"success","general":"success"}` + +### Home Assistant discovery + +If Home Assistant discovery is enabled (see the [Home Assistant Discovery](#home-assistant-discovery-optional) section of this README) Nuki Hub will create entities for almost all of the above settings. These entities will be disabled by default in Home Assistant, but can be found in the MQTT devices section of the Home Assistant UI under the "Configuration" section of the Nuki Lock/Opener and enabled there. ## Over-the-air Update (OTA)