python-chargepoint 2.1.0__tar.gz → 2.3.0__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.
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/PKG-INFO +27 -1
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/README.md +26 -0
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/pyproject.toml +1 -1
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/python_chargepoint/__main__.py +109 -0
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/python_chargepoint/client.py +81 -150
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/python_chargepoint/types.py +48 -18
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/LICENSE +0 -0
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/python_chargepoint/__init__.py +0 -0
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/python_chargepoint/constants.py +0 -0
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/python_chargepoint/exceptions.py +0 -0
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/python_chargepoint/global_config.py +0 -0
- {python_chargepoint-2.1.0 → python_chargepoint-2.3.0}/python_chargepoint/session.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-chargepoint
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A simple, Pythonic wrapper for the ChargePoint API.
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -172,6 +172,29 @@ await client.set_led_brightness(charger_id, 3) # 60%
|
|
|
172
172
|
await client.restart_home_charger(charger_id)
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
+
#### Charging schedule
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
schedule = await client.get_home_charger_schedule(charger_id)
|
|
179
|
+
print(schedule.schedule_enabled) # False
|
|
180
|
+
if schedule.default_schedule:
|
|
181
|
+
print(schedule.default_schedule.weekdays.start_time) # "23:00"
|
|
182
|
+
print(schedule.default_schedule.weekdays.end_time) # "07:00"
|
|
183
|
+
print(schedule.default_schedule.weekends.start_time) # "19:00"
|
|
184
|
+
print(schedule.default_schedule.weekends.end_time) # "15:00"
|
|
185
|
+
|
|
186
|
+
# Enable a schedule
|
|
187
|
+
schedule = await client.set_home_charger_schedule(
|
|
188
|
+
charger_id,
|
|
189
|
+
weekday_start="23:00", weekday_end="07:00",
|
|
190
|
+
weekend_start="19:00", weekend_end="15:00",
|
|
191
|
+
)
|
|
192
|
+
print(schedule.schedule_enabled) # True
|
|
193
|
+
|
|
194
|
+
# Disable the schedule
|
|
195
|
+
await client.disable_home_charger_schedule(charger_id)
|
|
196
|
+
```
|
|
197
|
+
|
|
175
198
|
---
|
|
176
199
|
|
|
177
200
|
### Charging Status and Sessions
|
|
@@ -327,6 +350,9 @@ chargepoint charger config <charger_id>
|
|
|
327
350
|
chargepoint charger set-amperage <charger_id> <amps>
|
|
328
351
|
chargepoint charger set-led <charger_id> <level> # 0=off 1=20% 2=40% 3=60% 4=80% 5=100%
|
|
329
352
|
chargepoint charger restart <charger_id>
|
|
353
|
+
chargepoint charger schedule <charger_id>
|
|
354
|
+
chargepoint charger set-schedule <charger_id> --weekday-start 23:00 --weekday-end 07:00 --weekend-start 19:00 --weekend-end 15:00
|
|
355
|
+
chargepoint charger disable-schedule <charger_id>
|
|
330
356
|
|
|
331
357
|
# Sessions
|
|
332
358
|
chargepoint session get <session_id>
|
|
@@ -151,6 +151,29 @@ await client.set_led_brightness(charger_id, 3) # 60%
|
|
|
151
151
|
await client.restart_home_charger(charger_id)
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
+
#### Charging schedule
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
schedule = await client.get_home_charger_schedule(charger_id)
|
|
158
|
+
print(schedule.schedule_enabled) # False
|
|
159
|
+
if schedule.default_schedule:
|
|
160
|
+
print(schedule.default_schedule.weekdays.start_time) # "23:00"
|
|
161
|
+
print(schedule.default_schedule.weekdays.end_time) # "07:00"
|
|
162
|
+
print(schedule.default_schedule.weekends.start_time) # "19:00"
|
|
163
|
+
print(schedule.default_schedule.weekends.end_time) # "15:00"
|
|
164
|
+
|
|
165
|
+
# Enable a schedule
|
|
166
|
+
schedule = await client.set_home_charger_schedule(
|
|
167
|
+
charger_id,
|
|
168
|
+
weekday_start="23:00", weekday_end="07:00",
|
|
169
|
+
weekend_start="19:00", weekend_end="15:00",
|
|
170
|
+
)
|
|
171
|
+
print(schedule.schedule_enabled) # True
|
|
172
|
+
|
|
173
|
+
# Disable the schedule
|
|
174
|
+
await client.disable_home_charger_schedule(charger_id)
|
|
175
|
+
```
|
|
176
|
+
|
|
154
177
|
---
|
|
155
178
|
|
|
156
179
|
### Charging Status and Sessions
|
|
@@ -306,6 +329,9 @@ chargepoint charger config <charger_id>
|
|
|
306
329
|
chargepoint charger set-amperage <charger_id> <amps>
|
|
307
330
|
chargepoint charger set-led <charger_id> <level> # 0=off 1=20% 2=40% 3=60% 4=80% 5=100%
|
|
308
331
|
chargepoint charger restart <charger_id>
|
|
332
|
+
chargepoint charger schedule <charger_id>
|
|
333
|
+
chargepoint charger set-schedule <charger_id> --weekday-start 23:00 --weekday-end 07:00 --weekend-start 19:00 --weekend-end 15:00
|
|
334
|
+
chargepoint charger disable-schedule <charger_id>
|
|
309
335
|
|
|
310
336
|
# Sessions
|
|
311
337
|
chargepoint session get <session_id>
|
|
@@ -519,6 +519,115 @@ async def charger_restart(ctx, charger_id: int) -> None:
|
|
|
519
519
|
await client.close()
|
|
520
520
|
|
|
521
521
|
|
|
522
|
+
@charger.command("schedule")
|
|
523
|
+
@click.argument("charger_id", type=int)
|
|
524
|
+
@click.pass_context
|
|
525
|
+
@async_cmd
|
|
526
|
+
async def charger_schedule(ctx, charger_id: int) -> None:
|
|
527
|
+
"""Show the charging schedule for a home charger."""
|
|
528
|
+
client = await _make_client(ctx.obj["debug"])
|
|
529
|
+
try:
|
|
530
|
+
schedule = await client.get_home_charger_schedule(charger_id)
|
|
531
|
+
if ctx.obj["as_json"]:
|
|
532
|
+
_dump_json(schedule)
|
|
533
|
+
else:
|
|
534
|
+
click.echo(f"Enabled: {schedule.schedule_enabled}")
|
|
535
|
+
for label, window in [
|
|
536
|
+
(
|
|
537
|
+
"Weekdays",
|
|
538
|
+
(
|
|
539
|
+
schedule.default_schedule.weekdays
|
|
540
|
+
if schedule.default_schedule
|
|
541
|
+
else None
|
|
542
|
+
),
|
|
543
|
+
),
|
|
544
|
+
(
|
|
545
|
+
"Weekends",
|
|
546
|
+
(
|
|
547
|
+
schedule.default_schedule.weekends
|
|
548
|
+
if schedule.default_schedule
|
|
549
|
+
else None
|
|
550
|
+
),
|
|
551
|
+
),
|
|
552
|
+
]:
|
|
553
|
+
if window:
|
|
554
|
+
click.echo(f"{label}: {window.start_time} – {window.end_time}")
|
|
555
|
+
except CommunicationError as e:
|
|
556
|
+
click.echo(f"Error: {e.message}", err=True)
|
|
557
|
+
sys.exit(1)
|
|
558
|
+
finally:
|
|
559
|
+
await client.close()
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
@charger.command("set-schedule")
|
|
563
|
+
@click.argument("charger_id", type=int)
|
|
564
|
+
@click.option(
|
|
565
|
+
"--weekday-start", required=True, help='Weekday charge start time, e.g. "23:00"'
|
|
566
|
+
)
|
|
567
|
+
@click.option(
|
|
568
|
+
"--weekday-end", required=True, help='Weekday charge end time, e.g. "07:00"'
|
|
569
|
+
)
|
|
570
|
+
@click.option(
|
|
571
|
+
"--weekend-start", required=True, help='Weekend charge start time, e.g. "19:00"'
|
|
572
|
+
)
|
|
573
|
+
@click.option(
|
|
574
|
+
"--weekend-end", required=True, help='Weekend charge end time, e.g. "15:00"'
|
|
575
|
+
)
|
|
576
|
+
@click.pass_context
|
|
577
|
+
@async_cmd
|
|
578
|
+
async def charger_set_schedule(
|
|
579
|
+
ctx,
|
|
580
|
+
charger_id: int,
|
|
581
|
+
weekday_start: str,
|
|
582
|
+
weekday_end: str,
|
|
583
|
+
weekend_start: str,
|
|
584
|
+
weekend_end: str,
|
|
585
|
+
) -> None:
|
|
586
|
+
"""Set the charging schedule for a home charger."""
|
|
587
|
+
client = await _make_client(ctx.obj["debug"])
|
|
588
|
+
try:
|
|
589
|
+
schedule = await client.set_home_charger_schedule(
|
|
590
|
+
charger_id, weekday_start, weekday_end, weekend_start, weekend_end
|
|
591
|
+
)
|
|
592
|
+
if ctx.obj["as_json"]:
|
|
593
|
+
_dump_json(schedule)
|
|
594
|
+
else:
|
|
595
|
+
click.echo(f"Schedule enabled: {schedule.schedule_enabled}")
|
|
596
|
+
if schedule.user_schedule:
|
|
597
|
+
click.echo(
|
|
598
|
+
f"Weekdays: {schedule.user_schedule.weekdays.start_time} – {schedule.user_schedule.weekdays.end_time}"
|
|
599
|
+
)
|
|
600
|
+
click.echo(
|
|
601
|
+
f"Weekends: {schedule.user_schedule.weekends.start_time} – {schedule.user_schedule.weekends.end_time}"
|
|
602
|
+
)
|
|
603
|
+
except CommunicationError as e:
|
|
604
|
+
click.echo(f"Error: {e.message}", err=True)
|
|
605
|
+
sys.exit(1)
|
|
606
|
+
finally:
|
|
607
|
+
await client.close()
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
@charger.command("disable-schedule")
|
|
611
|
+
@click.argument("charger_id", type=int)
|
|
612
|
+
@click.confirmation_option(prompt="Disable the charging schedule?")
|
|
613
|
+
@click.pass_context
|
|
614
|
+
@async_cmd
|
|
615
|
+
async def charger_disable_schedule(ctx, charger_id: int) -> None:
|
|
616
|
+
"""Disable the charging schedule for a home charger."""
|
|
617
|
+
client = await _make_client(ctx.obj["debug"])
|
|
618
|
+
try:
|
|
619
|
+
schedule = await client.disable_home_charger_schedule(charger_id)
|
|
620
|
+
if ctx.obj["as_json"]:
|
|
621
|
+
_dump_json(schedule)
|
|
622
|
+
else:
|
|
623
|
+
click.echo(f"Schedule enabled: {schedule.schedule_enabled}")
|
|
624
|
+
except CommunicationError as e:
|
|
625
|
+
click.echo(f"Error: {e.message}", err=True)
|
|
626
|
+
sys.exit(1)
|
|
627
|
+
finally:
|
|
628
|
+
await client.close()
|
|
629
|
+
|
|
630
|
+
|
|
522
631
|
# ---------------------------------------------------------------------------
|
|
523
632
|
# session subgroup
|
|
524
633
|
# ---------------------------------------------------------------------------
|
|
@@ -13,6 +13,7 @@ from .types import (
|
|
|
13
13
|
Account,
|
|
14
14
|
ElectricVehicle,
|
|
15
15
|
HomeChargerConfiguration,
|
|
16
|
+
HomeChargerSchedule,
|
|
16
17
|
HomeChargerStatus,
|
|
17
18
|
HomeChargerTechnicalInfo,
|
|
18
19
|
MapFilter,
|
|
@@ -150,6 +151,18 @@ class ChargePoint:
|
|
|
150
151
|
|
|
151
152
|
return response
|
|
152
153
|
|
|
154
|
+
async def _raise_for_status(
|
|
155
|
+
self, response: aiohttp.ClientResponse, message: str
|
|
156
|
+
) -> None:
|
|
157
|
+
if response.status != 200:
|
|
158
|
+
text = await response.text()
|
|
159
|
+
_LOGGER.error(
|
|
160
|
+
"status_code=%s err=%s",
|
|
161
|
+
response.status,
|
|
162
|
+
text,
|
|
163
|
+
)
|
|
164
|
+
raise CommunicationError(response=response, message=message)
|
|
165
|
+
|
|
153
166
|
async def _init_account_parameters(self):
|
|
154
167
|
account: Account = await self.get_account()
|
|
155
168
|
self._user_id = account.user.user_id
|
|
@@ -231,13 +244,7 @@ class ChargePoint:
|
|
|
231
244
|
self._global_config.endpoints.sso_endpoint / "v1/user/logout",
|
|
232
245
|
)
|
|
233
246
|
|
|
234
|
-
|
|
235
|
-
text = await response.text()
|
|
236
|
-
_LOGGER.error(
|
|
237
|
-
"Failed to log out! status_code=%s err=%s", response.status, text
|
|
238
|
-
)
|
|
239
|
-
raise CommunicationError(response=response, message="Failed to log out!")
|
|
240
|
-
|
|
247
|
+
await self._raise_for_status(response, "Failed to log out!")
|
|
241
248
|
await response.release()
|
|
242
249
|
self._session.cookie_jar.clear()
|
|
243
250
|
self._user_id = None
|
|
@@ -246,17 +253,9 @@ class ChargePoint:
|
|
|
246
253
|
_LOGGER.debug("Discovering account region for username %s", username)
|
|
247
254
|
request = {"username": username}
|
|
248
255
|
response = await self._request("POST", DISCOVERY_API, json=request)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
"Failed to discover region! status_code=%s err=%s",
|
|
253
|
-
response.status,
|
|
254
|
-
text,
|
|
255
|
-
)
|
|
256
|
-
raise CommunicationError(
|
|
257
|
-
response=response,
|
|
258
|
-
message="Failed to discover region for provided username!",
|
|
259
|
-
)
|
|
256
|
+
await self._raise_for_status(
|
|
257
|
+
response, "Failed to discover region for provided username!"
|
|
258
|
+
)
|
|
260
259
|
config = GlobalConfiguration.model_validate(await response.json())
|
|
261
260
|
_LOGGER.debug(
|
|
262
261
|
"Discovered account region: %s / %s (%s)",
|
|
@@ -274,17 +273,7 @@ class ChargePoint:
|
|
|
274
273
|
self._global_config.endpoints.accounts_endpoint / "v1/driver/profile/user",
|
|
275
274
|
)
|
|
276
275
|
|
|
277
|
-
|
|
278
|
-
text = await response.text()
|
|
279
|
-
_LOGGER.error(
|
|
280
|
-
"Failed to get account information! status_code=%s err=%s",
|
|
281
|
-
response.status,
|
|
282
|
-
text,
|
|
283
|
-
)
|
|
284
|
-
raise CommunicationError(
|
|
285
|
-
response=response, message="Failed to get user information."
|
|
286
|
-
)
|
|
287
|
-
|
|
276
|
+
await self._raise_for_status(response, "Failed to get user information.")
|
|
288
277
|
return Account.model_validate(await response.json())
|
|
289
278
|
|
|
290
279
|
@_require_login
|
|
@@ -295,17 +284,7 @@ class ChargePoint:
|
|
|
295
284
|
self._global_config.endpoints.accounts_endpoint / "v1/driver/vehicle",
|
|
296
285
|
)
|
|
297
286
|
|
|
298
|
-
|
|
299
|
-
text = await response.text()
|
|
300
|
-
_LOGGER.error(
|
|
301
|
-
"Failed to list vehicles! status_code=%s err=%s",
|
|
302
|
-
response.status,
|
|
303
|
-
text,
|
|
304
|
-
)
|
|
305
|
-
raise CommunicationError(
|
|
306
|
-
response=response, message="Failed to retrieve EVs."
|
|
307
|
-
)
|
|
308
|
-
|
|
287
|
+
await self._raise_for_status(response, "Failed to retrieve EVs.")
|
|
309
288
|
evs = await response.json()
|
|
310
289
|
return [ElectricVehicle.model_validate(ev) for ev in evs]
|
|
311
290
|
|
|
@@ -318,17 +297,7 @@ class ChargePoint:
|
|
|
318
297
|
/ f"api/v1/configuration/users/{self.user_id}/chargers",
|
|
319
298
|
)
|
|
320
299
|
|
|
321
|
-
|
|
322
|
-
text = await response.text()
|
|
323
|
-
_LOGGER.error(
|
|
324
|
-
"Failed to get home chargers! status_code=%s err=%s",
|
|
325
|
-
response.status,
|
|
326
|
-
text,
|
|
327
|
-
)
|
|
328
|
-
raise CommunicationError(
|
|
329
|
-
response=response, message="Failed to retrieve Home Flex chargers."
|
|
330
|
-
)
|
|
331
|
-
|
|
300
|
+
await self._raise_for_status(response, "Failed to retrieve Home Flex chargers.")
|
|
332
301
|
data = (await response.json())["data"]
|
|
333
302
|
chargers = [int(item["id"]) for item in data]
|
|
334
303
|
_LOGGER.debug(
|
|
@@ -347,17 +316,7 @@ class ChargePoint:
|
|
|
347
316
|
/ f"api/v1/configuration/users/{self.user_id}/chargers/{charger_id}/status",
|
|
348
317
|
)
|
|
349
318
|
|
|
350
|
-
|
|
351
|
-
text = await response.text()
|
|
352
|
-
_LOGGER.error(
|
|
353
|
-
"Failed to determine home charger status! status_code=%s err=%s",
|
|
354
|
-
response.status,
|
|
355
|
-
text,
|
|
356
|
-
)
|
|
357
|
-
raise CommunicationError(
|
|
358
|
-
response=response, message="Failed to get home charger status."
|
|
359
|
-
)
|
|
360
|
-
|
|
319
|
+
await self._raise_for_status(response, "Failed to get home charger status.")
|
|
361
320
|
status = await response.json()
|
|
362
321
|
_LOGGER.debug(status)
|
|
363
322
|
return HomeChargerStatus.model_validate({"charger_id": charger_id, **status})
|
|
@@ -373,17 +332,7 @@ class ChargePoint:
|
|
|
373
332
|
/ f"api/v1/configuration/users/{self.user_id}/chargers/{charger_id}/technical-info",
|
|
374
333
|
)
|
|
375
334
|
|
|
376
|
-
|
|
377
|
-
text = await response.text()
|
|
378
|
-
_LOGGER.error(
|
|
379
|
-
"Failed to get home charger tech info! status_code=%s err=%s",
|
|
380
|
-
response.status,
|
|
381
|
-
text,
|
|
382
|
-
)
|
|
383
|
-
raise CommunicationError(
|
|
384
|
-
response=response, message="Failed to get home charger tech info."
|
|
385
|
-
)
|
|
386
|
-
|
|
335
|
+
await self._raise_for_status(response, "Failed to get home charger tech info.")
|
|
387
336
|
return HomeChargerTechnicalInfo.model_validate(await response.json())
|
|
388
337
|
|
|
389
338
|
@_require_login
|
|
@@ -394,17 +343,7 @@ class ChargePoint:
|
|
|
394
343
|
"POST", self._global_config.endpoints.mapcache_endpoint / "v2", json=request
|
|
395
344
|
)
|
|
396
345
|
|
|
397
|
-
|
|
398
|
-
text = await response.text()
|
|
399
|
-
_LOGGER.error(
|
|
400
|
-
"Failed to get account charging status! status_code=%s err=%s",
|
|
401
|
-
response.status,
|
|
402
|
-
text,
|
|
403
|
-
)
|
|
404
|
-
raise CommunicationError(
|
|
405
|
-
response=response, message="Failed to get user charging status."
|
|
406
|
-
)
|
|
407
|
-
|
|
346
|
+
await self._raise_for_status(response, "Failed to get user charging status.")
|
|
408
347
|
status = await response.json()
|
|
409
348
|
if not status["user_status"]:
|
|
410
349
|
_LOGGER.debug("No user status returned, assuming not charging.")
|
|
@@ -423,17 +362,7 @@ class ChargePoint:
|
|
|
423
362
|
json={"chargeAmperageLimit": amperage_limit},
|
|
424
363
|
)
|
|
425
364
|
|
|
426
|
-
|
|
427
|
-
text = await response.text()
|
|
428
|
-
_LOGGER.error(
|
|
429
|
-
"Failed to set amperage limit! status_code=%s err=%s",
|
|
430
|
-
response.status,
|
|
431
|
-
text,
|
|
432
|
-
)
|
|
433
|
-
raise CommunicationError(
|
|
434
|
-
response=response, message="Failed to set amperage limit."
|
|
435
|
-
)
|
|
436
|
-
|
|
365
|
+
await self._raise_for_status(response, "Failed to set amperage limit.")
|
|
437
366
|
await response.release()
|
|
438
367
|
|
|
439
368
|
@_require_login
|
|
@@ -452,17 +381,7 @@ class ChargePoint:
|
|
|
452
381
|
json={"ledBrightnessLevel": level},
|
|
453
382
|
)
|
|
454
383
|
|
|
455
|
-
|
|
456
|
-
text = await response.text()
|
|
457
|
-
_LOGGER.error(
|
|
458
|
-
"Failed to set LED brightness! status_code=%s err=%s",
|
|
459
|
-
response.status,
|
|
460
|
-
text,
|
|
461
|
-
)
|
|
462
|
-
raise CommunicationError(
|
|
463
|
-
response=response, message="Failed to set LED brightness."
|
|
464
|
-
)
|
|
465
|
-
|
|
384
|
+
await self._raise_for_status(response, "Failed to set LED brightness.")
|
|
466
385
|
await response.release()
|
|
467
386
|
|
|
468
387
|
@_require_login
|
|
@@ -474,17 +393,7 @@ class ChargePoint:
|
|
|
474
393
|
/ f"api/v1/configuration/users/{self.user_id}/chargers/{charger_id}/restart",
|
|
475
394
|
)
|
|
476
395
|
|
|
477
|
-
|
|
478
|
-
text = await response.text()
|
|
479
|
-
_LOGGER.error(
|
|
480
|
-
"Failed to restart charger! status_code=%s err=%s",
|
|
481
|
-
response.status,
|
|
482
|
-
text,
|
|
483
|
-
)
|
|
484
|
-
raise CommunicationError(
|
|
485
|
-
response=response, message="Failed to restart charger."
|
|
486
|
-
)
|
|
487
|
-
|
|
396
|
+
await self._raise_for_status(response, "Failed to restart charger.")
|
|
488
397
|
await response.release()
|
|
489
398
|
|
|
490
399
|
@_require_login
|
|
@@ -498,19 +407,61 @@ class ChargePoint:
|
|
|
498
407
|
/ f"api/v1/configuration/users/{self.user_id}/chargers/{charger_id}/configurations",
|
|
499
408
|
)
|
|
500
409
|
|
|
501
|
-
|
|
502
|
-
text = await response.text()
|
|
503
|
-
_LOGGER.error(
|
|
504
|
-
"Failed to get charger configuration! status_code=%s err=%s",
|
|
505
|
-
response.status,
|
|
506
|
-
text,
|
|
507
|
-
)
|
|
508
|
-
raise CommunicationError(
|
|
509
|
-
response=response, message="Failed to get charger configuration."
|
|
510
|
-
)
|
|
511
|
-
|
|
410
|
+
await self._raise_for_status(response, "Failed to get charger configuration.")
|
|
512
411
|
return HomeChargerConfiguration.model_validate(await response.json())
|
|
513
412
|
|
|
413
|
+
@_require_login
|
|
414
|
+
async def get_home_charger_schedule(self, charger_id: int) -> HomeChargerSchedule:
|
|
415
|
+
_LOGGER.debug("Getting schedule for charger: %s", charger_id)
|
|
416
|
+
response = await self._request(
|
|
417
|
+
"GET",
|
|
418
|
+
self._global_config.endpoints.hcpo_hcm_endpoint
|
|
419
|
+
/ f"api/v1/schedule/charger/{charger_id}/schedule",
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
await self._raise_for_status(response, "Failed to get charger schedule.")
|
|
423
|
+
return HomeChargerSchedule.model_validate(await response.json())
|
|
424
|
+
|
|
425
|
+
@_require_login
|
|
426
|
+
async def set_home_charger_schedule(
|
|
427
|
+
self,
|
|
428
|
+
charger_id: int,
|
|
429
|
+
weekday_start: str,
|
|
430
|
+
weekday_end: str,
|
|
431
|
+
weekend_start: str,
|
|
432
|
+
weekend_end: str,
|
|
433
|
+
) -> HomeChargerSchedule:
|
|
434
|
+
_LOGGER.debug("Setting schedule for charger: %s", charger_id)
|
|
435
|
+
response = await self._request(
|
|
436
|
+
"PUT",
|
|
437
|
+
self._global_config.endpoints.hcpo_hcm_endpoint
|
|
438
|
+
/ f"api/v1/schedule/charger/{charger_id}/schedule",
|
|
439
|
+
json={
|
|
440
|
+
"schedule": {
|
|
441
|
+
"weekdays": {"startTime": weekday_start, "endTime": weekday_end},
|
|
442
|
+
"weekends": {"startTime": weekend_start, "endTime": weekend_end},
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
await self._raise_for_status(response, "Failed to set charger schedule.")
|
|
448
|
+
return HomeChargerSchedule.model_validate(await response.json())
|
|
449
|
+
|
|
450
|
+
@_require_login
|
|
451
|
+
async def disable_home_charger_schedule(
|
|
452
|
+
self, charger_id: int
|
|
453
|
+
) -> HomeChargerSchedule:
|
|
454
|
+
_LOGGER.debug("Disabling schedule for charger: %s", charger_id)
|
|
455
|
+
response = await self._request(
|
|
456
|
+
"PUT",
|
|
457
|
+
self._global_config.endpoints.hcpo_hcm_endpoint
|
|
458
|
+
/ f"api/v1/schedule/charger/{charger_id}/schedule",
|
|
459
|
+
json={},
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
await self._raise_for_status(response, "Failed to disable charger schedule.")
|
|
463
|
+
return HomeChargerSchedule.model_validate(await response.json())
|
|
464
|
+
|
|
514
465
|
@_require_login
|
|
515
466
|
async def get_charging_session(self, session_id: int) -> ChargingSession:
|
|
516
467
|
session = ChargingSession(session_id=session_id)
|
|
@@ -530,17 +481,7 @@ class ChargePoint:
|
|
|
530
481
|
).update_query({"deviceId": str(device_id), "use_cache": "false"})
|
|
531
482
|
response = await self._request("GET", url)
|
|
532
483
|
|
|
533
|
-
|
|
534
|
-
text = await response.text()
|
|
535
|
-
_LOGGER.error(
|
|
536
|
-
"Failed to get station info! status_code=%s err=%s",
|
|
537
|
-
response.status,
|
|
538
|
-
text,
|
|
539
|
-
)
|
|
540
|
-
raise CommunicationError(
|
|
541
|
-
response=response, message="Failed to get station info."
|
|
542
|
-
)
|
|
543
|
-
|
|
484
|
+
await self._raise_for_status(response, "Failed to get station info.")
|
|
544
485
|
data = await response.json()
|
|
545
486
|
return StationInfo.model_validate(data)
|
|
546
487
|
|
|
@@ -576,17 +517,7 @@ class ChargePoint:
|
|
|
576
517
|
"POST", self._global_config.endpoints.mapcache_endpoint / "v2", json=request
|
|
577
518
|
)
|
|
578
519
|
|
|
579
|
-
|
|
580
|
-
text = await response.text()
|
|
581
|
-
_LOGGER.error(
|
|
582
|
-
"Failed to get nearby stations! status_code=%s err=%s",
|
|
583
|
-
response.status,
|
|
584
|
-
text,
|
|
585
|
-
)
|
|
586
|
-
raise CommunicationError(
|
|
587
|
-
response=response, message="Failed to get nearby stations."
|
|
588
|
-
)
|
|
589
|
-
|
|
520
|
+
await self._raise_for_status(response, "Failed to get nearby stations.")
|
|
590
521
|
data = await response.json()
|
|
591
522
|
stations = data["map_data"].get("stations", [])
|
|
592
523
|
return [MapStation.model_validate(s) for s in stations]
|
|
@@ -12,13 +12,21 @@ def _parse_ms_timestamp(v: float) -> datetime:
|
|
|
12
12
|
return datetime.fromtimestamp(v / 1000, tz=timezone.utc)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class
|
|
15
|
+
class _BaseModel(BaseModel):
|
|
16
|
+
"""Base for all library models. Passes through undeclared API fields for diagnostic use."""
|
|
17
|
+
|
|
18
|
+
model_config = ConfigDict(extra="allow")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _CamelModel(_BaseModel):
|
|
16
22
|
"""Base for models whose API responses use camelCase field names."""
|
|
17
23
|
|
|
18
|
-
model_config = ConfigDict(
|
|
24
|
+
model_config = ConfigDict(
|
|
25
|
+
alias_generator=to_camel, populate_by_name=True, extra="allow"
|
|
26
|
+
)
|
|
19
27
|
|
|
20
28
|
|
|
21
|
-
class ElectricVehicle(
|
|
29
|
+
class ElectricVehicle(_BaseModel):
|
|
22
30
|
make: str = ""
|
|
23
31
|
model: str = ""
|
|
24
32
|
primary_vehicle: bool = False
|
|
@@ -56,7 +64,7 @@ class User(_CamelModel):
|
|
|
56
64
|
username: str = ""
|
|
57
65
|
|
|
58
66
|
|
|
59
|
-
class AccountBalance(
|
|
67
|
+
class AccountBalance(_BaseModel):
|
|
60
68
|
account_number: str = Field("", alias="accountNumber")
|
|
61
69
|
account_state: str = Field("", alias="accountState")
|
|
62
70
|
amount: str = ""
|
|
@@ -158,14 +166,36 @@ class HomeChargerConfiguration(_CamelModel):
|
|
|
158
166
|
return {**settings, "led_brightness": led_brightness}
|
|
159
167
|
|
|
160
168
|
|
|
161
|
-
class
|
|
169
|
+
class ChargeScheduleWindow(_CamelModel):
|
|
170
|
+
start_time: str = ""
|
|
171
|
+
end_time: str = ""
|
|
172
|
+
start_weekday: Optional[int] = None
|
|
173
|
+
end_weekday: Optional[int] = None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class ChargeSchedule(_CamelModel):
|
|
177
|
+
weekdays: ChargeScheduleWindow = Field(default_factory=ChargeScheduleWindow)
|
|
178
|
+
weekends: ChargeScheduleWindow = Field(default_factory=ChargeScheduleWindow)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class HomeChargerSchedule(_CamelModel):
|
|
182
|
+
has_tou_pricing: bool = False
|
|
183
|
+
schedule_enabled: bool = False
|
|
184
|
+
has_utility_info: bool = False
|
|
185
|
+
based_on_utility: Optional["PowerUtility"] = None
|
|
186
|
+
default_schedule: Optional[ChargeSchedule] = None
|
|
187
|
+
user_schedule: Optional[ChargeSchedule] = None
|
|
188
|
+
utility_schedule: Optional[ChargeSchedule] = None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class Station(_BaseModel):
|
|
162
192
|
id: int = Field(0, alias="deviceId")
|
|
163
193
|
name: str = ""
|
|
164
194
|
latitude: float = Field(0.0, alias="lat")
|
|
165
195
|
longitude: float = Field(0.0, alias="lon")
|
|
166
196
|
|
|
167
197
|
|
|
168
|
-
class UserChargingStatus(
|
|
198
|
+
class UserChargingStatus(_BaseModel):
|
|
169
199
|
session_id: int = Field(0, alias="sessionId")
|
|
170
200
|
start_time: datetime = Field(
|
|
171
201
|
default_factory=lambda: datetime.now(tz=timezone.utc), alias="startTimeUTC"
|
|
@@ -194,7 +224,7 @@ class UserChargingStatus(BaseModel):
|
|
|
194
224
|
return self
|
|
195
225
|
|
|
196
226
|
|
|
197
|
-
class VehicleInfo(
|
|
227
|
+
class VehicleInfo(_BaseModel):
|
|
198
228
|
vehicle_id: int = 0
|
|
199
229
|
battery_capacity: float = 0.0
|
|
200
230
|
make: str = ""
|
|
@@ -204,7 +234,7 @@ class VehicleInfo(BaseModel):
|
|
|
204
234
|
is_primary_vehicle: bool = False
|
|
205
235
|
|
|
206
236
|
|
|
207
|
-
class ChargingSessionUpdate(
|
|
237
|
+
class ChargingSessionUpdate(_BaseModel):
|
|
208
238
|
energy_kwh: float = 0.0
|
|
209
239
|
power_kw: float = 0.0
|
|
210
240
|
timestamp: datetime = Field(default_factory=lambda: datetime.now(tz=timezone.utc))
|
|
@@ -215,20 +245,20 @@ class ChargingSessionUpdate(BaseModel):
|
|
|
215
245
|
return _parse_ms_timestamp(v)
|
|
216
246
|
|
|
217
247
|
|
|
218
|
-
class PowerUtilityPlan(
|
|
248
|
+
class PowerUtilityPlan(_BaseModel):
|
|
219
249
|
code: Union[str, int] = ""
|
|
220
250
|
id: int = 0
|
|
221
251
|
is_ev_plan: bool = False
|
|
222
252
|
name: str = ""
|
|
223
253
|
|
|
224
254
|
|
|
225
|
-
class PowerUtility(
|
|
255
|
+
class PowerUtility(_BaseModel):
|
|
226
256
|
id: int = 0
|
|
227
257
|
name: str = ""
|
|
228
258
|
plans: List[PowerUtilityPlan] = Field(default_factory=list)
|
|
229
259
|
|
|
230
260
|
|
|
231
|
-
class MapFilter(
|
|
261
|
+
class MapFilter(_BaseModel):
|
|
232
262
|
disabled_parking: bool = False
|
|
233
263
|
network_circuitelectric: bool = False
|
|
234
264
|
connector_l2: bool = False
|
|
@@ -254,7 +284,7 @@ class MapFilter(BaseModel):
|
|
|
254
284
|
connector_tesla: bool = False
|
|
255
285
|
|
|
256
286
|
|
|
257
|
-
class StationPort(
|
|
287
|
+
class StationPort(_BaseModel):
|
|
258
288
|
status_v2: str = ""
|
|
259
289
|
port_type: int = 0
|
|
260
290
|
outlet_number: int = 0
|
|
@@ -263,12 +293,12 @@ class StationPort(BaseModel):
|
|
|
263
293
|
status: str = ""
|
|
264
294
|
|
|
265
295
|
|
|
266
|
-
class MaxPower(
|
|
296
|
+
class MaxPower(_BaseModel):
|
|
267
297
|
unit: str = ""
|
|
268
298
|
max: float = 0.0
|
|
269
299
|
|
|
270
300
|
|
|
271
|
-
class MapChargingInfo(
|
|
301
|
+
class MapChargingInfo(_BaseModel):
|
|
272
302
|
session_id: int = 0
|
|
273
303
|
session_time: int = 0
|
|
274
304
|
energy_kwh: float = 0.0
|
|
@@ -326,13 +356,13 @@ class StationNetwork(_CamelModel):
|
|
|
326
356
|
in_network: bool = False
|
|
327
357
|
|
|
328
358
|
|
|
329
|
-
class StationAddress(
|
|
359
|
+
class StationAddress(_BaseModel):
|
|
330
360
|
address1: str = ""
|
|
331
361
|
city: str = ""
|
|
332
362
|
state: str = ""
|
|
333
363
|
|
|
334
364
|
|
|
335
|
-
class StationPricingFee(
|
|
365
|
+
class StationPricingFee(_BaseModel):
|
|
336
366
|
amount: float = 0.0
|
|
337
367
|
unit: str = ""
|
|
338
368
|
|
|
@@ -345,7 +375,7 @@ class StationTouEntry(_CamelModel):
|
|
|
345
375
|
fee: StationPricingFee = Field(default_factory=StationPricingFee)
|
|
346
376
|
|
|
347
377
|
|
|
348
|
-
class StationTax(
|
|
378
|
+
class StationTax(_BaseModel):
|
|
349
379
|
name: str = ""
|
|
350
380
|
percent: float = 0.0
|
|
351
381
|
|
|
@@ -391,7 +421,7 @@ class StationInfo(_CamelModel):
|
|
|
391
421
|
last_charged_date: Optional[str] = None
|
|
392
422
|
|
|
393
423
|
|
|
394
|
-
class MapStation(
|
|
424
|
+
class MapStation(_BaseModel):
|
|
395
425
|
device_id: int = 0
|
|
396
426
|
lat: float = 0.0
|
|
397
427
|
lon: float = 0.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|