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