Add Authorization entries (#456)
* Add and remove libs and components for Arduino Core 3 * Arduino Core 3 * Add back Solo1 * Change ESP32-S3 to 4MB build * Add Authorization info and control * Use esp_crt_bundle for HTTPS requests * Remove Solo1 support * Improve Nuki device config read functions * Webserial * OTA Improvements * Authorization Entries * Authorization entries * Authorization
This commit is contained in:
36
README.md
36
README.md
@@ -171,8 +171,10 @@ In a browser navigate to the IP address assigned to the ESP32.
|
|||||||
- Publish keypad entries information (Only available when a Keypad is detected): Enable to publish information about keypad codes through MQTT, see the "[Keypad control](#keypad-control-optional)" section of this README
|
- Publish keypad entries information (Only available when a Keypad is detected): Enable to publish information about keypad codes through MQTT, see the "[Keypad control](#keypad-control-optional)" section of this README
|
||||||
- Also publish keypad codes (Only available when a Keypad is detected): Enable to publish the actual keypad codes through MQTT, note that is could be considered a security risk
|
- Also publish keypad codes (Only available when a Keypad is detected): Enable to publish the actual keypad codes through MQTT, note that is could be considered a security risk
|
||||||
- Add, modify and delete keypad codes (Only available when a Keypad is detected): Enable to allow configuration of keypad codes through MQTT, see the "[Keypad control](#keypad-control-optional)" section of this README
|
- Add, modify and delete keypad codes (Only available when a Keypad is detected): Enable to allow configuration of keypad codes through MQTT, see the "[Keypad control](#keypad-control-optional)" section of this README
|
||||||
- Publish time control information: Enable to publish information about time control entries through MQTT, see the "[Time Control](#time-control)" section of this README
|
- Publish timecontrol information: Enable to publish information about timecontrol entries through MQTT, see the "[Timecontrol](#timecontrol)" section of this README
|
||||||
- Add, modify and delete time control entries: Enable to allow configuration of time control entries through MQTT, see the "[Time Control](#time-control)" section of this README
|
- Add, modify and delete timecontrol entries: Enable to allow configuration of timecontrol entries through MQTT, see the "[Timecontrol](#timecontrol)" section of this README
|
||||||
|
- Publish authorization information: Enable to publish information about authorization entries through MQTT, see the "[Authorization](#authorization)" section of this README
|
||||||
|
- Modify and delete authorization entries: Enable to allow configuration of authorization entries through MQTT, see the "[Authorization](#authorization)" section of this README
|
||||||
- Publish auth data: Enable to publish authorization data to the MQTT topic lock/log. Requires the Nuki security code / PIN to be set, see "[Nuki Lock PIN / Nuki Opener PIN](#nuki-lock-pin--nuki-opener-pin)" below.
|
- Publish auth data: Enable to publish authorization data to the MQTT topic lock/log. Requires the Nuki security code / PIN to be set, see "[Nuki Lock PIN / Nuki Opener PIN](#nuki-lock-pin--nuki-opener-pin)" below.
|
||||||
|
|
||||||
#### Nuki Lock/Opener Access Control
|
#### Nuki Lock/Opener Access Control
|
||||||
@@ -580,6 +582,36 @@ Examples:
|
|||||||
- Add: `{ "action": "add", "weekdays": [ "wed", "thu", "fri" ], "time": "08:00", "lockAction": "Unlock" }`
|
- Add: `{ "action": "add", "weekdays": [ "wed", "thu", "fri" ], "time": "08:00", "lockAction": "Unlock" }`
|
||||||
- Update: `{ "action": "update", "entryId": "1234", "enabled": "1", "weekdays": [ "mon", "tue", "sat", "sun" ], "time": "08:00", "lockAction": "Lock" }`
|
- Update: `{ "action": "update", "entryId": "1234", "enabled": "1", "weekdays": [ "mon", "tue", "sat", "sun" ], "time": "08:00", "lockAction": "Lock" }`
|
||||||
|
|
||||||
|
## Authorization entries control using JSON (optional)
|
||||||
|
|
||||||
|
Authorization entries can be updated and removed. This has to enabled first in the configuration portal. Check "Modify and delete authorization entries" under "Access Level Configuration" and save the configuration.
|
||||||
|
It is currently not (yet) possible to add authorization entries this way.
|
||||||
|
|
||||||
|
Information about current authorization entries is published as JSON data to the "authorization/json" MQTT topic.<br>
|
||||||
|
This needs to be enabled separately by checking "Publish authorization entries information" under "Access Level Configuration" and saving the configuration.
|
||||||
|
By default a maximum of 10 entries are published.
|
||||||
|
|
||||||
|
To change Nuki Lock/Opener authorization settings set the `authorization/actionJson` topic to a JSON formatted value containing the following nodes.
|
||||||
|
|
||||||
|
|
||||||
|
| Node | Delete | Add | Update | Usage | Possible values |
|
||||||
|
|------------------|----------|----------|----------|------------------------------------------------------------------------------------------------------------------|----------------------------------------|
|
||||||
|
| action | Required | Required | Required | The action to execute | "delete", "add", "update" |
|
||||||
|
| authId | Required | Not used | Required | The auth ID of the existing entry to delete or update | Integer |
|
||||||
|
| enabled | Not used | Not used | Optional | Enable or disable the authorization, always enabled on add | 1 = enabled, 0 = disabled |
|
||||||
|
| name | Not used | Required | Optional | The name of the authorization to create or update | String, max 20 chars |
|
||||||
|
| remoteAllowed | Not used | Optional | Optional | If this authorization is allowed remote access, requires enabled = 1 | 1 = enabled, 0 = disabled |
|
||||||
|
| timeLimited | Not used | Optional | Optional | If this authorization is restricted to access only at certain times, requires enabled = 1 | 1 = enabled, 0 = disabled |
|
||||||
|
| allowedFrom | Not used | Optional | Optional | The start timestamp from which access should be allowed (requires enabled = 1 and timeLimited = 1) | "YYYY-MM-DD HH:MM:SS" |
|
||||||
|
| allowedUntil | Not used | Optional | Optional | The end timestamp until access should be allowed (requires enabled = 1 and timeLimited = 1) | "YYYY-MM-DD HH:MM:SS" |
|
||||||
|
| allowedWeekdays | Not used | Optional | Optional | Weekdays on which access should be allowed (requires enabled = 1 and timeLimited = 1) | Array of days: "mon", "tue", "wed", "thu" , "fri" "sat", "sun"|
|
||||||
|
| allowedFromTime | Not used | Optional | Optional | The start time per day from which access should be allowed (requires enabled = 1 and timeLimited = 1) | "HH:MM" |
|
||||||
|
| allowedUntilTime | Not used | Optional | Optional | The end time per day until access should be allowed (requires enabled = 1 and timeLimited = 1) | "HH:MM" |
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- Delete: `{ "action": "delete", "authId": "1234" }`
|
||||||
|
- Update: `{ "action": "update", "authId": "1234", "enabled": "1", "name": "Test", "timeLimited": "1", "allowedFrom": "2024-04-12 10:00:00", "allowedUntil": "2034-04-12 10:00:00", "allowedWeekdays": [ "mon", "tue", "sat", "sun" ], "allowedFromTime": "08:00", "allowedUntilTime": "16:00" }`
|
||||||
|
|
||||||
## GPIO lock control (optional)
|
## GPIO lock control (optional)
|
||||||
|
|
||||||
The lock can be controlled via GPIO.<br>
|
The lock can be controlled via GPIO.<br>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
def get_board_name(env):
|
def get_board_name(env):
|
||||||
board = env.get('BOARD_MCU')
|
board = env.get('BOARD_MCU')
|
||||||
|
|
||||||
if env.get('BOARD') == 'esp32-solo1':
|
if env.get('BOARD') == 'esp32-solo1':
|
||||||
board = env.get('BOARD').replace('-', '')
|
board = env.get('BOARD').replace('-', '')
|
||||||
return board
|
return board
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ extra_scripts =
|
|||||||
build_flags =
|
build_flags =
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
-DCONFIG_ASYNC_TCP_STACK_SIZE=24576
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
-DWS_MAX_QUEUED_MESSAGES=128
|
-DWS_MAX_QUEUED_MESSAGES=128
|
||||||
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
|
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
|
||||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
|
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
|
||||||
@@ -137,8 +137,8 @@ build_flags =
|
|||||||
-DDEBUG_NUKI_COMMUNICATION
|
-DDEBUG_NUKI_COMMUNICATION
|
||||||
;-DDEBUG_NUKI_HEX_DATA
|
;-DDEBUG_NUKI_HEX_DATA
|
||||||
-DDEBUG_NUKI_READABLE_DATA
|
-DDEBUG_NUKI_READABLE_DATA
|
||||||
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=1024
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
-DCONFIG_ASYNC_TCP_STACK_SIZE=24576
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
-DWS_MAX_QUEUED_MESSAGES=512
|
-DWS_MAX_QUEUED_MESSAGES=512
|
||||||
|
|
||||||
[env:esp32-c3_dbg]
|
[env:esp32-c3_dbg]
|
||||||
@@ -158,8 +158,8 @@ build_flags =
|
|||||||
-DDEBUG_NUKI_COMMUNICATION
|
-DDEBUG_NUKI_COMMUNICATION
|
||||||
;-DDEBUG_NUKI_HEX_DATA
|
;-DDEBUG_NUKI_HEX_DATA
|
||||||
-DDEBUG_NUKI_READABLE_DATA
|
-DDEBUG_NUKI_READABLE_DATA
|
||||||
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=1024
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
-DCONFIG_ASYNC_TCP_STACK_SIZE=24576
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
-DWS_MAX_QUEUED_MESSAGES=512
|
-DWS_MAX_QUEUED_MESSAGES=512
|
||||||
|
|
||||||
[env:esp32-c6_dbg]
|
[env:esp32-c6_dbg]
|
||||||
@@ -179,8 +179,8 @@ build_flags =
|
|||||||
-DDEBUG_NUKI_COMMUNICATION
|
-DDEBUG_NUKI_COMMUNICATION
|
||||||
;-DDEBUG_NUKI_HEX_DATA
|
;-DDEBUG_NUKI_HEX_DATA
|
||||||
-DDEBUG_NUKI_READABLE_DATA
|
-DDEBUG_NUKI_READABLE_DATA
|
||||||
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=1024
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
-DCONFIG_ASYNC_TCP_STACK_SIZE=24576
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
-DWS_MAX_QUEUED_MESSAGES=512
|
-DWS_MAX_QUEUED_MESSAGES=512
|
||||||
|
|
||||||
[env:esp32-h2_dbg]
|
[env:esp32-h2_dbg]
|
||||||
@@ -201,8 +201,8 @@ build_flags =
|
|||||||
-DDEBUG_NUKI_COMMUNICATION
|
-DDEBUG_NUKI_COMMUNICATION
|
||||||
;-DDEBUG_NUKI_HEX_DATA
|
;-DDEBUG_NUKI_HEX_DATA
|
||||||
-DDEBUG_NUKI_READABLE_DATA
|
-DDEBUG_NUKI_READABLE_DATA
|
||||||
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=1024
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
-DCONFIG_ASYNC_TCP_STACK_SIZE=24576
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
-DWS_MAX_QUEUED_MESSAGES=512
|
-DWS_MAX_QUEUED_MESSAGES=512
|
||||||
|
|
||||||
[env:esp32-s3_dbg]
|
[env:esp32-s3_dbg]
|
||||||
@@ -222,8 +222,8 @@ build_flags =
|
|||||||
-DDEBUG_NUKI_COMMUNICATION
|
-DDEBUG_NUKI_COMMUNICATION
|
||||||
;-DDEBUG_NUKI_HEX_DATA
|
;-DDEBUG_NUKI_HEX_DATA
|
||||||
-DDEBUG_NUKI_READABLE_DATA
|
-DDEBUG_NUKI_READABLE_DATA
|
||||||
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=1024
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
-DCONFIG_ASYNC_TCP_STACK_SIZE=24576
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
-DWS_MAX_QUEUED_MESSAGES=512
|
-DWS_MAX_QUEUED_MESSAGES=512
|
||||||
|
|
||||||
[env:esp32-solo1_dbg]
|
[env:esp32-solo1_dbg]
|
||||||
@@ -243,6 +243,6 @@ build_flags =
|
|||||||
-DDEBUG_NUKI_COMMUNICATION
|
-DDEBUG_NUKI_COMMUNICATION
|
||||||
;-DDEBUG_NUKI_HEX_DATA
|
;-DDEBUG_NUKI_HEX_DATA
|
||||||
-DDEBUG_NUKI_READABLE_DATA
|
-DDEBUG_NUKI_READABLE_DATA
|
||||||
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=1024
|
-DCONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
-DCONFIG_ASYNC_TCP_STACK_SIZE=24576
|
-DCONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
-DWS_MAX_QUEUED_MESSAGES=512
|
-DWS_MAX_QUEUED_MESSAGES=512
|
||||||
41
resources/github_root_ca.pem
Normal file
41
resources/github_root_ca.pem
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
USERTrust ECC Certification Authority
|
||||||
|
=====================================
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC
|
||||||
|
VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
||||||
|
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
|
||||||
|
biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC
|
||||||
|
VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
||||||
|
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
|
||||||
|
biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2
|
||||||
|
0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez
|
||||||
|
nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV
|
||||||
|
HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB
|
||||||
|
HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu
|
||||||
|
9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
DigiCert Global Root G2
|
||||||
|
=====================================
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
|
||||||
|
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||||
|
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
|
||||||
|
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||||
|
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||||
|
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
|
||||||
|
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
|
||||||
|
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
|
||||||
|
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
|
||||||
|
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
|
||||||
|
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
|
||||||
|
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
|
||||||
|
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
|
||||||
|
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
|
||||||
|
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
|
||||||
|
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
|
||||||
|
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
|
||||||
|
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
|
||||||
|
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
|
||||||
|
MrY=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -80,4 +80,6 @@ CONFIG_ETH_SPI_ETHERNET_W5500=y
|
|||||||
CONFIG_ETH_SPI_ETHERNET_DM9051=y
|
CONFIG_ETH_SPI_ETHERNET_DM9051=y
|
||||||
CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y
|
CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y
|
||||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y
|
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y
|
||||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y
|
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE=y
|
||||||
|
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y
|
||||||
|
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="resources/github_root_ca.pem"
|
||||||
@@ -102,6 +102,7 @@
|
|||||||
#define MAX_AUTHLOG 5
|
#define MAX_AUTHLOG 5
|
||||||
#define MAX_KEYPAD 10
|
#define MAX_KEYPAD 10
|
||||||
#define MAX_TIMECONTROL 10
|
#define MAX_TIMECONTROL 10
|
||||||
|
#define MAX_AUTH 10
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NETWORK_TASK_SIZE 12288
|
#define NETWORK_TASK_SIZE 12288
|
||||||
|
|||||||
@@ -85,6 +85,12 @@
|
|||||||
#define mqtt_topic_timecontrol_action "/timecontrol/action"
|
#define mqtt_topic_timecontrol_action "/timecontrol/action"
|
||||||
#define mqtt_topic_timecontrol_command_result "/timecontrol/commandResult"
|
#define mqtt_topic_timecontrol_command_result "/timecontrol/commandResult"
|
||||||
|
|
||||||
|
#define mqtt_topic_auth "/authorization"
|
||||||
|
#define mqtt_topic_auth_entries "/authorization/entries"
|
||||||
|
#define mqtt_topic_auth_json "/authorization/json"
|
||||||
|
#define mqtt_topic_auth_action "/authorization/action"
|
||||||
|
#define mqtt_topic_auth_command_result "/authorization/commandResult"
|
||||||
|
|
||||||
#define mqtt_topic_info_hardware_version "/info/hardwareVersion"
|
#define mqtt_topic_info_hardware_version "/info/hardwareVersion"
|
||||||
#define mqtt_topic_info_firmware_version "/info/firmwareVersion"
|
#define mqtt_topic_info_firmware_version "/info/firmwareVersion"
|
||||||
#define mqtt_topic_info_nuki_hub_version "/info/nukiHubVersion"
|
#define mqtt_topic_info_nuki_hub_version "/info/nukiHubVersion"
|
||||||
|
|||||||
@@ -570,7 +570,6 @@ void NukiNetwork::initialize()
|
|||||||
bool NukiNetwork::update()
|
bool NukiNetwork::update()
|
||||||
{
|
{
|
||||||
int64_t ts = (esp_timer_get_time() / 1000);
|
int64_t ts = (esp_timer_get_time() / 1000);
|
||||||
|
|
||||||
_device->update();
|
_device->update();
|
||||||
|
|
||||||
if(!_mqttEnabled)
|
if(!_mqttEnabled)
|
||||||
@@ -660,6 +659,7 @@ bool NukiNetwork::update()
|
|||||||
{
|
{
|
||||||
char* presenceCsv = _presenceDetection->generateCsv();
|
char* presenceCsv = _presenceDetection->generateCsv();
|
||||||
bool success = publishString(_mqttPresencePrefix, mqtt_topic_presence, presenceCsv, true);
|
bool success = publishString(_mqttPresencePrefix, mqtt_topic_presence, presenceCsv, true);
|
||||||
|
|
||||||
if(!success)
|
if(!success)
|
||||||
{
|
{
|
||||||
Log->println(F("Failed to publish presence CSV data."));
|
Log->println(F("Failed to publish presence CSV data."));
|
||||||
@@ -706,10 +706,11 @@ bool NukiNetwork::update()
|
|||||||
if(_lastUpdateCheckTs == 0 || (ts - _lastUpdateCheckTs) > 86400000)
|
if(_lastUpdateCheckTs == 0 || (ts - _lastUpdateCheckTs) > 86400000)
|
||||||
{
|
{
|
||||||
_lastUpdateCheckTs = ts;
|
_lastUpdateCheckTs = ts;
|
||||||
|
bool otaManifestSuccess = false;
|
||||||
|
JsonDocument doc;
|
||||||
|
|
||||||
NetworkClientSecure *client = new NetworkClientSecure;
|
NetworkClientSecure *client = new NetworkClientSecure;
|
||||||
if (client) {
|
if (client) {
|
||||||
//client->setDefaultCACertBundle();
|
|
||||||
client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
||||||
{
|
{
|
||||||
HTTPClient https;
|
HTTPClient https;
|
||||||
@@ -721,10 +722,17 @@ bool NukiNetwork::update()
|
|||||||
|
|
||||||
if (httpResponseCode == HTTP_CODE_OK || httpResponseCode == HTTP_CODE_MOVED_PERMANENTLY)
|
if (httpResponseCode == HTTP_CODE_OK || httpResponseCode == HTTP_CODE_MOVED_PERMANENTLY)
|
||||||
{
|
{
|
||||||
JsonDocument doc;
|
|
||||||
DeserializationError jsonError = deserializeJson(doc, https.getStream());
|
DeserializationError jsonError = deserializeJson(doc, https.getStream());
|
||||||
|
|
||||||
if (!jsonError)
|
if (!jsonError) { otaManifestSuccess = true; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
https.end();
|
||||||
|
}
|
||||||
|
delete client;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otaManifestSuccess)
|
||||||
{
|
{
|
||||||
String currentVersion = NUKI_HUB_VERSION;
|
String currentVersion = NUKI_HUB_VERSION;
|
||||||
|
|
||||||
@@ -739,12 +747,6 @@ bool NukiNetwork::update()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
https.end();
|
|
||||||
}
|
|
||||||
delete client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const auto& gpioTs : _gpioTs)
|
for(const auto& gpioTs : _gpioTs)
|
||||||
{
|
{
|
||||||
@@ -861,7 +863,6 @@ bool NukiNetwork::reconnect()
|
|||||||
_mqttConnectedTs = millis();
|
_mqttConnectedTs = millis();
|
||||||
_mqttConnectionState = 1;
|
_mqttConnectionState = 1;
|
||||||
delay(100);
|
delay(100);
|
||||||
|
|
||||||
_device->mqttOnMessage(NukiNetwork::onMqttDataReceivedCallback);
|
_device->mqttOnMessage(NukiNetwork::onMqttDataReceivedCallback);
|
||||||
for(const String& topic : _subscribedTopics)
|
for(const String& topic : _subscribedTopics)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ public:
|
|||||||
bool encryptionSupported();
|
bool encryptionSupported();
|
||||||
bool mqttRecentlyConnected();
|
bool mqttRecentlyConnected();
|
||||||
bool pathEquals(const char* prefix, const char* path, const char* referencePath);
|
bool pathEquals(const char* prefix, const char* path, const char* referencePath);
|
||||||
|
|
||||||
uint16_t subscribe(const char* topic, uint8_t qos);
|
uint16_t subscribe(const char* topic, uint8_t qos);
|
||||||
|
|
||||||
void addReconnectedCallback(std::function<void()> reconnectedCallback);
|
void addReconnectedCallback(std::function<void()> reconnectedCallback);
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ void NukiNetworkLock::initialize()
|
|||||||
_network->subscribe(_mqttPath, mqtt_topic_lock_action);
|
_network->subscribe(_mqttPath, mqtt_topic_lock_action);
|
||||||
_network->initTopic(_mqttPath, mqtt_topic_config_action, "--");
|
_network->initTopic(_mqttPath, mqtt_topic_config_action, "--");
|
||||||
_network->subscribe(_mqttPath, mqtt_topic_config_action);
|
_network->subscribe(_mqttPath, mqtt_topic_config_action);
|
||||||
|
|
||||||
_network->subscribe(_mqttPath, mqtt_topic_reset);
|
_network->subscribe(_mqttPath, mqtt_topic_reset);
|
||||||
_network->initTopic(_mqttPath, mqtt_topic_reset, "0");
|
_network->initTopic(_mqttPath, mqtt_topic_reset, "0");
|
||||||
|
|
||||||
@@ -154,6 +153,12 @@ void NukiNetworkLock::initialize()
|
|||||||
_network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--");
|
_network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_preferences->getBool(preference_auth_control_enabled))
|
||||||
|
{
|
||||||
|
_network->subscribe(_mqttPath, mqtt_topic_auth_action);
|
||||||
|
_network->initTopic(_mqttPath, mqtt_topic_auth_action, "--");
|
||||||
|
}
|
||||||
|
|
||||||
if(_offEnabled)
|
if(_offEnabled)
|
||||||
{
|
{
|
||||||
char uidString[20];
|
char uidString[20];
|
||||||
@@ -403,6 +408,18 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const
|
|||||||
|
|
||||||
publishString(mqtt_topic_timecontrol_action, "--", true);
|
publishString(mqtt_topic_timecontrol_action, "--", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(comparePrefixedPath(topic, mqtt_topic_auth_action))
|
||||||
|
{
|
||||||
|
if(strcmp(value, "") == 0 || strcmp(value, "--") == 0) return;
|
||||||
|
|
||||||
|
if(_authCommandReceivedReceivedCallback != NULL)
|
||||||
|
{
|
||||||
|
_authCommandReceivedReceivedCallback(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
publishString(mqtt_topic_auth_action, "--", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurnerState, const NukiLock::KeyTurnerState& lastKeyTurnerState)
|
void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyTurnerState, const NukiLock::KeyTurnerState& lastKeyTurnerState)
|
||||||
@@ -1262,6 +1279,152 @@ void NukiNetworkLock::publishTimeControl(const std::list<NukiLock::TimeControlEn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiNetworkLock::publishAuth(const std::list<NukiLock::AuthorizationEntry>& authEntries, uint maxAuthEntryCount)
|
||||||
|
{
|
||||||
|
uint index = 0;
|
||||||
|
char str[50];
|
||||||
|
char uidString[20];
|
||||||
|
itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16);
|
||||||
|
String baseTopic = _preferences->getString(preference_mqtt_lock_path);
|
||||||
|
JsonDocument json;
|
||||||
|
|
||||||
|
for(const auto& entry : authEntries)
|
||||||
|
{
|
||||||
|
auto jsonEntry = json.add<JsonVariant>();
|
||||||
|
|
||||||
|
jsonEntry["authId"] = entry.authId;
|
||||||
|
jsonEntry["idType"] = entry.idType; //CONSIDER INT TO STRING
|
||||||
|
jsonEntry["enabled"] = entry.enabled;
|
||||||
|
jsonEntry["name"] = entry.name;
|
||||||
|
jsonEntry["remoteAllowed"] = entry.remoteAllowed;
|
||||||
|
char createdDT[20];
|
||||||
|
sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.createdYear, entry.createdMonth, entry.createdDay, entry.createdHour, entry.createdMinute, entry.createdSecond);
|
||||||
|
jsonEntry["dateCreated"] = createdDT;
|
||||||
|
jsonEntry["lockCount"] = entry.lockCount;
|
||||||
|
char lastActiveDT[20];
|
||||||
|
sprintf(lastActiveDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.lastActYear, entry.lastActMonth, entry.lastActDay, entry.lastActHour, entry.lastActMinute, entry.lastActSecond);
|
||||||
|
jsonEntry["dateLastActive"] = lastActiveDT;
|
||||||
|
jsonEntry["timeLimited"] = entry.timeLimited;
|
||||||
|
char allowedFromDT[20];
|
||||||
|
sprintf(allowedFromDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedFromYear, entry.allowedFromMonth, entry.allowedFromDay, entry.allowedFromHour, entry.allowedFromMinute, entry.allowedFromSecond);
|
||||||
|
jsonEntry["allowedFrom"] = allowedFromDT;
|
||||||
|
char allowedUntilDT[20];
|
||||||
|
sprintf(allowedUntilDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedUntilYear, entry.allowedUntilMonth, entry.allowedUntilDay, entry.allowedUntilHour, entry.allowedUntilMinute, entry.allowedUntilSecond);
|
||||||
|
jsonEntry["allowedUntil"] = allowedUntilDT;
|
||||||
|
|
||||||
|
uint8_t allowedWeekdaysInt = entry.allowedWeekdays;
|
||||||
|
JsonArray weekdays = jsonEntry["allowedWeekdays"].to<JsonArray>();
|
||||||
|
|
||||||
|
while(allowedWeekdaysInt > 0) {
|
||||||
|
if(allowedWeekdaysInt >= 64)
|
||||||
|
{
|
||||||
|
weekdays.add("mon");
|
||||||
|
allowedWeekdaysInt -= 64;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 32)
|
||||||
|
{
|
||||||
|
weekdays.add("tue");
|
||||||
|
allowedWeekdaysInt -= 32;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 16)
|
||||||
|
{
|
||||||
|
weekdays.add("wed");
|
||||||
|
allowedWeekdaysInt -= 16;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 8)
|
||||||
|
{
|
||||||
|
weekdays.add("thu");
|
||||||
|
allowedWeekdaysInt -= 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 4)
|
||||||
|
{
|
||||||
|
weekdays.add("fri");
|
||||||
|
allowedWeekdaysInt -= 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 2)
|
||||||
|
{
|
||||||
|
weekdays.add("sat");
|
||||||
|
allowedWeekdaysInt -= 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 1)
|
||||||
|
{
|
||||||
|
weekdays.add("sun");
|
||||||
|
allowedWeekdaysInt -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char allowedFromTimeT[5];
|
||||||
|
sprintf(allowedFromTimeT, "%02d:%02d", entry.allowedFromTimeHour, entry.allowedFromTimeMin);
|
||||||
|
jsonEntry["allowedFromTime"] = allowedFromTimeT;
|
||||||
|
char allowedUntilTimeT[5];
|
||||||
|
sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin);
|
||||||
|
jsonEntry["allowedUntilTime"] = allowedUntilTimeT;
|
||||||
|
|
||||||
|
if(_preferences->getBool(preference_auth_topic_per_entry, false))
|
||||||
|
{
|
||||||
|
String basePath = mqtt_topic_auth;
|
||||||
|
basePath.concat("/entries/");
|
||||||
|
basePath.concat(std::to_string(index).c_str());
|
||||||
|
jsonEntry["index"] = index;
|
||||||
|
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||||
|
publishString(basePath.c_str(), _buffer, true);
|
||||||
|
|
||||||
|
String basePathPrefix = "~";
|
||||||
|
basePathPrefix.concat(basePath);
|
||||||
|
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||||
|
|
||||||
|
std::string baseCommand = std::string("{ \"action\": \"update\", \"authId\": \"") + std::to_string(entry.authId);
|
||||||
|
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||||
|
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||||
|
std::string mqttDeviceName = std::string("auth_") + std::to_string(index);
|
||||||
|
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||||
|
std::string displayName = std::string("Authorization - ") + std::to_string(entry.authId);
|
||||||
|
|
||||||
|
_network->publishHassTopic("switch",
|
||||||
|
mqttDeviceName.c_str(),
|
||||||
|
uidString,
|
||||||
|
uidStringPostfix.c_str(),
|
||||||
|
displayName.c_str(),
|
||||||
|
_nukiName,
|
||||||
|
baseTopic.c_str(),
|
||||||
|
String("~") + basePath.c_str(),
|
||||||
|
(char*)"SmartLock",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"diagnostic",
|
||||||
|
String("~") + mqtt_topic_auth_action,
|
||||||
|
{ { (char*)"json_attr_t", (char*)basePathPrefixChr },
|
||||||
|
{ (char*)"pl_on", (char*)enaCommand.c_str() },
|
||||||
|
{ (char*)"pl_off", (char*)disCommand.c_str() },
|
||||||
|
{ (char*)"val_tpl", (char*)"{{value_json.enabled}}" },
|
||||||
|
{ (char*)"stat_on", (char*)"1" },
|
||||||
|
{ (char*)"stat_off", (char*)"0" }});
|
||||||
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeJson(json, _buffer, _bufferSize);
|
||||||
|
publishString(mqtt_topic_auth_json, _buffer, true);
|
||||||
|
|
||||||
|
for(int j=authEntries.size(); j<maxAuthEntryCount; j++)
|
||||||
|
{
|
||||||
|
String entriesTopic = _mqttPath;
|
||||||
|
entriesTopic.concat(mqtt_topic_auth_entries);
|
||||||
|
entriesTopic.concat("/");
|
||||||
|
_network->removeTopic(entriesTopic, (char*)std::to_string(j).c_str());
|
||||||
|
std::string mqttDeviceName = std::string("auth_") + std::to_string(j);
|
||||||
|
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NukiNetworkLock::publishConfigCommandResult(const char* result)
|
void NukiNetworkLock::publishConfigCommandResult(const char* result)
|
||||||
{
|
{
|
||||||
publishString(mqtt_topic_config_action_command_result, result, true);
|
publishString(mqtt_topic_config_action_command_result, result, true);
|
||||||
@@ -1283,6 +1446,11 @@ void NukiNetworkLock::publishTimeControlCommandResult(const char* result)
|
|||||||
publishString(mqtt_topic_timecontrol_command_result, result, true);
|
publishString(mqtt_topic_timecontrol_command_result, result, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiNetworkLock::publishAuthCommandResult(const char* result)
|
||||||
|
{
|
||||||
|
publishString(mqtt_topic_auth_command_result, result, true);
|
||||||
|
}
|
||||||
|
|
||||||
void NukiNetworkLock::publishStatusUpdated(const bool statusUpdated)
|
void NukiNetworkLock::publishStatusUpdated(const bool statusUpdated)
|
||||||
{
|
{
|
||||||
publishBool(mqtt_topic_lock_status_updated, statusUpdated, true);
|
publishBool(mqtt_topic_lock_status_updated, statusUpdated, true);
|
||||||
@@ -1319,6 +1487,11 @@ void NukiNetworkLock::setTimeControlCommandReceivedCallback(void (*timeControlCo
|
|||||||
_timeControlCommandReceivedReceivedCallback = timeControlCommandReceivedReceivedCallback;
|
_timeControlCommandReceivedReceivedCallback = timeControlCommandReceivedReceivedCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiNetworkLock::setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char *))
|
||||||
|
{
|
||||||
|
_authCommandReceivedReceivedCallback = authCommandReceivedReceivedCallback;
|
||||||
|
}
|
||||||
|
|
||||||
void NukiNetworkLock::buildMqttPath(const char* path, char* outPath, bool offPath)
|
void NukiNetworkLock::buildMqttPath(const char* path, char* outPath, bool offPath)
|
||||||
{
|
{
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|||||||
@@ -39,11 +39,13 @@ public:
|
|||||||
void removeHASSConfig(char* uidString);
|
void removeHASSConfig(char* uidString);
|
||||||
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
||||||
void publishTimeControl(const std::list<NukiLock::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount);
|
void publishTimeControl(const std::list<NukiLock::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount);
|
||||||
|
void publishAuth(const std::list<NukiLock::AuthorizationEntry>& authEntries, uint maxAuthEntryCount);
|
||||||
void publishStatusUpdated(const bool statusUpdated);
|
void publishStatusUpdated(const bool statusUpdated);
|
||||||
void publishConfigCommandResult(const char* result);
|
void publishConfigCommandResult(const char* result);
|
||||||
void publishKeypadCommandResult(const char* result);
|
void publishKeypadCommandResult(const char* result);
|
||||||
void publishKeypadJsonCommandResult(const char* result);
|
void publishKeypadJsonCommandResult(const char* result);
|
||||||
void publishTimeControlCommandResult(const char* result);
|
void publishTimeControlCommandResult(const char* result);
|
||||||
|
void publishAuthCommandResult(const char* result);
|
||||||
void publishOffAction(const int value);
|
void publishOffAction(const int value);
|
||||||
|
|
||||||
void setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char* value));
|
void setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char* value));
|
||||||
@@ -52,6 +54,7 @@ public:
|
|||||||
void setKeypadCommandReceivedCallback(void (*keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled));
|
void 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 setKeypadJsonCommandReceivedCallback(void (*keypadJsonCommandReceivedReceivedCallback)(const char* value));
|
||||||
void setTimeControlCommandReceivedCallback(void (*timeControlCommandReceivedReceivedCallback)(const char* value));
|
void setTimeControlCommandReceivedCallback(void (*timeControlCommandReceivedReceivedCallback)(const char* value));
|
||||||
|
void setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char* value));
|
||||||
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
||||||
|
|
||||||
void publishFloat(const char* topic, const float value, bool retain, const uint8_t precision = 2);
|
void publishFloat(const char* topic, const float value, bool retain, const uint8_t precision = 2);
|
||||||
@@ -132,4 +135,5 @@ private:
|
|||||||
void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr;
|
void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr;
|
||||||
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||||
void (*_timeControlCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
void (*_timeControlCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||||
|
void (*_authCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||||
};
|
};
|
||||||
@@ -110,6 +110,12 @@ void NukiNetworkOpener::initialize()
|
|||||||
_network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--");
|
_network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_preferences->getBool(preference_auth_control_enabled))
|
||||||
|
{
|
||||||
|
_network->subscribe(_mqttPath, mqtt_topic_auth_action);
|
||||||
|
_network->initTopic(_mqttPath, mqtt_topic_auth_action, "--");
|
||||||
|
}
|
||||||
|
|
||||||
if(_preferences->getBool(preference_publish_authdata, false))
|
if(_preferences->getBool(preference_publish_authdata, false))
|
||||||
{
|
{
|
||||||
_network->subscribe(_mqttPath, mqtt_topic_lock_log_rolling_last);
|
_network->subscribe(_mqttPath, mqtt_topic_lock_log_rolling_last);
|
||||||
@@ -281,6 +287,18 @@ void NukiNetworkOpener::onMqttDataReceived(const char* topic, byte* payload, con
|
|||||||
|
|
||||||
publishString(mqtt_topic_timecontrol_action, "--", true);
|
publishString(mqtt_topic_timecontrol_action, "--", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(comparePrefixedPath(topic, mqtt_topic_auth_action))
|
||||||
|
{
|
||||||
|
if(strcmp(value, "") == 0 || strcmp(value, "--") == 0) return;
|
||||||
|
|
||||||
|
if(_authCommandReceivedReceivedCallback != NULL)
|
||||||
|
{
|
||||||
|
_authCommandReceivedReceivedCallback(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
publishString(mqtt_topic_auth_action, "--", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NukiNetworkOpener::publishKeyTurnerState(const NukiOpener::OpenerState& keyTurnerState, const NukiOpener::OpenerState& lastKeyTurnerState)
|
void NukiNetworkOpener::publishKeyTurnerState(const NukiOpener::OpenerState& keyTurnerState, const NukiOpener::OpenerState& lastKeyTurnerState)
|
||||||
@@ -1093,7 +1111,6 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
|||||||
jsonEntry["index"] = index;
|
jsonEntry["index"] = index;
|
||||||
serializeJson(jsonEntry, _buffer, _bufferSize);
|
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||||
publishString(basePath.c_str(), _buffer, true);
|
publishString(basePath.c_str(), _buffer, true);
|
||||||
|
|
||||||
String basePathPrefix = "~";
|
String basePathPrefix = "~";
|
||||||
basePathPrefix.concat(basePath);
|
basePathPrefix.concat(basePath);
|
||||||
const char *basePathPrefixChr = basePathPrefix.c_str();
|
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||||
@@ -1142,6 +1159,152 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiNetworkOpener::publishAuth(const std::list<NukiOpener::AuthorizationEntry>& authEntries, uint maxAuthEntryCount)
|
||||||
|
{
|
||||||
|
uint index = 0;
|
||||||
|
char str[50];
|
||||||
|
char uidString[20];
|
||||||
|
itoa(_preferences->getUInt(preference_nuki_id_opener, 0), uidString, 16);
|
||||||
|
String baseTopic = _preferences->getString(preference_mqtt_opener_path);
|
||||||
|
JsonDocument json;
|
||||||
|
|
||||||
|
for(const auto& entry : authEntries)
|
||||||
|
{
|
||||||
|
auto jsonEntry = json.add<JsonVariant>();
|
||||||
|
|
||||||
|
jsonEntry["authId"] = entry.authId;
|
||||||
|
jsonEntry["idType"] = entry.idType; //CONSIDER INT TO STRING
|
||||||
|
jsonEntry["enabled"] = entry.enabled;
|
||||||
|
jsonEntry["name"] = entry.name;
|
||||||
|
jsonEntry["remoteAllowed"] = entry.remoteAllowed;
|
||||||
|
char createdDT[20];
|
||||||
|
sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.createdYear, entry.createdMonth, entry.createdDay, entry.createdHour, entry.createdMinute, entry.createdSecond);
|
||||||
|
jsonEntry["dateCreated"] = createdDT;
|
||||||
|
jsonEntry["lockCount"] = entry.lockCount;
|
||||||
|
char lastActiveDT[20];
|
||||||
|
sprintf(lastActiveDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.lastActYear, entry.lastActMonth, entry.lastActDay, entry.lastActHour, entry.lastActMinute, entry.lastActSecond);
|
||||||
|
jsonEntry["dateLastActive"] = lastActiveDT;
|
||||||
|
jsonEntry["timeLimited"] = entry.timeLimited;
|
||||||
|
char allowedFromDT[20];
|
||||||
|
sprintf(allowedFromDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedFromYear, entry.allowedFromMonth, entry.allowedFromDay, entry.allowedFromHour, entry.allowedFromMinute, entry.allowedFromSecond);
|
||||||
|
jsonEntry["allowedFrom"] = allowedFromDT;
|
||||||
|
char allowedUntilDT[20];
|
||||||
|
sprintf(allowedUntilDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedUntilYear, entry.allowedUntilMonth, entry.allowedUntilDay, entry.allowedUntilHour, entry.allowedUntilMinute, entry.allowedUntilSecond);
|
||||||
|
jsonEntry["allowedUntil"] = allowedUntilDT;
|
||||||
|
|
||||||
|
uint8_t allowedWeekdaysInt = entry.allowedWeekdays;
|
||||||
|
JsonArray weekdays = jsonEntry["allowedWeekdays"].to<JsonArray>();
|
||||||
|
|
||||||
|
while(allowedWeekdaysInt > 0) {
|
||||||
|
if(allowedWeekdaysInt >= 64)
|
||||||
|
{
|
||||||
|
weekdays.add("mon");
|
||||||
|
allowedWeekdaysInt -= 64;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 32)
|
||||||
|
{
|
||||||
|
weekdays.add("tue");
|
||||||
|
allowedWeekdaysInt -= 32;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 16)
|
||||||
|
{
|
||||||
|
weekdays.add("wed");
|
||||||
|
allowedWeekdaysInt -= 16;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 8)
|
||||||
|
{
|
||||||
|
weekdays.add("thu");
|
||||||
|
allowedWeekdaysInt -= 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 4)
|
||||||
|
{
|
||||||
|
weekdays.add("fri");
|
||||||
|
allowedWeekdaysInt -= 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 2)
|
||||||
|
{
|
||||||
|
weekdays.add("sat");
|
||||||
|
allowedWeekdaysInt -= 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 1)
|
||||||
|
{
|
||||||
|
weekdays.add("sun");
|
||||||
|
allowedWeekdaysInt -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char allowedFromTimeT[5];
|
||||||
|
sprintf(allowedFromTimeT, "%02d:%02d", entry.allowedFromTimeHour, entry.allowedFromTimeMin);
|
||||||
|
jsonEntry["allowedFromTime"] = allowedFromTimeT;
|
||||||
|
char allowedUntilTimeT[5];
|
||||||
|
sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin);
|
||||||
|
jsonEntry["allowedUntilTime"] = allowedUntilTimeT;
|
||||||
|
|
||||||
|
if(_preferences->getBool(preference_auth_topic_per_entry, false))
|
||||||
|
{
|
||||||
|
String basePath = mqtt_topic_auth;
|
||||||
|
basePath.concat("/entries/");
|
||||||
|
basePath.concat(std::to_string(index).c_str());
|
||||||
|
jsonEntry["index"] = index;
|
||||||
|
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||||
|
publishString(basePath.c_str(), _buffer, true);
|
||||||
|
|
||||||
|
String basePathPrefix = "~";
|
||||||
|
basePathPrefix.concat(basePath);
|
||||||
|
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||||
|
|
||||||
|
std::string baseCommand = std::string("{ \"action\": \"update\", \"authId\": \"") + std::to_string(entry.authId);
|
||||||
|
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||||
|
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||||
|
std::string mqttDeviceName = std::string("auth_") + std::to_string(index);
|
||||||
|
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||||
|
std::string displayName = std::string("Authorization - ") + std::to_string(entry.authId);
|
||||||
|
|
||||||
|
_network->publishHassTopic("switch",
|
||||||
|
mqttDeviceName.c_str(),
|
||||||
|
uidString,
|
||||||
|
uidStringPostfix.c_str(),
|
||||||
|
displayName.c_str(),
|
||||||
|
_nukiName,
|
||||||
|
baseTopic.c_str(),
|
||||||
|
String("~") + basePath.c_str(),
|
||||||
|
(char*)"Opener",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"diagnostic",
|
||||||
|
String("~") + mqtt_topic_auth_action,
|
||||||
|
{ { (char*)"json_attr_t", (char*)basePathPrefixChr },
|
||||||
|
{ (char*)"pl_on", (char*)enaCommand.c_str() },
|
||||||
|
{ (char*)"pl_off", (char*)disCommand.c_str() },
|
||||||
|
{ (char*)"val_tpl", (char*)"{{value_json.enabled}}" },
|
||||||
|
{ (char*)"stat_on", (char*)"1" },
|
||||||
|
{ (char*)"stat_off", (char*)"0" }});
|
||||||
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeJson(json, _buffer, _bufferSize);
|
||||||
|
publishString(mqtt_topic_auth_json, _buffer, true);
|
||||||
|
|
||||||
|
for(int j=authEntries.size(); j<maxAuthEntryCount; j++)
|
||||||
|
{
|
||||||
|
String entriesTopic = _mqttPath;
|
||||||
|
entriesTopic.concat(mqtt_topic_auth_entries);
|
||||||
|
entriesTopic.concat("/");
|
||||||
|
_network->removeTopic(entriesTopic, (char*)std::to_string(j).c_str());
|
||||||
|
std::string mqttDeviceName = std::string("auth_") + std::to_string(j);
|
||||||
|
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NukiNetworkOpener::publishConfigCommandResult(const char* result)
|
void NukiNetworkOpener::publishConfigCommandResult(const char* result)
|
||||||
{
|
{
|
||||||
publishString(mqtt_topic_config_action_command_result, result, true);
|
publishString(mqtt_topic_config_action_command_result, result, true);
|
||||||
@@ -1163,6 +1326,11 @@ void NukiNetworkOpener::publishTimeControlCommandResult(const char* result)
|
|||||||
publishString(mqtt_topic_timecontrol_command_result, result, true);
|
publishString(mqtt_topic_timecontrol_command_result, result, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiNetworkOpener::publishAuthCommandResult(const char* result)
|
||||||
|
{
|
||||||
|
publishString(mqtt_topic_auth_command_result, result, true);
|
||||||
|
}
|
||||||
|
|
||||||
void NukiNetworkOpener::publishStatusUpdated(const bool statusUpdated)
|
void NukiNetworkOpener::publishStatusUpdated(const bool statusUpdated)
|
||||||
{
|
{
|
||||||
publishBool(mqtt_topic_lock_status_updated, statusUpdated, true);
|
publishBool(mqtt_topic_lock_status_updated, statusUpdated, true);
|
||||||
@@ -1194,6 +1362,11 @@ void NukiNetworkOpener::setTimeControlCommandReceivedCallback(void (*timeControl
|
|||||||
_timeControlCommandReceivedReceivedCallback = timeControlCommandReceivedReceivedCallback;
|
_timeControlCommandReceivedReceivedCallback = timeControlCommandReceivedReceivedCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiNetworkOpener::setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char *))
|
||||||
|
{
|
||||||
|
_authCommandReceivedReceivedCallback = authCommandReceivedReceivedCallback;
|
||||||
|
}
|
||||||
|
|
||||||
void NukiNetworkOpener::publishFloat(const char *topic, const float value, bool retain, const uint8_t precision)
|
void NukiNetworkOpener::publishFloat(const char *topic, const float value, bool retain, const uint8_t precision)
|
||||||
{
|
{
|
||||||
_network->publishFloat(_mqttPath, topic, value, retain, precision);
|
_network->publishFloat(_mqttPath, topic, value, retain, precision);
|
||||||
|
|||||||
@@ -33,17 +33,20 @@ public:
|
|||||||
void removeHASSConfig(char* uidString);
|
void removeHASSConfig(char* uidString);
|
||||||
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
void publishKeypad(const std::list<NukiLock::KeypadEntry>& entries, uint maxKeypadCodeCount);
|
||||||
void publishTimeControl(const std::list<NukiOpener::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount);
|
void publishTimeControl(const std::list<NukiOpener::TimeControlEntry>& timeControlEntries, uint maxTimeControlEntryCount);
|
||||||
|
void publishAuth(const std::list<NukiLock::AuthorizationEntry>& authEntries, uint maxAuthEntryCount);
|
||||||
void publishStatusUpdated(const bool statusUpdated);
|
void publishStatusUpdated(const bool statusUpdated);
|
||||||
void publishConfigCommandResult(const char* result);
|
void publishConfigCommandResult(const char* result);
|
||||||
void publishKeypadCommandResult(const char* result);
|
void publishKeypadCommandResult(const char* result);
|
||||||
void publishKeypadJsonCommandResult(const char* result);
|
void publishKeypadJsonCommandResult(const char* result);
|
||||||
void publishTimeControlCommandResult(const char* result);
|
void publishTimeControlCommandResult(const char* result);
|
||||||
|
void publishAuthCommandResult(const char* result);
|
||||||
|
|
||||||
void setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char* value));
|
void setLockActionReceivedCallback(LockActionResult (*lockActionReceivedCallback)(const char* value));
|
||||||
void setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(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 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 setKeypadJsonCommandReceivedCallback(void (*keypadJsonCommandReceivedReceivedCallback)(const char* value));
|
||||||
void setTimeControlCommandReceivedCallback(void (*timeControlCommandReceivedReceivedCallback)(const char* value));
|
void setTimeControlCommandReceivedCallback(void (*timeControlCommandReceivedReceivedCallback)(const char* value));
|
||||||
|
void setAuthCommandReceivedCallback(void (*authCommandReceivedReceivedCallback)(const char* value));
|
||||||
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override;
|
||||||
|
|
||||||
bool reconnected();
|
bool reconnected();
|
||||||
@@ -105,4 +108,5 @@ private:
|
|||||||
void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr;
|
void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr;
|
||||||
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
void (*_keypadJsonCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||||
void (*_timeControlCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
void (*_timeControlCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||||
|
void (*_authCommandReceivedReceivedCallback)(const char* value) = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId
|
|||||||
network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback);
|
network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback);
|
||||||
if(_preferences->getBool(preference_disable_non_json, false)) network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback);
|
if(_preferences->getBool(preference_disable_non_json, false)) network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback);
|
||||||
network->setKeypadJsonCommandReceivedCallback(nukiOpenerInst->onKeypadJsonCommandReceivedCallback);
|
network->setKeypadJsonCommandReceivedCallback(nukiOpenerInst->onKeypadJsonCommandReceivedCallback);
|
||||||
|
network->setTimeControlCommandReceivedCallback(nukiOpenerInst->onTimeControlCommandReceivedCallback);
|
||||||
|
network->setAuthCommandReceivedCallback(nukiOpenerInst->onAuthCommandReceivedCallback);
|
||||||
|
|
||||||
_gpio->addCallback(NukiOpenerWrapper::gpioActionCallback);
|
_gpio->addCallback(NukiOpenerWrapper::gpioActionCallback);
|
||||||
}
|
}
|
||||||
@@ -72,6 +74,7 @@ void NukiOpenerWrapper::initialize()
|
|||||||
_publishAuthData = _preferences->getBool(preference_publish_authdata);
|
_publishAuthData = _preferences->getBool(preference_publish_authdata);
|
||||||
_maxKeypadCodeCount = _preferences->getUInt(preference_opener_max_keypad_code_count);
|
_maxKeypadCodeCount = _preferences->getUInt(preference_opener_max_keypad_code_count);
|
||||||
_maxTimeControlEntryCount = _preferences->getUInt(preference_opener_max_timecontrol_entry_count);
|
_maxTimeControlEntryCount = _preferences->getUInt(preference_opener_max_timecontrol_entry_count);
|
||||||
|
_maxAuthEntryCount = _preferences->getUInt(preference_opener_max_auth_entry_count);
|
||||||
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
||||||
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
|
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
|
||||||
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200);
|
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200);
|
||||||
@@ -219,6 +222,11 @@ void NukiOpenerWrapper::update()
|
|||||||
_waitTimeControlUpdateTs = 0;
|
_waitTimeControlUpdateTs = 0;
|
||||||
updateTimeControl(true);
|
updateTimeControl(true);
|
||||||
}
|
}
|
||||||
|
if(_waitAuthUpdateTs != 0 && ts > _waitAuthUpdateTs)
|
||||||
|
{
|
||||||
|
_waitAuthUpdateTs = 0;
|
||||||
|
updateAuth(true);
|
||||||
|
}
|
||||||
if(_hassEnabled && _nukiConfigValid && _nukiAdvancedConfigValid && _network->reconnected())
|
if(_hassEnabled && _nukiConfigValid && _nukiAdvancedConfigValid && _network->reconnected())
|
||||||
{
|
{
|
||||||
setupHASS();
|
setupHASS();
|
||||||
@@ -492,6 +500,7 @@ void NukiOpenerWrapper::updateConfig()
|
|||||||
if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishConfig(_nukiConfig);
|
if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishConfig(_nukiConfig);
|
||||||
_retryConfigCount = 0;
|
_retryConfigCount = 0;
|
||||||
if(_preferences->getBool(preference_timecontrol_info_enabled, false)) updateTimeControl(false);
|
if(_preferences->getBool(preference_timecontrol_info_enabled, false)) updateTimeControl(false);
|
||||||
|
if(_preferences->getBool(preference_auth_info_enabled)) updateAuth(false);
|
||||||
|
|
||||||
const int pinStatus = _preferences->getInt(preference_opener_pin_status, 4);
|
const int pinStatus = _preferences->getInt(preference_opener_pin_status, 4);
|
||||||
|
|
||||||
@@ -779,6 +788,67 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
|
|||||||
postponeBleWatchdog();
|
postponeBleWatchdog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiOpenerWrapper::updateAuth(bool retrieved)
|
||||||
|
{
|
||||||
|
if(!_preferences->getBool(preference_auth_info_enabled)) return;
|
||||||
|
|
||||||
|
if(!retrieved)
|
||||||
|
{
|
||||||
|
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||||
|
_retryCount = 0;
|
||||||
|
|
||||||
|
while(_retryCount < _nrOfRetries)
|
||||||
|
{
|
||||||
|
Log->print(F("Querying opener authorization: "));
|
||||||
|
result = _nukiOpener.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||||
|
delay(250);
|
||||||
|
if(result != Nuki::CmdResult::Success) {
|
||||||
|
++_retryCount;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printCommandResult(result);
|
||||||
|
if(result == Nuki::CmdResult::Success)
|
||||||
|
{
|
||||||
|
_waitAuthUpdateTs = millis() + 5000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::list<NukiOpener::AuthorizationEntry> authEntries;
|
||||||
|
_nukiOpener.getAuthorizationEntries(&authEntries);
|
||||||
|
|
||||||
|
Log->print(F("Opener authorization entries: "));
|
||||||
|
Log->println(authEntries.size());
|
||||||
|
|
||||||
|
authEntries.sort([](const NukiOpener::AuthorizationEntry& a, const NukiOpener::AuthorizationEntry& b) { return a.authId < b.authId; });
|
||||||
|
|
||||||
|
if(authEntries.size() > _preferences->getInt(preference_auth_max_entries, MAX_AUTH))
|
||||||
|
{
|
||||||
|
authEntries.resize(_preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint authCount = authEntries.size();
|
||||||
|
if(authCount > _maxAuthEntryCount)
|
||||||
|
{
|
||||||
|
_maxAuthEntryCount = authCount;
|
||||||
|
_preferences->putUInt(preference_opener_max_auth_entry_count, _maxAuthEntryCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
_network->publishAuth(authEntries, _maxAuthEntryCount);
|
||||||
|
|
||||||
|
_authIds.clear();
|
||||||
|
_authIds.reserve(authEntries.size());
|
||||||
|
for(const auto& entry : authEntries)
|
||||||
|
{
|
||||||
|
_authIds.push_back(entry.authId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postponeBleWatchdog();
|
||||||
|
}
|
||||||
|
|
||||||
void NukiOpenerWrapper::postponeBleWatchdog()
|
void NukiOpenerWrapper::postponeBleWatchdog()
|
||||||
{
|
{
|
||||||
_disableBleWatchdogTs = (esp_timer_get_time() / 1000) + 15000;
|
_disableBleWatchdogTs = (esp_timer_get_time() / 1000) + 15000;
|
||||||
@@ -1477,6 +1547,16 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceivedCallback(const char *value)
|
|||||||
nukiOpenerInst->onKeypadJsonCommandReceived(value);
|
nukiOpenerInst->onKeypadJsonCommandReceived(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiOpenerWrapper::onTimeControlCommandReceivedCallback(const char *value)
|
||||||
|
{
|
||||||
|
nukiOpenerInst->onTimeControlCommandReceived(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NukiOpenerWrapper::onAuthCommandReceivedCallback(const char *value)
|
||||||
|
{
|
||||||
|
nukiOpenerInst->onAuthCommandReceived(value);
|
||||||
|
}
|
||||||
|
|
||||||
void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
||||||
{
|
{
|
||||||
switch(action)
|
switch(action)
|
||||||
@@ -2257,13 +2337,13 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
|
|
||||||
if(!foundExisting)
|
if(!foundExisting)
|
||||||
{
|
{
|
||||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingKeypadEntry");
|
_network->publishTimeControlCommandResult("failedToRetrieveExistingTimeControlEntry");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingKeypadEntry");
|
_network->publishTimeControlCommandResult("failedToRetrieveExistingTimeControlEntry");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2314,6 +2394,464 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiOpenerWrapper::onAuthCommandReceived(const char *value)
|
||||||
|
{
|
||||||
|
if(!_nukiConfigValid)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("configNotReady");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isPinValid())
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noValidPinSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_preferences->getBool(preference_auth_control_enabled))
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("keypadControlDisabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument json;
|
||||||
|
DeserializationError jsonError = deserializeJson(json, value);
|
||||||
|
|
||||||
|
if(jsonError)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidJson");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char oldName[33];
|
||||||
|
const char *action = json["action"].as<const char*>();
|
||||||
|
uint16_t authId = json["authId"].as<unsigned int>();
|
||||||
|
//uint8_t idType = json["idType"].as<unsigned int>();
|
||||||
|
//unsigned char secretKeyK[32] = {0x00};
|
||||||
|
uint8_t remoteAllowed;
|
||||||
|
uint8_t enabled;
|
||||||
|
uint8_t timeLimited;
|
||||||
|
String name;
|
||||||
|
//String sharedKey;
|
||||||
|
String allowedFrom;
|
||||||
|
String allowedUntil;
|
||||||
|
String allowedWeekdays;
|
||||||
|
String allowedFromTime;
|
||||||
|
String allowedUntilTime;
|
||||||
|
|
||||||
|
if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as<unsigned int>();
|
||||||
|
else remoteAllowed = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>();
|
||||||
|
else enabled = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>();
|
||||||
|
else timeLimited = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("name")) name = json["name"].as<String>();
|
||||||
|
//if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as<String>();
|
||||||
|
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>();
|
||||||
|
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>();
|
||||||
|
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>();
|
||||||
|
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>();
|
||||||
|
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>();
|
||||||
|
|
||||||
|
if(action)
|
||||||
|
{
|
||||||
|
bool idExists = false;
|
||||||
|
|
||||||
|
if(authId)
|
||||||
|
{
|
||||||
|
idExists = std::find(_authIds.begin(), _authIds.end(), authId) != _authIds.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||||
|
_retryCount = 0;
|
||||||
|
|
||||||
|
while(_retryCount < _nrOfRetries)
|
||||||
|
{
|
||||||
|
if(strcmp(action, "delete") == 0) {
|
||||||
|
if(idExists)
|
||||||
|
{
|
||||||
|
result = _nukiOpener.deleteAuthorizationEntry(authId);
|
||||||
|
Log->print(F("Delete authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
|
||||||
|
{
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
if (strcmp(action, "update") != 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noNameSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(sharedKey.length() != 64)
|
||||||
|
{
|
||||||
|
if (strcmp(action, "update") != 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noSharedKeySet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i=0; i<sharedKey.length();i+=2) secretKeyK[(i/2)] = std::stoi(sharedKey.substring(i, i+2).c_str(), nullptr, 16);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned int allowedFromAr[6];
|
||||||
|
unsigned int allowedUntilAr[6];
|
||||||
|
unsigned int allowedFromTimeAr[2];
|
||||||
|
unsigned int allowedUntilTimeAr[2];
|
||||||
|
uint8_t allowedWeekdaysInt = 0;
|
||||||
|
|
||||||
|
if(timeLimited == 1)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() == 19)
|
||||||
|
{
|
||||||
|
allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt();
|
||||||
|
allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt();
|
||||||
|
allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt();
|
||||||
|
allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt();
|
||||||
|
allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt();
|
||||||
|
allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt();
|
||||||
|
|
||||||
|
if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedUntil.length() > 0 == 19)
|
||||||
|
{
|
||||||
|
allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt();
|
||||||
|
allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt();
|
||||||
|
allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt();
|
||||||
|
allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt();
|
||||||
|
allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt();
|
||||||
|
allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt();
|
||||||
|
|
||||||
|
if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedFromTime.length() == 5)
|
||||||
|
{
|
||||||
|
allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt();
|
||||||
|
allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt();
|
||||||
|
|
||||||
|
if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedUntilTime.length() == 5)
|
||||||
|
{
|
||||||
|
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt();
|
||||||
|
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt();
|
||||||
|
|
||||||
|
if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64;
|
||||||
|
if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32;
|
||||||
|
if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16;
|
||||||
|
if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8;
|
||||||
|
if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4;
|
||||||
|
if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2;
|
||||||
|
if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(action, "add") == 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("addActionNotSupported");
|
||||||
|
return;
|
||||||
|
|
||||||
|
NukiOpener::NewAuthorizationEntry entry;
|
||||||
|
memset(&entry, 0, sizeof(entry));
|
||||||
|
size_t nameLen = name.length();
|
||||||
|
memcpy(&entry.name, name.c_str(), nameLen > 32 ? 32 : nameLen);
|
||||||
|
/*
|
||||||
|
memcpy(&entry.sharedKey, secretKeyK, 32);
|
||||||
|
|
||||||
|
if(idType != 1)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidIdType");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.idType = idType;
|
||||||
|
*/
|
||||||
|
entry.remoteAllowed = remoteAllowed == 1 ? 1 : 0;
|
||||||
|
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||||
|
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromYear = allowedFromAr[0];
|
||||||
|
entry.allowedFromMonth = allowedFromAr[1];
|
||||||
|
entry.allowedFromDay = allowedFromAr[2];
|
||||||
|
entry.allowedFromHour = allowedFromAr[3];
|
||||||
|
entry.allowedFromMinute = allowedFromAr[4];
|
||||||
|
entry.allowedFromSecond = allowedFromAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilYear = allowedUntilAr[0];
|
||||||
|
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||||
|
entry.allowedUntilDay = allowedUntilAr[2];
|
||||||
|
entry.allowedUntilHour = allowedUntilAr[3];
|
||||||
|
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||||
|
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||||
|
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||||
|
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _nukiOpener.addAuthorizationEntry(entry);
|
||||||
|
Log->print(F("Add authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
else if (strcmp(action, "update") == 0)
|
||||||
|
{
|
||||||
|
if(!authId)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!idExists)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nuki::CmdResult resultAuth = _nukiOpener.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||||
|
bool foundExisting = false;
|
||||||
|
|
||||||
|
if(resultAuth == Nuki::CmdResult::Success)
|
||||||
|
{
|
||||||
|
delay(250);
|
||||||
|
std::list<NukiOpener::AuthorizationEntry> entries;
|
||||||
|
_nukiOpener.getAuthorizationEntries(&entries);
|
||||||
|
|
||||||
|
for(const auto& entry : entries)
|
||||||
|
{
|
||||||
|
if (authId != entry.authId) continue;
|
||||||
|
else foundExisting = true;
|
||||||
|
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
memset(oldName, 0, sizeof(oldName));
|
||||||
|
memcpy(oldName, entry.name, sizeof(entry.name));
|
||||||
|
}
|
||||||
|
if(remoteAllowed == 2) remoteAllowed = entry.remoteAllowed;
|
||||||
|
if(enabled == 2) enabled = entry.enabled;
|
||||||
|
if(timeLimited == 2) timeLimited = entry.timeLimited;
|
||||||
|
if(allowedFrom.length() < 1)
|
||||||
|
{
|
||||||
|
allowedFrom = "old";
|
||||||
|
allowedFromAr[0] = entry.allowedFromYear;
|
||||||
|
allowedFromAr[1] = entry.allowedFromMonth;
|
||||||
|
allowedFromAr[2] = entry.allowedFromDay;
|
||||||
|
allowedFromAr[3] = entry.allowedFromHour;
|
||||||
|
allowedFromAr[4] = entry.allowedFromMinute;
|
||||||
|
allowedFromAr[5] = entry.allowedFromSecond;
|
||||||
|
}
|
||||||
|
if(allowedUntil.length() < 1)
|
||||||
|
{
|
||||||
|
allowedUntil = "old";
|
||||||
|
allowedUntilAr[0] = entry.allowedUntilYear;
|
||||||
|
allowedUntilAr[1] = entry.allowedUntilMonth;
|
||||||
|
allowedUntilAr[2] = entry.allowedUntilDay;
|
||||||
|
allowedUntilAr[3] = entry.allowedUntilHour;
|
||||||
|
allowedUntilAr[4] = entry.allowedUntilMinute;
|
||||||
|
allowedUntilAr[5] = entry.allowedUntilSecond;
|
||||||
|
}
|
||||||
|
if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays;
|
||||||
|
if(allowedFromTime.length() < 1)
|
||||||
|
{
|
||||||
|
allowedFromTime = "old";
|
||||||
|
allowedFromTimeAr[0] = entry.allowedFromTimeHour;
|
||||||
|
allowedFromTimeAr[1] = entry.allowedFromTimeMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() < 1)
|
||||||
|
{
|
||||||
|
allowedUntilTime = "old";
|
||||||
|
allowedUntilTimeAr[0] = entry.allowedUntilTimeHour;
|
||||||
|
allowedUntilTimeAr[1] = entry.allowedUntilTimeMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!foundExisting)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NukiOpener::UpdatedAuthorizationEntry entry;
|
||||||
|
|
||||||
|
memset(&entry, 0, sizeof(entry));
|
||||||
|
entry.authId = authId;
|
||||||
|
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
size_t nameLen = strlen(oldName);
|
||||||
|
memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t nameLen = name.length();
|
||||||
|
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
|
||||||
|
}
|
||||||
|
entry.remoteAllowed = remoteAllowed;
|
||||||
|
entry.enabled = enabled;
|
||||||
|
entry.timeLimited = timeLimited;
|
||||||
|
|
||||||
|
if(enabled == 1)
|
||||||
|
{
|
||||||
|
if(timeLimited == 1)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromYear = allowedFromAr[0];
|
||||||
|
entry.allowedFromMonth = allowedFromAr[1];
|
||||||
|
entry.allowedFromDay = allowedFromAr[2];
|
||||||
|
entry.allowedFromHour = allowedFromAr[3];
|
||||||
|
entry.allowedFromMinute = allowedFromAr[4];
|
||||||
|
entry.allowedFromSecond = allowedFromAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilYear = allowedUntilAr[0];
|
||||||
|
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||||
|
entry.allowedUntilDay = allowedUntilAr[2];
|
||||||
|
entry.allowedUntilHour = allowedUntilAr[3];
|
||||||
|
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||||
|
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||||
|
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||||
|
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _nukiOpener.updateAuthorizationEntry(entry);
|
||||||
|
Log->print(F("Update authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAction");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result != Nuki::CmdResult::Success) {
|
||||||
|
++_retryCount;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAuth(false);
|
||||||
|
|
||||||
|
if((int)result != -1)
|
||||||
|
{
|
||||||
|
char resultStr[15];
|
||||||
|
memset(&resultStr, 0, sizeof(resultStr));
|
||||||
|
NukiOpener::cmdResultToString(result, resultStr);
|
||||||
|
_network->publishAuthCommandResult(resultStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noActionSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const NukiOpener::OpenerState &NukiOpenerWrapper::keyTurnerState()
|
const NukiOpener::OpenerState &NukiOpenerWrapper::keyTurnerState()
|
||||||
{
|
{
|
||||||
return _keyTurnerState;
|
return _keyTurnerState;
|
||||||
|
|||||||
@@ -53,12 +53,14 @@ private:
|
|||||||
static void onKeypadCommandReceivedCallback(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
static void onKeypadCommandReceivedCallback(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||||
static void onKeypadJsonCommandReceivedCallback(const char* value);
|
static void onKeypadJsonCommandReceivedCallback(const char* value);
|
||||||
static void onTimeControlCommandReceivedCallback(const char* value);
|
static void onTimeControlCommandReceivedCallback(const char* value);
|
||||||
|
static void onAuthCommandReceivedCallback(const char* value);
|
||||||
static void gpioActionCallback(const GpioAction& action, const int& pin);
|
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 onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||||
void onConfigUpdateReceived(const char* value);
|
void onConfigUpdateReceived(const char* value);
|
||||||
void onKeypadJsonCommandReceived(const char* value);
|
void onKeypadJsonCommandReceived(const char* value);
|
||||||
void onTimeControlCommandReceived(const char* value);
|
void onTimeControlCommandReceived(const char* value);
|
||||||
|
void onAuthCommandReceived(const char* value);
|
||||||
|
|
||||||
void updateKeyTurnerState();
|
void updateKeyTurnerState();
|
||||||
void updateBatteryState();
|
void updateBatteryState();
|
||||||
@@ -66,6 +68,7 @@ private:
|
|||||||
void updateAuthData(bool retrieved);
|
void updateAuthData(bool retrieved);
|
||||||
void updateKeypad(bool retrieved);
|
void updateKeypad(bool retrieved);
|
||||||
void updateTimeControl(bool retrieved);
|
void updateTimeControl(bool retrieved);
|
||||||
|
void updateAuth(bool retrieved);
|
||||||
void postponeBleWatchdog();
|
void postponeBleWatchdog();
|
||||||
|
|
||||||
void updateGpioOutputs();
|
void updateGpioOutputs();
|
||||||
@@ -109,6 +112,7 @@ private:
|
|||||||
int64_t _nextRetryTs = 0;
|
int64_t _nextRetryTs = 0;
|
||||||
std::vector<uint16_t> _keypadCodeIds;
|
std::vector<uint16_t> _keypadCodeIds;
|
||||||
std::vector<uint8_t> _timeControlIds;
|
std::vector<uint8_t> _timeControlIds;
|
||||||
|
std::vector<uint32_t> _authIds;
|
||||||
|
|
||||||
NukiOpener::OpenerState _lastKeyTurnerState;
|
NukiOpener::OpenerState _lastKeyTurnerState;
|
||||||
NukiOpener::OpenerState _keyTurnerState;
|
NukiOpener::OpenerState _keyTurnerState;
|
||||||
@@ -129,6 +133,7 @@ private:
|
|||||||
bool _keypadEnabled = false;
|
bool _keypadEnabled = false;
|
||||||
uint _maxKeypadCodeCount = 0;
|
uint _maxKeypadCodeCount = 0;
|
||||||
uint _maxTimeControlEntryCount = 0;
|
uint _maxTimeControlEntryCount = 0;
|
||||||
|
uint _maxAuthEntryCount = 0;
|
||||||
int _rssiPublishInterval = 0;
|
int _rssiPublishInterval = 0;
|
||||||
int64_t _nextLockStateUpdateTs = 0;
|
int64_t _nextLockStateUpdateTs = 0;
|
||||||
int64_t _nextBatteryReportTs = 0;
|
int64_t _nextBatteryReportTs = 0;
|
||||||
@@ -136,6 +141,7 @@ private:
|
|||||||
int64_t _waitAuthLogUpdateTs = 0;
|
int64_t _waitAuthLogUpdateTs = 0;
|
||||||
int64_t _waitKeypadUpdateTs = 0;
|
int64_t _waitKeypadUpdateTs = 0;
|
||||||
int64_t _waitTimeControlUpdateTs = 0;
|
int64_t _waitTimeControlUpdateTs = 0;
|
||||||
|
int64_t _waitAuthUpdateTs = 0;
|
||||||
int64_t _nextKeypadUpdateTs = 0;
|
int64_t _nextKeypadUpdateTs = 0;
|
||||||
int64_t _nextPairTs = 0;
|
int64_t _nextPairTs = 0;
|
||||||
int64_t _nextRssiTs = 0;
|
int64_t _nextRssiTs = 0;
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId,
|
|||||||
if(_disableNonJSON) network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback);
|
if(_disableNonJSON) network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback);
|
||||||
network->setKeypadJsonCommandReceivedCallback(nukiInst->onKeypadJsonCommandReceivedCallback);
|
network->setKeypadJsonCommandReceivedCallback(nukiInst->onKeypadJsonCommandReceivedCallback);
|
||||||
network->setTimeControlCommandReceivedCallback(nukiInst->onTimeControlCommandReceivedCallback);
|
network->setTimeControlCommandReceivedCallback(nukiInst->onTimeControlCommandReceivedCallback);
|
||||||
|
network->setAuthCommandReceivedCallback(nukiInst->onAuthCommandReceivedCallback);
|
||||||
|
|
||||||
_gpio->addCallback(NukiWrapper::gpioActionCallback);
|
_gpio->addCallback(NukiWrapper::gpioActionCallback);
|
||||||
}
|
}
|
||||||
@@ -76,6 +77,7 @@ void NukiWrapper::initialize(const bool& firstStart)
|
|||||||
_publishAuthData = _preferences->getBool(preference_publish_authdata);
|
_publishAuthData = _preferences->getBool(preference_publish_authdata);
|
||||||
_maxKeypadCodeCount = _preferences->getUInt(preference_lock_max_keypad_code_count);
|
_maxKeypadCodeCount = _preferences->getUInt(preference_lock_max_keypad_code_count);
|
||||||
_maxTimeControlEntryCount = _preferences->getUInt(preference_lock_max_timecontrol_entry_count);
|
_maxTimeControlEntryCount = _preferences->getUInt(preference_lock_max_timecontrol_entry_count);
|
||||||
|
_maxAuthEntryCount = _preferences->getUInt(preference_lock_max_auth_entry_count);
|
||||||
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
_restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost);
|
||||||
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
|
_hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != "";
|
||||||
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200);
|
_nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200);
|
||||||
@@ -262,7 +264,6 @@ void NukiWrapper::update()
|
|||||||
cmdResult = _nukiLock.lockAction(_nextLockAction, 0, 0);
|
cmdResult = _nukiLock.lockAction(_nextLockAction, 0, 0);
|
||||||
char resultStr[15] = {0};
|
char resultStr[15] = {0};
|
||||||
NukiLock::cmdResultToString(cmdResult, resultStr);
|
NukiLock::cmdResultToString(cmdResult, resultStr);
|
||||||
|
|
||||||
_network->publishCommandResult(resultStr);
|
_network->publishCommandResult(resultStr);
|
||||||
|
|
||||||
Log->print(F("Lock action result: "));
|
Log->print(F("Lock action result: "));
|
||||||
@@ -344,6 +345,11 @@ void NukiWrapper::update()
|
|||||||
_waitTimeControlUpdateTs = 0;
|
_waitTimeControlUpdateTs = 0;
|
||||||
updateTimeControl(true);
|
updateTimeControl(true);
|
||||||
}
|
}
|
||||||
|
if(_waitAuthUpdateTs != 0 && ts > _waitAuthUpdateTs)
|
||||||
|
{
|
||||||
|
_waitAuthUpdateTs = 0;
|
||||||
|
updateAuth(true);
|
||||||
|
}
|
||||||
if(_hassEnabled && _nukiConfigValid && _nukiAdvancedConfigValid && _network->reconnected())
|
if(_hassEnabled && _nukiConfigValid && _nukiAdvancedConfigValid && _network->reconnected())
|
||||||
{
|
{
|
||||||
setupHASS();
|
setupHASS();
|
||||||
@@ -446,7 +452,6 @@ void NukiWrapper::updateKeyTurnerState()
|
|||||||
Log->print(_retryCount + 1);
|
Log->print(_retryCount + 1);
|
||||||
Log->print("): ");
|
Log->print("): ");
|
||||||
result =_nukiLock.requestKeyTurnerState(&_keyTurnerState);
|
result =_nukiLock.requestKeyTurnerState(&_keyTurnerState);
|
||||||
|
|
||||||
if(result != Nuki::CmdResult::Success) {
|
if(result != Nuki::CmdResult::Success) {
|
||||||
++_retryCount;
|
++_retryCount;
|
||||||
}
|
}
|
||||||
@@ -566,6 +571,7 @@ void NukiWrapper::updateConfig()
|
|||||||
_hardwareVersion = std::to_string(_nukiConfig.hardwareRevision[0]) + "." + std::to_string(_nukiConfig.hardwareRevision[1]);
|
_hardwareVersion = std::to_string(_nukiConfig.hardwareRevision[0]) + "." + std::to_string(_nukiConfig.hardwareRevision[1]);
|
||||||
if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishConfig(_nukiConfig);
|
if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishConfig(_nukiConfig);
|
||||||
if(_preferences->getBool(preference_timecontrol_info_enabled)) updateTimeControl(false);
|
if(_preferences->getBool(preference_timecontrol_info_enabled)) updateTimeControl(false);
|
||||||
|
if(_preferences->getBool(preference_auth_info_enabled)) updateAuth(false);
|
||||||
|
|
||||||
const int pinStatus = _preferences->getInt(preference_lock_pin_status, 4);
|
const int pinStatus = _preferences->getInt(preference_lock_pin_status, 4);
|
||||||
|
|
||||||
@@ -577,7 +583,6 @@ void NukiWrapper::updateConfig()
|
|||||||
while(_retryCount < _nrOfRetries + 1)
|
while(_retryCount < _nrOfRetries + 1)
|
||||||
{
|
{
|
||||||
result = _nukiLock.verifySecurityPin();
|
result = _nukiLock.verifySecurityPin();
|
||||||
|
|
||||||
if(result != Nuki::CmdResult::Success) {
|
if(result != Nuki::CmdResult::Success) {
|
||||||
++_retryCount;
|
++_retryCount;
|
||||||
}
|
}
|
||||||
@@ -665,7 +670,6 @@ void NukiWrapper::updateAuthData(bool retrieved)
|
|||||||
{
|
{
|
||||||
Log->print(F("Retrieve log entries: "));
|
Log->print(F("Retrieve log entries: "));
|
||||||
result = _nukiLock.retrieveLogEntries(0, _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 1, false);
|
result = _nukiLock.retrieveLogEntries(0, _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 1, false);
|
||||||
|
|
||||||
if(result != Nuki::CmdResult::Success) {
|
if(result != Nuki::CmdResult::Success) {
|
||||||
++_retryCount;
|
++_retryCount;
|
||||||
}
|
}
|
||||||
@@ -737,7 +741,6 @@ void NukiWrapper::updateKeypad(bool retrieved)
|
|||||||
{
|
{
|
||||||
Log->print(F("Querying lock keypad: "));
|
Log->print(F("Querying lock keypad: "));
|
||||||
result = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
|
result = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
|
||||||
|
|
||||||
if(result != Nuki::CmdResult::Success) {
|
if(result != Nuki::CmdResult::Success) {
|
||||||
++_retryCount;
|
++_retryCount;
|
||||||
}
|
}
|
||||||
@@ -804,7 +807,6 @@ void NukiWrapper::updateTimeControl(bool retrieved)
|
|||||||
{
|
{
|
||||||
Log->print(F("Querying lock timecontrol: "));
|
Log->print(F("Querying lock timecontrol: "));
|
||||||
result = _nukiLock.retrieveTimeControlEntries();
|
result = _nukiLock.retrieveTimeControlEntries();
|
||||||
|
|
||||||
if(result != Nuki::CmdResult::Success) {
|
if(result != Nuki::CmdResult::Success) {
|
||||||
++_retryCount;
|
++_retryCount;
|
||||||
}
|
}
|
||||||
@@ -852,6 +854,67 @@ void NukiWrapper::updateTimeControl(bool retrieved)
|
|||||||
postponeBleWatchdog();
|
postponeBleWatchdog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiWrapper::updateAuth(bool retrieved)
|
||||||
|
{
|
||||||
|
if(!_preferences->getBool(preference_auth_info_enabled)) return;
|
||||||
|
|
||||||
|
if(!retrieved)
|
||||||
|
{
|
||||||
|
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||||
|
_retryCount = 0;
|
||||||
|
|
||||||
|
while(_retryCount < _nrOfRetries)
|
||||||
|
{
|
||||||
|
Log->print(F("Querying lock authorization: "));
|
||||||
|
result = _nukiLock.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||||
|
delay(250);
|
||||||
|
if(result != Nuki::CmdResult::Success) {
|
||||||
|
++_retryCount;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printCommandResult(result);
|
||||||
|
if(result == Nuki::CmdResult::Success)
|
||||||
|
{
|
||||||
|
_waitAuthUpdateTs = millis() + 5000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::list<NukiLock::AuthorizationEntry> authEntries;
|
||||||
|
_nukiLock.getAuthorizationEntries(&authEntries);
|
||||||
|
|
||||||
|
Log->print(F("Lock authorization entries: "));
|
||||||
|
Log->println(authEntries.size());
|
||||||
|
|
||||||
|
authEntries.sort([](const NukiLock::AuthorizationEntry& a, const NukiLock::AuthorizationEntry& b) { return a.authId < b.authId; });
|
||||||
|
|
||||||
|
if(authEntries.size() > _preferences->getInt(preference_auth_max_entries, MAX_AUTH))
|
||||||
|
{
|
||||||
|
authEntries.resize(_preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint authCount = authEntries.size();
|
||||||
|
if(authCount > _maxAuthEntryCount)
|
||||||
|
{
|
||||||
|
_maxAuthEntryCount = authCount;
|
||||||
|
_preferences->putUInt(preference_lock_max_auth_entry_count, _maxAuthEntryCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
_network->publishAuth(authEntries, _maxAuthEntryCount);
|
||||||
|
|
||||||
|
_authIds.clear();
|
||||||
|
_authIds.reserve(authEntries.size());
|
||||||
|
for(const auto& entry : authEntries)
|
||||||
|
{
|
||||||
|
_authIds.push_back(entry.authId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postponeBleWatchdog();
|
||||||
|
}
|
||||||
|
|
||||||
void NukiWrapper::postponeBleWatchdog()
|
void NukiWrapper::postponeBleWatchdog()
|
||||||
{
|
{
|
||||||
_disableBleWatchdogTs = (esp_timer_get_time() / 1000) + 15000;
|
_disableBleWatchdogTs = (esp_timer_get_time() / 1000) + 15000;
|
||||||
@@ -1725,6 +1788,11 @@ void NukiWrapper::onTimeControlCommandReceivedCallback(const char *value)
|
|||||||
nukiInst->onTimeControlCommandReceived(value);
|
nukiInst->onTimeControlCommandReceived(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiWrapper::onAuthCommandReceivedCallback(const char *value)
|
||||||
|
{
|
||||||
|
nukiInst->onAuthCommandReceived(value);
|
||||||
|
}
|
||||||
|
|
||||||
void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin)
|
||||||
{
|
{
|
||||||
switch(action)
|
switch(action)
|
||||||
@@ -2533,13 +2601,13 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
|
|
||||||
if(!foundExisting)
|
if(!foundExisting)
|
||||||
{
|
{
|
||||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingKeypadEntry");
|
_network->publishTimeControlCommandResult("failedToRetrieveExistingTimeControlEntry");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_network->publishTimeControlCommandResult("failedToRetrieveExistingKeypadEntry");
|
_network->publishTimeControlCommandResult("failedToRetrieveExistingTimeControlEntry");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2591,6 +2659,467 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiWrapper::onAuthCommandReceived(const char *value)
|
||||||
|
{
|
||||||
|
if(!_nukiConfigValid)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("configNotReady");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isPinValid())
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noValidPinSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_preferences->getBool(preference_auth_control_enabled))
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("keypadControlDisabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument json;
|
||||||
|
DeserializationError jsonError = deserializeJson(json, value);
|
||||||
|
|
||||||
|
if(jsonError)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidJson");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char oldName[33];
|
||||||
|
const char *action = json["action"].as<const char*>();
|
||||||
|
uint16_t authId = json["authId"].as<unsigned int>();
|
||||||
|
//uint8_t idType = json["idType"].as<unsigned int>();
|
||||||
|
//unsigned char secretKeyK[32] = {0x00};
|
||||||
|
uint8_t remoteAllowed;
|
||||||
|
uint8_t enabled;
|
||||||
|
uint8_t timeLimited;
|
||||||
|
String name;
|
||||||
|
//String sharedKey;
|
||||||
|
String allowedFrom;
|
||||||
|
String allowedUntil;
|
||||||
|
String allowedWeekdays;
|
||||||
|
String allowedFromTime;
|
||||||
|
String allowedUntilTime;
|
||||||
|
|
||||||
|
if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as<unsigned int>();
|
||||||
|
else remoteAllowed = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>();
|
||||||
|
else enabled = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>();
|
||||||
|
else timeLimited = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("name")) name = json["name"].as<String>();
|
||||||
|
//if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as<String>();
|
||||||
|
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>();
|
||||||
|
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>();
|
||||||
|
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>();
|
||||||
|
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>();
|
||||||
|
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>();
|
||||||
|
|
||||||
|
if(action)
|
||||||
|
{
|
||||||
|
bool idExists = false;
|
||||||
|
|
||||||
|
if(authId)
|
||||||
|
{
|
||||||
|
idExists = std::find(_authIds.begin(), _authIds.end(), authId) != _authIds.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||||
|
_retryCount = 0;
|
||||||
|
|
||||||
|
while(_retryCount < _nrOfRetries)
|
||||||
|
{
|
||||||
|
if(strcmp(action, "delete") == 0) {
|
||||||
|
if(idExists)
|
||||||
|
{
|
||||||
|
result = _nukiLock.deleteAuthorizationEntry(authId);
|
||||||
|
delay(250);
|
||||||
|
Log->print(F("Delete authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
|
||||||
|
{
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
if (strcmp(action, "update") != 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noNameSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(sharedKey.length() != 64)
|
||||||
|
{
|
||||||
|
if (strcmp(action, "update") != 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noSharedKeySet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i=0; i<sharedKey.length();i+=2) secretKeyK[(i/2)] = std::stoi(sharedKey.substring(i, i+2).c_str(), nullptr, 16);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned int allowedFromAr[6];
|
||||||
|
unsigned int allowedUntilAr[6];
|
||||||
|
unsigned int allowedFromTimeAr[2];
|
||||||
|
unsigned int allowedUntilTimeAr[2];
|
||||||
|
uint8_t allowedWeekdaysInt = 0;
|
||||||
|
|
||||||
|
if(timeLimited == 1)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() == 19)
|
||||||
|
{
|
||||||
|
allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt();
|
||||||
|
allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt();
|
||||||
|
allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt();
|
||||||
|
allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt();
|
||||||
|
allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt();
|
||||||
|
allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt();
|
||||||
|
|
||||||
|
if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedUntil.length() > 0 == 19)
|
||||||
|
{
|
||||||
|
allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt();
|
||||||
|
allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt();
|
||||||
|
allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt();
|
||||||
|
allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt();
|
||||||
|
allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt();
|
||||||
|
allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt();
|
||||||
|
|
||||||
|
if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedFromTime.length() == 5)
|
||||||
|
{
|
||||||
|
allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt();
|
||||||
|
allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt();
|
||||||
|
|
||||||
|
if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedUntilTime.length() == 5)
|
||||||
|
{
|
||||||
|
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt();
|
||||||
|
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt();
|
||||||
|
|
||||||
|
if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64;
|
||||||
|
if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32;
|
||||||
|
if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16;
|
||||||
|
if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8;
|
||||||
|
if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4;
|
||||||
|
if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2;
|
||||||
|
if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(action, "add") == 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("addActionNotSupported");
|
||||||
|
return;
|
||||||
|
|
||||||
|
NukiLock::NewAuthorizationEntry entry;
|
||||||
|
memset(&entry, 0, sizeof(entry));
|
||||||
|
size_t nameLen = name.length();
|
||||||
|
memcpy(&entry.name, name.c_str(), nameLen > 32 ? 32 : nameLen);
|
||||||
|
/*
|
||||||
|
memcpy(&entry.sharedKey, secretKeyK, 32);
|
||||||
|
|
||||||
|
if(idType != 1)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidIdType");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.idType = idType;
|
||||||
|
*/
|
||||||
|
entry.remoteAllowed = remoteAllowed == 1 ? 1 : 0;
|
||||||
|
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||||
|
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromYear = allowedFromAr[0];
|
||||||
|
entry.allowedFromMonth = allowedFromAr[1];
|
||||||
|
entry.allowedFromDay = allowedFromAr[2];
|
||||||
|
entry.allowedFromHour = allowedFromAr[3];
|
||||||
|
entry.allowedFromMinute = allowedFromAr[4];
|
||||||
|
entry.allowedFromSecond = allowedFromAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilYear = allowedUntilAr[0];
|
||||||
|
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||||
|
entry.allowedUntilDay = allowedUntilAr[2];
|
||||||
|
entry.allowedUntilHour = allowedUntilAr[3];
|
||||||
|
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||||
|
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||||
|
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||||
|
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _nukiLock.addAuthorizationEntry(entry);
|
||||||
|
delay(250);
|
||||||
|
Log->print(F("Add authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
else if (strcmp(action, "update") == 0)
|
||||||
|
{
|
||||||
|
if(!authId)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!idExists)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nuki::CmdResult resultAuth = _nukiLock.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||||
|
delay(250);
|
||||||
|
bool foundExisting = false;
|
||||||
|
|
||||||
|
if(resultAuth == Nuki::CmdResult::Success)
|
||||||
|
{
|
||||||
|
std::list<NukiLock::AuthorizationEntry> entries;
|
||||||
|
_nukiLock.getAuthorizationEntries(&entries);
|
||||||
|
|
||||||
|
for(const auto& entry : entries)
|
||||||
|
{
|
||||||
|
if (authId != entry.authId) continue;
|
||||||
|
else foundExisting = true;
|
||||||
|
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
memset(oldName, 0, sizeof(oldName));
|
||||||
|
memcpy(oldName, entry.name, sizeof(entry.name));
|
||||||
|
}
|
||||||
|
if(remoteAllowed == 2) remoteAllowed = entry.remoteAllowed;
|
||||||
|
if(enabled == 2) enabled = entry.enabled;
|
||||||
|
if(timeLimited == 2) timeLimited = entry.timeLimited;
|
||||||
|
if(allowedFrom.length() < 1)
|
||||||
|
{
|
||||||
|
allowedFrom = "old";
|
||||||
|
allowedFromAr[0] = entry.allowedFromYear;
|
||||||
|
allowedFromAr[1] = entry.allowedFromMonth;
|
||||||
|
allowedFromAr[2] = entry.allowedFromDay;
|
||||||
|
allowedFromAr[3] = entry.allowedFromHour;
|
||||||
|
allowedFromAr[4] = entry.allowedFromMinute;
|
||||||
|
allowedFromAr[5] = entry.allowedFromSecond;
|
||||||
|
}
|
||||||
|
if(allowedUntil.length() < 1)
|
||||||
|
{
|
||||||
|
allowedUntil = "old";
|
||||||
|
allowedUntilAr[0] = entry.allowedUntilYear;
|
||||||
|
allowedUntilAr[1] = entry.allowedUntilMonth;
|
||||||
|
allowedUntilAr[2] = entry.allowedUntilDay;
|
||||||
|
allowedUntilAr[3] = entry.allowedUntilHour;
|
||||||
|
allowedUntilAr[4] = entry.allowedUntilMinute;
|
||||||
|
allowedUntilAr[5] = entry.allowedUntilSecond;
|
||||||
|
}
|
||||||
|
if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays;
|
||||||
|
if(allowedFromTime.length() < 1)
|
||||||
|
{
|
||||||
|
allowedFromTime = "old";
|
||||||
|
allowedFromTimeAr[0] = entry.allowedFromTimeHour;
|
||||||
|
allowedFromTimeAr[1] = entry.allowedFromTimeMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() < 1)
|
||||||
|
{
|
||||||
|
allowedUntilTime = "old";
|
||||||
|
allowedUntilTimeAr[0] = entry.allowedUntilTimeHour;
|
||||||
|
allowedUntilTimeAr[1] = entry.allowedUntilTimeMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!foundExisting)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NukiLock::UpdatedAuthorizationEntry entry;
|
||||||
|
|
||||||
|
memset(&entry, 0, sizeof(entry));
|
||||||
|
entry.authId = authId;
|
||||||
|
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
size_t nameLen = strlen(oldName);
|
||||||
|
memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t nameLen = name.length();
|
||||||
|
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
|
||||||
|
}
|
||||||
|
entry.remoteAllowed = remoteAllowed;
|
||||||
|
entry.enabled = enabled;
|
||||||
|
entry.timeLimited = timeLimited;
|
||||||
|
|
||||||
|
if(enabled == 1)
|
||||||
|
{
|
||||||
|
if(timeLimited == 1)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromYear = allowedFromAr[0];
|
||||||
|
entry.allowedFromMonth = allowedFromAr[1];
|
||||||
|
entry.allowedFromDay = allowedFromAr[2];
|
||||||
|
entry.allowedFromHour = allowedFromAr[3];
|
||||||
|
entry.allowedFromMinute = allowedFromAr[4];
|
||||||
|
entry.allowedFromSecond = allowedFromAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilYear = allowedUntilAr[0];
|
||||||
|
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||||
|
entry.allowedUntilDay = allowedUntilAr[2];
|
||||||
|
entry.allowedUntilHour = allowedUntilAr[3];
|
||||||
|
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||||
|
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||||
|
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||||
|
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _nukiLock.updateAuthorizationEntry(entry);
|
||||||
|
delay(250);
|
||||||
|
Log->print(F("Update authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAction");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result != Nuki::CmdResult::Success) {
|
||||||
|
++_retryCount;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAuth(false);
|
||||||
|
|
||||||
|
if((int)result != -1)
|
||||||
|
{
|
||||||
|
char resultStr[15];
|
||||||
|
memset(&resultStr, 0, sizeof(resultStr));
|
||||||
|
NukiLock::cmdResultToString(result, resultStr);
|
||||||
|
_network->publishAuthCommandResult(resultStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noActionSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const NukiLock::KeyTurnerState &NukiWrapper::keyTurnerState()
|
const NukiLock::KeyTurnerState &NukiWrapper::keyTurnerState()
|
||||||
{
|
{
|
||||||
return _keyTurnerState;
|
return _keyTurnerState;
|
||||||
|
|||||||
@@ -53,12 +53,14 @@ private:
|
|||||||
static void onKeypadCommandReceivedCallback(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
static void onKeypadCommandReceivedCallback(const char* command, const uint& id, const String& name, const String& code, const int& enabled);
|
||||||
static void onKeypadJsonCommandReceivedCallback(const char* value);
|
static void onKeypadJsonCommandReceivedCallback(const char* value);
|
||||||
static void onTimeControlCommandReceivedCallback(const char* value);
|
static void onTimeControlCommandReceivedCallback(const char* value);
|
||||||
|
static void onAuthCommandReceivedCallback(const char* value);
|
||||||
static void gpioActionCallback(const GpioAction& action, const int& pin);
|
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 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 onOfficialUpdateReceived(const char* topic, const char* value);
|
||||||
void onConfigUpdateReceived(const char* value);
|
void onConfigUpdateReceived(const char* value);
|
||||||
void onKeypadJsonCommandReceived(const char* value);
|
void onKeypadJsonCommandReceived(const char* value);
|
||||||
void onTimeControlCommandReceived(const char* value);
|
void onTimeControlCommandReceived(const char* value);
|
||||||
|
void onAuthCommandReceived(const char* value);
|
||||||
|
|
||||||
void updateKeyTurnerState();
|
void updateKeyTurnerState();
|
||||||
void updateBatteryState();
|
void updateBatteryState();
|
||||||
@@ -66,6 +68,7 @@ private:
|
|||||||
void updateAuthData(bool retrieved);
|
void updateAuthData(bool retrieved);
|
||||||
void updateKeypad(bool retrieved);
|
void updateKeypad(bool retrieved);
|
||||||
void updateTimeControl(bool retrieved);
|
void updateTimeControl(bool retrieved);
|
||||||
|
void updateAuth(bool retrieved);
|
||||||
void postponeBleWatchdog();
|
void postponeBleWatchdog();
|
||||||
|
|
||||||
void updateGpioOutputs();
|
void updateGpioOutputs();
|
||||||
@@ -101,6 +104,7 @@ private:
|
|||||||
bool _clearAuthData = false;
|
bool _clearAuthData = false;
|
||||||
std::vector<uint16_t> _keypadCodeIds;
|
std::vector<uint16_t> _keypadCodeIds;
|
||||||
std::vector<uint8_t> _timeControlIds;
|
std::vector<uint8_t> _timeControlIds;
|
||||||
|
std::vector<uint32_t> _authIds;
|
||||||
|
|
||||||
NukiLock::KeyTurnerState _lastKeyTurnerState;
|
NukiLock::KeyTurnerState _lastKeyTurnerState;
|
||||||
NukiLock::KeyTurnerState _keyTurnerState;
|
NukiLock::KeyTurnerState _keyTurnerState;
|
||||||
@@ -122,6 +126,7 @@ private:
|
|||||||
bool _keypadEnabled = false;
|
bool _keypadEnabled = false;
|
||||||
uint _maxKeypadCodeCount = 0;
|
uint _maxKeypadCodeCount = 0;
|
||||||
uint _maxTimeControlEntryCount = 0;
|
uint _maxTimeControlEntryCount = 0;
|
||||||
|
uint _maxAuthEntryCount = 0;
|
||||||
int _nrOfRetries = 0;
|
int _nrOfRetries = 0;
|
||||||
int _retryDelay = 0;
|
int _retryDelay = 0;
|
||||||
int _retryCount = 0;
|
int _retryCount = 0;
|
||||||
@@ -137,6 +142,7 @@ private:
|
|||||||
int64_t _waitAuthLogUpdateTs = 0;
|
int64_t _waitAuthLogUpdateTs = 0;
|
||||||
int64_t _waitKeypadUpdateTs = 0;
|
int64_t _waitKeypadUpdateTs = 0;
|
||||||
int64_t _waitTimeControlUpdateTs = 0;
|
int64_t _waitTimeControlUpdateTs = 0;
|
||||||
|
int64_t _waitAuthUpdateTs = 0;
|
||||||
int64_t _nextKeypadUpdateTs = 0;
|
int64_t _nextKeypadUpdateTs = 0;
|
||||||
int64_t _nextRssiTs = 0;
|
int64_t _nextRssiTs = 0;
|
||||||
int64_t _lastRssi = 0;
|
int64_t _lastRssi = 0;
|
||||||
|
|||||||
@@ -96,6 +96,12 @@
|
|||||||
#define preference_show_secrets (char*)"showSecr"
|
#define preference_show_secrets (char*)"showSecr"
|
||||||
#define preference_ble_tx_power (char*)"bleTxPwr"
|
#define preference_ble_tx_power (char*)"bleTxPwr"
|
||||||
#define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis"
|
#define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis"
|
||||||
|
#define preference_lock_max_auth_entry_count (char*)"maxauth"
|
||||||
|
#define preference_opener_max_auth_entry_count (char*)"opmaxauth"
|
||||||
|
#define preference_auth_control_enabled (char*)"authCtrlEna"
|
||||||
|
#define preference_auth_topic_per_entry (char*)"authPerEntry"
|
||||||
|
#define preference_auth_info_enabled (char*)"authInfoEna"
|
||||||
|
#define preference_auth_max_entries (char*)"authmaxentry"
|
||||||
#define preference_network_custom_phy (char*)"ntwPHY"
|
#define preference_network_custom_phy (char*)"ntwPHY"
|
||||||
#define preference_network_custom_addr (char*)"ntwADDR"
|
#define preference_network_custom_addr (char*)"ntwADDR"
|
||||||
#define preference_network_custom_irq (char*)"ntwIRQ"
|
#define preference_network_custom_irq (char*)"ntwIRQ"
|
||||||
@@ -273,7 +279,8 @@ private:
|
|||||||
preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled,
|
preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled,
|
||||||
preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq,
|
preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq,
|
||||||
preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_mosi,
|
preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_mosi,
|
||||||
preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure
|
preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count,
|
||||||
|
preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries,
|
||||||
};
|
};
|
||||||
std::vector<char*> _redact =
|
std::vector<char*> _redact =
|
||||||
{
|
{
|
||||||
@@ -288,7 +295,8 @@ private:
|
|||||||
preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_register_as_app, preference_register_opener_as_app, preference_ip_dhcp_enabled,
|
preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_register_as_app, preference_register_opener_as_app, preference_ip_dhcp_enabled,
|
||||||
preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid,
|
preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid,
|
||||||
preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt,
|
preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt,
|
||||||
preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, preference_ntw_reconfigure
|
preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled,
|
||||||
|
preference_ntw_reconfigure
|
||||||
};
|
};
|
||||||
std::vector<char*> _bytePrefs =
|
std::vector<char*> _bytePrefs =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -283,10 +283,9 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
|||||||
#else
|
#else
|
||||||
String build_type = "debug";
|
String build_type = "debug";
|
||||||
#endif
|
#endif
|
||||||
|
response->print("<form onsubmit=\"if(document.getElementById('currentver').value == document.getElementById('latestver').value && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest release?'); } \" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"release\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"background: green\" value=\"Update to latest release\"></form>");
|
||||||
response->print("<form onsubmit=\"if(document.getElementById('currentver') == document.getElementById('latestver') && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest release?'); } \" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"release\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"background: green\" value=\"Update to latest release\"></form>");
|
response->print("<form onsubmit=\"if(document.getElementById('currentver').value == document.getElementById('betaver').value && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest beta? This version could contain breaking bugs and necessitate downgrading to the latest release version using USB/Serial'); }\" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"beta\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"color: black; background: yellow\" value=\"Update to latest beta\"></form>");
|
||||||
response->print("<form onsubmit=\"if(document.getElementById('currentver') == document.getElementById('betaver') && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest beta? This version could contain breaking bugs and necessitate downgrading to the latest release version using USB/Serial'); }\" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"beta\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"color: black; background: yellow\" value=\"Update to latest beta\"></form>");
|
response->print("<form onsubmit=\"if(document.getElementById('currentver').value == document.getElementById('devver').value && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest development version? This version could contain breaking bugs and necessitate downgrading to the latest release version using USB/Serial'); }\" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"master\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"background: red\" value=\"Update to latest development version\"></form>");
|
||||||
response->print("<form onsubmit=\"if(document.getElementById('currentver') == document.getElementById('devver') && '" + release_type + "' == '" + build_type + "') { alert('You are already on this version, build and build type'); return false; } else { return confirm('Do you really want to update to the latest development version? This version could contain breaking bugs and necessitate downgrading to the latest release version using USB/Serial'); }\" action=\"/autoupdate\" method=\"get\" style=\"float: left; margin-right: 10px\"><input type=\"hidden\" name=\"master\" value=\"1\" /><input type=\"hidden\" name=\"" + release_type + "\" value=\"1\" /><input type=\"hidden\" name=\"token\" value=\"" + _confirmCode + "\" /><br><input type=\"submit\" style=\"background: red\" value=\"Update to latest development version\"></form>");
|
|
||||||
response->print("<div style=\"clear: both\"></div><br>");
|
response->print("<div style=\"clear: both\"></div><br>");
|
||||||
|
|
||||||
response->print("<b>Current version: </b><span id=\"currentver\">");
|
response->print("<b>Current version: </b><span id=\"currentver\">");
|
||||||
@@ -299,10 +298,10 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
|||||||
|
|
||||||
#ifndef NUKI_HUB_UPDATER
|
#ifndef NUKI_HUB_UPDATER
|
||||||
bool manifestSuccess = false;
|
bool manifestSuccess = false;
|
||||||
|
JsonDocument doc;
|
||||||
|
|
||||||
NetworkClientSecure *client = new NetworkClientSecure;
|
NetworkClientSecure *client = new NetworkClientSecure;
|
||||||
if (client) {
|
if (client) {
|
||||||
//client->setDefaultCACertBundle();
|
|
||||||
client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
||||||
{
|
{
|
||||||
HTTPClient https;
|
HTTPClient https;
|
||||||
@@ -315,12 +314,21 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
|||||||
|
|
||||||
if (http_responseCode == HTTP_CODE_OK || http_responseCode == HTTP_CODE_MOVED_PERMANENTLY)
|
if (http_responseCode == HTTP_CODE_OK || http_responseCode == HTTP_CODE_MOVED_PERMANENTLY)
|
||||||
{
|
{
|
||||||
JsonDocument doc;
|
|
||||||
DeserializationError jsonError = deserializeJson(doc, https.getStream());
|
DeserializationError jsonError = deserializeJson(doc, https.getStream());
|
||||||
|
if (!jsonError) { manifestSuccess = true; }
|
||||||
|
}
|
||||||
|
https.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete client;
|
||||||
|
}
|
||||||
|
|
||||||
if (!jsonError)
|
if(!manifestSuccess)
|
||||||
|
{
|
||||||
|
response->print("<span id=\"currentver\" style=\"display: none;\">currentver</span><span id=\"latestver\" style=\"display: none;\">latestver</span><span id=\"devver\" style=\"display: none;\">devver</span><span id=\"betaver\" style=\"display: none;\">betaver</span>");
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
manifestSuccess = true;
|
|
||||||
response->print("<b>Latest release version: </b><span id=\"latestver\">");
|
response->print("<b>Latest release version: </b><span id=\"latestver\">");
|
||||||
response->print(doc["release"]["fullversion"].as<const char*>());
|
response->print(doc["release"]["fullversion"].as<const char*>());
|
||||||
response->print(" (");
|
response->print(" (");
|
||||||
@@ -351,17 +359,6 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
|||||||
response->print(doc["master"]["time"].as<const char*>());
|
response->print(doc["master"]["time"].as<const char*>());
|
||||||
response->print("<br>");
|
response->print("<br>");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
https.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete client;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!manifestSuccess)
|
|
||||||
{
|
|
||||||
response->print("<span id=\"currentver\" style=\"display: none;\">currentver</span><span id=\"latestver\" style=\"display: none;\">latestver</span><span id=\"devver\" style=\"display: none;\">devver</span><span id=\"betaver\" style=\"display: none;\">betaver</span>");
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
response->print("<br></div>");
|
response->print("<br></div>");
|
||||||
|
|
||||||
@@ -1458,6 +1455,19 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(key == "AUTHMAX")
|
||||||
|
{
|
||||||
|
if(value.toInt() > 0 && value.toInt() < 51)
|
||||||
|
{
|
||||||
|
if(_preferences->getInt(preference_auth_max_entries, MAX_AUTH) != value.toInt())
|
||||||
|
{
|
||||||
|
_preferences->putInt(preference_auth_max_entries, value.toInt());
|
||||||
|
Log->print(F("Setting changed: "));
|
||||||
|
Log->println(key);
|
||||||
|
configChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if(key == "BUFFSIZE")
|
else if(key == "BUFFSIZE")
|
||||||
{
|
{
|
||||||
if(value.toInt() > 4095 && value.toInt() < 32769)
|
if(value.toInt() > 4095 && value.toInt() < 32769)
|
||||||
@@ -1565,6 +1575,16 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
|||||||
configChanged = true;
|
configChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(key == "AUTHPUB")
|
||||||
|
{
|
||||||
|
if(_preferences->getBool(preference_auth_info_enabled, false) != (value == "1"))
|
||||||
|
{
|
||||||
|
_preferences->putBool(preference_auth_info_enabled, (value == "1"));
|
||||||
|
Log->print(F("Setting changed: "));
|
||||||
|
Log->println(key);
|
||||||
|
configChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if(key == "KPPER")
|
else if(key == "KPPER")
|
||||||
{
|
{
|
||||||
if(_preferences->getBool(preference_keypad_topic_per_entry, false) != (value == "1"))
|
if(_preferences->getBool(preference_keypad_topic_per_entry, false) != (value == "1"))
|
||||||
@@ -1595,6 +1615,26 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
|||||||
configChanged = true;
|
configChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(key == "AUTHPER")
|
||||||
|
{
|
||||||
|
if(_preferences->getBool(preference_auth_topic_per_entry, false) != (value == "1"))
|
||||||
|
{
|
||||||
|
_preferences->putBool(preference_auth_topic_per_entry, (value == "1"));
|
||||||
|
Log->print(F("Setting changed: "));
|
||||||
|
Log->println(key);
|
||||||
|
configChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(key == "AUTHENA")
|
||||||
|
{
|
||||||
|
if(_preferences->getBool(preference_auth_control_enabled, false) != (value == "1"))
|
||||||
|
{
|
||||||
|
_preferences->putBool(preference_auth_control_enabled, (value == "1"));
|
||||||
|
Log->print(F("Setting changed: "));
|
||||||
|
Log->println(key);
|
||||||
|
configChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if(key == "PUBAUTH")
|
else if(key == "PUBAUTH")
|
||||||
{
|
{
|
||||||
if(_preferences->getBool(preference_publish_authdata, false) != (value == "1"))
|
if(_preferences->getBool(preference_publish_authdata, false) != (value == "1"))
|
||||||
@@ -2220,7 +2260,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
|||||||
{
|
{
|
||||||
if(curAdvancedLockConfigAclPrefs[i] != advancedLockConfigAclPrefs[i])
|
if(curAdvancedLockConfigAclPrefs[i] != advancedLockConfigAclPrefs[i])
|
||||||
{
|
{
|
||||||
_preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs));
|
_preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs));
|
||||||
Log->print(F("Setting changed: "));
|
Log->print(F("Setting changed: "));
|
||||||
Log->println("ACLCONFADVANCEDLOCK");
|
Log->println("ACLCONFADVANCEDLOCK");
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
@@ -2232,7 +2272,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
|
|||||||
{
|
{
|
||||||
if(curBasicOpenerConfigAclPrefs[i] != basicOpenerConfigAclPrefs[i])
|
if(curBasicOpenerConfigAclPrefs[i] != basicOpenerConfigAclPrefs[i])
|
||||||
{
|
{
|
||||||
_preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs));
|
_preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs));
|
||||||
Log->print(F("Setting changed: "));
|
Log->print(F("Setting changed: "));
|
||||||
Log->println("ACLCONFBASICOPENER");
|
Log->println("ACLCONFBASICOPENER");
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
@@ -2450,25 +2490,13 @@ void WebCfgServer::buildImportExportHtml(AsyncWebServerRequest *request)
|
|||||||
response->print("<button title=\"Basic export\" onclick=\" window.open('/export', '_self'); return false;\">Basic export</button>");
|
response->print("<button title=\"Basic export\" onclick=\" window.open('/export', '_self'); return false;\">Basic export</button>");
|
||||||
response->print("<br><br><button title=\"Export with redacted settings\" onclick=\" window.open('/export?redacted=1'); return false;\">Export with redacted settings</button>");
|
response->print("<br><br><button title=\"Export with redacted settings\" onclick=\" window.open('/export?redacted=1'); return false;\">Export with redacted settings</button>");
|
||||||
response->print("<br><br><button title=\"Export with redacted settings and pairing data\" onclick=\" window.open('/export?redacted=1&pairing=1'); return false;\">Export with redacted settings and pairing data</button>");
|
response->print("<br><br><button title=\"Export with redacted settings and pairing data\" onclick=\" window.open('/export?redacted=1&pairing=1'); return false;\">Export with redacted settings and pairing data</button>");
|
||||||
response->print("</div><div id=\"msgdiv\" style=\"visibility:hidden\">Initiating config update. Please be patient.<br>You will be forwarded automatically when the import is complete.</div>");
|
response->print("</div></body></html>");
|
||||||
response->print("<script type=\"text/javascript\">");
|
|
||||||
response->print("window.addEventListener('load', function () {");
|
|
||||||
response->print(" var button = document.getElementById(\"submitbtn\");");
|
|
||||||
response->print(" button.addEventListener('click',hideshow,false);");
|
|
||||||
response->print(" function hideshow() {");
|
|
||||||
response->print(" document.getElementById('upform').style.visibility = 'hidden';");
|
|
||||||
response->print(" document.getElementById('gitdiv').style.visibility = 'hidden';");
|
|
||||||
response->print(" document.getElementById('msgdiv').style.visibility = 'visible';");
|
|
||||||
response->print(" }");
|
|
||||||
response->print("});");
|
|
||||||
response->print("</script>");
|
|
||||||
response->print("</body></html>");
|
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebCfgServer::buildCustomNetworkConfigHtml(AsyncWebServerRequest *request)
|
void WebCfgServer::buildCustomNetworkConfigHtml(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
String header = "<script>window.onload = function() { hideopt(document.getElementsByName('NWCUSTPHY')[0].value); var physelect = document.getElementsByName('NWCUSTPHY')[0]; physelect.addEventListener('change', function(event) { var select = event.target; var selectedOption = select.options[select.selectedIndex]; hideopt(selectedOption.getAttribute('value')); }); }; function hideopt(value) { var intopts = document.getElementsByClassName('internalopt'); var extopts = document.getElementsByClassName('externalopt'); if (value >= 1 && value <= 3) { hide(intopts); } else if (value >= 4 && value <= 9) { hide(extopts); } else { hide(intopts); hide(extopts); } } function hide(opts) { for (var i = 0; i < opts.length; i ++) { opts[i].style.display = 'none'; } }</script>";
|
String header = "<script>window.onload=function(){var physelect=document.getElementsByName('NWCUSTPHY')[0];hideshowopt(physelect.value);physelect.addEventListener('change', function(event){var select=event.target;var selectedOption=select.options[select.selectedIndex];hideshowopt(selectedOption.getAttribute('value'));});};function hideshowopt(value){if(value>=1&&value<=3){hideopt('internalopt',true);hideopt('externalopt',false);}else if(value>=4&&value<=9){hideopt('internalopt', false);hideopt('externalopt', true);}else {hideopt('internalopt', true);hideopt('externalopt', true);}}function hideopt(opts,hide){var hideopts = document.getElementsByClassName(opts);for(var i=0;i<hideopts.length;i++){if(hide==true){hideopts[i].style.display='none';}else{hideopts[i].style.display='block';}}}</script>";
|
||||||
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
||||||
buildHtmlHeader(response, header);
|
buildHtmlHeader(response, header);
|
||||||
response->print("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
|
response->print("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
|
||||||
@@ -2478,16 +2506,16 @@ void WebCfgServer::buildCustomNetworkConfigHtml(AsyncWebServerRequest *request)
|
|||||||
printInputField(response, "NWCUSTADDR", "ADDR", _preferences->getInt(preference_network_custom_addr, 1), 6, "");
|
printInputField(response, "NWCUSTADDR", "ADDR", _preferences->getInt(preference_network_custom_addr, 1), 6, "");
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
printDropDown(response, "NWCUSTCLK", "CLK", String(_preferences->getInt(preference_network_custom_clk, 0)), getNetworkCustomCLKOptions(), "internalopt");
|
printDropDown(response, "NWCUSTCLK", "CLK", String(_preferences->getInt(preference_network_custom_clk, 0)), getNetworkCustomCLKOptions(), "internalopt");
|
||||||
printInputField(response, "NWCUSTPWR", "PWR", _preferences->getInt(preference_network_custom_pwr, 12), 6, "internalopt");
|
printInputField(response, "NWCUSTPWR", "PWR", _preferences->getInt(preference_network_custom_pwr, 12), 6, "class=\"internalopt\"");
|
||||||
printInputField(response, "NWCUSTMDIO", "MDIO", _preferences->getInt(preference_network_custom_mdio), 6, "internalopt");
|
printInputField(response, "NWCUSTMDIO", "MDIO", _preferences->getInt(preference_network_custom_mdio), 6, "class=\"internalopt\"");
|
||||||
printInputField(response, "NWCUSTMDC", "MDC", _preferences->getInt(preference_network_custom_mdc), 6, "internalopt");
|
printInputField(response, "NWCUSTMDC", "MDC", _preferences->getInt(preference_network_custom_mdc), 6, "class=\"internalopt\"");
|
||||||
#endif
|
#endif
|
||||||
printInputField(response, "NWCUSTIRQ", "IRQ", _preferences->getInt(preference_network_custom_irq, -1), 6, "externalopt");
|
printInputField(response, "NWCUSTIRQ", "IRQ", _preferences->getInt(preference_network_custom_irq, -1), 6, "class=\"externalopt\"");
|
||||||
printInputField(response, "NWCUSTRST", "RST", _preferences->getInt(preference_network_custom_rst, -1), 6, "externalopt");
|
printInputField(response, "NWCUSTRST", "RST", _preferences->getInt(preference_network_custom_rst, -1), 6, "class=\"externalopt\"");
|
||||||
printInputField(response, "NWCUSTCS", "CS", _preferences->getInt(preference_network_custom_cs, -1), 6, "externalopt");
|
printInputField(response, "NWCUSTCS", "CS", _preferences->getInt(preference_network_custom_cs, -1), 6, "class=\"externalopt\"");
|
||||||
printInputField(response, "NWCUSTSCK", "SCK", _preferences->getInt(preference_network_custom_sck, -1), 6, "externalopt");
|
printInputField(response, "NWCUSTSCK", "SCK", _preferences->getInt(preference_network_custom_sck, -1), 6, "class=\"externalopt\"");
|
||||||
printInputField(response, "NWCUSTMISO", "MISO", _preferences->getInt(preference_network_custom_miso, -1), 6, "externalopt");
|
printInputField(response, "NWCUSTMISO", "MISO", _preferences->getInt(preference_network_custom_miso, -1), 6, "class=\"externalopt\"");
|
||||||
printInputField(response, "NWCUSTMOSI", "MOSI", _preferences->getInt(preference_network_custom_mosi, -1), 6, "externalopt");
|
printInputField(response, "NWCUSTMOSI", "MOSI", _preferences->getInt(preference_network_custom_mosi, -1), 6, "class=\"externalopt\"");
|
||||||
|
|
||||||
response->print("</table>");
|
response->print("</table>");
|
||||||
|
|
||||||
@@ -2695,7 +2723,6 @@ void WebCfgServer::buildMqttConfigHtml(AsyncWebServerRequest *request)
|
|||||||
printInputField(response, "IPGTW", "Default gateway", _preferences->getString(preference_ip_gateway).c_str(), 15, "");
|
printInputField(response, "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, "DNSSRV", "DNS Server", _preferences->getString(preference_ip_dns_server).c_str(), 15, "");
|
||||||
response->print("</table>");
|
response->print("</table>");
|
||||||
|
|
||||||
response->print("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
response->print("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
|
||||||
response->print("</form>");
|
response->print("</form>");
|
||||||
response->print("</body></html>");
|
response->print("</body></html>");
|
||||||
@@ -2872,6 +2899,9 @@ void WebCfgServer::partAccLvlHtml(String &partString, int aclPart)
|
|||||||
printCheckBox(partString, "TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), "");
|
printCheckBox(partString, "TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), "");
|
||||||
printCheckBox(partString, "TCPER", "Publish a topic per time control entry and create HA sensor", _preferences->getBool(preference_timecontrol_topic_per_entry), "");
|
printCheckBox(partString, "TCPER", "Publish a topic per time control entry and create HA sensor", _preferences->getBool(preference_timecontrol_topic_per_entry), "");
|
||||||
printCheckBox(partString, "TCENA", "Add, modify and delete time control entries", _preferences->getBool(preference_timecontrol_control_enabled), "");
|
printCheckBox(partString, "TCENA", "Add, modify and delete time control entries", _preferences->getBool(preference_timecontrol_control_enabled), "");
|
||||||
|
printCheckBox(partString, "AUTHPUB", "Publish authorization entries information", _preferences->getBool(preference_auth_info_enabled), "");
|
||||||
|
printCheckBox(partString, "AUTHPER", "Publish a topic per authorization entry and create HA sensor", _preferences->getBool(preference_auth_topic_per_entry), "");
|
||||||
|
printCheckBox(partString, "AUTHENA", "Modify and delete authorization entries", _preferences->getBool(preference_auth_control_enabled), "");
|
||||||
printCheckBox(partString, "PUBAUTH", "Publish authorization log", _preferences->getBool(preference_publish_authdata), "");
|
printCheckBox(partString, "PUBAUTH", "Publish authorization log", _preferences->getBool(preference_publish_authdata), "");
|
||||||
partString.concat("</table><br>");
|
partString.concat("</table><br>");
|
||||||
partString.concat("<div id=\"acllock\"></div>");
|
partString.concat("<div id=\"acllock\"></div>");
|
||||||
@@ -3514,6 +3544,9 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request)
|
|||||||
sprintf(tmp, "%02x", authorizationId[i]);
|
sprintf(tmp, "%02x", authorizationId[i]);
|
||||||
response->print(tmp);
|
response->print(tmp);
|
||||||
}
|
}
|
||||||
|
uint32_t authorizationIdInt = authorizationId[0] + 256U*authorizationId[1] + 65536U*authorizationId[2] + 16777216U*authorizationId[3];
|
||||||
|
response->print("\nAuthorizationId (UINT32_T): ");
|
||||||
|
response->print(authorizationIdInt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3864,11 +3897,19 @@ void WebCfgServer::printInputField(AsyncResponseStream *response,
|
|||||||
response->print("<input type=");
|
response->print("<input type=");
|
||||||
response->print(isPassword ? "\"password\"" : "\"text\"");
|
response->print(isPassword ? "\"password\"" : "\"text\"");
|
||||||
if(strcmp(id, "") != 0)
|
if(strcmp(id, "") != 0)
|
||||||
|
{
|
||||||
|
if(strncmp(id, "class=", 6) != 0)
|
||||||
|
{
|
||||||
|
response->print(" ");
|
||||||
|
response->print(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
response->print(" id=\"");
|
response->print(" id=\"");
|
||||||
response->print(id);
|
response->print(id);
|
||||||
response->print("\"");
|
response->print("\"");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if(strcmp(value, "") != 0)
|
if(strcmp(value, "") != 0)
|
||||||
{
|
{
|
||||||
response->print(" value=\"");
|
response->print(" value=\"");
|
||||||
|
|||||||
10
src/main.cpp
10
src/main.cpp
@@ -154,7 +154,6 @@ void networkTask(void *pvParameters)
|
|||||||
}
|
}
|
||||||
|
|
||||||
esp_task_wdt_reset();
|
esp_task_wdt_reset();
|
||||||
|
|
||||||
delay(100);
|
delay(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,6 +242,7 @@ void bootloopDetection()
|
|||||||
preferences->putInt(preference_authlog_max_entries, MAX_AUTHLOG);
|
preferences->putInt(preference_authlog_max_entries, MAX_AUTHLOG);
|
||||||
preferences->putInt(preference_keypad_max_entries, MAX_KEYPAD);
|
preferences->putInt(preference_keypad_max_entries, MAX_KEYPAD);
|
||||||
preferences->putInt(preference_timecontrol_max_entries, MAX_TIMECONTROL);
|
preferences->putInt(preference_timecontrol_max_entries, MAX_TIMECONTROL);
|
||||||
|
preferences->putInt(preference_auth_max_entries, MAX_AUTH);
|
||||||
bootloopCounter = 0;
|
bootloopCounter = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,6 +382,14 @@ void setup()
|
|||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Log = &Serial;
|
Log = &Serial;
|
||||||
|
|
||||||
|
#ifndef NUKI_HUB_UPDATER
|
||||||
|
stdout = funopen(NULL, NULL, &write_fn, NULL, NULL);
|
||||||
|
static char linebuf[1024];
|
||||||
|
setvbuf(stdout, linebuf, _IOLBF, sizeof(linebuf));
|
||||||
|
esp_rom_install_channel_putc(1, &ets_putc_handler);
|
||||||
|
//ets_install_putc1(&ets_putc_handler);
|
||||||
|
#endif
|
||||||
|
|
||||||
preferences = new Preferences();
|
preferences = new Preferences();
|
||||||
preferences->begin("nukihub", false);
|
preferences->begin("nukihub", false);
|
||||||
bool firstStart = initPreferences(preferences);
|
bool firstStart = initPreferences(preferences);
|
||||||
|
|||||||
Reference in New Issue
Block a user