diff --git a/README.md b/README.md
index dbc7526b1..e23e98f6e 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ TeslaSolarCharger is a service to set one or multiple Teslas' charging current u
- [Setting up TeslaMate including TeslaSolarCharger](#Setting-up-TeslaMate-including-TeslaSolarCharger)
- [docker-compose.yml content](#docker-composeyml-content)
- [First startup of the application](#first-startup-of-the-application)
+ - [Install and setup BLE API](#install-and-setup-ble-api)
- [Often used optional settings](#often-used-optional-settings)
- [Power Buffer](#power-buffer)
- [Home Battery](#home-battery)
@@ -27,7 +28,7 @@ TeslaSolarCharger is a service to set one or multiple Teslas' charging current u
## How to install
-You can either install the software in a Docker container or download the binaries and deploy it on any server.
+You can either install the software in a Docker container or download the binaries and deploy it on any server. In June 2024, Tesla implemented rate limits to their API, so there is a BLE (Bluetooth Low Energy, implemented since Bluetooth Version 4.0) capable device needed near the car. You can find details on how to set up BLE [here](#setup-ble-api).
### Docker compose
@@ -929,6 +930,185 @@ Assuming the `Measurement` node with `Type` `AC_Power` is the power your inverte
**Note:** These values are not needed. They are just used to show additional information.
+#### Install and setup BLE API
+To go around Teslas API limitations, you can use Bluetooth (BLE) to control your car. You can do this either by using the same device as your TSC is running on, or by using a separate device. Note: The device needs to be placed near the car.
+
+##### Install BLE API on the same device as TSC
+To set up the BLE API on the same device as your TSC is running on, you need to add the following lines to your docker-compose.yml:
+
+```yaml
+services:
+#here are all the other services like TeslaMate, TSC, etc.
+ bleapi:
+ image: ghcr.io/pkuehnel/teslasolarchargerbleapi:latest
+ container_name: TeslaSolarChargerBleApi
+ privileged: true
+ restart: unless-stopped
+ network_mode: host
+ environment:
+ - ASPNETCORE_URLS=http://+:7210
+ volumes:
+ - tscbleapi:/externalFiles
+ - /var/run/dbus:/var/run/dbus
+
+volumes:
+ #here are all the other volumes like teslamate-db, teslamate-grafana-data, etc.
+ tscbleapi:
+```
+
+You can also copy the complete content from here:
+
+ Complete file including BLE API
+
+```yaml
+version: '3.3'
+
+services:
+ teslamate:
+ image: teslamate/teslamate:latest
+ restart: always
+ environment:
+ - DATABASE_USER=teslamate
+ - DATABASE_PASS=secret ##You can change your password here
+ - DATABASE_NAME=teslamate
+ - DATABASE_HOST=database
+ - MQTT_HOST=mosquitto
+ - ENCRYPTION_KEY=supersecret ##You can change your encryption key here
+ - TZ=Europe/Berlin ##You can change your Timezone here
+ ports:
+ - 4000:4000
+ volumes:
+ - ./import:/opt/app/import
+ cap_drop:
+ - all
+
+ database:
+ image: postgres:15
+ restart: always
+ environment:
+ - POSTGRES_USER=teslamate
+ - POSTGRES_PASSWORD=secret ##You can change your password here
+ - POSTGRES_DB=teslamate
+ volumes:
+ - teslamate-db:/var/lib/postgresql/data
+
+ grafana:
+ image: teslamate/grafana:latest
+ restart: always
+ environment:
+ - DATABASE_USER=teslamate
+ - DATABASE_PASS=secret ##You can change your password here
+ - DATABASE_NAME=teslamate
+ - DATABASE_HOST=database
+ ports:
+ - 3100:3000
+ volumes:
+ - teslamate-grafana-data:/var/lib/grafana
+
+ mosquitto:
+ image: eclipse-mosquitto:2
+ restart: always
+ command: mosquitto -c /mosquitto-no-auth.conf
+ #ports:
+ # - 1883:1883
+ volumes:
+ - mosquitto-conf:/mosquitto/config
+ - mosquitto-data:/mosquitto/data
+
+ teslamateapi:
+ image: tobiasehlert/teslamateapi:latest
+ logging:
+ driver: "json-file"
+ options:
+ max-file: "5"
+ max-size: "10m"
+ restart: always
+ depends_on:
+ - database
+ environment:
+ - DATABASE_USER=teslamate
+ - DATABASE_PASS=secret ##You can change your password here
+ - DATABASE_NAME=teslamate
+ - DATABASE_HOST=database
+ - MQTT_HOST=mosquitto
+ - TZ=Europe/Berlin ##You can change your Timezone here
+ - ENABLE_COMMANDS=true
+ - COMMANDS_ALL=true
+ - API_TOKEN_DISABLE=true
+ - ENCRYPTION_KEY=supersecret ##You can change your encryption key here
+ #ports:
+ # - 8080:8080
+
+ teslasolarcharger:
+ image: pkuehnel/teslasolarcharger:latest
+ container_name: teslasolarcharger
+ logging:
+ driver: "json-file"
+ options:
+ max-file: "10"
+ max-size: "100m"
+ restart: always
+ depends_on:
+ - teslamateapi
+ environment:
+# - Serilog__MinimumLevel__Default=Verbose #uncomment this line and recreate container with docker compose up -d for more detailed logs
+ - TZ=Europe/Berlin ##You can change your Timezone here
+ ports:
+ - 7190:80
+ volumes:
+ - teslasolarcharger-configs:/app/configs
+
+ bleapi:
+ image: ghcr.io/pkuehnel/teslasolarchargerbleapi:latest
+ container_name: TeslaSolarChargerBleApi
+ privileged: true
+ restart: unless-stopped
+ network_mode: host
+ environment:
+ - ASPNETCORE_URLS=http://+:7210
+ volumes:
+ - tscbleapi:/externalFiles
+ - /var/run/dbus:/var/run/dbus
+
+volumes:
+ teslamate-db:
+ teslamate-grafana-data:
+ mosquitto-conf:
+ mosquitto-data:
+ teslasolarcharger-configs:
+ tscbleapi:
+```
+
+
+
+##### Install BLE API on a separate device
+To set up a separate device for the BLE API, you need to install Docker on the device, like described [here](#docker-compose). Thereafter, you can use the following docker-compose.yml and start the container with `docker compose up -d`:
+
+```yaml
+services:
+ bleapi:
+ image: ghcr.io/pkuehnel/teslasolarchargerbleapi:latest
+ container_name: TeslaSolarChargerBleApi
+ privileged: true
+ restart: unless-stopped
+ network_mode: host
+ environment:
+ - ASPNETCORE_URLS=http://+:7210
+ volumes:
+ - tscbleapi:/externalFiles
+ - /var/run/dbus:/var/run/dbus
+
+volumes:
+ tscbleapi:
+```
+
+##### Setup BLE (same device and separate device)
+After starting the BLE API, you need to add the BLE API Base URL to your TeslaSolarCharger configuration. The URL is `http://:7210/`
+
+Now you can pair each car by going to the `Car Settings` enable "Use BLE", click Save and then click on Pair Car. Note: It could take up to three tries to pair the car. After you get a message that pairing succeeded, you can test the API by clicking on the `Set to 7A`. Note: The car needs to be awake during the pairing and test process.
+
+
+
## Often used optional settings
When you are at this point, your car connected to any charging cable in your set home area should start charging based on solar power. But there are a few additional settings that are maybe helpful for your environment:
diff --git a/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor b/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor
index cccddc917..ae77efea6 100644
--- a/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor
+++ b/TeslaSolarCharger/Client/Pages/BaseConfiguration.razor
@@ -75,6 +75,15 @@ else
+
+
+
+
+
+
}
TeslaMate:
BLE Pairing and test
- TSC can use BLE instead of the Fleet API. This is useful to come around rate limits.
+ To come around rate limits TSC can use BLE instead of the Fleet API. This requires setting up a BLE API.
@if (_pairingResults.TryGetValue(carBasicConfiguration.Vin, out var result))
{
@@ -54,7 +54,7 @@ else
Before you can test BLE access you must pair the car with TSC. This includes placing the card on your center console and confirming the new "phone key" on the car's screen.
- After clicking the test button the front lights should flash. Note: The car needs to be awake for this test.
+ After clicking the test button the car 's current should be set to 7A. Note: The car needs to be awake for this test.
@if (_bleTestResults.TryGetValue(carBasicConfiguration.Vin, out var bleResult))
{
@@ -64,7 +64,7 @@ else
}
-
+
}
@@ -114,10 +114,11 @@ else
{
_bleTestResults.Remove(vin);
_loadingVins.Add(vin);
- var result = await HttpClient.GetFromJsonAsync($"/api/Ble/FlashLights?vin={vin}").ConfigureAwait(false) ?? new DtoBleResult { Success = false, Message = "Could not deserialize message from TSC." };
+ var resultString = await HttpClient.GetStringAsync($"/api/Ble/SetAmp?vin={vin}&s=7").ConfigureAwait(false);
+ var result = JsonConvert.DeserializeObject(resultString) ?? new DtoBleResult { Success = false, Message = "Could not deserialize message from TSC." };
if(result.Success && string.IsNullOrWhiteSpace(result.Message))
{
- result.Message = "Ble success seems to work. Please double check if lights were flashing.";
+ result.Message = "Ble access seems to work. Please double check if the charge current was set to 7A. Note: As TSC starts using BLE as soon as it is working you might see the 7A only for a short time as TSC changes it every 30 seconds by default.";
}
_bleTestResults[vin] = result;
_loadingVins.Remove(vin);
diff --git a/TeslaSolarCharger/Client/Pages/Index.razor b/TeslaSolarCharger/Client/Pages/Index.razor
index 8f25c6d1c..5f2b6673c 100644
--- a/TeslaSolarCharger/Client/Pages/Index.razor
+++ b/TeslaSolarCharger/Client/Pages/Index.razor
@@ -152,7 +152,7 @@ else
Car currently rate limited.
- Your car is rate limited by Tesla and TSC won't be able to set the charging power until @(car.RateLimitedUntil.Value.ToLocalTime().ToString("g")). For now there is no solution to this as Tesla implemented the rate limits without an option to pay for more. So even after the rate limit is over, TSC only has 50 requests. After the 50 requests within 24 hours, the rate limit is present again for the next 24 hours. As a workaround you can go to basic configuration, scroll down to advanced settings and set the Car power adjustment intervall to 1800 seconds. This leads to 50 calls per day max.
+ Your car is rate limited by Tesla and TSC won't be able to set the charging power until @(car.RateLimitedUntil.Value.ToLocalTime().ToString("g")). For now, the only solution for this is to set up a BLE device (see here). Currently there is no option from Tesla's side to pay for more requests per day. So even after the rate limit is over, TSC only has 50 requests for the next day. After the 50 requests within 24 hours, the rate limit is present again for the next 24 hours. Despite setting up the BLE API as a workaround, you can go to basic configuration, scroll down to advanced settings and set the Car power adjustment interval to 1800 seconds. This leads to 50 calls per day max but also means TSC can only adjust the charging power every 30 minutes.
diff --git a/TeslaSolarCharger/Server/Controllers/BleController.cs b/TeslaSolarCharger/Server/Controllers/BleController.cs
index ee494348f..dba513b59 100644
--- a/TeslaSolarCharger/Server/Controllers/BleController.cs
+++ b/TeslaSolarCharger/Server/Controllers/BleController.cs
@@ -12,13 +12,13 @@ public class BleController (IBleService bleService) : ApiBaseController
public Task PairKey(string vin) => bleService.PairKey(vin);
[HttpGet]
- public Task StartCharging(string vin) => bleService.StartCharging(vin);
+ public Task StartCharging(string vin) => bleService.StartCharging(vin);
[HttpGet]
- public Task StopCharging(string vin) => bleService.StopCharging(vin);
+ public Task StopCharging(string vin) => bleService.StopCharging(vin);
[HttpGet]
- public Task SetAmp(string vin, int amps) => bleService.SetAmp(vin, amps);
+ public Task SetAmp(string vin, int amps) => bleService.SetAmp(vin, amps);
[HttpGet]
public Task FlashLights(string vin) => bleService.FlashLights(vin);
diff --git a/TeslaSolarCharger/Server/Services/Contracts/IBleService.cs b/TeslaSolarCharger/Server/Services/Contracts/IBleService.cs
index 9d457cae8..81abc6f44 100644
--- a/TeslaSolarCharger/Server/Services/Contracts/IBleService.cs
+++ b/TeslaSolarCharger/Server/Services/Contracts/IBleService.cs
@@ -6,9 +6,9 @@ namespace TeslaSolarCharger.Server.Services.Contracts;
public interface IBleService
{
- Task StartCharging(string vin);
- Task StopCharging(string vin);
- Task SetAmp(string vin, int amps);
+ Task StartCharging(string vin);
+ Task StopCharging(string vin);
+ Task SetAmp(string vin, int amps);
Task FlashLights(string vin);
Task PairKey(string vin);
}
diff --git a/TeslaSolarCharger/Server/Services/TeslaBleService.cs b/TeslaSolarCharger/Server/Services/TeslaBleService.cs
index f6285f762..60474ee4d 100644
--- a/TeslaSolarCharger/Server/Services/TeslaBleService.cs
+++ b/TeslaSolarCharger/Server/Services/TeslaBleService.cs
@@ -16,7 +16,7 @@ public class TeslaBleService(ILogger logger,
ITeslamateApiService teslamateApiService,
ISettings settings) : IBleService
{
- public async Task StartCharging(string vin)
+ public async Task StartCharging(string vin)
{
logger.LogTrace("{method}({vin})", nameof(StartCharging), vin);
var request = new DtoBleRequest
@@ -25,6 +25,7 @@ public async Task StartCharging(string vin)
CommandName = "charging-start",
};
var result = await SendCommandToBle(request).ConfigureAwait(false);
+ return result;
}
public Task WakeUpCar(int carId)
@@ -32,7 +33,7 @@ public Task WakeUpCar(int carId)
throw new NotImplementedException();
}
- public async Task StopCharging(string vin)
+ public async Task StopCharging(string vin)
{
var request = new DtoBleRequest
{
@@ -40,9 +41,10 @@ public async Task StopCharging(string vin)
CommandName = "charging-stop",
};
var result = await SendCommandToBle(request).ConfigureAwait(false);
+ return result;
}
- public async Task SetAmp(string vin, int amps)
+ public async Task SetAmp(string vin, int amps)
{
logger.LogTrace("{method}({vin}, {amps})", nameof(SetAmp), vin, amps);
var request = new DtoBleRequest
@@ -52,6 +54,7 @@ public async Task SetAmp(string vin, int amps)
Parameters = new List { amps.ToString() },
};
var result = await SendCommandToBle(request).ConfigureAwait(false);
+ return result;
}
public async Task FlashLights(string vin)
@@ -71,7 +74,7 @@ public async Task PairKey(string vin)
var bleBaseUrl = configurationWrapper.BleBaseUrl();
if (string.IsNullOrWhiteSpace(bleBaseUrl))
{
- return new DtoBleResult() { Message = "BLE Base Url is not set.", StatusCode = HttpStatusCode.BadRequest, Success = false, };
+ return new DtoBleResult() { Message = "BLE Base URL is not set. Set a BLE URL in your base configuration.", StatusCode = HttpStatusCode.BadRequest, Success = false, };
}
bleBaseUrl += "Pairing/PairCar";
@@ -119,7 +122,7 @@ private async Task SendCommandToBle(DtoBleRequest request)
return new DtoBleResult()
{
Success = false,
- Message = "BLE Base Url is not set.",
+ Message = "BLE Base URL is not set. Set a BLE URL in your base configuration.",
StatusCode = HttpStatusCode.BadRequest,
};
}
diff --git a/TeslaSolarCharger/Shared/Dtos/BaseConfiguration/BaseConfigurationBase.cs b/TeslaSolarCharger/Shared/Dtos/BaseConfiguration/BaseConfigurationBase.cs
index 8f8191f2c..667f38dac 100644
--- a/TeslaSolarCharger/Shared/Dtos/BaseConfiguration/BaseConfigurationBase.cs
+++ b/TeslaSolarCharger/Shared/Dtos/BaseConfiguration/BaseConfigurationBase.cs
@@ -90,6 +90,7 @@ public class BaseConfigurationBase
public int? HomeBatteryChargingPower { get; set; }
public int? MaxCombinedCurrent { get; set; }
public int? MaxInverterAcPower { get; set; }
+ public string? BleApiBaseUrl { get; set; }
public FrontendConfiguration? FrontendConfiguration { get; set; }
diff --git a/TeslaSolarCharger/Shared/Wrappers/ConfigurationWrapper.cs b/TeslaSolarCharger/Shared/Wrappers/ConfigurationWrapper.cs
index 0c0bd2c52..b0da7e0ed 100644
--- a/TeslaSolarCharger/Shared/Wrappers/ConfigurationWrapper.cs
+++ b/TeslaSolarCharger/Shared/Wrappers/ConfigurationWrapper.cs
@@ -171,8 +171,12 @@ public string FleetApiClientId()
public string? BleBaseUrl()
{
- var environmentVariableName = "BleBaseUrl";
- var value = configuration.GetValue(environmentVariableName);
+ var value = GetBaseConfiguration().BleApiBaseUrl;
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ var environmentVariableName = "BleBaseUrl";
+ value = configuration.GetValue(environmentVariableName);
+ }
if (!string.IsNullOrWhiteSpace(value))
{
if (!value.EndsWith("/"))