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