def test_sdk(self): res = { 'lastPosition': {'type': 'Feature', 'geometry': {'type': 'Point', 'coordinates': [9.65457, 49.96119, 21]}, 'properties': {'updatedAt': '2021-03-29T05:16:10Z', 'heading': 126, 'type': 'Estimated'}}, 'preconditionning': { 'airConditioning': {'updatedAt': '2021-04-01T16:17:01Z', 'status': 'Disabled', 'programs': [ {'enabled': False, 'slot': 1, 'recurrence': 'Daily', 'start': 'PT21H40M', 'occurence': {'day': ['Sat']}}]}}, 'energy': [{'updatedAt': '2021-02-23T22:29:03Z', 'type': 'Fuel', 'level': 0}, {'updatedAt': '2021-04-01T16:17:01Z', 'type': 'Electric', 'level': 70, 'autonomy': 192, 'charging': {'plugged': True, 'status': 'InProgress', 'remainingTime': 'PT0S', 'chargingRate': 20, 'chargingMode': 'Slow', 'nextDelayedTime': 'PT21H30M'}}], 'createdAt': '2021-04-01T16:17:01Z', 'battery': {'voltage': 99, 'current': 0, 'createdAt': '2021-04-01T16:17:01Z'}, 'kinetic': {'createdAt': '2021-03-29T05:16:10Z', 'moving': False}, 'privacy': {'createdAt': '2021-04-01T16:17:01Z', 'state': 'None'}, 'service': {'type': 'Electric', 'updatedAt': '2021-02-23T21:10:29Z'}, '_links': {'self': { 'href': 'https://api.groupe-psa.com/connectedcar/v4/user/vehicles/myid/status'}, 'vehicles': { 'href': 'https://api.groupe-psa.com/connectedcar/v4/user/vehicles/myid'}}, 'timed.odometer': {'createdAt': None, 'mileage': 1107.1}, 'updatedAt': '2021-04-01T16:17:01Z'} api = ApiClient() status: psacc.models.status.Status = api._ApiClient__deserialize(res, "Status") geocode_res = reverse_geocode.search([(status.last_position.geometry.coordinates[:2])[::-1]])[0] assert geocode_res["country_code"] == "DE" get_new_test_db() car = Car("XX", "vid", "Peugeot") car.status = status myp = MyPSACC.load_config(DATA_DIR + "config.json") myp.record_info(car) assert "features" in json.loads(Database.get_recorded_position()) # electric should be first assert car.status.energy[0].type == 'Electric'
def __init__(self, methodName='runTest'): super().__init__(methodName) self.test_online = os.environ.get("TEST_ONLINE", "0") == "1" self.vehicule_list = Cars() self.vehicule_list.extend([ Car("VR3UHZKX", "vid", "Peugeot"), Car("VXXXXX", "XXXX", "Peugeot", label="SUV 3008") ])
def call(self, car: Car, ext_temp: float = None): try: if self.token is None or len(self.token) == 0: logger.debug("No abrp 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.exception("abrp:") return False
def get_battery_curve_fig(row: dict, car: Car): start_date = dash_date_to_datetime(row["start_at"]) stop_at = dash_date_to_datetime(row["stop_at"]) conn = Database.get_db() res = Database.get_battery_curve(conn, start_date, stop_at, car.vin) conn.close() battery_curves = [] if len(res) > 0: battery_capacity = res[-1]["level"] * car.battery_power / 100 km_by_kw = 0.8 * res[-1]["autonomy"] / battery_capacity start = 0 speeds = [] def speed_in_kw_from_km(row): try: speed = row["rate"] / km_by_kw if speed > 0: speeds.append(speed) except (KeyError, TypeError): pass for end in range(1, len(res)): start_level = res[start]["level"] end_level = res[end]["level"] diff_level = end_level - start_level diff_sec = (res[end]["date"] - res[start]["date"]).total_seconds() speed_in_kw_from_km(res[end - 1]) if diff_sec > 0 and diff_level > 3: speed_in_kw_from_km(res[end]) speed = car.get_charge_speed(diff_level, diff_sec) if len(speeds) > 0: speed = mean([*speeds, speed]) speed = round(speed * 2) / 2 battery_curves.append({"level": start_level, "speed": speed}) start = end speeds = [] battery_curves.append({"level": row["end_level"], "speed": 0}) else: speed = car.get_charge_speed(row["end_level"] - row["start_level"], (stop_at - start_date).total_seconds()) battery_curves.append({"level": row["start_level"], "speed": speed}) battery_curves.append({"level": row["end_level"], "speed": speed}) fig = px.line(battery_curves, x="level", y="speed") fig.update_layout(xaxis_title="Battery %", yaxis_title="Charging speed in kW") return html.Div(Graph(figure=fig))
def get_vehicles(self): try: res = self.api().get_vehicles_by_device() for vehicle in res.embedded.vehicles: self.vehicles_list.add(Car(vehicle.vin, vehicle.id, vehicle.brand, vehicle.label)) self.vehicles_list.save_cars() except (ApiException, InvalidHeader): logger.exception("get_vehicles:") return self.vehicles_list
def update_trips(): global trips, chargings, cached_layout, min_date, max_date, min_millis, max_millis, step, marks logger.info("update_data") conn = Database.get_db(update_callback=False) Database.add_altitude_to_db(conn) conn.close() min_date = None max_date = None if CONFIG.is_good: car = CONFIG.myp.vehicles_list[0] # todo handle multiple car try: trips_by_vin = Trips.get_trips(Cars([car])) trips = trips_by_vin[car.vin] assert len(trips) > 0 min_date = trips[0].start_at max_date = trips[-1].start_at figures.get_figures(trips[0].car) except (AssertionError, KeyError): logger.debug("No trips yet") figures.get_figures(Car("vin", "vid", "brand")) try: chargings = Charging.get_chargings() assert len(chargings) > 0 if min_date: min_date = min(min_date, chargings[0]["start_at"]) max_date = max(max_date, chargings[-1]["start_at"]) else: min_date = chargings[0]["start_at"] max_date = chargings[-1]["start_at"] except AssertionError: logger.debug("No chargings yet") if min_date is None: return # update for slider try: logger.debug("min_date:%s - max_date:%s", min_date, max_date) min_millis = web.utils.unix_time_millis(min_date) max_millis = web.utils.unix_time_millis(max_date) step = (max_millis - min_millis) / 100 marks = web.utils.get_marks_from_start_end(min_date, max_date) cached_layout = None # force regenerate layout figures.get_figures(car) except (ValueError, IndexError): logger.error("update_trips (slider): %s", exc_info=True) except AttributeError: logger.debug("position table is probably empty :", exc_info=True) return
def get_battery_curve_fig(row: dict, car: Car): start_date = dash_date_to_datetime(row["start_at"]) stop_at = dash_date_to_datetime(row["stop_at"]) conn = Database.get_db() res = Database.get_battery_curve(conn, start_date, car.vin) conn.close() res.insert(0, {"level": row["start_level"], "date": start_date}) res.append({"level": row["end_level"], "date": stop_at}) battery_curves = [] speed = 0 for x in range(1, len(res)): start_level = res[x - 1]["level"] end_level = res[x]["level"] speed = car.get_charge_speed(start_level, end_level, (res[x]["date"] - res[x - 1]["date"]).total_seconds()) battery_curves.append({"level": start_level, "speed": speed}) battery_curves.append({"level": row["end_level"], "speed": speed}) fig = px.line(battery_curves, x="level", y="speed") fig.update_layout(xaxis_title="Battery %", yaxis_title="Charging speed in kW") return html.Div(Graph(figure=fig))
def test_battery_curve(self): from libs.car import Car from libs.charging import Charging try: os.remove("tmp.db") except: pass Database.DEFAULT_DB_FILE = "tmp.db" conn = Database.get_db() list(map(dict, conn.execute('PRAGMA database_list').fetchall())) vin = "VR3UHZKXZL" car = Car(vin, "id", "Peugeot") Charging.record_charging(car, "InProgress", date0, 50, latitude, longitude, "FR", "slow") Charging.record_charging(car, "InProgress", date1, 75, latitude, longitude, "FR", "slow") Charging.record_charging(car, "InProgress", date2, 85, latitude, longitude, "FR", "slow") Charging.record_charging(car, "InProgress", date3, 90, latitude, longitude, "FR", "slow") res = Database.get_battery_curve(Database.get_db(), date0, vin) assert len(res) == 3
from libs.car import Car, Cars from libs.charging import Charging from web.db import Database DATA_DIR = os.path.dirname(os.path.realpath(__file__)) + "/data/" latitude = 47.2183 longitude = -1.55362 date3 = datetime.utcnow().replace(2021, 3, 1, 12, 00, 00, 00, tzinfo=pytz.UTC) date2 = date3 - timedelta(minutes=20) date1 = date3 - timedelta(minutes=40) date0 = date3 - timedelta(minutes=60) date4 = date3 + timedelta(minutes=1) vehicule_list = Cars() vehicule_list.extend( [Car("VR3UHZKX", "vid", "Peugeot"), Car("VXXXXX", "XXXX", "Peugeot", label="SUV 3008 Hybrid 225")]) car = vehicule_list[0] DB_DIR = DATA_DIR + "tmp.db" def get_new_test_db(): try: os.remove(DATA_DIR + "tmp.db") except FileNotFoundError: pass Database.DEFAULT_DB_FILE = DB_DIR Database.db_initialized = False conn = Database.get_db() return conn
def get_figures(car: Car): global consumption_fig, consumption_df, trips_map, consumption_fig_by_speed, table_fig, info, \ battery_table, consumption_fig_by_temp lats = [42, 41] lons = [1, 2] names = ["undefined", "undefined"] trips_map = px.line_mapbox(lat=lats, lon=lons, hover_name=names, zoom=12, mapbox_style="style.json") trips_map.add_trace(go.Scattermapbox( mode="markers", marker={"symbol": "marker", "size": 20}, lon=[lons[0]], lat=[lats[0]], showlegend=False, name="Last Position")) # table nb_format = Format(precision=2, scheme=Scheme.fixed, symbol=Symbol.yes) # pylint: disable=no-member style_cell_conditional = [] if car.is_electric(): style_cell_conditional.append({'if': {'column_id': 'consumption_fuel_km', }, 'display': 'None', }) if car.is_thermal(): style_cell_conditional.append({'if': {'column_id': 'consumption_km', }, 'display': 'None', }) table_fig = dash_table.DataTable( id='trips-table', sort_action='custom', sort_by=[{'column_id': 'id', 'direction': 'desc'}], columns=[{'id': 'id', 'name': '#', 'type': 'numeric'}, {'id': 'start_at_str', 'name': 'start at', 'type': 'datetime'}, {'id': 'duration', 'name': 'duration', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" min").precision(0)}, {'id': 'speed_average', 'name': 'average speed', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" km/h").precision(0)}, {'id': 'consumption_km', 'name': 'average consumption', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" kWh/100km")}, {'id': 'consumption_fuel_km', 'name': 'average consumption fuel', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" L/100km")}, {'id': 'distance', 'name': 'distance', 'type': 'numeric', 'format': nb_format.symbol_suffix(" km").precision(1)}, {'id': 'mileage', 'name': 'mileage', 'type': 'numeric', 'format': nb_format}, {'id': 'altitude_diff', 'name': 'Altitude diff', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" m").precision(0)} ], style_data_conditional=[ { 'if': {'column_id': ['altitude_diff']}, 'color': 'dodgerblue', "text-decoration": "underline" } ], style_cell_conditional=style_cell_conditional, data=[], page_size=50 ) # consumption_fig consumption_fig = px.histogram(x=[0], y=[1], title='Consumption of the car', histfunc="avg") consumption_fig.update_layout(yaxis_title="Consumption kWh/100Km", xaxis_title="date") consumption_fig_by_speed = px.histogram(data_frame=[{"start_at": 1, "speed_average": 2}], x="start_at", y="speed_average", histfunc="avg", title="Consumption by speed") consumption_fig_by_speed.update_traces(xbins_size=15) consumption_fig_by_speed.update_layout(bargap=0.05) consumption_fig_by_speed.add_trace(go.Scatter(mode="markers", x=[0], y=[0], name="Trips")) consumption_fig_by_speed.update_layout(xaxis_title="average Speed km/h", yaxis_title="Consumption kWh/100Km") # battery_table battery_table = dash_table.DataTable( id='battery-table', sort_action='custom', sort_by=[{'column_id': 'start_at_str', 'direction': 'desc'}], columns=[{'id': 'start_at_str', 'name': 'start at', 'type': 'datetime'}, {'id': 'stop_at_str', 'name': 'stop at', 'type': 'datetime'}, {'id': 'start_level', 'name': 'start level', 'type': 'numeric'}, {'id': 'end_level', 'name': 'end level', 'type': 'numeric'}, {'id': 'co2', 'name': 'CO2', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" g/kWh").precision(1)}, {'id': 'kw', 'name': 'consumption', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" kWh").precision(2)}, {'id': 'price', 'name': 'price', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" " + ElecPrice.currency).precision(2), 'editable': True} ], data=[], style_data_conditional=[ { 'if': {'column_id': ['start_level', "end_level"]}, 'color': 'dodgerblue', "text-decoration": "underline" }, { 'if': {'column_id': 'price'}, 'backgroundColor': '#ABE2FB' } ], ) consumption_fig_by_temp = px.histogram(x=[0], y=[0], histfunc="avg", title="Consumption by temperature") consumption_fig_by_temp.update_traces(xbins_size=2) consumption_fig_by_temp.update_layout(bargap=0.05) consumption_fig_by_temp.add_trace( go.Scatter(mode="markers", x=[0], y=[0], name="Trips")) consumption_fig_by_temp.update_layout(xaxis_title="average temperature in °C", yaxis_title="Consumption kWh/100Km") return True
def test_car(self): car1 = Car("VRAAAAAAA", "1sdfdksnfk222", "Peugeot", "208", 46, 0) car2 = Car("VR3UHZKX", "1sdfdksnfk222", "Peugeot") cars = Cars([car1, car2]) cars.save_cars(name=DATA_DIR + "test_car.json") Cars.load_cars(name=DATA_DIR + "test_car.json")