mmrelay 1.1.3__py3-none-any.whl → 1.1.4__py3-none-any.whl

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.

mmrelay/__init__.py CHANGED
@@ -2,4 +2,4 @@
2
2
  Meshtastic Matrix Relay - Bridge between Meshtastic mesh networks and Matrix chat rooms.
3
3
  """
4
4
 
5
- __version__ = "1.1.3"
5
+ __version__ = "1.1.4"
mmrelay/cli.py CHANGED
@@ -12,15 +12,36 @@ from yaml.loader import SafeLoader
12
12
 
13
13
  # Import version from package
14
14
  from mmrelay import __version__
15
+ from mmrelay.config import get_config_paths
16
+ from mmrelay.constants.app import WINDOWS_PLATFORM
17
+ from mmrelay.constants.config import (
18
+ CONFIG_KEY_ACCESS_TOKEN,
19
+ CONFIG_KEY_BOT_USER_ID,
20
+ CONFIG_KEY_HOMESERVER,
21
+ CONFIG_SECTION_MATRIX,
22
+ CONFIG_SECTION_MESHTASTIC,
23
+ )
24
+ from mmrelay.constants.network import (
25
+ CONFIG_KEY_BLE_ADDRESS,
26
+ CONFIG_KEY_CONNECTION_TYPE,
27
+ CONFIG_KEY_HOST,
28
+ CONFIG_KEY_SERIAL_PORT,
29
+ CONNECTION_TYPE_BLE,
30
+ CONNECTION_TYPE_NETWORK,
31
+ CONNECTION_TYPE_SERIAL,
32
+ CONNECTION_TYPE_TCP,
33
+ )
15
34
  from mmrelay.tools import get_sample_config_path
16
35
 
17
36
 
18
37
  def parse_arguments():
19
38
  """
20
- Parse command-line arguments.
39
+ Parse and validate command-line arguments for the Meshtastic Matrix Relay CLI.
40
+
41
+ Supports options for specifying configuration file, data directory, logging preferences, version display, sample configuration generation, service installation, and configuration validation. On Windows, also accepts a deprecated positional argument for the config file path with a warning. Ignores unknown arguments outside of test environments and warns if any are present.
21
42
 
22
43
  Returns:
23
- argparse.Namespace: The parsed command-line arguments
44
+ argparse.Namespace: Parsed command-line arguments.
24
45
  """
25
46
  parser = argparse.ArgumentParser(
26
47
  description="Meshtastic Matrix Relay - Bridge between Meshtastic and Matrix"
@@ -61,16 +82,20 @@ def parse_arguments():
61
82
 
62
83
  # Windows-specific handling for backward compatibility
63
84
  # On Windows, add a positional argument for the config file path
64
- if sys.platform == "win32":
85
+ if sys.platform == WINDOWS_PLATFORM:
65
86
  parser.add_argument(
66
87
  "config_path", nargs="?", help=argparse.SUPPRESS, default=None
67
88
  )
68
89
 
69
- args = parser.parse_args()
90
+ # Use parse_known_args to handle unknown arguments gracefully (e.g., pytest args)
91
+ args, unknown = parser.parse_known_args()
92
+ # If there are unknown arguments and we're not in a test environment, warn about them
93
+ if unknown and not any("pytest" in arg or "test" in arg for arg in sys.argv):
94
+ print(f"Warning: Unknown arguments ignored: {unknown}")
70
95
 
71
96
  # If on Windows and a positional config path is provided but --config is not, use the positional one
72
97
  if (
73
- sys.platform == "win32"
98
+ sys.platform == WINDOWS_PLATFORM
74
99
  and hasattr(args, "config_path")
75
100
  and args.config_path
76
101
  and not args.config
@@ -105,15 +130,16 @@ def print_version():
105
130
 
106
131
  def check_config(args=None):
107
132
  """
108
- Check if the configuration file is valid.
133
+ Validates the application's configuration file for required structure and fields.
109
134
 
110
- Args:
111
- args: The parsed command-line arguments
135
+ If a configuration file is found, checks for the presence and correctness of required sections and keys, including Matrix and Meshtastic settings, and validates the format of matrix rooms. Prints errors or warnings for missing or deprecated fields. Returns True if the configuration is valid, otherwise False.
136
+
137
+ Parameters:
138
+ args: Parsed command-line arguments. If None, arguments are parsed internally.
112
139
 
