conson-xp 1.18.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 (176) hide show
  1. conson_xp-1.18.0.dist-info/METADATA +412 -0
  2. conson_xp-1.18.0.dist-info/RECORD +176 -0
  3. conson_xp-1.18.0.dist-info/WHEEL +4 -0
  4. conson_xp-1.18.0.dist-info/entry_points.txt +5 -0
  5. conson_xp-1.18.0.dist-info/licenses/LICENSE +29 -0
  6. xp/__init__.py +9 -0
  7. xp/cli/__init__.py +5 -0
  8. xp/cli/__main__.py +6 -0
  9. xp/cli/commands/__init__.py +153 -0
  10. xp/cli/commands/conbus/__init__.py +25 -0
  11. xp/cli/commands/conbus/conbus.py +128 -0
  12. xp/cli/commands/conbus/conbus_actiontable_commands.py +233 -0
  13. xp/cli/commands/conbus/conbus_autoreport_commands.py +108 -0
  14. xp/cli/commands/conbus/conbus_blink_commands.py +163 -0
  15. xp/cli/commands/conbus/conbus_config_commands.py +29 -0
  16. xp/cli/commands/conbus/conbus_custom_commands.py +57 -0
  17. xp/cli/commands/conbus/conbus_datapoint_commands.py +113 -0
  18. xp/cli/commands/conbus/conbus_discover_commands.py +61 -0
  19. xp/cli/commands/conbus/conbus_event_commands.py +81 -0
  20. xp/cli/commands/conbus/conbus_lightlevel_commands.py +207 -0
  21. xp/cli/commands/conbus/conbus_linknumber_commands.py +102 -0
  22. xp/cli/commands/conbus/conbus_modulenumber_commands.py +104 -0
  23. xp/cli/commands/conbus/conbus_msactiontable_commands.py +94 -0
  24. xp/cli/commands/conbus/conbus_output_commands.py +163 -0
  25. xp/cli/commands/conbus/conbus_raw_commands.py +62 -0
  26. xp/cli/commands/conbus/conbus_receive_commands.py +59 -0
  27. xp/cli/commands/conbus/conbus_scan_commands.py +58 -0
  28. xp/cli/commands/file_commands.py +186 -0
  29. xp/cli/commands/homekit/__init__.py +3 -0
  30. xp/cli/commands/homekit/homekit.py +118 -0
  31. xp/cli/commands/homekit/homekit_start_commands.py +43 -0
  32. xp/cli/commands/module_commands.py +187 -0
  33. xp/cli/commands/reverse_proxy_commands.py +178 -0
  34. xp/cli/commands/server/__init__.py +3 -0
  35. xp/cli/commands/server/server_commands.py +135 -0
  36. xp/cli/commands/telegram/__init__.py +5 -0
  37. xp/cli/commands/telegram/telegram.py +41 -0
  38. xp/cli/commands/telegram/telegram_blink_commands.py +79 -0
  39. xp/cli/commands/telegram/telegram_checksum_commands.py +112 -0
  40. xp/cli/commands/telegram/telegram_discover_commands.py +41 -0
  41. xp/cli/commands/telegram/telegram_linknumber_commands.py +86 -0
  42. xp/cli/commands/telegram/telegram_parse_commands.py +75 -0
  43. xp/cli/commands/telegram/telegram_version_commands.py +52 -0
  44. xp/cli/main.py +87 -0
  45. xp/cli/utils/__init__.py +1 -0
  46. xp/cli/utils/click_tree.py +57 -0
  47. xp/cli/utils/datapoint_type_choice.py +57 -0
  48. xp/cli/utils/decorators.py +351 -0
  49. xp/cli/utils/error_handlers.py +201 -0
  50. xp/cli/utils/formatters.py +312 -0
  51. xp/cli/utils/module_type_choice.py +56 -0
  52. xp/cli/utils/serial_number_type.py +52 -0
  53. xp/cli/utils/system_function_choice.py +57 -0
  54. xp/cli/utils/xp_module_type.py +53 -0
  55. xp/connection/__init__.py +13 -0
  56. xp/connection/exceptions.py +22 -0
  57. xp/models/__init__.py +36 -0
  58. xp/models/actiontable/__init__.py +1 -0
  59. xp/models/actiontable/actiontable.py +43 -0
  60. xp/models/actiontable/msactiontable_xp20.py +53 -0
  61. xp/models/actiontable/msactiontable_xp24.py +58 -0
  62. xp/models/actiontable/msactiontable_xp33.py +65 -0
  63. xp/models/conbus/__init__.py +1 -0
  64. xp/models/conbus/conbus.py +87 -0
  65. xp/models/conbus/conbus_autoreport.py +67 -0
  66. xp/models/conbus/conbus_blink.py +80 -0
  67. xp/models/conbus/conbus_client_config.py +55 -0
  68. xp/models/conbus/conbus_connection_status.py +40 -0
  69. xp/models/conbus/conbus_custom.py +58 -0
  70. xp/models/conbus/conbus_datapoint.py +89 -0
  71. xp/models/conbus/conbus_discover.py +64 -0
  72. xp/models/conbus/conbus_event_raw.py +47 -0
  73. xp/models/conbus/conbus_lightlevel.py +52 -0
  74. xp/models/conbus/conbus_linknumber.py +54 -0
  75. xp/models/conbus/conbus_output.py +57 -0
  76. xp/models/conbus/conbus_raw.py +45 -0
  77. xp/models/conbus/conbus_receive.py +42 -0
  78. xp/models/conbus/conbus_writeconfig.py +60 -0
  79. xp/models/homekit/__init__.py +1 -0
  80. xp/models/homekit/homekit_accessory.py +35 -0
  81. xp/models/homekit/homekit_config.py +106 -0
  82. xp/models/homekit/homekit_conson_config.py +86 -0
  83. xp/models/log_entry.py +130 -0
  84. xp/models/protocol/__init__.py +1 -0
  85. xp/models/protocol/conbus_protocol.py +312 -0
  86. xp/models/response.py +42 -0
  87. xp/models/telegram/__init__.py +1 -0
  88. xp/models/telegram/action_type.py +31 -0
  89. xp/models/telegram/datapoint_type.py +82 -0
  90. xp/models/telegram/event_telegram.py +140 -0
  91. xp/models/telegram/event_type.py +15 -0
  92. xp/models/telegram/input_action_type.py +69 -0
  93. xp/models/telegram/input_type.py +17 -0
  94. xp/models/telegram/module_type.py +188 -0
  95. xp/models/telegram/module_type_code.py +205 -0
  96. xp/models/telegram/output_telegram.py +103 -0
  97. xp/models/telegram/reply_telegram.py +297 -0
  98. xp/models/telegram/system_function.py +116 -0
  99. xp/models/telegram/system_telegram.py +94 -0
  100. xp/models/telegram/telegram.py +28 -0
  101. xp/models/telegram/telegram_type.py +19 -0
  102. xp/models/telegram/timeparam_type.py +51 -0
  103. xp/models/write_config_type.py +33 -0
  104. xp/services/__init__.py +26 -0
  105. xp/services/actiontable/__init__.py +1 -0
  106. xp/services/actiontable/actiontable_serializer.py +273 -0
  107. xp/services/actiontable/msactiontable_serializer.py +7 -0
  108. xp/services/actiontable/msactiontable_xp20_serializer.py +169 -0
  109. xp/services/actiontable/msactiontable_xp24_serializer.py +120 -0
  110. xp/services/actiontable/msactiontable_xp33_serializer.py +239 -0
  111. xp/services/conbus/__init__.py +1 -0
  112. xp/services/conbus/actiontable/__init__.py +1 -0
  113. xp/services/conbus/actiontable/actiontable_download_service.py +158 -0
  114. xp/services/conbus/actiontable/actiontable_list_service.py +91 -0
  115. xp/services/conbus/actiontable/actiontable_show_service.py +89 -0
  116. xp/services/conbus/actiontable/actiontable_upload_service.py +211 -0
  117. xp/services/conbus/actiontable/msactiontable_service.py +232 -0
  118. xp/services/conbus/conbus_blink_all_service.py +181 -0
  119. xp/services/conbus/conbus_blink_service.py +158 -0
  120. xp/services/conbus/conbus_custom_service.py +156 -0
  121. xp/services/conbus/conbus_datapoint_queryall_service.py +182 -0
  122. xp/services/conbus/conbus_datapoint_service.py +170 -0
  123. xp/services/conbus/conbus_discover_service.py +312 -0
  124. xp/services/conbus/conbus_event_raw_service.py +181 -0
  125. xp/services/conbus/conbus_output_service.py +194 -0
  126. xp/services/conbus/conbus_raw_service.py +122 -0
  127. xp/services/conbus/conbus_receive_service.py +115 -0
  128. xp/services/conbus/conbus_scan_service.py +150 -0
  129. xp/services/conbus/write_config_service.py +194 -0
  130. xp/services/homekit/__init__.py +1 -0
  131. xp/services/homekit/homekit_cache_service.py +307 -0
  132. xp/services/homekit/homekit_conbus_service.py +93 -0
  133. xp/services/homekit/homekit_config_validator.py +310 -0
  134. xp/services/homekit/homekit_conson_validator.py +121 -0
  135. xp/services/homekit/homekit_dimminglight.py +182 -0
  136. xp/services/homekit/homekit_dimminglight_service.py +148 -0
  137. xp/services/homekit/homekit_hap_service.py +342 -0
  138. xp/services/homekit/homekit_lightbulb.py +120 -0
  139. xp/services/homekit/homekit_lightbulb_service.py +86 -0
  140. xp/services/homekit/homekit_module_service.py +56 -0
  141. xp/services/homekit/homekit_outlet.py +168 -0
  142. xp/services/homekit/homekit_outlet_service.py +121 -0
  143. xp/services/homekit/homekit_service.py +359 -0
  144. xp/services/log_file_service.py +309 -0
  145. xp/services/module_type_service.py +257 -0
  146. xp/services/protocol/__init__.py +21 -0
  147. xp/services/protocol/conbus_event_protocol.py +360 -0
  148. xp/services/protocol/conbus_protocol.py +318 -0
  149. xp/services/protocol/protocol_factory.py +78 -0
  150. xp/services/protocol/telegram_protocol.py +264 -0
  151. xp/services/reverse_proxy_service.py +435 -0
  152. xp/services/server/__init__.py +1 -0
  153. xp/services/server/base_server_service.py +366 -0
  154. xp/services/server/cp20_server_service.py +65 -0
  155. xp/services/server/device_service_factory.py +94 -0
  156. xp/services/server/server_service.py +428 -0
  157. xp/services/server/xp130_server_service.py +67 -0
  158. xp/services/server/xp20_server_service.py +92 -0
  159. xp/services/server/xp230_server_service.py +58 -0
  160. xp/services/server/xp24_server_service.py +245 -0
  161. xp/services/server/xp33_server_service.py +535 -0
  162. xp/services/telegram/__init__.py +1 -0
  163. xp/services/telegram/telegram_blink_service.py +138 -0
  164. xp/services/telegram/telegram_checksum_service.py +149 -0
  165. xp/services/telegram/telegram_datapoint_service.py +82 -0
  166. xp/services/telegram/telegram_discover_service.py +277 -0
  167. xp/services/telegram/telegram_link_number_service.py +216 -0
  168. xp/services/telegram/telegram_output_service.py +322 -0
  169. xp/services/telegram/telegram_service.py +380 -0
  170. xp/services/telegram/telegram_version_service.py +288 -0
  171. xp/utils/__init__.py +12 -0
  172. xp/utils/checksum.py +61 -0
  173. xp/utils/dependencies.py +531 -0
  174. xp/utils/event_helper.py +31 -0
  175. xp/utils/serialization.py +205 -0
  176. xp/utils/time_utils.py +134 -0
