SPIFFS MQTT SSL and import/export

This commit is contained in:
iranl
2025-01-02 17:00:54 +01:00
parent 102773a764
commit e29976c8ca
8 changed files with 809 additions and 321 deletions

View File

@@ -32,9 +32,6 @@ build_unflags =
-Wall -Wall
build_flags = build_flags =
-fexceptions -fexceptions
-DTLS_CA_MAX_SIZE=2200
-DTLS_CERT_MAX_SIZE=2200
-DTLS_KEY_MAX_SIZE=2200
-DESP_PLATFORM -DESP_PLATFORM
-DESP32 -DESP32
-DARDUINO_ARCH_ESP32 -DARDUINO_ARCH_ESP32

View File

@@ -22,7 +22,7 @@ CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y
CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 CONFIG_BT_NIMBLE_PINNED_TO_CORE=0
CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192 CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
CONFIG_BT_NIMBLE_ROLE_CENTRAL=y CONFIG_BT_NIMBLE_ROLE_CENTRAL=y
CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=n
CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
CONFIG_BT_NIMBLE_SM_LEGACY=y CONFIG_BT_NIMBLE_SM_LEGACY=y

View File

@@ -5,7 +5,7 @@
#define NUKI_HUB_VERSION "9.07" #define NUKI_HUB_VERSION "9.07"
#define NUKI_HUB_VERSION_INT (uint32_t)907 #define NUKI_HUB_VERSION_INT (uint32_t)907
#define NUKI_HUB_BUILD "unknownbuildnr" #define NUKI_HUB_BUILD "unknownbuildnr"
#define NUKI_HUB_DATE "2025-01-02" #define NUKI_HUB_DATE "2025-01-03"
#define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest"
#define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json"

View File

