From c07d968abc362c2036befdf1f44bf741d23e4ff4 Mon Sep 17 00:00:00 2001 From: Space-Banane Date: Mon, 2 Feb 2026 19:27:41 +0100 Subject: [PATCH] Add support for Kuma push monitoring with retry logic --- listen.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/listen.py b/listen.py index 5eb219e..ef5b8b0 100644 --- a/listen.py +++ b/listen.py @@ -29,6 +29,8 @@ ESP_API_KEY = os.getenv("ESP_API_KEY", "") KUMA_URL = os.getenv("KUMA_URL") KUMA_KEY = os.getenv("KUMA_KEY") POLL_INTERVAL = int(os.getenv("POLL_INTERVAL", "30")) +KUMA_PUSH_URL = os.getenv("KUMA_PUSH_URL") +KUMA_PUSH_INTERVAL = int(os.getenv("KUMA_PUSH_INTERVAL", "60")) class UptimeKumaMonitor: @@ -271,6 +273,51 @@ class ESP32Controller: logger.info("Disconnected from ESP32") +async def push_to_kuma(session: aiohttp.ClientSession, url: str): + """Send a GET request to the Kuma push URL with retry logic""" + try: + # First attempt + async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp: + logger.info(f"Kuma push: status {resp.status}") + return + except Exception as e: + logger.warning(f"Kuma push failed: {e} (retrying in 15s)") + await asyncio.sleep(15) + + try: + # Retry attempt + async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp: + logger.info(f"Kuma push retry succeeded: status {resp.status}") + except Exception as retry_e: + logger.error(f"Kuma push retry failed: {retry_e}") + + +async def kuma_push_monitor( + url: str, interval: int, session: Optional[aiohttp.ClientSession] = None +): + """Periodically send GET requests to the Kuma push URL. + + If a session is provided, it will be reused and not closed here. + If no session is provided, this function will create one and ensure it is + properly closed, even if the task is cancelled. + """ + own_session = False + if session is None: + session = aiohttp.ClientSession() + own_session = True + + try: + while True: + await push_to_kuma(session, url) + await asyncio.sleep(interval) + except asyncio.CancelledError: + # Ensure proper cleanup on cancellation before propagating + raise + finally: + if own_session and not session.closed: + await session.close() + + async def main(): """Main monitoring loop""" @@ -285,12 +332,13 @@ async def main(): if not KUMA_KEY: logger.error("KUMA_KEY not set in environment") - logger.error("Get your API key from Uptime Kuma: Settings → API Keys") sys.exit(1) # Initialize ESP32 controller esp = ESP32Controller(ESP_IP, ESP_API_KEY) + # Initialize ESP32 controller + esp = ESP32Controller(ESP_IP, ESP_API_KEY) # Connect to ESP32 logger.info(f"Connecting to ESP32 at {ESP_IP}...") if not await esp.connect(): @@ -300,6 +348,14 @@ async def main(): logger.info(f"Starting monitoring loop (interval: {POLL_INTERVAL}s)") + # Start Kuma push monitor if configured + push_task = None + if KUMA_PUSH_URL: + push_task = asyncio.create_task(kuma_push_monitor(KUMA_PUSH_URL, KUMA_PUSH_INTERVAL)) + logger.info(f"Kuma push monitor started (URL: {KUMA_PUSH_URL}, Interval: {KUMA_PUSH_INTERVAL}s)") + else: + logger.info("Kuma push monitor not configured") + try: async with aiohttp.ClientSession() as session: # Wait for esp to be fully connected @@ -327,6 +383,13 @@ async def main(): except Exception as e: logger.error(f"Unexpected error: {e}") finally: + # Cancel push monitor task if running + if push_task and not push_task.done(): + push_task.cancel() + try: + await push_task + except asyncio.CancelledError: + pass await esp.disconnect()