Refactor LedHandler to a class

This commit is contained in:
Djuri 2025-01-05 21:19:28 +01:00
parent ac13098824
commit d023643090
Signed by: djuri
GPG key ID: 61B9B2DDE5AA3AC1
11 changed files with 931 additions and 935 deletions

View file

@ -1,4 +1,5 @@
#include "block_notify.hpp"
#include "led_handler.hpp"
char *wsServer;
esp_websocket_client_handle_t blockNotifyClient = NULL;
@ -217,7 +218,7 @@ void processNewBlock(uint32_t newBlockHeight) {
if (preferences.getBool("ledFlashOnUpd", DEFAULT_LED_FLASH_ON_UPD))
{
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
getLedHandler().queueEffect(LED_FLASH_BLOCK_NOTIFY);
}
}
}

View file

@ -1,4 +1,5 @@
#include "config.hpp"
#include "led_handler.hpp"
#define MAX_ATTEMPTS_WIFI_CONNECTION 20
@ -50,7 +51,8 @@ void setup()
setupDisplays();
if (preferences.getBool("ledTestOnPower", DEFAULT_LED_TEST_ON_POWER))
{
queueLedEffect(LED_POWER_TEST);
auto& ledHandler = getLedHandler();
ledHandler.queueEffect(LED_POWER_TEST);
}
{
std::lock_guard<std::mutex> lockMcp(mcpMutex);
@ -60,7 +62,8 @@ void setup()
preferences.remove("txPower");
WiFi.eraseAP();
queueLedEffect(LED_EFFECT_WIFI_ERASE_SETTINGS);
auto& ledHandler = getLedHandler();
ledHandler.queueEffect(LED_EFFECT_WIFI_ERASE_SETTINGS);
}
}
@ -76,7 +79,8 @@ void setup()
else if (mcp1.read1(1) == LOW)
{
preferences.clear();
queueLedEffect(LED_EFFECT_WIFI_ERASE_SETTINGS);
auto& ledHandler = getLedHandler();
ledHandler.queueEffect(LED_EFFECT_WIFI_ERASE_SETTINGS);
nvs_flash_erase();
delay(1000);
@ -116,13 +120,13 @@ void setup()
#ifdef HAS_FRONTLIGHT
if (!preferences.getBool("flAlwaysOn", DEFAULT_FL_ALWAYS_ON))
{
frontlightFadeOutAll(preferences.getUInt("flEffectDelay"), true);
auto& ledHandler = getLedHandler();
ledHandler.frontlightFadeOutAll(preferences.getUInt("flEffectDelay"), true);
flArray.allOFF();
}
#endif
forceFullRefresh();
}
void setupWifi()
@ -144,7 +148,8 @@ void setupWifi()
// if (!preferences.getBool("wifiConfigured", DEFAULT_WIFI_CONFIGURED)
{
queueLedEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG);
auto& ledHandler = getLedHandler();
ledHandler.queueEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG);
bool buttonPress = false;
{
@ -279,7 +284,8 @@ void syncTime()
while (!getLocalTime(&timeinfo))
{
queueLedEffect(LED_EFFECT_CONFIGURING);
auto& ledHandler = getLedHandler();
ledHandler.queueEffect(LED_EFFECT_CONFIGURING);
configTime(preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS), 0,
NTP_SERVER);
delay(500);
@ -420,13 +426,14 @@ void setupTimers()
void finishSetup()
{
auto& ledHandler = getLedHandler();
if (preferences.getBool("ledStatus", DEFAULT_LED_STATUS))
{
restoreLedState();
ledHandler.restoreLedState();
}
else
{
clearLeds();
ledHandler.clear();
}
}
@ -475,22 +482,9 @@ void setupHardware()
Serial.println(F("Error loading WebUI"));
}
// {
// File f = LittleFS.open("/qr.txt", "w");
// if(f) {
// if (f.print("Hello")) {
// Serial.println(F("Written QR to FS"));
// Serial.printf("\nLittleFS free: %zu\n", LittleFS.totalBytes() - LittleFS.usedBytes());
// }
// } else {
// Serial.println(F("Can't write QR to FS"));
// }
// f.close();
// }
setupLeds();
// Initialize LED handler
auto& ledHandler = getLedHandler();
ledHandler.setup();
WiFi.setHostname(getMyHostname().c_str());
if (!psramInit())
@ -548,7 +542,8 @@ void setupHardware()
#endif
#ifdef HAS_FRONTLIGHT
setupFrontlight();
// Initialize frontlight through LedHandler
ledHandler.initializeFrontlight();
Wire.beginTransmission(0x5C);
byte error = Wire.endTransmission();
@ -570,6 +565,7 @@ void setupHardware()
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
{
static bool first_connect = true;
auto& ledHandler = getLedHandler(); // Get ledHandler reference once at the start
Serial.printf("[WiFi-event] event: %d\n", event);
@ -595,7 +591,7 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
if (!first_connect)
{
Serial.println(F("Disconnected from WiFi access point"));
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
ledHandler.queueEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
uint8_t reason = info.wifi_sta_disconnected.reason;
if (reason)
Serial.printf("Disconnect reason: %s, ",
@ -611,13 +607,13 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
Serial.print("Obtained IP address: ");
Serial.println(WiFi.localIP());
if (!first_connect)
queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
ledHandler.queueEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
first_connect = false;
break;
}
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
Serial.println(F("Lost IP address and IP address is reset to 0"));
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
ledHandler.queueEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
WiFi.reconnect();
break;
case ARDUINO_EVENT_WIFI_AP_START:
@ -667,30 +663,6 @@ uint getLastTimeSync()
}
#ifdef HAS_FRONTLIGHT
void setupFrontlight()
{
if (!flArray.begin(PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, PCA9685_MODE2_TOTEMPOLE))
{
Serial.println(F("FL driver error"));
return;
}
flArray.setFrequency(200);
Serial.println(F("FL driver active"));
if (!preferences.isKey("flMaxBrightness"))
{
preferences.putUInt("flMaxBrightness", DEFAULT_FL_MAX_BRIGHTNESS);
}
if (!preferences.isKey("flEffectDelay"))
{
preferences.putUInt("flEffectDelay", DEFAULT_FL_EFFECT_DELAY);
}
if (!preferences.isKey("flFlashOnUpd"))
{
preferences.putBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE);
}
}
float getLightLevel()
{

View file

@ -52,10 +52,10 @@ void setupTimers();
void finishSetup();
void setupMcp();
#ifdef HAS_FRONTLIGHT
void setupFrontlight();
extern BH1750 bh1750;
extern bool hasLuxSensor;
float getLightLevel();
bool hasLightLevel();
extern PCA9685 flArray;
#endif
String getMyHostname();
@ -98,6 +98,10 @@ extern MCP23017 mcp1;
extern MCP23017 mcp2;
#endif
#ifdef HAS_FRONTLIGHT
extern PCA9685 flArray;
#endif
// Expose DataSourceType enum
extern DataSourceType getDataSource();
extern void setDataSource(DataSourceType source);

View file

@ -140,8 +140,6 @@ const GFXfont *FONT_SATSYMBOL;
std::mutex epdUpdateMutex;
std::mutex epdMutex[NUM_SCREENS];
uint8_t qrcode[800];
#ifdef IS_BTCLOCK_V8
#define EPD_TASK_STACK_SIZE 4096
#else
@ -159,8 +157,6 @@ void forceFullRefresh()
}
}
GFXfont font90;
void loadFonts(const String& fontName) {
if (fontName == FontNames::ANTONIO) {
// Load Antonio fonts
@ -628,35 +624,45 @@ bool renderIcon(const uint dispNum, const String &text, bool partial)
void renderQr(const uint dispNum, const String &text, bool partial)
{
#ifdef USE_QR
// Dynamically allocate QR buffer
uint8_t* qrcode = (uint8_t*)malloc(qrcodegen_BUFFER_LEN_MAX);
if (!qrcode) {
log_e("Failed to allocate QR buffer");
return;
}
uint8_t tempBuffer[800];
bool ok = qrcodegen_encodeText(
text.substring(2).c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW,
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
const int size = qrcodegen_getSize(qrcode);
if (ok) {
const int size = qrcodegen_getSize(qrcode);
const int padding = floor(float(displays[dispNum].width() - (size * 4)) / 2);
const int paddingY =
floor(float(displays[dispNum].height() - (size * 4)) / 2);
displays[dispNum].setRotation(2);
const int padding = floor(float(displays[dispNum].width() - (size * 4)) / 2);
const int paddingY =
floor(float(displays[dispNum].height() - (size * 4)) / 2);
displays[dispNum].setRotation(2);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(),
displays[dispNum].height());
displays[dispNum].fillScreen(GxEPD_WHITE);
const int border = 0;
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(),
displays[dispNum].height());
displays[dispNum].fillScreen(GxEPD_WHITE);
const int border = 0;
for (int y = -border; y < size * 4 + border; y++)
{
for (int x = -border; x < size * 4 + border; x++)
for (int y = -border; y < size * 4 + border; y++)
{
displays[dispNum].drawPixel(
padding + x, paddingY + y,
qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4))
? GxEPD_BLACK
: GxEPD_WHITE);
for (int x = -border; x < size * 4 + border; x++)
{
displays[dispNum].drawPixel(
padding + x, paddingY + y,
qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4))
? GxEPD_BLACK
: GxEPD_WHITE);
}
}
}
// Free the buffer after we're done
free(qrcode);
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <memory>
#include "lib/shared.hpp"
#include "lib/webserver.hpp"
@ -15,12 +16,11 @@
#define NEOPIXEL_COUNT 4
#endif
// LED effect constants
const int LED_FLASH_ERROR = 0;
const int LED_FLASH_SUCCESS = 1;
const int LED_FLASH_UPDATE = 2;
const int LED_EFFECT_CONFIGURING = 10;
const int LED_FLASH_BLOCK_NOTIFY = 4;
const int LED_EFFECT_START_TIMER = 5;
const int LED_EFFECT_PAUSE_TIMER = 6;
@ -30,61 +30,15 @@ const int LED_EFFECT_WIFI_CONNECTING = 101;
const int LED_EFFECT_WIFI_CONNECT_ERROR = 102;
const int LED_EFFECT_WIFI_CONNECT_SUCCESS = 103;
const int LED_EFFECT_WIFI_ERASE_SETTINGS = 104;
const int LED_PROGRESS_25 = 200;
const int LED_PROGRESS_50 = 201;
const int LED_PROGRESS_75 = 202;
const int LED_PROGRESS_100 = 203;
const int LED_DATA_PRICE_ERROR = 300;
const int LED_DATA_BLOCK_ERROR = 301;
const int LED_EFFECT_NOSTR_ZAP = 400;
const int LED_FLASH_IDENTIFY = 990;
const int LED_POWER_TEST = 999;
extern TaskHandle_t ledTaskHandle;
extern Adafruit_NeoPixel pixels;
void ledTask(void *pvParameters);
void setupLeds();
void setupLedTask();
void blinkDelay(int d, int times);
void blinkDelayColor(int d, int times, uint r, uint g, uint b);
void blinkDelayTwoColor(int d, int times, const uint32_t& c1, const uint32_t& c2);
void clearLeds();
void saveLedState();
void restoreLedState();
QueueHandle_t getLedTaskQueue();
bool queueLedEffect(uint effect);
void setLights(int r, int g, int b);
void setLights(uint32_t color);
void ledRainbow(int wait);
void ledTheaterChaseRainbow(int wait);
void ledTheaterChase(uint32_t color, int wait);
Adafruit_NeoPixel getPixels();
void lightningStrike();
#ifdef HAS_FRONTLIGHT
void frontlightFlash(int flDelayTime);
void frontlightFadeInAll();
void frontlightFadeOutAll();
void frontlightFadeIn(uint num);
void frontlightFadeOut(uint num);
std::vector<uint16_t> frontlightGetStatus();
void frontlightSetBrightness(uint brightness);
bool frontlightIsOn();
void frontlightFadeInAll(int flDelayTime);
void frontlightFadeInAll(int flDelayTime, bool staggered);
void frontlightFadeOutAll(int flDelayTime);
void frontlightFadeOutAll(int flDelayTime, bool staggered);
void frontlightFadeIn(uint num, int flDelayTime);
void frontlightFadeOut(uint num, int flDelayTime);
#endif
// Do Not Disturb mode settings
struct DNDTimeRange {
@ -94,12 +48,88 @@ struct DNDTimeRange {
uint8_t endMinute;
};
extern bool dndEnabled;
extern bool dndTimeBasedEnabled;
extern DNDTimeRange dndTimeRange;
class LedHandler {
public:
static LedHandler& getInstance();
// Delete copy constructor and assignment operator
LedHandler(const LedHandler&) = delete;
LedHandler& operator=(const LedHandler&) = delete;
void setDNDEnabled(bool enabled);
void setDNDTimeBasedEnabled(bool enabled);
void setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute);
bool isDNDActive();
bool isTimeInDNDRange(uint8_t hour, uint8_t minute);
void setup();
void setupTask();
bool queueEffect(uint effect);
void clear();
void setLights(int r, int g, int b);
void setLights(uint32_t color);
void saveLedState();
void restoreLedState();
QueueHandle_t getTaskQueue() const { return ledTaskQueue; }
Adafruit_NeoPixel& getPixels() { return pixels; }
// DND methods
void setDNDEnabled(bool enabled);
void setDNDTimeBasedEnabled(bool enabled);
void setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute);
bool isDNDActive() const;
bool isTimeInDNDRange(uint8_t hour, uint8_t minute) const;
// DND getters
bool isDNDEnabled() const { return dndEnabled; }
bool isDNDTimeBasedEnabled() const { return dndTimeBasedEnabled; }
uint8_t getDNDStartHour() const { return dndTimeRange.startHour; }
uint8_t getDNDStartMinute() const { return dndTimeRange.startMinute; }
uint8_t getDNDEndHour() const { return dndTimeRange.endHour; }
uint8_t getDNDEndMinute() const { return dndTimeRange.endMinute; }
// Effect methods
void rainbow(int wait);
void theaterChase(uint32_t color, int wait);
void theaterChaseRainbow(int wait);
void lightningStrike();
void blinkDelay(int d, int times);
void blinkDelayColor(int d, int times, uint r, uint g, uint b);
void blinkDelayTwoColor(int d, int times, const uint32_t& c1, const uint32_t& c2);
#ifdef HAS_FRONTLIGHT
void frontlightFlash(int flDelayTime);
void frontlightFadeInAll();
void frontlightFadeOutAll();
void frontlightFadeIn(uint num);
void frontlightFadeOut(uint num);
std::vector<uint16_t> frontlightGetStatus();
void frontlightSetBrightness(uint brightness);
bool frontlightIsOn() const { return frontlightOn; }
void frontlightFadeInAll(int flDelayTime, bool staggered = false);
void frontlightFadeOutAll(int flDelayTime, bool staggered = false);
void frontlightFadeIn(uint num, int flDelayTime);
void frontlightFadeOut(uint num, int flDelayTime);
void initializeFrontlight();
#endif
private:
LedHandler(); // Private constructor for singleton
void loadDNDSettings();
static void ledTask(void* pvParameters);
Adafruit_NeoPixel pixels;
TaskHandle_t ledTaskHandle;
QueueHandle_t ledTaskQueue;
uint ledTaskParams;
// DND members
bool dndEnabled;
bool dndTimeBasedEnabled;
DNDTimeRange dndTimeRange;
#ifdef HAS_FRONTLIGHT
static constexpr uint16_t FL_FADE_STEP = 25;
bool frontlightOn;
bool flInTransition;
#endif
};
// Global accessor function
inline LedHandler& getLedHandler() {
return LedHandler::getInstance();
}

