Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .agents/skills/openevse-api-guide/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
name: openevse-api-guide
description: >-
Use this skill when implementing, refactoring, or testing OpenEVSE charger
commands, REST API endpoints, RAPI commands, properties, or exception handling in python-openevse-http.
---

# OpenEVSE API & Command Implementation Guide

This skill provides architectural guidelines, endpoint conventions, and error handling patterns for developing `python-openevse-http`.

## Architecture

The main client `OpenEVSE` combines several mixins:
- `CommandsMixin` (`openevsehttp/commands.py`): Command execution methods.
- `PropertiesMixin` (`openevsehttp/properties.py`): Configuration & state properties.
- `SensorsMixin` (`openevsehttp/sensors.py`): Energy, current, voltage, temperature telemetry.
- `WebsocketMixin` (`openevsehttp/websocket.py`): Real-time event streams.

## Endpoints & RAPI Commands Reference

| Action | HTTP Endpoint (v4+) | RAPI Command (v2/v3) | Method |
| :--- | :--- | :--- | :--- |
| Status | `/status` | N/A | GET |
| Config | `/config` | N/A | GET / POST |
| Manual Override | `/override` | `$FE` (enable) / `$FS` (sleep) | GET / POST / PATCH / DELETE |
| Soft Current Limit | `/override` (charge_current) | `$SC <amps> [N\|V]` | POST |
| Shaper Mode | `/shaper` | N/A | POST |
| Divert Mode | `/divertmode` or `/config` | N/A | POST |
| Module Restart | `/restart` (`device: gateway\|evse`) | `$FR` (evse restart) | POST |
| Firmware Update | `/update` | N/A | POST (multipart or JSON URL) |

## Firmware Version Branching

Always check firmware compatibility using `self._version_check(min_version)`:
```python
if self._version_check("4.0.1"):
# Use HTTP REST endpoint
response = await self.process_request(url=f"{self.url}override", method="patch")
else:
# Fallback to RAPI command for older firmware
response, msg = await self.send_command("$FE" if state == 254 else "$FS")
```

If a feature is not supported on older firmware:
```python
if not self._version_check("4.1.0"):
_LOGGER.debug("Feature not supported for older firmware.")
raise UnsupportedFeature
```

## Exception Handling Conventions

All custom exceptions inherit from `OpenEVSEError(Exception)`.

- **`CommandFailedError`**: Raise when a command returns an error response, fails HTTP verification, or returns `$NK` / `RAPI_ERRORS`.
- **`UnknownStateError`**: Raise when prior charger state or configuration is required to determine the command payload (e.g. toggling) but is missing or `None`.
- **`FirmwareResolutionError`**: Raise when GitHub release download URL cannot be determined from the release metadata.
- **`UnsupportedFeature`**: Raise when charger firmware is below the minimum supported version for a feature.
- **`AuthenticationError`**: Raise on 401 unauthorized.

```python
from .exceptions import CommandFailedError, UnknownStateError, UnsupportedFeature
```

## Validating Endpoints Against Firmware Repositories

When adding, modifying, or debugging endpoints and RAPI commands, cross-reference against the upstream OpenEVSE firmware sources:

