python-openevse-http 1.6.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.
Files changed (88) hide show
  1. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.agents/skills/openevse-api-guide/SKILL.md +19 -4
  2. python_openevse_http-1.7.0/.agents/skills/openevse-api-guide/references/endpoints_matrix.md +110 -0
  3. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/workflows/publish-to-pypi.yml +1 -1
  4. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/workflows/test.yml +1 -1
  5. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/AGENTS.md +3 -2
  6. {python_openevse_http-1.6.0/python_openevse_http.egg-info → python_openevse_http-1.7.0}/PKG-INFO +34 -7
  7. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/README.md +33 -6
  8. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/client.py +85 -2
  9. python_openevse_http-1.7.0/openevsehttp/commands.py +33 -0
  10. python_openevse_http-1.7.0/openevsehttp/commands_base.py +66 -0
  11. python_openevse_http-1.6.0/openevsehttp/commands.py → python_openevse_http-1.7.0/openevsehttp/commands_core.py +18 -297
  12. python_openevse_http-1.7.0/openevsehttp/commands_diagnostics.py +229 -0
  13. python_openevse_http-1.7.0/openevsehttp/commands_firmware.py +260 -0
  14. python_openevse_http-1.7.0/openevsehttp/commands_schedule.py +147 -0
  15. python_openevse_http-1.7.0/openevsehttp/commands_security.py +265 -0
  16. python_openevse_http-1.7.0/openevsehttp/commands_time.py +186 -0
  17. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/const.py +10 -1
  18. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/exceptions.py +13 -0
  19. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/managers.py +6 -1
  20. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/properties.py +95 -0
  21. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/sensors.py +6 -1
  22. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/pyproject.toml +1 -1
  23. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0/python_openevse_http.egg-info}/PKG-INFO +34 -7
  24. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/SOURCES.txt +8 -0
  25. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/scm_file_list.json +8 -0
  26. python_openevse_http-1.7.0/python_openevse_http.egg-info/scm_version.json +8 -0
  27. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/requirements_lint.txt +2 -2
  28. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/requirements_test.txt +2 -2
  29. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/conftest.py +6 -2
  30. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_client.py +183 -1
  31. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_commands.py +1348 -1
  32. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_managers.py +4 -4
  33. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_mixins.py +31 -1
  34. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_properties.py +72 -0
  35. python_openevse_http-1.6.0/python_openevse_http.egg-info/scm_version.json +0 -8
  36. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.agents/skills/openevse-dev-workflow/SKILL.md +0 -0
  37. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  38. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  39. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  40. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/dependabot.yml +0 -0
  41. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/pull_request_template.md +0 -0
  42. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/release-drafter.yml +0 -0
  43. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/workflows/autolabeler.yml +0 -0
  44. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/workflows/links.yml +0 -0
  45. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.github/workflows/release-drafter.yml +0 -0
  46. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.gitignore +0 -0
  47. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.pre-commit-config.yaml +0 -0
  48. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/.yamllint +0 -0
  49. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/EXTERNAL_SESSION.md +0 -0
  50. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/LICENSE +0 -0
  51. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/codecov.yml +0 -0
  52. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/example_external_session.py +0 -0
  53. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/__init__.py +0 -0
  54. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/__main__.py +0 -0
  55. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/py.typed +0 -0
  56. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/utils.py +0 -0
  57. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/openevsehttp/websocket.py +0 -0
  58. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/dependency_links.txt +0 -0
  59. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/not-zip-safe +0 -0
  60. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/requires.txt +0 -0
  61. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/python_openevse_http.egg-info/top_level.txt +0 -0
  62. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/requirements.txt +0 -0
  63. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/setup.cfg +0 -0
  64. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/setup.py +0 -0
  65. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/__init__.py +0 -0
  66. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/common.py +0 -0
  67. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/github_v2.json +0 -0
  68. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/github_v4.json +0 -0
  69. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v2_json/config.json +0 -0
  70. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v2_json/status.json +0 -0
  71. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
  72. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-broken.json +0 -0
  73. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-dev.json +0 -0
  74. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-extra-version.json +0 -0
  75. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-new.json +0 -0
  76. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config-unknown-semver.json +0 -0
  77. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/config.json +0 -0
  78. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/schedule.json +0 -0
  79. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/status-broken.json +0 -0
  80. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/status-new.json +0 -0
  81. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/v4_json/status.json +0 -0
  82. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/fixtures/websocket.json +0 -0
  83. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_external_session.py +0 -0
  84. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_main_edge_cases.py +0 -0
  85. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_sensors.py +0 -0
  86. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_shaper.py +0 -0
  87. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tests/test_websocket.py +0 -0
  88. {python_openevse_http-1.6.0 → python_openevse_http-1.7.0}/tox.ini +0 -0
