python-chargepoint 1.9.2__tar.gz → 2.0.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,366 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-chargepoint
3
+ Version: 2.0.0
4
+ Summary: A simple, Pythonic wrapper for the ChargePoint API.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Marc Billow
8
+ Author-email: mbillow@users.noreply.github.compoetry
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
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
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Dist: aiohttp (>=3.9,<4.0)
18
+ Requires-Dist: click (>=8.1,<9.0)
19
+ Requires-Dist: pydantic (>=2.12,<3.0)
20
+ Description-Content-Type: text/markdown
21
+
22
+ # python-chargepoint
23
+
24
+ A simple, async Pythonic wrapper around the ChargePoint EV Charging Network API.
25
+
26
+ ## Disclaimer
27
+
28
+ This project is not affiliated with or endorsed by ChargePoint in any way. Use at your own risk.
29
+ ChargePoint is a registered trademark of ChargePoint, Inc.
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install python-chargepoint
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Library Usage
42
+
43
+ All client methods are `async` and must be called from an async context.
44
+
45
+ ### Authentication
46
+
47
+ Three authentication methods are supported. The client is created via the async factory `ChargePoint.create()`.
48
+
49
+ **Password:**
50
+ ```python
51
+ import asyncio
52
+ from python_chargepoint import ChargePoint
53
+
54
+ async def main():
55
+ client = await ChargePoint.create(username="user@example.com")
56
+ await client.login_with_password("password")
57
+ # ...
58
+ await client.close()
59
+
60
+ asyncio.run(main())
61
+ ```
62
+
63
+ **Long-lived session token** (recommended for automation):
64
+ ```python
65
+ client = await ChargePoint.create(
66
+ username="user@example.com",
67
+ coulomb_token="<coulomb_sess cookie value>",
68
+ )
69
+ ```
70
+
71
+ **SSO JWT:**
72
+ ```python
73
+ client = await ChargePoint.create(username="user@example.com")
74
+ await client.login_with_sso_session("<sso jwt>")
75
+ # you can then grab client.coulomb_token and use the above pattern going forward
76
+ ```
77
+
78
+ ---
79
+
80
+ ### Account
81
+
82
+ ```python
83
+ acct = await client.get_account()
84
+ print(acct.user.full_name) # "Jane Smith"
85
+ print(acct.account_balance.amount) # "12.34"
86
+
87
+ evs = await client.get_vehicles()
88
+ for ev in evs:
89
+ print(f"{ev.year} {ev.make} {ev.model}") # "2023 Polestar 2"
90
+ print(f" AC: {ev.charging_speed} kW DC: {ev.dc_charging_speed} kW")
91
+ ```
92
+
93
+ ---
94
+
95
+ ### Home Charger
96
+
97
+ ```python
98
+ charger_ids = await client.get_home_chargers()
99
+ # [12345678]
100
+
101
+ charger_id = charger_ids[0]
102
+
103
+ status = await client.get_home_charger_status(charger_id)
104
+ # HomeChargerStatus(
105
+ # charger_id=12345678,
106
+ # brand='CP',
107
+ # model='HOME FLEX',
108
+ # charging_status='AVAILABLE',
109
+ # is_plugged_in=True,
110
+ # is_connected=True,
111
+ # amperage_limit=28,
112
+ # possible_amperage_limits=[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32])
113
+
114
+ tech = await client.get_home_charger_technical_info(charger_id)
115
+ # HomeChargerTechnicalInfo(
116
+ # model_number='CPH50-NEMA6-50-L23',
117
+ # serial_number='...',
118
+ # software_version='1.2.3.4',
119
+ # last_connected_at=datetime(...))
120
+
121
+ config = await client.get_home_charger_config(charger_id)
122
+ # HomeChargerConfiguration(
123
+ # station_nickname='Home Flex',
124
+ # led_brightness=LEDBrightness(level=5, supported_levels=[0,1,2,3,4,5]),
125
+ # utility=PowerUtility(name='Austin Energy', ...))
126
+ ```
127
+
128
+ #### Amperage limit
129
+
130
+ ```python
131
+ # Print valid amperage values
132
+ print(status.possible_amperage_limits)
133
+ # [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
134
+
135
+ await client.set_amperage_limit(charger_id, 24)
136
+ ```
137
+
138
+ #### LED brightness
139
+
140
+ Levels map to: `0`=off, `1`=20%, `2`=40%, `3`=60%, `4`=80%, `5`=100%.
141
+ Available levels are returned by `get_home_charger_config()`.
142
+
143
+ ```python
144
+ await client.set_led_brightness(charger_id, 3) # 60%
145
+ ```
146
+
147
+ #### Restart
148
+
149
+ ```python
150
+ await client.restart_home_charger(charger_id)
151
+ ```
152
+
153
+ ---
154
+
155
+ ### Charging Status and Sessions
156
+
157
+ ```python
158
+ status = await client.get_user_charging_status()
159
+ if status:
160
+ print(status.state) # "fully_charged"
161
+ print(status.session_id) # 1234567890
162
+
163
+ session = await client.get_charging_session(status.session_id)
164
+ print(session.charging_state) # "fully_charged"
165
+ print(session.energy_kwh) # 6.42
166
+ print(session.miles_added) # 22.3
167
+ ```
168
+
169
+ #### Starting and stopping a session
170
+
171
+ ```python
172
+ # Stop the current session
173
+ session = await client.get_charging_session(status.session_id)
174
+ await session.stop()
175
+
176
+ # Start a new session on any device
177
+ new_session = await client.start_charging_session(device_id=charger_id)
178
+ print(new_session.session_id)
179
+ ```
180
+
181
+ ---
182
+
183
+ ### Station Info
184
+
185
+ Fetch detailed information about any station by device ID — ports, pricing, connector types, and real-time status.
186
+
187
+ ```python
188
+ info = await client.get_station(device_id=13055991)
189
+ print(f"{' / '.join(info.name)}") # "DOMAIN TOWER 2 / LVL 2_STATION 2"
190
+ print(info.address.address1) # "10025 Alterra Pkwy"
191
+ print(info.station_status_v2) # "available"
192
+ print(info.ports_info.port_count) # 2
193
+
194
+ for port in info.ports_info.ports:
195
+ print(f"Port {port.outlet_number}: {port.status_v2} ({port.level})")
196
+ for c in port.connector_list:
197
+ print(f" {c.display_plug_type}: {c.status_v2}")
198
+
199
+ if info.station_price:
200
+ for tou in info.station_price.tou_fees:
201
+ print(f"Rate: {tou.fee.amount} {info.station_price.currency_code}/{tou.fee.unit}")
202
+ ```
203
+
204
+ ---
205
+
206
+ ### Nearby Stations
207
+
208
+ Fetch all charging stations visible within a geographic bounding box. Results include
209
+ both public stations and the user's home charger (if it falls within the bounds).
210
+
211
+ ```python
212
+ from python_chargepoint.types import MapFilter, ZoomBounds
213
+
214
+ bounds = ZoomBounds(sw_lat=30.37, sw_lon=-97.66, ne_lat=30.40, ne_lon=-97.64)
215
+
216
+ # No filter — return all stations
217
+ stations = await client.get_nearby_stations(bounds)
218
+
219
+ # Optional: filter by connector type or status
220
+ f = MapFilter(connector_l2=True, connector_combo=True, status_available=True)
221
+ stations = await client.get_nearby_stations(bounds, station_filter=f)
222
+
223
+ for s in stations:
224
+ print(f"{s.name1} — {s.station_status_v2}")
225
+ if s.is_home and s.charging_info:
226
+ print(f" Charging: {s.charging_info.current_charging}")
227
+ ```
228
+
229
+ **`MapFilter` fields** (all `bool`, default `False`):
230
+
231
+ | Field | Description |
232
+ |---|---|
233
+ | `connector_l2` | Level 2 AC |
234
+ | `connector_combo` | CCS combo (DC) |
235
+ | `connector_chademo` | CHAdeMO (DC) |
236
+ | `connector_tesla` | Tesla proprietary |
237
+ | `connector_l1` | Level 1 AC |
238
+ | `connector_l2_tesla` | Tesla Level 2 |
239
+ | `connector_l2_nema_1450` | NEMA 14-50 |
240
+ | `dc_fast_charging` | Any DC fast charger |
241
+ | `status_available` | Only available stations |
242
+ | `price_free` | Only free stations |
243
+ | `van_accessible` | Van-accessible spaces |
244
+ | `disabled_parking` | Disability-accessible parking |
245
+ | `network_chargepoint` | ChargePoint network |
246
+ | `network_blink` | Blink network |
247
+ | `network_evgo` | EVgo network |
248
+ | `network_flo` | FLO network |
249
+ | `network_ionna` | IONNA network |
250
+ | `network_evconnect` | EV Connect |
251
+ | `network_evgateway` | EV Gateway |
252
+ | `network_bchydro` | BC Hydro |
253
+ | `network_greenlots` | Greenlots |
254
+ | `network_mercedes` | Mercedes-Benz |
255
+ | `network_circuitelectric` | Circuit Électrique |
256
+
257
+ ---
258
+
259
+ ## CLI
260
+
261
+ After installation, a `chargepoint` command is available.
262
+
263
+ ### Authentication
264
+
265
+ Credentials are read from environment variables. The CLI falls back to prompting for a password if no token is set.
266
+
267
+ ```bash
268
+ export CP_USERNAME="user@example.com"
269
+ export CP_COULOMB_TOKEN="<coulomb_sess cookie value>"
270
+ # or
271
+ export CP_SSO_JWT="<sso jwt>"
272
+ ```
273
+
274
+ ### Global options
275
+
276
+ ```
277
+ chargepoint [--debug] [--json] <command>
278
+ ```
279
+
280
+ `--json` dumps the raw API response as JSON — useful for scripting.
281
+
282
+ ### Commands
283
+
284
+ ```bash
285
+ # Account
286
+ chargepoint account
287
+ chargepoint vehicles
288
+
289
+ # Charging status
290
+ chargepoint charging-status
291
+
292
+ # Station info
293
+ chargepoint station <device_id>
294
+
295
+ # Nearby stations
296
+ chargepoint nearby --sw-lat 30.37 --sw-lon -97.66 --ne-lat 30.40 --ne-lon -97.64 \
297
+ [--connector-l2] [--connector-combo] [--connector-chademo] [--connector-tesla] \
298
+ [--dc-fast] [--available-only] [--free-only]
299
+
300
+ # Home charger
301
+ chargepoint charger list
302
+ chargepoint charger status <charger_id>
303
+ chargepoint charger tech-info <charger_id>
304
+ chargepoint charger config <charger_id>
305
+ chargepoint charger set-amperage <charger_id> <amps>
306
+ chargepoint charger set-led <charger_id> <level> # 0=off 1=20% 2=40% 3=60% 4=80% 5=100%
307
+ chargepoint charger restart <charger_id>
308
+
309
+ # Sessions
310
+ chargepoint session get <session_id>
311
+ chargepoint session start <device_id>
312
+ chargepoint session stop <session_id>
313
+ ```
314
+
315
+ Use `--help` on any command or subgroup for details:
316
+
317
+ ```bash
318
+ chargepoint nearby --help
319
+ chargepoint charger --help
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Development
325
+
326
+ ### Setup
327
+
328
+ ```bash
329
+ git clone https://github.com/mbillow/python-chargepoint.git
330
+ cd python-chargepoint
331
+ poetry install
332
+ poetry run pre-commit install
333
+ ```
334
+
335
+ ### Checks
336
+
337
+ The following checks run automatically on every commit via pre-commit, and are also enforced in CI:
338
+
339
+ | Tool | Purpose |
340
+ |---|---|
341
+ | `black` | Code formatting |
342
+ | `flake8` | Style and error linting |
343
+ | `mypy` | Static type checking |
344
+ | `pyright` | Pylance-compatible type checking |
345
+
346
+ To run them manually:
347
+
348
+ ```bash
349
+ poetry run pre-commit run --all-files
350
+ ```
351
+
352
+ Or individually:
353
+
354
+ ```bash
355
+ poetry run black --check python_chargepoint/ tests/
356
+ poetry run flake8 python_chargepoint/ tests/
357
+ poetry run mypy python_chargepoint/
358
+ poetry run pyright python_chargepoint/
359
+ ```
360
+
361
+ ### Tests
362
+
363
+ ```bash
364
+ poetry run pytest
365
+ ```
366
+
@@ -0,0 +1,344 @@
1
+ # python-chargepoint
2
+
3
+ A simple, async Pythonic wrapper around the ChargePoint EV Charging Network API.
4
+
5
+ ## Disclaimer
6
+
7
+ This project is not affiliated with or endorsed by ChargePoint in any way. Use at your own risk.
8
+ ChargePoint is a registered trademark of ChargePoint, Inc.
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pip install python-chargepoint
16
+ ```
17
+
18
+ ---
19
+
20
+ ## Library Usage
21
+
22
+ All client methods are `async` and must be called from an async context.
23
+
24
+ ### Authentication
25
+
26
+ Three authentication methods are supported. The client is created via the async factory `ChargePoint.create()`.
27
+
28
+ **Password:**
29
+ ```python
30
+ import asyncio
31
+ from python_chargepoint import ChargePoint
32
+
33
+ async def main():
34
+ client = await ChargePoint.create(username="user@example.com")
35
+ await client.login_with_password("password")
36
+ # ...
37
+ await client.close()
38
+
39
+ asyncio.run(main())
40
+ ```
41
+
42
+ **Long-lived session token** (recommended for automation):
43
+ ```python
44
+ client = await ChargePoint.create(
45
+ username="user@example.com",
46
+ coulomb_token="<coulomb_sess cookie value>",
47
+ )
48
+ ```
49
+
50
+ **SSO JWT:**
51
+ ```python
52
+ client = await ChargePoint.create(username="user@example.com")
53
+ await client.login_with_sso_session("<sso jwt>")
54
+ # you can then grab client.coulomb_token and use the above pattern going forward
55
+ ```
56
+
57
+ ---
58
+
59
+ ### Account
60
+
61
+ ```python
62
+ acct = await client.get_account()
63
+ print(acct.user.full_name) # "Jane Smith"
64
+ print(acct.account_balance.amount) # "12.34"
65
+
66
+ evs = await client.get_vehicles()
67
+ for ev in evs:
68
+ print(f"{ev.year} {ev.make} {ev.model}") # "2023 Polestar 2"
69
+ print(f" AC: {ev.charging_speed} kW DC: {ev.dc_charging_speed} kW")
70
+ ```
71
+
72
+ ---
73
+
74
+ ### Home Charger
75
+
76
+ ```python
77
+ charger_ids = await client.get_home_chargers()
78
+ # [12345678]
79
+
80
+ charger_id = charger_ids[0]
81
+
82
+ status = await client.get_home_charger_status(charger_id)
83
+ # HomeChargerStatus(
84
+ # charger_id=12345678,
85
+ # brand='CP',
86
+ # model='HOME FLEX',
87
+ # charging_status='AVAILABLE',
88
+ # is_plugged_in=True,
89
+ # is_connected=True,
90
+ # amperage_limit=28,
91
+ # possible_amperage_limits=[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32])
92
+
93
+ tech = await client.get_home_charger_technical_info(charger_id)
94
+ # HomeChargerTechnicalInfo(
95
+ # model_number='CPH50-NEMA6-50-L23',
96
+ # serial_number='...',
97
+ # software_version='1.2.3.4',
98
+ # last_connected_at=datetime(...))
99
+
100
+ config = await client.get_home_charger_config(charger_id)
101
+ # HomeChargerConfiguration(
102
+ # station_nickname='Home Flex',
103
+ # led_brightness=LEDBrightness(level=5, supported_levels=[0,1,2,3,4,5]),
104
+ # utility=PowerUtility(name='Austin Energy', ...))
105
+ ```
106
+
107
+ #### Amperage limit
108
+
109
+ ```python
110
+ # Print valid amperage values
111
+ print(status.possible_amperage_limits)
112
+ # [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
113
+
114
+ await client.set_amperage_limit(charger_id, 24)
115
+ ```
116
+
117
+ #### LED brightness
118
+
119
+ Levels map to: `0`=off, `1`=20%, `2`=40%, `3`=60%, `4`=80%, `5`=100%.
120
+ Available levels are returned by `get_home_charger_config()`.
121
+
122
+ ```python
123
+ await client.set_led_brightness(charger_id, 3) # 60%
124
+ ```
125
+
126
+ #### Restart
127
+
128
+ ```python
129
+ await client.restart_home_charger(charger_id)
130
+ ```
131
+
132
+ ---
133
+
134
+ ### Charging Status and Sessions
135
+
136
+ ```python
137
+ status = await client.get_user_charging_status()
138
+ if status:
139
+ print(status.state) # "fully_charged"
140
+ print(status.session_id) # 1234567890
141
+
142
+ session = await client.get_charging_session(status.session_id)
143
+ print(session.charging_state) # "fully_charged"
144
+ print(session.energy_kwh) # 6.42
145
+ print(session.miles_added) # 22.3
146
+ ```
147
+
148
+ #### Starting and stopping a session
149
+
150
+ ```python
151
+ # Stop the current session
152
+ session = await client.get_charging_session(status.session_id)
153
+ await session.stop()
154
+
155
+ # Start a new session on any device
156
+ new_session = await client.start_charging_session(device_id=charger_id)
157
+ print(new_session.session_id)
158
+ ```
159
+
160
+ ---
161
+
162
+ ### Station Info
163
+
164
+ Fetch detailed information about any station by device ID — ports, pricing, connector types, and real-time status.
165
+
166
+ ```python
167
+ info = await client.get_station(device_id=13055991)
168
+ print(f"{' / '.join(info.name)}") # "DOMAIN TOWER 2 / LVL 2_STATION 2"
169
+ print(info.address.address1) # "10025 Alterra Pkwy"
170
+ print(info.station_status_v2) # "available"
171
+ print(info.ports_info.port_count) # 2
172
+
173
+ for port in info.ports_info.ports:
174
+ print(f"Port {port.outlet_number}: {port.status_v2} ({port.level})")
175
+ for c in port.connector_list:
176
+ print(f" {c.display_plug_type}: {c.status_v2}")
177
+
178
+ if info.station_price:
179
+ for tou in info.station_price.tou_fees:
180
+ print(f"Rate: {tou.fee.amount} {info.station_price.currency_code}/{tou.fee.unit}")
181
+ ```
182
+
183
+ ---
184
+
185
+ ### Nearby Stations
186
+
187
+ Fetch all charging stations visible within a geographic bounding box. Results include
188
+ both public stations and the user's home charger (if it falls within the bounds).
189
+
190
+ ```python
191
+ from python_chargepoint.types import MapFilter, ZoomBounds
192
+
193
+ bounds = ZoomBounds(sw_lat=30.37, sw_lon=-97.66, ne_lat=30.40, ne_lon=-97.64)
194
+
195
+ # No filter — return all stations
196
+ stations = await client.get_nearby_stations(bounds)
197
+
198
+ # Optional: filter by connector type or status
199
+ f = MapFilter(connector_l2=True, connector_combo=True, status_available=True)
200
+ stations = await client.get_nearby_stations(bounds, station_filter=f)
201
+
202
+ for s in stations:
203
+ print(f"{s.name1} — {s.station_status_v2}")
204
+ if s.is_home and s.charging_info:
205
+ print(f" Charging: {s.charging_info.current_charging}")
206
+ ```
207
+
208
+ **`MapFilter` fields** (all `bool`, default `False`):
209
+
210
+ | Field | Description |
211
+ |---|---|
212
+ | `connector_l2` | Level 2 AC |
213
+ | `connector_combo` | CCS combo (DC) |
214
+ | `connector_chademo` | CHAdeMO (DC) |
215
+ | `connector_tesla` | Tesla proprietary |
216
+ | `connector_l1` | Level 1 AC |
217
+ | `connector_l2_tesla` | Tesla Level 2 |
218
+ | `connector_l2_nema_1450` | NEMA 14-50 |
219
+ | `dc_fast_charging` | Any DC fast charger |
220
+ | `status_available` | Only available stations |
221
+ | `price_free` | Only free stations |
222
+ | `van_accessible` | Van-accessible spaces |
223
+ | `disabled_parking` | Disability-accessible parking |
224
+ | `network_chargepoint` | ChargePoint network |
225
+ | `network_blink` | Blink network |
226
+ | `network_evgo` | EVgo network |
227
+ | `network_flo` | FLO network |
228
+ | `network_ionna` | IONNA network |
229
+ | `network_evconnect` | EV Connect |
230
+ | `network_evgateway` | EV Gateway |
231
+ | `network_bchydro` | BC Hydro |
232
+ | `network_greenlots` | Greenlots |
233
+ | `network_mercedes` | Mercedes-Benz |
234
+ | `network_circuitelectric` | Circuit Électrique |
235
+
236
+ ---
237
+
238
+ ## CLI
239
+
240
+ After installation, a `chargepoint` command is available.
241
+
242
+ ### Authentication
243
+
244
+ Credentials are read from environment variables. The CLI falls back to prompting for a password if no token is set.
245
+
246
+ ```bash
247
+ export CP_USERNAME="user@example.com"
248
+ export CP_COULOMB_TOKEN="<coulomb_sess cookie value>"
249
+ # or
250
+ export CP_SSO_JWT="<sso jwt>"
251
+ ```
252
+
253
+ ### Global options
254
+
255
+ ```
256
+ chargepoint [--debug] [--json] <command>
257
+ ```
258
+
259
+ `--json` dumps the raw API response as JSON — useful for scripting.
260
+
261
+ ### Commands
262
+
263
+ ```bash
264
+ # Account
265
+ chargepoint account
266
+ chargepoint vehicles
267
+
268
+ # Charging status
269
+ chargepoint charging-status
270
+
271
+ # Station info
272
+ chargepoint station <device_id>
273
+
274
+ # Nearby stations
275
+ chargepoint nearby --sw-lat 30.37 --sw-lon -97.66 --ne-lat 30.40 --ne-lon -97.64 \
276
+ [--connector-l2] [--connector-combo] [--connector-chademo] [--connector-tesla] \
277
+ [--dc-fast] [--available-only] [--free-only]
278
+
279
+ # Home charger
280
+ chargepoint charger list
281
+ chargepoint charger status <charger_id>
282
+ chargepoint charger tech-info <charger_id>
283
+ chargepoint charger config <charger_id>
284
+ chargepoint charger set-amperage <charger_id> <amps>
285
+ chargepoint charger set-led <charger_id> <level> # 0=off 1=20% 2=40% 3=60% 4=80% 5=100%
286
+ chargepoint charger restart <charger_id>
287
+
288
+ # Sessions
289
+ chargepoint session get <session_id>
290
+ chargepoint session start <device_id>
291
+ chargepoint session stop <session_id>
292
+ ```
293
+
294
+ Use `--help` on any command or subgroup for details:
295
+
296
+ ```bash
297
+ chargepoint nearby --help
298
+ chargepoint charger --help
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Development
304
+
305
+ ### Setup
306
+
307
+ ```bash
308
+ git clone https://github.com/mbillow/python-chargepoint.git
309
+ cd python-chargepoint
310
+ poetry install
311
+ poetry run pre-commit install
312
+ ```
313
+
314
+ ### Checks
315
+
316
+ The following checks run automatically on every commit via pre-commit, and are also enforced in CI:
317
+
318
+ | Tool | Purpose |
319
+ |---|---|
320
+ | `black` | Code formatting |
321
+ | `flake8` | Style and error linting |
322
+ | `mypy` | Static type checking |
323
+ | `pyright` | Pylance-compatible type checking |
324
+
325
+ To run them manually:
326
+
327
+ ```bash
328
+ poetry run pre-commit run --all-files
329
+ ```
330
+
331
+ Or individually:
332
+
333
+ ```bash
334
+ poetry run black --check python_chargepoint/ tests/
335
+ poetry run flake8 python_chargepoint/ tests/
336
+ poetry run mypy python_chargepoint/
337
+ poetry run pyright python_chargepoint/
338
+ ```
339
+
340
+ ### Tests
341
+
342
+ ```bash
343
+ poetry run pytest
344
+ ```