Metadata-Version: 2.4
Name: pyks2
Version: 1.1.0b1
Summary: A Python client and CLI for the Pentax K-S2's undocumented WiFi HTTP API, plus a reverse-engineering write-up. (Web GUI planned.)
Author-email: Jamal El Siblany <jamalsiblani@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/PICKLERICK2005/pyks2
Project-URL: Documentation, https://PICKLERICK2005.github.io/pyks2/
Project-URL: Repository, https://github.com/PICKLERICK2005/pyks2
Project-URL: Issues, https://github.com/PICKLERICK2005/pyks2/issues
Project-URL: Changelog, https://github.com/PICKLERICK2005/pyks2/blob/main/CHANGELOG.md
Keywords: pentax,k-s2,camera,wifi,reverse-engineering,tethering,remote-control,mjpeg
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Topic :: Multimedia :: Graphics :: Capture
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25
Provides-Extra: async
Requires-Dist: httpx>=0.24; extra == "async"
Requires-Dist: websockets>=11; extra == "async"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: pyks2[async]; extra == "dev"
Dynamic: license-file

# PyKS2

[![tests](https://github.com/PICKLERICK2005/pyks2/actions/workflows/test.yml/badge.svg)](https://github.com/PICKLERICK2005/pyks2/actions/workflows/test.yml)
[![python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
[![license](https://img.shields.io/badge/license-MIT-green)](https://github.com/PICKLERICK2005/pyks2/blob/main/LICENSE)

A Python library and CLI for controlling the **Pentax K-S2** over its
built-in WiFi, built on an extensive, hardware-verified reverse-engineering of
the camera's undocumented HTTP API. (A web GUI is planned; see below.)

The K-S2 has a WiFi remote-control API, but Pentax never documented it. This
project maps the **entire surface** of the K-S2's API against a physical camera,
writes it up as a proper dissection, and ships a clean client that is lighter
and more capable than the vendor's own Image Sync app.

## Known gaps

This maps what I could exercise on my own body. Two areas are not fully
verified:

- **GPS** — the optional top-mounted Pentax GPS unit surfaces in
  `exposureModeList` but I couldn't test it (I don't own the unit). Untested.
- **Live-view digital zoom** — `POST /v1/liveview/zoom` exists but was gated on
  my setup: every parameter shape I tried returned `errCode 200`. It may need a
  lens or camera state I couldn't reach over the API. No working digital-zoom
  control was observed.

Everything else is behaviourally verified against the hardware below.

## Tested on

- **Body:** Pentax K-S2
- **Firmware:** 01.10
- **Lenses:** smc PENTAX-DA 18–50mm, DA 50–200mm

Other Pentax bodies with the same WiFi stack (K-1, KP, K-70, …) likely share
much of this API but are **untested**, reports welcome.

> **Two things make this more than "another camera library":**
> 1. A full [protocol dissection](https://picklerick2005.github.io/PyKS2/PROTOCOL.html) and
>    [reverse-engineering methodology](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html): the endpoints quirks, and hardware
>    limitations I could exercise on my camera, with the raw captures to back
>    it up.
> 2. A design that **beats the official app**: event-driven via the camera's
>    WebSocket instead of the poll-storm Image Sync uses (~90% of its total
>    requests were just polling one endpoint while interacting with the app actively).

---

## What's here

```
pyks2/          the library (camera-only HTTP client, typed models, WS events)
  ├─ client.py       K_S2_WiFi, the API client
  ├─ models.py       typed response models (defensive parsing)
  ├─ events.py       /v1/changes WebSocket client (stdlib, zero-dep)
  ├─ constants.py    endpoints + capability enums
  ├─ errors.py       typed exceptions (errCode-aware)
  ├─ _mjpeg.py       shared MJPEG frame parser (sync + async liveview)
  ├─ async_client.py optional async streaming (pyks2[async]; beta)
  └─ cli.py          the command-line interface
docs/           the reverse-engineering write-up (GitHub Pages source)
  ├─ PROTOCOL.md     the API dissection
  └─ METHODOLOGY.md  how it was probed (approach, false trails, traffic capture)
examples/       real captured JSON responses + the machine-readable API reference
```

---

## Quick start

```bash
pip install pyks2            # or: pip install -e .  from a clone
```

Join the camera's WiFi (`PENTAX_XXXXXX`), then:

```python
from pyks2 import K_S2_WiFi

cam = K_S2_WiFi()            # defaults to 192.168.0.1
assert cam.ping()

# take one photo safely: records the baseline, fires, waits for the NEW file,
# and downloads it all in one call (af="off" is MF-safe: always releases)
info = cam.capture(af="off", download_to="shot.dng")
print("captured:", info.path)

# change settings (validated by the camera; illegal values raise)
cam.set_camera_params(av="8.0", sv="400")

# ...or use the typed accessors, which also catch camera-controlled fields
# (e.g. av in Sv mode) and raise instead of silently no-opping
cam.set_iso(400)
cam.set_aperture(8.0)

# browse the card
for photo in cam.list_photos():
    print(photo.path)
```

Event-driven, no polling:

```python
with cam.events() as ev:              # /v1/changes WebSocket
    for change in ev:
        if change.is_storage:         # a frame just landed
            print("captured:", cam.latest_info().path)
        elif change.is_camera:        # a setting changed on the body
            print("settings:", cam.get_camera_params())
```

Live view (MJPEG):

```python
with cam.liveview() as stream:       # closes the stream (drops the mirror)
    for jpeg in stream:               # on exit, even if you break out early
        open("frame.jpg", "wb").write(jpeg)
        break
```

---

## Async streaming (beta)

`pip install pyks2[async]` pulls in `httpx`/`websockets` for async equivalents
of the event stream and live view. **Not yet verified against the physical
camera** — see [CHANGELOG](CHANGELOG.md).

```python
async with cam.events_async() as ev:
    async for change in ev:
        if change.is_storage:
            print("captured:", cam.latest_info().path)
```

---

## CLI

Everything the library does, from a terminal:

```bash
pyks2 ping
pyks2 info                          # model, firmware, battery (%), storage
pyks2 shoot --af off --wait --download shot.dng
pyks2 settings                      # show current settings
pyks2 settings av=8.0 sv=400        # set them
pyks2 lists                         # capability lists (dropdown sources)
pyks2 browse --limit 20             # list photos
pyks2 download 100_1507/IMGP1974.DNG --size view -o preview.jpg
pyks2 liveview -o frame.jpg
pyks2 watch --resolve               # stream camera events live
```

> `pyks2 info` reports the K-S2 battery as an integer percentage. In practice the camera reports values like `100`, `66`, and `0`, plus a `hot` thermal state flag in `status/device`.

---

## Web GUI

A browser-based control panel is planned, full Image-Sync parity plus
extensions (live view, remote capture, touch-to-focus, a settings panel driven
by the camera's own capability lists, an idle-safe gallery, and event-driven
updates), served by a thin local backend that reuses this library so it runs on
any OS in any browser. *Not yet included in this release, the library and CLI
are the shipping surfaces.*

---

## The reverse engineering

The heart of this project is the write-up. Highlights:

- **The API surface I mapped spans 38 endpoint templates**, organised as five
  read *groups* (`constants`/`params`/`variables`/`status`/`props`) × four
  *subsystems* (`camera`/`lens`/`liveview`/`device`), plus capture/focus/photo/
  liveview actions and a WebSocket. The main gaps I could not verify were GPS
  and LiveView Zoom.
- **Two protocol laws** every client must respect: the real status is in the
  body's `errCode` (not the HTTP status), and datetime/numeric formats are
  inconsistent across endpoints.
- **Hardware interlocks** mapped and explained: the AF/MF lever, mode dial, and
  movie mode are physical-only; movie mode *disables WiFi entirely*; opening the
  SD-card door kills the connection; and the camera's WiFi access point uses
  client isolation (which shaped how the official app's traffic had to be
  captured).
- **How Image Sync actually works**, captured off the wire — and why this
  client's event-driven design is better.

Start with **[docs/PROTOCOL.md](https://picklerick2005.github.io/PyKS2/PROTOCOL.html)**, then
**[docs/METHODOLOGY.md](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html)**. The raw evidence is in
**[examples/](https://github.com/PICKLERICK2005/pyks2/tree/main/examples)** and the machine-readable spec is
**[examples/API_REFERENCE.json](https://github.com/PICKLERICK2005/pyks2/blob/main/examples/API_REFERENCE.json)**.

---

## Development & tests

```bash
git clone https://github.com/PICKLERICK2005/pyks2.git
cd pyks2
pip install -e ".[dev]"     # installs pytest, mypy, ruff (+ the async extra)
pytest -q                    # 70 tests, no camera required
```

The test suite runs entirely against captured fixtures (`examples/*.json`) via a
mock camera in `tests/conftest.py`, so it needs no hardware. Tests also serve as
executable documentation of the API's behaviour — including the trickier
findings (async capture, the Bulb correction, dynamic capability lists).

## Compatibility

Verified against a **Pentax K-S2, firmware 01.10**. Other Pentax bodies (K-1,
KP, K-70, K-3…) share much of this API family but differ in specifics. Running
the probing approach in [docs/METHODOLOGY.md](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html) on another
body and sending the diffs is the most useful contribution you could make!

## Gaps

The only remaining gaps in this project would be:

> 1. The endpoint affiliated with the GPS module
> 2. The Liveview Zoom endpoint

Both of which likely require a corresponding hardware addon.

## License

MIT — see [LICENSE](https://github.com/PICKLERICK2005/pyks2/blob/main/LICENSE).

## Acknowledgements

The [2016 K-1 WiFi analysis](https://www.pentaxforums.com/forums/190-pentax-k-1-k-1-ii/359018-k-1-wifi-transfer.html) on the pentax forums was a useful starting point for
hypotheses. Everything here was independently verified against a physical K-S2.
This project inspects only the camera's own network behaviour and my own hardware;
it contains no vendor code or any unmentioned references.
