python-chargepoint 1.8.0__tar.gz → 1.10.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.
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.3
2
+ Name: python-chargepoint
3
+ Version: 1.10.0
4
+ Summary: A simple, Pythonic wrapper for the ChargePoint API.
5
+ License: MIT
6
+ Author: Marc Billow
7
+ Author-email: mbillow@users.noreply.github.compoetry
8
+ Requires-Python: >=3.9,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Requires-Dist: requests (>=2.32.3,<3.0.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # python-chargepoint
20
+
21
+ A simple Pythonic wrapper around the ChargePoint EV Charging Network API.
22
+
23
+ ## DISCLAIMER
24
+
25
+ I, nor this project, are in _any way_ associated with ChargePoint. Use this project at your own risk.
26
+ ChargePoint is a registered trademark of ChargePoint, Inc.
27
+
28
+ I just wanted a way to retrieve and store charging data from my ChargePoint Home Flex
29
+ in a way that is easy to model and query. This project is the first step in getting that data into a
30
+ more robust time series database.
31
+
32
+ ## Use
33
+
34
+ ### Login
35
+
36
+ ```python
37
+ from python_chargepoint import ChargePoint
38
+
39
+ client = ChargePoint(username="user", password="password")
40
+ print(client.user_id)
41
+ # 1234567890
42
+ ```
43
+
44
+ ### Home Chargers
45
+ ```python
46
+ from python_chargepoint import ChargePoint
47
+
48
+ client = ChargePoint(username="user", password="password")
49
+ chargers = client.get_home_chargers()
50
+ print(chargers)
51
+ # [12345678]
52
+
53
+ for charger_id in chargers:
54
+ charger = client.get_home_charger_status(charger_id=charger_id)
55
+ print(charger)
56
+ # HomeChargerStatus(
57
+ # brand='CP',
58
+ # plugged_in=True,
59
+ # connected=True,
60
+ # charging_status='NOT_CHARGING',
61
+ # last_connected_at=datetime.datetime(2022, 1, 30, 15, 14, 36),
62
+ # reminder_enabled=False,
63
+ # reminder_time='21:00',
64
+ # model='CPH50-NEMA6-50-L23',
65
+ # mac_address='0024B10000012345',
66
+ # amperage_limit=25,
67
+ # possible_amperage_limits=[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32])
68
+ ```
69
+
70
+ ### Account Charging Status and Session
71
+
72
+ ```python
73
+ from python_chargepoint import ChargePoint
74
+
75
+ client = ChargePoint(username="user", password="password")
76
+ charging = client.get_user_charging_status()
77
+ if charging:
78
+ print(charging)
79
+ # UserChargingStatus(
80
+ # session_id=1234567890,
81
+ # start_time=datetime.datetime(2022, 1, 30, 13, 32, 45),
82
+ # state='fully_charged',
83
+ # stations=[
84
+ # ChargePointStation(
85
+ # id=12345678,
86
+ # name='CP HOME ',
87
+ # latitude=30.0000000,
88
+ # longitude=-90.0000000)
89
+ # ])
90
+
91
+ session = client.get_charging_session(charging.session_id)
92
+ print(session)
93
+ # ChargingSession(
94
+ # session_id=1234567890,
95
+ # start_time=datetime.datetime(2022, 1, 30, 13, 32, 45),
96
+ # device_id=12345678,
97
+ # device_name='CP HOME',
98
+ # charging_state='fully_charged',
99
+ # charging_time=2426000,
100
+ # energy_kwh=4.3404,
101
+ # miles_added=15.024461538461539,
102
+ # miles_added_per_hour=0.0,
103
+ # outlet_number=1,
104
+ # port_level=2,
105
+ # power_kw=0.0,
106
+ # purpose='PERSONAL',
107
+ # currency_iso_code='USD',
108
+ # payment_completed=True,
109
+ # payment_type='none',
110
+ # pricing_spec_id=0,
111
+ # total_amount=0.67,
112
+ # api_flag=False,
113
+ # enable_stop_charging=True,
114
+ # has_charging_receipt=False,
115
+ # has_utility_info=True,
116
+ # is_home_charger=True,
117
+ # is_purpose_finalized=True,
118
+ # last_update_data_timestamp=datetime.datetime(2022, 1, 30, 15, 12, 48),
119
+ # stop_charge_supported=True,
120
+ # company_id=12345,
121
+ # company_name='CP Home',
122
+ # latitude=30.0000000,
123
+ # longitude=-90.0000000,
124
+ # address='Home Charger',
125
+ # city='City',
126
+ # state_name='State',
127
+ # country='United States',
128
+ # zipcode='12345',
129
+ # update_data=[
130
+ # ChargingSessionUpdate(
131
+ # energy_kwh=0.0,
132
+ # power_kw=0.0002,
133
+ # timestamp=datetime.datetime(2022, 1, 30, 13, 32, 57)),
134
+ # ChargingSessionUpdate(
135
+ # energy_kwh=0.0001,
136
+ # power_kw=0.1568,
137
+ # timestamp=datetime.datetime(2022, 1, 30, 13, 33, 9)),
138
+ # ChargingSessionUpdate(
139
+ # energy_kwh=0.0025,
140
+ # power_kw=3.7337,
141
+ # timestamp=datetime.datetime(2022, 1, 30, 13, 33, 12)),
142
+ # ChargingSessionUpdate(
143
+ # energy_kwh=0.0161,
144
+ # power_kw=1.3854,
145
+ # timestamp=datetime.datetime(2022, 1, 30, 13, 33, 33)),
146
+ # ...],
147
+ # update_period=300000,
148
+ # utility=PowerUtility(
149
+ # id=0,
150
+ # name='Energy Company',
151
+ # plans=[
152
+ # PowerUtilityPlan(
153
+ # code='R',
154
+ # id=12345,
155
+ # is_ev_plan=False,
156
+ # name='Residential')
157
+ # ]))
158
+ ```
159
+
160
+ #### Starting or Stopping a Session
161
+
162
+ ```python
163
+ from python_chargepoint import ChargePoint
164
+
165
+ client = ChargePoint(username="user", password="password")
166
+ charging = client.get_user_charging_status()
167
+
168
+ if charging:
169
+ session = client.get_charging_session(charging.session_id)
170
+ session.stop()
171
+
172
+ # If you wanted to charge again, you can start a new session.
173
+ session = client.start_charging_session(session.device_id)
174
+ ```
175
+
176
+ You can also start a new session by providing any device ID you want to start charging on.
177
+
178
+ ```python
179
+ from python_chargepoint import ChargePoint
180
+
181
+ client = ChargePoint(username="user", password="password")
182
+ home_flex_id = client.get_home_chargers()[0]
183
+ home_flex = client.get_home_charger_status(home_flex_id)
184
+
185
+ if home_flex.charging_status == "AVAILABLE":
186
+ session = client.start_charging_session(home_flex_id)
187
+ ```
188
+
189
+ #### Setting the amperage limit
190
+
191
+ ```python
192
+ from python_chargepoint import ChargePoint
193
+
194
+ client = ChargePoint(username="user", password="password")
195
+ home_flex_id = client.get_home_chargers()[0]
196
+
197
+ # Print out valid amperage values.
198
+ print(client.get_home_charger_status(home_flex_id).possible_amperage_limits)
199
+ # [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
200
+
201
+ client.set_amperage_limit(home_flex_id, 23)
202
+ print(client.get_home_charger_status(home_flex_id).amperage_limit)
203
+ # 23
204
+ ```
205
+
@@ -0,0 +1,186 @@
1
+ # python-chargepoint
2
+
3
+ A simple Pythonic wrapper around the ChargePoint EV Charging Network API.
4
+
5
+ ## DISCLAIMER
6
+
7
+ I, nor this project, are in _any way_ associated with ChargePoint. Use this project at your own risk.
8
+ ChargePoint is a registered trademark of ChargePoint, Inc.
9
+
10
+ I just wanted a way to retrieve and store charging data from my ChargePoint Home Flex
11
+ in a way that is easy to model and query. This project is the first step in getting that data into a
12
+ more robust time series database.
13
+
14
+ ## Use
15
+
16
+ ### Login
17
+
18
+ ```python
19
+ from python_chargepoint import ChargePoint
20
+
21
+ client = ChargePoint(username="user", password="password")
22
+ print(client.user_id)
23
+ # 1234567890
24
+ ```
25
+
26
+ ### Home Chargers
27
+ ```python
28
+ from python_chargepoint import ChargePoint
29
+
30
+ client = ChargePoint(username="user", password="password")
31
+ chargers = client.get_home_chargers()
32
+ print(chargers)
33
+ # [12345678]
34
+
35
+ for charger_id in chargers:
36
+ charger = client.get_home_charger_status(charger_id=charger_id)
37
+ print(charger)
38
+ # HomeChargerStatus(
39
+ # brand='CP',
40
+ # plugged_in=True,
41
+ # connected=True,
42
+ # charging_status='NOT_CHARGING',
43
+ # last_connected_at=datetime.datetime(2022, 1, 30, 15, 14, 36),
44
+ # reminder_enabled=False,
45
+ # reminder_time='21:00',
46
+ # model='CPH50-NEMA6-50-L23',
47
+ # mac_address='0024B10000012345',
48
+ # amperage_limit=25,
49
+ # possible_amperage_limits=[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32])
50
+ ```
51
+
52
+ ### Account Charging Status and Session
53
+
54
+ ```python
55
+ from python_chargepoint import ChargePoint
56
+
57
+ client = ChargePoint(username="user", password="password")
58
+ charging = client.get_user_charging_status()
59
+ if charging:
60
+ print(charging)
61
+ # UserChargingStatus(
62
+ # session_id=1234567890,
63
+ # start_time=datetime.datetime(2022, 1, 30, 13, 32, 45),
64
+ # state='fully_charged',
65
+ # stations=[
66
+ # ChargePointStation(
67
+ # id=12345678,
68
+ # name='CP HOME ',
69
+ # latitude=30.0000000,
70
+ # longitude=-90.0000000)
71
+ # ])
72
+
73
+ session = client.get_charging_session(charging.session_id)
74
+ print(session)
75
+ # ChargingSession(
76
+ # session_id=1234567890,
77
+ # start_time=datetime.datetime(2022, 1, 30, 13, 32, 45),
78
+ # device_id=12345678,
79
+ # device_name='CP HOME',
80
+ # charging_state='fully_charged',
81
+ # charging_time=2426000,
82
+ # energy_kwh=4.3404,
83
+ # miles_added=15.024461538461539,
84
+ # miles_added_per_hour=0.0,
85
+ # outlet_number=1,
86
+ # port_level=2,
87
+ # power_kw=0.0,
88
+ # purpose='PERSONAL',
89
+ # currency_iso_code='USD',
90
+ # payment_completed=True,
91
+ # payment_type='none',
92
+ # pricing_spec_id=0,
93
+ # total_amount=0.67,
94
+ # api_flag=False,
95
+ # enable_stop_charging=True,
96
+ # has_charging_receipt=False,
97
+ # has_utility_info=True,
98
+ # is_home_charger=True,
99
+ # is_purpose_finalized=True,
100
+ # last_update_data_timestamp=datetime.datetime(2022, 1, 30, 15, 12, 48),
101
+ # stop_charge_supported=True,
102
+ # company_id=12345,
103
+ # company_name='CP Home',
104
+ # latitude=30.0000000,
105
+ # longitude=-90.0000000,
106
+ # address='Home Charger',
107
+ # city='City',
108
+ # state_name='State',
109
+ # country='United States',
110
+ # zipcode='12345',
111
+ # update_data=[
112
+ # ChargingSessionUpdate(
113
+ # energy_kwh=0.0,
114
+ # power_kw=0.0002,
115
+ # timestamp=datetime.datetime(2022, 1, 30, 13, 32, 57)),
116
+ # ChargingSessionUpdate(
117
+ # energy_kwh=0.0001,
118
+ # power_kw=0.1568,
119
+ # timestamp=datetime.datetime(2022, 1, 30, 13, 33, 9)),
120
+ # ChargingSessionUpdate(
121
+ # energy_kwh=0.0025,
122
+ # power_kw=3.7337,
123
+ # timestamp=datetime.datetime(2022, 1, 30, 13, 33, 12)),
124
+ # ChargingSessionUpdate(
125
+ # energy_kwh=0.0161,
126
+ # power_kw=1.3854,
127
+ # timestamp=datetime.datetime(2022, 1, 30, 13, 33, 33)),
128
+ # ...],
129
+ # update_period=300000,
130
+ # utility=PowerUtility(
131
+ # id=0,
132
+ # name='Energy Company',
133
+ # plans=[
134
+ # PowerUtilityPlan(
135
+ # code='R',
136
+ # id=12345,
137
+ # is_ev_plan=False,
138
+ # name='Residential')
139
+ # ]))
140
+ ```
141
+
142
+ #### Starting or Stopping a Session
143
+
144
+ ```python
145
+ from python_chargepoint import ChargePoint
146
+
147
+ client = ChargePoint(username="user", password="password")
148
+ charging = client.get_user_charging_status()
149
+
150
+ if charging:
151
+ session = client.get_charging_session(charging.session_id)
152
+ session.stop()
153
+
154
+ # If you wanted to charge again, you can start a new session.
155
+ session = client.start_charging_session(session.device_id)
156
+ ```
157
+
158
+ You can also start a new session by providing any device ID you want to start charging on.
159
+
160
+ ```python
161
+ from python_chargepoint import ChargePoint
162
+
163
+ client = ChargePoint(username="user", password="password")
164
+ home_flex_id = client.get_home_chargers()[0]
165
+ home_flex = client.get_home_charger_status(home_flex_id)
166
+
167
+ if home_flex.charging_status == "AVAILABLE":
168
+ session = client.start_charging_session(home_flex_id)
169
+ ```
170
+
171
+ #### Setting the amperage limit
172
+
173
+ ```python
174
+ from python_chargepoint import ChargePoint
175
+
176
+ client = ChargePoint(username="user", password="password")
177
+ home_flex_id = client.get_home_chargers()[0]
178
+
179
+ # Print out valid amperage values.
180
+ print(client.get_home_charger_status(home_flex_id).possible_amperage_limits)
181
+ # [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
182
+
183
+ client.set_amperage_limit(home_flex_id, 23)
184
+ print(client.get_home_charger_status(home_flex_id).amperage_limit)
185
+ # 23
186
+ ```
@@ -1,23 +1,24 @@
1
1
  [tool.poetry]
2
2
  name = "python-chargepoint"
3
- version = "1.8.0"
3
+ version = "1.10.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"
7
7
  packages = [
8
8
  { include = "python_chargepoint" },
9
9
  ]
10
+ readme = "README.md"
10
11
 
11
12
  [tool.poetry.dependencies]
12
13
  python = "^3.9"
13
- requests = "^2.27.1"
14
+ requests = "^2.32.3"
14
15
 
15
- [tool.poetry.dev-dependencies]
16
+ [tool.poetry.group.dev.dependencies]
16
17
  tox = "^3.24.5"
17
- black = "^22.1.0"
18
+ black = "^25.1.0"
18
19
  pytest = "^6.2.5"
19
20
  responses = "^0.18.0"
20
- flake8 = "^4.0.1"
21
+ flake8 = "^7.2.0"
21
22
  pytest-mock = "^3.7.0"
22
23
  pytest-cov = "^3.0.0"
23
24
 
@@ -1,6 +1,7 @@
1
1
  from uuid import uuid4
2
2
  from typing import List, Optional
3
3
  from functools import wraps
4
+ from time import sleep
4
5
 
5
6
  from requests import Session, codes, post
6
7
 
@@ -357,6 +358,79 @@ class ChargePoint:
357
358
 
358
359
  return UserChargingStatus.from_json(status["user_status"])
359
360
 
361
+ @_require_login
362
+ def set_amperage_limit(
363
+ self, charger_id: int, amperage_limit: int, max_retry: int = 5
364
+ ) -> None:
365
+ _LOGGER.debug(f"Setting amperage limit for {charger_id} to {amperage_limit}")
366
+ request = {
367
+ "deviceData": self._device_data,
368
+ "chargeAmperageLimit": amperage_limit,
369
+ }
370
+ response = self._session.post(
371
+ f"{self._global_config.endpoints.internal_api}/driver/charger/{charger_id}/config/v1/charge-amperage-limit",
372
+ json=request,
373
+ )
374
+
375
+ if response.status_code != codes.ok:
376
+ _LOGGER.error(
377
+ "Failed to set amperage limit! status_code=%s err=%s",
378
+ response.status_code,
379
+ response.text,
380
+ )
381
+ raise ChargePointCommunicationException(
382
+ response=response, message="Failed to set amperage limit."
383
+ )
384
+ status = response.json()
385
+ # The API can return 200 but still have a failure status.
386
+ if status["status"] != "success":
387
+ message = status.get("message", "empty message")
388
+ _LOGGER.error(
389
+ "Failed to set amperage limit! status=%s err=%s",
390
+ status["status"],
391
+ message,
392
+ )
393
+ raise ChargePointCommunicationException(
394
+ response=response, message=f"Failed to set amperage limit: {message}"
395
+ )
396
+
397
+ # This is eventually consistent so we wait until the new limit is reflected.
398
+ for _ in range(1, max_retry): # pragma: no cover
399
+ charger_status = self.get_home_charger_status(charger_id)
400
+ if charger_status.amperage_limit == amperage_limit:
401
+ return
402
+ sleep(1)
403
+
404
+ raise ChargePointCommunicationException(
405
+ response=response,
406
+ message="New amperage limit did not persist to charger after retries",
407
+ )
408
+
409
+ @_require_login
410
+ def restart_home_charger(self, charger_id: int) -> None:
411
+ _LOGGER.debug("Sending restart command for panda: %s", charger_id)
412
+ restart = {
413
+ "user_id": self.user_id,
414
+ "restart_panda": {"device_id": charger_id, "mfhs": {}},
415
+ }
416
+ response = self._session.post(
417
+ f"{self._global_config.endpoints.webservices}mobileapi/v5", json=restart
418
+ )
419
+
420
+ if response.status_code != codes.ok:
421
+ _LOGGER.error(
422
+ "Failed to restart charger! status_code=%s err=%s",
423
+ response.status_code,
424
+ response.text,
425
+ )
426
+ raise ChargePointCommunicationException(
427
+ response=response, message="Failed to restart charger."
428
+ )
429
+
430
+ status = response.json()
431
+ _LOGGER.debug(status)
432
+ return
433
+
360
434
  @_require_login
361
435
  def get_charging_session(self, session_id: int) -> ChargingSession:
362
436
  return ChargingSession(session_id=session_id, client=self)
@@ -53,6 +53,7 @@ def _safe_get_endpoint(json: dict, endpoint_key: str) -> str:
53
53
  @dataclass
54
54
  class ChargePointEndpoints:
55
55
  accounts: str
56
+ internal_api: str
56
57
  mapcache: str
57
58
  panda_websocket: str
58
59
  payment_java: str
@@ -67,6 +68,7 @@ class ChargePointEndpoints:
67
68
  def from_json(cls, json: dict):
68
69
  return cls(
69
70
  accounts=_safe_get_endpoint(json, "accounts_endpoint"),
71
+ internal_api=_safe_get_endpoint(json, "internal_api_gateway_endpoint"),
70
72
  mapcache=_safe_get_endpoint(json, "mapcache_endpoint"),
71
73
  panda_websocket=_safe_get_endpoint(json, "panda_websocket_endpoint"),
72
74
  payment_java=_safe_get_endpoint(json, "payment_java_endpoint"),
@@ -102,6 +102,8 @@ class HomeChargerStatus:
102
102
  reminder_time: str
103
103
  model: str
104
104
  mac_address: str
105
+ amperage_limit: int
106
+ possible_amperage_limits: List[int]
105
107
 
106
108
  @classmethod
107
109
  def from_json(cls, charger_id: int, json: dict):
@@ -118,6 +120,12 @@ class HomeChargerStatus:
118
120
  reminder_time=json.get("plug_in_reminder_time", ""),
119
121
  model=json.get("model", ""),
120
122
  mac_address=json.get("mac_address", "00:00:00:00:00:00"),
123
+ amperage_limit=json.get("charge_amperage_setting", {}).get(
124
+ "charge_limit", 0
125
+ ),
126
+ possible_amperage_limits=json.get("charge_amperage_setting", {}).get(
127
+ "possible_charge_limit", 0
128
+ ),
121
129
  )
122
130
 
123
131
 
@@ -1,14 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: python-chargepoint
3
- Version: 1.8.0
4
- Summary: A simple, Pythonic wrapper for the ChargePoint API.
5
- License: MIT
6
- Author: Marc Billow
7
- Author-email: mbillow@users.noreply.github.compoetry
8
- Requires-Python: >=3.9,<4.0
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Requires-Dist: requests (>=2.27.1,<3.0.0)
@@ -1,30 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from setuptools import setup
3
-
4
- packages = \
5
- ['python_chargepoint']
6
-
7
- package_data = \
8
- {'': ['*']}
9
-
10
- install_requires = \
11
- ['requests>=2.27.1,<3.0.0']
12
-
13
- setup_kwargs = {
14
- 'name': 'python-chargepoint',
15
- 'version': '1.8.0',
16
- 'description': 'A simple, Pythonic wrapper for the ChargePoint API.',
17
- 'long_description': 'None',
18
- 'author': 'Marc Billow',
19
- 'author_email': 'mbillow@users.noreply.github.compoetry',
20
- 'maintainer': 'None',
21
- 'maintainer_email': 'None',
22
- 'url': 'None',
23
- 'packages': packages,
24
- 'package_data': package_data,
25
- 'install_requires': install_requires,
26
- 'python_requires': '>=3.9,<4.0',
27
- }
28
-
29
-
30
- setup(**setup_kwargs)