NAME

Tesla::API - Interface to Tesla's API

Coverage Status

SYNOPSIS

use Tesla::API;

my $tesla = Tesla::API->new;

my @endpoint_names = keys %{ $tesla->endpoints };

# See Tesla::Vehicle for direct access to vehicle-related methods

my $endpoint_name   = 'VEHICLE_DATA';
my $vehicle_id      = 3234234242124;

# Get the entire list of car data

my $car_data = $tesla->api(
    endpoint    => $endpoint_name,
    id          => $vehicle_id
);

# Send the open trunk command

$tesla->api(
    endpoint    => 'ACTUATE_TRUNK',
    id          => $vehicle_id,
    api_params  => {which_trunk => 'rear'}
);

DESCRIPTION

This distribution provides access to the Tesla API.

This class is designed to be subclassed. For example, I have already begun a new Tesla::Vehicle distribution which will have access and update methods that deal specifically with Tesla autos, then a Tesla::Powerwall distribution for their battery storage etc.

METHODS - CORE

new(%params)

Instantiates and returns a new Tesla::API object.

NOTE: When instantiating an object and you haven't previously authenticated, a URL will be displayed on the console for you to navigate to. You will then be redirected to Tesla's login page where you will authenticate. You will be redirected again to a "Page Not Found" page, in which you must copy the URL from the address bar and paste it back into the console.

We then internally generate an access token for you, store it in a tesla_auth_cache.json file in your home directory, and use it on all subsequent accesses.

NOTE: If you do not have a Tesla account, you can still instantiate a Tesla::API object by supplying the unauthenticated => 1 parameter to new().

Parameters:

All parameters are to be sent in the form of a hash.

unauthenticated

Optional, Bool: Set to true to bypass the access token generation.

Default: undef

api_cache_persist

Optional, Bool: Set this to true if you want to make multiple calls against the same data set, where having the cache time out and re-populated between these calls would be non-beneficial.

Default: False

api_cache_time

Optional, Integer: By default, we cache the fetched data from the Tesla API for two seconds. If you make calls that have already been called within that time, we will return the cached data.

Send in the number of seconds you'd like to cache the data for. A value of zero (0) will disable caching and all calls through this library will go directly to Tesla every time.

Return: Integer, the number of seconds we're caching Tesla API data for.

api(%params)

Responsible for disseminating the endpoints and retrieving data through the Tesla API.

All parameters are to be sent in as a hash.

Parameters:

endpoint

Mandatory, String: A valid Tesla API endpoint name. The entire list can be found in the share/endpoints.json file for the time being.

id

Optional, Integer: Some endpoints require an ID sent in (eg. vehicle ID, Powerwall ID etc).

api_params

Optional, Hash Reference: Some API calls require additional parameters. Send in a hash reference where the keys are the API parameter name, and the value is, well, the value.

Return: Hash or array reference, depending on the endpoint. If an error occurs while communicating with the Tesla API, we'll send back a hash reference containing error => 'Tesla error message'.

endpoints

Returns a hash reference of hash references. Each key is the name of the endpoint, and its value contains data on how we process the call to Tesla.

Example (snipped for brevity):

{
    MEDIA_VOLUME_DOWN => {
        TYPE => 'POST',
        URI => 'api/1/vehicles/{vehicle_id}/command/media_volume_down'
        AUTH => $VAR1->{'UPGRADES_CREATE_OFFLINE_ORDER'}{'AUTH'},
    },
    VEHICLE_DATA => {
        TYPE => 'GET',
        URI => 'api/1/vehicles/{vehicle_id}/vehicle_data',
        AUTH => $VAR1->{'UPGRADES_CREATE_OFFLINE_ORDER'}{'AUTH'}
    },
}

Bracketed names in the URI (eg: {vehicle_id}) are variable placeholders. It will be replaced with the ID sent in to the various method or api() call.

To get a list of endpoint names:

my @endpoint_names = keys %{ $tesla->endpoints };

mech

Returns the WWW::Mechanize object we've instantiated internally.

option_codes

NOTE: I'm unsure if the option codes are vehicle specific, or general for all Tesla products, so I'm leaving this method here for now.

Returns a hash reference of 'option code' => 'description' pairs.

update_data_files($type)

Checks to see if there are any updates to the endpoints.json or option_codes.json files online, and updates them locally.

Parameters:

$type

Optional, String: One of endpoints or option_codes. If set, we'll operate on only that file.

Return: None. croak()s on faiure.

uri($url)

Parameters:

$url