113
140
  Returns:
114
- bool: True if the configuration is valid, False otherwise.
141
+ bool: True if the configuration file is valid, False otherwise.
115
142
  """
116
- from mmrelay.config import get_config_paths
117
143
 
118
144
  # If args is None, parse them now
119
145
  if args is None:
@@ -137,12 +163,16 @@ def check_config(args=None):
137
163
  return False
138
164
 
139
165
  # Check matrix section
140
- if "matrix" not in config:
166
+ if CONFIG_SECTION_MATRIX not in config:
141
167
  print("Error: Missing 'matrix' section in config")
142
168
  return False
143
169
 
144
- matrix_section = config["matrix"]
145
- required_matrix_fields = ["homeserver", "access_token", "bot_user_id"]
170
+ matrix_section = config[CONFIG_SECTION_MATRIX]
171
+ required_matrix_fields = [
172
+ CONFIG_KEY_HOMESERVER,
173
+ CONFIG_KEY_ACCESS_TOKEN,
174
+ CONFIG_KEY_BOT_USER_ID,
175
+ ]
146
176
  missing_matrix_fields = [
147
177
  field
148
178
  for field in required_matrix_fields
@@ -178,47 +208,55 @@ def check_config(args=None):
178
208
  return False
179
209
 
180
210
  # Check meshtastic section
181
- if "meshtastic" not in config:
211
+ if CONFIG_SECTION_MESHTASTIC not in config:
182
212
  print("Error: Missing 'meshtastic' section in config")
183
213
  return False
184
214
 
185
- meshtastic_section = config["meshtastic"]
215
+ meshtastic_section = config[CONFIG_SECTION_MESHTASTIC]
186
216
  if "connection_type" not in meshtastic_section:
187
217
  print("Error: Missing 'connection_type' in 'meshtastic' section")
188
218
  return False
189
219
 
190
- connection_type = meshtastic_section["connection_type"]
191
- if connection_type not in ["tcp", "serial", "ble", "network"]:
220
+ connection_type = meshtastic_section[CONFIG_KEY_CONNECTION_TYPE]
221
+ if connection_type not in [
222
+ CONNECTION_TYPE_TCP,
223
+ CONNECTION_TYPE_SERIAL,
224
+ CONNECTION_TYPE_BLE,
225
+ CONNECTION_TYPE_NETWORK,
226
+ ]:
192
227
  print(
193
- f"Error: Invalid 'connection_type': {connection_type}. Must be 'tcp', 'serial', or 'ble'"
228
+ f"Error: Invalid 'connection_type': {connection_type}. Must be '{CONNECTION_TYPE_TCP}', '{CONNECTION_TYPE_SERIAL}', or '{CONNECTION_TYPE_BLE}'"
194
229
  )
195
230
  return False
196
231
 
197
232
  # Check for deprecated connection_type
198
- if connection_type == "network":
233
+ if connection_type == CONNECTION_TYPE_NETWORK:
199
234
  print(
200
235
  "\nWarning: 'network' connection_type is deprecated. Please use 'tcp' instead."
201
236
  )
202
237
  print(
203
- "See ANNOUNCEMENT.md for more information about deprecated options.\n"
238
+ "This option still works but may be removed in future versions.\n"
204
239
  )
205
240
 
206
241
  # Check connection-specific fields
207
242
  if (
208
- connection_type == "serial"
209
- and "serial_port" not in meshtastic_section
243
+ connection_type == CONNECTION_TYPE_SERIAL
244
+ and CONFIG_KEY_SERIAL_PORT not in meshtastic_section
210
245
  ):
211
246
  print("Error: Missing 'serial_port' for 'serial' connection type")
212
247
  return False
213
248
 
214
249
  if (
215
- connection_type in ["tcp", "network"]
216
- and "host" not in meshtastic_section
250
+ connection_type in [CONNECTION_TYPE_TCP, CONNECTION_TYPE_NETWORK]
251
+ and CONFIG_KEY_HOST not in meshtastic_section
217
252
  ):
218
253
  print("Error: Missing 'host' for 'tcp' connection type")
219
254
  return False
220
255
 
221
- if connection_type == "ble" and "ble_address" not in meshtastic_section:
256
+ if (
257
+ connection_type == CONNECTION_TYPE_BLE
258
+ and CONFIG_KEY_BLE_ADDRESS not in meshtastic_section
259
+ ):
222
260
  print("Error: Missing 'ble_address' for 'ble' connection type")
223
261
  return False
224
262
 
@@ -228,7 +266,7 @@ def check_config(args=None):
228
266
  "\nWarning: 'db' section is deprecated. Please use 'database' instead."
229
267
  )
230
268
  print(
231
- "See ANNOUNCEMENT.md for more information about deprecated options.\n"
269
+ "This option still works but may be removed in future versions.\n"
232
270
  )
233
271
 
234
272
  print("Configuration file is valid!")
@@ -248,36 +286,50 @@ def check_config(args=None):
248
286
 
249
287
 
250
288
  def main():
251
- """Entry point for CLI commands.
289
+ """
290
+ Runs the Meshtastic Matrix Relay CLI, handling argument parsing, command execution, and error reporting.
252
291
 
253
292
  Returns:
254
- int: Exit code (0 for success, non-zero for failure)
293
+ int: Exit code indicating success (0) or failure (non-zero).
255
294
  """
