mirror of
https://github.com/flobz/psa_car_controller.git
synced 2026-08-26 10:17:18 +00:00
Feature abrp (#79)
* add ABRP connection * update doc * fix get_last_temp() * add image * update abrp * fix abrpname * fix no last postition * add type point * find model by vin * add Spacetourer * fix indent
This commit is contained in:
+51
@@ -0,0 +1,51 @@
|
||||
import json
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
import requests
|
||||
|
||||
from Car import Car
|
||||
from MyLogger import logger
|
||||
|
||||
|
||||
class Abrp:
|
||||
api_key = "1e28ad14-df16-49f0-97da-364c9154b44a"
|
||||
url = "https://api.iternio.com/1/tlm/send"
|
||||
|
||||
def __init__(self, token: str = "", abrp_enable_vin=None):
|
||||
if abrp_enable_vin is None:
|
||||
abrp_enable_vin = list()
|
||||
self.token = token
|
||||
self.abrp_enable_vin = set(abrp_enable_vin)
|
||||
self.proxies = None
|
||||
|
||||
def call(self, car: Car, ext_temp: float = None):
|
||||
try:
|
||||
if self.token is None or len(self.token) == 0:
|
||||
logger.error("No token provided")
|
||||
elif car.vin in self.abrp_enable_vin:
|
||||
energy = car.status.get_energy('Electric')
|
||||
tlm = {"utc": int(datetime.timestamp(energy.updated_at)),
|
||||
"soc": energy.level,
|
||||
"speed": getattr(car.status.kinetic, "speed", None),
|
||||
"car_model": car.get_abrp_name(),
|
||||
"current": car.status.battery.current,
|
||||
"is_charging": energy.charging.status == "InProgress",
|
||||
"lat": car.status.last_position.geometry.coordinates[1],
|
||||
"lon": car.status.last_position.geometry.coordinates[0],
|
||||
"power": energy.consumption
|
||||
}
|
||||
if ext_temp is not None:
|
||||
tlm["ext_temp"] = ext_temp
|
||||
params = {"tlm": json.dumps(tlm), "token": self.token, "api_key": self.api_key}
|
||||
response = requests.request("POST", self.url, params=params, proxies=self.proxies,
|
||||
verify=self.proxies is None)
|
||||
logger.debug(response.text)
|
||||
return response.json()["status"] == "ok"
|
||||
except (AttributeError, IndexError, ValueError):
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
def __iter__(self):
|
||||
yield "abrp_enable_vin", list(self.abrp_enable_vin)
|
||||
yield "token", self.token
|
||||
@@ -46,3 +46,12 @@ def clean_position(conn):
|
||||
logger.debug("Delete duplicate line")
|
||||
conn.execute("DELETE FROM position where Timestamp=?;", (res[1]["Timestamp"],))
|
||||
conn.commit()
|
||||
|
||||
|
||||
def get_last_temp(vin):
|
||||
conn = get_db()
|
||||
res = conn.execute("SELECT temperature FROM position WHERE VIN=? ORDER BY Timestamp DESC limit 1",
|
||||
(vin,)).fetchone()
|
||||
if res is None:
|
||||
return None
|
||||
return res[0]
|
||||
|
||||
+2
-2
@@ -173,10 +173,10 @@ def get_figures(trips: Trips, charging: Tuple[dict]):
|
||||
y=consumption_by_temp_df["consumption_km"], name="Trips"))
|
||||
consumption_fig_by_temp.update_layout(xaxis_title="average temperature in °C",
|
||||
yaxis_title="Consumption kWh/100Km")
|
||||
consumption_graph_by_temp = Graph(figure=consumption_fig_by_temp, id="consumption_fig_by_temp")
|
||||
consumption_graph_by_temp = html.Div(Graph(figure=consumption_fig_by_temp), id="consumption_graph_by_temp")
|
||||
|
||||
else:
|
||||
consumption_graph_by_temp = Graph(style={'display': 'none'})
|
||||
consumption_graph_by_temp = html.Div(Graph(style={'display': 'none'}), id="consumption_graph_by_temp")
|
||||
|
||||
|
||||
def __calculate_co2_per_kw(charging_data):
|
||||
|
||||
+59
-4
@@ -2,9 +2,10 @@ import json
|
||||
import traceback
|
||||
from datetime import datetime, timezone
|
||||
import dash_bootstrap_components as dbc
|
||||
from dash.dependencies import Output, Input
|
||||
from dash.dependencies import Output, Input, MATCH
|
||||
import dash_core_components as dcc
|
||||
import dash_html_components as html
|
||||
import dash_daq as daq
|
||||
|
||||
from MyLogger import logger
|
||||
from flask import jsonify, request, Response as FlaskResponse
|
||||
@@ -16,6 +17,12 @@ from web import figures
|
||||
from web.app import app, dash_app, myp, chc
|
||||
import web.db
|
||||
|
||||
RESPONSE = "-response"
|
||||
|
||||
EMPTY_DIV = "empty-div"
|
||||
|
||||
ABRP_SWITCH = 'abrp-switch'
|
||||
|
||||
ERROR_DIV = dbc.Alert("No data to show, there is probably no trips recorded yet", color="danger")
|
||||
trips: Trips
|
||||
chargings: dict
|
||||
@@ -25,7 +32,7 @@ min_date = max_date = min_millis = max_millis = step = marks = cached_layout = N
|
||||
@dash_app.callback(Output('trips_map', 'figure'),
|
||||
Output('consumption_fig', 'figure'),
|
||||
Output('consumption_fig_by_speed', 'figure'),
|
||||
Output('consumption_fig_by_temp', 'graph'),
|
||||
Output('consumption_graph_by_temp', 'children'),
|
||||
Output('consumption', 'children'),
|
||||
Output('tab_trips', 'children'),
|
||||
Output('tab_battery', 'children'),
|
||||
@@ -49,6 +56,19 @@ def display_value(value):
|
||||
figures.battery_table, max_millis, step, marks
|
||||
|
||||
|
||||
@dash_app.callback(Output({'role': ABRP_SWITCH + RESPONSE, 'vin': MATCH}, 'children'),
|
||||
Input({'role': ABRP_SWITCH, 'vin': MATCH}, 'id'),
|
||||
Input({'role': ABRP_SWITCH, 'vin': MATCH}, 'value'))
|
||||
def update_abrp(div_id, value):
|
||||
vin = div_id["vin"]
|
||||
if value:
|
||||
myp.abrp.abrp_enable_vin.add(vin)
|
||||
else:
|
||||
myp.abrp.abrp_enable_vin.discard(vin)
|
||||
myp.save_config()
|
||||
return " "
|
||||
|
||||
|
||||
@app.route('/get_vehicles')
|
||||
def get_vehicules():
|
||||
response = app.response_class(
|
||||
@@ -127,6 +147,21 @@ def get_recorded_position():
|
||||
return FlaskResponse(myp.get_recorded_position(), mimetype='application/json')
|
||||
|
||||
|
||||
@app.route('/abrp')
|
||||
def abrp():
|
||||
vin = request.args.get('vin', None)
|
||||
enable = request.args.get('enable', None)
|
||||
token = request.args.get('token', None)
|
||||
if vin is not None and enable is not None:
|
||||
if enable == '1':
|
||||
myp.abrp.abrp_enable_vin.add(vin)
|
||||
else:
|
||||
myp.abrp.abrp_enable_vin.discard(vin)
|
||||
if token is not None:
|
||||
myp.abrp.token = token
|
||||
return jsonify(dict(myp.abrp))
|
||||
|
||||
|
||||
@app.after_request
|
||||
def after_request(response):
|
||||
header = response.headers
|
||||
@@ -160,6 +195,25 @@ def update_trips():
|
||||
logger.error("update_trips (slider): %s", traceback.format_exc())
|
||||
return
|
||||
|
||||
|
||||
def __get_control_tabs():
|
||||
tabs = []
|
||||
for car in myp.vehicles_list:
|
||||
if car.label is None:
|
||||
label = car.vin
|
||||
else:
|
||||
label = car.label
|
||||
tabs.append(dbc.Tab(label=label, id="tab-" + car.vin, children=[
|
||||
daq.ToggleSwitch(
|
||||
id={'role': ABRP_SWITCH, 'vin': car.vin},
|
||||
value=car.vin in myp.abrp.abrp_enable_vin,
|
||||
label="Send data to ABRP"
|
||||
),
|
||||
html.Div(id={'role': ABRP_SWITCH + RESPONSE, 'vin': car.vin})
|
||||
]))
|
||||
return tabs
|
||||
|
||||
|
||||
def serve_layout():
|
||||
global cached_layout
|
||||
if cached_layout is None:
|
||||
@@ -188,13 +242,14 @@ def serve_layout():
|
||||
dbc.Tab(label="Charge", tab_id="charge", id="tab_charge", children=[figures.battery_table]),
|
||||
dbc.Tab(label="Map", tab_id="map", children=[
|
||||
dcc.Graph(figure=figures.trips_map, id="trips_map", style={"height": '90vh'})]),
|
||||
dbc.Tab(label="Control", tab_id="control", children=dbc.Tabs(id="control-tabs",
|
||||
children=__get_control_tabs()))
|
||||
],
|
||||
id="tabs",
|
||||
active_tab="summary",
|
||||
),
|
||||
html.Div(id="tab-content", className="p-4"),
|
||||
html.Div(id=EMPTY_DIV),
|
||||
])])
|
||||
|
||||
except (IndexError, TypeError, NameError):
|
||||
logger.warning("Failed to generate figure, there is probably not enough data yet")
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
Reference in New Issue
Block a user