@@ -3,6 +3,9 @@
#include <vector> #include <vector>
#include "Config.h" #include "Config.h"
#include "Logger.h" #include "Logger.h"
#include "FS.h"
#include "SPIFFS.h"
#include "RestartReason.h"
#ifndef CONFIG_IDF_TARGET_ESP32H2 #ifndef CONFIG_IDF_TARGET_ESP32H2
#include <WiFi.h> #include <WiFi.h>
@@ -69,6 +72,7 @@
#define preference_connect_mode (char*)"nukiConnMode" #define preference_connect_mode (char*)"nukiConnMode"
#define preference_http_auth_type (char*)"httpdAuthType" #define preference_http_auth_type (char*)"httpdAuthType"
#define preference_update_time (char*)"updateTime" #define preference_update_time (char*)"updateTime"
#define preference_mqtt_ssl_enabled (char*)"mqttSSLena"
// CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT
#define preference_find_best_rssi (char*)"nwbestrssi" #define preference_find_best_rssi (char*)"nwbestrssi"
@@ -199,6 +203,7 @@ inline void initPreferences(Preferences* preferences)
preferences->putBool(preference_publish_authdata, false); preferences->putBool(preference_publish_authdata, false);
preferences->putBool(preference_register_as_app, false); preferences->putBool(preference_register_as_app, false);
preferences->putBool(preference_register_opener_as_app, false); preferences->putBool(preference_register_opener_as_app, false);
preferences->putBool(preference_mqtt_ssl_enabled, false);
preferences->putInt(preference_mqtt_broker_port, 1883); preferences->putInt(preference_mqtt_broker_port, 1883);
preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE); preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE);
@@ -226,7 +231,6 @@ inline void initPreferences(Preferences* preferences)
preferences->putBool(preference_connect_mode, true); preferences->putBool(preference_connect_mode, true);
preferences->putBool(preference_http_auth_type, false); preferences->putBool(preference_http_auth_type, false);
preferences->putBool(preference_retain_gpio, false); preferences->putBool(preference_retain_gpio, false);
#ifndef CONFIG_IDF_TARGET_ESP32H2 #ifndef CONFIG_IDF_TARGET_ESP32H2
WiFi.begin(); WiFi.begin();
@@ -236,6 +240,7 @@ inline void initPreferences(Preferences* preferences)
else else
{ {
int lastConfigVer = preferences->getInt(preference_config_version); int lastConfigVer = preferences->getInt(preference_config_version);
bool rebootOnMigrate = false;
Log->print("Last config version: "); Log->print("Last config version: ");
Log->println(lastConfigVer); Log->println(lastConfigVer);
@@ -357,7 +362,100 @@ inline void initPreferences(Preferences* preferences)
Log->println("Migration 9.02"); Log->println("Migration 9.02");
preferences->putBool(preference_reset_mqtt_topics, true); preferences->putBool(preference_reset_mqtt_topics, true);
} }
if (lastConfigVer < 907)
{
Log->println("Migration 9.07");
char ca[2200] = {0};
char cert[2200] = {0};
char key[2200] = {0};
size_t caLength = preferences->getString(preference_mqtt_ca, ca, 2200);
size_t crtLength = preferences->getString(preference_mqtt_crt, cert, 2200);
size_t keyLength = preferences->getString(preference_mqtt_key, key, 2200);
if (caLength > 1)
{
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/mqtt_ssl.ca", FILE_WRITE);
if (!file) {
Log->println("Failed to open /mqtt_ssl.ca for writing");
}
else
{
if (!file.print(ca))
{
Log->println("Failed to write /mqtt_ssl.ca");
}
file.close();
}
}
rebootOnMigrate = true;
preferences->putBool(preference_mqtt_ssl_enabled, true);
preferences->putString(preference_mqtt_ca, "");
}
if (crtLength > 1)
{
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/mqtt_ssl.crt", FILE_WRITE);
if (!file) {
Log->println("Failed to open /mqtt_ssl.crt for writing");
}
else
{
if (!file.print(cert))
{
Log->println("Failed to write /mqtt_ssl.crt");
}
file.close();
}
}
rebootOnMigrate = true;
preferences->putString(preference_mqtt_crt, "");
}
if (keyLength > 1)
{
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/mqtt_ssl.key", FILE_WRITE);
if (!file) {
Log->println("Failed to open /mqtt_ssl.key for writing");
}
else
{
if (!file.print(key))
{
Log->println("Failed to write /mqtt_ssl.key");
}
file.close();
}
}
rebootOnMigrate = true;
preferences->putString(preference_mqtt_key, "");
}
}
preferences->putInt(preference_config_version, NUKI_HUB_VERSION_INT); preferences->putInt(preference_config_version, NUKI_HUB_VERSION_INT);
if (rebootOnMigrate)
{
restartEsp(RestartReason::OTACompleted);
}
} }
#endif #endif
} }
@@ -367,29 +465,29 @@ class DebugPreferences
private: private:
std::vector<char*> _keys = std::vector<char*> _keys =
{ {
preference_started_before, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener, preference_started_before, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener,
preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates,
preference_webserver_enabled, preference_lock_enabled, preference_lock_pin_status, preference_mqtt_lock_path, preference_opener_enabled, preference_opener_pin_status, preference_webserver_enabled, preference_lock_enabled, preference_lock_pin_status, preference_mqtt_lock_path, preference_opener_enabled, preference_opener_pin_status,
preference_opener_continuous_mode, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_update_time, preference_opener_continuous_mode, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_update_time,
preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt, preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt,
preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address,
preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_http_auth_type, preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_http_auth_type,
preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect,
preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry,
preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled,
preference_keypad_info_enabled, preference_keypad_publish_code, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_conf_info_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_conf_info_enabled,
preference_register_as_app, preference_register_opener_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user, preference_register_as_app, preference_register_opener_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user,
preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, preference_mqtt_ssl_enabled,
preference_official_hybrid_enabled, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_official_hybrid_enabled, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry,
preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries, preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries,
preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_webserial_enabled, preference_find_best_rssi, preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_webserial_enabled, preference_find_best_rssi,
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_lock_max_auth_entry_count, preference_opener_max_auth_entry_count, 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, preference_wifi_ssid, preference_wifi_pass, preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, preference_wifi_ssid, preference_wifi_pass,
preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_mqtt_hass_enabled, preference_hass_device_discovery, preference_retain_gpio, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_mqtt_hass_enabled, preference_hass_device_discovery, preference_retain_gpio,
preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode, preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode,
preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_nukihub_id preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_nukihub_id
}; };
std::vector<char*> _redact = std::vector<char*> _redact =
{ {
@@ -398,34 +496,34 @@ private:
}; };
std::vector<char*> _boolPrefs = std::vector<char*> _boolPrefs =
{ {
preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode, preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode,
preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_enable_bootloop_reset, preference_webserver_enabled, preference_update_time, preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_enable_bootloop_reset, preference_webserver_enabled, preference_update_time,
preference_restart_on_disconnect, preference_keypad_control_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, preference_show_secrets, preference_restart_on_disconnect, preference_keypad_control_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, preference_show_secrets,
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_official_hybrid_enabled, preference_mqtt_hass_enabled, preference_retain_gpio, preference_publish_authdata, preference_publish_debug_info, preference_official_hybrid_enabled, preference_mqtt_hass_enabled, preference_retain_gpio,
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_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_webserial_enabled, preference_hass_device_discovery, preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_webserial_enabled, preference_hass_device_discovery,
preference_ntw_reconfigure, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi, preference_http_auth_type, preference_ntw_reconfigure, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi, preference_http_auth_type,
preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode, preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode,
preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_mqtt_ssl_enabled
}; };
std::vector<char*> _bytePrefs = std::vector<char*> _bytePrefs =
{ {
preference_acl, preference_conf_info_enabled, preference_conf_lock_basic_acl, preference_conf_lock_advanced_acl, preference_conf_opener_basic_acl, preference_acl, preference_conf_info_enabled, preference_conf_lock_basic_acl, preference_conf_lock_advanced_acl, preference_conf_opener_basic_acl,
preference_conf_opener_advanced_acl, preference_gpio_configuration preference_conf_opener_advanced_acl, preference_gpio_configuration
}; };
std::vector<char*> _intPrefs = std::vector<char*> _intPrefs =
{ {
preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener, preference_mqtt_broker_port, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener, preference_mqtt_broker_port,
preference_lock_pin_status, preference_opener_pin_status, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_lock_pin_status, preference_opener_pin_status, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count,
preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_buffer_size, preference_network_hardware, preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_buffer_size, preference_network_hardware,
preference_rssi_publish_interval, preference_network_timeout, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_rssi_publish_interval, preference_network_timeout, preference_restart_ble_beacon_lost, preference_query_interval_lockstate,
preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_command_nr_of_retries, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_command_nr_of_retries,
preference_command_retry_delay, preference_query_interval_hybrid_lockstate, preference_command_retry_delay, preference_query_interval_hybrid_lockstate,
preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries, preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries,
preference_ble_tx_power, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_ble_tx_power, 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_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso,
preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio
}; };
public: public:
const std::vector<char*> getPreferencesKeys() const std::vector<char*> getPreferencesKeys()

View File

