add dashboard

This commit is contained in:
Florian Bezannier
2020-12-18 16:58:51 +01:00
parent fdeba33685
commit cd58a9c55a
7 changed files with 334 additions and 99 deletions
+150
View File
@@ -0,0 +1,150 @@
import json
import threading
from datetime import datetime, timezone
import dash
import dash_bootstrap_components as dbc
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
from MyLogger import logger
from flask import jsonify, request, Response as FlaskResponse
from web import figures
from MyPSACC import MyPSACC
dash_app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app = dash_app.server
myp = None
chc = None
@dash_app.callback(Output('trips_map', 'figure'),
Output('consumption_fig', 'figure'),
Output('consumption_fig_by_speed', 'figure'),
Output('consumption', 'children'),
Input('date-slider', 'value'))
def display_value(value):
min = datetime.fromtimestamp(value[0], tz=timezone.utc)
max = datetime.fromtimestamp(value[1], tz=timezone.utc)
filtered_trips = []
for trip in trips:
if min <= trip.start_at <= max:
filtered_trips.append(trip)
print(len(filtered_trips))
figures.get_figures(filtered_trips)
consumption = "Average consumption: {:.1f} kW/100km".format(float(figures.consumption_df.mean()))
return figures.trips_map, figures.consumption_fig, figures.consumption_fig_by_speed, consumption
@app.route('/getvehicles')
def getvehicules():
return jsonify(myp.getVIN())
@app.route('/get_vehicleinfo/<string:vin>')
def get_vehicle_Info(vin):
response = app.response_class(
response=json.dumps(myp.get_vehicle_info(vin).to_dict(), default=str),
status=200,
mimetype='application/json'
)
return response
@app.route('/charge_now/<string:vin>/<int:charge>')
def charge_now(vin, charge):
return jsonify(myp.charge_now(vin, charge != 0))
@app.route('/charge_hour')
def change_charge_hour():
return jsonify(myp.change_charge_hour(request.form['vin'], request.form['hour'], request.form['minute']))
@app.route('/wakeup/<string:vin>')
def wakeup(vin):
return jsonify(myp.wakeup(vin))
@app.route('/preconditioning/<string:vin>/<int:activate>')
def preconditioning(vin, activate):
return jsonify(myp.preconditioning(vin, activate))
@app.route('/position/<string:vin>')
def get_position(vin):
res = myp.get_vehicle_info(vin)
longitude, latitude = res.last_position.geometry.coordinates
return jsonify(
{"longitude": longitude, "latitude": latitude, "url": f"http://maps.google.com/maps?q={latitude},{longitude}"})
# Set a battery threshold and schedule an hour to stop the charge
@app.route('/charge_control')
def charge_control():
logger.info(request)
vin = request.args['vin']
charge_control = chc.get(vin)
if charge_control is None:
return jsonify("error: VIN not in list")
if 'hour' in request.args or 'minute' in request.args:
charge_control.set_stop_hour([int(request.args["hour"]), int(request.args["minute"])])
if 'percentage' in request.args:
charge_control.percentage_threshold = int(request.args['percentage'])
save_config()
return jsonify(charge_control.get_dict())
@app.route('/positions')
def get_recorded_position():
return FlaskResponse(myp.get_recorded_position(), mimetype='application/json')
@app.after_request
def after_request(response):
header = response.headers
header['Access-Control-Allow-Origin'] = '*'
return response
def save_config(my_peugeot: MyPSACC):
my_peugeot.save_config()
threading.Timer(30, save_config, args=[my_peugeot]).start()
trips = MyPSACC.get_trips()
figures.get_figures(trips)
dash_app.layout = dbc.Container([
html.H1('My car info'),
html.Hr(),
html.Div([
dcc.RangeSlider(
id='date-slider',
min=figures.unix_time_millis(figures.consumption_df["date"].min()),
max=figures.unix_time_millis(figures.consumption_df["date"].max()),
step=None,
marks=figures.get_marks_from_start_end(figures.consumption_df["date"].min(),
figures.consumption_df["date"].max()),
value=[figures.unix_time_millis(figures.consumption_df["date"].min()),
figures.unix_time_millis(figures.consumption_df["date"].max())],
),
html.Div(id='updatemode-output-container', style={'margin-top': 20})
],
style={'margin-left': 20}
),
html.Div([
html.Div([
dcc.Graph(figure=figures.trips_map, id="trips_map")
]),
html.Div([
dcc.Graph(figure=figures.consumption_fig, id="consumption_fig")
]),
html.H2(id="consumption",
children="Average consumption: {:.1f} kW/100km".format(float(figures.consumption_df.mean()))),
html.Div([
dcc.Graph(figure=figures.consumption_fig_by_speed, id="consumption_fig_by_speed")
]),
], id="data-div"),
])
+80
View File
@@ -0,0 +1,80 @@
from datetime import datetime
from typing import List
import numpy as np
import pytz
from dateutil.relativedelta import relativedelta
from pandas import DataFrame
import plotly.express as px
import plotly.graph_objects as go
from Trips import Trips
def unix_time_millis(dt):
return int(dt.timestamp())
def get_marks_from_start_end(start, end):
nb_marks = 5
result = []
time_delta = int((end - start).total_seconds() / nb_marks)
current = start
if time_delta > 0:
while current <= end:
result.append(current)
current += relativedelta(seconds=time_delta)
result[-1] = end
if time_delta < 3600 * 24:
if time_delta > 3600:
date_f = '%y-%m-%d %Hh'
else:
date_f = '%y-%m-%d %Hh%M'
else:
date_f = '%Y-%m'
marks = {}
for date in result:
marks[unix_time_millis(date)] = str(date.strftime(date_f))
return marks
def convert_datetime(st):
return datetime.strptime(st.decode("utf-8"), "%Y-%m-%d %H:%M:%S+00:00").replace(tzinfo=pytz.UTC)
consumption_fig = None
consumption_df = None
trips_map = None
consumption_fig_by_speed = None
def get_figures(trips: List[Trips]):
global consumption_fig, consumption_df, trips_map, consumption_fig_by_speed
lats = []
lons = []
names = []
for trip in trips:
for points in trip.positions:
lats = np.append(lats, points.longitude)
lons = np.append(lons, points.latitude)
names = np.append(names, [str(trip.start_at)])
lats = np.append(lats, None)
lons = np.append(lons, None)
names = np.append(names, None)
trips_map = px.line_mapbox(lat=lats, lon=lons, hover_name=names,
mapbox_style="stamen-terrain", zoom=12)
consumption_df = DataFrame.from_records([tr.get_consumption() for tr in trips])
consumption_fig = px.line(consumption_df, x="date", y="consumption", title='Consumption of the car')
consum_df_by_speed = DataFrame.from_records(
[{"speed": tr.speed_average, "value": tr.consumption_km} for tr in trips])
consumption_fig_by_speed = px.histogram(consum_df_by_speed, x="speed", y="value", histfunc="avg",
title="Consumption by speed")
consumption_fig_by_speed.update_traces(xbins_size=15)
consumption_fig_by_speed.update_layout(bargap=0.1)
consumption_fig_by_speed.add_trace(
go.Scatter(mode="markers", x=consum_df_by_speed["speed"], y=consum_df_by_speed["value"],
name="Trips"))
consumption_fig_by_speed.update_layout(xaxis_title="average Speed km/h", yaxis_title="Consumption kW/100Km")