Mandatory, String: The URL to instantiate the object with.

Instantiates and returns a new "URI" object ready to be used.

useragent_string($ua_string)

Sets/gets the useragent string we send to the Tesla API.

Optional, String: The user agent browser string to send to the Tesla API.

Return: String, the currently set value.

useragent_timeout($timeout)

Sets/gets the timeout we use in the WWW::Mechanize object that we communicate to the Tesla API with.

Parameters:

$timeout

Optional, Integer/Float: The timeout in seconds or fractions of a second.

Return: Integer/Float, the currently set value.

uuid

Each "Tesla::API" object is identified internally by a unique identifier string. This method returns it for you.

Example:

5A7C01A5-0C47-4815-8B33-9AE3A475FF01

METHODS - API CACHE

api_cache_clear

Some methods chain method calls. For example, calling $vehicle->doors_lock will poll the API, then cache the state data.

if another call is made to $vehicle->locked immediately thereafter to check whether the door is actually closed or not, the old cached data would normally be returned.

If we don't clear the cache out between these two calls, we will be returned stale data.

Takes no parameters, has no return. Only use this call in API calls that somehow manipulate the state of the object you're working with.

api_cache_persist($bool)

$bool

Optional, Bool: Set this to true if you want to make multiple calls against the same data set, where having the cache time out and re-populated between these calls would be non-beneficial.

You can ensure fresh data for the set by making a call to api_cache_clear() before the first call that fetches data.

Default: False

api_cache_time($cache_seconds)

The number of seconds we will cache retrieved endpoint data from the Tesla API for, to reduce the number of successive calls to retrieve the same data.

Parameters:

$cache_seconds

Optional, Integer: By default, we cache the fetched data from the Tesla API for two seconds. If you make calls that have already been called within that time, we will return the cached data.

Send in the number of seconds you'd like to cache the data for. A value of zero (0) will disable caching and all calls through this library will go directly to Tesla every time.

Return: Integer, the number of seconds we're caching Tesla API data for.

API CACHING

We've employed a complex caching mechanism for data received from Tesla's API.

Each Tesla::API object you instantiate has its own cache storage. Modifications to the cache or any cache attributes or parameters will not affect the caching of other objects whether they be created in the same or a different process/script. The cache is kept separate by using the stored UUID of each object.

By default, we cache retrieved data for every endpoint/ID pair in the cache for two seconds (modifiable by api_cache_timeout(), or api_cache_timeout in new()).

This means that if you call three methods in a row that all extract information from the data returned via a single endpoint/ID pair, you may get back the cached result, or if the cache has timed out, you'll get data from another call to the Tesla API. In some cases, having the data updated may be desirable, sometimes you want data from the same set.

Here are some examples on how to deal with the caching mechanism. We will use a Tesla::Vehicle object for this example:

Store API cache for 10 seconds

Again, by default, we cache and return data from the Tesla API for two seconds. Change it to 10:

my $api = Tesla::API->new(api_cache_timeout => 10);

...or:

my $car = Tesla::Vehicle->new(api_cache_timeout => 10);

...or:

$car->api_cache_timeout(10);

Disable API caching

my $api = Tesla::API->new(api_cache_timeout => 0);

...or:

my $car = Tesla::Vehicle->new(api_cache_timeout => 0);

...or:

$car->api_cache_timeout(0);

Flush the API cache

$api->api_cache_clear;

...or:

$car->api_cache_clear;

Permanently use the cached data until manually flushed

my $api = Tesla::API->new(api_cache_persist => 1);

...or:

my $car = Tesla::Vehicle->new(api_cache_persist => 1);

...or:

$car->api_cache_persist(1);

Use the cache for a period of time

If making multiple calls to methods that use the same data set and want to be sure the data doesn't change until you're done, do this:

my $car = Tesla::Vehicle->new; # Default caching of 2 seconds

sub work {

    # Clear the cache so it gets updated, but set it to persistent so once
    # the cache data is updated, it remains

    $car->api_cache_clear;
    $car->api_cache_persist(1);

    say $car->online;
    say $car->lat;
    say $car->lon;
    say $car->battery_level;

    # Now unset the persist flag so other parts of your program won't be
    # affected by it

    $car->api_cache_persist(0);
}

If you are sure no other parts of your program will be affected by having a persistent cache, you can set it globally:

my $car = Tesla::Vehicle->new(api_cache_persist => 1);