View file

@ -1,4 +1,5 @@
#include "nostr_notify.hpp"
#include "led_handler.hpp"
std::vector<nostr::NostrPool *> pools;
nostr::Transport *transport;
@ -286,7 +287,7 @@ void handleNostrZapCallback(const String &subId, nostr::SignedNostrEvent *event)
vTaskDelay(pdMS_TO_TICKS(315 * NUM_SCREENS) + pdMS_TO_TICKS(250));
if (preferences.getBool("ledFlashOnZap", DEFAULT_LED_FLASH_ON_ZAP))
{
queueLedEffect(LED_EFFECT_NOSTR_ZAP);
getLedHandler().queueEffect(LED_EFFECT_NOSTR_ZAP);
}
if (timerPeriod > 0)
{

View file

@ -1,4 +1,5 @@
#include "ota.hpp"
#include "led_handler.hpp"
TaskHandle_t taskOtaHandle = NULL;
bool isOtaUpdating = false;
@ -31,6 +32,9 @@ void setupOTA()
void onOTAProgress(unsigned int progress, unsigned int total)
{
uint percentage = progress / (total / 100);
auto& ledHandler = getLedHandler();
auto& pixels = ledHandler.getPixels();
pixels.fill(pixels.Color(0, 255, 0));
if (percentage < 100)
{
@ -84,15 +88,15 @@ void handleOTATask(void *parameter)
{
if (msg.updateType == UPDATE_ALL) {
isOtaUpdating = true;
queueLedEffect(LED_FLASH_UPDATE);
getLedHandler().queueEffect(LED_FLASH_UPDATE);
int resultWebUi = downloadUpdateHandler(UPDATE_WEBUI);
queueLedEffect(LED_FLASH_UPDATE);
getLedHandler().queueEffect(LED_FLASH_UPDATE);
int resultFw = downloadUpdateHandler(UPDATE_FIRMWARE);
if (resultWebUi == 0 && resultFw == 0) {
ESP.restart();
} else {
queueLedEffect(LED_FLASH_ERROR);
getLedHandler().queueEffect(LED_FLASH_ERROR);
vTaskDelay(pdMS_TO_TICKS(3000));
ESP.restart();
}

View file

@ -1,4 +1,5 @@
#include "timers.hpp"
#include "led_handler.hpp"
esp_timer_handle_t screenRotateTimer;
esp_timer_handle_t minuteTimer;
@ -49,11 +50,11 @@ void setTimerActive(bool status) {
if (status) {
esp_timer_start_periodic(screenRotateTimer,
getTimerSeconds() * usPerSecond);
queueLedEffect(LED_EFFECT_START_TIMER);
getLedHandler().queueEffect(LED_EFFECT_START_TIMER);
preferences.putBool("timerActive", true);
} else {
esp_timer_stop(screenRotateTimer);
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
getLedHandler().queueEffect(LED_EFFECT_PAUSE_TIMER);
preferences.putBool("timerActive", false);
}

View file

@ -1,4 +1,6 @@
#include "webserver.hpp"
#include "lib/led_handler.hpp"
#include "lib/shared.hpp"
static const char* JSON_CONTENT = "application/json";
@ -231,6 +233,7 @@ void asyncFirmwareUpdateHandler(AsyncWebServerRequest *request, String filename,
JsonDocument getStatusObject()
{
auto& ledHandler = getLedHandler();
JsonDocument root;
root["currentScreen"] = ScreenHandler::getCurrentScreen();
@ -238,25 +241,21 @@ JsonDocument getStatusObject()
root["timerRunning"] = isTimerActive();
root["isOTAUpdating"] = getIsOTAUpdating();
root["espUptime"] = esp_timer_get_time() / 1000000;
// root["currentPrice"] = getPrice();
// root["currentBlockHeight"] = getBlockHeight();
root["espFreeHeap"] = ESP.getFreeHeap();
root["espHeapSize"] = ESP.getHeapSize();
// root["espFreePsram"] = ESP.getFreePsram();
// root["espPsramSize"] = ESP.getPsramSize();
JsonObject conStatus = root["connectionStatus"].to<JsonObject>();
conStatus["price"] = isPriceNotifyConnected();
conStatus["blocks"] = isBlockNotifyConnected();
conStatus["V2"] = V2Notify::isV2NotifyConnected();
conStatus["nostr"] = nostrConnected();
root["rssi"] = WiFi.RSSI();
root["currency"] = getCurrencyCode(ScreenHandler::getCurrentCurrency());
#ifdef HAS_FRONTLIGHT
std::vector<uint16_t> statuses = frontlightGetStatus();
std::vector<uint16_t> statuses = ledHandler.frontlightGetStatus();
uint16_t arr[NUM_SCREENS];
std::copy(statuses.begin(), statuses.end(), arr);
@ -270,22 +269,24 @@ JsonDocument getStatusObject()
#endif
// Add DND status
root["dnd"]["enabled"] = dndEnabled;
root["dnd"]["timeBasedEnabled"] = dndTimeBasedEnabled;
root["dnd"]["startTime"] = String(dndTimeRange.startHour) + ":" +
(dndTimeRange.startMinute < 10 ? "0" : "") + String(dndTimeRange.startMinute);
root["dnd"]["endTime"] = String(dndTimeRange.endHour) + ":" +
(dndTimeRange.endMinute < 10 ? "0" : "") + String(dndTimeRange.endMinute);
root["dnd"]["active"] = isDNDActive();
root["dnd"]["enabled"] = ledHandler.isDNDEnabled();
root["dnd"]["timeBasedEnabled"] = ledHandler.isDNDTimeBasedEnabled();
root["dnd"]["startTime"] = String(ledHandler.getDNDStartHour()) + ":" +
(ledHandler.getDNDStartMinute() < 10 ? "0" : "") + String(ledHandler.getDNDStartMinute());
root["dnd"]["endTime"] = String(ledHandler.getDNDEndHour()) + ":" +
(ledHandler.getDNDEndMinute() < 10 ? "0" : "") + String(ledHandler.getDNDEndMinute());
root["dnd"]["active"] = ledHandler.isDNDActive();
return root;
}
JsonDocument getLedStatusObject()
{
auto& ledHandler = getLedHandler();
auto& pixels = ledHandler.getPixels();
JsonDocument root;
JsonArray colors = root["data"].to<JsonArray>();
// Adafruit_NeoPixel pix = getPixels();
for (uint i = 0; i < pixels.numPixels(); i++)
{
@ -295,13 +296,7 @@ JsonDocument getLedStatusObject()
uint blue = pixColor & 0xFF;
char hexColor[8];
snprintf(hexColor, sizeof(hexColor), "#%02X%02X%02X", red, green, blue);
JsonObject object = colors.add<JsonObject>();
object["red"] = red;
object["green"] = green;
object["blue"] = blue;
object["hex"] = hexColor;
colors.add(hexColor);
}
return root;
@ -621,24 +616,26 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
// Handle DND settings
if (settings.containsKey("dnd")) {
JsonObject dndObj = settings["dnd"];
auto& ledHandler = getLedHandler();
if (dndObj.containsKey("timeBasedEnabled")) {
setDNDTimeBasedEnabled(dndObj["timeBasedEnabled"].as<bool>());
ledHandler.setDNDTimeBasedEnabled(dndObj["timeBasedEnabled"].as<bool>());
}
if (dndObj.containsKey("startHour") && dndObj.containsKey("startMinute") &&
dndObj.containsKey("endHour") && dndObj.containsKey("endMinute")) {
setDNDTimeRange(
dndObj["startHour"].as<uint8_t>(),
dndObj["startMinute"].as<uint8_t>(),
dndObj["endHour"].as<uint8_t>(),
dndObj["endMinute"].as<uint8_t>()
);
ledHandler.setDNDTimeRange(
dndObj["startHour"].as<uint8_t>(),
dndObj["startMinute"].as<uint8_t>(),
dndObj["endHour"].as<uint8_t>(),
dndObj["endMinute"].as<uint8_t>());
}
}
request->send(HTTP_OK);
if (settingsChanged)
{
queueLedEffect(LED_FLASH_SUCCESS);
auto& ledHandler = getLedHandler();
ledHandler.queueEffect(LED_FLASH_SUCCESS);
}
}
@ -659,7 +656,8 @@ void onApiRestart(AsyncWebServerRequest *request)
void onApiIdentify(AsyncWebServerRequest *request)
{
queueLedEffect(LED_FLASH_IDENTIFY);
auto& ledHandler = getLedHandler();
ledHandler.queueEffect(LED_FLASH_IDENTIFY);
request->send(HTTP_OK);
}
@ -797,12 +795,13 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
root["ceDisableSSL"] = preferences.getBool("ceDisableSSL", DEFAULT_CUSTOM_ENDPOINT_DISABLE_SSL);
// Add DND settings
root["dnd"]["enabled"] = dndEnabled;
root["dnd"]["timeBasedEnabled"] = dndTimeBasedEnabled;
root["dnd"]["startHour"] = dndTimeRange.startHour;
root["dnd"]["startMinute"] = dndTimeRange.startMinute;
root["dnd"]["endHour"] = dndTimeRange.endHour;
root["dnd"]["endMinute"] = dndTimeRange.endMinute;
auto& ledHandler = getLedHandler();
root["dnd"]["enabled"] = ledHandler.isDNDEnabled();
root["dnd"]["timeBasedEnabled"] = ledHandler.isDNDTimeBasedEnabled();
root["dnd"]["startHour"] = ledHandler.getDNDStartHour();
root["dnd"]["startMinute"] = ledHandler.getDNDStartMinute();
root["dnd"]["endHour"] = ledHandler.getDNDEndHour();
root["dnd"]["endMinute"] = ledHandler.getDNDEndMinute();
AsyncResponseStream *response =
request->beginResponseStream(JSON_CONTENT);
@ -929,7 +928,8 @@ void onApiRestartDataSources(AsyncWebServerRequest *request)
void onApiLightsOff(AsyncWebServerRequest *request)
{
setLights(0, 0, 0);
auto& ledHandler = getLedHandler();
ledHandler.setLights(0, 0, 0);
request->send(HTTP_OK);
}
@ -944,13 +944,15 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
if (rgbColor.compareTo("off") == 0)
{
setLights(0, 0, 0);
auto& ledHandler = getLedHandler();
ledHandler.setLights(0, 0, 0);
}
else
{
uint r, g, b;
sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b);
setLights(r, g, b);
auto& ledHandler = getLedHandler();
ledHandler.setLights(r, g, b);
}
JsonDocument doc;
@ -968,6 +970,9 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
{
auto& ledHandler = getLedHandler();
auto& pixels = ledHandler.getPixels();
JsonArray lights = json.as<JsonArray>();
if (lights.size() != pixels.numPixels())
@ -1016,7 +1021,7 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
}
pixels.show();
saveLedState();
ledHandler.saveLedState();
request->send(HTTP_OK);
}
@ -1080,19 +1085,21 @@ void onApiShowCurrency(AsyncWebServerRequest *request)
#ifdef HAS_FRONTLIGHT
void onApiFrontlightOn(AsyncWebServerRequest *request)
{
frontlightFadeInAll();
auto& ledHandler = getLedHandler();
ledHandler.frontlightFadeInAll();
request->send(HTTP_OK);
}
void onApiFrontlightStatus(AsyncWebServerRequest *request)
{
auto& ledHandler = getLedHandler();
AsyncResponseStream *response =
request->beginResponseStream(JSON_CONTENT);
JsonDocument root;
std::vector<uint16_t> statuses = frontlightGetStatus();
std::vector<uint16_t> statuses = ledHandler.frontlightGetStatus();
uint16_t arr[NUM_SCREENS];
std::copy(statuses.begin(), statuses.end(), arr);
@ -1105,7 +1112,8 @@ void onApiFrontlightStatus(AsyncWebServerRequest *request)
void onApiFrontlightFlash(AsyncWebServerRequest *request)
{
frontlightFlash(preferences.getUInt("flEffectDelay"));
auto& ledHandler = getLedHandler();
ledHandler.frontlightFlash(preferences.getUInt("flEffectDelay"));
request->send(HTTP_OK);
}
@ -1114,7 +1122,8 @@ void onApiFrontlightSetBrightness(AsyncWebServerRequest *request)
{
if (request->hasParam("b"))
{
frontlightSetBrightness(request->getParam("b")->value().toInt());
auto& ledHandler = getLedHandler();
ledHandler.frontlightSetBrightness(request->getParam("b")->value().toInt());
request->send(HTTP_OK);
}
else
@ -1125,21 +1134,51 @@ void onApiFrontlightSetBrightness(AsyncWebServerRequest *request)
void onApiFrontlightOff(AsyncWebServerRequest *request)
{
frontlightFadeOutAll();
auto& ledHandler = getLedHandler();
ledHandler.frontlightFadeOutAll();
request->send(HTTP_OK);
}
#endif
void onApiDNDTimeBasedEnable(AsyncWebServerRequest *request) {
auto& ledHandler = getLedHandler();
ledHandler.setDNDTimeBasedEnabled(true);
request->send(200);
}
void onApiDNDTimeBasedDisable(AsyncWebServerRequest *request) {
auto& ledHandler = getLedHandler();
ledHandler.setDNDTimeBasedEnabled(false);
request->send(200);
}
void onApiDNDSetTimeRange(AsyncWebServerRequest *request) {
if (request->hasParam("startHour") && request->hasParam("startMinute") &&
request->hasParam("endHour") && request->hasParam("endMinute")) {
auto& ledHandler = getLedHandler();
uint8_t startHour = request->getParam("startHour")->value().toInt();
uint8_t startMinute = request->getParam("startMinute")->value().toInt();
uint8_t endHour = request->getParam("endHour")->value().toInt();
uint8_t endMinute = request->getParam("endMinute")->value().toInt();
ledHandler.setDNDTimeRange(startHour, startMinute, endHour, endMinute);
request->send(200);
} else {
request->send(400);
}
}
void onApiDNDStatus(AsyncWebServerRequest *request) {
auto& ledHandler = getLedHandler();
JsonDocument doc;
doc["enabled"] = dndEnabled;
doc["timeBasedEnabled"] = dndTimeBasedEnabled;
doc["startTime"] = String(dndTimeRange.startHour) + ":" +
(dndTimeRange.startMinute < 10 ? "0" : "") + String(dndTimeRange.startMinute);
doc["endTime"] = String(dndTimeRange.endHour) + ":" +
(dndTimeRange.endMinute < 10 ? "0" : "") + String(dndTimeRange.endMinute);
doc["active"] = isDNDActive();
doc["enabled"] = ledHandler.isDNDEnabled();
doc["timeBasedEnabled"] = ledHandler.isDNDTimeBasedEnabled();
doc["startTime"] = String(ledHandler.getDNDStartHour()) + ":" +
(ledHandler.getDNDStartMinute() < 10 ? "0" : "") + String(ledHandler.getDNDStartMinute());
doc["endTime"] = String(ledHandler.getDNDEndHour()) + ":" +
(ledHandler.getDNDEndMinute() < 10 ? "0" : "") + String(ledHandler.getDNDEndMinute());
doc["active"] = ledHandler.isDNDActive();
String response;
serializeJson(doc, response);
@ -1147,11 +1186,92 @@ void onApiDNDStatus(AsyncWebServerRequest *request) {
}
void onApiDNDEnable(AsyncWebServerRequest *request) {
setDNDEnabled(true);
auto& ledHandler = getLedHandler();
ledHandler.setDNDEnabled(true);
request->send(200);
}
void onApiDNDDisable(AsyncWebServerRequest *request) {
setDNDEnabled(false);
auto& ledHandler = getLedHandler();
ledHandler.setDNDEnabled(false);
request->send(200);
}
void onApiLightsGet(AsyncWebServerRequest *request)
{
auto& ledHandler = getLedHandler();
auto& pixels = ledHandler.getPixels();
DynamicJsonDocument doc(1024);
JsonArray lights = doc.createNestedArray("lights");
for (uint i = 0; i < pixels.numPixels(); i++)
{
uint32_t pixColor = pixels.getPixelColor(pixels.numPixels() - i - 1);
JsonObject light = lights.createNestedObject();
light["r"] = (uint8_t)(pixColor >> 16);
light["g"] = (uint8_t)(pixColor >> 8);
light["b"] = (uint8_t)pixColor;
}
String output;
serializeJson(doc, output);
request->send(200, "application/json", output);
}
void onApiLightsPost(AsyncWebServerRequest *request, uint8_t *data, size_t len,
size_t index, size_t total)
{
auto& ledHandler = getLedHandler();
auto& pixels = ledHandler.getPixels();
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, data);
if (error)
{
request->send(400);
return;
}
JsonArray lights = doc["lights"];
if (lights.size() != pixels.numPixels())
{
request->send(400);
return;
}
for (uint i = 0; i < pixels.numPixels(); i++)
{
JsonObject light = lights[i];
uint8_t red = light["r"];
uint8_t green = light["g"];
uint8_t blue = light["b"];
pixels.setPixelColor((pixels.numPixels() - i - 1),
pixels.Color(red, green, blue));
}
pixels.show();
request->send(200);
}
void onApiSettings(AsyncWebServerRequest *request, JsonVariant &json)
{
JsonObject settings = json.as<JsonObject>();
auto& ledHandler = getLedHandler();
if (settings.containsKey("dnd")) {
JsonObject dndObj = settings["dnd"];
if (dndObj.containsKey("timeBasedEnabled")) {
ledHandler.setDNDTimeBasedEnabled(dndObj["timeBasedEnabled"].as<bool>());
}
if (dndObj.containsKey("startHour") && dndObj.containsKey("startMinute") &&
dndObj.containsKey("endHour") && dndObj.containsKey("endMinute")) {
ledHandler.setDNDTimeRange(
dndObj["startHour"].as<uint8_t>(),
dndObj["startMinute"].as<uint8_t>(),
dndObj["endHour"].as<uint8_t>(),
dndObj["endMinute"].as<uint8_t>());
}
}
}

View file

@ -18,6 +18,7 @@
#define WEBSERVER_H
#include "ESPAsyncWebServer.h"
#include "lib/config.hpp"
#include "lib/led_handler.hpp"
uint wifiLostConnection;
uint priceNotifyLostConnection = 0;
@ -58,13 +59,14 @@ void handleFrontlight() {
if (hasLightLevel() && preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE) != 0) {
uint lightLevel = getLightLevel();
uint luxThreshold = preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE);
auto& ledHandler = getLedHandler();
if (lightLevel <= 1 && preferences.getBool("flOffWhenDark", DEFAULT_FL_OFF_WHEN_DARK)) {
if (frontlightIsOn()) frontlightFadeOutAll();
} else if (lightLevel < luxThreshold && !frontlightIsOn()) {
frontlightFadeInAll();
} else if (frontlightIsOn() && lightLevel > luxThreshold) {
frontlightFadeOutAll();
if (ledHandler.frontlightIsOn()) ledHandler.frontlightFadeOutAll();
} else if (lightLevel < luxThreshold && !ledHandler.frontlightIsOn()) {
ledHandler.frontlightFadeInAll();
} else if (ledHandler.frontlightIsOn() && lightLevel > luxThreshold) {
ledHandler.frontlightFadeOutAll();
}
}
#endif
@ -100,9 +102,7 @@ void checkMissedBlocks() {
}
}
void monitorDataConnections() {
// Price notification monitoring
if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE) && !isPriceNotifyConnected()) {
handlePriceNotifyDisconnection();
@ -137,7 +137,6 @@ extern "C" void app_main() {
bool thirdPartySource = getDataSource() == THIRD_PARTY_SOURCE;
while (true) {
if (eventSourceTaskHandle != NULL) {
xTaskNotifyGive(eventSourceTaskHandle);