|
| 1 | +#!/usr/bin/env python3 |
| 2 | +import asyncio |
| 3 | +import logging |
| 4 | +from datetime import datetime, timezone |
| 5 | + |
| 6 | +from modules.common.component_state import CarState |
| 7 | + |
| 8 | +import pycarwings3 |
| 9 | + |
| 10 | +log = logging.getLogger(__name__) |
| 11 | + |
| 12 | + |
| 13 | +async def _fetch_soc(username, password, region, vehicle) -> CarState: |
| 14 | + |
| 15 | + async def getNissanSession(): |
| 16 | + # open HTTPS session with Nissan server |
| 17 | + log.debug("vehicle%s: login = %s, region = %s" % (vehicle, username, region)) |
| 18 | + session = pycarwings3.Session(username, password, region) |
| 19 | + leaf = await session.get_leaf() |
| 20 | + await asyncio.sleep(1) |
| 21 | + return leaf |
| 22 | + |
| 23 | + async def readSoc(leaf) -> CarState: |
| 24 | + # get SoC & range & time stamp from Nissan server |
| 25 | + leaf_info = await leaf.get_latest_battery_status() |
| 26 | + soc = float(leaf_info.battery_percent) |
| 27 | + log.debug("vehicle%s: Battery State of Charge %s" % (vehicle, soc)) |
| 28 | + range = int(leaf_info.answer["BatteryStatusRecords"]["CruisingRangeAcOff"])/1000 |
| 29 | + log.debug("vehicle%s: Cruising range AC Off %s" % (vehicle, range)) |
| 30 | + time_stamp_str_utc = leaf_info.answer["BatteryStatusRecords"]["NotificationDateAndTime"] |
| 31 | + soc_time = datetime.strptime(f"{time_stamp_str_utc}", "%Y/%m/%d %H:%M").replace(tzinfo=timezone.utc) |
| 32 | + log.debug("vehicle%s: Date&Time of SoC (UTC) %s" % (vehicle, soc_time)) |
| 33 | + soc_timestamp = soc_time.timestamp() |
| 34 | + log.debug("vehicle%s: soc_timestamp %s" % (vehicle, soc_timestamp)) |
| 35 | + log.debug("vehicle%s: local Date&Time of SoC %s" % (vehicle, datetime.fromtimestamp(soc_timestamp))) |
| 36 | + return CarState(soc, range, soc_timestamp) |
| 37 | + |
| 38 | + async def requestSoc(leaf: pycarwings3.Leaf): |
| 39 | + # request Nissan server to request last SoC from car |
| 40 | + log.debug("vehicle%s: Request SoC Update from vehicle" % (vehicle)) |
| 41 | + key = await leaf.request_update() |
| 42 | + sleepsecs = 20 |
| 43 | + for _ in range(0, 3): |
| 44 | + log.debug("Waiting {0} seconds".format(sleepsecs)) |
| 45 | + await asyncio.sleep(sleepsecs) |
| 46 | + status = await leaf.get_status_from_update(key) |
| 47 | + if status is not None: |
| 48 | + log.debug("vehicle%s: Update successful" % (vehicle)) |
| 49 | + return status |
| 50 | + log.debug("vehicle%s: Update not successful" % (vehicle)) |
| 51 | + return status |
| 52 | + |
| 53 | + try: |
| 54 | + leaf = await getNissanSession() # start HTTPS session with Nissan server |
| 55 | + soc_range = await readSoc(leaf) # read old SoC & range values from server |
| 56 | + await asyncio.sleep(1) # give Nissan server some time |
| 57 | + status = await requestSoc(leaf) # Nissan server to request new values from vehicle |
| 58 | + if status is not None: # was update of values successful? |
| 59 | + await asyncio.sleep(1) # give Nissan server some time |
| 60 | + soc_range = await readSoc(leaf) # final read of SoC & range from server |
| 61 | + except pycarwings3.CarwingsError as e: |
| 62 | + log.info("vehicle%s: SoC & range request not successful" % (vehicle)) |
| 63 | + log.info(e) |
| 64 | + soc_range = CarState(0.0, 0.0) |
| 65 | + return soc_range |
| 66 | + |
| 67 | + |
| 68 | +# main entry - _fetch_soc needs to be run async |
| 69 | +def fetch_soc(user_id: str, password: str, region: str, vehicle: int) -> CarState: |
| 70 | + |
| 71 | + loop = asyncio.new_event_loop() # prepare and call async method |
| 72 | + asyncio.set_event_loop(loop) |
| 73 | + |
| 74 | + # get SoC and range from vehicle via server |
| 75 | + soc_range = loop.run_until_complete(_fetch_soc(user_id, password, region, vehicle)) |
| 76 | + |
| 77 | + return soc_range |
0 commit comments