256
- args = parse_arguments()
295
+ try:
296
+ args = parse_arguments()
257
297
 
258
- # Handle --check-config
259
- if args.check_config:
260
- return 0 if check_config(args) else 1
298
+ # Handle --check-config
299
+ if args.check_config:
300
+ return 0 if check_config(args) else 1
261
301
 
262
- # Handle --install-service
263
- if args.install_service:
264
- from mmrelay.setup_utils import install_service
302
+ # Handle --install-service
303
+ if args.install_service:
304
+ try:
305
+ from mmrelay.setup_utils import install_service
265
306
 
266
- return 0 if install_service() else 1
307
+ return 0 if install_service() else 1
308
+ except ImportError as e:
309
+ print(f"Error importing setup utilities: {e}")
310
+ return 1
267
311
 
268
- # Handle --generate-config
269
- if args.generate_config:
270
- return 0 if generate_sample_config() else 1
312
+ # Handle --generate-config
313
+ if args.generate_config:
314
+ return 0 if generate_sample_config() else 1
271
315
 
272
- # Handle --version
273
- if args.version:
274
- print_version()
275
- return 0
316
+ # Handle --version
317
+ if args.version:
318
+ print_version()
319
+ return 0
276
320
 
277
- # If no command was specified, run the main functionality
278
- from mmrelay.main import run_main
321
+ # If no command was specified, run the main functionality
322
+ try:
323
+ from mmrelay.main import run_main
279
324
 
280
- return run_main(args)
325
+ return run_main(args)
326
+ except ImportError as e:
327
+ print(f"Error importing main module: {e}")
328
+ return 1
329
+
330
+ except Exception as e:
331
+ print(f"Unexpected error: {e}")
332
+ return 1
281
333
 
282
334
 
283
335
  if __name__ == "__main__":
@@ -332,16 +384,17 @@ def handle_cli_commands(args):
332
384
 
333
385
 
334
386
  def generate_sample_config():
335
- """Generate a sample config.yaml file.
387
+ """
388
+ Generate a sample configuration file (`config.yaml`) in the default location if one does not already exist.
389
+
390
+ Attempts to copy a sample config from various sources, handling directory creation and file system errors gracefully. Prints informative messages on success or failure.
336
391
 
337
392
  Returns:
