From 2e1b15e688c4a7cfb675c96c10d2cb01e2de5545 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 30 Dec 2024 02:04:18 +0100 Subject: [PATCH] Add Do Not Disturb feature --- data | 2 +- src/lib/led_handler.cpp | 108 +++++++++++++++++++++++++++++++++++++--- src/lib/led_handler.hpp | 21 +++++++- src/lib/webserver.cpp | 67 ++++++++++++++++++++++++- src/lib/webserver.hpp | 4 ++ 5 files changed, 189 insertions(+), 13 deletions(-) diff --git a/data b/data index 0041ec3..033fe09 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 0041ec3d9a174955383836bba02caf79f3961072 +Subproject commit 033fe098295ab6da6568d6298b4380e51bec0b98 diff --git a/src/lib/led_handler.cpp b/src/lib/led_handler.cpp index 42ef5e5..16612ba 100644 --- a/src/lib/led_handler.cpp +++ b/src/lib/led_handler.cpp @@ -50,6 +50,9 @@ void frontlightFadeOut(uint num) void frontlightSetBrightness(uint brightness) { + if (isDNDActive()) { + return; // Don't change brightness during DND mode + } if (brightness > 4096) { return; @@ -183,6 +186,9 @@ bool frontlightIsOn() void frontlightFadeIn(uint num, int flDelayTime) { + if (isDNDActive()) { + return; // Don't change brightness during DND mode + } if (preferences.getBool("flDisable")) return; for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += 5) @@ -194,6 +200,9 @@ void frontlightFadeIn(uint num, int flDelayTime) void frontlightFadeOut(uint num, int flDelayTime) { + if (isDNDActive()) { + return; // Don't change brightness during DND mode + } if (preferences.getBool("flDisable")) return; if (!frontlightIsOn()) @@ -207,6 +216,85 @@ void frontlightFadeOut(uint num, int flDelayTime) } #endif +// Do Not Disturb mode variables +bool dndEnabled = false; +bool dndTimeBasedEnabled = false; +DNDTimeRange dndTimeRange = {23, 0, 7, 0}; // Default: 23:00 to 07:00 + +void loadDNDSettings() { + dndEnabled = preferences.getBool("dndEnabled", false); + dndTimeBasedEnabled = preferences.getBool("dndTimeEnabled", false); + + dndTimeRange.startHour = preferences.getUChar("dndStartHour", 23); + dndTimeRange.startMinute = preferences.getUChar("dndStartMin", 0); + dndTimeRange.endHour = preferences.getUChar("dndEndHour", 7); + dndTimeRange.endMinute = preferences.getUChar("dndEndMin", 0); +} + +void setDNDEnabled(bool enabled) { + dndEnabled = enabled; + preferences.putBool("dndEnabled", enabled); + if (enabled && isDNDActive()) { + clearLeds(); + #ifdef HAS_FRONTLIGHT + frontlightFadeOutAll(); + #endif + } +} + +void setDNDTimeBasedEnabled(bool enabled) { + dndTimeBasedEnabled = enabled; + preferences.putBool("dndTimeEnabled", enabled); + if (enabled && isDNDActive()) { + clearLeds(); + #ifdef HAS_FRONTLIGHT + frontlightFadeOutAll(); + #endif + } +} + +void setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute) { + dndTimeRange.startHour = startHour; + dndTimeRange.startMinute = startMinute; + dndTimeRange.endHour = endHour; + dndTimeRange.endMinute = endMinute; + + preferences.putUChar("dndStartHour", startHour); + preferences.putUChar("dndStartMin", startMinute); + preferences.putUChar("dndEndHour", endHour); + preferences.putUChar("dndEndMin", endMinute); +} + +bool isTimeInDNDRange(uint8_t hour, uint8_t minute) { + uint16_t currentTime = hour * 60 + minute; + uint16_t startTime = dndTimeRange.startHour * 60 + dndTimeRange.startMinute; + uint16_t endTime = dndTimeRange.endHour * 60 + dndTimeRange.endMinute; + + if (startTime <= endTime) { + // Simple case: start time is before end time (e.g., 09:00 to 17:00) + return currentTime >= startTime && currentTime < endTime; + } else { + // Complex case: start time is after end time (e.g., 23:00 to 07:00) + return currentTime >= startTime || currentTime < endTime; + } +} + +bool isDNDActive() { + if (dndEnabled) { + return true; + } + + if (dndTimeBasedEnabled) { + time_t now; + struct tm timeinfo; + time(&now); + localtime_r(&now, &timeinfo); + return isTimeInDNDRange(timeinfo.tm_hour, timeinfo.tm_min); + } + + return false; +} + void ledTask(void *parameter) { while (1) @@ -450,6 +538,7 @@ void ledTask(void *parameter) void setupLeds() { + loadDNDSettings(); pixels.begin(); pixels.setBrightness(preferences.getUInt("ledBrightness", DEFAULT_LED_BRIGHTNESS)); pixels.clear(); @@ -595,15 +684,18 @@ void restoreLedState() QueueHandle_t getLedTaskQueue() { return ledTaskQueue; } -bool queueLedEffect(uint effect) -{ - if (ledTaskQueue == NULL) - { - return false; - } +bool queueLedEffect(uint effect) { + if (isDNDActive()) { + return false; // Don't queue any effects during DND mode + } + if (ledTaskQueue == NULL) + { + return false; + } - uint flashType = effect; - xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY); + uint flashType = effect; + xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY); + return true; } void ledRainbow(int wait) diff --git a/src/lib/led_handler.hpp b/src/lib/led_handler.hpp index 98d8834..4fdbd28 100644 --- a/src/lib/led_handler.hpp +++ b/src/lib/led_handler.hpp @@ -28,7 +28,6 @@ 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; @@ -82,4 +81,22 @@ void frontlightFadeOutAll(int flDelayTime, bool staggered); void frontlightFadeIn(uint num, int flDelayTime); void frontlightFadeOut(uint num, int flDelayTime); -#endif \ No newline at end of file +#endif + +// Do Not Disturb mode settings +struct DNDTimeRange { + uint8_t startHour; + uint8_t startMinute; + uint8_t endHour; + uint8_t endMinute; +}; + +extern bool dndEnabled; +extern bool dndTimeBasedEnabled; +extern DNDTimeRange dndTimeRange; + +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); \ No newline at end of file diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index c2bc86a..2ba0a4d 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -16,7 +16,7 @@ static const char *const PROGMEM boolSettings[] = {"ledTestOnPower", "ledFlashOn "mempoolSecure", "bitaxeEnabled", "miningPoolStats", "verticalDesc", "nostrZapNotify", "httpAuthEnabled", - "enableDebugLog", "ceDisableSSL"}; + "enableDebugLog", "ceDisableSSL", "dndEnabled", "dndTimeBasedEnabled"}; AsyncWebServer server(80); AsyncEventSource events("/events"); @@ -116,6 +116,10 @@ void setupWebserver() server.addRewrite(new OneParamRewrite("/api/show/number/{number}", "/api/show/text?t={text}")); + server.on("/api/dnd/status", HTTP_GET, onApiDNDStatus); + server.on("/api/dnd/enable", HTTP_POST, onApiDNDEnable); + server.on("/api/dnd/disable", HTTP_POST, onApiDNDDisable); + server.onNotFound(onNotFound); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); @@ -265,6 +269,15 @@ 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(); + return root; } @@ -605,6 +618,23 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) settingsChanged = true; } + // Handle DND settings + if (settings.containsKey("dnd")) { + JsonObject dndObj = settings["dnd"]; + if (dndObj.containsKey("timeBasedEnabled")) { + setDNDTimeBasedEnabled(dndObj["timeBasedEnabled"].as()); + } + if (dndObj.containsKey("startHour") && dndObj.containsKey("startMinute") && + dndObj.containsKey("endHour") && dndObj.containsKey("endMinute")) { + setDNDTimeRange( + dndObj["startHour"].as(), + dndObj["startMinute"].as(), + dndObj["endHour"].as(), + dndObj["endMinute"].as() + ); + } + } + request->send(HTTP_OK); if (settingsChanged) { @@ -766,6 +796,14 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root["ceEndpoint"] = preferences.getString("ceEndpoint", DEFAULT_CUSTOM_ENDPOINT); 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; + AsyncResponseStream *response = request->beginResponseStream(JSON_CONTENT); serializeJson(root, *response); @@ -1091,4 +1129,29 @@ void onApiFrontlightOff(AsyncWebServerRequest *request) request->send(HTTP_OK); } -#endif \ No newline at end of file +#endif + +void onApiDNDStatus(AsyncWebServerRequest *request) { + 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(); + + String response; + serializeJson(doc, response); + request->send(200, "application/json", response); +} + +void onApiDNDEnable(AsyncWebServerRequest *request) { + setDNDEnabled(true); + request->send(200); +} + +void onApiDNDDisable(AsyncWebServerRequest *request) { + setDNDEnabled(false); + request->send(200); +} \ No newline at end of file diff --git a/src/lib/webserver.hpp b/src/lib/webserver.hpp index 45fa854..ddd6b73 100644 --- a/src/lib/webserver.hpp +++ b/src/lib/webserver.hpp @@ -67,6 +67,10 @@ void eventSourceTask(void *pvParameters); void onApiStopDataSources(AsyncWebServerRequest *request); void onApiRestartDataSources(AsyncWebServerRequest *request); +void onApiDNDStatus(AsyncWebServerRequest *request); +void onApiDNDEnable(AsyncWebServerRequest *request); +void onApiDNDDisable(AsyncWebServerRequest *request); + #ifdef HAS_FRONTLIGHT void onApiFrontlightOn(AsyncWebServerRequest *request); void onApiFrontlightFlash(AsyncWebServerRequest *request);