Python Tesla Powerwall API for consuming a local endpoint.
Note: This is not an official API provided by Tesla and this project is not affilated with Tesla in any way.
Powerwall Software versions from 1.47.0 to 1.50.1 as well as 20.40 to 22.9.2 are tested, but others will probably work too.
Install the library via pip:
$ pip install tesla_powerwall
Currently it is not possible to control the Backup Percentage, because you need to be logged in as installer, which requires physical switch toggle. There is an ongoing discussion about a possible solution here. However, if you believe there exists a solution, feel free to open an issue detailing the solution.
For a basic Overview of the functionality of this library you can take a look at examples/example.py
:
$ export POWERWALL_IP=<ip of your Powerwall>
$ export POWERWALL_PASSWORD=<your password>
$ python3 examples/example.py
from tesla_powerwall import Powerwall
# Create a simple powerwall object by providing the IP
powerwall = Powerwall("<ip of your Powerwall>")
#=> <Powerwall ...>
# Create a powerwall object with more options
powerwall = Powerwall(
endpoint="<ip of your powerwall>",
# Configure timeout; default is 10
timeout=10,
# Provide a requests.Session or None. If None is provided, a Session will be created.
http_session=None,
# Whether to verify the SSL certificate or not
verify_ssl=False,
disable_insecure_warning=True
)
#=> <Powerwall ...>
Note: By default the API client does not verify the SSL certificate of the Powerwall. If you want to verify the SSL certificate you can set
verify_ssl
toTrue
. The API client suppresses warnings about an inseucre request (because we aren't verifing the certificate). If you want to enable those warnings you can setdisable_insecure_warning
toFalse
.
Since version 20.49.0 authentication is required for all methods. For that reason you must call login
before making a request to the API.
When you perform a request without being authenticated, an AccessDeniedError
will be thrown.
To login you can either use login
or login_as
. login
logs you in as User.CUSTOMER
whereas with login_as
you can choose a different user:
from tesla_powerwall import User
# Login as customer without email
# The default value for the email is ""
powerwall.login("<password>")
#=> <LoginResponse ...>
# Login as customer with email
powerwall.login("<password>", "<email>")
#=> <LoginResponse ...>
# Login with different user
powerwall.login_as(User.INSTALLER, "<password>", "<email>")
#=> <LoginResponse ...>
# Check if we are logged in
# This method only checks wether a cookie with a Bearer token exists
# It does not verify whether this token is valid
powerwall.is_authenticated()
#=> True
# Logout
powerwall.logout()
powerwall.is_authenticated()
#=> False
The API object directly maps the REST endpoints with a python method in the form of <verb>_<path>
. So if you need the raw json responses you can use the API object. It can be either created manually or retrived from an existing Powerwall
:
from tesla_powerwall import API
# Manually create API object
api = API('https://<ip>/')
# Perform get on 'system_status/soe'
api.get_system_status_soe()
#=> {'percentage': 97.59281925744594}
# From existing powerwall
api = powerwall.get_api()
api.get_system_status_soe()
The Powerwall
objet provides a wrapper around the API and exposes common methods.
Get charge in percent:
powerwall.get_charge()
#=> 97.59281925744594 (%)
Get charge in watt:
powerwall.get_energy()
#=> 14807 (Wh)
Get the capacity of your powerwall in watt:
powerwall.get_capacity()
#=> 28078 (Wh)
Get information about the battery packs that are installed:
batteries = powerwall.get_batteries()
#=> [<Battery ...>, <Battery ...>]
batteries[0].part_number
#=> "XXX-G"
batteries[0].serial_number
#=> "TGXXX"
batteries[0].energy_remaining
#=> 7378 (Wh)
batteries[0].capacity
#=> 14031 (Wh)
batteries[0].energy_charged
#=> 5525740 (Wh)
batteries[0].energy_discharged
#=> 4659550 (Wh)
batteries[0].wobble_detected
#=> False
status = powerwall.get_status()
#=> <PowerwallStatus ...>
status.version
#=> '1.49.0'
status.up_time_seconds
#=> datetime.timedelta(days=13, seconds=63287, microseconds=146455)
status.start_time
#=> datetime.datetime(2020, 9, 23, 23, 31, 16, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800)))
status.device_type
#=> DeviceType.GW2
sm = powerwall.sitemaster
#=> <SiteMaster ...>
sm.status
#=> StatusUp
sm.running
#=> true
sm.connected_to_tesla
#=> true
The sitemaster can be started and stopped using run()
and stop()
info = powerwall.get_site_info()
#=> <SiteInfo ...>
info.site_name
#=> 'Tesla Home'
info.country
#=> 'Germany'
info.nominal_system_energy
#=> 13.5 (kWh)
info.timezone
#=> 'Europe/Berlin'
from tesla_powerwall import MeterType
meters = powerwall.get_meters()
#=> <MetersAggregates ...>
# access meter, but may return None when meter is not available
meters.get_meter(MeterType.SOLAR)
#=> <Meter ...>
# access meter, but may raise MeterNotAvailableError when the meter is not available at your powerwall (e.g. no solar panels installed)
meters.solar
#=> <MeterResponse ...>
# get all available meters at the current powerwall
meters.meters.keys()
#=> [<MeterType.SITE: 'site'>, <MeterType.BATTERY: 'battery'>, <MeterType.LOAD: 'load'>, <MeterType.SOLAR: 'solar'>]
Available meters are: solar
, site
, load
, battery
, generator
, and busway
. Some of those meters might not be available based on the installation and raise MeterNotAvailableError when accessed.
Meter
provides different methods for checking current power supply/draw:
meters = powerwall.get_meters()
meters.solar.get_power()
#=> 0.4 (kW)
meters.solar.instant_power
#=> 409.941801071167 (W)
meters.solar.is_drawing_from()
#=> True
meters.load.is_sending_to()
#=> True
meters.battery.is_active()
#=> False
# Different precision settings might return different results
meters.battery.is_active(precision=5)
#=> True
Note: For MeterType.LOAD
is_drawing_from
always returnsFalse
because it cannot be drawn fromload
.
Get energy exported/imported in watt-hours (Wh) with energy_exported
and energy_imported
. For the values in kilowatt-hours (kWh) use get_energy_exported
and get_energy_imported
:
meters.battery.energy_exported
#=> 6394100 (Wh)
meters.battery.get_energy_exported()
#=> 6394.1 (kWh)
meters.battery.energy_imported
#=> 7576570 (Wh)
meters.battery.get_energy_imported()
#=> 7576.6 (kWh)
You can receive more detailed information about the meters site
and solar
:
meter_details = powerwall.get_meter_site() # or get_meter_solar() for the solar meter
#=> <MeterDetailsResponse ...>
readings = meter_details.readings
#=> <MeterDetailsReadings ...>
readings.real_power_a # same for real_power_b and real_power_c
#=> 619.13532458
readings.i_a_current # same for i_b_current and i_c_current
#=> 3.02
readings.v_l1n # smae for v_l2n and v_l3n
#=> 235.82
readings.instant_power
#=> -18.000023458
readings.is_sending()
As MeterDetailsReadings
inherits from MeterResponse
(which is used in MetersAggratesResponse
) it exposes the same data and methods.
For the meters battery and grid no additional details are provided, therefore no methods exist for those meters
powerwall.get_device_type()
#=> <DeviceType.GW1: 'hec'>
Get current grid status.
powerwall.get_grid_status()
#=> <GridStatus.Connected: 'SystemGridConnected'>
powerwall.is_grid_services_active()
#=> False
powerwall.get_operation_mode()
#=> <OperationMode.SELF_CONSUMPTION: ...>
powerwall.get_backup_reserve_percentage()
#=> 5.000019999999999 (%)
serials = powerwall.get_serial_numbers()
#=> ["...", "...", ...]
din = powerwall.get_gateway_din()
#=> 4159645-02-A--TGXXX
vin = powerwall.get_vin()
Take your powerwall on- and off-grid similar to the "Take off-grid" button in the Tesla app.
powerwall.set_island_mode(IslandMode.OFFGRID)
powerwall.set_island_mode(IslandMode.ONGRID)
$ python -m build
To run unit tests use tox:
$ tox -e unit
$ tox -e integration