diff --git a/sketches/doorbell/doorbell.ino b/sketches/doorbell/doorbell.ino index 9fc33c8..12e2476 100644 --- a/sketches/doorbell/doorbell.ino +++ b/sketches/doorbell/doorbell.ino @@ -1,5 +1,5 @@ -// ============== KLUBHAUS DOORBELL v3.8.4 ============== -// Fix: Use ?since=all to retrieve cached messages, decode HTTP errors +// ============== KLUBHAUS DOORBELL v3.8.5 ============== +// Fix: Restore button handling, fix double ntfy.sh in URL #include #include @@ -43,7 +43,7 @@ String lastProcessedId = ""; unsigned long lastAlertPoll = 0; unsigned long lastSilencePoll = 0; -const unsigned long POLL_INTERVAL_MS = 3000; // Faster polling for testing +const unsigned long POLL_INTERVAL_MS = 3000; // ============== LOGGING ============== enum LogLevel { LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; @@ -59,8 +59,7 @@ void logMsg(LogLevel level, const char* component, const char* msg) { // ============== HTTP ERROR DECODER ============== String decodeHttpError(int code) { - // HTTPClient error codes (negative values) - if (code == -1) return "CONNECTION_REFUSED/SEND_FAILED"; + if (code == -1) return "CONNECTION_REFUSED"; if (code == -2) return "SEND_PAYLOAD_FAILED"; if (code == -3) return "NOT_CONNECTED"; if (code == -4) return "CONNECTION_LOST"; @@ -71,13 +70,12 @@ String decodeHttpError(int code) { if (code == -9) return "STREAM_WRITE"; if (code == -10) return "READ_TIMEOUT"; if (code == -11) return "CONNECTION_TIMEOUT"; - // Positive = HTTP status return "HTTP_" + String(code); } // ============== TIME ============== unsigned long getEpochMs() { - return millis(); // Simplified for now + return millis(); } // ============== DISPLAY ============== @@ -88,7 +86,6 @@ void initDisplay() { gfx->setRotation(1); gfx->fillScreen(COLOR_BLACK); - // Test pattern gfx->fillRect(0, 0, 80, 172, COLOR_TEAL); gfx->fillRect(80, 0, 80, 172, COLOR_FUCHSIA); delay(500); @@ -118,6 +115,18 @@ void showAlert(const String& msg) { gfx->println(msg); } +void flashConfirm(const char* text) { + setBacklight(true); + gfx->fillScreen(COLOR_MINT); + gfx->setTextColor(COLOR_BLACK); + gfx->setTextSize(2); + gfx->setCursor(100, 80); + gfx->println(text); + delay(500); + setBacklight(false); + showSilent(); +} + // ============== NETWORK ============== void initWiFi() { logMsg(LOG_INFO, "WIFI", "Connecting..."); @@ -129,17 +138,15 @@ void initWiFi() { logMsg(LOG_INFO, "WIFI", "IP: " + WiFi.localIP().toString()); } -// ============== ntfy.sh WITH ?since=all ============== +// ============== ntfy.sh ============== bool parseNtfyLine(const String& line, String& outId, String& outMessage, String& outEvent) { if (line.length() == 0 || line.indexOf('{') < 0) return false; - // Quick JSON extraction (no library needed for simple fields) auto extract = [&](const char* key) -> String { String search = String("\"") + key + "\":\""; int start = line.indexOf(search); if (start < 0) { - // Try number format: "key":123 search = String("\"") + key + "\":"; start = line.indexOf(search); if (start < 0) return ""; @@ -160,11 +167,13 @@ bool parseNtfyLine(const String& line, String& outId, String& outMessage, String return outId.length() > 0; } +// CRITICAL FIX: Correct URL construction — was double ntfy.sh! bool fetchNtfy(const char* topic, String& outId, String& outMessage, String& outEvent) { HTTPClient http; - // KEY FIX: ?since=all forces retrieval of cached messages - // Without this, we only get "open" subscription events + // FIXED: NTFY_SERVER is "ntfy.sh", topic is "ALERT_klubhaus_topic" + // Was: https://ntfy.sh/ntfy.sh/ALERT... — WRONG! + // Now: https://ntfy.sh/ALERT... — CORRECT! String url = String("https://") + NTFY_SERVER + "/" + topic + "/json?since=all"; logMsg(LOG_DEBUG, "HTTP", "GET " + url); @@ -174,7 +183,7 @@ bool fetchNtfy(const char* topic, String& outId, String& outMessage, String& out int code = http.GET(); if (code != 200) { - logMsg(LOG_ERROR, "HTTP", String(code) + "=" + decodeHttpError(code) + " | " + url); + logMsg(LOG_ERROR, "HTTP", String(code) + "=" + decodeHttpError(code)); http.end(); return false; } @@ -182,12 +191,10 @@ bool fetchNtfy(const char* topic, String& outId, String& outMessage, String& out String payload = http.getString(); http.end(); - // Log first 150 chars of raw response String preview = payload.substring(0, min((int)payload.length(), 150)); preview.replace("\n", "\\n"); logMsg(LOG_DEBUG, "RAW", preview); - // Parse all lines, return LAST valid message (newest) bool found = false; int lineStart = 0; int lineNum = 0; @@ -203,16 +210,13 @@ bool fetchNtfy(const char* topic, String& outId, String& outMessage, String& out if (line.length() > 0) { String id, msg, evt; if (parseNtfyLine(line, id, msg, evt)) { - logMsg(LOG_DEBUG, "JSON", - String("L") + lineNum + " evt=" + evt + " id=" + id + " msg=" + (msg.length() ? msg : "EMPTY")); + logMsg(LOG_DEBUG, "JSON", String("L") + lineNum + " evt=" + evt + " id=" + id + " msg=" + (msg.length() ? msg : "EMPTY")); - // Only count actual messages, not "open" events if (evt == "message" && msg.length() > 0) { outId = id; outMessage = msg; outEvent = evt; found = true; - // Continue to get the LAST (newest) message } } } @@ -249,13 +253,45 @@ void setState(State newState, const String& msg) { postStatus(newState == ALERTING ? "ALERTING" : "SILENT", msg); } +// ============== BUTTON HANDLING (RESTORED) ============== +void checkButton() { + static bool lastState = HIGH; + static unsigned long pressStart = 0; + static bool wasPressed = false; + + bool pressed = (digitalRead(BUTTON_PIN) == LOW); + + if (pressed && !lastState) { + logMsg(LOG_INFO, "BUTTON", "PRESSED"); + setBacklight(true); + pressStart = millis(); + wasPressed = true; + } else if (!pressed && lastState && wasPressed) { + unsigned long duration = millis() - pressStart; + logMsg(LOG_INFO, "BUTTON", String("RELEASED ") + duration + "ms"); + + if (currentState == ALERTING) { + logMsg(LOG_INFO, "BUTTON", "Force silence"); + setState(SILENT, ""); + flashConfirm("SILENCED"); + } else { + setBacklight(false); + } + wasPressed = false; + } + + lastState = pressed; +} + // ============== MAIN ============== void setup() { Serial.begin(115200); delay(1000); - logMsg(LOG_INFO, "MAIN", "=== KLUBHAUS DOORBELL v3.8.4 ==="); - logMsg(LOG_INFO, "MAIN", "Fix: ?since=all for cached messages"); + logMsg(LOG_INFO, "MAIN", "=== KLUBHAUS DOORBELL v3.8.5 ==="); + logMsg(LOG_INFO, "MAIN", "Fix: Button restored, URL fixed"); + + pinMode(BUTTON_PIN, INPUT_PULLUP); // RESTORED initDisplay(); initWiFi(); @@ -265,9 +301,10 @@ void setup() { } void loop() { + checkButton(); // RESTORED + unsigned long now = millis(); - // Poll ALERT topic if (now - lastAlertPoll >= POLL_INTERVAL_MS) { lastAlertPoll = now; @@ -285,12 +322,9 @@ void loop() { } else { logMsg(LOG_DEBUG, "ALERT", "Duplicate ID, ignored"); } - } else { - logMsg(LOG_DEBUG, "ALERT", "No messages or error"); } } - // Poll SILENCE topic (staggered) if (now - lastSilencePoll >= POLL_INTERVAL_MS + 1500) { lastSilencePoll = now;