Add support for Tesla Powerwall

Mine and my father in-law’s setup have stopped working too. We were using the original setup and we were not using a Tesla API Token. I have tried to follow the instructions for to add the Tesla API token as explained by @craigrouse and when I get to the stage of running the tesla-auth.py I get an ‘OSError: Could not find a suitable TLS CA certificate bundle error.’ Any ideas?

I assume this still works as @Hodor has said their setup is still working, but has to refresh the token every month. It would be better if Tesla just added the function to fully charge the battery in the app.

TIA

Sorry I have been offline for some time.

I also got it offline and was able to workaround it. Seems TeslaPy was using a method to login that Tesla completely removed. Now it has to go through their web login (this is where all the other Tesla integrations were breaking).
To get it to work again:

  1. Update TeslaPy to 2.3.0: go to custom_components/tesla_gateway/manifest.json, look for the requirements line, it should look like:
"requirements": ["teslapy==2.3.0"],
  1. In custom_components/tesla_gateway/__init__.py, remove the password from being passed to the TeslaPy constructor, replace something that looks like
tesla = teslapy.Tesla(domain_config[CONF_USERNAME], domain_config[CONF_PASSWORD])

with

tesla = teslapy.Tesla(domain_config[CONF_USERNAME])

(Just remove the second parameter being passed)
3. In some machine (not necessary to do in the same as homeassistant), install teslapy and pywebview:

python3 -m pip install teslapy
python3 -m pip install pywebview 
  1. Create a py file (I named it tesla_login.py) with the following contents:
import webview
import teslapy

def custom_auth(url):
    result = ['']
    window = webview.create_window('Login', url)
    def on_loaded():
        result[0] = window.get_current_url()
        if 'void/callback' in result[0].split('?')[0]:
            window.destroy()
    window.loaded += on_loaded
    webview.start()
    return result[0]

with teslapy.Tesla('<fill with the tesla's account email>', authenticator=custom_auth) as tesla:
    tesla.fetch_token()
  1. Replace <fill with the tesla's account email> with the email from your account
  2. execute the py script
python3 ./tesla_login.py
  1. When executing it, it will pop a tesla login page, login. That will generate a cache.json file.
  2. Copy the cache.json file to homeassistant’s conf root (there should be one there) (overwrite it)
  3. Restart HA

Moving forward, we need to integrate the login into HA’s config flow. This will allow this login process to happen within HA. The integration has a way to forward the url and do the login, and HA has a way to integrate such logins: Data Entry Flow | Home Assistant Developer Docs
After that, we could potentially use this same integration for all Tesla-related things (that will be quite some work), and forget about pasting tokens all over the place (tired of having 3 different integrations that constantly break and have to paste tokens in)

Hope that helps,
Esteban

2 Likes

Thanks, @estebanp ! Glad to see you’re back. I made the changes and am getting an error. I was getting this error prior to your changes, so not related to them, but thought that the error was due to them.

When I call the Tesla Gateway: set_operation service under development tools, I get this error:

service: tesla_gateway.set_operation
data:
real_mode: self_consumption
backup_reserve_percent: ‘100’

Failed to call service tesla_gateway.set_operation. Unknown error

Any hints on where I should begin looking?

I suggest enabling the logs and getting a callstack of the error.

Hmm, I haven’t ever seen this one before.

2022-01-17 12:15:40 ERROR (MainThread) [homeassistant.helpers.script.websocket_api_script] websocket_api script: Error executing script. Unexpected error for call_service at pos 1: Could not find a suitable TLS CA certificate bundle, invalid path: nXz-Pdt-uxX-B7E
Traceback (most recent call last):
raise IOError("Could not find a suitable TLS CA certificate bundle, "
OSError: Could not find a suitable TLS CA certificate bundle, invalid path: nXz-Pdt-uxX-B7E
2022-01-17 12:15:40 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [2806694936] Error handling message: Unknown error

Ah, sorry about that. In the __init__.py file, remove the password from being passed to the TeslaPy constructor, it should look like:

tesla = teslapy.Tesla(conf_user)

Here’s my init..py. Remove this line? tesla = teslapy.Tesla(domain_config[CONF_USERNAME], domain_config[CONF_PASSWORD])

"""
Monitors and controls the Tesla gateway.
"""
import logging

import asyncio
import voluptuous as vol
import teslapy

from homeassistant.const import (
    CONF_USERNAME,
    CONF_PASSWORD
    )
import homeassistant.helpers.config_validation as cv

DOMAIN = 'tesla_gateway'

_LOGGER = logging.getLogger(__name__)

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Required(CONF_USERNAME): cv.string,
        vol.Required(CONF_PASSWORD): cv.string
    }),
}, extra=vol.ALLOW_EXTRA)

