From d720d6a263ba15be1be25d62cbcc52a97320bb24 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Sun, 28 Dec 2025 17:10:33 +0100 Subject: [PATCH] fix(bridge): Replace polling with msg_subscribe for real-time messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical fixes based on user feedback: 1. **Remove auto-recv polling (30s interval)** - Polling with 'recv' doesn't fetch NEW messages, only reads from .msgs - Wasteful - creates unnecessary command traffic 2. **Add msg_subscribe for real-time message reception** - meshcli's msg_subscribe enables automatic message events - Messages arrive via EventType.CHANNEL_MSG_RECV events - No polling needed - truly asynchronous 3. **Make TZ configurable via .env instead of hardcoded** - Changed docker-compose.yml: TZ=${TZ:-UTC} - Added TZ=Europe/Warsaw to .env.example - Users can now set their own timezone 4. **Remove unused shlex import** - Not needed after switching to manual double-quote wrapping Technical details: - msg_subscribe sends subscription command to meshcli at init - meshcli then emits events when messages arrive - Events trigger TTY errors (harmless - meshcli tries to print_above) - Messages are still saved to .msgs file by meshcli core 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .env.example | 12 ++++++++++-- docker-compose.yml | 4 ++-- meshcore-bridge/bridge.py | 39 +++------------------------------------ 3 files changed, 15 insertions(+), 40 deletions(-) diff --git a/.env.example b/.env.example index dbdae94..fc6761a 100644 --- a/.env.example +++ b/.env.example @@ -13,7 +13,7 @@ MC_SERIAL_PORT=/dev/serial/by-id/usb-Espressif_Systems_heltec_wifi_lora_32_v4__1 MC_DEVICE_NAME=MarWoj # MeshCore configuration directory (where .msgs file is stored) -MC_CONFIG_DIR=/root/.config/meshcore +MC_CONFIG_DIR=/home/marek/.config/meshcore # ============================================ # Application Settings @@ -30,7 +30,7 @@ MC_INACTIVE_HOURS=48 # ============================================ # Directory for storing archived messages -MC_ARCHIVE_DIR=/mnt/archive/meshcore +MC_ARCHIVE_DIR=/home/marek/.config/meshcore/archive # Enable automatic daily archiving at midnight MC_ARCHIVE_ENABLED=true @@ -50,3 +50,11 @@ FLASK_PORT=5000 # Debug mode (true/false) - use false in production FLASK_DEBUG=false + +# ============================================ +# System Configuration +# ============================================ + +# Timezone for container logs (default: UTC) +# Examples: Europe/Warsaw, America/New_York, Asia/Tokyo +TZ=Europe/Warsaw diff --git a/docker-compose.yml b/docker-compose.yml index 747a9d3..e3ad321 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: - MC_SERIAL_PORT=${MC_SERIAL_PORT} - MC_CONFIG_DIR=/root/.config/meshcore - MC_DEVICE_NAME=${MC_DEVICE_NAME} - - TZ=Europe/Warsaw + - TZ=${TZ:-UTC} networks: - meshcore-net healthcheck: @@ -48,7 +48,7 @@ services: - FLASK_HOST=${FLASK_HOST:-0.0.0.0} - FLASK_PORT=${FLASK_PORT:-5000} - FLASK_DEBUG=${FLASK_DEBUG:-false} - - TZ=Europe/Warsaw + - TZ=${TZ:-UTC} env_file: - .env depends_on: diff --git a/meshcore-bridge/bridge.py b/meshcore-bridge/bridge.py index d9baad8..3371615 100644 --- a/meshcore-bridge/bridge.py +++ b/meshcore-bridge/bridge.py @@ -18,7 +18,6 @@ import time import json import queue import uuid -import shlex from pathlib import Path from flask import Flask, request, jsonify @@ -74,14 +73,10 @@ class MeshCLISession: self.stderr_thread = None self.stdin_thread = None self.watchdog_thread = None - self.auto_recv_thread = None # Shutdown flag self.shutdown_flag = threading.Event() - # Auto-recv interval (seconds) - periodic message sync - self.auto_recv_interval = 30 - # Start session self._start_session() @@ -114,9 +109,6 @@ class MeshCLISession: self.watchdog_thread = threading.Thread(target=self._watchdog, daemon=True, name="watchdog") self.watchdog_thread.start() - self.auto_recv_thread = threading.Thread(target=self._auto_recv, daemon=True, name="auto-recv") - self.auto_recv_thread.start() - # Initialize session settings time.sleep(0.5) # Let meshcli initialize self._init_session_settings() @@ -128,7 +120,7 @@ class MeshCLISession: raise def _init_session_settings(self): - """Configure meshcli session for advert logging""" + """Configure meshcli session for advert logging and message subscription""" logger.info("Configuring meshcli session settings") # Send configuration commands directly to stdin (bypass queue for init) @@ -136,8 +128,9 @@ class MeshCLISession: try: self.process.stdin.write('set json_log_rx on\n') self.process.stdin.write('set print_adverts on\n') + self.process.stdin.write('msg_subscribe\n') self.process.stdin.flush() - logger.info("Session settings applied: json_log_rx=on, print_adverts=on") + logger.info("Session settings applied: json_log_rx=on, print_adverts=on, msg_subscribe") except Exception as e: logger.error(f"Failed to apply session settings: {e}") @@ -284,32 +277,6 @@ class MeshCLISession: logger.info("watchdog thread exiting") - def _auto_recv(self): - """Thread: Periodically call recv to sync messages in background""" - logger.info(f"auto-recv thread started (interval: {self.auto_recv_interval}s)") - - # Wait a bit before first recv to let session initialize - time.sleep(5) - - while not self.shutdown_flag.is_set(): - try: - # Execute recv command via internal queue - logger.debug("Auto-recv: fetching new messages") - result = self.execute_command(['recv'], timeout=RECV_TIMEOUT) - - if result['success']: - logger.debug("Auto-recv: messages synced successfully") - else: - logger.warning(f"Auto-recv failed: {result.get('stderr', 'unknown error')}") - - except Exception as e: - logger.error(f"Auto-recv error: {e}") - - # Wait for next interval (or until shutdown) - self.shutdown_flag.wait(self.auto_recv_interval) - - logger.info("auto-recv thread exiting") - def _is_advert_json(self, line): """Check if line is a JSON advert""" try: