pytoyoda.api
Toyota Connected Services API.
1"""Toyota Connected Services API.""" 2 3from datetime import date, datetime, timezone 4from typing import Any, TypeVar 5from uuid import uuid4 6 7from loguru import logger 8 9from pytoyoda.const import ( 10 VEHICLE_ASSOCIATION_ENDPOINT, 11 VEHICLE_CLIMATE_CONTROL_ENDPOINT, 12 VEHICLE_CLIMATE_SETTINGS_ENDPOINT, 13 VEHICLE_CLIMATE_STATUS_ENDPOINT, 14 VEHICLE_CLIMATE_STATUS_REFRESH_ENDPOINT, 15 VEHICLE_COMMAND_ENDPOINT, 16 VEHICLE_GLOBAL_REMOTE_ELECTRIC_CONTROL_ENDPOINT, 17 VEHICLE_GLOBAL_REMOTE_ELECTRIC_REALTIME_STATUS_ENDPOINT, 18 VEHICLE_GLOBAL_REMOTE_ELECTRIC_STATUS_ENDPOINT, 19 VEHICLE_GLOBAL_REMOTE_STATUS_ENDPOINT, 20 VEHICLE_GUID_ENDPOINT, 21 VEHICLE_HEALTH_STATUS_ENDPOINT, 22 VEHICLE_LOCATION_ENDPOINT, 23 VEHICLE_NOTIFICATION_HISTORY_ENDPOINT, 24 VEHICLE_SERVICE_HISTORY_ENDPONT, 25 VEHICLE_TELEMETRY_ENDPOINT, 26 VEHICLE_TRIPS_ENDPOINT, 27) 28from pytoyoda.controller import Controller 29from pytoyoda.models.endpoints.climate import ( 30 ClimateControlModel, 31 ClimateSettingsModel, 32 ClimateSettingsResponseModel, 33 ClimateStatusResponseModel, 34) 35from pytoyoda.models.endpoints.command import CommandType, RemoteCommandModel 36from pytoyoda.models.endpoints.common import StatusModel 37from pytoyoda.models.endpoints.electric import ( 38 ElectricCommandResponseModel, 39 ElectricResponseModel, 40 NextChargeSettings, 41) 42from pytoyoda.models.endpoints.location import LocationResponseModel 43from pytoyoda.models.endpoints.notifications import NotificationResponseModel 44from pytoyoda.models.endpoints.service_history import ServiceHistoryResponseModel 45from pytoyoda.models.endpoints.status import RemoteStatusResponseModel 46from pytoyoda.models.endpoints.telemetry import TelemetryResponseModel 47from pytoyoda.models.endpoints.trips import TripsResponseModel 48from pytoyoda.models.endpoints.vehicle_guid import VehiclesResponseModel 49from pytoyoda.models.endpoints.vehicle_health import VehicleHealthResponseModel 50 51# Type variable for generic model handling 52T = TypeVar( 53 "T", 54 bound=StatusModel 55 | ElectricCommandResponseModel 56 | ElectricResponseModel 57 | LocationResponseModel 58 | NotificationResponseModel 59 | ServiceHistoryResponseModel 60 | RemoteStatusResponseModel 61 | TelemetryResponseModel 62 | TripsResponseModel 63 | VehiclesResponseModel 64 | VehicleHealthResponseModel, 65) 66 67 68class Api: 69 """API for Toyota Connected Services. 70 71 This class provides access to Toyota Connected Services endpoints to 72 retrieve and manipulate vehicle data. 73 74 """ 75 76 def __init__(self, controller: Controller) -> None: 77 """Initialize the API with a controller for communication. 78 79 Args: 80 controller: A controller instance to manage the API communication 81 82 """ 83 self.controller = controller 84 85 async def _request_and_parse( 86 self, 87 model: type[T], 88 method: str, 89 endpoint: str, 90 **kwargs, # noqa : ANN003 91 ) -> T: 92 """Parse requests and responses using Pydantic models. 93 94 Args: 95 model: Pydantic model class to parse response into 96 method: HTTP method (GET, POST, PUT, DELETE) 97 endpoint: API endpoint path 98 **kwargs: Additional arguments to pass to request method 99 100 Returns: 101 Parsed response data as specified model instance 102 103 """ 104 response = await self.controller.request_json( 105 method=method, endpoint=endpoint, **kwargs 106 ) 107 parsed_response = model(**response) 108 logger.debug(f"Parsed '{model.__name__}': {parsed_response}") 109 return parsed_response 110 111 async def _create_standard_headers(self) -> dict[str, str]: 112 """Create standard headers for API requests. 113 114 Returns: 115 Dictionary with standard headers 116 117 """ 118 return { 119 "datetime": str(int(datetime.now(timezone.utc).timestamp() * 1000)), 120 "x-correlationid": str(uuid4()), 121 "Content-Type": "application/json", 122 } 123 124 # Vehicle Management 125 126 async def set_vehicle_alias(self, alias: str, guid: str, vin: str) -> Any: # noqa :ANN401 127 """Set a nickname/alias for a vehicle. 128 129 Args: 130 alias: New nickname for the vehicle 131 guid: Global unique identifier for the vehicle 132 vin: Vehicle Identification Number 133 134 Returns: 135 Raw response from the API 136 137 """ 138 return await self.controller.request_raw( 139 method="PUT", 140 endpoint=VEHICLE_ASSOCIATION_ENDPOINT, 141 vin=vin, 142 headers=await self._create_standard_headers(), 143 body={"guid": guid, "vin": vin, "nickName": alias}, 144 ) 145 146 async def get_vehicles(self) -> VehiclesResponseModel: 147 """Get a list of vehicles registered with the Toyota account. 148 149 Returns: 150 Model containing vehicle information 151 152 """ 153 return await self._request_and_parse( 154 VehiclesResponseModel, "GET", VEHICLE_GUID_ENDPOINT 155 ) 156 157 # Vehicle Status and Information 158 159 async def get_location(self, vin: str) -> LocationResponseModel: 160 """Get the last known location of a vehicle. 161 162 Only updates when car is parked. 163 Includes latitude and longitude if supported. 164 165 Args: 166 vin: Vehicle Identification Number 167 168 Returns: 169 Model containing location information 170 171 """ 172 return await self._request_and_parse( 173 LocationResponseModel, "GET", VEHICLE_LOCATION_ENDPOINT, vin=vin 174 ) 175 176 async def get_vehicle_health_status(self, vin: str) -> VehicleHealthResponseModel: 177 """Get the latest health status of a vehicle. 178 179 Includes engine oil quantity and dashboard warning lights if supported. 180 181 Args: 182 vin: Vehicle Identification Number 183 184 Returns: 185 Model containing vehicle health information 186 187 """ 188 return await self._request_and_parse( 189 VehicleHealthResponseModel, "GET", VEHICLE_HEALTH_STATUS_ENDPOINT, vin=vin 190 ) 191 192 async def get_remote_status(self, vin: str) -> RemoteStatusResponseModel: 193 """Get general information about a vehicle. 194 195 Args: 196 vin: Vehicle Identification Number 197 198 Returns: 199 Model containing general vehicle status 200 201 """ 202 return await self._request_and_parse( 203 RemoteStatusResponseModel, 204 "GET", 205 VEHICLE_GLOBAL_REMOTE_STATUS_ENDPOINT, 206 vin=vin, 207 ) 208 209 async def get_vehicle_electric_status(self, vin: str) -> ElectricResponseModel: 210 """Get the latest electric status of a vehicle. 211 212 Includes current battery level, EV range, fuel level, and charging status. 213 214 Args: 215 vin: Vehicle Identification Number 216 217 Returns: 218 Model containing electric vehicle information 219 220 """ 221 return await self._request_and_parse( 222 ElectricResponseModel, 223 "GET", 224 VEHICLE_GLOBAL_REMOTE_ELECTRIC_STATUS_ENDPOINT, 225 vin=vin, 226 ) 227 228 async def refresh_electric_realtime_status(self, vin: str) -> StatusModel: 229 """Update realtime SOC. 230 231 Only requests a updated soc 232 233 Args: 234 vin: Vehicle Identification Number 235 236 Returns: 237 Model containing status of the refresh request 238 239 """ 240 return await self._request_and_parse( 241 StatusModel, 242 "POST", 243 VEHICLE_GLOBAL_REMOTE_ELECTRIC_REALTIME_STATUS_ENDPOINT, 244 vin=vin, 245 ) 246 247 async def get_telemetry(self, vin: str) -> TelemetryResponseModel: 248 """Get the latest telemetry data for a vehicle. 249 250 Includes current fuel level, distance to empty, and odometer. 251 252 Args: 253 vin: Vehicle Identification Number 254 255 Returns: 256 Model containing telemetry information 257 258 """ 259 return await self._request_and_parse( 260 TelemetryResponseModel, "GET", VEHICLE_TELEMETRY_ENDPOINT, vin=vin 261 ) 262 263 # Notifications and History 264 265 async def get_notifications(self, vin: str) -> NotificationResponseModel: 266 """Get all available notifications for a vehicle. 267 268 Includes message text, notification date, read flag, and date read. 269 Note: No way to mark notifications as read or limit the response. 270 271 Args: 272 vin: Vehicle Identification Number 273 274 Returns: 275 Model containing notification information 276 277 """ 278 return await self._request_and_parse( 279 NotificationResponseModel, 280 "GET", 281 VEHICLE_NOTIFICATION_HISTORY_ENDPOINT, 282 vin=vin, 283 ) 284 285 async def get_service_history(self, vin: str) -> ServiceHistoryResponseModel: 286 """Get the service history for a vehicle. 287 288 Includes service category, date, and dealer information. 289 290 Args: 291 vin: Vehicle Identification Number 292 293 Returns: 294 Model containing service history information 295 296 """ 297 return await self._request_and_parse( 298 ServiceHistoryResponseModel, "GET", VEHICLE_SERVICE_HISTORY_ENDPONT, vin=vin 299 ) 300 301 # Climate Control 302 303 async def get_climate_status(self, vin: str) -> ClimateStatusResponseModel: 304 """Get the current climate control status. 305 306 Note: Only returns data if climate control is on. If off, 307 it returns status == 0 and all other fields are None. 308 309 Args: 310 vin: Vehicle Identification Number 311 312 Returns: 313 Model containing climate status information 314 315 """ 316 return await self._request_and_parse( 317 ClimateStatusResponseModel, 318 "GET", 319 VEHICLE_CLIMATE_STATUS_ENDPOINT, 320 vin=vin, 321 ) 322 323 async def refresh_climate_status(self, vin: str) -> StatusModel: 324 """Request an update to the climate status from the vehicle. 325 326 Args: 327 vin: Vehicle Identification Number 328 329 Returns: 330 Model containing status of the refresh request 331 332 """ 333 return await self._request_and_parse( 334 StatusModel, "POST", VEHICLE_CLIMATE_STATUS_REFRESH_ENDPOINT, vin=vin 335 ) 336 337 async def get_climate_settings(self, vin: str) -> ClimateSettingsResponseModel: 338 """Get the current climate control settings. 339 340 Args: 341 vin: Vehicle Identification Number 342 343 Returns: 344 Model containing climate settings information 345 346 """ 347 return await self._request_and_parse( 348 ClimateSettingsResponseModel, 349 "GET", 350 VEHICLE_CLIMATE_SETTINGS_ENDPOINT, 351 vin=vin, 352 ) 353 354 async def update_climate_settings( 355 self, vin: str, settings: ClimateSettingsModel 356 ) -> StatusModel: 357 """Update the climate control settings for a vehicle. 358 359 Args: 360 vin: Vehicle Identification Number 361 settings: New climate control settings 362 363 Returns: 364 Model containing status of the update request 365 366 """ 367 return await self._request_and_parse( 368 StatusModel, 369 "PUT", 370 VEHICLE_CLIMATE_SETTINGS_ENDPOINT, 371 vin=vin, 372 body=settings.model_dump(exclude_unset=True, by_alias=True), 373 ) 374 375 async def send_climate_control_command( 376 self, vin: str, command: ClimateControlModel 377 ) -> StatusModel: 378 """Send a control command to the climate system. 379 380 Args: 381 vin: Vehicle Identification Number 382 command: Climate control command to send 383 384 Returns: 385 Model containing status of the command request 386 387 """ 388 return await self._request_and_parse( 389 StatusModel, 390 "POST", 391 VEHICLE_CLIMATE_CONTROL_ENDPOINT, 392 vin=vin, 393 body=command.model_dump(exclude_unset=True, by_alias=True), 394 ) 395 396 # Trip Data 397 398 async def get_trips( # noqa: PLR0913 399 self, 400 vin: str, 401 from_date: date, 402 to_date: date, 403 route: bool = False, # noqa : FBT001, FBT002 404 summary: bool = True, # noqa : FBT001, FBT002 405 limit: int = 5, 406 offset: int = 0, 407 ) -> TripsResponseModel: 408 """Get a list of trips for a vehicle within a date range. 409 410 Args: 411 vin: Vehicle Identification Number 412 from_date: Start date for trip data (inclusive, cannot be in future) 413 to_date: End date for trip data (inclusive, cannot be in future) 414 route: If True, returns route coordinates for each trip 415 summary: If True, returns monthly and daily trip summaries 416 limit: Maximum number of trips to return (max 50) 417 offset: Starting offset for pagination 418 419 Returns: 420 Model containing trip information 421 422 """ 423 endpoint = VEHICLE_TRIPS_ENDPOINT.format( 424 from_date=from_date, 425 to_date=to_date, 426 route=route, 427 summary=summary, 428 limit=limit, 429 offset=offset, 430 ) 431 return await self._request_and_parse( 432 TripsResponseModel, "GET", endpoint, vin=vin 433 ) 434 435 # Remote Commands 436 437 async def send_command( 438 self, vin: str, command: CommandType, beeps: int = 0 439 ) -> StatusModel: 440 """Send a remote command to a vehicle. 441 442 Args: 443 vin: Vehicle Identification Number 444 command: Type of command to send 445 beeps: Number of beeps for commands that support it 446 447 Returns: 448 Model containing status of the command request 449 450 """ 451 remote_command = RemoteCommandModel(beep_count=beeps, command=command) 452 return await self._request_and_parse( 453 StatusModel, 454 "POST", 455 VEHICLE_COMMAND_ENDPOINT, 456 vin=vin, 457 body=remote_command.model_dump(exclude_unset=True, by_alias=True), 458 ) 459 460 async def send_next_charging_command( 461 self, vin: str, command: NextChargeSettings 462 ) -> ElectricCommandResponseModel: 463 """Send the next charging start/end time to the vehicle. 464 465 Args: 466 vin: Vehicle Identification Number 467 command: NextChargeSettings command to send 468 469 Returns: 470 Model containing status of the command request 471 472 """ 473 return await self._request_and_parse( 474 ElectricCommandResponseModel, 475 "POST", 476 VEHICLE_GLOBAL_REMOTE_ELECTRIC_CONTROL_ENDPOINT, 477 vin=vin, 478 body=command.model_dump(exclude_none=True, by_alias=True), 479 )
69class Api: 70 """API for Toyota Connected Services. 71 72 This class provides access to Toyota Connected Services endpoints to 73 retrieve and manipulate vehicle data. 74 75 """ 76 77 def __init__(self, controller: Controller) -> None: 78 """Initialize the API with a controller for communication. 79 80 Args: 81 controller: A controller instance to manage the API communication 82 83 """ 84 self.controller = controller 85 86 async def _request_and_parse( 87 self, 88 model: type[T], 89 method: str, 90 endpoint: str, 91 **kwargs, # noqa : ANN003 92 ) -> T: 93 """Parse requests and responses using Pydantic models. 94 95 Args: 96 model: Pydantic model class to parse response into 97 method: HTTP method (GET, POST, PUT, DELETE) 98 endpoint: API endpoint path 99 **kwargs: Additional arguments to pass to request method 100 101 Returns: 102 Parsed response data as specified model instance 103 104 """ 105 response = await self.controller.request_json( 106 method=method, endpoint=endpoint, **kwargs 107 ) 108 parsed_response = model(**response) 109 logger.debug(f"Parsed '{model.__name__}': {parsed_response}") 110 return parsed_response 111 112 async def _create_standard_headers(self) -> dict[str, str]: 113 """Create standard headers for API requests. 114 115 Returns: 116 Dictionary with standard headers 117 118 """ 119 return { 120 "datetime": str(int(datetime.now(timezone.utc).timestamp() * 1000)), 121 "x-correlationid": str(uuid4()), 122 "Content-Type": "application/json", 123 } 124 125 # Vehicle Management 126 127 async def set_vehicle_alias(self, alias: str, guid: str, vin: str) -> Any: # noqa :ANN401 128 """Set a nickname/alias for a vehicle. 129 130 Args: 131 alias: New nickname for the vehicle 132 guid: Global unique identifier for the vehicle 133 vin: Vehicle Identification Number 134 135 Returns: 136 Raw response from the API 137 138 """ 139 return await self.controller.request_raw( 140 method="PUT", 141 endpoint=VEHICLE_ASSOCIATION_ENDPOINT, 142 vin=vin, 143 headers=await self._create_standard_headers(), 144 body={"guid": guid, "vin": vin, "nickName": alias}, 145 ) 146 147 async def get_vehicles(self) -> VehiclesResponseModel: 148 """Get a list of vehicles registered with the Toyota account. 149 150 Returns: 151 Model containing vehicle information 152 153 """ 154 return await self._request_and_parse( 155 VehiclesResponseModel, "GET", VEHICLE_GUID_ENDPOINT 156 ) 157 158 # Vehicle Status and Information 159 160 async def get_location(self, vin: str) -> LocationResponseModel: 161 """Get the last known location of a vehicle. 162 163 Only updates when car is parked. 164 Includes latitude and longitude if supported. 165 166 Args: 167 vin: Vehicle Identification Number 168 169 Returns: 170 Model containing location information 171 172 """ 173 return await self._request_and_parse( 174 LocationResponseModel, "GET", VEHICLE_LOCATION_ENDPOINT, vin=vin 175 ) 176 177 async def get_vehicle_health_status(self, vin: str) -> VehicleHealthResponseModel: 178 """Get the latest health status of a vehicle. 179 180 Includes engine oil quantity and dashboard warning lights if supported. 181 182 Args: 183 vin: Vehicle Identification Number 184 185 Returns: 186 Model containing vehicle health information 187 188 """ 189 return await self._request_and_parse( 190 VehicleHealthResponseModel, "GET", VEHICLE_HEALTH_STATUS_ENDPOINT, vin=vin 191 ) 192 193 async def get_remote_status(self, vin: str) -> RemoteStatusResponseModel: 194 """Get general information about a vehicle. 195 196 Args: 197 vin: Vehicle Identification Number 198 199 Returns: 200 Model containing general vehicle status 201 202 """ 203 return await self._request_and_parse( 204 RemoteStatusResponseModel, 205 "GET", 206 VEHICLE_GLOBAL_REMOTE_STATUS_ENDPOINT, 207 vin=vin, 208 ) 209 210 async def get_vehicle_electric_status(self, vin: str) -> ElectricResponseModel: 211 """Get the latest electric status of a vehicle. 212 213 Includes current battery level, EV range, fuel level, and charging status. 214 215 Args: 216 vin: Vehicle Identification Number 217 218 Returns: 219 Model containing electric vehicle information 220 221 """ 222 return await self._request_and_parse( 223 ElectricResponseModel, 224 "GET", 225 VEHICLE_GLOBAL_REMOTE_ELECTRIC_STATUS_ENDPOINT, 226 vin=vin, 227 ) 228 229 async def refresh_electric_realtime_status(self, vin: str) -> StatusModel: 230 """Update realtime SOC. 231 232 Only requests a updated soc 233 234 Args: 235 vin: Vehicle Identification Number 236 237 Returns: 238 Model containing status of the refresh request 239 240 """ 241 return await self._request_and_parse( 242 StatusModel, 243 "POST", 244 VEHICLE_GLOBAL_REMOTE_ELECTRIC_REALTIME_STATUS_ENDPOINT, 245 vin=vin, 246 ) 247 248 async def get_telemetry(self, vin: str) -> TelemetryResponseModel: 249 """Get the latest telemetry data for a vehicle. 250 251 Includes current fuel level, distance to empty, and odometer. 252 253 Args: 254 vin: Vehicle Identification Number 255 256 Returns: 257 Model containing telemetry information 258 259 """ 260 return await self._request_and_parse( 261 TelemetryResponseModel, "GET", VEHICLE_TELEMETRY_ENDPOINT, vin=vin 262 ) 263 264 # Notifications and History 265 266 async def get_notifications(self, vin: str) -> NotificationResponseModel: 267 """Get all available notifications for a vehicle. 268 269 Includes message text, notification date, read flag, and date read. 270 Note: No way to mark notifications as read or limit the response. 271 272 Args: 273 vin: Vehicle Identification Number 274 275 Returns: 276 Model containing notification information 277 278 """ 279 return await self._request_and_parse( 280 NotificationResponseModel, 281 "GET", 282 VEHICLE_NOTIFICATION_HISTORY_ENDPOINT, 283 vin=vin, 284 ) 285 286 async def get_service_history(self, vin: str) -> ServiceHistoryResponseModel: 287 """Get the service history for a vehicle. 288 289 Includes service category, date, and dealer information. 290 291 Args: 292 vin: Vehicle Identification Number 293 294 Returns: 295 Model containing service history information 296 297 """ 298 return await self._request_and_parse( 299 ServiceHistoryResponseModel, "GET", VEHICLE_SERVICE_HISTORY_ENDPONT, vin=vin 300 ) 301 302 # Climate Control 303 304 async def get_climate_status(self, vin: str) -> ClimateStatusResponseModel: 305 """Get the current climate control status. 306 307 Note: Only returns data if climate control is on. If off, 308 it returns status == 0 and all other fields are None. 309 310 Args: 311 vin: Vehicle Identification Number 312 313 Returns: 314 Model containing climate status information 315 316 """ 317 return await self._request_and_parse( 318 ClimateStatusResponseModel, 319 "GET", 320 VEHICLE_CLIMATE_STATUS_ENDPOINT, 321 vin=vin, 322 ) 323 324 async def refresh_climate_status(self, vin: str) -> StatusModel: 325 """Request an update to the climate status from the vehicle. 326 327 Args: 328 vin: Vehicle Identification Number 329 330 Returns: 331 Model containing status of the refresh request 332 333 """ 334 return await self._request_and_parse( 335 StatusModel, "POST", VEHICLE_CLIMATE_STATUS_REFRESH_ENDPOINT, vin=vin 336 ) 337 338 async def get_climate_settings(self, vin: str) -> ClimateSettingsResponseModel: 339 """Get the current climate control settings. 340 341 Args: 342 vin: Vehicle Identification Number 343 344 Returns: 345 Model containing climate settings information 346 347 """ 348 return await self._request_and_parse( 349 ClimateSettingsResponseModel, 350 "GET", 351 VEHICLE_CLIMATE_SETTINGS_ENDPOINT, 352 vin=vin, 353 ) 354 355 async def update_climate_settings( 356 self, vin: str, settings: ClimateSettingsModel 357 ) -> StatusModel: 358 """Update the climate control settings for a vehicle. 359 360 Args: 361 vin: Vehicle Identification Number 362 settings: New climate control settings 363 364 Returns: 365 Model containing status of the update request 366 367 """ 368 return await self._request_and_parse( 369 StatusModel, 370 "PUT", 371 VEHICLE_CLIMATE_SETTINGS_ENDPOINT, 372 vin=vin, 373 body=settings.model_dump(exclude_unset=True, by_alias=True), 374 ) 375 376 async def send_climate_control_command( 377 self, vin: str, command: ClimateControlModel 378 ) -> StatusModel: 379 """Send a control command to the climate system. 380 381 Args: 382 vin: Vehicle Identification Number 383 command: Climate control command to send 384 385 Returns: 386 Model containing status of the command request 387 388 """ 389 return await self._request_and_parse( 390 StatusModel, 391 "POST", 392 VEHICLE_CLIMATE_CONTROL_ENDPOINT, 393 vin=vin, 394 body=command.model_dump(exclude_unset=True, by_alias=True), 395 ) 396 397 # Trip Data 398 399 async def get_trips( # noqa: PLR0913 400 self, 401 vin: str, 402 from_date: date, 403 to_date: date, 404 route: bool = False, # noqa : FBT001, FBT002 405 summary: bool = True, # noqa : FBT001, FBT002 406 limit: int = 5, 407 offset: int = 0, 408 ) -> TripsResponseModel: 409 """Get a list of trips for a vehicle within a date range. 410 411 Args: 412 vin: Vehicle Identification Number 413 from_date: Start date for trip data (inclusive, cannot be in future) 414 to_date: End date for trip data (inclusive, cannot be in future) 415 route: If True, returns route coordinates for each trip 416 summary: If True, returns monthly and daily trip summaries 417 limit: Maximum number of trips to return (max 50) 418 offset: Starting offset for pagination 419 420 Returns: 421 Model containing trip information 422 423 """ 424 endpoint = VEHICLE_TRIPS_ENDPOINT.format( 425 from_date=from_date, 426 to_date=to_date, 427 route=route, 428 summary=summary, 429 limit=limit, 430 offset=offset, 431 ) 432 return await self._request_and_parse( 433 TripsResponseModel, "GET", endpoint, vin=vin 434 ) 435 436 # Remote Commands 437 438 async def send_command( 439 self, vin: str, command: CommandType, beeps: int = 0 440 ) -> StatusModel: 441 """Send a remote command to a vehicle. 442 443 Args: 444 vin: Vehicle Identification Number 445 command: Type of command to send 446 beeps: Number of beeps for commands that support it 447 448 Returns: 449 Model containing status of the command request 450 451 """ 452 remote_command = RemoteCommandModel(beep_count=beeps, command=command) 453 return await self._request_and_parse( 454 StatusModel, 455 "POST", 456 VEHICLE_COMMAND_ENDPOINT, 457 vin=vin, 458 body=remote_command.model_dump(exclude_unset=True, by_alias=True), 459 ) 460 461 async def send_next_charging_command( 462 self, vin: str, command: NextChargeSettings 463 ) -> ElectricCommandResponseModel: 464 """Send the next charging start/end time to the vehicle. 465 466 Args: 467 vin: Vehicle Identification Number 468 command: NextChargeSettings command to send 469 470 Returns: 471 Model containing status of the command request 472 473 """ 474 return await self._request_and_parse( 475 ElectricCommandResponseModel, 476 "POST", 477 VEHICLE_GLOBAL_REMOTE_ELECTRIC_CONTROL_ENDPOINT, 478 vin=vin, 479 body=command.model_dump(exclude_none=True, by_alias=True), 480 )
API for Toyota Connected Services.
This class provides access to Toyota Connected Services endpoints to retrieve and manipulate vehicle data.
77 def __init__(self, controller: Controller) -> None: 78 """Initialize the API with a controller for communication. 79 80 Args: 81 controller: A controller instance to manage the API communication 82 83 """ 84 self.controller = controller
Initialize the API with a controller for communication.
Arguments:
- controller: A controller instance to manage the API communication
127 async def set_vehicle_alias(self, alias: str, guid: str, vin: str) -> Any: # noqa :ANN401 128 """Set a nickname/alias for a vehicle. 129 130 Args: 131 alias: New nickname for the vehicle 132 guid: Global unique identifier for the vehicle 133 vin: Vehicle Identification Number 134 135 Returns: 136 Raw response from the API 137 138 """ 139 return await self.controller.request_raw( 140 method="PUT", 141 endpoint=VEHICLE_ASSOCIATION_ENDPOINT, 142 vin=vin, 143 headers=await self._create_standard_headers(), 144 body={"guid": guid, "vin": vin, "nickName": alias}, 145 )
Set a nickname/alias for a vehicle.
Arguments:
- alias: New nickname for the vehicle
- guid: Global unique identifier for the vehicle
- vin: Vehicle Identification Number
Returns:
Raw response from the API
147 async def get_vehicles(self) -> VehiclesResponseModel: 148 """Get a list of vehicles registered with the Toyota account. 149 150 Returns: 151 Model containing vehicle information 152 153 """ 154 return await self._request_and_parse( 155 VehiclesResponseModel, "GET", VEHICLE_GUID_ENDPOINT 156 )
Get a list of vehicles registered with the Toyota account.
Returns:
Model containing vehicle information
160 async def get_location(self, vin: str) -> LocationResponseModel: 161 """Get the last known location of a vehicle. 162 163 Only updates when car is parked. 164 Includes latitude and longitude if supported. 165 166 Args: 167 vin: Vehicle Identification Number 168 169 Returns: 170 Model containing location information 171 172 """ 173 return await self._request_and_parse( 174 LocationResponseModel, "GET", VEHICLE_LOCATION_ENDPOINT, vin=vin 175 )
Get the last known location of a vehicle.
Only updates when car is parked. Includes latitude and longitude if supported.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing location information
177 async def get_vehicle_health_status(self, vin: str) -> VehicleHealthResponseModel: 178 """Get the latest health status of a vehicle. 179 180 Includes engine oil quantity and dashboard warning lights if supported. 181 182 Args: 183 vin: Vehicle Identification Number 184 185 Returns: 186 Model containing vehicle health information 187 188 """ 189 return await self._request_and_parse( 190 VehicleHealthResponseModel, "GET", VEHICLE_HEALTH_STATUS_ENDPOINT, vin=vin 191 )
Get the latest health status of a vehicle.
Includes engine oil quantity and dashboard warning lights if supported.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing vehicle health information
193 async def get_remote_status(self, vin: str) -> RemoteStatusResponseModel: 194 """Get general information about a vehicle. 195 196 Args: 197 vin: Vehicle Identification Number 198 199 Returns: 200 Model containing general vehicle status 201 202 """ 203 return await self._request_and_parse( 204 RemoteStatusResponseModel, 205 "GET", 206 VEHICLE_GLOBAL_REMOTE_STATUS_ENDPOINT, 207 vin=vin, 208 )
Get general information about a vehicle.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing general vehicle status
210 async def get_vehicle_electric_status(self, vin: str) -> ElectricResponseModel: 211 """Get the latest electric status of a vehicle. 212 213 Includes current battery level, EV range, fuel level, and charging status. 214 215 Args: 216 vin: Vehicle Identification Number 217 218 Returns: 219 Model containing electric vehicle information 220 221 """ 222 return await self._request_and_parse( 223 ElectricResponseModel, 224 "GET", 225 VEHICLE_GLOBAL_REMOTE_ELECTRIC_STATUS_ENDPOINT, 226 vin=vin, 227 )
Get the latest electric status of a vehicle.
Includes current battery level, EV range, fuel level, and charging status.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing electric vehicle information
229 async def refresh_electric_realtime_status(self, vin: str) -> StatusModel: 230 """Update realtime SOC. 231 232 Only requests a updated soc 233 234 Args: 235 vin: Vehicle Identification Number 236 237 Returns: 238 Model containing status of the refresh request 239 240 """ 241 return await self._request_and_parse( 242 StatusModel, 243 "POST", 244 VEHICLE_GLOBAL_REMOTE_ELECTRIC_REALTIME_STATUS_ENDPOINT, 245 vin=vin, 246 )
Update realtime SOC.
Only requests a updated soc
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing status of the refresh request
248 async def get_telemetry(self, vin: str) -> TelemetryResponseModel: 249 """Get the latest telemetry data for a vehicle. 250 251 Includes current fuel level, distance to empty, and odometer. 252 253 Args: 254 vin: Vehicle Identification Number 255 256 Returns: 257 Model containing telemetry information 258 259 """ 260 return await self._request_and_parse( 261 TelemetryResponseModel, "GET", VEHICLE_TELEMETRY_ENDPOINT, vin=vin 262 )
Get the latest telemetry data for a vehicle.
Includes current fuel level, distance to empty, and odometer.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing telemetry information
266 async def get_notifications(self, vin: str) -> NotificationResponseModel: 267 """Get all available notifications for a vehicle. 268 269 Includes message text, notification date, read flag, and date read. 270 Note: No way to mark notifications as read or limit the response. 271 272 Args: 273 vin: Vehicle Identification Number 274 275 Returns: 276 Model containing notification information 277 278 """ 279 return await self._request_and_parse( 280 NotificationResponseModel, 281 "GET", 282 VEHICLE_NOTIFICATION_HISTORY_ENDPOINT, 283 vin=vin, 284 )
Get all available notifications for a vehicle.
Includes message text, notification date, read flag, and date read. Note: No way to mark notifications as read or limit the response.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing notification information
286 async def get_service_history(self, vin: str) -> ServiceHistoryResponseModel: 287 """Get the service history for a vehicle. 288 289 Includes service category, date, and dealer information. 290 291 Args: 292 vin: Vehicle Identification Number 293 294 Returns: 295 Model containing service history information 296 297 """ 298 return await self._request_and_parse( 299 ServiceHistoryResponseModel, "GET", VEHICLE_SERVICE_HISTORY_ENDPONT, vin=vin 300 )
Get the service history for a vehicle.
Includes service category, date, and dealer information.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing service history information
304 async def get_climate_status(self, vin: str) -> ClimateStatusResponseModel: 305 """Get the current climate control status. 306 307 Note: Only returns data if climate control is on. If off, 308 it returns status == 0 and all other fields are None. 309 310 Args: 311 vin: Vehicle Identification Number 312 313 Returns: 314 Model containing climate status information 315 316 """ 317 return await self._request_and_parse( 318 ClimateStatusResponseModel, 319 "GET", 320 VEHICLE_CLIMATE_STATUS_ENDPOINT, 321 vin=vin, 322 )
Get the current climate control status.
Note: Only returns data if climate control is on. If off, it returns status == 0 and all other fields are None.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing climate status information
324 async def refresh_climate_status(self, vin: str) -> StatusModel: 325 """Request an update to the climate status from the vehicle. 326 327 Args: 328 vin: Vehicle Identification Number 329 330 Returns: 331 Model containing status of the refresh request 332 333 """ 334 return await self._request_and_parse( 335 StatusModel, "POST", VEHICLE_CLIMATE_STATUS_REFRESH_ENDPOINT, vin=vin 336 )
Request an update to the climate status from the vehicle.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing status of the refresh request
338 async def get_climate_settings(self, vin: str) -> ClimateSettingsResponseModel: 339 """Get the current climate control settings. 340 341 Args: 342 vin: Vehicle Identification Number 343 344 Returns: 345 Model containing climate settings information 346 347 """ 348 return await self._request_and_parse( 349 ClimateSettingsResponseModel, 350 "GET", 351 VEHICLE_CLIMATE_SETTINGS_ENDPOINT, 352 vin=vin, 353 )
Get the current climate control settings.
Arguments:
- vin: Vehicle Identification Number
Returns:
Model containing climate settings information
355 async def update_climate_settings( 356 self, vin: str, settings: ClimateSettingsModel 357 ) -> StatusModel: 358 """Update the climate control settings for a vehicle. 359 360 Args: 361 vin: Vehicle Identification Number 362 settings: New climate control settings 363 364 Returns: 365 Model containing status of the update request 366 367 """ 368 return await self._request_and_parse( 369 StatusModel, 370 "PUT", 371 VEHICLE_CLIMATE_SETTINGS_ENDPOINT, 372 vin=vin, 373 body=settings.model_dump(exclude_unset=True, by_alias=True), 374 )
Update the climate control settings for a vehicle.
Arguments:
- vin: Vehicle Identification Number
- settings: New climate control settings
Returns:
Model containing status of the update request
376 async def send_climate_control_command( 377 self, vin: str, command: ClimateControlModel 378 ) -> StatusModel: 379 """Send a control command to the climate system. 380 381 Args: 382 vin: Vehicle Identification Number 383 command: Climate control command to send 384 385 Returns: 386 Model containing status of the command request 387 388 """ 389 return await self._request_and_parse( 390 StatusModel, 391 "POST", 392 VEHICLE_CLIMATE_CONTROL_ENDPOINT, 393 vin=vin, 394 body=command.model_dump(exclude_unset=True, by_alias=True), 395 )
Send a control command to the climate system.
Arguments:
- vin: Vehicle Identification Number
- command: Climate control command to send
Returns:
Model containing status of the command request
399 async def get_trips( # noqa: PLR0913 400 self, 401 vin: str, 402 from_date: date, 403 to_date: date, 404 route: bool = False, # noqa : FBT001, FBT002 405 summary: bool = True, # noqa : FBT001, FBT002 406 limit: int = 5, 407 offset: int = 0, 408 ) -> TripsResponseModel: 409 """Get a list of trips for a vehicle within a date range. 410 411 Args: 412 vin: Vehicle Identification Number 413 from_date: Start date for trip data (inclusive, cannot be in future) 414 to_date: End date for trip data (inclusive, cannot be in future) 415 route: If True, returns route coordinates for each trip 416 summary: If True, returns monthly and daily trip summaries 417 limit: Maximum number of trips to return (max 50) 418 offset: Starting offset for pagination 419 420 Returns: 421 Model containing trip information 422 423 """ 424 endpoint = VEHICLE_TRIPS_ENDPOINT.format( 425 from_date=from_date, 426 to_date=to_date, 427 route=route, 428 summary=summary, 429 limit=limit, 430 offset=offset, 431 ) 432 return await self._request_and_parse( 433 TripsResponseModel, "GET", endpoint, vin=vin 434 )
Get a list of trips for a vehicle within a date range.
Arguments:
- vin: Vehicle Identification Number
- from_date: Start date for trip data (inclusive, cannot be in future)
- to_date: End date for trip data (inclusive, cannot be in future)
- route: If True, returns route coordinates for each trip
- summary: If True, returns monthly and daily trip summaries
- limit: Maximum number of trips to return (max 50)
- offset: Starting offset for pagination
Returns:
Model containing trip information
438 async def send_command( 439 self, vin: str, command: CommandType, beeps: int = 0 440 ) -> StatusModel: 441 """Send a remote command to a vehicle. 442 443 Args: 444 vin: Vehicle Identification Number 445 command: Type of command to send 446 beeps: Number of beeps for commands that support it 447 448 Returns: 449 Model containing status of the command request 450 451 """ 452 remote_command = RemoteCommandModel(beep_count=beeps, command=command) 453 return await self._request_and_parse( 454 StatusModel, 455 "POST", 456 VEHICLE_COMMAND_ENDPOINT, 457 vin=vin, 458 body=remote_command.model_dump(exclude_unset=True, by_alias=True), 459 )
Send a remote command to a vehicle.
Arguments:
- vin: Vehicle Identification Number
- command: Type of command to send
- beeps: Number of beeps for commands that support it
Returns:
Model containing status of the command request
461 async def send_next_charging_command( 462 self, vin: str, command: NextChargeSettings 463 ) -> ElectricCommandResponseModel: 464 """Send the next charging start/end time to the vehicle. 465 466 Args: 467 vin: Vehicle Identification Number 468 command: NextChargeSettings command to send 469 470 Returns: 471 Model containing status of the command request 472 473 """ 474 return await self._request_and_parse( 475 ElectricCommandResponseModel, 476 "POST", 477 VEHICLE_GLOBAL_REMOTE_ELECTRIC_CONTROL_ENDPOINT, 478 vin=vin, 479 body=command.model_dump(exclude_none=True, by_alias=True), 480 )
Send the next charging start/end time to the vehicle.
Arguments:
- vin: Vehicle Identification Number
- command: NextChargeSettings command to send
Returns:
Model containing status of the command request