@asyncio.coroutine
def async_setup(hass, config):

    domain_config = config[DOMAIN]
    conf_user = domain_config[CONF_USERNAME]
    conf_password = domain_config[CONF_PASSWORD]
    
    tesla = teslapy.Tesla(domain_config[CONF_USERNAME], domain_config[CONF_PASSWORD])

    def get_battery():
        batteries = tesla.battery_list()
        if len(batteries) > 0:
            return batteries[0]
        else:
            return None

    @asyncio.coroutine
    async def set_operation(service):
        
        battery = await hass.async_add_executor_job(get_battery)
        if not battery:
            _LOGGER.warning('Battery object is None')
            return None

        await hass.async_add_executor_job(battery.set_operation, service.data['real_mode'])
        if 'backup_reserve_percent' in service.data:
            await hass.async_add_executor_job(battery.set_backup_reserve_percent, service.data['backup_reserve_percent'])

    hass.services.async_register(DOMAIN, 'set_operation', set_operation)

    @asyncio.coroutine
    async def set_reserve(service):
        
        battery = await hass.async_add_executor_job(get_battery)
        if not battery:
            _LOGGER.warning('Battery object is None')
            return None
            
        if 'backup_reserve_percent' in service.data:
            await hass.async_add_executor_job(battery.set_backup_reserve_percent, service.data['backup_reserve_percent'])

    hass.services.async_register(DOMAIN, 'set_reserve', set_reserve)

    return True

do not remove, replace:

tesla = teslapy.Tesla(domain_config[CONF_USERNAME], domain_config[CONF_PASSWORD])

with

tesla = teslapy.Tesla(domain_config[CONF_USERNAME])

it’s working now. you’re the best, Esteban!

1 Like

that’s all great, thanks !
I’d got most of the way there by trial and error, but hadn’t taken out the password bit of init.

Hello, I was originally getting the error ‘Failed to call service tesla_gateway.set_operation. Unknown error’ but changed my init.py to this

tesla = teslapy.Tesla(domain_config[CONF_USERNAME])

However when I fire this event

service: tesla_gateway.set_reserve
data_template:
backup_reserve_percent: 0

I now get this error

Unable to find service tesla_gateway.set_reserve

As always any thoughts would be appreciated.

Update
Have removed the various integrations, restarted, re-added the integrations and amended the 2 tesla_gateway files as above, again restarted, and all now looks good…if in doubt go back to the beginning, lol

I have modified init.py and manifest.json, generated the cache.json and copied it the the config directory, but I still get this during boot up:

2022-02-09 07:23:32 ERROR (MainThread) [homeassistant.setup] Error during setup of component tesla_gateway
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/setup.py", line 227, in _async_setup_component
result = await task
File "/usr/local/lib/python3.9/asyncio/coroutines.py", line 124, in coro
res = func(*args, **kw)
File "/config/custom_components/tesla_gateway/__init__.py", line 30, in async_setup
domain_config = config[DOMAIN]
KeyError: 'tesla_gateway'

Any suggestions?

I added this python script to my config directory and it runs fine:

import teslapy
with teslapy.Tesla('[email protected]') as tesla:
    if tesla.authorized:
        vehicles = tesla.vehicle_list()
        print(vehicles[0])

so I know the cache.json file is correct.

I’m relatively new to HA and very new to adding custom components. Is the config directory not the place the cache.json file is supposed to go? I could not find any directory called “conf”, so I assumed it was short hand for “config”.

I managed to get around the “async_setup_component” issue by using the init.py posted by
@craigrouse, renamed __init__.py and with all parameters but the email address removed in the teslapy.Tesla() calls, as described by @estebanp

I generated the cache.json file using the method from @estebanp, and placed it in the /config directory.

It now works perfectly. Thanks for all the info posted here.

Just got powerwalls and trying to control them so I can utilize super off peak rates. I am a bit lost in the thread there - could someone kindly educate me on what the current status is? I have the powerwall integration working but seems to be read only sensor data. Is it possible to set values currently? If so, what is the best way to do it? Anything available through HACS?

I used TeslaPy to generate a cache.json file using the SSO token refresh mechanism, and then followed these two posts to patch the addon.

Initially got a rate limit error, restarted HA 24h later, and it’s been working for 24h. I’m not using HA to control. I’m using teslapy scripts to set my battery reserve levels based on my time of use rates. Here’s what it looks like on my Linux server.

username@hostnamename:~$ git clone https://github.com/tdorssers/TeslaPy
Cloning into 'TeslaPy'...

username@hostnamename:~$ cd TeslaPy/

username@hostnamename:~/TeslaPy$ pip install -r requirements.txt
<lots of output>

username@hostnamename:~/TeslaPy$ vi tesla_auth.py
import teslapy
with teslapy.Tesla('[email protected]') as tesla:
    if not tesla.authorized:
        tesla.refresh_token(refresh_token=input('Enter SSO refresh token: '))
        vehicles = tesla.vehicle_list()
        print(vehicles[0])


username@hostnamename:~/TeslaPy$ python3 tesla_auth.py
/usr/lib/python3/dist-packages/requests/__init__.py:89: RequestsDependencyWarning: urllib3 (1.26.8) or chardet (3.0.4) doesn't match a supported version!
  warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported "
