hyundai-kia-connect-api 3.19.1__tar.gz → 3.20.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/PKG-INFO +2 -1
  2. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/README.rst +1 -0
  3. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/ApiImpl.py +32 -0
  4. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/KiaUvoApiEU.py +288 -15
  5. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/Vehicle.py +44 -0
  6. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/VehicleManager.py +13 -1
  7. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/__init__.py +6 -1
  8. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api.egg-info/PKG-INFO +2 -1
  9. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/setup.py +1 -1
  10. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/AUTHORS.rst +0 -0
  11. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/CONTRIBUTING.rst +0 -0
  12. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/HISTORY.rst +0 -0
  13. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/LICENSE +0 -0
  14. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/MANIFEST.in +0 -0
  15. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/Makefile +0 -0
  16. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/authors.rst +0 -0
  17. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/conf.py +0 -0
  18. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/contributing.rst +0 -0
  19. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/history.rst +0 -0
  20. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/index.rst +0 -0
  21. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/installation.rst +0 -0
  22. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/make.bat +0 -0
  23. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/readme.rst +0 -0
  24. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/docs/usage.rst +0 -0
  25. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/ApiImplType1.py +0 -0
  26. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/HyundaiBlueLinkAPIUSA.py +0 -0
  27. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/KiaUvoAPIUSA.py +0 -0
  28. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/KiaUvoApiAU.py +0 -0
  29. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/KiaUvoApiCA.py +0 -0
  30. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/KiaUvoApiCN.py +0 -0
  31. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/Token.py +0 -0
  32. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/const.py +0 -0
  33. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/exceptions.py +0 -0
  34. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api/utils.py +0 -0
  35. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api.egg-info/SOURCES.txt +0 -0
  36. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api.egg-info/dependency_links.txt +0 -0
  37. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api.egg-info/not-zip-safe +0 -0
  38. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api.egg-info/requires.txt +0 -0
  39. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/hyundai_kia_connect_api.egg-info/top_level.txt +0 -0
  40. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/requirements.txt +0 -0
  41. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/setup.cfg +0 -0
  42. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/tests/__init__.py +0 -0
  43. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/tests/au_login_test.py +0 -0
  44. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/tests/ca_login_test.py +0 -0
  45. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/tests/eu_check_response_for_errors_test.py +0 -0
  46. {hyundai_kia_connect_api-3.19.1 → hyundai_kia_connect_api-3.20.2}/tests/eu_login_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hyundai_kia_connect_api
3
- Version: 3.19.1
3
+ Version: 3.20.2
4
4
  Summary: Python Boilerplate contains all the boilerplate you need to create a Python package.
5
5
  Home-page: https://github.com/fuatakgun/hyundai_kia_connect_api
6
6
  Author: Fuat Akgun
@@ -112,6 +112,7 @@ For a list of language codes, see here: https://www.science.co.il/language/Codes
112
112
  - "fi" Finnish
113
113
  - "pt" Portuguese
114
114
 
115
+
115
116
  In Europe also trip info can be retrieved. For a month you can ask the days with trips. And you can ask for a specific day for all the trips of that specific day.::
116
117
  - First call vm.update_month_trip_info(vehicle.id, yyymm) before getting vehicle.month_trip_info for that month
117
118
  - First call vm.update_day_trip_info(vehicle.id, day.yyyymmdd) before getting vehicle.day_trip_info for that day
@@ -90,6 +90,7 @@ For a list of language codes, see here: https://www.science.co.il/language/Codes
90
90
  - "fi" Finnish
91
91
  - "pt" Portuguese
92
92
 
93
+
93
94
  In Europe also trip info can be retrieved. For a month you can ask the days with trips. And you can ask for a specific day for all the trips of that specific day.::
94
95
  - First call vm.update_month_trip_info(vehicle.id, yyymm) before getting vehicle.month_trip_info for that month
95
96
  - First call vm.update_day_trip_info(vehicle.id, day.yyyymmdd) before getting vehicle.day_trip_info for that day
@@ -37,6 +37,26 @@ class WindowRequestOptions:
37
37
  front_right: WINDOW_STATE = None
38
38
 
