# Дополнительные фичи ## Время приложения При открытии ПУ приложение отправляет точное время (время операционной системы, на которой запущено приложение) в 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); } ```