Official MQTT - Nuki Hub coexistence, keypad/timecontrol sensor per code/entry, option not to publish config, authorization log improvements, various fixes (#389)
* Coexistence with official MQTT over Wifi and Thread * Coexistence with official MQTT over Wifi and Thread * Arduino Core 2.0.17 cmake and README * Coexistence with official MQTT over Wifi and Thread * Keep updating status until state is known * Coexistence with official MQTT over Wifi and Thread
This commit is contained in:
@@ -24,6 +24,19 @@
|
||||
#define mqtt_topic_lock_address "/lock/address"
|
||||
#define mqtt_topic_lock_retry "/lock/retry"
|
||||
|
||||
#define mqtt_topic_official_lock_action "/lockAction"
|
||||
//#define mqtt_topic_official_mode "/mode"
|
||||
#define mqtt_topic_official_state "/state"
|
||||
#define mqtt_topic_official_batteryCritical "/batteryCritical"
|
||||
#define mqtt_topic_official_batteryChargeState "/batteryChargeState"
|
||||
#define mqtt_topic_official_batteryCharging "/batteryCharging"
|
||||
#define mqtt_topic_official_keypadBatteryCritical "/keypadBatteryCritical"
|
||||
#define mqtt_topic_official_doorsensorState "/doorsensorState"
|
||||
#define mqtt_topic_official_doorsensorBatteryCritical "/doorsensorBatteryCritical"
|
||||
#define mqtt_topic_official_connected "/connected"
|
||||
#define mqtt_topic_official_commandResponse "/commandResponse"
|
||||
#define mqtt_topic_official_lockActionEvent "/lockActionEvent"
|
||||
|
||||
#define mqtt_topic_config_action "/configuration/action"
|
||||
#define mqtt_topic_config_action_command_result "/configuration/commandResult"
|
||||
#define mqtt_topic_config_basic_json "/configuration/basicJson"
|
||||
@@ -50,10 +63,12 @@
|
||||
#define mqtt_topic_battery_max_turn_current "/battery/maxTurnCurrent"
|
||||
#define mqtt_topic_battery_lock_distance "/battery/lockDistance"
|
||||
#define mqtt_topic_battery_keypad_critical "/battery/keypadCritical"
|
||||
#define mqtt_topic_battery_doorsensor_critical "/battery/doorSensorCritical"
|
||||
#define mqtt_topic_battery_basic_json "/battery/basicJson"
|
||||
#define mqtt_topic_battery_advanced_json "/battery/advancedJson"
|
||||
|
||||
#define mqtt_topic_keypad "/keypad"
|
||||
#define mqtt_topic_keypad_codes "/keypad/codes"
|
||||
#define mqtt_topic_keypad_command_action "/keypad/command/action"
|
||||
#define mqtt_topic_keypad_command_id "/keypad/command/id"
|
||||
#define mqtt_topic_keypad_command_name "/keypad/command/name"
|
||||
@@ -64,6 +79,8 @@
|
||||
#define mqtt_topic_keypad_json_action "/keypad/actionJson"
|
||||
#define mqtt_topic_keypad_json_command_result "/keypad/commandResultJson"
|
||||
|
||||
#define mqtt_topic_timecontrol "/timecontrol"
|
||||
#define mqtt_topic_timecontrol_entries "/timecontrol/entries"
|
||||
#define mqtt_topic_timecontrol_json "/timecontrol/json"
|
||||
#define mqtt_topic_timecontrol_action "/timecontrol/action"
|
||||
#define mqtt_topic_timecontrol_command_result "/timecontrol/commandResult"
|
||||
|
||||
171
src/Network.cpp
171
src/Network.cpp
@@ -160,7 +160,7 @@ void Network::setupDevice()
|
||||
|
||||
void Network::initialize()
|
||||
{
|
||||
_restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect);
|
||||
_restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect, false);
|
||||
_rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000;
|
||||
|
||||
_hostname = _preferences->getString(preference_hostname);
|
||||
@@ -358,6 +358,8 @@ bool Network::update()
|
||||
if(_lastMaintenanceTs == 0 || (ts - _lastMaintenanceTs) > 30000)
|
||||
{
|
||||
publishULong(_maintenancePathPrefix, mqtt_topic_uptime, ts / 1000 / 60);
|
||||
publishString(_maintenancePathPrefix, mqtt_topic_mqtt_connection_state, "online");
|
||||
|
||||
if(_publishDebugInfo)
|
||||
{
|
||||
publishUInt(_maintenancePathPrefix, mqtt_topic_freeheap, esp_get_free_heap_size());
|
||||
@@ -706,56 +708,56 @@ const String Network::networkBSSID() const
|
||||
return _device->BSSIDstr();
|
||||
}
|
||||
|
||||
void Network::publishFloat(const char* prefix, const char* topic, const float value, const uint8_t precision)
|
||||
void Network::publishFloat(const char* prefix, const char* topic, const float value, const uint8_t precision, bool retain)
|
||||
{
|
||||
char str[30];
|
||||
dtostrf(value, 0, precision, str);
|
||||
char path[200] = {0};
|
||||
buildMqttPath(path, { prefix, topic });
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, true, str);
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str);
|
||||
}
|
||||
|
||||
void Network::publishInt(const char* prefix, const char *topic, const int value)
|
||||
void Network::publishInt(const char* prefix, const char *topic, const int value, bool retain)
|
||||
{
|
||||
char str[30];
|
||||
itoa(value, str, 10);
|
||||
char path[200] = {0};
|
||||
buildMqttPath(path, { prefix, topic });
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, true, str);
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str);
|
||||
}
|
||||
|
||||
void Network::publishUInt(const char* prefix, const char *topic, const unsigned int value)
|
||||
void Network::publishUInt(const char* prefix, const char *topic, const unsigned int value, bool retain)
|
||||
{
|
||||
char str[30];
|
||||
utoa(value, str, 10);
|
||||
char path[200] = {0};
|
||||
buildMqttPath(path, { prefix, topic });
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, true, str);
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str);
|
||||
}
|
||||
|
||||
void Network::publishULong(const char* prefix, const char *topic, const unsigned long value)
|
||||
void Network::publishULong(const char* prefix, const char *topic, const unsigned long value, bool retain)
|
||||
{
|
||||
char str[30];
|
||||
utoa(value, str, 10);
|
||||
char path[200] = {0};
|
||||
buildMqttPath(path, { prefix, topic });
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, true, str);
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str);
|
||||
}
|
||||
|
||||
void Network::publishBool(const char* prefix, const char *topic, const bool value)
|
||||
void Network::publishBool(const char* prefix, const char *topic, const bool value, bool retain)
|
||||
{
|
||||
char str[2] = {0};
|
||||
str[0] = value ? '1' : '0';
|
||||
char path[200] = {0};
|
||||
buildMqttPath(path, { prefix, topic });
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, true, str);
|
||||
_device->mqttPublish(path, MQTT_QOS_LEVEL, retain, str);
|
||||
}
|
||||
|
||||
bool Network::publishString(const char* prefix, const char *topic, const char *value)
|
||||
bool Network::publishString(const char* prefix, const char *topic, const char *value, bool retain)
|
||||
{
|
||||
char path[200] = {0};
|
||||
buildMqttPath(path, { prefix, topic });
|
||||
return _device->mqttPublish(path, MQTT_QOS_LEVEL, true, value) > 0;
|
||||
return _device->mqttPublish(path, MQTT_QOS_LEVEL, retain, value) > 0;
|
||||
}
|
||||
|
||||
void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction)
|
||||
@@ -796,6 +798,8 @@ void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* n
|
||||
json["stat_locking"] = "locking";
|
||||
json["stat_unlocked"] = "unlocked";
|
||||
json["stat_unlocking"] = "unlocking";
|
||||
json["stat_open"] = "open";
|
||||
json["stat_opening"] = "opening";
|
||||
json["opt"] = "false";
|
||||
|
||||
serializeJson(json, _buffer, _bufferSize);
|
||||
@@ -1101,10 +1105,15 @@ void Network::publishHASSConfigAdditionalLockEntities(char *deviceType, const ch
|
||||
{
|
||||
uint32_t aclPrefs[17];
|
||||
_preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs));
|
||||
uint32_t basicLockConfigAclPrefs[16];
|
||||
_preferences->getBytes(preference_conf_lock_basic_acl, &basicLockConfigAclPrefs, sizeof(basicLockConfigAclPrefs));
|
||||
uint32_t advancedLockConfigAclPrefs[22];
|
||||
_preferences->getBytes(preference_conf_lock_advanced_acl, &advancedLockConfigAclPrefs, sizeof(advancedLockConfigAclPrefs));
|
||||
|
||||
uint32_t basicLockConfigAclPrefs[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint32_t advancedLockConfigAclPrefs[22] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
if(_preferences->getBool(preference_conf_info_enabled, true))
|
||||
{
|
||||
_preferences->getBytes(preference_conf_lock_basic_acl, &basicLockConfigAclPrefs, sizeof(basicLockConfigAclPrefs));
|
||||
_preferences->getBytes(preference_conf_lock_advanced_acl, &advancedLockConfigAclPrefs, sizeof(advancedLockConfigAclPrefs));
|
||||
}
|
||||
|
||||
if((int)aclPrefs[2])
|
||||
{
|
||||
@@ -2166,10 +2175,14 @@ void Network::publishHASSConfigAdditionalOpenerEntities(char *deviceType, const
|
||||
{
|
||||
uint32_t aclPrefs[17];
|
||||
_preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs));
|
||||
uint32_t basicOpenerConfigAclPrefs[16];
|
||||
_preferences->getBytes(preference_conf_opener_basic_acl, &basicOpenerConfigAclPrefs, sizeof(basicOpenerConfigAclPrefs));
|
||||
uint32_t advancedOpenerConfigAclPrefs[22];
|
||||
_preferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs));
|
||||
uint32_t basicOpenerConfigAclPrefs[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint32_t advancedOpenerConfigAclPrefs[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
if(_preferences->getBool(preference_conf_info_enabled, true))
|
||||
{
|
||||
_preferences->getBytes(preference_conf_opener_basic_acl, &basicOpenerConfigAclPrefs, sizeof(basicOpenerConfigAclPrefs));
|
||||
_preferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs));
|
||||
}
|
||||
|
||||
if((int)aclPrefs[11])
|
||||
{
|
||||
@@ -2195,64 +2208,6 @@ void Network::publishHASSConfigAdditionalOpenerEntities(char *deviceType, const
|
||||
removeHassTopic((char*)"button", (char*)"unlatch", uidString);
|
||||
}
|
||||
|
||||
if((int)basicOpenerConfigAclPrefs[5] == 1)
|
||||
{
|
||||
// LED enabled
|
||||
publishHassTopic("switch",
|
||||
"led_enabled",
|
||||
uidString,
|
||||
"_led_enabled",
|
||||
"LED enabled",
|
||||
name,
|
||||
baseTopic,
|
||||
String("~") + mqtt_topic_config_basic_json,
|
||||
deviceType,
|
||||
"",
|
||||
"",
|
||||
"config",
|
||||
String("~") + mqtt_topic_config_action,
|
||||
{ { (char*)"en", (char*)"true" },
|
||||
{ (char*)"ic", (char*)"mdi:led-variant-on" },
|
||||
{ (char*)"pl_on", (char*)"{ \"ledEnabled\": \"1\"}" },
|
||||
{ (char*)"pl_off", (char*)"{ \"ledEnabled\": \"0\"}" },
|
||||
{ (char*)"val_tpl", (char*)"{{value_json.ledEnabled}}" },
|
||||
{ (char*)"stat_on", (char*)"1" },
|
||||
{ (char*)"stat_off", (char*)"0" }});
|
||||
}
|
||||
else
|
||||
{
|
||||
removeHassTopic((char*)"switch", (char*)"led_enabled", uidString);
|
||||
}
|
||||
|
||||
if((int)basicOpenerConfigAclPrefs[4] == 1)
|
||||
{
|
||||
// Button enabled
|
||||
publishHassTopic("switch",
|
||||
"button_enabled",
|
||||
uidString,
|
||||
"_button_enabled",
|
||||
"Button enabled",
|
||||
name,
|
||||
baseTopic,
|
||||
String("~") + mqtt_topic_config_basic_json,
|
||||
deviceType,
|
||||
"",
|
||||
"",
|
||||
"config",
|
||||
String("~") + mqtt_topic_config_action,
|
||||
{ { (char*)"en", (char*)"true" },
|
||||
{ (char*)"ic", (char*)"mdi:radiobox-marked" },
|
||||
{ (char*)"pl_on", (char*)"{ \"buttonEnabled\": \"1\"}" },
|
||||
{ (char*)"pl_off", (char*)"{ \"buttonEnabled\": \"0\"}" },
|
||||
{ (char*)"val_tpl", (char*)"{{value_json.buttonEnabled}}" },
|
||||
{ (char*)"stat_on", (char*)"1" },
|
||||
{ (char*)"stat_off", (char*)"0" }});
|
||||
}
|
||||
else
|
||||
{
|
||||
removeHassTopic((char*)"switch", (char*)"button_enabled", uidString);
|
||||
}
|
||||
|
||||
publishHassTopic("binary_sensor",
|
||||
"continuous_mode",
|
||||
uidString,
|
||||
@@ -2319,6 +2274,64 @@ void Network::publishHASSConfigAdditionalOpenerEntities(char *deviceType, const
|
||||
String path = createHassTopicPath("event", "ring", uidString);
|
||||
_device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, _buffer);
|
||||
|
||||
if((int)basicOpenerConfigAclPrefs[5] == 1)
|
||||
{
|
||||
// LED enabled
|
||||
publishHassTopic("switch",
|
||||
"led_enabled",
|
||||
uidString,
|
||||
"_led_enabled",
|
||||
"LED enabled",
|
||||
name,
|
||||
baseTopic,
|
||||
String("~") + mqtt_topic_config_basic_json,
|
||||
deviceType,
|
||||
"",
|
||||
"",
|
||||
"config",
|
||||
String("~") + mqtt_topic_config_action,
|
||||
{ { (char*)"en", (char*)"true" },
|
||||
{ (char*)"ic", (char*)"mdi:led-variant-on" },
|
||||
{ (char*)"pl_on", (char*)"{ \"ledEnabled\": \"1\"}" },
|
||||
{ (char*)"pl_off", (char*)"{ \"ledEnabled\": \"0\"}" },
|
||||
{ (char*)"val_tpl", (char*)"{{value_json.ledEnabled}}" },
|
||||
{ (char*)"stat_on", (char*)"1" },
|
||||
{ (char*)"stat_off", (char*)"0" }});
|
||||
}
|
||||
else
|
||||
{
|
||||
removeHassTopic((char*)"switch", (char*)"led_enabled", uidString);
|
||||
}
|
||||
|
||||
if((int)basicOpenerConfigAclPrefs[4] == 1)
|
||||
{
|
||||
// Button enabled
|
||||
publishHassTopic("switch",
|
||||
"button_enabled",
|
||||
uidString,
|
||||
"_button_enabled",
|
||||
"Button enabled",
|
||||
name,
|
||||
baseTopic,
|
||||
String("~") + mqtt_topic_config_basic_json,
|
||||
deviceType,
|
||||
"",
|
||||
"",
|
||||
"config",
|
||||
String("~") + mqtt_topic_config_action,
|
||||
{ { (char*)"en", (char*)"true" },
|
||||
{ (char*)"ic", (char*)"mdi:radiobox-marked" },
|
||||
{ (char*)"pl_on", (char*)"{ \"buttonEnabled\": \"1\"}" },
|
||||
{ (char*)"pl_off", (char*)"{ \"buttonEnabled\": \"0\"}" },
|
||||
{ (char*)"val_tpl", (char*)"{{value_json.buttonEnabled}}" },
|
||||
{ (char*)"stat_on", (char*)"1" },
|
||||
{ (char*)"stat_off", (char*)"0" }});
|
||||
}
|
||||
else
|
||||
{
|
||||
removeHassTopic((char*)"switch", (char*)"button_enabled", uidString);
|
||||
}
|
||||
|
||||
if((int)advancedOpenerConfigAclPrefs[15] == 1)
|
||||
{
|
||||
publishHassTopic("number",
|
||||
|
||||
@@ -40,12 +40,12 @@ public:
|
||||
|
||||
void subscribe(const char* prefix, const char* path);
|
||||
void initTopic(const char* prefix, const char* path, const char* value);
|
||||
void publishFloat(const char* prefix, const char* topic, const float value, const uint8_t precision = 2);
|
||||
void publishInt(const char* prefix, const char* topic, const int value);
|
||||
void publishUInt(const char* prefix, const char* topic, const unsigned int value);
|
||||
void publishULong(const char* prefix, const char* topic, const unsigned long value);
|
||||
void publishBool(const char* prefix, const char* topic, const bool value);
|
||||
bool publishString(const char* prefix, const char* topic, const char* value);
|
||||
void publishFloat(const char* prefix, const char* topic, const float value, const uint8_t precision = 2, bool retain = false);
|
||||
void publishInt(const char* prefix, const char* topic, const int value, bool retain = false);
|
||||
void publishUInt(const char* prefix, const char* topic, const unsigned int value, bool retain = false);
|
||||
void publishULong(const char* prefix, const char* topic, const unsigned long value, bool retain = false);
|
||||
void publishBool(const char* prefix, const char* topic, const bool value, bool retain = false);
|
||||
bool publishString(const char* prefix, const char* topic, const char* value, bool retain = false);
|
||||
|
||||
void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const char* availabilityTopic, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction);
|
||||
void publishHASSConfigAdditionalLockEntities(char* deviceType, const char* baseTopic, char* name, char* uidString);
|
||||
@@ -56,6 +56,22 @@ public:
|
||||
void publishHASSWifiRssiConfig(char* deviceType, const char* baseTopic, char* name, char* uidString);
|
||||
void removeHASSConfig(char* uidString);
|
||||
void removeHASSConfigTopic(char* deviceType, char* name, char* uidString);
|
||||
void publishHassTopic(const String& mqttDeviceType,
|
||||
const String& mqttDeviceName,
|
||||
const String& uidString,
|
||||
const String& uidStringPostfix,
|
||||
const String& displayName,
|
||||
const String& name,
|
||||
const String& baseTopic,
|
||||
const String& stateTopic,
|
||||
const String& deviceType,
|
||||
const String& deviceClass,
|
||||
const String& stateClass = "",
|
||||
const String& entityCat = "",
|
||||
const String& commandTopic = "",
|
||||
std::vector<std::pair<char*, char*>> additionalEntries = {}
|
||||
);
|
||||
void removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString);
|
||||
void removeTopic(const String& mqttPath, const String& mqttTopic);
|
||||
void batteryTypeToString(const Nuki::BatteryType battype, char* str);
|
||||
void advertisingModeToString(const Nuki::AdvertisingMode advmode, char* str);
|
||||
@@ -87,24 +103,7 @@ private:
|
||||
void setupDevice();
|
||||
bool reconnect();
|
||||
|
||||
void publishHassTopic(const String& mqttDeviceType,
|
||||
const String& mqttDeviceName,
|
||||
const String& uidString,
|
||||
const String& uidStringPostfix,
|
||||
const String& displayName,
|
||||
const String& name,
|
||||
const String& baseTopic,
|
||||
const String& stateTopic,
|
||||
const String& deviceType,
|
||||
const String& deviceClass,
|
||||
const String& stateClass = "",
|
||||
const String& entityCat = "",
|
||||
const String& commandTopic = "",
|
||||
std::vector<std::pair<char*, char*>> additionalEntries = {}
|
||||
);
|
||||
|
||||
String createHassTopicPath(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString);
|
||||
void removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString);
|
||||
JsonDocument createHassJson(const String& uidString,
|
||||
const String& uidStringPostfix,
|
||||
const String& displayName,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Logger.h"
|
||||
#include "RestartReason.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <ctype.h>
|
||||
|
||||
NetworkLock::NetworkLock(Network* network, Preferences* preferences, char* buffer, size_t bufferSize)
|
||||
: _network(network),
|
||||
@@ -16,6 +17,19 @@ NetworkLock::NetworkLock(Network* network, Preferences* preferences, char* buffe
|
||||
memset(_authName, 0, sizeof(_authName));
|
||||
_authName[0] = '\0';
|
||||
|
||||
_offTopics.reserve(10);
|
||||
//_offTopics.push_back(mqtt_topic_official_mode);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_state);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_batteryCritical);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_batteryChargeState);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_batteryCharging);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_keypadBatteryCritical);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_doorsensorState);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_doorsensorBatteryCritical);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_connected);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_commandResponse);
|
||||
_offTopics.push_back((char*)mqtt_topic_official_lockActionEvent);
|
||||
|
||||
_network->registerMqttReceiver(this);
|
||||
}
|
||||
|
||||
@@ -88,6 +102,18 @@ void NetworkLock::initialize()
|
||||
//_network->removeTopic(_mqttPath, mqtt_topic_presence);
|
||||
}
|
||||
|
||||
if(!_preferences->getBool(preference_conf_info_enabled, false))
|
||||
{
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_basic_json);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_advanced_json);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_button_enabled);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_led_enabled);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_led_brightness);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_auto_unlock);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_auto_lock);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_single_lock);
|
||||
}
|
||||
|
||||
if(_preferences->getBool(preference_keypad_control_enabled))
|
||||
{
|
||||
if(!_preferences->getBool(preference_disable_non_json, false))
|
||||
@@ -116,6 +142,20 @@ void NetworkLock::initialize()
|
||||
_network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--");
|
||||
}
|
||||
|
||||
if(_preferences->getBool(preference_official_hybrid, false))
|
||||
{
|
||||
char uidString[20];
|
||||
itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16);
|
||||
for(char* c=uidString; *c=toupper(*c); ++c);
|
||||
strcpy(_offMqttPath, "nuki/");
|
||||
strcat(_offMqttPath,uidString);
|
||||
|
||||
for(const auto& offTopic : _offTopics)
|
||||
{
|
||||
_network->subscribe(_offMqttPath, offTopic);
|
||||
}
|
||||
}
|
||||
|
||||
if(_preferences->getBool(preference_publish_authdata, false))
|
||||
{
|
||||
_network->subscribe(_mqttPath, mqtt_topic_lock_log_rolling_last);
|
||||
@@ -170,6 +210,20 @@ void NetworkLock::onMqttDataReceived(const char* topic, byte* payload, const uns
|
||||
if(atoi(value) > 0 && atoi(value) > _lastRollingLog) _lastRollingLog = atoi(value);
|
||||
}
|
||||
|
||||
if(_preferences->getBool(preference_official_hybrid, false))
|
||||
{
|
||||
for(auto offTopic : _offTopics)
|
||||
{
|
||||
if(comparePrefixedPath(topic, offTopic, true))
|
||||
{
|
||||
if(_officialUpdateReceivedCallback != nullptr)
|
||||
{
|
||||
_officialUpdateReceivedCallback(offTopic, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(comparePrefixedPath(topic, mqtt_topic_lock_action))
|
||||
{
|
||||
if(strcmp(value, "") == 0 ||
|
||||
@@ -313,31 +367,49 @@ void NetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurne
|
||||
JsonDocument json;
|
||||
JsonDocument jsonBattery;
|
||||
|
||||
lockstateToString(keyTurnerState.lockState, str);
|
||||
|
||||
if(keyTurnerState.lockState != NukiLock::LockState::Undefined)
|
||||
if(!_offConnected)
|
||||
{
|
||||
lockstateToString(keyTurnerState.lockState, str);
|
||||
|
||||
publishString(mqtt_topic_lock_state, str);
|
||||
|
||||
if(_haEnabled)
|
||||
if(keyTurnerState.lockState != NukiLock::LockState::Undefined)
|
||||
{
|
||||
publishState(keyTurnerState.lockState);
|
||||
|
||||
publishString(mqtt_topic_lock_state, str);
|
||||
|
||||
if(_haEnabled)
|
||||
{
|
||||
publishState(keyTurnerState.lockState);
|
||||
}
|
||||
}
|
||||
|
||||
json["lock_state"] = str;
|
||||
}
|
||||
else
|
||||
{
|
||||
lockstateToString((NukiLock::LockState)_offState, str);
|
||||
json["lock_state"] = str;
|
||||
}
|
||||
|
||||
json["lock_state"] = str;
|
||||
json["lockngo_state"] = (keyTurnerState.lockNgoTimer == 0 ? 0 : 1);
|
||||
|
||||
memset(&str, 0, sizeof(str));
|
||||
triggerToString(keyTurnerState.trigger, str);
|
||||
|
||||
if(_firstTunerStatePublish || keyTurnerState.trigger != lastKeyTurnerState.trigger)
|
||||
if(!_offConnected)
|
||||
{
|
||||
publishString(mqtt_topic_lock_trigger, str);
|
||||
}
|
||||
triggerToString(keyTurnerState.trigger, str);
|
||||
|
||||
json["trigger"] = str;
|
||||
if(_firstTunerStatePublish || keyTurnerState.trigger != lastKeyTurnerState.trigger)
|
||||
{
|
||||
publishString(mqtt_topic_lock_trigger, str);
|
||||
}
|
||||
|
||||
json["trigger"] = str;
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerToString((NukiLock::Trigger)_offTrigger, str);
|
||||
json["trigger"] = str;
|
||||
}
|
||||
|
||||
char curTime[20];
|
||||
sprintf(curTime, "%04d-%02d-%02d %02d:%02d:%02d", keyTurnerState.currentTimeYear, keyTurnerState.currentTimeMonth, keyTurnerState.currentTimeDay, keyTurnerState.currentTimeHour, keyTurnerState.currentTimeMinute, keyTurnerState.currentTimeSecond);
|
||||
@@ -346,14 +418,23 @@ void NetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurne
|
||||
json["nightModeActive"] = keyTurnerState.nightModeActive;
|
||||
|
||||
memset(&str, 0, sizeof(str));
|
||||
lockactionToString(keyTurnerState.lastLockAction, str);
|
||||
|
||||
if(_firstTunerStatePublish || keyTurnerState.lastLockAction != lastKeyTurnerState.lastLockAction)
|
||||
if(!_offConnected)
|
||||
{
|
||||
publishString(mqtt_topic_lock_last_lock_action, str);
|
||||
}
|
||||
lockactionToString(keyTurnerState.lastLockAction, str);
|
||||
|
||||
json["last_lock_action"] = str;
|
||||
if(_firstTunerStatePublish || keyTurnerState.lastLockAction != lastKeyTurnerState.lastLockAction)
|
||||
{
|
||||
publishString(mqtt_topic_lock_last_lock_action, str);
|
||||
}
|
||||
|
||||
json["last_lock_action"] = str;
|
||||
}
|
||||
else
|
||||
{
|
||||
lockactionToString((NukiLock::LockAction)_offLockAction, str);
|
||||
json["last_lock_action"] = str;
|
||||
}
|
||||
|
||||
memset(&str, 0, sizeof(str));
|
||||
triggerToString(keyTurnerState.lastLockActionTrigger, str);
|
||||
@@ -368,37 +449,45 @@ void NetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurne
|
||||
}
|
||||
|
||||
json["lock_completion_status"] = str;
|
||||
|
||||
memset(&str, 0, sizeof(str));
|
||||
NukiLock::doorSensorStateToString(keyTurnerState.doorSensorState, str);
|
||||
|
||||
if(_firstTunerStatePublish || keyTurnerState.doorSensorState != lastKeyTurnerState.doorSensorState)
|
||||
if(!_offConnected)
|
||||
{
|
||||
publishString(mqtt_topic_lock_door_sensor_state, str);
|
||||
NukiLock::doorSensorStateToString(keyTurnerState.doorSensorState, str);
|
||||
|
||||
if(_firstTunerStatePublish || keyTurnerState.doorSensorState != lastKeyTurnerState.doorSensorState)
|
||||
{
|
||||
publishString(mqtt_topic_lock_door_sensor_state, str);
|
||||
}
|
||||
|
||||
json["door_sensor_state"] = str;
|
||||
|
||||
bool critical = (keyTurnerState.criticalBatteryState & 0b00000001) > 0;
|
||||
bool charging = (keyTurnerState.criticalBatteryState & 0b00000010) > 0;
|
||||
uint8_t level = (keyTurnerState.criticalBatteryState & 0b11111100) >> 1;
|
||||
bool keypadCritical = (keyTurnerState.accessoryBatteryState & (1 << 7)) != 0 ? (keyTurnerState.accessoryBatteryState & (1 << 6)) != 0 : false;
|
||||
|
||||
jsonBattery["critical"] = critical ? "1" : "0";
|
||||
jsonBattery["charging"] = charging ? "1" : "0";
|
||||
jsonBattery["level"] = level;
|
||||
jsonBattery["keypadCritical"] = keypadCritical ? "1" : "0";
|
||||
|
||||
if((_firstTunerStatePublish || keyTurnerState.criticalBatteryState != lastKeyTurnerState.criticalBatteryState) && !_preferences->getBool(preference_disable_non_json, false))
|
||||
{
|
||||
publishBool(mqtt_topic_battery_critical, critical);
|
||||
publishBool(mqtt_topic_battery_charging, charging);
|
||||
publishInt(mqtt_topic_battery_level, level);
|
||||
}
|
||||
|
||||
if((_firstTunerStatePublish || keyTurnerState.accessoryBatteryState != lastKeyTurnerState.accessoryBatteryState) && !_preferences->getBool(preference_disable_non_json, false))
|
||||
{
|
||||
publishBool(mqtt_topic_battery_keypad_critical, keypadCritical);
|
||||
}
|
||||
}
|
||||
|
||||
json["door_sensor_state"] = str;
|
||||
|
||||
bool critical = (keyTurnerState.criticalBatteryState & 0b00000001) > 0;
|
||||
bool charging = (keyTurnerState.criticalBatteryState & 0b00000010) > 0;
|
||||
uint8_t level = (keyTurnerState.criticalBatteryState & 0b11111100) >> 1;
|
||||
bool keypadCritical = (keyTurnerState.accessoryBatteryState & (1 << 7)) != 0 ? (keyTurnerState.accessoryBatteryState & (1 << 6)) != 0 : false;
|
||||
|
||||
jsonBattery["critical"] = critical ? "1" : "0";
|
||||
jsonBattery["charging"] = charging ? "1" : "0";
|
||||
jsonBattery["level"] = level;
|
||||
jsonBattery["keypadCritical"] = keypadCritical ? "1" : "0";
|
||||
|
||||
if((_firstTunerStatePublish || keyTurnerState.criticalBatteryState != lastKeyTurnerState.criticalBatteryState) && !_preferences->getBool(preference_disable_non_json, false))
|
||||
else
|
||||
{
|
||||
publishBool(mqtt_topic_battery_critical, critical);
|
||||
publishBool(mqtt_topic_battery_charging, charging);
|
||||
publishInt(mqtt_topic_battery_level, level);
|
||||
}
|
||||
|
||||
if((_firstTunerStatePublish || keyTurnerState.accessoryBatteryState != lastKeyTurnerState.accessoryBatteryState) && !_preferences->getBool(preference_disable_non_json, false))
|
||||
{
|
||||
publishBool(mqtt_topic_battery_keypad_critical, keypadCritical);
|
||||
NukiLock::doorSensorStateToString((NukiLock::DoorSensorState)_offDoorsensorState, str);
|
||||
json["door_sensor_state"] = str;
|
||||
}
|
||||
|
||||
json["auth_id"] = _authId;
|
||||
@@ -430,12 +519,18 @@ void NetworkLock::publishState(NukiLock::LockState lockState)
|
||||
publishString(mqtt_topic_lock_binary_state, "unlocked");
|
||||
break;
|
||||
case NukiLock::LockState::Unlocked:
|
||||
case NukiLock::LockState::Unlatched:
|
||||
case NukiLock::LockState::Unlatching:
|
||||
case NukiLock::LockState::UnlockedLnga:
|
||||
publishString(mqtt_topic_lock_ha_state, "unlocked");
|
||||
publishString(mqtt_topic_lock_binary_state, "unlocked");
|
||||
break;
|
||||
case NukiLock::LockState::Unlatched:
|
||||
publishString(mqtt_topic_lock_ha_state, "open");
|
||||
publishString(mqtt_topic_lock_binary_state, "unlocked");
|
||||
break;
|
||||
case NukiLock::LockState::Unlatching:
|
||||
publishString(mqtt_topic_lock_ha_state, "opening");
|
||||
publishString(mqtt_topic_lock_binary_state, "unlocked");
|
||||
break;
|
||||
case NukiLock::LockState::Uncalibrated:
|
||||
case NukiLock::LockState::Calibration:
|
||||
case NukiLock::LockState::BootRun:
|
||||
@@ -506,22 +601,41 @@ void NetworkLock::publishAuthorizationInfo(const std::list<NukiLock::LogEntry>&
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiLock::completionStatusToString((NukiLock::CompletionStatus)log.data[3], str);
|
||||
entry["completionStatus"] = str;
|
||||
entry["completionStatusVal"] = log.data[3];
|
||||
break;
|
||||
case NukiLock::LoggingType::KeypadAction:
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiLock::lockactionToString((NukiLock::LockAction)log.data[0], str);
|
||||
entry["action"] = str;
|
||||
|
||||
switch(log.data[1])
|
||||
{
|
||||
case 0:
|
||||
entry["trigger"] = "arrowkey";
|
||||
break;
|
||||
case 1:
|
||||
entry["trigger"] = "code";
|
||||
break;
|
||||
case 2:
|
||||
entry["trigger"] = "fingerprint";
|
||||
break;
|
||||
default:
|
||||
entry["trigger"] = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiLock::completionStatusToString((NukiLock::CompletionStatus)log.data[2], str);
|
||||
entry["completionStatus"] = str;
|
||||
entry["completionStatusVal"] = log.data[2];
|
||||
|
||||
if(log.data[2] == 9) entry["completionStatus"] = "notAuthorized";
|
||||
else if (log.data[2] == 224) entry["completionStatus"] = "invalidCode";
|
||||
else
|
||||
{
|
||||
NukiLock::completionStatusToString((NukiLock::CompletionStatus)log.data[2], str);
|
||||
entry["completionStatus"] = str;
|
||||
}
|
||||
|
||||
entry["codeId"] = 256U*log.data[4]+log.data[3];
|
||||
break;
|
||||
case NukiLock::LoggingType::DoorSensor:
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiLock::lockactionToString((NukiLock::LockAction)log.data[0], str);
|
||||
|
||||
switch(log.data[0])
|
||||
{
|
||||
case 0:
|
||||
@@ -537,10 +651,6 @@ void NetworkLock::publishAuthorizationInfo(const std::list<NukiLock::LogEntry>&
|
||||
entry["action"] = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiLock::completionStatusToString((NukiLock::CompletionStatus)log.data[2], str);
|
||||
entry["completionStatus"] = str;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -622,6 +732,9 @@ void NetworkLock::publishConfig(const NukiLock::Config &config)
|
||||
|
||||
JsonDocument json;
|
||||
|
||||
memset(_nukiName, 0, sizeof(_nukiName));
|
||||
memcpy(_nukiName, config.name, sizeof(config.name));
|
||||
|
||||
json["nukiID"] = uidString;
|
||||
json["name"] = config.name;
|
||||
//json["latitude"] = config.latitude;
|
||||
@@ -742,7 +855,9 @@ void NetworkLock::publishBleAddress(const std::string &address)
|
||||
void NetworkLock::publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount)
|
||||
{
|
||||
uint index = 0;
|
||||
|
||||
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 : entries)
|
||||
@@ -833,6 +948,51 @@ void NetworkLock::publishKeypad(const std::list<NukiLock::KeypadEntry>& entries,
|
||||
sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin);
|
||||
jsonEntry["allowedUntilTime"] = allowedUntilTimeT;
|
||||
|
||||
if(_preferences->getBool(preference_keypad_topic_per_entry, false))
|
||||
{
|
||||
basePath = mqtt_topic_keypad;
|
||||
basePath.concat("/codes/");
|
||||
basePath.concat(std::to_string(index).c_str());
|
||||
jsonEntry["name_ha"] = entry.name;
|
||||
jsonEntry["index"] = index;
|
||||
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||
publishString(basePath.c_str(), _buffer);
|
||||
|
||||
String basePathPrefix = "~";
|
||||
basePathPrefix.concat(basePath);
|
||||
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||
|
||||
std::string baseCommand = std::string("{ \"action\": \"update\", \"codeId\": \"") + std::to_string(entry.codeId);
|
||||
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||
std::string mqttDeviceName = std::string("keypad_") + std::to_string(index);
|
||||
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||
char codeName[33];
|
||||
memcpy(codeName, entry.name, sizeof(entry.name));
|
||||
codeName[sizeof(entry.name)] = '\0';
|
||||
std::string displayName = std::string("Keypad - ") + std::string((char*)codeName) + " - " + std::to_string(entry.codeId);
|
||||
|
||||
_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_keypad_json_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;
|
||||
}
|
||||
|
||||
@@ -866,7 +1026,7 @@ void NetworkLock::publishKeypad(const std::list<NukiLock::KeypadEntry>& entries,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (maxKeypadCodeCount > 0)
|
||||
else
|
||||
{
|
||||
for(int i=0; i<maxKeypadCodeCount; i++)
|
||||
{
|
||||
@@ -887,14 +1047,54 @@ void NetworkLock::publishKeypad(const std::list<NukiLock::KeypadEntry>& entries,
|
||||
_network->removeTopic(codeTopic, "createdSec");
|
||||
_network->removeTopic(codeTopic, "lockCount");
|
||||
}
|
||||
|
||||
_preferences->putUInt(preference_lock_max_keypad_code_count, 0);
|
||||
|
||||
for(int j=entries.size(); j<maxKeypadCodeCount; j++)
|
||||
{
|
||||
String codesTopic = _mqttPath;
|
||||
codesTopic.concat(mqtt_topic_keypad_codes);
|
||||
codesTopic.concat("/");
|
||||
String codeTopic = "code_";
|
||||
codeTopic.concat(std::to_string(j).c_str());
|
||||
_network->removeTopic(codesTopic, codeTopic);
|
||||
std::string mqttDeviceName = std::string("keypad_") + std::to_string(j);
|
||||
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkLock::publishTimeControl(const std::list<NukiLock::TimeControlEntry>& timeControlEntries)
|
||||
void NetworkLock::publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry)
|
||||
{
|
||||
if(_preferences->getBool(preference_disable_non_json, false)) return;
|
||||
|
||||
char codeName[sizeof(entry.name) + 1];
|
||||
memset(codeName, 0, sizeof(codeName));
|
||||
memcpy(codeName, entry.name, sizeof(entry.name));
|
||||
|
||||
publishInt(concat(topic, "/id").c_str(), entry.codeId);
|
||||
publishBool(concat(topic, "/enabled").c_str(), entry.enabled);
|
||||
publishString(concat(topic, "/name").c_str(), codeName);
|
||||
|
||||
if(_preferences->getBool(preference_keypad_publish_code, false))
|
||||
{
|
||||
publishInt(concat(topic, "/code").c_str(), entry.code);
|
||||
}
|
||||
|
||||
publishInt(concat(topic, "/createdYear").c_str(), entry.dateCreatedYear);
|
||||
publishInt(concat(topic, "/createdMonth").c_str(), entry.dateCreatedMonth);
|
||||
publishInt(concat(topic, "/createdDay").c_str(), entry.dateCreatedDay);
|
||||
publishInt(concat(topic, "/createdHour").c_str(), entry.dateCreatedHour);
|
||||
publishInt(concat(topic, "/createdMin").c_str(), entry.dateCreatedMin);
|
||||
publishInt(concat(topic, "/createdSec").c_str(), entry.dateCreatedSec);
|
||||
publishInt(concat(topic, "/lockCount").c_str(), entry.lockCount);
|
||||
}
|
||||
|
||||
void NetworkLock::publishTimeControl(const std::list<NukiLock::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount)
|
||||
{
|
||||
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 : timeControlEntries)
|
||||
@@ -958,10 +1158,63 @@ void NetworkLock::publishTimeControl(const std::list<NukiLock::TimeControlEntry>
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiLock::lockactionToString(entry.lockAction, str);
|
||||
jsonEntry["lockAction"] = str;
|
||||
|
||||
if(_preferences->getBool(preference_timecontrol_topic_per_entry, false))
|
||||
{
|
||||
String basePath = mqtt_topic_timecontrol;
|
||||
basePath.concat("/entries/");
|
||||
basePath.concat(std::to_string(index).c_str());
|
||||
jsonEntry["index"] = index;
|
||||
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||
publishString(basePath.c_str(), _buffer);
|
||||
|
||||
String basePathPrefix = "~";
|
||||
basePathPrefix.concat(basePath);
|
||||
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||
|
||||
std::string baseCommand = std::string("{ \"action\": \"update\", \"entryId\": \"") + std::to_string(entry.entryId);
|
||||
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||
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,
|
||||
uidStringPostfix.c_str(),
|
||||
displayName.c_str(),
|
||||
_nukiName,
|
||||
baseTopic.c_str(),
|
||||
String("~") + basePath.c_str(),
|
||||
(char*)"SmartLock",
|
||||
"",
|
||||
"",
|
||||
"diagnostic",
|
||||
String("~") + mqtt_topic_timecontrol_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_timecontrol_json, _buffer);
|
||||
|
||||
for(int j=timeControlEntries.size(); j<maxTimeControlEntryCount; j++)
|
||||
{
|
||||
String entriesTopic = _mqttPath;
|
||||
entriesTopic.concat(mqtt_topic_timecontrol_entries);
|
||||
entriesTopic.concat("/");
|
||||
_network->removeTopic(entriesTopic, (char*)std::to_string(j).c_str());
|
||||
std::string mqttDeviceName = std::string("timecontrol_") + std::to_string(j);
|
||||
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkLock::publishConfigCommandResult(const char* result)
|
||||
@@ -995,6 +1248,11 @@ void NetworkLock::setLockActionReceivedCallback(LockActionResult (*lockActionRec
|
||||
_lockActionReceivedCallback = lockActionReceivedCallback;
|
||||
}
|
||||
|
||||
void NetworkLock::setOfficialUpdateReceivedCallback(void (*officialUpdateReceivedCallback)(const char *, const char *))
|
||||
{
|
||||
_officialUpdateReceivedCallback = officialUpdateReceivedCallback;
|
||||
}
|
||||
|
||||
void NetworkLock::setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char *))
|
||||
{
|
||||
_configUpdateReceivedCallback = configUpdateReceivedCallback;
|
||||
@@ -1016,10 +1274,15 @@ void NetworkLock::setTimeControlCommandReceivedCallback(void (*timeControlComman
|
||||
_timeControlCommandReceivedReceivedCallback = timeControlCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NetworkLock::buildMqttPath(const char* path, char* outPath)
|
||||
void NetworkLock::buildMqttPath(const char* path, char* outPath, bool offPath)
|
||||
{
|
||||
int offset = 0;
|
||||
for(const char& c : _mqttPath)
|
||||
char inPath[181] = {0};
|
||||
|
||||
if(offPath) memcpy(inPath, _offMqttPath, sizeof(_offMqttPath));
|
||||
else memcpy(inPath, _mqttPath, sizeof(_mqttPath));
|
||||
|
||||
for(const char& c : inPath)
|
||||
{
|
||||
if(c == 0x00)
|
||||
{
|
||||
@@ -1038,10 +1301,10 @@ void NetworkLock::buildMqttPath(const char* path, char* outPath)
|
||||
outPath[i+1] = 0x00;
|
||||
}
|
||||
|
||||
bool NetworkLock::comparePrefixedPath(const char *fullPath, const char *subPath)
|
||||
bool NetworkLock::comparePrefixedPath(const char *fullPath, const char *subPath, bool offPath)
|
||||
{
|
||||
char prefixedPath[500];
|
||||
buildMqttPath(subPath, prefixedPath);
|
||||
buildMqttPath(subPath, prefixedPath, offPath);
|
||||
return strcmp(fullPath, prefixedPath) == 0;
|
||||
}
|
||||
|
||||
@@ -1087,76 +1350,55 @@ void NetworkLock::removeHASSConfig(char *uidString)
|
||||
_network->removeHASSConfig(uidString);
|
||||
}
|
||||
|
||||
void NetworkLock::publishFloat(const char *topic, const float value, const uint8_t precision)
|
||||
void NetworkLock::publishOffAction(const int value)
|
||||
{
|
||||
_network->publishFloat(_mqttPath, topic, value, precision);
|
||||
_network->publishInt(_offMqttPath, mqtt_topic_official_lock_action, value, false);
|
||||
}
|
||||
|
||||
void NetworkLock::publishInt(const char *topic, const int value)
|
||||
void NetworkLock::publishFloat(const char *topic, const float value, const uint8_t precision, bool retain)
|
||||
{
|
||||
_network->publishInt(_mqttPath, topic, value);
|
||||
_network->publishFloat(_mqttPath, topic, value, precision, retain);
|
||||
}
|
||||
|
||||
void NetworkLock::publishUInt(const char *topic, const unsigned int value)
|
||||
void NetworkLock::publishInt(const char *topic, const int value, bool retain)
|
||||
{
|
||||
_network->publishUInt(_mqttPath, topic, value);
|
||||
_network->publishInt(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
void NetworkLock::publishBool(const char *topic, const bool value)
|
||||
void NetworkLock::publishUInt(const char *topic, const unsigned int value, bool retain)
|
||||
{
|
||||
_network->publishBool(_mqttPath, topic, value);
|
||||
_network->publishUInt(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
bool NetworkLock::publishString(const char *topic, const String &value)
|
||||
void NetworkLock::publishBool(const char *topic, const bool value, bool retain)
|
||||
{
|
||||
_network->publishBool(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
bool NetworkLock::publishString(const char *topic, const String &value, bool retain)
|
||||
{
|
||||
char str[value.length() + 1];
|
||||
memset(str, 0, sizeof(str));
|
||||
memcpy(str, value.begin(), value.length());
|
||||
return publishString(topic, str);
|
||||
return publishString(topic, str, retain);
|
||||
}
|
||||
|
||||
bool NetworkLock::publishString(const char *topic, const std::string &value)
|
||||
bool NetworkLock::publishString(const char *topic, const std::string &value, bool retain)
|
||||
{
|
||||
char str[value.size() + 1];
|
||||
memset(str, 0, sizeof(str));
|
||||
memcpy(str, value.data(), value.length());
|
||||
return publishString(topic, str);
|
||||
return publishString(topic, str, retain);
|
||||
}
|
||||
|
||||
bool NetworkLock::publishString(const char *topic, const char *value)
|
||||
bool NetworkLock::publishString(const char *topic, const char *value, bool retain)
|
||||
{
|
||||
return _network->publishString(_mqttPath, topic, value);
|
||||
return _network->publishString(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
void NetworkLock::publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry)
|
||||
void NetworkLock::publishULong(const char *topic, const unsigned long value, bool retain)
|
||||
{
|
||||
if(_preferences->getBool(preference_disable_non_json, false)) return;
|
||||
|
||||
char codeName[sizeof(entry.name) + 1];
|
||||
memset(codeName, 0, sizeof(codeName));
|
||||
memcpy(codeName, entry.name, sizeof(entry.name));
|
||||
|
||||
publishInt(concat(topic, "/id").c_str(), entry.codeId);
|
||||
publishBool(concat(topic, "/enabled").c_str(), entry.enabled);
|
||||
publishString(concat(topic, "/name").c_str(), codeName);
|
||||
|
||||
if(_preferences->getBool(preference_keypad_publish_code, false))
|
||||
{
|
||||
publishInt(concat(topic, "/code").c_str(), entry.code);
|
||||
}
|
||||
|
||||
publishInt(concat(topic, "/createdYear").c_str(), entry.dateCreatedYear);
|
||||
publishInt(concat(topic, "/createdMonth").c_str(), entry.dateCreatedMonth);
|
||||
publishInt(concat(topic, "/createdDay").c_str(), entry.dateCreatedDay);
|
||||
publishInt(concat(topic, "/createdHour").c_str(), entry.dateCreatedHour);
|
||||
publishInt(concat(topic, "/createdMin").c_str(), entry.dateCreatedMin);
|
||||
publishInt(concat(topic, "/createdSec").c_str(), entry.dateCreatedSec);
|
||||
publishInt(concat(topic, "/lockCount").c_str(), entry.lockCount);
|
||||
}
|
||||
|
||||
void NetworkLock::publishULong(const char *topic, const unsigned long value)
|
||||
{
|
||||
return _network->publishULong(_mqttPath, topic, value);
|
||||
return _network->publishULong(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
String NetworkLock::concat(String a, String b)
|
||||
|
||||
@@ -37,34 +37,59 @@ public:
|
||||
void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const bool& hasDoorSensor, const bool& hasKeypad, const bool& publishAuthData, char* lockAction, char* unlockAction, char* openAction);
|
||||
void removeHASSConfig(char* uidString);
|
||||
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
||||
void publishTimeControl(const std::list<NukiLock::TimeControlEntry>& timeControlEntries);
|
||||
void publishTimeControl(const std::list<NukiLock::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount);
|
||||
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 publishOffAction(const int value);
|
||||
|
||||
void setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char* value));
|
||||
void setOfficialUpdateReceivedCallback(void (*officialUpdateReceivedCallback)(const char* path, 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 onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
||||
|
||||
void publishFloat(const char* topic, const float value, const uint8_t precision = 2, bool retain = false);
|
||||
void publishInt(const char* topic, const int value, bool retain = false);
|
||||
void publishUInt(const char* topic, const unsigned int value, bool retain = false);
|
||||
void publishULong(const char* topic, const unsigned long value, bool retain = false);
|
||||
void publishBool(const char* topic, const bool value, bool retain = false);
|
||||
bool publishString(const char* topic, const String& value, bool retain = false);
|
||||
bool publishString(const char* topic, const std::string& value, bool retain = false);
|
||||
bool publishString(const char* topic, const char* value, bool retain = false);
|
||||
|
||||
bool reconnected();
|
||||
uint8_t queryCommands();
|
||||
//uint8_t _offMode = 0;
|
||||
uint8_t _offState = 0;
|
||||
bool _offCritical = false;
|
||||
uint8_t _offChargeState = 100;
|
||||
bool _offCharging = false;
|
||||
bool _offKeypadCritical = false;
|
||||
uint8_t _offDoorsensorState = 0;
|
||||
bool _offDoorsensorCritical = false;
|
||||
bool _offConnected = false;
|
||||
uint8_t _offCommandResponse = 0;
|
||||
char* _offLockActionEvent;
|
||||
uint8_t _offLockAction = 0;
|
||||
uint8_t _offTrigger = 0;
|
||||
uint32_t _offAuthId = 0;
|
||||
uint32_t _offCodeId = 0;
|
||||
uint8_t _offContext = 0;
|
||||
uint32_t _authId = 0;
|
||||
unsigned long _offCommandExecutedTs = 0;
|
||||
NukiLock::LockAction _offCommand = (NukiLock::LockAction)0xff;
|
||||
char _nukiName[33];
|
||||
char _authName[33];
|
||||
bool _authFound = false;
|
||||
|
||||
private:
|
||||
bool comparePrefixedPath(const char* fullPath, const char* subPath);
|
||||
bool comparePrefixedPath(const char* fullPath, const char* subPath, bool offPath = false);
|
||||
|
||||
void publishFloat(const char* topic, const float value, const uint8_t precision = 2);
|
||||
void publishInt(const char* topic, const int value);
|
||||
void publishUInt(const char* topic, const unsigned int value);
|
||||
void publishULong(const char* topic, const unsigned long value);
|
||||
void publishBool(const char* topic, const bool value);
|
||||
bool publishString(const char* topic, const String& value);
|
||||
bool publishString(const char* topic, const std::string& value);
|
||||
bool publishString(const char* topic, const char* value);
|
||||
void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry);
|
||||
void buttonPressActionToString(const NukiLock::ButtonPressAction btnPressAction, char* str);
|
||||
void homeKitStatusToString(const int hkstatus, char* str);
|
||||
@@ -72,12 +97,14 @@ private:
|
||||
|
||||
String concat(String a, String b);
|
||||
|
||||
void buildMqttPath(const char* path, char* outPath);
|
||||
void buildMqttPath(const char* path, char* outPath, bool offPath = false);
|
||||
|
||||
Network* _network;
|
||||
Preferences* _preferences;
|
||||
|
||||
std::vector<char*> _offTopics;
|
||||
char _mqttPath[181] = {0};
|
||||
char _offMqttPath[181] = {0};
|
||||
|
||||
bool _firstTunerStatePublish = true;
|
||||
unsigned long _lastMaintenanceTs = 0;
|
||||
@@ -89,15 +116,13 @@ private:
|
||||
uint _keypadCommandId = 0;
|
||||
int _keypadCommandEnabled = 1;
|
||||
uint8_t _queryCommands = 0;
|
||||
uint32_t _authId = 0;
|
||||
char _authName[33];
|
||||
bool _authFound = false;
|
||||
uint32_t _lastRollingLog = 0;
|
||||
|
||||
char* _buffer;
|
||||
size_t _bufferSize;
|
||||
|
||||
LockActionResult (*_lockActionReceivedCallback)(const char* value) = nullptr;
|
||||
void (*_officialUpdateReceivedCallback)(const char* path, const char* value) = nullptr;
|
||||
void (*_configUpdateReceivedCallback)(const char* value) = nullptr;
|
||||
void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr;
|
||||
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||
|
||||
@@ -68,6 +68,18 @@ void NetworkOpener::initialize()
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_battery_keypad_critical);
|
||||
//_network->removeTopic(_mqttPath, mqtt_topic_presence);
|
||||
}
|
||||
|
||||
if(!_preferences->getBool(preference_conf_info_enabled, false))
|
||||
{
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_basic_json);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_advanced_json);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_button_enabled);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_led_enabled);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_led_brightness);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_auto_unlock);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_auto_lock);
|
||||
_network->removeTopic(_mqttPath, mqtt_topic_config_single_lock);
|
||||
}
|
||||
|
||||
if(_preferences->getBool(preference_keypad_control_enabled))
|
||||
{
|
||||
@@ -397,12 +409,15 @@ void NetworkOpener::publishState(NukiOpener::OpenerState lockState)
|
||||
publishString(mqtt_topic_lock_binary_state, "locked");
|
||||
break;
|
||||
case NukiOpener::LockState::RTOactive:
|
||||
case NukiOpener::LockState::Open:
|
||||
publishString(mqtt_topic_lock_ha_state, "unlocked");
|
||||
publishString(mqtt_topic_lock_binary_state, "unlocked");
|
||||
break;
|
||||
case NukiOpener::LockState::Open:
|
||||
publishString(mqtt_topic_lock_ha_state, "open");
|
||||
publishString(mqtt_topic_lock_binary_state, "unlocked");
|
||||
break;
|
||||
case NukiOpener::LockState::Opening:
|
||||
publishString(mqtt_topic_lock_ha_state, "unlocking");
|
||||
publishString(mqtt_topic_lock_ha_state, "opening");
|
||||
publishString(mqtt_topic_lock_binary_state, "unlocked");
|
||||
break;
|
||||
case NukiOpener::LockState::Undefined:
|
||||
@@ -479,10 +494,34 @@ void NetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::LogEntr
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiOpener::lockactionToString((NukiOpener::LockAction)log.data[0], str);
|
||||
entry["action"] = str;
|
||||
|
||||
switch(log.data[1])
|
||||
{
|
||||
case 0:
|
||||
entry["trigger"] = "arrowkey";
|
||||
break;
|
||||
case 1:
|
||||
entry["trigger"] = "code";
|
||||
break;
|
||||
case 2:
|
||||
entry["trigger"] = "fingerprint";
|
||||
break;
|
||||
default:
|
||||
entry["trigger"] = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiOpener::completionStatusToString((NukiOpener::CompletionStatus)log.data[2], str);
|
||||
entry["completionStatus"] = str;
|
||||
|
||||
if(log.data[2] == 9) entry["completionStatus"] = "notAuthorized";
|
||||
else if (log.data[2] == 224) entry["completionStatus"] = "invalidCode";
|
||||
else
|
||||
{
|
||||
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:
|
||||
switch(log.data[0] & 3)
|
||||
@@ -530,8 +569,11 @@ void NetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::LogEntr
|
||||
|
||||
entry["geofence"] = log.data[2] == 1 ? "active" : "inactive";
|
||||
entry["doorbellSuppression"] = log.data[3] == 1 ? "active" : "inactive";
|
||||
entry["soundId"] = log.data[4];
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiOpener::completionStatusToString((NukiOpener::CompletionStatus)log.data[5], str);
|
||||
entry["completionStatus"] = str;
|
||||
|
||||
entry["codeId"] = 256U*log.data[7]+log.data[6];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -605,6 +647,9 @@ void NetworkOpener::publishConfig(const NukiOpener::Config &config)
|
||||
itoa(config.nukiId, uidString, 16);
|
||||
|
||||
JsonDocument json;
|
||||
|
||||
memset(_nukiName, 0, sizeof(_nukiName));
|
||||
memcpy(_nukiName, config.name, sizeof(config.name));
|
||||
|
||||
json["nukiID"] = uidString;
|
||||
json["name"] = config.name;
|
||||
@@ -758,7 +803,9 @@ void NetworkOpener::removeHASSConfig(char* uidString)
|
||||
void NetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount)
|
||||
{
|
||||
uint index = 0;
|
||||
|
||||
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 : entries)
|
||||
@@ -771,14 +818,14 @@ void NetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& entrie
|
||||
auto jsonEntry = json.add<JsonVariant>();
|
||||
|
||||
jsonEntry["codeId"] = entry.codeId;
|
||||
jsonEntry["enabled"] = entry.enabled;
|
||||
jsonEntry["name"] = entry.name;
|
||||
|
||||
if(_preferences->getBool(preference_keypad_publish_code, false))
|
||||
{
|
||||
jsonEntry["code"] = entry.code;
|
||||
}
|
||||
|
||||
jsonEntry["enabled"] = entry.enabled;
|
||||
jsonEntry["name"] = entry.name;
|
||||
char createdDT[20];
|
||||
sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.dateCreatedYear, entry.dateCreatedMonth, entry.dateCreatedDay, entry.dateCreatedHour, entry.dateCreatedMin, entry.dateCreatedSec);
|
||||
jsonEntry["dateCreated"] = createdDT;
|
||||
@@ -849,6 +896,51 @@ void NetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& entrie
|
||||
sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin);
|
||||
jsonEntry["allowedUntilTime"] = allowedUntilTimeT;
|
||||
|
||||
if(_preferences->getBool(preference_keypad_topic_per_entry, false))
|
||||
{
|
||||
basePath = mqtt_topic_keypad;
|
||||
basePath.concat("/codes/");
|
||||
basePath.concat(std::to_string(index).c_str());
|
||||
jsonEntry["name_ha"] = entry.name;
|
||||
jsonEntry["index"] = index;
|
||||
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||
publishString(basePath.c_str(), _buffer);
|
||||
|
||||
String basePathPrefix = "~";
|
||||
basePathPrefix.concat(basePath);
|
||||
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||
|
||||
std::string baseCommand = std::string("{ \"action\": \"update\", \"codeId\": \"") + std::to_string(entry.codeId);
|
||||
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||
std::string mqttDeviceName = std::string("keypad_") + std::to_string(index);
|
||||
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||
char codeName[33];
|
||||
memcpy(codeName, entry.name, sizeof(entry.name));
|
||||
codeName[sizeof(entry.name)] = '\0';
|
||||
std::string displayName = std::string("Keypad - ") + std::string((char*)codeName) + " - " + std::to_string(entry.codeId);
|
||||
|
||||
_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_keypad_json_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;
|
||||
}
|
||||
|
||||
@@ -868,7 +960,7 @@ void NetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& entrie
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
|
||||
if(!_preferences->getBool(preference_keypad_publish_code, false))
|
||||
{
|
||||
for(int i=0; i<maxKeypadCodeCount; i++)
|
||||
@@ -882,7 +974,7 @@ void NetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& entrie
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (maxKeypadCodeCount > 0)
|
||||
else
|
||||
{
|
||||
for(int i=0; i<maxKeypadCodeCount; i++)
|
||||
{
|
||||
@@ -903,14 +995,26 @@ void NetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& entrie
|
||||
_network->removeTopic(codeTopic, "createdSec");
|
||||
_network->removeTopic(codeTopic, "lockCount");
|
||||
}
|
||||
|
||||
_preferences->putUInt(preference_lock_max_keypad_code_count, 0);
|
||||
|
||||
for(int j=entries.size(); j<maxKeypadCodeCount; j++)
|
||||
{
|
||||
String codesTopic = _mqttPath;
|
||||
codesTopic.concat(mqtt_topic_keypad_codes);
|
||||
codesTopic.concat("/");
|
||||
_network->removeTopic(codesTopic, (char*)std::to_string(j).c_str());
|
||||
std::string mqttDeviceName = std::string("keypad_") + std::to_string(j);
|
||||
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeControlEntry>& timeControlEntries)
|
||||
void NetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount)
|
||||
{
|
||||
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 : timeControlEntries)
|
||||
@@ -974,10 +1078,62 @@ void NetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeControlEn
|
||||
memset(str, 0, sizeof(str));
|
||||
NukiOpener::lockactionToString(entry.lockAction, str);
|
||||
jsonEntry["lockAction"] = str;
|
||||
|
||||
if(_preferences->getBool(preference_timecontrol_topic_per_entry, false))
|
||||
{
|
||||
String basePath = mqtt_topic_timecontrol;
|
||||
basePath.concat("/entries/");
|
||||
basePath.concat(std::to_string(index).c_str());
|
||||
jsonEntry["index"] = index;
|
||||
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||
publishString(basePath.c_str(), _buffer);
|
||||
|
||||
String basePathPrefix = "~";
|
||||
basePathPrefix.concat(basePath);
|
||||
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||
std::string baseCommand = std::string("{ \"action\": \"update\", \"entryId\": \"") + std::to_string(entry.entryId);
|
||||
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||
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,
|
||||
uidStringPostfix.c_str(),
|
||||
displayName.c_str(),
|
||||
_nukiName,
|
||||
baseTopic.c_str(),
|
||||
String("~") + basePath.c_str(),
|
||||
(char*)"Opener",
|
||||
"",
|
||||
"",
|
||||
"diagnostic",
|
||||
String("~") + mqtt_topic_timecontrol_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_timecontrol_json, _buffer);
|
||||
|
||||
for(int j=timeControlEntries.size(); j<maxTimeControlEntryCount; j++)
|
||||
{
|
||||
String entriesTopic = _mqttPath;
|
||||
entriesTopic.concat(mqtt_topic_timecontrol_entries);
|
||||
entriesTopic.concat("/");
|
||||
_network->removeTopic(entriesTopic, (char*)std::to_string(j).c_str());
|
||||
std::string mqttDeviceName = std::string("timecontrol_") + std::to_string(j);
|
||||
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkOpener::publishConfigCommandResult(const char* result)
|
||||
@@ -1032,45 +1188,45 @@ void NetworkOpener::setTimeControlCommandReceivedCallback(void (*timeControlComm
|
||||
_timeControlCommandReceivedReceivedCallback = timeControlCommandReceivedReceivedCallback;
|
||||
}
|
||||
|
||||
void NetworkOpener::publishFloat(const char *topic, const float value, const uint8_t precision)
|
||||
void NetworkOpener::publishFloat(const char *topic, const float value, const uint8_t precision, bool retain)
|
||||
{
|
||||
_network->publishFloat(_mqttPath, topic, value, precision);
|
||||
_network->publishFloat(_mqttPath, topic, value, precision, retain);
|
||||
}
|
||||
|
||||
void NetworkOpener::publishInt(const char *topic, const int value)
|
||||
void NetworkOpener::publishInt(const char *topic, const int value, bool retain)
|
||||
{
|
||||
_network->publishInt(_mqttPath, topic, value);
|
||||
_network->publishInt(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
void NetworkOpener::publishUInt(const char *topic, const unsigned int value)
|
||||
void NetworkOpener::publishUInt(const char *topic, const unsigned int value, bool retain)
|
||||
{
|
||||
_network->publishUInt(_mqttPath, topic, value);
|
||||
_network->publishUInt(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
void NetworkOpener::publishBool(const char *topic, const bool value)
|
||||
void NetworkOpener::publishBool(const char *topic, const bool value, bool retain)
|
||||
{
|
||||
_network->publishBool(_mqttPath, topic, value);
|
||||
_network->publishBool(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
void NetworkOpener::publishString(const char *topic, const String &value)
|
||||
void NetworkOpener::publishString(const char *topic, const String &value, bool retain)
|
||||
{
|
||||
char str[value.length() + 1];
|
||||
memset(str, 0, sizeof(str));
|
||||
memcpy(str, value.begin(), value.length());
|
||||
publishString(topic, str);
|
||||
publishString(topic, str, retain);
|
||||
}
|
||||
|
||||
void NetworkOpener::publishString(const char *topic, const std::string &value)
|
||||
void NetworkOpener::publishString(const char *topic, const std::string &value, bool retain)
|
||||
{
|
||||
char str[value.size() + 1];
|
||||
memset(str, 0, sizeof(str));
|
||||
memcpy(str, value.data(), value.length());
|
||||
publishString(topic, str);
|
||||
publishString(topic, str, retain);
|
||||
}
|
||||
|
||||
void NetworkOpener::publishString(const char* topic, const char* value)
|
||||
void NetworkOpener::publishString(const char* topic, const char* value, bool retain)
|
||||
{
|
||||
_network->publishString(_mqttPath, topic, value);
|
||||
_network->publishString(_mqttPath, topic, value, retain);
|
||||
}
|
||||
|
||||
void NetworkOpener::publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry)
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, const char *softwareVersion, const char *hardwareVersion, const bool& publishAuthData, const bool& hasKeypad, char* lockAction, char* unlockAction, char* openAction);
|
||||
void removeHASSConfig(char* uidString);
|
||||
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
||||
void publishTimeControl(const std::list<NukiOpener::TimeControlEntry>& timeControlEntries);
|
||||
void publishTimeControl(const std::list<NukiOpener::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount);
|
||||
void publishStatusUpdated(const bool statusUpdated);
|
||||
void publishConfigCommandResult(const char* result);
|
||||
void publishKeypadCommandResult(const char* result);
|
||||
@@ -50,17 +50,18 @@ public:
|
||||
|
||||
bool reconnected();
|
||||
uint8_t queryCommands();
|
||||
char _nukiName[33];
|
||||
|
||||
private:
|
||||
bool comparePrefixedPath(const char* fullPath, const char* subPath);
|
||||
|
||||
void publishFloat(const char* topic, const float value, const uint8_t precision = 2);
|
||||
void publishInt(const char* topic, const int value);
|
||||
void publishUInt(const char* topic, const unsigned int value);
|
||||
void publishBool(const char* topic, const bool value);
|
||||
void publishString(const char* topic, const String& value);
|
||||
void publishString(const char* topic, const std::string& value);
|
||||
void publishString(const char* topic, const char* value);
|
||||
void publishFloat(const char* topic, const float value, const uint8_t precision = 2, bool retain = false);
|
||||
void publishInt(const char* topic, const int value, bool retain = false);
|
||||
void publishUInt(const char* topic, const unsigned int value, bool retain = false);
|
||||
void publishBool(const char* topic, const bool value, bool retain = false);
|
||||
void publishString(const char* topic, const String& value, bool retain = false);
|
||||
void publishString(const char* topic, const std::string& value, bool retain = false);
|
||||
void publishString(const char* topic, const char* value, bool retain = false);
|
||||
void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry);
|
||||
|
||||
void buildMqttPath(const char* path, char* outPath);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -100,6 +100,7 @@ private:
|
||||
int _restartBeaconTimeout = 0; // seconds
|
||||
bool _publishAuthData = false;
|
||||
bool _clearAuthData = false;
|
||||
bool _taskRunning = false;
|
||||
int _nrOfRetries = 0;
|
||||
int _retryDelay = 0;
|
||||
int _retryCount = 0;
|
||||
@@ -107,7 +108,6 @@ private:
|
||||
int _retryLockstateCount = 0;
|
||||
unsigned long _nextRetryTs = 0;
|
||||
std::vector<uint16_t> _keypadCodeIds;
|
||||
std::vector<uint32_t> _keypadCodes;
|
||||
std::vector<uint8_t> _timeControlIds;
|
||||
|
||||
NukiOpener::OpenerState _lastKeyTurnerState;
|
||||
@@ -128,6 +128,7 @@ private:
|
||||
bool _hasKeypad = false;
|
||||
bool _keypadEnabled = false;
|
||||
uint _maxKeypadCodeCount = 0;
|
||||
uint _maxTimeControlEntryCount = 0;
|
||||
bool _configRead = false;
|
||||
long _rssiPublishInterval = 0;
|
||||
unsigned long _nextLockStateUpdateTs = 0;
|
||||
|
||||
2270
src/NukiWrapper.cpp
2270
src/NukiWrapper.cpp
File diff suppressed because it is too large
Load Diff
@@ -46,12 +46,14 @@ public:
|
||||
|
||||
private:
|
||||
static LockActionResult onLockActionReceivedCallback(const char* value);
|
||||
static void onOfficialUpdateReceivedCallback(const char* topic, const char* value);
|
||||
static void onConfigUpdateReceivedCallback(const char* value);
|
||||
static void onKeypadCommandReceivedCallback(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||
static void onKeypadJsonCommandReceivedCallback(const char* value);
|
||||
static void onTimeControlCommandReceivedCallback(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);
|
||||
@@ -88,14 +90,15 @@ private:
|
||||
Gpio* _gpio = nullptr;
|
||||
Preferences* _preferences;
|
||||
int _intervalLockstate = 0; // seconds
|
||||
int _intervalHybridLockstate = 0; // seconds
|
||||
int _intervalBattery = 0; // seconds
|
||||
int _intervalConfig = 60 * 60; // seconds
|
||||
int _intervalKeypad = 0; // seconds
|
||||
int _restartBeaconTimeout = 0; // seconds
|
||||
bool _publishAuthData = false;
|
||||
bool _clearAuthData = false;
|
||||
bool _taskRunning = false;
|
||||
std::vector<uint16_t> _keypadCodeIds;
|
||||
std::vector<uint32_t> _keypadCodes;
|
||||
std::vector<uint8_t> _timeControlIds;
|
||||
|
||||
NukiLock::KeyTurnerState _lastKeyTurnerState;
|
||||
@@ -116,6 +119,7 @@ private:
|
||||
bool _hasKeypad = false;
|
||||
bool _keypadEnabled = false;
|
||||
uint _maxKeypadCodeCount = 0;
|
||||
uint _maxTimeControlEntryCount = 0;
|
||||
bool _configRead = false;
|
||||
int _nrOfRetries = 0;
|
||||
int _retryDelay = 0;
|
||||
@@ -123,8 +127,9 @@ private:
|
||||
int _retryConfigCount = 0;
|
||||
int _retryLockstateCount = 0;
|
||||
long _rssiPublishInterval = 0;
|
||||
unsigned long _nextRetryTs = 0;
|
||||
unsigned long _nextRetryTs = 0;
|
||||
unsigned long _nextLockStateUpdateTs = 0;
|
||||
unsigned long _nextHybridLockStateUpdateTs = 0;
|
||||
unsigned long _nextBatteryReportTs = 0;
|
||||
unsigned long _nextConfigUpdateTs = 0;
|
||||
unsigned long _waitAuthLogUpdateTs = 0;
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#define preference_check_updates (char*)"checkupdates"
|
||||
#define preference_lock_max_keypad_code_count (char*)"maxkpad"
|
||||
#define preference_opener_max_keypad_code_count (char*)"opmaxkpad"
|
||||
#define preference_lock_max_timecontrol_entry_count (char*)"maxtc"
|
||||
#define preference_opener_max_timecontrol_entry_count (char*)"opmaxtc"
|
||||
#define preference_mqtt_ca (char*)"mqttca"
|
||||
#define preference_mqtt_crt (char*)"mqttcrt"
|
||||
#define preference_mqtt_key (char*)"mqttkey"
|
||||
@@ -50,17 +52,21 @@
|
||||
#define preference_query_interval_keypad (char*)"kpInterval"
|
||||
#define preference_access_level (char*)"accLvl"
|
||||
#define preference_keypad_info_enabled (char*)"kpInfoEnabled"
|
||||
#define preference_keypad_topic_per_entry (char*)"kpPerEntry"
|
||||
#define preference_keypad_control_enabled (char*)"kpCntrlEnabled"
|
||||
#define preference_keypad_publish_code (char*)"kpPubCode"
|
||||
#define preference_timecontrol_control_enabled (char*)"tcCntrlEnabled"
|
||||
#define preference_timecontrol_topic_per_entry (char*)"tcPerEntry"
|
||||
#define preference_timecontrol_info_enabled (char*)"tcInfoEnabled"
|
||||
#define preference_publish_authdata (char*)"pubAuth"
|
||||
#define preference_acl (char*)"aclLckOpn"
|
||||
#define preference_conf_info_enabled (char*)"cnfInfoEnabled"
|
||||
#define preference_conf_lock_basic_acl (char*)"confLckBasAcl"
|
||||
#define preference_conf_lock_advanced_acl (char*)"confLckAdvAcl"
|
||||
#define preference_conf_opener_basic_acl (char*)"confOpnBasAcl"
|
||||
#define preference_conf_opener_advanced_acl (char*)"confOpnAdvAcl"
|
||||
#define preference_register_as_app (char*)"regAsApp" // true = register as hub; false = register as app
|
||||
#define preference_register_opener_as_app (char*)"regOpnAsApp"
|
||||
#define preference_command_nr_of_retries (char*)"nrRetry"
|
||||
#define preference_command_retry_delay (char*)"rtryDelay"
|
||||
#define preference_cred_user (char*)"crdusr"
|
||||
@@ -84,6 +90,10 @@
|
||||
#define preference_enable_bootloop_reset (char*)"enabtlprst"
|
||||
#define preference_buffer_size (char*)"buffsize"
|
||||
#define preference_disable_non_json (char*)"disnonjson"
|
||||
#define preference_official_hybrid (char*)"offHybrid"
|
||||
#define preference_official_hybrid_actions (char*)"hybridAct"
|
||||
#define preference_official_hybrid_retry (char*)"hybridRtry"
|
||||
#define preference_query_interval_hybrid_lockstate (char*)"hybridTimer"
|
||||
|
||||
class DebugPreferences
|
||||
{
|
||||
@@ -92,18 +102,18 @@ private:
|
||||
{
|
||||
preference_started_before, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates, preference_webserver_enabled,
|
||||
preference_lock_enabled, preference_lock_pin_status, preference_mqtt_lock_path, preference_opener_enabled, preference_opener_pin_status,
|
||||
preference_opener_continuous_mode, preference_mqtt_opener_path, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count,
|
||||
preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url,
|
||||
preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address, preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server,
|
||||
preference_network_hardware, preference_network_wifi_fallback_disabled, preference_rssi_publish_interval, preference_hostname, preference_find_best_rssi,
|
||||
preference_network_timeout, preference_restart_on_disconnect, preference_restart_ble_beacon_lost, preference_query_interval_lockstate,
|
||||
preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled,
|
||||
preference_keypad_info_enabled, preference_keypad_publish_code, preference_acl, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled,
|
||||
preference_conf_lock_basic_acl, preference_conf_lock_advanced_acl, preference_conf_opener_basic_acl, preference_conf_opener_advanced_acl,
|
||||
preference_access_level, preference_register_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user,
|
||||
preference_cred_password, preference_publish_authdata, preference_publish_debug_info, preference_presence_detection_timeout, preference_disable_non_json,
|
||||
preference_has_mac_saved, preference_has_mac_byte_0, preference_has_mac_byte_1, preference_has_mac_byte_2, preference_latest_version,
|
||||
preference_task_size_network, preference_task_size_nuki, preference_task_size_pd, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries
|
||||
preference_opener_continuous_mode, preference_mqtt_opener_path, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_lock_max_timecontrol_entry_count,
|
||||
preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery,
|
||||
preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address, preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server,
|
||||
preference_network_hardware, preference_network_wifi_fallback_disabled, preference_rssi_publish_interval, preference_hostname, preference_find_best_rssi,
|
||||
preference_network_timeout, preference_restart_on_disconnect, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_timecontrol_topic_per_entry,
|
||||
preference_keypad_topic_per_entry, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled,
|
||||
preference_keypad_info_enabled, preference_keypad_publish_code, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_conf_info_enabled,
|
||||
preference_register_as_app, preference_register_opener_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user, preference_cred_password,
|
||||
preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, preference_presence_detection_timeout, preference_official_hybrid, preference_query_interval_hybrid_lockstate,
|
||||
preference_official_hybrid_actions, preference_official_hybrid_retry, preference_has_mac_saved, preference_has_mac_byte_0, preference_has_mac_byte_1, preference_has_mac_byte_2,
|
||||
preference_latest_version, preference_task_size_network, preference_task_size_nuki, preference_task_size_pd, preference_authlog_max_entries, preference_keypad_max_entries,
|
||||
preference_timecontrol_max_entries
|
||||
};
|
||||
std::vector<char*> _redact =
|
||||
{
|
||||
@@ -115,9 +125,10 @@ private:
|
||||
std::vector<char*> _boolPrefs =
|
||||
{
|
||||
preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode,
|
||||
preference_enable_bootloop_reset, preference_webserver_enabled, preference_find_best_rssi, preference_restart_on_disconnect, preference_keypad_control_enabled,
|
||||
preference_keypad_info_enabled, preference_keypad_publish_code, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_register_as_app,
|
||||
preference_ip_dhcp_enabled, preference_publish_authdata, preference_has_mac_saved, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_disable_non_json
|
||||
preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_enable_bootloop_reset, preference_webserver_enabled, preference_find_best_rssi, preference_restart_on_disconnect,
|
||||
preference_keypad_control_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, 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_has_mac_saved, 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
|
||||
};
|
||||
|
||||
const bool isRedacted(const char* key) const
|
||||
|
||||
@@ -430,6 +430,28 @@ bool WebCfgServer::processArgs(String& message)
|
||||
_preferences->putBool(preference_check_updates, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "OFFHYBRID")
|
||||
{
|
||||
_preferences->putBool(preference_official_hybrid, (value == "1"));
|
||||
if((value == "1")) _preferences->putBool(preference_register_as_app, true);
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "HYBRIDACT")
|
||||
{
|
||||
_preferences->putBool(preference_official_hybrid_actions, (value == "1"));
|
||||
if(value == "1") _preferences->putBool(preference_register_as_app, true);
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "HYBRIDTIMER")
|
||||
{
|
||||
_preferences->putInt(preference_query_interval_hybrid_lockstate, value.toInt());
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "HYBRIDRETRY")
|
||||
{
|
||||
_preferences->putBool(preference_official_hybrid_retry, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "DISNONJSON")
|
||||
{
|
||||
_preferences->putBool(preference_disable_non_json, (value == "1"));
|
||||
@@ -565,6 +587,11 @@ bool WebCfgServer::processArgs(String& message)
|
||||
{
|
||||
aclLvlChanged = true;
|
||||
}
|
||||
else if(key == "CONFPUB")
|
||||
{
|
||||
_preferences->putBool(preference_conf_info_enabled, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "KPPUB")
|
||||
{
|
||||
_preferences->putBool(preference_keypad_info_enabled, (value == "1"));
|
||||
@@ -585,6 +612,16 @@ bool WebCfgServer::processArgs(String& message)
|
||||
_preferences->putBool(preference_timecontrol_info_enabled, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "KPPER")
|
||||
{
|
||||
_preferences->putBool(preference_keypad_topic_per_entry, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "TCPER")
|
||||
{
|
||||
_preferences->putBool(preference_timecontrol_topic_per_entry, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "TCENA")
|
||||
{
|
||||
_preferences->putBool(preference_timecontrol_control_enabled, (value == "1"));
|
||||
@@ -956,6 +993,11 @@ bool WebCfgServer::processArgs(String& message)
|
||||
_preferences->putBool(preference_register_as_app, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "REGAPPOPN")
|
||||
{
|
||||
_preferences->putBool(preference_register_opener_as_app, (value == "1"));
|
||||
configChanged = true;
|
||||
}
|
||||
else if(key == "LOCKENA")
|
||||
{
|
||||
_preferences->putBool(preference_lock_enabled, (value == "1"));
|
||||
@@ -1177,9 +1219,9 @@ void WebCfgServer::buildCredHtml(String &response)
|
||||
response.concat("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Credentials</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, false, true);
|
||||
printInputField(response, "CREDPASS", "Password", "*", 30, true, true);
|
||||
printInputField(response, "CREDPASSRE", "Retype password", "*", 30, true);
|
||||
printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, "", false, true);
|
||||
printInputField(response, "CREDPASS", "Password", "*", 30, "", true, true);
|
||||
printInputField(response, "CREDPASSRE", "Retype password", "*", 30, "", true);
|
||||
response.concat("</table>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
@@ -1189,7 +1231,7 @@ void WebCfgServer::buildCredHtml(String &response)
|
||||
response.concat("<br><br><form class=\"adapt\" method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Nuki Lock PIN</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "NUKIPIN", "PIN Code (# to clear)", "*", 20, true);
|
||||
printInputField(response, "NUKIPIN", "PIN Code (# to clear)", "*", 20, "", true);
|
||||
response.concat("</table>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
@@ -1200,7 +1242,7 @@ void WebCfgServer::buildCredHtml(String &response)
|
||||
response.concat("<br><br><form class=\"adapt\" method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Nuki Opener PIN</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "NUKIOPPIN", "PIN Code (# to clear)", "*", 20, true);
|
||||
printInputField(response, "NUKIOPPIN", "PIN Code (# to clear)", "*", 20, "", true);
|
||||
response.concat("</table>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
@@ -1215,7 +1257,7 @@ void WebCfgServer::buildCredHtml(String &response)
|
||||
String message = "Type ";
|
||||
message.concat(_confirmCode);
|
||||
message.concat(" to confirm unpair");
|
||||
printInputField(response, "CONFIRMTOKEN", message.c_str(), "", 10);
|
||||
printInputField(response, "CONFIRMTOKEN", message.c_str(), "", 10, "");
|
||||
response.concat("</table>");
|
||||
response.concat("<br><button type=\"submit\">OK</button></form>");
|
||||
}
|
||||
@@ -1228,7 +1270,7 @@ void WebCfgServer::buildCredHtml(String &response)
|
||||
String message = "Type ";
|
||||
message.concat(_confirmCode);
|
||||
message.concat(" to confirm unpair");
|
||||
printInputField(response, "CONFIRMTOKEN", message.c_str(), "", 10);
|
||||
printInputField(response, "CONFIRMTOKEN", message.c_str(), "", 10, "");
|
||||
response.concat("</table>");
|
||||
response.concat("<br><button type=\"submit\">OK</button></form>");
|
||||
}
|
||||
@@ -1240,7 +1282,7 @@ void WebCfgServer::buildCredHtml(String &response)
|
||||
String message = "Type ";
|
||||
message.concat(_confirmCode);
|
||||
message.concat(" to confirm factory reset");
|
||||
printInputField(response, "CONFIRMTOKEN", message.c_str(), "", 10);
|
||||
printInputField(response, "CONFIRMTOKEN", message.c_str(), "", 10, "");
|
||||
printCheckBox(response, "WIFI", "Also reset WiFi settings", false, "");
|
||||
response.concat("</table>");
|
||||
response.concat("<br><button type=\"submit\">OK</button></form>");
|
||||
@@ -1304,17 +1346,17 @@ void WebCfgServer::buildMqttConfigHtml(String &response)
|
||||
response.concat("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
|
||||
response.concat("<h3>Basic MQTT and Network Configuration</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100);
|
||||
printInputField(response, "MQTTSERVER", "MQTT Broker", _preferences->getString(preference_mqtt_broker).c_str(), 100);
|
||||
printInputField(response, "MQTTPORT", "MQTT Broker port", _preferences->getInt(preference_mqtt_broker_port), 5);
|
||||
printInputField(response, "MQTTUSER", "MQTT User (# to clear)", _preferences->getString(preference_mqtt_user).c_str(), 30, false, true);
|
||||
printInputField(response, "MQTTPASS", "MQTT Password", "*", 30, true, true);
|
||||
printInputField(response, "HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100, "");
|
||||
printInputField(response, "MQTTSERVER", "MQTT Broker", _preferences->getString(preference_mqtt_broker).c_str(), 100, "");
|
||||
printInputField(response, "MQTTPORT", "MQTT Broker port", _preferences->getInt(preference_mqtt_broker_port), 5, "");
|
||||
printInputField(response, "MQTTUSER", "MQTT User (# to clear)", _preferences->getString(preference_mqtt_user).c_str(), 30, "", false, true);
|
||||
printInputField(response, "MQTTPASS", "MQTT Password", "*", 30, "", true, true);
|
||||
response.concat("</table><br>");
|
||||
|
||||
response.concat("<h3>Advanced MQTT and Network Configuration</h3>");
|
||||
response.concat("<table>");
|
||||
printInputField(response, "HASSDISCOVERY", "Home Assistant discovery topic (empty to disable; usually homeassistant)", _preferences->getString(preference_mqtt_hass_discovery).c_str(), 30);
|
||||
printInputField(response, "HASSCUURL", "Home Assistant device configuration URL (empty to use http://LOCALIP; fill when using a reverse proxy for example)", _preferences->getString(preference_mqtt_hass_cu_url).c_str(), 261);
|
||||
printInputField(response, "HASSDISCOVERY", "Home Assistant discovery topic (empty to disable; usually homeassistant)", _preferences->getString(preference_mqtt_hass_discovery).c_str(), 30, "");
|
||||
printInputField(response, "HASSCUURL", "Home Assistant device configuration URL (empty to use http://LOCALIP; fill when using a reverse proxy for example)", _preferences->getString(preference_mqtt_hass_cu_url).c_str(), 261, "");
|
||||
if(_nukiOpener != nullptr) printCheckBox(response, "OPENERCONT", "Set Nuki Opener Lock/Unlock action in Home Assistant to Continuous mode", _preferences->getBool(preference_opener_continuous_mode), "");
|
||||
printTextarea(response, "MQTTCA", "MQTT SSL CA Certificate (*, optional)", _preferences->getString(preference_mqtt_ca).c_str(), TLS_CA_MAX_SIZE, _network->encryptionSupported(), true);
|
||||
printTextarea(response, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", _preferences->getString(preference_mqtt_crt).c_str(), TLS_CERT_MAX_SIZE, _network->encryptionSupported(), true);
|
||||
@@ -1322,22 +1364,26 @@ void WebCfgServer::buildMqttConfigHtml(String &response)
|
||||
printDropDown(response, "NWHW", "Network hardware", String(_preferences->getInt(preference_network_hardware)), getNetworkDetectionOptions());
|
||||
printCheckBox(response, "NWHWWIFIFB", "Disable fallback to Wi-Fi / Wi-Fi config portal", _preferences->getBool(preference_network_wifi_fallback_disabled), "");
|
||||
printCheckBox(response, "BESTRSSI", "Connect to AP with the best signal in an environment with multiple APs with the same SSID", _preferences->getBool(preference_find_best_rssi), "");
|
||||
printInputField(response, "RSSI", "RSSI Publish interval (seconds; -1 to disable)", _preferences->getInt(preference_rssi_publish_interval), 6);
|
||||
printInputField(response, "NETTIMEOUT", "Network Timeout until restart (seconds; -1 to disable)", _preferences->getInt(preference_network_timeout), 5);
|
||||
printInputField(response, "RSSI", "RSSI Publish interval (seconds; -1 to disable)", _preferences->getInt(preference_rssi_publish_interval), 6, "");
|
||||
printInputField(response, "NETTIMEOUT", "Network Timeout until restart (seconds; -1 to disable)", _preferences->getInt(preference_network_timeout), 5, "");
|
||||
printCheckBox(response, "RSTDISC", "Restart on disconnect", _preferences->getBool(preference_restart_on_disconnect), "");
|
||||
printCheckBox(response, "MQTTLOG", "Enable MQTT logging", _preferences->getBool(preference_mqtt_log_enabled), "");
|
||||
printCheckBox(response, "CHECKUPDATE", "Check for Firmware Updates every 24h", _preferences->getBool(preference_check_updates), "");
|
||||
printCheckBox(response, "DISNONJSON", "Disable some extraneous non-JSON topics", _preferences->getBool(preference_disable_non_json), "");
|
||||
printCheckBox(response, "OFFHYBRID", "Enable hybrid official MQTT and Nuki Hub setup", _preferences->getBool(preference_official_hybrid), "");
|
||||
printCheckBox(response, "HYBRIDACT", "Enable sending actions through official MQTT", _preferences->getBool(preference_official_hybrid_actions), "");
|
||||
printInputField(response, "HYBRIDTIMER", "Time between status updates when official MQTT is offline (seconds)", _preferences->getInt(preference_query_interval_hybrid_lockstate), 5, "");
|
||||
printCheckBox(response, "HYBRIDRETRY", "Retry command sent using official MQTT over BLE if failed", _preferences->getBool(preference_official_hybrid_retry), "");
|
||||
response.concat("</table>");
|
||||
response.concat("* If no encryption is configured for the MQTT broker, leave empty. Only supported for Wi-Fi connections.<br><br>");
|
||||
|
||||
response.concat("<h3>IP Address assignment</h3>");
|
||||
response.concat("<table>");
|
||||
printCheckBox(response, "DHCPENA", "Enable DHCP", _preferences->getBool(preference_ip_dhcp_enabled), "");
|
||||
printInputField(response, "IPADDR", "Static IP address", _preferences->getString(preference_ip_address).c_str(), 15);
|
||||
printInputField(response, "IPSUB", "Subnet", _preferences->getString(preference_ip_subnet).c_str(), 15);
|
||||
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);
|
||||
printInputField(response, "IPADDR", "Static IP address", _preferences->getString(preference_ip_address).c_str(), 15, "");
|
||||
printInputField(response, "IPSUB", "Subnet", _preferences->getString(preference_ip_subnet).c_str(), 15, "");
|
||||
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.concat("</table>");
|
||||
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
@@ -1356,18 +1402,20 @@ void WebCfgServer::buildAdvancedConfigHtml(String &response)
|
||||
response.concat(_preferences->getBool(preference_enable_bootloop_reset, false) ? "Enabled" : "Disabled");
|
||||
response.concat("</td></tr>");
|
||||
printCheckBox(response, "BTLPRST", "Enable Bootloop prevention (Try to reset these settings to default on bootloop)", true, "");
|
||||
printInputField(response, "BUFFSIZE", "Char buffer size (min 4096, max 32768)", _preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE), 6);
|
||||
printInputField(response, "TSKNTWK", "Task size Network (min 12288, max 32768)", _preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), 6);
|
||||
printInputField(response, "TSKNUKI", "Task size Nuki (min 8192, max 32768)", _preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), 6);
|
||||
printInputField(response, "TSKPD", "Task size Presence Detection (min 1024, max 4048)", _preferences->getInt(preference_task_size_pd, PD_TASK_SIZE), 6);
|
||||
printInputField(response, "ALMAX", "Max auth log entries (min 1, max 50)", _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 3);
|
||||
printInputField(response, "KPMAX", "Max keypad entries (min 1, max 100)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3);
|
||||
printInputField(response, "TCMAX", "Max timecontrol entries (min 1, max 50)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3);
|
||||
printInputField(response, "BUFFSIZE", "Char buffer size (min 4096, max 32768)", _preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE), 6, "");
|
||||
printInputField(response, "TSKNTWK", "Task size Network (min 12288, max 32768)", _preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), 6, "");
|
||||
printInputField(response, "TSKNUKI", "Task size Nuki (min 8192, max 32768)", _preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), 6, "");
|
||||
printInputField(response, "TSKPD", "Task size Presence Detection (min 1024, max 4048)", _preferences->getInt(preference_task_size_pd, PD_TASK_SIZE), 6, "");
|
||||
printInputField(response, "ALMAX", "Max auth log entries (min 1, max 50)", _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 3, "inputmaxauthlog");
|
||||
printInputField(response, "KPMAX", "Max keypad entries (min 1, max 100)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3, "inputmaxkeypad");
|
||||
printInputField(response, "TCMAX", "Max timecontrol entries (min 1, max 50)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3, "inputmaxtimecontrol");
|
||||
response.concat("<tr><td>Advised minimum char buffer size based on current settings</td><td id=\"mincharbuffer\"></td>");
|
||||
response.concat("<tr><td>Advised minimum network task size based on current settings</td><td id=\"minnetworktask\"></td>");
|
||||
response.concat("</table>");
|
||||
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
response.concat("</body></html>");
|
||||
response.concat("</body><script>window.onload=function(){ document.getElementById(\"inputmaxauthlog\").addEventListener(\"keyup\", calculate);document.getElementById(\"inputmaxkeypad\").addEventListener(\"keyup\", calculate);document.getElementById(\"inputmaxtimecontrol\").addEventListener(\"keyup\", calculate); calculate(); }; function calculate() { var authlog = document.getElementById(\"inputmaxauthlog\").value; var keypad = document.getElementById(\"inputmaxkeypad\").value; var timecontrol = document.getElementById(\"inputmaxtimecontrol\").value; var charbuf = 0; var networktask = 0; var sizeauthlog = 0; var sizekeypad = 0; var sizetimecontrol = 0; if(authlog > 0) { sizeauthlog = 280 * authlog; } if(keypad > 0) { sizekeypad = 350 * keypad; } if(timecontrol > 0) { sizetimecontrol = 120 * timecontrol; } charbuf = sizetimecontrol; networktask = 10240 + sizetimecontrol; if(sizeauthlog>sizekeypad && sizeauthlog>sizetimecontrol) { charbuf = sizeauthlog; networktask = 10240 + sizeauthlog;} else if(sizekeypad>sizeauthlog && sizekeypad>sizetimecontrol) { charbuf = sizekeypad; networktask = 10240 + sizekeypad;} if(charbuf<4096) { charbuf = 4096; } else if (charbuf>32768) { charbuf = 32768; } if(networktask<12288) { networktask = 12288; } else if (networktask>32768) { networktask = 32768; } document.getElementById(\"mincharbuffer\").innerHTML = charbuf; document.getElementById(\"minnetworktask\").innerHTML = networktask; }</script></html>");
|
||||
}
|
||||
|
||||
void WebCfgServer::buildStatusHtml(String &response)
|
||||
@@ -1400,7 +1448,7 @@ void WebCfgServer::buildStatusHtml(String &response)
|
||||
if(_nuki->isPaired())
|
||||
{
|
||||
json["lockPin"] = pinStateToString(_preferences->getInt(preference_lock_pin_status, 4));
|
||||
lockDone = true;
|
||||
if(strcmp(lockStateArr, "undefined") != 0) lockDone = true;
|
||||
}
|
||||
else json["lockPin"] = "Not Paired";
|
||||
}
|
||||
@@ -1419,7 +1467,7 @@ void WebCfgServer::buildStatusHtml(String &response)
|
||||
if(_nukiOpener->isPaired())
|
||||
{
|
||||
json["openerPin"] = pinStateToString(_preferences->getInt(preference_opener_pin_status, 4));
|
||||
openerDone = true;
|
||||
if(strcmp(openerStateArr, "undefined") != 0) openerDone = true;
|
||||
}
|
||||
else json["openerPin"] = "Not Paired";
|
||||
}
|
||||
@@ -1462,14 +1510,17 @@ void WebCfgServer::buildAccLvlHtml(String &response)
|
||||
response.concat("<input type=\"hidden\" name=\"ACLLVLCHANGED\" value=\"1\">");
|
||||
response.concat("<h3>Nuki General Access Control</h3>");
|
||||
response.concat("<table><tr><th>Setting</th><th>Enabled</th></tr>");
|
||||
printCheckBox(response, "CONFPUB", "Publish Nuki configuration information", _preferences->getBool(preference_conf_info_enabled, true), "");
|
||||
|
||||
if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad()))
|
||||
{
|
||||
printCheckBox(response, "KPPUB", "Publish keypad entries information", _preferences->getBool(preference_keypad_info_enabled), "");
|
||||
printCheckBox(response, "KPPER", "Publish a topic per keypad entry and create HA sensor", _preferences->getBool(preference_keypad_topic_per_entry), "");
|
||||
printCheckBox(response, "KPCODE", "Also publish keypad codes (<span class=\"warning\">Disadvised for security reasons</span>)", _preferences->getBool(preference_keypad_publish_code, false), "");
|
||||
printCheckBox(response, "KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled), "");
|
||||
}
|
||||
printCheckBox(response, "TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), "");
|
||||
printCheckBox(response, "TCPER", "Publish a topic per time control entry and create HA sensor", _preferences->getBool(preference_timecontrol_topic_per_entry), "");
|
||||
printCheckBox(response, "TCENA", "Add, modify and delete time control entries", _preferences->getBool(preference_timecontrol_control_enabled), "");
|
||||
printCheckBox(response, "PUBAUTH", "Publish authorization log (may reduce battery life)", _preferences->getBool(preference_publish_authdata), "");
|
||||
response.concat("</table><br>");
|
||||
@@ -1548,9 +1599,9 @@ void WebCfgServer::buildAccLvlHtml(String &response)
|
||||
}
|
||||
if(_nukiOpener != nullptr)
|
||||
{
|
||||
uint32_t basicOpenerConfigAclPrefs[16];
|
||||
uint32_t basicOpenerConfigAclPrefs[14];
|
||||
_preferences->getBytes(preference_conf_opener_basic_acl, &basicOpenerConfigAclPrefs, sizeof(basicOpenerConfigAclPrefs));
|
||||
uint32_t advancedOpenerConfigAclPrefs[22];
|
||||
uint32_t advancedOpenerConfigAclPrefs[20];
|
||||
_preferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs));
|
||||
|
||||
response.concat("<h3>Nuki Opener Access Control</h3>");
|
||||
@@ -1630,32 +1681,33 @@ void WebCfgServer::buildNukiConfigHtml(String &response)
|
||||
|
||||
if(_preferences->getBool(preference_lock_enabled))
|
||||
{
|
||||
printInputField(response, "MQTTPATH", "MQTT Nuki Smartlock Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180);
|
||||
printInputField(response, "MQTTPATH", "MQTT Nuki Smartlock Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180, "");
|
||||
}
|
||||
|
||||
printCheckBox(response, "OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled), "");
|
||||
|
||||
if(_preferences->getBool(preference_opener_enabled))
|
||||
{
|
||||
printInputField(response, "MQTTOPPATH", "MQTT Nuki Opener Path", _preferences->getString(preference_mqtt_opener_path).c_str(), 180);
|
||||
printInputField(response, "MQTTOPPATH", "MQTT Nuki Opener Path", _preferences->getString(preference_mqtt_opener_path).c_str(), 180, "");
|
||||
}
|
||||
response.concat("</table><br>");
|
||||
|
||||
response.concat("<h3>Advanced Nuki Configuration</h3>");
|
||||
response.concat("<table>");
|
||||
|
||||
printInputField(response, "LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10);
|
||||
printInputField(response, "CFGINT", "Query interval configuration (seconds)", _preferences->getInt(preference_query_interval_configuration), 10);
|
||||
printInputField(response, "BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10);
|
||||
printInputField(response, "LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10, "");
|
||||
printInputField(response, "CFGINT", "Query interval configuration (seconds)", _preferences->getInt(preference_query_interval_configuration), 10, "");
|
||||
printInputField(response, "BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10, "");
|
||||
if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad()))
|
||||
{
|
||||
printInputField(response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10);
|
||||
printInputField(response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10, "");
|
||||
}
|
||||
printInputField(response, "NRTRY", "Number of retries if command failed", _preferences->getInt(preference_command_nr_of_retries), 10);
|
||||
printInputField(response, "TRYDLY", "Delay between retries (milliseconds)", _preferences->getInt(preference_command_retry_delay), 10);
|
||||
printCheckBox(response, "REGAPP", "Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_as_app), "");
|
||||
printInputField(response, "PRDTMO", "Presence detection timeout (seconds; -1 to disable)", _preferences->getInt(preference_presence_detection_timeout), 10);
|
||||
printInputField(response, "RSBC", "Restart if bluetooth beacons not received (seconds; -1 to disable)", _preferences->getInt(preference_restart_ble_beacon_lost), 10);
|
||||
printInputField(response, "NRTRY", "Number of retries if command failed", _preferences->getInt(preference_command_nr_of_retries), 10, "");
|
||||
printInputField(response, "TRYDLY", "Delay between retries (milliseconds)", _preferences->getInt(preference_command_retry_delay), 10, "");
|
||||
if(_nuki != nullptr) printCheckBox(response, "REGAPP", "Lock: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_as_app), "");
|
||||
if(_nukiOpener != nullptr) printCheckBox(response, "REGAPPOPN", "Opener: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_opener_as_app), "");
|
||||
printInputField(response, "PRDTMO", "Presence detection timeout (seconds; -1 to disable)", _preferences->getInt(preference_presence_detection_timeout), 10, "");
|
||||
printInputField(response, "RSBC", "Restart if bluetooth beacons not received (seconds; -1 to disable)", _preferences->getInt(preference_restart_ble_beacon_lost), 10, "");
|
||||
response.concat("</table>");
|
||||
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||
response.concat("</form>");
|
||||
@@ -1845,9 +1897,9 @@ void WebCfgServer::buildInfoHtml(String &response)
|
||||
|
||||
if(_nukiOpener != nullptr)
|
||||
{
|
||||
uint32_t basicOpenerConfigAclPrefs[16];
|
||||
uint32_t basicOpenerConfigAclPrefs[14];
|
||||
_preferences->getBytes(preference_conf_opener_basic_acl, &basicOpenerConfigAclPrefs, sizeof(basicOpenerConfigAclPrefs));
|
||||
uint32_t advancedOpenerConfigAclPrefs[22];
|
||||
uint32_t advancedOpenerConfigAclPrefs[20];
|
||||
_preferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs));
|
||||
response.concat("Opener firmware version: ");
|
||||
response.concat(_nukiOpener->firmwareVersion().c_str());
|
||||
@@ -2105,6 +2157,7 @@ void WebCfgServer::printInputField(String& response,
|
||||
const char *description,
|
||||
const char *value,
|
||||
const size_t& maxLength,
|
||||
const char *id,
|
||||
const bool& isPassword,
|
||||
const bool& showLengthRestriction)
|
||||
{
|
||||
@@ -2124,7 +2177,13 @@ void WebCfgServer::printInputField(String& response,
|
||||
|
||||
response.concat("</td><td>");
|
||||
response.concat("<input type=");
|
||||
response.concat(isPassword ? "password" : "text");
|
||||
response.concat(isPassword ? "\"password\"" : "\"text\"");
|
||||
if(id)
|
||||
{
|
||||
response.concat(" id=\"");
|
||||
response.concat(id);
|
||||
response.concat("\"");
|
||||
}
|
||||
response.concat(" value=\"");
|
||||
response.concat(value);
|
||||
response.concat("\" name=\"");
|
||||
@@ -2139,11 +2198,12 @@ void WebCfgServer::printInputField(String& response,
|
||||
const char *token,
|
||||
const char *description,
|
||||
const int value,
|
||||
size_t maxLength)
|
||||
size_t maxLength,
|
||||
const char *id)
|
||||
{
|
||||
char valueStr[20];
|
||||
itoa(value, valueStr, 10);
|
||||
printInputField(response, token, description, valueStr, maxLength);
|
||||
printInputField(response, token, description, valueStr, maxLength, id);
|
||||
}
|
||||
|
||||
void WebCfgServer::printCheckBox(String &response, const char *token, const char *description, const bool value, const char *htmlClass)
|
||||
|
||||
@@ -53,10 +53,9 @@ private:
|
||||
void sendFavicon();
|
||||
void processUnpair(bool opener);
|
||||
void processFactoryReset();
|
||||
|
||||
void buildHtmlHeader(String& response, String additionalHeader = "");
|
||||
void printInputField(String& response, const char* token, const char* description, const char* value, const size_t& maxLength, const bool& isPassword = false, const bool& showLengthRestriction = false);
|
||||
void printInputField(String& response, const char* token, const char* description, const int value, size_t maxLength);
|
||||
void printInputField(String& response, const char* token, const char* description, const char* value, const size_t& maxLength, const char* id, const bool& isPassword = false, const bool& showLengthRestriction = false);
|
||||
void printInputField(String& response, const char* token, const char* description, const int value, size_t maxLength, const char* id);
|
||||
void printCheckBox(String& response, const char* token, const char* description, const bool value, const char* htmlClass);
|
||||
void printTextarea(String& response, const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false);
|
||||
void printDropDown(String &response, const char *token, const char *description, const String preselectedValue, std::vector<std::pair<String, String>> options);
|
||||
|
||||
@@ -154,6 +154,7 @@ bool initPreferences()
|
||||
{
|
||||
preferences->putBool(preference_started_before, true);
|
||||
preferences->putBool(preference_lock_enabled, true);
|
||||
preferences->putBool(preference_conf_info_enabled, true);
|
||||
uint32_t aclPrefs[17] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
preferences->putBytes(preference_acl, (byte*)(&aclPrefs), sizeof(aclPrefs));
|
||||
uint32_t basicLockConfigAclPrefs[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
|
||||
virtual bool isConnected() = 0;
|
||||
virtual int8_t signalStrength() = 0;
|
||||
|
||||
|
||||
virtual String localIP() = 0;
|
||||
virtual String BSSIDstr() = 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user