39
39
 
40
+ @dataclass
41
+ class ScheduleChargingClimateRequestOptions:
42
+ @dataclass
43
+ class DepartureOptions:
44
+ enabled: bool = None
45
+ days: list[int] = None # Sun=0, Mon=1, ..., Sat=6
46
+ time: dt.time = None
47
+
48
+ first_departure: DepartureOptions = None
49
+ second_departure: DepartureOptions = None
50
+ charging_enabled: bool = None
51
+ off_peak_start_time: dt.time = None
52
+ off_peak_end_time: dt.time = None
53
+ off_peak_charge_only_enabled: bool = None
54
+ climate_enabled: bool = None
55
+ temperature: float = None
56
+ temperature_unit: int = None
57
+ defrost: bool = None
58
+
59
+
40
60
  class ApiImpl:
41
61
  data_timezone = dt.timezone.utc
42
62
  temperature_range = None
@@ -163,3 +183,15 @@ class ApiImpl:
163
183
  day_trip_info: DayTripInfo = None
164
184
  """
165
185
  pass
186
+
187
+ def schedule_charging_and_climate(
188
+ self,
189
+ token: Token,
190
+ vehicle: Vehicle,
191
+ options: ScheduleChargingClimateRequestOptions,
192
+ ) -> str:
193
+ """
194
+ Europe feature only.
195
+ Schedule charging and climate control. Returns the tracking ID
196
+ """
197
+ pass
@@ -7,6 +7,7 @@ import random
7
7
  import datetime as dt
8
8
  import logging
9
9
  import uuid
10
+ import math
10
11
  from time import sleep
11
12
  from urllib.parse import parse_qs, urlparse
12
13
 
@@ -17,6 +18,7 @@ from dateutil import tz
17
18
 
18
19
  from .ApiImpl import (
19
20
  ClimateRequestOptions,
21
+ ScheduleChargingClimateRequestOptions,
20
22
  )
21
23
  from .ApiImplType1 import ApiImplType1
22
24
 
@@ -228,6 +230,16 @@ class KiaUvoApiEU(ApiImplType1):
228
230
  + "&state=$service_id:$user_id"
229
231
  )
230
232
 
233
+ def _get_control_headers(self, token: Token, vehicle: Vehicle) -> dict:
234
+ control_token, _ = self._get_control_token(token)
235
+ authenticated_headers = self._get_authenticated_headers(
236
+ token, vehicle.ccu_ccs2_protocol_support
237
+ )
238
+ return authenticated_headers | {
239
+ "Authorization": control_token,
240
+ "AuthorizationCCSP": control_token,
241
+ }
242
+
231
243
  def login(self, username: str, password: str) -> Token:
232
244
  stamp = self._get_stamp()
233
245
  device_id = self._get_device_id(stamp)
@@ -265,7 +277,8 @@ class KiaUvoApiEU(ApiImplType1):
265
277
  def get_vehicles(self, token: Token) -> list[Vehicle]:
266
278
  url = self.SPA_API_URL + "vehicles"
267
279
  response = requests.get(
268
- url, headers=self._get_authenticated_headers(token)
280
+ url,
281
+ headers=self._get_authenticated_headers(token),
269
282
  ).json()
270
283
  _LOGGER.debug(f"{DOMAIN} - Get Vehicles Response: {response}")
271
284
  _check_response_for_errors(response)
@@ -677,6 +690,72 @@ class KiaUvoApiEU(ApiImplType1):
677
690
  ),
678
691
  )
679
692
 
693
+ vehicle.ev_first_departure_climate_enabled = bool(
694
+ get_child_value(
695
+ state,
696
+ "vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.airCtrl", # noqa
697
+ )
698
+ )
699
+
700
+ vehicle.ev_second_departure_climate_enabled = bool(
701
+ get_child_value(
702
+ state,
703
+ "vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.airCtrl", # noqa
704
+ )
705
+ )
706
+
707
+ if get_child_value(
708
+ state,
709
+ "vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.airTemp.value",
710
+ ): # noqa
711
+ temp_index = get_hex_temp_into_index(
712
+ get_child_value(
713
+ state,
714
+ "vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.airTemp.value",
715
+ ) # noqa
716
+ )
717
+
718
+ vehicle.ev_first_departure_climate_temperature = (
719
+ self.temperature_range[temp_index],
720
+ TEMPERATURE_UNITS[
721
+ get_child_value(
722
+ state,
723
+ "vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.airTemp.unit", # noqa
724
+ )
725
+ ],
726
+ )
727
+
728
+ if get_child_value(
729
+ state,
730
+ "vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.airTemp.value",
731
+ ): # noqa
732
+ temp_index = get_hex_temp_into_index(
733
+ get_child_value(
734
+ state,
735
+ "vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.airTemp.value",
736
+ ) # noqa
737
+ )
738
+
739
+ vehicle.ev_second_departure_climate_temperature = (
740
+ self.temperature_range[temp_index],
741
+ TEMPERATURE_UNITS[
742
+ get_child_value(
743
+ state,
744
+ "vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.airTemp.unit", # noqa
745
+ )
746
+ ],
747
+ )
748
+
749
+ vehicle.ev_first_departure_climate_defrost = get_child_value(
750
+ state,
751
+ "vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.defrost", # noqa
752
+ )
753
+
754
+ vehicle.ev_second_departure_climate_defrost = get_child_value(
755
+ state,
756
+ "vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.defrost", # noqa
757
+ )
758
+
680
759
  vehicle.ev_off_peak_start_time = self._get_time_from_string(
681
760
  get_child_value(
682
761
  state,
@@ -720,6 +799,23 @@ class KiaUvoApiEU(ApiImplType1):
720
799
  ):
721
800
  vehicle.ev_off_peak_charge_only_enabled = False
722
801
 
802
+ if (
803
+ get_child_value(
804
+ state,
805
+ "vehicleStatus.evStatus.reservChargeInfos.reservFlag", # noqa
806
+ )
807
+ == 1
808
+ ):
809
+ vehicle.ev_schedule_charge_enabled = True
810
+ elif (
811
+ get_child_value(
812
+ state,
813
+ "vehicleStatus.evStatus.reservChargeInfos.reservFlag", # noqa
814
+ )
815
+ == 0
816
+ ):
817
+ vehicle.ev_schedule_charge_enabled = False
818
+
723
819
  vehicle.washer_fluid_warning_is_on = get_child_value(
724
820
  state, "vehicleStatus.washerFluidStatus"
725
821
  )
@@ -774,7 +870,10 @@ class KiaUvoApiEU(ApiImplType1):
774
870
 
775
871
  try:
776
872
  response = requests.get(
777
- url, headers=self._get_authenticated_headers(token)
873
+ url,
874
+ headers=self._get_authenticated_headers(
875
+ token, vehicle.ccu_ccs2_protocol_support
876
+ ),
778
877
  ).json()
779
878
  _LOGGER.debug(f"{DOMAIN} - _get_location response: {response}")
780
879
  _check_response_for_errors(response)
@@ -786,7 +885,10 @@ class KiaUvoApiEU(ApiImplType1):
786
885
  def _get_forced_vehicle_state(self, token: Token, vehicle: Vehicle) -> dict:
787
886
  url = self.SPA_API_URL + "vehicles/" + vehicle.id + "/status"
788
887
  response = requests.get(
789
- url, headers=self._get_authenticated_headers(token)
888
+ url,
889
+ headers=self._get_authenticated_headers(
890
+ token, vehicle.ccu_ccs2_protocol_support
891
+ ),
790
892
  ).json()
791
893
  _LOGGER.debug(f"{DOMAIN} - Received forced vehicle data: {response}")
792
894
  _check_response_for_errors(response)
@@ -802,7 +904,11 @@ class KiaUvoApiEU(ApiImplType1):
802
904
  payload = {"action": action.value, "deviceId": token.device_id}
803
905
  _LOGGER.debug(f"{DOMAIN} - Lock Action Request: {payload}")
804
906
  response = requests.post(
805
- url, json=payload, headers=self._get_authenticated_headers(token)
907
+ url,
908
+ json=payload,
909
+ headers=self._get_authenticated_headers(
910
+ token, vehicle.ccu_ccs2_protocol_support
911
+ ),
806
912
  ).json()
807
913
  _LOGGER.debug(f"{DOMAIN} - Lock Action Response: {response}")
808
914
  _check_response_for_errors(response)
@@ -817,7 +923,7 @@ class KiaUvoApiEU(ApiImplType1):
817
923
  payload = {"action": action.value}
818
924
  _LOGGER.debug(f"{DOMAIN} - Charge Port Action Request: {payload}")
819
925
  response = requests.post(
820
- url, json=payload, headers=self._get_authenticated_headers(token)
926
+ url, json=payload, headers=self._get_control_headers(token, vehicle)
821
927
  ).json()
822
928
  _LOGGER.debug(f"{DOMAIN} - Charge Port Action Response: {response}")
823
929
  _check_response_for_errors(response)
@@ -858,7 +964,11 @@ class KiaUvoApiEU(ApiImplType1):
858
964
  }
859
965
  _LOGGER.debug(f"{DOMAIN} - Start Climate Action Request: {payload}")
860
966
  response = requests.post(
861
- url, json=payload, headers=self._get_authenticated_headers(token)
967
+ url,
968
+ json=payload,
969
+ headers=self._get_authenticated_headers(
970
+ token, vehicle.ccu_ccs2_protocol_support
971
+ ),
862
972
  ).json()
863
973
  _LOGGER.debug(f"{DOMAIN} - Start Climate Action Response: {response}")
864
974
  _check_response_for_errors(response)
@@ -880,7 +990,11 @@ class KiaUvoApiEU(ApiImplType1):
880
990
  }
881
991
  _LOGGER.debug(f"{DOMAIN} - Stop Climate Action Request: {payload}")
882
992
  response = requests.post(
883
- url, json=payload, headers=self._get_authenticated_headers(token)
993
+ url,
994
+ json=payload,
995
+ headers=self._get_authenticated_headers(
996
+ token, vehicle.ccu_ccs2_protocol_support
997
+ ),
884
998
  ).json()
885
999
  _LOGGER.debug(f"{DOMAIN} - Stop Climate Action Response: {response}")
886
1000
  _check_response_for_errors(response)
@@ -893,7 +1007,11 @@ class KiaUvoApiEU(ApiImplType1):
893
1007
  payload = {"action": "start", "deviceId": token.device_id}
894
1008
  _LOGGER.debug(f"{DOMAIN} - Start Charge Action Request: {payload}")
895
1009
  response = requests.post(
896
- url, json=payload, headers=self._get_authenticated_headers(token)
1010
+ url,
1011
+ json=payload,
1012
+ headers=self._get_authenticated_headers(
1013
+ token, vehicle.ccu_ccs2_protocol_support
1014
+ ),
897
1015
  ).json()
898
1016
  _LOGGER.debug(f"{DOMAIN} - Start Charge Action Response: {response}")
899
1017
  _check_response_for_errors(response)
@@ -906,7 +1024,11 @@ class KiaUvoApiEU(ApiImplType1):
906
1024
  payload = {"action": "stop", "deviceId": token.device_id}
907
1025
  _LOGGER.debug(f"{DOMAIN} - Stop Charge Action Request {payload}")
908
1026
  response = requests.post(
909
- url, json=payload, headers=self._get_authenticated_headers(token)
1027
+ url,
1028
+ json=payload,
1029
+ headers=self._get_authenticated_headers(
1030
+ token, vehicle.ccu_ccs2_protocol_support
1031
+ ),
910
1032
  ).json()
911
1033
  _LOGGER.debug(f"{DOMAIN} - Stop Charge Action Response: {response}")
912
1034
  _check_response_for_errors(response)
@@ -920,7 +1042,10 @@ class KiaUvoApiEU(ApiImplType1):
920
1042
 
921
1043
  _LOGGER.debug(f"{DOMAIN} - Get Charging Limits Request")
922
1044
  response = requests.get(
923
- url, headers=self._get_authenticated_headers(token)
1045
+ url,
1046
+ headers=self._get_authenticated_headers(
1047
+ token, vehicle.ccu_ccs2_protocol_support
1048
+ ),
924
1049
  ).json()
925
1050
  _LOGGER.debug(f"{DOMAIN} - Get Charging Limits Response: {response}")
926
1051
  _check_response_for_errors(response)
@@ -947,7 +1072,9 @@ class KiaUvoApiEU(ApiImplType1):
947
1072
  response = requests.post(
948
1073
  url,
949
1074
  json=payload,
950
- headers=self._get_authenticated_headers(token),
1075
+ headers=self._get_authenticated_headers(
1076
+ token, vehicle.ccu_ccs2_protocol_support
1077
+ ),
951
1078
  )
952
1079
  response = response.json()
953
1080
  _LOGGER.debug(f"{DOMAIN} - get_trip_info response {response}")
@@ -1058,7 +1185,9 @@ class KiaUvoApiEU(ApiImplType1):
1058
1185
  responseAlltime = requests.post(
1059
1186
  url,
1060
1187
  json={"periodTarget": 1},
1061
- headers=self._get_authenticated_headers(token),
1188
+ headers=self._get_authenticated_headers(
1189
+ token, vehicle.ccu_ccs2_protocol_support
1190
+ ),
1062
1191
  )
1063
1192
  responseAlltime = responseAlltime.json()
1064
1193
  _LOGGER.debug(f"{DOMAIN} - get_driving_info responseAlltime {responseAlltime}")
@@ -1067,7 +1196,9 @@ class KiaUvoApiEU(ApiImplType1):
1067
1196
  response30d = requests.post(
1068
1197
  url,
1069
1198
  json={"periodTarget": 0},
1070
- headers=self._get_authenticated_headers(token),
1199
+ headers=self._get_authenticated_headers(
1200
+ token, vehicle.ccu_ccs2_protocol_support
1201
+ ),
1071
1202
  )
1072
1203
  response30d = response30d.json()
1073
1204
  _LOGGER.debug(f"{DOMAIN} - get_driving_info response30d {response30d}")
@@ -1144,12 +1275,130 @@ class KiaUvoApiEU(ApiImplType1):
1144
1275
  ]
1145
1276
  }
1146
1277
  response = requests.post(
1147
- url, json=body, headers=self._get_authenticated_headers(token)
1278
+ url,
1279
+ json=body,
1280
+ headers=self._get_authenticated_headers(
1281
+ token, vehicle.ccu_ccs2_protocol_support
1282
+ ),
1148
1283
  ).json()
1149
1284
  _LOGGER.debug(f"{DOMAIN} - Set Charge Limits Response: {response}")
1150
1285
  _check_response_for_errors(response)
1151
1286
  return response["msgId"]
1152
1287
 
1288
+ def schedule_charging_and_climate(
1289
+ self,
1290
+ token: Token,
1291
+ vehicle: Vehicle,
1292
+ options: ScheduleChargingClimateRequestOptions,
1293
+ ) -> str:
1294
+ url = self.SPA_API_URL_V2 + "vehicles/" + vehicle.id
1295
+ url = url + "/ccs2" # does not depend on vehicle.ccu_ccs2_protocol_support
1296
+ url = url + "/reservation/chargehvac"
1297
+
1298
+ def set_default_departure_options(
1299
+ departure_options: ScheduleChargingClimateRequestOptions.DepartureOptions,
1300
+ ) -> None:
1301
+ if departure_options.enabled is None:
1302
+ departure_options.enabled = False
1303
+ if departure_options.days is None:
1304
+ departure_options.days = []
1305
+ if departure_options.time is None:
1306
+ departure_options.time = dt.time()
1307
+
1308
+ if options.first_departure is None:
1309
+ options.first_departure = (
1310
+ ScheduleChargingClimateRequestOptions.DepartureOptions()
1311
+ )
1312
+ if options.second_departure is None:
1313
+ options.second_departure = (
1314
+ ScheduleChargingClimateRequestOptions.DepartureOptions()
1315
+ )
1316
+
1317
+ set_default_departure_options(options.first_departure)
1318
+ set_default_departure_options(options.second_departure)
1319
+ departures = [options.first_departure, options.second_departure]
1320
+
1321
+ if options.charging_enabled is None:
1322
+ options.charging_enabled = False
1323
+ if options.off_peak_start_time is None:
1324
+ options.off_peak_start_time = dt.time()
1325
+ if options.off_peak_end_time is None:
1326
+ options.off_peak_end_time = options.off_peak_start_time
1327
+ if options.off_peak_charge_only_enabled is None:
1328
+ options.off_peak_charge_only_enabled = False
1329
+ if options.climate_enabled is None:
1330
+ options.climate_enabled = False
1331
+ if options.temperature is None:
1332
+ options.temperature = 21.0
1333
+ if options.temperature_unit is None:
1334
+ options.temperature_unit = 0
1335
+ if options.defrost is None:
1336
+ options.defrost = False
1337
+
1338
+ temperature: float = options.temperature
1339
+ if options.temperature_unit == 0:
1340
+ # Round to nearest 0.5
1341
+ temperature = round(temperature * 2.0) / 2.0
1342
+ # Cap at 27, floor at 17
1343
+ if temperature > 27.0:
1344
+ temperature = 27.0
1345
+ elif temperature < 17.0:
1346
+ temperature = 17.0
1347
+
1348
+ payload = {
1349
+ "reservChargeInfo" + str(i + 1): {
1350
+ "reservChargeSet": departures[i].enabled,
1351
+ "reservInfo": {
1352
+ "day": departures[i].days,
1353
+ "time": {
1354
+ "time": departures[i].time.strftime("%I%M"),
1355
+ "timeSection": 1 if departures[i].time >= dt.time(12, 0) else 0,
1356
+ },
1357
+ },
1358
+ "reservFatcSet": {
1359
+ "airCtrl": 1 if options.climate_enabled else 0,
1360
+ "airTemp": {
1361
+ "value": "{:.1f}".format(temperature),
1362
+ "hvacTempType": 1,
1363
+ "unit": options.temperature_unit,
1364
+ },
1365
+ "heating1": 0,
1366
+ "defrost": options.defrost,
1367
+ },
1368
+ }
1369
+ for i in range(2)
1370
+ }
1371
+
1372
+ payload = payload | {
1373
+ "offPeakPowerInfo": {
1374
+ "offPeakPowerTime1": {
1375
+ "endtime": {
1376
+ "timeSection": 1
1377
+ if options.off_peak_end_time >= dt.time(12, 0)
1378
+ else 0,
1379
+ "time": options.off_peak_end_time.strftime("%I%M"),
1380
+ },
1381
+ "starttime": {
1382
+ "timeSection": 1
1383
+ if options.off_peak_start_time >= dt.time(12, 0)
1384
+ else 0,
1385
+ "time": options.off_peak_start_time.strftime("%I%M"),
1386
+ },
1387
+ },
1388
+ "offPeakPowerFlag": 2 if options.off_peak_charge_only_enabled else 1,
1389
+ },
1390
+ "reservFlag": 1 if options.charging_enabled else 0,
1391
+ }
1392
+
1393
+ _LOGGER.debug(f"{DOMAIN} - Schedule Charging and Climate Request: {payload}")
1394
+ response = requests.post(
1395
+ url, json=payload, headers=self._get_control_headers(token, vehicle)
1396
+ ).json()
1397
+ _LOGGER.debug(f"{DOMAIN} - Schedule Charging and Climate Response: {response}")
1398
+ _check_response_for_errors(response)
1399
+ token.device_id = self._get_device_id(self._get_stamp())
1400
+ return response["msgId"]
1401
+
1153
1402
  def _get_stamp(self) -> str:
1154
1403
  raw_data = f"{self.APP_ID}:{int(dt.datetime.now().timestamp())}".encode()
1155
1404
  result = bytes(b1 ^ b2 for b1, b2 in zip(self.CFB, raw_data))
@@ -1380,6 +1629,27 @@ class KiaUvoApiEU(ApiImplType1):
1380
1629
  refresh_token = token_type + " " + response["access_token"]
1381
1630
  return token_type, refresh_token
1382
1631
 
1632
+ def _get_control_token(self, token: Token) -> Token:
1633
+ url = self.USER_API_URL + "pin?token="
1634
+ headers = {
1635
+ "Authorization": token.access_token,
1636
+ "Content-type": "application/json",
1637
+ "Host": self.BASE_URL,
1638
+ "Accept-Encoding": "gzip",
1639
+ "User-Agent": USER_AGENT_OK_HTTP,
1640
+ }
1641
+
1642
+ data = {"deviceId": token.device_id, "pin": token.pin}
1643
+ _LOGGER.debug(f"{DOMAIN} - Get Control Token Data: {data}")
1644
+ response = requests.put(url, json=data, headers=headers)
1645
+ response = response.json()
1646
+ _LOGGER.debug(f"{DOMAIN} - Get Control Token Response {response}")
1647
+ control_token = "Bearer " + response["controlToken"]
1648
+ control_token_expire_at = math.floor(
1649
+ dt.datetime.now().timestamp() + response["expiresTime"]
1650
+ )
1651
+ return control_token, control_token_expire_at
1652
+
1383
1653
  def check_action_status(
1384
1654
  self,
1385
1655
  token: Token,
@@ -1413,7 +1683,10 @@ class KiaUvoApiEU(ApiImplType1):
1413
1683
 
1414
1684
  else:
1415
1685
  response = requests.get(
1416
- url, headers=self._get_authenticated_headers(token)
1686
+ url,
1687
+ headers=self._get_authenticated_headers(
1688
+ token, vehicle.ccu_ccs2_protocol_support
1689
+ ),
1417
1690
  ).json()
1418
1691
  _LOGGER.debug(f"{DOMAIN} - Check last action status Response: {response}")
1419
1692
  _check_response_for_errors(response)
@@ -220,10 +220,26 @@ class Vehicle:
220
220
  ev_first_departure_time: typing.Union[datetime.time, None] = None
221
221
  ev_second_departure_time: typing.Union[datetime.time, None] = None
222
222
 
223
+ ev_first_departure_climate_enabled: typing.Union[bool, None] = None
224
+ ev_second_departure_climate_enabled: typing.Union[bool, None] = None
225
+
226
+ _ev_first_departure_climate_temperature: typing.Union[float, None] = None
227
+ _ev_first_departure_climate_temperature_value: typing.Union[float, None] = None
228
+ _ev_first_departure_climate_temperature_unit: typing.Union[str, None] = None
229
+
230
+ _ev_second_departure_climate_temperature: typing.Union[float, None] = None
231
+ _ev_second_departure_climate_temperature_value: typing.Union[float, None] = None
232
+ _ev_second_departure_climate_temperature_unit: typing.Union[str, None] = None
233
+
234
+ ev_first_departure_climate_defrost: typing.Union[bool, None] = None
235
+ ev_second_departure_climate_defrost: typing.Union[bool, None] = None
236
+
223
237
  ev_off_peak_start_time: typing.Union[datetime.time, None] = None
224
238
  ev_off_peak_end_time: typing.Union[datetime.time, None] = None
225
239
  ev_off_peak_charge_only_enabled: typing.Union[bool, None] = None
226
240
 
241
+ ev_schedule_charge_enabled: typing.Union[bool, None] = None
242
+
227
243
  # IC fields (PHEV/HEV/IC)
228
244
  _fuel_driving_range: float = None
229
245
  _fuel_driving_range_value: float = None
@@ -415,6 +431,34 @@ class Vehicle:
415
431
  self._ev_target_range_charge_DC_unit = value[1]
416
432
  self._ev_target_range_charge_DC = value[0]
417
433
 
434
+ @property
435
+ def ev_first_departure_climate_temperature(self):
436
+ return self._ev_first_departure_climate_temperature
437
+
438
+ @property
439
+ def ev_first_departure_climate_temperature_unit(self):
440
+ return self._ev_first_departure_climate_temperature_unit
441
+
442
+ @ev_first_departure_climate_temperature.setter
443
+ def ev_first_departure_climate_temperature(self, value):
444
+ self._ev_first_departure_climate_temperature_value = value[0]
445
+ self._ev_first_departure_climate_temperature_unit = value[1]
446
+ self._ev_first_departure_climate_temperature = value[0]
447
+
448
+ @property
449
+ def ev_second_departure_climate_temperature(self):
450
+ return self._ev_second_departure_climate_temperature
451
+
452
+ @property
453
+ def ev_second_departure_climate_temperature_unit(self):
454
+ return self._ev_second_departure_climate_temperature_unit
455
+
456
+ @ev_second_departure_climate_temperature.setter
457
+ def ev_second_departure_climate_temperature(self, value):
458
+ self._ev_second_departure_climate_temperature_value = value[0]
459
+ self._ev_second_departure_climate_temperature_unit = value[1]
460
+ self._ev_second_departure_climate_temperature = value[0]
461
+
418
462
  @property
419
463
  def fuel_driving_range(self):
420
464
  return self._fuel_driving_range
@@ -8,7 +8,12 @@ import logging
8
8
  import pytz
9
9
 
10
10
  from .exceptions import APIError
11
- from .ApiImpl import ApiImpl, ClimateRequestOptions, WindowRequestOptions
11
+ from .ApiImpl import (
12
+ ApiImpl,
13
+ ClimateRequestOptions,
14
+ WindowRequestOptions,
15
+ ScheduleChargingClimateRequestOptions,
16
+ )
12
17
  from .HyundaiBlueLinkAPIUSA import HyundaiBlueLinkAPIUSA
13
18
  from .KiaUvoAPIUSA import KiaUvoAPIUSA
14
19
  from .KiaUvoApiCA import KiaUvoApiCA
@@ -237,6 +242,13 @@ class VehicleManager:
237
242
  def enable_vehicle(self, vehicle_id: str) -> None:
238
243
  self.get_vehicle(vehicle_id).enabled = True
239
244
 
245
+ def schedule_charging_and_climate(
246
+ self, vehicle_id: str, options: ScheduleChargingClimateRequestOptions
247
+ ) -> None:
248
+ self.api.schedule_charging_and_climate(
249
+ self.token, self.get_vehicle(vehicle_id), options
250
+ )
251
+
240
252
  @staticmethod
241
253
  def get_implementation_by_region_brand(
242
254
  region: int, brand: int, language: str
@@ -1,7 +1,12 @@
1
1
  """Top-level package for Hyundai / Kia Connect."""
2
2
 
3
3
  # flake8: noqa
4
- from .ApiImpl import ApiImpl, ClimateRequestOptions, WindowRequestOptions
4
+ from .ApiImpl import (
5
+ ApiImpl,
6
+ ClimateRequestOptions,
7
+ WindowRequestOptions,
8
+ ScheduleChargingClimateRequestOptions,
9
+ )
5
10
  from .ApiImplType1 import ApiImplType1
6
11
  from .HyundaiBlueLinkAPIUSA import HyundaiBlueLinkAPIUSA
7
12
  from .KiaUvoApiCA import KiaUvoApiCA
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hyundai_kia_connect_api
3
- Version: 3.19.1
3
+ Version: 3.20.2
4
4
  Summary: Python Boilerplate contains all the boilerplate you need to create a Python package.
5
5
  Home-page: https://github.com/fuatakgun/hyundai_kia_connect_api
6
6
  Author: Fuat Akgun
@@ -112,6 +112,7 @@ For a list of language codes, see here: https://www.science.co.il/language/Codes
112
112
  - "fi" Finnish
113
113
  - "pt" Portuguese
114
114
 
115
+
115
116
  In Europe also trip info can be retrieved. For a month you can ask the days with trips. And you can ask for a specific day for all the trips of that specific day.::
116
117
  - First call vm.update_month_trip_info(vehicle.id, yyymm) before getting vehicle.month_trip_info for that month
117
118
  - First call vm.update_day_trip_info(vehicle.id, day.yyyymmdd) before getting vehicle.day_trip_info for that day
@@ -44,6 +44,6 @@ setup(
44
44
  test_suite="tests",
45
45
  tests_require=test_requirements,
46
46
  url="https://github.com/fuatakgun/hyundai_kia_connect_api",
47
- version="3.19.1",
47
+ version="3.20.2",
48
48
  zip_safe=False,
49
49
  )