# Документация GyverHub
### Оглавление
0. [Примеры](https://github.com/GyverLibs/GyverHub/tree/main/examples)
1. [Документация](https://github.com/GyverLibs/GyverHub/wiki)
2. [Начало работы](https://github.com/GyverLibs/GyverHub/wiki/2.-%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B)
3. [Приложение](https://github.com/GyverLibs/GyverHub/wiki/3.-%D0%9F%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5)
4. [Билдер и компоненты](https://github.com/GyverLibs/GyverHub/wiki/4.-%D0%91%D0%B8%D0%BB%D0%B4%D0%B5%D1%80-%D0%B8-%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82%D1%8B)
5. [Дополнительно](https://github.com/GyverLibs/GyverHub/wiki/5.-%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE)
6. [Холст (Canvas)](https://github.com/GyverLibs/GyverHub/wiki/6.-%D0%A5%D0%BE%D0%BB%D1%81%D1%82-(Canvas))
7. [Интеграция в умный дом](https://github.com/GyverLibs/GyverHub/wiki/7.-%D0%98%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D1%8F-%D0%B2-%D1%83%D0%BC%D0%BD%D1%8B%D0%B9-%D0%B4%D0%BE%D0%BC)
8. [Ручной режим](https://github.com/GyverLibs/GyverHub/wiki/8.-%D0%A0%D1%83%D1%87%D0%BD%D0%BE%D0%B9-%D1%80%D0%B5%D0%B6%D0%B8%D0%BC)

[![Foo](https://img.shields.io/badge/ENGLISH-VERSION-blueviolet.svg?style=flat-square)](https://github-com.translate.goog/GyverLibs/GyverHub/wiki?_x_tr_sl=ru&_x_tr_tl=en)

## GyverHub
<details>
<summary>Компиляция</summary>

#### Настройки
В файле библиотеки `config.hpp` есть некоторые системные настройки, менять их нужно именно в этом файле:
```cpp
#define GH_LIB_VERSION "v0.1b"  // версия библиотеки
#define GH_HTTP_PATH "/fs/"     // путь к папке с файлами с HTTP доступом
#define GH_CONN_TOUT 5          // таймаут соединения, с
#define GH_HTTP_PORT 80         // http порт
#define GH_WS_PORT 81           // websocket порт
#define GH_DOWN_CHUNK_SIZE 512  // размер чанка при скачивании с платы
#define GH_UPL_CHUNK_SIZE 200   // размер чанка при загрузке на плату
#define GH_FS_DEPTH 5           // глубина сканирования файловой системы (esp32)
#define GH_FS LittleFS          // файловая система
#define GH_MQTT_RECONNECT 5000  // период переподключения MQTT
#define GH_CACHE_PRD "max-age=604800"   // период кеширования файлов для портала
```

#### Платформа
Библиотека сама определяет, на какой платформе компилируется. Для ESP8266/ESP32 предусмотрен отдельный флаг `GH_ESP_BUILD` (доступен после подключения библиотеки), включюащий сетевые возможности в компиляцию. Для создания кросс-платформенного кода можно "прятать" код для ESPxx внутри условной конструкции:

```cpp
#include <GyverHub.h>

#ifdef GH_ESP_BUILD
// этот код будет компилироваться только для esp
#endif
```

#### Дефайны настроек
Вводятся **до** подключения библиотеки

```cpp
#define ATOMIC_FS_UPDATE    // OTA обновление сжатым .gz файлом (вместо .bin)
#define GH_ASYNC            // использовать ASYNC библиотеки

// включить сайт в память программы, ~40кБ (не нужно загружать файлы в память)
#define GH_INCLUDE_PORTAL

// полное ОТКЛЮЧЕНИЕ модулей из кода библиотеки
#define GH_NO_STREAM    // поддержка Stream режима
#define GH_NO_PORTAL    // открытие сайта из памяти esp
#define GH_NO_WS        // WebSocket
#define GH_NO_MQTT      // MQTT
#define GH_NO_FS        // работа с файлами (включая ОТА!)
#define GH_NO_OTA       // ОТА файлом с приложения
#define GH_NO_OTA_URL   // ОТА по URL
#define GH_NO_DNS       // DNS сервер (открытие AP при подключении)

#define GH_NO_HTTP_UPLOAD     // загрузка по HTTP
#define GH_NO_HTTP_DOWNLOAD   // скачивание по HTTP
#define GH_NO_HTTP_OTA        // OTA по HTTP
```

</details>

<details>
<summary>Инициализация</summary>

Иконки Font Awesome v5 Solid, бесплатный пак:
- [Поиск иконок](https://fontawesome.com/v5/search?o=r&m=free&s=solid)
- Вставлять САМ СИМВОЛ в "строку", например `""` , `""` , `""`
- Пустая строка `""` по умолч. - отключить иконку

```cpp
GyverHub;
GyverHub(char* prefix);                                         // + префикс сети
GyverHub(char* prefix, char* name);                             // + имя в списке устройств
GyverHub(char* prefix, char* name, char* icon);                 // + иконка в списке устройств
GyverHub(char* prefix, char* name, char* icon, uint32_t id);    // + вручную задать ID устройства
```
> Примечание: id нужно обязательно задавать для отличных от ESP платформ (для esp генерируется автоматически). При задании id у esp он заменит сгенерированный библиотекой

</details>

<details>
<summary>Система</summary>

```cpp
// =================== CONFIG ==================
// Аналог конструктора
void config(char* nprefix, char* nname, char* nicon, uint32_t nid);

void setVersion(char* v);   // установить версию прошивки для отображения в Info
void begin();               // запустить
void end();                 // остановить
bool tick();                // тикер, вызывать в loop. Вернёт true, если система запущена

// ================== MODULES =================
// по умолчанию все модули включены
void modules.set(uint16_t mods);    // включить модуль
void modules.unset(uint16_t mods);  // выключить модуль

// можно выбрать несколько модулей с разделителем | 
// modules.set(GH_MOD_DOWNLOAD | GH_MOD_UPLOAD | GH_MOD_INFO);
// modules.unset(GH_MOD_DOWNLOAD | GH_MOD_UPLOAD | GH_MOD_INFO);

bool modules.read(GHmodule_t mod);  // проверить включен ли модуль
void modules.setAll();              // установить все
void modules.unsetAll();            // выключить все

// ==================== PIN ===================
void setPIN(uint32_t npin); // установить пин-код устройства (число больше 1000, не может начинаться с 0)
uint32_t getPIN();          // прочитать пин-код

// =================== BUILD ==================
void onBuild(f);            // подключить билдер. Функция вида void f()
void refresh();             // обновить панель управления. Вызывать внутри бидлдера

// свойства билда
GHbuild getBuild();         // получить все свойства текущего билда. Вызывать внутри билдера
bool buildRead();           // true - билдер вызван для set или read операций
bool buildUI();             // true - если билдер вызван для запроса компонентов (при загрузке панели управления)

const GHaction& action();   // получить текущее действие для ручной обработки значений
// к результату можно применять name/nameString/value/valueInt/valueFloat/valueString
// например action().valueInt()

// ================== STATUS ==================
void onEvent(f);            // подключить обработчик изменения статуса. Функция вида void f(GHevent_t event, GHconn_t from)
bool running();             // вернёт true, если система запущена
bool focused();             // true - панель управления сейчас открыта в приложении
bool focused(GHconn_t c);   // проверить фокус по указанному типу связи

// подключить функцию-обработчик перезагрузки. Будет вызвана перед перезагрузкой. Функция вида void f(GHreason_t r)
void onReboot(f);

// подключить обработчик запроса клиента. Функция вида bool f(GHbuild b)
// должна вернуть true, чтобы обработать запрос
void onRequest(f);

// =================== CLI ===================
void onCLI(f);          // подключить обработчик входящих сообщений с веб-консоли. Функция вида void f(String& s)
void print(String s);   // отправить текст в веб-консоль
void print(String s, uint32_t color);    // + цвет

// =================== INFO ==================
void onInfo(f);     // подключить функцию-сборщик инфо. Функция вида void f(GHinfo_t info)
void addInfo(String label, String text);    // добавить поле в info

// ================= NOTIFY ==================
void sendPush(String text);                     // отправить пуш-уведомление
void sendAlert(String text);                    // показать всплывающее окно

void sendNotice(String text);                   // отправить всплывающее уведомление
void sendNotice(String text, uint32_t color);   // + цвет

// ================= UPDATE ==================
// отправить update с указанием имени и значения
void sendUpdate(String name, String value);

// отправить update по имени
// - значение будет прочитано в билдере
// - имена можно передать списком через запятую
// - нельзя вызывать внутри билидера
void sendUpdate(String name);

// автоматически рассылать обновления клиентам при действиях на странице (умолч. true)
void sendUpdateAuto(bool f);

// ============= CANVAS UPDATE ==============
// обновление canvas
void sendCanvasBegin(String name, GHcanvas& cv);  // начать отправку холста
void sendCanvasEnd(GHcanvas& cv);                 // закончить отправку холста

// второй вариант (не рекомендуется)
void sendCanvas(String name, GHcanvas& cv);       // отправить холст

// ================== MQTT ==================
// настроить MQTT (только TCP)
void setupMQTT(char* host, uint16_t port);
void setupMQTT(char* host, uint16_t port, char* login = 0, const char* pass = 0);
void setupMQTT(char* host, uint16_t port, char* login = 0, const char* pass = 0, uint8_t qos = 0, bool retained = 0);

void setupMQTT(IPAddress ip, uint16_t port);
void setupMQTT(IPAddress ip, uint16_t port, char* login = 0, const char* pass = 0);
void setupMQTT(IPAddress ip, uint16_t port, char* login = 0, const char* pass = 0, uint8_t qos = 0, bool retained = 0);
// для игнорирования login/pass нужно установить их 0

bool online();      // MQTT подключен

void turnOn();      // отправить MQTT LWT команду на включение
void turnOff();     // отправить MQTT LWT команду на выключение

// автоматически отправлять новое состояние на get-топик при изменении через set (умолч. false)
void sendGetAuto(bool v);

// отправить имя-значение на get-топик (MQTT)
void sendGet(String name, String value);

// отправить значение по имени компонента на get-топик (MQTT)
// - значение будет прочитано в билдере
// - имена можно передать списком через запятую
// - нельзя вызывать внутри билдера
void sendGet(String name);

// ============== MANUAL BUILD ==============
// подключить обработчик запроса при ручном соединении
// Функция вида void f(String& s, GHconn_t from, bool broadcast)
void onManual(f);                                   

// парсить команду вида PREFIX/ID/HUB_ID/CMD/NAME=VALUE, указать тип подключения
void parse(char* url, GHconn_t from);

// парсить команду вида PREFIX/ID/HUB_ID/CMD/NAME, value отдельно, указать тип подключения
void parse(char* url, char* val, GHconn_t from);

// установить размер буфера строки для сборки панели управления в режиме MANUAL (умолч. 0)
// 0 - пакет будет собран и отправлен цельной строкой, иначе пакет будет отправляться частями размером с буфер
void setBufferSize(uint16_t size);

// подключить объект Stream (Serial, Bluetooth Serial...) на обработку указанного соединения
void setupStream(Stream* nstream, GHconn_t nfrom);

// ============== MANUAL FETCH =============
// подключить обработчик скачивания. Функция вида f(String& path, bool start)
void onFetch(f);

// отправить файл (вызывать в обработчике onFetch)
void fetchFile(const char* path);

// отправить сырые данные (вызывать в обработчике onFetch)
void fetchBytes(uint8_t* bytes, uint32_t size);

// ============== MANUAL DATA ==============
// подключить обработчик данных (см. GyverHub.js API). Функция вида f(const char* name, const char* value)
void onData(f);

// ответить клиенту. Вызывать в обработчике onData (см. GyverHub.js API)
void answer(const String& data);

// отправить сырые данные вручную (см. GyverHub.js API)
void send(const String& data);
```
</details>

<details>
<summary>Утилиты</summary>

```cpp
// вернёт строку, находящуюся под индексом idx в строке str с разделителями div ("abc,def,xyz")
String GH_listIdx(const String& str, int idx, char div = ',');

// запишет имена файлов из папки path в строку str, соединив их разделителями div
void GH_listDir(String& str, const String& path = "/", char div = ',');

// разделитель c-строк по разделителям div. Не дублирует строку в памяти
char* GH_splitter(char* list, char div = ',');

// пример
char* str = "abc,def,xyz";
char* p = str;
GH_splitter(NULL);
while ((p = GH_splitter(str)) != NULL) {
  Serial.println(p);
}
```
</details>

## Компоненты
<details>
<summary>Общие параметры и особенности</summary>

### Типы данных
- `String` - для краткости под этим типом подразумевается `const String&`. По умолчанию может иметь значение пустой строки - `""`
    - `func("текст в кавычках");`  - строки const char*
    - `String s; func(s);`         - String-строки, объявленные выше
    - `func(String(123) + 456);`   - String-строки, объявленные внутри (не рекомендуется, но иногда удобно)
    - `func(F("строка в flash"));` - строки, сохранённые в Flash памяти через F() - `F("строка")`
    - `func(FPSTR(pgm_str));` - здесь `pgm_str` - строка, сохранённая в `PROGMEM`
- `FSTR` - для краткости под этим типом подразумевается `const __FlashStringHelper*` - строки, сохранённые в Flash памяти. Так как это указатель, по умолчанию может иметь значение `0` или `nullptr`
    - `func(F("F-строка"));`  - строки, сохранённые в Flash памяти через F() - `F("строка")`
    - `func(FPSTR(pgm_str));` - здесь `pgm_str` - строка, сохранённая в `PROGMEM`

> Разница между `FSTR` и `String` - почти у всех функций компонентов есть два варианта - с FSTR и String строками, вариант с FSTR более быстрый и эффективный с точки зрения памяти. При использовании FSTR-строк во всех аргументах компилятор выбирает FSTR-функцию

### Параметры
- `name` - уникальное имя, по которому библиотека может обратиться к компоненту. Может генерироваться автоматически или задаваться вручную, для установки вручную предусмотрена функция компонента, оканчивающаяся на `_`, например `Button_("имя")`. Задавать имя вручную нужно в случаях, когда на компонент будут отправляться обновления дальше в программе или он будет читаться/устанавливаться из системы умного дома. Если задать имя пустой строкой - оно будет сгенерировано автоматически, как при вызове функции компонента без `_` на конце
    - Требования к заданному имени: *только английские буквы, цифры и знак подчёркивания. Не должно начинаться с подчёркивания*
- `label` - подпись компонента. Если оставить пустую строку (как по умолч.) - подпись будет установлена как название типа компонента. Чтобы полностью убрать подпись - вместо строки нужно передать константу `GH_NO_LABEL`
- `color` - цвет компонента. Может задаваться разными способами:
    - 24-бит RGB, например `0xff0000`
    - Переменная типа `GHcolor`
    - Цвет темы по умолчанию - константа `GH_DEFAULT`
    - Константа цвета библиотеки: 
        - `GH_RED`
        - `GH_ORANGE`
        - `GH_YELLOW`
        - `GH_GREEN`
        - `GH_MINT`
        - `GH_AQUA`
        - `GH_BLUE`
        - `GH_VIOLET`
        - `GH_PINK`
- `тип* var` - адрес подключаемой переменной, например `(&myVar)`. Тип переменной должен соответствовать указанному в функции
    - Если подключение переменной не нужно - передай `0` вместо адреса
    - При подключении члена массива оператор `&` не нужен, например `(myArr[1])`
    - Если тип void (`void* var`) - подключить можно переменную любого типа из поддерживаемых (см. `GHdata_t` ниже)
- `GHdata_t type` - тип подключенной `void*` переменной:
    - `GH_NULL`     - переменная не подключена
    - `GH_STR`      - String
    - `GH_CSTR`     - char[]
    - `GH_BOOL`     - bool
    - `GH_INT8`     - int8_t
    - `GH_UINT8`    - uint8_t
    - `GH_INT16`    - int16_t
    - `GH_UINT16`   - uint16_t
    - `GH_INT32`    - int32_t
    - `GH_UINT32`   - uint32_t
    - `GH_FLOAT`    - float
    - `GH_DOUBLE`   - double
</details>

<details>
<summary>Оформление и навигация</summary>

```cpp
// ========================== ВИДЖЕТ ==========================
void BeginWidgets();            // начать строку виджетов
void BeginWidgets(int height);  // height - минимальная высота в px
void EndWidgets();              // завершить строку виджетов
void WidgetSize(int width);     // width - ширина следующего виджета в %

// ======================== ОФОРМЛЕНИЕ ========================
void Space();                   // пустое место (в режиме виджетов - пустой виджет)
void Space(int height);         // height - высота в px

void Title(String text);        // заголовок (автоматически завершает строку виджетов)

// ======================== НАВИГАЦИЯ =========================
// вкладки
// text - передать список пунктов через запятую без пробелов
// var - переменная, получит значение номера вкладки начиная с 0
bool Tabs(uint8_t* var, FSTR text, FSTR label = 0);
bool Tabs(uint8_t* var, String text, String label = "");
bool Tabs_(FSTR name, uint8_t* var, FSTR text, FSTR label = 0);
bool Tabs_(String name, uint8_t* var, String text, String label = "");

// выпадающее меню устройства, передать список пунктов через запятую без пробелов
// номер текущего пункта хранится в переменной menu - член класса GyverHub
bool Menu(String text);
```
</details>

<details>
<summary>Текст/Таблица</summary>

```cpp
// крупный цветной текст
// size - размер шрифта в px
// может обновляться по name
void Label(String value = "", FSTR label = 0, uint32_t color = GH_DEFAULT, int size = 40);
void Label(String value = "", String label = "", uint32_t color = GH_DEFAULT, int size = 40);
void Label_(FSTR name, String value = "", FSTR label = 0, uint32_t color = GH_DEFAULT, int size = 40);
void Label_(String name, String value = "", String label = "", uint32_t color = GH_DEFAULT, int size = 40);

// дисплей
// Для переноса строк используй символ '\n'
// rows - количество строк
// size - размер шрифта в px
// может обновляться по name
void Display(FSTR value = 0, FSTR label = 0, uint32_t color = GH_DEFAULT, int rows = 2, int size = 40);
void Display(String value = "", String label = "", uint32_t color = GH_DEFAULT, int rows = 2, int size = 40);
void Display_(FSTR name, FSTR value = 0, FSTR label = 0, uint32_t color = GH_DEFAULT, int rows = 2, int size = 40);
void Display_(String name, String value = "", String label = "", uint32_t color = GH_DEFAULT, int rows = 2, int size = 40);

// таблица
// text - таблица в формате CSV: столбцы разделяются ',', строки - '\n'. Запятую внутри ячейки нужно экранировать \",\"
// align - выравнивание, список вида "center,left,right", должен соответствовать кол-ву столбцов
// width - ширины столбцов в %, список вида "50,25,25", должен соответствовать кол-ву столбцов
void Table(FSTR text = nullptr, FSTR align = nullptr, FSTR width = nullptr, FSTR label = nullptr);
void Table(CSREF text = "", CSREF align = "", CSREF width = "", CSREF label = "");
void Table_(FSTR name, FSTR text = nullptr, FSTR align = nullptr, FSTR width = nullptr, FSTR label = nullptr);
void Table_(CSREF name, CSREF text = "", CSREF align = "", CSREF width = "", CSREF label = "");
```
</details>

<details>
<summary>Индикация</summary>

```cpp
// светодиод
// value - состояние, 0 выкл, 1 вкл
// icon - иконка Font Awesome строкой, например F("") (ссылка на иконки выше)
// может обновляться по name
void LED(bool value = 0, FSTR label = 0, FSTR icon = 0);
void LED(bool value = 0, String label = "", String icon = "");
void LED_(FSTR name, bool value = 0, FSTR label = 0, FSTR icon = 0);
void LED_(String name, bool value = 0, String label = "", String icon = "");

// индикаторная шкала
// text - текст после значения (например "°C")
// minv - минимальное значение
// maxv - максимальное значение
// step - шаг
// может обновляться по name
void Gauge(float value = 0, FSTR text = 0, FSTR label = 0, float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
void Gauge(float value = 0, String text = "", String label = "", float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
void Gauge_(FSTR name, float value = 0, FSTR text = 0, FSTR label = 0, float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
void Gauge_(String name, float value = 0, String text = "", String label = "", float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
```
</details>

<details>
<summary>Кнопки</summary>

```cpp
// кнопка с текстом
// если первым символом в названии будет иконка - она выведется на кнопку. Пробел после иконки игнорируется
// var - переменная, будет true пока кнопка удерживается
// size - размер шрифта, px
bool Button(GHbutton* var = 0, FSTR text = 0, uint32_t color = GH_DEFAULT, int size = 20);
bool Button(GHbutton* var = 0, String text = "", uint32_t color = GH_DEFAULT, int size = 20);
bool Button_(FSTR name, GHbutton* var = 0, FSTR text = 0, uint32_t color = GH_DEFAULT, int size = 20);
bool Button_(String name, GHbutton* var = 0, String text = "", uint32_t color = GH_DEFAULT, int size = 20);

// кнопка с иконкой
bool ButtonIcon(GHbutton* var = 0, FSTR icon = 0, uint32_t color = GH_DEFAULT, int size = 50);
bool ButtonIcon(GHbutton* var = 0, String icon = "", uint32_t color = GH_DEFAULT, int size = 50);
bool ButtonIcon_(FSTR name, GHbutton* var = 0, FSTR icon = 0, uint32_t color = GH_DEFAULT, int size = 50);
bool ButtonIcon_(String name, GHbutton* var = 0, String icon = "", uint32_t color = GH_DEFAULT, int size = 50);

// примечание: кнопки выглядят по разному в режиме виджетов и без него!
```

Если подключить переменную `GHbutton`, то можно узнать о факте отпускания кнопки, а также опрашивать состояние кнопки ниже в программе:
```cpp
GHbutton b1, b2;

void build() {
  GHbutton b0;
  if (hub.Button(&b0)) {
    Serial.println(b0 ? "b0 press" : "b0 release");
  }
  hub.Button(&b1);
  hub.Button(&b2);
}

void loop() {
  if (b1) {
    static GHtimer tmr(500);
    if (tmr.ready()) Serial.println("hold b1");
  }

  if (b2.changed()) Serial.println(b2 ? "b2 press" : "b2 release");
}
```
</details>

<details>
<summary>Поле ввода/пароль</summary>

```cpp
// ввод любых данных
// max - макс. кол-во символов, 0 - лимит отключен
// regex - регулярное выражение, текст не отправится если не соответствует
// может обновляться по name
bool Input(void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0, int max = 0, FSTR regex = 0, uint32_t color = GH_DEFAULT);
bool Input(void* var = 0, GHdata_t type = GH_NULL, String label = "", int max = 0, String regex = "", uint32_t color = GH_DEFAULT);
bool Input_(FSTR name, void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0, int max = 0, FSTR regex = 0, uint32_t color = GH_DEFAULT);
bool Input_(String name, void* var = 0, GHdata_t type = GH_NULL, String label = "", int max = 0, String regex = "", uint32_t color = GH_DEFAULT);

// RegEx
// backslash экранируем! '\\'
// Примеры: "^abc" - текст начинается с abc, "def$" - заканчивается на def, "^\\d{4}$" - содержит только 4 цифры
// "^\\d{5,10}$" - содержит от 5 до 10 цифр, "^\\d{,5}$" - не более 5 цифр
// константы: GH_NUMBERS (только цифры), GH_LETTERS (только буквы), GH_LETTERS_S (маленькие буквы), GH_LETTERS_C (большие буквы)
// дока http://htmlbook.ru/html/input/pattern

// пароль
// может обновляться по name
bool Pass(void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0, int max = 0, uint32_t color = GH_DEFAULT);
bool Pass(void* var = 0, GHdata_t type = GH_NULL, String label = "", int max = 0, uint32_t color = GH_DEFAULT);
bool Pass_(FSTR name, void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0, int max = 0, uint32_t color = GH_DEFAULT);
bool Pass_(String name, void* var = 0, GHdata_t type = GH_NULL, String label = "", int max = 0, uint32_t color = GH_DEFAULT);
```
</details>

<details>
<summary>Слайдер/спиннер</summary>

```cpp
// слайдер
// minv - минимальное значение
// maxv - максимальное значение
// step - шаг
// может обновляться по name
bool Slider(void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0, float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
bool Slider(void* var = 0, GHdata_t type = GH_NULL, String label = "", float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
bool Slider_(FSTR name, void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0, float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
bool Slider_(String name, void* var = 0, GHdata_t type = GH_NULL, String label = "", float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);

// спиннер
// minv - минимальное значение
// maxv - максимальное значение
// step - шаг
// может обновляться по name
bool Spinner(void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0, float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
bool Spinner(void* var = 0, GHdata_t type = GH_NULL, String label = "", float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
bool Spinner_(FSTR name, void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0, float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
bool Spinner_(String name, void* var = 0, GHdata_t type = GH_NULL, String label = "", float minv = 0, float maxv = 100, float step = 1, uint32_t color = GH_DEFAULT);
```
</details>

<details>
<summary>Джойстик/Dpad</summary>

```cpp
// Джойстик
// autoc - автоматический возврат в центр
// exp - обработка позиции по экспоненте (менее чувствительно при малых отклонениях)
// диапазон pos: -255.. 255
bool Joystick(GHpos* pos, bool autoc = 1, bool exp = 0, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Joystick(GHpos* pos, bool autoc = 1, bool exp = 0, String label = "", uint32_t color = GH_DEFAULT);
bool Joystick_(FSTR name, GHpos* pos, bool autoc = 1, bool exp = 0, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Joystick_(String name, GHpos* pos, bool autoc = 1, bool exp = 0, String label = "", uint32_t color = GH_DEFAULT);

// Dpad (4-х кнопочный джойстик)
// диапазон pos: -1.. 1
bool Dpad(GHpos* pos, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Dpad(GHpos* pos, String label = "", uint32_t color = GH_DEFAULT);
bool Dpad_(FSTR name, GHpos* pos, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Dpad_(String name, GHpos* pos, String label = "", uint32_t color = GH_DEFAULT);
```

Чтобы получить координаты джойстика/dpad, нужно подключить переменную `GHpos`:
```cpp
GHpos pos2;

void build() {
  // первый обработаем в билдере
  GHpos pos1;
  if (hub.Joystick(&pos1)) {
    Serial.println(pos1.x);
    Serial.println(pos1.y);
  }

  // второй в loop
  hub.Dpad(&pos2);
}

void loop() {
  if (pos2.changed()) {
    Serial.println("dpad:");
    Serial.println(pos2.x);
    Serial.println(pos2.y);
  }
}
```
</details>

<details>
<summary>Выключатели</summary>

```cpp
// выключатель
// может обновляться по name
bool Switch(bool* var = 0, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Switch(bool* var = 0, String label = "", uint32_t color = GH_DEFAULT);
bool Switch_(FSTR name, bool* var = 0, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Switch_(String name, bool* var = 0, String label = "", uint32_t color = GH_DEFAULT);

// выключатель-иконка
// может обновляться по name
bool SwitchIcon(bool* var = 0, FSTR label = 0, FSTR icon = 0, uint32_t color = GH_DEFAULT);
bool SwitchIcon(bool* var = 0, String label = "", String icon = "", uint32_t color = GH_DEFAULT);
bool SwitchIcon_(FSTR name, bool* var = 0, FSTR label = 0, FSTR icon = 0, uint32_t color = GH_DEFAULT);
bool SwitchIcon_(String name, bool* var = 0, String label = "", String icon = "", uint32_t color = GH_DEFAULT);

// выключатель-текст
// может обновляться по name
bool SwitchText(bool* var = 0, FSTR label = 0, FSTR text = 0, uint32_t color = GH_DEFAULT);
bool SwitchText(bool* var = 0, String label = "", String text = "", uint32_t color = GH_DEFAULT);
bool SwitchText_(FSTR name, bool* var = 0, FSTR label = 0, FSTR text = 0, uint32_t color = GH_DEFAULT);
bool SwitchText_(String name, bool* var = 0, String label = "", String text = "", uint32_t color = GH_DEFAULT);
```
</details>

<details>
<summary>Дата/время</summary>

```cpp
// подключается 32-бит переменная, хранящая unix-время
// может подключаться объект класса Stamp для удобства работы с временем

// ввод даты
// может обновляться по name
bool Date(uint32_t* var, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Date(uint32_t* var, String label = "", uint32_t color = GH_DEFAULT);
bool Date_(FSTR name, uint32_t* var, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Date_(String name, uint32_t* var, String label = "", uint32_t color = GH_DEFAULT);

// ввод времени
// может обновляться по name
bool Time(uint32_t* var, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Time(uint32_t* var, String label = "", uint32_t color = GH_DEFAULT);
bool Time_(FSTR name, uint32_t* var, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Time_(String name, uint32_t* var, String label = "", uint32_t color = GH_DEFAULT);

// ввод даты и времени
// может обновляться по name
bool DateTime(uint32_t* var, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool DateTime(uint32_t* var, String label = "", uint32_t color = GH_DEFAULT);
bool DateTime_(FSTR name, uint32_t* var, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool DateTime_(String name, uint32_t* var, String label = "", uint32_t color = GH_DEFAULT);
```
</details>

<details>
<summary>Выбор</summary>

```cpp
// выпадающий список
// text - передать список пунктов через запятую без пробелов
// может обновляться по name
bool Select(uint8_t* var, FSTR text, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Select(uint8_t* var, String text, String label = "", uint32_t color = GH_DEFAULT);
bool Select_(FSTR name, uint8_t* var, FSTR text, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Select_(String name, uint8_t* var, String text, String label = "", uint32_t color = GH_DEFAULT);

// флаги
// text - передать список пунктов через запятую без пробелов
// может обновляться по name
bool Flags(GHflags* var = 0, FSTR text = 0, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Flags(GHflags* var = 0, String text = "", String label = "", uint32_t color = GH_DEFAULT);
bool Flags_(FSTR name, GHflags* var = 0, FSTR text = 0, FSTR label = 0, uint32_t color = GH_DEFAULT);
bool Flags_(String name, GHflags* var = 0, String text = "", String label = "", uint32_t color = GH_DEFAULT);
```
</details>

<details>
<summary>Цвет</summary>

По типу `GHcolor` читай подробнее ниже
```cpp
// может обновляться по name
bool Color(GHcolor* var = 0, FSTR label = 0);
bool Color(GHcolor* var = 0, String label = "");
bool Color_(FSTR name, GHcolor* var = 0, FSTR label = 0);
bool Color_(String name, GHcolor* var = 0, String label = "");
```
</details>

<details>
<summary>Всплывающие окна</summary>

```cpp
// всплывающее окно ввода
// для активации отправь sendUpdate() с именем компонента и пустой строкой или новым текстом для окна
bool Prompt(void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0);
bool Prompt(void* var = 0, GHdata_t type = GH_NULL, String label = "");
bool Prompt_(FSTR name, void* var = 0, GHdata_t type = GH_NULL, FSTR label = 0);
bool Prompt_(String name, void* var = 0, GHdata_t type = GH_NULL, String label = "");

// всплывающее окно с кнопками ОК и ОТМЕНА
// для активации отправь sendUpdate() с именем и пустой строкой или новым текстом для окна
bool Confirm(bool* var = 0, FSTR label = 0);
bool Confirm(bool* var = 0, String label = "");
bool Confirm_(FSTR name, bool* var = 0, FSTR label = 0);
bool Confirm_(String name, bool* var = 0, String label = "");
```
</details>

<details>
<summary>Холст</summary>

Подробнее [читай здесь](https://github.com/GyverLibs/GyverHub/wiki/6.-%D0%A5%D0%BE%D0%BB%D1%81%D1%82-(Canvas))
```cpp
// пустой холст
// width - ширина, px
// height - высота, px
// pos - обработчик кликов
// может обновляться по name
bool Canvas(int width = 400, int height = 300, GHcanvas* cv = 0, GHpos* pos = 0, FSTR label = 0);
bool Canvas(int width = 400, int height = 300, GHcanvas* cv = 0, GHpos* pos = 0, String label = "");
bool Canvas_(FSTR name, int width = 400, int height = 300, GHcanvas* cv = 0, GHpos* pos = 0, FSTR label = 0);
bool Canvas_(String name, int width = 400, int height = 300, GHcanvas* cv = 0, GHpos* pos = 0, String label = "");

// начать холст с рисунком
// width - ширина, px
// height - высота, px
// pos - обработчик кликов
// может обновляться по name
bool BeginCanvas(int width = 400, int height = 300, GHcanvas* cv = 0, GHpos* pos = 0, FSTR label = 0);
bool BeginCanvas(int width = 400, int height = 300, GHcanvas* cv = 0, GHpos* pos = 0, String label = "");
bool BeginCanvas_(FSTR name, int width = 400, int height = 300, GHcanvas* cv = 0, GHpos* pos = 0, FSTR label = 0);
bool BeginCanvas_(String name, int width = 400, int height = 300, GHcanvas* cv = 0, GHpos* pos = 0, String label = "");

// закончить холст с рисунком
void EndCanvas();
```
</details>

<details>
<summary>Картинки и видео</summary>

```cpp
// картинки
// указывается путь во flash памяти, файл сам скачается
// может обновляться по name: указать путь для файла или "" для обновления по тому же пути
// для камеры смотри пример esp32cam-frame
void Image(FSTR path, FSTR label = nullptr);
void Image(CSREF path, CSREF label = "");
void Image_(FSTR name, FSTR path, FSTR label = nullptr);
void Image_(CSREF name, CSREF path, CSREF label = "");

// HTTPD видео поток. См. пример esp32cam-stream
void Stream(uint16_t port = 82);
```
</details>

<details>
<summary>Лог</summary>

По типу `GHlog` читай подробнее ниже
```cpp
// может обновляться по name
void Log(GHlog* log, FSTR label = 0);
void Log(GHlog* log, String label = "");
void Log_(FSTR name, GHlog* log, FSTR label = 0);
void Log_(String name, GHlog* log, String label = "");
```
</details>

<details>
<summary>Кастомный код</summary>

```cpp
// пустой компонент, не отображается в панели управления
// может быть опрошен и установлен через MQTT и прямые запросы API
bool Dummy(void* var = 0, GHdata_t type = GH_NULL);
bool Dummy_(FSTR name, void* var = 0, GHdata_t type = GH_NULL);
bool Dummy_(String name, void* var = 0, GHdata_t type = GH_NULL);

// блок HTML кода
// может обновляться по name
void HTML(FSTR value = 0, FSTR label = 0);
void HTML(String value = "", String label = "");
void HTML_(FSTR name, FSTR value = 0, FSTR label = 0);
void HTML_(String name, String value = "", String label = "");

// JavaScript код, будет исполнен в eval()
void JS(String text);
```
</details>

## Canvas API
<details>
<summary>GHcanvas</summary>

```cpp
void extBuffer(String* sptr);   // подключить внешний буфер
void clearBuffer();             // очистить буфер (внутренний)
void custom(String s);          // добавить строку кода на js
```
</details>

<details>
<summary>Processing API</summary>

https://processing.org/reference/
```cpp
// =============== BACKGROUND ===============
void clear();                           // очистить полотно
void background();                      // залить полотно установленным в fill() цветом
void background(uint32_t hex);          // залить полотно указанным цветом
void background(uint32_t hex, a);       // залить полотно указанным цветом + прозрачность 0-255

// ================== FILL ==================
void noFill();                          // отключить заливку (по умолч. вкл)
void fill(uint32_t hex);                // выбрать цвет заливки
void fill(uint32_t hex, uint8_t a);     // выбрать цвет заливки + прозрачность 0-255

// ================= STROKE =================
void noStroke();                        // отключить обводку (по умолч. вкл)
void stroke(uint32_t hex);              // выбрать цвет обводки
void stroke(uint32_t hex, uint8_t a);   // выбрать цвет обводки + прозрачность 0-255
void strokeWeight(int v);               // толщина обводки, px

// соединение линий: CV_MITER (умолч), CV_BEVEL, CV_ROUND
// https://processing.org/reference/strokeJoin_.html
void strokeJoin(v);

// края линий: CV_PROJECT (умолч), CV_ROUND, CV_SQUARE
// https://processing.org/reference/strokeCap_.html
void strokeCap(v);

// =============== PRIMITIVES ===============
void point(int x, int y);                   // точка
void circle(int x, int y, int r);           // окружность (x, y, радиус), px
void line(int x1, int y1, int x2, int y2);  // линия (координаты начала и конца)
void square(int x, int y, int w);           // квадрат

void rect(int x, int y, int w, int h);          // прямоугольник
void rect(int x, int y, int w, int h, int tl);  // скруглить углы
void rect(int x, int y, int w, int h, int tl, int tr, int br, int bl);  // скруглить углы
// top left, top right, bottom right, bottom left

void triangle(int x1, int y1, int x2, int y2, int x3, int y3);  // треугольник (координаты углов)
void quadrangle(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);    // четырёхугольник (координаты углов)

// режим окружности: CV_CENTER (умолч), CV_CORNER
// https://processing.org/reference/ellipseMode_.html
void ellipseMode(mode);

// режим прямоугольника: CV_CORNER (умолч), CV_CORNERS, CV_CENTER, CV_RADIUS
// https://processing.org/reference/rectMode_.html
void rectMode(mode);

// ================== TEXT ==================
void text(String text, int x, int y, int w = 0); // вывести текст, опционально макс длина
void textFont(const char* name);    // шрифт
void textSize(int size);            // размер шрифта, px

// выравнивание текста
// h: CV_LEFT, CV_CENTER, CV_RIGHT
// v: TXT_TOP, TXT_BOTTOM, TXT_CENTER, TXT_BASELINE
// https://processing.org/reference/textAlign_.html
void textAlign(h, v);

// ================= CANVAS =================
void push();                    // сохранить конфигурацию полотна
void pop();                     // восстановить конфигурацию полотна
void rotate(float v);           // вращать (радианы)
void translate(int x, int y);   // переместить область рисования (px)
```
</details>

<details>
<summary>HTML API</summary>

https://www.w3schools.com/tags/ref_canvas.asp

```cpp
void fillStyle(uint32_t hex);               // цвет заполнения
void fillStyle(uint32_t hex, uint8_t a);    // цвет заполнения

void strokeStyle(uint32_t hex);             // цвет обводки
void strokeStyle(uint32_t hex, uint8_t a);  // цвет обводки

void shadowColor(uint32_t hex);             // цвет тени
void shadowColor(uint32_t hex, uint8_t a);  // цвет тени

void shadowBlur(int v);         // размытость тени, px
void shadowOffsetX(int v);      // отступ тени, px
void shadowOffsetY(int v);      // отступ тени, px

// края линий: CV_BUTT (умолч), CV_ROUND, CV_SQUARE
// https://www.w3schools.com/tags/canvas_linecap.asp
void lineCap(v);

// соединение линий: CV_MITER (умолч), CV_BEVEL, CV_ROUND
// https://www.w3schools.com/tags/canvas_linejoin.asp
void lineJoin(v);

// ширина линий, px
void lineWidth(int v);

// длина соединения CV_MITER, px
// https://www.w3schools.com/tags/canvas_miterlimit.asp
void miterLimit(int v);

// шрифт: "30px Arial"
// https://www.w3schools.com/tags/canvas_font.asp
void font(String v);

// выравнивание текста: CV_START (умолч), CV_END, CV_CENTER, CV_LEFT, CV_RIGHT
// https://www.w3schools.com/tags/canvas_textalign.asp
void textAlign(v);

// позиция текста: CV_ALPHABETIC (умолч), CV_TOP, CV_HANGING, CV_MIDDLE, CV_IDEOGRAPHIC, CV_BOTTOM
// https://www.w3schools.com/tags/canvas_textbaseline.asp
void textBaseline(v);

// прозрачность рисовки, 0.0-1.0
void globalAlpha(float v);

// тип наложения графики: CV_SRC_OVER (умолч), CV_SRC_ATOP, CV_SRC_IN, CV_SRC_OUT, CV_DST_OVER, CV_DST_ATOP, CV_DST_IN, CV_DST_OUT, CV_LIGHTER, CV_COPY, CV_XOR
// https://www.w3schools.com/tags/canvas_globalcompositeoperation.asp
void globalCompositeOperation(v);

// скруглённый прямоугольник
void roundRect(int x, int y, int w, int h, int tl = 0, int tr = -1, int br = -1, int bl = -1);

void drawRect(int x, int y, int w, int h);      // прямоугольник (в HTML API это rect)
void fillRect(int x, int y, int w, int h);      // закрашенный прямоугольник
void strokeRect(int x, int y, int w, int h);    // обведённый прямоугольник
void clearRect(int x, int y, int w, int h);     // очистить область
void fill();                                    // залить
void stroke();                                  // обвести
void beginPath();                               // начать путь
void moveTo(int x, int y);                      // переместить курсор
void closePath();                               // завершить путь (провести линию на начало)
void lineTo(int x, int y);                      // нарисовать линию от курсора

// ограничить область рисования
// https://www.w3schools.com/tags/canvas_clip.asp
void clip();

// провести кривую
// https://www.w3schools.com/tags/canvas_quadraticcurveto.asp
void quadraticCurveTo(int cpx, int cpy, int x, int y);

// провести кривую Безье
// https://www.w3schools.com/tags/canvas_beziercurveto.asp
void bezierCurveTo(int cp1x, int cp1y, int cp2x, int cp2y, int x, int y);

// провести дугу (радианы)
// https://www.w3schools.com/tags/canvas_arc.asp
void arc(int x, int y, int r, float sa = 0, float ea = TWO_PI, bool ccw = 0);

// скруглить
// https://www.w3schools.com/tags/canvas_arcto.asp
void arcTo(int x1, int y1, int x2, int y2, int r);

// масштабировать область рисования
// https://www.w3schools.com/tags/canvas_scale.asp
void scale(int sw, int sh);

// вращать область рисования (радианы)
// https://www.w3schools.com/tags/canvas_rotate.asp
void rotate(float v);

// перемещать область рисования
// https://www.w3schools.com/tags/canvas_translate.asp
void translate(int x, int y);

// вывести закрашенный текст, опционально макс. длина
void fillText(String text, int x, int y, int w = 0);

// вывести обведённый текст, опционально макс. длина
void strokeText(String text, int x, int y, int w = 0);

// вывести картинку
// https://www.w3schools.com/tags/canvas_drawimage.asp
void drawImage(String img, int x, int y);
void drawImage(String img, int x, int y, int w, int h);
void drawImage(String img, int sx, int sy, int sw, int sh, int x, int y, int w, int h);

// сохранить конфигурацию полотна
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
void save();

// восстановить конфигурацию полотна
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
void restore();
```
</details>

<details>
<summary>Обработка геометрии</summary>

```cpp
// расстояние между двумя точками
int16_t GHdist(int16_t x0, int16_t y0, int16_t x1, int16_t y1);

// точка xy лежит внутри прямоугольника (координаты угла и размеры)
bool GHinRect(int16_t x, int16_t y, int16_t rx, int16_t ry, int16_t w, int16_t h);

// точка xy лежит внутри окружности (координаты центра и радиус)
bool GHinCircle(int16_t x, int16_t y, int16_t cx, int16_t cy, int16_t r);
```
</details>

## Вспомогательные классы
<details>
<summary>Stamp</summary>

Удобное ханение и преобразование даты и времени, устанавливается и подключается как [отдельная библиотека](https://github.com/GyverLibs/Stamp). Объект типа `Stamp` можно передать в компоненты `Date`, `Time` и `DateTime` в качестве переменной для хранения времени:

```cpp
#include <Stamp.h>
Stamp gdate;

void build() {
  hub.Date(&gdate);
}
```
</details>

<details>
<summary>GHlog</summary>

"Printable" текстовый буфер для ведения оффлайн логов
```cpp
// в лог можно делать print()/println()
void begin(int n = 64);     // начать и указать размер буфера
void end();                 // остановить
void read(String* s);       // прочитать в строку
String read();              // прочитать строкой
void clear();               // очистить
bool available();           // есть данные
bool state();               // запущен
int length();               // длина
```

Пример:
```cpp
GHlog mylog;

void build() {
  hub.Log(&dlog);   // отправить
}

void setup() {
   // ...
  mylog.begin(200);   // указать размер, умолч. 64
}

void loop() {
  mylog.print("Hello diary! #");
  mylog.println(millis());
  delay(2000);
}
```
</details>

<details>
<summary>GHbutton</summary>

Обработка кнопки
```cpp
bool state;     // текущее состояние кнопки
// также может быть опрошено с объекта - if (btn) ...

// индикатор того, что состояние кнопки изменилось
// автоматически сбросится в false
bool changed();
```
</details>

<details>
<summary>GHpos</summary>

Хранение позиции для обработки джойстика и холста
```cpp
// конструктор
GHpos(int16_t x, int16_t y);

// координаты
int16_t x;
int16_t y;

// индикатор того, что координаты изменились
// автоматически сбросится в false
bool changed();

// вспомогательные функции для холста
// расстояние до точки
int16_t dist(int16_t x1, int16_t y1);

// точка лежит внутри прямоугольника
bool inRect(int16_t rx, int16_t ry, int16_t w, int16_t h);

// точка лежит внутри окружности
bool inCircle(int16_t cx, int16_t cy, int16_t r);
```
</details>

<details>
<summary>GHcolor</summary>

Хранение и преобразование цвета
```cpp
// содержит 3 компонента цвета
uint8_t r;
uint8_t g;
uint8_t b;

// конструктор
GHcolor();
GHcolor(uint8_t gray);         // (0..255) чёрный..белый
GHcolor(uint32_t hex, HEX);                     // HEX
GHcolor(uint8_t r, uint8_t g, uint8_t b);       // RGB (0..255)
GHcolor(uint8_t h, uint8_t s, uint8_t v, true); // HSV (0..255)

// установить
void setRGB(uint8_t r, uint8_t g, uint8_t b);   // RGB (0..255)
void setHSV(uint8_t h, uint8_t s, uint8_t v);   // HSV (0..255)
void setGray(uint8_t gray);                     // gray (0..255)
void setHue(uint8_t color);                     // hue (0..255)
void setHEX(uint32_t hex);                      // hex

// получить как 24-бит цвет
uint32_t getHEX();
```

Пример:
```cpp
GHcolor red(255, 0, 0);             // красный (RGB)
GHcolor green(50, 200, 255, true);  // пастельный зелёный (HSV)
GHcolor blue(0x0000ff, HEX);        // синий
GHcolor gray(100);                  // серый

GHcolor color;
color.setRGB(255, 255, 255);        // белый
color.setHSV(0, 150, 255);          // пастельный красный
color.setHEX(0xff00ff);             // розовый
color.setHue(100);                  // голубой
color.setGray(20);                  // тёмно-серый

// получить как hex
Serial.println(red.getHEX(), HEX);

// получить покомпонентно
Serial.print(red.r);
Serial.print(',');
Serial.print(red.g);
Serial.print(',');
Serial.println(red.b);
```
</details>

<details>
<summary>GHflags</summary>

Хранение однобитных флагов, максимум 16 штук
```cpp
uint16_t flags;

GHflags(uint16_t nflags);

// установить флаг под номером idx в значение val
void set(uint8_t idx, uint8_t val);

// получить значение флага под номером idx
uint8_t get(uint8_t idx);

// вывести строкой вида 010101010
String toString();
```
</details>

<details>
<summary>GHtimer</summary>

Простенький периодический таймер на millis()
```cpp
// конструктор
GHtimer();      // без запуска

// сразу запустить
GHtimer(uint32_t ms = 0, uint8_t seconds = 0, uint8_t minutes = 0, uint8_t hours = 0, uint8_t days = 0);

// запуск
void start();
void start(uint32_t ms);
void start(uint32_t ms, uint8_t seconds);
void start(uint32_t ms, uint8_t seconds, uint8_t minutes);
void start(uint32_t ms, uint8_t seconds, uint8_t minutes, uint8_t hours);
void start(uint32_t ms, uint8_t seconds, uint8_t minutes, uint8_t hours, uint8_t days);

void stop();    // остановить
bool ready();   // таймер сработал
```

Пример:
```cpp
GHtimer tmr1;       // глобальный

void setup() {
  tmr1.start(500);  // период 500 мс
}

void loop() {
  // статический, на 10 секунд
  static GHtimer tmr2(0, 10);
  if (tmr2.ready()) Serial.println("tmr2");

  // можно опрашивать готовность без ready()
  if (tmr1) Serial.println("tmr1");
}
```
</details>

<details>
<summary>GHbuild</summary>

Информация о билде
```cpp
GHbuild_t type;     // тип билда
GHclient client;    // данные клиента
GHaction action;    // действие
GHevent_t cmd;      // команда
```
</details>

<details>
<summary>GHclient</summary>

Информация о клиенте
```cpp
GHconn_t from;      // тип соединения
GHsource_t source;  // источник
char id[9];         // id клиента
String idString();  // id как String
```
</details>

<details>
<summary>GHaction</summary>

Информация о действии
```cpp
const char* name;       // имя компонента
const char* nameStr();  // имя как const char*
String nameString();    // имя как String

const char* value;      // значение компонента
const char* valueStr(); // значение как const char*
String valueString();   // значение как String
int32_t valueInt();     // значение как int (32 бит)
float valueFloat();     // значение как float
bool valueBool();       // значение как bool
GHcolor valueColor();   // значение как GHcolor
GHflags valueFlags();   // значение как GHflags
GHpos valuePos();       // значение как GHpos
```
</details>

## Типы данных и константы
<details>
<summary>Цвет</summary>

- `GH_RED`
- `GH_ORANGE`
- `GH_YELLOW`
- `GH_GREEN`
- `GH_MINT`
- `GH_AQUA`
- `GH_BLUE`
- `GH_VIOLET`
- `GH_PINK`
- `GH_DEFAULT`
</details>

<details>
<summary>GHdata_t (тип переменной)</summary>

```cpp
GH_NULL     // нет

GH_STR      // String
GH_CSTR     // char[]

GH_BOOL     // bool
GH_INT8     // int8_t
GH_UINT8    // uint8_t
GH_INT16    // int16_t
GH_UINT16   // uint16_t
GH_INT32    // int32_t
GH_UINT32   // uint32_t

GH_FLOAT    // float
GH_DOUBLE   // double
```
</details>

<details>
<summary>GHreason_t (причина перезагрузки)</summary>

```cpp
GH_REB_NONE         // нет
GH_REB_BUTTON       // по кнопке с сайта
GH_REB_OTA          // после ОТА обновления
GH_REB_OTA_URL      // после OTA обновления по URL
```

Для чтения как текст (`FlashStringHelper`) можно использовать функцию:
```cpp
FSTR GHreadReason(GHreason_t n);
```
</details>

<details>
<summary>GHinfo_t (тип инфо)</summary>

```cpp
GH_INFO_VERSION
GH_INFO_NETWORK
GH_INFO_MEMORY
GH_INFO_SYSTEM
```
</details>

<details>
<summary>GHbuild_t (тип билда)</summary>

```cpp
GH_BUILD_NONE       // нет
GH_BUILD_ACTION     // действие из панели управления
GH_BUILD_COUNT      // измерение размера пакета
GH_BUILD_READ       // чтение значения по имени (get, update)
GH_BUILD_UI         // сборка панели управления для отправки
GH_BUILD_TG         // сборка для Telegram
```

Для чтения как текст (`FlashStringHelper`) можно использовать функцию:
```cpp
FSTR GHreadBuild(GHbuild_t n);
```
</details>

<details>
<summary>GHconn_t (тип подключения)</summary>

```cpp
GH_SERIAL   // Serial
GH_BT       // Bluetooth
GH_WS       // WebSocket
GH_MQTT     // MQTT
GH_SYSTEM   // системное событие
```

Для чтения как текст (`FlashStringHelper`) можно использовать функцию:
```cpp
FSTR GHreadConn(GHconn_t n);
```
</details>

<details>
<summary>GHsource_t (источник подключения)</summary>

```cpp
GH_ESP      // внутренние обработчики ESP
GH_MANUAL   // ручной режим
GH_STREAM   // Stream
```

Для чтения как текст (`FlashStringHelper`) можно использовать функцию:
```cpp
FSTR GHreadConn(GHconn_t n);
```
</details>

<details>
<summary>GHmodule_t (отключаемые модули)</summary>

```cpp
GH_MOD_INFO     // разрешить вкладку инфо
GH_MOD_FSBR     // разрешить вкладку менеджера файлов
GH_MOD_FORMAT   // разрешить форматирование FS
GH_MOD_DOWNLOAD // разрешить скачивание
GH_MOD_UPLOAD   // разрешить загрузку
GH_MOD_OTA      // разрешить ОТА
GH_MOD_OTA_URL  // разрешить ОТА по URL
GH_MOD_REBOOT   // разрешить перезагрузку из инфо
GH_MOD_SET      // разрешить установку значений
GH_MOD_READ     // разрешить чтение
GH_MOD_DELETE   // разрешить удаление файлов
GH_MOD_RENAME   // разрешить переименование файлов

GH_MOD_SERIAL   // разрешить Serial
GH_MOD_BT       // разрешить Bluetooth
GH_MOD_WS       // разрешить WebSocket
GH_MOD_MQTT     // разрешить MQTT
```
</details>

<details>
<summary>GHevent_t (системное событие)</summary>

```cpp
GH_IDLE
GH_START
GH_STOP

GH_CONNECTING
GH_CONNECTED
GH_DISCONNECTED
GH_ERROR

GH_UNKNOWN
GH_DISCOVER_ALL
GH_DISCOVER
GH_DATA
GH_FOCUS
GH_UNFOCUS

GH_SET
GH_CLI
GH_PING

GH_READ_HOOK
GH_SET_HOOK
GH_INFO
GH_REBOOT
GH_FSBR
GH_FORMAT
GH_DELETE
GH_RENAME

GH_DOWNLOAD
GH_DOWNLOAD_CHUNK
GH_DOWNLOAD_ERROR
GH_DOWNLOAD_ABORTED
GH_DOWNLOAD_FINISH

GH_UPLOAD
GH_UPLOAD_CHUNK
GH_UPLOAD_ERROR
GH_UPLOAD_ABORTED
GH_UPLOAD_FINISH

GH_OTA
GH_OTA_CHUNK
GH_OTA_ERROR
GH_OTA_ABORTED
GH_OTA_FINISH

GH_OTA_URL
```

Для чтения как текст (`FlashStringHelper`) можно использовать функцию:
```cpp
FSTR GHreadEvent(GHevent_t n);
```
</details>


## API

<details>
<summary>Условные обозначения</summary>

| Имя      | Значение                |
|:---------|:------------------------|
| `PREFIX` | префикс сети            |
| `HUB_ID` | ID клиента              |
| `ID`     | ID устройства           |
| `CMD`    | команда                 |
| `NAME`   | имя команды             |
| `VALUE`  | значение                |

</details>

<details>
<summary>MQTT топики</summary>

#### Для подписки
| topic                     | value                     | Описание        |
|:--------------------------|:--------------------------|:----------------|
| `PREFIX/hub/ID/get/NAME`  | `VALUE`                   | get-топик       |
| `PREFIX/hub/ID/status`    | `'online'`<br>`'offline'` | Статус топик    |

#### Для отправки
| topic                        | value   | Описание                |
|:-----------------------------|:--------|:------------------------|
| `PREFIX/ID/read/NAME`        |         | Прочиать (в get-топик)  |
| `PREFIX/ID/set/NAME`         | `VALUE` | Установить              |

</details>

<details>
<summary>HTTP hook</summary>

### HTTP hook
Для использования WS обнаружения через HTTP hook устройство должно ответить на HTTP запрос `/hub_discover_all` на 80 порту ответом `OK`.

### HTTP возможности
Приложение запрашивает по адресу `/hub_http_cfg` JSON пакет вида:
```json
{
  "ota": 1,
  "upload": 1,
  "download": 1,
  "path": "/path/"
}
```

0 - параметр выключен, 1 - включен:
- `ota` - cервер должен принимать запросы на загрузку файлов методом HTTP POST на url `/ota`
- `upload` - сервер должен принимать запросы на загрузку файлов методом HTTP POST на url `/upload`
- `download` - Сервер должен отдавать по HTTP файлы, лежащие по пути `"path"`
</details>

<details>
<summary>Команды API</summary>

### MQTT
#### Для подписки

| topic                 | Описание          |
|:----------------------|:------------------|
| `PREFIX/hub`          | Broadcast ответы  |
| `PREFIX/hub/HUB_ID/#` | Ответы клиенту    |
| `PREFIX/hub/ID/get/#` | get топик         |

#### Для отправки

| topic                       | value    | Ответ        | Описание               |
|:----------------------------|:---------|:-------------|:-----------------------|
| `PREFIX`                    | `HUB_ID` | `{discover}` | discover all           |
| `PREFIX/ID`                 | `HUB_ID` | `{discover}` | discover               |
| `PREFIX/ID/HUB_ID/CMD`      |          | `{...}`      | command                |
| `PREFIX/ID/HUB_ID/CMD/NAME` |          | `{...}`      | command + name         |
| `PREFIX/ID/HUB_ID/CMD/NAME` | `VALUE`  | `{...}`      | command + name + value |

### URL

| URL                               | Ответ        | Описание               |
|:----------------------------------|:-------------|:-----------------------|
| `PREFIX`                          | `{discover}` | discover all           |
| `PREFIX/ID`                       | `{discover}` | discover               |
| `PREFIX/ID/HUB_ID/CMD`            | `...`        | command                |
| `PREFIX/ID/HUB_ID/CMD/NAME`       | `...`        | command + name         |
| `PREFIX/ID/HUB_ID/CMD/NAME=VALUE` | `...`        | command + name + value |

### Команды

| CMD           | Ответ                                 | Описание          |
|:--------------|:--------------------------------------|:------------------|
| `focus`       | `{ui}`                                | Запрос ПУ         |
| `ping`        | `{OK}`                                | Пинг              |
| `unfocus`     |                                       | Закрыть           |
| `info`        | `{info}`<br>`{ERR}`                   | Вкладка инфо      |
| `fsbr`        | `{fsbr}`<br>`{ERR}`<br>`{fs_error}`   | Вкладка файлов    |
| `format`      | `{OK}`<br>`{ERR}`                     | Форматировать FS  |
| `reboot`      | `{OK}`<br>`{ERR}`                     | Перезагрузить     |
| `fetch_chunk` | `{fetch_next_chunk}`<br>`{fetch_err}` | Скачивание        |


| CMD            | NAME                 | VALUE                  | Ответ                                | Описание                       |
|:---------------|:---------------------|:-----------------------|:-------------------------------------|:-------------------------------|
| `set`          | имя компонента       | значение компонента    | `{ui}`<br>`{OK}`                     | Установка значения             |
| `click`        | имя компонента       | `1` нажат, `2` отпущен | `{ui}`<br>`{OK}`                     | Клик                           |
| `cli`          | `'cli'`              | текст                  | `{OK}`                               | Отправка текста из консоли     |
| `delete`       | путь файла           |                        | `{fsbr}`<br>`{ERR}`                  | Удалить файл                   |
| `rename`       | путь файла           | новый путь файла       | `{fsbr}`<br>`{ERR}`                  | Переименовать/переместить файл |
| `fetch`        | путь файла           |                        | `{fetch_start}`<br>`{fetch_err}`     | Скачать файл                   |
| `upload`       | путь файла           |                        | `{upload_start}`<br>`{upload_err}`   | Начать загрузку файла          |
| `upload_chunk` | `'next'`<br>`'last'` | данные                 | `{upload_next_chunk}`<br>`{upload_end}`<br>`{upload_err}`    | Загрузка файла                 |
| `ota`          | `'flash'`<br>`'fs'`  |                        | `{ota_start}`<br>`{ota_err}`         | Начать OTA обновление          |
| `ota_chunk`    | `'next'`<br>`'last'` | данные                 | `{ota_next_chunk}`<br>`{ota_end}`<br>`{ota_err}`             | OTA обновление                 |
| `ota_url`      | `'flash'`<br>`'fs'`  | ссылка                 | `{OK}`<br>`{ERR}`                    | Начать OTA обновление из URL   |

Пакеты, отправляемые по инициативе устройства
- `{print}` - печать в консоль
- `{update}` - пакет обновлений
- `{push}` - уведомление

</details>

<details>
<summary>JSON-пакеты устройства</summary>

- Перед и после пакета должен быть символ переноса строки - `\n{}\n`. Это нужно для отправки пакетов частями (включение буфера в библиотеке для использования меньшего объёма оперативной памяти)

### {discover}
```json
{
  "id": 'id',
  "type": "discover",
  "name": 'имя',
  "icon": 'иконка',
  "PIN": хэш_пароля,
  "version": 'версия',
  "max_upl": размер_чанка,
  "ota_t": 'расширение_файла',
  "modules": маска_модулей
}
```

### {fsbr}
```json
{
  "id": 'id',
  "type": "fsbr",
  "total": всего_байт,
  "used": занято_байт,
  "fs": {
    "/путь": вес_байт
    ...
  }
}
```

### {info}
```json
{
  "id": 'id',
  "type": "info",
  "info": {
    "version":{
      "подпись": "значение",
      ...
    },
    "net":{
      "подпись": "значение",
      ...
    },
    "memory":{
      "подпись": [занято_байт, свободно_байт],
      "подпись": занято_байт,
      ...
    },
    "system":{
      "подпись": "значение",
      ...
    }
  }
}
```

</details>