Skip to content

Commit 27b7c21

Browse files
committed
Allow VIN as optional parameter
1 parent b0fec35 commit 27b7c21

File tree

2 files changed

+58
-56
lines changed

2 files changed

+58
-56
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ await auth.async_request_token(code)
6060
vin = "YV123456789012345"
6161

6262
# Create API client
63-
api = VolvoCarsApi(client_session, auth, vin, api_key)
63+
api = VolvoCarsApi(client_session, auth, api_key, vin)
6464

6565
# Make API request
6666
engine_warnings = await api.async_get_engine_warnings()
@@ -75,8 +75,6 @@ If you'd like to show your appreciation for this project, feel free to toss a co
7575
[![ko-fi sponsor][kofi-sponsor-shield]][kofi-sponsor]
7676
[![github sponsor][github-sponsor-shield]][github-sponsor]
7777

78-
<sub><sub>\* No affiliation with these brands — just personal favorites!</sub></sub>
79-
8078

8179
[releases-shield]: https://img.shields.io/github/v/release/thomasddn/volvo-cars-api?style=flat-square
8280
[releases]: https://github.com/thomasddn/volvo-cars-api/releases

src/volvocarsapi/api.py

Lines changed: 57 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ def __init__(
5151
self,
5252
client: ClientSession,
5353
token_manager: AccessTokenManager,
54-
vin: str,
5554
api_key: str,
55+
vin: str = "",
5656
) -> None:
5757
"""Initialize Volvo Cars API."""
5858
self._client = client
5959
self._token_manager = token_manager
60-
self._vin = vin
61-
self._api_key = api_key
60+
self.api_key = api_key
61+
self.vin = vin
6262

6363
async def async_get_api_status(self) -> dict[str, VolvoCarsValue]:
6464
"""Check the API status."""
@@ -81,84 +81,84 @@ async def async_get_api_status(self) -> dict[str, VolvoCarsValue]:
8181

8282
return {"apiStatus": VolvoCarsValue(message)}
8383

84-
async def async_get_brakes_status(self) -> dict[str, VolvoCarsValueField | None]:
84+
async def async_get_brakes_status(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
8585
"""Get brakes status.
8686
8787
Required scopes: openid conve:brake_status
8888
"""
89-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "brakes")
89+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "brakes", vin)
9090

9191
async def async_get_command_accessibility(
92-
self,
92+
self, vin: str = ""
9393
) -> dict[str, VolvoCarsValueField | None]:
9494
"""Get availability status.
9595
9696
Required scopes: openid conve:command_accessibility
9797
"""
9898
return await self._async_get_field(
99-
_API_CONNECTED_ENDPOINT, "command-accessibility"
99+
_API_CONNECTED_ENDPOINT, "command-accessibility", vin
100100
)
101101

102-
async def async_get_commands(self) -> list[VolvoCarsAvailableCommand | None]:
102+
async def async_get_commands(self, vin: str = "") -> list[VolvoCarsAvailableCommand | None]:
103103
"""Get available commands.
104104
105105
Required scopes: openid conve:commands
106106
"""
107-
body = await self._async_get(_API_CONNECTED_ENDPOINT, "commands")
107+
body = await self._async_get(_API_CONNECTED_ENDPOINT, "commands", vin)
108108
items = self._get_data_list(body)
109109
return [VolvoCarsAvailableCommand.from_dict(item) for item in items]
110110

111-
async def async_get_diagnostics(self) -> dict[str, VolvoCarsValueField | None]:
111+
async def async_get_diagnostics(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
112112
"""Get diagnostics.
113113
114114
Required scopes: openid conve:diagnostics_workshop
115115
"""
116-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "diagnostics")
116+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "diagnostics", vin)
117117

118-
async def async_get_doors_status(self) -> dict[str, VolvoCarsValueField | None]:
118+
async def async_get_doors_status(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
119119
"""Get doors status.
120120
121121
Required scopes: openid conve:doors_status conve:lock_status
122122
"""
123-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "doors")
123+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "doors", vin)
124124

125-
async def async_get_engine_status(self) -> dict[str, VolvoCarsValueField | None]:
125+
async def async_get_engine_status(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
126126
"""Get engine status.
127127
128128
Required scopes: openid conve:engine_status
129129
"""
130-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "engine-status")
130+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "engine-status", vin)
131131

132-
async def async_get_engine_warnings(self) -> dict[str, VolvoCarsValueField | None]:
132+
async def async_get_engine_warnings(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
133133
"""Get engine warnings.
134134
135135
Required scopes: openid conve:diagnostics_engine_status
136136
"""
137-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "engine")
137+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "engine", vin)
138138

