conson-xp 1.1.0__py3-none-any.whl → 1.3.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 (174) hide show
  1. {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/METADATA +1 -5
  2. conson_xp-1.3.0.dist-info/RECORD +164 -0
  3. xp/__init__.py +4 -3
  4. xp/cli/__init__.py +1 -1
  5. xp/cli/commands/__init__.py +1 -2
  6. xp/cli/commands/conbus/conbus.py +9 -37
  7. xp/cli/commands/conbus/conbus_actiontable_commands.py +26 -4
  8. xp/cli/commands/conbus/conbus_autoreport_commands.py +58 -30
  9. xp/cli/commands/conbus/conbus_blink_commands.py +61 -29
  10. xp/cli/commands/conbus/conbus_config_commands.py +10 -5
  11. xp/cli/commands/conbus/conbus_custom_commands.py +16 -5
  12. xp/cli/commands/conbus/conbus_datapoint_commands.py +32 -10
  13. xp/cli/commands/conbus/conbus_discover_commands.py +20 -7
  14. xp/cli/commands/conbus/conbus_lightlevel_commands.py +114 -39
  15. xp/cli/commands/conbus/conbus_linknumber_commands.py +50 -25
  16. xp/cli/commands/conbus/conbus_msactiontable_commands.py +36 -5
  17. xp/cli/commands/conbus/conbus_output_commands.py +52 -14
  18. xp/cli/commands/conbus/conbus_raw_commands.py +17 -6
  19. xp/cli/commands/conbus/conbus_receive_commands.py +20 -10
  20. xp/cli/commands/conbus/conbus_scan_commands.py +17 -4
  21. xp/cli/commands/file_commands.py +35 -18
  22. xp/cli/commands/homekit/homekit.py +14 -8
  23. xp/cli/commands/homekit/homekit_start_commands.py +8 -6
  24. xp/cli/commands/module_commands.py +38 -23
  25. xp/cli/commands/reverse_proxy_commands.py +27 -19
  26. xp/cli/commands/server/server_commands.py +18 -18
  27. xp/cli/commands/telegram/telegram.py +4 -12
  28. xp/cli/commands/telegram/telegram_blink_commands.py +10 -8
  29. xp/cli/commands/telegram/telegram_checksum_commands.py +19 -8
  30. xp/cli/commands/telegram/telegram_discover_commands.py +2 -4
  31. xp/cli/commands/telegram/telegram_linknumber_commands.py +11 -8
  32. xp/cli/commands/telegram/telegram_parse_commands.py +10 -9
  33. xp/cli/commands/telegram/telegram_version_commands.py +8 -4
  34. xp/cli/main.py +5 -3
  35. xp/cli/utils/click_tree.py +23 -3
  36. xp/cli/utils/datapoint_type_choice.py +20 -0
  37. xp/cli/utils/decorators.py +165 -14
  38. xp/cli/utils/error_handlers.py +49 -18
  39. xp/cli/utils/formatters.py +95 -10
  40. xp/cli/utils/serial_number_type.py +18 -0
  41. xp/cli/utils/system_function_choice.py +20 -0
  42. xp/cli/utils/xp_module_type.py +20 -0
  43. xp/connection/__init__.py +1 -1
  44. xp/connection/exceptions.py +5 -5
  45. xp/models/__init__.py +1 -1
  46. xp/models/actiontable/__init__.py +1 -0
  47. xp/models/actiontable/actiontable.py +17 -1
  48. xp/models/actiontable/msactiontable_xp20.py +10 -0
  49. xp/models/actiontable/msactiontable_xp24.py +20 -3
  50. xp/models/actiontable/msactiontable_xp33.py +27 -4
  51. xp/models/conbus/__init__.py +1 -0
  52. xp/models/conbus/conbus.py +34 -4
  53. xp/models/conbus/conbus_autoreport.py +20 -2
  54. xp/models/conbus/conbus_blink.py +22 -2
  55. xp/models/conbus/conbus_client_config.py +22 -1
  56. xp/models/conbus/conbus_connection_status.py +16 -2
  57. xp/models/conbus/conbus_custom.py +21 -2
  58. xp/models/conbus/conbus_datapoint.py +25 -2
  59. xp/models/conbus/conbus_discover.py +18 -2
  60. xp/models/conbus/conbus_lightlevel.py +20 -2
  61. xp/models/conbus/conbus_linknumber.py +20 -2
  62. xp/models/conbus/conbus_output.py +22 -2
  63. xp/models/conbus/conbus_raw.py +17 -2
  64. xp/models/conbus/conbus_receive.py +16 -2
  65. xp/models/conbus/conbus_writeconfig.py +60 -0
  66. xp/models/homekit/__init__.py +1 -0
  67. xp/models/homekit/homekit_accessory.py +15 -1
  68. xp/models/homekit/homekit_config.py +52 -0
  69. xp/models/homekit/homekit_conson_config.py +32 -0
  70. xp/models/log_entry.py +49 -9
  71. xp/models/protocol/__init__.py +1 -0
  72. xp/models/protocol/conbus_protocol.py +130 -21
  73. xp/models/telegram/__init__.py +1 -0
  74. xp/models/telegram/action_type.py +16 -2
  75. xp/models/telegram/datapoint_type.py +36 -2
  76. xp/models/telegram/event_telegram.py +46 -10
  77. xp/models/telegram/event_type.py +8 -1
  78. xp/models/telegram/input_action_type.py +34 -2
  79. xp/models/telegram/input_type.py +9 -1
  80. xp/models/telegram/module_type.py +69 -19
  81. xp/models/telegram/module_type_code.py +43 -1
  82. xp/models/telegram/output_telegram.py +30 -6
  83. xp/models/telegram/reply_telegram.py +56 -11
  84. xp/models/telegram/system_function.py +35 -3
  85. xp/models/telegram/system_telegram.py +18 -4
  86. xp/models/telegram/telegram.py +12 -3
  87. xp/models/telegram/telegram_type.py +8 -1
  88. xp/models/telegram/timeparam_type.py +27 -0
  89. xp/models/write_config_type.py +17 -2
  90. xp/services/__init__.py +1 -1
  91. xp/services/conbus/__init__.py +1 -0
  92. xp/services/conbus/actiontable/__init__.py +1 -0
  93. xp/services/conbus/actiontable/actiontable_service.py +33 -2
  94. xp/services/conbus/actiontable/msactiontable_service.py +40 -3
  95. xp/services/conbus/actiontable/msactiontable_xp24_serializer.py +36 -4
  96. xp/services/conbus/actiontable/msactiontable_xp33_serializer.py +45 -5
  97. xp/services/conbus/conbus_blink_all_service.py +40 -21
  98. xp/services/conbus/conbus_blink_service.py +37 -13
  99. xp/services/conbus/conbus_custom_service.py +29 -13
  100. xp/services/conbus/conbus_datapoint_queryall_service.py +40 -16
  101. xp/services/conbus/conbus_datapoint_service.py +42 -18
  102. xp/services/conbus/conbus_discover_service.py +43 -7
  103. xp/services/conbus/conbus_output_service.py +33 -13
  104. xp/services/conbus/conbus_raw_service.py +36 -16
  105. xp/services/conbus/conbus_receive_service.py +38 -6
  106. xp/services/conbus/conbus_scan_service.py +44 -18
  107. xp/services/conbus/write_config_service.py +193 -0
  108. xp/services/homekit/__init__.py +1 -0
  109. xp/services/homekit/homekit_cache_service.py +31 -6
  110. xp/services/homekit/homekit_conbus_service.py +33 -2
  111. xp/services/homekit/homekit_config_validator.py +97 -15
  112. xp/services/homekit/homekit_conson_validator.py +51 -7
  113. xp/services/homekit/homekit_dimminglight.py +47 -1
  114. xp/services/homekit/homekit_dimminglight_service.py +35 -1
  115. xp/services/homekit/homekit_hap_service.py +71 -18
  116. xp/services/homekit/homekit_lightbulb.py +35 -1
  117. xp/services/homekit/homekit_lightbulb_service.py +30 -2
  118. xp/services/homekit/homekit_module_service.py +23 -1
  119. xp/services/homekit/homekit_outlet.py +47 -1
  120. xp/services/homekit/homekit_outlet_service.py +44 -2
  121. xp/services/homekit/homekit_service.py +113 -19
  122. xp/services/log_file_service.py +37 -41
  123. xp/services/module_type_service.py +26 -5
  124. xp/services/protocol/__init__.py +1 -1
  125. xp/services/protocol/conbus_protocol.py +110 -16
  126. xp/services/protocol/protocol_factory.py +40 -0
  127. xp/services/protocol/telegram_protocol.py +38 -7
  128. xp/services/reverse_proxy_service.py +79 -14
  129. xp/services/server/__init__.py +1 -0
  130. xp/services/server/base_server_service.py +102 -14
  131. xp/services/server/cp20_server_service.py +12 -4
  132. xp/services/server/server_service.py +26 -11
  133. xp/services/server/xp130_server_service.py +11 -3
  134. xp/services/server/xp20_server_service.py +11 -3
  135. xp/services/server/xp230_server_service.py +11 -3
  136. xp/services/server/xp24_server_service.py +33 -6
  137. xp/services/server/xp33_server_service.py +41 -8
  138. xp/services/telegram/__init__.py +1 -0
  139. xp/services/telegram/telegram_blink_service.py +19 -31
  140. xp/services/telegram/telegram_checksum_service.py +10 -10
  141. xp/services/telegram/telegram_datapoint_service.py +70 -0
  142. xp/services/telegram/telegram_discover_service.py +58 -29
  143. xp/services/telegram/telegram_link_number_service.py +27 -40
  144. xp/services/telegram/telegram_output_service.py +46 -49
  145. xp/services/telegram/telegram_service.py +41 -41
  146. xp/services/telegram/telegram_version_service.py +4 -2
  147. xp/utils/__init__.py +1 -1
  148. xp/utils/dependencies.py +4 -47
  149. xp/utils/serialization.py +6 -0
  150. xp/utils/time_utils.py +6 -11
  151. conson_xp-1.1.0.dist-info/RECORD +0 -181
  152. xp/api/__init__.py +0 -1
  153. xp/api/main.py +0 -110
  154. xp/api/models/__init__.py +0 -1
  155. xp/api/models/api.py +0 -20
  156. xp/api/models/discover.py +0 -21
  157. xp/api/routers/__init__.py +0 -17
  158. xp/api/routers/conbus.py +0 -5
  159. xp/api/routers/conbus_blink.py +0 -105
  160. xp/api/routers/conbus_custom.py +0 -63
  161. xp/api/routers/conbus_datapoint.py +0 -67
  162. xp/api/routers/conbus_output.py +0 -147
  163. xp/api/routers/errors.py +0 -37
  164. xp/cli/commands/api.py +0 -16
  165. xp/cli/commands/api_start_commands.py +0 -126
  166. xp/services/conbus/conbus_autoreport_get_service.py +0 -85
  167. xp/services/conbus/conbus_autoreport_set_service.py +0 -128
  168. xp/services/conbus/conbus_lightlevel_get_service.py +0 -101
  169. xp/services/conbus/conbus_lightlevel_set_service.py +0 -205
  170. xp/services/conbus/conbus_linknumber_get_service.py +0 -86
  171. xp/services/conbus/conbus_linknumber_set_service.py +0 -155
  172. {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/WHEEL +0 -0
  173. {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/entry_points.txt +0 -0
  174. {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,63 +0,0 @@
1
- """FastAPI router for Conbus operations."""
2
-
3
- import logging
4
- from typing import Union
5
-
6
- from fastapi import Request
7
- from fastapi.responses import JSONResponse
8
-
9
- from xp.api.models.api import ApiErrorResponse, ApiResponse
10
- from xp.api.routers.conbus import router
11
- from xp.api.routers.errors import handle_service_error
12
- from xp.services.conbus.conbus_custom_service import ConbusCustomService
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- @router.get(
18
- "/custom/{serial_number}/{function_code}/{data}",
19
- response_model=Union[ApiResponse, ApiErrorResponse],
20
- responses={
21
- 200: {"model": ApiResponse, "description": "Datapoint completed successfully"},
22
- 400: {"model": ApiErrorResponse, "description": "Connection or request error"},
23
- 408: {"model": ApiErrorResponse, "description": "Request timeout"},
24
- 500: {"model": ApiErrorResponse, "description": "Internal server error"},
25
- },
26
- )
27
- async def custom_function(
28
- request: Request,
29
- serial_number: str = "1702033007",
30
- function_code: str = "02",
31
- data: str = "00",
32
- ) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
33
- """
34
- Initiate a Datapoint operation to find devices on the network.
35
-
36
- Sends a broadcastDatapoint telegram and collects responses from all connected devices.
37
- """
38
- service = request.app.state.container.get_container().resolve(ConbusCustomService)
39
- # SendDatapoint telegram and receive responses
40
- with service:
41
- response = service.send_custom_telegram(serial_number, function_code, data)
42
-
43
- if not response.success:
44
- return handle_service_error(response.error or "Unknown error")
45
-
46
- if response.reply_telegram is None:
47
- return ApiErrorResponse(
48
- success=False,
49
- error=response.error or "Unknown error",
50
- )
51
-
52
- # Build successful response
53
- if response.reply_telegram and response.reply_telegram.datapoint_type:
54
- return ApiResponse(
55
- success=True,
56
- result=response.reply_telegram.data_value,
57
- description=response.reply_telegram.datapoint_type.name,
58
- )
59
- return ApiResponse(
60
- success=True,
61
- result=response.reply_telegram.data_value,
62
- description="Custom command executed successfully",
63
- )
@@ -1,67 +0,0 @@
1
- """FastAPI router for Conbus operations."""
2
-
3
- import logging
4
- from typing import Union
5
-
6
- from fastapi import Request
7
- from fastapi.responses import JSONResponse
8
-
9
- from xp.api.models.api import ApiErrorResponse, ApiResponse
10
- from xp.api.routers.conbus import router
11
- from xp.api.routers.errors import handle_service_error
12
- from xp.models.telegram.datapoint_type import DataPointType
13
- from xp.services.conbus.conbus_datapoint_service import ConbusDatapointService
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- @router.get(
19
- "/datapoint/{datapoint}/{serial_number}",
20
- response_model=Union[ApiResponse, ApiErrorResponse],
21
- responses={
22
- 200: {"model": ApiResponse, "description": "Datapoint completed successfully"},
23
- 400: {"model": ApiErrorResponse, "description": "Connection or request error"},
24
- 408: {"model": ApiErrorResponse, "description": "Request timeout"},
25
- 500: {"model": ApiErrorResponse, "description": "Internal server error"},
26
- },
27
- )
28
- async def datapoint_devices(
29
- request: Request,
30
- datapoint: DataPointType = DataPointType.SW_VERSION,
31
- serial_number: str = "1702033007",
32
- ) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
33
- """
34
- Initiate a Datapoint operation to find devices on the network.
35
-
36
- Sends a broadcastDatapoint telegram and collects responses from all connected devices.
37
- """
38
- service = request.app.state.container.get_container().resolve(
39
- ConbusDatapointService
40
- )
41
- # SendDatapoint telegram and receive responses
42
- with service:
43
- response = service.query_datapoint(
44
- datapoint_type=datapoint, serial_number=serial_number
45
- )
46
-
47
- if not response.success:
48
- return handle_service_error(response.error or "Unknown error")
49
-
50
- if response.datapoint_telegram is None:
51
- return ApiErrorResponse(
52
- success=False,
53
- error=response.error or "Unknown error",
54
- )
55
-
56
- # Build successful response
57
- if response.datapoint_telegram and response.datapoint_telegram.datapoint_type:
58
- return ApiResponse(
59
- success=True,
60
- result=response.datapoint_telegram.data_value,
61
- description=response.datapoint_telegram.datapoint_type.name,
62
- )
63
- return ApiResponse(
64
- success=True,
65
- result=response.datapoint_telegram.data_value,
66
- description="Datapoint value retrieved",
67
- )
@@ -1,147 +0,0 @@
1
- """FastAPI router for Conbus operations."""
2
-
3
- import json
4
- import logging
5
- from typing import Union
6
-
7
- from fastapi import Request
8
- from fastapi.responses import JSONResponse
9
-
10
- from xp.api.models.api import ApiErrorResponse, ApiResponse
11
- from xp.api.routers.conbus import router
12
- from xp.api.routers.errors import handle_service_error
13
- from xp.models.telegram.action_type import ActionType
14
- from xp.services.conbus.conbus_output_service import ConbusOutputService
15
-
16
- logger = logging.getLogger(__name__)
17
-
18
-
19
- @router.get(
20
- "/output/{action}/{serial}/{device_input}",
21
- response_model=Union[ApiResponse, ApiErrorResponse],
22
- responses={
23
- 200: {"model": ApiResponse, "description": "Input completed successfully"},
24
- 400: {"model": ApiErrorResponse, "description": "Connection or request error"},
25
- 408: {"model": ApiErrorResponse, "description": "Request timeout"},
26
- 500: {"model": ApiErrorResponse, "description": "Internal server error"},
27
- },
28
- )
29
- async def input_action(
30
- request: Request,
31
- action: ActionType = ActionType.OFF_PRESS,
32
- serial: str = "1702033007",
33
- device_input: int = 0,
34
- ) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
35
- """
36
- Initiate Input operation to find devices on the network.
37
-
38
- Sends a broadcastInput telegram and collects responses from all connected devices.
39
- """
40
- service = request.app.state.container.get_container().resolve(ConbusOutputService)
41
-
42
- # SendInput telegram and receive responses
43
- with service:
44
- response = service.send_action(serial, device_input, action)
45
-
46
- if not response.success:
47
- return handle_service_error(response.error or "Unknown error")
48
-
49
- logger.debug(json.dumps(response.to_dict(), indent=2))
50
-
51
- # Build successful response
52
- if response.output_telegram and response.output_telegram.system_function:
53
- return ApiResponse(
54
- success=True,
55
- result=response.output_telegram.system_function.name,
56
- description=response.output_telegram.system_function.get_description(),
57
- # raw_telegram = response.output_telegram.raw_telegram,
58
- )
59
- return ApiResponse(
60
- success=True,
61
- result="Output command sent",
62
- description="Output command was sent successfully",
63
- )
64
-
65
-
66
- @router.get(
67
- "/output/status/{serial_number}",
68
- response_model=Union[ApiResponse, ApiErrorResponse],
69
- responses={
70
- 200: {"model": ApiResponse, "description": "Query completed successfully"},
71
- 400: {"model": ApiErrorResponse, "description": "Connection or request error"},
72
- 408: {"model": ApiErrorResponse, "description": "Request timeout"},
73
- 500: {"model": ApiErrorResponse, "description": "Internal server error"},
74
- },
75
- )
76
- async def output_status(
77
- request: Request,
78
- serial_number: str,
79
- ) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
80
- """
81
- Initiate Input operation to find devices on the network.
82
-
83
- Sends a broadcastInput telegram and collects responses from all connected devices.
84
- """
85
- service = request.app.state.container.get_container().resolve(ConbusOutputService)
86
-
87
- # SendInput telegram and receive responses
88
- with service:
89
- response = service.get_output_state(serial_number)
90
-
91
- if not response.success:
92
- return handle_service_error(response.error or "Unknown error")
93
-
94
- # Build successful response
95
- if response.datapoint_telegram and response.datapoint_telegram.datapoint_type:
96
- return ApiResponse(
97
- success=True,
98
- result=response.datapoint_telegram.data_value,
99
- description=response.datapoint_telegram.datapoint_type.name,
100
- )
101
- return ApiResponse(
102
- success=True,
103
- result="No data available",
104
- description="Output status retrieved but no data available",
105
- )
106
-
107
-
108
- @router.get(
109
- "/output/state/{serial_number}",
110
- response_model=Union[ApiResponse, ApiErrorResponse],
111
- responses={
112
- 200: {"model": ApiResponse, "description": "Query completed successfully"},
113
- 400: {"model": ApiErrorResponse, "description": "Connection or request error"},
114
- 408: {"model": ApiErrorResponse, "description": "Request timeout"},
115
- 500: {"model": ApiErrorResponse, "description": "Internal server error"},
116
- },
117
- )
118
- async def output_state(
119
- request: Request,
120
- serial_number: str,
121
- ) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
122
- """
123
- Initiate Input operation to find devices on the network.
124
-
125
- Sends a broadcastInput telegram and collects responses from all connected devices.
126
- """
127
- service = request.app.state.container.get_container().resolve(ConbusOutputService)
128
-
129
- # SendInput telegram and receive responses
130
- with service:
131
- response = service.get_module_state(serial_number)
132
-
133
- if not response.success:
134
- return handle_service_error(response.error or "Unknown error")
135
-
136
- # Build successful response
137
- if response.datapoint_telegram and response.datapoint_telegram.datapoint_type:
138
- return ApiResponse(
139
- success=True,
140
- result=response.datapoint_telegram.data_value,
141
- description=response.datapoint_telegram.datapoint_type.name,
142
- )
143
- return ApiResponse(
144
- success=True,
145
- result="No data available",
146
- description="Module state retrieved but no data available",
147
- )
xp/api/routers/errors.py DELETED
@@ -1,37 +0,0 @@
1
- from starlette import status
2
- from starlette.responses import JSONResponse
3
-
4
- from xp.api.models.discover import DiscoverErrorResponse
5
-
6
-
7
- def handle_service_error(
8
- error: str, default_status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR
9
- ) -> JSONResponse:
10
- """
11
- Handle service errors by creating a standardized JSON error response.
12
-
13
- Args:
14
- error: Service response object with success and error attributes
15
- default_status_code: HTTP status code to use (defaults to 500)
16
-
17
- Returns:
18
- JSONResponse with error details
19
- """
20
- error_msg = error or "Unknown service error"
21
-
22
- # Map specific error patterns to appropriate HTTP status codes
23
- if "Not connected to server" in error_msg:
24
- status_code = status.HTTP_400_BAD_REQUEST
25
- elif "Failed to generate telegram" in error_msg:
26
- status_code = status.HTTP_400_BAD_REQUEST
27
- elif "Response timeout" in error_msg:
28
- status_code = status.HTTP_408_REQUEST_TIMEOUT
29
- elif "Failed to send telegram" in error_msg:
30
- status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
31
- else:
32
- status_code = default_status_code
33
-
34
- return JSONResponse(
35
- status_code=status_code,
36
- content=DiscoverErrorResponse(error=error_msg).model_dump(),
37
- )
xp/cli/commands/api.py DELETED
@@ -1,16 +0,0 @@
1
- """API server management CLI commands."""
2
-
3
- import click
4
- from click_help_colors import HelpColorsGroup
5
-
6
-
7
- @click.group(
8
- cls=HelpColorsGroup, help_headers_color="yellow", help_options_color="green"
9
- )
10
- def api() -> None:
11
- """
12
- API server management commands.
13
-
14
- Manage the FastAPI server for XP Protocol operations.
15
- """
16
- pass
@@ -1,126 +0,0 @@
1
- """API server start command."""
2
-
3
- import sys
4
-
5
- import click
6
- import uvicorn
7
-
8
- from xp.api.main import create_app
9
- from xp.cli.commands.api import api
10
-
11
-
12
- @api.command("start")
13
- @click.option(
14
- "--host",
15
- default="127.0.0.1",
16
- help="Host to bind the server to (default: 127.0.0.1)",
17
- show_default=True,
18
- )
19
- @click.option(
20
- "--port",
21
- default=8000,
22
- type=int,
23
- help="Port to bind the server to (default: 8000)",
24
- show_default=True,
25
- )
26
- @click.option(
27
- "--reload",
28
- is_flag=True,
29
- default=True,
30
- help="Enable auto-reload for development",
31
- )
32
- @click.option(
33
- "--workers",
34
- default=1,
35
- type=int,
36
- help="Number of worker processes (default: 1)",
37
- show_default=True,
38
- )
39
- @click.option(
40
- "--log-level",
41
- default="info",
42
- type=click.Choice(["critical", "error", "warning", "info", "debug", "trace"]),
43
- help="Log level (default: info)",
44
- show_default=True,
45
- )
46
- @click.option(
47
- "--access-log/--no-access-log",
48
- default=True,
49
- help="Enable/disable access log (default: enabled)",
50
- )
51
- @click.pass_context
52
- def start_api_server(
53
- context: click.Context,
54
- host: str,
55
- port: int,
56
- reload: bool,
57
- workers: int,
58
- log_level: str,
59
- access_log: bool,
60
- ) -> None:
61
- """
62
- Start the FastAPI server.
63
-
64
- This command starts the XP Protocol FastAPI server using uvicorn.
65
- The server provides REST API endpoints for Conbus operations.
66
-
67
- Examples:
68
-
69
- \b
70
- # Start server on default host and port
71
- xp api start
72
-
73
- \b
74
- # Start server on specific host and port
75
- xp api start --host 0.0.0.0 --port 8080
76
-
77
- \b
78
- # Start development server with auto-reload
79
- xp api start --reload
80
-
81
- \b
82
- # Start production server with multiple workers
83
- xp api start --host 0.0.0.0 --workers 4 --no-access-log
84
- """
85
-
86
- # Validate workers and reload options
87
- if reload and workers > 1:
88
- click.echo(
89
- click.style(
90
- "Warning: Auto-reload is enabled. Setting workers to 1.",
91
- fg="yellow",
92
- )
93
- )
94
- workers = 1
95
-
96
- click.echo("Starting XP Protocol API server...")
97
- click.echo(f"Server will be available at: https://{host}:{port}")
98
- click.echo(f"API documentation at: https://{host}:{port}/docs")
99
- click.echo(f"Health check at: https://{host}:{port}/health")
100
-
101
- if reload:
102
- click.echo(click.style("Development mode: Auto-reload enabled", fg="green"))
103
-
104
- # Get container from CLI context or create new one
105
- container = context.obj.get("container")
106
-
107
- try:
108
- # For production mode, create app instance with container
109
- app = create_app(container)
110
- uvicorn.run(
111
- app,
112
- host=host,
113
- port=port,
114
- reload=reload,
115
- workers=workers,
116
- log_level=log_level,
117
- access_log=access_log,
118
- )
119
- except KeyboardInterrupt:
120
- click.echo("\nShutting down server...")
121
- except Exception as e:
122
- click.echo(
123
- click.style(f"Error starting server: {e}", fg="red"),
124
- err=True,
125
- )
126
- sys.exit(1)
@@ -1,85 +0,0 @@
1
- """Conbus Auto Report Service for getting and setting module auto report status.
2
-
3
- This service handles auto report status operations for modules through Conbus telegrams.
4
- """
5
-
6
- import logging
7
- from typing import Callable, Optional
8
-
9
- from twisted.internet.posixbase import PosixReactorBase
10
-
11
- from xp.models import ConbusClientConfig, ConbusDatapointResponse
12
- from xp.models.conbus.conbus_autoreport import ConbusAutoreportResponse
13
- from xp.models.telegram.datapoint_type import DataPointType
14
- from xp.services.conbus.conbus_datapoint_service import ConbusDatapointService
15
- from xp.services.telegram.telegram_service import TelegramService
16
-
17
-
18
- class ConbusAutoreportGetService(ConbusDatapointService):
19
- """
20
- Service for receiving telegrams from Conbus servers.
21
-
22
- Uses composition with ConbusService to provide receive-only functionality
23
- for collecting waiting event telegrams from the server.
24
- """
25
-
26
- def __init__(
27
- self,
28
- telegram_service: TelegramService,
29
- cli_config: ConbusClientConfig,
30
- reactor: PosixReactorBase,
31
- ) -> None:
32
- """Initialize the Conbus client send service"""
33
- super().__init__(telegram_service, cli_config, reactor)
34
- self.service_callback: Optional[Callable[[ConbusAutoreportResponse], None]] = (
35
- None
36
- )
37
-
38
- # Set up logging
39
- self.logger = logging.getLogger(__name__)
40
-
41
- def finish_service_callback(
42
- self, datapoint_response: ConbusDatapointResponse
43
- ) -> None:
44
-
45
- self.logger.debug("Parsing datapoint response")
46
- autoreport_status = ""
47
- if datapoint_response.success and datapoint_response.datapoint_telegram:
48
- autoreport_status = datapoint_response.datapoint_telegram.data_value
49
-
50
- service_response = ConbusAutoreportResponse(
51
- success=datapoint_response.success,
52
- serial_number=self.serial_number,
53
- auto_report_status=autoreport_status,
54
- error=datapoint_response.error,
55
- sent_telegram=datapoint_response.sent_telegram,
56
- received_telegrams=datapoint_response.received_telegrams,
57
- timestamp=datapoint_response.timestamp,
58
- )
59
-
60
- if self.service_callback:
61
- self.service_callback(service_response)
62
-
63
- def get_autoreport_status(
64
- self,
65
- serial_number: str,
66
- finish_callback: Callable[[ConbusAutoreportResponse], None],
67
- timeout_seconds: Optional[float] = None,
68
- ) -> None:
69
- """
70
- Get the current auto report status for a specific module.
71
-
72
- Args:
73
- :param serial_number: 10-digit module serial number
74
- :param finish_callback: callback function to call when the linknumber status is
75
- :param timeout_seconds: timeout in seconds
76
-
77
- """
78
- self.logger.info("Starting get_autoreport_status")
79
- if timeout_seconds:
80
- self.timeout_seconds = timeout_seconds
81
- self.serial_number = serial_number
82
- self.datapoint_type = DataPointType.AUTO_REPORT_STATUS
83
- self.finish_callback = self.finish_service_callback
84
- self.service_callback = finish_callback
85
- self.start_reactor()
@@ -1,128 +0,0 @@
1
- """Conbus Auto Report Service for getting and setting module auto report status.
2
-
3
- This service handles auto report status operations for modules through Conbus telegrams.
4
- """
5
-
6
- import logging
7
- from datetime import datetime
8
- from typing import Callable, Optional
9
-
10
- from twisted.internet.posixbase import PosixReactorBase
11
-
12
- from xp.models import ConbusClientConfig
13
- from xp.models.conbus.conbus_autoreport import ConbusAutoreportResponse
14
- from xp.models.protocol.conbus_protocol import TelegramReceivedEvent
15
- from xp.models.telegram.datapoint_type import DataPointType
16
- from xp.models.telegram.system_function import SystemFunction
17
- from xp.models.telegram.telegram_type import TelegramType
18
- from xp.services.protocol import ConbusProtocol
19
-
20
-
21
- class ConbusAutoreportSetService(ConbusProtocol):
22
- """
23
- Service for receiving telegrams from Conbus servers.
24
-
25
- Uses composition with ConbusService to provide receive-only functionality
26
- for collecting waiting event telegrams from the server.
27
- """
28
-
29
- def __init__(
30
- self,
31
- cli_config: ConbusClientConfig,
32
- reactor: PosixReactorBase,
33
- ) -> None:
34
- """Initialize the Conbus client send service"""
35
- super().__init__(cli_config, reactor)
36
- self.serial_number: str = ""
37
- self.status: bool = False
38
- self.finish_callback: Optional[Callable[[ConbusAutoreportResponse], None]] = (
39
- None
40
- )
41
- self.service_response: ConbusAutoreportResponse = ConbusAutoreportResponse(
42
- success=False,
43
- serial_number=self.serial_number,
44
- )
45
-
46
- # Set up logging
47
- self.logger = logging.getLogger(__name__)
48
-
49
- def connection_established(self) -> None:
50
- # Convert boolean to appropriate value
51
- status_value = "PP" if self.status else "AA"
52
- status_text = "on" if self.status else "off"
53
-
54
- self.logger.debug("Connection established, set autoreport to %s", status_text)
55
- self.send_telegram(
56
- telegram_type=TelegramType.SYSTEM,
57
- serial_number=self.serial_number,
58
- system_function=SystemFunction.WRITE_CONFIG,
59
- data_value=f"{DataPointType.AUTO_REPORT_STATUS.value}{status_value}",
60
- )
61
-
62
- def telegram_sent(self, telegram_sent: str) -> None:
63
- self.logger.debug("Autoreport reply telegram sent %s", telegram_sent)
64
- self.service_response.sent_telegram = telegram_sent
65
-
66
- def telegram_received(self, telegram_received: TelegramReceivedEvent) -> None:
67
-
68
- self.logger.debug(f"Telegram received: {telegram_received}")
69
- if not self.service_response.received_telegrams:
70
- self.service_response.received_telegrams = []
71
- self.service_response.received_telegrams.append(telegram_received.frame)
72
-
73
- if not (
74
- telegram_received.checksum_valid
75
- and telegram_received.telegram_type == TelegramType.REPLY
76
- and telegram_received.serial_number == self.serial_number
77
- and telegram_received.system_function
78
- in (SystemFunction.ACK, SystemFunction.NAK)
79
- ):
80
- self.logger.debug(f"Not a reply telegram received: {telegram_received}")
81
- return
82
-
83
- self.service_response.success = True
84
- self.service_response.timestamp = datetime.now()
85
- self.service_response.result = telegram_received.system_function.name
86
- self.service_response.auto_report_status = "on" if self.status else "off"
87
-
88
- self.logger.debug("Received autoreport reply telegram")
89
- if self.finish_callback:
90
- self.finish_callback(self.service_response)
91
-
92
- def failed(self, message: str) -> None:
93
- self.logger.debug(f"Failed with message: {message}")
94
- self.service_response.success = False
95
- self.service_response.error = message
96
- self.service_response.timestamp = datetime.now()
97
- if self.finish_callback:
98
- self.finish_callback(self.service_response)
99
-
100
- def set_autoreport_status(
101
- self,
102
- serial_number: str,
103
- status: bool,
104
- finish_callback: Callable[[ConbusAutoreportResponse], None],
105
- timeout_seconds: Optional[float] = None,
106
- ) -> None:
107
- """
108
- Set the auto report status for a specific module.
109
-
110
- Args:
111
- serial_number: 10-digit module serial number
112
- status: True for ON, False for OFF
113
- finish_callback: callback function to call when the autoreport status is
114
- timeout_seconds: timeout in seconds
115
-
116
- Returns:
117
- ConbusAutoreportResponse with operation result
118
-
119
- Raises:
120
- ConbusAutoreportError: If parameters are invalid
121
- """
122
- self.logger.info("Starting set_autoreport_status")
123
- if timeout_seconds:
124
- self.timeout_seconds = timeout_seconds
125
- self.finish_callback = finish_callback
126
- self.serial_number = serial_number
127
- self.status = status
128
- self.start_reactor()