conson-xp 1.0.1__py3-none-any.whl → 1.2.0__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.
Files changed (167) hide show
  1. {conson_xp-1.0.1.dist-info → conson_xp-1.2.0.dist-info}/METADATA +1 -1
  2. conson_xp-1.2.0.dist-info/RECORD +181 -0
  3. xp/__init__.py +4 -3
  4. xp/api/main.py +18 -3
  5. xp/api/models/api.py +13 -2
  6. xp/api/models/discover.py +12 -2
  7. xp/api/routers/conbus_blink.py +18 -6
  8. xp/api/routers/conbus_custom.py +11 -3
  9. xp/api/routers/conbus_datapoint.py +10 -3
  10. xp/api/routers/conbus_output.py +29 -9
  11. xp/api/routers/errors.py +6 -5
  12. xp/cli/__init__.py +1 -1
  13. xp/cli/commands/__init__.py +1 -0
  14. xp/cli/commands/api.py +1 -5
  15. xp/cli/commands/api_start_commands.py +14 -8
  16. xp/cli/commands/conbus/conbus.py +9 -37
  17. xp/cli/commands/conbus/conbus_actiontable_commands.py +21 -1
  18. xp/cli/commands/conbus/conbus_autoreport_commands.py +21 -11
  19. xp/cli/commands/conbus/conbus_blink_commands.py +53 -21
  20. xp/cli/commands/conbus/conbus_config_commands.py +7 -4
  21. xp/cli/commands/conbus/conbus_custom_commands.py +13 -4
  22. xp/cli/commands/conbus/conbus_datapoint_commands.py +28 -8
  23. xp/cli/commands/conbus/conbus_discover_commands.py +15 -4
  24. xp/cli/commands/conbus/conbus_lightlevel_commands.py +50 -11
  25. xp/cli/commands/conbus/conbus_linknumber_commands.py +21 -11
  26. xp/cli/commands/conbus/conbus_msactiontable_commands.py +25 -1
  27. xp/cli/commands/conbus/conbus_output_commands.py +46 -12
  28. xp/cli/commands/conbus/conbus_raw_commands.py +17 -6
  29. xp/cli/commands/conbus/conbus_receive_commands.py +15 -7
  30. xp/cli/commands/conbus/conbus_scan_commands.py +35 -102
  31. xp/cli/commands/file_commands.py +26 -15
  32. xp/cli/commands/homekit/homekit.py +14 -8
  33. xp/cli/commands/homekit/homekit_start_commands.py +5 -5
  34. xp/cli/commands/module_commands.py +26 -19
  35. xp/cli/commands/reverse_proxy_commands.py +24 -18
  36. xp/cli/commands/server/server_commands.py +18 -18
  37. xp/cli/commands/telegram/telegram.py +4 -12
  38. xp/cli/commands/telegram/telegram_blink_commands.py +10 -8
  39. xp/cli/commands/telegram/telegram_checksum_commands.py +19 -8
  40. xp/cli/commands/telegram/telegram_discover_commands.py +2 -4
  41. xp/cli/commands/telegram/telegram_linknumber_commands.py +11 -8
  42. xp/cli/commands/telegram/telegram_parse_commands.py +10 -9
  43. xp/cli/commands/telegram/telegram_version_commands.py +8 -4
  44. xp/cli/main.py +5 -1
  45. xp/cli/utils/click_tree.py +23 -3
  46. xp/cli/utils/datapoint_type_choice.py +20 -0
  47. xp/cli/utils/decorators.py +165 -14
  48. xp/cli/utils/error_handlers.py +49 -18
  49. xp/cli/utils/formatters.py +95 -10
  50. xp/cli/utils/serial_number_type.py +18 -0
  51. xp/cli/utils/system_function_choice.py +20 -0
  52. xp/cli/utils/xp_module_type.py +20 -0
  53. xp/connection/__init__.py +1 -1
  54. xp/connection/exceptions.py +5 -5
  55. xp/models/__init__.py +1 -1
  56. xp/models/actiontable/__init__.py +1 -0
  57. xp/models/actiontable/actiontable.py +17 -1
  58. xp/models/actiontable/msactiontable_xp20.py +10 -0
  59. xp/models/actiontable/msactiontable_xp24.py +20 -3
  60. xp/models/actiontable/msactiontable_xp33.py +27 -4
  61. xp/models/conbus/__init__.py +1 -0
  62. xp/models/conbus/conbus.py +34 -4
  63. xp/models/conbus/conbus_autoreport.py +20 -2
  64. xp/models/conbus/conbus_blink.py +22 -2
  65. xp/models/conbus/conbus_client_config.py +22 -1
  66. xp/models/conbus/conbus_connection_status.py +16 -2
  67. xp/models/conbus/conbus_custom.py +21 -2
  68. xp/models/conbus/conbus_datapoint.py +22 -2
  69. xp/models/conbus/conbus_discover.py +18 -2
  70. xp/models/conbus/conbus_lightlevel.py +20 -2
  71. xp/models/conbus/conbus_linknumber.py +20 -2
  72. xp/models/conbus/conbus_output.py +22 -2
  73. xp/models/conbus/conbus_raw.py +17 -2
  74. xp/models/conbus/conbus_receive.py +16 -2
  75. xp/models/homekit/__init__.py +1 -0
  76. xp/models/homekit/homekit_accessory.py +15 -1
  77. xp/models/homekit/homekit_config.py +52 -0
  78. xp/models/homekit/homekit_conson_config.py +32 -0
  79. xp/models/log_entry.py +49 -9
  80. xp/models/protocol/__init__.py +1 -0
  81. xp/models/protocol/conbus_protocol.py +130 -21
  82. xp/models/telegram/__init__.py +1 -0
  83. xp/models/telegram/action_type.py +16 -2
  84. xp/models/telegram/datapoint_type.py +36 -2
  85. xp/models/telegram/event_telegram.py +46 -10
  86. xp/models/telegram/event_type.py +8 -1
  87. xp/models/telegram/input_action_type.py +34 -2
  88. xp/models/telegram/input_type.py +9 -1
  89. xp/models/telegram/module_type.py +69 -19
  90. xp/models/telegram/module_type_code.py +43 -1
  91. xp/models/telegram/output_telegram.py +30 -6
  92. xp/models/telegram/reply_telegram.py +56 -11
  93. xp/models/telegram/system_function.py +35 -3
  94. xp/models/telegram/system_telegram.py +18 -4
  95. xp/models/telegram/telegram.py +12 -3
  96. xp/models/telegram/telegram_type.py +8 -1
  97. xp/models/telegram/timeparam_type.py +27 -0
  98. xp/models/write_config_type.py +17 -2
  99. xp/services/__init__.py +1 -1
  100. xp/services/conbus/__init__.py +1 -0
  101. xp/services/conbus/actiontable/__init__.py +1 -0
  102. xp/services/conbus/actiontable/actiontable_service.py +33 -2
  103. xp/services/conbus/actiontable/msactiontable_service.py +40 -3
  104. xp/services/conbus/actiontable/msactiontable_xp24_serializer.py +36 -4
  105. xp/services/conbus/actiontable/msactiontable_xp33_serializer.py +45 -5
  106. xp/services/conbus/conbus_autoreport_get_service.py +17 -8
  107. xp/services/conbus/conbus_autoreport_set_service.py +29 -16
  108. xp/services/conbus/conbus_blink_all_service.py +40 -21
  109. xp/services/conbus/conbus_blink_service.py +37 -13
  110. xp/services/conbus/conbus_custom_service.py +29 -13
  111. xp/services/conbus/conbus_datapoint_queryall_service.py +40 -16
  112. xp/services/conbus/conbus_datapoint_service.py +33 -12
  113. xp/services/conbus/conbus_discover_service.py +43 -7
  114. xp/services/conbus/conbus_lightlevel_get_service.py +22 -14
  115. xp/services/conbus/conbus_lightlevel_set_service.py +40 -20
  116. xp/services/conbus/conbus_linknumber_get_service.py +18 -10
  117. xp/services/conbus/conbus_linknumber_set_service.py +34 -8
  118. xp/services/conbus/conbus_output_service.py +33 -13
  119. xp/services/conbus/conbus_raw_service.py +36 -16
  120. xp/services/conbus/conbus_receive_service.py +38 -6
  121. xp/services/conbus/conbus_scan_service.py +45 -19
  122. xp/services/homekit/__init__.py +1 -0
  123. xp/services/homekit/homekit_cache_service.py +31 -6
  124. xp/services/homekit/homekit_conbus_service.py +33 -2
  125. xp/services/homekit/homekit_config_validator.py +97 -15
  126. xp/services/homekit/homekit_conson_validator.py +51 -7
  127. xp/services/homekit/homekit_dimminglight.py +47 -1
  128. xp/services/homekit/homekit_dimminglight_service.py +35 -1
  129. xp/services/homekit/homekit_hap_service.py +71 -18
  130. xp/services/homekit/homekit_lightbulb.py +35 -1
  131. xp/services/homekit/homekit_lightbulb_service.py +30 -2
  132. xp/services/homekit/homekit_module_service.py +23 -1
  133. xp/services/homekit/homekit_outlet.py +47 -1
  134. xp/services/homekit/homekit_outlet_service.py +44 -2
  135. xp/services/homekit/homekit_service.py +113 -19
  136. xp/services/log_file_service.py +37 -41
  137. xp/services/module_type_service.py +26 -5
  138. xp/services/protocol/__init__.py +1 -1
  139. xp/services/protocol/conbus_protocol.py +115 -20
  140. xp/services/protocol/protocol_factory.py +40 -0
  141. xp/services/protocol/telegram_protocol.py +38 -7
  142. xp/services/reverse_proxy_service.py +79 -14
  143. xp/services/server/__init__.py +1 -0
  144. xp/services/server/base_server_service.py +102 -14
  145. xp/services/server/cp20_server_service.py +12 -4
  146. xp/services/server/server_service.py +26 -11
  147. xp/services/server/xp130_server_service.py +11 -3
  148. xp/services/server/xp20_server_service.py +11 -3
  149. xp/services/server/xp230_server_service.py +11 -3
  150. xp/services/server/xp24_server_service.py +33 -6
  151. xp/services/server/xp33_server_service.py +41 -8
  152. xp/services/telegram/__init__.py +1 -0
  153. xp/services/telegram/telegram_blink_service.py +19 -31
  154. xp/services/telegram/telegram_checksum_service.py +10 -10
  155. xp/services/telegram/telegram_discover_service.py +58 -29
  156. xp/services/telegram/telegram_link_number_service.py +27 -40
  157. xp/services/telegram/telegram_output_service.py +46 -49
  158. xp/services/telegram/telegram_service.py +41 -41
  159. xp/services/telegram/telegram_version_service.py +4 -2
  160. xp/utils/__init__.py +1 -1
  161. xp/utils/dependencies.py +0 -1
  162. xp/utils/serialization.py +6 -0
  163. xp/utils/time_utils.py +6 -11
  164. conson_xp-1.0.1.dist-info/RECORD +0 -181
  165. {conson_xp-1.0.1.dist-info → conson_xp-1.2.0.dist-info}/WHEEL +0 -0
  166. {conson_xp-1.0.1.dist-info → conson_xp-1.2.0.dist-info}/entry_points.txt +0 -0
  167. {conson_xp-1.0.1.dist-info → conson_xp-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -11,15 +11,39 @@ from xp.cli.utils.formatters import OutputFormatter
11
11
  def handle_service_errors(
12
12
  *service_exceptions: Type[Exception],
13
13
  ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
14
- """Decorator to handle common service exceptions with consistent JSON error formatting.
14
+ """Handle common service exceptions with consistent JSON error formatting.
15
15
 
16
16
  Args:
17
- service_exceptions: Tuple of exception types to catch and handle
17
+ service_exceptions: Tuple of exception types to catch and handle.
18
+
19
+ Returns:
20
+ Decorator function that wraps commands with error handling.
18
21
  """
19
22
 
20
23
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
24
+ """Apply error handling to the decorated function.
25
+
26
+ Args:
27
+ func: The function to decorate.
28
+
29
+ Returns:
30
+ Wrapped function with error handling.
31
+ """
32
+
21
33
  @functools.wraps(func)
22
34
  def wrapper(*args: Any, **kwargs: Any) -> Any:
35
+ """Execute function with error handling.
36
+
37
+ Args:
38
+ args: Positional arguments passed to the decorated function.
39
+ kwargs: Keyword arguments passed to the decorated function.
40
+
41
+ Returns:
42
+ Result from the decorated function.
43
+
44
+ Raises:
45
+ SystemExit: When a service exception or unexpected error occurs.
46
+ """
23
47
  formatter = OutputFormatter(True)
24
48
 
25
49
  try:
@@ -40,20 +64,38 @@ def handle_service_errors(
40
64
 
41
65
 
42
66
  def common_options(func: Callable[..., Any]) -> Callable[..., Any]:
43
- """Decorator to add validation option."""
67
+ """Add validation option to command.
68
+
69
+ Args:
70
+ func: The function to decorate.
71
+
72
+ Returns:
73
+ Decorated function with common options.
74
+ """
44
75
  return func
45
76
 
46
77
 
47
78
  def telegram_parser_command(
48
79
  service_exceptions: Tuple[Type[Exception], ...] = (),
49
80
  ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
50
- """Decorator for telegram parsing commands with standard error handling.
81
+ """Apply telegram parsing commands with standard error handling.
51
82
 
52
83
  Args:
53
- service_exceptions: Additional service exceptions to handle
84
+ service_exceptions: Additional service exceptions to handle.
85
+
86
+ Returns:
87
+ Decorator function for telegram parsing commands.
54
88
  """
55
89
 
56
90
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
91
+ """Apply telegram parser decorators.
92
+
93
+ Args:
94
+ func: The function to decorate.
95
+
96
+ Returns:
97
+ Decorated function with telegram parsing support.
98
+ """
57
99
  # Apply common options
58
100
  func = common_options(func)
59
101
 
@@ -71,13 +113,24 @@ def telegram_parser_command(
71
113
  def service_command(
72
114
  *service_exceptions: Type[Exception],
73
115
  ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
74
- """Decorator for service-based commands with error handling and JSON output.
116
+ """Apply service-based commands with error handling and JSON output.
75
117
 
76
118
  Args:
77
- service_exceptions: Service exception types to handle
119
+ service_exceptions: Service exception types to handle.
120
+
121
+ Returns:
122
+ Decorator function for service commands.
78
123
  """
79
124
 
80
125
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
126
+ """Apply service command decorators.
127
+
128
+ Args:
129
+ func: The function to decorate.
130
+
131
+ Returns:
132
+ Decorated function with service error handling.
133
+ """
81
134
  func = handle_service_errors(*service_exceptions)(func)
82
135
  return func
83
136
 
@@ -87,9 +140,24 @@ def service_command(
87
140
  def list_command(
88
141
  *service_exceptions: Type[Exception],
89
142
  ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
90
- """Decorator for list/search commands with common options."""
143
+ """Apply list/search commands with common options.
144
+
145
+ Args:
146
+ service_exceptions: Service exception types to handle.
147
+
148
+ Returns:
149
+ Decorator function for list commands.
150
+ """
91
151
 
92
152
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
153
+ """Apply list command decorators.
154
+
155
+ Args:
156
+ func: The function to decorate.
157
+
158
+ Returns:
159
+ Decorated function with list error handling.
160
+ """
93
161
  func = handle_service_errors(*service_exceptions)(func)
94
162
  return func
95
163
 
@@ -97,9 +165,21 @@ def list_command(
97
165
 
98
166
 
99
167
  def file_operation_command() -> Callable[[Callable[..., Any]], Callable[..., Any]]:
100
- """Decorator for file operation commands with common filters."""
168
+ """Apply file operation commands with common filters.
169
+
170
+ Returns:
171
+ Decorator function for file operation commands.
172
+ """
101
173
 
102
174
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
175
+ """Apply file operation decorators.
176
+
177
+ Args:
178
+ func: The function to decorate.
179
+
180
+ Returns:
181
+ Decorated function with filter options.
182
+ """
103
183
  func = click.option(
104
184
  "--time-range", help="Filter by time range (HH:MM:SS,mmm-HH:MM:SS,mmm)"
105
185
  )(func)
@@ -121,15 +201,36 @@ def file_operation_command() -> Callable[[Callable[..., Any]], Callable[..., Any
121
201
  def with_formatter(
122
202
  formatter_class: Any = None,
123
203
  ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
124
- """Decorator to inject a formatter instance into the command.
204
+ """Inject a formatter instance into the command.
125
205
 
126
206
  Args:
127
- formatter_class: Custom formatter class to use
207
+ formatter_class: Custom formatter class to use.
208
+
209
+ Returns:
210
+ Decorator function that injects a formatter.
128
211
  """
129
212
 
130
213
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
214
+ """Apply formatter injection.
215
+
216
+ Args:
217
+ func: The function to decorate.
218
+
219
+ Returns:
220
+ Wrapped function with formatter injection.
221
+ """
222
+
131
223
  @functools.wraps(func)
132
224
  def wrapper(*args: Any, **kwargs: Any) -> Any:
225
+ """Execute function with injected formatter.
226
+
227
+ Args:
228
+ args: Positional arguments passed to the decorated function.
229
+ kwargs: Keyword arguments passed to the decorated function.
230
+
231
+ Returns:
232
+ Result from the decorated function.
233
+ """
133
234
  formatter_cls = formatter_class or OutputFormatter
134
235
  formatter = formatter_cls(True)
135
236
  kwargs["formatter"] = formatter
@@ -143,15 +244,39 @@ def with_formatter(
143
244
  def require_arguments(
144
245
  *required_args: str,
145
246
  ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
146
- """Decorator to validate required arguments are present.
247
+ """Validate required arguments are present.
147
248
 
148
249
  Args:
149
- required_args: Names of required arguments
250
+ required_args: Names of required arguments.
251
+
252
+ Returns:
253
+ Decorator function that validates required arguments.
150
254
  """
151
255
 
152
256
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
257
+ """Apply argument validation.
258
+
259
+ Args:
260
+ func: The function to decorate.
261
+
262
+ Returns:
263
+ Wrapped function with argument validation.
264
+ """
265
+
153
266
  @functools.wraps(func)
154
267
  def wrapper(*args: Any, **kwargs: Any) -> Any:
268
+ """Execute function with argument validation.
269
+
270
+ Args:
271
+ args: Positional arguments passed to the decorated function.
272
+ kwargs: Keyword arguments passed to the decorated function.
273
+
274
+ Returns:
275
+ Result from the decorated function.
276
+
277
+ Raises:
278
+ SystemExit: When required arguments are missing.
279
+ """
155
280
  formatter = OutputFormatter(True)
156
281
 
157
282
  # Check for missing required arguments
@@ -175,11 +300,37 @@ def require_arguments(
175
300
 
176
301
 
177
302
  def connection_command() -> Callable[[Callable[..., Any]], Callable[..., Any]]:
178
- """Decorator for commands that connect to remote services."""
303
+ """Apply commands that connect to remote services.
304
+
305
+ Returns:
306
+ Decorator function for connection commands.
307
+ """
179
308
 
180
309
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
310
+ """Apply connection command decorators.
311
+
312
+ Args:
313
+ func: The function to decorate.
314
+
315
+ Returns:
316
+ Wrapped function with connection error handling.
317
+ """
318
+
181
319
  @functools.wraps(func)
182
320
  def wrapper(*args: Any, **kwargs: Any) -> Any:
321
+ """Execute function with connection error handling.
322
+
323
+ Args:
324
+ args: Positional arguments passed to the decorated function.
325
+ kwargs: Keyword arguments passed to the decorated function.
326
+
327
+ Returns:
328
+ Result from the decorated function.
329
+
330
+ Raises:
331
+ SystemExit: When a connection timeout occurs.
332
+ Exception: Re-raises other exceptions for handling by other decorators.
333
+ """
183
334
  formatter = OutputFormatter(True)
184
335
 
185
336
  try:
@@ -19,9 +19,12 @@ class CLIErrorHandler:
19
19
  """Handle telegram parsing errors with JSON formatting.
20
20
 
21
21
  Args:
22
- error: The parsing error that occurred
23
- raw_input: The raw input that failed to parse
24
- context: Additional context information
22
+ error: The parsing error that occurred.
23
+ raw_input: The raw input that failed to parse.
24
+ context: Additional context information.
25
+
26
+ Raises:
27
+ SystemExit: Always exits with code 1 after displaying error.
25
28
  """
26
29
  formatter = OutputFormatter(True)
27
30
  error_data = {"raw_input": raw_input}
@@ -40,8 +43,11 @@ class CLIErrorHandler:
40
43
  """Handle connection/network errors with JSON formatting.
41
44
 
42
45
  Args:
43
- error: The connection error that occurred
44
- config: Configuration information (IP, port, timeout)
46
+ error: The connection error that occurred.
47
+ config: Configuration information (IP, port, timeout).
48
+
49
+ Raises:
50
+ SystemExit: Always exits with code 1 after displaying error.
45
51
  """
46
52
  formatter = OutputFormatter(True)
47
53
 
@@ -73,9 +79,12 @@ class CLIErrorHandler:
73
79
  """Handle general service errors with JSON formatting.
74
80
 
75
81
  Args:
76
- error: The service error that occurred
77
- operation: Description of the operation that failed
78
- context: Additional context information
82
+ error: The service error that occurred.
83
+ operation: Description of the operation that failed.
84
+ context: Additional context information.
85
+
86
+ Raises:
87
+ SystemExit: Always exits with code 1 after displaying error.
79
88
  """
80
89
  formatter = OutputFormatter(True)
81
90
  error_data = {"operation": operation}
@@ -92,8 +101,11 @@ class CLIErrorHandler:
92
101
  """Handle validation errors with JSON formatting.
93
102
 
94
103
  Args:
95
- error: The validation error that occurred
96
- input_data: The input that failed validation
104
+ error: The validation error that occurred.
105
+ input_data: The input that failed validation.
106
+
107
+ Raises:
108
+ SystemExit: Always exits with code 1 after displaying error.
97
109
  """
98
110
  formatter = OutputFormatter(True)
99
111
  error_data = {"valid_format": False, "raw_input": input_data}
@@ -111,9 +123,12 @@ class CLIErrorHandler:
111
123
  """Handle file operation errors with JSON formatting.
112
124
 
113
125
  Args:
114
- error: The file error that occurred
115
- file_path: Path to the file that caused the error
116
- operation: Type of file operation (parsing, reading, etc.)
126
+ error: The file error that occurred.
127
+ file_path: Path to the file that caused the error.
128
+ operation: Type of file operation (parsing, reading, etc.).
129
+
130
+ Raises:
131
+ SystemExit: Always exits with code 1 after displaying error.
117
132
  """
118
133
  formatter = OutputFormatter(True)
119
134
  error_data = {"file_path": file_path, "operation": operation}
@@ -129,9 +144,12 @@ class CLIErrorHandler:
129
144
  """Handle 'not found' errors with JSON formatting.
130
145
 
131
146
  Args:
132
- error: The not found error that occurred
133
- item_type: Type of item that was not found
134
- identifier: Identifier used to search for the item
147
+ error: The not found error that occurred.
148
+ item_type: Type of item that was not found.
149
+ identifier: Identifier used to search for the item.
150
+
151
+ Raises:
152
+ SystemExit: Always exits with code 1 after displaying error.
135
153
  """
136
154
  formatter = OutputFormatter(True)
137
155
  error_data = {"item_type": item_type, "identifier": identifier}
@@ -148,7 +166,16 @@ class ServerErrorHandler(CLIErrorHandler):
148
166
  def handle_server_startup_error(
149
167
  error: Exception, port: int, config_path: str
150
168
  ) -> None:
151
- """Handle server startup errors with JSON formatting."""
169
+ """Handle server startup errors with JSON formatting.
170
+
171
+ Args:
172
+ error: The server startup error that occurred.
173
+ port: Port number the server attempted to use.
174
+ config_path: Path to the configuration file.
175
+
176
+ Raises:
177
+ SystemExit: Always exits with code 1 after displaying error.
178
+ """
152
179
  formatter = OutputFormatter(True)
153
180
  error_data = {
154
181
  "port": port,
@@ -162,7 +189,11 @@ class ServerErrorHandler(CLIErrorHandler):
162
189
 
163
190
  @staticmethod
164
191
  def handle_server_not_running_error() -> None:
165
- """Handle errors when server is not running with JSON formatting."""
192
+ """Handle errors when server is not running with JSON formatting.
193
+
194
+ Raises:
195
+ SystemExit: Always exits with code 1 after displaying error.
196
+ """
166
197
  error_response = OutputFormatter(True).error_response(
167
198
  "No server is currently running"
168
199
  )
@@ -8,10 +8,22 @@ class OutputFormatter:
8
8
  """Handles standardized output formatting for CLI commands."""
9
9
 
10
10
  def __init__(self, json_output: bool = False):
11
+ """Initialize the output formatter.
12
+
13
+ Args:
14
+ json_output: Whether to format output as JSON (default: False).
15
+ """
11
16
  self.json_output = json_output
12
17
 
13
18
  def success_response(self, data: Dict[str, Any]) -> str:
14
- """Format a successful response."""
19
+ """Format a successful response.
20
+
21
+ Args:
22
+ data: Response data to format.
23
+
24
+ Returns:
25
+ Formatted success response as string.
26
+ """
15
27
  if self.json_output:
16
28
  return json.dumps(data, indent=2)
17
29
  return self._format_text_response(data)
@@ -19,7 +31,15 @@ class OutputFormatter:
19
31
  def error_response(
20
32
  self, error: str, extra_data: Optional[Dict[str, Any]] = None
21
33
  ) -> str:
22
- """Format an error response."""
34
+ """Format an error response.
35
+
36
+ Args:
37
+ error: Error message.
38
+ extra_data: Additional error data to include.
39
+
40
+ Returns:
41
+ Formatted error response as string.
42
+ """
23
43
  error_data = {"success": False, "error": error}
24
44
  if extra_data:
25
45
  error_data.update(extra_data)
@@ -29,7 +49,15 @@ class OutputFormatter:
29
49
  return f"Error: {error}"
30
50
 
31
51
  def validation_response(self, is_valid: bool, data: Dict[str, Any]) -> str:
32
- """Format a validation response."""
52
+ """Format a validation response.
53
+
54
+ Args:
55
+ is_valid: Whether validation passed.
56
+ data: Validation data to include.
57
+
58
+ Returns:
59
+ Formatted validation response as string.
60
+ """
33
61
  if self.json_output:
34
62
  response_data = {"valid": is_valid} | data
35
63
  return json.dumps(response_data, indent=2)
@@ -38,7 +66,14 @@ class OutputFormatter:
38
66
  return f"Status: {status}"
39
67
 
40
68
  def checksum_status(self, is_valid: bool) -> str:
41
- """Format checksum validation status."""
69
+ """Format checksum validation status.
70
+
71
+ Args:
72
+ is_valid: Whether checksum is valid.
73
+
74
+ Returns:
75
+ Formatted checksum status as string.
76
+ """
42
77
  if self.json_output:
43
78
  return json.dumps({"checksum_valid": is_valid}, indent=2)
44
79
 
@@ -46,7 +81,14 @@ class OutputFormatter:
46
81
 
47
82
  @staticmethod
48
83
  def _format_text_response(data: Dict[str, Any]) -> str:
49
- """Format data for human-readable text output."""
84
+ """Format data for human-readable text output.
85
+
86
+ Args:
87
+ data: Data dictionary to format.
88
+
89
+ Returns:
90
+ Formatted text output as string.
91
+ """
50
92
  lines = []
51
93
 
52
94
  # Handle common data patterns
@@ -77,7 +119,15 @@ class TelegramFormatter(OutputFormatter):
77
119
  def format_telegram_summary(
78
120
  self, telegram_data: Dict[str, Any], service_formatter_method: Any = None
79
121
  ) -> str:
80
- """Format telegram summary using service method when available."""
122
+ """Format telegram summary using service method when available.
123
+
124
+ Args:
125
+ telegram_data: Telegram data to format.
126
+ service_formatter_method: Optional service formatter method.
127
+
128
+ Returns:
129
+ Formatted telegram summary as string.
130
+ """
81
131
  if self.json_output:
82
132
  return json.dumps(telegram_data, indent=2)
83
133
 
@@ -98,7 +148,16 @@ class TelegramFormatter(OutputFormatter):
98
148
  def format_validation_result(
99
149
  self, parsed_telegram: Any, checksum_valid: Optional[bool], service_summary: str
100
150
  ) -> str:
101
- """Format telegram validation results."""
151
+ """Format telegram validation results.
152
+
153
+ Args:
154
+ parsed_telegram: Parsed telegram object.
155
+ checksum_valid: Whether checksum is valid.
156
+ service_summary: Summary from service.
157
+
158
+ Returns:
159
+ Formatted validation result as string.
160
+ """
102
161
  if self.json_output:
103
162
  output = parsed_telegram.to_dict()
104
163
  output["checksum_valid"] = checksum_valid
@@ -118,7 +177,16 @@ class ListFormatter(OutputFormatter):
118
177
  def format_list_response(
119
178
  self, items: list, title: str, item_formatter: Any = None
120
179
  ) -> str:
121
- """Format a list of items with optional custom formatter."""
180
+ """Format a list of items with optional custom formatter.
181
+
182
+ Args:
183
+ items: List of items to format.
184
+ title: Title for the list.
185
+ item_formatter: Optional custom formatter function.
186
+
187
+ Returns:
188
+ Formatted list as string.
189
+ """
122
190
  if self.json_output:
123
191
  return json.dumps(
124
192
  {
@@ -144,7 +212,15 @@ class ListFormatter(OutputFormatter):
144
212
  return "\n".join(lines)
145
213
 
146
214
  def format_search_results(self, matches: list, query: str) -> str:
147
- """Format search results."""
215
+ """Format search results.
216
+
217
+ Args:
218
+ matches: List of matching items.
219
+ query: Search query string.
220
+
221
+ Returns:
222
+ Formatted search results as string.
223
+ """
148
224
  if self.json_output:
149
225
  return json.dumps(
150
226
  {
@@ -181,7 +257,16 @@ class StatisticsFormatter(OutputFormatter):
181
257
  def format_file_statistics(
182
258
  self, file_path: str, stats: Dict[str, Any], entry_count: int
183
259
  ) -> str:
184
- """Format file analysis statistics."""
260
+ """Format file analysis statistics.
261
+
262
+ Args:
263
+ file_path: Path to the analyzed file.
264
+ stats: Statistics dictionary.
265
+ entry_count: Total number of entries.
266
+
267
+ Returns:
268
+ Formatted statistics as string.
269
+ """
185
270
  if self.json_output:
186
271
  return json.dumps(
187
272
  {
@@ -1,14 +1,32 @@
1
+ """Click parameter type for serial number validation."""
2
+
1
3
  from typing import Any, Optional
2
4
 
3
5
  import click
4
6
 
5
7
 
6
8
  class SerialNumberParamType(click.ParamType):
9
+ """Click parameter type for validating and formatting serial numbers.
10
+
11
+ Attributes:
12
+ name: The parameter type name.
13
+ """
14
+
7
15
  name = "serial_number"
8
16
 
9
17
  def convert(
10
18
  self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]
11
19
  ) -> Optional[str]:
20
+ """Convert and validate serial number input.
21
+
22
+ Args:
23
+ value: The input value to convert.
24
+ param: The Click parameter.
25
+ ctx: The Click context.
26
+
27
+ Returns:
28
+ 10-character zero-padded serial number string, or None if input is None.
29
+ """
12
30
  if value is None:
13
31
  return None
14
32
 
@@ -1,3 +1,5 @@
1
+ """Click parameter type for SystemFunction enum validation."""
2
+
1
3
  from typing import Any, Optional
2
4
 
3
5
  import click
@@ -7,14 +9,32 @@ from xp.models.telegram.system_function import SystemFunction
7
9
 
8
10
  # noinspection DuplicatedCode
9
11
  class SystemFunctionChoice(click.ParamType):
12
+ """Click parameter type for validating SystemFunction enum values.
13
+
14
+ Attributes:
15
+ name: The parameter type name.
16
+ choices: List of valid choice strings.
17
+ """
18
+
10
19
  name = "system_function"
11
20
 
12
21
  def __init__(self) -> None:
22
+ """Initialize the SystemFunctionChoice parameter type."""
13
23
  self.choices = [key.lower() for key in SystemFunction.__members__.keys()]
14
24
 
15
25
  def convert(
16
26
  self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]
17
27
  ) -> Any:
28
+ """Convert and validate input to SystemFunction enum.
29
+
30
+ Args:
31
+ value: The input value to convert.
32
+ param: The Click parameter.
33
+ ctx: The Click context.
34
+
35
+ Returns:
36
+ SystemFunction enum member if valid, None if input is None.
37
+ """
18
38
  if value is None:
19
39
  return value
20
40
 
@@ -1,17 +1,37 @@
1
+ """Click parameter type for XP module type validation."""
2
+
1
3
  from typing import Any, Optional
2
4
 
3
5
  import click
4
6
 
5
7
 
6
8
  class XpModuleTypeChoice(click.ParamType):
9
+ """Click parameter type for validating XP module types.
10
+
11
+ Attributes:
12
+ name: The parameter type name.
13
+ choices: List of valid module type strings.
14
+ """
15
+
7
16
  name = "xpmoduletype"
8
17
 
9
18
  def __init__(self) -> None:
19
+ """Initialize the XpModuleTypeChoice parameter type."""
10
20
  self.choices = ["xp20", "xp24", "xp31", "xp33"]
11
21
 
12
22
  def convert(
13
23
  self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]
14
24
  ) -> Any:
25
+ """Convert and validate XP module type input.
26
+
27
+ Args:
28
+ value: The input value to convert.
29
+ param: The Click parameter.
30
+ ctx: The Click context.
31
+
32
+ Returns:
33
+ Lowercase module type string if valid, None if input is None.
34
+ """
15
35
  if value is None:
16
36
  return value
17
37
 
xp/connection/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- """Connection layer for XP CLI tool"""
1
+ """Connection layer for XP CLI tool."""
2
2
 
3
3
  from xp.connection.exceptions import (
4
4
  ProtocolError,