@@ -0,0 +1,112 @@
1
+ """Checksum calculation and validation CLI commands."""
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from xp.cli.commands.telegram.telegram import checksum
8
+ from xp.cli.utils.decorators import handle_service_errors
9
+ from xp.cli.utils.error_handlers import CLIErrorHandler
10
+ from xp.cli.utils.formatters import OutputFormatter
11
+ from xp.services.telegram.telegram_checksum_service import TelegramChecksumService
12
+
13
+
14
+ @checksum.command("calculate")
15
+ @click.argument("data")
16
+ @click.option(
17
+ "--algorithm",
18
+ "-a",
19
+ type=click.Choice(["simple", "crc32"]),
20
+ default="simple",
21
+ help="Checksum algorithm to use",
22
+ )
23
+ @handle_service_errors(Exception)
24
+ def calculate_checksum(data: str, algorithm: str) -> None:
25
+ r"""Calculate checksum for given data string.
26
+
27
+ Args:
28
+ data: Data string to calculate checksum for.
29
+ algorithm: Checksum algorithm to use.
30
+
31
+ Examples:
32
+ \b
33
+ xp checksum calculate "E14L00I02M"
34
+ xp checksum calculate "E14L00I02M" --algorithm crc32
35
+
36
+ Raises:
37
+ SystemExit: If checksum calculation fails.
38
+ """
39
+ service = TelegramChecksumService()
40
+ formatter = OutputFormatter(True)
41
+
42
+ try:
43
+ if algorithm == "simple":
44
+ result = service.calculate_simple_checksum(data)
45
+ else: # crc32
46
+ result = service.calculate_crc32_checksum(data)
47
+
48
+ if not result.success:
49
+ error_response = formatter.error_response(
50
+ result.error or "Unknown error", {"input": data}
51
+ )
52
+ click.echo(error_response)
53
+ raise SystemExit(1)
54
+
55
+ click.echo(json.dumps(result.to_dict(), indent=2))
56
+
57
+ except Exception as e:
58
+ CLIErrorHandler.handle_service_error(e, "checksum calculation", {"input": data})
59
+
60
+
61
+ @checksum.command("validate")
62
+ @click.argument("data")
63
+ @click.argument("expected_checksum")
64
+ @click.option(
65
+ "--algorithm",
66
+ "-a",
67
+ type=click.Choice(["simple", "crc32"]),
68
+ default="simple",
69
+ help="Checksum algorithm to use",
70
+ )
71
+ @handle_service_errors(Exception)
72
+ def validate_checksum(data: str, expected_checksum: str, algorithm: str) -> None:
73
+ r"""Validate data against expected checksum.
74
+
75
+ Args:
76
+ data: Data string to validate.
77
+ expected_checksum: Expected checksum value.
78
+ algorithm: Checksum algorithm to use.
79
+
80
+ Examples:
81
+ \b
82
+ xp checksum validate "E14L00I02M" "AK"
83
+ xp checksum validate "E14L00I02M" "ABCDABCD" --algorithm crc32
84
+
85
+ Raises:
86
+ SystemExit: If checksum validation fails.
87
+ """
88
+ service = TelegramChecksumService()
89
+ formatter = OutputFormatter(True)
90
+
91
+ try:
92
+ if algorithm == "simple":
93
+ result = service.validate_checksum(data, expected_checksum)
94
+ else: # crc32
95
+ result = service.validate_crc32_checksum(data, expected_checksum)
96
+
97
+ if not result.success:
98
+ error_response = formatter.error_response(
99
+ result.error or "Unknown error",
100
+ {"input": data, "expected_checksum": expected_checksum},
101
+ )
102
+ click.echo(error_response)
103
+ raise SystemExit(1)
104
+
105
+ click.echo(json.dumps(result.to_dict(), indent=2))
106
+
107
+ except Exception as e:
108
+ CLIErrorHandler.handle_service_error(
109
+ e,
110
+ "checksum validation",
111
+ {"input": data, "expected_checksum": expected_checksum},
112
+ )
@@ -0,0 +1,41 @@
1
+ """Device discover operations CLI commands."""
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from xp.cli.commands.telegram.telegram import telegram
8
+ from xp.cli.utils.decorators import handle_service_errors
9
+ from xp.cli.utils.error_handlers import CLIErrorHandler
10
+ from xp.cli.utils.formatters import OutputFormatter
11
+ from xp.services.telegram.telegram_discover_service import (
12
+ DiscoverError,
13
+ TelegramDiscoverService,
14
+ )
15
+
16
+
17
+ @telegram.command("discover")
18
+ @handle_service_errors(DiscoverError)
19
+ def generate_discover() -> None:
20
+ r"""Generate a discover telegram for device enumeration.
21
+
22
+ Examples:
23
+ \b
24
+ xp telegram discover
25
+ """
26
+ service = TelegramDiscoverService()
27
+ OutputFormatter(True)
28
+
29
+ try:
30
+ discover = service.generate_discover_telegram()
31
+
32
+ output = {
33
+ "success": True,
34
+ "telegram": discover,
35
+ "operation": "discover_broadcast",
36
+ "broadcast_address": "0000000000",
37
+ }
38
+ click.echo(json.dumps(output, indent=2))
39
+
40
+ except DiscoverError as e:
41
+ CLIErrorHandler.handle_service_error(e, "discover telegram generation")
@@ -0,0 +1,86 @@
1
+ """Link number operations CLI commands."""
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from xp.cli.commands.telegram.telegram import linknumber
8
+ from xp.cli.utils.decorators import handle_service_errors
9
+ from xp.cli.utils.error_handlers import CLIErrorHandler
10
+ from xp.cli.utils.formatters import OutputFormatter
11
+ from xp.cli.utils.serial_number_type import SERIAL
12
+ from xp.services.telegram.telegram_link_number_service import (
13
+ LinkNumberError,
14
+ LinkNumberService,
15
+ )
16
+
17
+
18
+ @linknumber.command("write")
19
+ @click.argument("serial_number", type=SERIAL)
20
+ @click.argument("link_number", type=int)
21
+ @handle_service_errors(LinkNumberError)
22
+ def generate_set_link_number(serial_number: str, link_number: int) -> None:
23
+ r"""Generate a telegram to set module link number.
24
+
25
+ Args:
26
+ serial_number: 10-digit module serial number.
27
+ link_number: Link number to set.
28
+
29
+ Examples:
30
+ \b
31
+ xp telegram linknumber write 0012345005 25
32
+ """
33
+ service = LinkNumberService()
34
+ OutputFormatter(True)
35
+
36
+ try:
37
+ telegram = service.generate_set_link_number_telegram(serial_number, link_number)
38
+
39
+ output = {
40
+ "success": True,
41
+ "telegram": telegram,
42
+ "serial_number": serial_number,
43
+ "link_number": link_number,
44
+ "operation": "set_link_number",
45
+ }
46
+ click.echo(json.dumps(output, indent=2))
47
+
48
+ except LinkNumberError as e:
49
+ CLIErrorHandler.handle_service_error(
50
+ e,
51
+ "link number telegram generation",
52
+ {"serial_number": serial_number, "link_number": link_number},
53
+ )
54
+
55
+
56
+ @linknumber.command("read")
57
+ @click.argument("serial_number", type=SERIAL)
58
+ @handle_service_errors(LinkNumberError)
59
+ def generate_read_link_number(serial_number: str) -> None:
60
+ r"""Generate a telegram to read module link number.
61
+
62
+ Args:
63
+ serial_number: 10-digit module serial number.
64
+
65
+ Examples:
66
+ \b
67
+ xp telegram linknumber read 0012345005
68
+ """
69
+ service = LinkNumberService()
70
+ OutputFormatter(True)
71
+
72
+ try:
73
+ telegram = service.generate_read_link_number_telegram(serial_number)
74
+
75
+ output = {
76
+ "success": True,
77
+ "telegram": telegram,
78
+ "serial_number": serial_number,
79
+ "operation": "read_link_number",
80
+ }
81
+ click.echo(json.dumps(output, indent=2))
82
+
83
+ except LinkNumberError as e:
84
+ CLIErrorHandler.handle_service_error(
85
+ e, "read telegram generation", {"serial_number": serial_number}
86
+ )
@@ -0,0 +1,75 @@
1
+ """Telegram-related CLI commands."""
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from xp.cli.commands.telegram.telegram import telegram
8
+ from xp.cli.utils.decorators import (
9
+ handle_service_errors,
10
+ )
11
+ from xp.cli.utils.error_handlers import CLIErrorHandler
12
+ from xp.cli.utils.formatters import TelegramFormatter
13
+ from xp.services.telegram.telegram_service import TelegramParsingError, TelegramService
14
+
15
+
16
+ @telegram.command("parse")
17
+ @click.argument("telegram_string")
18
+ @handle_service_errors(TelegramParsingError)
19
+ def parse_any_telegram(telegram_string: str) -> None:
20
+ r"""Auto-detect and parse any type of telegram (event, system, reply, or discover).
21
+
22
+ Args:
23
+ telegram_string: Telegram string to parse.
24
+
25
+ Examples:
26
+ \b
27
+ xp telegram parse "<E14L00I02MAK>"
28
+ xp telegram parse "<S0020012521F02D18FN>"
29
+ xp telegram parse "<R0020012521F02D18+26,0§CIL>"
30
+ xp telegram parse "<S0000000000F01D00FA>"
31
+ xp telegram parse "<R0012345011F01DFM>"
32
+ xp telegram parse "<R0012345003F18DFF>"
33
+ """
34
+ service = TelegramService()
35
+ TelegramFormatter(True)
36
+
37
+ try:
38
+ parsed = service.parse_telegram(telegram_string)
39
+ output = parsed.to_dict()
40
+ click.echo(json.dumps(output, indent=2))
41
+
42
+ except TelegramParsingError as e:
43
+ CLIErrorHandler.handle_parsing_error(e, telegram_string)
44
+
45
+
46
+ @telegram.command("validate")
47
+ @click.argument("telegram_string")
48
+ @handle_service_errors(TelegramParsingError)
49
+ def validate_telegram(telegram_string: str) -> None:
50
+ r"""Validate the format of an event telegram.
51
+
52
+ Args:
53
+ telegram_string: Telegram string to validate.
54
+
55
+ Examples:
56
+ \b
57
+ xp telegram validate "<E14L00I02MAK>"
58
+ """
59
+ service = TelegramService()
60
+ TelegramFormatter(True)
61
+
62
+ try:
63
+ parsed = service.parse_event_telegram(telegram_string)
64
+ checksum_valid = service.validate_checksum(parsed)
65
+
66
+ output = {
67
+ "success": True,
68
+ "valid_format": True,
69
+ "valid_checksum": checksum_valid,
70
+ "telegram": parsed.to_dict(),
71
+ }
72
+ click.echo(json.dumps(output, indent=2))
73
+
74
+ except TelegramParsingError as e:
75
+ CLIErrorHandler.handle_validation_error(e, telegram_string)
@@ -0,0 +1,52 @@
1
+ """Version information operations CLI commands."""
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from xp.cli.commands.telegram.telegram import telegram
8
+ from xp.cli.utils.decorators import handle_service_errors
9
+ from xp.cli.utils.error_handlers import CLIErrorHandler
10
+ from xp.cli.utils.formatters import OutputFormatter
11
+ from xp.cli.utils.serial_number_type import SERIAL
12
+ from xp.services.telegram.telegram_version_service import (
13
+ VersionParsingError,
14
+ VersionService,
15
+ )
16
+
17
+
18
+ @telegram.command("version")
19
+ @click.argument("serial_number", type=SERIAL)
20
+ @handle_service_errors(VersionParsingError)
21
+ def generate_version_request(serial_number: str) -> None:
22
+ r"""Generate a telegram to request version information from a device.
23
+
24
+ Args:
25
+ serial_number: 10-digit module serial number.
26
+
27
+ Examples:
28
+ \b
29
+ xp telegram version 0012345011
30
+
31
+ Raises:
32
+ SystemExit: If request cannot be generated.
33
+ """
34
+ service = VersionService()
35
+ formatter = OutputFormatter(True)
36
+
37
+ try:
38
+ result = service.generate_version_request_telegram(serial_number)
39
+
40
+ if not result.success:
41
+ error_response = formatter.error_response(
42
+ result.error or "Unknown error", {"serial_number": serial_number}
43
+ )
44
+ click.echo(error_response)
45
+ raise SystemExit(1)
46
+
47
+ click.echo(json.dumps(result.to_dict(), indent=2))
48
+
49
+ except VersionParsingError as e:
50
+ CLIErrorHandler.handle_service_error(
51
+ e, "version request generation", {"serial_number": serial_number}
52
+ )
xp/cli/main.py ADDED
@@ -0,0 +1,87 @@
1
+ """XP CLI tool entry point with modular command structure."""
2
+
3
+ import logging
4
+
5
+ import click
6
+ from click_help_colors import HelpColorsGroup
7
+
8
+ from xp.cli.commands import homekit
9
+ from xp.cli.commands.conbus.conbus import conbus
10
+ from xp.cli.commands.file_commands import file
11
+ from xp.cli.commands.module_commands import module
12
+
13
+ # Import all conbus command modules to register their commands
14
+ from xp.cli.commands.reverse_proxy_commands import reverse_proxy
15
+ from xp.cli.commands.server.server_commands import server
16
+
17
+ # Import command groups from modular structure
18
+ from xp.cli.commands.telegram.telegram_parse_commands import telegram
19
+ from xp.cli.utils.click_tree import add_tree_command
20
+ from xp.utils.dependencies import ServiceContainer
21
+
22
+
23
+ @click.group(
24
+ cls=HelpColorsGroup, help_headers_color="yellow", help_options_color="green"
25
+ )
26
+ @click.version_option()
27
+ @click.pass_context
28
+ def cli(ctx: click.Context) -> None:
29
+ """XP CLI tool for remote console bus operations.
30
+
31
+ Args:
32
+ ctx: Click context object for passing state between commands.
33
+ """
34
+ # Configure logging with thread information
35
+ log_format = "%(asctime)s - [%(threadName)s-%(thread)d] - %(levelname)s - %(name)s - %(message)s"
36
+ date_format = "%H:%M:%S"
37
+
38
+ # Force format on root logger and all handlers
39
+ formatter = logging.Formatter(log_format, datefmt=date_format)
40
+ root_logger = logging.getLogger()
41
+ root_logger.setLevel(logging.DEBUG)
42
+
43
+ # Update all existing handlers or create new one
44
+ if root_logger.handlers:
45
+ for handler in root_logger.handlers:
46
+ handler.setFormatter(formatter)
47
+ else:
48
+ handler = logging.StreamHandler()
49
+ handler.setFormatter(formatter)
50
+ root_logger.addHandler(handler)
51
+
52
+ # Suppress pyhap.hap_protocol logs
53
+
54
+ # bubus
55
+ logging.getLogger("bubus").setLevel(logging.WARNING)
56
+
57
+ # xp
58
+ logging.getLogger("xp").setLevel(logging.DEBUG)
59
+ logging.getLogger("xp.services.homekit").setLevel(logging.DEBUG)
60
+
61
+ # pyhap
62
+ logging.getLogger("pyhap").setLevel(logging.WARNING)
63
+ logging.getLogger("pyhap.hap_handler").setLevel(logging.WARNING)
64
+ logging.getLogger("pyhap.hap_protocol").setLevel(logging.WARNING)
65
+ # logging.getLogger('pyhap.accessory_driver').setLevel(logging.WARNING)
66
+
67
+ # Initialize the service container and store it in the context
68
+ ctx.ensure_object(dict)
69
+ # Only create a new container if one wasn't provided (e.g., for testing)
70
+ if "container" not in ctx.obj:
71
+ ctx.obj["container"] = ServiceContainer()
72
+
73
+
74
+ # Register all command groups
75
+ cli.add_command(conbus)
76
+ cli.add_command(homekit)
77
+ cli.add_command(telegram)
78
+ cli.add_command(module)
79
+ cli.add_command(file)
80
+ cli.add_command(server)
81
+ cli.add_command(reverse_proxy)
82
+
83
+ # Add the tree command
84
+ add_tree_command(cli)
85
+
86
+ if __name__ == "__main__":
87
+ cli()
@@ -0,0 +1 @@
1
+ """CLI utilities for common functionality."""
@@ -0,0 +1,57 @@
1
+ """Utilities for displaying Click command trees."""
2
+
3
+ from typing import Any
4
+
5
+ import click
6
+
7
+
8
+ def add_tree_command(cli_group: click.Group, command_name: str = "help") -> Any:
9
+ """Add a tree command to any Click group.
10
+
11
+ Args:
12
+ cli_group: The Click group to add the tree command to.
13
+ command_name: Name of the tree command (default: "help").
14
+
15
+ Returns:
16
+ The tree command function.
17
+ """
18
+
19
+ def print_command_tree(group: click.Group, ctx: click.Context, suffix: str) -> None:
20
+ """Print command tree recursively.
21
+
22
+ Args:
23
+ group: The Click group to print.
24
+ ctx: The Click context.
25
+ suffix: Prefix string for tree display.
26
+ """
27
+ for name in sorted(group.list_commands(ctx)):
28
+ cmd = group.get_command(ctx, name)
29
+
30
+ if isinstance(cmd, click.Group):
31
+ # print(f"")
32
+ print()
33
+ print(f"{suffix} {name}")
34
+ print_command_tree(cmd, ctx, f"{suffix} {name}")
35
+ print()
36
+ else:
37
+ print(f"{suffix} {name}")
38
+
39
+ @cli_group.command(command_name)
40
+ @click.pass_context
41
+ def tree_command(ctx: click.Context) -> None:
42
+ """Show complete command tree.
43
+
44
+ Args:
45
+ ctx: The Click context.
46
+ """
47
+ root = ctx.find_root().command
48
+ root_ctx = ctx.find_root()
49
+ root_name = root_ctx.info_name or "cli"
50
+ print()
51
+ print(root_name)
52
+ if root.short_help:
53
+ print(str(root.short_help))
54
+ if isinstance(root, click.Group):
55
+ print_command_tree(root, root_ctx, root_name)
56
+
57
+ return tree_command
@@ -0,0 +1,57 @@
1
+ """Click parameter type for DataPointType enum validation."""
2
+
3
+ from typing import Any, Optional
4
+
5
+ import click
6
+
7
+ from xp.models.telegram.datapoint_type import DataPointType
8
+
9
+
10
+ # noinspection DuplicatedCode
11
+ class DatapointTypeChoice(click.ParamType):
12
+ """Click parameter type for validating DataPointType enum values.
13
+
14
+ Attributes:
15
+ name: The parameter type name.
16
+ choices: List of valid choice strings.
17
+ """
18
+
19
+ name = "telegram_type"
20
+
21
+ def __init__(self) -> None:
22
+ """Initialize the DatapointTypeChoice parameter type."""
23
+ self.choices = [key.lower() for key in DataPointType.__members__.keys()]
24
+
25
+ def convert(
26
+ self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]
27
+ ) -> Any:
28
+ """Convert and validate input to DataPointType 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
+ DataPointType enum member if valid, None if input is None.
37
+ """
38
+ if value is None:
39
+ return value
40
+
41
+ # Convert to lower for comparison
42
+ normalized_value = value.lower()
43
+
44
+ if normalized_value in self.choices:
45
+ # Return the actual enum member
46
+ return DataPointType[normalized_value.upper()]
47
+
48
+ # If not found, show error with available choices
49
+ choices_list = "\n".join(f" - {choice}" for choice in sorted(self.choices))
50
+ self.fail(
51
+ f"{value!r} is not a valid choice. " f"Choose from:\n{choices_list}",
52
+ param,
53
+ ctx,
54
+ )
55
+
56
+
57
+ DATAPOINT = DatapointTypeChoice()