@@ -19,6 +19,9 @@ The main client `OpenEVSE` combines several mixins:
19
19
 
20
20
  ## Endpoints & RAPI Commands Reference
21
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
+
22
25
  | Action | HTTP Endpoint (v4+) | RAPI Command (v2/v3) | Method |
23
26
  | :--- | :--- | :--- | :--- |
24
27
  | Status | `/status` | N/A | GET |
@@ -29,6 +32,17 @@ The main client `OpenEVSE` combines several mixins:
29
32
  | Divert Mode | `/divertmode` or `/config` | N/A | POST |
30
33
  | Module Restart | `/restart` (`device: gateway\|evse`) | `$FR` (evse restart) | POST |
31
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.
32
46
 
33
47
  ## Firmware Version Branching
34
48
 
@@ -67,19 +81,20 @@ from .exceptions import CommandFailedError, UnknownStateError, UnsupportedFeatur
67
81
 
68
82
  When adding, modifying, or debugging endpoints and RAPI commands, cross-reference against the upstream OpenEVSE firmware sources:
69
83
 
70
- - **WiFi Gateway Firmware (v3/v4/v5)**: [`OpenEVSE/ESP32_WiFi_V4.x`](https://github.com/OpenEVSE/ESP32_WiFi_V4.x)
71
- - **Legacy WiFi Firmware (v2)**: [`OpenEVSE/ESP8266_WiFi_v2.x`](https://github.com/OpenEVSE/ESP8266_WiFi_v2.x)
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)
72
87
  - **OpenEVSE Controller Firmware (RAPI)**: [`OpenEVSE/open_evse`](https://github.com/OpenEVSE/open_evse)
73
88
 
74
89
  ### What to Verify in Firmware Sources:
75
90
  1. **Route & Method Handlers**:
76
- - Check `src/http.cpp`, `src/web_server.cpp`, or `src/web_server.h` in `ESP32_WiFi_V4.x` to confirm HTTP methods (`GET`, `POST`, `PATCH`, `DELETE`).
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`).
77
92
  - Confirm expected query parameters or JSON body fields (e.g. `divertmode=...`, `{"device": "gateway"}`, `{"charge_current": ...}`).
78
93
  2. **Response Formats & Statuses**:
79
94
  - Verify success and error response payloads (e.g., `{"msg": "done"}`, `{"result": "OK", "msg": "..."}`, or plain string messages like `"Current Shaper state changed"`).
80
95
  - Update `SUCCESS_ANSWERS` in `openevsehttp/const.py` if new success indicators are introduced.
81
96
  3. **Firmware Version Thresholds**:
82
- - Check git history or release tags in `ESP32_WiFi_V4.x` to determine when a route or feature was introduced, ensuring accurate `_version_check("x.y.z")` values.
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.
83
98
  4. **RAPI Command Specifications**:
84
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`).
85
100
  5. **Mock Test Fixtures**:
@@ -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`)
@@ -31,7 +31,7 @@ jobs:
31
31
  fetch-depth: 0
32
32
 
33
33
  - name: Install uv
34
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
34
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
35
35
  with:
36
36
  enable-cache: true
37
37
  version: "0.10.9"
@@ -82,6 +82,6 @@ jobs:
82
82
  with:
83
83
  name: coverage-data
84
84
  - name: Upload coverage report
85
- uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6
85
+ uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
86
86
  with:
87
87
  token: ${{ secrets.CODECOV_TOKEN }}
@@ -32,8 +32,9 @@ OpenEVSE chargers run various firmware versions (v2.x, v3.x, v4.x, v5.x) with di
32
32
 
33
33
  ### Validating Endpoints Against Firmware Sources
34
34
  When adding or updating endpoints, payload keys, or RAPI commands, cross-reference against:
35
- - **WiFi Gateway (v3/v4/v5)**: [`OpenEVSE/ESP32_WiFi_V4.x`](https://github.com/OpenEVSE/ESP32_WiFi_V4.x) (routes in `src/http.cpp`, `src/web_server.cpp`)
36
- - **Legacy WiFi (v2)**: [`OpenEVSE/ESP8266_WiFi_v2.x`](https://github.com/OpenEVSE/ESP8266_WiFi_v2.x)
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)
37
38
  - **Controller / RAPI**: [`OpenEVSE/open_evse`](https://github.com/OpenEVSE/open_evse) (commands in `src/rapi.cpp`)
38
39
  Verify HTTP methods, expected JSON fields, success/error payload shapes, and version thresholds.
39
40
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python_openevse_http
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: Python wrapper for OpenEVSE HTTP API
5
5
  Home-page: https://github.com/firstof9/python-openevse-http
6
6
  Download-URL: https://github.com/firstof9/python-openevse-http
@@ -51,6 +51,9 @@ A Python library for communicating with [OpenEVSE](https://www.openevse.com/) ch
51
51
  - Control charging claims and limits.
52
52
  - Handle schedules.
53
53
  - **Shaper Toggle**: Enable or disable the grid shaper feature (requires firmware 4.0.0+).
54
+ - **Time Synchronization & RTC**: Manage time, timezone, and NTP sync (requires firmware 4.0.0+).
55
+ - **Event Logs**: Retrieve diagnostic log blocks and log event history (requires firmware 4.0.0+).
56
+ - **Certificates**: Manage SSL/TLS Root CA and client certificates (requires firmware 4.0.0+).
54
57
 
55
58
  ## Installation
56
59
 
@@ -116,6 +119,25 @@ To bypass certificate verification:
116
119
  )
117
120
  ```
118
121
 
122
+ ### GitHub API Token (Optional)
123
+
124
+ When performing firmware update checks (`charger.firmware_check()`) or automatic firmware updates (`charger.update_firmware()`), the library queries GitHub's Releases API. To avoid hitting unauthenticated rate limits (60 requests/hour), you can supply a GitHub personal access token (PAT):
125
+
126
+ ```python
127
+ # Provide a GitHub token during client initialization
128
+ charger = OpenEVSE(
129
+ "192.168.1.30",
130
+ session=session,
131
+ github_token="ghp_your_github_token_here",
132
+ )
133
+
134
+ # Or set / update dynamically via property
135
+ charger.github_token = "ghp_your_github_token_here"
136
+
137
+ # Or pass per-call
138
+ latest = await charger.firmware_check(github_token="ghp_your_github_token_here")
139
+ ```
140
+
119
141
  ## API Support Matrix
120
142
 
121
143
  | Endpoint | Methods | Supported | Description |
@@ -124,22 +146,27 @@ To bypass certificate verification:
124
146
  | `/config` | GET, POST | ✅ | System and WiFi configuration |
125
147
  | `/override` | GET, POST, PATCH, DELETE | ✅ | Manual charging overrides & current limits |
126
148
  | `/claims` | GET, POST, DELETE | ✅ | Client-based charging claims |
127
- | `/schedule` | GET, POST | ⚠️ | Charging schedule management (Retrieval only) |
149
+ | `/schedule` | GET, POST, DELETE | ✅ | Charging schedule management (v4.0.0+) |
128
150
  | `/limit` | GET, POST, DELETE | ✅ | Charge limits (Time, Energy, SoC) |
129
151
  | `/shaper` | POST | ✅ | Grid shaper control (v4.0.0+) |
130
152
  | `/restart` | POST | ✅ | Reboot WiFi gateway or EVSE module |
131
153
  | `/divertmode` | POST | ✅ | Solar divert mode control |
132
154
  | `/r` (RAPI) | POST | ✅ | Direct RAPI command interface |
133
155
  | `/ws` | GET | ✅ | WebSocket real-time updates |
134
- | `/time` | GET, POST | ❌ | RTC and NTP time settings |
135
- | `/logs` | GET | ❌ | System and debug event logs |
156
+ | `/time` | GET, POST | ✅ | RTC and NTP time settings (v4.0.0+) |
157
+ | `/settime` | POST | ✅ | Legacy time setting interface (v3.x / fallback to `$S1` on v2.x) |
158
+ | `/logs` | GET | ✅ | Event log block bounds and block events (v4.0.0+) |
136
159
  | `/emeter` | DELETE | ❌ | Energy meter reset |
137
160
  | `/wifi` | GET, POST | ❌ | Network scanning and AP configuration |
138
161
  | `/tesla` | GET | ❌ | Tesla vehicle integration |
139
- | `/certificates`| GET, POST, DELETE | ❌ | SSL/TLS certificate management |
140
- | `/schedule/plan`| GET | ❌ | Schedule planning and optimization |
162
+ | `/certificates`| GET, POST, DELETE | ✅ | SSL/TLS certificate management (v4.0.0+) |
163
+ | `/schedule/plan`| GET | ✅ | Schedule planning and optimization (v4.1.0+) |
141
164
  | `/update` | POST | ✅ | Firmware update interface |
142
- | `/rfid/add` | POST | ❌ | RFID tag management |
165
+ | `/relay/recovery` | POST | ✅ | Stuck relay recovery cycle (v5.1.0+ / `$FK`) |
166
+ | `/relay/reset` | POST | ✅ | Relay contact-life health estimation reset (v5.1.0+ / `$FH`) |
167
+ | `/cabletemp` | GET, POST | ✅ | Cable temperature monitor configuration and status (v5.1.0+) |
168
+ | `/rfid/add` | POST | ✅ | RFID tag pairing mode (v4.0.0+) |
169
+ | `/rfid/users` | GET, POST, DELETE | ✅ | RFID tag to user mappings (v5.0.0+) |
143
170
 
144
171
  ✅ = Fully Supported \| ⚠️ = Partial Support \| ❌ = Not yet implemented
145
172
 
@@ -18,6 +18,9 @@ A Python library for communicating with [OpenEVSE](https://www.openevse.com/) ch
18
18
  - Control charging claims and limits.
19
19
  - Handle schedules.
20
20
  - **Shaper Toggle**: Enable or disable the grid shaper feature (requires firmware 4.0.0+).
21
+ - **Time Synchronization & RTC**: Manage time, timezone, and NTP sync (requires firmware 4.0.0+).
22
+ - **Event Logs**: Retrieve diagnostic log blocks and log event history (requires firmware 4.0.0+).
23
+ - **Certificates**: Manage SSL/TLS Root CA and client certificates (requires firmware 4.0.0+).
21
24
 
22
25
  ## Installation
23
26
 
@@ -83,6 +86,25 @@ To bypass certificate verification:
83
86
  )
84
87
  ```
85
88
 
89
+ ### GitHub API Token (Optional)
90
+
91
+ When performing firmware update checks (`charger.firmware_check()`) or automatic firmware updates (`charger.update_firmware()`), the library queries GitHub's Releases API. To avoid hitting unauthenticated rate limits (60 requests/hour), you can supply a GitHub personal access token (PAT):
92
+
93
+ ```python
94
+ # Provide a GitHub token during client initialization
95
+ charger = OpenEVSE(
96
+ "192.168.1.30",
97
+ session=session,
98
+ github_token="ghp_your_github_token_here",
99
+ )
100
+
101
+ # Or set / update dynamically via property
102
+ charger.github_token = "ghp_your_github_token_here"
103
+
104
+ # Or pass per-call
105
+ latest = await charger.firmware_check(github_token="ghp_your_github_token_here")
106
+ ```
107
+
86
108
  ## API Support Matrix
87
109
 
88
110
  | Endpoint | Methods | Supported | Description |
@@ -91,22 +113,27 @@ To bypass certificate verification:
91
113
  | `/config` | GET, POST | ✅ | System and WiFi configuration |
92
114
  | `/override` | GET, POST, PATCH, DELETE | ✅ | Manual charging overrides & current limits |
93
115
  | `/claims` | GET, POST, DELETE | ✅ | Client-based charging claims |
94
- | `/schedule` | GET, POST | ⚠️ | Charging schedule management (Retrieval only) |
116
+ | `/schedule` | GET, POST, DELETE | ✅ | Charging schedule management (v4.0.0+) |
95
117
  | `/limit` | GET, POST, DELETE | ✅ | Charge limits (Time, Energy, SoC) |
96
118
  | `/shaper` | POST | ✅ | Grid shaper control (v4.0.0+) |
97
119
  | `/restart` | POST | ✅ | Reboot WiFi gateway or EVSE module |
98
120
  | `/divertmode` | POST | ✅ | Solar divert mode control |
99
121
  | `/r` (RAPI) | POST | ✅ | Direct RAPI command interface |
100
122
  | `/ws` | GET | ✅ | WebSocket real-time updates |
101
- | `/time` | GET, POST | ❌ | RTC and NTP time settings |
102
- | `/logs` | GET | ❌ | System and debug event logs |
123
+ | `/time` | GET, POST | ✅ | RTC and NTP time settings (v4.0.0+) |
124
+ | `/settime` | POST | ✅ | Legacy time setting interface (v3.x / fallback to `$S1` on v2.x) |
125
+ | `/logs` | GET | ✅ | Event log block bounds and block events (v4.0.0+) |
103
126
  | `/emeter` | DELETE | ❌ | Energy meter reset |
104
127
  | `/wifi` | GET, POST | ❌ | Network scanning and AP configuration |
105
128
  | `/tesla` | GET | ❌ | Tesla vehicle integration |
106
- | `/certificates`| GET, POST, DELETE | ❌ | SSL/TLS certificate management |
107
- | `/schedule/plan`| GET | ❌ | Schedule planning and optimization |
129
+ | `/certificates`| GET, POST, DELETE | ✅ | SSL/TLS certificate management (v4.0.0+) |
130
+ | `/schedule/plan`| GET | ✅ | Schedule planning and optimization (v4.1.0+) |
108
131
  | `/update` | POST | ✅ | Firmware update interface |
109
- | `/rfid/add` | POST | ❌ | RFID tag management |
132
+ | `/relay/recovery` | POST | ✅ | Stuck relay recovery cycle (v5.1.0+ / `$FK`) |
133
+ | `/relay/reset` | POST | ✅ | Relay contact-life health estimation reset (v5.1.0+ / `$FH`) |
134
+ | `/cabletemp` | GET, POST | ✅ | Cable temperature monitor configuration and status (v5.1.0+) |
135
+ | `/rfid/add` | POST | ✅ | RFID tag pairing mode (v4.0.0+) |
136
+ | `/rfid/users` | GET, POST, DELETE | ✅ | RFID tag to user mappings (v5.0.0+) |
110
137
 
111
138
  ✅ = Fully Supported \| ⚠️ = Partial Support \| ❌ = Not yet implemented
112
139
 
@@ -21,6 +21,7 @@ from .const import (
21
21
  ERROR_SESSION_REQUIRED,
22
22
  ERROR_TIMEOUT,
23
23
  UPDATE_TRIGGERS,
24
+ USER_AGENT,
24
25
  )
25
26
  from .exceptions import (
26
27
  AlreadyListening,
@@ -28,6 +29,7 @@ from .exceptions import (
28
29
  MissingMethod,
29
30
  MissingSerial,
30
31
  ParseJSONError,
32
+ UnsupportedFeature,
31
33
  )
32
34
  from .managers import ManagersMixin
33
35
  from .properties import PropertiesMixin
@@ -55,6 +57,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
55
57
  session: aiohttp.ClientSession | None = None,
56
58
  ssl: bool = False,
57
59
  ssl_verify: bool = True,
60
+ github_token: str | None = None,
58
61
  ) -> None:
59
62
  """Connect to an OpenEVSE charger equipped with wifi or ethernet."""
60
63
  self._user = user or ""
@@ -75,6 +78,11 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
75
78
  self._owns_loop = False
76
79
  self._loop_thread: threading.Thread | None = None
77
80
  self._session = session
81
+ self._github_token = (
82
+ github_token.strip()
83
+ if isinstance(github_token, str) and github_token.strip()
84
+ else None
85
+ )
78
86
 
79
87
  def _get_session(self) -> aiohttp.ClientSession:
80
88
  """Return the configured HTTP session or fail fast."""
@@ -93,6 +101,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
93
101
  method: str = "",
94
102
  data: Any = None,
95
103
  rapi: Any = None,
104
+ headers: dict[str, str] | None = None,
96
105
  ) -> Mapping[str, Any] | list[Any] | str | bool:
97
106
  """Return result of processed HTTP request."""
98
107
  auth = None
@@ -105,7 +114,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
105
114
 
106
115
  session = self._get_session()
107
116
  return await self._process_request_with_session(
108
- session, url, method, data, rapi, auth
117
+ session, url, method, data, rapi, auth, headers
109
118
  )
110
119
 
111
120
  def _normalize_response(self, response: Any) -> dict[str, Any] | list[Any]:
@@ -123,6 +132,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
123
132
  data: Any,
124
133
  rapi: Any,
125
134
  auth: Any,
135
+ headers: dict[str, str] | None = None,
126
136
  ) -> Mapping[str, Any] | list[Any] | str | bool:
127
137
  """Process a request with a given session."""
128
138
  if not hasattr(session, method):
@@ -136,7 +146,17 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
136
146
  method,
137
147
  )
138
148
  try:
139
- kwargs = {"data": rapi, "auth": auth}
149
+ req_headers: dict[str, str] = {
150
+ "User-Agent": USER_AGENT,
151
+ "X-Requested-With": "OpenEVSE",
152
+ }
153
+ if headers:
154
+ req_headers.update(headers)
155
+ kwargs: dict[str, Any] = {
156
+ "data": rapi,
157
+ "auth": auth,
158
+ "headers": req_headers,
159
+ }
140
160
  if data is not None:
141
161
  kwargs["json"] = data
142
162
  if url.startswith("https://") and not self.ssl_verify:
@@ -517,3 +537,66 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
517
537
  def version_check(self, min_version: str, max_version: str = "") -> bool:
518
538
  """Unprotected function call for version checking."""
519
539
  return self._version_check(min_version=min_version, max_version=max_version)
540
+
541
+ def _controller_version_check(
542
+ self, min_version: str, max_version: str = ""
543
+ ) -> bool:
544
+ """Return bool if minimum controller (open_evse) version is met."""
545
+ if "firmware" not in self._config:
546
+ _LOGGER.debug("Unable to find controller firmware version.")
547
+ return False
548
+ cutoff = AwesomeVersion(min_version)
549
+ limit = ""
550
+ if max_version != "":
551
+ limit = AwesomeVersion(max_version)
552
+
553
+ current = get_awesome_version(self._config["firmware"])
554
+ if current.strategy == "unknown":
555
+ _LOGGER.debug(
556
+ "Non-semver controller firmware version detected: %s",
557
+ self._config["firmware"],
558
+ )
559
+ return False
560
+
561
+ if limit:
562
+ try:
563
+ if cutoff <= current < limit:
564
+ return True
565
+ except AwesomeVersionCompareException:
566
+ _LOGGER.debug("Non-semver controller firmware version detected.")
567
+ return False
568
+
569
+ try:
570
+ if current >= cutoff:
571
+ return True
572
+ except AwesomeVersionCompareException:
573
+ _LOGGER.debug("Non-semver controller firmware version detected.")
574
+ return False
575
+
576
+ def controller_version_check(self, min_version: str, max_version: str = "") -> bool:
577
+ """Unprotected function call for controller version checking."""
578
+ return self._controller_version_check(
579
+ min_version=min_version, max_version=max_version
580
+ )
581
+
582
+ def _require_firmware(
583
+ self, min_version: str, feature: str, max_version: str = ""
584
+ ) -> None:
585
+ """Verify minimum gateway firmware version or log and raise UnsupportedFeature."""
586
+ if not self._version_check(min_version, max_version):
587
+ err = UnsupportedFeature(
588
+ feature, min_version=min_version, component="gateway"
589
+ )
590
+ _LOGGER.debug("%s", err)
591
+ raise err
592
+
593
+ def _require_controller_firmware(
594
+ self, min_version: str, feature: str, max_version: str = ""
595
+ ) -> None:
596
+ """Verify minimum OpenEVSE controller firmware version or log and raise UnsupportedFeature."""
597
+ if not self._controller_version_check(min_version, max_version):
598
+ err = UnsupportedFeature(
599
+ feature, min_version=min_version, component="OpenEVSE controller"
600
+ )
601
+ _LOGGER.debug("%s", err)
602
+ raise err
@@ -0,0 +1,33 @@
1
+ """Commands mixin for OpenEVSE HTTP client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .commands_base import BaseCommandMixin
6
+ from .commands_core import CoreCommandsMixin
7
+ from .commands_diagnostics import DiagnosticsCommandsMixin
8
+ from .commands_firmware import FirmwareCommandsMixin
9
+ from .commands_schedule import ScheduleCommandsMixin
10
+ from .commands_security import SecurityCommandsMixin
11
+ from .commands_time import TimeCommandsMixin
12
+
13
+ __all__ = [
14
+ "BaseCommandMixin",
15
+ "CommandsMixin",
16
+ "CoreCommandsMixin",
17
+ "DiagnosticsCommandsMixin",
18
+ "FirmwareCommandsMixin",
19
+ "ScheduleCommandsMixin",
20
+ "SecurityCommandsMixin",
21
+ "TimeCommandsMixin",
22
+ ]
23
+
24
+
25
+ class CommandsMixin(
26
+ CoreCommandsMixin,
27
+ FirmwareCommandsMixin,
28
+ ScheduleCommandsMixin,
29
+ SecurityCommandsMixin,
30
+ DiagnosticsCommandsMixin,
31
+ TimeCommandsMixin,
32
+ ):
33
+ """Composite mixin providing all command methods for OpenEVSE."""
@@ -0,0 +1,66 @@
1
+ """Base mixin providing protocol stubs and common helpers for command mixins."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from collections.abc import Mapping
7
+ from typing import Any
8
+
9
+ import aiohttp
10
+
11
+ _LOGGER = logging.getLogger(__name__)
12
+
13
+
14
+ class BaseCommandMixin:
15
+ """Base mixin defining internal attributes and stub methods required by command mixins."""
16
+
17
+ url: str
18
+ ssl: bool
19
+ ssl_verify: bool
20
+ _status: dict[str, Any]
21
+ _config: dict[str, Any]
22
+ _session: aiohttp.ClientSession | None
23
+ _github_token: str | None
24
+
25
+ # These are implemented in OpenEVSE (client.py)
26
+ def _version_check(self, min_version: str, max_version: str = "") -> bool:
27
+ raise NotImplementedError
28
+
29
+ def _controller_version_check(
30
+ self, min_version: str, max_version: str = ""
31
+ ) -> bool:
32
+ raise NotImplementedError
33
+
34
+ def _require_firmware(
35
+ self, min_version: str, feature: str, max_version: str = ""
36
+ ) -> None:
37
+ raise NotImplementedError
38
+
39
+ def _require_controller_firmware(
40
+ self, min_version: str, feature: str, max_version: str = ""
41
+ ) -> None:
42
+ raise NotImplementedError
43
+
44
+ async def process_request(
45
+ self,
46
+ url: str,
47
+ method: str = "",
48
+ data: Any = None,
49
+ rapi: Any = None,
50
+ headers: dict[str, str] | None = None,
51
+ ) -> Mapping[str, Any] | list[Any] | str | bool:
52
+ raise NotImplementedError
53
+
54
+ async def send_command(self, command: str) -> tuple[Any, Any]:
55
+ raise NotImplementedError
56
+
57
+ async def update(self, force_status: bool = False) -> None:
58
+ raise NotImplementedError
59
+
60
+ def _normalize_response(self, response: Any) -> dict[str, Any] | list[Any]:
61
+ """Normalize response to a dict or list."""
62
+ raise NotImplementedError
63
+
64
+ def _get_session(self) -> aiohttp.ClientSession:
65
+ """Return the configured HTTP session."""
66
+ raise NotImplementedError