Package: tesla-powerwall
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.Table of ContentsInstallationLimitationsAdjusting Backup Reserve PercentageUsageSetupAuthenticationGeneralErrorsResponseBattery levelCapacityBattery PacksPowerwall StatusSitemasterSiteinfoMetersAggregatesCurrent power supply/drawEnergy exported/importedDetailsDevice TypeGrid StatusOperation modePowerwalls Serial NumbersGateway DINVINOff-grid statusDevelopmentInstallationInstall the library via pip:$pipinstalltesla_powerwallLimitationsAdjusting Backup Reserve PercentageCurrently 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 solutionhere.
However, if you believe there exists a solution, feel free to open an issue detailing the solution.UsageFor a basic Overview of the functionality of this library you can take a look atexamples/example.py. You can run the example, by cloning the repo and executing in your shell:$exportPOWERWALL_IP=<ipofyourPowerwall>
$exportPOWERWALL_PASSWORD=<yourpassword>
$tox-eexampleSetupfromtesla_powerwallimportPowerwall# Create a simple powerwall object by providing the IPpowerwall=Powerwall("<ip of your Powerwall>")#=> <Powerwall ...># Create a powerwall object with more optionspowerwall=Powerwall(endpoint="<ip of your powerwall>",# Configure timeout; default is 10timeout=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 notverify_ssl=False)#=> <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 setverify_ssltoTrue.AuthenticationSince version 20.49.0 authentication is required for all methods. For that reason you must callloginbefore making a request to the API.
When you perform a request without being authenticated, anAccessDeniedErrorwill be thrown.To login you can either useloginorlogin_as.loginlogs you in asUser.CUSTOMERwhereas withlogin_asyou can choose a different user:fromtesla_powerwallimportUser# Login as customer without email# The default value for the email is ""awaitpowerwall.login("<password>")#=> <LoginResponse ...># Login as customer with emailawaitpowerwall.login("<password>","<email>")#=> <LoginResponse ...># Login with different userawaitpowerwall.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 validpowerwall.is_authenticated()#=> True# Logoutawaitpowerwall.logout()powerwall.is_authenticated()#=> FalseGeneralThe 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 existingPowerwall:fromtesla_powerwallimportAPI# Manually create API objectapi=API('https://<ip>/')# Perform get on 'system_status/soe'awaitapi.get_system_status_soe()#=> {'percentage': 97.59281925744594}# From existing powerwallapi=powerwall.get_api()awaitapi.get_system_status_soe()ThePowerwallobjet provides a wrapper around the API and exposes common methods.Battery levelGet charge in percent:awaitpowerwall.get_charge()#=> 97.59281925744594 (%)Get charge in watt:awaitpowerwall.get_energy()#=> 14807 (Wh)CapacityGet the capacity of your powerwall in watt:awaitpowerwall.get_capacity()#=> 28078 (Wh)Battery PacksGet information about the battery packs that are installed:Assuming that the battery is operational, you can retrive a number of values about each battery:batteries=awaitpowerwall.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#=> Falsebatteries[0].p_out#=> 260batteries[0].q_out#=> -1080batteries[0].v_out#=> 245.70batteries[0].f_out#=> 49.953batteries[0].i_out#=> -7.4batteries[0].grid_state#=> GridState.COMPLIANTbatteries[0].disabled_reasons#=> []If a battery is disabled it'sgrid_statewill beGridState.DISABLEDand some values will beNone. The variabledisabled_reasonsmight contain more information why the battery is disabled:...batteries[1].grid_state#=> GridState.DISABLEDbatteries[1].disabled_reasons#=> ["DisabledExcessiveVoltageDrop"]batteries[1].p_out#=> Nonebatteries[1].energy_charged#=> NonePowerwall Statusstatus=awaitpowerwall.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.GW2Sitemastersm=awaitpowerwall.get_sitemaster()#=> <SiteMaster ...>sm.status#=> StatusUpsm.running#=> truesm.connected_to_tesla#=> trueThe sitemaster can be started and stopped usingrun()andstop()Siteinfoinfo=awaitpowerwall.get_site_info()#=> <SiteInfo ...>info.site_name#=> 'Tesla Home'info.country#=> 'Germany'info.nominal_system_energy#=> 13.5 (kWh)info.timezone#=> 'Europe/Berlin'MetersAggregatesfromtesla_powerwallimportMeterTypemeters=awaitpowerwall.get_meters()#=> <MetersAggregates ...># access meter, but may return None when meter is not availablemeters.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 powerwallmeters.meters.keys()#=> [<MeterType.SITE: 'site'>, <MeterType.BATTERY: 'battery'>, <MeterType.LOAD: 'load'>, <MeterType.SOLAR: 'solar'>]Available meters are:solar,site,load,battery,generator, andbusway. Some of those meters might not be available based on the installation and raise MeterNotAvailableError when accessed.Current power supply/drawMeterprovides different methods for checking current power supply/draw:meters=awaitpowerwall.get_meters()meters.solar.get_power()#=> 0.4 (kW)meters.solar.instant_power#=> 409.941801071167 (W)meters.solar.is_drawing_from()#=> Truemeters.load.is_sending_to()#=> Truemeters.battery.is_active()#=> False# Different precision settings might return different resultsmeters.battery.is_active(precision=5)#=> TrueNote: For MeterType.LOADis_drawing_fromalwaysreturnsFalsebecause it cannot be drawn fromload.Energy exported/importedGet energy exported/imported in watt-hours (Wh) withenergy_exportedandenergy_imported. For the values in kilowatt-hours (kWh) useget_energy_exportedandget_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)DetailsYou can receive more detailed information about the meterssiteandsolar:meter_details=awaitpowerwall.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.13532458readings.i_a_current# same for i_b_current and i_c_current#=> 3.02readings.v_l1n# smae for v_l2n and v_l3n#=> 235.82readings.instant_power#=> -18.000023458readings.is_sending()AsMeterDetailsReadingsinherits fromMeterResponse(which is used inMetersAggratesResponse) it exposes the same data and methods.For the meters battery and grid no additional details are provided, therefore no methods exist for those metersDevice Typeawaitpowerwall.get_device_type()#=> <DeviceType.GW1: 'hec'>Grid StatusGet current grid status.awaitpowerwall.get_grid_status()#=> <GridStatus.Connected: 'SystemGridConnected'>awaitpowerwall.is_grid_services_active()#=> FalseOperation modeawaitpowerwall.get_operation_mode()#=> <OperationMode.SELF_CONSUMPTION: ...>awaitpowerwall.get_backup_reserve_percentage()#=> 5.000019999999999 (%)Powerwalls Serial Numbersawaitserials=powerwall.get_serial_numbers()#=> ["...", "...", ...]Gateway DINawaitdin=powerwall.get_gateway_din()#=> 4159645-02-A--TGXXXVINawaitvin=powerwall.get_vin()Off-grid status (Set Island mode)Take your powerwall on- and off-grid similar to the "Take off-grid" button in the Tesla app.Set powerwall to off-grid (Islanded)awaitpowerwall.set_island_mode(IslandMode.OFFGRID)Set powerwall to off-grid (Connected)awaitpowerwall.set_island_mode(IslandMode.ONGRID)Developmentpre-commitThis project uses pre-commit to run linters, formatters and type checking. You can easily run those checks locally:# Install the pre-commit hooks$pre-commitinstall
pre-commitinstalledat.git/hooks/pre-commitNow those checks will be execute on everygit commit. You can also execute all checks manually withpre-commit run --all-files.Building$python-mbuildTestingThe tests are split in unit and integration tests.
The unit tests are self-contained and can simply be run locally by executingtox -e unit, whereas the integration test, run against a real powerwall.Unit-TestsTo run unit tests use tox:$tox-eunitIntegration-TestsTo execute the integration tests you need to first provide some information about your powerwall:$exportPOWERWALL_IP=<ipofyourpowerwall>
$exportPOWERWALL_PASSWORD=<passwordforyourpowerwall>
$tox-eintegrationThe integration tests might take your powerwall off grid and bring it back online. Before running the tests, make sure that you know what you are doing!