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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-chargepoint
3
- Version: 2.1.0
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>
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-chargepoint"
3
- version = "2.1.0"
3
+ version = "2.3.0"
4
4
  description = "A simple, Pythonic wrapper for the ChargePoint API."
5
5
  authors = ["Marc Billow <mbillow@users.noreply.github.compoetry>"]
6
6
  license = "MIT"
@@ -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
- if response.status != 200:
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
- if response.status != 200:
250
- text = await response.text()
251
- _LOGGER.error(
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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
- if response.status != 200:
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 _CamelModel(BaseModel):
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(alias_generator=to_camel, populate_by_name=True)
24
+ model_config = ConfigDict(
25
+ alias_generator=to_camel, populate_by_name=True, extra="allow"
26
+ )
19
27
 
20
28
 
21
- class ElectricVehicle(BaseModel):
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(BaseModel):
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 Station(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
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(BaseModel):
296
+ class MaxPower(_BaseModel):
267
297
  unit: str = ""
268
298
  max: float = 0.0
269
299
 
270
300
 
271
- class MapChargingInfo(BaseModel):
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(BaseModel):
359
+ class StationAddress(_BaseModel):
330
360
  address1: str = ""
331
361
  city: str = ""
332
362
  state: str = ""
333
363
 
334
364
 
335
- class StationPricingFee(BaseModel):
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(BaseModel):
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(BaseModel):
424
+ class MapStation(_BaseModel):
395
425
  device_id: int = 0
396
426
  lat: float = 0.0
397
427
  lon: float = 0.0