# Дополнительные фичи
## Время приложения
При открытии ПУ приложение отправляет точное время (время операционной системы, на которой запущено приложение) в unix-формате. Это время можно использовать для синхронизации часов устройства и так далее. Для получения времени нужно подключить обработчик `onUnix`:
```cpp
void onunix(uint32_t stamp) {
    Serial.println(stamp);
}

void setup() {
    hub.onUnix(onunix);
}
```

## Веб-консоль
В настройках ПУ можно включить текстовую консоль. Сообщения, которые в неё отправляются, можно принять в обработчике `onCLI`:
```cpp
void cli(String str) {
    Serial.println(str);
}

void setup() {
    hub.onCLI(cli);
}
```
Для отправки сообщений обратно используется `void sendCLI(AnyText str)`

## Информация о билде
Внутри билдера можно узнать, кто и с какой целью вызвал билдер:
```cpp
void build(gh::Builder& b) {
    // это запрос виджетов
    if (b.build.isUI()) {
        Serial.println("=== UI BUILD ===");
    }

    // это действие с виджетом
    if (b.build.isSet()) {
        Serial.println("=== SET ===");

        // можно напрямую получить имя и значение виджета
        Serial.print("name: ");
        Serial.println(b.build.name);
        Serial.print("value: ");
        Serial.println(b.build.value);
    }

    // инфо о клиенте
    Serial.print("client from: ");
    Serial.println(gh::readConnection(b.build.client.connection()));
    Serial.print("ID: ");
    Serial.println(b.build.client.id);
    Serial.println();
}
```

Так как билдер динамический, это даёт возможность например показывать некоторые виджеты только определённому клиенту или только по конкретному способу связи:
```cpp
if (b.build.client.id == "abc123") b.Input();
```

А также перехватывать входящие значения без подключения переменной:
```cpp
if (b.Input().click()) Serial.println(b.build.value);
```

## Обработка запроса
Библиотека позволяет получить всю информацию о запросе до того, как он будет применён, и разрешить или запретить его. Это относится ко всем запросам с приложения, что позволяет очень гибко настроить систему. Например разрешить или какие то действия только определённым клиентам или по определённым способам связи, что может быть полезно для "общественных" проектов: у юзеров одни права, а у админа - другие. Длня начала нужно подключить обработчик запроса:
```cpp
bool req(gh::Request& r) {
    Serial.print("Request: ");
    Serial.print(gh::readConnection(r.client.connection()));    // подключение
    Serial.print(',');
    Serial.print(r.client.id);  // id клиента
    Serial.print(',');
    Serial.print(gh::readCMD(r.cmd));   // команда (см. доку)
    Serial.print(',');
    Serial.print(r.name);   // имя виджета
    Serial.print(',');
    Serial.print(r.value);  // значение виджета
    Serial.println();
    return 1;
}

void setup() {
    hub.onRequest(req);
}

```
Из функции нужно вернуть `true`, чтобы разрешить запрос!

## Модули
Доступ к устройству из приложения можно контролировать при помощи системы модулей: их можно включать и отключать. По умолчанию все модули **включены**. Список модулей:
```cpp
gh::Module::ModUI       // разрешить ПУ
gh::Module::ModInfo     // разрешить вкладку инфо
gh::Module::ModSet      // разрешить установку виджетов
gh::Module::ModRead     // разрешить чтение виджетов
gh::Module::ModGet      // разрешить чтение из билдера
gh::Module::ModData
gh::Module::ModReboot   // разрешить перезагрузку из инфо
gh::Module::ModFiles    // разрешить вкладку менеджера файлов
gh::Module::ModFormat   // разрешить форматирование FS
gh::Module::ModDelete   // разрешить удаление файлов
gh::Module::ModRename   // разрешить переименование файлов
gh::Module::ModCreate   // разрешить создание файлов
gh::Module::ModFetch    // разрешить скачивание
gh::Module::ModUpload   // разрешить скачивание
gh::Module::ModOta      // разрешить ОТА
gh::Module::ModOtaUrl   // разрешить ОТА по URL
```
Для включения нужно вызвать `hub.modules.set()`, для выключения - `hub.modules.clear()` и передать одну или несколько констант через `|`, например:

```cpp
// выключить менеджер файлов и кнопку переименования файлов
hub.modules.clear(gh::Module::ModFiles | gh::Module::ModRename);

// запретить все
hub.modules.clearAll();

// разрешить Info
hub.modules.set(gh::Module::ModInfo);
```

## Перехват скачивания
По умолчанию менеджер файлов и все виджеты с указанием пути к файлу работают с flash-памятью и файл автоматически открывается по указанному пути. Открытие файла можно перехватить в обработчике `onFetch`:
```cpp
const char* fetch_bytes = "fetch bytes";
const char fetch_pgm[] PROGMEM = "fetch pgm";

void fetch(gh::Fetcher& f) {
    // например по имени файла можно отправить данные из памяти программы или из PROGMEM
    if (f.path == "/fetch_bytes.txt") f.fetchBytes((uint8_t*)fetch_bytes, strlen(fetch_bytes));
    if (f.path == "/fetch_pgm.txt") f.fetchBytes_P((uint8_t*)fetch_pgm, strlen_P(fetch_pgm));

    // или отправить другой файл
    if (f.path == "/fetch_file.txt") f.fetchFile("/fetch_file123.txt");

    // также можно отправить например кадр с камеры:
    if (f.path == "frame.jpg") {
        // start == true - начало загрузки
        if (f.start) {
            frame = esp_camera_fb_get();  // получить кадр
            if (frame) f.fetchBytes((uint8_t*)frame->buf, frame->len);  // начать отправку

        // start == true - конец загрузки
        } else {
            esp_camera_fb_return(frame);  // освободить кадр
        }
    }
}

void setup() {
    hub.onFetch(fetch);
}
```

## Окончание загрузки
Также можно отловить момент окончания загрузки файла с приложения в память устройства:
```cpp
void upl(String& path) {
    Serial.print("Uploaded: ");
    Serial.println(path);
}

void setup() {
    hub.onUpload(upl);
}
```

## Свои поля в Info
Во вкладку Info в любой из 4-х блоков можно добавить свои поля:
```cpp
void info(gh::Info& info) {
    switch (info.type) {
        case gh::Info::Type::Version:
            info.add("Module", "v123");
            break;

        case gh::Info::Type::Network:
            info.add(F("5G"), "50%");
            break;

        case gh::Info::Type::Memory:
            info.add(F("SD"), "10 GB");
            break;
            
        case gh::Info::Type::System:
            info.add(F("Battery"), 3.63, 2);
            info.add("ur mom", "120kg");
            break;
    }
}

void setup() {
    hub.onInfo(info);
}
```