pybluecurrent 0.2.0__tar.gz → 0.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.
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/.github/workflows/publish.yaml +4 -4
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/.github/workflows/test.yaml +4 -4
- pybluecurrent-0.3.0/CHANGELOG.md +66 -0
- pybluecurrent-0.3.0/PKG-INFO +445 -0
- pybluecurrent-0.3.0/README.md +417 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/pyproject.toml +5 -1
- pybluecurrent-0.3.0/src/pybluecurrent/__init__.py +4 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/src/pybluecurrent/_version.py +3 -3
- pybluecurrent-0.3.0/src/pybluecurrent/cli/__init__.py +19 -0
- pybluecurrent-0.3.0/src/pybluecurrent/cli/transactions.py +128 -0
- pybluecurrent-0.3.0/src/pybluecurrent/client.py +1070 -0
- pybluecurrent-0.3.0/src/pybluecurrent/enums.py +31 -0
- pybluecurrent-0.3.0/src/pybluecurrent/exceptions.py +31 -0
- pybluecurrent-0.3.0/src/pybluecurrent/models.py +282 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/src/pybluecurrent/utilities.py +31 -1
- pybluecurrent-0.3.0/src/pybluecurrent.egg-info/PKG-INFO +445 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/src/pybluecurrent.egg-info/SOURCES.txt +11 -1
- pybluecurrent-0.3.0/src/pybluecurrent.egg-info/entry_points.txt +2 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/src/pybluecurrent.egg-info/requires.txt +2 -1
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/src/pybluecurrent.egg-info/scm_file_list.json +23 -14
- pybluecurrent-0.3.0/src/pybluecurrent.egg-info/scm_version.json +8 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/conftest.py +19 -3
- pybluecurrent-0.3.0/tests/fake_rest.py +54 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/fake_socket.py +33 -0
- pybluecurrent-0.3.0/tests/fixtures/charge_point_settings.json +92 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/fixtures/charge_points.json +9 -2
- pybluecurrent-0.3.0/tests/fixtures/transactions.json +60 -0
- pybluecurrent-0.3.0/tests/models_check.py +66 -0
- pybluecurrent-0.3.0/tests/test_cli.py +121 -0
- pybluecurrent-0.3.0/tests/test_client.py +356 -0
- pybluecurrent-0.3.0/tests/test_enums.py +32 -0
- pybluecurrent-0.3.0/tests/test_offline.py +834 -0
- pybluecurrent-0.2.0/CHANGELOG.md +0 -40
- pybluecurrent-0.2.0/PKG-INFO +0 -417
- pybluecurrent-0.2.0/README.md +0 -390
- pybluecurrent-0.2.0/src/pybluecurrent/__init__.py +0 -3
- pybluecurrent-0.2.0/src/pybluecurrent/client.py +0 -574
- pybluecurrent-0.2.0/src/pybluecurrent/exceptions.py +0 -6
- pybluecurrent-0.2.0/src/pybluecurrent.egg-info/PKG-INFO +0 -417
- pybluecurrent-0.2.0/src/pybluecurrent.egg-info/scm_version.json +0 -8
- pybluecurrent-0.2.0/tests/fixtures/charge_point_settings.json +0 -50
- pybluecurrent-0.2.0/tests/test_client.py +0 -181
- pybluecurrent-0.2.0/tests/test_offline.py +0 -225
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/.gitignore +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/.pre-commit-config.yaml +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/LICENSE +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/setup.cfg +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/src/pybluecurrent/py.typed +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/src/pybluecurrent.egg-info/dependency_links.txt +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/src/pybluecurrent.egg-info/top_level.txt +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/fixtures/account.json +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/fixtures/charge_cards.json +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/fixtures/error_forbidden.json +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/fixtures/grid_status.json +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/fixtures/sustainability_status.json +0 -0
- {pybluecurrent-0.2.0 → pybluecurrent-0.3.0}/tests/test_utilities.py +0 -0
|
@@ -13,10 +13,10 @@ jobs:
|
|
|
13
13
|
timeout-minutes: 10
|
|
14
14
|
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
17
|
|
|
18
18
|
- name: Set up Python
|
|
19
|
-
uses: actions/setup-python@
|
|
19
|
+
uses: actions/setup-python@v6
|
|
20
20
|
with:
|
|
21
21
|
python-version: "3.x"
|
|
22
22
|
|
|
@@ -36,10 +36,10 @@ jobs:
|
|
|
36
36
|
contents: read
|
|
37
37
|
|
|
38
38
|
steps:
|
|
39
|
-
- uses: actions/checkout@
|
|
39
|
+
- uses: actions/checkout@v5
|
|
40
40
|
|
|
41
41
|
- name: Set up Python
|
|
42
|
-
uses: actions/setup-python@
|
|
42
|
+
uses: actions/setup-python@v6
|
|
43
43
|
with:
|
|
44
44
|
python-version: "3.x"
|
|
45
45
|
|
|
@@ -13,10 +13,10 @@ jobs:
|
|
|
13
13
|
timeout-minutes: 10
|
|
14
14
|
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
17
|
|
|
18
18
|
- name: Set up Python
|
|
19
|
-
uses: actions/setup-python@
|
|
19
|
+
uses: actions/setup-python@v6
|
|
20
20
|
with:
|
|
21
21
|
python-version: "3.x"
|
|
22
22
|
|
|
@@ -36,10 +36,10 @@ jobs:
|
|
|
36
36
|
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
37
37
|
|
|
38
38
|
steps:
|
|
39
|
-
- uses: actions/checkout@
|
|
39
|
+
- uses: actions/checkout@v5
|
|
40
40
|
|
|
41
41
|
- name: Set up Python
|
|
42
|
-
uses: actions/setup-python@
|
|
42
|
+
uses: actions/setup-python@v6
|
|
43
43
|
with:
|
|
44
44
|
python-version: ${{ matrix.python-version }}
|
|
45
45
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
This project is pre-1.0: breaking changes may land in minor releases.
|
|
7
|
+
|
|
8
|
+
## [0.3.0] - 2026-07-25
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- A `pybluecurrent` command-line interface. `pybluecurrent transactions` exports your charging transactions to CSV, JSON or JSON Lines, to stdout or to `--output`, optionally limited to the last `--days` or to specific `--evse-id` charge points. Credentials come from `BLUECURRENT_USERNAME`/`BLUECURRENT_PASSWORD` (or `BLUECURRENT_API_TOKEN`) or the matching options.
|
|
13
|
+
- `start_date` and `end_date` arguments on `get_transactions()` and `iterate_transactions()`, so the backend filters by date instead of you fetching everything and discarding the old ones.
|
|
14
|
+
- Typed response models in `pybluecurrent.models`. Each getter now declares a `TypedDict` return type (`Account`, `ChargePoint`, `ChargePointSettings`, `Transaction`, …) instead of a loose `dict`. Responses stay plain dictionaries, so existing `response["key"]` access is unchanged; you now get autocomplete and type checking, and `get_transactions` is fully typed rather than `dict[str, Any]`.
|
|
15
|
+
- Automatic reconnection. A long-lived client now keeps itself connected: when the websocket drops it reconnects in the background with exponential backoff, reusing the cached session token. Calls made while reconnecting block until the connection is restored (bounded by `reconnect_wait_timeout`) rather than failing; calls already in flight when the drop happens still raise `ConnectionLost`. Tunable via the `auto_reconnect` (default on) and `reconnect_*` class attributes. Reconnection gives up after too many attempts, or immediately on rejected credentials, rather than retrying forever.
|
|
16
|
+
- Delayed (time-window) smart charging: `set_delayed_charging()` to switch the profile on or off, and `set_delayed_charging_schedule()` to set the window and the days it applies to.
|
|
17
|
+
- Price-based (dynamic-tariff) smart charging: `set_price_based_charging()` to switch the profile on or off, and `set_price_based_charging_settings()` to set the expected departure time and energy.
|
|
18
|
+
- `boost()` to charge now, overriding whichever smart charging profile (delayed or price-based) is currently active.
|
|
19
|
+
- `Weekday`, an `IntEnum` numbered like `date.isoweekday()`. Days can also be given as plain numbers or as names (`"monday"`, `"mo"`), so importing it is optional.
|
|
20
|
+
- `ConnectionLost` and `RequestTimeout` exceptions. Both derive from `BlueCurrentException`; `RequestTimeout` also subclasses the builtin `TimeoutError` (so existing `except TimeoutError` keeps working), and `AuthenticationFailed` now derives from `BlueCurrentException` as well as `ValueError`.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- `get_charge_points` and `get_charge_point_settings` now report the delayed- and price-based-charging schedules under the same names used to set them: `days` (previously `selected_days`) and `expected_departure_time` (previously `expected_leave_time`). This is a breaking change for code that read the old keys.
|
|
25
|
+
- The delayed- and price-based-charging schedule times (`start_time`, `end_time`, `expected_departure_time`) are now returned as `datetime.time` objects instead of `"HH:MM"` strings.
|
|
26
|
+
- Websocket connection failures are now surfaced instead of hanging. When the handler stops (the connection drops, or an unexpected error), pending and subsequent calls raise `ConnectionLost` immediately rather than blocking until they time out. A single malformed (non-JSON) frame is now logged and skipped instead of silently killing the connection.
|
|
27
|
+
- `_receive` now enforces a single per-call deadline (a stream of unrelated frames can no longer postpone it indefinitely) and raises `RequestTimeout` rather than a bare `TimeoutError`.
|
|
28
|
+
- `set_status()`, `unlock_connector()` and `soft_reset()` now raise `BlueCurrentException` when the command fails (a `STATUS_` frame with `success: false`), instead of `set_status()` returning silently or the others handing back the failed frame. On success all three now return `None` (previously `unlock_connector()` and `soft_reset()` returned the raw status frame), so the command methods are consistent. Their wait for that verdict is also longer (a new `command_timeout`, default 60s), so the backend's own ~30s answer is no longer cut off just before it arrives.
|
|
29
|
+
- Logging now uses a module logger (`pybluecurrent.client`) instead of a fixed `BlueCurrentClient` name, and the reconnect failure paths (attempts, back-off, wait-timeouts, and permanent give-up) are logged.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- The session token is no longer written to debug logs. Received frames were logged verbatim at debug level, which included the token carried by the login-response frames; those fields are now masked.
|
|
34
|
+
- The client no longer leaks the websocket, handler task, or HTTP client when connecting fails partway (for example on a rejected login), and teardown now awaits the handler and closes the HTTP client even if closing the websocket errors.
|
|
35
|
+
|
|
36
|
+
## [0.2.0] - 2026-07-11
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- API-token authentication: construct the client with `BlueCurrentClient(api_token=...)` instead of a username and password.
|
|
41
|
+
- `get_api_token()` and `generate_api_token()` to fetch or rotate your account's API token (home automation key).
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- REST calls now use `api.bluecurrent.nl` instead of the legacy `bo.bluecurrent.nl` backoffice host (same `bc_api` v2.0 API and response shapes).
|
|
46
|
+
- REST requests now use a 30-second timeout instead of httpx's 5-second default, so occasional slow backend responses no longer raise `httpx.ReadTimeout`; override with the `http_timeout` attribute.
|
|
47
|
+
- Internal: added an offline websocket test harness (fake socket + recorded fixtures) so the auth/`_send`/`_receive`/`_handler` logic runs in CI without live credentials.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Concurrent websocket calls are now safe: calls awaiting the same response type are serialized so overlapping same-type calls can no longer receive each other's replies (different-type calls still run concurrently). Errors the backend tags with a request id are routed to the originating call rather than failing every in-flight call.
|
|
52
|
+
|
|
53
|
+
## [0.1.1] - 2026-07-04
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- `get_account` no longer raises a `ValueError` on BlueCurrent's current date format; `first_login_app` now parses both the legacy (`01-JAN-20`) and ISO (`2020-01-15T13:33:52`) formats.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- `get_account` returns `first_login_app` as a `datetime` (previously a `date`).
|
|
62
|
+
- Internal: switched tooling to Ruff and ty, added a Python 3.10–3.13 CI matrix, and moved to PyPI trusted publishing (OIDC).
|
|
63
|
+
|
|
64
|
+
[0.3.0]: https://github.com/rogiervandergeer/pybluecurrent/compare/0.2.0...0.3.0
|
|
65
|
+
[0.2.0]: https://github.com/rogiervandergeer/pybluecurrent/compare/0.1.1...0.2.0
|
|
66
|
+
[0.1.1]: https://github.com/rogiervandergeer/pybluecurrent/compare/0.1.0...0.1.1
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pybluecurrent
|
|
3
|
+
Version: 0.3.0
|
|
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: httpx>=0.28
|
|
19
|
+
Requires-Dist: sjcl>=0.2.1
|
|
20
|
+
Requires-Dist: typer>=0.12
|
|
21
|
+
Requires-Dist: websockets>=14.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pre-commit>=3.3.3; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest==8.4.2; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-asyncio==1.2.0; extra == "dev"
|
|
26
|
+
Requires-Dist: typeguard>=4.0; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# pybluecurrent
|
|
30
|
+
|
|
31
|
+
Python client for [BlueCurrent](https://www.bluecurrent.nl) charge points.
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
`pybluecurrent` is an **unofficial, third-party async client** — it is not affiliated with
|
|
39
|
+
BlueCurrent.
|
|
40
|
+
|
|
41
|
+
Compared to BlueCurrent's official [`bluecurrent-api`](https://github.com/bluecurrent/HomeAssistantAPI):
|
|
42
|
+
|
|
43
|
+
- **Per-call `async`/`await`** — each method awaits its own response, rather than a single callback
|
|
44
|
+
receiver that routes every server message.
|
|
45
|
+
- **Typed responses** — getters return `TypedDict`-annotated dictionaries; the official client
|
|
46
|
+
hands back untyped dicts.
|
|
47
|
+
- **Instance-scoped state** — no process-global mutable state.
|
|
48
|
+
- **Username/password *or* API token** — the official client is API-token only.
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
Using the client is as simple as:
|
|
53
|
+
```python
|
|
54
|
+
from pybluecurrent import BlueCurrentClient
|
|
55
|
+
|
|
56
|
+
client = BlueCurrentClient("your_username", "your_secret_password")
|
|
57
|
+
|
|
58
|
+
async with client:
|
|
59
|
+
charge_points = await client.get_charge_points()
|
|
60
|
+
transactions = await client.get_transactions(charge_points[0]["evse_id"])
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Connection
|
|
64
|
+
|
|
65
|
+
The client can only be used while its websocket is connected. For example:
|
|
66
|
+
```python
|
|
67
|
+
client = BlueCurrentClient("your_username", "your_secret_password")
|
|
68
|
+
async with client:
|
|
69
|
+
result = await client.get_account()
|
|
70
|
+
```
|
|
71
|
+
Entering the async context automatically logs in.
|
|
72
|
+
|
|
73
|
+
Instead of a username and password, you can authenticate with an API token:
|
|
74
|
+
```python
|
|
75
|
+
client = BlueCurrentClient(api_token="your_api_token")
|
|
76
|
+
```
|
|
77
|
+
Retrieve or rotate the token with [`get_api_token`](#get_api_token) and
|
|
78
|
+
[`generate_api_token`](#generate_api_token), or from the [BlueCurrent website](https://my.bluecurrent.nl).
|
|
79
|
+
|
|
80
|
+
## Command line
|
|
81
|
+
|
|
82
|
+
Installing the package also installs a `pybluecurrent` command, which exports your transactions:
|
|
83
|
+
|
|
84
|
+
```shell
|
|
85
|
+
export BLUECURRENT_USERNAME="your_username"
|
|
86
|
+
export BLUECURRENT_PASSWORD="your_secret_password"
|
|
87
|
+
|
|
88
|
+
pybluecurrent transactions --format csv -o transactions.csv
|
|
89
|
+
pybluecurrent transactions --format json --days 30
|
|
90
|
+
pybluecurrent transactions --format jsonl --evse-id BCU123456
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Credentials come from `BLUECURRENT_USERNAME` / `BLUECURRENT_PASSWORD` (or `BLUECURRENT_API_TOKEN`),
|
|
94
|
+
or from the matching `--username` / `--password` / `--api-token` options.
|
|
95
|
+
|
|
96
|
+
**Options**
|
|
97
|
+
- `--format`: `csv` (default), `json` (one array) or `jsonl` (one object per line).
|
|
98
|
+
- `--evse-id`: Charge point to export, repeatable. Defaults to all of your charge points.
|
|
99
|
+
- `--days`: Only export the last N days. Defaults to your whole history.
|
|
100
|
+
- `-o`, `--output`: Write to a file instead of stdout.
|
|
101
|
+
- `--newest-first` / `--oldest-first`: Output order, newest first by default.
|
|
102
|
+
|
|
103
|
+
## Methods
|
|
104
|
+
|
|
105
|
+
Every method is a coroutine on `BlueCurrentClient`; call them inside the async context (see
|
|
106
|
+
[Connection](#connection)). Charge points are addressed by their `evse_id`.
|
|
107
|
+
|
|
108
|
+
- **Account & authentication** — [`get_account`](#get_account), [`get_api_token`](#get_api_token), [`generate_api_token`](#generate_api_token), [`get_contracts`](#get_contracts)
|
|
109
|
+
- **Charge points & cards** — [`get_charge_points`](#get_charge_points), [`get_charge_point_settings`](#get_charge_point_settings), [`get_charge_point_status`](#get_charge_point_status), [`get_charge_cards`](#get_charge_cards)
|
|
110
|
+
- **Grid & sustainability** — [`get_grid_status`](#get_grid_status), [`get_grids`](#get_grids), [`get_sustainability_status`](#get_sustainability_status)
|
|
111
|
+
- **Settings & control** — [`set_plug_and_charge_charge_card`](#set_plug_and_charge_charge_card), [`set_status`](#set_status), [`soft_reset`](#soft_reset)
|
|
112
|
+
- **Smart charging** — [`set_delayed_charging`](#set_delayed_charging), [`set_delayed_charging_schedule`](#set_delayed_charging_schedule), [`set_price_based_charging`](#set_price_based_charging), [`set_price_based_charging_settings`](#set_price_based_charging_settings), [`boost`](#boost)
|
|
113
|
+
- **Transactions** — [`get_transactions`](#get_transactions), [`iterate_transactions`](#iterate_transactions)
|
|
114
|
+
|
|
115
|
+
### Response models
|
|
116
|
+
|
|
117
|
+
The getters return plain dictionaries annotated with `TypedDict`s from
|
|
118
|
+
[`pybluecurrent.models`](https://github.com/rogiervandergeer/pybluecurrent/blob/main/src/pybluecurrent/models.py).
|
|
119
|
+
Access is unchanged — `response["key"]`, `.get()`, `**response` and `json.dumps` all keep working —
|
|
120
|
+
and an unexpected field the backend adds simply rides along; the types just add autocomplete and
|
|
121
|
+
static checking:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from pybluecurrent.models import ChargePoint, Transaction
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**The model definitions are the field-level reference** — each field, its type, and any parsing
|
|
128
|
+
notes live there. The response types are `Account`, `ChargeCard`, `ChargePoint`,
|
|
129
|
+
`ChargePointSettings`, `ChargePointStatus`, `GridStatus`, `Grid`, `SustainabilityStatus`,
|
|
130
|
+
`Contract`, `TransactionsPage` and `Transaction`, built from the nested shapes `Tariff`,
|
|
131
|
+
`Location`, `Address`, `DelayedCharging`, `PriceBasedCharging`, `CardRef`, `BoolSetting` and
|
|
132
|
+
`IntSetting`. Dates and times are parsed for you: `date`/`datetime` fields are Python objects, and
|
|
133
|
+
schedule times (`start_time`, `end_time`, `expected_departure_time`) are `datetime.time`.
|
|
134
|
+
|
|
135
|
+
### Account & authentication
|
|
136
|
+
|
|
137
|
+
#### get_account
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
async def get_account(self) -> Account
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Returns your account information as an [`Account`](#response-models).
|
|
144
|
+
|
|
145
|
+
#### get_api_token
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
async def get_api_token(self) -> str
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Returns the API token (home automation key) for your account. It can be used to authenticate
|
|
152
|
+
instead of a username and password, by constructing the client with `BlueCurrentClient(api_token=...)`.
|
|
153
|
+
|
|
154
|
+
#### generate_api_token
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
async def generate_api_token(self) -> str
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Generates a new API token and returns it. **Warning:** this rotates the token — any previously
|
|
161
|
+
issued token is invalidated, which will break anything still using the old one.
|
|
162
|
+
|
|
163
|
+
#### get_contracts
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
async def get_contracts(self) -> list[Contract]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Returns your contracts, each a [`Contract`](#response-models).
|
|
170
|
+
|
|
171
|
+
### Charge points & cards
|
|
172
|
+
|
|
173
|
+
#### get_charge_points
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
async def get_charge_points(self) -> list[ChargePoint]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Returns your charge points, each a [`ChargePoint`](#response-models). A disabled smart-charging
|
|
180
|
+
profile is still present as its `{value, permission}` wrapper; its schedule/settings fields appear
|
|
181
|
+
only while the profile is enabled.
|
|
182
|
+
|
|
183
|
+
#### get_charge_point_settings
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
async def get_charge_point_settings(self, evse_id: str) -> ChargePointSettings
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Returns the settings of a charge point as a [`ChargePointSettings`](#response-models). All of this
|
|
190
|
+
is already included in the response of [`get_charge_points`](#get_charge_points).
|
|
191
|
+
|
|
192
|
+
**Arguments**
|
|
193
|
+
- `evse_id`: The ID of the charge point.
|
|
194
|
+
|
|
195
|
+
#### get_charge_point_status
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
async def get_charge_point_status(self, evse_id: str) -> ChargePointStatus
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Returns the live status of a charge point as a [`ChargePointStatus`](#response-models).
|
|
202
|
+
|
|
203
|
+
**Arguments**
|
|
204
|
+
- `evse_id`: The ID of the charge point.
|
|
205
|
+
|
|
206
|
+
#### get_charge_cards
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
async def get_charge_cards(self) -> list[ChargeCard]
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Returns your charge cards, each a [`ChargeCard`](#response-models).
|
|
213
|
+
|
|
214
|
+
### Grid & sustainability
|
|
215
|
+
|
|
216
|
+
#### get_grid_status
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
async def get_grid_status(self, evse_id: str) -> GridStatus
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Returns the grid status associated with a charge point (currents in amps) as a
|
|
223
|
+
[`GridStatus`](#response-models).
|
|
224
|
+
|
|
225
|
+
**Arguments**
|
|
226
|
+
- `evse_id`: The ID of the charge point.
|
|
227
|
+
|
|
228
|
+
#### get_grids
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
async def get_grids(self) -> list[Grid]
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Returns your grid connections, each a [`Grid`](#response-models).
|
|
235
|
+
|
|
236
|
+
#### get_sustainability_status
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
async def get_sustainability_status(self) -> SustainabilityStatus
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Returns sustainability statistics for all your charge points as a
|
|
243
|
+
[`SustainabilityStatus`](#response-models) — `{"trees": ..., "co2": ...}`.
|
|
244
|
+
|
|
245
|
+
### Settings & control
|
|
246
|
+
|
|
247
|
+
#### set_plug_and_charge_charge_card
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
async def set_plug_and_charge_charge_card(self, evse_id: str, uid: str | None = None) -> None
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Sets the plug-and-charge card for the charge point. `uid` must be the `uid` of one of your
|
|
254
|
+
[charge cards](#get_charge_cards), or `None` to charge without a card. Raises `BlueCurrentException`
|
|
255
|
+
if the command fails.
|
|
256
|
+
|
|
257
|
+
**Arguments**
|
|
258
|
+
- `evse_id`: The ID of the charge point.
|
|
259
|
+
- `uid`: A charge card UID, or `None` (the default) to use no charge card.
|
|
260
|
+
|
|
261
|
+
#### set_status
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
async def set_status(self, evse_id: str, enabled: bool) -> None
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Enables or disables a charge point. Raises `BlueCurrentException` if the command fails.
|
|
268
|
+
|
|
269
|
+
**Arguments**
|
|
270
|
+
- `evse_id`: The ID of the charge point.
|
|
271
|
+
- `enabled`: Boolean that indicates the desired status.
|
|
272
|
+
|
|
273
|
+
#### soft_reset
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
async def soft_reset(self, evse_id: str) -> None
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Soft-resets a charge point. Raises `BlueCurrentException` if the command fails.
|
|
280
|
+
|
|
281
|
+
**Arguments**
|
|
282
|
+
- `evse_id`: The ID of the charge point.
|
|
283
|
+
|
|
284
|
+
### Smart charging
|
|
285
|
+
|
|
286
|
+
#### set_delayed_charging
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
async def set_delayed_charging(self, evse_id: str, enabled: bool) -> None
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Enables or disables delayed charging. While enabled, the charge point only charges within the window
|
|
293
|
+
configured with [`set_delayed_charging_schedule`](#set_delayed_charging_schedule), and delays
|
|
294
|
+
charging outside of it. A charge point has at most one smart-charging profile active, so enabling
|
|
295
|
+
this disables any other profile.
|
|
296
|
+
|
|
297
|
+
**Arguments**
|
|
298
|
+
- `evse_id`: The ID of the charge point.
|
|
299
|
+
- `enabled`: Whether delayed charging should be enabled.
|
|
300
|
+
|
|
301
|
+
#### set_delayed_charging_schedule
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
async def set_delayed_charging_schedule(
|
|
305
|
+
self,
|
|
306
|
+
evse_id: str,
|
|
307
|
+
start_time: time | str,
|
|
308
|
+
end_time: time | str,
|
|
309
|
+
days: Iterable[Weekday | int | str],
|
|
310
|
+
) -> None
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Sets the window in which the charge point may charge on the selected days. The window may span
|
|
314
|
+
midnight. It is applied only while delayed charging is enabled with
|
|
315
|
+
[`set_delayed_charging`](#set_delayed_charging).
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
from datetime import time
|
|
319
|
+
from pybluecurrent import Weekday
|
|
320
|
+
|
|
321
|
+
await client.set_delayed_charging_schedule(
|
|
322
|
+
"BCU123456", start_time=time(23, 0), end_time=time(7, 0), days=[Weekday.MONDAY, "tu", 3]
|
|
323
|
+
)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Arguments**
|
|
327
|
+
- `evse_id`: The ID of the charge point.
|
|
328
|
+
- `start_time`: The time at which charging may start, as a `time` or a `"HH:MM"` string.
|
|
329
|
+
- `end_time`: The time at which charging must stop, as a `time` or a `"HH:MM"` string.
|
|
330
|
+
- `days`: The days on which the schedule applies. Each day may be a `pybluecurrent.Weekday`, an
|
|
331
|
+
isoweekday number (1 for Monday through 7 for Sunday), or a name such as `"monday"` or `"mo"`.
|
|
332
|
+
|
|
333
|
+
The schedule is read back from the `delayed_charging` key of
|
|
334
|
+
[`get_charge_point_settings`](#get_charge_point_settings).
|
|
335
|
+
|
|
336
|
+
#### set_price_based_charging
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
async def set_price_based_charging(self, evse_id: str, enabled: bool) -> None
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Enables or disables price-based charging. While enabled, the charge point charges during the
|
|
343
|
+
cheapest hours before the expected departure time, as configured with
|
|
344
|
+
[`set_price_based_charging_settings`](#set_price_based_charging_settings). A charge point has at most
|
|
345
|
+
one smart-charging profile active, so enabling this disables any other profile.
|
|
346
|
+
|
|
347
|
+
**Arguments**
|
|
348
|
+
- `evse_id`: The ID of the charge point.
|
|
349
|
+
- `enabled`: Whether price-based charging should be enabled.
|
|
350
|
+
|
|
351
|
+
#### set_price_based_charging_settings
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
async def set_price_based_charging_settings(
|
|
355
|
+
self,
|
|
356
|
+
evse_id: str,
|
|
357
|
+
expected_departure_time: time | str,
|
|
358
|
+
expected_kwh: float,
|
|
359
|
+
minimum_kwh: float,
|
|
360
|
+
) -> None
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Configures how much energy to charge before departure. Applied only while price-based charging is
|
|
364
|
+
enabled with [`set_price_based_charging`](#set_price_based_charging).
|
|
365
|
+
|
|
366
|
+
**Arguments**
|
|
367
|
+
- `evse_id`: The ID of the charge point.
|
|
368
|
+
- `expected_departure_time`: The time the vehicle is expected to leave, as a `time` or a `"HH:MM"`
|
|
369
|
+
string.
|
|
370
|
+
- `expected_kwh`: The amount of energy, in kWh, expected to be charged before departure.
|
|
371
|
+
- `minimum_kwh`: The amount of energy, in kWh, to charge immediately regardless of price.
|
|
372
|
+
|
|
373
|
+
The settings are read back from the `price_based_charging` key of
|
|
374
|
+
[`get_charge_point_settings`](#get_charge_point_settings).
|
|
375
|
+
|
|
376
|
+
#### boost
|
|
377
|
+
|
|
378
|
+
```python
|
|
379
|
+
async def boost(self, evse_id: str) -> None
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Starts charging immediately, overriding whichever smart-charging profile is currently delaying
|
|
383
|
+
charging — delayed charging or price-based charging — for the ongoing session. The override cannot
|
|
384
|
+
be undone. While it is active, [`get_charge_point_status`](#get_charge_point_status) reports
|
|
385
|
+
`"boosting": True`. Raises `ValueError` if no smart-charging profile is active.
|
|
386
|
+
|
|
387
|
+
**Arguments**
|
|
388
|
+
- `evse_id`: The ID of the charge point.
|
|
389
|
+
|
|
390
|
+
### Transactions
|
|
391
|
+
|
|
392
|
+
#### get_transactions
|
|
393
|
+
|
|
394
|
+
```python
|
|
395
|
+
async def get_transactions(
|
|
396
|
+
self,
|
|
397
|
+
evse_id: str,
|
|
398
|
+
newest_first: bool = True,
|
|
399
|
+
page: int = 1,
|
|
400
|
+
start_date: date | None = None,
|
|
401
|
+
end_date: date | None = None,
|
|
402
|
+
) -> TransactionsPage
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Returns a single page of transactions as a [`TransactionsPage`](#response-models); its
|
|
406
|
+
`transactions` key holds a list of [`Transaction`](#response-models).
|
|
407
|
+
|
|
408
|
+
**Arguments**
|
|
409
|
+
- `evse_id`: The ID of the charge point.
|
|
410
|
+
- `newest_first`: If `True`, start with the most recent transaction. Defaults to `True`.
|
|
411
|
+
- `page`: Page number to get. Defaults to `1`.
|
|
412
|
+
- `start_date`: Only return transactions from this date onwards. Omitted by default.
|
|
413
|
+
- `end_date`: Only return transactions up to this date. Omitted by default.
|
|
414
|
+
|
|
415
|
+
#### iterate_transactions
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
async def iterate_transactions(
|
|
419
|
+
self,
|
|
420
|
+
evse_id: str,
|
|
421
|
+
newest_first: bool = True,
|
|
422
|
+
start_date: date | None = None,
|
|
423
|
+
end_date: date | None = None,
|
|
424
|
+
) -> AsyncIterable[Transaction]
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Iterates over all your transactions, fetching further pages as needed. Yields
|
|
428
|
+
[`Transaction`](#response-models) dictionaries.
|
|
429
|
+
|
|
430
|
+
**Arguments**
|
|
431
|
+
- `evse_id`: The ID of the charge point.
|
|
432
|
+
- `newest_first`: If `True`, start with the most recent transaction. Defaults to `True`.
|
|
433
|
+
- `start_date`: Only return transactions from this date onwards. Omitted by default.
|
|
434
|
+
- `end_date`: Only return transactions up to this date. Omitted by default.
|
|
435
|
+
|
|
436
|
+
## Development
|
|
437
|
+
|
|
438
|
+
- **Install** (editable, with dev extras): `uv sync --extra dev` (or `pip install -e ".[dev]"`).
|
|
439
|
+
- **Pre-commit**: the repo ships a `.pre-commit-config.yaml`, but git installs no hooks on clone, so
|
|
440
|
+
it is a one-time manual step — run `uvx pre-commit install`.
|
|
441
|
+
- **Contributions** and feature requests are welcome.
|
|
442
|
+
|
|
443
|
+
## Changelog
|
|
444
|
+
|
|
445
|
+
See [CHANGELOG.md](https://github.com/rogiervandergeer/pybluecurrent/blob/main/CHANGELOG.md).
|