Skip to content

Commit

Permalink
Added initial working implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Zegorax committed Jul 22, 2024
1 parent 51a3e0b commit 0c30242
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 117 deletions.
14 changes: 14 additions & 0 deletions .env_sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
PORT=5051
DATA_DIR=/data/
SECRET_KEY=RANDOMLY_GENERATED_HERE
ADMIN_PASSWORD=HTPASS_PASSWORD_FOR_ADMIN_PAGE
BASE_URL=/map
MAPBOX_TOKEN=pk.BLA

# BACKEND_PROVIDER can be either "teslalogger" or "teslamate"
# For Teslamate, you need to deploy TeslamateAPI, available here : https://github.com/tobiasehlert/teslamateapi
BACKEND_PROVIDER=teslamate
BACKEND_PROVIDER_BASE_URL=http://insert-base-api-here:withport/
BACKEND_PROVIDER_CAR_ID=1

TZ=Europe/Berlin
69 changes: 32 additions & 37 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@

load_dotenv()
MAPBOX_TOKEN = os.getenv('MAPBOX_TOKEN')
TESLALOGGER_BASEURL = os.getenv('TESLALOGGER_BASEURL')
TESLALOGGER_CARID = os.getenv('TESLALOGGER_CARID', 1)
BASE_URL = os.getenv('BASE_URL')
PORT = os.getenv('PORT', 5051)
DATA_DIR = os.getenv('DATA_DIR', '/data/')

BACKEND_PROVIDER = os.getenv('BACKEND_PROVIDER', 'teslalogger')
BACKEND_PROVIDER_BASE_URL = os.getenv('BACKEND_PROVIDER_BASE_URL')
BACKEND_PROVIDER_CAR_ID = os.getenv('BACKEND_PROVIDER_CAR_ID', 1)

# Backend provider instanciation
provider_factory = BackendProviderFactory(BACKEND_PROVIDER)
backend = provider_factory.get_instance()
provider_factory = BackendProviderFactory(BACKEND_PROVIDER, BACKEND_PROVIDER_BASE_URL, BACKEND_PROVIDER_CAR_ID)
provider = provider_factory.get_instance()

provider.refresh_data()

app = Flask(__name__)
app.secret_key = os.getenv('SECRET_KEY')
Expand Down Expand Up @@ -161,44 +163,37 @@ def carstate(shortuuid):
conn.close()

if result:
lat = result[0]
lng = result[1]
db_latitude = result[0]
db_longitude = result[1]
expiry = result[2]

if expiry > time.time():
teslalogger = requests.get(TESLALOGGER_BASEURL + 'currentjson/' + TESLALOGGER_CARID + '/')

carstate = {
'latitude': teslalogger.json()['latitude'],
'longitude': teslalogger.json()['longitude'],
'odometer': teslalogger.json()['odometer'],
'driving': teslalogger.json()['driving'],
'charging': teslalogger.json()['charging'],
'battery_level': teslalogger.json()['battery_level'],
}

provider.refresh_data()

temp_carstate = vars(provider)

# Check if ETA destination is similar and use Tesla provided destination if it's within 250m
destination_db = (lat, lng)
if teslalogger.json()['active_route_destination']:
destination_tesla = (teslalogger.json()['active_route_latitude'], teslalogger.json()['active_route_longitude'])
distance = geodesic(destination_db, destination_tesla).km

if distance < 0.25:
carstate['eta_destination_lat'] = destination_tesla[0]
carstate['eta_destination_lng'] = destination_tesla[1]
carstate['eta_destination_tesla_seconds'] = teslalogger.json()['active_route_minutes_to_arrival'] * 60
carstate['eta_destination_tesla_battery_level'] = teslalogger.json()['active_route_energy_at_arrival']
# destination_db = (lat, lng)
if provider.active_route_destination:
tesla_destination = (provider.active_route_latitude, provider.active_route_longitude)

if not db_latitude or not db_longitude:
# If the lat/lon for the destination was not set on the shared link stored in DB, use the destination set in Tesla
temp_carstate['eta_destination_lat'] = provider.active_route_latitude
temp_carstate['eta_destination_lng'] = provider.active_route_longitude
temp_carstate['eta_destination_tesla_seconds'] = provider.active_route_seconds_to_arrival
temp_carstate['eta_destination_tesla_battery_level'] = provider.active_route_energy_at_arrival
else:
carstate['eta_destination_lat'] = destination_db[0]
carstate['eta_destination_lng'] = destination_db[1]
carstate['eta_waypoint_lat'] = destination_tesla[0]
carstate['eta_waypoint_lng'] = destination_tesla[1]
temp_carstate['eta_destination_lat'] = db_latitude
temp_carstate['eta_destination_lng'] = db_longitude
temp_carstate['eta_waypoint_lat'] = provider.active_route_latitude
temp_carstate['eta_waypoint_lng'] = provider.active_route_longitude

else:
carstate['eta_destination_lat'] = destination_db[0]
carstate['eta_destination_lng'] = destination_db[1]
temp_carstate['eta_destination_lat'] = provider.latitude
temp_carstate['eta_destination_lng'] = provider.longitude

return carstate
return temp_carstate
else:
return('Link Expired'), 410
else:
Expand Down Expand Up @@ -236,12 +231,12 @@ def map_admin():

print(result)

teslalogger = requests.get(TESLALOGGER_BASEURL + 'currentjson/' + TESLALOGGER_CARID + '/')
provider.refresh_data()

