hyundai-kia-connect-api 3.17.6__py2.py3-none-any.whl → 3.32.0__py2.py3-none-any.whl
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.
- hyundai_kia_connect_api/ApiImpl.py +102 -29
- hyundai_kia_connect_api/ApiImplType1.py +321 -0
- hyundai_kia_connect_api/{HyundaiBlueLinkAPIUSA.py → HyundaiBlueLinkApiUSA.py} +295 -35
- hyundai_kia_connect_api/KiaUvoApiAU.py +77 -88
- hyundai_kia_connect_api/KiaUvoApiCA.py +70 -41
- hyundai_kia_connect_api/KiaUvoApiCN.py +30 -42
- hyundai_kia_connect_api/KiaUvoApiEU.py +466 -376
- hyundai_kia_connect_api/{KiaUvoAPIUSA.py → KiaUvoApiUSA.py} +98 -102
- hyundai_kia_connect_api/Vehicle.py +142 -19
- hyundai_kia_connect_api/VehicleManager.py +60 -19
- hyundai_kia_connect_api/__init__.py +5 -6
- hyundai_kia_connect_api/bluelink.py +457 -0
- hyundai_kia_connect_api/const.py +12 -1
- hyundai_kia_connect_api/utils.py +30 -3
- {hyundai_kia_connect_api-3.17.6.dist-info → hyundai_kia_connect_api-3.32.0.dist-info}/LICENSE +0 -1
- {hyundai_kia_connect_api-3.17.6.dist-info → hyundai_kia_connect_api-3.32.0.dist-info}/METADATA +53 -18
- hyundai_kia_connect_api-3.32.0.dist-info/RECORD +23 -0
- {hyundai_kia_connect_api-3.17.6.dist-info → hyundai_kia_connect_api-3.32.0.dist-info}/WHEEL +1 -1
- hyundai_kia_connect_api-3.32.0.dist-info/entry_points.txt +2 -0
- hyundai_kia_connect_api-3.17.6.dist-info/RECORD +0 -20
- {hyundai_kia_connect_api-3.17.6.dist-info → hyundai_kia_connect_api-3.32.0.dist-info}/AUTHORS.rst +0 -0
- {hyundai_kia_connect_api-3.17.6.dist-info → hyundai_kia_connect_api-3.32.0.dist-info}/top_level.txt +0 -0
@@ -6,8 +6,8 @@ import base64
|
|
6
6
|
import random
|
7
7
|
import datetime as dt
|
8
8
|
import logging
|
9
|
-
import re
|
10
9
|
import uuid
|
10
|
+
import math
|
11
11
|
from time import sleep
|
12
12
|
from urllib.parse import parse_qs, urlparse
|
13
13
|
|
@@ -17,9 +17,11 @@ from bs4 import BeautifulSoup
|
|
17
17
|
from dateutil import tz
|
18
18
|
|
19
19
|
from .ApiImpl import (
|
20
|
-
ApiImpl,
|
21
20
|
ClimateRequestOptions,
|
21
|
+
ScheduleChargingClimateRequestOptions,
|
22
22
|
)
|
23
|
+
from .ApiImplType1 import ApiImplType1
|
24
|
+
|
23
25
|
from .Token import Token
|
24
26
|
from .Vehicle import (
|
25
27
|
Vehicle,
|
@@ -43,23 +45,31 @@ from .const import (
|
|
43
45
|
SEAT_STATUS,
|
44
46
|
TEMPERATURE_UNITS,
|
45
47
|
VEHICLE_LOCK_ACTION,
|
48
|
+
VALET_MODE_ACTION,
|
49
|
+
)
|
50
|
+
from .exceptions import (
|
51
|
+
AuthenticationError,
|
52
|
+
DuplicateRequestError,
|
53
|
+
RequestTimeoutError,
|
54
|
+
ServiceTemporaryUnavailable,
|
55
|
+
NoDataFound,
|
56
|
+
InvalidAPIResponseError,
|
57
|
+
APIError,
|
58
|
+
RateLimitingError,
|
59
|
+
DeviceIDError,
|
46
60
|
)
|
47
|
-
from .exceptions import *
|
48
61
|
from .utils import (
|
49
62
|
get_child_value,
|
50
63
|
get_index_into_hex_temp,
|
51
64
|
get_hex_temp_into_index,
|
65
|
+
parse_datetime,
|
52
66
|
)
|
53
67
|
|
54
68
|
_LOGGER = logging.getLogger(__name__)
|
55
69
|
|
56
70
|
USER_AGENT_OK_HTTP: str = "okhttp/3.12.0"
|
57
|
-
USER_AGENT_MOZILLA: str = (
|
58
|
-
|
59
|
-
)
|
60
|
-
ACCEPT_HEADER_ALL: str = (
|
61
|
-
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" # noqa
|
62
|
-
)
|
71
|
+
USER_AGENT_MOZILLA: str = "Mozilla/5.0 (Linux; Android 4.1.1; Galaxy Nexus Build/JRO03C) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19" # noqa
|
72
|
+
ACCEPT_HEADER_ALL: str = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" # noqa
|
63
73
|
|
64
74
|
SUPPORTED_LANGUAGES_LIST = [
|
65
75
|
"en", # English
|
@@ -122,12 +132,11 @@ def _check_response_for_errors(response: dict) -> None:
|
|
122
132
|
)
|
123
133
|
|
124
134
|
|
125
|
-
class KiaUvoApiEU(
|
135
|
+
class KiaUvoApiEU(ApiImplType1):
|
126
136
|
data_timezone = tz.gettz("Europe/Berlin")
|
127
137
|
temperature_range = [x * 0.5 for x in range(28, 60)]
|
128
138
|
|
129
139
|
def __init__(self, region: int, brand: int, language: str) -> None:
|
130
|
-
self.ccu_ccs2_protocol_support = None
|
131
140
|
language = language.lower()
|
132
141
|
# Strip language variants (e.g. en-Gb)
|
133
142
|
if len(language) > 2:
|
@@ -159,9 +168,7 @@ class KiaUvoApiEU(ApiImpl):
|
|
159
168
|
self.CFB: str = base64.b64decode(
|
160
169
|
"RFtoRq/vDXJmRndoZaZQyfOot7OrIqGVFj96iY2WL3yyH5Z/pUvlUhqmCxD2t+D65SQ="
|
161
170
|
)
|
162
|
-
self.BASIC_AUTHORIZATION: str =
|
163
|
-
"Basic NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==" # noqa
|
164
|
-
)
|
171
|
+
self.BASIC_AUTHORIZATION: str = "Basic NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==" # noqa
|
165
172
|
self.LOGIN_FORM_HOST = "eu-account.hyundai.com"
|
166
173
|
self.PUSH_TYPE = "GCM"
|
167
174
|
elif BRANDS[self.brand] == BRAND_GENESIS:
|
@@ -172,9 +179,7 @@ class KiaUvoApiEU(ApiImpl):
|
|
172
179
|
self.CFB: str = base64.b64decode(
|
173
180
|
"RFtoRq/vDXJmRndoZaZQyYo3/qFLtVReW8P7utRPcc0ZxOzOELm9mexvviBk/qqIp4A="
|
174
181
|
)
|
175
|
-
self.BASIC_AUTHORIZATION: str =
|
176
|
-
"Basic NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==" # noqa
|
177
|
-
)
|
182
|
+
self.BASIC_AUTHORIZATION: str = "Basic NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==" # noqa
|
178
183
|
self.LOGIN_FORM_HOST = "accounts-eu.genesis.com"
|
179
184
|
self.PUSH_TYPE = "GCM"
|
180
185
|
|
@@ -226,18 +231,14 @@ class KiaUvoApiEU(ApiImpl):
|
|
226
231
|
+ "&state=$service_id:$user_id"
|
227
232
|
)
|
228
233
|
|
229
|
-
def
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
"
|
236
|
-
"
|
237
|
-
"Connection": "Keep-Alive",
|
238
|
-
"Accept-Encoding": "gzip",
|
239
|
-
"Ccuccs2protocolsupport": self.ccu_ccs2_protocol_support,
|
240
|
-
"User-Agent": USER_AGENT_OK_HTTP,
|
234
|
+
def _get_control_headers(self, token: Token, vehicle: Vehicle) -> dict:
|
235
|
+
control_token, _ = self._get_control_token(token)
|
236
|
+
authenticated_headers = self._get_authenticated_headers(
|
237
|
+
token, vehicle.ccu_ccs2_protocol_support
|
238
|
+
)
|
239
|
+
return authenticated_headers | {
|
240
|
+
"Authorization": control_token,
|
241
|
+
"AuthorizationCCSP": control_token,
|
241
242
|
}
|
242
243
|
|
243
244
|
def login(self, username: str, password: str) -> Token:
|
@@ -277,7 +278,8 @@ class KiaUvoApiEU(ApiImpl):
|
|
277
278
|
def get_vehicles(self, token: Token) -> list[Vehicle]:
|
278
279
|
url = self.SPA_API_URL + "vehicles"
|
279
280
|
response = requests.get(
|
280
|
-
url,
|
281
|
+
url,
|
282
|
+
headers=self._get_authenticated_headers(token),
|
281
283
|
).json()
|
282
284
|
_LOGGER.debug(f"{DOMAIN} - Get Vehicles Response: {response}")
|
283
285
|
_check_response_for_errors(response)
|
@@ -307,25 +309,6 @@ class KiaUvoApiEU(ApiImpl):
|
|
307
309
|
result.append(vehicle)
|
308
310
|
return result
|
309
311
|
|
310
|
-
def get_last_updated_at(self, value) -> dt.datetime:
|
311
|
-
_LOGGER.debug(f"{DOMAIN} - last_updated_at - before {value}")
|
312
|
-
if value is None:
|
313
|
-
value = dt.datetime(2000, 1, 1, tzinfo=self.data_timezone)
|
314
|
-
else:
|
315
|
-
m = re.match(r"(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})", value)
|
316
|
-
value = dt.datetime(
|
317
|
-
year=int(m.group(1)),
|
318
|
-
month=int(m.group(2)),
|
319
|
-
day=int(m.group(3)),
|
320
|
-
hour=int(m.group(4)),
|
321
|
-
minute=int(m.group(5)),
|
322
|
-
second=int(m.group(6)),
|
323
|
-
tzinfo=self.data_timezone,
|
324
|
-
)
|
325
|
-
|
326
|
-
_LOGGER.debug(f"{DOMAIN} - last_updated_at - after {value}")
|
327
|
-
return value
|
328
|
-
|
329
312
|
def _get_time_from_string(self, value, timesection) -> dt.datetime.time:
|
330
313
|
if value is not None:
|
331
314
|
lastTwo = int(value[-2:])
|
@@ -341,12 +324,29 @@ class KiaUvoApiEU(ApiImpl):
|
|
341
324
|
return value
|
342
325
|
|
343
326
|
def update_vehicle_with_cached_state(self, token: Token, vehicle: Vehicle) -> None:
|
344
|
-
|
345
|
-
|
327
|
+
url = self.SPA_API_URL + "vehicles/" + vehicle.id
|
328
|
+
is_ccs2 = vehicle.ccu_ccs2_protocol_support != 0
|
329
|
+
if is_ccs2:
|
330
|
+
url += "/ccs2/carstatus/latest"
|
331
|
+
else:
|
332
|
+
url += "/status/latest"
|
333
|
+
|
334
|
+
response = requests.get(
|
335
|
+
url,
|
336
|
+
headers=self._get_authenticated_headers(
|
337
|
+
token, vehicle.ccu_ccs2_protocol_support
|
338
|
+
),
|
339
|
+
).json()
|
340
|
+
|
341
|
+
_LOGGER.debug(f"{DOMAIN} - get_cached_vehicle_status response: {response}")
|
342
|
+
_check_response_for_errors(response)
|
346
343
|
|
347
344
|
if vehicle.ccu_ccs2_protocol_support == 0:
|
348
|
-
self._update_vehicle_properties(
|
345
|
+
self._update_vehicle_properties(
|
346
|
+
vehicle, response["resMsg"]["vehicleStatusInfo"]
|
347
|
+
)
|
349
348
|
else:
|
349
|
+
state = response["resMsg"]["state"]["Vehicle"]
|
350
350
|
self._update_vehicle_properties_ccs2(vehicle, state)
|
351
351
|
|
352
352
|
if (
|
@@ -396,294 +396,23 @@ class KiaUvoApiEU(ApiImpl):
|
|
396
396
|
else:
|
397
397
|
self._update_vehicle_drive_info(vehicle, state)
|
398
398
|
|
399
|
-
def _update_vehicle_properties_ccs2(self, vehicle: Vehicle, state: dict) -> None:
|
400
|
-
if get_child_value(state, "Date"):
|
401
|
-
vehicle.last_updated_at = self.get_last_updated_at(
|
402
|
-
get_child_value(state, "Date")
|
403
|
-
)
|
404
|
-
else:
|
405
|
-
vehicle.last_updated_at = dt.datetime.now(self.data_timezone)
|
406
|
-
|
407
|
-
vehicle.odometer = (
|
408
|
-
get_child_value(state, "Drivetrain.Odometer"),
|
409
|
-
DISTANCE_UNITS[1],
|
410
|
-
)
|
411
|
-
vehicle.car_battery_percentage = get_child_value(
|
412
|
-
state, "Electronics.Battery.Level"
|
413
|
-
)
|
414
|
-
|
415
|
-
vehicle.engine_is_running = get_child_value(state, "DrivingReady")
|
416
|
-
|
417
|
-
air_temp = get_child_value(
|
418
|
-
state,
|
419
|
-
"Cabin.HVAC.Row1.Driver.Temperature.Value",
|
420
|
-
)
|
421
|
-
|
422
|
-
if air_temp != "OFF":
|
423
|
-
vehicle.air_temperature = (air_temp, TEMPERATURE_UNITS[1])
|
424
|
-
|
425
|
-
defrost_is_on = get_child_value(state, "Body.Windshield.Front.Defog.State")
|
426
|
-
if defrost_is_on in [0, 2]:
|
427
|
-
vehicle.defrost_is_on = False
|
428
|
-
elif defrost_is_on == 1:
|
429
|
-
vehicle.defrost_is_on = True
|
430
|
-
|
431
|
-
steer_wheel_heat = get_child_value(state, "Cabin.SteeringWheel.Heat.State")
|
432
|
-
if steer_wheel_heat in [0, 2]:
|
433
|
-
vehicle.steering_wheel_heater_is_on = False
|
434
|
-
elif steer_wheel_heat == 1:
|
435
|
-
vehicle.steering_wheel_heater_is_on = True
|
436
|
-
|
437
|
-
defrost_rear_is_on = get_child_value(state, "Body.Windshield.Rear.Defog.State")
|
438
|
-
if defrost_rear_is_on in [0, 2]:
|
439
|
-
vehicle.back_window_heater_is_on = False
|
440
|
-
elif defrost_rear_is_on == 1:
|
441
|
-
vehicle.back_window_heater_is_on = True
|
442
|
-
|
443
|
-
# TODO: status.sideMirrorHeat
|
444
|
-
|
445
|
-
vehicle.front_left_seat_status = SEAT_STATUS[
|
446
|
-
get_child_value(state, "Cabin.Seat.Row1.Driver.Climate.State")
|
447
|
-
]
|
448
|
-
|
449
|
-
vehicle.front_right_seat_status = SEAT_STATUS[
|
450
|
-
get_child_value(state, "Cabin.Seat.Row1.Passenger.Climate.State")
|
451
|
-
]
|
452
|
-
|
453
|
-
vehicle.rear_left_seat_status = SEAT_STATUS[
|
454
|
-
get_child_value(state, "Cabin.Seat.Row2.Left.Climate.State")
|
455
|
-
]
|
456
|
-
|
457
|
-
vehicle.rear_right_seat_status = SEAT_STATUS[
|
458
|
-
get_child_value(state, "Cabin.Seat.Row2.Right.Climate.State")
|
459
|
-
]
|
460
|
-
|
461
|
-
# TODO: status.doorLock
|
462
|
-
|
463
|
-
vehicle.front_left_door_is_open = get_child_value(
|
464
|
-
state, "Cabin.Door.Row1.Driver.Open"
|
465
|
-
)
|
466
|
-
vehicle.front_right_door_is_open = get_child_value(
|
467
|
-
state, "Cabin.Door.Row1.Passenger.Open"
|
468
|
-
)
|
469
|
-
vehicle.back_left_door_is_open = get_child_value(
|
470
|
-
state, "Cabin.Door.Row2.Left.Open"
|
471
|
-
)
|
472
|
-
vehicle.back_right_door_is_open = get_child_value(
|
473
|
-
state, "Cabin.Door.Row2.Right.Open"
|
474
|
-
)
|
475
|
-
|
476
|
-
# TODO: should the windows and trunc also be checked?
|
477
|
-
vehicle.is_locked = not (
|
478
|
-
vehicle.front_left_door_is_open
|
479
|
-
or vehicle.front_right_door_is_open
|
480
|
-
or vehicle.back_left_door_is_open
|
481
|
-
or vehicle.back_right_door_is_open
|
482
|
-
)
|
483
|
-
|
484
|
-
vehicle.hood_is_open = get_child_value(state, "Body.Hood.Open")
|
485
|
-
vehicle.front_left_window_is_open = get_child_value(
|
486
|
-
state, "Cabin.Window.Row1.Driver.Open"
|
487
|
-
)
|
488
|
-
vehicle.front_right_window_is_open = get_child_value(
|
489
|
-
state, "Cabin.Window.Row1.Passenger.Open"
|
490
|
-
)
|
491
|
-
vehicle.back_left_window_is_open = get_child_value(
|
492
|
-
state, "Cabin.Window.Row2.Left.Open"
|
493
|
-
)
|
494
|
-
vehicle.back_right_window_is_open = get_child_value(
|
495
|
-
state, "Cabin.Window.Row2.Right.Open"
|
496
|
-
)
|
497
|
-
vehicle.tire_pressure_rear_left_warning_is_on = bool(
|
498
|
-
get_child_value(state, "Chassis.Axle.Row2.Left.Tire.PressureLow")
|
499
|
-
)
|
500
|
-
vehicle.tire_pressure_front_left_warning_is_on = bool(
|
501
|
-
get_child_value(state, "Chassis.Axle.Row1.Left.Tire.PressureLow")
|
502
|
-
)
|
503
|
-
vehicle.tire_pressure_front_right_warning_is_on = bool(
|
504
|
-
get_child_value(state, "Chassis.Axle.Row1.Right.Tire.PressureLow")
|
505
|
-
)
|
506
|
-
vehicle.tire_pressure_rear_right_warning_is_on = bool(
|
507
|
-
get_child_value(state, "Chassis.Axle.Row2.Right.Tire.PressureLow")
|
508
|
-
)
|
509
|
-
vehicle.tire_pressure_all_warning_is_on = bool(
|
510
|
-
get_child_value(state, "Chassis.Axle.Tire.PressureLow")
|
511
|
-
)
|
512
|
-
vehicle.trunk_is_open = get_child_value(state, "Body.Trunk.Open")
|
513
|
-
|
514
|
-
vehicle.ev_battery_percentage = get_child_value(
|
515
|
-
state, "Green.BatteryManagement.BatteryRemain.Ratio"
|
516
|
-
)
|
517
|
-
vehicle.ev_battery_remain = get_child_value(
|
518
|
-
state, "Green.BatteryManagement.BatteryRemain.Value"
|
519
|
-
)
|
520
|
-
vehicle.ev_battery_capacity = get_child_value(
|
521
|
-
state, "Green.BatteryManagement.BatteryCapacity.Value"
|
522
|
-
)
|
523
|
-
vehicle.ev_battery_soh_percentage = get_child_value(
|
524
|
-
state, "Green.BatteryManagement.SoH.Ratio"
|
525
|
-
)
|
526
|
-
vehicle.ev_battery_is_plugged_in = get_child_value(
|
527
|
-
state, "Green.ChargingInformation.ElectricCurrentLevel.State"
|
528
|
-
)
|
529
|
-
vehicle.ev_battery_is_plugged_in = get_child_value(
|
530
|
-
state, "Green.ChargingInformation.ConnectorFastening.State"
|
531
|
-
)
|
532
|
-
charging_door_state = get_child_value(state, "Green.ChargingDoor.State")
|
533
|
-
if charging_door_state in [0, 2]:
|
534
|
-
vehicle.ev_charge_port_door_is_open = False
|
535
|
-
elif charging_door_state == 1:
|
536
|
-
vehicle.ev_charge_port_door_is_open = True
|
537
|
-
|
538
|
-
vehicle.total_driving_range = (
|
539
|
-
float(
|
540
|
-
get_child_value(
|
541
|
-
state,
|
542
|
-
"Drivetrain.FuelSystem.DTE.Total", # noqa
|
543
|
-
)
|
544
|
-
),
|
545
|
-
DISTANCE_UNITS[
|
546
|
-
get_child_value(
|
547
|
-
state,
|
548
|
-
"Drivetrain.FuelSystem.DTE.Unit", # noqa
|
549
|
-
)
|
550
|
-
],
|
551
|
-
)
|
552
|
-
|
553
|
-
if vehicle.engine_type == ENGINE_TYPES.EV:
|
554
|
-
# ev_driving_range is the same as total_driving_range for pure EV
|
555
|
-
vehicle.ev_driving_range = (
|
556
|
-
vehicle.total_driving_range,
|
557
|
-
vehicle.total_driving_range_unit,
|
558
|
-
)
|
559
|
-
# TODO: vehicle.ev_driving_range for non EV
|
560
|
-
|
561
|
-
vehicle.washer_fluid_warning_is_on = get_child_value(
|
562
|
-
state, "Body.Windshield.Front.WasherFluid.LevelLow"
|
563
|
-
)
|
564
|
-
|
565
|
-
vehicle.ev_estimated_current_charge_duration = (
|
566
|
-
get_child_value(state, "Green.ChargingInformation.Charging.RemainTime"),
|
567
|
-
"m",
|
568
|
-
)
|
569
|
-
vehicle.ev_estimated_fast_charge_duration = (
|
570
|
-
get_child_value(state, "Green.ChargingInformation.EstimatedTime.Standard"),
|
571
|
-
"m",
|
572
|
-
)
|
573
|
-
vehicle.ev_estimated_portable_charge_duration = (
|
574
|
-
get_child_value(state, "Green.ChargingInformation.EstimatedTime.ICCB"),
|
575
|
-
"m",
|
576
|
-
)
|
577
|
-
vehicle.ev_estimated_station_charge_duration = (
|
578
|
-
get_child_value(state, "Green.ChargingInformation.EstimatedTime.Quick"),
|
579
|
-
"m",
|
580
|
-
)
|
581
|
-
vehicle.ev_charge_limits_ac = get_child_value(
|
582
|
-
state, "Green.ChargingInformation.TargetSoC.Standard"
|
583
|
-
)
|
584
|
-
vehicle.ev_charge_limits_dc = get_child_value(
|
585
|
-
state, "Green.ChargingInformation.TargetSoC.Quick"
|
586
|
-
)
|
587
|
-
vehicle.ev_v2l_discharge_limit = get_child_value(
|
588
|
-
state, "Green.Electric.SmartGrid.VehicleToLoad.DischargeLimitation.SoC"
|
589
|
-
)
|
590
|
-
vehicle.ev_target_range_charge_AC = (
|
591
|
-
get_child_value(
|
592
|
-
state,
|
593
|
-
"Green.ChargingInformation.DTE.TargetSoC.Standard", # noqa
|
594
|
-
),
|
595
|
-
DISTANCE_UNITS[
|
596
|
-
get_child_value(
|
597
|
-
state,
|
598
|
-
"Drivetrain.FuelSystem.DTE.Unit", # noqa
|
599
|
-
)
|
600
|
-
],
|
601
|
-
)
|
602
|
-
vehicle.ev_target_range_charge_DC = (
|
603
|
-
get_child_value(
|
604
|
-
state,
|
605
|
-
"Green.ChargingInformation.DTE.TargetSoC.Quick", # noqa
|
606
|
-
),
|
607
|
-
DISTANCE_UNITS[
|
608
|
-
get_child_value(
|
609
|
-
state,
|
610
|
-
"Drivetrain.FuelSystem.DTE.Unit", # noqa
|
611
|
-
)
|
612
|
-
],
|
613
|
-
)
|
614
|
-
vehicle.ev_first_departure_enabled = bool(
|
615
|
-
get_child_value(state, "Green.Reservation.Departure.Schedule1.Enable")
|
616
|
-
)
|
617
|
-
|
618
|
-
vehicle.ev_second_departure_enabled = bool(
|
619
|
-
get_child_value(state, "Green.Reservation.Departure.Schedule2.Enable")
|
620
|
-
)
|
621
|
-
|
622
|
-
# TODO: vehicle.ev_first_departure_days --> Green.Reservation.Departure.Schedule1.(Mon,Tue,Wed,Thu,Fri,Sat,Sun) # noqa
|
623
|
-
# TODO: vehicle.ev_second_departure_days --> Green.Reservation.Departure.Schedule2.(Mon,Tue,Wed,Thu,Fri,Sat,Sun) # noqa
|
624
|
-
# TODO: vehicle.ev_first_departure_time --> Green.Reservation.Departure.Schedule1.(Min,Hour) # noqa
|
625
|
-
# TODO: vehicle.ev_second_departure_time --> Green.Reservation.Departure.Schedule2.(Min,Hour) # noqa
|
626
|
-
# TODO: vehicle.ev_off_peak_charge_only_enabled --> unknown settings are in --> Green.Reservation.OffPeakTime and OffPeakTime2 # noqa
|
627
|
-
|
628
|
-
vehicle.washer_fluid_warning_is_on = get_child_value(
|
629
|
-
state, "Body.Windshield.Front.WasherFluid.LevelLow"
|
630
|
-
)
|
631
|
-
vehicle.brake_fluid_warning_is_on = get_child_value(
|
632
|
-
state, "Chassis.Brake.Fluid.Warning"
|
633
|
-
)
|
634
|
-
|
635
|
-
vehicle.fuel_level = get_child_value(state, "Drivetrain.FuelSystem.FuelLevel")
|
636
|
-
vehicle.fuel_level_is_low = get_child_value(
|
637
|
-
state, "Drivetrain.FuelSystem.LowFuelWarning"
|
638
|
-
)
|
639
|
-
vehicle.air_control_is_on = get_child_value(
|
640
|
-
state, "Cabin.HVAC.Row1.Driver.Blower.SpeedLevel"
|
641
|
-
)
|
642
|
-
vehicle.smart_key_battery_warning_is_on = bool(
|
643
|
-
get_child_value(state, "Electronics.FOB.LowBattery")
|
644
|
-
)
|
645
|
-
|
646
|
-
if get_child_value(state, "Location.GeoCoord.Latitude"):
|
647
|
-
location_last_updated_at = dt.datetime(
|
648
|
-
2000, 1, 1, tzinfo=self.data_timezone
|
649
|
-
)
|
650
|
-
timestamp = get_child_value(state, "Location.TimeStamp")
|
651
|
-
if timestamp is not None:
|
652
|
-
location_last_updated_at = dt.datetime(
|
653
|
-
year=int(get_child_value(timestamp, "Year")),
|
654
|
-
month=int(get_child_value(timestamp, "Mon")),
|
655
|
-
day=int(get_child_value(timestamp, "Day")),
|
656
|
-
hour=int(get_child_value(timestamp, "Hour")),
|
657
|
-
minute=int(get_child_value(timestamp, "Min")),
|
658
|
-
second=int(get_child_value(timestamp, "Sec")),
|
659
|
-
tzinfo=self.data_timezone,
|
660
|
-
)
|
661
|
-
|
662
|
-
vehicle.location = (
|
663
|
-
get_child_value(state, "Location.GeoCoord.Latitude"),
|
664
|
-
get_child_value(state, "Location.GeoCoord.Longitude"),
|
665
|
-
location_last_updated_at,
|
666
|
-
)
|
667
|
-
|
668
|
-
vehicle.data = state
|
669
|
-
|
670
399
|
def _update_vehicle_properties(self, vehicle: Vehicle, state: dict) -> None:
|
671
400
|
if get_child_value(state, "vehicleStatus.time"):
|
672
|
-
vehicle.last_updated_at =
|
673
|
-
get_child_value(state, "vehicleStatus.time")
|
401
|
+
vehicle.last_updated_at = parse_datetime(
|
402
|
+
get_child_value(state, "vehicleStatus.time"), self.data_timezone
|
674
403
|
)
|
675
404
|
else:
|
676
405
|
vehicle.last_updated_at = dt.datetime.now(self.data_timezone)
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
406
|
+
if get_child_value(state, "odometer.value"):
|
407
|
+
vehicle.odometer = (
|
408
|
+
get_child_value(state, "odometer.value"),
|
409
|
+
DISTANCE_UNITS[
|
410
|
+
get_child_value(
|
411
|
+
state,
|
412
|
+
"odometer.unit",
|
413
|
+
)
|
414
|
+
],
|
415
|
+
)
|
687
416
|
vehicle.car_battery_percentage = get_child_value(
|
688
417
|
state, "vehicleStatus.battery.batSoc"
|
689
418
|
)
|
@@ -791,6 +520,11 @@ class KiaUvoApiEU(ApiImpl):
|
|
791
520
|
vehicle.ev_charge_port_door_is_open = True
|
792
521
|
elif ev_charge_port_door_is_open == 2:
|
793
522
|
vehicle.ev_charge_port_door_is_open = False
|
523
|
+
|
524
|
+
vehicle.ev_charging_power = get_child_value(
|
525
|
+
state, "vehicleStatus.evStatus.batteryPower.batteryStndChrgPower"
|
526
|
+
)
|
527
|
+
|
794
528
|
if (
|
795
529
|
get_child_value(
|
796
530
|
state,
|
@@ -866,7 +600,7 @@ class KiaUvoApiEU(ApiImpl):
|
|
866
600
|
vehicle.ev_charge_limits_dc = [
|
867
601
|
x["targetSOClevel"] for x in target_soc_list if x["plugType"] == 0
|
868
602
|
][-1]
|
869
|
-
except:
|
603
|
+
except Exception:
|
870
604
|
_LOGGER.debug(f"{DOMAIN} - SOC Levels couldn't be found. May not be an EV.")
|
871
605
|
if (
|
872
606
|
get_child_value(
|
@@ -962,6 +696,72 @@ class KiaUvoApiEU(ApiImpl):
|
|
962
696
|
),
|
963
697
|
)
|
964
698
|
|
699
|
+
vehicle.ev_first_departure_climate_enabled = bool(
|
700
|
+
get_child_value(
|
701
|
+
state,
|
702
|
+
"vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.airCtrl", # noqa
|
703
|
+
)
|
704
|
+
)
|
705
|
+
|
706
|
+
vehicle.ev_second_departure_climate_enabled = bool(
|
707
|
+
get_child_value(
|
708
|
+
state,
|
709
|
+
"vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.airCtrl", # noqa
|
710
|
+
)
|
711
|
+
)
|
712
|
+
|
713
|
+
if get_child_value(
|
714
|
+
state,
|
715
|
+
"vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.airTemp.value", # noqa
|
716
|
+
):
|
717
|
+
temp_index = get_hex_temp_into_index(
|
718
|
+
get_child_value(
|
719
|
+
state,
|
720
|
+
"vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.airTemp.value", # noqa
|
721
|
+
)
|
722
|
+
)
|
723
|
+
|
724
|
+
vehicle.ev_first_departure_climate_temperature = (
|
725
|
+
self.temperature_range[temp_index],
|
726
|
+
TEMPERATURE_UNITS[
|
727
|
+
get_child_value(
|
728
|
+
state,
|
729
|
+
"vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.airTemp.unit", # noqa
|
730
|
+
)
|
731
|
+
],
|
732
|
+
)
|
733
|
+
|
734
|
+
if get_child_value(
|
735
|
+
state,
|
736
|
+
"vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.airTemp.value", # noqa
|
737
|
+
):
|
738
|
+
temp_index = get_hex_temp_into_index(
|
739
|
+
get_child_value(
|
740
|
+
state,
|
741
|
+
"vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.airTemp.value", # noqa
|
742
|
+
)
|
743
|
+
)
|
744
|
+
|
745
|
+
vehicle.ev_second_departure_climate_temperature = (
|
746
|
+
self.temperature_range[temp_index],
|
747
|
+
TEMPERATURE_UNITS[
|
748
|
+
get_child_value(
|
749
|
+
state,
|
750
|
+
"vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.airTemp.unit", # noqa
|
751
|
+
)
|
752
|
+
],
|
753
|
+
)
|
754
|
+
|
755
|
+
vehicle.ev_first_departure_climate_defrost = get_child_value(
|
756
|
+
state,
|
757
|
+
"vehicleStatus.evStatus.reservChargeInfos.reservChargeInfo.reservChargeInfoDetail.reservFatcSet.defrost", # noqa
|
758
|
+
)
|
759
|
+
|
760
|
+
vehicle.ev_second_departure_climate_defrost = get_child_value(
|
761
|
+
state,
|
762
|
+
"vehicleStatus.evStatus.reservChargeInfos.reserveChargeInfo2.reservChargeInfoDetail.reservFatcSet.defrost", # noqa
|
763
|
+
)
|
764
|
+
|
965
765
|
vehicle.ev_off_peak_start_time = self._get_time_from_string(
|
966
766
|
get_child_value(
|
967
767
|
state,
|
@@ -1005,6 +805,23 @@ class KiaUvoApiEU(ApiImpl):
|
|
1005
805
|
):
|
1006
806
|
vehicle.ev_off_peak_charge_only_enabled = False
|
1007
807
|
|
808
|
+
if (
|
809
|
+
get_child_value(
|
810
|
+
state,
|
811
|
+
"vehicleStatus.evStatus.reservChargeInfos.reservFlag", # noqa
|
812
|
+
)
|
813
|
+
== 1
|
814
|
+
):
|
815
|
+
vehicle.ev_schedule_charge_enabled = True
|
816
|
+
elif (
|
817
|
+
get_child_value(
|
818
|
+
state,
|
819
|
+
"vehicleStatus.evStatus.reservChargeInfos.reservFlag", # noqa
|
820
|
+
)
|
821
|
+
== 0
|
822
|
+
):
|
823
|
+
vehicle.ev_schedule_charge_enabled = False
|
824
|
+
|
1008
825
|
vehicle.washer_fluid_warning_is_on = get_child_value(
|
1009
826
|
state, "vehicleStatus.washerFluidStatus"
|
1010
827
|
)
|
@@ -1022,8 +839,8 @@ class KiaUvoApiEU(ApiImpl):
|
|
1022
839
|
vehicle.location = (
|
1023
840
|
get_child_value(state, "vehicleLocation.coord.lat"),
|
1024
841
|
get_child_value(state, "vehicleLocation.coord.lon"),
|
1025
|
-
|
1026
|
-
get_child_value(state, "vehicleLocation.time")
|
842
|
+
parse_datetime(
|
843
|
+
get_child_value(state, "vehicleLocation.time"), self.data_timezone
|
1027
844
|
),
|
1028
845
|
)
|
1029
846
|
vehicle.data = state
|
@@ -1041,7 +858,10 @@ class KiaUvoApiEU(ApiImpl):
|
|
1041
858
|
else:
|
1042
859
|
url = url + "/ccs2/carstatus/latest"
|
1043
860
|
response = requests.get(
|
1044
|
-
url,
|
861
|
+
url,
|
862
|
+
headers=self._get_authenticated_headers(
|
863
|
+
token, vehicle.ccu_ccs2_protocol_support
|
864
|
+
),
|
1045
865
|
).json()
|
1046
866
|
_LOGGER.debug(f"{DOMAIN} - get_cached_vehicle_status response: {response}")
|
1047
867
|
_check_response_for_errors(response)
|
@@ -1056,19 +876,25 @@ class KiaUvoApiEU(ApiImpl):
|
|
1056
876
|
|
1057
877
|
try:
|
1058
878
|
response = requests.get(
|
1059
|
-
url,
|
879
|
+
url,
|
880
|
+
headers=self._get_authenticated_headers(
|
881
|
+
token, vehicle.ccu_ccs2_protocol_support
|
882
|
+
),
|
1060
883
|
).json()
|
1061
884
|
_LOGGER.debug(f"{DOMAIN} - _get_location response: {response}")
|
1062
885
|
_check_response_for_errors(response)
|
1063
886
|
return response["resMsg"]["gpsDetail"]
|
1064
|
-
except:
|
887
|
+
except Exception:
|
1065
888
|
_LOGGER.warning(f"{DOMAIN} - _get_location failed")
|
1066
889
|
return None
|
1067
890
|
|
1068
891
|
def _get_forced_vehicle_state(self, token: Token, vehicle: Vehicle) -> dict:
|
1069
892
|
url = self.SPA_API_URL + "vehicles/" + vehicle.id + "/status"
|
1070
893
|
response = requests.get(
|
1071
|
-
url,
|
894
|
+
url,
|
895
|
+
headers=self._get_authenticated_headers(
|
896
|
+
token, vehicle.ccu_ccs2_protocol_support
|
897
|
+
),
|
1072
898
|
).json()
|
1073
899
|
_LOGGER.debug(f"{DOMAIN} - Received forced vehicle data: {response}")
|
1074
900
|
_check_response_for_errors(response)
|
@@ -1079,12 +905,28 @@ class KiaUvoApiEU(ApiImpl):
|
|
1079
905
|
def lock_action(
|
1080
906
|
self, token: Token, vehicle: Vehicle, action: VEHICLE_LOCK_ACTION
|
1081
907
|
) -> str:
|
1082
|
-
|
908
|
+
if not vehicle.ccu_ccs2_protocol_support:
|
909
|
+
url = self.SPA_API_URL + "vehicles/" + vehicle.id + "/control/door"
|
910
|
+
|
911
|
+
payload = {"action": action.value, "deviceId": token.device_id}
|
912
|
+
headers = self._get_authenticated_headers(
|
913
|
+
token, vehicle.ccu_ccs2_protocol_support
|
914
|
+
)
|
915
|
+
|
916
|
+
else:
|
917
|
+
url = self.SPA_API_URL_V2 + "vehicles/" + vehicle.id + "/ccs2/control/door"
|
918
|
+
|
919
|
+
payload = {"command": action.value}
|
920
|
+
headers = self._get_control_headers(
|
921
|
+
token, vehicle
|
922
|
+
)
|
1083
923
|
|
1084
|
-
payload = {"action": action.value, "deviceId": token.device_id}
|
1085
924
|
_LOGGER.debug(f"{DOMAIN} - Lock Action Request: {payload}")
|
925
|
+
|
1086
926
|
response = requests.post(
|
1087
|
-
url,
|
927
|
+
url,
|
928
|
+
json=payload,
|
929
|
+
headers=headers
|
1088
930
|
).json()
|
1089
931
|
_LOGGER.debug(f"{DOMAIN} - Lock Action Response: {response}")
|
1090
932
|
_check_response_for_errors(response)
|
@@ -1099,7 +941,7 @@ class KiaUvoApiEU(ApiImpl):
|
|
1099
941
|
payload = {"action": action.value}
|
1100
942
|
_LOGGER.debug(f"{DOMAIN} - Charge Port Action Request: {payload}")
|
1101
943
|
response = requests.post(
|
1102
|
-
url, json=payload, headers=self.
|
944
|
+
url, json=payload, headers=self._get_control_headers(token, vehicle)
|
1103
945
|
).json()
|
1104
946
|
_LOGGER.debug(f"{DOMAIN} - Charge Port Action Response: {response}")
|
1105
947
|
_check_response_for_errors(response)
|
@@ -1140,7 +982,11 @@ class KiaUvoApiEU(ApiImpl):
|
|
1140
982
|
}
|
1141
983
|
_LOGGER.debug(f"{DOMAIN} - Start Climate Action Request: {payload}")
|
1142
984
|
response = requests.post(
|
1143
|
-
url,
|
985
|
+
url,
|
986
|
+
json=payload,
|
987
|
+
headers=self._get_authenticated_headers(
|
988
|
+
token, vehicle.ccu_ccs2_protocol_support
|
989
|
+
),
|
1144
990
|
).json()
|
1145
991
|
_LOGGER.debug(f"{DOMAIN} - Start Climate Action Response: {response}")
|
1146
992
|
_check_response_for_errors(response)
|
@@ -1162,7 +1008,11 @@ class KiaUvoApiEU(ApiImpl):
|
|
1162
1008
|
}
|
1163
1009
|
_LOGGER.debug(f"{DOMAIN} - Stop Climate Action Request: {payload}")
|
1164
1010
|
response = requests.post(
|
1165
|
-
url,
|
1011
|
+
url,
|
1012
|
+
json=payload,
|
1013
|
+
headers=self._get_authenticated_headers(
|
1014
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1015
|
+
),
|
1166
1016
|
).json()
|
1167
1017
|
_LOGGER.debug(f"{DOMAIN} - Stop Climate Action Response: {response}")
|
1168
1018
|
_check_response_for_errors(response)
|
@@ -1170,12 +1020,27 @@ class KiaUvoApiEU(ApiImpl):
|
|
1170
1020
|
return response["msgId"]
|
1171
1021
|
|
1172
1022
|
def start_charge(self, token: Token, vehicle: Vehicle) -> str:
|
1173
|
-
|
1023
|
+
if not vehicle.ccu_ccs2_protocol_support:
|
1024
|
+
url = self.SPA_API_URL + "vehicles/" + vehicle.id + "/control/charge"
|
1025
|
+
|
1026
|
+
payload = {"action": "start", "deviceId": token.device_id}
|
1027
|
+
headers = self._get_authenticated_headers(
|
1028
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1029
|
+
)
|
1030
|
+
|
1031
|
+
else:
|
1032
|
+
url = self.SPA_API_URL_V2 + "vehicles/" + vehicle.id + "/ccs2/control/charge"
|
1033
|
+
|
1034
|
+
payload = {"command": "start"}
|
1035
|
+
headers = self._get_control_headers(
|
1036
|
+
token, vehicle
|
1037
|
+
)
|
1174
1038
|
|
1175
|
-
payload = {"action": "start", "deviceId": token.device_id}
|
1176
1039
|
_LOGGER.debug(f"{DOMAIN} - Start Charge Action Request: {payload}")
|
1177
1040
|
response = requests.post(
|
1178
|
-
url,
|
1041
|
+
url,
|
1042
|
+
json=payload,
|
1043
|
+
headers=headers
|
1179
1044
|
).json()
|
1180
1045
|
_LOGGER.debug(f"{DOMAIN} - Start Charge Action Response: {response}")
|
1181
1046
|
_check_response_for_errors(response)
|
@@ -1183,18 +1048,63 @@ class KiaUvoApiEU(ApiImpl):
|
|
1183
1048
|
return response["msgId"]
|
1184
1049
|
|
1185
1050
|
def stop_charge(self, token: Token, vehicle: Vehicle) -> str:
|
1186
|
-
|
1051
|
+
if not vehicle.ccu_ccs2_protocol_support:
|
1052
|
+
url = self.SPA_API_URL + "vehicles/" + vehicle.id + "/control/charge"
|
1053
|
+
|
1054
|
+
payload = {"action": "stop", "deviceId": token.device_id}
|
1055
|
+
headers = self._get_authenticated_headers(
|
1056
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1057
|
+
)
|
1058
|
+
|
1059
|
+
else:
|
1060
|
+
url = self.SPA_API_URL_V2 + "vehicles/" + vehicle.id + "/ccs2/control/charge"
|
1061
|
+
|
1062
|
+
payload = {"command": "stop"}
|
1063
|
+
headers = self._get_control_headers(
|
1064
|
+
token, vehicle
|
1065
|
+
)
|
1187
1066
|
|
1188
|
-
|
1189
|
-
_LOGGER.debug(f"{DOMAIN} - Stop Charge Action Request {payload}")
|
1067
|
+
_LOGGER.debug(f"{DOMAIN} - Stop Charge Action Request: {payload}")
|
1190
1068
|
response = requests.post(
|
1191
|
-
url,
|
1069
|
+
url,
|
1070
|
+
json=payload,
|
1071
|
+
headers=headers
|
1192
1072
|
).json()
|
1193
1073
|
_LOGGER.debug(f"{DOMAIN} - Stop Charge Action Response: {response}")
|
1194
1074
|
_check_response_for_errors(response)
|
1195
1075
|
token.device_id = self._get_device_id(self._get_stamp())
|
1196
1076
|
return response["msgId"]
|
1197
1077
|
|
1078
|
+
def start_hazard_lights(self, token: Token, vehicle: Vehicle) -> str:
|
1079
|
+
url = self.SPA_API_URL_V2 + "vehicles/" + vehicle.id + "/ccs2/control/light"
|
1080
|
+
|
1081
|
+
payload = {"command": "on"}
|
1082
|
+
_LOGGER.debug(f"{DOMAIN} - Start Hazard Lights Request: {payload}")
|
1083
|
+
response = requests.post(
|
1084
|
+
url,
|
1085
|
+
json=payload,
|
1086
|
+
headers=self._get_control_headers(token, vehicle),
|
1087
|
+
).json()
|
1088
|
+
_LOGGER.debug(f"{DOMAIN} - Start Hazard Lights Response: {response}")
|
1089
|
+
_check_response_for_errors(response)
|
1090
|
+
token.device_id = self._get_device_id(self._get_stamp())
|
1091
|
+
return response["msgId"]
|
1092
|
+
|
1093
|
+
def start_hazard_lights_and_horn(self, token: Token, vehicle: Vehicle) -> str:
|
1094
|
+
url = self.SPA_API_URL_V2 + "vehicles/" + vehicle.id + "/ccs2/control/hornlight"
|
1095
|
+
|
1096
|
+
payload = {"command": "on"}
|
1097
|
+
_LOGGER.debug(f"{DOMAIN} - Start Hazard Lights and Horn Request: {payload}")
|
1098
|
+
response = requests.post(
|
1099
|
+
url,
|
1100
|
+
json=payload,
|
1101
|
+
headers=self._get_control_headers(token, vehicle),
|
1102
|
+
).json()
|
1103
|
+
_LOGGER.debug(f"{DOMAIN} - Start Hazard Lights and Horn Response: {response}")
|
1104
|
+
_check_response_for_errors(response)
|
1105
|
+
token.device_id = self._get_device_id(self._get_stamp())
|
1106
|
+
return response["msgId"]
|
1107
|
+
|
1198
1108
|
def _get_charge_limits(self, token: Token, vehicle: Vehicle) -> dict:
|
1199
1109
|
# Not currently used as value is in the general get.
|
1200
1110
|
# Most likely this forces the car the update it.
|
@@ -1202,7 +1112,10 @@ class KiaUvoApiEU(ApiImpl):
|
|
1202
1112
|
|
1203
1113
|
_LOGGER.debug(f"{DOMAIN} - Get Charging Limits Request")
|
1204
1114
|
response = requests.get(
|
1205
|
-
url,
|
1115
|
+
url,
|
1116
|
+
headers=self._get_authenticated_headers(
|
1117
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1118
|
+
),
|
1206
1119
|
).json()
|
1207
1120
|
_LOGGER.debug(f"{DOMAIN} - Get Charging Limits Response: {response}")
|
1208
1121
|
_check_response_for_errors(response)
|
@@ -1229,7 +1142,9 @@ class KiaUvoApiEU(ApiImpl):
|
|
1229
1142
|
response = requests.post(
|
1230
1143
|
url,
|
1231
1144
|
json=payload,
|
1232
|
-
headers=self._get_authenticated_headers(
|
1145
|
+
headers=self._get_authenticated_headers(
|
1146
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1147
|
+
),
|
1233
1148
|
)
|
1234
1149
|
response = response.json()
|
1235
1150
|
_LOGGER.debug(f"{DOMAIN} - get_trip_info response {response}")
|
@@ -1243,7 +1158,7 @@ class KiaUvoApiEU(ApiImpl):
|
|
1243
1158
|
yyyymm_string,
|
1244
1159
|
) -> None:
|
1245
1160
|
"""
|
1246
|
-
|
1161
|
+
feature only available for some regions.
|
1247
1162
|
Updates the vehicle.month_trip_info for the specified month.
|
1248
1163
|
|
1249
1164
|
Default this information is None:
|
@@ -1278,9 +1193,6 @@ class KiaUvoApiEU(ApiImpl):
|
|
1278
1193
|
)
|
1279
1194
|
result.day_list.append(processed_day)
|
1280
1195
|
|
1281
|
-
if len(result.day_list) > 0: # sort on increasing yyyymmdd
|
1282
|
-
result.day_list.sort(key=lambda k: k.yyyymmdd)
|
1283
|
-
|
1284
1196
|
vehicle.month_trip_info = result
|
1285
1197
|
|
1286
1198
|
def update_day_trip_info(
|
@@ -1290,7 +1202,7 @@ class KiaUvoApiEU(ApiImpl):
|
|
1290
1202
|
yyyymmdd_string,
|
1291
1203
|
) -> None:
|
1292
1204
|
"""
|
1293
|
-
|
1205
|
+
feature only available for some regions.
|
1294
1206
|
Updates the vehicle.day_trip_info information for the specified day.
|
1295
1207
|
|
1296
1208
|
Default this information is None:
|
@@ -1329,9 +1241,6 @@ class KiaUvoApiEU(ApiImpl):
|
|
1329
1241
|
)
|
1330
1242
|
result.trip_list.append(processed_trip)
|
1331
1243
|
|
1332
|
-
if len(result.trip_list) > 0: # sort on descending hhmmss
|
1333
|
-
result.trip_list.sort(reverse=True, key=lambda k: k.hhmmss)
|
1334
|
-
|
1335
1244
|
vehicle.day_trip_info = result
|
1336
1245
|
|
1337
1246
|
def _get_driving_info(self, token: Token, vehicle: Vehicle) -> dict:
|
@@ -1340,7 +1249,9 @@ class KiaUvoApiEU(ApiImpl):
|
|
1340
1249
|
responseAlltime = requests.post(
|
1341
1250
|
url,
|
1342
1251
|
json={"periodTarget": 1},
|
1343
|
-
headers=self._get_authenticated_headers(
|
1252
|
+
headers=self._get_authenticated_headers(
|
1253
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1254
|
+
),
|
1344
1255
|
)
|
1345
1256
|
responseAlltime = responseAlltime.json()
|
1346
1257
|
_LOGGER.debug(f"{DOMAIN} - get_driving_info responseAlltime {responseAlltime}")
|
@@ -1349,7 +1260,9 @@ class KiaUvoApiEU(ApiImpl):
|
|
1349
1260
|
response30d = requests.post(
|
1350
1261
|
url,
|
1351
1262
|
json={"periodTarget": 0},
|
1352
|
-
headers=self._get_authenticated_headers(
|
1263
|
+
headers=self._get_authenticated_headers(
|
1264
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1265
|
+
),
|
1353
1266
|
)
|
1354
1267
|
response30d = response30d.json()
|
1355
1268
|
_LOGGER.debug(f"{DOMAIN} - get_driving_info response30d {response30d}")
|
@@ -1373,13 +1286,22 @@ class KiaUvoApiEU(ApiImpl):
|
|
1373
1286
|
),
|
1374
1287
|
regenerated_energy=get_child_value(day, "regenPwr"),
|
1375
1288
|
distance=get_child_value(day, "calculativeOdo"),
|
1289
|
+
distance_unit=vehicle.odometer_unit,
|
1376
1290
|
)
|
1377
1291
|
drivingInfo["dailyStats"].append(processedDay)
|
1378
1292
|
|
1379
1293
|
for drivingInfoItem in response30d["resMsg"]["drivingInfo"]:
|
1380
1294
|
if (
|
1381
1295
|
drivingInfoItem["drivingPeriod"] == 0
|
1382
|
-
and
|
1296
|
+
and next(
|
1297
|
+
(
|
1298
|
+
v
|
1299
|
+
for k, v in drivingInfoItem.items()
|
1300
|
+
if k.lower() == "calculativeodo"
|
1301
|
+
),
|
1302
|
+
0,
|
1303
|
+
)
|
1304
|
+
> 0
|
1383
1305
|
):
|
1384
1306
|
drivingInfo["consumption30d"] = round(
|
1385
1307
|
drivingInfoItem["totalPwrCsp"]
|
@@ -1387,12 +1309,6 @@ class KiaUvoApiEU(ApiImpl):
|
|
1387
1309
|
)
|
1388
1310
|
break
|
1389
1311
|
|
1390
|
-
daily_stats = drivingInfo["dailyStats"]
|
1391
|
-
_LOGGER.debug(f"KiaUvoApiEU: before daily_stats: {daily_stats}") # noqa
|
1392
|
-
if len(daily_stats) > 0: # sort on decreasing date
|
1393
|
-
daily_stats.sort(reverse=True, key=lambda k: k.date)
|
1394
|
-
drivingInfo["dailyStats"] = daily_stats
|
1395
|
-
_LOGGER.debug(f"KiaUvoApiEU: after daily_stats: {daily_stats}") # noqa
|
1396
1312
|
return drivingInfo
|
1397
1313
|
else:
|
1398
1314
|
_LOGGER.debug(
|
@@ -1418,12 +1334,162 @@ class KiaUvoApiEU(ApiImpl):
|
|
1418
1334
|
]
|
1419
1335
|
}
|
1420
1336
|
response = requests.post(
|
1421
|
-
url,
|
1337
|
+
url,
|
1338
|
+
json=body,
|
1339
|
+
headers=self._get_authenticated_headers(
|
1340
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1341
|
+
),
|
1422
1342
|
).json()
|
1423
1343
|
_LOGGER.debug(f"{DOMAIN} - Set Charge Limits Response: {response}")
|
1424
1344
|
_check_response_for_errors(response)
|
1425
1345
|
return response["msgId"]
|
1426
1346
|
|
1347
|
+
def set_charging_current(self, token: Token, vehicle: Vehicle, level: int) -> str:
|
1348
|
+
url = (
|
1349
|
+
self.SPA_API_URL + "vehicles/" + vehicle.id + "/ccs2/charge/chargingcurrent"
|
1350
|
+
)
|
1351
|
+
|
1352
|
+
body = {"chargingCurrent": level}
|
1353
|
+
response = requests.post(
|
1354
|
+
url,
|
1355
|
+
json=body,
|
1356
|
+
headers=self._get_authenticated_headers(
|
1357
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1358
|
+
),
|
1359
|
+
).json()
|
1360
|
+
_LOGGER.debug(f"{DOMAIN} - Set Charging Current Response: {response}")
|
1361
|
+
_check_response_for_errors(response)
|
1362
|
+
return response["msgId"]
|
1363
|
+
|
1364
|
+
def schedule_charging_and_climate(
|
1365
|
+
self,
|
1366
|
+
token: Token,
|
1367
|
+
vehicle: Vehicle,
|
1368
|
+
options: ScheduleChargingClimateRequestOptions,
|
1369
|
+
) -> str:
|
1370
|
+
url = self.SPA_API_URL_V2 + "vehicles/" + vehicle.id
|
1371
|
+
url = url + "/ccs2" # does not depend on vehicle.ccu_ccs2_protocol_support
|
1372
|
+
url = url + "/reservation/chargehvac"
|
1373
|
+
|
1374
|
+
def set_default_departure_options(
|
1375
|
+
departure_options: ScheduleChargingClimateRequestOptions.DepartureOptions,
|
1376
|
+
) -> None:
|
1377
|
+
if departure_options.enabled is None:
|
1378
|
+
departure_options.enabled = False
|
1379
|
+
if departure_options.days is None:
|
1380
|
+
departure_options.days = [0]
|
1381
|
+
if departure_options.time is None:
|
1382
|
+
departure_options.time = dt.time()
|
1383
|
+
|
1384
|
+
if options.first_departure is None:
|
1385
|
+
options.first_departure = (
|
1386
|
+
ScheduleChargingClimateRequestOptions.DepartureOptions()
|
1387
|
+
)
|
1388
|
+
if options.second_departure is None:
|
1389
|
+
options.second_departure = (
|
1390
|
+
ScheduleChargingClimateRequestOptions.DepartureOptions()
|
1391
|
+
)
|
1392
|
+
|
1393
|
+
set_default_departure_options(options.first_departure)
|
1394
|
+
set_default_departure_options(options.second_departure)
|
1395
|
+
departures = [options.first_departure, options.second_departure]
|
1396
|
+
|
1397
|
+
if options.charging_enabled is None:
|
1398
|
+
options.charging_enabled = False
|
1399
|
+
if options.off_peak_start_time is None:
|
1400
|
+
options.off_peak_start_time = dt.time()
|
1401
|
+
if options.off_peak_end_time is None:
|
1402
|
+
options.off_peak_end_time = options.off_peak_start_time
|
1403
|
+
if options.off_peak_charge_only_enabled is None:
|
1404
|
+
options.off_peak_charge_only_enabled = False
|
1405
|
+
if options.climate_enabled is None:
|
1406
|
+
options.climate_enabled = False
|
1407
|
+
if options.temperature is None:
|
1408
|
+
options.temperature = 21.0
|
1409
|
+
if options.temperature_unit is None:
|
1410
|
+
options.temperature_unit = 0
|
1411
|
+
if options.defrost is None:
|
1412
|
+
options.defrost = False
|
1413
|
+
|
1414
|
+
temperature: float = options.temperature
|
1415
|
+
if options.temperature_unit == 0:
|
1416
|
+
# Round to nearest 0.5
|
1417
|
+
temperature = round(temperature * 2.0) / 2.0
|
1418
|
+
# Cap at 27, floor at 17
|
1419
|
+
if temperature > 27.0:
|
1420
|
+
temperature = 27.0
|
1421
|
+
elif temperature < 17.0:
|
1422
|
+
temperature = 17.0
|
1423
|
+
|
1424
|
+
payload = {
|
1425
|
+
"reservChargeInfo" + str(i + 1): {
|
1426
|
+
"reservChargeSet": departures[i].enabled,
|
1427
|
+
"reservInfo": {
|
1428
|
+
"day": departures[i].days,
|
1429
|
+
"time": {
|
1430
|
+
"time": departures[i].time.strftime("%I%M"),
|
1431
|
+
"timeSection": 1 if departures[i].time >= dt.time(12, 0) else 0,
|
1432
|
+
},
|
1433
|
+
},
|
1434
|
+
"reservFatcSet": {
|
1435
|
+
"airCtrl": 1 if options.climate_enabled else 0,
|
1436
|
+
"airTemp": {
|
1437
|
+
"value": f"{temperature:.1f}",
|
1438
|
+
"hvacTempType": 1,
|
1439
|
+
"unit": options.temperature_unit,
|
1440
|
+
},
|
1441
|
+
"heating1": 0,
|
1442
|
+
"defrost": options.defrost,
|
1443
|
+
},
|
1444
|
+
}
|
1445
|
+
for i in range(2)
|
1446
|
+
}
|
1447
|
+
|
1448
|
+
payload = payload | {
|
1449
|
+
"offPeakPowerInfo": {
|
1450
|
+
"offPeakPowerTime1": {
|
1451
|
+
"endtime": {
|
1452
|
+
"timeSection": (
|
1453
|
+
1 if options.off_peak_end_time >= dt.time(12, 0) else 0
|
1454
|
+
),
|
1455
|
+
"time": options.off_peak_end_time.strftime("%I%M"),
|
1456
|
+
},
|
1457
|
+
"starttime": {
|
1458
|
+
"timeSection": (
|
1459
|
+
1 if options.off_peak_start_time >= dt.time(12, 0) else 0
|
1460
|
+
),
|
1461
|
+
"time": options.off_peak_start_time.strftime("%I%M"),
|
1462
|
+
},
|
1463
|
+
},
|
1464
|
+
"offPeakPowerFlag": 2 if options.off_peak_charge_only_enabled else 1,
|
1465
|
+
},
|
1466
|
+
"reservFlag": 1 if options.charging_enabled else 0,
|
1467
|
+
}
|
1468
|
+
|
1469
|
+
_LOGGER.debug(f"{DOMAIN} - Schedule Charging and Climate Request: {payload}")
|
1470
|
+
response = requests.post(
|
1471
|
+
url, json=payload, headers=self._get_control_headers(token, vehicle)
|
1472
|
+
).json()
|
1473
|
+
_LOGGER.debug(f"{DOMAIN} - Schedule Charging and Climate Response: {response}")
|
1474
|
+
_check_response_for_errors(response)
|
1475
|
+
token.device_id = self._get_device_id(self._get_stamp())
|
1476
|
+
return response["msgId"]
|
1477
|
+
|
1478
|
+
def valet_mode_action(
|
1479
|
+
self, token: Token, vehicle: Vehicle, action: VALET_MODE_ACTION
|
1480
|
+
) -> str:
|
1481
|
+
url = self.SPA_API_URL_V2 + "vehicles/" + vehicle.id + "/control/valet"
|
1482
|
+
|
1483
|
+
payload = {"action": action.value}
|
1484
|
+
_LOGGER.debug(f"{DOMAIN} - Valet Mode Action Request: {payload}")
|
1485
|
+
response = requests.post(
|
1486
|
+
url, json=payload, headers=self._get_control_headers(token, vehicle)
|
1487
|
+
).json()
|
1488
|
+
_LOGGER.debug(f"{DOMAIN} - Valet Mode Action Response: {response}")
|
1489
|
+
_check_response_for_errors(response)
|
1490
|
+
token.device_id = self._get_device_id(self._get_stamp())
|
1491
|
+
return response["msgId"]
|
1492
|
+
|
1427
1493
|
def _get_stamp(self) -> str:
|
1428
1494
|
raw_data = f"{self.APP_ID}:{int(dt.datetime.now().timestamp())}".encode()
|
1429
1495
|
result = bytes(b1 ^ b2 for b1, b2 in zip(self.CFB, raw_data))
|
@@ -1654,6 +1720,27 @@ class KiaUvoApiEU(ApiImpl):
|
|
1654
1720
|
refresh_token = token_type + " " + response["access_token"]
|
1655
1721
|
return token_type, refresh_token
|
1656
1722
|
|
1723
|
+
def _get_control_token(self, token: Token) -> Token:
|
1724
|
+
url = self.USER_API_URL + "pin?token="
|
1725
|
+
headers = {
|
1726
|
+
"Authorization": token.access_token,
|
1727
|
+
"Content-type": "application/json",
|
1728
|
+
"Host": self.BASE_URL,
|
1729
|
+
"Accept-Encoding": "gzip",
|
1730
|
+
"User-Agent": USER_AGENT_OK_HTTP,
|
1731
|
+
}
|
1732
|
+
|
1733
|
+
data = {"deviceId": token.device_id, "pin": token.pin}
|
1734
|
+
_LOGGER.debug(f"{DOMAIN} - Get Control Token Data: {data}")
|
1735
|
+
response = requests.put(url, json=data, headers=headers)
|
1736
|
+
response = response.json()
|
1737
|
+
_LOGGER.debug(f"{DOMAIN} - Get Control Token Response {response}")
|
1738
|
+
control_token = "Bearer " + response["controlToken"]
|
1739
|
+
control_token_expire_at = math.floor(
|
1740
|
+
dt.datetime.now().timestamp() + response["expiresTime"]
|
1741
|
+
)
|
1742
|
+
return control_token, control_token_expire_at
|
1743
|
+
|
1657
1744
|
def check_action_status(
|
1658
1745
|
self,
|
1659
1746
|
token: Token,
|
@@ -1687,7 +1774,10 @@ class KiaUvoApiEU(ApiImpl):
|
|
1687
1774
|
|
1688
1775
|
else:
|
1689
1776
|
response = requests.get(
|
1690
|
-
url,
|
1777
|
+
url,
|
1778
|
+
headers=self._get_authenticated_headers(
|
1779
|
+
token, vehicle.ccu_ccs2_protocol_support
|
1780
|
+
),
|
1691
1781
|
).json()
|
1692
1782
|
_LOGGER.debug(f"{DOMAIN} - Check last action status Response: {response}")
|
1693
1783
|
_check_response_for_errors(response)
|