pybluecurrent 0.0.1__tar.gz → 0.0.3__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.
Files changed (28) hide show
  1. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/.github/workflows/publish.yaml +1 -1
  2. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/.github/workflows/test.yaml +1 -1
  3. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/.gitignore +3 -0
  4. pybluecurrent-0.0.3/PKG-INFO +404 -0
  5. pybluecurrent-0.0.3/README.md +378 -0
  6. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/pyproject.toml +4 -1
  7. pybluecurrent-0.0.3/src/pybluecurrent/_version.py +16 -0
  8. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/src/pybluecurrent/client.py +76 -29
  9. pybluecurrent-0.0.3/src/pybluecurrent/utilities.py +36 -0
  10. pybluecurrent-0.0.3/src/pybluecurrent.egg-info/PKG-INFO +404 -0
  11. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/src/pybluecurrent.egg-info/SOURCES.txt +3 -1
  12. pybluecurrent-0.0.3/src/pybluecurrent.egg-info/requires.txt +10 -0
  13. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/tests/test_client.py +48 -5
  14. pybluecurrent-0.0.3/tests/test_utilities.py +41 -0
  15. pybluecurrent-0.0.1/PKG-INFO +0 -21
  16. pybluecurrent-0.0.1/README.md +0 -3
  17. pybluecurrent-0.0.1/src/pybluecurrent/_version.py +0 -4
  18. pybluecurrent-0.0.1/src/pybluecurrent.egg-info/PKG-INFO +0 -21
  19. pybluecurrent-0.0.1/src/pybluecurrent.egg-info/requires.txt +0 -6
  20. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/.pre-commit-config.yaml +0 -0
  21. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/LICENSE +0 -0
  22. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/setup.cfg +0 -0
  23. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/src/pybluecurrent/__init__.py +0 -0
  24. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/src/pybluecurrent/exceptions.py +0 -0
  25. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/src/pybluecurrent/py.typed +0 -0
  26. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/src/pybluecurrent.egg-info/dependency_links.txt +0 -0
  27. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/src/pybluecurrent.egg-info/top_level.txt +0 -0
  28. {pybluecurrent-0.0.1 → pybluecurrent-0.0.3}/tests/conftest.py +0 -0
@@ -16,7 +16,7 @@ jobs:
16
16
  - uses: actions/checkout@v3
17
17
 
18
18
  - name: Set up Python
19
- uses: actions/setup-python@v3
19
+ uses: actions/setup-python@v4
20
20
  with:
21
21
  python-version: "3.x"
22
22
 
@@ -16,7 +16,7 @@ jobs:
16
16
  - uses: actions/checkout@v3
17
17
 
18
18
  - name: Set up Python
19
- uses: actions/setup-python@v3
19
+ uses: actions/setup-python@v4
20
20
  with:
21
21
  python-version: "3.x"
22
22
 
@@ -128,5 +128,8 @@ dmypy.json
128
128
  # Pyre type checker
129
129
  .pyre/
130
130
 
131
+ # Ruff
132
+ .ruff_cache
133
+
131
134
  # Setuptools SCM version
132
135
  _version.py
