myvsb-developer-api 0.1.2__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.
- myskoda_openapi/api_layer/const.py +3 -0
- myskoda_openapi/api_layer/exceptions.py +37 -0
- myskoda_openapi/api_layer/open_api_client.py +118 -0
- myskoda_openapi/api_layer/rest_api.py +257 -0
- myskoda_openapi/api_layer/utils.py +0 -0
- myskoda_openapi/models/active_ventilation.py +12 -0
- myskoda_openapi/models/air_conditioning.py +23 -0
- myskoda_openapi/models/auxiliary_heating.py +16 -0
- myskoda_openapi/models/base_model.py +13 -0
- myskoda_openapi/models/charging.py +44 -0
- myskoda_openapi/models/common.py +20 -0
- myskoda_openapi/models/driving_range.py +21 -0
- myskoda_openapi/models/enums.py +132 -0
- myskoda_openapi/models/parking_position.py +17 -0
- myskoda_openapi/models/vehicle.py +41 -0
- myskoda_openapi/models/vehicle_status.py +33 -0
- myvsb_developer_api-0.1.2.dist-info/METADATA +24 -0
- myvsb_developer_api-0.1.2.dist-info/RECORD +21 -0
- myvsb_developer_api-0.1.2.dist-info/WHEEL +5 -0
- myvsb_developer_api-0.1.2.dist-info/licenses/LICENSE +21 -0
- myvsb_developer_api-0.1.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OpenApiError(Exception):
|
|
5
|
+
"""Obecná chyba při komunikaci se Škoda API."""
|
|
6
|
+
def __init__(self, message: str, status_code: Optional[int] = None) -> None:
|
|
7
|
+
super().__init__(message)
|
|
8
|
+
self.status_code = status_code
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OpenApiAuthenticationError(OpenApiError):
|
|
12
|
+
"""Error 401 - Invalid or Expired X-API-Key."""
|
|
13
|
+
def __init__(self, message: str = "Invalid or expired X-API-Key.") -> None:
|
|
14
|
+
super().__init__(message, status_code=401)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class OpenApiForbiddenError(OpenApiError):
|
|
18
|
+
"""Error 403 - Forbidden access to the requested resource."""
|
|
19
|
+
def __init__(self, message: str = "Access to the requested resource is forbidden.") -> None:
|
|
20
|
+
super().__init__(message, status_code=403)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class OpenApiVehicleNotFoundError(OpenApiError):
|
|
24
|
+
"""Error 404 - Vehicle with this VIN does not exist."""
|
|
25
|
+
def __init__(self, message: str = "Vehicle with this VIN does not exist.") -> None:
|
|
26
|
+
super().__init__(message, status_code=404)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class OpenApiRateLimitError(OpenApiError):
|
|
30
|
+
"""Error 429 - Rate limit exceeded or weak 12V battery."""
|
|
31
|
+
def __init__(self, message: str = "Rate limit exceeded or weak 12V battery.") -> None:
|
|
32
|
+
super().__init__(message, status_code=429)
|
|
33
|
+
|
|
34
|
+
class OpenApiServerError(OpenApiError):
|
|
35
|
+
"""Error 500 / 504 - Server error or timeout."""
|
|
36
|
+
def __init__(self, message: str, status_code: int = 500) -> None:
|
|
37
|
+
super().__init__(message, status_code=status_code)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Optional, List
|
|
3
|
+
from aiohttp import ClientSession
|
|
4
|
+
from .rest_api import SkodaRestAPI
|
|
5
|
+
from models.vehicle import VehicleResponse
|
|
6
|
+
from models.vehicle_status import VehicleStatus
|
|
7
|
+
|
|
8
|
+
class OpenAPIClient:
|
|
9
|
+
"""Client for interacting with rest API and used in HomeAssistant."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, api_key: str, session: ClientSession) -> None:
|
|
12
|
+
self.rest_api = SkodaRestAPI(api_key=api_key, session=session)
|
|
13
|
+
|
|
14
|
+
async def get_vehicle(self, vin: str, include: Optional[List[str]] = None) -> VehicleResponse:
|
|
15
|
+
"""Get the actual vehicle data and return the parsed Pydantic model."""
|
|
16
|
+
endpoint_result = await self.rest_api.get_vehicle(vin, include=include)
|
|
17
|
+
return endpoint_result.result
|
|
18
|
+
|
|
19
|
+
async def get_vehicle_status(self, vin: str, include: Optional[List[str]] = None) -> VehicleStatus:
|
|
20
|
+
"""Get the actual vehicle status and return the parsed Pydantic model."""
|
|
21
|
+
endpoint_result = await self.rest_api.get_vehicle_status(vin, include=include)
|
|
22
|
+
return endpoint_result.result
|
|
23
|
+
|
|
24
|
+
async def start_air_conditioning(self, vin: str, temperature: float) -> None:
|
|
25
|
+
"""Starts the air conditioning for the vehicle with the given VIN."""
|
|
26
|
+
payload = {"targetTemperature": temperature}
|
|
27
|
+
await self.rest_api.start_air_conditioning(vin, payload=payload)
|
|
28
|
+
|
|
29
|
+
async def stop_air_conditioning(self, vin: str) -> None:
|
|
30
|
+
"""Stops the air_conditioning of the vehicle and return None if it was succesfull else catch an exception in HomeAssistant"""
|
|
31
|
+
await self.rest_api.stop_air_conditioning(vin)
|
|
32
|
+
|
|
33
|
+
async def start_auxiliary_heating(self, vin: str, spin: str, duration_in_seconds: int = 120, start_mode: str = "HEATING", target_temperature: Optional[float] = None, unit: str = "CELSIUS") -> None:
|
|
34
|
+
"""Starts the auxiliary heating for the vehicle with the given VIN."""
|
|
35
|
+
payload = {
|
|
36
|
+
"spin": spin,
|
|
37
|
+
"durationInSeconds": duration_in_seconds,
|
|
38
|
+
"startMode": start_mode
|
|
39
|
+
}
|
|
40
|
+
# If there is target temperature, add it into the payload
|
|
41
|
+
if target_temperature is not None:
|
|
42
|
+
payload["targetTemperature"] = {
|
|
43
|
+
"value": target_temperature,
|
|
44
|
+
"unit": unit
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await self.rest_api.start_auxiliary_heating(vin, payload=payload)
|
|
48
|
+
|
|
49
|
+
async def stop_auxiliary_heating(self, vin: str) -> None:
|
|
50
|
+
"""Stops the auxiliary_heating of the vehicle and return None if it was succesfull else catch an exception in HomeAssistant"""
|
|
51
|
+
await self.rest_api.stop_auxiliary_heating(vin)
|
|
52
|
+
|
|
53
|
+
async def start_active_ventilation(self, vin: str) -> None:
|
|
54
|
+
"""Starts the active_ventilation for the vehicle with the given VIN."""
|
|
55
|
+
await self.rest_api.start_active_ventilation(vin)
|
|
56
|
+
|
|
57
|
+
async def stop_active_ventilation(self, vin: str) -> None:
|
|
58
|
+
"""Stops the active_ventilation of the vehicle and return None if it was succesfull else catch an exception in HomeAssistant"""
|
|
59
|
+
await self.rest_api.stop_active_ventilation(vin)
|
|
60
|
+
|
|
61
|
+
async def start_charging(self, vin: str) -> None:
|
|
62
|
+
"""Starts the charging process for the vehicle with the given VIN."""
|
|
63
|
+
await self.rest_api.start_charging(vin)
|
|
64
|
+
|
|
65
|
+
async def stop_charging(self, vin: str) -> None:
|
|
66
|
+
"""Stops the charging process for the vehicle with the given VIN."""
|
|
67
|
+
await self.rest_api.stop_charging(vin)
|
|
68
|
+
|
|
69
|
+
async def connect(self):
|
|
70
|
+
"""TODO: Implementation of connect mechanism."""
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
async def disconnect(self) -> None:
|
|
74
|
+
"""TODO: Implementation of disconnect mechanism."""
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
async def verify_spin(self, spin: str) -> bool:
|
|
78
|
+
"""TODO: Implementation of SPIN verification."""
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
======= NOT IMPLEMENTED FUNCTIONS - CAN BE USED SOMETIME IN THE FUTURE =======
|
|
83
|
+
"""
|
|
84
|
+
async def refresh_auxiliary_heating(self, vin: str) -> None:
|
|
85
|
+
"""Refreshes the auxiliary heating status of the vehicle with the given VIN."""
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
async def set_reduced_current_limit(self, vin: str) -> None:
|
|
89
|
+
"""Sets the charging current limit (e.g., to REDUCED or MAXIMUM) for the vehicle with the given VIN."""
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
async def set_seats_heating(self, vin: str) -> None:
|
|
93
|
+
"""Configures the seats heating settings for the vehicle with the given VIN."""
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
async def start_window_heating(self, vin: str) -> None:
|
|
97
|
+
"""Starts the electric window heating for the vehicle with the given VIN."""
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
async def stop_window_heating(self, vin: str) -> None:
|
|
101
|
+
"""Stops the window heating of the vehicle and return None if it was succesfull else catch an exception in HomeAssistant."""
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
async def set_windows_heating(self, vin: str) -> None:
|
|
105
|
+
"""Configures the front and rear windows heating settings for the vehicle with the given VIN."""
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
async def set_auto_unlock_plug(self, vin: str) -> None:
|
|
109
|
+
"""Sets the automatic unlocking behavior of the charging plug (e.g., PERMANENT or OFF) for the vehicle with the given VIN."""
|
|
110
|
+
pass
|
|
111
|
+
async def set_charge_limit(self, vin: str, limit: int) -> None:
|
|
112
|
+
"""Sets the maximum charging limit in percent for the vehicle with the given VIN."""
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
async def set_target_temperature(self, vin: str, temperature: float) -> None:
|
|
116
|
+
"""Sets the target temperature for AC or auxiliary heating for the vehicle with the given VIN."""
|
|
117
|
+
pass
|
|
118
|
+
# TODO: Add more methods for other endpoints as needed
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Generic, Optional, TypeVar, Any, List
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from aiohttp import ClientResponseError, ClientSession
|
|
6
|
+
from .const import SANDBOX_BASE_URL, PRODUCTION_BASE_URL, TEST_BASE_URL
|
|
7
|
+
from models.vehicle import VehicleResponse
|
|
8
|
+
from models.vehicle_status import VehicleStatus
|
|
9
|
+
from models.vehicle import Odometer
|
|
10
|
+
from models.air_conditioning import AirConditioning
|
|
11
|
+
from models.parking_position import ParkingPosition
|
|
12
|
+
from models.auxiliary_heating import AuxiliaryHeating
|
|
13
|
+
from models.charging import Charging
|
|
14
|
+
from models.active_ventilation import ActiveVentilation
|
|
15
|
+
from .exceptions import (
|
|
16
|
+
OpenApiError,
|
|
17
|
+
OpenApiAuthenticationError,
|
|
18
|
+
OpenApiForbiddenError,
|
|
19
|
+
OpenApiVehicleNotFoundError,
|
|
20
|
+
OpenApiRateLimitError,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
T = TypeVar("T")
|
|
24
|
+
_LOGGER = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
class GetEndpointResult(Generic[T]):
|
|
27
|
+
"""Wrapper for the result of a GET request to an endpoint, containing the URL, raw JSON data, and the parsed Pydantic model."""
|
|
28
|
+
def __init__(self, url: str, raw_json: Any, result: T) -> None:
|
|
29
|
+
self.url = url
|
|
30
|
+
self.raw_json = raw_json
|
|
31
|
+
self.result = result # Final Pydantic model after parsing raw Json.
|
|
32
|
+
|
|
33
|
+
class PostEndpointResult:
|
|
34
|
+
"""Wrapper for the result of a POST request to an endpoint, containing the URL, status code, and headers."""
|
|
35
|
+
def __init__(self, url: str, status: int, headers: dict) -> None:
|
|
36
|
+
self.url = url
|
|
37
|
+
self.status = status
|
|
38
|
+
self.headers = headers # Užitečné např. pro sledování RateLimit-Remaining nebo X-API-Key-Expires-At
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SkodaRestAPI:
|
|
42
|
+
"""Rest API client for interacting with the Škoda Open API."""
|
|
43
|
+
|
|
44
|
+
def __init__(self, api_key: str, session: ClientSession) -> None:
|
|
45
|
+
self._api_key = api_key
|
|
46
|
+
self._session = session
|
|
47
|
+
self._base_url = TEST_BASE_URL # Default to TEST_BASE_URL; can be changed to SANDBOX_BASE_URL or PRODUCTION_BASE_URL as needed.
|
|
48
|
+
#TODO will be exteded with methods for authorization, token management, and other endpoints as needed.
|
|
49
|
+
|
|
50
|
+
async def _make_post_request(self, url: str, json_data: Optional[dict] = None) -> int:
|
|
51
|
+
"""Helper method to make POST requests to the Škoda API."""
|
|
52
|
+
full_url = f"{self._base_url}{url}"
|
|
53
|
+
headers = {
|
|
54
|
+
"X-API-Key": self._api_key,
|
|
55
|
+
"Accept": "application/json",
|
|
56
|
+
"Content-Type": "application/json",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
async with self._session.post(full_url, headers=headers, json=json_data) as response:
|
|
61
|
+
if response.status not in (200, 202):
|
|
62
|
+
match response.status:
|
|
63
|
+
case 401: raise OpenApiAuthenticationError("Invalid or expired X-API-Key.")
|
|
64
|
+
case 403: raise OpenApiForbiddenError("Access forbidden. User not allowed to execute operation.")
|
|
65
|
+
case 404: raise OpenApiVehicleNotFoundError("Vehicle with this VIN does not exist.")
|
|
66
|
+
case 429: raise OpenApiRateLimitError("Rate limit exceeded or insufficient battery level.")
|
|
67
|
+
case 400: raise OpenApiError("Bad request (e.g. validation or VIN issues).")
|
|
68
|
+
|
|
69
|
+
return response.status
|
|
70
|
+
except Exception as err:
|
|
71
|
+
if isinstance(err, OpenApiError):
|
|
72
|
+
raise
|
|
73
|
+
raise OpenApiError(f"There has been an error when trying to make the POST request: {err}") from err
|
|
74
|
+
|
|
75
|
+
async def _make_get_request(self, url: str, params: Optional[dict] = None) -> Any:
|
|
76
|
+
"""Helper method to make GET requests to the Škoda API."""
|
|
77
|
+
full_url = f"{self._base_url}{url}"
|
|
78
|
+
headers = {
|
|
79
|
+
"X-API-Key": self._api_key,
|
|
80
|
+
"Accept": "application/json",
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
async with self._session.get(full_url, headers=headers, params=params) as response:
|
|
85
|
+
if response.status not in (200, 202):
|
|
86
|
+
match response.status:
|
|
87
|
+
case 401: raise OpenApiAuthenticationError("Invalid or expired X-API-Key.")
|
|
88
|
+
case 403: raise OpenApiForbiddenError("Access to the requested resource is forbidden.")
|
|
89
|
+
case 404: raise OpenApiVehicleNotFoundError("Vehicle with this VIN does not exist.")
|
|
90
|
+
case 429: raise OpenApiRateLimitError("Rate limit exceeded or weak 12V battery.")
|
|
91
|
+
return await response.json()
|
|
92
|
+
except Exception as err:
|
|
93
|
+
raise OpenApiError(f"There has been an error when trying to make the request: {err}") from err
|
|
94
|
+
|
|
95
|
+
# =========================================================================
|
|
96
|
+
# ENDPOINT METHODS - GET
|
|
97
|
+
# =========================================================================
|
|
98
|
+
async def get_vehicle(self, vin: str, include: Optional[List[str]] = None) -> GetEndpointResult[VehicleResponse]:
|
|
99
|
+
"""Call the GET /api/v1/vehicles/{vin} endpoint to retrieve vehicle data for a given VIN."""
|
|
100
|
+
url = f"/api/v1/vehicles/{vin}"
|
|
101
|
+
params = {}
|
|
102
|
+
if include:
|
|
103
|
+
params["include"] = ",".join(include)
|
|
104
|
+
|
|
105
|
+
raw_json = await self._make_get_request(url, params=params)
|
|
106
|
+
|
|
107
|
+
# Deserializace raw_json into Pydantic model VehicleResponse
|
|
108
|
+
full_response = VehicleResponse.model_validate(raw_json)
|
|
109
|
+
|
|
110
|
+
# Pack and retunr the result in GetEndpointResult
|
|
111
|
+
return GetEndpointResult(url=url, raw_json=raw_json, result=full_response)
|
|
112
|
+
|
|
113
|
+
async def get_vehicle_status(self, vin: str, include: Optional[List[str]] = None) -> GetEndpointResult[Optional[VehicleStatus]]:
|
|
114
|
+
"""Retrieve ONLY the vehicle status by filtering via the 'include' parameter."""
|
|
115
|
+
url = f"/api/v1/vehicles/{vin}"
|
|
116
|
+
# Set the 'include' parameter to 'status' to retrieve only the vehicle status
|
|
117
|
+
params = {"include": "status"}
|
|
118
|
+
|
|
119
|
+
raw_json = await self._make_get_request(url, params=params)
|
|
120
|
+
# Parse whole response into VehicleResponse model
|
|
121
|
+
full_response = VehicleResponse.model_validate(raw_json)
|
|
122
|
+
status_model = full_response.vehicle.status
|
|
123
|
+
|
|
124
|
+
return GetEndpointResult(url=url, raw_json=raw_json, result=status_model)
|
|
125
|
+
|
|
126
|
+
async def get_air_conditioning(self, vin: str, include: Optional[List[str]] = None) -> GetEndpointResult[Optional[AirConditioning]]:
|
|
127
|
+
"""Retrieve ONLY the vehicles air conditioning object by filtering via the 'include' parameter."""
|
|
128
|
+
url = f"/api/v1/vehicles/{vin}"
|
|
129
|
+
params = {"include": "airConditioning"}
|
|
130
|
+
|
|
131
|
+
raw_json = await self._make_get_request(url, params=params)
|
|
132
|
+
|
|
133
|
+
full_response = VehicleResponse.model_validate(raw_json)
|
|
134
|
+
status_model = full_response.vehicle.air_conditioning
|
|
135
|
+
|
|
136
|
+
return GetEndpointResult(url=url, raw_json=raw_json, result=status_model)
|
|
137
|
+
|
|
138
|
+
async def get_parking_positions(self, vin: str, include: Optional[List[str]] = None) -> GetEndpointResult[Optional[ParkingPosition]]:
|
|
139
|
+
"""Retrieve ONLY the vehicle parking positions object by filtering via the 'include' parameter."""
|
|
140
|
+
url = f"/api/v1/vehicles/{vin}"
|
|
141
|
+
params = {"include": "parkingPosition"}
|
|
142
|
+
|
|
143
|
+
raw_json = await self._make_get_request(url, params=params)
|
|
144
|
+
|
|
145
|
+
full_response = VehicleResponse.model_validate(raw_json)
|
|
146
|
+
status_model = full_response.vehicle.parking_position
|
|
147
|
+
|
|
148
|
+
return GetEndpointResult(url=url, raw_json=raw_json, result=status_model)
|
|
149
|
+
|
|
150
|
+
async def get_auxiliary_heating(self, vin: str, include: Optional[List[str]] = None) -> GetEndpointResult[Optional[AuxiliaryHeating]]:
|
|
151
|
+
"""Retrieve ONLY the vehicle auxiliary heating by filtering via the 'include' parameter."""
|
|
152
|
+
url = f"/api/v1/vehicles/{vin}"
|
|
153
|
+
params = {"include": "auxiliaryHeating"}
|
|
154
|
+
|
|
155
|
+
raw_json = await self._make_get_request(url, params=params)
|
|
156
|
+
|
|
157
|
+
full_response = VehicleResponse.model_validate(raw_json)
|
|
158
|
+
status_model = full_response.vehicle.auxiliary_heating
|
|
159
|
+
|
|
160
|
+
return GetEndpointResult(url=url, raw_json=raw_json, result=status_model)
|
|
161
|
+
|
|
162
|
+
async def get_odometer(self, vin: str, include: Optional[List[str]] = None) -> GetEndpointResult[Optional[Odometer]]:
|
|
163
|
+
"""Retrieve ONLY the vehicle odometer object by filtering via the 'include' parameter."""
|
|
164
|
+
url = f"/api/v1/vehicles/{vin}"
|
|
165
|
+
params = {"include": "odometer"}
|
|
166
|
+
|
|
167
|
+
raw_json = await self._make_get_request(url, params=params)
|
|
168
|
+
|
|
169
|
+
full_response = VehicleResponse.model_validate(raw_json)
|
|
170
|
+
status_model = full_response.vehicle.odometer
|
|
171
|
+
|
|
172
|
+
return GetEndpointResult(url=url, raw_json=raw_json, result=status_model)
|
|
173
|
+
|
|
174
|
+
async def get_charging(self, vin: str, include: Optional[List[str]] = None) -> GetEndpointResult[Optional[Charging]]:
|
|
175
|
+
"""Retrieve ONLY the vehicle charging object by filtering via the 'include' parameter."""
|
|
176
|
+
url = f"/api/v1/vehicles/{vin}"
|
|
177
|
+
params = {"include": "charging"}
|
|
178
|
+
|
|
179
|
+
raw_json = await self._make_get_request(url, params=params)
|
|
180
|
+
|
|
181
|
+
full_response = VehicleResponse.model_validate(raw_json)
|
|
182
|
+
status_model = full_response.vehicle.charging
|
|
183
|
+
|
|
184
|
+
return GetEndpointResult(url=url, raw_json=raw_json, result=status_model)
|
|
185
|
+
|
|
186
|
+
async def get_active_ventilation(self, vin: str, include: Optional[List[str]] = None) -> GetEndpointResult[Optional[ActiveVentilation]]:
|
|
187
|
+
"""Retrieve ONLY the vehicle active ventilation object by filtering via the 'include' parameter."""
|
|
188
|
+
url = f"/api/v1/vehicles/{vin}"
|
|
189
|
+
params = {"include": "activeVentilation"}
|
|
190
|
+
|
|
191
|
+
raw_json = await self._make_get_request(url, params=params)
|
|
192
|
+
|
|
193
|
+
full_response = VehicleResponse.model_validate(raw_json)
|
|
194
|
+
status_model = full_response.vehicle.active_ventilation
|
|
195
|
+
|
|
196
|
+
return GetEndpointResult(url=url, raw_json=raw_json, result=status_model)
|
|
197
|
+
|
|
198
|
+
# =========================================================================
|
|
199
|
+
# ENDPOINT METHODS - POST
|
|
200
|
+
# =========================================================================
|
|
201
|
+
async def start_air_conditioning(self, vin: str, payload: dict) -> PostEndpointResult:
|
|
202
|
+
"""Starts the air conditioning for the vehicle with the given VIN. The payload should contain any necessary parameters for starting the air conditioning."""
|
|
203
|
+
url = f"/api/v1/vehicles/{vin}/air-conditioning/start"
|
|
204
|
+
|
|
205
|
+
status = await self._make_post_request(url, json_data=payload)
|
|
206
|
+
return PostEndpointResult(url=url, status=status, headers={})
|
|
207
|
+
|
|
208
|
+
async def stop_air_conditioning(self, vin: str) -> PostEndpointResult:
|
|
209
|
+
"""Stops the air_conditioning for the given VIN"""
|
|
210
|
+
url = f"/api/v1/vehicles/{vin}/air-conditioning/stop"
|
|
211
|
+
|
|
212
|
+
status = await self._make_post_request(url)
|
|
213
|
+
return PostEndpointResult(url=url, status=status, headers={})
|
|
214
|
+
|
|
215
|
+
async def start_auxiliary_heating(self, vin: str, payload: dict) -> PostEndpointResult:
|
|
216
|
+
"""Starts the auxiliary heating for the vehicle with the given VIN. The payload should contain any necessary parameters for starting the auxiliary heating."""
|
|
217
|
+
url = f"/api/v1/vehicles/{vin}/auxiliary-heating/start"
|
|
218
|
+
|
|
219
|
+
status = await self._make_post_request(url, json_data=payload)
|
|
220
|
+
return PostEndpointResult(url=url, status=status, headers={})
|
|
221
|
+
|
|
222
|
+
async def stop_auxiliary_heating(self, vin: str) -> PostEndpointResult:
|
|
223
|
+
"""Stops the auxiliary heating for the given VIN"""
|
|
224
|
+
url = f"/api/v1/vehicles/{vin}/auxiliary-heating/stop"
|
|
225
|
+
|
|
226
|
+
status = await self._make_post_request(url)
|
|
227
|
+
return PostEndpointResult(url=url, status=status, headers={})
|
|
228
|
+
|
|
229
|
+
async def start_active_ventilation(self, vin: str, payload: dict) -> PostEndpointResult:
|
|
230
|
+
"""Starts the active ventilation for the vehicle with the given VIN. The payload should contain any necessary parameters for starting the active ventilation."""
|
|
231
|
+
url = f"/api/v1/vehicles/{vin}/active-ventilation/start"
|
|
232
|
+
|
|
233
|
+
status = await self._make_post_request(url, json_data=payload)
|
|
234
|
+
return PostEndpointResult(url=url, status=status, headers={})
|
|
235
|
+
|
|
236
|
+
async def stop_active_ventilation(self, vin: str) -> PostEndpointResult:
|
|
237
|
+
"""Stops the active ventilation for the given VIN"""
|
|
238
|
+
url = f"/api/v1/vehicles/{vin}/active-ventilation/stop"
|
|
239
|
+
|
|
240
|
+
status = await self._make_post_request(url)
|
|
241
|
+
return PostEndpointResult(url=url, status=status, headers={})
|
|
242
|
+
|
|
243
|
+
async def start_charging(self, vin: str) -> PostEndpointResult:
|
|
244
|
+
"""Starts the charging process for the vehicle with the given VIN."""
|
|
245
|
+
url = f"/api/v1/vehicles/{vin}/charging/start"
|
|
246
|
+
|
|
247
|
+
status = await self._make_post_request(url)
|
|
248
|
+
return PostEndpointResult(url=url, status=status, headers={})
|
|
249
|
+
|
|
250
|
+
async def stop_charging(self, vin: str) -> PostEndpointResult:
|
|
251
|
+
"""Stops the charging process for the given VIN"""
|
|
252
|
+
url = f"/api/v1/vehicles/{vin}/charging/stop"
|
|
253
|
+
|
|
254
|
+
status = await self._make_post_request(url)
|
|
255
|
+
return PostEndpointResult(url=url, status=status, headers={})
|
|
256
|
+
|
|
257
|
+
#TODO rest of the methods
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from .base_model import BaseModel
|
|
3
|
+
from .common import TargetTemperature
|
|
4
|
+
from .enums import (
|
|
5
|
+
VentilationState
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
class ActiveVentilation(BaseModel):
|
|
9
|
+
"""Active ventilation status."""
|
|
10
|
+
state: VentilationState
|
|
11
|
+
duration_in_seconds: Optional[int] = None
|
|
12
|
+
car_captured_timestamp: Optional[str] = None
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from .base_model import BaseModel
|
|
4
|
+
from .common import TargetTemperature
|
|
5
|
+
from .enums import OnOffState, AirConditioningState
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WindowHeating(BaseModel):
|
|
9
|
+
"""State of the electric window heating."""
|
|
10
|
+
enabled: Optional[bool] = None
|
|
11
|
+
front: Optional[OnOffState] = None
|
|
12
|
+
rear: Optional[OnOffState] = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AirConditioning(BaseModel):
|
|
16
|
+
"""Information about the vehicle's air conditioning."""
|
|
17
|
+
state: AirConditioningState
|
|
18
|
+
target_temperature: Optional[TargetTemperature] = None
|
|
19
|
+
estimated_reach_of_target_temperature_at: Optional[str] = None
|
|
20
|
+
air_conditioning_without_external_power: Optional[bool] = None
|
|
21
|
+
air_conditioning_at_unlock: Optional[bool] = None
|
|
22
|
+
window_heating: Optional[WindowHeating] = None
|
|
23
|
+
car_captured_timestamp: Optional[str] = None
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from .base_model import BaseModel
|
|
3
|
+
from .common import TargetTemperature
|
|
4
|
+
from .enums import (
|
|
5
|
+
AuxiliaryHeatingState,
|
|
6
|
+
AuxiliaryHeatingStartMode,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
class AuxiliaryHeating(BaseModel):
|
|
10
|
+
"""Auxiliary heating status."""
|
|
11
|
+
state: AuxiliaryHeatingState
|
|
12
|
+
start_mode: Optional[AuxiliaryHeatingStartMode] = None
|
|
13
|
+
duration_in_seconds: Optional[int] = None
|
|
14
|
+
target_temperature: Optional[TargetTemperature] = None
|
|
15
|
+
estimated_reach_of_target_temperature_at: Optional[str] = None
|
|
16
|
+
car_captured_timestamp: Optional[str] = None
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from pydantic import BaseModel, ConfigDict
|
|
2
|
+
|
|
3
|
+
def to_camel_case(snake_str: str) -> str:
|
|
4
|
+
words = snake_str.split('_')
|
|
5
|
+
return words[0] + ''.join(x.title() for x in words[1:])
|
|
6
|
+
|
|
7
|
+
class BaseModel(BaseModel):
|
|
8
|
+
model_config = ConfigDict(
|
|
9
|
+
alias_generator=to_camel_case,
|
|
10
|
+
populate_by_name=True,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import Optional, List
|
|
2
|
+
|
|
3
|
+
from .base_model import BaseModel
|
|
4
|
+
from .enums import (
|
|
5
|
+
ChargingState,
|
|
6
|
+
ChargeType,
|
|
7
|
+
ChargeMode,
|
|
8
|
+
ChargeCareModeState,
|
|
9
|
+
AutoUnlockPlugState,
|
|
10
|
+
MaxChargeCurrentAcState
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
class BatteryStatus(BaseModel):
|
|
14
|
+
"""Battery status information."""
|
|
15
|
+
remaining_cruising_range_in_meters: Optional[int] = None
|
|
16
|
+
state_of_charge_in_percent: Optional[int] = None
|
|
17
|
+
|
|
18
|
+
class ChargingStatus(BaseModel):
|
|
19
|
+
"""Charging status information."""
|
|
20
|
+
charging_rate_in_kilometers_per_hour: Optional[float] = None
|
|
21
|
+
charge_power_in_kw: Optional[float] = None
|
|
22
|
+
remaining_time_to_fully_charged_in_minutes: Optional[int] = None
|
|
23
|
+
fully_charged_at: Optional[str] = None
|
|
24
|
+
state: Optional[ChargingState] = None
|
|
25
|
+
charge_type: Optional[ChargeType] = None
|
|
26
|
+
battery: Optional[BatteryStatus] = None
|
|
27
|
+
|
|
28
|
+
class ChargingSettings(BaseModel):
|
|
29
|
+
"""Information about charging settings."""
|
|
30
|
+
target_state_of_charge_in_percent: Optional[int] = None
|
|
31
|
+
battery_care_mode_target_value_in_percent: Optional[int] = None
|
|
32
|
+
preferred_charge_mode: Optional[ChargeMode] = None
|
|
33
|
+
available_charge_modes: Optional[List[ChargeMode]] = None
|
|
34
|
+
charging_care_mode: Optional[ChargeCareModeState] = None
|
|
35
|
+
auto_unlock_plug_when_charged: Optional[AutoUnlockPlugState] = None
|
|
36
|
+
max_charge_current_ac: Optional[MaxChargeCurrentAcState] = None
|
|
37
|
+
max_charge_current_ac_ampere: Optional[int] = None
|
|
38
|
+
|
|
39
|
+
class Charging(BaseModel):
|
|
40
|
+
"""Charging represents information about charging and battery settings."""
|
|
41
|
+
is_vehicle_in_saved_location: bool
|
|
42
|
+
status: Optional[ChargingStatus] = None
|
|
43
|
+
settings: Optional[ChargingSettings] = None
|
|
44
|
+
car_captured_timestamp: Optional[str] = None
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from .base_model import BaseModel
|
|
6
|
+
from .enums import (
|
|
7
|
+
TemperatureUnit,
|
|
8
|
+
VehicleErrorState
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TargetTemperature(BaseModel):
|
|
13
|
+
"""Target temperature settings for the vehicle."""
|
|
14
|
+
value: float
|
|
15
|
+
unit: TemperatureUnit
|
|
16
|
+
|
|
17
|
+
class VehicleError(BaseModel):
|
|
18
|
+
"""Error information related to the vehicle."""
|
|
19
|
+
type: VehicleErrorState
|
|
20
|
+
description: str
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from .base_model import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class EngineRange(BaseModel):
|
|
7
|
+
"""Details of the vehicle's engine range."""
|
|
8
|
+
engine_type: Optional[str] = None
|
|
9
|
+
current_soc_in_percent: Optional[int] = None
|
|
10
|
+
current_fuel_level_in_percent: Optional[int] = None
|
|
11
|
+
remaining_range_in_km: Optional[int] = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DrivingRange(BaseModel):
|
|
15
|
+
"""Details of the vehicle's fuel status and driving range."""
|
|
16
|
+
car_type: Optional[str] = None
|
|
17
|
+
ad_blue_range: Optional[int] = None
|
|
18
|
+
total_range_in_km: Optional[int] = None
|
|
19
|
+
primary_engine_range: Optional[EngineRange] = None
|
|
20
|
+
secondary_engine_range: Optional[EngineRange] = None
|
|
21
|
+
car_captured_timestamp: Optional[str] = None
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
class TemperatureUnit(str, Enum):
|
|
4
|
+
CELSIUS = "CELSIUS"
|
|
5
|
+
FAHRENHEIT = "FAHRENHEIT"
|
|
6
|
+
UNKNOWN = "UNKNOWN"
|
|
7
|
+
|
|
8
|
+
class OnOffState(str, Enum):
|
|
9
|
+
ON = "ON"
|
|
10
|
+
OFF = "OFF"
|
|
11
|
+
UNKNOWN = "UNKNOWN"
|
|
12
|
+
UNSUPPORTED = "UNSUPPORTED"
|
|
13
|
+
|
|
14
|
+
class LockState(str, Enum):
|
|
15
|
+
LOCKED = "LOCKED"
|
|
16
|
+
UNLOCKED = "UNLOCKED"
|
|
17
|
+
UNKNOWN = "UNKNOWN"
|
|
18
|
+
|
|
19
|
+
class OpenCloseState(str, Enum):
|
|
20
|
+
OPEN = "OPEN"
|
|
21
|
+
CLOSED = "CLOSED"
|
|
22
|
+
UNKNOWN = "UNKNOWN"
|
|
23
|
+
UNSUPPORTED = "UNSUPPORTED"
|
|
24
|
+
|
|
25
|
+
class YesNoState(str, Enum):
|
|
26
|
+
YES = "YES"
|
|
27
|
+
NO = "NO"
|
|
28
|
+
UNKNOWN = "UNKNOWN"
|
|
29
|
+
|
|
30
|
+
class DoorsState(str, Enum):
|
|
31
|
+
YES = "YES"
|
|
32
|
+
NO = "NO"
|
|
33
|
+
TRUNK_OPENED = "TRUNK_OPENED"
|
|
34
|
+
OPENED = "OPENED"
|
|
35
|
+
UNKNOWN = "UNKNOWN"
|
|
36
|
+
|
|
37
|
+
class AuxiliaryHeatingState(str, Enum):
|
|
38
|
+
HEATING = "HEATING"
|
|
39
|
+
VENTILATION = "VENTILATION"
|
|
40
|
+
OFF = "OFF"
|
|
41
|
+
HEATING_AUXILIARY = "HEATING_AUXILIARY"
|
|
42
|
+
UNKNOWN = "UNKNOWN"
|
|
43
|
+
UNSUPPORTED = "UNSUPPORTED"
|
|
44
|
+
|
|
45
|
+
class AuxiliaryHeatingStartMode(str, Enum):
|
|
46
|
+
HEATING = "HEATING"
|
|
47
|
+
VENTILATION = "VENTILATION"
|
|
48
|
+
|
|
49
|
+
class VentilationState(str, Enum):
|
|
50
|
+
VENTILATION = "VENTILATION"
|
|
51
|
+
PREHEATING = "PREHEATING"
|
|
52
|
+
OFF = "OFF"
|
|
53
|
+
UNKNOWN = "UNKNOWN"
|
|
54
|
+
UNSUPPORTED = "UNSUPPORTED"
|
|
55
|
+
|
|
56
|
+
class HeaterSourceState(str, Enum):
|
|
57
|
+
AUTOMATIC = "AUTOMATIC"
|
|
58
|
+
ELECTRIC = "ELECTRIC"
|
|
59
|
+
|
|
60
|
+
class ChargeType(str, Enum):
|
|
61
|
+
AC = "AC"
|
|
62
|
+
DC = "DC"
|
|
63
|
+
OFF = "OFF"
|
|
64
|
+
|
|
65
|
+
class ChargingState(str, Enum):
|
|
66
|
+
CONNECT_CABLE = "CONNECT_CABLE"
|
|
67
|
+
CHARGING = "CHARGING"
|
|
68
|
+
CONSERVING = "CONSERVING"
|
|
69
|
+
READY_FOR_CHARGING = "READY_FOR_CHARGING"
|
|
70
|
+
DISCHARGING = "DISCHARGING"
|
|
71
|
+
CHARGING_INTERRUPTED = "CHARGING_INTERRUPTED"
|
|
72
|
+
|
|
73
|
+
class ChargeMode(str, Enum):
|
|
74
|
+
MANUAL = "MANUAL"
|
|
75
|
+
TIMER = "TIMER"
|
|
76
|
+
TIMER_CHARGING_WITH_CLIMATISATION = "TIMER_CHARGING_WITH_CLIMATISATION"
|
|
77
|
+
PREFERRED_CHARGING_TIMES = "PREFERRED_CHARGING_TIMES"
|
|
78
|
+
ONLY_OWN_CURRENT = "ONLY_OWN_CURRENT"
|
|
79
|
+
IMMEDIATE_DISCHARGING = "IMMEDIATE_DISCHARGING"
|
|
80
|
+
HOME_STORAGE_CHARGING = "HOME_STORAGE_CHARGING"
|
|
81
|
+
|
|
82
|
+
class ChargeCareModeState(str, Enum):
|
|
83
|
+
ACTIVATED = "ACTIVATED"
|
|
84
|
+
DEACTIVATED = "DEACTIVATED"
|
|
85
|
+
|
|
86
|
+
class AutoUnlockPlugState(str, Enum):
|
|
87
|
+
PERMANENT = "PERMANENT"
|
|
88
|
+
OFF = "OFF"
|
|
89
|
+
|
|
90
|
+
class MaxChargeCurrentAcState(str, Enum):
|
|
91
|
+
REDUCED = "REDUCED"
|
|
92
|
+
MAXIMUM = "MAXIMUM"
|
|
93
|
+
|
|
94
|
+
class MovementState(str, Enum):
|
|
95
|
+
IN_MOTION = "IN_MOTION"
|
|
96
|
+
PARKED = "PARKED"
|
|
97
|
+
|
|
98
|
+
class VehicleErrorState(str, Enum):
|
|
99
|
+
VEHICLE_STATUS_UNSUPPORTED = "VEHICLE_STATUS_UNSUPPORTED"
|
|
100
|
+
VEHICLE_STATUS_DISABLED = "VEHICLE_STATUS_DISABLED"
|
|
101
|
+
VEHICLE_STATUS_UNAVAILABLE = "VEHICLE_STATUS_UNAVAILABLE"
|
|
102
|
+
DRIVING_RANGE_UNSUPPORTED = "DRIVING_RANGE_UNSUPPORTED"
|
|
103
|
+
DRIVING_RANGE_DISABLED = "DRIVING_RANGE_DISABLED"
|
|
104
|
+
DRIVING_RANGE_UNAVAILABLE = "DRIVING_RANGE_UNAVAILABLE"
|
|
105
|
+
ODOMETER_UNSUPPORTED = "ODOMETER_UNSUPPORTED"
|
|
106
|
+
ODOMETER_DISABLED = "ODOMETER_DISABLED"
|
|
107
|
+
ODOMETER_UNAVAILABLE = "ODOMETER_UNAVAILABLE"
|
|
108
|
+
PARKING_POSITION_UNSUPPORTED = "PARKING_POSITION_UNSUPPORTED"
|
|
109
|
+
PARKING_POSITION_DISABLED = "PARKING_POSITION_DISABLED"
|
|
110
|
+
PARKING_POSITION_UNAVAILABLE = "PARKING_POSITION_UNAVAILABLE"
|
|
111
|
+
AIR_CONDITIONING_UNSUPPORTED = "AIR_CONDITIONING_UNSUPPORTED"
|
|
112
|
+
AIR_CONDITIONING_DISABLED = "AIR_CONDITIONING_DISABLED"
|
|
113
|
+
AIR_CONDITIONING_UNAVAILABLE = "AIR_CONDITIONING_UNAVAILABLE"
|
|
114
|
+
AUXILIARY_HEATING_UNSUPPORTED = "AUXILIARY_HEATING_UNSUPPORTED"
|
|
115
|
+
AUXILIARY_HEATING_DISABLED = "AUXILIARY_HEATING_DISABLED"
|
|
116
|
+
AUXILIARY_HEATING_UNAVAILABLE = "AUXILIARY_HEATING_UNAVAILABLE"
|
|
117
|
+
ACTIVE_VENTILATION_UNSUPPORTED = "ACTIVE_VENTILATION_UNSUPPORTED"
|
|
118
|
+
ACTIVE_VENTILATION_DISABLED = "ACTIVE_VENTILATION_DISABLED"
|
|
119
|
+
ACTIVE_VENTILATION_UNAVAILABLE = "ACTIVE_VENTILATION_UNAVAILABLE"
|
|
120
|
+
CHARGING_UNSUPPORTED = "CHARGING_UNSUPPORTED"
|
|
121
|
+
CHARGING_DISABLED = "CHARGING_DISABLED"
|
|
122
|
+
CHARGING_UNAVAILABLE = "CHARGING_UNAVAILABLE"
|
|
123
|
+
|
|
124
|
+
class AirConditioningState(str, Enum):
|
|
125
|
+
OFF = "OFF"
|
|
126
|
+
COOLING = "COOLING"
|
|
127
|
+
HEATING = "HEATING"
|
|
128
|
+
HEATING_AUXILIARY = "HEATING_AUXILIARY"
|
|
129
|
+
VENTILATION = "VENTILATION"
|
|
130
|
+
COMPLETED = "COMPLETED"
|
|
131
|
+
UNKNOWN = "UNKNOWN"
|
|
132
|
+
UNSUPPORTED = "UNSUPPORTED"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from .base_model import BaseModel
|
|
3
|
+
from .enums import (
|
|
4
|
+
MovementState
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
class GPRCoordinates(BaseModel):
|
|
8
|
+
"""GPR coordinates of the vehicle."""
|
|
9
|
+
latitude: float
|
|
10
|
+
longitude: float
|
|
11
|
+
|
|
12
|
+
class ParkingPosition(BaseModel):
|
|
13
|
+
"""Parking position information."""
|
|
14
|
+
state: MovementState
|
|
15
|
+
gps_coordinates: Optional[GPRCoordinates] = None
|
|
16
|
+
formatted_address: Optional[str] = None
|
|
17
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from .base_model import BaseModel
|
|
6
|
+
|
|
7
|
+
from .vehicle_status import VehicleStatus
|
|
8
|
+
from .common import VehicleError
|
|
9
|
+
from .parking_position import ParkingPosition
|
|
10
|
+
from .auxiliary_heating import AuxiliaryHeating
|
|
11
|
+
from .active_ventilation import ActiveVentilation
|
|
12
|
+
from .charging import Charging
|
|
13
|
+
from .driving_range import DrivingRange
|
|
14
|
+
from .air_conditioning import AirConditioning
|
|
15
|
+
|
|
16
|
+
class Odometer(BaseModel):
|
|
17
|
+
"""Odometer of the vehicle."""
|
|
18
|
+
mileage_in_km: int
|
|
19
|
+
car_captured_timestamp: Optional[str]
|
|
20
|
+
|
|
21
|
+
class VehicleObject(BaseModel):
|
|
22
|
+
"""Pack whole vehicle object into one object for the vehicle."""
|
|
23
|
+
name: Optional[str] = None
|
|
24
|
+
vin: str
|
|
25
|
+
license_plate: Optional[str] = None
|
|
26
|
+
render_url: Optional[str] = None
|
|
27
|
+
odometer: Optional[Odometer] = None
|
|
28
|
+
status: Optional[VehicleStatus] = None
|
|
29
|
+
parking_position: Optional[ParkingPosition] = None
|
|
30
|
+
auxiliary_heating: Optional[AuxiliaryHeating] = None
|
|
31
|
+
active_ventilation: Optional[ActiveVentilation] = None
|
|
32
|
+
air_conditioning: Optional[AirConditioning] = None
|
|
33
|
+
driving_range: Optional[DrivingRange] = None
|
|
34
|
+
charging: Optional[Charging] = None
|
|
35
|
+
|
|
36
|
+
class VehicleResponse(BaseModel):
|
|
37
|
+
"""Response object for vehicle data."""
|
|
38
|
+
vehicle: VehicleObject
|
|
39
|
+
errors: List[VehicleError] = Field(default_factory=list)
|
|
40
|
+
|
|
41
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from .base_model import BaseModel
|
|
6
|
+
from .enums import (
|
|
7
|
+
LockState,
|
|
8
|
+
DoorsState,
|
|
9
|
+
YesNoState,
|
|
10
|
+
OpenCloseState,
|
|
11
|
+
OnOffState
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
class OverallVehicleStatus(BaseModel):
|
|
15
|
+
"""Overall status of the vehicle."""
|
|
16
|
+
doors_locked: DoorsState
|
|
17
|
+
locked: YesNoState
|
|
18
|
+
doors: OpenCloseState
|
|
19
|
+
windows: OpenCloseState
|
|
20
|
+
lights: OnOffState
|
|
21
|
+
reliable_lock_status: Optional[LockState] = None
|
|
22
|
+
|
|
23
|
+
class VehicleStatusDetail(BaseModel):
|
|
24
|
+
"""Status of individual components of the vehicle."""
|
|
25
|
+
sunroof: OpenCloseState
|
|
26
|
+
trunk: OpenCloseState
|
|
27
|
+
bonnet: OpenCloseState
|
|
28
|
+
|
|
29
|
+
class VehicleStatus(BaseModel):
|
|
30
|
+
"""Pack all the objects into one object for the vehicle status."""
|
|
31
|
+
overall: OverallVehicleStatus
|
|
32
|
+
detail: VehicleStatusDetail
|
|
33
|
+
car_captured_timestamp: Optional[str] = None
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: myvsb-developer-api
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Asynchronous Python library for the MyVSB Developer API
|
|
5
|
+
Author-email: net0045 <stepan.netolicky@vsb.cz>
|
|
6
|
+
Project-URL: Homepage, https://github.com/mobility-lab-vsb/myvsb-developer-api
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/mobility-lab-vsb/myvsb-developer-api/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Framework :: AsyncIO
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
16
|
+
Requires-Dist: pydantic>=2.5.0
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: pytest>=9.0.0; extra == "test"
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
|
|
20
|
+
Requires-Dist: aresponses>=3.0.0; extra == "test"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# vsb-developer-api
|
|
24
|
+
VSB developer API is a Python library to interact with the new VSB API for connected-vehicle data and remote control of VSB vehicles.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
myskoda_openapi/api_layer/const.py,sha256=akV2Cpvlpk92BFv8QEi4ggMmG5u1p2iVzVqODpJJKd4,196
|
|
2
|
+
myskoda_openapi/api_layer/exceptions.py,sha256=Ue7VO-E7S3aelmx3PhP0f1pxLp3Bd4UhRFsfXq8FGhc,1484
|
|
3
|
+
myskoda_openapi/api_layer/open_api_client.py,sha256=581Ngn5loPTKjPzk1a9lXBH4mnSkRG89f1ufQalR4zc,5592
|
|
4
|
+
myskoda_openapi/api_layer/rest_api.py,sha256=khFH5arVNBOpKx8eSIIL9JdV9kAjcsnbmUz2t6Nf--o,13292
|
|
5
|
+
myskoda_openapi/api_layer/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
myskoda_openapi/models/active_ventilation.py,sha256=cezsslMRA_uJ4rXZQIf4dLB-rrh4O-xU6-gw71koOt8,343
|
|
7
|
+
myskoda_openapi/models/air_conditioning.py,sha256=YKgIvW-Fhg6eJMurH5Zm7H7WKBummz9-r8KbgtGwpv8,821
|
|
8
|
+
myskoda_openapi/models/auxiliary_heating.py,sha256=-6Xa_ggwXvjWqhV2GB7VWV6AhwkS4hENaQWEsug32hs,568
|
|
9
|
+
myskoda_openapi/models/base_model.py,sha256=CegdMq2ASpixeGMw1QzRfmvRipPwWX4tfmle-rNkEgw,320
|
|
10
|
+
myskoda_openapi/models/charging.py,sha256=NQnxMdzl9EZsOP_M5a7dxGM2upWX3ozH2BSI9XGzioI,1704
|
|
11
|
+
myskoda_openapi/models/common.py,sha256=r41LSkDt5V9OvbT2wdsCu55rv1DcuIsZmxsq3NwaGyE,456
|
|
12
|
+
myskoda_openapi/models/driving_range.py,sha256=qQh39bEWd-Ou3cWb5CoAlmPXeFy8kbgLh4xBKO63vgM,713
|
|
13
|
+
myskoda_openapi/models/enums.py,sha256=yOcqbtGsj1kSyem3nLnBcf0SxSJ32vn2RH3LB-pAppk,4048
|
|
14
|
+
myskoda_openapi/models/parking_position.py,sha256=K_GLaeZqT2Fl7OQ9emAL3D_ohbmfXtc9ykdV3P4NjiY,419
|
|
15
|
+
myskoda_openapi/models/vehicle.py,sha256=3uffRuu2XKUENkRqNZvpV2pCy5Gew2QzTgm09kuqFHE,1384
|
|
16
|
+
myskoda_openapi/models/vehicle_status.py,sha256=GjgzIcQwC73wyq9hVhxKZgNnB1IzGIzi5lCYC4tThkQ,888
|
|
17
|
+
myvsb_developer_api-0.1.2.dist-info/licenses/LICENSE,sha256=JH8dA4ELqb8p0QcClibxxAuMyew7AElvtO3Xgcm4Yzo,1109
|
|
18
|
+
myvsb_developer_api-0.1.2.dist-info/METADATA,sha256=BqgWw8MaZzpX-7oKplXBk5hShxzAntcFOSM1lZQCQ4M,1019
|
|
19
|
+
myvsb_developer_api-0.1.2.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
20
|
+
myvsb_developer_api-0.1.2.dist-info/top_level.txt,sha256=Y4aqNREBD9ZK3NZl-DEL3Au3DMTcmiKJkc8TQGN7bXA,16
|
|
21
|
+
myvsb_developer_api-0.1.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mobility Lab @ VSB - Technical University of Ostrava
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
myskoda_openapi
|