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.
- {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/METADATA +1 -5
- conson_xp-1.3.0.dist-info/RECORD +164 -0
- xp/__init__.py +4 -3
- xp/cli/__init__.py +1 -1
- xp/cli/commands/__init__.py +1 -2
- xp/cli/commands/conbus/conbus.py +9 -37
- xp/cli/commands/conbus/conbus_actiontable_commands.py +26 -4
- xp/cli/commands/conbus/conbus_autoreport_commands.py +58 -30
- xp/cli/commands/conbus/conbus_blink_commands.py +61 -29
- xp/cli/commands/conbus/conbus_config_commands.py +10 -5
- xp/cli/commands/conbus/conbus_custom_commands.py +16 -5
- xp/cli/commands/conbus/conbus_datapoint_commands.py +32 -10
- xp/cli/commands/conbus/conbus_discover_commands.py +20 -7
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +114 -39
- xp/cli/commands/conbus/conbus_linknumber_commands.py +50 -25
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +36 -5
- xp/cli/commands/conbus/conbus_output_commands.py +52 -14
- xp/cli/commands/conbus/conbus_raw_commands.py +17 -6
- xp/cli/commands/conbus/conbus_receive_commands.py +20 -10
- xp/cli/commands/conbus/conbus_scan_commands.py +17 -4
- xp/cli/commands/file_commands.py +35 -18
- xp/cli/commands/homekit/homekit.py +14 -8
- xp/cli/commands/homekit/homekit_start_commands.py +8 -6
- xp/cli/commands/module_commands.py +38 -23
- xp/cli/commands/reverse_proxy_commands.py +27 -19
- xp/cli/commands/server/server_commands.py +18 -18
- xp/cli/commands/telegram/telegram.py +4 -12
- xp/cli/commands/telegram/telegram_blink_commands.py +10 -8
- xp/cli/commands/telegram/telegram_checksum_commands.py +19 -8
- xp/cli/commands/telegram/telegram_discover_commands.py +2 -4
- xp/cli/commands/telegram/telegram_linknumber_commands.py +11 -8
- xp/cli/commands/telegram/telegram_parse_commands.py +10 -9
- xp/cli/commands/telegram/telegram_version_commands.py +8 -4
- xp/cli/main.py +5 -3
- xp/cli/utils/click_tree.py +23 -3
- xp/cli/utils/datapoint_type_choice.py +20 -0
- xp/cli/utils/decorators.py +165 -14
- xp/cli/utils/error_handlers.py +49 -18
- xp/cli/utils/formatters.py +95 -10
- xp/cli/utils/serial_number_type.py +18 -0
- xp/cli/utils/system_function_choice.py +20 -0
- xp/cli/utils/xp_module_type.py +20 -0
- xp/connection/__init__.py +1 -1
- xp/connection/exceptions.py +5 -5
- xp/models/__init__.py +1 -1
- xp/models/actiontable/__init__.py +1 -0
- xp/models/actiontable/actiontable.py +17 -1
- xp/models/actiontable/msactiontable_xp20.py +10 -0
- xp/models/actiontable/msactiontable_xp24.py +20 -3
- xp/models/actiontable/msactiontable_xp33.py +27 -4
- xp/models/conbus/__init__.py +1 -0
- xp/models/conbus/conbus.py +34 -4
- xp/models/conbus/conbus_autoreport.py +20 -2
- xp/models/conbus/conbus_blink.py +22 -2
- xp/models/conbus/conbus_client_config.py +22 -1
- xp/models/conbus/conbus_connection_status.py +16 -2
- xp/models/conbus/conbus_custom.py +21 -2
- xp/models/conbus/conbus_datapoint.py +25 -2
- xp/models/conbus/conbus_discover.py +18 -2
- xp/models/conbus/conbus_lightlevel.py +20 -2
- xp/models/conbus/conbus_linknumber.py +20 -2
- xp/models/conbus/conbus_output.py +22 -2
- xp/models/conbus/conbus_raw.py +17 -2
- xp/models/conbus/conbus_receive.py +16 -2
- xp/models/conbus/conbus_writeconfig.py +60 -0
- xp/models/homekit/__init__.py +1 -0
- xp/models/homekit/homekit_accessory.py +15 -1
- xp/models/homekit/homekit_config.py +52 -0
- xp/models/homekit/homekit_conson_config.py +32 -0
- xp/models/log_entry.py +49 -9
- xp/models/protocol/__init__.py +1 -0
- xp/models/protocol/conbus_protocol.py +130 -21
- xp/models/telegram/__init__.py +1 -0
- xp/models/telegram/action_type.py +16 -2
- xp/models/telegram/datapoint_type.py +36 -2
- xp/models/telegram/event_telegram.py +46 -10
- xp/models/telegram/event_type.py +8 -1
- xp/models/telegram/input_action_type.py +34 -2
- xp/models/telegram/input_type.py +9 -1
- xp/models/telegram/module_type.py +69 -19
- xp/models/telegram/module_type_code.py +43 -1
- xp/models/telegram/output_telegram.py +30 -6
- xp/models/telegram/reply_telegram.py +56 -11
- xp/models/telegram/system_function.py +35 -3
- xp/models/telegram/system_telegram.py +18 -4
- xp/models/telegram/telegram.py +12 -3
- xp/models/telegram/telegram_type.py +8 -1
- xp/models/telegram/timeparam_type.py +27 -0
- xp/models/write_config_type.py +17 -2
- xp/services/__init__.py +1 -1
- xp/services/conbus/__init__.py +1 -0
- xp/services/conbus/actiontable/__init__.py +1 -0
- xp/services/conbus/actiontable/actiontable_service.py +33 -2
- xp/services/conbus/actiontable/msactiontable_service.py +40 -3
- xp/services/conbus/actiontable/msactiontable_xp24_serializer.py +36 -4
- xp/services/conbus/actiontable/msactiontable_xp33_serializer.py +45 -5
- xp/services/conbus/conbus_blink_all_service.py +40 -21
- xp/services/conbus/conbus_blink_service.py +37 -13
- xp/services/conbus/conbus_custom_service.py +29 -13
- xp/services/conbus/conbus_datapoint_queryall_service.py +40 -16
- xp/services/conbus/conbus_datapoint_service.py +42 -18
- xp/services/conbus/conbus_discover_service.py +43 -7
- xp/services/conbus/conbus_output_service.py +33 -13
- xp/services/conbus/conbus_raw_service.py +36 -16
- xp/services/conbus/conbus_receive_service.py +38 -6
- xp/services/conbus/conbus_scan_service.py +44 -18
- xp/services/conbus/write_config_service.py +193 -0
- xp/services/homekit/__init__.py +1 -0
- xp/services/homekit/homekit_cache_service.py +31 -6
- xp/services/homekit/homekit_conbus_service.py +33 -2
- xp/services/homekit/homekit_config_validator.py +97 -15
- xp/services/homekit/homekit_conson_validator.py +51 -7
- xp/services/homekit/homekit_dimminglight.py +47 -1
- xp/services/homekit/homekit_dimminglight_service.py +35 -1
- xp/services/homekit/homekit_hap_service.py +71 -18
- xp/services/homekit/homekit_lightbulb.py +35 -1
- xp/services/homekit/homekit_lightbulb_service.py +30 -2
- xp/services/homekit/homekit_module_service.py +23 -1
- xp/services/homekit/homekit_outlet.py +47 -1
- xp/services/homekit/homekit_outlet_service.py +44 -2
- xp/services/homekit/homekit_service.py +113 -19
- xp/services/log_file_service.py +37 -41
- xp/services/module_type_service.py +26 -5
- xp/services/protocol/__init__.py +1 -1
- xp/services/protocol/conbus_protocol.py +110 -16
- xp/services/protocol/protocol_factory.py +40 -0
- xp/services/protocol/telegram_protocol.py +38 -7
- xp/services/reverse_proxy_service.py +79 -14
- xp/services/server/__init__.py +1 -0
- xp/services/server/base_server_service.py +102 -14
- xp/services/server/cp20_server_service.py +12 -4
- xp/services/server/server_service.py +26 -11
- xp/services/server/xp130_server_service.py +11 -3
- xp/services/server/xp20_server_service.py +11 -3
- xp/services/server/xp230_server_service.py +11 -3
- xp/services/server/xp24_server_service.py +33 -6
- xp/services/server/xp33_server_service.py +41 -8
- xp/services/telegram/__init__.py +1 -0
- xp/services/telegram/telegram_blink_service.py +19 -31
- xp/services/telegram/telegram_checksum_service.py +10 -10
- xp/services/telegram/telegram_datapoint_service.py +70 -0
- xp/services/telegram/telegram_discover_service.py +58 -29
- xp/services/telegram/telegram_link_number_service.py +27 -40
- xp/services/telegram/telegram_output_service.py +46 -49
- xp/services/telegram/telegram_service.py +41 -41
- xp/services/telegram/telegram_version_service.py +4 -2
- xp/utils/__init__.py +1 -1
- xp/utils/dependencies.py +4 -47
- xp/utils/serialization.py +6 -0
- xp/utils/time_utils.py +6 -11
- conson_xp-1.1.0.dist-info/RECORD +0 -181
- xp/api/__init__.py +0 -1
- xp/api/main.py +0 -110
- xp/api/models/__init__.py +0 -1
- xp/api/models/api.py +0 -20
- xp/api/models/discover.py +0 -21
- xp/api/routers/__init__.py +0 -17
- xp/api/routers/conbus.py +0 -5
- xp/api/routers/conbus_blink.py +0 -105
- xp/api/routers/conbus_custom.py +0 -63
- xp/api/routers/conbus_datapoint.py +0 -67
- xp/api/routers/conbus_output.py +0 -147
- xp/api/routers/errors.py +0 -37
- xp/cli/commands/api.py +0 -16
- xp/cli/commands/api_start_commands.py +0 -126
- xp/services/conbus/conbus_autoreport_get_service.py +0 -85
- xp/services/conbus/conbus_autoreport_set_service.py +0 -128
- xp/services/conbus/conbus_lightlevel_get_service.py +0 -101
- xp/services/conbus/conbus_lightlevel_set_service.py +0 -205
- xp/services/conbus/conbus_linknumber_get_service.py +0 -86
- xp/services/conbus/conbus_linknumber_set_service.py +0 -155
- {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.1.0.dist-info → conson_xp-1.3.0.dist-info}/licenses/LICENSE +0 -0
xp/api/routers/conbus_custom.py
DELETED
|
@@ -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
|
-
)
|
xp/api/routers/conbus_output.py
DELETED
|
@@ -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()
|