diff --git a/Car.py b/Car.py index 6e82724..20172f9 100644 --- a/Car.py +++ b/Car.py @@ -1,4 +1,5 @@ import json +from copy import copy from MyLogger import logger @@ -20,6 +21,7 @@ class Car: self.battery_power = None self.fuel_capacity = None self.set_energy_capacity(battery_power, fuel_capacity) + self.status = None def set_energy_capacity(self, battery_power = None, fuel_capacity = None): if battery_power is not None and fuel_capacity is not None: @@ -37,8 +39,13 @@ class Car: def from_json(cls, data: dict): return cls(**data) + def to_dict(self): + car_dict = copy(self.__dict__) + car_dict.pop("status") + return car_dict + def __str__(self): - return str(self.__dict__) + return str(self.to_dict()) class Cars(list): def __init__(self, *args): @@ -69,7 +76,7 @@ class Cars(list): return str(list(map(str, self))) def save_cars(self, name="cars.json"): - config_str = json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) + config_str = json.dumps(self, default=lambda car: car.to_dict(), sort_keys=True, indent=4) with open(name, "w") as f: f.write(config_str) @@ -79,5 +86,5 @@ class Cars(list): with open(name, "r") as f: json_str = f.read() return Cars.from_json(json.loads(json_str)) - except FileNotFoundError: - return Cars() \ No newline at end of file + except (FileNotFoundError, TypeError): + return Cars() diff --git a/ChargeControl.py b/ChargeControl.py index 89eec24..f876978 100644 --- a/ChargeControl.py +++ b/ChargeControl.py @@ -10,7 +10,6 @@ import pytz from MyPSACC import MyPSACC from MyLogger import logger -from psa_connectedcar.rest import ApiException class ChargeControl: @@ -23,7 +22,6 @@ class ChargeControl: self.set_stop_hour(stop_hour) self.psacc = psacc self.retry_count = 0 - self.thread: threading.Timer = None self.always_check = True def set_stop_hour(self, stop_hour): @@ -36,9 +34,9 @@ class ChargeControl: if self._next_stop_hour < datetime.now(): self._next_stop_hour += timedelta(days=1) - def start(self): - periodicity = ChargeControl.periodicity + def process(self): now = datetime.now() + vehicle_status = self.psacc.vehicles_list.get_car_by_vin(self.vin).status try: if self._next_stop_hour is not None and self._next_stop_hour < now: stop_charge = True @@ -48,47 +46,41 @@ class ChargeControl: stop_charge = False if self.percentage_threshold != 100 or stop_charge or self.always_check: - res = None - try: - res = self.psacc.get_vehicle_info(self.vin) - except ApiException: - logger.error(traceback.format_exc()) - if res is not None: - status = res.get_energy('Electric').charging.status - level = res.get_energy('Electric').level + if vehicle_status is not None: + status = vehicle_status.get_energy('Electric').charging.status + level = vehicle_status.get_energy('Electric').level logger.info("charging status of %s is %s, battery level: %d", self.vin, status, level) if status == "InProgress": # force update if the car doesn't send info during 10 minutes - last_update = res.get_energy('Electric').updated_at + last_update = vehicle_status.get_energy('Electric').updated_at if (datetime.utcnow().replace(tzinfo=pytz.UTC) - last_update).total_seconds() > 60 * 10: self.psacc.wakeup(self.vin) if (level >= self.percentage_threshold and self.retry_count < 2) or stop_charge: self.psacc.charge_now(self.vin, False) self.retry_count += 1 sleep(ChargeControl.MQTT_TIMEOUT) - res = self.psacc.get_vehicle_info(self.vin) - status = res.get_energy('Electric').charging.status + vehicle_status = self.psacc.get_vehicle_info(self.vin) + status = vehicle_status.get_energy('Electric').charging.status if status == "InProgress": logger.warning("retry to stop the charge of %s", self.vin) self.psacc.charge_now(self.vin, False) self.retry_count += 1 if self._next_stop_hour is not None: next_in_second = (self._next_stop_hour - now).total_seconds() - if next_in_second < periodicity: + if next_in_second < self.psacc.info_refresh_rate: periodicity = next_in_second + self.thread = threading.Timer(periodicity, self.process, args=[vehicle_status]) + self.thread.start() else: self.retry_count = 0 else: - logger.error("error when get vehicle info of %s", self.vin) + logger.error("error can't retrieve vehicle info of %s", self.vin) except: logger.error(traceback.format_exc()) - self.thread = threading.Timer(periodicity, self.start) - self.thread.start() def get_dict(self): chd = copy(self.__dict__) chd.pop("psacc") - chd.pop("thread") return chd @@ -110,6 +102,7 @@ class ChargeControls: self._confighash = new_hash logger.info("save config change") + @staticmethod def load_config(psacc: MyPSACC, name="charge_config.json"): with open(name, "r") as f: config_str = f.read() @@ -127,4 +120,4 @@ class ChargeControls: def start(self): for charge_control in self.list.values(): - charge_control.start() + charge_control.psacc.info_callback.append(charge_control.process) diff --git a/MyPSACC.py b/MyPSACC.py index 14d3c58..5f8a9e9 100644 --- a/MyPSACC.py +++ b/MyPSACC.py @@ -181,6 +181,8 @@ class MyPSACC: self.country_code = country_code self.mqtt_client = None self.precond_programs = {} + self.info_callback = [] + self.info_refresh_rate = 120 def refresh_token(self): self.manager._refresh_token() @@ -200,21 +202,25 @@ class MyPSACC: self.manager.proxies = self._proxies def get_vehicle_info(self, vin): - res = self.api().get_vehicle_status(self.vehicles_list.get_car_by_vin(vin).vehicle_id, extension=["odometer"]) - # retry - if res is None: - res = self.api().get_vehicle_status(self.vehicles_list.get_car_by_vin(vin).vehicle_id, - extension=["odometer"]) - if self._record_enabled: - self.record_info(vin, res) + car = self.vehicles_list.get_car_by_vin(vin) + for attempt in range(0, 2): + res = self.api().get_vehicle_status(car.vehicle_id, extension=["odometer"]) + if res is not None: + car.status = res + if self._record_enabled: + self.record_info(vin, res) + break return res - def refresh_vehicle_info(self, refresh=5): - while True: - sleep(refresh * 60) - logger.info("refresh_vehicle_info") - for car in self.vehicles_list: - self.get_vehicle_info(car.vin) + def refresh_vehicle_info(self): + if self.info_refresh_rate is not None: + while True: + sleep(self.info_refresh_rate) + logger.info("refresh_vehicle_info") + for car in self.vehicles_list: + self.get_vehicle_info(car.vin) + for callback in self.info_callback: + callback() # monitor doesn't seem to work def newMonitor(self, vin, body): diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..b3f631e --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,2 @@ +prospector>=1.3.0 +pre-commit diff --git a/server.py b/server.py index 027f9cf..65b21de 100755 --- a/server.py +++ b/server.py @@ -63,16 +63,21 @@ if __name__ == "__main__": client_password = input("mypeugeot password: ") web.app.myp.connect(client_email, client_password) logger.info(str(web.app.myp.get_vehicles())) - if args.offline or args.remote_disable: - logger.info("mqtt disabled") + if args.offline: + logger.info("offline mode") else: - web.app.myp.start_mqtt() - if args.charge_control: - web.app.chc = ChargeControls.load_config(web.app.myp, name=args.charge_control) - web.app.chc.start() + if args.remote_disable: + logger.info("mqtt disabled") + else: + web.app.myp.start_mqtt() + if args.refresh or args.charge_control: + if args.refresh: + web.app.myp.info_refresh_rate = args.refresh * 60 + if args.charge_control: + web.app.chc = ChargeControls.load_config(web.app.myp, name=args.charge_control) + web.app.chc.start() + Thread(target=web.app.myp.refresh_vehicle_info).start() + save_config(web.app.myp) t1 = Thread(target=start_app, args=["My car info", args.base_path, args.debug < 20, args.listen, int(args.port)]) t1.start() - - if args.refresh: - Thread(target=web.app.myp.refresh_vehicle_info, args=[args.refresh]).start()