Enter SSO refresh token: <input my token>

<output with my vehicle info>

Now you have a cache.json you can plug into the patched HA powerwall addon.


username@hostnamename:~/TeslaPy$ vi powerwall_set_battery_020percent.py
import teslapy
with teslapy.Tesla("[email protected]") as tesla:
        tesla.fetch_token()
        battery = tesla.battery_list()[0]
        battery.set_backup_reserve_percent(20)
        #print(battery) # should print battery status once successfully authenticated
        #print(battery.get_battery_data()['backup'])

username@hostnamename:~/TeslaPy$ vi powerwall_set_battery_100percent.py
import teslapy
with teslapy.Tesla("[email protected]") as tesla:
        tesla.fetch_token()
        battery = tesla.battery_list()[0]
        battery.set_backup_reserve_percent(100)
        #print(battery) # should print battery status once successfully authenticated
        #print(battery.get_battery_data()['backup'])

username@hostnamename:~/TeslaPy$ vi tesla_test.py
import teslapy
with teslapy.Tesla("[email protected]") as tesla:
        tesla.fetch_token()
        battery = tesla.battery_list()[0]
        print(battery.get_battery_data())
        vehicles = tesla.vehicle_list()
        print(vehicles[0])


username@hostnamename:~/TeslaPy$ crontab -e

#Mon-Fri at 9:01pm, set battery reserve to 100 percent
1 21 * * 1-5 cd /home/username/TeslaPy/ && /usr/bin/python3 powerwall_set_battery_100percent.py

#Mon-Fri at 6:59am, set battery reserve to 20 percent
59 6 * * 1-5 cd /home/username/TeslaPy/ && /usr/bin/python3 powerwall_set_battery_020percent.py

#Saturday at 9am, update, test, and refresh the integration
00 9 * * 6 cd /home/username/TeslaPy/ && git pull && /usr/bin/python3 tesla_test.py && /usr/bin/python3 powerwall_set_battery_100percent.py
1 Like

Hi @estebanp!

Thanks for all this, although when I try to run the py script I get this error, any ideas?

  File "C:\Users\adam1\Desktop\tesla_login.py", line 16, in <module>
    tesla.fetch_token()
  File "C:\Users\adam1\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\teslapy\__init__.py", line 183, in fetch_token
    url = self.authorization_url()
  File "C:\Users\adam1\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\teslapy\__init__.py", line 162, in authorization_url
    response.raise_for_status()  # Raise HTTPError, if one occurred
  File "C:\Users\adam1\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\requests\models.py", line 960, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://auth.tesla.com/oauth2/v3/authorize?response_type=code&client_id=ownerapi&redirect_uri=https%3A%2F%2Fauth.tesla.com%2Fvoid%2Fcallback&scope=openid+email+offline_access&state=MOyX6HEzKqjo2nXp33Glu7rufW2dPA&code_challenge=ihUgFvc_t6k19B5Pn9eiP7VVqL1mZh4ePaXQyRhOFUw&code_challenge_method=S256&login_hint=MYEMAILADDRESS

An automation to reload the integration when it fails. If the service goes unavailable for 15 minutes, it will restart the integration and then waits four hours to avoid hammering the API.

  1. Find and open your core.config_entries file.
  2. Look for the name of the integration in the domain field (probably tesla_gateway)
  3. Copy out the entry ID.
  4. Create a new automation in YAML view. Update the entry ID. You can switch back to GUI after pasting this in to more easily select the correct entity you want to monitor.
alias: Reload Tesla Custom Integration
trigger:
  - platform: state
    entity_id: binary_sensor.grid_services_active
    to: unavailable
    for:
      hours: 0
      minutes: 15
      seconds: 0
condition: []
action:
  - service: homeassistant.reload_config_entry 
    data:
      entry_id: f9988hkjkd7e4dce1c459869786575e <REPLACE THIS ENTRY ID>
  - delay:
      hours: 4
      minutes: 0
      seconds: 0
      milliseconds: 0
mode: single

I think this belongs in the TeslaPy forum.

Check here: requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://auth.tesla.com/oauth2/v3/authorize? · Issue #60 · tdorssers/TeslaPy · GitHub

Since the authentication now moved to the tokens with 8hr lifespan, my method of manually updating the token as my password every month has stopped working.

Ive tried removing this integration removing any reference to it in configuration.yaml and installing from scratch but receive the following error:

Error during setup of component tesla_gateway
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/setup.py", line 235, in _async_setup_component
    result = await task
  File "/usr/local/lib/python3.9/asyncio/coroutines.py", line 124, in coro
    res = func(*args, **kw)
  File "/config/custom_components/tesla_gateway/__init__.py", line 30, in async_setup
    domain_config = config[DOMAIN]
KeyError: 'tesla_gateway'

Any ideas?

Ive tried updatng the requirements to teslapy 2.3.0

I cant run teslapy scripts to create a cache.json as im running on a virtual box running HA OS.

Any help greatly appreciated as I really would like to get this working again.