python-openevse-http 1.7.1__tar.gz → 1.8.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.1 → python_openevse_http-1.8.0}/.agents/skills/openevse-api-guide/references/endpoints_matrix.md +3 -3
- {python_openevse_http-1.7.1/python_openevse_http.egg-info → python_openevse_http-1.8.0}/PKG-INFO +9 -2
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/README.md +8 -1
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands_diagnostics.py +104 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/const.py +2 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/properties.py +18 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0/python_openevse_http.egg-info}/PKG-INFO +9 -2
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/scm_version.json +2 -2
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/requirements_test.txt +1 -1
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_commands.py +163 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_properties.py +17 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.agents/skills/openevse-api-guide/SKILL.md +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.agents/skills/openevse-dev-workflow/SKILL.md +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/dependabot.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/pull_request_template.md +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/release-drafter.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/workflows/autolabeler.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/workflows/links.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/workflows/release-drafter.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/workflows/test.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.gitignore +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.pre-commit-config.yaml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.yamllint +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/AGENTS.md +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/EXTERNAL_SESSION.md +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/LICENSE +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/codecov.yml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/example_external_session.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/__init__.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/__main__.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/client.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands_base.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands_core.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands_firmware.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands_schedule.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands_security.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands_time.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/exceptions.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/managers.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/py.typed +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/sensors.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/utils.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/websocket.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/pyproject.toml +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/SOURCES.txt +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/dependency_links.txt +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/not-zip-safe +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/requires.txt +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/scm_file_list.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/top_level.txt +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/requirements.txt +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/requirements_lint.txt +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/setup.cfg +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/setup.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/__init__.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/common.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/conftest.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/github_v2.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/github_v4.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v2_json/config.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v2_json/status.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-broken.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-dev.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-extra-version.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-new.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-unknown-semver.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/schedule.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/status-broken.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/status-new.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/status.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/websocket.json +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_client.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_external_session.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_main_edge_cases.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_managers.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_mixins.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_sensors.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_shaper.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/test_websocket.py +0 -0
- {python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tox.ini +0 -0
|
@@ -29,9 +29,9 @@ This reference document compiles all known HTTP REST API endpoints, WebSocket pa
|
|
|
29
29
|
| `/schedule/plan` | `GET` | ❌ | ❌ | ✅ (v4.1.0+) | N/A | ✅ Fully Supported |
|
|
30
30
|
| `/time` | `GET`, `POST` | ❌ | ❌ (`/settime`) | ✅ (v4.0.0+) | `$S1` (RTC set) | ✅ Fully Supported |
|
|
31
31
|
| `/settime` | `GET`, `POST` | ❌ | ✅ | ⚠️ Legacy alias | `$S1` | ✅ Fully Supported |
|
|
32
|
-
| `/emeter` | `DELETE` | ❌ | ❌ | ✅ (v4.0.0+) | N/A |
|
|
33
|
-
| `/notifications` | `GET` | ❌ | ❌ | ✅ (v5.1.0+) | N/A |
|
|
34
|
-
| `/notifications/ack` | `POST` | ❌ | ❌ | ✅ (v5.1.0+) | N/A |
|
|
32
|
+
| `/emeter` | `DELETE` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ✅ Fully Supported |
|
|
33
|
+
| `/notifications` | `GET` | ❌ | ❌ | ✅ (v5.1.0+) | N/A | ✅ Fully Supported |
|
|
34
|
+
| `/notifications/ack` | `POST` | ❌ | ❌ | ✅ (v5.1.0+) | N/A | ✅ Fully Supported |
|
|
35
35
|
| `/update` | `GET`, `POST` | `GET`, `POST` | `GET`, `POST` | `GET`, `POST` | N/A | ✅ Fully Supported |
|
|
36
36
|
| `/logs` | `GET` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ✅ Fully Supported |
|
|
37
37
|
| `/logs/export` | `GET` | ❌ | ❌ | ✅ (v4.0.0+) | N/A | ❌ Not Implemented |
|
{python_openevse_http-1.7.1/python_openevse_http.egg-info → python_openevse_http-1.8.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python_openevse_http
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.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
|
|
@@ -54,6 +54,11 @@ A Python library for communicating with [OpenEVSE](https://www.openevse.com/) ch
|
|
|
54
54
|
- **Time Synchronization & RTC**: Manage time, timezone, and NTP sync (requires firmware 4.0.0+).
|
|
55
55
|
- **Event Logs**: Retrieve diagnostic log blocks and log event history (requires firmware 4.0.0+).
|
|
56
56
|
- **Certificates**: Manage SSL/TLS Root CA and client certificates (requires firmware 4.0.0+).
|
|
57
|
+
- **RFID Management**: RFID tag pairing mode and tag-to-user mappings (requires firmware 4.0.0+ / 5.0.0+).
|
|
58
|
+
- **Relay Diagnostics**: Stuck relay recovery cycle and contact-life estimation reset (requires firmware 5.1.0+).
|
|
59
|
+
- **Cable Temperature Monitoring**: Cable temperature sensor configuration and monitoring (requires firmware 5.1.0+).
|
|
60
|
+
- **Energy Meter Reset**: Reset the energy meter counters with soft/hard options (requires firmware 4.0.0+).
|
|
61
|
+
- **Advisory Notifications**: Retrieve gateway advisory notifications and acknowledge/mute active advisories (requires firmware 5.1.0+).
|
|
57
62
|
|
|
58
63
|
## Installation
|
|
59
64
|
|
|
@@ -156,7 +161,9 @@ When performing firmware update checks (`charger.firmware_check()`) or automatic
|
|
|
156
161
|
| `/time` | GET, POST | ✅ | RTC and NTP time settings (v4.0.0+) |
|
|
157
162
|
| `/settime` | POST | ✅ | Legacy time setting interface (v3.x / fallback to `$S1` on v2.x) |
|
|
158
163
|
| `/logs` | GET | ✅ | Event log block bounds and block events (v4.0.0+) |
|
|
159
|
-
| `/emeter` | DELETE |
|
|
164
|
+
| `/emeter` | DELETE | ✅ | Energy meter reset (v4.0.0+) |
|
|
165
|
+
| `/notifications` | GET | ✅ | Gateway advisory notifications list (v5.1.0+) |
|
|
166
|
+
| `/notifications/ack` | POST | ✅ | Acknowledge/mute active advisory notification (v5.1.0+) |
|
|
160
167
|
| `/wifi` | GET, POST | ❌ | Network scanning and AP configuration |
|
|
161
168
|
| `/tesla` | GET | ❌ | Tesla vehicle integration |
|
|
162
169
|
| `/certificates`| GET, POST, DELETE | ✅ | SSL/TLS certificate management (v4.0.0+) |
|
|
@@ -21,6 +21,11 @@ A Python library for communicating with [OpenEVSE](https://www.openevse.com/) ch
|
|
|
21
21
|
- **Time Synchronization & RTC**: Manage time, timezone, and NTP sync (requires firmware 4.0.0+).
|
|
22
22
|
- **Event Logs**: Retrieve diagnostic log blocks and log event history (requires firmware 4.0.0+).
|
|
23
23
|
- **Certificates**: Manage SSL/TLS Root CA and client certificates (requires firmware 4.0.0+).
|
|
24
|
+
- **RFID Management**: RFID tag pairing mode and tag-to-user mappings (requires firmware 4.0.0+ / 5.0.0+).
|
|
25
|
+
- **Relay Diagnostics**: Stuck relay recovery cycle and contact-life estimation reset (requires firmware 5.1.0+).
|
|
26
|
+
- **Cable Temperature Monitoring**: Cable temperature sensor configuration and monitoring (requires firmware 5.1.0+).
|
|
27
|
+
- **Energy Meter Reset**: Reset the energy meter counters with soft/hard options (requires firmware 4.0.0+).
|
|
28
|
+
- **Advisory Notifications**: Retrieve gateway advisory notifications and acknowledge/mute active advisories (requires firmware 5.1.0+).
|
|
24
29
|
|
|
25
30
|
## Installation
|
|
26
31
|
|
|
@@ -123,7 +128,9 @@ When performing firmware update checks (`charger.firmware_check()`) or automatic
|
|
|
123
128
|
| `/time` | GET, POST | ✅ | RTC and NTP time settings (v4.0.0+) |
|
|
124
129
|
| `/settime` | POST | ✅ | Legacy time setting interface (v3.x / fallback to `$S1` on v2.x) |
|
|
125
130
|
| `/logs` | GET | ✅ | Event log block bounds and block events (v4.0.0+) |
|
|
126
|
-
| `/emeter` | DELETE |
|
|
131
|
+
| `/emeter` | DELETE | ✅ | Energy meter reset (v4.0.0+) |
|
|
132
|
+
| `/notifications` | GET | ✅ | Gateway advisory notifications list (v5.1.0+) |
|
|
133
|
+
| `/notifications/ack` | POST | ✅ | Acknowledge/mute active advisory notification (v5.1.0+) |
|
|
127
134
|
| `/wifi` | GET, POST | ❌ | Network scanning and AP configuration |
|
|
128
135
|
| `/tesla` | GET | ❌ | Tesla vehicle integration |
|
|
129
136
|
| `/certificates`| GET, POST, DELETE | ✅ | SSL/TLS certificate management (v4.0.0+) |
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/openevsehttp/commands_diagnostics.py
RENAMED
|
@@ -227,3 +227,107 @@ class DiagnosticsCommandsMixin(BaseCommandMixin):
|
|
|
227
227
|
raise CommandFailedError(f"Invalid response from /logs: {response}")
|
|
228
228
|
|
|
229
229
|
return dict(response)
|
|
230
|
+
|
|
231
|
+
async def get_notifications(self) -> dict[str, Any]:
|
|
232
|
+
"""Retrieve the advisory notifications list from the gateway.
|
|
233
|
+
|
|
234
|
+
Queries 'GET /notifications'.
|
|
235
|
+
Requires gateway firmware 5.1.0 or higher.
|
|
236
|
+
|
|
237
|
+
Expected response shape:
|
|
238
|
+
{
|
|
239
|
+
"count": int,
|
|
240
|
+
"max_severity": "info" | "warning" | "critical" | None,
|
|
241
|
+
"notifications": [
|
|
242
|
+
{
|
|
243
|
+
"id": str,
|
|
244
|
+
"category": str,
|
|
245
|
+
"severity": str,
|
|
246
|
+
"sticky": bool,
|
|
247
|
+
"acked": bool,
|
|
248
|
+
"first_seen": int,
|
|
249
|
+
"last_seen": int,
|
|
250
|
+
},
|
|
251
|
+
...
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
:return: Dict containing 'count', 'max_severity', and 'notifications' list.
|
|
256
|
+
:raises UnsupportedFeature: If gateway firmware is older than 5.1.0.
|
|
257
|
+
:raises CommandFailedError: If response is invalid.
|
|
258
|
+
"""
|
|
259
|
+
self._require_firmware("5.1.0", "get_notifications")
|
|
260
|
+
|
|
261
|
+
url = f"{self.url}notifications"
|
|
262
|
+
_LOGGER.debug("Querying notifications: %s", url)
|
|
263
|
+
response = await self.process_request(url=url, method="get")
|
|
264
|
+
|
|
265
|
+
if not isinstance(response, Mapping):
|
|
266
|
+
_LOGGER.error("Invalid response from /notifications: %s", response)
|
|
267
|
+
raise CommandFailedError(
|
|
268
|
+
f"Invalid response from /notifications: {response}"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
return dict(response)
|
|
272
|
+
|
|
273
|
+
async def acknowledge_notification(self, notification_id: str) -> None:
|
|
274
|
+
"""Acknowledge (mute) an active advisory notification by ID.
|
|
275
|
+
|
|
276
|
+
Issues 'POST /notifications/ack' with the notification ID.
|
|
277
|
+
Requires gateway firmware 5.1.0 or higher.
|
|
278
|
+
|
|
279
|
+
:param notification_id: The identifier of the advisory (e.g. 'safety.ground_check').
|
|
280
|
+
:raises UnsupportedFeature: If gateway firmware is older than 5.1.0.
|
|
281
|
+
:raises ValueError: If notification_id is empty or not a string.
|
|
282
|
+
:raises CommandFailedError: If the acknowledgment fails or returns an error.
|
|
283
|
+
"""
|
|
284
|
+
self._require_firmware("5.1.0", "acknowledge_notification")
|
|
285
|
+
|
|
286
|
+
if not isinstance(notification_id, str) or not notification_id.strip():
|
|
287
|
+
raise ValueError("notification_id must be a non-empty string.")
|
|
288
|
+
|
|
289
|
+
url = f"{self.url}notifications/ack"
|
|
290
|
+
data = {"id": notification_id.strip()}
|
|
291
|
+
|
|
292
|
+
_LOGGER.debug(
|
|
293
|
+
"Acknowledging notification %s at %s", notification_id.strip(), url
|
|
294
|
+
)
|
|
295
|
+
response = await self.process_request(url=url, method="post", data=data)
|
|
296
|
+
response = self._normalize_response(response)
|
|
297
|
+
|
|
298
|
+
msg = response.get("msg") if isinstance(response, Mapping) else response
|
|
299
|
+
if msg not in SUCCESS_ANSWERS:
|
|
300
|
+
_LOGGER.error("Problem acknowledging notification: %s", response)
|
|
301
|
+
raise CommandFailedError(f"Problem acknowledging notification: {response}")
|
|
302
|
+
|
|
303
|
+
async def reset_energy_meter(
|
|
304
|
+
self, hard: bool = False, import_from_evse: bool = False
|
|
305
|
+
) -> None:
|
|
306
|
+
"""Reset the charger energy meter.
|
|
307
|
+
|
|
308
|
+
Issues DELETE /emeter with hard and import flags.
|
|
309
|
+
|
|
310
|
+
:param hard: If True, sets total energy and switch counter to 0 (hard reset).
|
|
311
|
+
If False, performs a soft reset.
|
|
312
|
+
:param import_from_evse: If True, allows re-importing total energy counters from EVSE.
|
|
313
|
+
:raises UnsupportedFeature: If gateway firmware is older than 4.0.0.
|
|
314
|
+
:raises CommandFailedError: If the reset operation fails.
|
|
315
|
+
"""
|
|
316
|
+
self._require_firmware("4.0.0", "reset_energy_meter")
|
|
317
|
+
|
|
318
|
+
url = f"{self.url}emeter"
|
|
319
|
+
data = {"hard": bool(hard), "import": bool(import_from_evse)}
|
|
320
|
+
|
|
321
|
+
_LOGGER.debug(
|
|
322
|
+
"Resetting energy meter: %s (hard=%s, import=%s)",
|
|
323
|
+
url,
|
|
324
|
+
hard,
|
|
325
|
+
import_from_evse,
|
|
326
|
+
)
|
|
327
|
+
response = await self.process_request(url=url, method="delete", data=data)
|
|
328
|
+
response = self._normalize_response(response)
|
|
329
|
+
|
|
330
|
+
msg = response.get("msg") if isinstance(response, Mapping) else None
|
|
331
|
+
if msg not in SUCCESS_ANSWERS:
|
|
332
|
+
_LOGGER.error("Problem resetting energy meter: %s", response)
|
|
333
|
+
raise CommandFailedError(f"Problem resetting energy meter: {response}")
|
|
@@ -671,3 +671,21 @@ class PropertiesMixin:
|
|
|
671
671
|
self._github_token = (
|
|
672
672
|
token.strip() if isinstance(token, str) and token.strip() else None
|
|
673
673
|
)
|
|
674
|
+
|
|
675
|
+
@property
|
|
676
|
+
def notifications_count(self) -> int | None:
|
|
677
|
+
"""Return unmuted notification advisories count from status summary."""
|
|
678
|
+
notifications = self._status.get("notifications")
|
|
679
|
+
if isinstance(notifications, Mapping):
|
|
680
|
+
return notifications.get("count")
|
|
681
|
+
return None
|
|
682
|
+
|
|
683
|
+
@property
|
|
684
|
+
def notifications_severity(self) -> str | None:
|
|
685
|
+
"""Return max severity ('info', 'warning', 'critical') from status summary."""
|
|
686
|
+
notifications = self._status.get("notifications")
|
|
687
|
+
if isinstance(notifications, Mapping):
|
|
688
|
+
severity = notifications.get("severity")
|
|
689
|
+
if severity is not None:
|
|
690
|
+
return str(severity)
|
|
691
|
+
return None
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0/python_openevse_http.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python_openevse_http
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.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
|
|
@@ -54,6 +54,11 @@ A Python library for communicating with [OpenEVSE](https://www.openevse.com/) ch
|
|
|
54
54
|
- **Time Synchronization & RTC**: Manage time, timezone, and NTP sync (requires firmware 4.0.0+).
|
|
55
55
|
- **Event Logs**: Retrieve diagnostic log blocks and log event history (requires firmware 4.0.0+).
|
|
56
56
|
- **Certificates**: Manage SSL/TLS Root CA and client certificates (requires firmware 4.0.0+).
|
|
57
|
+
- **RFID Management**: RFID tag pairing mode and tag-to-user mappings (requires firmware 4.0.0+ / 5.0.0+).
|
|
58
|
+
- **Relay Diagnostics**: Stuck relay recovery cycle and contact-life estimation reset (requires firmware 5.1.0+).
|
|
59
|
+
- **Cable Temperature Monitoring**: Cable temperature sensor configuration and monitoring (requires firmware 5.1.0+).
|
|
60
|
+
- **Energy Meter Reset**: Reset the energy meter counters with soft/hard options (requires firmware 4.0.0+).
|
|
61
|
+
- **Advisory Notifications**: Retrieve gateway advisory notifications and acknowledge/mute active advisories (requires firmware 5.1.0+).
|
|
57
62
|
|
|
58
63
|
## Installation
|
|
59
64
|
|
|
@@ -156,7 +161,9 @@ When performing firmware update checks (`charger.firmware_check()`) or automatic
|
|
|
156
161
|
| `/time` | GET, POST | ✅ | RTC and NTP time settings (v4.0.0+) |
|
|
157
162
|
| `/settime` | POST | ✅ | Legacy time setting interface (v3.x / fallback to `$S1` on v2.x) |
|
|
158
163
|
| `/logs` | GET | ✅ | Event log block bounds and block events (v4.0.0+) |
|
|
159
|
-
| `/emeter` | DELETE |
|
|
164
|
+
| `/emeter` | DELETE | ✅ | Energy meter reset (v4.0.0+) |
|
|
165
|
+
| `/notifications` | GET | ✅ | Gateway advisory notifications list (v5.1.0+) |
|
|
166
|
+
| `/notifications/ack` | POST | ✅ | Acknowledge/mute active advisory notification (v5.1.0+) |
|
|
160
167
|
| `/wifi` | GET, POST | ❌ | Network scanning and AP configuration |
|
|
161
168
|
| `/tesla` | GET | ❌ | Tesla vehicle integration |
|
|
162
169
|
| `/certificates`| GET, POST, DELETE | ✅ | SSL/TLS certificate management (v4.0.0+) |
|
|
@@ -38,6 +38,9 @@ TEST_URL_RFID_ADD = "http://openevse.test.tld/rfid/add"
|
|
|
38
38
|
TEST_URL_RFID_USERS = "http://openevse.test.tld/rfid/users"
|
|
39
39
|
TEST_URL_SCHEDULE = "http://openevse.test.tld/schedule"
|
|
40
40
|
TEST_URL_SCHEDULE_PLAN = "http://openevse.test.tld/schedule/plan"
|
|
41
|
+
TEST_URL_NOTIFICATIONS = "http://openevse.test.tld/notifications"
|
|
42
|
+
TEST_URL_NOTIFICATIONS_ACK = "http://openevse.test.tld/notifications/ack"
|
|
43
|
+
TEST_URL_EMETER = "http://openevse.test.tld/emeter"
|
|
41
44
|
SERVER_URL = "openevse.test.tld"
|
|
42
45
|
|
|
43
46
|
|
|
@@ -2773,3 +2776,163 @@ async def test_get_schedule_plan(test_charger, test_charger_v2, mock_aioclient,
|
|
|
2773
2776
|
CommandFailedError, match="Invalid response format for /schedule/plan: invalid"
|
|
2774
2777
|
):
|
|
2775
2778
|
await test_charger.get_schedule_plan()
|
|
2779
|
+
|
|
2780
|
+
|
|
2781
|
+
# ── notifications ────────────────────────────────────────────────────
|
|
2782
|
+
|
|
2783
|
+
|
|
2784
|
+
async def test_get_notifications(test_charger, test_charger_v2, mock_aioclient, caplog):
|
|
2785
|
+
"""Test get_notifications command."""
|
|
2786
|
+
await test_charger.update()
|
|
2787
|
+
|
|
2788
|
+
# 1. Version check failure on older firmware
|
|
2789
|
+
with pytest.raises(
|
|
2790
|
+
UnsupportedFeature, match="get_notifications requires gateway firmware 5.1.0"
|
|
2791
|
+
):
|
|
2792
|
+
await test_charger_v2.get_notifications()
|
|
2793
|
+
|
|
2794
|
+
# Force test_charger gateway version to 5.1.0
|
|
2795
|
+
test_charger._config["version"] = "5.1.0"
|
|
2796
|
+
|
|
2797
|
+
# 2. Successful GET /notifications
|
|
2798
|
+
notif_data = {
|
|
2799
|
+
"count": 1,
|
|
2800
|
+
"max_severity": "warning",
|
|
2801
|
+
"notifications": [
|
|
2802
|
+
{
|
|
2803
|
+
"id": "safety.ground_check",
|
|
2804
|
+
"category": "safety",
|
|
2805
|
+
"severity": "warning",
|
|
2806
|
+
"sticky": False,
|
|
2807
|
+
"acked": False,
|
|
2808
|
+
"first_seen": 1726050000,
|
|
2809
|
+
"last_seen": 1726050005,
|
|
2810
|
+
}
|
|
2811
|
+
],
|
|
2812
|
+
}
|
|
2813
|
+
mock_aioclient.get(
|
|
2814
|
+
TEST_URL_NOTIFICATIONS,
|
|
2815
|
+
status=200,
|
|
2816
|
+
body=json.dumps(notif_data),
|
|
2817
|
+
)
|
|
2818
|
+
with caplog.at_level(logging.DEBUG):
|
|
2819
|
+
result = await test_charger.get_notifications()
|
|
2820
|
+
assert result == notif_data
|
|
2821
|
+
assert (
|
|
2822
|
+
"Querying notifications: http://openevse.test.tld/notifications" in caplog.text
|
|
2823
|
+
)
|
|
2824
|
+
|
|
2825
|
+
# 3. Invalid non-mapping response
|
|
2826
|
+
mock_aioclient.get(
|
|
2827
|
+
TEST_URL_NOTIFICATIONS,
|
|
2828
|
+
status=200,
|
|
2829
|
+
body="invalid",
|
|
2830
|
+
)
|
|
2831
|
+
with pytest.raises(
|
|
2832
|
+
CommandFailedError, match="Invalid response from /notifications: invalid"
|
|
2833
|
+
):
|
|
2834
|
+
await test_charger.get_notifications()
|
|
2835
|
+
|
|
2836
|
+
|
|
2837
|
+
async def test_acknowledge_notification(
|
|
2838
|
+
test_charger, test_charger_v2, mock_aioclient, caplog
|
|
2839
|
+
):
|
|
2840
|
+
"""Test acknowledge_notification command."""
|
|
2841
|
+
await test_charger.update()
|
|
2842
|
+
|
|
2843
|
+
# 1. Version check failure on older firmware
|
|
2844
|
+
with pytest.raises(
|
|
2845
|
+
UnsupportedFeature,
|
|
2846
|
+
match="acknowledge_notification requires gateway firmware 5.1.0",
|
|
2847
|
+
):
|
|
2848
|
+
await test_charger_v2.acknowledge_notification("safety.ground_check")
|
|
2849
|
+
|
|
2850
|
+
# Force test_charger gateway version to 5.1.0
|
|
2851
|
+
test_charger._config["version"] = "5.1.0"
|
|
2852
|
+
|
|
2853
|
+
# 2. Invalid empty ID
|
|
2854
|
+
with pytest.raises(ValueError, match="notification_id must be a non-empty string"):
|
|
2855
|
+
await test_charger.acknowledge_notification("")
|
|
2856
|
+
|
|
2857
|
+
# 3. Successful POST /notifications/ack
|
|
2858
|
+
mock_aioclient.post(
|
|
2859
|
+
TEST_URL_NOTIFICATIONS_ACK,
|
|
2860
|
+
status=200,
|
|
2861
|
+
body="acknowledged",
|
|
2862
|
+
)
|
|
2863
|
+
with caplog.at_level(logging.DEBUG):
|
|
2864
|
+
await test_charger.acknowledge_notification("safety.ground_check")
|
|
2865
|
+
assert (
|
|
2866
|
+
"Acknowledging notification safety.ground_check at http://openevse.test.tld/notifications/ack"
|
|
2867
|
+
in caplog.text
|
|
2868
|
+
)
|
|
2869
|
+
|
|
2870
|
+
# 4. Failure response from firmware
|
|
2871
|
+
mock_aioclient.post(
|
|
2872
|
+
TEST_URL_NOTIFICATIONS_ACK,
|
|
2873
|
+
status=200,
|
|
2874
|
+
body="no such active notification",
|
|
2875
|
+
)
|
|
2876
|
+
with pytest.raises(CommandFailedError, match="Problem acknowledging notification"):
|
|
2877
|
+
await test_charger.acknowledge_notification("safety.ground_check")
|
|
2878
|
+
|
|
2879
|
+
|
|
2880
|
+
# ── reset_energy_meter ───────────────────────────────────────────────
|
|
2881
|
+
|
|
2882
|
+
|
|
2883
|
+
async def test_reset_energy_meter(
|
|
2884
|
+
test_charger, test_charger_v2, mock_aioclient, caplog
|
|
2885
|
+
):
|
|
2886
|
+
"""Test reset_energy_meter command."""
|
|
2887
|
+
await test_charger.update()
|
|
2888
|
+
|
|
2889
|
+
# 1. Version check failure on older firmware
|
|
2890
|
+
with pytest.raises(
|
|
2891
|
+
UnsupportedFeature, match="reset_energy_meter requires gateway firmware 4.0.0"
|
|
2892
|
+
):
|
|
2893
|
+
await test_charger_v2.reset_energy_meter()
|
|
2894
|
+
|
|
2895
|
+
# 2. Successful reset with default params (hard=False, import=False)
|
|
2896
|
+
mock_aioclient.delete(
|
|
2897
|
+
TEST_URL_EMETER,
|
|
2898
|
+
status=200,
|
|
2899
|
+
body='{"msg": "Reset done"}',
|
|
2900
|
+
)
|
|
2901
|
+
with caplog.at_level(logging.DEBUG):
|
|
2902
|
+
await test_charger.reset_energy_meter()
|
|
2903
|
+
assert (
|
|
2904
|
+
"Resetting energy meter: http://openevse.test.tld/emeter (hard=False, import=False)"
|
|
2905
|
+
in caplog.text
|
|
2906
|
+
)
|
|
2907
|
+
|
|
2908
|
+
# 3. Successful reset with hard=True, import_from_evse=True
|
|
2909
|
+
caplog.clear()
|
|
2910
|
+
mock_aioclient.delete(
|
|
2911
|
+
TEST_URL_EMETER,
|
|
2912
|
+
status=200,
|
|
2913
|
+
body='{"msg": "Reset done"}',
|
|
2914
|
+
)
|
|
2915
|
+
with caplog.at_level(logging.DEBUG):
|
|
2916
|
+
await test_charger.reset_energy_meter(hard=True, import_from_evse=True)
|
|
2917
|
+
assert (
|
|
2918
|
+
"Resetting energy meter: http://openevse.test.tld/emeter (hard=True, import=True)"
|
|
2919
|
+
in caplog.text
|
|
2920
|
+
)
|
|
2921
|
+
|
|
2922
|
+
# 4. Failure response from firmware
|
|
2923
|
+
mock_aioclient.delete(
|
|
2924
|
+
TEST_URL_EMETER,
|
|
2925
|
+
status=200,
|
|
2926
|
+
body='{"msg": "Reset failed"}',
|
|
2927
|
+
)
|
|
2928
|
+
with pytest.raises(CommandFailedError, match="Problem resetting energy meter"):
|
|
2929
|
+
await test_charger.reset_energy_meter()
|
|
2930
|
+
|
|
2931
|
+
# 5. Non-mapping / error response
|
|
2932
|
+
mock_aioclient.delete(
|
|
2933
|
+
TEST_URL_EMETER,
|
|
2934
|
+
status=200,
|
|
2935
|
+
body="invalid response",
|
|
2936
|
+
)
|
|
2937
|
+
with pytest.raises(CommandFailedError, match="Problem resetting energy meter"):
|
|
2938
|
+
await test_charger.reset_energy_meter()
|
|
@@ -611,3 +611,20 @@ async def test_time_properties():
|
|
|
611
611
|
assert charger.time_offset == "+0000"
|
|
612
612
|
assert charger.sntp_enabled is True
|
|
613
613
|
assert charger.sntp_hostname == "pool.ntp.org"
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
async def test_notification_properties():
|
|
617
|
+
"""Test notification summary properties."""
|
|
618
|
+
charger = OpenEVSE(SERVER_URL)
|
|
619
|
+
charger._status = {}
|
|
620
|
+
|
|
621
|
+
assert charger.notifications_count is None
|
|
622
|
+
assert charger.notifications_severity is None
|
|
623
|
+
|
|
624
|
+
charger._status = {"notifications": {"count": 2, "severity": "warning"}}
|
|
625
|
+
assert charger.notifications_count == 2
|
|
626
|
+
assert charger.notifications_severity == "warning"
|
|
627
|
+
|
|
628
|
+
charger._status = {"notifications": "invalid"}
|
|
629
|
+
assert charger.notifications_count is None
|
|
630
|
+
assert charger.notifications_severity is None
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.agents/skills/openevse-api-guide/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/ISSUE_TEMPLATE/bug_report.yml
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/ISSUE_TEMPLATE/feature_request.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/workflows/publish-to-pypi.yml
RENAMED
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/.github/workflows/release-drafter.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/python_openevse_http.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v2_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v2_json/status.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-dev.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config-new.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/schedule.json
RENAMED
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/status-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/status-new.json
RENAMED
|
File without changes
|
{python_openevse_http-1.7.1 → python_openevse_http-1.8.0}/tests/fixtures/v4_json/status.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|