- **WiFi Gateway Firmware (v3/v4/v5)**: [`OpenEVSE/ESP32_WiFi_V4.x`](https://github.com/OpenEVSE/ESP32_WiFi_V4.x)
- **Legacy WiFi Firmware (v2)**: [`OpenEVSE/ESP8266_WiFi_v2.x`](https://github.com/OpenEVSE/ESP8266_WiFi_v2.x)
- **OpenEVSE Controller Firmware (RAPI)**: [`OpenEVSE/open_evse`](https://github.com/OpenEVSE/open_evse)

### What to Verify in Firmware Sources:
1. **Route & Method Handlers**:
- 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`).
- Confirm expected query parameters or JSON body fields (e.g. `divertmode=...`, `{"device": "gateway"}`, `{"charge_current": ...}`).
2. **Response Formats & Statuses**:
- Verify success and error response payloads (e.g., `{"msg": "done"}`, `{"result": "OK", "msg": "..."}`, or plain string messages like `"Current Shaper state changed"`).
- Update `SUCCESS_ANSWERS` in `openevsehttp/const.py` if new success indicators are introduced.
3. **Firmware Version Thresholds**:
- 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.
4. **RAPI Command Specifications**:
- Check `src/rapi.cpp` or OpenEVSE controller docs for valid RAPI commands (e.g., `$SC`, `$FE`, `$FS`, `$FR`, `$ST`) and return formats (`$OK`, `$NK`).
5. **Mock Test Fixtures**:
- Update or add mock JSON payloads under `tests/fixtures/v4_json/` and `tests/fixtures/v2_json/` to mirror real firmware response shapes.

## Writing Tests for Commands

When testing command methods:
1. Use fixtures from `tests/conftest.py` (`test_charger`, `test_charger_v2`, `test_charger_new`).
2. Mock responses using `mock_aioclient`:
```python
mock_aioclient.post(
TEST_URL_CONFIG,
status=200,
body='{"msg": "done"}',
)
```
3. Test success paths, failure responses (`CommandFailedError`), missing state paths (`UnknownStateError`), and older firmware version behavior (`UnsupportedFeature` / RAPI commands).
87 changes: 87 additions & 0 deletions .agents/skills/openevse-dev-workflow/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
name: openevse-dev-workflow
description: >-
Use this skill when running tests, formatting code, checking linters,
running type checks, or managing tox environments in python-openevse-http.
---

# OpenEVSE Development & Testing Workflow

This skill guides you through executing tests, linting, formatting, and type checks within the `python-openevse-http` repository.

## Environment & Tooling

The project uses `tox` for managing isolated virtual environments and running test tools (`pytest`, `ruff`, `mypy`).

### 1. Running Unit Tests

Run full test suite via tox:
```bash
tox -e py314
```

To run fast targeted test runs with the existing tox environment:
```bash
# Run all tests
.tox/py314/bin/pytest

# Run a specific test file
.tox/py314/bin/pytest tests/test_commands.py

# Run a single test function
.tox/py314/bin/pytest tests/test_commands.py -k "test_toggle_override"

# Run with verbose output and stdout
.tox/py314/bin/pytest -v -s tests/test_client.py
```

### 2. Formatting & Linting (Ruff)

Check formatting and linting:
```bash
tox -e lint
```

To auto-format or auto-fix lint errors:
```bash
# Format code
.tox/lint/bin/ruff format ./

# Auto-fix linting issues
.tox/lint/bin/ruff check --fix openevsehttp tests
```

### 3. Type Checking (Mypy)

Run static type checks:
```bash
tox -e mypy
```
Or directly:
```bash
.tox/mypy/bin/mypy openevsehttp
```

### 4. Running All CI Checks Together

Before submitting PRs or finalizing tasks, verify everything in one step:
```bash
tox -e py314,lint,mypy
```

### 5. Pre-commit Hooks

Pre-commit hooks are configured via `.pre-commit-config.yaml`. They run automatically on `git commit`, or you can trigger them manually:
```bash
pre-commit run --all-files
```

### 6. Pull Requests & Issue Creation

- **Pull Requests**:
- Always use the template in [`.github/pull_request_template.md`](../../.github/pull_request_template.md).
- Include a summary, issue link (`Fixes #<number>`), type of change, and completed checklist.
- Follow conventional commits in PR titles (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`).
- **Issues & Feature Requests**:
- Use [`.github/ISSUE_TEMPLATE/bug_report.yml`](../../.github/ISSUE_TEMPLATE/bug_report.yml) for bugs (`[Bug]: <summary>`).
- Use [`.github/ISSUE_TEMPLATE/feature_request.yml`](../../.github/ISSUE_TEMPLATE/feature_request.yml) for feature requests (`[Feature Request]: <summary>`).
128 changes: 128 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Agent Guidelines for python-openevse-http

This document outlines key architecture, conventions, workflows, and testing practices for agentic assistants operating in this repository.

---

## 1. Project Overview & Architecture

`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.

### Core Modules & Mixins
The main client class `OpenEVSE` in `openevsehttp/client.py` inherits from multiple mixins:
- **`openevsehttp/client.py`**: Core client lifecycle, authentication, request processing (`process_request`, `send_command`), status updates (`update`), and session management.
- **`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`).
- **`openevsehttp/properties.py` (`PropertiesMixin`)**: Charger properties, configuration parsing, state decoding (`states`, `divert_mode`), firmware version parsing.
- **`openevsehttp/sensors.py` (`SensorsMixin`)**: Sensor values, telemetry, power/voltage calculations.
- **`openevsehttp/websocket.py` (`WebsocketMixin`, `OpenEVSEWebsocket`)**: Real-time websocket communication and state change listeners.
- **`openevsehttp/exceptions.py`**: Typed library exceptions inheriting from `OpenEVSEError`.

### Client Session Requirement
- `OpenEVSE` uses caller-provided `aiohttp.ClientSession` (via `session=...`). If not provided, accessing network operations raises `RuntimeError(ERROR_SESSION_REQUIRED)`.
- The session must run on the active event loop.

---

## 2. Firmware Version Handling & Upstream Validation

OpenEVSE chargers run various firmware versions (v2.x, v3.x, v4.x, v5.x) with different capabilities:
- **`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`).
- Always handle version edge cases (e.g. non-semver development strings like `4.1.2.dev`).
- Raise `UnsupportedFeature` if a feature is not supported on older firmware.

### Validating Endpoints Against Firmware Sources
When adding or updating endpoints, payload keys, or RAPI commands, cross-reference against:
- **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`)
- **Legacy WiFi (v2)**: [`OpenEVSE/ESP8266_WiFi_v2.x`](https://github.com/OpenEVSE/ESP8266_WiFi_v2.x)
- **Controller / RAPI**: [`OpenEVSE/open_evse`](https://github.com/OpenEVSE/open_evse) (commands in `src/rapi.cpp`)
Verify HTTP methods, expected JSON fields, success/error payload shapes, and version thresholds.

---

## 3. Exception Handling

All custom exceptions inherit from `OpenEVSEError(Exception)`:
- `CommandFailedError`: Command execution failure, RAPI rejection (`$NK`), or error HTTP response.
- `UnknownStateError`: Required state or configuration missing before command execution (e.g. toggle state).
- `FirmwareResolutionError`: GitHub release asset resolution failure.
- `AuthenticationError`: HTTP 401 / auth failures.
- `UnsupportedFeature`: Feature not available for current firmware version.
- `ParseJSONError`, `InvalidType`, `MissingMethod`, `MissingSerial`, `AlreadyListening`.

Export all public exception classes in `openevsehttp/__init__.py`.

---

## 4. Development & Testing Workflow

### Running Tests
Use `tox` for isolated environments:
```bash
# Run unit tests on Python 3.14 / active environment
tox -e py314

# Or run pytest directly within the tox environment
.tox/py314/bin/pytest

# Target specific test files
.tox/py314/bin/pytest tests/test_commands.py -k "test_toggle_override"
```

### Linting & Formatting
```bash
# Run ruff formatting check & linter
tox -e lint

# Format code automatically
.tox/lint/bin/ruff format ./

# Run linter with auto-fixes
.tox/lint/bin/ruff check --fix openevsehttp tests
```

### Type Checking
```bash
tox -e mypy
# Or directly:
.tox/mypy/bin/mypy openevsehttp
```

---

## 5. Testing & Mocking Guidelines

- Tests use `pytest` with `pytest-asyncio` (`asyncio_default_fixture_loop_scope = "function"`).
- Test fixtures in `tests/conftest.py`:
- `test_charger`: Standard v4 charger client with mocked endpoints.
- `test_charger_v2`: Legacy v2 firmware mock.
- `test_charger_new`: Newer v4 fixture with shaper and modern endpoints.
- `test_charger_auth`: Authenticated charger mock.
- `mock_aioclient`: `AiohttpClientMocker` instance for intercepting HTTP requests (`get`, `post`, `patch`, `delete`).
- Fixture data files are located in `tests/fixtures/` (`v4_json/`, `v2_json/`).

---

## 6. Commit, Pull Request & Issue Guidelines

### Creating Pull Requests
- **Use the PR Template**: Always structure PR descriptions according to [`.github/pull_request_template.md`](.github/pull_request_template.md):
- **Description**: Provide a clear summary of changes, motivation, and link related issues (`Fixes #<number>`).
- **Type of change**: Check the relevant boxes (`Bug fix`, `New feature`, `Breaking change`, `Code quality / Refactoring`, `Documentation update`).
- **Checklist**: Complete all checklist items before opening or marking ready for review.
- **Semantic PR Titles**: Use conventional commit titles matching [`.github/release-drafter.yml`](.github/release-drafter.yml):
- `feat:` New features / enhancements
- `fix:` Bug fixes
- `refactor:` Refactoring / code quality
- `test:` Test additions / updates
- `docs:` Documentation changes
- `chore:` Maintenance / dependency updates
- Ensure all tests (`tox -e py314`), linting (`tox -e lint`), and type checks (`tox -e mypy`) pass before submitting PRs.

### Creating Issues & Feature Requests
Always follow the templates in [`.github/ISSUE_TEMPLATE/`](.github/ISSUE_TEMPLATE/):
- **Bug Reports** ([`bug_report.yml`](.github/ISSUE_TEMPLATE/bug_report.yml)):
- Prefix title with `[Bug]: <summary>`.
- Include: Description, Steps to Reproduce, Expected Behavior, Environment Info (Library version, Python version, OpenEVSE WiFi Firmware version), and Debug Logs / Stack Trace.
- **Feature Requests** ([`feature_request.yml`](.github/ISSUE_TEMPLATE/feature_request.yml)):
- Prefix title with `[Feature Request]: <summary>`.
- Include: Problem statement, Desired solution, Alternatives considered, and Context.
Loading