From 2afb6b81ed6bb2638591881b31f7ac5572a0bf6d Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Mon, 16 Feb 2026 00:23:25 -0800 Subject: [PATCH] snapshot --- .../DoorbellLogic.cpp | 79 ++++++++++++++----- .../doorbell-touch-esp32-32e/DoorbellLogic.h | 3 - sketches/doorbell-touch-esp32-32e/mise.toml | 3 +- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/sketches/doorbell-touch-esp32-32e/DoorbellLogic.cpp b/sketches/doorbell-touch-esp32-32e/DoorbellLogic.cpp index a4b3df5..32875db 100644 --- a/sketches/doorbell-touch-esp32-32e/DoorbellLogic.cpp +++ b/sketches/doorbell-touch-esp32-32e/DoorbellLogic.cpp @@ -78,6 +78,10 @@ void DoorbellLogic::finishBoot() { flushStatus(); } + Serial.printf("[CONFIG] ALERT_URL: %s\n", ALERT_URL); + Serial.printf("[CONFIG] SILENCE_URL: %s\n", SILENCE_URL); + Serial.printf("[CONFIG] ADMIN_URL: %s\n", ADMIN_URL); + transitionTo(DeviceState::SILENT); Serial.printf("[BOOT] Grace period: %d ms\n", BOOT_GRACE_MS); } @@ -223,7 +227,7 @@ void DoorbellLogic::transitionTo(DeviceState newState) { switch (newState) { case DeviceState::SILENT: _screen.screen = ScreenID::OFF; - _alertStartEpoch = 0; // <-- ADD: clear on silence + _alertMsgEpoch = 0; Serial.println("-> SILENT"); break; case DeviceState::ALERTING: @@ -248,24 +252,31 @@ void DoorbellLogic::transitionTo(DeviceState newState) { void DoorbellLogic::handleAlert(const String& msg) { if (_state == DeviceState::ALERTING && _currentMessage == msg) return; _currentMessage = msg; + _alertMsgEpoch = _lastParsedMsgEpoch; // store ntfy server time of this alert + Serial.printf("[ALERT] Accepted. ntfy time=%ld\n", (long)_alertMsgEpoch); transitionTo(DeviceState::ALERTING); queueStatus("ALERTING", msg); } void DoorbellLogic::handleSilence(const String& msg) { - // When called from ntfy poll, reject silence messages that predate the current alert. - // This prevents stale silence messages from immediately canceling new alerts. - // When called from touch/admin/timeout, _lastParsedMsgEpoch is 0 → bypass check. - if (_state == DeviceState::ALERTING && - _lastParsedMsgEpoch > 0 && - _alertStartEpoch > 0 && - _lastParsedMsgEpoch <= _alertStartEpoch) { - Serial.printf("[SILENCE] Ignored — predates alert (msg:%ld alert:%ld)\n", - (long)_lastParsedMsgEpoch, (long)_alertStartEpoch); + if (_state != DeviceState::ALERTING) { + Serial.println("[SILENCE] Ignored — not alerting"); return; } + // If this came from ntfy poll (_lastParsedMsgEpoch > 0), reject if it + // predates or equals the alert. Both timestamps are from ntfy's server clock. + if (_lastParsedMsgEpoch > 0 && _alertMsgEpoch > 0 && + _lastParsedMsgEpoch <= _alertMsgEpoch) { + Serial.printf("[SILENCE] Ignored — predates alert (silence:%ld <= alert:%ld)\n", + (long)_lastParsedMsgEpoch, (long)_alertMsgEpoch); + return; + } + + Serial.printf("[SILENCE] Accepted (silence:%ld > alert:%ld)\n", + (long)_lastParsedMsgEpoch, (long)_alertMsgEpoch); _currentMessage = ""; + _alertMsgEpoch = 0; transitionTo(DeviceState::SILENT); queueStatus("SILENT", "silenced"); } @@ -363,38 +374,64 @@ void DoorbellLogic::checkNetwork() { // ntfy Polling // ===================================================================== void DoorbellLogic::pollTopics() { - Serial.println("[POLL] Starting poll cycle..."); + Serial.printf("[POLL] Starting poll cycle... WiFi:%s NTP:%d Grace:%d\n", + WiFi.isConnected() ? "OK" : "DOWN", _ntpSynced, _inBootGrace); + pollTopic(ALERT_URL, &DoorbellLogic::handleAlert, "ALERT", _lastAlertId); - yield(); // ← MISSING + yield(); pollTopic(SILENCE_URL, &DoorbellLogic::handleSilence, "SILENCE", _lastSilenceId); - yield(); // ← MISSING + yield(); pollTopic(ADMIN_URL, &DoorbellLogic::handleAdmin, "ADMIN", _lastAdminId); - Serial.printf("[POLL] Done. Heap: %dKB\n", ESP.getFreeHeap() / 1024); // ← MISSING + + Serial.printf("[POLL] Done. Heap: %dKB\n", ESP.getFreeHeap() / 1024); } void DoorbellLogic::pollTopic(const char* url, void (DoorbellLogic::*handler)(const String&), const char* name, String& lastId) { - if (!WiFi.isConnected()) { // ← MISSING - Serial.printf("[%s] Skipped — WiFi down\n", name); + Serial.printf("[%s] Polling: %s\n", name, url); + + if (!WiFi.isConnected()) { + Serial.printf("[%s] SKIPPED — WiFi down\n", name); return; } WiFiClientSecure client; client.setInsecure(); - client.setTimeout(10); // ← MISSING + client.setTimeout(10); HTTPClient http; http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http.setTimeout(10000); - http.setReuse(false); // ← MISSING + http.setReuse(false); - // ... rest of method ... + if (!http.begin(client, url)) { + Serial.printf("[%s] begin() FAILED\n", name); + return; + } + + int code = http.GET(); + Serial.printf("[%s] HTTP %d\n", name, code); + + if (code == HTTP_CODE_OK) { + String response = http.getString(); + Serial.printf("[%s] %d bytes\n", name, response.length()); + if (response.length() > 0) { + parseMessages(response, name, handler, lastId); + } else { + Serial.printf("[%s] Empty response\n", name); + } + } else if (code < 0) { + Serial.printf("[%s] ERROR: %s\n", name, http.errorToString(code).c_str()); + _networkOK = false; + } else { + Serial.printf("[%s] Unexpected code: %d\n", name, code); + } http.end(); - client.stop(); // ← MISSING + client.stop(); - yield(); // ← MISSING + yield(); } void DoorbellLogic::parseMessages(String& response, const char* name, diff --git a/sketches/doorbell-touch-esp32-32e/DoorbellLogic.h b/sketches/doorbell-touch-esp32-32e/DoorbellLogic.h index a3e9778..4a14be2 100644 --- a/sketches/doorbell-touch-esp32-32e/DoorbellLogic.h +++ b/sketches/doorbell-touch-esp32-32e/DoorbellLogic.h @@ -49,9 +49,6 @@ private: unsigned long _lastHeartbeat = 0; bool _blinkState = false; - time_t _lastParsedMsgEpoch = 0; // <-- ADD - time_t _alertStartEpoch = 0; // <-- ADD - // Deferred status publish bool _pendingStatus = false; String _pendStatusState; diff --git a/sketches/doorbell-touch-esp32-32e/mise.toml b/sketches/doorbell-touch-esp32-32e/mise.toml index fb8a73e..24f29f7 100644 --- a/sketches/doorbell-touch-esp32-32e/mise.toml +++ b/sketches/doorbell-touch-esp32-32e/mise.toml @@ -1,6 +1,7 @@ [tools] arduino-cli = "latest" lazygit = "latest" +python = "latest" [env] FQBN = "esp32:esp32:esp32:UploadSpeed=921600,CPUFreq=240,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=default,DebugLevel=info,EraseFlash=none" @@ -24,7 +25,7 @@ depends = ["compile"] run = "arduino-cli upload --fqbn $FQBN -p $(arduino-cli board list | grep -i 'esp32\\|usb\\|ttyUSB\\|CP210\\|CH340' | head -1 | awk '{print $1}') ." [tasks.monitor] -run = "tio /dev/ttyUSB0" +run = "arduino-cli monitor --port /dev/ttyUSB0 -c 115200" [tasks.all] depends = ["upload"]