From e98b0ff5db0fdbb1f8f955667ea89f31270a4cfa Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Mon, 28 Mar 2022 20:51:45 +0200 Subject: [PATCH] fix #331, #306 and #333 --- psa_car_controller/psa/RemoteClient.py | 29 +++++++++-------- .../psacc/repository/car_model.py | 2 +- psa_car_controller/psacc/repository/trips.py | 7 ++--- pyproject.toml | 1 + tests/test_psa.py | 31 +++++++++++++++++++ tests/test_unit.py | 10 ++++++ 6 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 tests/test_psa.py diff --git a/psa_car_controller/psa/RemoteClient.py b/psa_car_controller/psa/RemoteClient.py index 84ae0ee..f543c06 100644 --- a/psa_car_controller/psa/RemoteClient.py +++ b/psa_car_controller/psa/RemoteClient.py @@ -3,7 +3,7 @@ import logging import threading from datetime import datetime from os import environ -from time import sleep +import time import paho.mqtt.client as mqtt from requests import RequestException @@ -82,19 +82,22 @@ class RemoteClient: elif msg.topic.startswith(MQTT_EVENT_TOPIC): charge_info = data["charging_state"] self.precond_programs[data["vin"]] = data["precond_state"]["programs"] - if charge_info is not None and charge_info['remaining_time'] != 0: - try: - car = self.vehicles_list.get_car_by_vin(vin=msg.topic.split("/")[-1]) - if car and car.status.get_energy('Electric').charging.status != INPROGRESS: - # fix a psa server bug where charge beginning without status api being properly updated - logger.warning("charge begin but API isn't updated") - sleep(60) - self.wakeup(data["vin"]) - except (IndexError, AttributeError, RateLimitException): - logger.exception("on_mqtt_message:") + self._fix_not_updated_api(charge_info, data["vin"]) except KeyError: logger.exception("on_mqtt_message:") + def _fix_not_updated_api(self, charge_info, vin): + if charge_info is not None and charge_info['remaining_time'] != 0: + try: + car = self.vehicles_list.get_car_by_vin(vin=vin) + if car and car.status.get_energy('Electric').charging.status != INPROGRESS: + # fix a psa server bug where charge beginning without status api being properly updated + logger.warning("charge begin but API isn't updated") + time.sleep(60) + self.wakeup(vin) + except (IndexError, AttributeError, RateLimitException): + logger.exception("on_mqtt_message:") + def start(self): if self.load_otp(): self.mqtt_client = mqtt.Client(clean_session=True, protocol=mqtt.MQTTv311) @@ -172,7 +175,7 @@ class RemoteClient: return True except (RequestException, RateLimitException) as e: logger.exception("Can't refresh remote token %s", e) - sleep(60) + time.sleep(60) return False def get_sms_otp_code(self): @@ -204,7 +207,7 @@ class RemoteClient: return res except RequestException as e: logger.error("Can't refresh remote token %s", e) - sleep(60) + time.sleep(60) return None def horn(self, vin, count): diff --git a/psa_car_controller/psacc/repository/car_model.py b/psa_car_controller/psacc/repository/car_model.py index b63be23..0608a38 100644 --- a/psa_car_controller/psacc/repository/car_model.py +++ b/psa_car_controller/psacc/repository/car_model.py @@ -18,7 +18,7 @@ class CarModelRepository(metaclass=Singleton): yaml = ruamel.yaml.YAML() yaml.register_class(ElecModel) yaml.register_class(CarModel) - self.models: List = yaml.load(models) + self.models: List[CarModel] = yaml.load(models) def find_model_by_vin(self, vin) -> CarModel: if vin != "vin": diff --git a/psa_car_controller/psacc/repository/trips.py b/psa_car_controller/psacc/repository/trips.py index 63aebe8..b8f68bc 100644 --- a/psa_car_controller/psacc/repository/trips.py +++ b/psa_car_controller/psacc/repository/trips.py @@ -3,14 +3,13 @@ from typing import Dict from geojson import FeatureCollection +from psa_car_controller.common.mylogger import logger from psa_car_controller.psacc.application.trip_parser import TripParser from psa_car_controller.psacc.model.trip import Trip from psa_car_controller.psacc.model.car import Cars from psa_car_controller.psacc.repository.db import Database -logger = logging.getLogger(__name__) - class Trips(list): def __init__(self, *args): @@ -47,8 +46,8 @@ class Trips(list): trips = Trips() vin = vin[0] res = conn.execute('SELECT Timestamp, VIN, longitude, latitude, mileage, level, moving, temperature, ' - 'level_fuel, altitude FROM position WHERE VIN=? AND mileage NOT NULL ORDER BY Timestamp', - (vin,)).fetchall() + 'level_fuel, altitude FROM position WHERE VIN=? ' + 'AND mileage IS NOT NULL ORDER BY Timestamp', (vin,)).fetchall() if len(res) > 1: car = vehicles_list.get_car_by_vin(vin) assert car is not None diff --git a/pyproject.toml b/pyproject.toml index 88042ea..531dac1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ prospector = ">=1.3.0" pre-commit = "^2.17.0" coverage = "^6.3.2" deepdiff = "^5.7.0" +greenery = "^3.3.5" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/test_psa.py b/tests/test_psa.py new file mode 100644 index 0000000..aad6772 --- /dev/null +++ b/tests/test_psa.py @@ -0,0 +1,31 @@ +import unittest +from unittest.mock import MagicMock, patch + +from psa_car_controller.psa.RemoteClient import RemoteClient +from psa_car_controller.psa.connected_car_api import Vehicles, ApiClient +from psa_car_controller.psa.constants import DISCONNECTED +from psa_car_controller.psacc.model.car import Car +from tests.data.car_status import ELECTRIC_CAR_STATUS + + +class TestUnit(unittest.TestCase): + + def get_rc(self) -> RemoteClient: + account_info = MagicMock() + account_info.realm = "" + return RemoteClient(account_info, Vehicles, None, None) + + @patch('time.sleep', return_value=None) + def test_fix_not_updated_api(self, patched_time_sleep): + # GIVEN + remote_client = self.get_rc() + vin = "myvin" + car = Car("a", "b", "c") + car.status = ApiClient()._ApiClient__deserialize(ELECTRIC_CAR_STATUS, "Status") + car.status.get_energy('Electric').charging.status = DISCONNECTED + remote_client.vehicles_list.get_car_by_vin = MagicMock(return_value=car) + remote_client.wakeup = MagicMock() + # WHEN + remote_client._fix_not_updated_api({'remaining_time': 1}, vin) + # THEN + remote_client.wakeup.assert_called_once_with(vin) diff --git a/tests/test_unit.py b/tests/test_unit.py index 303e6a1..f16684c 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -7,6 +7,7 @@ from unittest.mock import MagicMock, patch import reverse_geocode from dateutil.tz import tzutc +from greenery.lego import parse, charclass from pytz import UTC from psa_car_controller import psa @@ -302,6 +303,15 @@ class TestUnit(unittest.TestCase): f.write(" ") assert github_file_need_to_be_downloaded(GITHUB_USER, GITHUB_REPO, "", filename) is True + def test_regex(self): + car_models = CarModelRepository().models + for x in range(0, len(car_models)): + for y in range(x + 1, len(car_models)): + reg_a = car_models[x].reg + reg_b = car_models[y].reg + res: charclass = parse(reg_a) & parse(reg_b) + self.assertTrue(res.empty(), msg=f"{reg_a} and {reg_b} can match the same string") + if __name__ == '__main__': my_logger(handler_level=os.environ.get("DEBUG_LEVEL", 20))