Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webcfg: add header and meta refresh for main page #379

Merged
merged 4 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 119 additions & 55 deletions src/WebCfgServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "RestartReason.h"
#include <esp_task_wdt.h>
#include <esp_wifi.h>
#include <ArduinoJson.h>

WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, Network* network, Gpio* gpio, EthServer* ethServer, Preferences* preferences, bool allowRestartToPortal)
: _server(ethServer),
Expand Down Expand Up @@ -65,6 +66,14 @@ void WebCfgServer::initialize()
}
sendCss();
});
_server.on("/status", HTTP_GET, [&]() {
if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) {
return _server.requestAuthentication();
}
String response = "";
buildStatusHtml(response);
_server.send(200, "text/html", response);
iranl marked this conversation as resolved.
Show resolved Hide resolved
});
_server.on("/favicon.ico", HTTP_GET, [&]() {
if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) {
return _server.requestAuthentication();
Expand Down Expand Up @@ -1082,77 +1091,46 @@ void WebCfgServer::update()

void WebCfgServer::buildHtml(String& response)
{
buildHtmlHeader(response);
String header = "<script>let intervalId; window.onload = function() { updateInfo(); intervalId = setInterval(updateInfo, 3000); }; function updateInfo() { var request = new XMLHttpRequest(); request.open('GET', '/status', true); request.onload = () => { const obj = JSON.parse(request.responseText); if (obj.stop == 1) { clearInterval(intervalId); } for (var key of Object.keys(obj)) { if(key=='ota' && document.getElementById(key) !== null) { document.getElementById(key).innerText = \"<a href='/ota'>\" + obj[key] + \"</a>\"; } else if(document.getElementById(key) !== null) { document.getElementById(key).innerText = obj[key]; } } }; request.send(); }</script>";
buildHtmlHeader(response, header);

response.concat("<br><h3>Info</h3>\n");
response.concat("<table>");

printParameter(response, "Hostname", _hostname.c_str());
printParameter(response, "MQTT Connected", _network->mqttConnectionState() > 0 ? "Yes" : "No");
printParameter(response, "Hostname", _hostname.c_str(), "", "hostname");
printParameter(response, "MQTT Connected", _network->mqttConnectionState() > 0 ? "Yes" : "No", "", "mqttState");
if(_nuki != nullptr)
{
char lockstateArr[20];
NukiLock::lockstateToString(_nuki->keyTurnerState().lockState, lockstateArr);
printParameter(response, "Nuki Lock paired", _nuki->isPaired() ? ("Yes (BLE Address " + _nuki->getBleAddress().toString() + ")").c_str() : "No");
printParameter(response, "Nuki Lock state", lockstateArr);
char lockStateArr[20];
NukiLock::lockstateToString(_nuki->keyTurnerState().lockState, lockStateArr);
printParameter(response, "Nuki Lock paired", _nuki->isPaired() ? ("Yes (BLE Address " + _nuki->getBleAddress().toString() + ")").c_str() : "No", "", "lockPaired");
printParameter(response, "Nuki Lock state", lockStateArr, "", "lockState");

if(_nuki->isPaired())
{
switch(_preferences->getInt(preference_lock_pin_status, 4))
{
case 0:
printParameter(response, "Nuki Lock PIN status", "PIN not set");
break;
case 1:
printParameter(response, "Nuki Lock PIN status", "PIN valid");
break;
case 2:
printParameter(response, "Nuki Lock PIN status", "PIN set but invalid");
break;
default:
printParameter(response, "Nuki Lock PIN status", "Unknown");
break;
}
String lockState = pinStateToString(_preferences->getInt(preference_lock_pin_status, 4));
printParameter(response, "Nuki Lock PIN status", lockState.c_str(), "", "lockPin");
}
}
if(_nukiOpener != nullptr)
{
char lockstateArr[20];
NukiOpener::lockstateToString(_nukiOpener->keyTurnerState().lockState, lockstateArr);
printParameter(response, "Nuki Opener paired", _nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No");
char openerStateArr[20];
NukiOpener::lockstateToString(_nukiOpener->keyTurnerState().lockState, openerStateArr);
printParameter(response, "Nuki Opener paired", _nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No", "", "openerPaired");

if(_nukiOpener->keyTurnerState().nukiState == NukiOpener::State::ContinuousMode)
{
printParameter(response, "Nuki Opener state", "Open (Continuous Mode)");
}
else
{
printParameter(response, "Nuki Opener state", lockstateArr);
}
if(_nukiOpener->keyTurnerState().nukiState == NukiOpener::State::ContinuousMode) printParameter(response, "Nuki Opener state", "Open (Continuous Mode)", "", "openerState");
else printParameter(response, "Nuki Opener state", openerStateArr, "", "openerState");

if(_nukiOpener->isPaired())
{
switch(_preferences->getInt(preference_opener_pin_status, 4))
{
case 0:
printParameter(response, "Nuki Opener PIN status", "PIN not set");
break;
case 1:
printParameter(response, "Nuki Opener PIN status", "PIN valid");
break;
case 2:
printParameter(response, "Nuki Opener PIN status", "PIN set but invalid");
break;
default:
printParameter(response, "Nuki Opener PIN status", "Unknown");
break;
}
String openerState = pinStateToString(_preferences->getInt(preference_opener_pin_status, 4));
printParameter(response, "Nuki Opener PIN status", openerState.c_str(), "", "openerPin");
}
}

printParameter(response, "Firmware", NUKI_HUB_VERSION, "/info");
printParameter(response, "Firmware", NUKI_HUB_VERSION, "/info", "firmware");

if(_preferences->getBool(preference_check_updates)) printParameter(response, "Latest Firmware", _preferences->getString(preference_latest_version).c_str(), "/ota");
if(_preferences->getBool(preference_check_updates)) printParameter(response, "Latest Firmware", _preferences->getString(preference_latest_version).c_str(), "/ota", "ota");

response.concat("</table><br><table id=\"tblnav\"><tbody>");
response.concat("<tr><td><h5>MQTT and Network Configuration</h5></td><td class=\"tdbtn\">");
Expand Down Expand Up @@ -1387,6 +1365,88 @@ void WebCfgServer::buildAdvancedConfigHtml(String &response)
response.concat("</body></html>");
}

void WebCfgServer::buildStatusHtml(String &response)
{
JsonDocument json;
char _resbuf[2048];
bool mqttDone = false;
bool lockDone = false;
bool openerDone = false;
bool latestDone = false;

json["stop"] = 0;

if(_network->mqttConnectionState() > 0)
{
json["mqttState"] = "Yes";
mqttDone = true;
}
else json["mqttState"] = "No";

if(_nuki != nullptr)
{
char lockStateArr[20];
NukiLock::lockstateToString(_nuki->keyTurnerState().lockState, lockStateArr);
String lockState = lockStateArr;
String LockPaired = (_nuki->isPaired() ? ("Yes (BLE Address " + _nuki->getBleAddress().toString() + ")").c_str() : "No");
json["lockPaired"] = LockPaired;
json["lockState"] = lockState;

if(_nuki->isPaired())
{
json["lockPin"] = pinStateToString(_preferences->getInt(preference_lock_pin_status, 4));
lockDone = true;
}
else json["lockPin"] = "Not Paired";
}
else lockDone = true;
if(_nukiOpener != nullptr)
{
char openerStateArr[20];
NukiOpener::lockstateToString(_nukiOpener->keyTurnerState().lockState, openerStateArr);
String openerState = openerStateArr;
String openerPaired = (_nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No");
json["openerPaired"] = openerPaired;

if(_nukiOpener->keyTurnerState().nukiState == NukiOpener::State::ContinuousMode) json["openerState"] = "Open (Continuous Mode)";
else json["openerState"] = openerState;

if(_nukiOpener->isPaired())
{
json["openerPin"] = pinStateToString(_preferences->getInt(preference_opener_pin_status, 4));
openerDone = true;
}
else json["openerPin"] = "Not Paired";
}
else openerDone = true;

if(_preferences->getBool(preference_check_updates))
{
json["latestFirmware"] = _preferences->getString(preference_latest_version);
latestDone = true;
}
else latestDone = true;

if(mqttDone && lockDone && openerDone && latestDone) json["stop"] = 1;

serializeJson(json, _resbuf, sizeof(_resbuf));
response = _resbuf;
}

String WebCfgServer::pinStateToString(uint8_t value) {
switch(value)
{
case 0:
return (String)"PIN not set";
case 1:
return (String)"PIN valid";
case 2:
return (String)"PIN set but invalid";;
default:
return (String)"Unknown";
}
}

void WebCfgServer::buildAccLvlHtml(String &response)
{
buildHtmlHeader(response);
Expand Down Expand Up @@ -2030,10 +2090,11 @@ void WebCfgServer::processFactoryReset()
restartEsp(RestartReason::NukiHubReset);
}

void WebCfgServer::buildHtmlHeader(String &response)
void WebCfgServer::buildHtmlHeader(String &response, String additionalHeader)
{
response.concat("<html><head>");
response.concat("<meta name='viewport' content='width=device-width, initial-scale=1'>");
if(strcmp(additionalHeader.c_str(), "") != 0) response.concat(additionalHeader);
response.concat("<link rel='stylesheet' href='/style.css'>");
response.concat("<title>Nuki Hub</title></head><body>");

Expand Down Expand Up @@ -2186,17 +2247,20 @@ void WebCfgServer::buildNavigationButton(String &response, const char *caption,
response.concat("</form>");
}

void WebCfgServer::printParameter(String& response, const char *description, const char *value, const char *link)
void WebCfgServer::printParameter(String& response, const char *description, const char *value, const char *link, const char *id)
{
response.concat("<tr>");
response.concat("<td>");
response.concat(description);
response.concat("</td>");
response.concat("<td>");
if(strcmp(link, "") == 0)
if(strcmp(id, "") == 0) response.concat("<td>");
else
{
response.concat(value);
response.concat("<td id=\"");
response.concat(id);
response.concat("\">");
}
if(strcmp(link, "") == 0) response.concat(value);
else
{
response.concat("<a href=\"");
Expand Down
8 changes: 5 additions & 3 deletions src/WebCfgServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class WebCfgServer
void buildOtaHtml(String& response, bool errored);
void buildOtaCompletedHtml(String& response);
void buildMqttConfigHtml(String& response);
void buildStatusHtml(String& response);
void buildAdvancedConfigHtml(String& response);
void buildNukiConfigHtml(String& response);
void buildGpioConfigHtml(String& response);
Expand All @@ -53,7 +54,7 @@ class WebCfgServer
void processUnpair(bool opener);
void processFactoryReset();

void buildHtmlHeader(String& response);
void buildHtmlHeader(String& response, String additionalHeader = "");
void printInputField(String& response, const char* token, const char* description, const char* value, const size_t& maxLength, const bool& isPassword = false, const bool& showLengthRestriction = false);
void printInputField(String& response, const char* token, const char* description, const int value, size_t maxLength);
void printCheckBox(String& response, const char* token, const char* description, const bool value, const char* htmlClass);
Expand All @@ -64,8 +65,9 @@ class WebCfgServer
const std::vector<std::pair<String, String>> getNetworkDetectionOptions() const;
const std::vector<std::pair<String, String>> getGpioOptions() const;
String getPreselectionForGpio(const uint8_t& pin);
String pinStateToString(uint8_t value);

void printParameter(String& response, const char* description, const char* value, const char *link = "");
void printParameter(String& response, const char* description, const char* value, const char *link = "", const char *id = "");

String generateConfirmCode();
void waitAndProcess(const bool blocking, const uint32_t duration);
Expand All @@ -92,4 +94,4 @@ class WebCfgServer
String _confirmCode = "----";

bool _enabled = true;
};
};