if 'uuid' in locals():
return render_template('map_admin.html.j2', result=result, BASE_URL=BASE_URL, uuid=uuid, mbtoken=MAPBOX_TOKEN, car_location=[teslalogger.json()['longitude'],teslalogger.json()['latitude']])
return render_template('map_admin.html.j2', result=result, BASE_URL=BASE_URL, uuid=uuid, mbtoken=MAPBOX_TOKEN, car_location=[provider.longitude, provider.latitude])
else:
return render_template('map_admin.html.j2', result=result, BASE_URL=BASE_URL, mbtoken=MAPBOX_TOKEN, car_location=[teslalogger.json()['longitude'],teslalogger.json()['latitude']])
return render_template('map_admin.html.j2', result=result, BASE_URL=BASE_URL, mbtoken=MAPBOX_TOKEN, car_location=[provider.longitude, provider.latitude])

@app.template_filter('fromtimestamp')
def _jinja2_filter_datetime(date, fmt=None):
Expand Down
48 changes: 16 additions & 32 deletions backendproviders/teslalogger.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,20 @@
from interfaces.backendinterface import IBackendProvider
import requests

class TeslaloggerBackendProvider(IBackendProvider):

def latitude():
pass

def longitude():
pass

def odometer():
pass

def is_driving():
pass

def is_charging():
pass

def get_battery_level():
pass

def active_route_destination():
pass

def active_route_latitude():
pass

def active_route_longitude():
pass

def active_route_minutes_to_arrival():
pass
def refresh_data(self):
data = requests.get(f"{self.base_url}/currentjson/{self.car_id}/").json()

def active_route_energy_at_arrival():
pass
self.latitude = data["latitude"]
self.longitude = data["longitude"]
self.odometer = data["odometer"]
self.is_driving = data["driving"]
self.is_charging = data["charging"]
self.battery_level = data["battery_level"]

self.active_route_latitude = data["active_route_latitude"]
self.active_route_longitude = data["active_route_longitude"]

self.active_route_destination = data["active_route_destination"]
self.active_route_minutes_to_arrival = data["active_route_minutes_to_arrival"]
self.active_route_energy_at_arrival = data["active_route_energy_at_arrival"]
23 changes: 21 additions & 2 deletions backendproviders/teslamate.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
from interfaces.backendinterface import IBackendProvider
import requests

class TeslamateBackendProvider():
pass
class TeslamateBackendProvider(IBackendProvider):
def refresh_data(self):

data = requests.get(f"{self.base_url}/api/v1/cars/{self.car_id}/status").json()["data"]["status"]

self.latitude = data["car_geodata"]["latitude"]
self.longitude = data["car_geodata"]["longitude"]
self.odometer = data["odometer"]
self.is_driving = data["driving_details"]["shift_state"] is not "P"
self.is_charging = data["charging_details"]["time_to_full_charge"] > 0
self.battery_level = data["battery_details"]["battery_level"]


self.active_route_latitude = data["driving_details"]["active_route"]["location"]["latitude"]
self.active_route_longitude = data["driving_details"]["active_route"]["location"]["longitude"]

self.active_route_destination = data["driving_details"]["active_route"]["destination"]
self.active_route_minutes_to_arrival = data["driving_details"]["active_route"]["minutes_to_arrival"]
self.active_route_energy_at_arrival = data["driving_details"]["active_route"]["energy_at_arrival"]
10 changes: 5 additions & 5 deletions interfaces/backendfactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
from interfaces.backendinterface import IBackendProvider

class BackendProviderFactory:
def _load_provider(self, name):
def _load_provider(self, name, base_url, car_id):
if name == "teslalogger":
return teslalogger.TeslaloggerBackendProvider()
return teslalogger.TeslaloggerBackendProvider(base_url, car_id)
elif name == "teslamate":
return teslamate.TeslamateBackendProvider()
return teslamate.TeslamateBackendProvider(base_url, car_id)
else:
raise Exception(f"Unknown backend provider : {name}. Available choices are 'teslalogger' or 'teslamagte'.")


def __init__(self, provider_name):
def __init__(self, provider_name, base_url, car_id):
self.provider_name = provider_name

self.provider_instance = self._load_provider(provider_name)
self.provider_instance = self._load_provider(provider_name, base_url, car_id)

def get_instance(self) -> IBackendProvider:
return self.provider_instance
69 changes: 28 additions & 41 deletions interfaces/backendinterface.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,33 @@
from abc import ABC, abstractmethod

class IBackendProvider(ABC):
base_url: str = None
car_id: int = None

latitude: float = None
longitude: float = None
odometer: float = None
is_driving: bool = False
is_charging: bool = False
battery_level: int = 0

active_route_destination: str = None
active_route_latitude: float = None
active_route_longitude: float = None
active_route_minutes_to_arrival: float = None
active_route_energy_at_arrival: int = None


def __init__(self, base_url, car_id):
self.base_url = base_url
self.car_id = car_id

print(f"Starting provider. BASE_URL : {base_url}, CAR_ID : {car_id}")

@abstractmethod
def latitude():
pass

@abstractmethod
def longitude():
pass

@abstractmethod
def odometer():
pass

@abstractmethod
def is_driving():
pass

@abstractmethod
def is_charging():
pass

@abstractmethod
def get_battery_level():
pass

@abstractmethod
def active_route_destination():
pass

@abstractmethod
def active_route_latitude():
pass

@abstractmethod
def active_route_longitude():
pass

@abstractmethod
def active_route_minutes_to_arrival():
def refresh_data(self):
pass
@abstractmethod
def active_route_energy_at_arrival():
pass

@property
def active_route_seconds_to_arrival(self) -> float:
return self.active_route_minutes_to_arrival * 60

0 comments on commit 0c30242

Please sign in to comment.