Skip to content

Commit

Permalink
Merge pull request #621 from iranl/various
Browse files Browse the repository at this point in the history
Add info page to main menu, improve CSS, allow BLE transmit power up to 20db, conform to HA guidelines for device config sharing
  • Loading branch information
iranl authored Jan 28, 2025
2 parents 78d3817 + bec28b4 commit 6e45dab
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ In a browser navigate to the IP address assigned to the ESP32.
- Lock: Nuki Bridge is running alongside Nuki Hub: Enable to allow Nuki Hub to co-exist with a Nuki Bridge by registering Nuki Hub as an (smartphone) app instead of a bridge. Changing this setting will require re-pairing. Enabling this setting is strongly discouraged as described in the "[Pairing with a Nuki Lock or Opener](#pairing-with-a-nuki-lock-or-opener)" section of this README, ***unless when used in [Hybrid mode](/HYBRID.md) (Official MQTT / Nuki Hub co-existance)***
- Opener: Nuki Bridge is running alongside Nuki Hub: Enable to allow Nuki Hub to co-exist with a Nuki Bridge by registering Nuki Hub as an (smartphone) app instead of a bridge. Changing this setting will require re-pairing. Enabling this setting is strongly discouraged as described in the "[Pairing with a Nuki Lock or Opener](#pairing-with-a-nuki-lock-or-opener)" section of this README
- Restart if bluetooth beacons not received: Set to a positive integer to restart the Nuki Hub after the set amount of seconds has passed without receiving a bluetooth beacon from the Nuki device, set to -1 to disable, default 60. Because the bluetooth stack of the ESP32 can silently fail it is not recommended to disable this setting.
- BLE transmit power in dB: Set to a integer between -12 and 9 to set the Bluetooth transmit power, default 9.
- BLE transmit power in dB: Set to a integer between -12 and 9 (ESP32) or -12 and 20 (All newer ESP32 variants) to set the Bluetooth transmit power, default 9.
- Update Nuki Hub and Lock/Opener time using NTP: Enable to update the ESP32 time and Nuki Lock and/or Nuki Opener time every 12 hours using a NTP time server. Updating the Nuki device time requires the Nuki security code / PIN to be set, see "[Nuki Lock PIN / Nuki Opener PIN](#nuki-lock-pin--nuki-opener-pin)" below.
- NTP server: Set to the NTP server you want to use, defaults to "pool.ntp.org". If DHCP is used and NTP servers are provided using DHCP these will take precedence over the specified NTP server.

Expand Down
2 changes: 1 addition & 1 deletion src/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#define NUKI_HUB_VERSION "9.09"
#define NUKI_HUB_VERSION_INT (uint32_t)909
#define NUKI_HUB_BUILD "unknownbuildnr"
#define NUKI_HUB_DATE "2025-01-23"
#define NUKI_HUB_DATE "2025-01-28"

#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"
Expand Down
3 changes: 3 additions & 0 deletions src/HomeAssistantDiscovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ void HomeAssistantDiscovery::publishHASSNukiHubConfig()
json["dev"]["name"] = _hostname.c_str();
json["dev"]["sw"] = NUKI_HUB_VERSION;
json["dev"]["hw"] = NUKI_HUB_HW;
_uidToName[_nukiHubUidString] = _hostname.c_str();

String cuUrl = _preferences->getString(preference_mqtt_hass_cu_url, "");