139-
async def async_get_fuel_status(self) -> dict[str, VolvoCarsValueField | None]:
139+
async def async_get_fuel_status(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
140140
"""Get fuel status.
141141
142142
Required scopes: openid conve:fuel_status conve:battery_charge_level
143143
"""
144-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "fuel")
144+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "fuel", vin)
145145

146-
async def async_get_location(self) -> dict[str, VolvoCarsLocation | None]:
146+
async def async_get_location(self, vin: str = "") -> dict[str, VolvoCarsLocation | None]:
147147
"""Get location.
148148
149149
Required scopes: openid location:read
150150
"""
151-
data = await self._async_get_data_dict(_API_LOCATION_ENDPOINT, "location")
151+
data = await self._async_get_data_dict(_API_LOCATION_ENDPOINT, "location", vin)
152152
return {"location": VolvoCarsLocation.from_dict(data)}
153153

154-
async def async_get_odometer(self) -> dict[str, VolvoCarsValueField | None]:
154+
async def async_get_odometer(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
155155
"""Get odometer.
156156
157157
Required scopes: openid conve:odometer_status
158158
"""
159-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "odometer")
159+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "odometer", vin)
160160

161-
async def async_get_recharge_status(self) -> dict[str, VolvoCarsValueField | None]:
161+
async def async_get_recharge_status(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
162162
"""Get recharge status.
163163
164164
Required scopes: openid
@@ -169,21 +169,21 @@ async def async_get_recharge_status(self) -> dict[str, VolvoCarsValueField | Non
169169
energy:charging_system_status energy:charging_current_limit
170170
energy:target_battery_level
171171
"""
172-
return await self._async_get_field(_API_ENERGY_ENDPOINT, "recharge-status")
172+
return await self._async_get_field(_API_ENERGY_ENDPOINT, "recharge-status", vin)
173173

174-
async def async_get_statistics(self) -> dict[str, VolvoCarsValueField | None]:
174+
async def async_get_statistics(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
175175
"""Get statistics.
176176
177177
Required scopes: openid conve:trip_statistics
178178
"""
179-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "statistics")
179+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "statistics", vin)
180180

181-
async def async_get_tyre_states(self) -> dict[str, VolvoCarsValueField | None]:
181+
async def async_get_tyre_states(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
182182
"""Get tyre states.
183183
184184
Required scopes: openid conve:tyre_status
185185
"""
186-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "tyres")
186+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "tyres", vin)
187187

