pySmartHashtag 0.9.0__tar.gz → 0.9.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.
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.devcontainer.json +1 -1
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.github/copilot-instructions.md +4 -0
- {pysmarthashtag-0.9.0/pySmartHashtag.egg-info → pysmarthashtag-0.9.2}/PKG-INFO +1 -1
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2/pySmartHashtag.egg-info}/PKG-INFO +1 -1
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pySmartHashtag.egg-info/SOURCES.txt +6 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/__init__.py +0 -1
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/account.py +164 -1
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/api/authentication.py +240 -87
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/const.py +25 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/control/climate.py +3 -2
- pysmarthashtag-0.9.2/pysmarthashtag/control/journal.py +95 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/__init__.py +7 -2
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/common.py +56 -2
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/auth_context.url +1 -1
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/auth_intermediate.url +1 -1
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/auth_result.url +1 -1
- pysmarthashtag-0.9.2/pysmarthashtag/tests/replys/journal_response.json +52 -0
- pysmarthashtag-0.9.2/pysmarthashtag/tests/replys/journal_toggle_success.json +9 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_dc_charging.py +2 -0
- pysmarthashtag-0.9.2/pysmarthashtag/tests/test_journal.py +236 -0
- pysmarthashtag-0.9.2/pysmarthashtag/tests/test_journal_control.py +67 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/battery.py +1 -2
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/climate.py +1 -2
- pysmarthashtag-0.9.2/pysmarthashtag/vehicle/journal.py +206 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/maintenance.py +1 -2
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/position.py +1 -2
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/running.py +1 -2
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/safety.py +1 -2
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/tires.py +1 -2
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/vehicle.py +17 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/requirements-test.txt +1 -1
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.github/dependabot.yml +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.github/workflows/python-package.yml +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.github/workflows/python-publish.yml +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.gitignore +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.pre-commit-config.yaml +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.vscode/launch.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/.vscode/settings.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/AUTHORS +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/CODE_OF_CONDUCT.md +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/CONTRIBUTING.md +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/ChangeLog +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/LICENSE +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/README.md +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pySmartHashtag.egg-info/dependency_links.txt +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pySmartHashtag.egg-info/requires.txt +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pySmartHashtag.egg-info/top_level.txt +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pyproject.toml +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/api/__init__.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/api/client.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/api/log_sanitizer.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/api/ssl_context.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/api/utils.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/cli.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/control/charging.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/models.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/conftest.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/Human_and_vehicle_relationship_does_not_exist.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/api_access.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/charging_success.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/climate_success.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/login_result.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/ota_response.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/soc_80.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/soc_90.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/token_expired.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/vehicle_info.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/vehicle_info2.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/vehicle_info_dc_charging.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/vehicle_response.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/replys/vehicle_result.json +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_account.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_actions.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_authentication_backoff.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_charging.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_endpoint_urls.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_log_sanitizer.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_missing_fields.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/tests/test_ssl_context.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/pysmarthashtag/vehicle/__init__.py +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/requirements-cli.txt +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/requirements.txt +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/run.sh +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/setup.cfg +0 -0
- {pysmarthashtag-0.9.0 → pysmarthashtag-0.9.2}/setup.py +0 -0
|
@@ -61,6 +61,7 @@ pre-commit run --all-files
|
|
|
61
61
|
### Ruff Configuration
|
|
62
62
|
|
|
63
63
|
The project uses ruff with the following rule sets:
|
|
64
|
+
|
|
64
65
|
- C (complexity)
|
|
65
66
|
- D (docstrings)
|
|
66
67
|
- E (pycodestyle)
|
|
@@ -116,6 +117,7 @@ async def example_method(self) -> dict:
|
|
|
116
117
|
## Error Handling
|
|
117
118
|
|
|
118
119
|
Use the custom exception classes defined in `models.py`:
|
|
120
|
+
|
|
119
121
|
- `SmartAuthError` - Authentication failures
|
|
120
122
|
- `SmartTokenRefreshNecessary` - Token refresh required
|
|
121
123
|
- `SmartHumanCarConnectionError` - Human-car connection issues
|
|
@@ -136,11 +138,13 @@ Use `sanitize_log_data()` from `api.log_sanitizer` when logging sensitive data.
|
|
|
136
138
|
## Dependencies
|
|
137
139
|
|
|
138
140
|
Main dependencies:
|
|
141
|
+
|
|
139
142
|
- `httpx` - Async HTTP client
|
|
140
143
|
- `pycryptodome` - Cryptographic functions
|
|
141
144
|
- `pyjwt` - JWT token handling
|
|
142
145
|
|
|
143
146
|
Test dependencies:
|
|
147
|
+
|
|
144
148
|
- `pytest`, `pytest-asyncio`, `pytest-cov`
|
|
145
149
|
- `respx` - HTTP mocking
|
|
146
150
|
- `ruff` - Linting and formatting
|
|
@@ -38,6 +38,7 @@ pysmarthashtag/api/ssl_context.py
|
|
|
38
38
|
pysmarthashtag/api/utils.py
|
|
39
39
|
pysmarthashtag/control/charging.py
|
|
40
40
|
pysmarthashtag/control/climate.py
|
|
41
|
+
pysmarthashtag/control/journal.py
|
|
41
42
|
pysmarthashtag/tests/__init__.py
|
|
42
43
|
pysmarthashtag/tests/common.py
|
|
43
44
|
pysmarthashtag/tests/conftest.py
|
|
@@ -47,6 +48,8 @@ pysmarthashtag/tests/test_authentication_backoff.py
|
|
|
47
48
|
pysmarthashtag/tests/test_charging.py
|
|
48
49
|
pysmarthashtag/tests/test_dc_charging.py
|
|
49
50
|
pysmarthashtag/tests/test_endpoint_urls.py
|
|
51
|
+
pysmarthashtag/tests/test_journal.py
|
|
52
|
+
pysmarthashtag/tests/test_journal_control.py
|
|
50
53
|
pysmarthashtag/tests/test_log_sanitizer.py
|
|
51
54
|
pysmarthashtag/tests/test_missing_fields.py
|
|
52
55
|
pysmarthashtag/tests/test_ssl_context.py
|
|
@@ -57,6 +60,8 @@ pysmarthashtag/tests/replys/auth_intermediate.url
|
|
|
57
60
|
pysmarthashtag/tests/replys/auth_result.url
|
|
58
61
|
pysmarthashtag/tests/replys/charging_success.json
|
|
59
62
|
pysmarthashtag/tests/replys/climate_success.json
|
|
63
|
+
pysmarthashtag/tests/replys/journal_response.json
|
|
64
|
+
pysmarthashtag/tests/replys/journal_toggle_success.json
|
|
60
65
|
pysmarthashtag/tests/replys/login_result.json
|
|
61
66
|
pysmarthashtag/tests/replys/ota_response.json
|
|
62
67
|
pysmarthashtag/tests/replys/soc_80.json
|
|
@@ -70,6 +75,7 @@ pysmarthashtag/tests/replys/vehicle_result.json
|
|
|
70
75
|
pysmarthashtag/vehicle/__init__.py
|
|
71
76
|
pysmarthashtag/vehicle/battery.py
|
|
72
77
|
pysmarthashtag/vehicle/climate.py
|
|
78
|
+
pysmarthashtag/vehicle/journal.py
|
|
73
79
|
pysmarthashtag/vehicle/maintenance.py
|
|
74
80
|
pysmarthashtag/vehicle/position.py
|
|
75
81
|
pysmarthashtag/vehicle/running.py
|
|
@@ -41,6 +41,15 @@ class SmartAccount:
|
|
|
41
41
|
vehicles: dict[str, SmartVehicle] = field(default_factory=dict, init=False)
|
|
42
42
|
"""Vehicles associated with the account."""
|
|
43
43
|
|
|
44
|
+
_journal_grant_cache: dict[str, str] = field(default_factory=dict, init=False)
|
|
45
|
+
"""Per-VIN cache of the access_token under which the trip-journal
|
|
46
|
+
authorization grant was last accepted. ``{vin: access_token_string}``.
|
|
47
|
+
Used to skip the redundant grant POST when the same token is still
|
|
48
|
+
valid; auto-invalidates as soon as the token rotates (any reason —
|
|
49
|
+
expiry, manual relogin, future refresh-token flow). Maintained by
|
|
50
|
+
:meth:`grant_journal_authorization`.
|
|
51
|
+
"""
|
|
52
|
+
|
|
44
53
|
def __post_init__(self, password, log_responses):
|
|
45
54
|
"""Initialize the account."""
|
|
46
55
|
# Ensure endpoint_urls is set
|
|
@@ -134,7 +143,24 @@ class SmartAccount:
|
|
|
134
143
|
vehicle_info = await self.get_vehicle_information(vin)
|
|
135
144
|
vehicle_soc = await self.get_vehicle_soc(vin)
|
|
136
145
|
vehicle_ota_info = await self.get_vehicle_ota_info(vin)
|
|
137
|
-
|
|
146
|
+
# Trip journal is best-effort: the endpoint can return 8153
|
|
147
|
+
# ("data unavailable") on vehicles where on-vehicle trip
|
|
148
|
+
# recording is OFF, or transiently when the per-session auth
|
|
149
|
+
# grant hasn't been accepted yet. Never let an empty journal
|
|
150
|
+
# fail the whole refresh.
|
|
151
|
+
journal_response = None
|
|
152
|
+
try:
|
|
153
|
+
journal_response = await self.get_trip_journal(vin)
|
|
154
|
+
except Exception: # noqa: BLE001 # Best-effort: any failure (8153, transport, parse) must not break refresh.
|
|
155
|
+
_LOGGER.debug(
|
|
156
|
+
"Trip journal fetch failed for %s", sanitize_log_data(vin), exc_info=True
|
|
157
|
+
)
|
|
158
|
+
vehicle.combine_data(
|
|
159
|
+
vehicle_info,
|
|
160
|
+
charging_settings=vehicle_soc,
|
|
161
|
+
ota_info=vehicle_ota_info,
|
|
162
|
+
journal_response=journal_response,
|
|
163
|
+
)
|
|
138
164
|
|
|
139
165
|
async def select_active_vehicle(self, vin) -> None:
|
|
140
166
|
"""Select the active vehicle."""
|
|
@@ -257,6 +283,143 @@ class SmartAccount:
|
|
|
257
283
|
raise SmartAuthError("Could not get vehicle information")
|
|
258
284
|
return data
|
|
259
285
|
|
|
286
|
+
async def grant_journal_authorization(self, vin, force: bool = False) -> bool:
|
|
287
|
+
"""Grant cloud-side authorization for trip-journal data access.
|
|
288
|
+
|
|
289
|
+
``POST /remote-control/user/authorization/insert`` with
|
|
290
|
+
``{"serviceCode": "travelLogBusiCode", "authStatus": 1, "vin": <vin>}``.
|
|
291
|
+
This is a per-session handshake that unlocks the journalLogV4
|
|
292
|
+
endpoint — without it, journalLogV4 returns ``code: 8153``
|
|
293
|
+
("data unavailable") even on vehicles where the on-vehicle
|
|
294
|
+
recording flag is set and trip data exists cloud-side.
|
|
295
|
+
|
|
296
|
+
Cached per-VIN per-access-token. The grant POST is observed to
|
|
297
|
+
rotate the access_token server-side (every poll without the cache
|
|
298
|
+
returns ``1402 token invalid`` on the next call, forcing a
|
|
299
|
+
re-login). The cache stores the access_token under which the
|
|
300
|
+
grant was accepted; subsequent calls under the *same* token
|
|
301
|
+
skip the redundant POST. The cache auto-invalidates as soon as
|
|
302
|
+
the token rotates (relogin, refresh, expiry — any reason).
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
vin: Vehicle identification number.
|
|
306
|
+
force: If True, ignore the cache and re-issue the grant.
|
|
307
|
+
Use this for explicit init flows where you want to be
|
|
308
|
+
certain the grant is fresh.
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
True if the grant succeeded (or was already cached);
|
|
312
|
+
False if the POST failed.
|
|
313
|
+
|
|
314
|
+
"""
|
|
315
|
+
token = self.config.authentication.api_access_token
|
|
316
|
+
if not force and token and self._journal_grant_cache.get(vin) == token:
|
|
317
|
+
_LOGGER.debug(
|
|
318
|
+
"Journal authorization cached for %s under current token; skipping POST",
|
|
319
|
+
sanitize_log_data(vin),
|
|
320
|
+
)
|
|
321
|
+
return True
|
|
322
|
+
|
|
323
|
+
_LOGGER.debug("Granting journal authorization for %s", sanitize_log_data(vin))
|
|
324
|
+
path = "/remote-control/user/authorization/insert"
|
|
325
|
+
body = json.dumps({"serviceCode": "travelLogBusiCode", "authStatus": 1, "vin": vin})
|
|
326
|
+
async with SmartClient(self.config) as client:
|
|
327
|
+
for retry in range(3):
|
|
328
|
+
try:
|
|
329
|
+
r = await client.post(
|
|
330
|
+
self.vehicles[vin].base_url + path,
|
|
331
|
+
headers={
|
|
332
|
+
**utils.generate_default_header(
|
|
333
|
+
client.config.authentication.device_id,
|
|
334
|
+
client.config.authentication.api_access_token,
|
|
335
|
+
params={},
|
|
336
|
+
method="POST",
|
|
337
|
+
url=path,
|
|
338
|
+
body=body,
|
|
339
|
+
)
|
|
340
|
+
},
|
|
341
|
+
content=body.encode("utf-8"),
|
|
342
|
+
)
|
|
343
|
+
payload = r.json()
|
|
344
|
+
success = bool(payload.get("success") or payload.get("code") == "1000")
|
|
345
|
+
if success:
|
|
346
|
+
# Record the token under which this grant was accepted.
|
|
347
|
+
# Re-read after the POST since the server may have rotated
|
|
348
|
+
# it during the call.
|
|
349
|
+
self._journal_grant_cache[vin] = (
|
|
350
|
+
self.config.authentication.api_access_token
|
|
351
|
+
)
|
|
352
|
+
return success
|
|
353
|
+
except SmartTokenRefreshNecessary:
|
|
354
|
+
_LOGGER.debug("Token refresh needed during auth-grant retry %d", retry)
|
|
355
|
+
continue
|
|
356
|
+
except SmartHumanCarConnectionError:
|
|
357
|
+
_LOGGER.debug("Human-car connection error during auth-grant retry %d", retry)
|
|
358
|
+
await self.select_active_vehicle(vin)
|
|
359
|
+
continue
|
|
360
|
+
break
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
async def get_trip_journal(self, vin, page_size: int = 20, window_days: int = 14) -> dict:
|
|
364
|
+
"""Fetch the most recent trip-journal entries for a vehicle.
|
|
365
|
+
|
|
366
|
+
Hits ``/geelyTCAccess/tcservices/vehicle/status/journalLogV4/{vin}``
|
|
367
|
+
— the endpoint that carries server-side reverse-geocoded start/end
|
|
368
|
+
addresses alongside per-trip energy/distance/speed metrics.
|
|
369
|
+
|
|
370
|
+
Sends ``startTime`` / ``endTime`` (ms-epoch window), ``pageIndex``,
|
|
371
|
+
``pageSize``, and ``userId`` as query params. The endpoint requires
|
|
372
|
+
a per-session authorization grant
|
|
373
|
+
(:meth:`grant_journal_authorization`), which this method calls
|
|
374
|
+
first; without it the endpoint returns ``code: 8153``. The grant
|
|
375
|
+
is cached per-VIN per-access-token, so subsequent calls under the
|
|
376
|
+
same token skip the grant POST.
|
|
377
|
+
|
|
378
|
+
Returns the raw response dict (with ``code``/``message``/``data``)
|
|
379
|
+
or an empty dict when the request fails server-side; raising is
|
|
380
|
+
avoided so a single flaky vehicle doesn't break the whole refresh.
|
|
381
|
+
"""
|
|
382
|
+
await self.grant_journal_authorization(vin)
|
|
383
|
+
_LOGGER.debug("Getting trip journal for vehicle")
|
|
384
|
+
end_ms = int(datetime.datetime.now(datetime.timezone.utc).timestamp() * 1000)
|
|
385
|
+
start_ms = end_ms - window_days * 86400 * 1000
|
|
386
|
+
params = {
|
|
387
|
+
"endTime": str(end_ms),
|
|
388
|
+
"pageIndex": "1",
|
|
389
|
+
"pageSize": str(page_size),
|
|
390
|
+
"startTime": str(start_ms),
|
|
391
|
+
"userId": str(self.config.authentication.api_user_id),
|
|
392
|
+
}
|
|
393
|
+
path = "/geelyTCAccess/tcservices/vehicle/status/journalLogV4/" + vin
|
|
394
|
+
url = path + "?" + utils.join_url_params(params)
|
|
395
|
+
data: dict = {}
|
|
396
|
+
async with SmartClient(self.config) as client:
|
|
397
|
+
for retry in range(3):
|
|
398
|
+
try:
|
|
399
|
+
r_journal = await client.get(
|
|
400
|
+
self.vehicles[vin].base_url + url,
|
|
401
|
+
headers={
|
|
402
|
+
**utils.generate_default_header(
|
|
403
|
+
client.config.authentication.device_id,
|
|
404
|
+
client.config.authentication.api_access_token,
|
|
405
|
+
params=params,
|
|
406
|
+
method="GET",
|
|
407
|
+
url=path,
|
|
408
|
+
)
|
|
409
|
+
},
|
|
410
|
+
)
|
|
411
|
+
_LOGGER.debug("Got response %d", r_journal.status_code)
|
|
412
|
+
data = r_journal.json()
|
|
413
|
+
except SmartTokenRefreshNecessary:
|
|
414
|
+
_LOGGER.debug("Got Token Error, retry: %d", retry)
|
|
415
|
+
continue
|
|
416
|
+
except SmartHumanCarConnectionError:
|
|
417
|
+
_LOGGER.debug("Got Human Car Connection Error, retry: %d", retry)
|
|
418
|
+
await self.select_active_vehicle(vin)
|
|
419
|
+
continue
|
|
420
|
+
break
|
|
421
|
+
return data
|
|
422
|
+
|
|
260
423
|
async def get_vehicle_ota_info(self, vin) -> dict:
|
|
261
424
|
"""Get information about a vehicle from OTA server."""
|
|
262
425
|
_LOGGER.debug("Getting OTA information for vehicle")
|