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.
- conson_xp-1.18.0.dist-info/METADATA +412 -0
- conson_xp-1.18.0.dist-info/RECORD +176 -0
- conson_xp-1.18.0.dist-info/WHEEL +4 -0
- conson_xp-1.18.0.dist-info/entry_points.txt +5 -0
- conson_xp-1.18.0.dist-info/licenses/LICENSE +29 -0
- xp/__init__.py +9 -0
- xp/cli/__init__.py +5 -0
- xp/cli/__main__.py +6 -0
- xp/cli/commands/__init__.py +153 -0
- xp/cli/commands/conbus/__init__.py +25 -0
- xp/cli/commands/conbus/conbus.py +128 -0
- xp/cli/commands/conbus/conbus_actiontable_commands.py +233 -0
- xp/cli/commands/conbus/conbus_autoreport_commands.py +108 -0
- xp/cli/commands/conbus/conbus_blink_commands.py +163 -0
- xp/cli/commands/conbus/conbus_config_commands.py +29 -0
- xp/cli/commands/conbus/conbus_custom_commands.py +57 -0
- xp/cli/commands/conbus/conbus_datapoint_commands.py +113 -0
- xp/cli/commands/conbus/conbus_discover_commands.py +61 -0
- xp/cli/commands/conbus/conbus_event_commands.py +81 -0
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +207 -0
- xp/cli/commands/conbus/conbus_linknumber_commands.py +102 -0
- xp/cli/commands/conbus/conbus_modulenumber_commands.py +104 -0
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +94 -0
- xp/cli/commands/conbus/conbus_output_commands.py +163 -0
- xp/cli/commands/conbus/conbus_raw_commands.py +62 -0
- xp/cli/commands/conbus/conbus_receive_commands.py +59 -0
- xp/cli/commands/conbus/conbus_scan_commands.py +58 -0
- xp/cli/commands/file_commands.py +186 -0
- xp/cli/commands/homekit/__init__.py +3 -0
- xp/cli/commands/homekit/homekit.py +118 -0
- xp/cli/commands/homekit/homekit_start_commands.py +43 -0
- xp/cli/commands/module_commands.py +187 -0
- xp/cli/commands/reverse_proxy_commands.py +178 -0
- xp/cli/commands/server/__init__.py +3 -0
- xp/cli/commands/server/server_commands.py +135 -0
- xp/cli/commands/telegram/__init__.py +5 -0
- xp/cli/commands/telegram/telegram.py +41 -0
- xp/cli/commands/telegram/telegram_blink_commands.py +79 -0
- xp/cli/commands/telegram/telegram_checksum_commands.py +112 -0
- xp/cli/commands/telegram/telegram_discover_commands.py +41 -0
- xp/cli/commands/telegram/telegram_linknumber_commands.py +86 -0
- xp/cli/commands/telegram/telegram_parse_commands.py +75 -0
- xp/cli/commands/telegram/telegram_version_commands.py +52 -0
- xp/cli/main.py +87 -0
- xp/cli/utils/__init__.py +1 -0
- xp/cli/utils/click_tree.py +57 -0
- xp/cli/utils/datapoint_type_choice.py +57 -0
- xp/cli/utils/decorators.py +351 -0
- xp/cli/utils/error_handlers.py +201 -0
- xp/cli/utils/formatters.py +312 -0
- xp/cli/utils/module_type_choice.py +56 -0
- xp/cli/utils/serial_number_type.py +52 -0
- xp/cli/utils/system_function_choice.py +57 -0
- xp/cli/utils/xp_module_type.py +53 -0
- xp/connection/__init__.py +13 -0
- xp/connection/exceptions.py +22 -0
- xp/models/__init__.py +36 -0
- xp/models/actiontable/__init__.py +1 -0
- xp/models/actiontable/actiontable.py +43 -0
- xp/models/actiontable/msactiontable_xp20.py +53 -0
- xp/models/actiontable/msactiontable_xp24.py +58 -0
- xp/models/actiontable/msactiontable_xp33.py +65 -0
- xp/models/conbus/__init__.py +1 -0
- xp/models/conbus/conbus.py +87 -0
- xp/models/conbus/conbus_autoreport.py +67 -0
- xp/models/conbus/conbus_blink.py +80 -0
- xp/models/conbus/conbus_client_config.py +55 -0
- xp/models/conbus/conbus_connection_status.py +40 -0
- xp/models/conbus/conbus_custom.py +58 -0
- xp/models/conbus/conbus_datapoint.py +89 -0
- xp/models/conbus/conbus_discover.py +64 -0
- xp/models/conbus/conbus_event_raw.py +47 -0
- xp/models/conbus/conbus_lightlevel.py +52 -0
- xp/models/conbus/conbus_linknumber.py +54 -0
- xp/models/conbus/conbus_output.py +57 -0
- xp/models/conbus/conbus_raw.py +45 -0
- xp/models/conbus/conbus_receive.py +42 -0
- xp/models/conbus/conbus_writeconfig.py +60 -0
- xp/models/homekit/__init__.py +1 -0
- xp/models/homekit/homekit_accessory.py +35 -0
- xp/models/homekit/homekit_config.py +106 -0
- xp/models/homekit/homekit_conson_config.py +86 -0
- xp/models/log_entry.py +130 -0
- xp/models/protocol/__init__.py +1 -0
- xp/models/protocol/conbus_protocol.py +312 -0
- xp/models/response.py +42 -0
- xp/models/telegram/__init__.py +1 -0
- xp/models/telegram/action_type.py +31 -0
- xp/models/telegram/datapoint_type.py +82 -0
- xp/models/telegram/event_telegram.py +140 -0
- xp/models/telegram/event_type.py +15 -0
- xp/models/telegram/input_action_type.py +69 -0
- xp/models/telegram/input_type.py +17 -0
- xp/models/telegram/module_type.py +188 -0
- xp/models/telegram/module_type_code.py +205 -0
- xp/models/telegram/output_telegram.py +103 -0
- xp/models/telegram/reply_telegram.py +297 -0
- xp/models/telegram/system_function.py +116 -0
- xp/models/telegram/system_telegram.py +94 -0
- xp/models/telegram/telegram.py +28 -0
- xp/models/telegram/telegram_type.py +19 -0
- xp/models/telegram/timeparam_type.py +51 -0
- xp/models/write_config_type.py +33 -0
- xp/services/__init__.py +26 -0
- xp/services/actiontable/__init__.py +1 -0
- xp/services/actiontable/actiontable_serializer.py +273 -0
- xp/services/actiontable/msactiontable_serializer.py +7 -0
- xp/services/actiontable/msactiontable_xp20_serializer.py +169 -0
- xp/services/actiontable/msactiontable_xp24_serializer.py +120 -0
- xp/services/actiontable/msactiontable_xp33_serializer.py +239 -0
- xp/services/conbus/__init__.py +1 -0
- xp/services/conbus/actiontable/__init__.py +1 -0
- xp/services/conbus/actiontable/actiontable_download_service.py +158 -0
- xp/services/conbus/actiontable/actiontable_list_service.py +91 -0
- xp/services/conbus/actiontable/actiontable_show_service.py +89 -0
- xp/services/conbus/actiontable/actiontable_upload_service.py +211 -0
- xp/services/conbus/actiontable/msactiontable_service.py +232 -0
- xp/services/conbus/conbus_blink_all_service.py +181 -0
- xp/services/conbus/conbus_blink_service.py +158 -0
- xp/services/conbus/conbus_custom_service.py +156 -0
- xp/services/conbus/conbus_datapoint_queryall_service.py +182 -0
- xp/services/conbus/conbus_datapoint_service.py +170 -0
- xp/services/conbus/conbus_discover_service.py +312 -0
- xp/services/conbus/conbus_event_raw_service.py +181 -0
- xp/services/conbus/conbus_output_service.py +194 -0
- xp/services/conbus/conbus_raw_service.py +122 -0
- xp/services/conbus/conbus_receive_service.py +115 -0
- xp/services/conbus/conbus_scan_service.py +150 -0
- xp/services/conbus/write_config_service.py +194 -0
- xp/services/homekit/__init__.py +1 -0
- xp/services/homekit/homekit_cache_service.py +307 -0
- xp/services/homekit/homekit_conbus_service.py +93 -0
- xp/services/homekit/homekit_config_validator.py +310 -0
- xp/services/homekit/homekit_conson_validator.py +121 -0
- xp/services/homekit/homekit_dimminglight.py +182 -0
- xp/services/homekit/homekit_dimminglight_service.py +148 -0
- xp/services/homekit/homekit_hap_service.py +342 -0
- xp/services/homekit/homekit_lightbulb.py +120 -0
- xp/services/homekit/homekit_lightbulb_service.py +86 -0
- xp/services/homekit/homekit_module_service.py +56 -0
- xp/services/homekit/homekit_outlet.py +168 -0
- xp/services/homekit/homekit_outlet_service.py +121 -0
- xp/services/homekit/homekit_service.py +359 -0
- xp/services/log_file_service.py +309 -0
- xp/services/module_type_service.py +257 -0
- xp/services/protocol/__init__.py +21 -0
- xp/services/protocol/conbus_event_protocol.py +360 -0
- xp/services/protocol/conbus_protocol.py +318 -0
- xp/services/protocol/protocol_factory.py +78 -0
- xp/services/protocol/telegram_protocol.py +264 -0
- xp/services/reverse_proxy_service.py +435 -0
- xp/services/server/__init__.py +1 -0
- xp/services/server/base_server_service.py +366 -0
- xp/services/server/cp20_server_service.py +65 -0
- xp/services/server/device_service_factory.py +94 -0
- xp/services/server/server_service.py +428 -0
- xp/services/server/xp130_server_service.py +67 -0
- xp/services/server/xp20_server_service.py +92 -0
- xp/services/server/xp230_server_service.py +58 -0
- xp/services/server/xp24_server_service.py +245 -0
- xp/services/server/xp33_server_service.py +535 -0
- xp/services/telegram/__init__.py +1 -0
- xp/services/telegram/telegram_blink_service.py +138 -0
- xp/services/telegram/telegram_checksum_service.py +149 -0
- xp/services/telegram/telegram_datapoint_service.py +82 -0
- xp/services/telegram/telegram_discover_service.py +277 -0
- xp/services/telegram/telegram_link_number_service.py +216 -0
- xp/services/telegram/telegram_output_service.py +322 -0
- xp/services/telegram/telegram_service.py +380 -0
- xp/services/telegram/telegram_version_service.py +288 -0
- xp/utils/__init__.py +12 -0
- xp/utils/checksum.py +61 -0
- xp/utils/dependencies.py +531 -0
- xp/utils/event_helper.py +31 -0
- xp/utils/serialization.py +205 -0
- 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()
|
xp/cli/utils/__init__.py
ADDED
|
@@ -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()
|