338
- bool: True if the config was generated successfully, False otherwise.
393
+ bool: True if the sample config was generated successfully, False otherwise.
339
394
  """
340
395
 
341
396
  import shutil
342
397
 
343
- from mmrelay.config import get_config_paths
344
-
345
398
  # Get the first config path (highest priority)
346
399
  config_paths = get_config_paths()
347
400
 
@@ -362,8 +415,7 @@ def generate_sample_config():
362
415
  # No config file exists, generate one in the first location
363
416
  target_path = config_paths[0]
364
417
 
365
- # Ensure the directory exists
366
- os.makedirs(os.path.dirname(target_path), exist_ok=True)
418
+ # Directory should already exist from get_config_paths() call
367
419
 
368
420
  # Use the helper function to get the sample config path
369
421
  sample_config_path = get_sample_config_path()
@@ -372,12 +424,16 @@ def generate_sample_config():
372
424
  # Copy the sample config file to the target path
373
425
  import shutil
374
426
 
375
- shutil.copy2(sample_config_path, target_path)
376
- print(f"Generated sample config file at: {target_path}")
377
- print(
378
- "\nEdit this file with your Matrix and Meshtastic settings before running mmrelay."
379
- )
380
- return True
427
+ try:
428
+ shutil.copy2(sample_config_path, target_path)
429
+ print(f"Generated sample config file at: {target_path}")
430
+ print(
431
+ "\nEdit this file with your Matrix and Meshtastic settings before running mmrelay."
432
+ )
433
+ return True
434
+ except (IOError, OSError) as e:
435
+ print(f"Error copying sample config file: {e}")
436
+ return False
381
437
 
382
438
  # If the helper function failed, try using importlib.resources directly
383
439
  try:
@@ -418,12 +474,16 @@ def generate_sample_config():
418
474
 
419
475
  for path in sample_config_paths:
420
476
  if os.path.exists(path):
421
- shutil.copy(path, target_path)
422
- print(f"Generated sample config file at: {target_path}")
423
- print(
424
- "\nEdit this file with your Matrix and Meshtastic settings before running mmrelay."
425
- )
426
- return True
477
+ try:
478
+ shutil.copy(path, target_path)
479
+ print(f"Generated sample config file at: {target_path}")
480
+ print(
481
+ "\nEdit this file with your Matrix and Meshtastic settings before running mmrelay."
482
+ )
483
+ return True
484
+ except (IOError, OSError) as e:
485
+ print(f"Error copying sample config file from {path}: {e}")
486
+ return False
427
487
 
428
488
  print("Error: Could not find sample_config.yaml")
429
489
  return False
mmrelay/config.py CHANGED
@@ -6,10 +6,14 @@ import platformdirs
6
6
  import yaml
7
7
  from yaml.loader import SafeLoader
8
8
 
9
- # Define custom base directory for Unix systems
10
- APP_NAME = "mmrelay"
11
- APP_AUTHOR = None # No author directory
12
-
9
+ # Import application constants
10
+ from mmrelay.constants.app import APP_AUTHOR, APP_NAME
11
+ from mmrelay.constants.config import (
12
+ CONFIG_KEY_ACCESS_TOKEN,
13
+ CONFIG_KEY_BOT_USER_ID,
14
+ CONFIG_KEY_HOMESERVER,
15
+ CONFIG_SECTION_MATRIX,
16
+ )
13
17
 
14
18
  # Global variable to store the custom data directory
15
19
  custom_data_dir = None
@@ -49,14 +53,15 @@ def get_app_path():
49
53
 
50
54
  def get_config_paths(args=None):
51
55
  """
52
- Returns a list of possible config file paths in order of priority:
53
- 1. Command line argument (if provided)
54
- 2. User config directory (~/.mmrelay/config/ on Linux)
55
- 3. Current directory (for backward compatibility)
56
- 4. Application directory (for backward compatibility)
57
-
58
- Args:
59
- args: The parsed command-line arguments
56
+ Return a prioritized list of possible configuration file paths for the application.
57
+
58
+ The search order is: a command-line specified path (if provided), the user config directory, the current working directory, and the application directory. The user config directory is skipped if it cannot be created due to permission or OS errors.
59
+
60
+ Parameters:
61
+ args: Parsed command-line arguments, expected to have a 'config' attribute specifying a config file path.
62
+
63
+ Returns:
64
+ List of absolute paths to candidate configuration files, ordered by priority.
60
65
  """
61
66
  paths = []
62
67
 
@@ -72,9 +77,13 @@ def get_config_paths(args=None):
72
77
  # Use platformdirs default for Windows
73
78
  user_config_dir = platformdirs.user_config_dir(APP_NAME, APP_AUTHOR)
74
79
 
75
- os.makedirs(user_config_dir, exist_ok=True)
76
- user_config_path = os.path.join(user_config_dir, "config.yaml")
77
- paths.append(user_config_path)
80
+ try:
81
+ os.makedirs(user_config_dir, exist_ok=True)
82
+ user_config_path = os.path.join(user_config_dir, "config.yaml")
83
+ paths.append(user_config_path)
84
+ except (OSError, PermissionError):
85
+ # If we can't create the user config directory, skip it
86
+ pass
78
87
 
79
88
  # Check current directory (for backward compatibility)
80
89
  current_dir_config = os.path.join(os.getcwd(), "config.yaml")
@@ -164,14 +173,12 @@ config_path = None
164
173
 
165
174
  def set_config(module, passed_config):
166
175
  """
