diff --git a/data/assets/misc.css b/data/assets/misc.css index c8dc6fb..4fe5c6a 100644 --- a/data/assets/misc.css +++ b/data/assets/misc.css @@ -59,7 +59,23 @@ button { border-color: hsl(240 3.7% 15.9%) !important; } -@media only screen and (min-width: 1200px) { +div[class$="-selected-body"] { + display: flex; + flex-direction: column; + padding-inline: 1rem; + padding-bottom: .5rem; + gap: 16px; +} + +h3[class$="-selected-tab"] { + border-bottom: 2px #8e8271 solid; +} + +div[class$="-hidden-body"] { + display: none; +} + +@media only screen and (min-width: 800px) { button:hover { opacity: .9; } @@ -71,6 +87,17 @@ button { #restart-btn:active { background-color: #4040402e!important; } + + #mqtt-broker-con, #mqtt-topics-container { + min-width: 20rem; + max-width: 25rem; + } + .cards-container { + align-items: flex-start; + } + #component { + max-width: 65rem; + } } @media only screen and (max-width: 600px) { @@ -83,15 +110,31 @@ button { flex-wrap: wrap; justify-content: center; } + #mqtt-broker, #mqtt-topics { + width: auto; + } + .cards-container { + flex-direction: column; + } + .nfc-triggers-selected-body { + flex-wrap: wrap; + } } -#mqtt-broker-conn { +.fill-container { width: 100%; width: -moz-available; width: -webkit-fill-available; width: fill-available; } +#buttons-group { + display: flex; + justify-content: center; + margin-top: 2rem; + gap: 64px; +} + .loader { width: 100%; diff --git a/data/assets/restart.webp b/data/assets/restart.webp deleted file mode 100644 index d83e404..0000000 Binary files a/data/assets/restart.webp and /dev/null differ diff --git a/data/index.html b/data/index.html index 43ad6a7..edf4e95 100644 --- a/data/index.html +++ b/data/index.html @@ -1,5 +1,5 @@ - + @@ -24,6 +24,13 @@ } wifiSignalStrength(); setInterval(wifiSignalStrength, 5000) + function switchTab(el) { + var parentId = el.parentElement.id; + document.querySelector(`.${parentId}-selected-body`).classList.replace(`${parentId}-selected-body`,`${parentId}-hidden-body`); + document.querySelector(`div[data-${parentId}-body="${el.dataset.tabIndex}"]`).classList.replace(`${parentId}-hidden-body`, `${parentId}-selected-body`); + document.querySelector(`.${parentId}-selected-tab`).className = ""; + el.classList.add(`${parentId}-selected-tab`); + } async function addComponent(name, button) { let loaderEl = document.createElement("span") loaderEl.className = "loader"; @@ -31,7 +38,7 @@ document.querySelector("#main > span").insertAdjacentElement("afterend",loaderEl); let element = document.querySelector("#component"); if(element){ - element.remove(); + element.remove(); } let sel = document.querySelector(".selected-btn"); if(sel){ @@ -40,8 +47,8 @@ let main = document.querySelector("#main"); let el = document.createElement("div"); el.id = "component"; - el.style = "display: flex;flex-direction: column;max-width: 32rem;margin-bottom: 1rem;"; - let data = await fetch(name); + el.style = "display: flex;flex-direction: column;margin-bottom: 1rem;"; + let data = await fetch(`${name}`); let string = await data.text(); el.innerHTML = string; main.appendChild(el); diff --git a/data/misc.html b/data/misc.html deleted file mode 100644 index 9c767ca..0000000 --- a/data/misc.html +++ /dev/null @@ -1,193 +0,0 @@ -

Miscellaneous

-
Changes in this section will reboot the device
-
-
-

Personalization

-
-

HomeKit

-
- - -
- -
- - -
- -
- - -
-
- - -
-
- HomeKey Card Finish: -
-
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
-
-
-

PN532

-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
-
-

WebUI

-
-
- - -
-
- - -
-
- - -
-
-
-
-

HomeSpan

-
-
- - -
-
- - -
- -
- - -
-
-
- -
- - -
-
- \ No newline at end of file diff --git a/data/mqtt.html b/data/mqtt.html deleted file mode 100644 index 424f1a8..0000000 --- a/data/mqtt.html +++ /dev/null @@ -1,177 +0,0 @@ -

MQTT Configuration

-
Changes in this section will reboot the device
-
-
-

Broker Connection

-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
-
-
- - -
-
-
-
-

MQTT Topics

-
-

Core Topics

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
-

Custom State Topics

-
-
- - -
-
- - -
-
- - -
-
- -
-
- Custom Lock Actions -
-
- - -
-
- - -
-
-
-
- Custom Lock States -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
-
-
-
- - -
-
- \ No newline at end of file diff --git a/data/actions.html b/data/routes/actions.html similarity index 63% rename from data/actions.html rename to data/routes/actions.html index 2b4b2e6..b9bc172 100644 --- a/data/actions.html +++ b/data/routes/actions.html @@ -3,27 +3,35 @@

Hardware Actions

Assigning 255 to any Pin field will disable the respective option
- -
-

HomeKey/NFC Triggers

-

Executed on successful/failed HomeKey Authentication and on NFC Tag detection(marked as a failed event)

-

Both Neopixel and Simple GPIO have a momentary state

-
-

Neopixel

+
+
+

HomeKey/NFC Triggers

+

Executed on successful/failed HomeKey Authentication and on NFC Tag detection(marked as a failed event)

+

Both Neopixel and Simple GPIO have a momentary state

+
+
+

Neopixel

+

Simple GPIO

+
+ +
+
- +
-
+
-
+
-
+
-
-
- - -
-
- - -
-
- - -
-
- - +
+
+

HomeKit Triggers

+

Executed upon interaction in the Home app and optionally on successful HomeKey Authentication(enabled by default)

+
+
+

Simple GPIO

+
+
-
- - +
+
+
* Simple GPIO follows the "Always Lock/Unlock on HomeKey" option
+
** Momentary state applies only if initial state is "LOCKED"
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
-
+
diff --git a/data/info.html b/data/routes/info.html similarity index 100% rename from data/info.html rename to data/routes/info.html diff --git a/data/routes/misc.html b/data/routes/misc.html new file mode 100644 index 0000000..eca4b9c --- /dev/null +++ b/data/routes/misc.html @@ -0,0 +1,196 @@ +

Miscellaneous

+
Changes in this section will reboot the device
+
+
+
+

General settings

+
+
+
+

HomeKit

+

PN532

+

HomeSpan

+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+ HomeKey Card Finish: +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ HomeSpan Documentation +
+ + +
+
+ + +
+
+ + +
+
+
+
+
+

WebUI

+
+
+
+

Authentication

+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+ + +
+
+ \ No newline at end of file diff --git a/data/routes/mqtt.html b/data/routes/mqtt.html new file mode 100644 index 0000000..c5a9db1 --- /dev/null +++ b/data/routes/mqtt.html @@ -0,0 +1,187 @@ +

MQTT Configuration

+
Changes in this section will reboot the device
+
+
+
+

Broker Connection

+
TCP - Without TLS
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+

MQTT Topics

+
+
+

Core Topics

+

Custom Topics

+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ Custom Lock Actions +
+
+ + +
+
+ + +
+
+
+
+ Custom Lock States +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+ + +
+
+ \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 9e88680..108cc34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -70,7 +70,7 @@ namespace espConfig lockCStateCmd.append(id).append("/" MQTT_SET_CURRENT_STATE_TOPIC); lockTStateCmd.append(id).append("/" MQTT_SET_TARGET_STATE_TOPIC); lockCustomStateTopic.append(id).append("/" MQTT_CUSTOM_STATE_TOPIC); - lockCustomStateCmd.append(id).append("/" MQTT_CUSTOM_STATE_TOPIC); + lockCustomStateCmd.append(id).append("/" MQTT_CUSTOM_STATE_CTRL_TOPIC); } /* MQTT Broker */ std::string mqttBroker = MQTT_HOST; @@ -946,16 +946,16 @@ String actionsProcess(const String& var) { } bool headersFix(AsyncWebServerRequest* request) { request->addInterestingHeader("ANY"); return true; }; void setupWeb() { - auto infoHandle = new AsyncStaticWebHandler("/info", LittleFS, "/info.html", NULL); + auto infoHandle = new AsyncStaticWebHandler("/info", LittleFS, "/routes/info.html", NULL); webServer.addHandler(infoHandle); infoHandle->setTemplateProcessor(hkInfoHtmlProcess).setFilter(headersFix); - auto mqttHandle = new AsyncStaticWebHandler("/mqtt", LittleFS, "/mqtt.html", NULL); + auto mqttHandle = new AsyncStaticWebHandler("/mqtt", LittleFS, "/routes/mqtt.html", NULL); webServer.addHandler(mqttHandle); mqttHandle->setTemplateProcessor(mqttHtmlProcess).setFilter(headersFix); - auto miscHandle = new AsyncStaticWebHandler("/misc", LittleFS, "/misc.html", NULL); + auto miscHandle = new AsyncStaticWebHandler("/misc", LittleFS, "/routes/misc.html", NULL); webServer.addHandler(miscHandle); miscHandle->setTemplateProcessor(miscHtmlProcess).setFilter(headersFix); - auto actionsHandle = new AsyncStaticWebHandler("/actions", LittleFS, "/actions.html", NULL); + auto actionsHandle = new AsyncStaticWebHandler("/actions", LittleFS, "/routes/actions.html", NULL); webServer.addHandler(actionsHandle); actionsHandle->setTemplateProcessor(actionsProcess).setFilter(headersFix); auto assetsHandle = new AsyncStaticWebHandler("/assets", LittleFS, "/assets/", NULL); @@ -1063,8 +1063,20 @@ void setupWeb() { } } } else if (!strcmp(p->name().c_str(), "control-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } espConfig::miscConfig.controlPin = p->value().toInt(); } else if (!strcmp(p->name().c_str(), "led-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } espConfig::miscConfig.hsStatusPin = p->value().toInt(); } else if (!strcmp(p->name().c_str(), "hk-always-unlock")) { espConfig::miscConfig.lockAlwaysUnlock = p->value().toInt(); @@ -1079,12 +1091,36 @@ void setupWeb() { } else if (!strcmp(p->name().c_str(), "web-auth-password")) { espConfig::miscConfig.webPassword = p->value().c_str(); } else if (!strcmp(p->name().c_str(), "nfc-ss-gpio-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } espConfig::miscConfig.nfcGpioPins[0] = p->value().toInt(); } else if (!strcmp(p->name().c_str(), "nfc-sck-gpio-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } espConfig::miscConfig.nfcGpioPins[1] = p->value().toInt(); } else if (!strcmp(p->name().c_str(), "nfc-miso-gpio-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } espConfig::miscConfig.nfcGpioPins[2] = p->value().toInt(); } else if (!strcmp(p->name().c_str(), "nfc-mosi-gpio-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } espConfig::miscConfig.nfcGpioPins[3] = p->value().toInt(); } } @@ -1110,6 +1146,12 @@ void setupWeb() { AsyncWebParameter* p = request->getParam(i); LOG(V, "POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); if (!strcmp(p->name().c_str(), "nfc-neopixel-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } if (espConfig::miscConfig.nfcNeopixelPin == 255 && p->value().toInt() != 255) { xTaskCreate(neopixel_task, "neopixel_task", 4096, NULL, 2, &neopixel_task_handle); if (!pixel) { @@ -1143,6 +1185,12 @@ void setupWeb() { } else if (!strcmp(p->name().c_str(), "nfc-f-blue-pixel")) { espConfig::miscConfig.neopixelFailureColor[espConfig::misc_config_t::colorMap::B] = p->value().toInt(); } else if (!strcmp(p->name().c_str(), "nfc-s-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } if (espConfig::miscConfig.nfcSuccessPin == 255 && p->value().toInt() != 255 && gpio_led_task_handle == nullptr) { pinMode(p->value().toInt(), OUTPUT); xTaskCreate(nfc_gpio_task, "nfc_gpio_task", 4096, NULL, 2, &gpio_led_task_handle); @@ -1153,6 +1201,12 @@ void setupWeb() { } espConfig::miscConfig.nfcSuccessPin = p->value().toInt(); } else if (!strcmp(p->name().c_str(), "nfc-f-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } if (espConfig::miscConfig.nfcFailPin == 255 && p->value().toInt() != 255 && gpio_led_task_handle == nullptr) { pinMode(p->value().toInt(), OUTPUT); xTaskCreate(nfc_gpio_task, "nfc_gpio_task", 4096, NULL, 2, &gpio_led_task_handle); @@ -1171,6 +1225,12 @@ void setupWeb() { } else if (!strcmp(p->name().c_str(), "nfc-f-time")) { espConfig::miscConfig.nfcFailTime = p->value().toInt(); } else if (!strcmp(p->name().c_str(), "gpio-a-pin")) { + if (!GPIO_IS_VALID_GPIO(p->value().toInt()) && !GPIO_IS_VALID_OUTPUT_GPIO(p->value().toInt()) && p->value().toInt() != 255) { + std::string msg = p->value().c_str(); + msg.append(" is not a valid GPIO Pin"); + request->send(200, "text/plain", msg.c_str()); + return; + } if (espConfig::miscConfig.gpioActionPin == 255 && p->value().toInt() != 255) { pinMode(p->value().toInt(), OUTPUT); xTaskCreate(gpio_task, "gpio_task", 4096, NULL, 2, &gpio_lock_task_handle); @@ -1199,7 +1259,7 @@ void setupWeb() { LOG(V, "SET_STATUS: %s", esp_err_to_name(set_nvs)); LOG(V, "COMMIT_STATUS: %s", esp_err_to_name(commit_nvs)); - request->send(200, "text/plain", "Received Config!"); + request->send(200, "text/plain", "Configuration applied!"); }); webServer.addHandler(actionsConfigHandle); auto rebootDeviceHandle = new AsyncCallbackWebHandler(); @@ -1263,7 +1323,7 @@ void mqttConfigReset(const char* buf) { } void wifiCallback() { - if (espConfig::mqttData.mqttBroker.size() > 0 || !std::equal(espConfig::mqttData.mqttBroker.begin(), espConfig::mqttData.mqttBroker.end(), "0.0.0.0")) { + if (espConfig::mqttData.mqttBroker.size() >= 7 && espConfig::mqttData.mqttBroker.size() <= 16 && !std::equal(espConfig::mqttData.mqttBroker.begin(), espConfig::mqttData.mqttBroker.end(), "0.0.0.0")) { mqtt_app_start(); } setupWeb();