Skip to content

Commit fcf9225

Browse files
committed
upd
1 parent ab040d5 commit fcf9225

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1893
-715
lines changed

README.md

Lines changed: 116 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
# Settings
1010
Библиотека для создания простого веб-интерфейса настроек на esp8266/esp32
11-
- Веб-приложение весит всего 7кб и вшивается в программу в бинарном gzip виде, никакой возни с файлами
11+
- Веб-приложение весит около 10кб и вшивается в программу в бинарном gzip виде без возни с файлами
1212
- Удобный билдер панели управления из скетча
1313
- Десяток типовых виджетов с возможностью объединения в группы и вложенные меню
14+
- Система авторизации с разными правами для авторизованных юзеров и гостей
15+
- Файловый менеджер и OTA (обновление по воздуху)
1416
- Интеграция с библиотекой [GyverDB](https://github.com/GyverLibs/GyverDB) для полностью автоматического хранения данных
1517
- Компактный бинарный протокол связи
1618
- Легко адаптируется под любую библиотеку HTTP сервера, из коробки реализовано три версии: GyverHTTP, стандартный esp-WebServer, ESPAsyncWebserver
@@ -184,7 +186,7 @@ void build(sets::Builder& b) {
184186
```
185187

186188
### Контейнеры
187-
Виджеты можно объединять в контейнеры. Контейнер нужно начать и закончить, так как пакет данных собирается линейно в целях оптимизации скорости и памяти. Метод beginКонтейнер всегда вернёт true для красоты организации кода в блоке условия:
189+
Виджеты можно объединять в контейнеры. Контейнер нужно начать и закончить, так как пакет данных собирается линейно в целях оптимизации скорости и памяти. Метод `beginКонтейнер` всегда вернёт true для красоты организации кода в блоке условия:
188190
```cpp
189191
void build(sets::Builder& b) {
190192
if (b.beginGroup("Group 1")) {
@@ -258,8 +260,36 @@ void setup() {
258260
259261
Вебморда отслеживает статус устройства, при потере связи появится текст offline в заголовке страницы. После потери связи вебморда будет запрашивать информацию о виджетах, это очень удобно при разработке - например добавляем виджет, загружаем прошивку. За это время вебморда уже понимает что устройство оффлайн и при первом успешном подключении выводит актуальные виджеты. При изменении значений виджетов вебморда также следит за доставкой пакета, при ошибке связи появится надпись error у соответствующего виджета.
260262
263+
### Авторизация
264+
В системе предусмотрена авторизация: если в прошивке указать отличный от пустой строки пароль - вебморда будет работать в "гостевом" режиме: отображаются только разрешённые гостям виджеты, файловый менеджер и OTA скрыты и заблокированы. Для ввода пароля нужно зайти в меню (правая верхняя кнопка) и нажать на ключик. Серый ключик означает что авторизация отключена, зелёный - клиент авторизован, красный - неверный пароль. Пароль может содержать любые символы и иметь любую длину - в явном виде он не хранится и не передаётся.
265+
266+
Для разделения админского и гостевого доступа предусмотрен виртуальный контейнер Guest. Если пароль установлен и клиент не авторизован - он будет видеть только виджеты из гостевых контейнеров. Для корректной работы гостевой контейнер не должен прерываться обычными контейнерами. Пример:
267+
268+
```cpp
269+
if (b.beginGroup("Group 1")) {
270+
// гости не видят
271+
b.Pass(kk::pass, "Password");
272+
273+
// виджеты, которые видят гости и админы
274+
{
275+
sets::GuestAccess g(b);
276+
b.Input(kk::uintw, "uint");
277+
b.Input(kk::intw, "int");
278+
b.Input(kk::int64w, "int 64");
279+
}
280+
281+
// гости не видят
282+
{
283+
sets::Menu m(b, "sub sub");
284+
b.Label(kk::lbl2, "millis()", "", sets::Colors::Red);
285+
}
286+
287+
b.endGroup();
288+
}
289+
```
290+
В гостевой контейнер можно поместить несколько обычных контейнеров, например групп.
291+
261292
## Описание классов
262-
- `SettingsBase` (*SettingsBase.h*) - базовый класс без вебсервера
263293
- `SettingsGyver` (*SettingsGyver.h*) - на вебсервере GyverHTTP
264294
- `SettingsESP` (*SettingsESP.h*) - на стандартном вебсервере ESP
265295
- `SettingsAsync` (*SettingsAsync.h*) - на асинхронном ESPAsyncWebserver
@@ -268,6 +298,12 @@ void setup() {
268298
```cpp
269299
Settings(const String& title = "", GyverDB* db = nullptr);
270300

301+
// установить пароль на вебморду. Пустая строка "" чтобы отключить
302+
void setPass(Text pass);
303+
304+
// перезагрузить страницу. Можно вызывать где угодно + в обработчике update
305+
void reload();
306+
271307
// установить заголовок страницы
272308
void setTitle(const String& title);
273309

@@ -295,47 +331,111 @@ void tick();
295331
// инфо о билде
296332
Build build();
297333
298-
// перезагрузить страницу
334+
// перезагрузить страницу (вызывать в действии)
299335
void reload();
300336
301-
// контейнеры
337+
// КОНТЕЙНЕРЫ
338+
// разрешить неавторизованным клиентам следующий код
339+
bool beginGuest();
340+
341+
// запретить неавторизованным клиентам
342+
void endGuest();
343+
344+
// группа
302345
bool beginGroup(Text title = Text());
303346
void endGroup();
304347
348+
// вложенное меню
305349
bool beginMenu(Text title);
306350
void endMenu();
307351
352+
// ряд кнопок
308353
bool beginButtons();
309354
void endButtons();
310355
311-
// пассивные виджеты
356+
// ВИДЖЕТЫ
357+
// ПАССИВНЫЕ
358+
// многострочный текст
312359
void Paragraph(size_t id, Text label, Text text = Text());
360+
361+
// текстовое значение
313362
void Label(size_t id, Text label, Text text = Text(), uint32_t color = SETS_DEFAULT_COLOR);
314363
void Label(size_t id, Text label, Text text, sets::Colors color);
315364
316-
// активные виджеты
365+
// без id
366+
void Label(Text label, Text text = Text(), uint32_t color = SETS_DEFAULT_COLOR);
367+
void Label(Text label, Text text, sets::Colors color);
368+
369+
// светодиод (включен - зелёный, выключен - красный)
370+
void LED(size_t id, Text label, bool value);
371+
void LED(Text label, bool value);
372+
373+
// АКТИВНЫЕ
374+
// ввод текста и цифр
317375
bool Input(size_t id, Text label, Text value = Text());
376+
377+
// ввод пароля
318378
bool Pass(size_t id, Text label, Text value = Text());
379+
380+
// ввод цвета, результат в обычном 24-бит формате
319381
bool Color(size_t id, Text label, Text value = Text());
382+
383+
// переключатель, результат 1/0
320384
bool Switch(size_t id, Text label, Text value = Text());
385+
386+
// дата, результат в unix секундах
321387
bool Date(size_t id, Text label, Text value = Text());
322-
bool Time(size_t id, Text label, Text value = Text());
388+
389+
// дата и время, результат в unix секундах
323390
bool DateTime(size_t id, Text label, Text value = Text());
391+
392+
// время, результат в секундах с начала суток
393+
bool Time(size_t id, Text label, Text value = Text());
394+
395+
// слайдер
324396
bool Slider(size_t id, Text label, float min = 0, float max = 100, float step = 1, Text unit = Text(), Text value = Text());
325397
326398
// кнопку можно добавлять как внутри контейнера кнопок, так и как одиночный виджет
327399
bool Button(size_t id, Text label, uint32_t color = SETS_DEFAULT_COLOR);
328400
bool Button(size_t id, Text label, sets::Colors color);
329401
330-
// опции разделяются ;
402+
// список выбора, опции в виде текста разделяются ;
331403
bool Select(size_t id, Text label, Text options, Text value = Text());
332404
333-
// для активации отправь пустой update на его id
405+
// окно подтверждения, для активации отправь пустой update на его id
334406
bool Confirm(size_t id, Text label);
335407
```
336408

337409
Здесь `Text` - универсальный текстовый формат, принимает строки в любом виде. При указании `value` отличным от стандартного будет отправлено его значение. Иначе будет отправлено значение из БД, если она подключена. Если в качестве значения нужно число - используйте конструктор `Value`, например `b.Color("col", "Color", Value(my_color));`, где `my_color` это `uint32_t`.
338410

411+
### Build
412+
Инфо о билде
413+
```cpp
414+
// клиент авторизован (или пароль не указан, т.е. все админы)
415+
bool isGranted();
416+
417+
// id виджета (действие)
418+
size_t id();
419+
420+
// значение виджета (действие)
421+
Text& value();
422+
```
423+
424+
### Контейнеры
425+
```cpp
426+
// контейнер гостевого доступа
427+
class GuestAccess(Builder& b);
428+
429+
// контейнер группы виджетов
430+
class Group(Builder& b, Text title = Text());
431+
432+
// контейнер вложенного меню
433+
class Menu(Builder& b, Text title);
434+
435+
// контейнер кнопок
436+
class Buttons(Builder& b);
437+
```
438+
339439
### Updater
340440
```cpp
341441
// пустой апдейт (например для вызова Confirm)
@@ -358,6 +458,12 @@ void update(size_t id, <любой численный тип> value);
358458
- v1.0.2
359459
- Добавлен виджет Confirm (всплывающее окно подтверждения)
360460
- Кастомные всплывающие окна для Input (Input теперь работает на просмотре AP WiFi точки на Xiaomi)
461+
- v1.0.5
462+
- Добавлен виджет LED
463+
- Добавлен файловый менеджер
464+
- Добавлено ОТА обновление
465+
- Добавлена авторизация и гостевой фильтр виджетов
466+
- Новый стиль для Select
361467
362468
<a id="install"></a>
363469
## Установка

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=Settings
2-
version=1.0.4
2+
version=1.0.5
33
author=AlexGyver <[email protected]>
44
maintainer=AlexGyver <[email protected]>
55
sentence=Simple UI webface builder for esp8266/esp32

src/SettingsAsync.h

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#pragma once
44
#include <Arduino.h>
5+
#include <LittleFS.h>
56

67
#ifdef ESP8266
78
#include <ESP8266WiFi.h>
@@ -14,6 +15,8 @@
1415

1516
#include "SettingsBase.h"
1617
#include "core/DnsWrapper.h"
18+
#include "core/ota.h"
19+
#include "core/fs.h"
1720
#include "web/settings.h"
1821

1922
class SettingsAsync : public SettingsBase {
@@ -25,18 +28,61 @@ class SettingsAsync : public SettingsBase {
2528
server.begin();
2629

2730
server.on("/settings", HTTP_GET, [this](AsyncWebServerRequest *request) {
28-
String action, id, value;
31+
String auth, action, id, value;
32+
if (request->hasParam("auth")) auth = request->getParam("auth")->value();
2933
if (request->hasParam("action")) action = request->getParam("action")->value();
3034
if (request->hasParam("id")) id = request->getParam("id")->value();
3135
if (request->hasParam("value")) value = request->getParam("value")->value();
3236

3337
_response = request->beginResponseStream("text/plain");
3438
cors_h(_response);
35-
parse(action, id, value);
39+
parse(auth, action, id, value);
3640
request->send(_response);
3741
_response = nullptr;
3842
});
3943

44+
server.on("/fetch", HTTP_GET, [this](AsyncWebServerRequest *request) {
45+
String auth, path;
46+
if (request->hasParam("auth")) auth = request->getParam("auth")->value();
47+
if (request->hasParam("path")) path = request->getParam("path")->value();
48+
49+
if (authenticate(auth)) {
50+
AsyncWebServerResponse *response = request->beginResponse(ST_FS, path);
51+
cors_h(response);
52+
request->send(response);
53+
} else {
54+
sendCode(401, request);
55+
}
56+
});
57+
58+
server.on("/upload", HTTP_POST,
59+
[this](AsyncWebServerRequest* request) {
60+
sendCode(200, request);
61+
},
62+
[this](AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) {
63+
String auth, path;
64+
if (request->hasParam("auth")) auth = request->getParam("auth")->value();
65+
if (request->hasParam("path")) path = request->getParam("path")->value();
66+
if (!authenticate(auth)) return;
67+
if (!index) _file = openFileWrite(path);
68+
if (len && _file) _file.write(data, len);
69+
if (final && _file) _file.close();
70+
});
71+
72+
server.on("/ota", HTTP_POST,
73+
[this](AsyncWebServerRequest* request) {
74+
sendCode(Update.hasError() ? 500 : 200, request);
75+
if (!Update.hasError()) restart();
76+
},
77+
[this](AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) {
78+
String auth;
79+
if (request->hasParam("auth")) auth = request->getParam("auth")->value();
80+
if (!authenticate(auth)) return;
81+
if (!index) sets::beginOta(true, true);
82+
if (len) Update.write(data, len);
83+
if (final) Update.end(true);
84+
});
85+
4086
server.onNotFound([this](AsyncWebServerRequest *request) {
4187
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", settings_index_gz, settings_index_gz_len);
4288
gzip_h(response);
@@ -55,6 +101,12 @@ class SettingsAsync : public SettingsBase {
55101
cache_h(response);
56102
request->send(response);
57103
});
104+
server.on("/favicon.svg", HTTP_GET, [this](AsyncWebServerRequest *request) {
105+
AsyncWebServerResponse *response = request->beginResponse_P(200, "image/svg+xml", settings_favicon_gz, settings_favicon_gz_len);
106+
gzip_h(response);
107+
cache_h(response);
108+
request->send(response);
109+
});
58110
}
59111

60112
void tick() {
@@ -66,10 +118,16 @@ class SettingsAsync : public SettingsBase {
66118
AsyncWebServer server;
67119
AsyncResponseStream *_response = nullptr;
68120
sets::DnsWrapper _dns;
121+
File _file;
69122

70123
void send(uint8_t *data, size_t len) {
71124
if (_response) _response->write(data, len);
72125
}
126+
void sendCode(int code, AsyncWebServerRequest* request) {
127+
AsyncWebServerResponse *response = request->beginResponse(code);
128+
cors_h(response);
129+
request->send(response);
130+
}
73131

74132
void gzip_h(AsyncWebServerResponse *response) {
75133
response->addHeader(F("Content-Encoding"), F("gzip"));
@@ -83,8 +141,10 @@ class SettingsAsync : public SettingsBase {
83141
response->addHeader(F("Expires"), F("0"));
84142
}
85143
void cors_h(AsyncWebServerResponse *response) {
144+
#ifdef SETS_USE_CORS
86145
response->addHeader(F("Access-Control-Allow-Origin"), F("*"));
87146
response->addHeader(F("Access-Control-Allow-Private-Network"), F("true"));
88147
response->addHeader(F("Access-Control-Allow-Methods"), F("*"));
148+
#endif
89149
}
90150
};

0 commit comments

Comments
 (0)