|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +`vudials_client` is a Python client library for Streacom's VU1 Dial hardware. It exposes two main classes: |
| 6 | + |
| 7 | +- **`VUDial`** — controls individual dials (value, color, background image, name, easing) |
| 8 | +- **`VUAdmin`** — manages the VU1 server (API key lifecycle, dial provisioning) |
| 9 | + |
| 10 | +The library communicates with a local VU1 server over plain HTTP using a key-in-URL query-parameter authentication scheme. |
| 11 | + |
| 12 | +**Package name (PyPI):** `vudials-client` |
| 13 | +**Import path:** `from vudials_client import vudialsclient` |
| 14 | +**Current version:** `2025.9.3` (calendar-versioned: `YYYY.M.patch`) |
| 15 | +**Requires:** Python ≥ 3.11, `requests ≥ 2.32.5` |
| 16 | + |
| 17 | +--- |
| 18 | + |
| 19 | +## Repository Layout |
| 20 | + |
| 21 | +``` |
| 22 | +src/vudials_client/ |
| 23 | + __init__.py # empty — intentional |
| 24 | + vudialsclient.py # VUUtil, VUAdminUtil, VUDial, VUAdmin |
| 25 | +
|
| 26 | +tests/ |
| 27 | + __init__.py |
| 28 | + conftest.py # pytest fixtures: vudial, vuadmin |
| 29 | + test_vudialsclient.py |
| 30 | +
|
| 31 | +.github/workflows/ |
| 32 | + ci.yml # matrix CI: Python 3.11 / 3.12 / 3.13 |
| 33 | +
|
| 34 | +pyproject.toml # build system, deps, pytest & coverage config |
| 35 | +pydoc-markdown.yml # generates docs/api.md from docstrings |
| 36 | +requirements.txt # loose runtime dep (requests>=2.0.0) |
| 37 | +``` |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +## Development Setup |
| 42 | + |
| 43 | +```bash |
| 44 | +git clone git@github.com:erinlkolp/vu1-dial-python-module.git |
| 45 | +cd vu1-dial-python-module |
| 46 | +pip install -e ".[dev]" |
| 47 | +``` |
| 48 | + |
| 49 | +The `[dev]` extra installs: `pytest>=8.0`, `responses>=0.25`, `pytest-cov>=5.0`. |
| 50 | + |
| 51 | +--- |
| 52 | + |
| 53 | +## Running Tests |
| 54 | + |
| 55 | +```bash |
| 56 | +# Run all tests with coverage |
| 57 | +pytest tests/ -v --cov=vudials_client --cov-report=term-missing |
| 58 | + |
| 59 | +# Run a specific test class |
| 60 | +pytest tests/test_vudialsclient.py::TestVUDialSetDialColor -v |
| 61 | + |
| 62 | +# Run without coverage (faster) |
| 63 | +pytest tests/ -v |
| 64 | +``` |
| 65 | + |
| 66 | +Tests use the `responses` library to mock all HTTP calls — no live server is needed. The two shared fixtures in `conftest.py` are `vudial` (a `VUDial` pointed at `localhost:5340`) and `vuadmin` (a `VUAdmin` pointed at the same address). |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +## Class Architecture |
| 71 | + |
| 72 | +### Utility base classes (internal) |
| 73 | + |
| 74 | +| Class | Purpose | |
| 75 | +|---|---| |
| 76 | +| `VUUtil` | Builds `key=` query-param URIs and dispatches GET / multipart-POST | |
| 77 | +| `VUAdminUtil` | Builds `admin_key=` query-param URIs and dispatches GET / POST | |
| 78 | + |
| 79 | +### Public classes |
| 80 | + |
| 81 | +| Class | Inherits | Key concern | |
| 82 | +|---|---|---| |
| 83 | +| `VUDial` | `VUUtil` | Dial control: value, color, image, name, easing | |
| 84 | +| `VUAdmin` | `VUAdminUtil` | Server admin: provision dials, CRUD on API keys | |
| 85 | + |
| 86 | +Every method returns a raw `requests.Response`. Callers must parse `.json()` or check `.status_code` themselves. All methods call `raise_for_status()` internally, so HTTP 4xx/5xx raise `requests.exceptions.HTTPError`. |
| 87 | + |
| 88 | +--- |
| 89 | + |
| 90 | +## Security Notes |
| 91 | + |
| 92 | +These are pre-existing design constraints of the VU1 server API — do not silently "fix" them: |
| 93 | + |
| 94 | +1. **Plain HTTP only.** The server only listens on HTTP; HTTPS is not supported. Keep traffic on trusted local interfaces. |
| 95 | +2. **Key-in-URL authentication.** Both `key=` and `admin_key=` parameters appear in the query string and will be recorded in server access logs, proxy logs, and HTTP client history. This is intentional until the upstream server adds header-based auth. |
| 96 | +3. **No input validation in the library.** The library does not validate value ranges (e.g., RGB 0–255) or UID format; that is the server's responsibility. |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +## Code Conventions |
| 101 | + |
| 102 | +- **No `logging.basicConfig()` in library code.** The module registers a named logger (`logging.getLogger(__name__)`) but never configures the root logger. Leave this pattern in place. |
| 103 | +- **`urllib.parse.quote` for user-supplied strings** in URL path segments (`uid`) and most query parameters to prevent URL injection. Do not remove these calls. |
| 104 | +- **Type annotations on all public methods.** Parameters and return types (`-> requests.Response`) must be annotated. |
| 105 | +- **Docstrings on all public methods** using the `:param name: description` / `:return:` style already present. |
| 106 | + |
| 107 | +--- |
| 108 | + |
| 109 | +## Adding New API Endpoints |
| 110 | + |
| 111 | +1. Identify whether the new endpoint belongs to the dial API (use `VUDial` + `VUUtil`) or the admin API (use `VUAdmin` + `VUAdminUtil`). |
| 112 | +2. URL-encode any user-supplied path segment with `quote(value, safe="")`. |
| 113 | +3. URL-encode query-parameter values with `quote(value)`. |
| 114 | +4. Coerce numeric parameters with `int()` before interpolating into the query string. |
| 115 | +5. Return the raw `requests.Response` without unwrapping JSON. |
| 116 | +6. Add a corresponding test class in `tests/test_vudialsclient.py` following the existing pattern: at minimum test the success path, the correct endpoint, key parameters, and HTTP error propagation. |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## Versioning & Publishing |
| 121 | + |
| 122 | +The project uses **calendar versioning** (`YYYY.M.patch`). To release a new version: |
| 123 | + |
| 124 | +1. Update `version` in `pyproject.toml`. |
| 125 | +2. Ensure the changelog / README reflects the change. |
| 126 | +3. Tag the commit and push; PyPI publishing is done manually via `hatch build && hatch publish` or the equivalent `twine upload`. |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +## CI/CD |
| 131 | + |
| 132 | +GitHub Actions runs `.github/workflows/ci.yml` on every push to `main` or any `claude/**` branch, and on PRs targeting `main`. |
| 133 | + |
| 134 | +- Matrix: Python 3.11, 3.12, 3.13 on `ubuntu-latest` |
| 135 | +- Command: `pytest tests/ -v --cov=vudials_client --cov-report=term-missing --cov-report=xml` |
| 136 | +- Coverage XML is uploaded as a build artifact (Python 3.12 run only) |
| 137 | + |
| 138 | +--- |
| 139 | + |
| 140 | +## Documentation Generation |
| 141 | + |
| 142 | +API docs in `docs/api.md` are generated from docstrings via `pydoc-markdown`: |
| 143 | + |
| 144 | +```bash |
| 145 | +pip install pydoc-markdown |
| 146 | +pydoc-markdown |
| 147 | +``` |
| 148 | + |
| 149 | +Configuration is in `pydoc-markdown.yml` (source: `src/vudials_client/`, output: `docs/api.md`). |
0 commit comments