167
- Set the configuration for a module.
176
+ Assigns the provided configuration dictionary to a module and sets additional attributes for known module types.
168
177
 
169
- Args:
170
- module: The module to set the configuration for
171
- passed_config: The configuration dictionary to use
178
+ For modules named "matrix_utils" or "meshtastic_utils", sets specific configuration attributes if present. Calls the module's `setup_config()` method if it exists for backward compatibility.
172
179
 
173
180
  Returns:
174
- The updated config
181
+ dict: The configuration dictionary that was assigned to the module.
175
182
  """
176
183
  # Set the module's config variable
177
184
  module.config = passed_config
@@ -181,11 +188,20 @@ def set_config(module, passed_config):
181
188
 
182
189
  if module_name == "matrix_utils":
183
190
  # Set Matrix-specific configuration
184
- if hasattr(module, "matrix_homeserver") and "matrix" in passed_config:
185
- module.matrix_homeserver = passed_config["matrix"]["homeserver"]
191
+ if (
192
+ hasattr(module, "matrix_homeserver")
193
+ and CONFIG_SECTION_MATRIX in passed_config
194
+ ):
195
+ module.matrix_homeserver = passed_config[CONFIG_SECTION_MATRIX][
196
+ CONFIG_KEY_HOMESERVER
197
+ ]
186
198
  module.matrix_rooms = passed_config["matrix_rooms"]
187
- module.matrix_access_token = passed_config["matrix"]["access_token"]
188
- module.bot_user_id = passed_config["matrix"]["bot_user_id"]
199
+ module.matrix_access_token = passed_config[CONFIG_SECTION_MATRIX][
200
+ CONFIG_KEY_ACCESS_TOKEN
201
+ ]
202
+ module.bot_user_id = passed_config[CONFIG_SECTION_MATRIX][
203
+ CONFIG_KEY_BOT_USER_ID
204
+ ]
189
205
 
190
206
  elif module_name == "meshtastic_utils":
191
207
  # Set Meshtastic-specific configuration
@@ -200,24 +216,31 @@ def set_config(module, passed_config):
200
216
 
201
217
 
202
218
  def load_config(config_file=None, args=None):
203
- """Load the configuration from the specified file or search for it.
219
+ """
220
+ Load the application configuration from a specified file or by searching standard locations.
221
+
222
+ If a config file path is provided and valid, attempts to load and parse it as YAML. If not, searches for a configuration file in prioritized locations and loads the first valid one found. Returns an empty dictionary if no valid configuration is found or if loading fails due to file or YAML errors.
204
223
 
205
- Args:
206
- config_file (str, optional): Path to the config file. If None, search for it.
207
- args: The parsed command-line arguments
224
+ Parameters:
225
+ config_file (str, optional): Path to a specific configuration file. If None, searches default locations.
226
+ args: Parsed command-line arguments, used to determine config search order.
208
227
 
209
228
  Returns:
210
- dict: The loaded configuration
229
+ dict: The loaded configuration dictionary, or an empty dictionary if loading fails.
211
230
  """
212
231
  global relay_config, config_path
213
232
 
214
233
  # If a specific config file was provided, use it
215
234
  if config_file and os.path.isfile(config_file):
216
235
  # Store the config path but don't log it yet - will be logged by main.py
217
- with open(config_file, "r") as f:
218
- relay_config = yaml.load(f, Loader=SafeLoader)
219
- config_path = config_file
220
- return relay_config
236
+ try:
237
+ with open(config_file, "r") as f:
238
+ relay_config = yaml.load(f, Loader=SafeLoader)
239
+ config_path = config_file
240
+ return relay_config
241
+ except (yaml.YAMLError, PermissionError, OSError) as e:
242
+ logger.error(f"Error loading config file {config_file}: {e}")
243
+ return {}
221
244
 