188188
async def async_get_vehicles(self) -> list[str]:
189189
"""Get vehicles.
@@ -195,30 +195,30 @@ async def async_get_vehicles(self) -> list[str]:
195195
items = self._get_data_list(body)
196196
return [item["vin"] for item in items]
197197

198-
async def async_get_vehicle_details(self) -> VolvoCarsVehicle | None:
198+
async def async_get_vehicle_details(self, vin: str = "") -> VolvoCarsVehicle | None:
199199
"""Get vehicle details.
200200
201201
Required scopes: openid conve:vehicle_relation
202202
"""
203203
data = await self._async_get_data_dict(_API_CONNECTED_ENDPOINT, "")
204204
return VolvoCarsVehicle.from_dict(data)
205205

206-
async def async_get_warnings(self) -> dict[str, VolvoCarsValueField | None]:
206+
async def async_get_warnings(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
207207
"""Get warnings.
208208
209209
Required scopes: openid conve:warnings
210210
"""
211-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "warnings")
211+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "warnings", vin)
212212

213-
async def async_get_window_states(self) -> dict[str, VolvoCarsValueField | None]:
213+
async def async_get_window_states(self, vin: str = "") -> dict[str, VolvoCarsValueField | None]:
214214
"""Get window states.
215215
216216
Required scopes: openid conve:windows_status
217217
"""
218-
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "windows")
218+
return await self._async_get_field(_API_CONNECTED_ENDPOINT, "windows", vin)
219219

220220
async def async_execute_command(
221-
self, command: str, body: dict[str, Any] | None = None
221+
self, command: str, body: dict[str, Any] | None = None, vin: str = ""
222222
) -> VolvoCarsCommandResult | None:
223223
"""Execute a command.
224224
@@ -235,45 +235,48 @@ async def async_execute_command(
235235
- unlock: conve:unlock
236236
"""
237237
body = await self._async_post(
238-
_API_CONNECTED_ENDPOINT, f"commands/{command}", body
238+
_API_CONNECTED_ENDPOINT, f"commands/{command}", body=body, vin=vin
239239
)
240240
data: dict = body.get("data", {})
241241
data["invoke_status"] = data.pop("invokeStatus", None)
242242
return VolvoCarsCommandResult.from_dict(data)
243243

244244
async def _async_get_field(
245-
self, endpoint: str, operation: str
245+
self, endpoint: str, operation: str, vin: str = ""
246246
) -> dict[str, VolvoCarsValueField | None]:
247-
body = await self._async_get(endpoint, operation)
247+
body = await self._async_get(endpoint, operation, vin)
248248
data: dict = body.get("data", {})
249249
return {
250250
key: VolvoCarsValueField.from_dict(value) for key, value in data.items()
251251
}
252252

253253
async def _async_get_data_dict(
254-
self, endpoint: str, operation: str
254+
self, endpoint: str, operation: str, vin: str = ""
255255
) -> dict[str, Any]:
256-
body = await self._async_get(endpoint, operation)
256+
body = await self._async_get(endpoint, operation, vin)
257257
return cast(dict[str, Any], body.get("data", {}))
258258

259-
async def _async_get(self, endpoint: str, operation: str) -> dict[str, Any]:
260-
url = self._create_vin_url(endpoint, operation)
261-
return await self._async_request(hdrs.METH_GET, url, operation=operation)
259+
async def _async_get(self, endpoint: str, operation: str, vin: str = "") -> dict[str, Any]:
260+
url = self._create_vin_url(endpoint, operation, vin)
261+
return await self._async_request(hdrs.METH_GET, url, operation=operation, vin=vin)
262262

263263
async def _async_post(
264-
self, endpoint: str, operation: str, body: dict[str, Any] | None = None
264+
self, endpoint: str, operation: str, *, body: dict[str, Any] | None = None, vin: str = ""
265265
) -> dict[str, Any]:
266-
url = self._create_vin_url(endpoint, operation)
267-
return await self._async_request(hdrs.METH_POST, url, operation=operation, body=body)
266+
url = self._create_vin_url(endpoint, operation, vin)
267+
return await self._async_request(hdrs.METH_POST, url, operation=operation, body=body, vin=vin)
268268

269269
def _get_data_list(self, body: dict[str, Any]) -> list[Any]:
270270
return cast(list[Any], body.get("data", []))
271271

272-
def _create_vin_url(self, endpoint: str, operation: str) -> str:
272+
def _create_vin_url(self, endpoint: str, operation: str, vin: str = "") -> str:
273+
if not vin:
274+
vin = self.vin
275+
273276
return (
274-
f"{_API_URL}{endpoint}/{self._vin}/{operation}"
277+
f"{_API_URL}{endpoint}/{vin}/{operation}"
275278
if operation
276-
else f"{_API_URL}{endpoint}/{self._vin}"
279+
else f"{_API_URL}{endpoint}/{vin}"
277280
)
278281

279282
async def _async_request(
@@ -283,11 +286,12 @@ async def _async_request(
283286
*,
284287
operation: str,
285288
body: dict[str, Any] | None = None,
289+
vin: str = "",
286290
) -> dict[str, Any]:
287291
access_token = await self._token_manager.async_get_access_token()
288292
headers = {
289293
hdrs.AUTHORIZATION: f"Bearer {access_token}",
290-
"vcc-api-key": self._api_key,
294+
"vcc-api-key": self.api_key,
291295
}
292296

293297
if method == hdrs.METH_POST:
@@ -300,7 +304,7 @@ async def _async_request(
300304
"Request [%s]: %s %s",
301305
operation,
302306
method,
303-
redact_url(url, self._vin),
307+
redact_url(url, vin),
304308
)
305309
async with self._client.request(
306310
method, url, headers=headers, json=body, timeout=_API_REQUEST_TIMEOUT
@@ -324,13 +328,13 @@ async def _async_request(
324328
if ex.status == 422 and "/commands" in url:
325329
return {
326330
"data": {
327-
"vin": self._vin,
331+
"vin": vin,
328332
"invokeStatus": "UNKNOWN",
329333
"message": "",
330334
}
331335
}
332336

333-
redacted_exception = RedactedClientResponseError(ex, self._vin)
337+
redacted_exception = RedactedClientResponseError(ex, vin)
334338
message = redacted_exception.message
335339

336340
if data and (error_data := data.get("error")):

0 commit comments

Comments
 (0)