Metadata-Version: 2.4
Name: pi-frequency-lock-client
Version: 0.2.0
Summary: HTTP client for the 1530 PI frequency-lock service
Project-URL: Source, https://github.com/VV-rydberg/1530_pi_frequency_lock
Author: Quera
License: MIT
Requires-Python: >=3.10
Requires-Dist: httpx-sse>=0.4
Requires-Dist: httpx>=0.27
Description-Content-Type: text/markdown

# pi-frequency-lock-client

HTTP client for the 1530 PI frequency-lock service. A user installs this
package on any machine that can reach the lock server over HTTP, sets a
wavelength setpoint, and reads back live lock status.

## Prerequisites

To use the client, a machine needs:
- **Python ≥ 3.10**
- **Network access** to wherever the lock server is running (e.g. `http://pi.local:8000`)
- **`pip` (or `uv pip`)**

The client itself depends on `httpx` and `httpx-sse`; pip resolves these
automatically. There are no native or hardware dependencies — the hardware
all lives on the server.

## Installing the client

The way you `pip install` this depends on how it has been published. The
package is **not** auto-magically discoverable; one of the options below has
to actually be set up before any other computer can install it.

### Option A — From PyPI (only works once published)

```
pip install pi-frequency-lock-client
```

This is the smoothest path for end users, but it requires that a maintainer
has already done the following one-time setup:
1. Verified the name `pi-frequency-lock-client` is available on
   https://pypi.org (otherwise pick another name and update
   `client/pyproject.toml`).
2. Created a PyPI account and either a project API token or a Trusted
   Publisher configuration tying this repo to the PyPI project.
3. Published a release (see "Publishing" below).

Until that has happened, this command will fail with `No matching
distribution found`. Use Option B or C in the meantime.

### Option B — Directly from this git repo

Works from any machine that has `git` and can reach GitHub. No PyPI account
or release is needed; pip clones the repo, builds a wheel from `client/`,
and installs it.

```
pip install "git+https://github.com/VV-rydberg/1530_pi_frequency_lock.git#subdirectory=client"
```

To pin a specific version, target a tag:

```
pip install "git+https://github.com/VV-rydberg/1530_pi_frequency_lock.git@v0.1.0#subdirectory=client"
```

If the repo is private, the user's machine needs Git credentials configured
(SSH key or a GitHub Personal Access Token) — same as for `git clone`.

### Option C — From a wheel attached to a GitHub release

Build a wheel once, attach it to a release, then any user can:

```
pip install https://github.com/VV-rydberg/1530_pi_frequency_lock/releases/download/v0.1.0/pi_frequency_lock_client-0.1.0-py3-none-any.whl
```

No git needed on the user's side, just network access to GitHub.

### Option D — From a private package index

If you run a private index (AWS CodeArtifact, devpi, gemfury, JFrog
Artifactory, etc.), publish there and have users configure pip to use it
(`pip install --index-url https://<your-index>/simple/ pi-frequency-lock-client`,
or a per-user `~/.pip/pip.conf`).

## Quickstart

```python
from pi_frequency_lock_client import PiLockClient

c = PiLockClient("http://pi.local:8000")

# Set a wavelength setpoint and start the lock loop.
c.set_setpoint(1530.0)

# Block until lock is acquired (|err| <= 5 MHz held >= 2 s by default).
status = c.wait_for_lock(timeout_s=30)
print(f"locked: err={status.err_mhz:+.2f} MHz at V={status.voltage_v:.3f}")

# Stream live status.
for s in c.stream():
    print(s.state, s.err_mhz)
    if s.locked:
        break

# Release the hardware so you can use the wavemeter / DAC manually.
c.disconnect()
# ... do stuff ...
c.connect()
```

## API

- `PiLockClient(base_url, timeout=5.0)`
- `set_setpoint(nm, *, start_voltage=None, tolerance_mhz=20.0, holdoff_s=2.0, kp=None, ki=None, auto_connect=True)` → `Status`
- `set_gains(*, kp=None, ki=None)` → `Status`
- `stop()` → `Status`
- `connect()` → dict, `disconnect()` → dict
- `status()` → `Status`
- `health()` → dict
- `stream()` → iterator of `Status` (SSE)
- `wait_for_lock(timeout_s=60.0)` → `Status` (raises `TimeoutError`)

`Status` is a dataclass with fields: `state`, `connected`, `target_nm`,
`target_thz`, `meas_nm`, `meas_thz`, `err_mhz`, `voltage_v`,
`tolerance_mhz`, `holdoff_s`, `in_tolerance_for_s`, `locked`,
`last_update_ts`, `kp`, `ki`, `fault`.

## Publishing (maintainer-only)

From a checkout of the repo:

```
cd client
uv build              # produces dist/pi_frequency_lock_client-X.Y.Z-py3-none-any.whl
uvx twine upload dist/*    # PyPI; needs an API token on first run
```

Recommended: tag the repo (`git tag v0.1.0 && git push --tags`) and let CI
publish on tag push. A minimal workflow that uses Trusted Publishing
(no PyPI token in CI secrets):

```yaml
# .github/workflows/release-client.yml
name: release-client
on:
  push:
    tags: ["v*"]
jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    permissions: { id-token: write }
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: astral-sh/setup-uv@v3
      - run: uv build
        working-directory: client
      - uses: pypa/gh-action-pypi-publish@release/v1
        with: { packages-dir: client/dist }
```

Versions come from git tags via `hatch-vcs` — no manual bumps in
`pyproject.toml` needed.