222
245
  # Otherwise, search for a config file
223
246
  config_paths = get_config_paths(args)
@@ -227,9 +250,13 @@ def load_config(config_file=None, args=None):
227
250
  if os.path.isfile(path):
228
251
  config_path = path
229
252
  # Store the config path but don't log it yet - will be logged by main.py
230
- with open(config_path, "r") as f:
231
- relay_config = yaml.load(f, Loader=SafeLoader)
232
- return relay_config
253
+ try:
254
+ with open(config_path, "r") as f:
255
+ relay_config = yaml.load(f, Loader=SafeLoader)
256
+ return relay_config
257
+ except (yaml.YAMLError, PermissionError, OSError) as e:
258
+ logger.error(f"Error loading config file {path}: {e}")
259
+ continue # Try the next config path
233
260
 
234
261
  # No config file found
235
262
  logger.error("Configuration file not found in any of the following locations:")
mmrelay/config_checker.py CHANGED
@@ -1,18 +1,37 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
+ """
5
+ Configuration checker module for MMRelay.
6
+
7
+ Note: This module contains similar functionality to the check_config function
8
+ in mmrelay.cli. The CLI version is more complete and uses centralized constants.
9
+ Future refactoring should consider consolidating these implementations to reduce
10
+ code duplication while maintaining backward compatibility and test coverage.
11
+ """
12
+
4
13
  import os
5
14
 
6
15
  import yaml
7
16
  from yaml.loader import SafeLoader
8
17
 
18
+ from mmrelay.constants.network import (
19
+ CONFIG_KEY_BLE_ADDRESS,
20
+ CONFIG_KEY_CONNECTION_TYPE,
21
+ CONFIG_KEY_HOST,
22
+ CONFIG_KEY_SERIAL_PORT,
23
+ CONNECTION_TYPE_BLE,
24
+ CONNECTION_TYPE_SERIAL,
25
+ CONNECTION_TYPE_TCP,
26
+ )
27
+
9
28
 
10
29
  def get_config_paths():
11
30
  """
12
- Get a list of possible configuration file paths.
31
+ Return a list of possible file paths where the mmrelay configuration file may be located.
13
32
 
14
33
  Returns:
15
- list: A list of possible configuration file paths
34
+ list: Paths to potential configuration files.
16
35
  """
17
36
  from mmrelay.config import get_config_paths as get_paths
18
37
 
@@ -21,10 +40,10 @@ def get_config_paths():
21
40
 
22
41
  def check_config():
23
42
  """
24
- Check if the configuration file is valid.
43
+ Validates the mmrelay configuration file by checking for required sections and fields.
25
44
 
26
45
  Returns:
27
- bool: True if the configuration is valid, False otherwise.
46
+ bool: True if a valid configuration file is found and passes all checks; False otherwise.
28
47
  """
29
48
  config_paths = get_config_paths()
30
49
  config_path = None
@@ -90,30 +109,40 @@ def check_config():
90
109
  return False
91
110
 
92
111
  meshtastic_section = config["meshtastic"]
93
- if "connection_type" not in meshtastic_section:
112
+ if CONFIG_KEY_CONNECTION_TYPE not in meshtastic_section:
94
113
  print("Error: Missing 'connection_type' in 'meshtastic' section")
95
114
  return False
96
115
 
97
- connection_type = meshtastic_section["connection_type"]
98
- if connection_type not in ["tcp", "serial", "ble"]:
116
+ connection_type = meshtastic_section[CONFIG_KEY_CONNECTION_TYPE]
117
+ if connection_type not in [
118
+ CONNECTION_TYPE_TCP,
119
+ CONNECTION_TYPE_SERIAL,
120
+ CONNECTION_TYPE_BLE,
121
+ ]:
99
122
  print(
100
- f"Error: Invalid 'connection_type': {connection_type}. Must be 'tcp', 'serial', or 'ble'"
123
+ f"Error: Invalid 'connection_type': {connection_type}. Must be '{CONNECTION_TYPE_TCP}', '{CONNECTION_TYPE_SERIAL}', or '{CONNECTION_TYPE_BLE}'"
101
124
  )
102
125
  return False
103
126
 
