conson-xp 1.2.0__py3-none-any.whl → 1.4.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.2.0.dist-info → conson_xp-1.4.0.dist-info}/METADATA +1 -5
- {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/RECORD +43 -60
- xp/__init__.py +1 -1
- xp/cli/commands/__init__.py +0 -2
- xp/cli/commands/conbus/conbus_actiontable_commands.py +5 -3
- xp/cli/commands/conbus/conbus_autoreport_commands.py +39 -21
- xp/cli/commands/conbus/conbus_blink_commands.py +8 -8
- xp/cli/commands/conbus/conbus_config_commands.py +3 -1
- xp/cli/commands/conbus/conbus_custom_commands.py +3 -1
- xp/cli/commands/conbus/conbus_datapoint_commands.py +4 -2
- xp/cli/commands/conbus/conbus_discover_commands.py +5 -3
- xp/cli/commands/conbus/conbus_lightlevel_commands.py +68 -32
- xp/cli/commands/conbus/conbus_linknumber_commands.py +32 -17
- xp/cli/commands/conbus/conbus_msactiontable_commands.py +11 -4
- xp/cli/commands/conbus/conbus_output_commands.py +6 -2
- xp/cli/commands/conbus/conbus_receive_commands.py +5 -3
- xp/cli/commands/file_commands.py +9 -3
- xp/cli/commands/homekit/homekit_start_commands.py +3 -1
- xp/cli/commands/module_commands.py +12 -4
- xp/cli/commands/reverse_proxy_commands.py +3 -1
- xp/cli/main.py +0 -2
- xp/models/conbus/conbus_datapoint.py +3 -0
- xp/models/conbus/conbus_discover.py +19 -3
- xp/models/conbus/conbus_writeconfig.py +60 -0
- xp/models/telegram/system_telegram.py +4 -4
- xp/services/conbus/conbus_datapoint_service.py +9 -6
- xp/services/conbus/conbus_discover_service.py +120 -2
- xp/services/conbus/conbus_scan_service.py +1 -1
- xp/services/conbus/{conbus_linknumber_set_service.py → write_config_service.py} +78 -66
- xp/services/protocol/telegram_protocol.py +4 -4
- xp/services/server/base_server_service.py +9 -4
- xp/services/server/cp20_server_service.py +2 -1
- xp/services/server/server_service.py +75 -4
- xp/services/server/xp130_server_service.py +2 -1
- xp/services/server/xp20_server_service.py +2 -1
- xp/services/server/xp230_server_service.py +2 -1
- xp/services/server/xp24_server_service.py +123 -50
- xp/services/server/xp33_server_service.py +150 -20
- xp/services/telegram/telegram_datapoint_service.py +70 -0
- xp/utils/dependencies.py +4 -46
- xp/api/__init__.py +0 -1
- xp/api/main.py +0 -125
- xp/api/models/__init__.py +0 -1
- xp/api/models/api.py +0 -31
- xp/api/models/discover.py +0 -31
- xp/api/routers/__init__.py +0 -17
- xp/api/routers/conbus.py +0 -5
- xp/api/routers/conbus_blink.py +0 -117
- xp/api/routers/conbus_custom.py +0 -71
- xp/api/routers/conbus_datapoint.py +0 -74
- xp/api/routers/conbus_output.py +0 -167
- xp/api/routers/errors.py +0 -38
- xp/cli/commands/api.py +0 -12
- xp/cli/commands/api_start_commands.py +0 -132
- xp/services/conbus/conbus_autoreport_get_service.py +0 -94
- xp/services/conbus/conbus_autoreport_set_service.py +0 -141
- xp/services/conbus/conbus_lightlevel_get_service.py +0 -109
- xp/services/conbus/conbus_lightlevel_set_service.py +0 -225
- xp/services/conbus/conbus_linknumber_get_service.py +0 -94
- {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/WHEEL +0 -0
- {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/entry_points.txt +0 -0
- {conson_xp-1.2.0.dist-info → conson_xp-1.4.0.dist-info}/licenses/LICENSE +0 -0
xp/api/main.py
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
"""FastAPI application for XP Protocol API endpoints."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import os
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
import yaml
|
|
9
|
-
from fastapi import FastAPI
|
|
10
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
11
|
-
|
|
12
|
-
from xp.api.routers import conbus
|
|
13
|
-
from xp.utils.dependencies import ServiceContainer
|
|
14
|
-
|
|
15
|
-
# Set up logging
|
|
16
|
-
logging.basicConfig(level=logging.INFO)
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def load_api_config() -> dict[str, Any]:
|
|
21
|
-
"""Load API configuration from api.yml or environment variables.
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
Dictionary containing API configuration settings.
|
|
25
|
-
"""
|
|
26
|
-
config = {
|
|
27
|
-
"title": "XP Protocol API",
|
|
28
|
-
"description": "REST API for XP Protocol Conbus operations",
|
|
29
|
-
"version": "0.2.0",
|
|
30
|
-
"cors_origins": ["*"],
|
|
31
|
-
"cors_methods": ["GET", "POST"],
|
|
32
|
-
"cors_headers": ["*"],
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# Try to load from api.yml
|
|
36
|
-
try:
|
|
37
|
-
if Path("api.yml").exists():
|
|
38
|
-
with Path("api.yml").open("r") as file:
|
|
39
|
-
file_config = yaml.safe_load(file)
|
|
40
|
-
if file_config:
|
|
41
|
-
config.update(file_config.get("api", {}))
|
|
42
|
-
logger.info("Loaded API configuration from api.yml")
|
|
43
|
-
except Exception as e:
|
|
44
|
-
logger.warning(f"Could not load api.yml: {e}")
|
|
45
|
-
|
|
46
|
-
# Override with environment variables
|
|
47
|
-
config["title"] = os.getenv("API_TITLE", config["title"])
|
|
48
|
-
config["description"] = os.getenv("API_DESCRIPTION", config["description"])
|
|
49
|
-
config["version"] = os.getenv("API_VERSION", config["version"])
|
|
50
|
-
|
|
51
|
-
# CORS configuration from environment
|
|
52
|
-
cors_origins = os.getenv("CORS_ORIGINS")
|
|
53
|
-
if cors_origins is not None:
|
|
54
|
-
config["cors_origins"] = cors_origins.split(",")
|
|
55
|
-
cors_methods = os.getenv("CORS_METHODS")
|
|
56
|
-
if cors_methods is not None:
|
|
57
|
-
config["cors_methods"] = cors_methods.split(",")
|
|
58
|
-
cors_headers = os.getenv("CORS_HEADERS")
|
|
59
|
-
if cors_headers is not None:
|
|
60
|
-
config["cors_headers"] = cors_headers.split(",")
|
|
61
|
-
|
|
62
|
-
return config
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def create_app(container: ServiceContainer) -> FastAPI:
|
|
66
|
-
"""Create and configure the FastAPI application.
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
container: Optional ServiceContainer instance. If not provided, a new one will be created.
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
Configured FastAPI application instance.
|
|
73
|
-
"""
|
|
74
|
-
config = load_api_config()
|
|
75
|
-
|
|
76
|
-
fastapi = FastAPI(
|
|
77
|
-
title=config["title"],
|
|
78
|
-
description=config["description"],
|
|
79
|
-
version=config["version"],
|
|
80
|
-
docs_url="/docs",
|
|
81
|
-
redoc_url="/redoc",
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# Add CORS middleware
|
|
85
|
-
fastapi.add_middleware(
|
|
86
|
-
CORSMiddleware,
|
|
87
|
-
allow_origins=config["cors_origins"],
|
|
88
|
-
allow_credentials=True,
|
|
89
|
-
allow_methods=config["cors_methods"],
|
|
90
|
-
allow_headers=config["cors_headers"],
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
# Initialize service container
|
|
94
|
-
fastapi.state.container = container
|
|
95
|
-
|
|
96
|
-
# Include routers
|
|
97
|
-
fastapi.include_router(conbus.router)
|
|
98
|
-
|
|
99
|
-
# Health check endpoint
|
|
100
|
-
@fastapi.get("/health")
|
|
101
|
-
async def health_check() -> dict[str, str]:
|
|
102
|
-
"""Return health status of the API.
|
|
103
|
-
|
|
104
|
-
Returns:
|
|
105
|
-
Dictionary containing status and service information.
|
|
106
|
-
"""
|
|
107
|
-
return {"status": "healthy", "service": "xp-api"}
|
|
108
|
-
|
|
109
|
-
# Root endpoint
|
|
110
|
-
@fastapi.get("/")
|
|
111
|
-
async def root() -> dict[str, str]:
|
|
112
|
-
"""Return API information and available endpoints.
|
|
113
|
-
|
|
114
|
-
Returns:
|
|
115
|
-
Dictionary containing API metadata and endpoint links.
|
|
116
|
-
"""
|
|
117
|
-
return {
|
|
118
|
-
"message": "XP Protocol API",
|
|
119
|
-
"version": config["version"],
|
|
120
|
-
"docs": "/docs",
|
|
121
|
-
"health": "/health",
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
logger.info(f"FastAPI application created: {config['title']} v{config['version']}")
|
|
125
|
-
return fastapi
|
xp/api/models/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""API models for request and response validation."""
|
xp/api/models/api.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"""Pydantic models for Input API endpoints."""
|
|
2
|
-
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ApiResponse(BaseModel):
|
|
9
|
-
"""Response model for successful Input operation.
|
|
10
|
-
|
|
11
|
-
Attributes:
|
|
12
|
-
success: Operation success status.
|
|
13
|
-
result: Result value.
|
|
14
|
-
description: Description of the result.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
success: bool = Field(default=True, description="Operation success status")
|
|
18
|
-
result: Optional[str] = Field(default=None, description="Result")
|
|
19
|
-
description: Optional[str] = Field(default=None, description="Description")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ApiErrorResponse(BaseModel):
|
|
23
|
-
"""Response model for failed Input operation.
|
|
24
|
-
|
|
25
|
-
Attributes:
|
|
26
|
-
success: Operation success status (always False).
|
|
27
|
-
error: Error message describing what went wrong.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
success: bool = Field(default=False, description="Operation success status")
|
|
31
|
-
error: str = Field(..., description="Error message")
|
xp/api/models/discover.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"""Pydantic models for discover API endpoints."""
|
|
2
|
-
|
|
3
|
-
from typing import List
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class DiscoverResponse(BaseModel):
|
|
9
|
-
"""Response model for successful discover operation.
|
|
10
|
-
|
|
11
|
-
Attributes:
|
|
12
|
-
success: Operation success status.
|
|
13
|
-
devices: List of discovered device information strings.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
success: bool = Field(default=True, description="Operation success status")
|
|
17
|
-
devices: List[str] = Field(
|
|
18
|
-
default_factory=list, description="Parsed device information"
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class DiscoverErrorResponse(BaseModel):
|
|
23
|
-
"""Response model for failed discover operation.
|
|
24
|
-
|
|
25
|
-
Attributes:
|
|
26
|
-
success: Operation success status (always False).
|
|
27
|
-
error: Error message describing what went wrong.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
success: bool = Field(default=False, description="Operation success status")
|
|
31
|
-
error: str = Field(..., description="Error message")
|
xp/api/routers/__init__.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""API routers for FastAPI endpoints."""
|
|
2
|
-
|
|
3
|
-
from xp.api.routers import (
|
|
4
|
-
conbus_blink,
|
|
5
|
-
conbus_custom,
|
|
6
|
-
conbus_datapoint,
|
|
7
|
-
conbus_output,
|
|
8
|
-
)
|
|
9
|
-
from xp.api.routers.conbus import router
|
|
10
|
-
|
|
11
|
-
__all__ = [
|
|
12
|
-
"router",
|
|
13
|
-
"conbus_blink",
|
|
14
|
-
"conbus_custom",
|
|
15
|
-
"conbus_datapoint",
|
|
16
|
-
"conbus_output",
|
|
17
|
-
]
|
xp/api/routers/conbus.py
DELETED
xp/api/routers/conbus_blink.py
DELETED
|
@@ -1,117 +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.services.conbus.conbus_blink_service import ConbusBlinkService
|
|
14
|
-
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@router.get(
|
|
19
|
-
"/blink/on/{serial_number}",
|
|
20
|
-
response_model=Union[ApiResponse, ApiErrorResponse],
|
|
21
|
-
responses={
|
|
22
|
-
200: {"model": ApiResponse, "description": "Input 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 blink_on(
|
|
29
|
-
request: Request,
|
|
30
|
-
serial_number: str = "1702033007",
|
|
31
|
-
) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
|
|
32
|
-
"""Turn on device blinking.
|
|
33
|
-
|
|
34
|
-
Sends a blink on telegram to make the device blink.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
request: FastAPI request object.
|
|
38
|
-
serial_number: Serial number of the device.
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
API response with blink result or error.
|
|
42
|
-
"""
|
|
43
|
-
service = request.app.state.container.get_container().resolve(ConbusBlinkService)
|
|
44
|
-
|
|
45
|
-
# SendInput telegram and receive responses
|
|
46
|
-
with service:
|
|
47
|
-
response = service.send_blink_telegram(
|
|
48
|
-
serial_number=serial_number, on_or_off="on"
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
if not response.success:
|
|
52
|
-
return handle_service_error(response.error or "Unknown error")
|
|
53
|
-
|
|
54
|
-
logger.debug(json.dumps(response.to_dict(), indent=2))
|
|
55
|
-
|
|
56
|
-
# Build successful response
|
|
57
|
-
return ApiResponse(
|
|
58
|
-
success=True,
|
|
59
|
-
result=response.system_function.name,
|
|
60
|
-
description=(
|
|
61
|
-
response.reply_telegram.system_function.get_description()
|
|
62
|
-
if response.reply_telegram and response.reply_telegram.system_function
|
|
63
|
-
else None
|
|
64
|
-
),
|
|
65
|
-
# raw_telegram = response.output_telegram.raw_telegram,
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@router.get(
|
|
70
|
-
"/blink/off/{serial_number}",
|
|
71
|
-
response_model=Union[ApiResponse, ApiErrorResponse],
|
|
72
|
-
responses={
|
|
73
|
-
200: {"model": ApiResponse, "description": "Input completed successfully"},
|
|
74
|
-
400: {"model": ApiErrorResponse, "description": "Connection or request error"},
|
|
75
|
-
408: {"model": ApiErrorResponse, "description": "Request timeout"},
|
|
76
|
-
500: {"model": ApiErrorResponse, "description": "Internal server error"},
|
|
77
|
-
},
|
|
78
|
-
)
|
|
79
|
-
async def blink_off(
|
|
80
|
-
request: Request,
|
|
81
|
-
serial_number: str = "1702033007",
|
|
82
|
-
) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
|
|
83
|
-
"""Turn off device blinking.
|
|
84
|
-
|
|
85
|
-
Sends a blink off telegram to stop the device from blinking.
|
|
86
|
-
|
|
87
|
-
Args:
|
|
88
|
-
request: FastAPI request object.
|
|
89
|
-
serial_number: Serial number of the device.
|
|
90
|
-
|
|
91
|
-
Returns:
|
|
92
|
-
API response with blink result or error.
|
|
93
|
-
"""
|
|
94
|
-
service = request.app.state.container.get_container().resolve(ConbusBlinkService)
|
|
95
|
-
|
|
96
|
-
# SendInput telegram and receive responses
|
|
97
|
-
with service:
|
|
98
|
-
response = service.send_blink_telegram(
|
|
99
|
-
serial_number=serial_number, on_or_off="off"
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
if not response.success:
|
|
103
|
-
return handle_service_error(response.error or "Unknown error")
|
|
104
|
-
|
|
105
|
-
logger.debug(json.dumps(response.to_dict(), indent=2))
|
|
106
|
-
|
|
107
|
-
# Build successful response
|
|
108
|
-
return ApiResponse(
|
|
109
|
-
success=True,
|
|
110
|
-
result=response.system_function.name,
|
|
111
|
-
description=(
|
|
112
|
-
response.reply_telegram.system_function.get_description()
|
|
113
|
-
if response.reply_telegram and response.reply_telegram.system_function
|
|
114
|
-
else None
|
|
115
|
-
),
|
|
116
|
-
# raw_telegram = response.output_telegram.raw_telegram,
|
|
117
|
-
)
|
xp/api/routers/conbus_custom.py
DELETED
|
@@ -1,71 +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
|
-
"""Execute a custom function on a device.
|
|
34
|
-
|
|
35
|
-
Sends a custom telegram with specified function code and data.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
request: FastAPI request object.
|
|
39
|
-
serial_number: Serial number of the device.
|
|
40
|
-
function_code: Function code to execute.
|
|
41
|
-
data: Data to send with the function.
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
API response with custom function result or error.
|
|
45
|
-
"""
|
|
46
|
-
service = request.app.state.container.get_container().resolve(ConbusCustomService)
|
|
47
|
-
# SendDatapoint telegram and receive responses
|
|
48
|
-
with service:
|
|
49
|
-
response = service.send_custom_telegram(serial_number, function_code, data)
|
|
50
|
-
|
|
51
|
-
if not response.success:
|
|
52
|
-
return handle_service_error(response.error or "Unknown error")
|
|
53
|
-
|
|
54
|
-
if response.reply_telegram is None:
|
|
55
|
-
return ApiErrorResponse(
|
|
56
|
-
success=False,
|
|
57
|
-
error=response.error or "Unknown error",
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
# Build successful response
|
|
61
|
-
if response.reply_telegram and response.reply_telegram.datapoint_type:
|
|
62
|
-
return ApiResponse(
|
|
63
|
-
success=True,
|
|
64
|
-
result=response.reply_telegram.data_value,
|
|
65
|
-
description=response.reply_telegram.datapoint_type.name,
|
|
66
|
-
)
|
|
67
|
-
return ApiResponse(
|
|
68
|
-
success=True,
|
|
69
|
-
result=response.reply_telegram.data_value,
|
|
70
|
-
description="Custom command executed successfully",
|
|
71
|
-
)
|
|
@@ -1,74 +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
|
-
"""Query a datapoint value from a device.
|
|
34
|
-
|
|
35
|
-
Sends a datapoint query telegram and retrieves the requested datapoint value.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
request: FastAPI request object.
|
|
39
|
-
datapoint: Type of datapoint to query (default: SW_VERSION).
|
|
40
|
-
serial_number: Serial number of the device.
|
|
41
|
-
|
|
42
|
-
Returns:
|
|
43
|
-
API response with datapoint value or error.
|
|
44
|
-
"""
|
|
45
|
-
service = request.app.state.container.get_container().resolve(
|
|
46
|
-
ConbusDatapointService
|
|
47
|
-
)
|
|
48
|
-
# SendDatapoint telegram and receive responses
|
|
49
|
-
with service:
|
|
50
|
-
response = service.query_datapoint(
|
|
51
|
-
datapoint_type=datapoint, serial_number=serial_number
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
if not response.success:
|
|
55
|
-
return handle_service_error(response.error or "Unknown error")
|
|
56
|
-
|
|
57
|
-
if response.datapoint_telegram is None:
|
|
58
|
-
return ApiErrorResponse(
|
|
59
|
-
success=False,
|
|
60
|
-
error=response.error or "Unknown error",
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
# Build successful response
|
|
64
|
-
if response.datapoint_telegram and response.datapoint_telegram.datapoint_type:
|
|
65
|
-
return ApiResponse(
|
|
66
|
-
success=True,
|
|
67
|
-
result=response.datapoint_telegram.data_value,
|
|
68
|
-
description=response.datapoint_telegram.datapoint_type.name,
|
|
69
|
-
)
|
|
70
|
-
return ApiResponse(
|
|
71
|
-
success=True,
|
|
72
|
-
result=response.datapoint_telegram.data_value,
|
|
73
|
-
description="Datapoint value retrieved",
|
|
74
|
-
)
|
xp/api/routers/conbus_output.py
DELETED
|
@@ -1,167 +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
|
-
"""Initiate Input operation to find devices on the network.
|
|
36
|
-
|
|
37
|
-
Sends a broadcast Input telegram and collects responses from all connected devices.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
request: FastAPI request object.
|
|
41
|
-
action: Action type to perform (default: OFF_PRESS).
|
|
42
|
-
serial: Serial number of the device.
|
|
43
|
-
device_input: Device input number.
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
API response with operation result or error.
|
|
47
|
-
"""
|
|
48
|
-
service = request.app.state.container.get_container().resolve(ConbusOutputService)
|
|
49
|
-
|
|
50
|
-
# SendInput telegram and receive responses
|
|
51
|
-
with service:
|
|
52
|
-
response = service.send_action(serial, device_input, action)
|
|
53
|
-
|
|
54
|
-
if not response.success:
|
|
55
|
-
return handle_service_error(response.error or "Unknown error")
|
|
56
|
-
|
|
57
|
-
logger.debug(json.dumps(response.to_dict(), indent=2))
|
|
58
|
-
|
|
59
|
-
# Build successful response
|
|
60
|
-
if response.output_telegram and response.output_telegram.system_function:
|
|
61
|
-
return ApiResponse(
|
|
62
|
-
success=True,
|
|
63
|
-
result=response.output_telegram.system_function.name,
|
|
64
|
-
description=response.output_telegram.system_function.get_description(),
|
|
65
|
-
# raw_telegram = response.output_telegram.raw_telegram,
|
|
66
|
-
)
|
|
67
|
-
return ApiResponse(
|
|
68
|
-
success=True,
|
|
69
|
-
result="Output command sent",
|
|
70
|
-
description="Output command was sent successfully",
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@router.get(
|
|
75
|
-
"/output/status/{serial_number}",
|
|
76
|
-
response_model=Union[ApiResponse, ApiErrorResponse],
|
|
77
|
-
responses={
|
|
78
|
-
200: {"model": ApiResponse, "description": "Query completed successfully"},
|
|
79
|
-
400: {"model": ApiErrorResponse, "description": "Connection or request error"},
|
|
80
|
-
408: {"model": ApiErrorResponse, "description": "Request timeout"},
|
|
81
|
-
500: {"model": ApiErrorResponse, "description": "Internal server error"},
|
|
82
|
-
},
|
|
83
|
-
)
|
|
84
|
-
async def output_status(
|
|
85
|
-
request: Request,
|
|
86
|
-
serial_number: str,
|
|
87
|
-
) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
|
|
88
|
-
"""Query output status from a device.
|
|
89
|
-
|
|
90
|
-
Sends a status query telegram and retrieves the output state.
|
|
91
|
-
|
|
92
|
-
Args:
|
|
93
|
-
request: FastAPI request object.
|
|
94
|
-
serial_number: Serial number of the device to query.
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
API response with output status or error.
|
|
98
|
-
"""
|
|
99
|
-
service = request.app.state.container.get_container().resolve(ConbusOutputService)
|
|
100
|
-
|
|
101
|
-
# SendInput telegram and receive responses
|
|
102
|
-
with service:
|
|
103
|
-
response = service.get_output_state(serial_number)
|
|
104
|
-
|
|
105
|
-
if not response.success:
|
|
106
|
-
return handle_service_error(response.error or "Unknown error")
|
|
107
|
-
|
|
108
|
-
# Build successful response
|
|
109
|
-
if response.datapoint_telegram and response.datapoint_telegram.datapoint_type:
|
|
110
|
-
return ApiResponse(
|
|
111
|
-
success=True,
|
|
112
|
-
result=response.datapoint_telegram.data_value,
|
|
113
|
-
description=response.datapoint_telegram.datapoint_type.name,
|
|
114
|
-
)
|
|
115
|
-
return ApiResponse(
|
|
116
|
-
success=True,
|
|
117
|
-
result="No data available",
|
|
118
|
-
description="Output status retrieved but no data available",
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
@router.get(
|
|
123
|
-
"/output/state/{serial_number}",
|
|
124
|
-
response_model=Union[ApiResponse, ApiErrorResponse],
|
|
125
|
-
responses={
|
|
126
|
-
200: {"model": ApiResponse, "description": "Query completed successfully"},
|
|
127
|
-
400: {"model": ApiErrorResponse, "description": "Connection or request error"},
|
|
128
|
-
408: {"model": ApiErrorResponse, "description": "Request timeout"},
|
|
129
|
-
500: {"model": ApiErrorResponse, "description": "Internal server error"},
|
|
130
|
-
},
|
|
131
|
-
)
|
|
132
|
-
async def output_state(
|
|
133
|
-
request: Request,
|
|
134
|
-
serial_number: str,
|
|
135
|
-
) -> Union[ApiResponse, ApiErrorResponse, JSONResponse]:
|
|
136
|
-
"""Query module state from a device.
|
|
137
|
-
|
|
138
|
-
Sends a state query telegram and retrieves the module state.
|
|
139
|
-
|
|
140
|
-
Args:
|
|
141
|
-
request: FastAPI request object.
|
|
142
|
-
serial_number: Serial number of the device to query.
|
|
143
|
-
|
|
144
|
-
Returns:
|
|
145
|
-
API response with module state or error.
|
|
146
|
-
"""
|
|
147
|
-
service = request.app.state.container.get_container().resolve(ConbusOutputService)
|
|
148
|
-
|
|
149
|
-
# SendInput telegram and receive responses
|
|
150
|
-
with service:
|
|
151
|
-
response = service.get_module_state(serial_number)
|
|
152
|
-
|
|
153
|
-
if not response.success:
|
|
154
|
-
return handle_service_error(response.error or "Unknown error")
|
|
155
|
-
|
|
156
|
-
# Build successful response
|
|
157
|
-
if response.datapoint_telegram and response.datapoint_telegram.datapoint_type:
|
|
158
|
-
return ApiResponse(
|
|
159
|
-
success=True,
|
|
160
|
-
result=response.datapoint_telegram.data_value,
|
|
161
|
-
description=response.datapoint_telegram.datapoint_type.name,
|
|
162
|
-
)
|
|
163
|
-
return ApiResponse(
|
|
164
|
-
success=True,
|
|
165
|
-
result="No data available",
|
|
166
|
-
description="Module state retrieved but no data available",
|
|
167
|
-
)
|
xp/api/routers/errors.py
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"""Error handling utilities for API endpoints."""
|
|
2
|
-
|
|
3
|
-
from starlette import status
|
|
4
|
-
from starlette.responses import JSONResponse
|
|
5
|
-
|
|
6
|
-
from xp.api.models.discover import DiscoverErrorResponse
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def handle_service_error(
|
|
10
|
-
error: str, default_status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
11
|
-
) -> JSONResponse:
|
|
12
|
-
"""Handle service errors by creating a standardized JSON error response.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
error: Service response object with success and error attributes.
|
|
16
|
-
default_status_code: HTTP status code to use (defaults to 500).
|
|
17
|
-
|
|
18
|
-
Returns:
|
|
19
|
-
JSONResponse with error details.
|
|
20
|
-
"""
|
|
21
|
-
error_msg = error or "Unknown service error"
|
|
22
|
-
|
|
23
|
-
# Map specific error patterns to appropriate HTTP status codes
|
|
24
|
-
if "Not connected to server" in error_msg:
|
|
25
|
-
status_code = status.HTTP_400_BAD_REQUEST
|
|
26
|
-
elif "Failed to generate telegram" in error_msg:
|
|
27
|
-
status_code = status.HTTP_400_BAD_REQUEST
|
|
28
|
-
elif "Response timeout" in error_msg:
|
|
29
|
-
status_code = status.HTTP_408_REQUEST_TIMEOUT
|
|
30
|
-
elif "Failed to send telegram" in error_msg:
|
|
31
|
-
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
32
|
-
else:
|
|
33
|
-
status_code = default_status_code
|
|
34
|
-
|
|
35
|
-
return JSONResponse(
|
|
36
|
-
status_code=status_code,
|
|
37
|
-
content=DiscoverErrorResponse(error=error_msg).model_dump(),
|
|
38
|
-
)
|