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:
46
README.md
46
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
|
||||||
@@ -556,15 +558,15 @@ For example, to add a code:
|
|||||||
- write 1 to enabled
|
- write 1 to enabled
|
||||||
- write "add" to action
|
- write "add" to action
|
||||||
|
|
||||||
## Time control using JSON (optional)
|
## Timecontrol using JSON (optional)
|
||||||
|
|
||||||
Time control entries can be added, updated and removed. This has to enabled first in the configuration portal. Check "Add, modify and delete time control entries" under "Access Level Configuration" and save the configuration.
|
Timecontrol entries can be added, updated and removed. This has to enabled first in the configuration portal. Check "Add, modify and delete timecontrol entries" under "Access Level Configuration" and save the configuration.
|
||||||
|
|
||||||
Information about current time control entries is published as JSON data to the "timecontrol/json" MQTT topic.<br>
|
Information about current timecontrol entries is published as JSON data to the "timecontrol/json" MQTT topic.<br>
|
||||||
This needs to be enabled separately by checking "Publish time control entries information" under "Access Level Configuration" and saving the configuration.
|
This needs to be enabled separately by checking "Publish timecontrol entries information" under "Access Level Configuration" and saving the configuration.
|
||||||
By default a maximum of 10 entries are published.
|
By default a maximum of 10 entries are published.
|
||||||
|
|
||||||
To change Nuki Lock/Opener time control settings set the `timecontrol/actionJson` topic to a JSON formatted value containing the following nodes.
|
To change Nuki Lock/Opener timecontrol settings set the `timecontrol/actionJson` topic to a JSON formatted value containing the following nodes.
|
||||||
|
|
||||||
| Node | Delete | Add | Update | Usage | Possible values |
|
| Node | Delete | Add | Update | Usage | Possible values |
|
||||||
|------------------|----------|----------|----------|------------------------------------------------------------------------------------------|----------------------------------------------------------------|
|
|------------------|----------|----------|----------|------------------------------------------------------------------------------------------|----------------------------------------------------------------|
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ void NukiNetwork::setupDevice()
|
|||||||
_networkDeviceType = NetworkDeviceType::WiFi;
|
_networkDeviceType = NetworkDeviceType::WiFi;
|
||||||
#else
|
#else
|
||||||
int custEth = _preferences->getInt(preference_network_custom_phy, 0);
|
int custEth = _preferences->getInt(preference_network_custom_phy, 0);
|
||||||
|
|
||||||
if(custEth<3) custEth++;
|
if(custEth<3) custEth++;
|
||||||
else custEth = 0;
|
else custEth = 0;
|
||||||
_preferences->putInt(preference_network_custom_phy, custEth);
|
_preferences->putInt(preference_network_custom_phy, custEth);
|
||||||
@@ -377,7 +377,7 @@ void NukiNetwork::setupDevice()
|
|||||||
_preferences->getInt(preference_network_custom_mosi, -1),
|
_preferences->getInt(preference_network_custom_mosi, -1),
|
||||||
ETH_PHY_SPI_FREQ_MHZ,
|
ETH_PHY_SPI_FREQ_MHZ,
|
||||||
ETH_PHY_W5500);
|
ETH_PHY_W5500);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -630,9 +629,9 @@ bool NukiNetwork::update()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_mqttConnectCounter = 0;
|
_mqttConnectCounter = 0;
|
||||||
if(forceEnableWebServer && !_webEnabled)
|
if(forceEnableWebServer && !_webEnabled)
|
||||||
{
|
{
|
||||||
forceEnableWebServer = false;
|
forceEnableWebServer = false;
|
||||||
delay(200);
|
delay(200);
|
||||||
restartEsp(RestartReason::ReconfigureWebServer);
|
restartEsp(RestartReason::ReconfigureWebServer);
|
||||||
}
|
}
|
||||||
@@ -654,12 +653,13 @@ bool NukiNetwork::update()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_lastConnectedTs = ts;
|
_lastConnectedTs = ts;
|
||||||
|
|
||||||
#if PRESENCE_DETECTION_ENABLED
|
#if PRESENCE_DETECTION_ENABLED
|
||||||
if(_presenceDetection != nullptr && (_lastPresenceTs == 0 || (ts - _lastPresenceTs) > 3000))
|
if(_presenceDetection != nullptr && (_lastPresenceTs == 0 || (ts - _lastPresenceTs) > 3000))
|
||||||
{
|
{
|
||||||
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,11 +706,12 @@ 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;
|
||||||
https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
@@ -721,28 +722,29 @@ 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; }
|
||||||
{
|
|
||||||
String currentVersion = NUKI_HUB_VERSION;
|
|
||||||
|
|
||||||
if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) _latestVersion = doc["release"]["fullversion"];
|
|
||||||
else if(currentVersion.indexOf("beta") > 0) _latestVersion = doc["beta"]["fullversion"];
|
|
||||||
else if(currentVersion.indexOf("master") > 0) _latestVersion = doc["master"]["fullversion"];
|
|
||||||
else _latestVersion = doc["release"]["fullversion"];
|
|
||||||
|
|
||||||
publishString(_maintenancePathPrefix, mqtt_topic_info_nuki_hub_latest, _latestVersion, true);
|
|
||||||
|
|
||||||
if(strcmp(_latestVersion, _preferences->getString(preference_latest_version).c_str()) != 0) _preferences->putString(preference_latest_version, _latestVersion);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
https.end();
|
https.end();
|
||||||
}
|
}
|
||||||
delete client;
|
delete client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (otaManifestSuccess)
|
||||||
|
{
|
||||||
|
String currentVersion = NUKI_HUB_VERSION;
|
||||||
|
|
||||||
|
if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) _latestVersion = doc["release"]["fullversion"];
|
||||||
|
else if(currentVersion.indexOf("beta") > 0) _latestVersion = doc["beta"]["fullversion"];
|
||||||
|
else if(currentVersion.indexOf("master") > 0) _latestVersion = doc["master"]["fullversion"];
|
||||||
|
else _latestVersion = doc["release"]["fullversion"];
|
||||||
|
|
||||||
|
publishString(_maintenancePathPrefix, mqtt_topic_info_nuki_hub_latest, _latestVersion, true);
|
||||||
|
|
||||||
|
if(strcmp(_latestVersion, _preferences->getString(preference_latest_version).c_str()) != 0) _preferences->putString(preference_latest_version, _latestVersion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,7 +863,6 @@ bool NukiNetwork::reconnect()
|
|||||||
_mqttConnectedTs = millis();
|
_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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ public:
|
|||||||
explicit NukiNetwork(Preferences* preferences, PresenceDetection* presenceDetection, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize);
|
explicit NukiNetwork(Preferences* preferences, PresenceDetection* presenceDetection, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize);
|
||||||
|
|
||||||
void registerMqttReceiver(MqttReceiver* receiver);
|
void registerMqttReceiver(MqttReceiver* receiver);
|
||||||
#if PRESENCE_DETECTION_ENABLED
|
#if PRESENCE_DETECTION_ENABLED
|
||||||
void setMqttPresencePath(char* path);
|
void setMqttPresencePath(char* path);
|
||||||
#endif
|
#endif
|
||||||
void disableAutoRestarts(); // disable on OTA start
|
void disableAutoRestarts(); // disable on OTA start
|
||||||
void disableMqtt();
|
void disableMqtt();
|
||||||
String localIP();
|
String localIP();
|
||||||
@@ -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];
|
||||||
@@ -200,7 +205,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const
|
|||||||
{
|
{
|
||||||
Log->println(F("Update requested via MQTT."));
|
Log->println(F("Update requested via MQTT."));
|
||||||
String currentVersion = NUKI_HUB_VERSION;
|
String currentVersion = NUKI_HUB_VERSION;
|
||||||
|
|
||||||
if(atof(_preferences->getString(preference_latest_version).c_str()) >= atof(currentVersion.c_str()))
|
if(atof(_preferences->getString(preference_latest_version).c_str()) >= atof(currentVersion.c_str()))
|
||||||
{
|
{
|
||||||
_preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL);
|
_preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL);
|
||||||
@@ -403,6 +408,18 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const
|
|||||||
|
|
||||||
publishString(mqtt_topic_timecontrol_action, "--", true);
|
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;
|
||||||
};
|
};
|
||||||
@@ -69,7 +69,7 @@ void NukiNetworkOpener::initialize()
|
|||||||
_network->removeTopic(_mqttPath, mqtt_topic_battery_keypad_critical);
|
_network->removeTopic(_mqttPath, mqtt_topic_battery_keypad_critical);
|
||||||
//_network->removeTopic(_mqttPath, mqtt_topic_presence);
|
//_network->removeTopic(_mqttPath, mqtt_topic_presence);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!_preferences->getBool(preference_conf_info_enabled, true))
|
if(!_preferences->getBool(preference_conf_info_enabled, true))
|
||||||
{
|
{
|
||||||
_network->removeTopic(_mqttPath, mqtt_topic_config_basic_json);
|
_network->removeTopic(_mqttPath, mqtt_topic_config_basic_json);
|
||||||
@@ -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)
|
||||||
@@ -501,7 +519,7 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::Log
|
|||||||
memset(str, 0, sizeof(str));
|
memset(str, 0, sizeof(str));
|
||||||
NukiOpener::lockactionToString((NukiOpener::LockAction)log.data[0], str);
|
NukiOpener::lockactionToString((NukiOpener::LockAction)log.data[0], str);
|
||||||
entry["action"] = str;
|
entry["action"] = str;
|
||||||
|
|
||||||
switch(log.data[1])
|
switch(log.data[1])
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@@ -527,7 +545,7 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::Log
|
|||||||
NukiOpener::completionStatusToString((NukiOpener::CompletionStatus)log.data[2], str);
|
NukiOpener::completionStatusToString((NukiOpener::CompletionStatus)log.data[2], str);
|
||||||
entry["completionStatus"] = str;
|
entry["completionStatus"] = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry["codeId"] = 256U*log.data[4]+log.data[3];
|
entry["codeId"] = 256U*log.data[4]+log.data[3];
|
||||||
break;
|
break;
|
||||||
case NukiOpener::LoggingType::DoorbellRecognition:
|
case NukiOpener::LoggingType::DoorbellRecognition:
|
||||||
@@ -583,7 +601,7 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list<NukiOpener::Log
|
|||||||
entry["codeId"] = 256U*log.data[7]+log.data[6];
|
entry["codeId"] = 256U*log.data[7]+log.data[6];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(log.index > _lastRollingLog)
|
if(log.index > _lastRollingLog)
|
||||||
{
|
{
|
||||||
_lastRollingLog = log.index;
|
_lastRollingLog = log.index;
|
||||||
@@ -654,7 +672,7 @@ void NukiNetworkOpener::publishConfig(const NukiOpener::Config &config)
|
|||||||
itoa(config.nukiId, uidString, 16);
|
itoa(config.nukiId, uidString, 16);
|
||||||
|
|
||||||
JsonDocument json;
|
JsonDocument json;
|
||||||
|
|
||||||
memset(_nukiName, 0, sizeof(_nukiName));
|
memset(_nukiName, 0, sizeof(_nukiName));
|
||||||
memcpy(_nukiName, config.name, sizeof(config.name));
|
memcpy(_nukiName, config.name, sizeof(config.name));
|
||||||
|
|
||||||
@@ -1000,7 +1018,7 @@ void NukiNetworkOpener::publishKeypad(const std::list<NukiLock::KeypadEntry>& en
|
|||||||
_network->removeTopic(codeTopic, "createdSec");
|
_network->removeTopic(codeTopic, "createdSec");
|
||||||
_network->removeTopic(codeTopic, "lockCount");
|
_network->removeTopic(codeTopic, "lockCount");
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int j=entries.size(); j<maxKeypadCodeCount; j++)
|
for(int j=entries.size(); j<maxKeypadCodeCount; j++)
|
||||||
{
|
{
|
||||||
String codesTopic = _mqttPath;
|
String codesTopic = _mqttPath;
|
||||||
@@ -1084,7 +1102,7 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
|||||||
memset(str, 0, sizeof(str));
|
memset(str, 0, sizeof(str));
|
||||||
NukiOpener::lockactionToString(entry.lockAction, str);
|
NukiOpener::lockactionToString(entry.lockAction, str);
|
||||||
jsonEntry["lockAction"] = str;
|
jsonEntry["lockAction"] = str;
|
||||||
|
|
||||||
if(topicPerEntry)
|
if(topicPerEntry)
|
||||||
{
|
{
|
||||||
String basePath = mqtt_topic_timecontrol;
|
String basePath = mqtt_topic_timecontrol;
|
||||||
@@ -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();
|
||||||
@@ -1103,7 +1120,7 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
|||||||
std::string mqttDeviceName = std::string("timecontrol_") + std::to_string(index);
|
std::string mqttDeviceName = std::string("timecontrol_") + std::to_string(index);
|
||||||
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||||
std::string displayName = std::string("Timecontrol - ") + std::to_string(entry.entryId);
|
std::string displayName = std::string("Timecontrol - ") + std::to_string(entry.entryId);
|
||||||
|
|
||||||
_network->publishHassTopic("switch",
|
_network->publishHassTopic("switch",
|
||||||
mqttDeviceName.c_str(),
|
mqttDeviceName.c_str(),
|
||||||
uidString,
|
uidString,
|
||||||
@@ -1124,13 +1141,13 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
|||||||
{ (char*)"stat_on", (char*)"1" },
|
{ (char*)"stat_on", (char*)"1" },
|
||||||
{ (char*)"stat_off", (char*)"0" }});
|
{ (char*)"stat_off", (char*)"0" }});
|
||||||
}
|
}
|
||||||
|
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
|
||||||
serializeJson(json, _buffer, _bufferSize);
|
serializeJson(json, _buffer, _bufferSize);
|
||||||
publishString(mqtt_topic_timecontrol_json, _buffer, true);
|
publishString(mqtt_topic_timecontrol_json, _buffer, true);
|
||||||
|
|
||||||
for(int j=timeControlEntries.size(); j<maxTimeControlEntryCount; j++)
|
for(int j=timeControlEntries.size(); j<maxTimeControlEntryCount; j++)
|
||||||
{
|
{
|
||||||
String entriesTopic = _mqttPath;
|
String entriesTopic = _mqttPath;
|
||||||
@@ -1142,6 +1159,152 @@ void NukiNetworkOpener::publishTimeControl(const std::list<NukiOpener::TimeContr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiNetworkOpener::publishAuth(const std::list<NukiOpener::AuthorizationEntry>& authEntries, uint maxAuthEntryCount)
|
||||||
|
{
|
||||||
|
uint index = 0;
|
||||||
|
char str[50];
|
||||||
|
char uidString[20];
|
||||||
|
itoa(_preferences->getUInt(preference_nuki_id_opener, 0), uidString, 16);
|
||||||
|
String baseTopic = _preferences->getString(preference_mqtt_opener_path);
|
||||||
|
JsonDocument json;
|
||||||
|
|
||||||
|
for(const auto& entry : authEntries)
|
||||||
|
{
|
||||||
|
auto jsonEntry = json.add<JsonVariant>();
|
||||||
|
|
||||||
|
jsonEntry["authId"] = entry.authId;
|
||||||
|
jsonEntry["idType"] = entry.idType; //CONSIDER INT TO STRING
|
||||||
|
jsonEntry["enabled"] = entry.enabled;
|
||||||
|
jsonEntry["name"] = entry.name;
|
||||||
|
jsonEntry["remoteAllowed"] = entry.remoteAllowed;
|
||||||
|
char createdDT[20];
|
||||||
|
sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.createdYear, entry.createdMonth, entry.createdDay, entry.createdHour, entry.createdMinute, entry.createdSecond);
|
||||||
|
jsonEntry["dateCreated"] = createdDT;
|
||||||
|
jsonEntry["lockCount"] = entry.lockCount;
|
||||||
|
char lastActiveDT[20];
|
||||||
|
sprintf(lastActiveDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.lastActYear, entry.lastActMonth, entry.lastActDay, entry.lastActHour, entry.lastActMinute, entry.lastActSecond);
|
||||||
|
jsonEntry["dateLastActive"] = lastActiveDT;
|
||||||
|
jsonEntry["timeLimited"] = entry.timeLimited;
|
||||||
|
char allowedFromDT[20];
|
||||||
|
sprintf(allowedFromDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedFromYear, entry.allowedFromMonth, entry.allowedFromDay, entry.allowedFromHour, entry.allowedFromMinute, entry.allowedFromSecond);
|
||||||
|
jsonEntry["allowedFrom"] = allowedFromDT;
|
||||||
|
char allowedUntilDT[20];
|
||||||
|
sprintf(allowedUntilDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.allowedUntilYear, entry.allowedUntilMonth, entry.allowedUntilDay, entry.allowedUntilHour, entry.allowedUntilMinute, entry.allowedUntilSecond);
|
||||||
|
jsonEntry["allowedUntil"] = allowedUntilDT;
|
||||||
|
|
||||||
|
uint8_t allowedWeekdaysInt = entry.allowedWeekdays;
|
||||||
|
JsonArray weekdays = jsonEntry["allowedWeekdays"].to<JsonArray>();
|
||||||
|
|
||||||
|
while(allowedWeekdaysInt > 0) {
|
||||||
|
if(allowedWeekdaysInt >= 64)
|
||||||
|
{
|
||||||
|
weekdays.add("mon");
|
||||||
|
allowedWeekdaysInt -= 64;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 32)
|
||||||
|
{
|
||||||
|
weekdays.add("tue");
|
||||||
|
allowedWeekdaysInt -= 32;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 16)
|
||||||
|
{
|
||||||
|
weekdays.add("wed");
|
||||||
|
allowedWeekdaysInt -= 16;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 8)
|
||||||
|
{
|
||||||
|
weekdays.add("thu");
|
||||||
|
allowedWeekdaysInt -= 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 4)
|
||||||
|
{
|
||||||
|
weekdays.add("fri");
|
||||||
|
allowedWeekdaysInt -= 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 2)
|
||||||
|
{
|
||||||
|
weekdays.add("sat");
|
||||||
|
allowedWeekdaysInt -= 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(allowedWeekdaysInt >= 1)
|
||||||
|
{
|
||||||
|
weekdays.add("sun");
|
||||||
|
allowedWeekdaysInt -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char allowedFromTimeT[5];
|
||||||
|
sprintf(allowedFromTimeT, "%02d:%02d", entry.allowedFromTimeHour, entry.allowedFromTimeMin);
|
||||||
|
jsonEntry["allowedFromTime"] = allowedFromTimeT;
|
||||||
|
char allowedUntilTimeT[5];
|
||||||
|
sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin);
|
||||||
|
jsonEntry["allowedUntilTime"] = allowedUntilTimeT;
|
||||||
|
|
||||||
|
if(_preferences->getBool(preference_auth_topic_per_entry, false))
|
||||||
|
{
|
||||||
|
String basePath = mqtt_topic_auth;
|
||||||
|
basePath.concat("/entries/");
|
||||||
|
basePath.concat(std::to_string(index).c_str());
|
||||||
|
jsonEntry["index"] = index;
|
||||||
|
serializeJson(jsonEntry, _buffer, _bufferSize);
|
||||||
|
publishString(basePath.c_str(), _buffer, true);
|
||||||
|
|
||||||
|
String basePathPrefix = "~";
|
||||||
|
basePathPrefix.concat(basePath);
|
||||||
|
const char *basePathPrefixChr = basePathPrefix.c_str();
|
||||||
|
|
||||||
|
std::string baseCommand = std::string("{ \"action\": \"update\", \"authId\": \"") + std::to_string(entry.authId);
|
||||||
|
std::string enaCommand = baseCommand + (char*)"\", \"enabled\": \"1\" }";
|
||||||
|
std::string disCommand = baseCommand + (char*)"\", \"enabled\": \"0\" }";
|
||||||
|
std::string mqttDeviceName = std::string("auth_") + std::to_string(index);
|
||||||
|
std::string uidStringPostfix = std::string("_") + mqttDeviceName;
|
||||||
|
std::string displayName = std::string("Authorization - ") + std::to_string(entry.authId);
|
||||||
|
|
||||||
|
_network->publishHassTopic("switch",
|
||||||
|
mqttDeviceName.c_str(),
|
||||||
|
uidString,
|
||||||
|
uidStringPostfix.c_str(),
|
||||||
|
displayName.c_str(),
|
||||||
|
_nukiName,
|
||||||
|
baseTopic.c_str(),
|
||||||
|
String("~") + basePath.c_str(),
|
||||||
|
(char*)"Opener",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"diagnostic",
|
||||||
|
String("~") + mqtt_topic_auth_action,
|
||||||
|
{ { (char*)"json_attr_t", (char*)basePathPrefixChr },
|
||||||
|
{ (char*)"pl_on", (char*)enaCommand.c_str() },
|
||||||
|
{ (char*)"pl_off", (char*)disCommand.c_str() },
|
||||||
|
{ (char*)"val_tpl", (char*)"{{value_json.enabled}}" },
|
||||||
|
{ (char*)"stat_on", (char*)"1" },
|
||||||
|
{ (char*)"stat_off", (char*)"0" }});
|
||||||
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeJson(json, _buffer, _bufferSize);
|
||||||
|
publishString(mqtt_topic_auth_json, _buffer, true);
|
||||||
|
|
||||||
|
for(int j=authEntries.size(); j<maxAuthEntryCount; j++)
|
||||||
|
{
|
||||||
|
String entriesTopic = _mqttPath;
|
||||||
|
entriesTopic.concat(mqtt_topic_auth_entries);
|
||||||
|
entriesTopic.concat("/");
|
||||||
|
_network->removeTopic(entriesTopic, (char*)std::to_string(j).c_str());
|
||||||
|
std::string mqttDeviceName = std::string("auth_") + std::to_string(j);
|
||||||
|
_network->removeHassTopic((char*)"switch", (char*)mqttDeviceName.c_str(), uidString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NukiNetworkOpener::publishConfigCommandResult(const char* result)
|
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);
|
||||||
|
|
||||||
@@ -729,7 +738,7 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
|
|||||||
|
|
||||||
while(_retryCount < _nrOfRetries + 1)
|
while(_retryCount < _nrOfRetries + 1)
|
||||||
{
|
{
|
||||||
Log->print(F("Querying opener time control: "));
|
Log->print(F("Querying opener timecontrol: "));
|
||||||
result = _nukiOpener.retrieveTimeControlEntries();
|
result = _nukiOpener.retrieveTimeControlEntries();
|
||||||
|
|
||||||
if(result != Nuki::CmdResult::Success) {
|
if(result != Nuki::CmdResult::Success) {
|
||||||
@@ -749,7 +758,7 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
|
|||||||
std::list<NukiOpener::TimeControlEntry> timeControlEntries;
|
std::list<NukiOpener::TimeControlEntry> timeControlEntries;
|
||||||
_nukiOpener.getTimeControlEntries(&timeControlEntries);
|
_nukiOpener.getTimeControlEntries(&timeControlEntries);
|
||||||
|
|
||||||
Log->print(F("Opener time control entries: "));
|
Log->print(F("Opener timecontrol entries: "));
|
||||||
Log->println(timeControlEntries.size());
|
Log->println(timeControlEntries.size());
|
||||||
|
|
||||||
timeControlEntries.sort([](const NukiOpener::TimeControlEntry& a, const NukiOpener::TimeControlEntry& b) { return a.entryId < b.entryId; });
|
timeControlEntries.sort([](const NukiOpener::TimeControlEntry& a, const NukiOpener::TimeControlEntry& b) { return a.entryId < b.entryId; });
|
||||||
@@ -779,6 +788,67 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
|
|||||||
postponeBleWatchdog();
|
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)
|
||||||
@@ -2161,7 +2241,7 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
if(idExists)
|
if(idExists)
|
||||||
{
|
{
|
||||||
result = _nukiOpener.removeTimeControlEntry(entryId);
|
result = _nukiOpener.removeTimeControlEntry(entryId);
|
||||||
Log->print(F("Delete time control: "));
|
Log->print(F("Delete timecontrol: "));
|
||||||
Log->println((int)result);
|
Log->println((int)result);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2219,7 +2299,7 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
|
|
||||||
entry.lockAction = timeControlLockAction;
|
entry.lockAction = timeControlLockAction;
|
||||||
result = _nukiOpener.addTimeControlEntry(entry);
|
result = _nukiOpener.addTimeControlEntry(entry);
|
||||||
Log->print(F("Add time control: "));
|
Log->print(F("Add timecontrol: "));
|
||||||
Log->println((int)result);
|
Log->println((int)result);
|
||||||
}
|
}
|
||||||
else if (strcmp(action, "update") == 0)
|
else if (strcmp(action, "update") == 0)
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2281,7 +2361,7 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
|
|
||||||
entry.lockAction = timeControlLockAction;
|
entry.lockAction = timeControlLockAction;
|
||||||
result = _nukiOpener.updateTimeControlEntry(entry);
|
result = _nukiOpener.updateTimeControlEntry(entry);
|
||||||
Log->print(F("Update time control: "));
|
Log->print(F("Update timecontrol: "));
|
||||||
Log->println((int)result);
|
Log->println((int)result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2314,6 +2394,464 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiOpenerWrapper::onAuthCommandReceived(const char *value)
|
||||||
|
{
|
||||||
|
if(!_nukiConfigValid)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("configNotReady");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isPinValid())
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noValidPinSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_preferences->getBool(preference_auth_control_enabled))
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("keypadControlDisabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument json;
|
||||||
|
DeserializationError jsonError = deserializeJson(json, value);
|
||||||
|
|
||||||
|
if(jsonError)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidJson");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char oldName[33];
|
||||||
|
const char *action = json["action"].as<const char*>();
|
||||||
|
uint16_t authId = json["authId"].as<unsigned int>();
|
||||||
|
//uint8_t idType = json["idType"].as<unsigned int>();
|
||||||
|
//unsigned char secretKeyK[32] = {0x00};
|
||||||
|
uint8_t remoteAllowed;
|
||||||
|
uint8_t enabled;
|
||||||
|
uint8_t timeLimited;
|
||||||
|
String name;
|
||||||
|
//String sharedKey;
|
||||||
|
String allowedFrom;
|
||||||
|
String allowedUntil;
|
||||||
|
String allowedWeekdays;
|
||||||
|
String allowedFromTime;
|
||||||
|
String allowedUntilTime;
|
||||||
|
|
||||||
|
if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as<unsigned int>();
|
||||||
|
else remoteAllowed = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>();
|
||||||
|
else enabled = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>();
|
||||||
|
else timeLimited = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("name")) name = json["name"].as<String>();
|
||||||
|
//if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as<String>();
|
||||||
|
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>();
|
||||||
|
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>();
|
||||||
|
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>();
|
||||||
|
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>();
|
||||||
|
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>();
|
||||||
|
|
||||||
|
if(action)
|
||||||
|
{
|
||||||
|
bool idExists = false;
|
||||||
|
|
||||||
|
if(authId)
|
||||||
|
{
|
||||||
|
idExists = std::find(_authIds.begin(), _authIds.end(), authId) != _authIds.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||||
|
_retryCount = 0;
|
||||||
|
|
||||||
|
while(_retryCount < _nrOfRetries)
|
||||||
|
{
|
||||||
|
if(strcmp(action, "delete") == 0) {
|
||||||
|
if(idExists)
|
||||||
|
{
|
||||||
|
result = _nukiOpener.deleteAuthorizationEntry(authId);
|
||||||
|
Log->print(F("Delete authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
|
||||||
|
{
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
if (strcmp(action, "update") != 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noNameSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(sharedKey.length() != 64)
|
||||||
|
{
|
||||||
|
if (strcmp(action, "update") != 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noSharedKeySet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i=0; i<sharedKey.length();i+=2) secretKeyK[(i/2)] = std::stoi(sharedKey.substring(i, i+2).c_str(), nullptr, 16);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned int allowedFromAr[6];
|
||||||
|
unsigned int allowedUntilAr[6];
|
||||||
|
unsigned int allowedFromTimeAr[2];
|
||||||
|
unsigned int allowedUntilTimeAr[2];
|
||||||
|
uint8_t allowedWeekdaysInt = 0;
|
||||||
|
|
||||||
|
if(timeLimited == 1)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() == 19)
|
||||||
|
{
|
||||||
|
allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt();
|
||||||
|
allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt();
|
||||||
|
allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt();
|
||||||
|
allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt();
|
||||||
|
allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt();
|
||||||
|
allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt();
|
||||||
|
|
||||||
|
if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedUntil.length() > 0 == 19)
|
||||||
|
{
|
||||||
|
allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt();
|
||||||
|
allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt();
|
||||||
|
allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt();
|
||||||
|
allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt();
|
||||||
|
allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt();
|
||||||
|
allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt();
|
||||||
|
|
||||||
|
if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedFromTime.length() == 5)
|
||||||
|
{
|
||||||
|
allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt();
|
||||||
|
allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt();
|
||||||
|
|
||||||
|
if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedUntilTime.length() == 5)
|
||||||
|
{
|
||||||
|
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt();
|
||||||
|
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt();
|
||||||
|
|
||||||
|
if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64;
|
||||||
|
if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32;
|
||||||
|
if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16;
|
||||||
|
if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8;
|
||||||
|
if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4;
|
||||||
|
if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2;
|
||||||
|
if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(action, "add") == 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("addActionNotSupported");
|
||||||
|
return;
|
||||||
|
|
||||||
|
NukiOpener::NewAuthorizationEntry entry;
|
||||||
|
memset(&entry, 0, sizeof(entry));
|
||||||
|
size_t nameLen = name.length();
|
||||||
|
memcpy(&entry.name, name.c_str(), nameLen > 32 ? 32 : nameLen);
|
||||||
|
/*
|
||||||
|
memcpy(&entry.sharedKey, secretKeyK, 32);
|
||||||
|
|
||||||
|
if(idType != 1)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidIdType");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.idType = idType;
|
||||||
|
*/
|
||||||
|
entry.remoteAllowed = remoteAllowed == 1 ? 1 : 0;
|
||||||
|
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||||
|
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromYear = allowedFromAr[0];
|
||||||
|
entry.allowedFromMonth = allowedFromAr[1];
|
||||||
|
entry.allowedFromDay = allowedFromAr[2];
|
||||||
|
entry.allowedFromHour = allowedFromAr[3];
|
||||||
|
entry.allowedFromMinute = allowedFromAr[4];
|
||||||
|
entry.allowedFromSecond = allowedFromAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilYear = allowedUntilAr[0];
|
||||||
|
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||||
|
entry.allowedUntilDay = allowedUntilAr[2];
|
||||||
|
entry.allowedUntilHour = allowedUntilAr[3];
|
||||||
|
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||||
|
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||||
|
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||||
|
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _nukiOpener.addAuthorizationEntry(entry);
|
||||||
|
Log->print(F("Add authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
else if (strcmp(action, "update") == 0)
|
||||||
|
{
|
||||||
|
if(!authId)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!idExists)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nuki::CmdResult resultAuth = _nukiOpener.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||||
|
bool foundExisting = false;
|
||||||
|
|
||||||
|
if(resultAuth == Nuki::CmdResult::Success)
|
||||||
|
{
|
||||||
|
delay(250);
|
||||||
|
std::list<NukiOpener::AuthorizationEntry> entries;
|
||||||
|
_nukiOpener.getAuthorizationEntries(&entries);
|
||||||
|
|
||||||
|
for(const auto& entry : entries)
|
||||||
|
{
|
||||||
|
if (authId != entry.authId) continue;
|
||||||
|
else foundExisting = true;
|
||||||
|
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
memset(oldName, 0, sizeof(oldName));
|
||||||
|
memcpy(oldName, entry.name, sizeof(entry.name));
|
||||||
|
}
|
||||||
|
if(remoteAllowed == 2) remoteAllowed = entry.remoteAllowed;
|
||||||
|
if(enabled == 2) enabled = entry.enabled;
|
||||||
|
if(timeLimited == 2) timeLimited = entry.timeLimited;
|
||||||
|
if(allowedFrom.length() < 1)
|
||||||
|
{
|
||||||
|
allowedFrom = "old";
|
||||||
|
allowedFromAr[0] = entry.allowedFromYear;
|
||||||
|
allowedFromAr[1] = entry.allowedFromMonth;
|
||||||
|
allowedFromAr[2] = entry.allowedFromDay;
|
||||||
|
allowedFromAr[3] = entry.allowedFromHour;
|
||||||
|
allowedFromAr[4] = entry.allowedFromMinute;
|
||||||
|
allowedFromAr[5] = entry.allowedFromSecond;
|
||||||
|
}
|
||||||
|
if(allowedUntil.length() < 1)
|
||||||
|
{
|
||||||
|
allowedUntil = "old";
|
||||||
|
allowedUntilAr[0] = entry.allowedUntilYear;
|
||||||
|
allowedUntilAr[1] = entry.allowedUntilMonth;
|
||||||
|
allowedUntilAr[2] = entry.allowedUntilDay;
|
||||||
|
allowedUntilAr[3] = entry.allowedUntilHour;
|
||||||
|
allowedUntilAr[4] = entry.allowedUntilMinute;
|
||||||
|
allowedUntilAr[5] = entry.allowedUntilSecond;
|
||||||
|
}
|
||||||
|
if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays;
|
||||||
|
if(allowedFromTime.length() < 1)
|
||||||
|
{
|
||||||
|
allowedFromTime = "old";
|
||||||
|
allowedFromTimeAr[0] = entry.allowedFromTimeHour;
|
||||||
|
allowedFromTimeAr[1] = entry.allowedFromTimeMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() < 1)
|
||||||
|
{
|
||||||
|
allowedUntilTime = "old";
|
||||||
|
allowedUntilTimeAr[0] = entry.allowedUntilTimeHour;
|
||||||
|
allowedUntilTimeAr[1] = entry.allowedUntilTimeMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!foundExisting)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NukiOpener::UpdatedAuthorizationEntry entry;
|
||||||
|
|
||||||
|
memset(&entry, 0, sizeof(entry));
|
||||||
|
entry.authId = authId;
|
||||||
|
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
size_t nameLen = strlen(oldName);
|
||||||
|
memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t nameLen = name.length();
|
||||||
|
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
|
||||||
|
}
|
||||||
|
entry.remoteAllowed = remoteAllowed;
|
||||||
|
entry.enabled = enabled;
|
||||||
|
entry.timeLimited = timeLimited;
|
||||||
|
|
||||||
|
if(enabled == 1)
|
||||||
|
{
|
||||||
|
if(timeLimited == 1)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromYear = allowedFromAr[0];
|
||||||
|
entry.allowedFromMonth = allowedFromAr[1];
|
||||||
|
entry.allowedFromDay = allowedFromAr[2];
|
||||||
|
entry.allowedFromHour = allowedFromAr[3];
|
||||||
|
entry.allowedFromMinute = allowedFromAr[4];
|
||||||
|
entry.allowedFromSecond = allowedFromAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilYear = allowedUntilAr[0];
|
||||||
|
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||||
|
entry.allowedUntilDay = allowedUntilAr[2];
|
||||||
|
entry.allowedUntilHour = allowedUntilAr[3];
|
||||||
|
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||||
|
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||||
|
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||||
|
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _nukiOpener.updateAuthorizationEntry(entry);
|
||||||
|
Log->print(F("Update authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAction");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result != Nuki::CmdResult::Success) {
|
||||||
|
++_retryCount;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAuth(false);
|
||||||
|
|
||||||
|
if((int)result != -1)
|
||||||
|
{
|
||||||
|
char resultStr[15];
|
||||||
|
memset(&resultStr, 0, sizeof(resultStr));
|
||||||
|
NukiOpener::cmdResultToString(result, resultStr);
|
||||||
|
_network->publishAuthCommandResult(resultStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noActionSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const NukiOpener::OpenerState &NukiOpenerWrapper::keyTurnerState()
|
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;
|
||||||
}
|
}
|
||||||
@@ -802,9 +805,8 @@ void NukiWrapper::updateTimeControl(bool retrieved)
|
|||||||
|
|
||||||
while(_retryCount < _nrOfRetries + 1)
|
while(_retryCount < _nrOfRetries + 1)
|
||||||
{
|
{
|
||||||
Log->print(F("Querying lock time control: "));
|
Log->print(F("Querying lock timecontrol: "));
|
||||||
result = _nukiLock.retrieveTimeControlEntries();
|
result = _nukiLock.retrieveTimeControlEntries();
|
||||||
|
|
||||||
if(result != Nuki::CmdResult::Success) {
|
if(result != Nuki::CmdResult::Success) {
|
||||||
++_retryCount;
|
++_retryCount;
|
||||||
}
|
}
|
||||||
@@ -822,7 +824,7 @@ void NukiWrapper::updateTimeControl(bool retrieved)
|
|||||||
std::list<NukiLock::TimeControlEntry> timeControlEntries;
|
std::list<NukiLock::TimeControlEntry> timeControlEntries;
|
||||||
_nukiLock.getTimeControlEntries(&timeControlEntries);
|
_nukiLock.getTimeControlEntries(&timeControlEntries);
|
||||||
|
|
||||||
Log->print(F("Lock time control entries: "));
|
Log->print(F("Lock timecontrol entries: "));
|
||||||
Log->println(timeControlEntries.size());
|
Log->println(timeControlEntries.size());
|
||||||
|
|
||||||
timeControlEntries.sort([](const NukiLock::TimeControlEntry& a, const NukiLock::TimeControlEntry& b) { return a.entryId < b.entryId; });
|
timeControlEntries.sort([](const NukiLock::TimeControlEntry& a, const NukiLock::TimeControlEntry& b) { return a.entryId < b.entryId; });
|
||||||
@@ -852,6 +854,67 @@ void NukiWrapper::updateTimeControl(bool retrieved)
|
|||||||
postponeBleWatchdog();
|
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)
|
||||||
@@ -2436,7 +2504,7 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
if(idExists)
|
if(idExists)
|
||||||
{
|
{
|
||||||
result = _nukiLock.removeTimeControlEntry(entryId);
|
result = _nukiLock.removeTimeControlEntry(entryId);
|
||||||
Log->print(F("Delete time control: "));
|
Log->print(F("Delete timecontrol: "));
|
||||||
Log->println((int)result);
|
Log->println((int)result);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2495,7 +2563,7 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
entry.lockAction = timeControlLockAction;
|
entry.lockAction = timeControlLockAction;
|
||||||
|
|
||||||
result = _nukiLock.addTimeControlEntry(entry);
|
result = _nukiLock.addTimeControlEntry(entry);
|
||||||
Log->print(F("Add time control: "));
|
Log->print(F("Add timecontrol: "));
|
||||||
Log->println((int)result);
|
Log->println((int)result);
|
||||||
}
|
}
|
||||||
else if (strcmp(action, "update") == 0)
|
else if (strcmp(action, "update") == 0)
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2558,7 +2626,7 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
entry.lockAction = timeControlLockAction;
|
entry.lockAction = timeControlLockAction;
|
||||||
|
|
||||||
result = _nukiLock.updateTimeControlEntry(entry);
|
result = _nukiLock.updateTimeControlEntry(entry);
|
||||||
Log->print(F("Update time control: "));
|
Log->print(F("Update timecontrol: "));
|
||||||
Log->println((int)result);
|
Log->println((int)result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2591,6 +2659,467 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NukiWrapper::onAuthCommandReceived(const char *value)
|
||||||
|
{
|
||||||
|
if(!_nukiConfigValid)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("configNotReady");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isPinValid())
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noValidPinSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_preferences->getBool(preference_auth_control_enabled))
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("keypadControlDisabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument json;
|
||||||
|
DeserializationError jsonError = deserializeJson(json, value);
|
||||||
|
|
||||||
|
if(jsonError)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidJson");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char oldName[33];
|
||||||
|
const char *action = json["action"].as<const char*>();
|
||||||
|
uint16_t authId = json["authId"].as<unsigned int>();
|
||||||
|
//uint8_t idType = json["idType"].as<unsigned int>();
|
||||||
|
//unsigned char secretKeyK[32] = {0x00};
|
||||||
|
uint8_t remoteAllowed;
|
||||||
|
uint8_t enabled;
|
||||||
|
uint8_t timeLimited;
|
||||||
|
String name;
|
||||||
|
//String sharedKey;
|
||||||
|
String allowedFrom;
|
||||||
|
String allowedUntil;
|
||||||
|
String allowedWeekdays;
|
||||||
|
String allowedFromTime;
|
||||||
|
String allowedUntilTime;
|
||||||
|
|
||||||
|
if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as<unsigned int>();
|
||||||
|
else remoteAllowed = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>();
|
||||||
|
else enabled = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>();
|
||||||
|
else timeLimited = 2;
|
||||||
|
|
||||||
|
if(json.containsKey("name")) name = json["name"].as<String>();
|
||||||
|
//if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as<String>();
|
||||||
|
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>();
|
||||||
|
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>();
|
||||||
|
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>();
|
||||||
|
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>();
|
||||||
|
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>();
|
||||||
|
|
||||||
|
if(action)
|
||||||
|
{
|
||||||
|
bool idExists = false;
|
||||||
|
|
||||||
|
if(authId)
|
||||||
|
{
|
||||||
|
idExists = std::find(_authIds.begin(), _authIds.end(), authId) != _authIds.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
Nuki::CmdResult result = (Nuki::CmdResult)-1;
|
||||||
|
_retryCount = 0;
|
||||||
|
|
||||||
|
while(_retryCount < _nrOfRetries)
|
||||||
|
{
|
||||||
|
if(strcmp(action, "delete") == 0) {
|
||||||
|
if(idExists)
|
||||||
|
{
|
||||||
|
result = _nukiLock.deleteAuthorizationEntry(authId);
|
||||||
|
delay(250);
|
||||||
|
Log->print(F("Delete authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
|
||||||
|
{
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
if (strcmp(action, "update") != 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noNameSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(sharedKey.length() != 64)
|
||||||
|
{
|
||||||
|
if (strcmp(action, "update") != 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noSharedKeySet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i=0; i<sharedKey.length();i+=2) secretKeyK[(i/2)] = std::stoi(sharedKey.substring(i, i+2).c_str(), nullptr, 16);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned int allowedFromAr[6];
|
||||||
|
unsigned int allowedUntilAr[6];
|
||||||
|
unsigned int allowedFromTimeAr[2];
|
||||||
|
unsigned int allowedUntilTimeAr[2];
|
||||||
|
uint8_t allowedWeekdaysInt = 0;
|
||||||
|
|
||||||
|
if(timeLimited == 1)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() == 19)
|
||||||
|
{
|
||||||
|
allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt();
|
||||||
|
allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt();
|
||||||
|
allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt();
|
||||||
|
allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt();
|
||||||
|
allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt();
|
||||||
|
allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt();
|
||||||
|
|
||||||
|
if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFrom");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedUntil.length() > 0 == 19)
|
||||||
|
{
|
||||||
|
allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt();
|
||||||
|
allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt();
|
||||||
|
allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt();
|
||||||
|
allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt();
|
||||||
|
allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt();
|
||||||
|
allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt();
|
||||||
|
|
||||||
|
if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedFromTime.length() == 5)
|
||||||
|
{
|
||||||
|
allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt();
|
||||||
|
allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt();
|
||||||
|
|
||||||
|
if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedFromTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
if(allowedUntilTime.length() == 5)
|
||||||
|
{
|
||||||
|
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt();
|
||||||
|
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt();
|
||||||
|
|
||||||
|
if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAllowedUntilTime");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64;
|
||||||
|
if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32;
|
||||||
|
if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16;
|
||||||
|
if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8;
|
||||||
|
if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4;
|
||||||
|
if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2;
|
||||||
|
if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(action, "add") == 0)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("addActionNotSupported");
|
||||||
|
return;
|
||||||
|
|
||||||
|
NukiLock::NewAuthorizationEntry entry;
|
||||||
|
memset(&entry, 0, sizeof(entry));
|
||||||
|
size_t nameLen = name.length();
|
||||||
|
memcpy(&entry.name, name.c_str(), nameLen > 32 ? 32 : nameLen);
|
||||||
|
/*
|
||||||
|
memcpy(&entry.sharedKey, secretKeyK, 32);
|
||||||
|
|
||||||
|
if(idType != 1)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidIdType");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.idType = idType;
|
||||||
|
*/
|
||||||
|
entry.remoteAllowed = remoteAllowed == 1 ? 1 : 0;
|
||||||
|
entry.timeLimited = timeLimited == 1 ? 1 : 0;
|
||||||
|
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromYear = allowedFromAr[0];
|
||||||
|
entry.allowedFromMonth = allowedFromAr[1];
|
||||||
|
entry.allowedFromDay = allowedFromAr[2];
|
||||||
|
entry.allowedFromHour = allowedFromAr[3];
|
||||||
|
entry.allowedFromMinute = allowedFromAr[4];
|
||||||
|
entry.allowedFromSecond = allowedFromAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilYear = allowedUntilAr[0];
|
||||||
|
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||||
|
entry.allowedUntilDay = allowedUntilAr[2];
|
||||||
|
entry.allowedUntilHour = allowedUntilAr[3];
|
||||||
|
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||||
|
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||||
|
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||||
|
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _nukiLock.addAuthorizationEntry(entry);
|
||||||
|
delay(250);
|
||||||
|
Log->print(F("Add authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
else if (strcmp(action, "update") == 0)
|
||||||
|
{
|
||||||
|
if(!authId)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!idExists)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noExistingAuthIdSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nuki::CmdResult resultAuth = _nukiLock.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH));
|
||||||
|
delay(250);
|
||||||
|
bool foundExisting = false;
|
||||||
|
|
||||||
|
if(resultAuth == Nuki::CmdResult::Success)
|
||||||
|
{
|
||||||
|
std::list<NukiLock::AuthorizationEntry> entries;
|
||||||
|
_nukiLock.getAuthorizationEntries(&entries);
|
||||||
|
|
||||||
|
for(const auto& entry : entries)
|
||||||
|
{
|
||||||
|
if (authId != entry.authId) continue;
|
||||||
|
else foundExisting = true;
|
||||||
|
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
memset(oldName, 0, sizeof(oldName));
|
||||||
|
memcpy(oldName, entry.name, sizeof(entry.name));
|
||||||
|
}
|
||||||
|
if(remoteAllowed == 2) remoteAllowed = entry.remoteAllowed;
|
||||||
|
if(enabled == 2) enabled = entry.enabled;
|
||||||
|
if(timeLimited == 2) timeLimited = entry.timeLimited;
|
||||||
|
if(allowedFrom.length() < 1)
|
||||||
|
{
|
||||||
|
allowedFrom = "old";
|
||||||
|
allowedFromAr[0] = entry.allowedFromYear;
|
||||||
|
allowedFromAr[1] = entry.allowedFromMonth;
|
||||||
|
allowedFromAr[2] = entry.allowedFromDay;
|
||||||
|
allowedFromAr[3] = entry.allowedFromHour;
|
||||||
|
allowedFromAr[4] = entry.allowedFromMinute;
|
||||||
|
allowedFromAr[5] = entry.allowedFromSecond;
|
||||||
|
}
|
||||||
|
if(allowedUntil.length() < 1)
|
||||||
|
{
|
||||||
|
allowedUntil = "old";
|
||||||
|
allowedUntilAr[0] = entry.allowedUntilYear;
|
||||||
|
allowedUntilAr[1] = entry.allowedUntilMonth;
|
||||||
|
allowedUntilAr[2] = entry.allowedUntilDay;
|
||||||
|
allowedUntilAr[3] = entry.allowedUntilHour;
|
||||||
|
allowedUntilAr[4] = entry.allowedUntilMinute;
|
||||||
|
allowedUntilAr[5] = entry.allowedUntilSecond;
|
||||||
|
}
|
||||||
|
if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays;
|
||||||
|
if(allowedFromTime.length() < 1)
|
||||||
|
{
|
||||||
|
allowedFromTime = "old";
|
||||||
|
allowedFromTimeAr[0] = entry.allowedFromTimeHour;
|
||||||
|
allowedFromTimeAr[1] = entry.allowedFromTimeMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() < 1)
|
||||||
|
{
|
||||||
|
allowedUntilTime = "old";
|
||||||
|
allowedUntilTimeAr[0] = entry.allowedUntilTimeHour;
|
||||||
|
allowedUntilTimeAr[1] = entry.allowedUntilTimeMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!foundExisting)
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("failedToRetrieveExistingAuthorizationEntry");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NukiLock::UpdatedAuthorizationEntry entry;
|
||||||
|
|
||||||
|
memset(&entry, 0, sizeof(entry));
|
||||||
|
entry.authId = authId;
|
||||||
|
|
||||||
|
if(name.length() < 1)
|
||||||
|
{
|
||||||
|
size_t nameLen = strlen(oldName);
|
||||||
|
memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t nameLen = name.length();
|
||||||
|
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
|
||||||
|
}
|
||||||
|
entry.remoteAllowed = remoteAllowed;
|
||||||
|
entry.enabled = enabled;
|
||||||
|
entry.timeLimited = timeLimited;
|
||||||
|
|
||||||
|
if(enabled == 1)
|
||||||
|
{
|
||||||
|
if(timeLimited == 1)
|
||||||
|
{
|
||||||
|
if(allowedFrom.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromYear = allowedFromAr[0];
|
||||||
|
entry.allowedFromMonth = allowedFromAr[1];
|
||||||
|
entry.allowedFromDay = allowedFromAr[2];
|
||||||
|
entry.allowedFromHour = allowedFromAr[3];
|
||||||
|
entry.allowedFromMinute = allowedFromAr[4];
|
||||||
|
entry.allowedFromSecond = allowedFromAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntil.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilYear = allowedUntilAr[0];
|
||||||
|
entry.allowedUntilMonth = allowedUntilAr[1];
|
||||||
|
entry.allowedUntilDay = allowedUntilAr[2];
|
||||||
|
entry.allowedUntilHour = allowedUntilAr[3];
|
||||||
|
entry.allowedUntilMinute = allowedUntilAr[4];
|
||||||
|
entry.allowedUntilSecond = allowedUntilAr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.allowedWeekdays = allowedWeekdaysInt;
|
||||||
|
|
||||||
|
if(allowedFromTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedFromTimeHour = allowedFromTimeAr[0];
|
||||||
|
entry.allowedFromTimeMin = allowedFromTimeAr[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowedUntilTime.length() > 0)
|
||||||
|
{
|
||||||
|
entry.allowedUntilTimeHour = allowedUntilTimeAr[0];
|
||||||
|
entry.allowedUntilTimeMin = allowedUntilTimeAr[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _nukiLock.updateAuthorizationEntry(entry);
|
||||||
|
delay(250);
|
||||||
|
Log->print(F("Update authorization: "));
|
||||||
|
Log->println((int)result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("invalidAction");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result != Nuki::CmdResult::Success) {
|
||||||
|
++_retryCount;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAuth(false);
|
||||||
|
|
||||||
|
if((int)result != -1)
|
||||||
|
{
|
||||||
|
char resultStr[15];
|
||||||
|
memset(&resultStr, 0, sizeof(resultStr));
|
||||||
|
NukiLock::cmdResultToString(result, resultStr);
|
||||||
|
_network->publishAuthCommandResult(resultStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_network->publishAuthCommandResult("noActionSet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const NukiLock::KeyTurnerState &NukiWrapper::keyTurnerState()
|
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,42 +314,8 @@ 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; }
|
||||||
if (!jsonError)
|
|
||||||
{
|
|
||||||
manifestSuccess = true;
|
|
||||||
response->print("<b>Latest release version: </b><span id=\"latestver\">");
|
|
||||||
response->print(doc["release"]["fullversion"].as<const char*>());
|
|
||||||
response->print(" (");
|
|
||||||
response->print(doc["release"]["build"].as<const char*>());
|
|
||||||
response->print(")</span>, ");
|
|
||||||
response->print(doc["release"]["time"].as<const char*>());
|
|
||||||
response->print("<br>");
|
|
||||||
response->print("<b>Latest beta version: </b><span id=\"betaver\">");
|
|
||||||
if(doc["beta"]["fullversion"] != "No beta available")
|
|
||||||
{
|
|
||||||
response->print(doc["beta"]["fullversion"].as<const char*>());
|
|
||||||
response->print(" (");
|
|
||||||
response->print(doc["beta"]["build"].as<const char*>());
|
|
||||||
response->print(")</span>, ");
|
|
||||||
response->print(doc["beta"]["time"].as<const char*>());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response->print(doc["beta"]["fullversion"].as<const char*>());
|
|
||||||
response->print("</span>");
|
|
||||||
}
|
|
||||||
response->print("<br>");
|
|
||||||
response->print("<b>Latest development version: </b><span id=\"devver\">");
|
|
||||||
response->print(doc["master"]["fullversion"].as<const char*>());
|
|
||||||
response->print(" (");
|
|
||||||
response->print(doc["master"]["build"].as<const char*>());
|
|
||||||
response->print(")</span>, ");
|
|
||||||
response->print(doc["master"]["time"].as<const char*>());
|
|
||||||
response->print("<br>");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
https.end();
|
https.end();
|
||||||
}
|
}
|
||||||
@@ -362,6 +327,38 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug)
|
|||||||
{
|
{
|
||||||
response->print("<span id=\"currentver\" style=\"display: none;\">currentver</span><span id=\"latestver\" style=\"display: none;\">latestver</span><span id=\"devver\" style=\"display: none;\">devver</span><span id=\"betaver\" style=\"display: none;\">betaver</span>");
|
response->print("<span id=\"currentver\" style=\"display: none;\">currentver</span><span id=\"latestver\" style=\"display: none;\">latestver</span><span id=\"devver\" style=\"display: none;\">devver</span><span id=\"betaver\" style=\"display: none;\">betaver</span>");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response->print("<b>Latest release version: </b><span id=\"latestver\">");
|
||||||
|
response->print(doc["release"]["fullversion"].as<const char*>());
|
||||||
|
response->print(" (");
|
||||||
|
response->print(doc["release"]["build"].as<const char*>());
|
||||||
|
response->print(")</span>, ");
|
||||||
|
response->print(doc["release"]["time"].as<const char*>());
|
||||||
|
response->print("<br>");
|
||||||
|
response->print("<b>Latest beta version: </b><span id=\"betaver\">");
|
||||||
|
if(doc["beta"]["fullversion"] != "No beta available")
|
||||||
|
{
|
||||||
|
response->print(doc["beta"]["fullversion"].as<const char*>());
|
||||||
|
response->print(" (");
|
||||||
|
response->print(doc["beta"]["build"].as<const char*>());
|
||||||
|
response->print(")</span>, ");
|
||||||
|
response->print(doc["beta"]["time"].as<const char*>());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response->print(doc["beta"]["fullversion"].as<const char*>());
|
||||||
|
response->print("</span>");
|
||||||
|
}
|
||||||
|
response->print("<br>");
|
||||||
|
response->print("<b>Latest development version: </b><span id=\"devver\">");
|
||||||
|
response->print(doc["master"]["fullversion"].as<const char*>());
|
||||||
|
response->print(" (");
|
||||||
|
response->print(doc["master"]["build"].as<const char*>());
|
||||||
|
response->print(")</span>, ");
|
||||||
|
response->print(doc["master"]["time"].as<const char*>());
|
||||||
|
response->print("<br>");
|
||||||
|
}
|
||||||
#endif
|
#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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3865,9 +3898,17 @@ void WebCfgServer::printInputField(AsyncResponseStream *response,
|
|||||||
response->print(isPassword ? "\"password\"" : "\"text\"");
|
response->print(isPassword ? "\"password\"" : "\"text\"");
|
||||||
if(strcmp(id, "") != 0)
|
if(strcmp(id, "") != 0)
|
||||||
{
|
{
|
||||||
response->print(" id=\"");
|
if(strncmp(id, "class=", 6) != 0)
|
||||||
response->print(id);
|
{
|
||||||
response->print("\"");
|
response->print(" ");
|
||||||
|
response->print(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response->print(" id=\"");
|
||||||
|
response->print(id);
|
||||||
|
response->print("\"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(strcmp(value, "") != 0)
|
if(strcmp(value, "") != 0)
|
||||||
{
|
{
|
||||||
@@ -4129,4 +4170,4 @@ String WebCfgServer::getPreselectionForGpio(const uint8_t &pin)
|
|||||||
|
|
||||||
return String((int8_t)PinRole::Disabled);
|
return String((int8_t)PinRole::Disabled);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
14
src/main.cpp
14
src/main.cpp
@@ -154,7 +154,6 @@ void networkTask(void *pvParameters)
|
|||||||
}
|
}
|
||||||
|
|
||||||
esp_task_wdt_reset();
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,10 +344,10 @@ void otaTask(void *pvParameter)
|
|||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log->println("Firmware upgrade failed, restarting");
|
Log->println("Firmware upgrade failed, restarting");
|
||||||
esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL));
|
esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL));
|
||||||
restartEsp(RestartReason::OTAAborted);
|
restartEsp(RestartReason::OTAAborted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupTasks(bool ota)
|
void setupTasks(bool ota)
|
||||||
@@ -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