@@ -0,0 +1,404 @@
1
+ Metadata-Version: 2.1
2
+ Name: pybluecurrent
3
+ Version: 0.0.3
4
+ Summary: Python client for BlueCurrent charge points.
5
+ Author-email: Rogier van der Geer <rogier@vander-geer.nl>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/rogiervandergeer/pybluecurrent
8
+ Keywords: api,blue current,electric vehicle,ev
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: asyncio-multisubscriber-queue>=0.4.1
18
+ Requires-Dist: requests>=2.31.0
19
+ Requires-Dist: sjcl>=0.2.1
20
+ Requires-Dist: websockets>=11.0.3
21
+ Provides-Extra: dev
22
+ Requires-Dist: black==23.3.0; extra == "dev"
23
+ Requires-Dist: pre-commit>=3.3.3; extra == "dev"
24
+ Requires-Dist: pytest==8.1.2; extra == "dev"
25
+ Requires-Dist: pytest-asyncio==0.21.1; extra == "dev"
26
+
27
+ # pybluecurrent
28
+
29
+ Python client for [BlueCurrent](https://www.bluecurrent.nl) charge points.
30
+
31
+ ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/rogiervandergeer/pybluecurrent/test.yaml)
32
+ ![PyPI](https://img.shields.io/pypi/v/pybluecurrent)
33
+ ![PyPI - License](https://img.shields.io/pypi/l/pybluecurrent)
34
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/pybluecurrent)
35
+
36
+ ## Usage
37
+
38
+ Using the client is as simple as:
39
+ ```python
40
+ from pybluecurrent import BlueCurrentClient
41
+
42
+ client = BlueCurrentClient("your_username", "your_secret_password")
43
+
44
+ async with client:
45
+ charge_points = await client.get_charge_points()
46
+
47
+ transactions = client.get_transactions(charge_points[0]["evse_id"])
48
+ ```
49
+
50
+ ## Methods
51
+
52
+ BlueCurrent exposes two APIs, a synchronous REST API as well as an asynchronous Websocket API.
53
+ As a result, the `BlueCurrentClient` also exposes synchronous as well as asynchronous methods:
54
+
55
+ - [Asynchronous](#asynchronous)
56
+ - [`get_account`](#getaccount---get-your-account-information)
57
+ - [`get_charge_cards`](#getchargecards---get-your-charge-cards)
58
+ - [`get_charge_points`](#getchargepoints---get-your-charge-points)
59
+ - [`get_charge_point_settings`](#getchargepointsettings---get-the-settings-of-a-charge-point)
60
+ - [`get_grid_status`](#getgridstatus---get-the-grid-status-associated-to-a-charge-point)
61
+ - [`get_sustainability_status`](#getsustainabilitystatus---get-statistics-on-the-sustainability-of-all-your-charge-points)
62
+ - [`set_plug_and_charge_charge_card`](#setplugandchargechargecard---set-the-charge-card-for-plug-and-charge)
63
+ - [`set_status`](#setstatus---enable-or-disable-a-charge-point)
64
+ - [Synchronous](#synchronous)
65
+ - [`login`](#login---log-in)
66
+ - [`get_charge_point_status`](#getchargepointstatus---get-the-status-of-a-charge-point)
67
+ - [`get_contracts`](#getcontracts---get-your-contracts)
68
+ - [`get_grids`](#getgrids---get-your-grid-connections)
69
+ - [`get_transactions`](#gettransactions---get-a-list-of-transactions)
70
+ - [`iterate_transactions`](#iteratetransactions---iterate-through-your-transactions)
71
+
72
+ ### Asynchronous
73
+
74
+ The async methods can only be used when the websocket client is connected. For example:
75
+ ```python
76
+ client = BlueCurrentClient("your_username", "your_secret_password")
77
+ async with client:
78
+ result = await client.get_account()
79
+ ```
80
+ Entering the async context will automatically login.
81
+
82
+ #### `get_account` - Get your account information.
83
+
84
+ ```python
85
+ async def get_account(self) -> dict[str, bool | str]
86
+ ```
87
+
88
+ ##### Returns
89
+ A dictionary describing your account:
90
+
91
+ ```python
92
+ {
93
+ "full_name": "Your Full Name",
94
+ "email": "your@email.address",
95
+ "login": "your@email.address",
96
+ "should_reset_password": False,
97
+ "developer_mode_enabled": False,
98
+ "tel": "",
99
+ "marketing_target": "bluecurrent",
100
+ "first_login_app": date(2020, 1, 1),
101
+ "hubspot_user_identity": "a_very_long_string"
102
+ }
103
+ ```
104
+
105
+ #### `get_charge_cards` - Get your charge cards.
106
+
107
+ ```python
108
+ async def get_charge_cards(self) -> list[dict[str, date | int | str | None]]
109
+ ```
110
+
111
+ ##### Returns
112
+
113
+ A list of dictionaries, each representing a charge card:
114
+ ```python
115
+ {
116
+ "uid": "A1B2C3D4E5F6",
117
+ "id": "NL-ABC-123456-0",
118
+ "name": "My Charge Cards",
119
+ "customer_name": "Your Name",
120
+ "valid": 1,
121
+ "date_created": date(2023, 6, 27),
122
+ "date_modified": date(2023, 7, 11),
123
+ "date_became_invalid": None
124
+ }
125
+ ```
126
+
127
+ #### `get_charge_points` - Get your charge points.
128
+
129
+ ```python
130
+ async def get_charge_points(self) -> list[dict[str, bool | dict | str]]
131
+ ```
132
+
133
+ ##### Returns
134
+
135
+ A list of dictionaries, each representing a charge card:
136
+ ```python
137
+ {
138
+ "evse_id": "BCU123456",
139
+ "name": "",
140
+ "model_type": "H:MOVE-C32T2",
141
+ "chargepoint_type": "HIDDEN",
142
+ "is_cable": True,
143
+ "public_charging": {"value": False, "permission": "write"},
144
+ "default_card": {"uid": "A1B2C3D4E5F6", "id": "NL-ABC-123456-0", "name": "Your Card",
145
+ "customer_name": "Your Name", "valid": 1},
146
+ "preferred_card": {"uid": "A1B2C3D4E5F6", "id": "NL-ABC-123456-0", "name": "Your Card",
147
+ "customer_name": "Your Name", "valid": 1},
148
+ "plug_and_charge_card": {"uid": "A1B2C3D4E5F6", "id": "NL-ABC-123456-0", "name": "Your Card",
149
+ "customer_name": "Your Name", "valid": 1},
150
+ "tariff": {"tariff_id","NLBCUT58", "price_ex_vat": 0.2, "start_price_ex_vat": 0, "price_in_vat": 0.242,
151
+ "start_price_in_vat": 0, "currency": "EUR", "vat_percentage": 21},
152
+ "plug_and_charge_notification": False,
153
+ "plug_and_charge": {"value": True, "permission": "write"},
154
+ "led_interaction": {"value": False, "permission": "read"},
155
+ "publish_location": {"value": False, "permission": "write"},
156
+ "smart_charging": True,
157
+ "smart_charging_dynamic": True,
158
+ "activity": "available",
159
+ "location": {"x_coord": 50.1234, "y_coord": 5.01234, "street": "Europalaan", "housenumber": "100",
160
+ "zipcode": "3526KS", "city": "Utrecht", "country": "NL"},
161
+ "delayed_charging": {"value": False, "permission": "none"}
162
+ }
163
+ ```
164
+
165
+ #### `get_charge_point_settings` - Get the settings of a charge point.
166
+
167
+ All the information returned by this endpoint is already included
168
+ in the response of [`get_charge_points`](#getchargepoints---get-your-charge-points).
169
+
170
+ ```python
171
+ async def get_charge_point_settings(self, evse_id: str) -> dict[str, bool | dict[str, Any] | str]
172
+ ```
173
+
174
+ ##### Arguments
175
+ - `evse_id`: The ID of the charge point.
176
+
177
+ ##### Returns
178
+ A dictionary describing the settings:
179
+ ```python
180
+ {
181
+ "evse_id": "BCU123456",
182
+ "plug_and_charge": {"value": True, "permission": "write"},
183
+ "public_charging": {"value": False, "permission": "write"},
184
+ "default_card": {"uid": "A1B2C3D4E5F6", "id": "NL-ABC-123456-0", "name": "Your Card",
185
+ "customer_name": "Your Name", "valid": 1},
186
+ "preferred_card": {"uid": "A1B2C3D4E5F6", "id": "NL-ABC-123456-0", "name": "Your Card",
187
+ "customer_name": "Your Name", "valid": 1},
188
+ "plug_and_charge_card": {"uid": "A1B2C3D4E5F6", "id": "NL-ABC-123456-0", "name": "Your Card",
189
+ "customer_name": "Your Name", "valid": 1},
190
+ "smart_charging": True,
191
+ "smart_charging_dynamic": True,
192
+ "model_type": "H:MOVE-C32T2",
193
+ "is_cable": True,
194
+ "chargepoint_type": "HIDDEN",
195
+ "plug_and_charge_notification": False,
196
+ "led_intensity": {"value": 0, "permission": "none"},
197
+ "led_interaction": {"value": False, "permission": "none"}
198
+ }
199
+ ```
200
+
201
+ #### `get_grid_status` - Get the grid status associated to a charge point.
202
+
203
+ ```python
204
+ async def get_grid_status(self, evse_id: str) -> dict[str, int | str]
205
+ ```
206
+
207
+ ##### Arguments
208
+ - `evse_id`: The ID of the charge point.
209
+
210
+ ##### Returns
211
+ A dictionary describing the actual grid current and maximum current in amps:
212
+ ```python
213
+ {
214
+ "id": "GRID-BCU123456",
215
+ "grid_actual_p1": 1,
216
+ "grid_actual_p2": 2,
217
+ "grid_actual_p3": 3,
218
+ "grid_max_install": 25,
219
+ "grid_max_reserved": 25
220
+ }
221
+ ```
222
+
223
+ #### `get_sustainability_status` - Get statistics on the sustainability of all your charge points.
224
+
225
+ ```python
226
+ async def get_sustainability_status(self) -> dict[str, float | int]
227
+ ```
228
+
229
+ ##### Returns
230
+ A dictionary with two keys: `{"trees": 1, "co2": 12.345}`
231
+
232
+ #### `set_plug_and_charge_charge_card` - Set the charge card for plug-and-charge
233
+
234
+ ```python
235
+ async def set_plug_and_charge_charge_card(self, evse_id: str, uid: str | None = None) -> None
236
+ ```
237
+
238
+ Sets the plug-and-charge card for the charge point. The uid must be a `uid` of one of your charge cards (see [`get_charge_cards`](#getchargecards---get-your-charge-cards)) or `None` to use no charge card.
239
+
240
+ #### `set_status` - Enable or disable a charge point.
241
+
242
+ ```python
243
+ async def set_status(self, evse_id: str, enabled: bool) -> None
244
+ ```
245
+
246
+ ##### Arguments
247
+ - `evse_id`: The ID of the charge point.
248
+ - `enabled`: Boolean that indicates the desired status.
249
+
250
+ ### Synchronous
251
+
252
+ #### `login` - Log in
253
+ ```python
254
+ def login(self) -> None
255
+ ```
256
+
257
+ This method does not do anything if the client is already logged in.
258
+ Connection to the websocket api (async with client) automatically logs in the client,
259
+ so this endpoint is not needed when using the async API.
260
+
261
+
262
+ #### `get_charge_point_status` - Get the status of a charge point.
263
+
264
+ ```python
265
+ def get_charge_point_status(self, evse_id: str) -> dict[str, datetime | float | int | str | None]
266
+ ```
267
+
268
+ ##### Arguments
269
+ - `evse_id`: The ID of the charge point.
270
+
271
+ ##### Returns
272
+ A dictionary with the chargepoint status:
273
+ ```python
274
+ {
275
+ "actual_p1": 0,
276
+ "actual_p2": 0,
277
+ "actual_p3": 0,
278
+ "activity": "available",
279
+ "actual_v1": 0,
280
+ "actual_v2": 0,
281
+ "actual_v3": 0,
282
+ "actual_kwh": 0,
283
+ "max_usage": 20,
284
+ "smartcharging_max_usage": 6,
285
+ "max_offline": 10,
286
+ "offline_since": "",
287
+ "start_datetime": datetime(2023, 7, 24, 15, 25, 33),
288
+ "stop_datetime": datetime(2023, 7, 26, 7, 48, 40),
289
+ "total_cost": 9.93,
290
+ "vehicle_status": "A",
291
+ "evse_id": "BCU123456",
292
+ }
293
+ ```
294
+
295
+ #### `get_contracts` - Get your contracts.
296
+
297
+ ```python
298
+ def get_contracts(self) -> list[dict[str, str]]
299
+ ```
300
+
301
+ ##### Returns
302
+ A list of dictionaries, each representing a contract:
303
+ ```python
304
+ [
305
+ {
306
+ "contract_id": "BCU12345678",
307
+ "contact_email": "your@email.address",
308
+ "subscription_type": "BASIS",
309
+ "beneficiary_name": "Your Name",
310
+ "iban_beneficiary": "NL00ABCD0123456789"
311
+ }
312
+ ]
313
+ ```
314
+
315
+ #### `get_grids` - Get your grid connections.
316
+
317
+ ```python
318
+ def get_grids(self) -> list[dict[str, bool | dict[str, str] | str]]
319
+ ```
320
+
321
+ ##### Returns
322
+ A list of dictionaries, each representing a grid:
323
+ ```python
324
+ [
325
+ {
326
+ "address": {"street": "Street Name", "housenumber": "1", "postal_code": "1234AB",
327
+ "city": "Amsterdam", "country": "NL", "region": ""},
328
+ "smart_charging": True,
329
+ "id": "GRID-BCU123456"
330
+ }
331
+ ]
332
+ ```
333
+
334
+ #### `get_transactions` - Get a list of transactions.
335
+
336
+ ```python
337
+ def get_transactions(
338
+ self, evse_id: str, newest_first: bool = True, page: int = 1
339
+ ) -> dict[str, int | list[dict[str, Any]]]
340
+ ```
341
+
342
+ ##### Arguments
343
+ - `evse_id`: The ID of the charge point.
344
+ - `newest_first`: If `True`, start with the most recent transaction. Defaults to `True`.
345
+ - `page`: Page number to get. Defaults to `1`.
346
+
347
+ ##### Returns
348
+ A dictionary like this:
349
+ ```python
350
+ {
351
+ "current_page": 1,
352
+ "next_page": 2, # This is None when there is no next page.
353
+ "max_per_page": 25,
354
+ "total_pages": 8,
355
+ "transactions: [
356
+ {
357
+ "transaction_id": 12345678,
358
+ "chargepoint_id": "BCU123456",
359
+ "chargepoint_type": "HIDDEN",
360
+ "evse_name": "Charge Point Name",
361
+ "started_at": datetime(2023, 7, 1, 12, 34, 56),
362
+ "end_time": datetime(2023, 7, 1, 14, 0, 0),
363
+ "kwh": 12.34,
364
+ "card_id": "NL-ABC-123456-0",
365
+ "card_name": "Card Name",
366
+ "total_costs": 5.97,
367
+ "total_costs_ex_vat": 4.93,
368
+ "vat": 21,
369
+ "currency": "EUR"
370
+ },
371
+ ...
372
+ ]
373
+ }
374
+ ```
375
+
376
+ #### `iterate_transactions` - Iterate through your transactions
377
+
378
+ ```python
379
+ def iterate_transactions(self, evse_id: str, newest_first: bool = True) -> Iterable[dict[str, Any]]
380
+ ```
381
+
382
+ ##### Arguments
383
+ - `evse_id`: The ID of the charge point.
384
+ - `newest_first`: If `True`, start with the most recent transaction. Defaults to `True`.
385
+
386
+ ##### Returns
387
+ An iterable of dictionaries describing the transactions. Each dictionary looks like this:
388
+ ```python
389
+ {
390
+ "transaction_id": 12345678,
391
+ "chargepoint_id": "BCU123456",
392
+ "chargepoint_type": "HIDDEN",
393
+ "evse_name": "Charge Point Name",
394
+ "started_at": datetime(2023, 7, 1, 12, 34, 56),
395
+ "end_time": datetime(2023, 7, 1, 14, 0, 0),
396
+ "kwh": 12.34,
397
+ "card_id": "NL-ABC-123456-0",
398
+ "card_name": "Card Name",
399
+ "total_costs": 5.97,
400
+ "total_costs_ex_vat": 4.93,
401
+ "vat": 21,
402
+ "currency": "EUR"
403
+ }
404
+ ```