Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/TeslaBleHttpProxy.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions .idea/go.imports.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/golinter.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

251 changes: 1 addition & 250 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,253 +2,4 @@

TeslaBleHttpProxy is a program written in Go that receives HTTP requests and forwards them via Bluetooth to a Tesla vehicle. The program can, for example, be easily used together with [evcc](https://github.com/evcc-io/evcc).

The program stores the received requests in a queue and processes them one by one. This ensures that only one Bluetooth connection to the vehicle is established at a time.

## Table of Contents

- [How to install](#how-to-install)
- [Docker compose](#docker-compose)
- [Build yourself](#build-yourself)
- [Generate key for vehicle](#generate-key-for-vehicle)
- [Setup EVCC](#setup-evcc)
- [API](#api)
- [Vehicle Commands](#vehicle-commands)
- [Vehicle Data](#vehicle-data)
- [Body Controller State](#body-controller-state)
- [Version of Proxy](#version-of-proxy)
- [Troubleshooting](#troubleshooting)

## How to install

You can either compile and use the Go program yourself or install it in a Docker container. ([detailed instruction](docs/installation.md))

### Docker compose

Below you will find the necessary contents for your `docker-compose.yml`:

```
services:
tesla-ble-http-proxy:
image: wimaha/tesla-ble-http-proxy
container_name: tesla-ble-http-proxy
volumes:
- ~/TeslaBleHttpProxy/key:/key
- /var/run/dbus:/var/run/dbus
restart: always
privileged: true
network_mode: host
cap_add:
- NET_ADMIN
- SYS_ADMIN
```

Please remember to create an empty folder where the keys can be stored later. In this example, it is `~/TeslaBleHttpProxy/key`.

Pull and start TeslaBleHttpProxy with `docker compose up -d`.

Note that you can optionally set environment variables to override the default behavior. See [environment variables](docs/environment_variables.md) for more information.

**Key Security:** Private keys are protected by UNIX file permissions (0600 - owner read/write only). Ensure the key directory has proper permissions and is not accessible to unauthorized users.

### Build yourself

Download the code and save it in a folder named 'TeslaBleHttpProxy'. From there, you can easily compile the program.

```
go build .
./TeslaBleHttpProxy
```

Please remember to create an empty folder called `key` where the keys can be stored later.

Note that you can optionally set environment variables to override the default behavior. See [environment variables](docs/environment_variables.md) for more information.

## Generate key for vehicle

*(Here, the simple, automatic method is described. Besides the automatic method, you can also generate the keys [manually](docs/manually_gen_key.md).)*

**Security Recommendation:** We recommend using the **Charging Manager** role for security. It provides limited access suitable for charging management and works perfectly with [evcc tesla-ble template](https://docs.evcc.io/docs/devices/vehicles#tesla-ble). The Charging Manager role can:
- Read vehicle data
- Authorize charging-related commands: `wake`, `charge_start`, `charge_stop`, `set_charging_amps`

The **Owner** role provides full access to all vehicle functions (unlock, start, etc.) and should only be used if you need non-charging functions.

To generate the required keys browse to `http://YOUR_IP:8080/dashboard`. In the dashboard you will see that the keys are missing:

<img src="docs/proxy1.png" alt="Picture of the Dashboard with missing keys." width="40%" height="40%" style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); margin-bottom: 10px;">

Please click on `Generate` for the **Charging Manager** role (recommended for security). The keys will be automatically generated and saved. The Charging Manager key will be set as active by default.

<img src="docs/proxy2b.png" alt="Picture of the Dashboard with success message and keys." width="40%" height="40%"><br/>
<img src="docs/proxy2.png" alt="Picture of the Dashboard with success message and keys." width="40%" height="40%" style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); margin-bottom: 10px;">

After that please enter your VIN under `Setup Vehicle`. Before you proceed make sure your vehicle is awake! So you have to manually wake the vehicle before you send the key to the vehicle.

<img src="docs/proxy3.png" alt="Picture of Setup Vehicle Part of the Dashboard." width="40%" height="40%" style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); margin-bottom: 10px;">

The key is now sent to the vehicle. To complete the process, confirm by tapping your NFC card on the center console. (Note: There will be no message on the Tesla screen before confirmation with the NFC card.)

<img src="docs/proxy6.png" alt="Picture of success message sent add-key request." width="40%" height="40%">

You can now close the dashboard and use the proxy. 🙂

## Setup EVCC

You can use the following configuration in evcc (recommended):

```
vehicles:
- name: tesla
type: template
template: tesla-ble
title: Your Tesla (optional)
capacity: 60 # Akkukapazität in kWh (optional)
vin: VIN # Erforderlich für BLE-Verbindung
url: IP # URL des Tesla BLE HTTP Proxy
port: 8080 # Port des Tesla BLE HTTP Proxy (optional)
```

If you want to use this proxy only for commands, and not for vehicle data, you can use the following configuration. The vehicle data is then fetched via the Tesla API by evcc.

```
- name: model3
type: template
template: tesla
title: Tesla
icon: car
commandProxy: http://YOUR_IP:8080
accessToken: YOUR_ACCESS_TOKEN
refreshToken: YOUR_REFRSH_TOKEN
capacity: 60
vin: YOUR_VIN
```

(Hint for multiple vehicle support: https://github.com/wimaha/TeslaBleHttpProxy/issues/40)

## API

### Vehicle Commands

The program uses the same interfaces as the Tesla [Fleet API](https://developer.tesla.com/docs/fleet-api#vehicle-commands). Currently, the following requests are supported:

- wake_up
- charge_start
- charge_stop
- set_charging_amps
- set_charge_limit
- auto_conditioning_start
- auto_conditioning_stop
- charge_port_door_open
- charge_port_door_close
- flash_lights
- honk_horn
- door_lock
- door_unlock
- set_sentry_mode

By default, the program will return immediately after sending the command to the vehicle. If you want to wait for the command to complete, you can set the `wait` parameter to `true`.

**Wake Up Behavior:** Commands **automatically wake up** the vehicle if it is asleep. You don't need to manually wake the vehicle or use any parameters - the proxy handles this automatically to ensure commands execute successfully.

#### Example Request

*(All requests with method POST.)*

Start charging:
`http://localhost:8080/api/1/vehicles/{VIN}/command/charge_start`

Start charging and wait for the command to complete:
`http://localhost:8080/api/1/vehicles/{VIN}/command/charge_start?wait=true`

Stop charging:
`http://localhost:8080/api/1/vehicles/{VIN}/command/charge_stop`

Set charging amps to 5A:
`http://localhost:8080/api/1/vehicles/{VIN}/command/set_charging_amps` with body `{"charging_amps": "5"}`

Explicitly wake up the vehicle:
`http://localhost:8080/api/1/vehicles/{VIN}/command/wake_up`

### Vehicle Data

The vehicle data is fetched from the vehicle and returned in the response in the same format as the [Fleet API](https://developer.tesla.com/docs/fleet-api/endpoints/vehicle-endpoints#vehicle-data). Since a ble connection has to be established to fetch the data, it takes a few seconds before the data is returned.

**Caching:** VehicleData responses are cached in memory for faster subsequent requests. Each endpoint (e.g., `charge_state`, `climate_state`) is cached separately per VIN. The cache time can be configured via the `vehicleDataCacheTime` environment variable (default: 30 seconds). If all requested endpoints are cached and valid, the response is returned immediately without establishing a BLE connection.

**Wake Up Behavior:** By default, the car is **not** automatically woken up before fetching vehicle data. This allows for efficient data retrieval when the vehicle is already awake. If your vehicle is asleep and you need to wake it up first, you can use the `wakeup=true` parameter. The proxy uses intelligent caching to minimize unnecessary wakeup calls - if the vehicle was confirmed awake within the last 9 minutes, the sleep status check is skipped.

#### Example Request

*(All requests with method GET.)*

Get vehicle data:
`http://localhost:8080/api/1/vehicles/{VIN}/vehicle_data`

Get vehicle data with automatic wakeup:
`http://localhost:8080/api/1/vehicles/{VIN}/vehicle_data?wakeup=true`

Currently you will receive the following data:

- charge_state
- climate_state

If you want to receive specific data, you can add the endpoints to the request. For example:

`http://localhost:8080/api/1/vehicles/{VIN}/vehicle_data?endpoints=charge_state`

Get specific data with automatic wakeup:
`http://localhost:8080/api/1/vehicles/{VIN}/vehicle_data?endpoints=charge_state&wakeup=true`

This is recommended if you want to receive data frequently, since it will reduce the time it takes to receive the data.

### Body Controller State

The body controller state is fetched from the vehicle and returnes the state of the body controller. The request does not wake up the vehicle. The following information is returned:

- `vehicleLockState`
- `VEHICLELOCKSTATE_UNLOCKED`
- `VEHICLELOCKSTATE_LOCKED`
- `VEHICLELOCKSTATE_INTERNAL_LOCKED`
- `VEHICLELOCKSTATE_SELECTIVE_UNLOCKED`
- `vehicleSleepStatus`
- `VEHICLE_SLEEP_STATUS_UNKNOWN`
- `VEHICLE_SLEEP_STATUS_AWAKE`
- `VEHICLE_SLEEP_STATUS_ASLEEP`
- `userPresence`
- `VEHICLE_USER_PRESENCE_UNKNOWN`
- `VEHICLE_USER_PRESENCE_NOT_PRESENT`
- `VEHICLE_USER_PRESENCE_PRESENT`

#### Request

*(All requests with method GET.)*

Get body controller state:
`http://localhost:8080/api/1/vehicles/{VIN}/body_controller_state`

### Version of Proxy

Get version of proxy:
`http://localhost:8080/api/proxy/1/version`

The response will contain the version of the proxy.

## Troubleshooting

### Vehicle Requirements

TeslaBleHttpProxy requires your Tesla vehicle to support **Phone Key** functionality, as it relies on Bluetooth Low Energy (BLE) for communication. Most Tesla models from 2021 onward support Phone Key, but some older models (e.g., Model X 2015–2020) may not. Please verify Phone Key support in your Tesla app or consult Tesla's [Vehicle Keys Support Page](https://www.tesla.com/support/tesla-vehicle-keys) before setting up the proxy.

### Connection Timeouts

Due to BLE's power-saving design, Tesla vehicles may terminate connections after ~30 seconds, causing "connection timeout" logs. This is normal, and the proxy reconnects automatically, ensuring EVCC or other integrations work without issues. Keep the proxy device within ~5-10 meters of the vehicle for reliable connections.

### BLE Device Limit (Maximum 3 Devices)

**Problem:** Intermittent connection failures, undefined loss of connections, or unreliable behavior with evcc.

**Solution:** Tesla vehicles only accept a **maximum of 3 BLE keys/devices simultaneously**. If you exceed this limit (e.g., multiple phones, smartwatches, and the proxy), you'll experience connection issues.

**Fix:** Close the Tesla app on mobile phones when not needed, especially if you're also wearing a smartwatch with the Tesla app. The proxy counts as one device, so limit other active BLE connections accordingly.

This is a Tesla vehicle constraint, not a limitation of TeslaBleHttpProxy.
This program is a fork of the main TeslaBleHttpProxy with some changes so it works correctly with EVCC.
2 changes: 1 addition & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func LoadConfig() *Config {

scanTimeout := os.Getenv("scanTimeout")
if scanTimeout == "" {
scanTimeout = "5" // default value
scanTimeout = "2" // default value
}
scanTimeoutInt, err := strconv.Atoi(scanTimeout)
if err != nil {
Expand Down
15 changes: 14 additions & 1 deletion internal/api/handlers/tesla.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ var (
vehicleDataCacheMux sync.RWMutex
)

var vehicleOutOfRange = true

func commonDefer(w http.ResponseWriter, response *models.Response) {
var ret models.Ret
ret.Response = *response
Expand Down Expand Up @@ -176,6 +178,7 @@ func VehicleData(w http.ResponseWriter, r *http.Request) {
} else {
// Cache expired for this endpoint
logging.Debug("VehicleData endpoint cache expired", "VIN", vin, "Endpoint", endpoint, "Age", age)
cachedData[endpoint] = cachedEntry.data
missingEndpoints = append(missingEndpoints, endpoint)
}
} else {
Expand Down Expand Up @@ -213,11 +216,17 @@ func VehicleData(w http.ResponseWriter, r *http.Request) {
apiResponse.Ctx = r.Context()

wg.Add(1)
autoWakeup := r.URL.Query().Get("wakeup") == "true"
autoWakeup := r.URL.Query().Get("wakeup") == "true" || vehicleOutOfRange
control.BleControlInstance.PushCommand(command, vin, map[string]interface{}{"endpoints": endpoints}, &apiResponse, autoWakeup)

wg.Wait()

if !apiResponse.Result && strings.Contains(apiResponse.Error, "not in range") && !vehicleOutOfRange {
logging.Debug("Vehicle out of range. Setting bool vehicleOutOfRange", "VIN", vin)
fmt.Printf("Vehicle out of range. Setting bool vehicleOutOfRange")
vehicleOutOfRange = true
}

if apiResponse.Result {
// Parse the BLE response to extract individual endpoint data
var fetchedData map[string]json.RawMessage
Expand Down Expand Up @@ -256,6 +265,7 @@ func VehicleData(w http.ResponseWriter, r *http.Request) {
return
}

vehicleOutOfRange = false
response.Result = true
response.Reason = "The request was successfully processed."
response.Response = responseJson
Expand All @@ -267,12 +277,15 @@ func VehicleData(w http.ResponseWriter, r *http.Request) {
for endpoint, data := range cachedData {
combinedResponse[endpoint] = data
}

responseJson, err := json.Marshal(combinedResponse)
if err != nil {
response.Result = false
response.Reason = apiResponse.Error

return
}

response.Result = true
response.Reason = "The request was partially processed from cache. Some data may be stale."
response.Response = responseJson
Expand Down
Loading
Loading