Add Authorization entries (#456)
* Add and remove libs and components for Arduino Core 3 * Arduino Core 3 * Add back Solo1 * Change ESP32-S3 to 4MB build * Add Authorization info and control * Use esp_crt_bundle for HTTPS requests * Remove Solo1 support * Improve Nuki device config read functions * Webserial * OTA Improvements * Authorization Entries * Authorization entries * Authorization
This commit is contained in:
@@ -102,6 +102,7 @@
|
||||
#define MAX_AUTHLOG 5
|
||||
#define MAX_KEYPAD 10
|
||||
#define MAX_TIMECONTROL 10
|
||||
#define MAX_AUTH 10
|
||||
#endif
|
||||
|
||||
#define NETWORK_TASK_SIZE 12288
|
||||
|
||||
@@ -85,6 +85,12 @@
|
||||
#define mqtt_topic_timecontrol_action "/timecontrol/action"
|
||||
#define mqtt_topic_timecontrol_command_result "/timecontrol/commandResult"
|
||||
|
||||
#define mqtt_topic_auth "/authorization"
|
||||
#define mqtt_topic_auth_entries "/authorization/entries"
|
||||
#define mqtt_topic_auth_json "/authorization/json"
|
||||
#define mqtt_topic_auth_action "/authorization/action"
|
||||
#define mqtt_topic_auth_command_result "/authorization/commandResult"
|
||||
|
||||
#define mqtt_topic_info_hardware_version "/info/hardwareVersion"
|
||||
#define mqtt_topic_info_firmware_version "/info/firmwareVersion"
|
||||
#define mqtt_topic_info_nuki_hub_version "/info/nukiHubVersion"
|
||||
|
||||
@@ -107,7 +107,7 @@ void NukiNetwork::setupDevice()
|
||||
_networkDeviceType = NetworkDeviceType::WiFi;
|
||||
#else
|
||||
int custEth = _preferences->getInt(preference_network_custom_phy, 0);
|
||||
|
||||
|
||||
if(custEth<3) custEth++;
|
||||
else custEth = 0;
|
||||
_preferences->putInt(preference_network_custom_phy, custEth);
|
||||
@@ -377,7 +377,7 @@ void NukiNetwork::setupDevice()
|
||||
_preferences->getInt(preference_network_custom_mosi, -1),
|
||||
ETH_PHY_SPI_FREQ_MHZ,
|
||||
ETH_PHY_W5500);
|
||||
break;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -570,7 +570,6 @@ void NukiNetwork::initialize()
|
||||
bool NukiNetwork::update()
|
||||
{
|
||||
int64_t ts = (esp_timer_get_time() / 1000);
|
||||
|
||||
_device->update();
|
||||
|
||||
if(!_mqttEnabled)
|
||||
@@ -630,9 +629,9 @@ bool NukiNetwork::update()
|
||||
return false;
|
||||
}
|
||||
_mqttConnectCounter = 0;
|
||||
if(forceEnableWebServer && !_webEnabled)
|
||||
if(forceEnableWebServer && !_webEnabled)
|
||||
{
|
||||
forceEnableWebServer = false;
|
||||
forceEnableWebServer = false;
|
||||
delay(200);
|
||||
restartEsp(RestartReason::ReconfigureWebServer);
|
||||
}
|
||||
@@ -654,12 +653,13 @@ bool NukiNetwork::update()
|
||||
}
|
||||
|
||||
_lastConnectedTs = ts;
|
||||
|
||||
|
||||
#if PRESENCE_DETECTION_ENABLED
|
||||
if(_presenceDetection != nullptr && (_lastPresenceTs == 0 || (ts - _lastPresenceTs) > 3000))
|
||||
{
|
||||
char* presenceCsv = _presenceDetection->generateCsv();
|
||||
bool success = publishString(_mqttPresencePrefix, mqtt_topic_presence, presenceCsv, true);
|
||||
|
||||
if(!success)
|
||||
{
|
||||
Log->println(F("Failed to publish presence CSV data."));
|
||||
@@ -706,11 +706,12 @@ bool NukiNetwork::update()
|
||||
if(_lastUpdateCheckTs == 0 || (ts - _lastUpdateCheckTs) > 86400000)
|
||||
{
|
||||
_lastUpdateCheckTs = ts;
|
||||
bool otaManifestSuccess = false;
|
||||
JsonDocument doc;
|
||||
|
||||
NetworkClientSecure *client = new NetworkClientSecure;
|
||||
if (client) {
|
||||
//client->setDefaultCACertBundle();
|
||||
client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
||||
client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
||||
{
|
||||
HTTPClient https;
|
||||
https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
@@ -721,28 +722,29 @@ bool NukiNetwork::update()
|
||||
|
||||
if (httpResponseCode == HTTP_CODE_OK || httpResponseCode == HTTP_CODE_MOVED_PERMANENTLY)
|
||||
{
|
||||
JsonDocument doc;
|
||||
DeserializationError jsonError = deserializeJson(doc, https.getStream());
|
||||
|
||||
if (!jsonError)
|
||||
{
|
||||
String currentVersion = NUKI_HUB_VERSION;
|
||||
|
||||
if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) _latestVersion = doc["release"]["fullversion"];
|
||||
else if(currentVersion.indexOf("beta") > 0) _latestVersion = doc["beta"]["fullversion"];
|
||||
else if(currentVersion.indexOf("master") > 0) _latestVersion = doc["master"]["fullversion"];
|
||||
else _latestVersion = doc["release"]["fullversion"];
|
||||
|
||||
publishString(_maintenancePathPrefix, mqtt_topic_info_nuki_hub_latest, _latestVersion, true);
|
||||
|
||||
if(strcmp(_latestVersion, _preferences->getString(preference_latest_version).c_str()) != 0) _preferences->putString(preference_latest_version, _latestVersion);
|
||||
}
|
||||
if (!jsonError) { otaManifestSuccess = true; }
|
||||
}
|
||||
}
|
||||
https.end();
|
||||
}
|
||||
delete client;
|
||||
}
|
||||
|
||||
if (otaManifestSuccess)
|
||||
{
|
||||
String currentVersion = NUKI_HUB_VERSION;
|
||||
|
||||
if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) _latestVersion = doc["release"]["fullversion"];
|
||||
else if(currentVersion.indexOf("beta") > 0) _latestVersion = doc["beta"]["fullversion"];
|
||||
else if(currentVersion.indexOf("master") > 0) _latestVersion = doc["master"]["fullversion"];
|
||||
else _latestVersion = doc["release"]["fullversion"];
|
||||
|
||||
publishString(_maintenancePathPrefix, mqtt_topic_info_nuki_hub_latest, _latestVersion, true);
|
||||
|
||||
if(strcmp(_latestVersion, _preferences->getString(preference_latest_version).c_str()) != 0) _preferences->putString(preference_latest_version, _latestVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,7 +863,6 @@ bool NukiNetwork::reconnect()
|
||||
_mqttConnectedTs = millis();
|
||||
_mqttConnectionState = 1;
|
||||
delay(100);
|
||||
|
||||
_device->mqttOnMessage(NukiNetwork::onMqttDataReceivedCallback);
|
||||
for(const String& topic : _subscribedTopics)
|
||||
{
|
||||
|
||||
@@ -53,9 +53,9 @@ public:
|
||||
explicit NukiNetwork(Preferences* preferences, PresenceDetection* presenceDetection, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize);
|
||||
|
||||
void registerMqttReceiver(MqttReceiver* receiver);
|
||||
#if PRESENCE_DETECTION_ENABLED
|
||||
#if PRESENCE_DETECTION_ENABLED
|
||||
void setMqttPresencePath(char* path);
|
||||
#endif
|
||||
#endif
|
||||
void disableAutoRestarts(); // disable on OTA start
|
||||
void disableMqtt();
|
||||
String localIP();
|
||||
@@ -105,7 +105,6 @@ public:
|
||||
bool encryptionSupported();
|
||||
bool mqttRecentlyConnected();
|
||||
bool pathEquals(const char* prefix, const char* path, const char* referencePath);
|
||||
|
||||
uint16_t subscribe(const char* topic, uint8_t qos);
|
||||
|
||||
void addReconnectedCallback(std::function<void()> reconnectedCallback);
|
||||
|
||||
@@ -68,7 +68,6 @@ void NukiNetworkLock::initialize()
|
||||
_network->subscribe(_mqttPath, mqtt_topic_lock_action);
|
||||
_network->initTopic(_mqttPath, mqtt_topic_config_action, "--");
|
||||
_network->subscribe(_mqttPath, mqtt_topic_config_action);
|
||||
|
||||
_network->subscribe(_mqttPath, mqtt_topic_reset);
|
||||
_network->initTopic(_mqttPath, mqtt_topic_reset, "0");
|
||||
|
||||
@@ -154,6 +153,12 @@ void NukiNetworkLock::initialize()
|
||||
_network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--");
|
||||
}
|
||||
|
||||
if(_preferences->getBool(preference_auth_control_enabled))
|
||||
{
|
||||
_network->subscribe(_mqttPath, mqtt_topic_auth_action);
|
||||
_network->initTopic(_mqttPath, mqtt_topic_auth_action, "--");
|
||||
}
|
||||
|
||||
if(_offEnabled)
|
||||
{
|
||||
char uidString[20];
|
||||
@@ -200,7 +205,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const
|
||||
{
|
||||
Log->println(F("Update requested via MQTT."));
|
||||
String currentVersion = NUKI_HUB_VERSION;
|
||||
|
||||
|
||||
if(atof(_preferences->getString(preference_latest_version).c_str()) >= atof(currentVersion.c_str()))
|
||||
{
|
||||
_preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL);
|
||||
@@ -403,6 +408,18 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const
|
||||
|
||||
publishString(mqtt_topic_timecontrol_action, "--", true);
|
||||
}
|
||||
|
||||
if(comparePrefixedPath(topic, mqtt_topic_auth_action))
|
||||
{
|
||||
if(strcmp(value, "") == 0 || strcmp(value, "--") == 0) return;
|
||||
|
||||
if(_authCommandReceivedReceivedCallback != NULL)
|
||||
{
|
||||
_authCommandReceivedReceivedCallback(value);
|
||||
}
|
||||
|
||||
publishString(mqtt_topic_auth_action, "--", true);
|
||||
}
|
||||
}
|
||||
|
||||
void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurnerState, const NukiLock::KeyTurnerState& lastKeyTurnerState)
|
||||
@@ -1262,6 +1279,152 @@ void NukiNetworkLock::publishTimeControl(const std::list<NukiLock::TimeControlEn
|
||||
}
|
||||
}
|
||||
|
||||
void NukiNetworkLock::publishAuth(const std::list<NukiLock::AuthorizationEntry>& authEntries, uint maxAuthEntryCount)
|
||||
{
|
||||
uint index = 0;
|
||||
char str[50];
|
||||
char uidString[20];
|
||||
itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16);
|
||||
String baseTopic = _preferences->getString(preference_mqtt_lock_path);
|
||||
JsonDocument json;
|
||||
|
||||
for(const auto& entry : authEntries)
|
||||
{
|
||||
auto jsonEntry = json.add<JsonVariant>();
|
||||
|
||||
jsonEntry["authId"] = entry.authId;
|
||||
jsonEntry["idType"] = entry.idType; //CONSIDER INT TO STRING
|
||||
jsonEntry["enabled"] = entry.enabled;
|
||||
jsonEntry["name"] = entry.name;
|
||||
jsonEntry["remoteAllowed"] = entry.remoteAllowed;
|
||||
char createdDT[20];
|
||||
sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.createdYear, entry.createdMonth, entry.createdDay, entry.createdHour, entry.createdMinute, entry.createdSecond);
|
||||
jsonEntry["dateCreated"] = createdDT;
|
||||
jsonEntry["lockCount"] = entry.lockCount;
|
||||
char lastActiveDT[20];
|
||||
sprintf(lastActiveDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.lastActYear, entry.lastActMonth, entry.lastActDay, entry.lastActHour, entry.lastActMinute, entry.lastActSecond);
|
||||
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.allowedFromMinute, entry.allowedFromSecond);
|
||||
jsonEntry["allowedFrom"] = allowedFromDT;
|
||||
char allowedUntilDT[20];
|
||||
sprintf(allowedUntilDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedUntilYear, entry.allowedUntilMonth, entry.allowedUntilDay, entry.allowedUntilHour, entry.allowedUntilMinute, entry.allowedUntilSecond);
|
||||
jsonEntry["allowedUntil"] = allowedUntilDT;
|
||||
|
||||
uint8_t allowedWeekdaysInt = entry.allowedWeekdays;
|
||||
JsonArray weekdays = jsonEntry["allowedWeekdays"].to<JsonArray>();
|
||||
|
||||
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;
|
||||
|
||||
if(_preferences->getBool(preference_auth_topic_per_entry, false))
|
||||
{
|
||||
String basePath = mqtt_topic_auth;
|
||||
basePath.concat("/entries/");
|
||||
basePath.concat(std::to_string(index).c_str());
|
||||
jsonEntry["index"] = index;
|
||||
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||
publishString(basePath.c_str(), _buffer, true);
|
||||
|
||||
String basePathPrefix = "~";
|
||||
basePathPrefix.concat(basePath);
|
||||
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||
|
||||
std::string baseCommand = std::string("{ \"action\": \"update\", \"authId\": \"") + std::to_string(entry.authId);
|
||||
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||
std::string mqttDeviceName = std::string("auth_") + std::to_string(index);
|
||||
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||
std::string displayName = std::string("Authorization - ") + std::to_string(entry.authId);
|
||||
|
||||
_network->publishHassTopic("switch",
|
||||
mqttDeviceName.c_str(),
|
||||
uidString,
|
||||
uidStringPostfix.c_str(),
|
||||
displayName.c_str(),
|
||||
_nukiName,
|
||||
baseTopic.c_str(),
|
||||
String("~") + basePath.c_str(),
|
||||
(char*)"SmartLock",
|
||||
"",
|
||||
"",
|
||||
"diagnostic",
|
||||
String("~") + mqtt_topic_auth_action,
|
||||
{ { (char*)"json_attr_t", (char*)basePathPrefixChr },
|
||||
{ (char*)"pl_on", (char*)enaCommand.c_str() },
|
||||
{ (char*)"pl_off", (char*)disCommand.c_str() },
|
||||
{ (char*)"val_tpl", (char*)"{{value_json.enabled}}" },
|
||||
{ (char*)"stat_on", (char*)"1" },
|
||||
{ (char*)"stat_off", (char*)"0" }});
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
serializeJson(json, _buffer, _bufferSize);
|
||||
publishString(mqtt_topic_auth_json, _buffer, true);
|
||||
|
||||
for(int j=authEntries.size(); j<maxAuthEntryCount; j++)
|
||||
{
|
||||
String entriesTopic = _mqttPath;
|
||||
entriesTopic.concat(mqtt_topic_auth_entries);
|
||||
entriesTopic.concat("/");
|
||||
_network->removeTopic(entriesTopic, (char*)std::to_string(j).c_str());
|
||||
std::string mqttDeviceName = std::string("auth_") + std::to_string(j);
|
||||
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||
}
|
||||
}
|
||||
|
||||
void NukiNetworkLock::publishConfigCommandResult(const char* result)
|
||||
{
|
||||
publishString(mqtt_topic_config_action_command_result, result, true);
|
||||
@@ -1283,6 +1446,11 @@ void NukiNetworkLock::publishTimeControlCommandResult(const char* result)
|
||||
publishString(mqtt_topic_timecontrol_command_result, result, true);
|
||||
}
|
||||
|
||||
void NukiNetworkLock::publishAuthCommandResult(const char* result)
|
||||
{
|
||||
publishString(mqtt_topic_auth_command_result, result, true);
|
||||
}
|
||||
|
||||
void NukiNetworkLock::publishStatusUpdated(const bool statusUpdated)
|
||||
{
|
||||
publishBool(mqtt_topic_lock_status_updated, statusUpdated, true);
|
||||
@@ -1319,6 +1487,11 @@ void NukiNetworkLock::setTimeControlCommandReceivedCallback(void (*timeControlCo
|
||||
_timeControlCommandReceivedReceivedCallback = timeControlCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NukiNetworkLock::setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char *))
|
||||
{
|
||||
_authCommandReceivedReceivedCallback = authCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NukiNetworkLock::buildMqttPath(const char* path, char* outPath, bool offPath)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
@@ -39,11 +39,13 @@ public:
|
||||
void removeHASSConfig(char* uidString);
|
||||
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
||||
void publishTimeControl(const std::list<NukiLock::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount);
|
||||
void publishAuth(const std::list<NukiLock::AuthorizationEntry>& authEntries, uint maxAuthEntryCount);
|
||||
void publishStatusUpdated(const bool statusUpdated);
|
||||
void publishConfigCommandResult(const char* result);
|
||||
void publishKeypadCommandResult(const char* result);
|
||||
void publishKeypadJsonCommandResult(const char* result);
|
||||
void publishTimeControlCommandResult(const char* result);
|
||||
void publishAuthCommandResult(const char* result);
|
||||
void publishOffAction(const int value);
|
||||
|
||||
void setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char* value));
|
||||
@@ -52,6 +54,7 @@ public:
|
||||
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 setTimeControlCommandReceivedCallback(void (*timeControlCommandReceivedReceivedCallback)(const char* value));
|
||||
void setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char* value));
|
||||
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
||||
|
||||
void publishFloat(const char* topic, const float value, bool retain, const uint8_t precision = 2);
|
||||
@@ -132,4 +135,5 @@ private:
|
||||
void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr;
|
||||
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
void (*_timeControlCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
void (*_authCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
};
|
||||
@@ -69,7 +69,7 @@ void NukiNetworkOpener::initialize()
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_battery_keypad_critical);
|
||||
//_network->removeTopic(_mqttPath, mqtt_topic_presence);
|
||||
}
|
||||
|
||||
|
||||
if(!_preferences->getBool(preference_conf_info_enabled, true))
|
||||
{
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_basic_json);
|
||||
@@ -110,6 +110,12 @@ void NukiNetworkOpener::initialize()
|
||||
_network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--");
|
||||
}
|
||||
|
||||
if(_preferences->getBool(preference_auth_control_enabled))
|
||||
{
|
||||
_network->subscribe(_mqttPath, mqtt_topic_auth_action);
|
||||
_network->initTopic(_mqttPath, mqtt_topic_auth_action, "--");
|
||||
}
|
||||
|
||||
if(_preferences->getBool(preference_publish_authdata, false))
|
||||
{
|
||||
_network->subscribe(_mqttPath, mqtt_topic_lock_log_rolling_last);
|
||||
@@ -281,6 +287,18 @@ void NukiNetworkOpener::onMqttDataReceived(const char* topic, byte* payload, con
|
||||
|
||||
publishString(mqtt_topic_timecontrol_action, "--", true);
|
||||
}
|
||||
|
||||
if(comparePrefixedPath(topic, mqtt_topic_auth_action))
|
||||
{
|
||||
if(strcmp(value, "") == 0 || strcmp(value, "--") == 0) return;
|
||||
|
||||
if(_authCommandReceivedReceivedCallback != NULL)
|
||||
{
|
||||
_authCommandReceivedReceivedCallback(value);
|
||||
}
|
||||
|
||||
publishString(mqtt_topic_auth_action, "--", true);
|
||||
}
|
||||
}
|
||||
|
||||
void NukiNetworkOpener::publishKeyTurnerState(const NukiOpener::OpenerState& keyTurnerState, const NukiOpener::OpenerState& lastKeyTurnerState)
|
||||
@@ -501,7 +519,7 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::Log
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiOpener::lockactionToString((NukiOpener::LockAction)log.data[0], str);
|
||||
entry["action"] = str;
|
||||
|
||||
|
||||
switch(log.data[1])
|
||||
{
|
||||
case 0:
|
||||
@@ -527,7 +545,7 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::Log
|
||||
NukiOpener::completionStatusToString((NukiOpener::CompletionStatus)log.data[2], str);
|
||||
entry["completionStatus"] = str;
|
||||
}
|
||||
|
||||
|
||||
entry["codeId"] = 256U*log.data[4]+log.data[3];
|
||||
break;
|
||||
case NukiOpener::LoggingType::DoorbellRecognition:
|
||||
@@ -583,7 +601,7 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::Log
|
||||
entry["codeId"] = 256U*log.data[7]+log.data[6];
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(log.index > _lastRollingLog)
|
||||
{
|
||||
_lastRollingLog = log.index;
|
||||
@@ -654,7 +672,7 @@ void NukiNetworkOpener::publishConfig(const NukiOpener::Config &config)
|
||||
itoa(config.nukiId, uidString, 16);
|
||||
|
||||
JsonDocument json;
|
||||
|
||||
|
||||
memset(_nukiName, 0, sizeof(_nukiName));
|
||||
memcpy(_nukiName, config.name, sizeof(config.name));
|
||||
|
||||
@@ -1000,7 +1018,7 @@ void NukiNetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& en
|
||||
_network->removeTopic(codeTopic, "createdSec");
|
||||
_network->removeTopic(codeTopic, "lockCount");
|
||||
}
|
||||
|
||||
|
||||
for(int j=entries.size(); j<maxKeypadCodeCount; j++)
|
||||
{
|
||||
String codesTopic = _mqttPath;
|
||||
@@ -1084,7 +1102,7 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiOpener::lockactionToString(entry.lockAction, str);
|
||||
jsonEntry["lockAction"] = str;
|
||||
|
||||
|
||||
if(topicPerEntry)
|
||||
{
|
||||
String basePath = mqtt_topic_timecontrol;
|
||||
@@ -1093,7 +1111,6 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
||||
jsonEntry["index"] = index;
|
||||
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||
publishString(basePath.c_str(), _buffer, true);
|
||||
|
||||
String basePathPrefix = "~";
|
||||
basePathPrefix.concat(basePath);
|
||||
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||
@@ -1103,7 +1120,7 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
||||
std::string mqttDeviceName = std::string("timecontrol_") + std::to_string(index);
|
||||
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||
std::string displayName = std::string("Timecontrol - ") + std::to_string(entry.entryId);
|
||||
|
||||
|
||||
_network->publishHassTopic("switch",
|
||||
mqttDeviceName.c_str(),
|
||||
uidString,
|
||||
@@ -1124,13 +1141,13 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
||||
{ (char*)"stat_on", (char*)"1" },
|
||||
{ (char*)"stat_off", (char*)"0" }});
|
||||
}
|
||||
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
serializeJson(json, _buffer, _bufferSize);
|
||||
publishString(mqtt_topic_timecontrol_json, _buffer, true);
|
||||
|
||||
|
||||
for(int j=timeControlEntries.size(); j<maxTimeControlEntryCount; j++)
|
||||
{
|
||||
String entriesTopic = _mqttPath;
|
||||
@@ -1142,6 +1159,152 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
||||
}
|
||||
}
|
||||
|
||||
void NukiNetworkOpener::publishAuth(const std::list<NukiOpener::AuthorizationEntry>& authEntries, uint maxAuthEntryCount)
|
||||
{
|
||||
uint index = 0;
|
||||
char str[50];
|
||||
char uidString[20];
|
||||
itoa(_preferences->getUInt(preference_nuki_id_opener, 0), uidString, 16);
|
||||
String baseTopic = _preferences->getString(preference_mqtt_opener_path);
|
||||
JsonDocument json;
|
||||
|
||||
for(const auto& entry : authEntries)
|
||||
{
|
||||
auto jsonEntry = json.add<JsonVariant>();
|
||||
|
||||
jsonEntry["authId"] = entry.authId;
|
||||
jsonEntry["idType"] = entry.idType; //CONSIDER INT TO STRING
|
||||
jsonEntry["enabled"] = entry.enabled;
|
||||
jsonEntry["name"] = entry.name;
|
||||
jsonEntry["remoteAllowed"] = entry.remoteAllowed;
|
||||
char createdDT[20];
|
||||
sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.createdYear, entry.createdMonth, entry.createdDay, entry.createdHour, entry.createdMinute, entry.createdSecond);
|
||||
jsonEntry["dateCreated"] = createdDT;
|
||||
jsonEntry["lockCount"] = entry.lockCount;
|
||||
char lastActiveDT[20];
|
||||
sprintf(lastActiveDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.lastActYear, entry.lastActMonth, entry.lastActDay, entry.lastActHour, entry.lastActMinute, entry.lastActSecond);
|
||||
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.allowedFromMinute, entry.allowedFromSecond);
|
||||
jsonEntry["allowedFrom"] = allowedFromDT;
|
||||
char allowedUntilDT[20];
|
||||
sprintf(allowedUntilDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedUntilYear, entry.allowedUntilMonth, entry.allowedUntilDay, entry.allowedUntilHour, entry.allowedUntilMinute, entry.allowedUntilSecond);
|
||||
jsonEntry["allowedUntil"] = allowedUntilDT;
|
||||
|
||||
uint8_t allowedWeekdaysInt = entry.allowedWeekdays;
|
||||
JsonArray weekdays = jsonEntry["allowedWeekdays"].to<JsonArray>();
|
||||
|
||||
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;
|
||||
|
||||
if(_preferences->getBool(preference_auth_topic_per_entry, false))
|
||||
{
|
||||
String basePath = mqtt_topic_auth;
|
||||
basePath.concat("/entries/");
|
||||
basePath.concat(std::to_string(index).c_str());
|
||||
jsonEntry["index"] = index;
|
||||
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||
publishString(basePath.c_str(), _buffer, true);
|
||||
|
||||
String basePathPrefix = "~";
|
||||
basePathPrefix.concat(basePath);
|
||||
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||
|
||||
std::string baseCommand = std::string("{ \"action\": \"update\", \"authId\": \"") + std::to_string(entry.authId);
|
||||
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||
std::string mqttDeviceName = std::string("auth_") + std::to_string(index);
|
||||
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||
std::string displayName = std::string("Authorization - ") + std::to_string(entry.authId);
|
||||
|
||||
_network->publishHassTopic("switch",
|
||||
mqttDeviceName.c_str(),
|
||||
uidString,
|
||||
uidStringPostfix.c_str(),
|
||||
displayName.c_str(),
|
||||
_nukiName,
|
||||
baseTopic.c_str(),
|
||||
String("~") + basePath.c_str(),
|
||||
(char*)"Opener",
|
||||
"",
|
||||
"",
|
||||
"diagnostic",
|
||||
String("~") + mqtt_topic_auth_action,
|
||||
{ { (char*)"json_attr_t", (char*)basePathPrefixChr },
|
||||
{ (char*)"pl_on", (char*)enaCommand.c_str() },
|
||||
{ (char*)"pl_off", (char*)disCommand.c_str() },
|
||||
{ (char*)"val_tpl", (char*)"{{value_json.enabled}}" },
|
||||
{ (char*)"stat_on", (char*)"1" },
|
||||
{ (char*)"stat_off", (char*)"0" }});
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
serializeJson(json, _buffer, _bufferSize);
|
||||
publishString(mqtt_topic_auth_json, _buffer, true);
|
||||
|
||||
for(int j=authEntries.size(); j<maxAuthEntryCount; j++)
|
||||
{
|
||||
String entriesTopic = _mqttPath;
|
||||
entriesTopic.concat(mqtt_topic_auth_entries);
|
||||
entriesTopic.concat("/");
|
||||
_network->removeTopic(entriesTopic, (char*)std::to_string(j).c_str());
|
||||
std::string mqttDeviceName = std::string("auth_") + std::to_string(j);
|
||||
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||
}
|
||||
}
|
||||
|
||||
void NukiNetworkOpener::publishConfigCommandResult(const char* result)
|
||||
{
|
||||
publishString(mqtt_topic_config_action_command_result, result, true);
|
||||
@@ -1163,6 +1326,11 @@ void NukiNetworkOpener::publishTimeControlCommandResult(const char* result)
|
||||
publishString(mqtt_topic_timecontrol_command_result, result, true);
|
||||
}
|
||||
|
||||
void NukiNetworkOpener::publishAuthCommandResult(const char* result)
|
||||
{
|
||||
publishString(mqtt_topic_auth_command_result, result, true);
|
||||
}
|
||||
|
||||
void NukiNetworkOpener::publishStatusUpdated(const bool statusUpdated)
|
||||
{
|
||||
publishBool(mqtt_topic_lock_status_updated, statusUpdated, true);
|
||||
@@ -1194,6 +1362,11 @@ void NukiNetworkOpener::setTimeControlCommandReceivedCallback(void (*timeControl
|
||||
_timeControlCommandReceivedReceivedCallback = timeControlCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NukiNetworkOpener::setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char *))
|
||||
{
|
||||
_authCommandReceivedReceivedCallback = authCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NukiNetworkOpener::publishFloat(const char *topic, const float value, bool retain, const uint8_t precision)
|
||||
{
|
||||
_network->publishFloat(_mqttPath, topic, value, retain, precision);
|
||||
|
||||
@@ -33,17 +33,20 @@ public:
|
||||
void removeHASSConfig(char* uidString);
|
||||
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
||||
void publishTimeControl(const std::list<NukiOpener::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount);
|
||||
void publishAuth(const std::list<NukiLock::AuthorizationEntry>& authEntries, uint maxAuthEntryCount);
|
||||
void publishStatusUpdated(const bool statusUpdated);
|
||||
void publishConfigCommandResult(const char* result);
|
||||
void publishKeypadCommandResult(const char* result);
|
||||
void publishKeypadJsonCommandResult(const char* result);
|
||||
void publishTimeControlCommandResult(const char* result);
|
||||
void publishAuthCommandResult(const char* result);
|
||||
|
||||
void setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char* value));
|
||||
void setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(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 setTimeControlCommandReceivedCallback(void (*timeControlCommandReceivedReceivedCallback)(const char* value));
|
||||
void setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char* value));
|
||||
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
||||
|
||||
bool reconnected();
|
||||
@@ -105,4 +108,5 @@ private:
|
||||
void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr;
|
||||
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
void (*_timeControlCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
void (*_authCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
};
|
||||
|
||||
@@ -33,6 +33,8 @@ NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId
|
||||
network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback);
|
||||
if(_preferences->getBool(preference_disable_non_json, false)) network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback);
|
||||
network->setKeypadJsonCommandReceivedCallback(nukiOpenerInst->onKeypadJsonCommandReceivedCallback);
|
||||
network->setTimeControlCommandReceivedCallback(nukiOpenerInst->onTimeControlCommandReceivedCallback);
|
||||
network->setAuthCommandReceivedCallback(nukiOpenerInst->onAuthCommandReceivedCallback);
|
||||
|
||||
_gpio->addCallback(NukiOpenerWrapper::gpioActionCallback);
|
||||
}
|
||||
@@ -72,6 +74,7 @@ void NukiOpenerWrapper::initialize()
|
||||
_publishAuthData = _preferences->getBool(preference_publish_authdata);
|
||||
_maxKeypadCodeCount = _preferences->getUInt(preference_opener_max_keypad_code_count);
|
||||
_maxTimeControlEntryCount = _preferences->getUInt(preference_opener_max_timecontrol_entry_count);
|
||||
_maxAuthEntryCount = _preferences->getUInt(preference_opener_max_auth_entry_count);
|
||||
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
||||
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
|
||||
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200);
|
||||
@@ -219,6 +222,11 @@ void NukiOpenerWrapper::update()
|
||||
_waitTimeControlUpdateTs = 0;
|
||||
updateTimeControl(true);
|
||||
}
|
||||
if(_waitAuthUpdateTs != 0 && ts > _waitAuthUpdateTs)
|
||||
{
|
||||
_waitAuthUpdateTs = 0;
|
||||
updateAuth(true);
|
||||
}
|
||||
if(_hassEnabled && _nukiConfigValid && _nukiAdvancedConfigValid && _network->reconnected())
|
||||
{
|
||||
setupHASS();
|
||||
@@ -492,6 +500,7 @@ void NukiOpenerWrapper::updateConfig()
|
||||
if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishConfig(_nukiConfig);
|
||||
_retryConfigCount = 0;
|
||||
if(_preferences->getBool(preference_timecontrol_info_enabled, false)) updateTimeControl(false);
|
||||
if(_preferences->getBool(preference_auth_info_enabled)) updateAuth(false);
|
||||
|
||||
const int pinStatus = _preferences->getInt(preference_opener_pin_status, 4);
|
||||
|
||||
@@ -729,7 +738,7 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
|
||||
|
||||
while(_retryCount < _nrOfRetries + 1)
|
||||
{
|
||||
Log->print(F("Querying opener time control: "));
|
||||
Log->print(F("Querying opener timecontrol: "));
|
||||
result = _nukiOpener.retrieveTimeControlEntries();
|
||||
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
@@ -749,7 +758,7 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
|
||||
std::list<NukiOpener::TimeControlEntry> timeControlEntries;
|
||||
_nukiOpener.getTimeControlEntries(&timeControlEntries);
|
||||
|
||||
Log->print(F("Opener time control entries: "));
|
||||
Log->print(F("Opener timecontrol entries: "));
|
||||
Log->println(timeControlEntries.size());
|
||||
|
||||
timeControlEntries.sort([](const NukiOpener::TimeControlEntry& a, const NukiOpener::TimeControlEntry& b) { return a.entryId < b.entryId; });
|
||||
@@ -779,6 +788,67 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::updateAuth(bool retrieved)
|
||||
{
|
||||
if(!_preferences->getBool(preference_auth_info_enabled)) return;
|
||||
|
||||
if(!retrieved)
|
||||
{
|
||||
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||
_retryCount = 0;
|
||||
|
||||
while(_retryCount < _nrOfRetries)
|
||||
{
|
||||
Log->print(F("Querying opener authorization: "));
|
||||
result = _nukiOpener.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||
delay(250);
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
printCommandResult(result);
|
||||
if(result == Nuki::CmdResult::Success)
|
||||
{
|
||||
_waitAuthUpdateTs = millis() + 5000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::list<NukiOpener::AuthorizationEntry> authEntries;
|
||||
_nukiOpener.getAuthorizationEntries(&authEntries);
|
||||
|
||||
Log->print(F("Opener authorization entries: "));
|
||||
Log->println(authEntries.size());
|
||||
|
||||
authEntries.sort([](const NukiOpener::AuthorizationEntry& a, const NukiOpener::AuthorizationEntry& b) { return a.authId < b.authId; });
|
||||
|
||||
if(authEntries.size() > _preferences->getInt(preference_auth_max_entries, MAX_AUTH))
|
||||
{
|
||||
authEntries.resize(_preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||
}
|
||||
|
||||
uint authCount = authEntries.size();
|
||||
if(authCount > _maxAuthEntryCount)
|
||||
{
|
||||
_maxAuthEntryCount = authCount;
|
||||
_preferences->putUInt(preference_opener_max_auth_entry_count, _maxAuthEntryCount);
|
||||
}
|
||||
|
||||
_network->publishAuth(authEntries, _maxAuthEntryCount);
|
||||
|
||||
_authIds.clear();
|
||||
_authIds.reserve(authEntries.size());
|
||||
for(const auto& entry : authEntries)
|
||||
{
|
||||
_authIds.push_back(entry.authId);
|
||||
}
|
||||
}
|
||||
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::postponeBleWatchdog()
|
||||
{
|
||||
_disableBleWatchdogTs = (esp_timer_get_time() / 1000) + 15000;
|
||||
@@ -1477,6 +1547,16 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceivedCallback(const char *value)
|
||||
nukiOpenerInst->onKeypadJsonCommandReceived(value);
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::onTimeControlCommandReceivedCallback(const char *value)
|
||||
{
|
||||
nukiOpenerInst->onTimeControlCommandReceived(value);
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::onAuthCommandReceivedCallback(const char *value)
|
||||
{
|
||||
nukiOpenerInst->onAuthCommandReceived(value);
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
||||
{
|
||||
switch(action)
|
||||
@@ -2161,7 +2241,7 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
||||
if(idExists)
|
||||
{
|
||||
result = _nukiOpener.removeTimeControlEntry(entryId);
|
||||
Log->print(F("Delete time control: "));
|
||||
Log->print(F("Delete timecontrol: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
else
|
||||
@@ -2219,7 +2299,7 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
||||
|
||||
entry.lockAction = timeControlLockAction;
|
||||
result = _nukiOpener.addTimeControlEntry(entry);
|
||||
Log->print(F("Add time control: "));
|
||||
Log->print(F("Add timecontrol: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
else if (strcmp(action, "update") == 0)
|
||||
@@ -2257,13 +2337,13 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
||||
|
||||
if(!foundExisting)
|
||||
{
|
||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingKeypadEntry");
|
||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingTimeControlEntry");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingKeypadEntry");
|
||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingTimeControlEntry");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2281,7 +2361,7 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
||||
|
||||
entry.lockAction = timeControlLockAction;
|
||||
result = _nukiOpener.updateTimeControlEntry(entry);
|
||||
Log->print(F("Update time control: "));
|
||||
Log->print(F("Update timecontrol: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
}
|
||||
@@ -2314,6 +2394,464 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
||||
}
|
||||
}
|
||||
|
||||
void NukiOpenerWrapper::onAuthCommandReceived(const char *value)
|
||||
{
|
||||
if(!_nukiConfigValid)
|
||||
{
|
||||
_network->publishAuthCommandResult("configNotReady");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isPinValid())
|
||||
{
|
||||
_network->publishAuthCommandResult("noValidPinSet");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_preferences->getBool(preference_auth_control_enabled))
|
||||
{
|
||||
_network->publishAuthCommandResult("keypadControlDisabled");
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument json;
|
||||
DeserializationError jsonError = deserializeJson(json, value);
|
||||
|
||||
if(jsonError)
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidJson");
|
||||
return;
|
||||
}
|
||||
|
||||
char oldName[33];
|
||||
const char *action = json["action"].as<const char*>();
|
||||
uint16_t authId = json["authId"].as<unsigned int>();
|
||||
//uint8_t idType = json["idType"].as<unsigned int>();
|
||||
//unsigned char secretKeyK[32] = {0x00};
|
||||
uint8_t remoteAllowed;
|
||||
uint8_t enabled;
|
||||
uint8_t timeLimited;
|
||||
String name;
|
||||
//String sharedKey;
|
||||
String allowedFrom;
|
||||
String allowedUntil;
|
||||
String allowedWeekdays;
|
||||
String allowedFromTime;
|
||||
String allowedUntilTime;
|
||||
|
||||
if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as<unsigned int>();
|
||||
else remoteAllowed = 2;
|
||||
|
||||
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>();
|
||||
else enabled = 2;
|
||||
|
||||
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>();
|
||||
else timeLimited = 2;
|
||||
|
||||
if(json.containsKey("name")) name = json["name"].as<String>();
|
||||
//if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as<String>();
|
||||
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>();
|
||||
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>();
|
||||
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>();
|
||||
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>();
|
||||
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>();
|
||||
|
||||
if(action)
|
||||
{
|
||||
bool idExists = false;
|
||||
|
||||
if(authId)
|
||||
{
|
||||
idExists = std::find(_authIds.begin(), _authIds.end(), authId) != _authIds.end();
|
||||
}
|
||||
|
||||
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||
_retryCount = 0;
|
||||
|
||||
while(_retryCount < _nrOfRetries)
|
||||
{
|
||||
if(strcmp(action, "delete") == 0) {
|
||||
if(idExists)
|
||||
{
|
||||
result = _nukiOpener.deleteAuthorizationEntry(authId);
|
||||
Log->print(F("Delete authorization: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
|
||||
{
|
||||
if(name.length() < 1)
|
||||
{
|
||||
if (strcmp(action, "update") != 0)
|
||||
{
|
||||
_network->publishAuthCommandResult("noNameSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(sharedKey.length() != 64)
|
||||
{
|
||||
if (strcmp(action, "update") != 0)
|
||||
{
|
||||
_network->publishAuthCommandResult("noSharedKeySet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i=0; i<sharedKey.length();i+=2) secretKeyK[(i/2)] = std::stoi(sharedKey.substring(i, i+2).c_str(), nullptr, 16);
|
||||
}
|
||||
*/
|
||||
|
||||
unsigned int allowedFromAr[6];
|
||||
unsigned int allowedUntilAr[6];
|
||||
unsigned int allowedFromTimeAr[2];
|
||||
unsigned int allowedUntilTimeAr[2];
|
||||
uint8_t allowedWeekdaysInt = 0;
|
||||
|
||||
if(timeLimited == 1)
|
||||
{
|
||||
if(allowedFrom.length() > 0)
|
||||
{
|
||||
if(allowedFrom.length() == 19)
|
||||
{
|
||||
allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt();
|
||||
allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt();
|
||||
allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt();
|
||||
allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt();
|
||||
allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt();
|
||||
allowedFromAr[5] = (uint8_t)allowedFrom.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->publishAuthCommandResult("invalidAllowedFrom");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedUntil.length() > 0)
|
||||
{
|
||||
if(allowedUntil.length() > 0 == 19)
|
||||
{
|
||||
allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt();
|
||||
allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt();
|
||||
allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt();
|
||||
allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt();
|
||||
allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt();
|
||||
allowedUntilAr[5] = (uint8_t)allowedUntil.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->publishAuthCommandResult("invalidAllowedUntil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedFromTime.length() > 0)
|
||||
{
|
||||
if(allowedFromTime.length() == 5)
|
||||
{
|
||||
allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt();
|
||||
allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt();
|
||||
|
||||
if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedUntilTime.length() > 0)
|
||||
{
|
||||
if(allowedUntilTime.length() == 5)
|
||||
{
|
||||
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt();
|
||||
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt();
|
||||
|
||||
if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("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)
|
||||
{
|
||||
_network->publishAuthCommandResult("addActionNotSupported");
|
||||
return;
|
||||
|
||||
NukiOpener::NewAuthorizationEntry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
size_t nameLen = name.length();
|
||||
memcpy(&entry.name, name.c_str(), nameLen > 32 ? 32 : nameLen);
|
||||
/*
|
||||
memcpy(&entry.sharedKey, secretKeyK, 32);
|
||||
|
||||
if(idType != 1)
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidIdType");
|
||||
return;
|
||||
}
|
||||
|
||||
entry.idType = idType;
|
||||
*/
|
||||
entry.remoteAllowed = remoteAllowed == 1 ? 1 : 0;
|
||||
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||
|
||||
if(allowedFrom.length() > 0)
|
||||
{
|
||||
entry.allowedFromYear = allowedFromAr[0];
|
||||
entry.allowedFromMonth = allowedFromAr[1];
|
||||
entry.allowedFromDay = allowedFromAr[2];
|
||||
entry.allowedFromHour = allowedFromAr[3];
|
||||
entry.allowedFromMinute = allowedFromAr[4];
|
||||
entry.allowedFromSecond = allowedFromAr[5];
|
||||
}
|
||||
|
||||
if(allowedUntil.length() > 0)
|
||||
{
|
||||
entry.allowedUntilYear = allowedUntilAr[0];
|
||||
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||
entry.allowedUntilDay = allowedUntilAr[2];
|
||||
entry.allowedUntilHour = allowedUntilAr[3];
|
||||
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||
}
|
||||
|
||||
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||
|
||||
if(allowedFromTime.length() > 0)
|
||||
{
|
||||
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||
}
|
||||
|
||||
if(allowedUntilTime.length() > 0)
|
||||
{
|
||||
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||
}
|
||||
|
||||
result = _nukiOpener.addAuthorizationEntry(entry);
|
||||
Log->print(F("Add authorization: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
else if (strcmp(action, "update") == 0)
|
||||
{
|
||||
if(!authId)
|
||||
{
|
||||
_network->publishAuthCommandResult("noAuthIdSet");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!idExists)
|
||||
{
|
||||
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||
return;
|
||||
}
|
||||
|
||||
Nuki::CmdResult resultAuth = _nukiOpener.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||
bool foundExisting = false;
|
||||
|
||||
if(resultAuth == Nuki::CmdResult::Success)
|
||||
{
|
||||
delay(250);
|
||||
std::list<NukiOpener::AuthorizationEntry> entries;
|
||||
_nukiOpener.getAuthorizationEntries(&entries);
|
||||
|
||||
for(const auto& entry : entries)
|
||||
{
|
||||
if (authId != entry.authId) continue;
|
||||
else foundExisting = true;
|
||||
|
||||
if(name.length() < 1)
|
||||
{
|
||||
memset(oldName, 0, sizeof(oldName));
|
||||
memcpy(oldName, entry.name, sizeof(entry.name));
|
||||
}
|
||||
if(remoteAllowed == 2) remoteAllowed = entry.remoteAllowed;
|
||||
if(enabled == 2) enabled = entry.enabled;
|
||||
if(timeLimited == 2) timeLimited = entry.timeLimited;
|
||||
if(allowedFrom.length() < 1)
|
||||
{
|
||||
allowedFrom = "old";
|
||||
allowedFromAr[0] = entry.allowedFromYear;
|
||||
allowedFromAr[1] = entry.allowedFromMonth;
|
||||
allowedFromAr[2] = entry.allowedFromDay;
|
||||
allowedFromAr[3] = entry.allowedFromHour;
|
||||
allowedFromAr[4] = entry.allowedFromMinute;
|
||||
allowedFromAr[5] = entry.allowedFromSecond;
|
||||
}
|
||||
if(allowedUntil.length() < 1)
|
||||
{
|
||||
allowedUntil = "old";
|
||||
allowedUntilAr[0] = entry.allowedUntilYear;
|
||||
allowedUntilAr[1] = entry.allowedUntilMonth;
|
||||
allowedUntilAr[2] = entry.allowedUntilDay;
|
||||
allowedUntilAr[3] = entry.allowedUntilHour;
|
||||
allowedUntilAr[4] = entry.allowedUntilMinute;
|
||||
allowedUntilAr[5] = entry.allowedUntilSecond;
|
||||
}
|
||||
if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays;
|
||||
if(allowedFromTime.length() < 1)
|
||||
{
|
||||
allowedFromTime = "old";
|
||||
allowedFromTimeAr[0] = entry.allowedFromTimeHour;
|
||||
allowedFromTimeAr[1] = entry.allowedFromTimeMin;
|
||||
}
|
||||
|
||||
if(allowedUntilTime.length() < 1)
|
||||
{
|
||||
allowedUntilTime = "old";
|
||||
allowedUntilTimeAr[0] = entry.allowedUntilTimeHour;
|
||||
allowedUntilTimeAr[1] = entry.allowedUntilTimeMin;
|
||||
}
|
||||
}
|
||||
|
||||
if(!foundExisting)
|
||||
{
|
||||
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||
return;
|
||||
}
|
||||
|
||||
NukiOpener::UpdatedAuthorizationEntry entry;
|
||||
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.authId = authId;
|
||||
|
||||
if(name.length() < 1)
|
||||
{
|
||||
size_t nameLen = strlen(oldName);
|
||||
memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t nameLen = name.length();
|
||||
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
|
||||
}
|
||||
entry.remoteAllowed = remoteAllowed;
|
||||
entry.enabled = enabled;
|
||||
entry.timeLimited = timeLimited;
|
||||
|
||||
if(enabled == 1)
|
||||
{
|
||||
if(timeLimited == 1)
|
||||
{
|
||||
if(allowedFrom.length() > 0)
|
||||
{
|
||||
entry.allowedFromYear = allowedFromAr[0];
|
||||
entry.allowedFromMonth = allowedFromAr[1];
|
||||
entry.allowedFromDay = allowedFromAr[2];
|
||||
entry.allowedFromHour = allowedFromAr[3];
|
||||
entry.allowedFromMinute = allowedFromAr[4];
|
||||
entry.allowedFromSecond = allowedFromAr[5];
|
||||
}
|
||||
|
||||
if(allowedUntil.length() > 0)
|
||||
{
|
||||
entry.allowedUntilYear = allowedUntilAr[0];
|
||||
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||
entry.allowedUntilDay = allowedUntilAr[2];
|
||||
entry.allowedUntilHour = allowedUntilAr[3];
|
||||
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||
}
|
||||
|
||||
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||
|
||||
if(allowedFromTime.length() > 0)
|
||||
{
|
||||
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||
}
|
||||
|
||||
if(allowedUntilTime.length() > 0)
|
||||
{
|
||||
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = _nukiOpener.updateAuthorizationEntry(entry);
|
||||
Log->print(F("Update authorization: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAction");
|
||||
return;
|
||||
}
|
||||
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
updateAuth(false);
|
||||
|
||||
if((int)result != -1)
|
||||
{
|
||||
char resultStr[15];
|
||||
memset(&resultStr, 0, sizeof(resultStr));
|
||||
NukiOpener::cmdResultToString(result, resultStr);
|
||||
_network->publishAuthCommandResult(resultStr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("noActionSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const NukiOpener::OpenerState &NukiOpenerWrapper::keyTurnerState()
|
||||
{
|
||||
return _keyTurnerState;
|
||||
|
||||
@@ -53,12 +53,14 @@ private:
|
||||
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 onTimeControlCommandReceivedCallback(const char* value);
|
||||
static void onAuthCommandReceivedCallback(const char* value);
|
||||
static void gpioActionCallback(const GpioAction& action, const int& pin);
|
||||
|
||||
void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||
void onConfigUpdateReceived(const char* value);
|
||||
void onKeypadJsonCommandReceived(const char* value);
|
||||
void onTimeControlCommandReceived(const char* value);
|
||||
void onAuthCommandReceived(const char* value);
|
||||
|
||||
void updateKeyTurnerState();
|
||||
void updateBatteryState();
|
||||
@@ -66,6 +68,7 @@ private:
|
||||
void updateAuthData(bool retrieved);
|
||||
void updateKeypad(bool retrieved);
|
||||
void updateTimeControl(bool retrieved);
|
||||
void updateAuth(bool retrieved);
|
||||
void postponeBleWatchdog();
|
||||
|
||||
void updateGpioOutputs();
|
||||
@@ -109,6 +112,7 @@ private:
|
||||
int64_t _nextRetryTs = 0;
|
||||
std::vector<uint16_t> _keypadCodeIds;
|
||||
std::vector<uint8_t> _timeControlIds;
|
||||
std::vector<uint32_t> _authIds;
|
||||
|
||||
NukiOpener::OpenerState _lastKeyTurnerState;
|
||||
NukiOpener::OpenerState _keyTurnerState;
|
||||
@@ -129,6 +133,7 @@ private:
|
||||
bool _keypadEnabled = false;
|
||||
uint _maxKeypadCodeCount = 0;
|
||||
uint _maxTimeControlEntryCount = 0;
|
||||
uint _maxAuthEntryCount = 0;
|
||||
int _rssiPublishInterval = 0;
|
||||
int64_t _nextLockStateUpdateTs = 0;
|
||||
int64_t _nextBatteryReportTs = 0;
|
||||
@@ -136,6 +141,7 @@ private:
|
||||
int64_t _waitAuthLogUpdateTs = 0;
|
||||
int64_t _waitKeypadUpdateTs = 0;
|
||||
int64_t _waitTimeControlUpdateTs = 0;
|
||||
int64_t _waitAuthUpdateTs = 0;
|
||||
int64_t _nextKeypadUpdateTs = 0;
|
||||
int64_t _nextPairTs = 0;
|
||||
int64_t _nextRssiTs = 0;
|
||||
|
||||
@@ -37,6 +37,7 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId,
|
||||
if(_disableNonJSON) network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback);
|
||||
network->setKeypadJsonCommandReceivedCallback(nukiInst->onKeypadJsonCommandReceivedCallback);
|
||||
network->setTimeControlCommandReceivedCallback(nukiInst->onTimeControlCommandReceivedCallback);
|
||||
network->setAuthCommandReceivedCallback(nukiInst->onAuthCommandReceivedCallback);
|
||||
|
||||
_gpio->addCallback(NukiWrapper::gpioActionCallback);
|
||||
}
|
||||
@@ -76,6 +77,7 @@ void NukiWrapper::initialize(const bool& firstStart)
|
||||
_publishAuthData = _preferences->getBool(preference_publish_authdata);
|
||||
_maxKeypadCodeCount = _preferences->getUInt(preference_lock_max_keypad_code_count);
|
||||
_maxTimeControlEntryCount = _preferences->getUInt(preference_lock_max_timecontrol_entry_count);
|
||||
_maxAuthEntryCount = _preferences->getUInt(preference_lock_max_auth_entry_count);
|
||||
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
||||
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
|
||||
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200);
|
||||
@@ -262,7 +264,6 @@ void NukiWrapper::update()
|
||||
cmdResult = _nukiLock.lockAction(_nextLockAction, 0, 0);
|
||||
char resultStr[15] = {0};
|
||||
NukiLock::cmdResultToString(cmdResult, resultStr);
|
||||
|
||||
_network->publishCommandResult(resultStr);
|
||||
|
||||
Log->print(F("Lock action result: "));
|
||||
@@ -344,6 +345,11 @@ void NukiWrapper::update()
|
||||
_waitTimeControlUpdateTs = 0;
|
||||
updateTimeControl(true);
|
||||
}
|
||||
if(_waitAuthUpdateTs != 0 && ts > _waitAuthUpdateTs)
|
||||
{
|
||||
_waitAuthUpdateTs = 0;
|
||||
updateAuth(true);
|
||||
}
|
||||
if(_hassEnabled && _nukiConfigValid && _nukiAdvancedConfigValid && _network->reconnected())
|
||||
{
|
||||
setupHASS();
|
||||
@@ -446,7 +452,6 @@ void NukiWrapper::updateKeyTurnerState()
|
||||
Log->print(_retryCount + 1);
|
||||
Log->print("): ");
|
||||
result =_nukiLock.requestKeyTurnerState(&_keyTurnerState);
|
||||
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
@@ -566,6 +571,7 @@ void NukiWrapper::updateConfig()
|
||||
_hardwareVersion = std::to_string(_nukiConfig.hardwareRevision[0]) + "." + std::to_string(_nukiConfig.hardwareRevision[1]);
|
||||
if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishConfig(_nukiConfig);
|
||||
if(_preferences->getBool(preference_timecontrol_info_enabled)) updateTimeControl(false);
|
||||
if(_preferences->getBool(preference_auth_info_enabled)) updateAuth(false);
|
||||
|
||||
const int pinStatus = _preferences->getInt(preference_lock_pin_status, 4);
|
||||
|
||||
@@ -577,7 +583,6 @@ void NukiWrapper::updateConfig()
|
||||
while(_retryCount < _nrOfRetries + 1)
|
||||
{
|
||||
result = _nukiLock.verifySecurityPin();
|
||||
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
@@ -665,7 +670,6 @@ void NukiWrapper::updateAuthData(bool retrieved)
|
||||
{
|
||||
Log->print(F("Retrieve log entries: "));
|
||||
result = _nukiLock.retrieveLogEntries(0, _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 1, false);
|
||||
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
@@ -737,7 +741,6 @@ void NukiWrapper::updateKeypad(bool retrieved)
|
||||
{
|
||||
Log->print(F("Querying lock keypad: "));
|
||||
result = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
|
||||
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
@@ -802,9 +805,8 @@ void NukiWrapper::updateTimeControl(bool retrieved)
|
||||
|
||||
while(_retryCount < _nrOfRetries + 1)
|
||||
{
|
||||
Log->print(F("Querying lock time control: "));
|
||||
Log->print(F("Querying lock timecontrol: "));
|
||||
result = _nukiLock.retrieveTimeControlEntries();
|
||||
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
@@ -822,7 +824,7 @@ void NukiWrapper::updateTimeControl(bool retrieved)
|
||||
std::list<NukiLock::TimeControlEntry> timeControlEntries;
|
||||
_nukiLock.getTimeControlEntries(&timeControlEntries);
|
||||
|
||||
Log->print(F("Lock time control entries: "));
|
||||
Log->print(F("Lock timecontrol entries: "));
|
||||
Log->println(timeControlEntries.size());
|
||||
|
||||
timeControlEntries.sort([](const NukiLock::TimeControlEntry& a, const NukiLock::TimeControlEntry& b) { return a.entryId < b.entryId; });
|
||||
@@ -852,6 +854,67 @@ void NukiWrapper::updateTimeControl(bool retrieved)
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiWrapper::updateAuth(bool retrieved)
|
||||
{
|
||||
if(!_preferences->getBool(preference_auth_info_enabled)) return;
|
||||
|
||||
if(!retrieved)
|
||||
{
|
||||
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||
_retryCount = 0;
|
||||
|
||||
while(_retryCount < _nrOfRetries)
|
||||
{
|
||||
Log->print(F("Querying lock authorization: "));
|
||||
result = _nukiLock.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||
delay(250);
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
printCommandResult(result);
|
||||
if(result == Nuki::CmdResult::Success)
|
||||
{
|
||||
_waitAuthUpdateTs = millis() + 5000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::list<NukiLock::AuthorizationEntry> authEntries;
|
||||
_nukiLock.getAuthorizationEntries(&authEntries);
|
||||
|
||||
Log->print(F("Lock authorization entries: "));
|
||||
Log->println(authEntries.size());
|
||||
|
||||
authEntries.sort([](const NukiLock::AuthorizationEntry& a, const NukiLock::AuthorizationEntry& b) { return a.authId < b.authId; });
|
||||
|
||||
if(authEntries.size() > _preferences->getInt(preference_auth_max_entries, MAX_AUTH))
|
||||
{
|
||||
authEntries.resize(_preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||
}
|
||||
|
||||
uint authCount = authEntries.size();
|
||||
if(authCount > _maxAuthEntryCount)
|
||||
{
|
||||
_maxAuthEntryCount = authCount;
|
||||
_preferences->putUInt(preference_lock_max_auth_entry_count, _maxAuthEntryCount);
|
||||
}
|
||||
|
||||
_network->publishAuth(authEntries, _maxAuthEntryCount);
|
||||
|
||||
_authIds.clear();
|
||||
_authIds.reserve(authEntries.size());
|
||||
for(const auto& entry : authEntries)
|
||||
{
|
||||
_authIds.push_back(entry.authId);
|
||||
}
|
||||
}
|
||||
|
||||
postponeBleWatchdog();
|
||||
}
|
||||
|
||||
void NukiWrapper::postponeBleWatchdog()
|
||||
{
|
||||
_disableBleWatchdogTs = (esp_timer_get_time() / 1000) + 15000;
|
||||
@@ -1725,6 +1788,11 @@ void NukiWrapper::onTimeControlCommandReceivedCallback(const char *value)
|
||||
nukiInst->onTimeControlCommandReceived(value);
|
||||
}
|
||||
|
||||
void NukiWrapper::onAuthCommandReceivedCallback(const char *value)
|
||||
{
|
||||
nukiInst->onAuthCommandReceived(value);
|
||||
}
|
||||
|
||||
void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
||||
{
|
||||
switch(action)
|
||||
@@ -2436,7 +2504,7 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
||||
if(idExists)
|
||||
{
|
||||
result = _nukiLock.removeTimeControlEntry(entryId);
|
||||
Log->print(F("Delete time control: "));
|
||||
Log->print(F("Delete timecontrol: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
else
|
||||
@@ -2495,7 +2563,7 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
||||
entry.lockAction = timeControlLockAction;
|
||||
|
||||
result = _nukiLock.addTimeControlEntry(entry);
|
||||
Log->print(F("Add time control: "));
|
||||
Log->print(F("Add timecontrol: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
else if (strcmp(action, "update") == 0)
|
||||
@@ -2533,13 +2601,13 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
||||
|
||||
if(!foundExisting)
|
||||
{
|
||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingKeypadEntry");
|
||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingTimeControlEntry");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingKeypadEntry");
|
||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingTimeControlEntry");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2558,7 +2626,7 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
||||
entry.lockAction = timeControlLockAction;
|
||||
|
||||
result = _nukiLock.updateTimeControlEntry(entry);
|
||||
Log->print(F("Update time control: "));
|
||||
Log->print(F("Update timecontrol: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
}
|
||||
@@ -2591,6 +2659,467 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
||||
}
|
||||
}
|
||||
|
||||
void NukiWrapper::onAuthCommandReceived(const char *value)
|
||||
{
|
||||
if(!_nukiConfigValid)
|
||||
{
|
||||
_network->publishAuthCommandResult("configNotReady");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isPinValid())
|
||||
{
|
||||
_network->publishAuthCommandResult("noValidPinSet");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_preferences->getBool(preference_auth_control_enabled))
|
||||
{
|
||||
_network->publishAuthCommandResult("keypadControlDisabled");
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument json;
|
||||
DeserializationError jsonError = deserializeJson(json, value);
|
||||
|
||||
if(jsonError)
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidJson");
|
||||
return;
|
||||
}
|
||||
|
||||
char oldName[33];
|
||||
const char *action = json["action"].as<const char*>();
|
||||
uint16_t authId = json["authId"].as<unsigned int>();
|
||||
//uint8_t idType = json["idType"].as<unsigned int>();
|
||||
//unsigned char secretKeyK[32] = {0x00};
|
||||
uint8_t remoteAllowed;
|
||||
uint8_t enabled;
|
||||
uint8_t timeLimited;
|
||||
String name;
|
||||
//String sharedKey;
|
||||
String allowedFrom;
|
||||
String allowedUntil;
|
||||
String allowedWeekdays;
|
||||
String allowedFromTime;
|
||||
String allowedUntilTime;
|
||||
|
||||
if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as<unsigned int>();
|
||||
else remoteAllowed = 2;
|
||||
|
||||
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>();
|
||||
else enabled = 2;
|
||||
|
||||
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>();
|
||||
else timeLimited = 2;
|
||||
|
||||
if(json.containsKey("name")) name = json["name"].as<String>();
|
||||
//if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as<String>();
|
||||
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>();
|
||||
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>();
|
||||
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>();
|
||||
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>();
|
||||
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>();
|
||||
|
||||
if(action)
|
||||
{
|
||||
bool idExists = false;
|
||||
|
||||
if(authId)
|
||||
{
|
||||
idExists = std::find(_authIds.begin(), _authIds.end(), authId) != _authIds.end();
|
||||
}
|
||||
|
||||
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||
_retryCount = 0;
|
||||
|
||||
while(_retryCount < _nrOfRetries)
|
||||
{
|
||||
if(strcmp(action, "delete") == 0) {
|
||||
if(idExists)
|
||||
{
|
||||
result = _nukiLock.deleteAuthorizationEntry(authId);
|
||||
delay(250);
|
||||
Log->print(F("Delete authorization: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
|
||||
{
|
||||
if(name.length() < 1)
|
||||
{
|
||||
if (strcmp(action, "update") != 0)
|
||||
{
|
||||
_network->publishAuthCommandResult("noNameSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(sharedKey.length() != 64)
|
||||
{
|
||||
if (strcmp(action, "update") != 0)
|
||||
{
|
||||
_network->publishAuthCommandResult("noSharedKeySet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i=0; i<sharedKey.length();i+=2) secretKeyK[(i/2)] = std::stoi(sharedKey.substring(i, i+2).c_str(), nullptr, 16);
|
||||
}
|
||||
*/
|
||||
|
||||
unsigned int allowedFromAr[6];
|
||||
unsigned int allowedUntilAr[6];
|
||||
unsigned int allowedFromTimeAr[2];
|
||||
unsigned int allowedUntilTimeAr[2];
|
||||
uint8_t allowedWeekdaysInt = 0;
|
||||
|
||||
if(timeLimited == 1)
|
||||
{
|
||||
if(allowedFrom.length() > 0)
|
||||
{
|
||||
if(allowedFrom.length() == 19)
|
||||
{
|
||||
allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt();
|
||||
allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt();
|
||||
allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt();
|
||||
allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt();
|
||||
allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt();
|
||||
allowedFromAr[5] = (uint8_t)allowedFrom.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->publishAuthCommandResult("invalidAllowedFrom");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedUntil.length() > 0)
|
||||
{
|
||||
if(allowedUntil.length() > 0 == 19)
|
||||
{
|
||||
allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt();
|
||||
allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt();
|
||||
allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt();
|
||||
allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt();
|
||||
allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt();
|
||||
allowedUntilAr[5] = (uint8_t)allowedUntil.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->publishAuthCommandResult("invalidAllowedUntil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedFromTime.length() > 0)
|
||||
{
|
||||
if(allowedFromTime.length() == 5)
|
||||
{
|
||||
allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt();
|
||||
allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt();
|
||||
|
||||
if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedUntilTime.length() > 0)
|
||||
{
|
||||
if(allowedUntilTime.length() == 5)
|
||||
{
|
||||
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt();
|
||||
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt();
|
||||
|
||||
if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("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)
|
||||
{
|
||||
_network->publishAuthCommandResult("addActionNotSupported");
|
||||
return;
|
||||
|
||||
NukiLock::NewAuthorizationEntry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
size_t nameLen = name.length();
|
||||
memcpy(&entry.name, name.c_str(), nameLen > 32 ? 32 : nameLen);
|
||||
/*
|
||||
memcpy(&entry.sharedKey, secretKeyK, 32);
|
||||
|
||||
if(idType != 1)
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidIdType");
|
||||
return;
|
||||
}
|
||||
|
||||
entry.idType = idType;
|
||||
*/
|
||||
entry.remoteAllowed = remoteAllowed == 1 ? 1 : 0;
|
||||
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||
|
||||
if(allowedFrom.length() > 0)
|
||||
{
|
||||
entry.allowedFromYear = allowedFromAr[0];
|
||||
entry.allowedFromMonth = allowedFromAr[1];
|
||||
entry.allowedFromDay = allowedFromAr[2];
|
||||
entry.allowedFromHour = allowedFromAr[3];
|
||||
entry.allowedFromMinute = allowedFromAr[4];
|
||||
entry.allowedFromSecond = allowedFromAr[5];
|
||||
}
|
||||
|
||||
if(allowedUntil.length() > 0)
|
||||
{
|
||||
entry.allowedUntilYear = allowedUntilAr[0];
|
||||
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||
entry.allowedUntilDay = allowedUntilAr[2];
|
||||
entry.allowedUntilHour = allowedUntilAr[3];
|
||||
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||
}
|
||||
|
||||
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||
|
||||
if(allowedFromTime.length() > 0)
|
||||
{
|
||||
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||
}
|
||||
|
||||
if(allowedUntilTime.length() > 0)
|
||||
{
|
||||
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||
}
|
||||
|
||||
result = _nukiLock.addAuthorizationEntry(entry);
|
||||
delay(250);
|
||||
Log->print(F("Add authorization: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
else if (strcmp(action, "update") == 0)
|
||||
{
|
||||
if(!authId)
|
||||
{
|
||||
_network->publishAuthCommandResult("noAuthIdSet");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!idExists)
|
||||
{
|
||||
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||
return;
|
||||
}
|
||||
|
||||
Nuki::CmdResult resultAuth = _nukiLock.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||
delay(250);
|
||||
bool foundExisting = false;
|
||||
|
||||
if(resultAuth == Nuki::CmdResult::Success)
|
||||
{
|
||||
std::list<NukiLock::AuthorizationEntry> entries;
|
||||
_nukiLock.getAuthorizationEntries(&entries);
|
||||
|
||||
for(const auto& entry : entries)
|
||||
{
|
||||
if (authId != entry.authId) continue;
|
||||
else foundExisting = true;
|
||||
|
||||
if(name.length() < 1)
|
||||
{
|
||||
memset(oldName, 0, sizeof(oldName));
|
||||
memcpy(oldName, entry.name, sizeof(entry.name));
|
||||
}
|
||||
if(remoteAllowed == 2) remoteAllowed = entry.remoteAllowed;
|
||||
if(enabled == 2) enabled = entry.enabled;
|
||||
if(timeLimited == 2) timeLimited = entry.timeLimited;
|
||||
if(allowedFrom.length() < 1)
|
||||
{
|
||||
allowedFrom = "old";
|
||||
allowedFromAr[0] = entry.allowedFromYear;
|
||||
allowedFromAr[1] = entry.allowedFromMonth;
|
||||
allowedFromAr[2] = entry.allowedFromDay;
|
||||
allowedFromAr[3] = entry.allowedFromHour;
|
||||
allowedFromAr[4] = entry.allowedFromMinute;
|
||||
allowedFromAr[5] = entry.allowedFromSecond;
|
||||
}
|
||||
if(allowedUntil.length() < 1)
|
||||
{
|
||||
allowedUntil = "old";
|
||||
allowedUntilAr[0] = entry.allowedUntilYear;
|
||||
allowedUntilAr[1] = entry.allowedUntilMonth;
|
||||
allowedUntilAr[2] = entry.allowedUntilDay;
|
||||
allowedUntilAr[3] = entry.allowedUntilHour;
|
||||
allowedUntilAr[4] = entry.allowedUntilMinute;
|
||||
allowedUntilAr[5] = entry.allowedUntilSecond;
|
||||
}
|
||||
if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays;
|
||||
if(allowedFromTime.length() < 1)
|
||||
{
|
||||
allowedFromTime = "old";
|
||||
allowedFromTimeAr[0] = entry.allowedFromTimeHour;
|
||||
allowedFromTimeAr[1] = entry.allowedFromTimeMin;
|
||||
}
|
||||
|
||||
if(allowedUntilTime.length() < 1)
|
||||
{
|
||||
allowedUntilTime = "old";
|
||||
allowedUntilTimeAr[0] = entry.allowedUntilTimeHour;
|
||||
allowedUntilTimeAr[1] = entry.allowedUntilTimeMin;
|
||||
}
|
||||
}
|
||||
|
||||
if(!foundExisting)
|
||||
{
|
||||
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||
return;
|
||||
}
|
||||
|
||||
NukiLock::UpdatedAuthorizationEntry entry;
|
||||
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.authId = authId;
|
||||
|
||||
if(name.length() < 1)
|
||||
{
|
||||
size_t nameLen = strlen(oldName);
|
||||
memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t nameLen = name.length();
|
||||
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
|
||||
}
|
||||
entry.remoteAllowed = remoteAllowed;
|
||||
entry.enabled = enabled;
|
||||
entry.timeLimited = timeLimited;
|
||||
|
||||
if(enabled == 1)
|
||||
{
|
||||
if(timeLimited == 1)
|
||||
{
|
||||
if(allowedFrom.length() > 0)
|
||||
{
|
||||
entry.allowedFromYear = allowedFromAr[0];
|
||||
entry.allowedFromMonth = allowedFromAr[1];
|
||||
entry.allowedFromDay = allowedFromAr[2];
|
||||
entry.allowedFromHour = allowedFromAr[3];
|
||||
entry.allowedFromMinute = allowedFromAr[4];
|
||||
entry.allowedFromSecond = allowedFromAr[5];
|
||||
}
|
||||
|
||||
if(allowedUntil.length() > 0)
|
||||
{
|
||||
entry.allowedUntilYear = allowedUntilAr[0];
|
||||
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||
entry.allowedUntilDay = allowedUntilAr[2];
|
||||
entry.allowedUntilHour = allowedUntilAr[3];
|
||||
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||
}
|
||||
|
||||
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||
|
||||
if(allowedFromTime.length() > 0)
|
||||
{
|
||||
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||
}
|
||||
|
||||
if(allowedUntilTime.length() > 0)
|
||||
{
|
||||
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = _nukiLock.updateAuthorizationEntry(entry);
|
||||
delay(250);
|
||||
Log->print(F("Update authorization: "));
|
||||
Log->println((int)result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("invalidAction");
|
||||
return;
|
||||
}
|
||||
|
||||
if(result != Nuki::CmdResult::Success) {
|
||||
++_retryCount;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
updateAuth(false);
|
||||
|
||||
if((int)result != -1)
|
||||
{
|
||||
char resultStr[15];
|
||||
memset(&resultStr, 0, sizeof(resultStr));
|
||||
NukiLock::cmdResultToString(result, resultStr);
|
||||
_network->publishAuthCommandResult(resultStr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_network->publishAuthCommandResult("noActionSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const NukiLock::KeyTurnerState &NukiWrapper::keyTurnerState()
|
||||
{
|
||||
return _keyTurnerState;
|
||||
|
||||
@@ -53,12 +53,14 @@ private:
|
||||
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 onTimeControlCommandReceivedCallback(const char* value);
|
||||
static void onAuthCommandReceivedCallback(const char* value);
|
||||
static void gpioActionCallback(const GpioAction& action, const int& pin);
|
||||
void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||
void onOfficialUpdateReceived(const char* topic, const char* value);
|
||||
void onConfigUpdateReceived(const char* value);
|
||||
void onKeypadJsonCommandReceived(const char* value);
|
||||
void onTimeControlCommandReceived(const char* value);
|
||||
void onAuthCommandReceived(const char* value);
|
||||
|
||||
void updateKeyTurnerState();
|
||||
void updateBatteryState();
|
||||
@@ -66,6 +68,7 @@ private:
|
||||
void updateAuthData(bool retrieved);
|
||||
void updateKeypad(bool retrieved);
|
||||
void updateTimeControl(bool retrieved);
|
||||
void updateAuth(bool retrieved);
|
||||
void postponeBleWatchdog();
|
||||
|
||||
void updateGpioOutputs();
|
||||
@@ -101,6 +104,7 @@ private:
|
||||
bool _clearAuthData = false;
|
||||
std::vector<uint16_t> _keypadCodeIds;
|
||||
std::vector<uint8_t> _timeControlIds;
|
||||
std::vector<uint32_t> _authIds;
|
||||
|
||||
NukiLock::KeyTurnerState _lastKeyTurnerState;
|
||||
NukiLock::KeyTurnerState _keyTurnerState;
|
||||
@@ -122,6 +126,7 @@ private:
|
||||
bool _keypadEnabled = false;
|
||||
uint _maxKeypadCodeCount = 0;
|
||||
uint _maxTimeControlEntryCount = 0;
|
||||
uint _maxAuthEntryCount = 0;
|
||||
int _nrOfRetries = 0;
|
||||
int _retryDelay = 0;
|
||||
int _retryCount = 0;
|
||||
@@ -137,6 +142,7 @@ private:
|
||||
int64_t _waitAuthLogUpdateTs = 0;
|
||||
int64_t _waitKeypadUpdateTs = 0;
|
||||
int64_t _waitTimeControlUpdateTs = 0;
|
||||
int64_t _waitAuthUpdateTs = 0;
|
||||
int64_t _nextKeypadUpdateTs = 0;
|
||||
int64_t _nextRssiTs = 0;
|
||||
int64_t _lastRssi = 0;
|
||||
|
||||
@@ -96,6 +96,12 @@
|
||||
#define preference_show_secrets (char*)"showSecr"
|
||||
#define preference_ble_tx_power (char*)"bleTxPwr"
|
||||
#define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis"
|
||||
#define preference_lock_max_auth_entry_count (char*)"maxauth"
|
||||
#define preference_opener_max_auth_entry_count (char*)"opmaxauth"
|
||||
#define preference_auth_control_enabled (char*)"authCtrlEna"
|
||||
#define preference_auth_topic_per_entry (char*)"authPerEntry"
|
||||
#define preference_auth_info_enabled (char*)"authInfoEna"
|
||||
#define preference_auth_max_entries (char*)"authmaxentry"
|
||||
#define preference_network_custom_phy (char*)"ntwPHY"
|
||||
#define preference_network_custom_addr (char*)"ntwADDR"
|
||||
#define preference_network_custom_irq (char*)"ntwIRQ"
|
||||
@@ -273,7 +279,8 @@ private:
|
||||
preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled,
|
||||
preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq,
|
||||
preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_mosi,
|
||||
preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure
|
||||
preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count,
|
||||
preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries,
|
||||
};
|
||||
std::vector<char*> _redact =
|
||||
{
|
||||
@@ -288,7 +295,8 @@ private:
|
||||
preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_register_as_app, preference_register_opener_as_app, preference_ip_dhcp_enabled,
|
||||
preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid,
|
||||
preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt,
|
||||
preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, preference_ntw_reconfigure
|
||||
preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled,
|
||||
preference_ntw_reconfigure
|
||||
};
|
||||
std::vector<char*> _bytePrefs =
|
||||
{
|
||||
|
||||
@@ -283,10 +283,9 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
||||
#else
|
||||
String build_type = "debug";
|
||||
#endif
|
||||
|
||||
response->print("<form onsubmit=\"if(document.getElementById('currentver') == document.getElementById('latestver') && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest release?'); } \" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"release\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"background: green\" value=\"Update to latest release\"></form>");
|
||||
response->print("<form onsubmit=\"if(document.getElementById('currentver') == document.getElementById('betaver') && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest beta? This version could contain breaking bugs and necessitate downgrading to the latest release version using USB/Serial'); }\" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"beta\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"color: black; background: yellow\" value=\"Update to latest beta\"></form>");
|
||||
response->print("<form onsubmit=\"if(document.getElementById('currentver') == document.getElementById('devver') && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest development version? This version could contain breaking bugs and necessitate downgrading to the latest release version using USB/Serial'); }\" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"master\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"background: red\" value=\"Update to latest development version\"></form>");
|
||||
response->print("<form onsubmit=\"if(document.getElementById('currentver').value == document.getElementById('latestver').value && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest release?'); } \" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"release\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"background: green\" value=\"Update to latest release\"></form>");
|
||||
response->print("<form onsubmit=\"if(document.getElementById('currentver').value == document.getElementById('betaver').value && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest beta? This version could contain breaking bugs and necessitate downgrading to the latest release version using USB/Serial'); }\" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"beta\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"color: black; background: yellow\" value=\"Update to latest beta\"></form>");
|
||||
response->print("<form onsubmit=\"if(document.getElementById('currentver').value == document.getElementById('devver').value && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest development version? This version could contain breaking bugs and necessitate downgrading to the latest release version using USB/Serial'); }\" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"master\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"background: red\" value=\"Update to latest development version\"></form>");
|
||||
response->print("<div style=\"clear: both\"></div><br>");
|
||||
|
||||
response->print("<b>Current version: </b><span id=\"currentver\">");
|
||||
@@ -299,10 +298,10 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
||||
|
||||
#ifndef NUKI_HUB_UPDATER
|
||||
bool manifestSuccess = false;
|
||||
JsonDocument doc;
|
||||
|
||||
NetworkClientSecure *client = new NetworkClientSecure;
|
||||
if (client) {
|
||||
//client->setDefaultCACertBundle();
|
||||
client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
||||
{
|
||||
HTTPClient https;
|
||||
@@ -315,42 +314,8 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
||||
|
||||
if (http_responseCode == HTTP_CODE_OK || http_responseCode == HTTP_CODE_MOVED_PERMANENTLY)
|
||||
{
|
||||
JsonDocument doc;
|
||||
DeserializationError jsonError = deserializeJson(doc, https.getStream());
|
||||
|
||||
if (!jsonError)
|
||||
{
|
||||
manifestSuccess = true;
|
||||
response->print("<b>Latest release version: </b><span id=\"latestver\">");
|
||||
response->print(doc["release"]["fullversion"].as<const char*>());
|
||||
response->print(" (");
|
||||
response->print(doc["release"]["build"].as<const char*>());
|
||||
response->print(")</span>, ");
|
||||
response->print(doc["release"]["time"].as<const char*>());
|
||||
response->print("<br>");
|
||||
response->print("<b>Latest beta version: </b><span id=\"betaver\">");
|
||||
if(doc["beta"]["fullversion"] != "No beta available")
|
||||
{
|
||||
response->print(doc["beta"]["fullversion"].as<const char*>());
|
||||
response->print(" (");
|
||||
response->print(doc["beta"]["build"].as<const char*>());
|
||||
response->print(")</span>, ");
|
||||
response->print(doc["beta"]["time"].as<const char*>());
|
||||
}
|
||||
else
|
||||
{
|
||||
response->print(doc["beta"]["fullversion"].as<const char*>());
|
||||
response->print("</span>");
|
||||
}
|
||||
response->print("<br>");
|
||||
response->print("<b>Latest development version: </b><span id=\"devver\">");
|
||||
response->print(doc["master"]["fullversion"].as<const char*>());
|
||||
response->print(" (");
|
||||
response->print(doc["master"]["build"].as<const char*>());
|
||||
response->print(")</span>, ");
|
||||
response->print(doc["master"]["time"].as<const char*>());
|
||||
response->print("<br>");
|
||||
}
|
||||
if (!jsonError) { manifestSuccess = true; }
|
||||
}
|
||||
https.end();
|
||||
}
|
||||
@@ -362,6 +327,38 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
||||
{
|
||||
response->print("<span id=\"currentver\" style=\"display: none;\">currentver</span><span id=\"latestver\" style=\"display: none;\">latestver</span><span id=\"devver\" style=\"display: none;\">devver</span><span id=\"betaver\" style=\"display: none;\">betaver</span>");
|
||||
}
|
||||
else
|
||||
{
|
||||
response->print("<b>Latest release version: </b><span id=\"latestver\">");
|
||||
response->print(doc["release"]["fullversion"].as<const char*>());
|
||||
response->print(" (");
|
||||
response->print(doc["release"]["build"].as<const char*>());
|
||||
response->print(")</span>, ");
|
||||
response->print(doc["release"]["time"].as<const char*>());
|
||||
response->print("<br>");
|
||||
response->print("<b>Latest beta version: </b><span id=\"betaver\">");
|
||||
if(doc["beta"]["fullversion"] != "No beta available")
|
||||
{
|
||||
response->print(doc["beta"]["fullversion"].as<const char*>());
|
||||
response->print(" (");
|
||||
response->print(doc["beta"]["build"].as<const char*>());
|
||||
response->print(")</span>, ");
|
||||
response->print(doc["beta"]["time"].as<const char*>());
|
||||
}
|
||||
else
|
||||
{
|
||||
response->print(doc["beta"]["fullversion"].as<const char*>());
|
||||
response->print("</span>");
|
||||
}
|
||||
response->print("<br>");
|
||||
response->print("<b>Latest development version: </b><span id=\"devver\">");
|
||||
response->print(doc["master"]["fullversion"].as<const char*>());
|
||||
response->print(" (");
|
||||
response->print(doc["master"]["build"].as<const char*>());
|
||||
response->print(")</span>, ");
|
||||
response->print(doc["master"]["time"].as<const char*>());
|
||||
response->print("<br>");
|
||||
}
|
||||
#endif
|
||||
response->print("<br></div>");
|
||||
|
||||
@@ -1458,6 +1455,19 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(key == "AUTHMAX")
|
||||
{
|
||||
if(value.toInt() > 0 && value.toInt() < 51)
|
||||
{
|
||||
if(_preferences->getInt(preference_auth_max_entries, MAX_AUTH) != value.toInt())
|
||||
{
|
||||
_preferences->putInt(preference_auth_max_entries, value.toInt());
|
||||
Log->print(F("Setting changed: "));
|
||||
Log->println(key);
|
||||
configChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(key == "BUFFSIZE")
|
||||
{
|
||||
if(value.toInt() > 4095 && value.toInt() < 32769)
|
||||
@@ -1565,6 +1575,16 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
||||
configChanged = true;
|
||||
}
|
||||
}
|
||||
else if(key == "AUTHPUB")
|
||||
{
|
||||
if(_preferences->getBool(preference_auth_info_enabled, false) != (value == "1"))
|
||||
{
|
||||
_preferences->putBool(preference_auth_info_enabled, (value == "1"));
|
||||
Log->print(F("Setting changed: "));
|
||||
Log->println(key);
|
||||
configChanged = true;
|
||||
}
|
||||
}
|
||||
else if(key == "KPPER")
|
||||
{
|
||||
if(_preferences->getBool(preference_keypad_topic_per_entry, false) != (value == "1"))
|
||||
@@ -1595,6 +1615,26 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
||||
configChanged = true;
|
||||
}
|
||||
}
|
||||
else if(key == "AUTHPER")
|
||||
{
|
||||
if(_preferences->getBool(preference_auth_topic_per_entry, false) != (value == "1"))
|
||||
{
|
||||
_preferences->putBool(preference_auth_topic_per_entry, (value == "1"));
|
||||
Log->print(F("Setting changed: "));
|
||||
Log->println(key);
|
||||
configChanged = true;
|
||||
}
|
||||
}
|
||||
else if(key == "AUTHENA")
|
||||
{
|
||||
if(_preferences->getBool(preference_auth_control_enabled, false) != (value == "1"))
|
||||
{
|
||||
_preferences->putBool(preference_auth_control_enabled, (value == "1"));
|
||||
Log->print(F("Setting changed: "));
|
||||
Log->println(key);
|
||||
configChanged = true;
|
||||
}
|
||||
}
|
||||
else if(key == "PUBAUTH")
|
||||
{
|
||||
if(_preferences->getBool(preference_publish_authdata, false) != (value == "1"))
|
||||
@@ -2220,7 +2260,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
||||
{
|
||||
if(curAdvancedLockConfigAclPrefs[i] != advancedLockConfigAclPrefs[i])
|
||||
{
|
||||
_preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs));
|
||||
_preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs));
|
||||
Log->print(F("Setting changed: "));
|
||||
Log->println("ACLCONFADVANCEDLOCK");
|
||||
configChanged = true;
|
||||
@@ -2232,7 +2272,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
||||
{
|
||||
if(curBasicOpenerConfigAclPrefs[i] != basicOpenerConfigAclPrefs[i])
|
||||
{
|
||||
_preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs));
|
||||
_preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs));
|
||||
Log->print(F("Setting changed: "));
|
||||
Log->println("ACLCONFBASICOPENER");
|
||||
configChanged = true;
|
||||
@@ -2450,25 +2490,13 @@ void WebCfgServer::buildImportExportHtml(AsyncWebServerRequest *request)
|
||||
response->print("<button title=\"Basic export\" onclick=\" window.open('/export', '_self'); return false;\">Basic export</button>");
|
||||
response->print("<br><br><button title=\"Export with redacted settings\" onclick=\" window.open('/export?redacted=1'); return false;\">Export with redacted settings</button>");
|
||||
response->print("<br><br><button title=\"Export with redacted settings and pairing data\" onclick=\" window.open('/export?redacted=1&pairing=1'); return false;\">Export with redacted settings and pairing data</button>");
|
||||
response->print("</div><div id=\"msgdiv\" style=\"visibility:hidden\">Initiating config update. Please be patient.<br>You will be forwarded automatically when the import is complete.</div>");
|
||||
response->print("<script type=\"text/javascript\">");
|
||||
response->print("window.addEventListener('load', function () {");
|
||||
response->print(" var button = document.getElementById(\"submitbtn\");");
|
||||
response->print(" button.addEventListener('click',hideshow,false);");
|
||||
response->print(" function hideshow() {");
|
||||
response->print(" document.getElementById('upform').style.visibility = 'hidden';");
|
||||
response->print(" document.getElementById('gitdiv').style.visibility = 'hidden';");
|
||||
response->print(" document.getElementById('msgdiv').style.visibility = 'visible';");
|
||||
response->print(" }");
|
||||
response->print("});");
|
||||
response->print("</script>");
|
||||
response->print("</body></html>");
|
||||
response->print("</div></body></html>");
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
void WebCfgServer::buildCustomNetworkConfigHtml(AsyncWebServerRequest *request)
|
||||
{
|
||||
String header = "<script>window.onload = function() { hideopt(document.getElementsByName('NWCUSTPHY')[0].value); var physelect = document.getElementsByName('NWCUSTPHY')[0]; physelect.addEventListener('change', function(event) { var select = event.target; var selectedOption = select.options[select.selectedIndex]; hideopt(selectedOption.getAttribute('value')); }); }; function hideopt(value) { var intopts = document.getElementsByClassName('internalopt'); var extopts = document.getElementsByClassName('externalopt'); if (value >= 1 && value <= 3) { hide(intopts); } else if (value >= 4 && value <= 9) { hide(extopts); } else { hide(intopts); hide(extopts); } } function hide(opts) { for (var i = 0; i < opts.length; i ++) { opts[i].style.display = 'none'; } }</script>";
|
||||
String header = "<script>window.onload=function(){var physelect=document.getElementsByName('NWCUSTPHY')[0];hideshowopt(physelect.value);physelect.addEventListener('change', function(event){var select=event.target;var selectedOption=select.options[select.selectedIndex];hideshowopt(selectedOption.getAttribute('value'));});};function hideshowopt(value){if(value>=1&&value<=3){hideopt('internalopt',true);hideopt('externalopt',false);}else if(value>=4&&value<=9){hideopt('internalopt', false);hideopt('externalopt', true);}else {hideopt('internalopt', true);hideopt('externalopt', true);}}function hideopt(opts,hide){var hideopts = document.getElementsByClassName(opts);for(var i=0;i<hideopts.length;i++){if(hide==true){hideopts[i].style.display='none';}else{hideopts[i].style.display='block';}}}</script>";
|
||||
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
||||
buildHtmlHeader(response, header);
|
||||
response->print("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
|
||||
@@ -2478,16 +2506,16 @@ void WebCfgServer::buildCustomNetworkConfigHtml(AsyncWebServerRequest *request)
|
||||
printInputField(response, "NWCUSTADDR", "ADDR", _preferences->getInt(preference_network_custom_addr, 1), 6, "");
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
printDropDown(response, "NWCUSTCLK", "CLK", String(_preferences->getInt(preference_network_custom_clk, 0)), getNetworkCustomCLKOptions(), "internalopt");
|
||||
printInputField(response, "NWCUSTPWR", "PWR", _preferences->getInt(preference_network_custom_pwr, 12), 6, "internalopt");
|
||||
printInputField(response, "NWCUSTMDIO", "MDIO", _preferences->getInt(preference_network_custom_mdio), 6, "internalopt");
|
||||
printInputField(response, "NWCUSTMDC", "MDC", _preferences->getInt(preference_network_custom_mdc), 6, "internalopt");
|
||||
printInputField(response, "NWCUSTPWR", "PWR", _preferences->getInt(preference_network_custom_pwr, 12), 6, "class=\"internalopt\"");
|
||||
printInputField(response, "NWCUSTMDIO", "MDIO", _preferences->getInt(preference_network_custom_mdio), 6, "class=\"internalopt\"");
|
||||
printInputField(response, "NWCUSTMDC", "MDC", _preferences->getInt(preference_network_custom_mdc), 6, "class=\"internalopt\"");
|
||||
#endif
|
||||
printInputField(response, "NWCUSTIRQ", "IRQ", _preferences->getInt(preference_network_custom_irq, -1), 6, "externalopt");
|
||||
printInputField(response, "NWCUSTRST", "RST", _preferences->getInt(preference_network_custom_rst, -1), 6, "externalopt");
|
||||
printInputField(response, "NWCUSTCS", "CS", _preferences->getInt(preference_network_custom_cs, -1), 6, "externalopt");
|
||||
printInputField(response, "NWCUSTSCK", "SCK", _preferences->getInt(preference_network_custom_sck, -1), 6, "externalopt");
|
||||
printInputField(response, "NWCUSTMISO", "MISO", _preferences->getInt(preference_network_custom_miso, -1), 6, "externalopt");
|
||||
printInputField(response, "NWCUSTMOSI", "MOSI", _preferences->getInt(preference_network_custom_mosi, -1), 6, "externalopt");
|
||||
printInputField(response, "NWCUSTIRQ", "IRQ", _preferences->getInt(preference_network_custom_irq, -1), 6, "class=\"externalopt\"");
|
||||
printInputField(response, "NWCUSTRST", "RST", _preferences->getInt(preference_network_custom_rst, -1), 6, "class=\"externalopt\"");
|
||||
printInputField(response, "NWCUSTCS", "CS", _preferences->getInt(preference_network_custom_cs, -1), 6, "class=\"externalopt\"");
|
||||
printInputField(response, "NWCUSTSCK", "SCK", _preferences->getInt(preference_network_custom_sck, -1), 6, "class=\"externalopt\"");
|
||||
printInputField(response, "NWCUSTMISO", "MISO", _preferences->getInt(preference_network_custom_miso, -1), 6, "class=\"externalopt\"");
|
||||
printInputField(response, "NWCUSTMOSI", "MOSI", _preferences->getInt(preference_network_custom_mosi, -1), 6, "class=\"externalopt\"");
|
||||
|
||||
response->print("</table>");
|
||||
|
||||
@@ -2695,7 +2723,6 @@ void WebCfgServer::buildMqttConfigHtml(AsyncWebServerRequest *request)
|
||||
printInputField(response, "IPGTW", "Default gateway", _preferences->getString(preference_ip_gateway).c_str(), 15, "");
|
||||
printInputField(response, "DNSSRV", "DNS Server", _preferences->getString(preference_ip_dns_server).c_str(), 15, "");
|
||||
response->print("</table>");
|
||||
|
||||
response->print("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response->print("</form>");
|
||||
response->print("</body></html>");
|
||||
@@ -2872,6 +2899,9 @@ void WebCfgServer::partAccLvlHtml(String &partString, int aclPart)
|
||||
printCheckBox(partString, "TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), "");
|
||||
printCheckBox(partString, "TCPER", "Publish a topic per time control entry and create HA sensor", _preferences->getBool(preference_timecontrol_topic_per_entry), "");
|
||||
printCheckBox(partString, "TCENA", "Add, modify and delete time control entries", _preferences->getBool(preference_timecontrol_control_enabled), "");
|
||||
printCheckBox(partString, "AUTHPUB", "Publish authorization entries information", _preferences->getBool(preference_auth_info_enabled), "");
|
||||
printCheckBox(partString, "AUTHPER", "Publish a topic per authorization entry and create HA sensor", _preferences->getBool(preference_auth_topic_per_entry), "");
|
||||
printCheckBox(partString, "AUTHENA", "Modify and delete authorization entries", _preferences->getBool(preference_auth_control_enabled), "");
|
||||
printCheckBox(partString, "PUBAUTH", "Publish authorization log", _preferences->getBool(preference_publish_authdata), "");
|
||||
partString.concat("</table><br>");
|
||||
partString.concat("<div id=\"acllock\"></div>");
|
||||
@@ -3514,6 +3544,9 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request)
|
||||
sprintf(tmp, "%02x", authorizationId[i]);
|
||||
response->print(tmp);
|
||||
}
|
||||
uint32_t authorizationIdInt = authorizationId[0] + 256U*authorizationId[1] + 65536U*authorizationId[2] + 16777216U*authorizationId[3];
|
||||
response->print("\nAuthorizationId (UINT32_T): ");
|
||||
response->print(authorizationIdInt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3865,9 +3898,17 @@ void WebCfgServer::printInputField(AsyncResponseStream *response,
|
||||
response->print(isPassword ? "\"password\"" : "\"text\"");
|
||||
if(strcmp(id, "") != 0)
|
||||
{
|
||||
response->print(" id=\"");
|
||||
response->print(id);
|
||||
response->print("\"");
|
||||
if(strncmp(id, "class=", 6) != 0)
|
||||
{
|
||||
response->print(" ");
|
||||
response->print(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
response->print(" id=\"");
|
||||
response->print(id);
|
||||
response->print("\"");
|
||||
}
|
||||
}
|
||||
if(strcmp(value, "") != 0)
|
||||
{
|
||||
@@ -4129,4 +4170,4 @@ String WebCfgServer::getPreselectionForGpio(const uint8_t &pin)
|
||||
|
||||
return String((int8_t)PinRole::Disabled);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
14
src/main.cpp
14
src/main.cpp
@@ -154,7 +154,6 @@ void networkTask(void *pvParameters)
|
||||
}
|
||||
|
||||
esp_task_wdt_reset();
|
||||
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
@@ -243,6 +242,7 @@ void bootloopDetection()
|
||||
preferences->putInt(preference_authlog_max_entries, MAX_AUTHLOG);
|
||||
preferences->putInt(preference_keypad_max_entries, MAX_KEYPAD);
|
||||
preferences->putInt(preference_timecontrol_max_entries, MAX_TIMECONTROL);
|
||||
preferences->putInt(preference_auth_max_entries, MAX_AUTH);
|
||||
bootloopCounter = 0;
|
||||
}
|
||||
}
|
||||
@@ -344,10 +344,10 @@ void otaTask(void *pvParameter)
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Log->println("Firmware upgrade failed, restarting");
|
||||
esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL));
|
||||
restartEsp(RestartReason::OTAAborted);
|
||||
restartEsp(RestartReason::OTAAborted);
|
||||
}
|
||||
|
||||
void setupTasks(bool ota)
|
||||
@@ -382,6 +382,14 @@ void setup()
|
||||
Serial.begin(115200);
|
||||
Log = &Serial;
|
||||
|
||||
#ifndef NUKI_HUB_UPDATER
|
||||
stdout = funopen(NULL, NULL, &write_fn, NULL, NULL);
|
||||
static char linebuf[1024];
|
||||
setvbuf(stdout, linebuf, _IOLBF, sizeof(linebuf));
|
||||
esp_rom_install_channel_putc(1, &ets_putc_handler);
|
||||
//ets_install_putc1(&ets_putc_handler);
|
||||
#endif
|
||||
|
||||
preferences = new Preferences();
|
||||
preferences->begin("nukihub", false);
|
||||
bool firstStart = initPreferences(preferences);
|
||||
|
||||
Reference in New Issue
Block a user