while (1) {

    # Clear the cache at the beginning of the loop so it gets updated,
    # unless you never want new data after the first saving of data

    $car->api_cache_clear;

    say $car->online;
    say $car->lat;
    say $car->lon;
    say $car->battery_level;
}

EXAMPLE USAGE

See Tesla::Vehicle for vehicle specific methods.

use Data::Dumper;
use Tesla::API;
use feature 'say';

my $tesla = Tesla::API->new;
my $vehicle_id = 1234238782349137;

print Dumper $tesla->api(endpoint => 'VEHICLE_DATA', id => $vehicle_id);

Output (massively and significantly snipped for brevity):

$VAR1 = {
    'vehicle_config' => {
        'car_type' => 'modelx',
        'rear_seat_type' => 7,
        'rear_drive_unit' => 'Small',
        'wheel_type' => 'Turbine22Dark',
        'timestamp' => '1647461524710',
        'rear_seat_heaters' => 3,
        'trim_badging' => '100d',
        'headlamp_type' => 'Led',
        'driver_assist' => 'TeslaAP3',
    },
    'id_s' => 'XXXXXXXXXXXXXXXXX',
    'vehicle_id' => 'XXXXXXXXXX',
    'charge_state' => {
        'usable_battery_level' => 69,
        'battery_range' => '189.58',
        'charge_limit_soc_std' => 90,
        'charge_amps' => 48,
        'charge_limit_soc' => 90,
        'battery_level' => 69,
    },
    'vin' => 'XXXXXXXX',
    'in_service' => $VAR1->{'vehicle_config'}{'use_range_badging'},
    'user_id' => 'XXXXXX',
    'id' => 'XXXXXXXXXXXXX',
    'drive_state' => {
        'shift_state' => 'P',
        'heading' => 92,
        'longitude' => '-XXX.XXXXXX',
        'latitude' => 'XX.XXXXXX',
        'power' => 0,
        'speed' => undef,
    },
    'api_version' => 34,
    'display_name' => 'Dream machine',
    'state' => 'online',
    'access_type' => 'OWNER',
    'option_codes' => 'AD15,MDL3,PBSB,RENA,BT37,ID3W,RF3G,S3PB,DRLH,DV2W,W39B,APF0,COUS,BC3B,CH07,PC30,FC3P,FG31,GLFR,HL31,HM31,IL31,LTPB,MR31,FM3B,RS3H,SA3P,STCP,SC04,SU3C,T3CA,TW00,TM00,UT3P,WR00,AU3P,APH3,AF00,ZCST,MI00,CDM0',
    'vehicle_state' => {
        'valet_mode' => $VAR1->{'vehicle_config'}{'use_range_badging'},
        'vehicle_name' => 'Dream machine',
        'sentry_mode_available' => $VAR1->{'vehicle_config'}{'plg'},
        'sentry_mode' => $VAR1->{'vehicle_config'}{'use_range_badging'},
        'car_version' => '2022.4.5.4 abcfac6bfcdc',
        'homelink_device_count' => 3,
        'is_user_present' => $VAR1->{'vehicle_config'}{'use_range_badging'},
        'odometer' => 'XXXXXXX.233656',
        'media_state' => {
            'remote_control_enabled' => $VAR1->{'vehicle_config'}{'plg'}
        },
    },
    'autopark_style' => 'dead_man',
    'software_update' => {
        'expected_duration_sec' => 2700,
        'version' => ' ',
        'status' => '',
        'download_perc' => 0,
        'install_perc' => 1
    },
    'speed_limit_mode' => {
        'max_limit_mph' => 90,
        'min_limit_mph' => '50',
        'active' => $VAR1->{'vehicle_config'}{'use_range_badging'},
        'current_limit_mph' => '80.029031',
        'pin_code_set' => $VAR1->{'vehicle_config'}{'plg'}
    },
    'climate_state' => {
           'passenger_temp_setting' => '20.5',
           'driver_temp_setting' => '20.5',
           'side_mirror_heaters' => $VAR1->{'vehicle_config'}{'use_range_badging'},
           'is_climate_on' => $VAR1->{'vehicle_config'}{'use_range_badging'},
           'fan_status' => 0,
           'seat_heater_third_row_right' => 0,
           'seat_heater_right' => 0,
           'is_front_defroster_on' => $VAR1->{'vehicle_config'}{'use_range_badging'},
           'battery_heater' => $VAR1->{'vehicle_config'}{'use_range_badging'},
           'is_rear_defroster_on' => $VAR1->{'vehicle_config'}{'use_range_badging'},
    },
    'gui_settings' => {
          'gui_temperature_units' => 'C',
          'gui_charge_rate_units' => 'km/hr',
          'gui_24_hour_time' => $VAR1->{'vehicle_config'}{'use_range_badging'},
          'gui_range_display' => 'Ideal',
          'show_range_units' => $VAR1->{'vehicle_config'}{'plg'},
          'gui_distance_units' => 'km/hr',
          'timestamp' => '1647461524710'
    }
};

