python-openevse-http 1.5.0__tar.gz → 1.7.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.
- python_openevse_http-1.7.0/.agents/skills/openevse-api-guide/SKILL.md +115 -0
- python_openevse_http-1.7.0/.agents/skills/openevse-api-guide/references/endpoints_matrix.md +110 -0
- python_openevse_http-1.7.0/.agents/skills/openevse-dev-workflow/SKILL.md +87 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/release-drafter.yml +1 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/workflows/autolabeler.yml +2 -2
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/workflows/links.yml +3 -3
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/workflows/publish-to-pypi.yml +4 -4
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/workflows/release-drafter.yml +2 -2
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/workflows/test.yml +11 -11
- python_openevse_http-1.7.0/AGENTS.md +129 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/EXTERNAL_SESSION.md +3 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/PKG-INFO +36 -7
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/README.md +35 -6
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/__init__.py +8 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/client.py +85 -2
- python_openevse_http-1.7.0/openevsehttp/commands.py +33 -0
- python_openevse_http-1.7.0/openevsehttp/commands_base.py +66 -0
- python_openevse_http-1.5.0/openevsehttp/commands.py → python_openevse_http-1.7.0/openevsehttp/commands_core.py +44 -292
- python_openevse_http-1.7.0/openevsehttp/commands_diagnostics.py +229 -0
- python_openevse_http-1.7.0/openevsehttp/commands_firmware.py +260 -0
- python_openevse_http-1.7.0/openevsehttp/commands_schedule.py +147 -0
- python_openevse_http-1.7.0/openevsehttp/commands_security.py +265 -0
- python_openevse_http-1.7.0/openevsehttp/commands_time.py +186 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/const.py +10 -1
- python_openevse_http-1.7.0/openevsehttp/exceptions.py +62 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/managers.py +6 -1
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/properties.py +103 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/sensors.py +6 -1
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/pyproject.toml +1 -1
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/PKG-INFO +36 -7
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/SOURCES.txt +11 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/scm_file_list.json +55 -44
- python_openevse_http-1.7.0/python_openevse_http.egg-info/scm_version.json +8 -0
- python_openevse_http-1.7.0/requirements_lint.txt +4 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/requirements_test.txt +2 -2
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/conftest.py +6 -2
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_client.py +221 -4
- python_openevse_http-1.7.0/tests/test_commands.py +2764 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_managers.py +6 -5
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_mixins.py +31 -1
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_properties.py +95 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_shaper.py +7 -3
- python_openevse_http-1.5.0/openevsehttp/exceptions.py +0 -33
- python_openevse_http-1.5.0/python_openevse_http.egg-info/scm_version.json +0 -8
- python_openevse_http-1.5.0/requirements_lint.txt +0 -4
- python_openevse_http-1.5.0/tests/test_commands.py +0 -1369
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/dependabot.yml +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/pull_request_template.md +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.gitignore +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.pre-commit-config.yaml +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.yamllint +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/LICENSE +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/codecov.yml +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/example_external_session.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/__main__.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/py.typed +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/utils.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/openevsehttp/websocket.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/dependency_links.txt +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/not-zip-safe +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/requires.txt +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/top_level.txt +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/requirements.txt +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/setup.cfg +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/setup.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/__init__.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/common.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/github_v2.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/github_v4.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v2_json/config.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v2_json/status.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-broken.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-dev.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-extra-version.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-new.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-unknown-semver.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/schedule.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/status-broken.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/status-new.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/status.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/fixtures/websocket.json +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_external_session.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_main_edge_cases.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_sensors.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tests/test_websocket.py +0 -0
- {python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/tox.ini +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openevse-api-guide
|
|
3
|
+
description: >-
|
|
4
|
+
Use this skill when implementing, refactoring, or testing OpenEVSE charger
|
|
5
|
+
commands, REST API endpoints, RAPI commands, properties, or exception handling in python-openevse-http.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# OpenEVSE API & Command Implementation Guide
|
|
9
|
+
|
|
10
|
+
This skill provides architectural guidelines, endpoint conventions, and error handling patterns for developing `python-openevse-http`.
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
The main client `OpenEVSE` combines several mixins:
|
|
15
|
+
- `CommandsMixin` (`openevsehttp/commands.py`): Command execution methods.
|
|
16
|
+
- `PropertiesMixin` (`openevsehttp/properties.py`): Configuration & state properties.
|
|
17
|
+
- `SensorsMixin` (`openevsehttp/sensors.py`): Energy, current, voltage, temperature telemetry.
|
|
18
|
+
- `WebsocketMixin` (`openevsehttp/websocket.py`): Real-time event streams.
|
|
19
|
+
|
|
20
|
+
## Endpoints & RAPI Commands Reference
|
|
21
|
+
|
|
22
|
+
For a comprehensive matrix of all endpoints across firmware generations (v2.x, v3.x, and active v4.x/v5.x), field variations (`/config`, `/status`), and RAPI fallbacks, see:
|
|
23
|
+
- [OpenEVSE Endpoints Matrix](file:///home/firstof9/github/python-openevse-http/.agents/skills/openevse-api-guide/references/endpoints_matrix.md)
|
|
24
|
+
|
|
25
|
+
| Action | HTTP Endpoint (v4+) | RAPI Command (v2/v3) | Method |
|
|
26
|
+
| :--- | :--- | :--- | :--- |
|
|
27
|
+
| Status | `/status` | N/A | GET |
|
|
28
|
+
| Config | `/config` | N/A | GET / POST |
|
|
29
|
+
| Manual Override | `/override` | `$FE` (enable) / `$FS` (sleep) | GET / POST / PATCH / DELETE |
|
|
30
|
+
| Soft Current Limit | `/override` (charge_current) | `$SC <amps> [N\|V]` | POST |
|
|
31
|
+
| Shaper Mode | `/shaper` | N/A | POST |
|
|
32
|
+
| Divert Mode | `/divertmode` or `/config` | N/A | POST |
|
|
33
|
+
| Module Restart | `/restart` (`device: gateway\|evse`) | `$FR` (evse restart) | POST |
|
|
34
|
+
| Firmware Update | `/update` | N/A | POST (multipart or JSON URL) |
|
|
35
|
+
| Relay Stuck Recovery | `/relay/recovery` | `$FK` (controller stuck recovery) | POST |
|
|
36
|
+
| Relay Health Reset | `/relay/reset` | `$FH` (controller health reset) | POST |
|
|
37
|
+
| Cable Temperature | `/cabletemp` | `$GN` / `$SN` (cable temp monitor) | GET / POST |
|
|
38
|
+
| Time Settings | `/time` (v4+) / `/settime` (v3) | `$S1` (RTC set) | GET / POST |
|
|
39
|
+
| Event Logs | `/logs` / `/logs/{index}` | N/A | GET |
|
|
40
|
+
| Certificates | `/certificates` (`/root`, `/{id}`) | N/A | GET / POST / DELETE |
|
|
41
|
+
| RFID Tag Pairing | `/rfid/add` | N/A | POST |
|
|
42
|
+
| RFID Users | `/rfid/users` | N/A | GET / POST / DELETE |
|
|
43
|
+
|
|
44
|
+
> [!NOTE]
|
|
45
|
+
> Firmware development for **v2.x (ESP8266)** and **v3.x (ESP32)** has ended. Active development occurs in **`OpenEVSE/openevse_esp32_firmware`** (v4.x/v5.x). Always check `openevse_esp32_firmware` as the primary reference when evaluating new endpoints, features, or behaviors.
|
|
46
|
+
|
|
47
|
+
## Firmware Version Branching
|
|
48
|
+
|
|
49
|
+
Always check firmware compatibility using `self._version_check(min_version)`:
|
|
50
|
+
```python
|
|
51
|
+
if self._version_check("4.0.1"):
|
|
52
|
+
# Use HTTP REST endpoint
|
|
53
|
+
response = await self.process_request(url=f"{self.url}override", method="patch")
|
|
54
|
+
else:
|
|
55
|
+
# Fallback to RAPI command for older firmware
|
|
56
|
+
response, msg = await self.send_command("$FE" if state == 254 else "$FS")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If a feature is not supported on older firmware:
|
|
60
|
+
```python
|
|
61
|
+
if not self._version_check("4.1.0"):
|
|
62
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
63
|
+
raise UnsupportedFeature
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Exception Handling Conventions
|
|
67
|
+
|
|
68
|
+
All custom exceptions inherit from `OpenEVSEError(Exception)`.
|
|
69
|
+
|
|
70
|
+
- **`CommandFailedError`**: Raise when a command returns an error response, fails HTTP verification, or returns `$NK` / `RAPI_ERRORS`.
|
|
71
|
+
- **`UnknownStateError`**: Raise when prior charger state or configuration is required to determine the command payload (e.g. toggling) but is missing or `None`.
|
|
72
|
+
- **`FirmwareResolutionError`**: Raise when GitHub release download URL cannot be determined from the release metadata.
|
|
73
|
+
- **`UnsupportedFeature`**: Raise when charger firmware is below the minimum supported version for a feature.
|
|
74
|
+
- **`AuthenticationError`**: Raise on 401 unauthorized.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from .exceptions import CommandFailedError, UnknownStateError, UnsupportedFeature
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Validating Endpoints Against Firmware Repositories
|
|
81
|
+
|
|
82
|
+
When adding, modifying, or debugging endpoints and RAPI commands, cross-reference against the upstream OpenEVSE firmware sources:
|
|
83
|
+
|
|
84
|
+
- **WiFi Gateway Firmware (Current ESP32 v4/v5)**: [`OpenEVSE/openevse_esp32_firmware`](https://github.com/OpenEVSE/openevse_esp32_firmware) (formerly [`OpenEVSE/ESP32_WiFi_V4.x`](https://github.com/OpenEVSE/ESP32_WiFi_V4.x))
|
|
85
|
+
- **WiFi Gateway Firmware (Legacy ESP32 v3.x)**: [`OpenEVSE/ESP32_WiFi_V3.x`](https://github.com/OpenEVSE/ESP32_WiFi_V3.x)
|
|
86
|
+
- **Legacy WiFi Firmware (ESP8266 v2.x)**: [`OpenEVSE/ESP8266_WiFi_v2.x`](https://github.com/OpenEVSE/ESP8266_WiFi_v2.x)
|
|
87
|
+
- **OpenEVSE Controller Firmware (RAPI)**: [`OpenEVSE/open_evse`](https://github.com/OpenEVSE/open_evse)
|
|
88
|
+
|
|
89
|
+
### What to Verify in Firmware Sources:
|
|
90
|
+
1. **Route & Method Handlers**:
|
|
91
|
+
- Check `src/web_server.cpp`, `src/web_server_config.cpp`, `src/web_server_*.cpp` (in `openevse_esp32_firmware` / `ESP32_WiFi_V4.x`) or `src/web_server.cpp` (in `ESP32_WiFi_V3.x` / `ESP8266_WiFi_v2.x`) to confirm HTTP methods (`GET`, `POST`, `PATCH`, `DELETE`).
|
|
92
|
+
- Confirm expected query parameters or JSON body fields (e.g. `divertmode=...`, `{"device": "gateway"}`, `{"charge_current": ...}`).
|
|
93
|
+
2. **Response Formats & Statuses**:
|
|
94
|
+
- Verify success and error response payloads (e.g., `{"msg": "done"}`, `{"result": "OK", "msg": "..."}`, or plain string messages like `"Current Shaper state changed"`).
|
|
95
|
+
- Update `SUCCESS_ANSWERS` in `openevsehttp/const.py` if new success indicators are introduced.
|
|
96
|
+
3. **Firmware Version Thresholds**:
|
|
97
|
+
- Check git history or release tags across `openevse_esp32_firmware`, `ESP32_WiFi_V3.x`, and `ESP8266_WiFi_v2.x` to determine when a route or feature was introduced, ensuring accurate `_version_check("x.y.z")` values.
|
|
98
|
+
4. **RAPI Command Specifications**:
|
|
99
|
+
- Check `src/rapi.cpp` or OpenEVSE controller docs for valid RAPI commands (e.g., `$SC`, `$FE`, `$FS`, `$FR`, `$ST`) and return formats (`$OK`, `$NK`).
|
|
100
|
+
5. **Mock Test Fixtures**:
|
|
101
|
+
- Update or add mock JSON payloads under `tests/fixtures/v4_json/` and `tests/fixtures/v2_json/` to mirror real firmware response shapes.
|
|
102
|
+
|
|
103
|
+
## Writing Tests for Commands
|
|
104
|
+
|
|
105
|
+
When testing command methods:
|
|
106
|
+
1. Use fixtures from `tests/conftest.py` (`test_charger`, `test_charger_v2`, `test_charger_new`).
|
|
107
|
+
2. Mock responses using `mock_aioclient`:
|
|
108
|
+
```python
|
|
109
|
+
mock_aioclient.post(
|
|
110
|
+
TEST_URL_CONFIG,
|
|
111
|
+
status=200,
|
|
112
|
+
body='{"msg": "done"}',
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
3. Test success paths, failure responses (`CommandFailedError`), missing state paths (`UnknownStateError`), and older firmware version behavior (`UnsupportedFeature` / RAPI commands).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# OpenEVSE HTTP API & Firmware Endpoint Matrix
|
|
2
|
+
|
|
3
|
+
This reference document compiles all known HTTP REST API endpoints, WebSocket paths, and RAPI fallback commands across OpenEVSE WiFi firmware generations:
|
|
4
|
+
- **v4.x / v5.x (Active Development)**: [`OpenEVSE/openevse_esp32_firmware`](https://github.com/OpenEVSE/openevse_esp32_firmware) (formerly `ESP32_WiFi_V4.x`).
|
|
5
|
+
- **v3.x (Legacy ESP32 - EOL)**: [`OpenEVSE/ESP32_WiFi_V3.x`](https://github.com/OpenEVSE/ESP32_WiFi_V3.x) (e.g. v3.3.1).
|
|
6
|
+
- **v2.x (Legacy ESP8266 - EOL)**: [`OpenEVSE/ESP8266_WiFi_v2.x`](https://github.com/OpenEVSE/ESP8266_WiFi_v2.x) (up to v2.9.1).
|
|
7
|
+
- **Controller Firmware**: [`OpenEVSE/open_evse`](https://github.com/OpenEVSE/open_evse) (RAPI backend).
|
|
8
|
+
|
|
9
|
+
> [!IMPORTANT]
|
|
10
|
+
> **Active Development Warning**: Development on **v2.x** and **v3.x** has ended. Active development occurs exclusively on **v4.x / v5.x** in `OpenEVSE/openevse_esp32_firmware`. Whenever validating new features, routes, or bugfixes, always inspect `OpenEVSE/openevse_esp32_firmware` as the primary source of truth.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Endpoint Availability Matrix
|
|
15
|
+
|
|
16
|
+
| Route | Supported Methods | v2.x (ESP8266) | v3.x (ESP32) | v4.x / v5.x (ESP32) | RAPI Fallback (v2/v3) | python-openevse-http Status |
|
|
17
|
+
| :--- | :--- | :---: | :---: | :---: | :--- | :---: |
|
|
18
|
+
| `/status` | `GET`, `POST` | `GET` only | `GET` only | `GET`, `POST` | N/A | ✅ Fully Supported |
|
|
19
|
+
| `/config` | `GET`, `POST` | `GET`, `POST` | `GET`, `POST` | `GET`, `POST` | N/A | ✅ Fully Supported |
|
|
20
|
+
| `/override` | `GET`, `POST`, `PATCH`, `DELETE` | ❌ | ❌ | ✅ (v4.0.0+) | `$FE` (enable) / `$FS` (sleep), `$SC` | ✅ Fully Supported |
|
|
21
|
+
| `/claims` | `GET`, `POST`, `DELETE` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ✅ Fully Supported |
|
|
22
|
+
| `/limit` | `GET`, `POST`, `DELETE` | ❌ | ❌ | ✅ (v4.0.0+) | `$SH` (kWh limit), `$S3` (time limit) | ✅ Fully Supported |
|
|
23
|
+
| `/shaper` | `POST` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ✅ Fully Supported |
|
|
24
|
+
| `/divertmode` | `POST` | ✅ (`/divertmode`) | ✅ (`/divertmode`) | ✅ (`/divertmode`) | N/A (updates config flags) | ✅ Fully Supported |
|
|
25
|
+
| `/restart` | `POST` | ✅ (`/restart`) | ✅ (`/restart`) | ✅ (`/restart`) | `$FR` (EVSE controller reboot) | ✅ Fully Supported |
|
|
26
|
+
| `/r` or `/rapi` | `GET`, `POST` | `GET` (html/json) | `GET`, `POST` | `POST` (Mongoose) | Direct RAPI | ✅ Fully Supported |
|
|
27
|
+
| `/ws` | `WebSocket` | ✅ | ✅ | ✅ | N/A | ✅ Fully Supported |
|
|
28
|
+
| `/schedule` | `GET`, `POST`, `DELETE` | ❌ | ❌ | ✅ (v4.0.0+) | `$ST` / `$GD` | ✅ Fully Supported |
|
|
29
|
+
| `/schedule/plan` | `GET` | ❌ | ❌ | ✅ (v4.1.0+) | N/A | ✅ Fully Supported |
|
|
30
|
+
| `/time` | `GET`, `POST` | ❌ | ❌ (`/settime`) | ✅ (v4.0.0+) | `$S1` (RTC set) | ✅ Fully Supported |
|
|
31
|
+
| `/settime` | `GET`, `POST` | ❌ | ✅ | ⚠️ Legacy alias | `$S1` | ✅ Fully Supported |
|
|
32
|
+
| `/emeter` | `DELETE` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ❌ Not Implemented |
|
|
33
|
+
| `/notifications` | `GET` | ❌ | ❌ | ✅ (v5.1.0+) | N/A | ❌ Not Implemented |
|
|
34
|
+
| `/notifications/ack` | `POST` | ❌ | ❌ | ✅ (v5.1.0+) | N/A | ❌ Not Implemented |
|
|
35
|
+
| `/update` | `GET`, `POST` | `GET`, `POST` | `GET`, `POST` | `GET`, `POST` | N/A | ✅ Fully Supported |
|
|
36
|
+
| `/logs` | `GET` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ✅ Fully Supported |
|
|
37
|
+
| `/logs/export` | `GET` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ❌ Not Implemented |
|
|
38
|
+
| `/certificates` | `GET`, `POST`, `DELETE` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ✅ Fully Supported |
|
|
39
|
+
| `/scan` | `GET` | ✅ | ✅ | ✅ | N/A | ❌ Not Implemented |
|
|
40
|
+
| `/apoff` | `GET`, `POST` | ✅ | ✅ | ✅ | N/A | ❌ Not Implemented |
|
|
41
|
+
| `/reset` | `GET`, `POST` | ✅ | ✅ | ✅ | N/A | ❌ Not Implemented |
|
|
42
|
+
| `/rfid/add` | `POST` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ✅ Fully Supported |
|
|
43
|
+
| `/rfid/users` | `GET`, `POST`, `DELETE` | ❌ | ❌ | ✅ (v5.0.0+) | N/A | ✅ Fully Supported |
|
|
44
|
+
| `/relay/reset` | `POST` | ❌ | ❌ | ✅ (v5.1.0+) | `$FH` | ✅ Fully Supported |
|
|
45
|
+
| `/relay/recovery` | `POST` | ❌ | ❌ | ✅ (v5.1.0+) | `$FK` | ✅ Fully Supported |
|
|
46
|
+
| `/cabletemp` | `GET`, `POST` | ❌ | ❌ | ✅ (v5.1.0+) | `$GN`, `$SN` | ✅ Fully Supported |
|
|
47
|
+
| `/teslaveh` / `/tesla/vehicles` | `GET` | ❌ | ✅ (`/teslaveh`) | ✅ | N/A | ❌ Not Implemented |
|
|
48
|
+
| `/energy/raw`, `/daily`, etc. | `GET` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ❌ Not Implemented |
|
|
49
|
+
| `/migrate/expand16mb` | `POST` | ❌ | ❌ | ✅ (v5.1.0+) | N/A | ❌ Not Implemented |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 2. Core Endpoint Payload Differences
|
|
54
|
+
|
|
55
|
+
### A. `/config` (Device Configuration & Identification)
|
|
56
|
+
|
|
57
|
+
| Field Key | Type | v2.x (ESP8266) | v3.x (ESP32) | v4.x / v5.x (ESP32) | Notes |
|
|
58
|
+
| :--- | :--- | :---: | :---: | :---: | :--- |
|
|
59
|
+
| `wifi_serial` | `string` | ❌ (None) | ❌ (None) | ✅ | Formatted uppercase hardware MAC (e.g. `1234567890AB`). **Missing on v2/v3!** |
|
|
60
|
+
| `hostname` | `string` | ✅ (`"openevse"`) | ✅ (`"openevse-XXXX"`) | ✅ (`"openevse-XXXX"`) | Configured hostname. |
|
|
61
|
+
| `mqtt_announce_topic` | `string` | ✅ | ✅ | ✅ | Defaults to `"openevse/announce/XXXX"`. |
|
|
62
|
+
| `version` | `string` | ✅ (`"2.9.1"`) | ✅ (`"3.3.1"`) | ✅ (`"4.1.2"`) | WiFi gateway firmware version string. |
|
|
63
|
+
| `firmware` | `string` | ✅ (`"5.0.1"`) | ✅ | ✅ (`"7.1.3"`) | EVSE controller firmware version string. |
|
|
64
|
+
| `protocol` | `string` | ✅ (`"4.0.1"`) | ✅ (`"-"`) | ✅ (`"-"`) | RAPI protocol version. |
|
|
65
|
+
| `buildenv` | `string` | ❌ | ❌ | ✅ (e.g. `"openevse_wifi_v1"`) | PlatformIO build target name. |
|
|
66
|
+
| `d9_support` | `bool` | ❌ | ❌ | ✅ (v5.x) | Signals OpenEVSE D9+ controller hardware support. |
|
|
67
|
+
| `rfid_enabled` | `bool` | ❌ | ❌ | ✅ (v4.1.0+) | Virtual flag mask. |
|
|
68
|
+
| `led_brightness` | `int` | ❌ | ❌ | ✅ (v4.1.0+) | RGB LED brightness (0-255). |
|
|
69
|
+
| `flags` | `int` | ✅ (24-bit) | ✅ (24-bit) | ✅ (32-bit) | Bitmask of enabled services and configurations. |
|
|
70
|
+
|
|
71
|
+
### B. `/status` (Telemetry & State Monitoring)
|
|
72
|
+
|
|
73
|
+
| Field Key | Type | v2.x (ESP8266) | v3.x (ESP32) | v4.x / v5.x (ESP32) | Notes |
|
|
74
|
+
| :--- | :--- | :---: | :---: | :---: | :--- |
|
|
75
|
+
| `state` | `int` | ✅ | ✅ | ✅ | EVSE state: 1=Ready, 2=Connected, 3=Charging, 254=Sleeping, 255=Disabled. |
|
|
76
|
+
| `amp` | `float` | ✅ | ✅ | ✅ | Charging current in Amperes. |
|
|
77
|
+
| `voltage` | `int\|float` | ✅ | ✅ | ✅ | Voltage in Volts (e.g. 240). |
|
|
78
|
+
| `pilot` | `int` | ✅ | ✅ | ✅ | Pilot limit in Amperes. |
|
|
79
|
+
| `macaddress` | `string` | ❌ | ❌ | ✅ (v4.x+) | Hardware MAC address (e.g. `"AA:BB:CC:DD:EE:FF"`). |
|
|
80
|
+
| `ipaddress` | `string` | ✅ | ✅ | ✅ | Local IP address. |
|
|
81
|
+
| `shaper` | `int` | ❌ | ❌ | ✅ (v4.0.0+) | Current Shaper active: 0=Disabled, 1=Enabled. |
|
|
82
|
+
| `shaper_live_pwr` | `int` | ❌ | ❌ | ✅ (v4.0.0+) | Shaper live grid power (W). |
|
|
83
|
+
| `shaper_cur` | `float` | ❌ | ❌ | ✅ (v4.0.0+) | Shaper dynamically calculated max current (A). |
|
|
84
|
+
| `vehicle_soc` | `int` | ❌ | ❌ | ✅ (v4.1.0+) | Vehicle battery SoC (%). Pushed via `POST /status`. |
|
|
85
|
+
| `vehicle_range` | `int` | ❌ | ❌ | ✅ (v4.1.0+) | Vehicle range (km or miles). |
|
|
86
|
+
| `time_to_full_charge` | `int` | ❌ | ❌ | ✅ (v4.1.0+) | Seconds until complete charge. |
|
|
87
|
+
| `notifications` | `object` | ❌ | ❌ | ✅ (v5.1.0+) | Summary notifications object `{"count": N, "severity": "..."}`. |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. Actuator Security & CSRF (`actuatorMethodAllowed`)
|
|
92
|
+
|
|
93
|
+
In **v4.x / v5.x** firmware, destructive actuators (`/reset`, `/restart`, `/apoff`, `/divertmode`, `/shaper`, `/settime`, `/rfid/add`, `/relay/reset`, `/relay/recovery`) include CSRF protection:
|
|
94
|
+
- Calls must be sent via **`POST`** (or non-GET HTTP methods).
|
|
95
|
+
- If sent via `GET`, the firmware rejects with `403 Forbidden` unless the header `X-Requested-With: OpenEVSE` is present.
|
|
96
|
+
- `python-openevse-http` always sends actuators via `POST` or `PATCH`.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 4. Upstream Repository Reference Links
|
|
101
|
+
|
|
102
|
+
When cross-checking implementation or adding support for new firmware features:
|
|
103
|
+
- **Active WiFi Gateway Codebase**: [`OpenEVSE/openevse_esp32_firmware`](https://github.com/OpenEVSE/openevse_esp32_firmware)
|
|
104
|
+
- HTTP routes & endpoints: `src/web_server.cpp`
|
|
105
|
+
- `/config` serialization: `src/web_server_config.cpp` & `src/app_config.cpp`
|
|
106
|
+
- Shaper & Claims: `src/current_shaper.cpp`, `src/web_server_claims.cpp`
|
|
107
|
+
- Override handler: `src/web_server.cpp` (`handleOverride`)
|
|
108
|
+
- **Legacy ESP32 Firmware (EOL)**: [`OpenEVSE/ESP32_WiFi_V3.x`](https://github.com/OpenEVSE/ESP32_WiFi_V3.x)
|
|
109
|
+
- **Legacy ESP8266 Firmware (EOL)**: [`OpenEVSE/ESP8266_WiFi_v2.x`](https://github.com/OpenEVSE/ESP8266_WiFi_v2.x)
|
|
110
|
+
- **Controller RAPI Source**: [`OpenEVSE/open_evse`](https://github.com/OpenEVSE/open_evse) (specifically `src/rapi.cpp`)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openevse-dev-workflow
|
|
3
|
+
description: >-
|
|
4
|
+
Use this skill when running tests, formatting code, checking linters,
|
|
5
|
+
running type checks, or managing tox environments in python-openevse-http.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# OpenEVSE Development & Testing Workflow
|
|
9
|
+
|
|
10
|
+
This skill guides you through executing tests, linting, formatting, and type checks within the `python-openevse-http` repository.
|
|
11
|
+
|
|
12
|
+
## Environment & Tooling
|
|
13
|
+
|
|
14
|
+
The project uses `tox` for managing isolated virtual environments and running test tools (`pytest`, `ruff`, `mypy`).
|
|
15
|
+
|
|
16
|
+
### 1. Running Unit Tests
|
|
17
|
+
|
|
18
|
+
Run full test suite via tox:
|
|
19
|
+
```bash
|
|
20
|
+
tox -e py314
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
To run fast targeted test runs with the existing tox environment:
|
|
24
|
+
```bash
|
|
25
|
+
# Run all tests
|
|
26
|
+
.tox/py314/bin/pytest
|
|
27
|
+
|
|
28
|
+
# Run a specific test file
|
|
29
|
+
.tox/py314/bin/pytest tests/test_commands.py
|
|
30
|
+
|
|
31
|
+
# Run a single test function
|
|
32
|
+
.tox/py314/bin/pytest tests/test_commands.py -k "test_toggle_override"
|
|
33
|
+
|
|
34
|
+
# Run with verbose output and stdout
|
|
35
|
+
.tox/py314/bin/pytest -v -s tests/test_client.py
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Formatting & Linting (Ruff)
|
|
39
|
+
|
|
40
|
+
Check formatting and linting:
|
|
41
|
+
```bash
|
|
42
|
+
tox -e lint
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
To auto-format or auto-fix lint errors:
|
|
46
|
+
```bash
|
|
47
|
+
# Format code
|
|
48
|
+
.tox/lint/bin/ruff format ./
|
|
49
|
+
|
|
50
|
+
# Auto-fix linting issues
|
|
51
|
+
.tox/lint/bin/ruff check --fix openevsehttp tests
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Type Checking (Mypy)
|
|
55
|
+
|
|
56
|
+
Run static type checks:
|
|
57
|
+
```bash
|
|
58
|
+
tox -e mypy
|
|
59
|
+
```
|
|
60
|
+
Or directly:
|
|
61
|
+
```bash
|
|
62
|
+
.tox/mypy/bin/mypy openevsehttp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 4. Running All CI Checks Together
|
|
66
|
+
|
|
67
|
+
Before submitting PRs or finalizing tasks, verify everything in one step:
|
|
68
|
+
```bash
|
|
69
|
+
tox -e py314,lint,mypy
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 5. Pre-commit Hooks
|
|
73
|
+
|
|
74
|
+
Pre-commit hooks are configured via `.pre-commit-config.yaml`. They run automatically on `git commit`, or you can trigger them manually:
|
|
75
|
+
```bash
|
|
76
|
+
pre-commit run --all-files
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 6. Pull Requests & Issue Creation
|
|
80
|
+
|
|
81
|
+
- **Pull Requests**:
|
|
82
|
+
- Always use the template in [`.github/pull_request_template.md`](../../.github/pull_request_template.md).
|
|
83
|
+
- Include a summary, issue link (`Fixes #<number>`), type of change, and completed checklist.
|
|
84
|
+
- Follow conventional commits in PR titles (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`).
|
|
85
|
+
- **Issues & Feature Requests**:
|
|
86
|
+
- Use [`.github/ISSUE_TEMPLATE/bug_report.yml`](../../.github/ISSUE_TEMPLATE/bug_report.yml) for bugs (`[Bug]: <summary>`).
|
|
87
|
+
- Use [`.github/ISSUE_TEMPLATE/feature_request.yml`](../../.github/ISSUE_TEMPLATE/feature_request.yml) for feature requests (`[Feature Request]: <summary>`).
|
|
@@ -34,10 +34,10 @@ jobs:
|
|
|
34
34
|
runs-on: ubuntu-latest
|
|
35
35
|
timeout-minutes: 3
|
|
36
36
|
steps:
|
|
37
|
-
- uses: step-security/harden-runner@
|
|
37
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
38
38
|
with:
|
|
39
39
|
egress-policy: audit
|
|
40
40
|
|
|
41
|
-
- uses: release-drafter/release-drafter/autolabeler@
|
|
41
|
+
- uses: release-drafter/release-drafter/autolabeler@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7
|
|
42
42
|
env:
|
|
43
43
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -15,13 +15,13 @@ jobs:
|
|
|
15
15
|
linkChecker:
|
|
16
16
|
runs-on: ubuntu-latest
|
|
17
17
|
steps:
|
|
18
|
-
- uses: step-security/harden-runner@
|
|
18
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
19
19
|
with:
|
|
20
20
|
egress-policy: audit
|
|
21
21
|
|
|
22
|
-
- uses: actions/checkout@
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
23
|
|
|
24
24
|
- name: Link Checker
|
|
25
|
-
uses: lycheeverse/lychee-action@
|
|
25
|
+
uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2
|
|
26
26
|
with:
|
|
27
27
|
args: --verbose --no-progress './**/*.md'
|
{python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/workflows/publish-to-pypi.yml
RENAMED
|
@@ -21,17 +21,17 @@ jobs:
|
|
|
21
21
|
contents: read
|
|
22
22
|
id-token: write
|
|
23
23
|
steps:
|
|
24
|
-
- uses: step-security/harden-runner@
|
|
24
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
25
25
|
with:
|
|
26
26
|
egress-policy: audit
|
|
27
27
|
|
|
28
|
-
- uses: actions/checkout@
|
|
28
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
29
29
|
with:
|
|
30
30
|
ref: ${{ inputs.tag || github.ref }}
|
|
31
31
|
fetch-depth: 0
|
|
32
32
|
|
|
33
33
|
- name: Install uv
|
|
34
|
-
uses: astral-sh/setup-uv@
|
|
34
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
35
35
|
with:
|
|
36
36
|
enable-cache: true
|
|
37
37
|
version: "0.10.9"
|
|
@@ -43,4 +43,4 @@ jobs:
|
|
|
43
43
|
run: uv build
|
|
44
44
|
|
|
45
45
|
- name: Publish release to PyPI
|
|
46
|
-
uses: pypa/gh-action-pypi-publish@
|
|
46
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
{python_openevse_http-1.5.0 → python_openevse_http-1.7.0}/.github/workflows/release-drafter.yml
RENAMED
|
@@ -13,11 +13,11 @@ jobs:
|
|
|
13
13
|
update_release_draft:
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
steps:
|
|
16
|
-
- uses: step-security/harden-runner@
|
|
16
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
17
17
|
with:
|
|
18
18
|
egress-policy: audit
|
|
19
19
|
|
|
20
20
|
# Drafts your next Release notes as Pull Requests are merged into "main"
|
|
21
|
-
- uses: release-drafter/release-drafter@
|
|
21
|
+
- uses: release-drafter/release-drafter@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7
|
|
22
22
|
env:
|
|
23
23
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -18,17 +18,17 @@ jobs:
|
|
|
18
18
|
prek:
|
|
19
19
|
runs-on: ubuntu-latest
|
|
20
20
|
steps:
|
|
21
|
-
- uses: step-security/harden-runner@
|
|
21
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
22
22
|
with:
|
|
23
23
|
egress-policy: audit
|
|
24
24
|
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
26
|
- name: Set up Python
|
|
27
|
-
uses: actions/setup-python@
|
|
27
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
28
28
|
with:
|
|
29
29
|
python-version: "3.14"
|
|
30
|
-
- name: Install
|
|
31
|
-
run: pip install
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: pip install -r requirements_lint.txt
|
|
32
32
|
- name: Run prek
|
|
33
33
|
run: prek run --all-files --skip no-commit-to-branch
|
|
34
34
|
build:
|
|
@@ -41,15 +41,15 @@ jobs:
|
|
|
41
41
|
- "3.14"
|
|
42
42
|
|
|
43
43
|
steps:
|
|
44
|
-
- uses: step-security/harden-runner@
|
|
44
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
45
45
|
with:
|
|
46
46
|
egress-policy: audit
|
|
47
47
|
|
|
48
|
-
- uses: actions/checkout@
|
|
48
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
49
49
|
with:
|
|
50
50
|
fetch-depth: 2
|
|
51
51
|
- name: Set up Python ${{ matrix.python-version }}
|
|
52
|
-
uses: actions/setup-python@
|
|
52
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
53
53
|
with:
|
|
54
54
|
python-version: ${{ matrix.python-version }}
|
|
55
55
|
- name: Install dependencies
|
|
@@ -69,12 +69,12 @@ jobs:
|
|
|
69
69
|
runs-on: ubuntu-latest
|
|
70
70
|
needs: build
|
|
71
71
|
steps:
|
|
72
|
-
- uses: step-security/harden-runner@
|
|
72
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
73
73
|
with:
|
|
74
74
|
egress-policy: audit
|
|
75
75
|
|
|
76
76
|
- name: Check out the repository
|
|
77
|
-
uses: actions/checkout@
|
|
77
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
78
78
|
with:
|
|
79
79
|
fetch-depth: 2
|
|
80
80
|
- name: Download coverage data
|
|
@@ -82,6 +82,6 @@ jobs:
|
|
|
82
82
|
with:
|
|
83
83
|
name: coverage-data
|
|
84
84
|
- name: Upload coverage report
|
|
85
|
-
uses: codecov/codecov-action@
|
|
85
|
+
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
|
86
86
|
with:
|
|
87
87
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Agent Guidelines for python-openevse-http
|
|
2
|
+
|
|
3
|
+
This document outlines key architecture, conventions, workflows, and testing practices for agentic assistants operating in this repository.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Project Overview & Architecture
|
|
8
|
+
|
|
9
|
+
`python-openevse-http` is an asynchronous Python library for interacting with OpenEVSE electric vehicle chargers via their HTTP REST API, WebSocket streams, and RAPI commands.
|
|
10
|
+
|
|
11
|
+
### Core Modules & Mixins
|
|
12
|
+
The main client class `OpenEVSE` in `openevsehttp/client.py` inherits from multiple mixins:
|
|
13
|
+
- **`openevsehttp/client.py`**: Core client lifecycle, authentication, request processing (`process_request`, `send_command`), status updates (`update`), and session management.
|
|
14
|
+
- **`openevsehttp/commands.py` (`CommandsMixin`)**: Charger commands (e.g. `set_override`, `toggle_override`, `clear_override`, `set_current`, `set_charge_mode`, `divert_mode`, `set_shaper`, `toggle_shaper`, `restart_wifi`, `restart_evse`, `update_firmware`).
|
|
15
|
+
- **`openevsehttp/properties.py` (`PropertiesMixin`)**: Charger properties, configuration parsing, state decoding (`states`, `divert_mode`), firmware version parsing.
|
|
16
|
+
- **`openevsehttp/sensors.py` (`SensorsMixin`)**: Sensor values, telemetry, power/voltage calculations.
|
|
17
|
+
- **`openevsehttp/websocket.py` (`WebsocketMixin`, `OpenEVSEWebsocket`)**: Real-time websocket communication and state change listeners.
|
|
18
|
+
- **`openevsehttp/exceptions.py`**: Typed library exceptions inheriting from `OpenEVSEError`.
|
|
19
|
+
|
|
20
|
+
### Client Session Requirement
|
|
21
|
+
- `OpenEVSE` uses caller-provided `aiohttp.ClientSession` (via `session=...`). If not provided, accessing network operations raises `RuntimeError(ERROR_SESSION_REQUIRED)`.
|
|
22
|
+
- The session must run on the active event loop.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. Firmware Version Handling & Upstream Validation
|
|
27
|
+
|
|
28
|
+
OpenEVSE chargers run various firmware versions (v2.x, v3.x, v4.x, v5.x) with different capabilities:
|
|
29
|
+
- **`self._version_check(min_version, max_version="")`**: Use this helper to conditionally execute HTTP API endpoints (v4+) versus RAPI command fallbacks (v2/v3, e.g. `$FE`, `$FS`, `$SC`, `$FR`).
|
|
30
|
+
- Always handle version edge cases (e.g. non-semver development strings like `4.1.2.dev`).
|
|
31
|
+
- Raise `UnsupportedFeature` if a feature is not supported on older firmware.
|
|
32
|
+
|
|
33
|
+
### Validating Endpoints Against Firmware Sources
|
|
34
|
+
When adding or updating endpoints, payload keys, or RAPI commands, cross-reference against:
|
|
35
|
+
- **WiFi Gateway (Current ESP32 v4/v5)**: [`OpenEVSE/openevse_esp32_firmware`](https://github.com/OpenEVSE/openevse_esp32_firmware) (formerly [`OpenEVSE/ESP32_WiFi_V4.x`](https://github.com/OpenEVSE/ESP32_WiFi_V4.x))
|
|
36
|
+
- **WiFi Gateway (Legacy ESP32 v3.x)**: [`OpenEVSE/ESP32_WiFi_V3.x`](https://github.com/OpenEVSE/ESP32_WiFi_V3.x)
|
|
37
|
+
- **Legacy WiFi (ESP8266 v2.x)**: [`OpenEVSE/ESP8266_WiFi_v2.x`](https://github.com/OpenEVSE/ESP8266_WiFi_v2.x)
|
|
38
|
+
- **Controller / RAPI**: [`OpenEVSE/open_evse`](https://github.com/OpenEVSE/open_evse) (commands in `src/rapi.cpp`)
|
|
39
|
+
Verify HTTP methods, expected JSON fields, success/error payload shapes, and version thresholds.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. Exception Handling
|
|
44
|
+
|
|
45
|
+
All custom exceptions inherit from `OpenEVSEError(Exception)`:
|
|
46
|
+
- `CommandFailedError`: Command execution failure, RAPI rejection (`$NK`), or error HTTP response.
|
|
47
|
+
- `UnknownStateError`: Required state or configuration missing before command execution (e.g. toggle state).
|
|
48
|
+
- `FirmwareResolutionError`: GitHub release asset resolution failure.
|
|
49
|
+
- `AuthenticationError`: HTTP 401 / auth failures.
|
|
50
|
+
- `UnsupportedFeature`: Feature not available for current firmware version.
|
|
51
|
+
- `ParseJSONError`, `InvalidType`, `MissingMethod`, `MissingSerial`, `AlreadyListening`.
|
|
52
|
+
|
|
53
|
+
Export all public exception classes in `openevsehttp/__init__.py`.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 4. Development & Testing Workflow
|
|
58
|
+
|
|
59
|
+
### Running Tests
|
|
60
|
+
Use `tox` for isolated environments:
|
|
61
|
+
```bash
|
|
62
|
+
# Run unit tests on Python 3.14 / active environment
|
|
63
|
+
tox -e py314
|
|
64
|
+
|
|
65
|
+
# Or run pytest directly within the tox environment
|
|
66
|
+
.tox/py314/bin/pytest
|
|
67
|
+
|
|
68
|
+
# Target specific test files
|
|
69
|
+
.tox/py314/bin/pytest tests/test_commands.py -k "test_toggle_override"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Linting & Formatting
|
|
73
|
+
```bash
|
|
74
|
+
# Run ruff formatting check & linter
|
|
75
|
+
tox -e lint
|
|
76
|
+
|
|
77
|
+
# Format code automatically
|
|
78
|
+
.tox/lint/bin/ruff format ./
|
|
79
|
+
|
|
80
|
+
# Run linter with auto-fixes
|
|
81
|
+
.tox/lint/bin/ruff check --fix openevsehttp tests
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Type Checking
|
|
85
|
+
```bash
|
|
86
|
+
tox -e mypy
|
|
87
|
+
# Or directly:
|
|
88
|
+
.tox/mypy/bin/mypy openevsehttp
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 5. Testing & Mocking Guidelines
|
|
94
|
+
|
|
95
|
+
- Tests use `pytest` with `pytest-asyncio` (`asyncio_default_fixture_loop_scope = "function"`).
|
|
96
|
+
- Test fixtures in `tests/conftest.py`:
|
|
97
|
+
- `test_charger`: Standard v4 charger client with mocked endpoints.
|
|
98
|
+
- `test_charger_v2`: Legacy v2 firmware mock.
|
|
99
|
+
- `test_charger_new`: Newer v4 fixture with shaper and modern endpoints.
|
|
100
|
+
- `test_charger_auth`: Authenticated charger mock.
|
|
101
|
+
- `mock_aioclient`: `AiohttpClientMocker` instance for intercepting HTTP requests (`get`, `post`, `patch`, `delete`).
|
|
102
|
+
- Fixture data files are located in `tests/fixtures/` (`v4_json/`, `v2_json/`).
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 6. Commit, Pull Request & Issue Guidelines
|
|
107
|
+
|
|
108
|
+
### Creating Pull Requests
|
|
109
|
+
- **Use the PR Template**: Always structure PR descriptions according to [`.github/pull_request_template.md`](.github/pull_request_template.md):
|
|
110
|
+
- **Description**: Provide a clear summary of changes, motivation, and link related issues (`Fixes #<number>`).
|
|
111
|
+
- **Type of change**: Check the relevant boxes (`Bug fix`, `New feature`, `Breaking change`, `Code quality / Refactoring`, `Documentation update`).
|
|
112
|
+
- **Checklist**: Complete all checklist items before opening or marking ready for review.
|
|
113
|
+
- **Semantic PR Titles**: Use conventional commit titles matching [`.github/release-drafter.yml`](.github/release-drafter.yml):
|
|
114
|
+
- `feat:` New features / enhancements
|
|
115
|
+
- `fix:` Bug fixes
|
|
116
|
+
- `refactor:` Refactoring / code quality
|
|
117
|
+
- `test:` Test additions / updates
|
|
118
|
+
- `docs:` Documentation changes
|
|
119
|
+
- `chore:` Maintenance / dependency updates
|
|
120
|
+
- Ensure all tests (`tox -e py314`), linting (`tox -e lint`), and type checks (`tox -e mypy`) pass before submitting PRs.
|
|
121
|
+
|
|
122
|
+
### Creating Issues & Feature Requests
|
|
123
|
+
Always follow the templates in [`.github/ISSUE_TEMPLATE/`](.github/ISSUE_TEMPLATE/):
|
|
124
|
+
- **Bug Reports** ([`bug_report.yml`](.github/ISSUE_TEMPLATE/bug_report.yml)):
|
|
125
|
+
- Prefix title with `[Bug]: <summary>`.
|
|
126
|
+
- Include: Description, Steps to Reproduce, Expected Behavior, Environment Info (Library version, Python version, OpenEVSE WiFi Firmware version), and Debug Logs / Stack Trace.
|
|
127
|
+
- **Feature Requests** ([`feature_request.yml`](.github/ISSUE_TEMPLATE/feature_request.yml)):
|
|
128
|
+
- Prefix title with `[Feature Request]: <summary>`.
|
|
129
|
+
- Include: Problem statement, Desired solution, Alternatives considered, and Context.
|
|
@@ -19,6 +19,7 @@ The `python-openevse-http` library requires you to pass an external `aiohttp.Cli
|
|
|
19
19
|
import aiohttp
|
|
20
20
|
from openevsehttp import OpenEVSE
|
|
21
21
|
|
|
22
|
+
|
|
22
23
|
async def main():
|
|
23
24
|
timeout = aiohttp.ClientTimeout(total=30)
|
|
24
25
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
@@ -34,6 +35,7 @@ async def main():
|
|
|
34
35
|
import aiohttp
|
|
35
36
|
from openevsehttp import OpenEVSE
|
|
36
37
|
|
|
38
|
+
|
|
37
39
|
async def main():
|
|
38
40
|
async with aiohttp.ClientSession() as session:
|
|
39
41
|
charger1 = OpenEVSE("charger1.local", session=session)
|
|
@@ -52,6 +54,7 @@ Start websocket listening from the same event loop that owns the
|
|
|
52
54
|
import aiohttp
|
|
53
55
|
from openevsehttp import OpenEVSE
|
|
54
56
|
|
|
57
|
+
|
|
55
58
|
async def main():
|
|
56
59
|
async with aiohttp.ClientSession() as session:
|
|
57
60
|
charger = OpenEVSE("openevse.local", session=session)
|