pyks2 1.0.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.
Files changed (69) hide show
  1. pyks2-1.0.0/CHANGELOG.md +56 -0
  2. pyks2-1.0.0/LICENSE +21 -0
  3. pyks2-1.0.0/MANIFEST.in +5 -0
  4. pyks2-1.0.0/PKG-INFO +212 -0
  5. pyks2-1.0.0/README.md +179 -0
  6. pyks2-1.0.0/docs/METHODOLOGY.md +131 -0
  7. pyks2-1.0.0/docs/PROTOCOL.md +446 -0
  8. pyks2-1.0.0/docs/_config.yml +11 -0
  9. pyks2-1.0.0/docs/index.md +30 -0
  10. pyks2-1.0.0/examples/API_REFERENCE.json +317 -0
  11. pyks2-1.0.0/examples/README.md +44 -0
  12. pyks2-1.0.0/examples/apis.json +3 -0
  13. pyks2-1.0.0/examples/camera-shoot-finish-bulb.json +4 -0
  14. pyks2-1.0.0/examples/camera-shoot-response.json +7 -0
  15. pyks2-1.0.0/examples/camera-shoot-start-bulb.json +7 -0
  16. pyks2-1.0.0/examples/changes-events.jsonl +2 -0
  17. pyks2-1.0.0/examples/constants-camera.json +11 -0
  18. pyks2-1.0.0/examples/constants-device.json +7 -0
  19. pyks2-1.0.0/examples/constants-lens.json +2 -0
  20. pyks2-1.0.0/examples/constants-liveview.json +2 -0
  21. pyks2-1.0.0/examples/constants.json +145 -0
  22. pyks2-1.0.0/examples/dial-positions.json +262 -0
  23. pyks2-1.0.0/examples/error-400-bad-request.json +1 -0
  24. pyks2-1.0.0/examples/error-412-precondition.json +1 -0
  25. pyks2-1.0.0/examples/lens-focus-response.json +6 -0
  26. pyks2-1.0.0/examples/lens-identity-none.json +33 -0
  27. pyks2-1.0.0/examples/params-camera.json +13 -0
  28. pyks2-1.0.0/examples/params-device.json +5 -0
  29. pyks2-1.0.0/examples/params-lens.json +3 -0
  30. pyks2-1.0.0/examples/params-liveview.json +2 -0
  31. pyks2-1.0.0/examples/params.json +19 -0
  32. pyks2-1.0.0/examples/photos-latest-info.json +13 -0
  33. pyks2-1.0.0/examples/photos-listing.json +10 -0
  34. pyks2-1.0.0/examples/ping.json +4 -0
  35. pyks2-1.0.0/examples/props-camera.json +28 -0
  36. pyks2-1.0.0/examples/props-device.json +24 -0
  37. pyks2-1.0.0/examples/props-lens.json +5 -0
  38. pyks2-1.0.0/examples/props-liveview.json +3 -0
  39. pyks2-1.0.0/examples/props.json +54 -0
  40. pyks2-1.0.0/examples/status-camera.json +8 -0
  41. pyks2-1.0.0/examples/status-device-cardfull.json +19 -0
  42. pyks2-1.0.0/examples/status-device.json +16 -0
  43. pyks2-1.0.0/examples/status-lens.json +4 -0
  44. pyks2-1.0.0/examples/status-liveview.json +3 -0
  45. pyks2-1.0.0/examples/status.json +25 -0
  46. pyks2-1.0.0/examples/variables-camera.json +19 -0
  47. pyks2-1.0.0/examples/variables-device.json +19 -0
  48. pyks2-1.0.0/examples/variables-lens.json +5 -0
  49. pyks2-1.0.0/examples/variables-liveview.json +3 -0
  50. pyks2-1.0.0/examples/variables.json +40 -0
  51. pyks2-1.0.0/pyks2/__init__.py +69 -0
  52. pyks2-1.0.0/pyks2/cli.py +251 -0
  53. pyks2-1.0.0/pyks2/client.py +635 -0
  54. pyks2-1.0.0/pyks2/constants.py +115 -0
  55. pyks2-1.0.0/pyks2/errors.py +59 -0
  56. pyks2-1.0.0/pyks2/events.py +225 -0
  57. pyks2-1.0.0/pyks2/models.py +295 -0
  58. pyks2-1.0.0/pyks2.egg-info/PKG-INFO +212 -0
  59. pyks2-1.0.0/pyks2.egg-info/SOURCES.txt +67 -0
  60. pyks2-1.0.0/pyks2.egg-info/dependency_links.txt +1 -0
  61. pyks2-1.0.0/pyks2.egg-info/entry_points.txt +2 -0
  62. pyks2-1.0.0/pyks2.egg-info/requires.txt +6 -0
  63. pyks2-1.0.0/pyks2.egg-info/top_level.txt +1 -0
  64. pyks2-1.0.0/pyproject.toml +56 -0
  65. pyks2-1.0.0/setup.cfg +4 -0
  66. pyks2-1.0.0/tests/conftest.py +123 -0
  67. pyks2-1.0.0/tests/test_client.py +98 -0
  68. pyks2-1.0.0/tests/test_fixes.py +236 -0
  69. pyks2-1.0.0/tests/test_models.py +27 -0
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ All notable changes to **pyks2** are documented here. This project follows
4
+ [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [1.0.0] — 2026-07-16
7
+
8
+ First stable release: a complete, hardware-verified reverse-engineering of the
9
+ Pentax K-S2's built-in WiFi HTTP API, with a Python library, a CLI, and a full
10
+ protocol write-up. Every one of the camera's 38 API endpoint templates is
11
+ characterised — confirmed working, confirmed read-only, or confirmed
12
+ unsupported — with zero remaining unknowns.
13
+
14
+ ### The dissection
15
+ - Complete map of the `/v1/*` API: five read groups
16
+ (`constants`/`params`/`variables`/`status`/`props`) × four subsystems
17
+ (`camera`/`lens`/`liveview`/`device`), plus capture, focus, photo, live view,
18
+ and the `/v1/changes` WebSocket.
19
+ - Two protocol laws documented: `errCode` lives in the body (not the HTTP
20
+ status), and datetime/numeric formats vary by endpoint.
21
+ - Full mode-dial characterisation (P/Sv/Tv/Av/TAv/M/**Bulb**/U1/U2/auto/scene/
22
+ HDR/movie), with a per-mode value-writability matrix driven by list emptiness.
23
+ - Hardware interlocks mapped and explained: AF/MF lever, mode dial, movie mode
24
+ disabling WiFi, the SD-door disconnect, device/lens read-only params, and the
25
+ WiFi AP's client isolation.
26
+ - 40 real captured responses in `examples/`, plus a machine-readable
27
+ `examples/API_REFERENCE.json`.
28
+
29
+ ### Key findings that corrected earlier assumptions
30
+ - **`/v1/photos` does not hang.** The long-standing "it hangs indefinitely"
31
+ belief was a client-side artifact; the endpoint is reliable and scales gently
32
+ with file count. It also supports an undocumented `?limit=N`.
33
+ - **`shoot/start` / `shoot/finish` are Bulb exposure controls, not movie**, and
34
+ they work over WiFi when the dial is on B. (`bulb_start()`, `bulb_finish()`,
35
+ `bulb_exposure(seconds)`.)
36
+ - **`storages[].remain` is a frame count, not bytes.**
37
+ - **Lens identity is not exposed** over WiFi.
38
+ - **`/v1/liveview/zoom` is a no-op over WiFi** — accepts any param, returns 200,
39
+ but never changes the frame.
40
+
41
+ ### Library
42
+ - `K_S2_WiFi` a camera-only HTTP client with typed models, defensive parsing
43
+ for the datetime/numeric quirks, and `errCode`-aware exceptions.
44
+ - Race-free capture via `capture()` (baseline → shoot → wait for the new file).
45
+ - Event-driven workflow via the `/v1/changes` WebSocket (`events()`), replacing
46
+ the official app's polling.
47
+ - Correct handling of dynamic capability lists (e.g. `avList`) and the
48
+ list-emptiness writability signal for `av`/`tv`.
49
+
50
+ ### CLI
51
+ - `pyks2 ping | info | apis | lists | shoot | settings | focus | browse |
52
+ download | liveview | bulb | watch`.
53
+
54
+ ### Not included (planned)
55
+ - A web GUI is planned for a later release; this version ships the library, CLI,
56
+ and documentation.
pyks2-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jamal El Siblany
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ include CHANGELOG.md
2
+ include LICENSE
3
+ recursive-include docs *.md *.yml
4
+ recursive-include examples *.json *.jsonl *.md
5
+ recursive-include tests *.py
pyks2-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyks2
3
+ Version: 1.0.0
4
+ 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.)
5
+ Author-email: Jamal El Siblany <jamalsiblani@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/PICKLERICK2005/pyks2
8
+ Project-URL: Documentation, https://PICKLERICK2005.github.io/pyks2/
9
+ Project-URL: Repository, https://github.com/PICKLERICK2005/pyks2
10
+ Project-URL: Issues, https://github.com/PICKLERICK2005/pyks2/issues
11
+ Project-URL: Changelog, https://github.com/PICKLERICK2005/pyks2/blob/main/CHANGELOG.md
12
+ Keywords: pentax,k-s2,camera,wifi,reverse-engineering,tethering,remote-control,mjpeg
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Topic :: Multimedia :: Graphics :: Capture
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: requests>=2.25
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7; extra == "dev"
30
+ Requires-Dist: mypy>=1.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.4; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # pyks2
35
+
36
+ [![tests](https://github.com/PICKLERICK2005/pyks2/actions/workflows/test.yml/badge.svg)](https://github.com/PICKLERICK2005/pyks2/actions/workflows/test.yml)
37
+ [![python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
38
+ [![license](https://img.shields.io/badge/license-MIT-green)](https://github.com/PICKLERICK2005/pyks2/blob/main/LICENSE)
39
+
40
+ A Python library and CLI for controlling the **Pentax K-S2** over its
41
+ built-in WiFi, built on a complete, hardware-verified reverse-engineering of
42
+ the camera's undocumented HTTP API. (A web GUI is planned; see below.)
43
+
44
+ The K-S2 has a WiFi remote-control API, but Pentax never documented it. This
45
+ project maps the **entire surface** of the K-S2's API against a physical camera,
46
+ writes it up as a proper dissection, and ships a clean client that is lighter
47
+ and more capable than the vendor's own Image Sync app.
48
+
49
+ Visit the pages website ![here](https://picklerick2005.github.io/PyKS2/).
50
+
51
+ > **Two things make this more than "another camera library":**
52
+ > 1. A full [protocol dissection](https://picklerick2005.github.io/PyKS2/PROTOCOL.html) and
53
+ > [reverse-engineering methodology](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html), every endpoint,
54
+ > every quirk, every hardware limitation, with the raw captures to prove it.
55
+ > 2. A design that **beats the official app**: event-driven via the camera's
56
+ > WebSocket instead of the poll-storm Image Sync uses (~90% of its total
57
+ > requests were just polling one endpoint while interacting with app actively).
58
+
59
+ ---
60
+
61
+ ## What's here
62
+
63
+ ```
64
+ pyks2/ the library (camera-only HTTP client, typed models, WS events)
65
+ ├─ client.py K_S2_WiFi, the API client
66
+ ├─ models.py typed response models (defensive parsing)
67
+ ├─ events.py /v1/changes WebSocket client (stdlib, zero-dep)
68
+ ├─ constants.py endpoints + capability enums
69
+ ├─ errors.py typed exceptions (errCode-aware)
70
+ └─ cli.py the command-line interface
71
+ docs/ the reverse-engineering write-up (GitHub Pages source)
72
+ ├─ PROTOCOL.md the complete API dissection
73
+ └─ METHODOLOGY.md how it was probed (approach, false trails, traffic capture)
74
+ examples/ real captured JSON responses + the machine-readable API reference
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Quick start
80
+
81
+ ```bash
82
+ pip install pyks2 # or: pip install -e . from a clone
83
+ ```
84
+
85
+ Join the camera's WiFi (`PENTAX_XXXXXX`), then:
86
+
87
+ ```python
88
+ from pyks2 import K_S2_WiFi
89
+
90
+ cam = K_S2_WiFi() # defaults to 192.168.0.1
91
+ assert cam.ping()
92
+
93
+ # take one photo safely: records the baseline, fires, waits for the NEW file,
94
+ # and downloads it all in one call (af="off" is MF-safe: always releases)
95
+ info = cam.capture(af="off", download_to="shot.dng")
96
+ print("captured:", info.path)
97
+
98
+ # change settings (validated by the camera; illegal values raise)
99
+ cam.set_camera_params(av="8.0", sv="400")
100
+
101
+ # browse the card
102
+ for photo in cam.list_photos():
103
+ print(photo.path)
104
+ ```
105
+
106
+ Event-driven, no polling:
107
+
108
+ ```python
109
+ with cam.events() as ev: # /v1/changes WebSocket
110
+ for change in ev:
111
+ if change.is_storage: # a frame just landed
112
+ print("captured:", cam.latest_info().path)
113
+ elif change.is_camera: # a setting changed on the body
114
+ print("settings:", cam.get_camera_params())
115
+ ```
116
+
117
+ Live view (MJPEG):
118
+
119
+ ```python
120
+ for jpeg in cam.iter_liveview_frames(max_frames=1):
121
+ open("frame.jpg", "wb").write(jpeg)
122
+ ```
123
+
124
+ ---
125
+
126
+ ## CLI
127
+
128
+ Everything the library does, from a terminal:
129
+
130
+ ```bash
131
+ pyks2 ping
132
+ pyks2 info # model, firmware, battery, storage
133
+ pyks2 shoot --af off --wait --download shot.dng
134
+ pyks2 settings # show current settings
135
+ pyks2 settings av=8.0 sv=400 # set them
136
+ pyks2 lists # capability lists (dropdown sources)
137
+ pyks2 browse --limit 20 # list photos
138
+ pyks2 download 100_1507/IMGP1974.DNG --size view -o preview.jpg
139
+ pyks2 liveview -o frame.jpg
140
+ pyks2 watch --resolve # stream camera events live
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Web GUI
146
+
147
+ A browser-based control panel is planned, full Image-Sync parity plus
148
+ extensions (live view, remote capture, touch-to-focus, a settings panel driven
149
+ by the camera's own capability lists, an idle-safe gallery, and event-driven
150
+ updates), served by a thin local backend that reuses this library so it runs on
151
+ any OS in any browser. *Not yet included in this release, the library and CLI
152
+ are the shipping surfaces.*
153
+
154
+ ---
155
+
156
+ ## The reverse engineering
157
+
158
+ The heart of this project is the write-up. Highlights:
159
+
160
+ - **The full API is 40 endpoints**, organised as five read *groups*
161
+ (`constants`/`params`/`variables`/`status`/`props`) × four *subsystems*
162
+ (`camera`/`lens`/`liveview`/`device`), plus capture/focus/photo/liveview
163
+ actions and a WebSocket.
164
+ - **Two protocol laws** every client must respect: the real status is in the
165
+ body's `errCode` (not the HTTP status), and datetime/numeric formats are
166
+ inconsistent across endpoints.
167
+ - **Hardware interlocks** mapped and explained: the AF/MF lever, mode dial, and
168
+ movie mode are physical-only; movie mode *disables WiFi entirely*; opening the
169
+ SD-card door kills the connection; and the camera's WiFi access point uses
170
+ client isolation (which shaped how the official app's traffic had to be
171
+ captured).
172
+ - **How Image Sync actually works**, captured off the wire — and why this
173
+ client's event-driven design is better.
174
+
175
+ Start with **[docs/PROTOCOL.md](https://picklerick2005.github.io/PyKS2/PROTOCOL.html)**, then
176
+ **[docs/METHODOLOGY.md](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html)**. The raw evidence is in
177
+ **[examples/](https://github.com/PICKLERICK2005/pyks2/tree/main/examples)** and the machine-readable spec is
178
+ **[examples/API_REFERENCE.json](https://github.com/PICKLERICK2005/pyks2/blob/main/examples/API_REFERENCE.json)**.
179
+
180
+ ---
181
+
182
+ ## Development & tests
183
+
184
+ ```bash
185
+ git clone https://github.com/PICKLERICK2005/pyks2.git
186
+ cd pyks2
187
+ pip install -e ".[dev]" # installs pytest, mypy, ruff
188
+ pytest -q # 37 tests, no camera required
189
+ ```
190
+
191
+ The test suite runs entirely against captured fixtures (`examples/*.json`) via a
192
+ mock camera in `tests/conftest.py`, so it needs no hardware. Tests also serve as
193
+ executable documentation of the API's behaviour — including the trickier
194
+ findings (async capture, the Bulb correction, dynamic capability lists).
195
+
196
+ ## Compatibility
197
+
198
+ Verified against a **Pentax K-S2, firmware 01.10**. Other Pentax bodies (K-1,
199
+ KP, K-70, K-3…) share much of this API family but differ in specifics. Running
200
+ the probing approach in [docs/METHODOLOGY.md](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html) on another
201
+ body and sending the diffs is the most useful contribution you could make!
202
+
203
+ ## License
204
+
205
+ MIT — see [LICENSE](https://github.com/PICKLERICK2005/pyks2/blob/main/LICENSE).
206
+
207
+ ## Acknowledgements
208
+
209
+ The 2016 K-1 WiFi analysis on the antiguru wiki was a useful starting point for
210
+ hypotheses. Everything here was independently verified against a physical K-S2.
211
+ This project inspects only the camera's own network behaviour and my own hardware;
212
+ it contains no vendor code or any unmentioned references.
pyks2-1.0.0/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # pyks2
2
+
3
+ [![tests](https://github.com/PICKLERICK2005/pyks2/actions/workflows/test.yml/badge.svg)](https://github.com/PICKLERICK2005/pyks2/actions/workflows/test.yml)
4
+ [![python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
5
+ [![license](https://img.shields.io/badge/license-MIT-green)](https://github.com/PICKLERICK2005/pyks2/blob/main/LICENSE)
6
+
7
+ A Python library and CLI for controlling the **Pentax K-S2** over its
8
+ built-in WiFi, built on a complete, hardware-verified reverse-engineering of
9
+ the camera's undocumented HTTP API. (A web GUI is planned; see below.)
10
+
11
+ The K-S2 has a WiFi remote-control API, but Pentax never documented it. This
12
+ project maps the **entire surface** of the K-S2's API against a physical camera,
13
+ writes it up as a proper dissection, and ships a clean client that is lighter
14
+ and more capable than the vendor's own Image Sync app.
15
+
16
+ Visit the pages website ![here](https://picklerick2005.github.io/PyKS2/).
17
+
18
+ > **Two things make this more than "another camera library":**
19
+ > 1. A full [protocol dissection](https://picklerick2005.github.io/PyKS2/PROTOCOL.html) and
20
+ > [reverse-engineering methodology](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html), every endpoint,
21
+ > every quirk, every hardware limitation, with the raw captures to prove it.
22
+ > 2. A design that **beats the official app**: event-driven via the camera's
23
+ > WebSocket instead of the poll-storm Image Sync uses (~90% of its total
24
+ > requests were just polling one endpoint while interacting with app actively).
25
+
26
+ ---
27
+
28
+ ## What's here
29
+
30
+ ```
31
+ pyks2/ the library (camera-only HTTP client, typed models, WS events)
32
+ ├─ client.py K_S2_WiFi, the API client
33
+ ├─ models.py typed response models (defensive parsing)
34
+ ├─ events.py /v1/changes WebSocket client (stdlib, zero-dep)
35
+ ├─ constants.py endpoints + capability enums
36
+ ├─ errors.py typed exceptions (errCode-aware)
37
+ └─ cli.py the command-line interface
38
+ docs/ the reverse-engineering write-up (GitHub Pages source)
39
+ ├─ PROTOCOL.md the complete API dissection
40
+ └─ METHODOLOGY.md how it was probed (approach, false trails, traffic capture)
41
+ examples/ real captured JSON responses + the machine-readable API reference
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Quick start
47
+
48
+ ```bash
49
+ pip install pyks2 # or: pip install -e . from a clone
50
+ ```
51
+
52
+ Join the camera's WiFi (`PENTAX_XXXXXX`), then:
53
+
54
+ ```python
55
+ from pyks2 import K_S2_WiFi
56
+
57
+ cam = K_S2_WiFi() # defaults to 192.168.0.1
58
+ assert cam.ping()
59
+
60
+ # take one photo safely: records the baseline, fires, waits for the NEW file,
61
+ # and downloads it all in one call (af="off" is MF-safe: always releases)
62
+ info = cam.capture(af="off", download_to="shot.dng")
63
+ print("captured:", info.path)
64
+
65
+ # change settings (validated by the camera; illegal values raise)
66
+ cam.set_camera_params(av="8.0", sv="400")
67
+
68
+ # browse the card
69
+ for photo in cam.list_photos():
70
+ print(photo.path)
71
+ ```
72
+
73
+ Event-driven, no polling:
74
+
75
+ ```python
76
+ with cam.events() as ev: # /v1/changes WebSocket
77
+ for change in ev:
78
+ if change.is_storage: # a frame just landed
79
+ print("captured:", cam.latest_info().path)
80
+ elif change.is_camera: # a setting changed on the body
81
+ print("settings:", cam.get_camera_params())
82
+ ```
83
+
84
+ Live view (MJPEG):
85
+
86
+ ```python
87
+ for jpeg in cam.iter_liveview_frames(max_frames=1):
88
+ open("frame.jpg", "wb").write(jpeg)
89
+ ```
90
+
91
+ ---
92
+
93
+ ## CLI
94
+
95
+ Everything the library does, from a terminal:
96
+
97
+ ```bash
98
+ pyks2 ping
99
+ pyks2 info # model, firmware, battery, storage
100
+ pyks2 shoot --af off --wait --download shot.dng
101
+ pyks2 settings # show current settings
102
+ pyks2 settings av=8.0 sv=400 # set them
103
+ pyks2 lists # capability lists (dropdown sources)
104
+ pyks2 browse --limit 20 # list photos
105
+ pyks2 download 100_1507/IMGP1974.DNG --size view -o preview.jpg
106
+ pyks2 liveview -o frame.jpg
107
+ pyks2 watch --resolve # stream camera events live
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Web GUI
113
+
114
+ A browser-based control panel is planned, full Image-Sync parity plus
115
+ extensions (live view, remote capture, touch-to-focus, a settings panel driven
116
+ by the camera's own capability lists, an idle-safe gallery, and event-driven
117
+ updates), served by a thin local backend that reuses this library so it runs on
118
+ any OS in any browser. *Not yet included in this release, the library and CLI
119
+ are the shipping surfaces.*
120
+
121
+ ---
122
+
123
+ ## The reverse engineering
124
+
125
+ The heart of this project is the write-up. Highlights:
126
+
127
+ - **The full API is 40 endpoints**, organised as five read *groups*
128
+ (`constants`/`params`/`variables`/`status`/`props`) × four *subsystems*
129
+ (`camera`/`lens`/`liveview`/`device`), plus capture/focus/photo/liveview
130
+ actions and a WebSocket.
131
+ - **Two protocol laws** every client must respect: the real status is in the
132
+ body's `errCode` (not the HTTP status), and datetime/numeric formats are
133
+ inconsistent across endpoints.
134
+ - **Hardware interlocks** mapped and explained: the AF/MF lever, mode dial, and
135
+ movie mode are physical-only; movie mode *disables WiFi entirely*; opening the
136
+ SD-card door kills the connection; and the camera's WiFi access point uses
137
+ client isolation (which shaped how the official app's traffic had to be
138
+ captured).
139
+ - **How Image Sync actually works**, captured off the wire — and why this
140
+ client's event-driven design is better.
141
+
142
+ Start with **[docs/PROTOCOL.md](https://picklerick2005.github.io/PyKS2/PROTOCOL.html)**, then
143
+ **[docs/METHODOLOGY.md](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html)**. The raw evidence is in
144
+ **[examples/](https://github.com/PICKLERICK2005/pyks2/tree/main/examples)** and the machine-readable spec is
145
+ **[examples/API_REFERENCE.json](https://github.com/PICKLERICK2005/pyks2/blob/main/examples/API_REFERENCE.json)**.
146
+
147
+ ---
148
+
149
+ ## Development & tests
150
+
151
+ ```bash
152
+ git clone https://github.com/PICKLERICK2005/pyks2.git
153
+ cd pyks2
154
+ pip install -e ".[dev]" # installs pytest, mypy, ruff
155
+ pytest -q # 37 tests, no camera required
156
+ ```
157
+
158
+ The test suite runs entirely against captured fixtures (`examples/*.json`) via a
159
+ mock camera in `tests/conftest.py`, so it needs no hardware. Tests also serve as
160
+ executable documentation of the API's behaviour — including the trickier
161
+ findings (async capture, the Bulb correction, dynamic capability lists).
162
+
163
+ ## Compatibility
164
+
165
+ Verified against a **Pentax K-S2, firmware 01.10**. Other Pentax bodies (K-1,
166
+ KP, K-70, K-3…) share much of this API family but differ in specifics. Running
167
+ the probing approach in [docs/METHODOLOGY.md](https://picklerick2005.github.io/PyKS2/METHODOLOGY.html) on another
168
+ body and sending the diffs is the most useful contribution you could make!
169
+
170
+ ## License
171
+
172
+ MIT — see [LICENSE](https://github.com/PICKLERICK2005/pyks2/blob/main/LICENSE).
173
+
174
+ ## Acknowledgements
175
+
176
+ The 2016 K-1 WiFi analysis on the antiguru wiki was a useful starting point for
177
+ hypotheses. Everything here was independently verified against a physical K-S2.
178
+ This project inspects only the camera's own network behaviour and my own hardware;
179
+ it contains no vendor code or any unmentioned references.
@@ -0,0 +1,131 @@
1
+ ---
2
+ title: Methodology
3
+ nav_order: 3
4
+ ---
5
+
6
+ # How this was reverse-engineered
7
+
8
+ The [protocol dissection](PROTOCOL.md) presents the finished map. This document
9
+ is about *how* the map was made. The approach, the tools, the false trails, and
10
+ the traffic capture to find out how the official Image Sync app works. If you
11
+ want to replicate this on your own non-KS2 Pentax body, start here.
12
+
13
+ There was no documentation to work from. Everything below is the process of
14
+ turning a black-box camera into a fully characterised API.
15
+
16
+ ---
17
+
18
+ ## Principle: confirmed, not inferred
19
+
20
+ The guiding rule throughout: **a behaviour isn't "known" until the hardware
21
+ demonstrates it.** The official Pentax Image Sync android app, forum posts, and a
22
+ partial 2016 write-up of the related K-1 on the pentax forums were useful for coming up with
23
+ hypotheses, but every claim in the final map rests on a response captured from the actual camera body.
24
+ Several "facts" inherited from prior assumptions turned out to be wrong (see the `/v1/photos`
25
+ myth below), which is exactly why the this rule and methedology mattered most throughout
26
+ this project. The KS2 being very different from it's predecessors (post-ricoh acquisition)
27
+ is the main reason why this whole project was initiated, not to mention the fact that the
28
+ already-existing PKTRIGGERCORD does not support the KS2.
29
+
30
+ ---
31
+
32
+ ## Stage 1: find the surface
33
+
34
+ The break came from `GET /v1/apis`, which the camera answers with a list of all
35
+ its own endpoint templates. That turned an open-ended search into a finite
36
+ checklist of ~40 endpoints to characterise.
37
+
38
+ From there, the read endpoints were easy, and pleasantly so! because the
39
+ camera serves plain JSON that a browser renders directly (all hail firefox).
40
+ Navigating to `http://192.168.0.1/v1/props`, `/v1/constants/camera`, and so on in
41
+ Firefox showed each response formatted and readable, no tooling required. Hitting
42
+ each `/v1/{group}/{facet}` combination and diffing the results is where the
43
+ five-groups-×-four-facets structure became clear.
44
+
45
+ ## Stage 2: an automated scrubber
46
+
47
+ Hitting 40 endpoints by hand doesn't scale and isn't fun, so the
48
+ probing was scripted. A single sequenced scrubber:
49
+
50
+ - fired every endpoint in a defined order,
51
+ - captured status, headers, timing, and body for each,
52
+ - wrote every response to disk (the curated results became
53
+ [`examples/`](../examples/)),
54
+ - snapshotted the camera's settings before write-tests and restored them after
55
+ (so probing never left the camera in a weird state),
56
+ - and paused at the handful of probes that need physcial changes to the camera body or observations.
57
+
58
+ That last point became a core technique, using claude's help to make the scrubber script and instead
59
+ focusing on physically manipulating the camera when prompted.
60
+
61
+ The main methodology for these automated scripts is: human drives the hardware;
62
+ the script reads and correlates and logs any changes.**
63
+
64
+ This is how the event vocabulary was mapped. With the `/v1/changes` WebSocket
65
+ open, the script prompted through a scripted sequence of physical actions: "turn
66
+ the mode dial," "half-press the shutter," "flip the AF/MF lever," "open the SD
67
+ door" etc... and logged which `changed` events each action produced.
68
+
69
+ ---
70
+
71
+ ## Stage 5: capturing traffic from Pentax's Image Sync app on android
72
+
73
+ One question resisted every direct probe: how does the official app populate its
74
+ image gallery grid efficiently given that `?size=thumb` returns `400`?
75
+ The only way to know for certain was to watch the app's own traffic.
76
+ Because the camera speaks plain HTTP and does not verify it's being controlled by the
77
+ app whatsoever, no TLS interception was needed, only running an android phone's traffic through
78
+ a machine running mitmproxy was needed.
79
+
80
+ Although I was able to connect more than one device to the camera's hotspot,
81
+ **The camera's WiFi access point uses client isolation.** With the phone and a
82
+ PC both joined to the camera's network, so they cannot see each other, every
83
+ attempt to route the phone's traffic through a proxy on the PC timed out.
84
+ The PC could reach the camera (`192.168.0.1`) but not the phone on the same network.
85
+ Device-to-device traffic is blocked at the AP.
86
+
87
+ The fix was to change the topology so the phone→PC link doesn't go through the
88
+ camera at all:
89
+
90
+ ```
91
+ Phone ──(PC's own hotspot)──> Windows Computer ──(camera WiFi)──> Camera
92
+
93
+ proxy sits here
94
+ ```
95
+
96
+ The PC hosts a WiFi hotspot, the phone joins *that* (no isolation on the
97
+ PC's own network), and the PC bridges to the camera over its separate
98
+ WiFi connection with the proxy in the middle. Right away, even before setting up
99
+ the proxy, just by having the android device connected to the PC's hotspot which was
100
+ in turn connected to the KS2's hotspot, the Image Sync app was able to control the camera
101
+ just fine.
102
+
103
+ With the phone's proxy pointed at the PC, the app's requests were thoroughly recorded and
104
+ used to verify earlier findings. The gallery turned out to use exactly the documented endpoints
105
+ (`/v1/photos` →per-file `/info` → `?size=view` thumbnails), and the capture also revealed the
106
+ app's poll-heavy architecture (618 of 691 requests were `/v1/props` over a couple minutes).
107
+ No secret endpoints, just confirmation, plus the architectural insight that `pyks2` could
108
+ do better with the WebSocket due to the native app's poor performance.
109
+
110
+ > This technique was the least intrusive and didn't decompile or direclty modify any vendor software.
111
+
112
+ ---
113
+
114
+ ## Replicating on your own body
115
+
116
+ 1. Enable the camera's WiFi and join its network (`PENTAX_XXXXXX`).
117
+ 2. Confirm reachability by opening `http://192.168.0.1/v1/ping` in a browser
118
+ the camera serves readable JSON, so most reads can be explored this way.
119
+ 3. Walk the `/v1/{group}/{facet}` endpoints and the `/v1/apis` list; capture
120
+ each response. Scripting this (fire in sequence, save body + headers +
121
+ timing, snapshot/restore settings around any write-tests, and pause for
122
+ operator observation on the physical probes) makes it reproducible on different
123
+ bodies or firmware versions.
124
+ 4. Compare your captures against [`examples/`](../examples/). Different bodies
125
+ (K-1, KP, K-70) share this API family and will most likely differ in multiple ways
126
+ contributions documenting those differences are more than welcome!
127
+
128
+ ---
129
+
130
+ *Back to the [protocol dissection](PROTOCOL.md), or on to the library and CLI in
131
+ the [README](../README.md).*