mmrelay 1.0__tar.gz → 1.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mmrelay might be problematic. Click here for more details.

Files changed (41) hide show
  1. {mmrelay-1.0/src/mmrelay.egg-info → mmrelay-1.0.2}/PKG-INFO +24 -9
  2. {mmrelay-1.0 → mmrelay-1.0.2}/README.md +19 -5
  3. {mmrelay-1.0 → mmrelay-1.0.2}/requirements.txt +4 -3
  4. {mmrelay-1.0 → mmrelay-1.0.2}/sample_config.yaml +3 -2
  5. {mmrelay-1.0 → mmrelay-1.0.2}/setup.cfg +5 -4
  6. mmrelay-1.0.2/src/mmrelay/__init__.py +17 -0
  7. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/cli.py +9 -2
  8. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/config.py +2 -3
  9. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/log_utils.py +56 -27
  10. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/main.py +30 -1
  11. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/meshtastic_utils.py +54 -4
  12. mmrelay-1.0.2/src/mmrelay/setup_utils.py +426 -0
  13. {mmrelay-1.0 → mmrelay-1.0.2/src/mmrelay.egg-info}/PKG-INFO +24 -9
  14. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay.egg-info/requires.txt +4 -3
  15. mmrelay-1.0.2/tools/mmrelay.service +18 -0
  16. mmrelay-1.0/src/mmrelay/__init__.py +0 -9
  17. mmrelay-1.0/src/mmrelay/setup_utils.py +0 -263
  18. mmrelay-1.0/tools/mmrelay.service +0 -12
  19. {mmrelay-1.0 → mmrelay-1.0.2}/LICENSE +0 -0
  20. {mmrelay-1.0 → mmrelay-1.0.2}/MANIFEST.in +0 -0
  21. {mmrelay-1.0 → mmrelay-1.0.2}/pyproject.toml +0 -0
  22. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/config_checker.py +0 -0
  23. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/db_utils.py +0 -0
  24. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/matrix_utils.py +0 -0
  25. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugin_loader.py +0 -0
  26. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/__init__.py +0 -0
  27. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/base_plugin.py +0 -0
  28. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/debug_plugin.py +0 -0
  29. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/drop_plugin.py +0 -0
  30. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/health_plugin.py +0 -0
  31. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/help_plugin.py +0 -0
  32. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/map_plugin.py +0 -0
  33. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/mesh_relay_plugin.py +0 -0
  34. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/nodes_plugin.py +0 -0
  35. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/ping_plugin.py +0 -0
  36. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/telemetry_plugin.py +0 -0
  37. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay/plugins/weather_plugin.py +0 -0
  38. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay.egg-info/SOURCES.txt +0 -0
  39. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay.egg-info/dependency_links.txt +0 -0
  40. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay.egg-info/entry_points.txt +0 -0
  41. {mmrelay-1.0 → mmrelay-1.0.2}/src/mmrelay.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mmrelay
3
- Version: 1.0
3
+ Version: 1.0.2
4
4
  Summary: Bridge between Meshtastic mesh networks and Matrix chat rooms
5
5
  Home-page: https://github.com/geoffwhittington/meshtastic-matrix-relay
6
6
  Author: Geoff Whittington, Jeremiah K., and contributors
@@ -15,19 +15,32 @@ Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: meshtastic
18
- Requires-Dist: Pillow==11.1.0
18
+ Requires-Dist: Pillow==11.2.1
19
19
  Requires-Dist: matrix-nio==0.25.2
20
20
  Requires-Dist: matplotlib==3.10.1
21
21
  Requires-Dist: requests==2.32.3
22
- Requires-Dist: markdown==3.7
22
+ Requires-Dist: markdown==3.8
23
23
  Requires-Dist: haversine==2.9.0
24
24
  Requires-Dist: schedule==1.2.2
25
- Requires-Dist: platformdirs==4.2.0
25
+ Requires-Dist: platformdirs==4.3.7
26
26
  Requires-Dist: py-staticmaps>=0.4.0
27
+ Requires-Dist: rich==14.0.0
27
28
  Dynamic: license-file
28
29
 
29
30
  # M<>M Relay
30
31
 