@@ -4,10 +4,10 @@
#include "Logger.h" #include "Logger.h"
#include "RestartReason.h" #include "RestartReason.h"
#include <esp_task_wdt.h> #include <esp_task_wdt.h>
#ifdef CONFIG_SOC_SPIRAM_SUPPORTED
#include "esp_psram.h"
#include "FS.h" #include "FS.h"
#include "SPIFFS.h" #include "SPIFFS.h"
#ifdef CONFIG_SOC_SPIRAM_SUPPORTED
#include "esp_psram.h"
#endif #endif
#ifndef CONFIG_IDF_TARGET_ESP32H2 #ifndef CONFIG_IDF_TARGET_ESP32H2
#include <esp_wifi.h> #include <esp_wifi.h>
@@ -112,20 +112,10 @@ void WebCfgServer::initialize()
}); });
_psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) _psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp)
{ {
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword))
{
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in.");
}
return sendCss(request, resp); return sendCss(request, resp);
}); });
_psychicServer->on("/favicon.ico", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) _psychicServer->on("/favicon.ico", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp)
{ {
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword))
{
return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in.");
}
return sendFavicon(request, resp); return sendFavicon(request, resp);
}); });
@@ -1269,206 +1259,335 @@ void WebCfgServer::printInputField(PsychicStreamResponse *response,
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
esp_err_t WebCfgServer::sendSettings(PsychicRequest *request, PsychicResponse* resp) esp_err_t WebCfgServer::sendSettings(PsychicRequest *request, PsychicResponse* resp)
{ {
bool redacted = false;
bool pairing = false;
if(request->hasParam("redacted"))
{
const PsychicWebParameter* p = request->getParam("redacted");
if(p->value() == "1")
{
redacted = true;
}
}
if(request->hasParam("pairing"))
{
const PsychicWebParameter* p = request->getParam("pairing");
if(p->value() == "1")
{
pairing = true;
}
}
JsonDocument json; JsonDocument json;
String jsonPretty; String jsonPretty;
String name;
DebugPreferences debugPreferences; if(request->hasParam("type"))
const std::vector<char*> keysPrefs = debugPreferences.getPreferencesKeys();
const std::vector<char*> boolPrefs = debugPreferences.getPreferencesBoolKeys();
const std::vector<char*> redactedPrefs = debugPreferences.getPreferencesRedactedKeys();
const std::vector<char*> bytePrefs = debugPreferences.getPreferencesByteKeys();
for(const auto& key : keysPrefs)
{ {
if(strcmp(key, preference_show_secrets) == 0) name = "nuki_hub_http_ssl.json";
const PsychicWebParameter* p = request->getParam("type");
if(p->value() == "https")
{ {
continue; name = "nuki_hub_http_ssl.json";
} if (!SPIFFS.begin(true)) {
if(strcmp(key, preference_latest_version) == 0) Log->println("SPIFFS Mount Failed");
{ }
continue; else
} {
if(!redacted) if(std::find(redactedPrefs.begin(), redactedPrefs.end(), key) != redactedPrefs.end()) File file = SPIFFS.open("/http_ssl.crt");
{ if (!file || file.isDirectory()) {
continue; Log->println("http_ssl.crt not found");
}
else
{
Log->println("Reading http_ssl.crt");
size_t filesize = file.size();
char cert[filesize + 1];
file.read((uint8_t *)cert, sizeof(cert));
file.close();
cert[filesize] = '\0';
json["http_ssl.crt"] = cert;
}
}
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/http_ssl.key");
if (!file || file.isDirectory()) {
Log->println("http_ssl.key not found");
}
else
{
Log->println("Reading http_ssl.key");
size_t filesize = file.size();
char key[filesize + 1];
file.read((uint8_t *)key, sizeof(key));
file.close();
key[filesize] = '\0';
json["http_ssl.key"] = key;
}
} }
if(!_preferences->isKey(key))
{
json[key] = "";
}
else if(std::find(boolPrefs.begin(), boolPrefs.end(), key) != boolPrefs.end())
{
json[key] = _preferences->getBool(key) ? "1" : "0";
} }
else else
{ {
switch(_preferences->getType(key)) name = "nuki_hub_mqtt_ssl.json";
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{ {
case PT_I8: File file = SPIFFS.open("/mqtt_ssl.ca");
json[key] = String(_preferences->getChar(key)); if (!file || file.isDirectory()) {
break; Log->println("mqtt_ssl.ca not found");
case PT_I16: }
json[key] = String(_preferences->getShort(key)); else
break; {
case PT_I32: Log->println("Reading mqtt_ssl.ca");
json[key] = String(_preferences->getInt(key)); size_t filesize = file.size();
break; char ca[filesize + 1];
case PT_I64:
json[key] = String(_preferences->getLong64(key)); file.read((uint8_t *)ca, sizeof(ca));
break; file.close();
case PT_U8: ca[filesize] = '\0';
json[key] = String(_preferences->getUChar(key)); json["mqtt_ssl.ca"] = ca;
break; }
case PT_U16: }
json[key] = String(_preferences->getUShort(key));
break; if (!SPIFFS.begin(true)) {
case PT_U32: Log->println("SPIFFS Mount Failed");
json[key] = String(_preferences->getUInt(key)); }
break; else
case PT_U64: {
json[key] = String(_preferences->getULong64(key)); File file = SPIFFS.open("/mqtt_ssl.crt");
break; if (!file || file.isDirectory()) {
case PT_STR: Log->println("mqtt_ssl.crt not found");
json[key] = _preferences->getString(key); }
break; else
default: {
json[key] = _preferences->getString(key); Log->println("Reading mqtt_ssl.crt");
break; size_t filesize = file.size();
char cert[filesize + 1];
file.read((uint8_t *)cert, sizeof(cert));
file.close();
cert[filesize] = '\0';
json["mqtt_ssl.crt"] = cert;
}
}
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/mqtt_ssl.key");
if (!file || file.isDirectory()) {
Log->println("mqtt_ssl.key not found");
}
else
{
Log->println("Reading mqtt_ssl.key");
size_t filesize = file.size();
char key[filesize + 1];
file.read((uint8_t *)key, sizeof(key));
file.close();
key[filesize] = '\0';
json["mqtt_ssl.key"] = key;
}
} }
} }
} }
else
if(pairing)
{ {
if(_nuki != nullptr) name = "nuki_hub_settings.json";
bool redacted = false;
bool pairing = false;
if(request->hasParam("redacted"))
{ {
unsigned char currentBleAddress[6]; const PsychicWebParameter* p = request->getParam("redacted");
unsigned char authorizationId[4] = {0x00}; if(p->value() == "1")
unsigned char secretKeyK[32] = {0x00}; {
uint16_t storedPincode = 0000; redacted = true;
Preferences nukiBlePref; }
nukiBlePref.begin("NukiHub", false); }
nukiBlePref.getBytes("bleAddress", currentBleAddress, 6); if(request->hasParam("pairing"))
nukiBlePref.getBytes("secretKeyK", secretKeyK, 32); {
nukiBlePref.getBytes("authorizationId", authorizationId, 4); const PsychicWebParameter* p = request->getParam("pairing");
nukiBlePref.getBytes("securityPinCode", &storedPincode, 2); if(p->value() == "1")
nukiBlePref.end(); {
pairing = true;
}
}
DebugPreferences debugPreferences;
const std::vector<char*> keysPrefs = debugPreferences.getPreferencesKeys();
const std::vector<char*> boolPrefs = debugPreferences.getPreferencesBoolKeys();
const std::vector<char*> redactedPrefs = debugPreferences.getPreferencesRedactedKeys();
const std::vector<char*> bytePrefs = debugPreferences.getPreferencesByteKeys();
for(const auto& key : keysPrefs)
{
if(strcmp(key, preference_show_secrets) == 0)
{
continue;
}
if(strcmp(key, preference_latest_version) == 0)
{
continue;
}
if(!redacted) if(std::find(redactedPrefs.begin(), redactedPrefs.end(), key) != redactedPrefs.end())
{
continue;
}
if(!_preferences->isKey(key))
{
json[key] = "";
}
else if(std::find(boolPrefs.begin(), boolPrefs.end(), key) != boolPrefs.end())
{
json[key] = _preferences->getBool(key) ? "1" : "0";
}
else
{
switch(_preferences->getType(key))
{
case PT_I8:
json[key] = String(_preferences->getChar(key));
break;
case PT_I16:
json[key] = String(_preferences->getShort(key));
break;
case PT_I32:
json[key] = String(_preferences->getInt(key));
break;
case PT_I64:
json[key] = String(_preferences->getLong64(key));
break;
case PT_U8:
json[key] = String(_preferences->getUChar(key));
break;
case PT_U16:
json[key] = String(_preferences->getUShort(key));
break;
case PT_U32:
json[key] = String(_preferences->getUInt(key));
break;
case PT_U64:
json[key] = String(_preferences->getULong64(key));
break;
case PT_STR:
json[key] = _preferences->getString(key);
break;
default:
json[key] = _preferences->getString(key);
break;
}
}
}
if(pairing)
{
if(_nuki != nullptr)
{
unsigned char currentBleAddress[6];
unsigned char authorizationId[4] = {0x00};
unsigned char secretKeyK[32] = {0x00};
uint16_t storedPincode = 0000;
Preferences nukiBlePref;
nukiBlePref.begin("NukiHub", false);
nukiBlePref.getBytes("bleAddress", currentBleAddress, 6);
nukiBlePref.getBytes("secretKeyK", secretKeyK, 32);
nukiBlePref.getBytes("authorizationId", authorizationId, 4);
nukiBlePref.getBytes("securityPinCode", &storedPincode, 2);
nukiBlePref.end();
char text[255];
text[0] = '\0';
for(int i = 0 ; i < 6 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", currentBleAddress[i]);
}
json["bleAddressLock"] = text;
memset(text, 0, sizeof(text));
text[0] = '\0';
for(int i = 0 ; i < 32 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", secretKeyK[i]);
}
json["secretKeyKLock"] = text;
memset(text, 0, sizeof(text));
text[0] = '\0';
for(int i = 0 ; i < 4 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", authorizationId[i]);
}
json["authorizationIdLock"] = text;
memset(text, 0, sizeof(text));
json["securityPinCodeLock"] = storedPincode;
}
if(_nukiOpener != nullptr)
{
unsigned char currentBleAddressOpn[6];
unsigned char authorizationIdOpn[4] = {0x00};
unsigned char secretKeyKOpn[32] = {0x00};
uint16_t storedPincodeOpn = 0000;
Preferences nukiBlePref;
nukiBlePref.begin("NukiHubopener", false);
nukiBlePref.getBytes("bleAddress", currentBleAddressOpn, 6);
nukiBlePref.getBytes("secretKeyK", secretKeyKOpn, 32);
nukiBlePref.getBytes("authorizationId", authorizationIdOpn, 4);
nukiBlePref.getBytes("securityPinCode", &storedPincodeOpn, 2);
nukiBlePref.end();
char text[255];
text[0] = '\0';
for(int i = 0 ; i < 6 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", currentBleAddressOpn[i]);
}
json["bleAddressOpener"] = text;
memset(text, 0, sizeof(text));
text[0] = '\0';
for(int i = 0 ; i < 32 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", secretKeyKOpn[i]);
}
json["secretKeyKOpener"] = text;
memset(text, 0, sizeof(text));
text[0] = '\0';
for(int i = 0 ; i < 4 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", authorizationIdOpn[i]);
}
json["authorizationIdOpener"] = text;
memset(text, 0, sizeof(text));
json["securityPinCodeOpener"] = storedPincodeOpn;
}
}
for(const auto& key : bytePrefs)
{
size_t storedLength = _preferences->getBytesLength(key);
if(storedLength == 0)
{
continue;
}
uint8_t serialized[storedLength];
memset(serialized, 0, sizeof(serialized));
size_t size = _preferences->getBytes(key, serialized, sizeof(serialized));
if(size == 0)
{
continue;
}
char text[255]; char text[255];
text[0] = '\0'; text[0] = '\0';
for(int i = 0 ; i < 6 ; i++) for(int i = 0 ; i < size ; i++)
{ {
size_t offset = strlen(text); size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", currentBleAddress[i]); sprintf(&(text[offset]), "%02x", serialized[i]);
} }
json["bleAddressLock"] = text; json[key] = text;
memset(text, 0, sizeof(text)); memset(text, 0, sizeof(text));
text[0] = '\0';
for(int i = 0 ; i < 32 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", secretKeyK[i]);
}
json["secretKeyKLock"] = text;
memset(text, 0, sizeof(text));
text[0] = '\0';
for(int i = 0 ; i < 4 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", authorizationId[i]);
}
json["authorizationIdLock"] = text;
memset(text, 0, sizeof(text));
json["securityPinCodeLock"] = storedPincode;
} }
if(_nukiOpener != nullptr)
{
unsigned char currentBleAddressOpn[6];
unsigned char authorizationIdOpn[4] = {0x00};
unsigned char secretKeyKOpn[32] = {0x00};
uint16_t storedPincodeOpn = 0000;
Preferences nukiBlePref;
nukiBlePref.begin("NukiHubopener", false);
nukiBlePref.getBytes("bleAddress", currentBleAddressOpn, 6);
nukiBlePref.getBytes("secretKeyK", secretKeyKOpn, 32);
nukiBlePref.getBytes("authorizationId", authorizationIdOpn, 4);
nukiBlePref.getBytes("securityPinCode", &storedPincodeOpn, 2);
nukiBlePref.end();
char text[255];
text[0] = '\0';
for(int i = 0 ; i < 6 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", currentBleAddressOpn[i]);
}
json["bleAddressOpener"] = text;
memset(text, 0, sizeof(text));
text[0] = '\0';
for(int i = 0 ; i < 32 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", secretKeyKOpn[i]);
}
json["secretKeyKOpener"] = text;
memset(text, 0, sizeof(text));
text[0] = '\0';
for(int i = 0 ; i < 4 ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", authorizationIdOpn[i]);
}
json["authorizationIdOpener"] = text;
memset(text, 0, sizeof(text));
json["securityPinCodeOpener"] = storedPincodeOpn;
}
}
for(const auto& key : bytePrefs)
{
size_t storedLength = _preferences->getBytesLength(key);
if(storedLength == 0)
{
continue;
}
uint8_t serialized[storedLength];
memset(serialized, 0, sizeof(serialized));
size_t size = _preferences->getBytes(key, serialized, sizeof(serialized));
if(size == 0)
{
continue;
}
char text[255];
text[0] = '\0';
for(int i = 0 ; i < size ; i++)
{
size_t offset = strlen(text);
sprintf(&(text[offset]), "%02x", serialized[i]);
}
json[key] = text;
memset(text, 0, sizeof(text));
} }
serializeJsonPretty(json, jsonPretty); serializeJsonPretty(json, jsonPretty);
char buf[26 + name.length()];
snprintf(buf, sizeof(buf), "attachment; filename=\"%s\"", name.c_str());
resp->addHeader("Content-Disposition", buf);
resp->setCode(200); resp->setCode(200);
resp->setContentType("application/json"); resp->setContentType("application/json");
resp->setContent(jsonPretty.c_str()); resp->setContent(jsonPretty.c_str());
@@ -1580,9 +1699,32 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
} }
else if(key == "MQTTCA") else if(key == "MQTTCA")
{ {
if(_preferences->getString(preference_mqtt_ca, "") != value) if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{ {
_preferences->putString(preference_mqtt_ca, value); if(value != "")
{
File file = SPIFFS.open("/mqtt_ssl.ca", FILE_WRITE);
if (!file) {
Log->println("Failed to open /mqtt_ssl.ca for writing");
}
else
{
if (!file.print(value))
{
Log->println("Failed to write /mqtt_ssl.ca");
}
file.close();
}
}
else
{
if (!SPIFFS.remove("/mqtt_ssl.ca")) {
Serial.println("Failed to delete /mqtt_ssl.ca");
}
}
Log->print(F("Setting changed: ")); Log->print(F("Setting changed: "));
Log->println(key); Log->println(key);
configChanged = true; configChanged = true;
@@ -1590,9 +1732,32 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
} }
else if(key == "MQTTCRT") else if(key == "MQTTCRT")
{ {
if(_preferences->getString(preference_mqtt_crt, "") != value) if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{ {
_preferences->putString(preference_mqtt_crt, value); if(value != "")
{
File file = SPIFFS.open("/mqtt_ssl.crt", FILE_WRITE);
if (!file) {
Log->println("Failed to open /mqtt_ssl.crt for writing");
}
else
{
if (!file.print(value))
{
Log->println("Failed to write /mqtt_ssl.crt");
}
file.close();
}
}
else
{
if (!SPIFFS.remove("/mqtt_ssl.crt")) {
Serial.println("Failed to delete /mqtt_ssl.crt");
}
}
Log->print(F("Setting changed: ")); Log->print(F("Setting changed: "));
Log->println(key); Log->println(key);
configChanged = true; configChanged = true;
@@ -1600,9 +1765,32 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
} }
else if(key == "MQTTKEY") else if(key == "MQTTKEY")
{ {
if(_preferences->getString(preference_mqtt_key, "") != value) if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{ {
_preferences->putString(preference_mqtt_key, value); if(value != "")
{
File file = SPIFFS.open("/mqtt_ssl.key", FILE_WRITE);
if (!file) {
Log->println("Failed to open /mqtt_ssl.key for writing");
}
else
{
if (!file.print(value))
{
Log->println("Failed to write /mqtt_ssl.key");
}
file.close();
}
}
else
{
if (!SPIFFS.remove("/mqtt_ssl.key")) {
Serial.println("Failed to delete /mqtt_ssl.key");
}
}
Log->print(F("Setting changed: ")); Log->print(F("Setting changed: "));
Log->println(key); Log->println(key);
configChanged = true; configChanged = true;
@@ -1959,6 +2147,16 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
configChanged = true; configChanged = true;
} }
} }
else if(key == "MQTTSENA")
{
if(_preferences->getBool(preference_mqtt_ssl_enabled, false) != (value == "1"))
{
_preferences->putBool(preference_mqtt_ssl_enabled, (value == "1"));
Log->print(F("Setting changed: "));
Log->println(key);
configChanged = true;
}
}
else if(key == "WEBLOG") else if(key == "WEBLOG")
{ {
if(_preferences->getBool(preference_webserial_enabled, false) != (value == "1")) if(_preferences->getBool(preference_webserial_enabled, false) != (value == "1"))
@@ -3541,6 +3739,13 @@ esp_err_t WebCfgServer::buildImportExportHtml(PsychicRequest *request, PsychicRe
response.print("<button title=\"Basic export\" onclick=\" window.open('/get?page=export', '_self'); return false;\">Basic export</button>"); response.print("<button title=\"Basic export\" onclick=\" window.open('/get?page=export', '_self'); return false;\">Basic export</button>");
response.print("<br><br><button title=\"Export with redacted settings\" onclick=\" window.open('/get?page=export&redacted=1'); return false;\">Export with redacted settings</button>"); response.print("<br><br><button title=\"Export with redacted settings\" onclick=\" window.open('/get?page=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('/get?page=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('/get?page=export&redacted=1&pairing=1'); return false;\">Export with redacted settings and pairing data</button>");
#ifdef CONFIG_SOC_SPIRAM_SUPPORTED
if(esp_psram_get_size() > 0)
{
response.print("<br><br><button title=\"Export HTTP SSL certificate and key\" onclick=\" window.open('/get?page=export&type=https'); return false;\">Export HTTP SSL certificate and key</button>");
}
#endif
response.print("<br><br><button title=\"Export MQTT SSL CA, client certificate and client key\" onclick=\" window.open('/get?page=export&type=mqtts'); return false;\">Export MQTT SSL CA, client certificate and client key</button>");
response.print("</div></body></html>"); response.print("</div></body></html>");
return response.endSend(); return response.endSend();
} }
@@ -3783,7 +3988,7 @@ esp_err_t WebCfgServer::buildNetworkConfigHtml(PsychicRequest *request, PsychicR
if(esp_psram_get_size() > 0) if(esp_psram_get_size() > 0)
{ {
response.print("<tr><td>Set HTTP SSL Certificate</td><td><button title=\"Set HTTP SSL Certificate\" onclick=\" window.open('/get?page=httpcrtconfig', '_self'); return false;\">Change</button></td></tr>"); response.print("<tr><td>Set HTTP SSL Certificate</td><td><button title=\"Set HTTP SSL Certificate\" onclick=\" window.open('/get?page=httpcrtconfig', '_self'); return false;\">Change</button></td></tr>");
response.print("<tr><td>Set HTTP SSL Key</td><td><button title=\"Set MQTT SSL Key\" onclick=\" window.open('/get?page=httpkeyconfig', '_self'); return false;\">Change</button></td></tr>"); response.print("<tr><td>Set HTTP SSL Key</td><td><button title=\"Set HTTP SSL Key\" onclick=\" window.open('/get?page=httpkeyconfig', '_self'); return false;\">Change</button></td></tr>");
} }
#endif #endif
response.print("</table>"); response.print("</table>");
@@ -3826,6 +4031,7 @@ esp_err_t WebCfgServer::buildMqttConfigHtml(PsychicRequest *request, PsychicResp
{ {
printCheckBox(&response, "OPENERCONT", "Set Nuki Opener Lock/Unlock action in Home Assistant to Continuous mode", _preferences->getBool(preference_opener_continuous_mode), ""); printCheckBox(&response, "OPENERCONT", "Set Nuki Opener Lock/Unlock action in Home Assistant to Continuous mode", _preferences->getBool(preference_opener_continuous_mode), "");
} }
printCheckBox(&response, "MQTTSENA", "Enable MQTT SSL", _preferences->getBool(preference_mqtt_ssl_enabled, false), "");
response.print("<tr><td>Set MQTT SSL CA Certificate</td><td><button title=\"Set MQTT SSL CA Certificate\" onclick=\" window.open('/get?page=mqttcaconfig', '_self'); return false;\">Change</button></td></tr>"); response.print("<tr><td>Set MQTT SSL CA Certificate</td><td><button title=\"Set MQTT SSL CA Certificate\" onclick=\" window.open('/get?page=mqttcaconfig', '_self'); return false;\">Change</button></td></tr>");
response.print("<tr><td>Set MQTT SSL Client Certificate</td><td><button title=\"Set MQTT Client Certificate\" onclick=\" window.open('/get?page=mqttcrtconfig', '_self'); return false;\">Change</button></td></tr>"); response.print("<tr><td>Set MQTT SSL Client Certificate</td><td><button title=\"Set MQTT Client Certificate\" onclick=\" window.open('/get?page=mqttcrtconfig', '_self'); return false;\">Change</button></td></tr>");
response.print("<tr><td>Set MQTT SSL Client Key</td><td><button title=\"Set MQTT SSL Client Key\" onclick=\" window.open('/get?page=mqttkeyconfig', '_self'); return false;\">Change</button></td></tr>"); response.print("<tr><td>Set MQTT SSL Client Key</td><td><button title=\"Set MQTT SSL Client Key\" onclick=\" window.open('/get?page=mqttkeyconfig', '_self'); return false;\">Change</button></td></tr>");
@@ -3860,15 +4066,102 @@ esp_err_t WebCfgServer::buildMqttSSLConfigHtml(PsychicRequest *request, PsychicR
if (type == 0) if (type == 0)
{ {
printTextarea(&response, "MQTTCA", "MQTT SSL CA Certificate (*, optional)", _preferences->getString(preference_mqtt_ca).c_str(), TLS_CA_MAX_SIZE, true, true); bool found = false;
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/mqtt_ssl.ca");
if (!file || file.isDirectory()) {
Log->println("mqtt_ssl.ca not found");
}
else
{
Log->println("Reading mqtt_ssl.ca");
size_t filesize = file.size();
char ca[filesize + 1];
file.read((uint8_t *)ca, sizeof(ca));
file.close();
ca[filesize] = '\0';
printTextarea(&response, "MQTTCA", "MQTT SSL CA Certificate (*, optional)", ca, 2200, true, true);
found = true;
}
}
if (!found)
{
printTextarea(&response, "MQTTCA", "MQTT SSL CA Certificate (*, optional)", "", 2200, true, true);
}
} }
else if (type == 1) else if (type == 1)
{ {
printTextarea(&response, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", _preferences->getString(preference_mqtt_crt).c_str(), TLS_CERT_MAX_SIZE, true, true); bool found = false;
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/mqtt_ssl.crt");
if (!file || file.isDirectory()) {
Log->println("mqtt_ssl.crt not found");
}
else
{
Log->println("Reading mqtt_ssl.crt");
size_t filesize = file.size();
char cert[filesize + 1];
file.read((uint8_t *)cert, sizeof(cert));
file.close();
cert[filesize] = '\0';
printTextarea(&response, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", cert, 2200, true, true);
found = true;
}
}
if (!found)
{
printTextarea(&response, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", "", 2200, true, true);
}
} }
else else
{ {
printTextarea(&response, "MQTTKEY", "MQTT SSL Client Key (*, optional)", _preferences->getString(preference_mqtt_key).c_str(), TLS_KEY_MAX_SIZE, true, true); bool found = false;
if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed");
}
else
{
File file = SPIFFS.open("/mqtt_ssl.key");
if (!file || file.isDirectory()) {
Log->println("mqtt_ssl.key not found");
}
else
{
Log->println("Reading mqtt_ssl.key");
size_t filesize = file.size();
char key[filesize + 1];
file.read((uint8_t *)key, sizeof(key));
file.close();
key[filesize] = '\0';
printTextarea(&response, "MQTTKEY", "MQTT SSL Client Key (*, optional)", key, 2200, true, true);
found = true;
}
}
if (!found)
{
printTextarea(&response, "MQTTKEY", "MQTT SSL Client Key (*, optional)", "", 2200, true, true);
}
} }
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\">");
@@ -3891,8 +4184,8 @@ esp_err_t WebCfgServer::buildHttpSSLConfigHtml(PsychicRequest *request, PsychicR
if (type == 1) if (type == 1)
{ {
char cert[4400] = {0}; bool found = false;
#ifdef CONFIG_SOC_SPIRAM_SUPPORTED #ifdef CONFIG_SOC_SPIRAM_SUPPORTED
if (!SPIFFS.begin(true)) { if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed"); Log->println("SPIFFS Mount Failed");
@@ -3906,21 +4199,27 @@ esp_err_t WebCfgServer::buildHttpSSLConfigHtml(PsychicRequest *request, PsychicR
else else
{ {
Log->println("Reading http_ssl.crt"); Log->println("Reading http_ssl.crt");
uint32_t i = 0; size_t filesize = file.size();
while(file.available()){ char cert[filesize + 1];
cert[i] = file.read();
i++; file.read((uint8_t *)cert, sizeof(cert));
}
file.close(); file.close();
cert[filesize] = '\0';
printTextarea(&response, "HTTPCRT", "HTTP SSL Certificate (*, optional)", cert, 4400, true, true);
found = true;
} }
} }
#endif #endif
printTextarea(&response, "HTTPCRT", "HTTP SSL Certificate (*, optional)", cert, 4400, true, true); if (!found)
{
printTextarea(&response, "HTTPCRT", "HTTP SSL Certificate (*, optional)", "", 4400, true, true);
}
} }
else else
{ {
char key[2200] = {0}; bool found = false;
#ifdef CONFIG_SOC_SPIRAM_SUPPORTED #ifdef CONFIG_SOC_SPIRAM_SUPPORTED
if (!SPIFFS.begin(true)) { if (!SPIFFS.begin(true)) {
Log->println("SPIFFS Mount Failed"); Log->println("SPIFFS Mount Failed");
@@ -3934,16 +4233,22 @@ esp_err_t WebCfgServer::buildHttpSSLConfigHtml(PsychicRequest *request, PsychicR
else else
{ {
Log->println("Reading http_ssl.key"); Log->println("Reading http_ssl.key");
uint32_t i = 0; size_t filesize = file.size();
while(file.available()){ char key[filesize + 1];
key[i] = file.read();
i++; file.read((uint8_t *)key, sizeof(key));
}
file.close(); file.close();
key[filesize] = '\0';
printTextarea(&response, "HTTPKEY", "HTTP SSL Key (*, optional)", key, 2200, true, true);
found = true;
} }
} }
#endif #endif
printTextarea(&response, "HTTPKEY", "HTTP SSL Key (*, optional)", key, 2200, true, true); if (!found)
{
printTextarea(&response, "HTTPKEY", "HTTP SSL Key (*, optional)", "", 2200, true, true);
}
} }
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\">");
@@ -4528,10 +4833,29 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse*
response.print(_preferences->getString(preference_cred_password, "").length() > 0 ? "***" : "Not set"); response.print(_preferences->getString(preference_cred_password, "").length() > 0 ? "***" : "Not set");
response.print("\nWeb configurator enabled: "); response.print("\nWeb configurator enabled: ");
response.print(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No"); response.print(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No");
//response.print("\nHTTP SSL CRT: "); response.print("\nHTTP SSL: ");
//response.print(_preferences->getString(preference_http_crt, "").length() > 0 ? "***" : "Not set"); #ifdef CONFIG_SOC_SPIRAM_SUPPORTED
//response.print("\nHTTP SSL Key: "); if(esp_psram_get_size() > 0)
//response.print(_preferences->getString(preference_http_key, "").length() > 0 ? "***" : "Not set"); {
if (!SPIFFS.begin(true)) {
response.print("Disabled");
}
else
{
File file = SPIFFS.open("/http_ssl.crt");
File file2 = SPIFFS.open("/http_ssl.key");
response.print((!file || file.isDirectory() || !file2 || file2.isDirectory()) ? "Disabled" : "Enabled");
file.close();
file2.close();
}
}
else
{
response.print("Disabled");
}
#else
response.print("Disabled");
#endif
response.print("\nPublish debug information enabled: "); response.print("\nPublish debug information enabled: ");
response.print(_preferences->getBool(preference_publish_debug_info, false) ? "Yes" : "No"); response.print(_preferences->getBool(preference_publish_debug_info, false) ? "Yes" : "No");
response.print("\nMQTT log enabled: "); response.print("\nMQTT log enabled: ");
@@ -4628,12 +4952,38 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse*
response.print(_preferences->getString(preference_mqtt_password, "").length() > 0 ? "***" : "Not set"); response.print(_preferences->getString(preference_mqtt_password, "").length() > 0 ? "***" : "Not set");
response.print("\nMQTT base topic: "); response.print("\nMQTT base topic: ");
response.print(_preferences->getString(preference_mqtt_lock_path, "")); response.print(_preferences->getString(preference_mqtt_lock_path, ""));
response.print("\nMQTT SSL CA: "); response.print("\nMQTT SSL: ");
response.print(_preferences->getString(preference_mqtt_ca, "").length() > 0 ? "***" : "Not set"); if(_preferences->getBool(preference_mqtt_ssl_enabled, false))
response.print("\nMQTT SSL CRT: "); {
response.print(_preferences->getString(preference_mqtt_crt, "").length() > 0 ? "***" : "Not set"); if (!SPIFFS.begin(true)) {
response.print("\nMQTT SSL Key: "); response.print("Disabled");
response.print(_preferences->getString(preference_mqtt_key, "").length() > 0 ? "***" : "Not set"); }
else
{
File file = SPIFFS.open("/mqtt_ssl.ca");
if (!file || file.isDirectory()) {
response.print("Disabled");
}
else
{
response.print("Enabled");
response.print("\nMQTT SSL CA: ***");
File file2 = SPIFFS.open("/mqtt_ssl.crt");
File file3 = SPIFFS.open("/mqtt_ssl.key");
response.print("\nMQTT SSL CRT: ");
response.print((!file2 || file2.isDirectory()) ? "Not set" : "***");
response.print("\nMQTT SSL Key: ");
response.print((!file3 || file3.isDirectory()) ? "Not set" : "***");
file2.close();
file3.close();
}
file.close();
}
}
else
{
response.print("Disabled");
}
response.print("\n\n------------ BLUETOOTH ------------"); response.print("\n\n------------ BLUETOOTH ------------");
response.print("\nBluetooth TX power (dB): "); response.print("\nBluetooth TX power (dB): ");
response.print(_preferences->getInt(preference_ble_tx_power, 9)); response.print(_preferences->getInt(preference_ble_tx_power, 9));

View File

@@ -249,6 +249,19 @@ void networkTask(void *pvParameters)
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
void nukiTask(void *pvParameters) void nukiTask(void *pvParameters)
{ {
if (preferences->getBool(preference_mqtt_ssl_enabled, false))
{
#ifdef CONFIG_SOC_SPIRAM_SUPPORTED
if (esp_psram_get_size() <= 0)
{
Log->println("Waiting 20 seconds to start BLE because of MQTT SSL");
delay(20000);
}
#else
Log->println("Waiting 20 seconds to start BLE because of MQTT SSL");
delay(20000);
#endif
}
int64_t nukiLoopTs = 0; int64_t nukiLoopTs = 0;
bool whiteListed = false; bool whiteListed = false;
while(true) while(true)
@@ -572,15 +585,13 @@ void setup()
} }
else else
{ {
char cert[4400] = {0};
Log->println("Reading http_ssl.crt"); Log->println("Reading http_ssl.crt");
uint32_t i = 0; size_t filesize = file.size();
while(file.available()){ char cert[filesize + 1];
cert[i] = file.read();
i++; file.read((uint8_t *)cert, sizeof(cert));
}
file.close(); file.close();
cert[filesize] = '\0';
File file2 = SPIFFS.open("/http_ssl.key"); File file2 = SPIFFS.open("/http_ssl.key");
if (!file2 || file2.isDirectory()) { if (!file2 || file2.isDirectory()) {
@@ -589,15 +600,13 @@ void setup()
} }
else else
{ {
char key[2200] = {0};
Log->println("Reading http_ssl.key"); Log->println("Reading http_ssl.key");
i = 0; size_t filesize2 = file2.size();
while(file2.available()){ char key[filesize2 + 1];
key[i] = file2.read();
i++; file2.read((uint8_t *)key, sizeof(key));
}
file2.close(); file2.close();
key[filesize2] = '\0';
psychicSSLServer = new PsychicHttpsServer; psychicSSLServer = new PsychicHttpsServer;
psychicSSLServer->ssl_config.httpd.max_open_sockets = 8; psychicSSLServer->ssl_config.httpd.max_open_sockets = 8;
@@ -738,15 +747,13 @@ void setup()
} }
else else
{ {
char cert[4400] = {0};
Log->println("Reading http_ssl.crt"); Log->println("Reading http_ssl.crt");
uint32_t i = 0; size_t filesize = file.size();
while(file.available()){ char cert[filesize + 1];
cert[i] = file.read();
i++; file.read((uint8_t *)cert, sizeof(cert));
}
file.close(); file.close();
cert[filesize] = '\0';
File file2 = SPIFFS.open("/http_ssl.key"); File file2 = SPIFFS.open("/http_ssl.key");
if (!file2 || file2.isDirectory()) { if (!file2 || file2.isDirectory()) {
@@ -755,15 +762,13 @@ void setup()
} }
else else
{ {
char key[2200] = {0};
Log->println("Reading http_ssl.key"); Log->println("Reading http_ssl.key");
i = 0; size_t filesize2 = file2.size();
while(file2.available()){ char key[filesize2 + 1];
key[i] = file2.read();
i++; file2.read((uint8_t *)key, sizeof(key));
}
file2.close(); file2.close();
key[filesize2] = '\0';
psychicSSLServer = new PsychicHttpsServer; psychicSSLServer = new PsychicHttpsServer;
psychicSSLServer->ssl_config.httpd.max_open_sockets = 8; psychicSSLServer->ssl_config.httpd.max_open_sockets = 8;

View File

@@ -3,30 +3,71 @@
#include "../Logger.h" #include "../Logger.h"
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
#include "FS.h"
#include "SPIFFS.h"
#include "../MqttTopics.h" #include "../MqttTopics.h"
#include "PreferencesKeys.h" #include "PreferencesKeys.h"
void NetworkDevice::init() void NetworkDevice::init()
{ {
size_t caLength = _preferences->getString(preference_mqtt_ca, _ca, TLS_CA_MAX_SIZE); if(_preferences->getBool(preference_mqtt_ssl_enabled, false)) {
size_t crtLength = _preferences->getString(preference_mqtt_crt, _cert, TLS_CERT_MAX_SIZE); if (!SPIFFS.begin(true)) {
size_t keyLength = _preferences->getString(preference_mqtt_key, _key, TLS_KEY_MAX_SIZE); Log->println("SPIFFS Mount Failed");
}
_useEncryption = caLength > 1; // length is 1 when empty else
if(_useEncryption)
{
Log->println(F("MQTT over TLS."));
_mqttClientSecure = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
_mqttClientSecure->setCACert(_ca);
if(crtLength > 1 && keyLength > 1) // length is 1 when empty
{ {
Log->println(F("MQTT with client certificate.")); File file = SPIFFS.open("/mqtt_ssl.ca");
_mqttClientSecure->setCertificate(_cert); if (!file || file.isDirectory()) {
_mqttClientSecure->setPrivateKey(_key); Log->println("mqtt_ssl.ca not found");
}
else
{
Log->println("Reading mqtt_ssl.ca");
String ca_cert = file.readString();
file.close();
char* caDest;
caDest = (char *)malloc(sizeof(char) * (ca_cert.length()+1));
strcpy(caDest, ca_cert.c_str());
if(ca_cert.length() > 1)
{
_useEncryption = true;
Log->println(F("MQTT over TLS."));
_mqttClientSecure = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
_mqttClientSecure->setCACert(caDest);
File file2 = SPIFFS.open("/mqtt_ssl.crt");
File file3 = SPIFFS.open("/mqtt_ssl.key");
if (!file2 || file2.isDirectory() || !file3 || file3.isDirectory()) {
Log->println("mqtt_ssl.crt or mqtt_ssl.key not found");
}
else
{
String cert = file2.readString();
file2.close();
char* certDest;
certDest = (char *)malloc(sizeof(char) * (cert.length()+1));
strcpy(certDest, cert.c_str());
String key = file3.readString();
file3.close();
char* keyDest;
keyDest = (char *)malloc(sizeof(char) * (key.length()+1));
strcpy(keyDest, key.c_str());
if(cert.length() > 1 && key.length() > 1)
{
Log->println(F("MQTT with client certificate."));
_mqttClientSecure->setCertificate(certDest);
_mqttClientSecure->setPrivateKey(keyDest);
}
}
}
}
} }
} }
else
if (!_useEncryption)
{ {
Log->println(F("MQTT without TLS.")); Log->println(F("MQTT without TLS."));
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO); _mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO);

View File

@@ -64,9 +64,6 @@ protected:
bool _useEncryption = false; bool _useEncryption = false;
bool _mqttEnabled = true; bool _mqttEnabled = true;
char* _path; char* _path;
char _ca[TLS_CA_MAX_SIZE] = {0};
char _cert[TLS_CERT_MAX_SIZE] = {0};
char _key[TLS_KEY_MAX_SIZE] = {0};
#endif #endif
const String _hostname; const String _hostname;