104
127
  # Check connection-specific fields
105
128
  if (
106
- connection_type == "serial"
107
- and "serial_port" not in meshtastic_section
129
+ connection_type == CONNECTION_TYPE_SERIAL
130
+ and CONFIG_KEY_SERIAL_PORT not in meshtastic_section
108
131
  ):
109
132
  print("Error: Missing 'serial_port' for 'serial' connection type")
110
133
  return False
111
134
 
112
- if connection_type == "tcp" and "host" not in meshtastic_section:
135
+ if (
136
+ connection_type == CONNECTION_TYPE_TCP
137
+ and CONFIG_KEY_HOST not in meshtastic_section
138
+ ):
113
139
  print("Error: Missing 'host' for 'tcp' connection type")
114
140
  return False
115
141
 
116
- if connection_type == "ble" and "ble_address" not in meshtastic_section:
142
+ if (
143
+ connection_type == CONNECTION_TYPE_BLE
144
+ and CONFIG_KEY_BLE_ADDRESS not in meshtastic_section
145
+ ):
117
146
  print("Error: Missing 'ble_address' for 'ble' connection type")
118
147
  return False
119
148
 
@@ -0,0 +1,54 @@
1
+ """
2
+ Constants package for MMRelay.
3
+
4
+ This package organizes all application constants by functional area:
5
+ - app: Application metadata and version information
6
+ - queue: Message queue configuration constants
7
+ - network: Network connection and timeout constants
8
+ - formats: Message format templates and prefixes
9
+ - messages: User-facing strings and templates
10
+ - database: Database-related constants
11
+ - config: Configuration section and key constants
12
+
13
+ Usage:
14
+ from mmrelay.constants import queue
15
+ from mmrelay.constants.app import APP_NAME
16
+ from mmrelay.constants.queue import DEFAULT_MESSAGE_DELAY
17
+ """
18
+
19
+ # Re-export commonly used constants for convenience
20
+ from .app import APP_AUTHOR, APP_NAME
21
+ from .config import (
22
+ CONFIG_KEY_LEVEL,
23
+ CONFIG_SECTION_LOGGING,
24
+ CONFIG_SECTION_MATRIX,
25
+ CONFIG_SECTION_MESHTASTIC,
26
+ DEFAULT_LOG_LEVEL,
27
+ )
28
+ from .formats import DEFAULT_MATRIX_PREFIX, DEFAULT_MESHTASTIC_PREFIX
29
+ from .queue import (
30
+ DEFAULT_MESSAGE_DELAY,
31
+ MAX_QUEUE_SIZE,
32
+ QUEUE_HIGH_WATER_MARK,
33
+ QUEUE_MEDIUM_WATER_MARK,
34
+ )
35
+
36
+ __all__ = [
37
+ # App constants
38
+ "APP_NAME",
39
+ "APP_AUTHOR",
40
+ # Config constants
41
+ "CONFIG_SECTION_MATRIX",
42
+ "CONFIG_SECTION_MESHTASTIC",
43
+ "CONFIG_SECTION_LOGGING",
44
+ "CONFIG_KEY_LEVEL",
45
+ "DEFAULT_LOG_LEVEL",
46
+ # Queue constants
47
+ "DEFAULT_MESSAGE_DELAY",
48
+ "MAX_QUEUE_SIZE",
49
+ "QUEUE_HIGH_WATER_MARK",
50
+ "QUEUE_MEDIUM_WATER_MARK",
51
+ # Format constants
52
+ "DEFAULT_MESHTASTIC_PREFIX",
53
+ "DEFAULT_MATRIX_PREFIX",
54
+ ]
@@ -0,0 +1,17 @@
1
+ """
2
+ Application metadata constants.
3
+
4
+ Contains version information, application name, and other metadata
5
+ used throughout the MMRelay application.
6
+ """
7
+
8
+ # Application identification
9
+ APP_NAME = "mmrelay"
10
+ APP_AUTHOR = None # No author directory for platformdirs
11
+
12
+ # Application display names
13
+ APP_DISPLAY_NAME = "M<>M Relay"
14
+ APP_FULL_NAME = "Meshtastic Matrix Relay"
15
+
16
+ # Platform-specific constants
17
+ WINDOWS_PLATFORM = "win32"