CONFIGURATION VARIABLES

Most configuration options used in this software are defined as constants in the library file. Some are defaults that can be overridden via method calls, others are only modifiable by updating the actual value in the file.

DEBUG_CACHE

Prints to STDOUT debugging output from the caching mechanism.

Override: $ENV{DEBUG_TESLA_API_CACHE}. Note that this must be configured within a BEGIN block, prior to the use Tesla::API line for it to have effect.

API_CACHE_PERSIST

Always/never use the cache once data has been retrieved through the Tesla API.

Default: False (0).

Override: None

API_CACHE_TIMEOUT_SECONDS

How many seconds to reuse the cached data retrieved from the Tesla API.

Default: 2

Override: "api_cache_time($cache_seconds)"

API_CACHE_RETRIES

How many times we'll try a Tesla API call in the event of a failure.

Default: 3

Override: None

AUTH_CACHE_FILE

The path and filename of the file we'll store the Tesla API access token information.

Default: $home_dir/tesla_api_cache.json

Override: _authentication_cache_file(), used primarily for unit testing.

ENDPOINTS_FILE

The path and filename of the file we'll store the Tesla API endpoint description file.

Default: dist_file('Tesla-API', 'endpoints.json')

Override: $ENV{TESLA_API_ENDPOINTS_FILE}. Note that this must be configured within a BEGIN block, prior to the use Tesla::API line for it to have effect.

OPTION_CODES_FILE

The path and filename of the file we'll store the product option code list file for Tesla products.

Default: dist_file('Tesla-API', 'option_codes.json')

Override: $ENV{TESLA_API_OPTIONCODES_FILE}. Note that this must be configured within a BEGIN block, prior to the use Tesla::API line for it to have effect.

TOKEN_EXPIRY_WINDOW

The number of seconds we'll add to the current time when validating the token expiry. This is effectively a cusion window so that the token has at least this many seconds before expiring to ensure the next call won't use an invalidated token

Default: 5

Override: None

URI_API

The URL we use to communicate with the Tesla API for data retrieval operations.

Default: https://owner-api.teslamotors.com/

Override: None

URI_ENDPOINTS

The URL we use to retrieve the updated Tesla API endpoints file.

Default: https://raw.githubusercontent.com/tdorssers/TeslaPy/master/teslapy/endpoints.json

Override: None

URI_OPTION_CODES

The URL we use to retrieve the updated Tesla API product option codes file.

Default: https://raw.githubusercontent.com/tdorssers/TeslaPy/master/teslapy/option_codes.json

Override: None

URI_AUTH

The URL we use to perform the Tesla API authentication routines.

Default: https://auth.tesla.com/oauth2/v3/authorize

Override: None

URI_TOKEN

The URL we use to fetch and update the Tesla API access tokens.

Default: https://auth.tesla.com/oauth2/v3/token

Override: None

USERAGENT_STRING

String used to identify the 'browser' we're using to access the Tesla API.

Default: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:98.0) Gecko/20100101 Firefox/98.0

Override: "useragent_string($ua_string)"

USERAGENT_TIMEOUT

Number of seconds before we classify a call to the Tesla API as timed out.

Default: 180

Override: "useragent_timeout($timeout)"

AUTHOR

Steve Bertrand, <steveb at cpan.org>

ACKNOWLEDGEMENTS

This distribution suite has been a long time in the works. For my other projects written in Perl previous to writing this code that required data from the Tesla API, I wrapped Tim Dorssers wonderful TeslaPy Python project.

Much of the code in this distribution is heavily influenced by the code his project, and currently, we're using a direct copy of its Tesla API endpoint file.

Thanks Tim, and great work!

Also thanks goes out to https://teslaapi.io, as a lot of the actual request parameter information and response data layout I learned from that site while implementing the actual REST calls to the Tesla API.

LICENSE AND COPYRIGHT

Copyright 2022 Steve Bertrand.

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:

http://www.perlfoundation.org/artistic_license_2_0

The copied endpoint code data borrowed from Tim's TeslaPy project has been rebranded with the Perl license here, as permitted by the MIT license TeslaPy is licensed under.