Expand Down Expand Up @@ -502,6 +503,7 @@ void HomeAssistantDiscovery::publishHASSDeviceConfig(char* deviceType, const cha
json["dev"]["sw"] = softwareVersion;
json["dev"]["hw"] = hardwareVersion;
json["dev"]["via_device"] = String("nuki_") + _nukiHubUidString;
_uidToName[uidString] = name;

String cuUrl = _preferences->getString(preference_mqtt_hass_cu_url, "");

Expand Down Expand Up @@ -3175,6 +3177,7 @@ JsonDocument HomeAssistantDiscovery::createHassJson(const String& uidString,
}

json["avty"][0]["t"] = _baseTopic + mqtt_topic_mqtt_connection_state;
json["dev"]["name"] = _uidToName[uidString];

if (displayName != "Query lock state" && uidString.length() < 10)
{
Expand Down
1 change: 1 addition & 0 deletions src/HomeAssistantDiscovery.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class HomeAssistantDiscovery
String _baseTopic;
String _hostname;

JsonDocument _uidToName;
char _nukiHubUidString[20];

bool _offEnabled = false;
Expand Down
23 changes: 23 additions & 0 deletions src/NukiOpenerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,30 @@ void NukiOpenerWrapper::readSettings()

if(pwrLvl >= 9)
{
#if defined(CONFIG_IDF_TARGET_ESP32)
powerLevel = ESP_PWR_LVL_P9;
#else
if(pwrLvl >= 20)
{
powerLevel = ESP_PWR_LVL_P20;
}
else if(pwrLvl >= 18)
{
powerLevel = ESP_PWR_LVL_P18;
}
else if(pwrLvl >= 15)
{
powerLevel = ESP_PWR_LVL_P15;
}
else if(pwrLvl >= 12)
{
powerLevel = ESP_PWR_LVL_P12;
}
else
{
powerLevel = ESP_PWR_LVL_P9;
}
#endif
}
else if(pwrLvl >= 6)
{
Expand Down
23 changes: 23 additions & 0 deletions src/NukiWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,30 @@ void NukiWrapper::readSettings()

if(pwrLvl >= 9)
{
#if defined(CONFIG_IDF_TARGET_ESP32)
powerLevel = ESP_PWR_LVL_P9;
#else
if(pwrLvl >= 20)
{
powerLevel = ESP_PWR_LVL_P20;
}
else if(pwrLvl >= 18)
{
powerLevel = ESP_PWR_LVL_P18;
}
else if(pwrLvl >= 15)
{
powerLevel = ESP_PWR_LVL_P15;
}
else if(pwrLvl >= 12)
{
powerLevel = ESP_PWR_LVL_P12;
}
else
{
powerLevel = ESP_PWR_LVL_P9;
}
#endif
}
else if(pwrLvl >= 6)
{
Expand Down
9 changes: 9 additions & 0 deletions src/WebCfgServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3363,7 +3363,11 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S
}
else if(key == "TXPWR")
{
#if defined(CONFIG_IDF_TARGET_ESP32)
if(value.toInt() >= -12 && value.toInt() <= 9)
#else
if(value.toInt() >= -12 && value.toInt() <= 20)
#endif
{
if(_preferences->getInt(preference_ble_tx_power, 9) != value.toInt())
{
Expand Down Expand Up @@ -5030,6 +5034,7 @@ esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp
buildNavigationMenuEntry(&response, "Configure Wi-Fi", "/get?page=wifi");
}
#endif
buildNavigationMenuEntry(&response, "Info page", "/get?page=info");
String rebooturl = "/get?page=reboot&CONFIRMTOKEN=" + _confirmCode;
buildNavigationMenuEntry(&response, "Reboot Nuki Hub", rebooturl.c_str());
if (_preferences->getInt(preference_http_auth_type, 0) == 2)
Expand Down Expand Up @@ -5847,7 +5852,11 @@ esp_err_t WebCfgServer::buildNukiConfigHtml(PsychicRequest *request, PsychicResp
printCheckBox(&response, "REGAPPOPN", "Opener: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_opener_as_app), "");
}
printInputField(&response, "RSBC", "Restart if bluetooth beacons not received (seconds; -1 to disable)", _preferences->getInt(preference_restart_ble_beacon_lost), 10, "");
#if defined(CONFIG_IDF_TARGET_ESP32)
printInputField(&response, "TXPWR", "BLE transmit power in dB (minimum -12, maximum 9)", _preferences->getInt(preference_ble_tx_power, 9), 10, "");
#else
printInputField(&response, "TXPWR", "BLE transmit power in dB (minimum -12, maximum 20)", _preferences->getInt(preference_ble_tx_power, 9), 10, "");
#endif
printCheckBox(&response, "UPTIME", "Update Nuki Hub and Lock/Opener time using NTP", _preferences->getBool(preference_update_time, false), "");
printInputField(&response, "TIMESRV", "NTP server", _preferences->getString(preference_time_server, "pool.ntp.org").c_str(), 255, "");

Expand Down
2 changes: 1 addition & 1 deletion src/WebCfgServerConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// escaped by https://www.cescaper.com/
// source: https://cdn.jsdelivr.net/npm/@exampledev/[email protected]/new.min.css

const char stylecss[] = ":root{--nc-font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';--nc-font-mono:Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace;--nc-tx-1:#000;--nc-tx-2:#1a1a1a;--nc-bg-1:#fff;--nc-bg-2:#f6f8fa;--nc-bg-3:#e5e7eb;--nc-lk-1:#0070f3;--nc-lk-2:#0366d6;--nc-lk-tx:#fff;--nc-ac-1:#79ffe1;--nc-ac-tx:#0c4047}@media(prefers-color-scheme:dark){:root{--nc-tx-1:#fff;--nc-tx-2:#eee;--nc-bg-1:#000;--nc-bg-2:#111;--nc-bg-3:#222;--nc-lk-1:#3291ff;--nc-lk-2:#0070f3;--nc-lk-tx:#fff;--nc-ac-1:#7928ca;--nc-ac-tx:#fff}}*{margin:0;padding:0}img,input,option,p,table,textarea,ul{margin-bottom:1rem}button,html,input,select{font-family:var(--nc-font-sans)}body{margin:0 auto;max-width:750px;padding:2rem;border-radius:6px;overflow-x:hidden;word-break:normal;overflow-wrap:anywhere;background:var(--nc-bg-1);color:var(--nc-tx-2);font-size:1.03rem;line-height:1.5}::selection{background:var(--nc-ac-1);color:var(--nc-ac-tx)}h1,h2,h3,h4,h5,h6{line-height:1;color:var(--nc-tx-1);padding-top:.875rem}h1,h2,h3{color:var(--nc-tx-1);padding-bottom:2px;margin-bottom:8px;border-bottom:1px solid var(--nc-bg-2)}h4,h5,h6{margin-bottom:.3rem}h1{font-size:2.25rem}h2{font-size:1.85rem}h3{font-size:1.55rem}h4{font-size:1.25rem}h5{font-size:1rem}h6{font-size:.875rem}a{color:var(--nc-lk-1)}a:hover{color:var(--nc-lk-2) !important;}abbr{cursor:help}abbr:hover{cursor:help}a button,button,input[type=button],input[type=reset],input[type=submit]{font-size:1rem;display:inline-block;padding:6px 12px;text-align:center;text-decoration:none;white-space:nowrap;background:var(--nc-lk-1);color:var(--nc-lk-tx);border:0;border-radius:4px;box-sizing:border-box;cursor:pointer;color:var(--nc-lk-tx)}a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5;cursor:not-allowed}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background:var(--nc-lk-2)}table{border-collapse:collapse;width:100%}td,th{border:1px solid var(--nc-bg-3);text-align:left;padding:.5rem}th{background:var(--nc-bg-2)}tr:nth-child(even){background:var(--nc-bg-2)}textarea{max-width:100%}input,select,textarea{padding:6px 12px;margin-bottom:.5rem;background:var(--nc-bg-2);color:var(--nc-tx-2);border:1px solid var(--nc-bg-3);border-radius:4px;box-shadow:none;box-sizing:border-box}img{max-width:100%}td>input{margin-top:0;margin-bottom:0}td>textarea{margin-top:0;margin-bottom:0}td>select{margin-top:0;margin-bottom:0}.warning{color:red}@media only screen and (max-width:600px){.adapt td{display:block}.adapt input[type=text],.adapt input[type=password],.adapt input[type=submit],.adapt textarea,.adapt select{width:100%}.adapt td:has(input[type=checkbox]){text-align:center}.adapt input[type=checkbox]{width:1.5em;height:1.5em}.adapt table td:first-child{border-bottom:0}.adapt table td:last-child{border-top:0}#tblnav a li>span{max-width:140px}}#tblnav a{border:0;border-bottom:1px solid;display:block;font-size:1rem;font-weight:bold;padding:.6rem 0;line-height:1;color:var(--nc-tx-1);text-decoration:none;background:linear-gradient(to left,transparent 50%,rgba(255,255,255,0.4) 50%) right;background-size:200% 100%;transition:all .2s ease}#tblnav a:nth-child(even){background:linear-gradient(to left,var(--nc-bg-2) 50%,rgba(255,255,255,0.4) 50%) right;background-size:200% 100%}#tblnav a:hover{background-position:left;transition:all .45s ease}#tblnav a:active{background:var(--nc-lk-1);transition:all .15s ease}#tblnav a li{list-style:none;padding:.5rem;display:inline-block;width:100%}#tblnav a li>span{float:right;text-align:right;margin-right:10px;color:#f70;font-weight:100;font-style:italic;display:block}.tdbtn{text-align:center;vertical-align:middle}.naventry{float:left;max-width:375px;width:100%}";
const char stylecss[] = ":root{--nc-font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';--nc-font-mono:Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace;--nc-tx-1:#000;--nc-tx-2:#1a1a1a;--nc-bg-1:#fff;--nc-bg-2:#f6f8fa;--nc-bg-3:#e5e7eb;--nc-lk-1:#0070f3;--nc-lk-2:#0366d6;--nc-lk-tx:#fff;--nc-ac-1:#79ffe1;--nc-ac-tx:#0c4047}@media(prefers-color-scheme:dark){:root{--nc-tx-1:#fff;--nc-tx-2:#eee;--nc-bg-1:#000;--nc-bg-2:#111;--nc-bg-3:#222;--nc-lk-1:#3291ff;--nc-lk-2:#0070f3;--nc-lk-tx:#fff;--nc-ac-1:#7928ca;--nc-ac-tx:#fff}}*{margin:0;padding:0}img,input,option,p,table,textarea,ul{margin-bottom:1rem}button,html,input,select{font-family:var(--nc-font-sans)}body{margin:0 auto;max-width:750px;padding:2rem;border-radius:6px;overflow-x:hidden;word-break:normal;overflow-wrap:anywhere;background:var(--nc-bg-1);color:var(--nc-tx-2);font-size:1.03rem;line-height:1.5}::selection{background:var(--nc-ac-1);color:var(--nc-ac-tx)}h1,h2,h3,h4,h5,h6{line-height:1;color:var(--nc-tx-1);padding-top:.875rem}h1,h2,h3{color:var(--nc-tx-1);padding-bottom:2px;margin-bottom:8px;border-bottom:1px solid var(--nc-bg-2)}h4,h5,h6{margin-bottom:.3rem}h1{font-size:2.25rem}h2{font-size:1.85rem}h3{font-size:1.55rem}h4{font-size:1.25rem}h5{font-size:1rem}h6{font-size:.875rem}a{color:var(--nc-lk-1)}a:hover{color:var(--nc-lk-2) !important;}abbr{cursor:help}abbr:hover{cursor:help}a button,button,input[type=button],input[type=reset],input[type=submit]{font-size:1rem;display:inline-block;padding:6px 12px;text-align:center;text-decoration:none;white-space:nowrap;background:var(--nc-lk-1);color:var(--nc-lk-tx);border:0;border-radius:4px;box-sizing:border-box;cursor:pointer;color:var(--nc-lk-tx)}a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5;cursor:not-allowed}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background:var(--nc-lk-2)}table{border-collapse:collapse;width:100%}td,th{border:1px solid var(--nc-bg-3);text-align:left;padding:.5rem}th{background:var(--nc-bg-2)}tr{background:var(--nc-bg-2)}textarea{max-width:100%}input,select,textarea{padding:6px 12px;margin-bottom:.5rem;background:var(--nc-bg-2);color:var(--nc-tx-2);border:1px solid var(--nc-bg-3);border-radius:4px;box-shadow:none;box-sizing:border-box}img{max-width:100%}td>input{margin-top:0;margin-bottom:0}td>textarea{margin-top:0;margin-bottom:0}td>select{margin-top:0;margin-bottom:0}.warning{color:red}@media only screen and (max-width:600px){.adapt td{display:block}.adapt input[type=text],.adapt input[type=password],.adapt input[type=submit],.adapt textarea,.adapt select{width:100%}.adapt td:has(input[type=checkbox]){text-align:center}.adapt input[type=checkbox]{width:1.5em;height:1.5em}.adapt table td:first-child{border-bottom:0}.adapt table td:last-child{border-top:0}#tblnav a li>span{max-width:140px}}#tblnav a{border:0;border-bottom:1px solid;display:block;font-size:1rem;font-weight:bold;padding:.6rem 0;line-height:1;color:var(--nc-tx-1);text-decoration:none;background:linear-gradient(to left,transparent 50%,rgba(255,255,255,0.4) 50%) right;background-size:200% 100%;transition:all .2s ease}#tblnav a{background:linear-gradient(to left,var(--nc-bg-2) 50%,rgba(255,255,255,0.4) 50%) right;background-size:200% 100%}#tblnav a:hover{background-position:left;transition:all .45s ease}#tblnav a:active{background:var(--nc-lk-1);transition:all .15s ease}#tblnav a li{list-style:none;padding:.5rem;display:inline-block;width:100%}#tblnav a li>span{float:right;text-align:right;margin-right:10px;color:#f70;font-weight:100;font-style:italic;display:block}.tdbtn{text-align:center;vertical-align:middle}.naventry{float:left;max-width:375px;width:100%}";

// converted to char array by https://notisrac.github.io/FileToCArray/
const uint8_t favicon_32x32[] = {
Expand Down

0 comments on commit 6e45dab

Please sign in to comment.