Closed Gandhi11 closed 3 months ago
Note that in KiaUvoApiEU.py, the default timezone is set. This is not done in KiaUvoApiCA.py.
class KiaUvoApiEU(ApiImplType1):
data_timezone = tz.gettz("Europe/Berlin")
But probably also the above is not correct too and should get/convert to the local timezone whenever a datetime is stored, as well for all other regions? At least that all users of the API do not have to convert to the local timezone themselves??
@cdnninja Do you have an opinion on this?
Depend on the api. Some respond with data all in one time zone. Others are specific to the account. So would need to know that.
@Gandhi11 The datetimes given back for you in Canada, are they correct? So if the UTC datetime was converted to UTC-4, would that be the correct datetime?
@ZuinigeRijder I guess so. Currently all the date returned by the API seems to be in UTC.
So it could be a good solution to handle the conversion on the application that is using the API which should always return the datetime in UTC.
So in my case, it could be converted in your package https://github.com/ZuinigeRijder/hyundai_kia_connect_monitor with a configuration?
Keep in mind that currently were are in daylight saving (EDT) so UTC-4 but sometime we are in eastern time (EST) which is UTC-5.
@Gandhi11 Depends how consistent the behavior is of hyundai_kia_connect_api. Are the date/times consistent? Currently it is not, sometimes it is UTC time and sometimes it is local time?
@cdnninja So the IONIQ 5 in Canada, UTC-4 America/Toronto, shows:
When I run the debug.py, the output is saying timezone=datetime.timezone.utc, in the vm.vehicles
last_updated_at=datetime.datetime
(
2024,
5,
23,
10,
3,
17,
tzinfo=datetime.timezone.utc
),
timezone=datetime.timezone.utc,
My IONIQ 5 in the Netherlands (UTC+2, Europe/Amsterdam) shows:
last_updated_at=datetime.datetime
(
2024,
5,
22,
12,
41,
24,
tzinfo=tzfile
(
'Europe/Berlin'
)
),
timezone=tzfile
(
'Europe/Berlin'
),
I do not know what UK users will get back, if this is returned in their local timezone? I would expect that hyundai_kia_connect_api converts all datetimes to the local timezones (the timezone of the computer where the script runs). So Canada should also convert to local timezone.
Another approach could be to always convert to UTC time, to have consistent behavior in hyundai_kia_connect_api, but then hyundai_kia_connect_monitor (and other uses of the hyundai_kia_connect_api) need to convert to local time.
I locally tried to solve it in hyundai_kia_connect_api.
Added to utils.py:
def get_safe_local_datetime(date: datetime) -> datetime:
if date is not None and date.tzinfo is not None:
date = date.astimezone()
return date
Changed Vehicle.py last_updated_at to private instance variables and getter/setter:
_last_updated_at: datetime.datetime = None
@property
def last_updated_at(self):
return self._last_updated_at
@last_updated_at.setter
def last_updated_at(self, value):
self._last_updated_at = get_safe_local_datetime(value)
And:
@location.setter
def location(self, value):
self._location_latitude = value[0]
self._location_longitude = value[1]
self._location_last_set_time = get_safe_local_datetime(value[2])
Looks like that is working, without changing the rest of the code.
The theory is that the time zone setting in the api is for parsing the source data. This allows the consumer of the api to display in local time zone. Now a bug could exist.
@cdnninja I understand that the Hyundai Bluelink and Kia Connect API behaves different in what timezone is returned, dependent of region. However, I would expect that hyundai_kia_connect_api tries to have an unified entry point/approach and that the consumer of the hyundai_kia_connect_api does not need to think about converting to a local datetime, because sometimes it is in local time and sometimes it is in UTC time.
Would you agree with the approach in my previous post? If yes, I can make a Pull Request.
I'm a bit lost. It is unified. One region returns all data in UTC so we store that with the object. The other one doesn't.
As such the client shouldn't need to worry as the time zone data comes with the time.
I haven't looked at code closely so maybe it is a bug? Using local time zone is risky as it if offset it will create calls from home assistant trying to get local data.
When printed in local time zone is the data what is expected? As in recently updated?
Yes, the client needs to worry, because it always needs to convert to local time, because UTC is not really an end-user datetime. My changes are converting the UTC timezone to the local timezone always. Which my changes, the datetimes are changed from:
last_updated_at=datetime.datetime
(
2024,
5,
22,
12,
41,
24,
tzinfo=tzfile
(
'Europe/Berlin'
)
),
timezone=tzfile
(
'Europe/Berlin'
),
into
_last_updated_at=datetime.datetime
(
2024,
5,
22,
12,
41,
24,
tzinfo=datetime.timezone
(
datetime.timedelta
(
seconds=7200
),
'West-Europa
(
zomertijd
)'
)
),
So still timezone information is in the datetime. But if you display the datetime, it will show you the local datetime with the timezone indication, e.g.
Last updated at : 2024-05-22 12:41:24+02:00
And for Canada, the timezone will not be UTC, but become UTC-4 "-04:00"
So on concept always store in local time?
I'm okay with that however offset can't be used. Since DTC. It would need to poll the system and convert based on that.
I don't have a Hyundai Kia anymore so hard for me to test. :)
Yes, the concept is to always convert to the local timezone. I do not understand the comment about DTC. The conversion takes care of DTC. But I do not see a problem, also not in comparison of delta times.
@Gandhi11 Can you confirm that my changes are working as expected for you? Are the results then correct thereafter?
@ZuinigeRijder After making your changes, I received this error
20240529 09:25:49: Exception: 'property' object has no attribute 'tzinfo'
Traceback (most recent call last):
File "/volume1/Programs/hyundai_kia_connect_monitor/monitor.py", line 416, in handle_vehicles
manager.check_and_refresh_token()
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 130, in check_and_refresh_token
self.initialize()
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 76, in initialize
vehicles = self.api.get_vehicles(self.token)
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/KiaUvoApiCA.py", line 164, in get_vehicles
vehicle: Vehicle = Vehicle(
File "<string>", line 23, in __init__
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/Vehicle.py", line 309, in last_updated_at
self._last_updated_at = get_safe_local_datetime(value)
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/utils.py", line 8, in get_safe_local_datetime
if date is not None and date.tzinfo is not None:
AttributeError: 'property' object has no attribute 'tzinfo'
@Gandhi11 Strange, for me it is working. Can you try this for the method in utils.py.
def get_safe_local_datetime(date: datetime) -> datetime:
if date is not None:
date = date.astimezone()
return date
@ZuinigeRijder Now I get this error
20240529 09:53:00: Exception: 'property' object has no attribute 'astimezone'
Traceback (most recent call last):
File "/volume1/Programs/hyundai_kia_connect_monitor/monitor.py", line 416, in handle_vehicles
manager.check_and_refresh_token()
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 130, in check_and_refresh_token
self.initialize()
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 76, in initialize
vehicles = self.api.get_vehicles(self.token)
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/KiaUvoApiCA.py", line 164, in get_vehicles
vehicle: Vehicle = Vehicle(
File "<string>", line 23, in __init__
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/Vehicle.py", line 309, in last_updated_at
self._last_updated_at = get_safe_local_datetime(value)
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/utils.py", line 9, in get_safe_local_datetime
date = date.astimezone()
AttributeError: 'property' object has no attribute 'astimezone'
I am confused. Apparently in KiaUvoApiCA.py line 241 is constructing a datetime object without timezone info????
vehicle.last_updated_at = parse_datetime(
get_child_value(state, "status.lastStatusDate"), self.data_timezone
)
Can you add a print statement for debugging purposes.....looks like a datetime object is given as parameter without timezone info???
def get_safe_local_datetime(date: datetime) -> datetime:
print(f"get_safe_local_datetime: {date}")
if date is not None and date.tzinfo is not None:
date = date.astimezone()
return date
@Gandhi11 I also made a small test script, because I do not understand why it goes wrong
Can you provide the output of the previous post AND also the output of following test script?
# == test_ca.py Author: Zuinige Rijder ========= # pylint:disable=bare-except
""" Simple Python3 script to tests ca """
import datetime
import re
def parse_datetime(value, timezone) -> datetime.datetime:
"""parse_datetime"""
if value is None:
return datetime.datetime(2000, 1, 1, tzinfo=timezone)
value = value.replace("-", "").replace("T", "").replace(":", "").replace("Z", "")
m = re.match(r"(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})", value)
return datetime.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=timezone,
)
def get_safe_local_datetime(date: datetime) -> datetime:
"""get safe local datetime"""
print(f"get_safe_local_datetime: {date}")
if date is not None and date.tzinfo is not None:
date = date.astimezone()
return date
now = datetime.datetime.now()
get_safe_local_datetime(now)
now2 = parse_datetime("2024-05-2921:33:44", datetime.timezone.utc)
get_safe_local_datetime(now2)
now3 = parse_datetime("2024-05-2921:33:44", None)
get_safe_local_datetime(now3)
I'm also open to a PR once this is understood.
@ZuinigeRijder Here's the results.
For the first output ran on 2024-05-30 21:20 EDT UTC -4 get_safe_local_datetime: 2024-05-31 01:20:03+00:00
For the output of the test script ran on 2024-05-30 21:22 EDT UTC -4.
get_safe_local_datetime: 2024-05-30 21:22:34.814350
get_safe_local_datetime: 2024-05-29 21:33:44+00:00
get_safe_local_datetime: 2024-05-29 21:33:44
Looks like the first print is the good one.
@Gandhi11 Now I am even more confused, so with the first run, running monitor:
For the first output ran on 2024-05-30 21:20 EDT UTC -4 get_safe_local_datetime: 2024-05-31 01:20:03+00:00
You did no longer get the exceptions????:
AttributeError: 'property' object has no attribute 'tzinfo'
and
AttributeError: 'property' object has no attribute 'astimezone'
Note that the test script was just to see what would be the output when running variants. So the second output would be the expected outcome.
@ZuinigeRijder I reverted the change before running the scripts.
Here's the output with the changes you wanted me to apply.
get_safe_local_datetime: <property object at 0x417b8a28>
20240531 08:09:31: Exception: 'property' object has no attribute 'tzinfo'
Traceback (most recent call last):
File "/volume1/Programs/hyundai_kia_connect_monitor/monitor.py", line 416, in handle_vehicles
manager.check_and_refresh_token()
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 130, in check_and_refresh_token
self.initialize()
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 76, in initialize
vehicles = self.api.get_vehicles(self.token)
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/KiaUvoApiCA.py", line 164, in get_vehicles
vehicle: Vehicle = Vehicle(
File "<string>", line 23, in __init__
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/Vehicle.py", line 309, in last_updated_at
self._last_updated_at = get_safe_local_datetime(value)
File "/volume1/Programs/hyundai_kia_connect_monitor/hyundai_kia_connect_api/utils.py", line 9, in get_safe_local_datetime
if date is not None and date.tzinfo is not None:
AttributeError: 'property' object has no attribute 'tzinfo'
The date
variable doesn't seems to be a date... get_safe_local_datetime: <property object at 0x417b8a28>
@Gandhi11 I still do not understand. Can you run my solution again, with the method get_safe_local_datetime in utils.py as:
def get_safe_local_datetime(date: datetime) -> datetime:
"""get safe local datetime"""
print(f"get_safe_local_datetime: {date}")
print(type(date))
print(date)
print("Attributes and methods of date:")
print(dir(date))
if date is not None and hasattr(date, "tzinfo") and date.tzinfo is not None:
date = date.astimezone()
return date
@Gandhi11 Did you not have time for testing what is in the previous message?
I wonder if there have been any further developments on this topic. I managed to get monitor.py, summary.py and dailystats.py running on a Raspberry Pi. But I noticed that the results displayed in the Google Sheets spreadsheet did not make sense. I realized that this appeared to be because the time stamps represented UTC times, rather than Pacific Daylight times. I wrote a little auxiliary script that runs one minute after monitor.py. The script reads the datetime, longitude and latitude from the last row of monitor.csv, determines the time zone from the longitude and latitude and applies the appropriate correction to the datetime and then saves monitor.csv with the modified last row. After the auxiliary script runs, the results shown in the Google Sheets spreadsheet make sense. But I consider this to be somewhat of a kluge, and it would be nice if monitor.py were timezone aware when it is used outside Europe.
@gyveri Which region and which car do you have? Apparently @Gandhi11 is no longer reacting, to do some tests. I cannot do the tests, because I live in region Europe and there the local time is computed correctly. So if you are willing to do some tests, maybe we can fix this for e.g. Canada.
I live in central California (Pacific time zone, currently on Daylight Saving Time UTC-7:00) and have a 2024 Hyundai Ioniq5. I'll be happy to do some tests to help fix this issue.
@gyveri Thanks for helping to test. I will repeat the changes I did locally on top of hyundai_kia_connect_api latest release v3.22.1
Added to utils.py:
def get_safe_local_datetime(date: datetime) -> datetime:
"""get safe local datetime"""
print(f"get_safe_local_datetime: {date}")
print(type(date))
print(date)
print("Attributes and methods of date:")
print(dir(date))
if date is not None and hasattr(date, "tzinfo") and date.tzinfo is not None:
date = date.astimezone()
return date
Note that this does some logging statements (print), so if it does not work I have more of a clue why it does not work. If it works, those print statements can be removed.
Changed Vehicle.py last_updated_at to private instance variables and getter/setter. For convenience I post the whole Vehicle.py version:
# pylint:disable=missing-class-docstring,missing-function-docstring,wildcard-import,unused-wildcard-import,invalid-name
"""Vehicle class"""
import logging
import datetime
import typing
from dataclasses import dataclass, field
from .utils import get_float, get_safe_local_datetime
from .const import DISTANCE_UNITS
_LOGGER = logging.getLogger(__name__)
@dataclass
class TripInfo:
"""Trip Info"""
hhmmss: str = None # will not be filled by summary
drive_time: int = None
idle_time: int = None
distance: int = None
avg_speed: float = None
max_speed: int = None
@dataclass
class DayTripCounts:
"""Day trip counts"""
yyyymmdd: str = None
trip_count: int = None
@dataclass
class MonthTripInfo:
"""Month Trip Info"""
yyyymm: str = None
summary: TripInfo = None
day_list: list[DayTripCounts] = field(default_factory=list)
@dataclass
class DayTripInfo:
"""Day Trip Info"""
yyyymmdd: str = None
summary: TripInfo = None
trip_list: list[TripInfo] = field(default_factory=list)
@dataclass
class DailyDrivingStats:
# energy stats are expressed in watthours (Wh)
date: datetime.datetime = None
total_consumed: int = None
engine_consumption: int = None
climate_consumption: int = None
onboard_electronics_consumption: int = None
battery_care_consumption: int = None
regenerated_energy: int = None
# distance is expressed in (I assume) whatever unit the vehicle is
# configured in. KMs (rounded) in my case
distance: int = None
distance_unit = DISTANCE_UNITS[1] # set to kms by default
@dataclass
class Vehicle:
id: str = None
name: str = None
model: str = None
registration_date: str = None
year: int = None
VIN: str = None
key: str = None
ccu_ccs2_protocol_support: int = None
# Not part of the API, enabled in our library for scanning.
enabled: bool = True
# Shared (EV/PHEV/HEV/IC)
# General
_total_driving_range: float = None
_total_driving_range_value: float = None
_total_driving_range_unit: str = None
_odometer: float = None
_odometer_value: float = None
_odometer_unit: str = None
_geocode_address: str = None
_geocode_name: str = None
car_battery_percentage: int = None
engine_is_running: bool = None
_last_updated_at: datetime.datetime = None
timezone: datetime.timezone = datetime.timezone.utc # default UTC
dtc_count: typing.Union[int, None] = None
dtc_descriptions: typing.Union[dict, None] = None
smart_key_battery_warning_is_on: bool = None
washer_fluid_warning_is_on: bool = None
brake_fluid_warning_is_on: bool = None
# Climate
_air_temperature: float = None
_air_temperature_value: float = None
_air_temperature_unit: str = None
air_control_is_on: bool = None
defrost_is_on: bool = None
steering_wheel_heater_is_on: bool = None
back_window_heater_is_on: bool = None
side_mirror_heater_is_on: bool = None
front_left_seat_status: str = None
front_right_seat_status: str = None
rear_left_seat_status: str = None
rear_right_seat_status: str = None
# Door Status
is_locked: bool = None
front_left_door_is_open: bool = None
front_right_door_is_open: bool = None
back_left_door_is_open: bool = None
back_right_door_is_open: bool = None
trunk_is_open: bool = None
hood_is_open: bool = None
# Window Status
front_left_window_is_open: bool = None
front_right_window_is_open: bool = None
back_left_window_is_open: bool = None
back_right_window_is_open: bool = None
# Tire Pressure
tire_pressure_all_warning_is_on: bool = None
tire_pressure_rear_left_warning_is_on: bool = None
tire_pressure_front_left_warning_is_on: bool = None
tire_pressure_front_right_warning_is_on: bool = None
tire_pressure_rear_right_warning_is_on: bool = None
# Service Data
_next_service_distance: float = None
_next_service_distance_value: float = None
_next_service_distance_unit: str = None
_last_service_distance: float = None
_last_service_distance_value: float = None
_last_service_distance_unit: str = None
# Location
_location_latitude: float = None
_location_longitude: float = None
_location_last_set_time: datetime.datetime = None
# EV fields (EV/PHEV)
ev_charge_port_door_is_open: typing.Union[bool, None] = None
ev_charge_limits_dc: typing.Union[int, None] = None
ev_charge_limits_ac: typing.Union[int, None] = None
ev_charging_current: typing.Union[int, None] = None # Europe feature only
ev_v2l_discharge_limit: typing.Union[int, None] = None
# energy consumed and regenerated since the vehicle was paired with the account
# (so not necessarily for the vehicle's lifetime)
# expressed in watt-hours (Wh)
total_power_consumed: float = None # Europe feature only
total_power_regenerated: float = None # Europe feature only
# energy consumed in the last ~30 days
# expressed in watt-hours (Wh)
power_consumption_30d: float = None # Europe feature only
# Europe feature only
daily_stats: list[DailyDrivingStats] = field(default_factory=list)
month_trip_info: MonthTripInfo = None # Europe feature only
day_trip_info: DayTripInfo = None # Europe feature only
ev_battery_percentage: int = None
ev_battery_soh_percentage: int = None
ev_battery_remain: int = None
ev_battery_capacity: int = None
ev_battery_is_charging: bool = None
ev_battery_is_plugged_in: bool = None
_ev_driving_range: float = None
_ev_driving_range_value: float = None
_ev_driving_range_unit: str = None
_ev_estimated_current_charge_duration: int = None
_ev_estimated_current_charge_duration_value: int = None
_ev_estimated_current_charge_duration_unit: str = None
_ev_estimated_fast_charge_duration: int = None
_ev_estimated_fast_charge_duration_value: int = None
_ev_estimated_fast_charge_duration_unit: str = None
_ev_estimated_portable_charge_duration: int = None
_ev_estimated_portable_charge_duration_value: int = None
_ev_estimated_portable_charge_duration_unit: str = None
_ev_estimated_station_charge_duration: int = None
_ev_estimated_station_charge_duration_value: int = None
_ev_estimated_station_charge_duration_unit: str = None
_ev_target_range_charge_AC: typing.Union[float, None] = None
_ev_target_range_charge_AC_value: typing.Union[float, None] = None
_ev_target_range_charge_AC_unit: typing.Union[str, None] = None
_ev_target_range_charge_DC: typing.Union[float, None] = None
_ev_target_range_charge_DC_value: typing.Union[float, None] = None
_ev_target_range_charge_DC_unit: typing.Union[str, None] = None
ev_first_departure_enabled: typing.Union[bool, None] = None
ev_second_departure_enabled: typing.Union[bool, None] = None
ev_first_departure_days: typing.Union[list, None] = None
ev_second_departure_days: typing.Union[list, None] = None
ev_first_departure_time: typing.Union[datetime.time, None] = None
ev_second_departure_time: typing.Union[datetime.time, None] = None
ev_first_departure_climate_enabled: typing.Union[bool, None] = None
ev_second_departure_climate_enabled: typing.Union[bool, None] = None
_ev_first_departure_climate_temperature: typing.Union[float, None] = None
_ev_first_departure_climate_temperature_value: typing.Union[float, None] = None
_ev_first_departure_climate_temperature_unit: typing.Union[str, None] = None
_ev_second_departure_climate_temperature: typing.Union[float, None] = None
_ev_second_departure_climate_temperature_value: typing.Union[float, None] = None
_ev_second_departure_climate_temperature_unit: typing.Union[str, None] = None
ev_first_departure_climate_defrost: typing.Union[bool, None] = None
ev_second_departure_climate_defrost: typing.Union[bool, None] = None
ev_off_peak_start_time: typing.Union[datetime.time, None] = None
ev_off_peak_end_time: typing.Union[datetime.time, None] = None
ev_off_peak_charge_only_enabled: typing.Union[bool, None] = None
ev_schedule_charge_enabled: typing.Union[bool, None] = None
# IC fields (PHEV/HEV/IC)
_fuel_driving_range: float = None
_fuel_driving_range_value: float = None
_fuel_driving_range_unit: str = None
fuel_level: float = None
fuel_level_is_low: bool = None
# Calculated fields
engine_type: str = None
# Debug fields
data: dict = None
@property
def geocode(self):
return self._geocode_name, self._geocode_address
@geocode.setter
def geocode(self, value):
self._geocode_name = value[0]
self._geocode_address = value[1]
@property
def total_driving_range(self):
return self._total_driving_range
@property
def total_driving_range_unit(self):
return self._total_driving_range_unit
@total_driving_range.setter
def total_driving_range(self, value):
self._total_driving_range_value = value[0]
self._total_driving_range_unit = value[1]
self._total_driving_range = value[0]
@property
def next_service_distance(self):
return self._next_service_distance
@next_service_distance.setter
def next_service_distance(self, value):
self._next_service_distance_value = value[0]
self._next_service_distance_unit = value[1]
self._next_service_distance = value[0]
@property
def last_service_distance(self):
return self._last_service_distance
@last_service_distance.setter
def last_service_distance(self, value):
self._last_service_distance_value = value[0]
self._last_service_distance_unit = value[1]
self._last_service_distance = value[0]
@property
def last_updated_at(self):
return self._last_updated_at
@last_updated_at.setter
def last_updated_at(self, value):
self._last_updated_at = get_safe_local_datetime(value)
@property
def location_latitude(self):
return self._location_latitude
@property
def location_longitude(self):
return self._location_longitude
@property
def location(self):
return self._location_longitude, self._location_latitude
@property
def location_last_updated_at(self):
"""
return last location datetime.
last_updated_at and location_last_updated_at can be different.
The newest of those 2 can be computed by the caller.
"""
return self._location_last_set_time
@location.setter
def location(self, value):
self._location_latitude = value[0]
self._location_longitude = value[1]
self._location_last_set_time = get_safe_local_datetime(value[2])
@property
def odometer(self):
return self._odometer
@property
def odometer_unit(self):
return self._odometer_unit
@odometer.setter
def odometer(self, value):
float_value = get_float(value[0])
self._odometer_value = float_value
self._odometer_unit = value[1]
self._odometer = float_value
@property
def air_temperature(self):
return self._air_temperature
@air_temperature.setter
def air_temperature(self, value):
self._air_temperature_value = value[0]
self._air_temperature_unit = value[1]
self._air_temperature = value[0]
@property
def ev_driving_range(self):
return self._ev_driving_range
@property
def ev_driving_range_unit(self):
return self._ev_driving_range_unit
@ev_driving_range.setter
def ev_driving_range(self, value):
self._ev_driving_range_value = value[0]
self._ev_driving_range_unit = value[1]
self._ev_driving_range = value[0]
@property
def ev_estimated_current_charge_duration(self):
return self._ev_estimated_current_charge_duration
@ev_estimated_current_charge_duration.setter
def ev_estimated_current_charge_duration(self, value):
self._ev_estimated_current_charge_duration_value = value[0]
self._ev_estimated_current_charge_duration_unit = value[1]
self._ev_estimated_current_charge_duration = value[0]
@property
def ev_estimated_fast_charge_duration(self):
return self._ev_estimated_fast_charge_duration
@ev_estimated_fast_charge_duration.setter
def ev_estimated_fast_charge_duration(self, value):
self._ev_estimated_fast_charge_duration_value = value[0]
self._ev_estimated_fast_charge_duration_unit = value[1]
self._ev_estimated_fast_charge_duration = value[0]
@property
def ev_estimated_portable_charge_duration(self):
return self._ev_estimated_portable_charge_duration
@ev_estimated_portable_charge_duration.setter
def ev_estimated_portable_charge_duration(self, value):
self._ev_estimated_portable_charge_duration_value = value[0]
self._ev_estimated_portable_charge_duration_unit = value[1]
self._ev_estimated_portable_charge_duration = value[0]
@property
def ev_estimated_station_charge_duration(self):
return self._ev_estimated_station_charge_duration
@ev_estimated_station_charge_duration.setter
def ev_estimated_station_charge_duration(self, value):
self._ev_estimated_station_charge_duration_value = value[0]
self._ev_estimated_station_charge_duration_unit = value[1]
self._ev_estimated_station_charge_duration = value[0]
@property
def ev_target_range_charge_AC(self):
return self._ev_target_range_charge_AC
@property
def ev_target_range_charge_AC_unit(self):
return self._ev_target_range_charge_AC_unit
@ev_target_range_charge_AC.setter
def ev_target_range_charge_AC(self, value):
self._ev_target_range_charge_AC_value = value[0]
self._ev_target_range_charge_AC_unit = value[1]
self._ev_target_range_charge_AC = value[0]
@property
def ev_target_range_charge_DC(self):
return self._ev_target_range_charge_DC
@property
def ev_target_range_charge_DC_unit(self):
return self._ev_target_range_charge_DC_unit
@ev_target_range_charge_DC.setter
def ev_target_range_charge_DC(self, value):
self._ev_target_range_charge_DC_value = value[0]
self._ev_target_range_charge_DC_unit = value[1]
self._ev_target_range_charge_DC = value[0]
@property
def ev_first_departure_climate_temperature(self):
return self._ev_first_departure_climate_temperature
@property
def ev_first_departure_climate_temperature_unit(self):
return self._ev_first_departure_climate_temperature_unit
@ev_first_departure_climate_temperature.setter
def ev_first_departure_climate_temperature(self, value):
self._ev_first_departure_climate_temperature_value = value[0]
self._ev_first_departure_climate_temperature_unit = value[1]
self._ev_first_departure_climate_temperature = value[0]
@property
def ev_second_departure_climate_temperature(self):
return self._ev_second_departure_climate_temperature
@property
def ev_second_departure_climate_temperature_unit(self):
return self._ev_second_departure_climate_temperature_unit
@ev_second_departure_climate_temperature.setter
def ev_second_departure_climate_temperature(self, value):
self._ev_second_departure_climate_temperature_value = value[0]
self._ev_second_departure_climate_temperature_unit = value[1]
self._ev_second_departure_climate_temperature = value[0]
@property
def fuel_driving_range(self):
return self._fuel_driving_range
@fuel_driving_range.setter
def fuel_driving_range(self, value):
self._fuel_driving_range_value = value[0]
self._fuel_driving_range_unit = value[1]
self._fuel_driving_range = value[0]
-Not sure what's happening here. On a new installation, I updated the .api file, ran monitor.py and got the result below. I reverted to the prior, unmodified .api and got the same result. I also checked the version that's been running happily every hour on another machine and that's giving access token errors as well. It stopped working after the 03:00 PDT run today. Checked the Hyundai app and web site - both say they're having technical problems. Should have checked these first. I'll try again tomorrow.
pi@Pi5-1:~/hyundai_kia_connect_monitor $ python monitor.py 20240716 17:30:29: Exception: 'access_token'I reverted to the prior, unmodified .api and got the same result. I also checked the version that's been running happily every hour on another machine and that's giving access token errors as well. It stopped working after the 03:00 PDT run today. Checked the Hyundai app and web site - both say they're having technical problems. I'll try again tomorrow. Traceback (most recent call last): File "/home/pi/hyundai_kia_connect_monitor/monitor.py", line 416, in handle_vehicles manager.check_and_refresh_token() File "/home/pi/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 130, in check_and_refresh_token self.initialize() File "/home/pi/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 74, in initialize self.token: Token = self.api.login(self.username, self.password) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/pi/hyundai_kia_connect_monitor/hyundai_kia_connect_api/HyundaiBlueLinkAPIUSA.py", line 127, in login access_token = response["access_token"]
KeyError: 'access_token'
20240716 17:30:29: Sleeping a minute
20240716 17:31:30: Exception: 'access_token'
Traceback (most recent call last):
File "/home/pi/hyundai_kia_connect_monitor/monitor.py", line 416, in handle_vehicles
manager.check_and_refresh_token()
File "/home/pi/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 130, in check_and_refresh_token
self.initialize()
File "/home/pi/hyundai_kia_connect_monitor/hyundai_kia_connect_api/VehicleManager.py", line 74, in initialize
self.token: Token = self.api.login(self.username, self.password)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/pi/hyundai_kia_connect_monitor/hyundai_kia_connect_api/HyundaiBlueLinkAPIUSA.py", line 127, in login
access_token = response["access_token"]
~~~~~~~~^^^^^^^^^^^^^^^^
KeyError: 'access_token'
@gyveri Bummer. Hope that the technical problems of Hyundai are solved soon.
It looks like our recent messages have disappeared from this thread. The Hyundai network seems to be back. My hourly running system started giving results at 00:00 today PDT.
I downloaded and installed the updated .api and edited to make the addition to utils.py and replace Vehicle.py . I then ran monitor.py . There were no error messages and it printed the following:
get_safe_local_datetime: 2024-07-17 18:00:27+00:00 <class 'datetime.datetime'> 2024-07-17 18:00:27+00:00 Attributes and methods of date: ['add', 'class', 'delattr', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'getstate', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'ne', 'new', 'radd', 'reduce', 'reduce_ex', 'repr', 'rsub', 'setattr', 'sizeof', 'str', 'sub', 'subclasshook', 'astimezone', 'combine', 'ctime', 'date', 'day', 'dst', 'fold', 'fromisocalendar', 'fromisoformat', 'fromordinal', 'fromtimestamp', 'hour', 'isocalendar', 'isoformat', 'isoweekday', 'max', 'microsecond', 'min', 'minute', 'month', 'now', 'replace', 'resolution', 'second', 'strftime', 'strptime', 'time', 'timestamp', 'timetuple', 'timetz', 'today', 'toordinal', 'tzinfo', 'tzname', 'utcfromtimestamp', 'utcnow', 'utcoffset', 'utctimetuple', 'weekday', 'year'] get_safe_local_datetime: 2024-07-17 18:09:59+00:00 <class 'datetime.datetime'> 2024-07-17 18:09:59+00:00 Attributes and methods of date: ['add', 'class', 'delattr', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'getstate', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'ne', 'new', 'radd', 'reduce', 'reduce_ex', 'repr', 'rsub', 'setattr', 'sizeof', 'str', 'sub', 'subclasshook', 'astimezone', 'combine', 'ctime', 'date', 'day', 'dst', 'fold', 'fromisocalendar', 'fromisoformat', 'fromordinal', 'fromtimestamp', 'hour', 'isocalendar', 'isoformat', 'isoweekday', 'max', 'microsecond', 'min', 'minute', 'month', 'now', 'replace', 'resolution', 'second', 'strftime', 'strptime', 'time', 'timestamp', 'timetuple', 'timetz', 'today', 'toordinal', 'tzinfo', 'tzname', 'utcfromtimestamp', 'utcnow', 'utcoffset', 'utctimetuple', 'weekday', 'year']
In the above, I noticed that "print(f"get_safe_local_datetime: {date}")" appears to print the current UTC datetime - date doesn't get localized until the next-but-last line.
monitor.csv now contains the following (location details removed): datetime, longitude, latitude, engineOn, 12V%, odometer, SOC%, charging, plugged, address, EV range 2024-07-17 11:09:59-07:00, -121, 36, False, 81, 3256, 80, False, 2, [address details] ; California; United States, 0 The datetime information now looks correct for my location.
What next?
Ignore my comment regarding the recent messages - they reappeared after I posted my comment.
I have now installed the modified API in my hourly machine and it appears to be running OK. I have disabled my datetime localization script since the latest entry in monitor.csv now has the correct datetime for my location.
@gyveri Closed, as this is now part of Release v3.22.2 https://github.com/Hyundai-Kia-Connect/hyundai_kia_connect_api/releases/tag/v3.22.2
Description
When retrieving vehicle information, the timezone is set to UTC but the car is in UTC-4 America/Toronto
What I Did
Output
Maybe I am missing something. Any help would be appreciated.