Add Do Not Disturb feature
All checks were successful
BTClock CI / build (push) Successful in 21m1s
BTClock CI / merge (map[name:btclock_rev_b version:esp32s3], 213epd) (push) Successful in 31s
BTClock CI / merge (map[name:btclock_v8 version:esp32s3], 213epd) (push) Successful in 21s
BTClock CI / merge (map[name:lolin_s3_mini version:esp32s3], 213epd) (push) Successful in 28s
BTClock CI / merge (map[name:lolin_s3_mini version:esp32s3], 29epd) (push) Successful in 19s
BTClock CI / release (push) Successful in 11s

This commit is contained in:
Djuri Baars 2024-12-30 02:04:18 +01:00
parent 13c8e67b4c
commit 2e1b15e688
5 changed files with 189 additions and 13 deletions

2
data

@ -1 +1 @@
Subproject commit 0041ec3d9a174955383836bba02caf79f3961072
Subproject commit 033fe098295ab6da6568d6298b4380e51bec0b98

View file

@ -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)

View file

@ -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
#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);

View file

@ -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<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>()
);
}
}
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
#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);
}

View file

@ -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);