32
+ ## ✨ Version 1.0 Released! ✨
33
+
34
+ **We're excited to announce MMRelay v1.0 with improved packaging, standardized directories, and enhanced CLI!**
35
+
36
+ **Existing users:** Version 1.0 requires a few quick migration steps:
37
+
38
+ 1. Follow the [UPGRADE_TO_V1.md](UPGRADE_TO_V1.md) guide for a smooth transition
39
+ 2. Move your configuration to the new standard location (`~/.mmrelay/config.yaml`)
40
+ 3. See [ANNOUNCEMENT.md](ANNOUNCEMENT.md) for all the exciting new features
41
+
42
+ Not ready to upgrade yet? No problem! Run `git checkout 0.10.1` to continue using the previous version.
43
+
31
44
  ## (Meshtastic <=> Matrix Relay)
32
45
 
33
46
  A powerful and easy-to-use relay between Meshtastic devices and Matrix chat rooms, allowing seamless communication across platforms. This opens the door for bridging Meshtastic devices to [many other platforms](https://matrix.org/bridges/).
@@ -41,17 +54,19 @@ MMRelay runs on Linux, macOS, and Windows.
41
54
  ### Quick Installation
42
55
 
43
56
  ```bash
44
- # Install using pip
45
- pip install mmrelay
46
-
47
- # Or use pipx for isolated installation (recommended)
57
+ # Install using pipx for isolated installation (recommended)
48
58
  pipx install mmrelay
59
+
60
+ # Pip will also work if you prefer
61
+ pip install mmrelay
49
62
  ```
50
63
 
64
+ For pipx installation instructions, see: [pipx installation guide](https://pipx.pypa.io/stable/installation/#on-linux)
65
+
51
66
  ### Resources
52
67
 
53
68
  - **New Users**: See [INSTRUCTIONS.md](INSTRUCTIONS.md) for setup and configuration
54
- - **Existing Users**: Check [UPGRADE_TO_V1.md](UPGRADE_TO_V1.md) for migration guidance
69
+ - **Existing Users**: See [UPGRADE_TO_V1.md](UPGRADE_TO_V1.md) for migration guidance
55
70
  - **Configuration**: Review [sample_config.yaml](sample_config.yaml) for examples
56
71
 
57
72
  ### Command-Line Options
@@ -1,5 +1,17 @@
1
1
  # M<>M Relay
2
2
 
3
+ ## ✨ Version 1.0 Released! ✨
4
+
5
+ **We're excited to announce MMRelay v1.0 with improved packaging, standardized directories, and enhanced CLI!**
6
+
7
+ **Existing users:** Version 1.0 requires a few quick migration steps:
8
+
9
+ 1. Follow the [UPGRADE_TO_V1.md](UPGRADE_TO_V1.md) guide for a smooth transition
10
+ 2. Move your configuration to the new standard location (`~/.mmrelay/config.yaml`)
11
+ 3. See [ANNOUNCEMENT.md](ANNOUNCEMENT.md) for all the exciting new features
12
+
13
+ Not ready to upgrade yet? No problem! Run `git checkout 0.10.1` to continue using the previous version.
14
+
3
15
  ## (Meshtastic <=> Matrix Relay)
4
16
 
5
17
  A powerful and easy-to-use relay between Meshtastic devices and Matrix chat rooms, allowing seamless communication across platforms. This opens the door for bridging Meshtastic devices to [many other platforms](https://matrix.org/bridges/).
@@ -13,17 +25,19 @@ MMRelay runs on Linux, macOS, and Windows.
13
25
  ### Quick Installation
14
26
 
15
27
  ```bash
16
- # Install using pip
17
- pip install mmrelay
18
-
19
- # Or use pipx for isolated installation (recommended)
28
+ # Install using pipx for isolated installation (recommended)
20
29
  pipx install mmrelay
30
+
31
+ # Pip will also work if you prefer
32
+ pip install mmrelay
21
33
  ```
22
34
 
35
+ For pipx installation instructions, see: [pipx installation guide](https://pipx.pypa.io/stable/installation/#on-linux)
36
+
23
37
  ### Resources
24
38
 
25
39
  - **New Users**: See [INSTRUCTIONS.md](INSTRUCTIONS.md) for setup and configuration
26
- - **Existing Users**: Check [UPGRADE_TO_V1.md](UPGRADE_TO_V1.md) for migration guidance
40
+ - **Existing Users**: See [UPGRADE_TO_V1.md](UPGRADE_TO_V1.md) for migration guidance
27
41
  - **Configuration**: Review [sample_config.yaml](sample_config.yaml) for examples
28
42
 
29
43
  ### Command-Line Options
@@ -1,10 +1,11 @@
1
1
  meshtastic
2
- Pillow==11.1.0
2
+ Pillow==11.2.1
3
3
  matrix-nio==0.25.2
4
4
  matplotlib==3.10.1
5
5
  requests==2.32.3
6
- markdown==3.7
6
+ markdown==3.8
7
7
  haversine==2.9.0
8
8
  schedule==1.2.2
9
- platformdirs==4.2.0
9
+ platformdirs==4.3.7
10
10
  py-staticmaps>=0.4.0
11
+ rich==14.0.0
@@ -22,10 +22,11 @@ meshtastic:
22
22
 
23
23
  logging:
24
24
  level: info
25
- #log_to_file: true # Default is false
26
- #filename: ~/.mmrelay/logs/mmrelay.log # Default location
25
+ #log_to_file: true # Set to true to enable file logging
26
+ #filename: ~/.mmrelay/logs/mmrelay.log # Default location if log_to_file is true
27
27
  #max_log_size: 10485760 # 10 MB default if omitted
28
28
  #backup_count: 1 # Keeps 1 backup as the default if omitted
29
+ #color_enabled: true # Set to false to disable colored console output
29
30
 
30
31
  #database:
31
32
  # path: ~/.mmrelay/data/meshtastic.sqlite # Default location
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = mmrelay
3
- version = 1.0
3
+ version = 1.0.2
4
4
  author = Geoff Whittington, Jeremiah K., and contributors
5
5
  author_email = jeremiahk@gmx.com
6
6
  description = Bridge between Meshtastic mesh networks and Matrix chat rooms
@@ -23,15 +23,16 @@ packages = find:
23
23
  python_requires = >=3.8
24
24
  install_requires =
25
25
  meshtastic
26
- Pillow==11.1.0
26
+ Pillow==11.2.1
27
27
  matrix-nio==0.25.2
28
28
  matplotlib==3.10.1
29
29
  requests==2.32.3
30
- markdown==3.7
30
+ markdown==3.8
31
31
  haversine==2.9.0
32
32
  schedule==1.2.2
33
- platformdirs==4.2.0
33
+ platformdirs==4.3.7
34
34
  py-staticmaps>=0.4.0
35
+ rich==14.0.0
35
36
 
36
37
  [options.packages.find]
37
38
  where = src
@@ -0,0 +1,17 @@
1
+ """
2
+ Meshtastic Matrix Relay - Bridge between Meshtastic mesh networks and Matrix chat rooms.
3
+ """
4
+
5
+ import os
6
+ import pkg_resources
7
+
8
+ # First try to get version from environment variable (GitHub tag)
9
+ if "GITHUB_REF_NAME" in os.environ:
10
+ __version__ = os.environ.get("GITHUB_REF_NAME")
11
+ else:
12
+ # Fall back to setup.cfg metadata using pkg_resources (compatible with PyInstaller)
13
+ try:
14
+ __version__ = pkg_resources.get_distribution("mmrelay").version
15
+ except pkg_resources.DistributionNotFound:
16
+ # If all else fails, use hardcoded version
17
+ __version__ = "1.0.1"
@@ -94,6 +94,13 @@ def get_version():
94
94
  return __version__
95
95
 
96
96
 
97
+ def print_version():
98
+ """
99
+ Print the version in a simple format.
100
+ """
101
+ print(f"MMRelay v{__version__}")
102
+
103
+
97
104
  def check_config(args=None):
98
105
  """
99
106
  Check if the configuration file is valid.
@@ -262,7 +269,7 @@ def main():
262
269
 
263
270
  # Handle --version
264
271
  if args.version:
265
- print(f"mmrelay {get_version()}")
272
+ print_version()
266
273
  return 0
267
274
 
268
275
  # If no command was specified, run the main functionality
@@ -289,7 +296,7 @@ def handle_cli_commands(args):
289
296
  """
290
297
  # Handle --version
291
298
  if args.version:
292
- print(f"mmrelay {get_version()}")
299
+ print_version()
293
300
  return True
294
301
 
295
302
  # Handle --install-service
@@ -187,7 +187,7 @@ def load_config(config_file=None, args=None):
187
187
 
188
188
  # If a specific config file was provided, use it
189
189
  if config_file and os.path.isfile(config_file):
190
- logger.info(f"Loading configuration from: {config_file}")
190
+ # Store the config path but don't log it yet - will be logged by main.py
191
191
  with open(config_file, "r") as f:
192
192
  relay_config = yaml.load(f, Loader=SafeLoader)
193
193
  config_path = config_file
@@ -200,10 +200,9 @@ def load_config(config_file=None, args=None):
200
200
  for path in config_paths:
201
201
  if os.path.isfile(path):
202
202
  config_path = path
203
- logger.info(f"Loading configuration from: {config_path}")
203
+ # Store the config path but don't log it yet - will be logged by main.py
204
204
  with open(config_path, "r") as f:
205
205
  relay_config = yaml.load(f, Loader=SafeLoader)
206
- logger.info(f"Loaded configuration with keys: {list(relay_config.keys())}")
207
206
  return relay_config
208
207
 
209
208
  # No config file found
@@ -2,44 +2,84 @@ import logging
2
2
  import os
3
3
  from logging.handlers import RotatingFileHandler
4
4
 
5
+ from rich.console import Console
6
+ from rich.logging import RichHandler
7
+
5
8
  from mmrelay.cli import parse_arguments
6
9
  from mmrelay.config import get_log_dir
7
10
 
11
+ # Initialize Rich console
12
+ console = Console()
13
+
14
+ # Define custom log level styles - not used directly but kept for reference
15
+ # Rich 14.0.0+ supports level_styles parameter, but we're using an approach
16
+ # that works with older versions too
17
+ LOG_LEVEL_STYLES = {
18
+ "DEBUG": "dim blue",
19
+ "INFO": "green",
20
+ "WARNING": "yellow",
21
+ "ERROR": "bold red",
22
+ "CRITICAL": "bold white on red",
23
+ }
24
+
8
25
  # Global config variable that will be set from main.py
9
26
  config = None
10
27
 
28
+ # Global variable to store the log file path
29
+ log_file_path = None
30
+
11
31
 
12
32
  def get_logger(name):
13
33
  logger = logging.getLogger(name=name)
14
34
 
15
35
  # Default to INFO level if config is not available
16
36
  log_level = logging.INFO
37
+ color_enabled = True # Default to using colors
17
38
 
18
- # Try to get log level from config
39
+ # Try to get log level and color settings from config
19
40
  global config
20
- if config is not None and "logging" in config and "level" in config["logging"]:
21
- log_level = getattr(logging, config["logging"]["level"].upper())
41
+ if config is not None and "logging" in config:
42
+ if "level" in config["logging"]:
43
+ log_level = getattr(logging, config["logging"]["level"].upper())
44
+ # Check if colors should be disabled
45
+ if "color_enabled" in config["logging"]:
46
+ color_enabled = config["logging"]["color_enabled"]
22
47
 
23
48
  logger.setLevel(log_level)
24
49
  logger.propagate = False
25
50
 
26
- # Add stream handler (console logging)
27
- stream_handler = logging.StreamHandler()
28
- stream_handler.setFormatter(
29
- logging.Formatter(
30
- fmt="%(asctime)s %(levelname)s:%(name)s:%(message)s",
31
- datefmt="%Y-%m-%d %H:%M:%S %z",
51
+ # Add handler for console logging (with or without colors)
52
+ if color_enabled:
53
+ # Use Rich handler with colors
54
+ console_handler = RichHandler(
55
+ rich_tracebacks=True,
56
+ console=console,
57
+ show_time=True,
58
+ show_level=True,
59
+ show_path=False,
60
+ markup=True,
61
+ log_time_format="%Y-%m-%d %H:%M:%S",
62
+ omit_repeated_times=False,
63
+ )
64
+ console_handler.setFormatter(logging.Formatter("%(name)s: %(message)s"))
65
+ else:
66
+ # Use standard handler without colors
67
+ console_handler = logging.StreamHandler()
68
+ console_handler.setFormatter(
69
+ logging.Formatter(
70
+ fmt="%(asctime)s %(levelname)s:%(name)s:%(message)s",
71
+ datefmt="%Y-%m-%d %H:%M:%S %z",
72
+ )
32
73
  )
33
- )
34
- logger.addHandler(stream_handler)
74
+ logger.addHandler(console_handler)
35
75
 
36
76
  # Check command line arguments for log file path
37
77
  args = parse_arguments()
38
78
 
39
- # Check if file logging is enabled
79
+ # Check if file logging is enabled (default to True for better user experience)
40
80
  if (
41
81
  config is not None
42
- and config.get("logging", {}).get("log_to_file", False)
82
+ and config.get("logging", {}).get("log_to_file", True)
43
83
  or args.logfile
44
84
  ):
45
85
  # Priority: 1. Command line arg, 2. Config file, 3. Default location (~/.mmrelay/logs)
@@ -64,21 +104,10 @@ def get_logger(name):
64
104
  if log_dir: # Ensure non-empty directory paths exist
65
105
  os.makedirs(log_dir, exist_ok=True)
66
106
 
67
- # Log which file we're using (only for the first logger)
107
+ # Store the log file path for later use
68
108
  if name == "M<>M Relay":
69
- # Create a basic logger to log the log file path
70
- # This is needed because we can't use the logger we're creating to log its own creation
71
- basic_logger = logging.getLogger("LogSetup")
72
- basic_logger.setLevel(logging.INFO)
73
- basic_handler = logging.StreamHandler()
74
- basic_handler.setFormatter(
75
- logging.Formatter(
76
- fmt="%(asctime)s %(levelname)s:%(name)s:%(message)s",
77
- datefmt="%Y-%m-%d %H:%M:%S %z",
78
- )
79
- )
80
- basic_logger.addHandler(basic_handler)
81
- basic_logger.info(f"Writing logs to: {log_file}")
109
+ global log_file_path
110
+ log_file_path = log_file
82
111
 
83
112
  # Create a file handler for logging
84
113
  try:
@@ -10,8 +10,9 @@ import sys
10
10
 
11
11
  from nio import ReactionEvent, RoomMessageEmote, RoomMessageNotice, RoomMessageText
12
12
 
13
+ # Import version from package
13
14
  # Import meshtastic_utils as a module to set event_loop
14
- from mmrelay import meshtastic_utils
15
+ from mmrelay import __version__, meshtastic_utils
15
16
  from mmrelay.db_utils import (
16
17
  initialize_database,
17
18
  update_longnames,
@@ -33,6 +34,18 @@ logger = get_logger(name="M<>M Relay")
33
34
  logging.getLogger("nio").setLevel(logging.ERROR)
34
35
 
35
36
 
37
+ # Flag to track if banner has been printed
38
+ _banner_printed = False
39
+
40
+ def print_banner():
41
+ """Print a simple startup message with version information."""
42
+ global _banner_printed
43
+ # Only print the banner once
44
+ if not _banner_printed:
45
+ logger.info(f"Starting MMRelay v{__version__}")
46
+ _banner_printed = True
47
+
48
+
36
49
  async def main(config):
37
50
  """
38
51
  Main asynchronous function to set up and run the relay.
@@ -202,6 +215,9 @@ def run_main(args):
202
215
  Returns:
203
216
  int: Exit code (0 for success, non-zero for failure)
204
217
  """
218
+ # Print the banner at startup
219
+ print_banner()
220
+
205
221
  # Handle the --data-dir option
206
222
  if args and args.data_dir:
207
223
  import os
@@ -245,6 +261,19 @@ def run_main(args):
245
261
  set_config(db_utils, config)
246
262
  set_config(base_plugin, config)
247
263
 
264
+ # Get config path and log file path for logging
265
+ from mmrelay.config import config_path
266
+ from mmrelay.log_utils import log_file_path
267
+
268
+ # Create a logger with a different name to avoid conflicts with the one in config.py
269
+ config_rich_logger = get_logger("ConfigInfo")
270
+
271
+ # Now log the config file and log file locations with the properly formatted logger
272
+ if config_path:
273
+ config_rich_logger.info(f"Config file location: {config_path}")
274
+ if log_file_path:
275
+ config_rich_logger.info(f"Log file location: {log_file_path}")
276
+
248
277
  # Check if config exists and has the required keys
249
278
  required_keys = ["matrix", "meshtastic", "matrix_rooms"]
250
279
 
@@ -1,6 +1,8 @@
1
1
  import asyncio
2
2
  import contextlib
3
3
  import io
4
+ import os
5
+ import sys
4
6
  import threading
5
7
  import time
6
8
  from typing import List
@@ -46,6 +48,32 @@ shutting_down = False
46
48
  reconnect_task = None # To keep track of the reconnect task
47
49
 
48
50
 
51
+ def is_running_as_service():
52
+ """
53
+ Check if the application is running as a systemd service.
54
+ This is used to determine whether to show Rich progress indicators.
55
+
56
+ Returns:
57
+ bool: True if running as a service, False otherwise
58
+ """
59
+ # Check for INVOCATION_ID environment variable (set by systemd)
60
+ if os.environ.get("INVOCATION_ID"):
61
+ return True
62
+
63
+ # Check if parent process is systemd
64
+ try:
65
+ with open("/proc/self/status") as f:
66
+ for line in f:
67
+ if line.startswith("PPid:"):
68
+ ppid = int(line.split()[1])
69
+ with open(f"/proc/{ppid}/comm") as p:
70
+ return p.read().strip() == "systemd"
71
+ except (FileNotFoundError, PermissionError, ValueError):
72
+ pass
73
+
74
+ return False
75
+
76
+
49
77
  def serial_port_exists(port_name):
50
78
  """
51
79
  Check if the specified serial port exists.
@@ -118,7 +146,7 @@ def connect_meshtastic(passed_config=None, force_connect=False):
118
146
  if connection_type == "serial":
119
147
  # Serial connection
120
148
  serial_port = config["meshtastic"]["serial_port"]
121
- logger.info(f"Connecting to serial port {serial_port} ...")
149
+ logger.info(f"Connecting to serial port {serial_port}")
122
150
 
123
151
  # Check if serial port exists before connecting
124
152
  if not serial_port_exists(serial_port):
@@ -137,7 +165,9 @@ def connect_meshtastic(passed_config=None, force_connect=False):
137
165
  # BLE connection
138
166
  ble_address = config["meshtastic"].get("ble_address")
139
167
  if ble_address:
140
- logger.info(f"Connecting to BLE address {ble_address} ...")
168
+ logger.info(f"Connecting to BLE address {ble_address}")
169
+
170
+ # Connect without progress indicator
141
171
  meshtastic_client = meshtastic.ble_interface.BLEInterface(
142
172
  address=ble_address,
143
173
  noProto=False,
@@ -151,7 +181,9 @@ def connect_meshtastic(passed_config=None, force_connect=False):
151
181
  elif connection_type == "tcp":
152
182
  # TCP connection
153
183
  target_host = config["meshtastic"]["host"]
154
- logger.info(f"Connecting to host {target_host} ...")
184
+ logger.info(f"Connecting to host {target_host}")
185
+
186
+ # Connect without progress indicator
155
187
  meshtastic_client = meshtastic.tcp_interface.TCPInterface(
156
188
  hostname=target_host
157
189
  )
@@ -244,7 +276,25 @@ async def reconnect():
244
276
  logger.info(
245
277
  f"Reconnection attempt starting in {backoff_time} seconds..."
246
278
  )
247
- await asyncio.sleep(backoff_time)
279
+
280
+ # Show reconnection countdown with Rich (if not in a service)
281
+ if not is_running_as_service():
282
+ from rich.progress import Progress, TextColumn, BarColumn, TimeRemainingColumn
283
+ with Progress(
284
+ TextColumn("[cyan]Meshtastic: Reconnecting in"),
285
+ BarColumn(),
286
+ TextColumn("[cyan]{task.percentage:.0f}%"),
287
+ TimeRemainingColumn(),
288
+ transient=True,
289
+ ) as progress:
290
+ task = progress.add_task("Waiting", total=backoff_time)
291
+ for _ in range(backoff_time):
292
+ if shutting_down:
293
+ break
294
+ await asyncio.sleep(1)
295
+ progress.update(task, advance=1)
296
+ else:
297
+ await asyncio.sleep(backoff_time)
248
298
  if shutting_down:
249
299
  logger.debug(
250
300
  "Shutdown in progress. Aborting reconnection attempts."