text: import logging
  
  from datetime import datetime
  import re
  import requests
  import traceback
  
  from homeassistant.config_entries import ConfigEntry
  from homeassistant.core import HomeAssistant
  from homeassistant.helpers.dispatcher import (
      async_dispatcher_connect,
      async_dispatcher_send,
  )
  from homeassistant.helpers.event import async_call_later
  import homeassistant.util.dt as dt_util
  
  from .const import *
  from .KiaUvoApiImpl import KiaUvoApiImpl
  from .Token import Token
  
  _LOGGER = logging.getLogger(__name__)
  
  class Vehicle(object):
      def __init__(
          self,
          hass: HomeAssistant,
          config_entry: ConfigEntry,
          token: Token,
          kia_uvo_api: KiaUvoApiImpl,
          unit_of_measurement: str,
          enable_geolocation_entity: bool,
      ):
          self.hass = hass
          self.config_entry = config_entry
          self.token: Token = token
          self.kia_uvo_api: KiaUvoApiImpl = kia_uvo_api
          self.unit_of_measurement: str = unit_of_measurement
          self.enable_geolocation_entity: bool = enable_geolocation_entity
          self.name = token.vehicle_name
          self.model = token.vehicle_model
          self.id = token.vehicle_id
          self.registration_date = token.vehicle_registration_date
          self.vehicle_data = {}
          self.engine_type = None
          self.last_updated: datetime = datetime.min
          self.force_update_try_caller = None
          self.topic_update = TOPIC_UPDATE.format(self.id)
          self.current_ev_battery = None
          _LOGGER.debug(f"{DOMAIN} - Received token into Vehicle Object {vars(token)}")
  
      async def update(self):
          try:
              current_vehicle_location = self.get_child_value("vehicleLocation")
              self.vehicle_data = await self.hass.async_add_executor_job(self.kia_uvo_api.get_cached_vehicle_status, self.token)
              self.set_last_updated()
              self.set_engine_type()
              if self.enable_geolocation_entity == True:
                  await self.hass.async_add_executor_job(self.set_geocoded_location, current_vehicle_location)
  
              async_dispatcher_send(self.hass, self.topic_update)
          except Exception as ex:
              _LOGGER.error(f"{DOMAIN} - Exception in update : %s - traceback: %s", ex, traceback.format_exc())
  
      async def force_update(self):
          await self.hass.async_add_executor_job(self.kia_uvo_api.update_vehicle_status, self.token)
          await self.update()
  
      async def force_update_loop(self, _):
          _LOGGER.debug(
              f"{DOMAIN} - force_update_loop start {self.force_update_try_count} {COUNT_FORCE_UPDATE_AFTER_COMMAND}"
          )
          if self.force_update_try_count == COUNT_FORCE_UPDATE_AFTER_COMMAND:
              self.force_update_try_count = 0
              return
  
          last_updated: datetime = self.last_updated
          _LOGGER.debug(f"{DOMAIN} - force_update_loop last_updated {last_updated}")
  
          await self.force_update()
          _LOGGER.debug(
              f"{DOMAIN} - force_update_loop force_update_finished {last_updated} {self.last_updated}"
          )
          if last_updated == self.last_updated:
              self.force_update_try_count = self.force_update_try_count + 1
              self.force_update_try_caller = async_call_later(
                  self.hass, INTERVAL_FORCE_UPDATE_AFTER_COMMAND, self.force_update_loop
              )
  
      def set_geocoded_location(self, old_vehicle_location):
          old_lat = None
          old_lon = None
          old_geocode = None
          if not old_vehicle_location is None:
              old_lat = old_vehicle_location["coord"]["lat"]
              old_lon = old_vehicle_location["coord"]["lon"]
              old_geocode = old_vehicle_location.get("geocodedLocation", None)
  
          new_lat = self.get_child_value("vehicleLocation.coord.lat")
          new_lon = self.get_child_value("vehicleLocation.coord.lon")
  
          if (old_lat != new_lat or old_lon != new_lon) or old_geocode is None:
              self.vehicle_data["vehicleLocation"]["geocodedLocation"] = self.kia_uvo_api.get_geocoded_location(new_lat, new_lon)
          else:
              self.vehicle_data["vehicleLocation"]["geocodedLocation"] = old_geocode
  
  
      async def lock_action(self, action: VEHICLE_LOCK_ACTION):
          await self.hass.async_add_executor_job(
              self.kia_uvo_api.lock_action, self.token, action.value
          )
          self.force_update_try_count = 0
          self.force_update_try_caller = async_call_later(
              self.hass, START_FORCE_UPDATE_AFTER_COMMAND, self.force_update_loop
          )
  
      async def refresh_token(self):
          _LOGGER.debug(
              f"{DOMAIN} - Refresh token started {self.token.valid_until} {datetime.now()} {self.token.valid_until <= datetime.now().strftime(DATE_FORMAT)}"
          )
          if self.token.valid_until <= datetime.now().strftime(DATE_FORMAT):
              _LOGGER.debug(f"{DOMAIN} - Refresh token expired")
              await self.hass.async_add_executor_job(self.login)
              return True
          return False
  
      async def start_climate(self, set_temp, duration, defrost, climate, heating):
          if (set_temp is None):
              set_temp = 21
          if (duration is None):
              duration = 5
          if (defrost is None):
              defrost = False
          if (climate is None):
              climate = True
          if (heating is None):
              heating = False
          await self.hass.async_add_executor_job(
              self.kia_uvo_api.start_climate, self.token, set_temp, duration, defrost, climate, heating
          )
          self.force_update_try_count = 0
          self.force_update_try_caller = async_call_later(
              self.hass, START_FORCE_UPDATE_AFTER_COMMAND, self.force_update_loop
          )
  
      async def stop_climate(self):
          await self.hass.async_add_executor_job(
              self.kia_uvo_api.stop_climate, self.token
          )
          self.force_update_try_count = 0
          self.force_update_try_caller = async_call_later(
              self.hass, START_FORCE_UPDATE_AFTER_COMMAND, self.force_update_loop
          )
  
      async def start_charge(self):
          await self.hass.async_add_executor_job(
              self.kia_uvo_api.start_charge, self.token
          )
          self.force_update_try_count = 0
          self.force_update_try_caller = async_call_later(
              self.hass, START_FORCE_UPDATE_AFTER_COMMAND, self.force_update_loop
          )
  
      async def stop_charge(self):
          await self.hass.async_add_executor_job(self.kia_uvo_api.stop_charge, self.token)
          self.force_update_try_count = 0
          self.force_update_try_caller = async_call_later(
              self.hass, START_FORCE_UPDATE_AFTER_COMMAND, self.force_update_loop
          )
  
      def login(self):
          self.token = self.kia_uvo_api.login()
  
      def set_last_updated(self):
          m = re.match(
              r"(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})",
              self.vehicle_data["vehicleStatus"]["time"],
          )
          local_timezone = self.kia_uvo_api.get_timezone_by_region()
          last_updated = datetime(
              year=int(m.group(1)),
              month=int(m.group(2)),
              day=int(m.group(3)),
              hour=int(m.group(4)),
              minute=int(m.group(5)),
              second=int(m.group(6)),
              tzinfo=local_timezone,
          )
  
          _LOGGER.debug(
              f"{DOMAIN} - LastUpdated {last_updated} - Timezone {local_timezone}"
          )
  
          self.last_updated = last_updated
  
      def set_engine_type(self):
          if "evStatus" in self.vehicle_data["vehicleStatus"] and "lowFuelLight" in self.vehicle_data["vehicleStatus"]:
              self.engine_type = VEHICLE_ENGINE_TYPE.PHEV
          else:
              if "evStatus" in self.vehicle_data["vehicleStatus"]:
                  self.engine_type = VEHICLE_ENGINE_TYPE.EV
              else:
                  self.engine_type = VEHICLE_ENGINE_TYPE.IC
          _LOGGER.debug(f"{DOMAIN} - Engine type set {self.engine_type}")
  
      def get_child_value(self, key):
          value = self.vehicle_data
          for x in key.split("."):
              try:
                  value = value[x]
              except:
                  try:
                      value = value[int(x)]
                  except:
                      value = None
          return value
  