picokvm-client 0.1.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.
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .pytest_cache/
12
+ .coverage
13
+ htmlcov/
14
+ uv.lock
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ All notable changes to `picokvm-client` are documented in this file.
4
+
5
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-06-23
9
+
10
+ Initial release.
11
+
12
+ ### Added
13
+
14
+ - `PicoKVMClient`: synchronous JSON-RPC 2.0 client for the Luckfox
15
+ PicoKVM HTTP API, built on `httpx` (transport and cookie auth) and
16
+ `jsonrpcclient` (request framing). Typed wrappers cover login, HID
17
+ input (type, click, key, combo), video state, virtual-media mount,
18
+ power and reset, and device introspection.
19
+ - `rpc(method, **params)`: generic JSON-RPC call for any other device
20
+ method.
21
+ - `PicoKVMError` hierarchy (`AuthError`, `TransportError`, `RpcError`)
22
+ rooted at the stdlib `Exception`, each with advisory `exit_code`,
23
+ `hint`, and `retryable` attributes.
24
+ - `picokvm` command-line tool.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Onur Celep
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,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: picokvm-client
3
+ Version: 0.1.0
4
+ Summary: Unofficial Python client for the Luckfox PicoKVM JSON-RPC API
5
+ Project-URL: Homepage, https://github.com/onurcelep/picokvm-client
6
+ Project-URL: Repository, https://github.com/onurcelep/picokvm-client
7
+ Project-URL: Issues, https://github.com/onurcelep/picokvm-client/issues
8
+ Author-email: Onur Celep <onurcelep@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: automation,hid,jetkvm,jsonrpc,kvm,picokvm
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Classifier: Topic :: System :: Hardware
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: click<9,>=8.1
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: jsonrpcclient>=4.0.0
25
+ Requires-Dist: pydantic>=2.0.0
26
+ Requires-Dist: typer>=0.9.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: gitlint>=0.19.1; extra == 'dev'
29
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
30
+ Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
31
+ Requires-Dist: pytest-mock>=3.12.0; extra == 'dev'
32
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
33
+ Requires-Dist: respx>=0.21.0; extra == 'dev'
34
+ Requires-Dist: ruff>=0.8.0; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # picokvm-client
38
+
39
+ Python client for the [Luckfox PicoKVM](https://github.com/LuckfoxTECH/kvm).
40
+ A thin, synchronous wrapper around its JSON-RPC 2.0 API (`POST /api/rpc`)
41
+ and cookie-auth endpoint (`POST /auth/login-local`), built on `httpx` and
42
+ `jsonrpcclient`.
43
+
44
+ > **Unofficial.** Independent, community-built client. Not affiliated with,
45
+ > authorized by, or endorsed by Luckfox or JetKVM. "PicoKVM" and "JetKVM"
46
+ > belong to their respective owners and are used here only to describe
47
+ > compatibility. No vendor source code is included; this reimplements the
48
+ > device's public JSON-RPC protocol and standard USB HID Boot-Protocol
49
+ > reports.
50
+
51
+ ## Compatibility
52
+
53
+ Targets the Luckfox PicoKVM. Its firmware is a Luckfox-Pico fork of
54
+ [JetKVM](https://github.com/jetkvm/kvm), so the wire protocol currently
55
+ overlaps with JetKVM and its other forks. This client is only tested against
56
+ the PicoKVM and makes no compatibility promise to JetKVM; a JetKVM is better
57
+ served by a dedicated `jetkvm-client`.
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pip install picokvm-client
63
+ # or
64
+ uv add picokvm-client
65
+ ```
66
+
67
+ ## CLI
68
+
69
+ The package installs a `picokvm` command:
70
+
71
+ ```bash
72
+ export PICOKVM_URL=http://kvm.example PICOKVM_PASSWORD=hunter2
73
+
74
+ picokvm ping
75
+ picokvm device-id
76
+ picokvm video-state
77
+ picokvm type "hello world"
78
+ picokvm combo "Ctrl+Alt+Del"
79
+ picokvm rpc getJigglerState
80
+ ```
81
+
82
+ `--url` and `--password` override the environment variables. Run
83
+ `picokvm --help` for the full command list.
84
+
85
+ > **Safety.** The HID commands (`type`, `key`, `combo`, `click`) inject input
86
+ > into whatever host is attached to the KVM. A mistake can lock the keyboard,
87
+ > type into the wrong window, or click at random coordinates. Test against a
88
+ > throwaway target before using these in automation.
89
+
90
+ ## Library
91
+
92
+ ```python
93
+ from picokvm_client import PicoKVMClient
94
+
95
+ with PicoKVMClient("http://kvm.example", password="hunter2") as kvm:
96
+ if not kvm.ping():
97
+ raise RuntimeError("KVM not responding")
98
+
99
+ state = kvm.get_video_state()
100
+ print(f"Video: {state.width}x{state.height}, ready={state.ready}")
101
+
102
+ kvm.key_combo("Ctrl+Alt+Del")
103
+ kvm.type_text("hello world\n")
104
+ kvm.click(state.width // 2, state.height // 2)
105
+
106
+ kvm.mount_with_http("https://files.example.com/installer.iso")
107
+ kvm.trigger_reset()
108
+ ```
109
+
110
+ The client owns an `httpx.Client` that holds the session cookie. Use it as a
111
+ context manager so the connection pool and cookie are released on exit.
112
+
113
+ ## Methods
114
+
115
+ Common operations have typed methods. Anything else on the device is reachable
116
+ through `client.rpc(method, **params)`, a generic JSON-RPC call.
117
+
118
+ | Family | Typed methods | Via `.rpc()` |
119
+ |-------------------|--------------------------------------------------------------------------|--------------------|
120
+ | HID input | `keyboard_report`, `abs_mouse_report`, `rel_mouse_report`, `wheel_report`, `get_keyboard_led_state`, `send_usb_wakeup_signal`, `type_text`, `click`, `key_press`, `key_combo` | macros, layout get/set, jiggler |
121
+ | Video | `get_video_state`, `wait_for_video` | EDID, capture |
122
+ | Virtual media | `mount_with_http`, `mount_built_in_image`, `mount_with_storage`, `unmount_image` | upload, listing |
123
+ | Device management | `get_device_id`, `ping`, `reboot`, `send_wol_magic_packet` | network, OTA, logs |
124
+ | Power | `trigger_power`, `trigger_reset` | hold-power |
125
+ | Auth | `login` (called automatically by `__enter__` when a password is set) | logout |
126
+
127
+ ## Exceptions
128
+
129
+ All exceptions inherit from `PicoKVMError` (itself a plain `Exception`, no
130
+ third-party base). Each carries advisory `exit_code`, `hint`, and `retryable`
131
+ attributes that a caller can map onto its own error type.
132
+
133
+ | Exception | Raised on |
134
+ |------------------|----------------------------------------------------------|
135
+ | `AuthError` | HTTP 401 from login or RPC |
136
+ | `TransportError` | non-2xx HTTP (other than 401), invalid JSON, network failure |
137
+ | `RpcError` | a JSON-RPC 2.0 `error` response |
138
+ | `PicoKVMError` | base class for the above |
139
+
140
+ ## Examples
141
+
142
+ Runnable scripts in [examples/](examples/) (each reads `PICOKVM_URL` and
143
+ `PICOKVM_PASSWORD`):
144
+
145
+ | Script | What it does |
146
+ |------------------------------------------------------------------|------------------------------------------------|
147
+ | [01_login_and_status.py](examples/01_login_and_status.py) | Connect, ping, print device ID and video state |
148
+ | [02_send_text_and_combo.py](examples/02_send_text_and_combo.py) | Type a string and send a key combo |
149
+ | [03_mount_iso_and_reboot.py](examples/03_mount_iso_and_reboot.py)| Mount an ISO over HTTP and wait for boot |
150
+
151
+ ## Testing
152
+
153
+ Tests drive the client through `httpx.MockTransport`, so no sockets are
154
+ opened. Each typed method asserts the exact JSON-RPC `method` and `params` it
155
+ puts on the wire.
156
+
157
+ The HID commands (`type_text`, `click`, `key_press`, `key_combo`) are checked
158
+ at the payload level only, not against real hardware. Run a manual smoke test
159
+ on a throwaway target before relying on them.
160
+
161
+ ## Development
162
+
163
+ ```bash
164
+ uv sync --extra dev
165
+ pre-commit install --install-hooks
166
+ pre-commit install --hook-type commit-msg
167
+ git config commit.template .gitmessage
168
+ ```
169
+
170
+ Checks: `uv run pytest`, `uv run ruff check src tests`,
171
+ `uv run mypy src/picokvm_client --strict`. Commit messages follow `.gitlint`
172
+ (subject 10 to 72 chars, body wrapped at 72, `Signed-off-by` required, so use
173
+ `git commit -s`).
174
+
175
+ ## Issues
176
+
177
+ File issues at <https://github.com/onurcelep/picokvm-client/issues>.
@@ -0,0 +1,141 @@
1
+ # picokvm-client
2
+
3
+ Python client for the [Luckfox PicoKVM](https://github.com/LuckfoxTECH/kvm).
4
+ A thin, synchronous wrapper around its JSON-RPC 2.0 API (`POST /api/rpc`)
5
+ and cookie-auth endpoint (`POST /auth/login-local`), built on `httpx` and
6
+ `jsonrpcclient`.
7
+
8
+ > **Unofficial.** Independent, community-built client. Not affiliated with,
9
+ > authorized by, or endorsed by Luckfox or JetKVM. "PicoKVM" and "JetKVM"
10
+ > belong to their respective owners and are used here only to describe
11
+ > compatibility. No vendor source code is included; this reimplements the
12
+ > device's public JSON-RPC protocol and standard USB HID Boot-Protocol
13
+ > reports.
14
+
15
+ ## Compatibility
16
+
17
+ Targets the Luckfox PicoKVM. Its firmware is a Luckfox-Pico fork of
18
+ [JetKVM](https://github.com/jetkvm/kvm), so the wire protocol currently
19
+ overlaps with JetKVM and its other forks. This client is only tested against
20
+ the PicoKVM and makes no compatibility promise to JetKVM; a JetKVM is better
21
+ served by a dedicated `jetkvm-client`.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install picokvm-client
27
+ # or
28
+ uv add picokvm-client
29
+ ```
30
+
31
+ ## CLI
32
+
33
+ The package installs a `picokvm` command:
34
+
35
+ ```bash
36
+ export PICOKVM_URL=http://kvm.example PICOKVM_PASSWORD=hunter2
37
+
38
+ picokvm ping
39
+ picokvm device-id
40
+ picokvm video-state
41
+ picokvm type "hello world"
42
+ picokvm combo "Ctrl+Alt+Del"
43
+ picokvm rpc getJigglerState
44
+ ```
45
+
46
+ `--url` and `--password` override the environment variables. Run
47
+ `picokvm --help` for the full command list.
48
+
49
+ > **Safety.** The HID commands (`type`, `key`, `combo`, `click`) inject input
50
+ > into whatever host is attached to the KVM. A mistake can lock the keyboard,
51
+ > type into the wrong window, or click at random coordinates. Test against a
52
+ > throwaway target before using these in automation.
53
+
54
+ ## Library
55
+
56
+ ```python
57
+ from picokvm_client import PicoKVMClient
58
+
59
+ with PicoKVMClient("http://kvm.example", password="hunter2") as kvm:
60
+ if not kvm.ping():
61
+ raise RuntimeError("KVM not responding")
62
+
63
+ state = kvm.get_video_state()
64
+ print(f"Video: {state.width}x{state.height}, ready={state.ready}")
65
+
66
+ kvm.key_combo("Ctrl+Alt+Del")
67
+ kvm.type_text("hello world\n")
68
+ kvm.click(state.width // 2, state.height // 2)
69
+
70
+ kvm.mount_with_http("https://files.example.com/installer.iso")
71
+ kvm.trigger_reset()
72
+ ```
73
+
74
+ The client owns an `httpx.Client` that holds the session cookie. Use it as a
75
+ context manager so the connection pool and cookie are released on exit.
76
+
77
+ ## Methods
78
+
79
+ Common operations have typed methods. Anything else on the device is reachable
80
+ through `client.rpc(method, **params)`, a generic JSON-RPC call.
81
+
82
+ | Family | Typed methods | Via `.rpc()` |
83
+ |-------------------|--------------------------------------------------------------------------|--------------------|
84
+ | HID input | `keyboard_report`, `abs_mouse_report`, `rel_mouse_report`, `wheel_report`, `get_keyboard_led_state`, `send_usb_wakeup_signal`, `type_text`, `click`, `key_press`, `key_combo` | macros, layout get/set, jiggler |
85
+ | Video | `get_video_state`, `wait_for_video` | EDID, capture |
86
+ | Virtual media | `mount_with_http`, `mount_built_in_image`, `mount_with_storage`, `unmount_image` | upload, listing |
87
+ | Device management | `get_device_id`, `ping`, `reboot`, `send_wol_magic_packet` | network, OTA, logs |
88
+ | Power | `trigger_power`, `trigger_reset` | hold-power |
89
+ | Auth | `login` (called automatically by `__enter__` when a password is set) | logout |
90
+
91
+ ## Exceptions
92
+
93
+ All exceptions inherit from `PicoKVMError` (itself a plain `Exception`, no
94
+ third-party base). Each carries advisory `exit_code`, `hint`, and `retryable`
95
+ attributes that a caller can map onto its own error type.
96
+
97
+ | Exception | Raised on |
98
+ |------------------|----------------------------------------------------------|
99
+ | `AuthError` | HTTP 401 from login or RPC |
100
+ | `TransportError` | non-2xx HTTP (other than 401), invalid JSON, network failure |
101
+ | `RpcError` | a JSON-RPC 2.0 `error` response |
102
+ | `PicoKVMError` | base class for the above |
103
+
104
+ ## Examples
105
+
106
+ Runnable scripts in [examples/](examples/) (each reads `PICOKVM_URL` and
107
+ `PICOKVM_PASSWORD`):
108
+
109
+ | Script | What it does |
110
+ |------------------------------------------------------------------|------------------------------------------------|
111
+ | [01_login_and_status.py](examples/01_login_and_status.py) | Connect, ping, print device ID and video state |
112
+ | [02_send_text_and_combo.py](examples/02_send_text_and_combo.py) | Type a string and send a key combo |
113
+ | [03_mount_iso_and_reboot.py](examples/03_mount_iso_and_reboot.py)| Mount an ISO over HTTP and wait for boot |
114
+
115
+ ## Testing
116
+
117
+ Tests drive the client through `httpx.MockTransport`, so no sockets are
118
+ opened. Each typed method asserts the exact JSON-RPC `method` and `params` it
119
+ puts on the wire.
120
+
121
+ The HID commands (`type_text`, `click`, `key_press`, `key_combo`) are checked
122
+ at the payload level only, not against real hardware. Run a manual smoke test
123
+ on a throwaway target before relying on them.
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ uv sync --extra dev
129
+ pre-commit install --install-hooks
130
+ pre-commit install --hook-type commit-msg
131
+ git config commit.template .gitmessage
132
+ ```
133
+
134
+ Checks: `uv run pytest`, `uv run ruff check src tests`,
135
+ `uv run mypy src/picokvm_client --strict`. Commit messages follow `.gitlint`
136
+ (subject 10 to 72 chars, body wrapped at 72, `Signed-off-by` required, so use
137
+ `git commit -s`).
138
+
139
+ ## Issues
140
+
141
+ File issues at <https://github.com/onurcelep/picokvm-client/issues>.
@@ -0,0 +1,49 @@
1
+ """Example 01: connect, ping, print device ID and video state.
2
+
3
+ Prerequisites:
4
+ export PICOKVM_URL=http://kvm.example
5
+ export PICOKVM_PASSWORD=hunter2 # omit if device is in noPassword mode
6
+
7
+ What this script does:
8
+ Opens a session, verifies the firmware is responding, then prints
9
+ the device ID and HDMI input state. No host-side side effects --
10
+ safe to run against any KVM you have access to.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ import sys
17
+
18
+ from picokvm_client import PicoKVMClient, PicoKVMError
19
+
20
+
21
+ def main() -> int:
22
+ url = os.environ.get("PICOKVM_URL")
23
+ if not url:
24
+ sys.stderr.write("error: set PICOKVM_URL\n")
25
+ return 2
26
+ password = os.environ.get("PICOKVM_PASSWORD")
27
+
28
+ try:
29
+ with PicoKVMClient(url, password=password) as kvm:
30
+ if not kvm.ping():
31
+ sys.stderr.write("KVM did not respond to ping\n")
32
+ return 1
33
+
34
+ print(f"device id: {kvm.get_device_id()}")
35
+
36
+ state = kvm.get_video_state()
37
+ print(
38
+ f"video : {state.width}x{state.height} @ {state.fps:g}fps "
39
+ f"ready={state.ready} error={state.error!r}"
40
+ )
41
+ except PicoKVMError as exc:
42
+ sys.stderr.write(f"error: {exc}\n")
43
+ return exc.exit_code
44
+
45
+ return 0
46
+
47
+
48
+ if __name__ == "__main__":
49
+ raise SystemExit(main())
@@ -0,0 +1,41 @@
1
+ """Example 02: send keystrokes via the typed HID helpers.
2
+
3
+ Prerequisites:
4
+ export PICOKVM_URL=http://kvm.example
5
+ export PICOKVM_PASSWORD=hunter2 # omit if device is in noPassword mode
6
+ Host attached to the KVM should be at a focused text input.
7
+
8
+ What this script does:
9
+ Types a literal string and then issues a Ctrl+Alt+T combo.
10
+ BEWARE: this WILL inject characters into whatever has focus on the
11
+ host -- only run against a DUT you do not mind being typed into.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import os
17
+ import sys
18
+
19
+ from picokvm_client import PicoKVMClient, PicoKVMError
20
+
21
+
22
+ def main() -> int:
23
+ url = os.environ.get("PICOKVM_URL")
24
+ if not url:
25
+ sys.stderr.write("error: set PICOKVM_URL\n")
26
+ return 2
27
+ password = os.environ.get("PICOKVM_PASSWORD")
28
+
29
+ try:
30
+ with PicoKVMClient(url, password=password) as kvm:
31
+ kvm.type_text("hello world")
32
+ kvm.key_combo("Ctrl+Alt+T")
33
+ except PicoKVMError as exc:
34
+ sys.stderr.write(f"error: {exc}\n")
35
+ return exc.exit_code
36
+
37
+ return 0
38
+
39
+
40
+ if __name__ == "__main__":
41
+ raise SystemExit(main())
@@ -0,0 +1,49 @@
1
+ """Example 03: mount an installer ISO over HTTP and trigger a reset.
2
+
3
+ Prerequisites:
4
+ export PICOKVM_URL=http://kvm.example
5
+ export PICOKVM_PASSWORD=hunter2 # omit if device is in noPassword mode
6
+ export PICOKVM_INSTALLER_URL=https://files.example.com/installer.iso
7
+
8
+ What this script does:
9
+ Mounts the ISO at PICOKVM_INSTALLER_URL as a virtual CD-ROM,
10
+ triggers a hard reset on the host so it boots from the new media,
11
+ then waits for the HDMI input to come back ready.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import os
17
+ import sys
18
+
19
+ from picokvm_client import PicoKVMClient, PicoKVMError
20
+
21
+
22
+ def main() -> int:
23
+ url = os.environ.get("PICOKVM_URL")
24
+ iso = os.environ.get("PICOKVM_INSTALLER_URL")
25
+ if not url or not iso:
26
+ sys.stderr.write("error: set PICOKVM_URL and PICOKVM_INSTALLER_URL\n")
27
+ return 2
28
+ password = os.environ.get("PICOKVM_PASSWORD")
29
+
30
+ try:
31
+ with PicoKVMClient(url, password=password) as kvm:
32
+ print(f"mounting {iso} ...")
33
+ kvm.mount_with_http(iso)
34
+
35
+ print("triggering reset ...")
36
+ kvm.trigger_reset()
37
+
38
+ print("waiting for video signal ...")
39
+ state = kvm.wait_for_video(timeout=120.0, poll_interval=2.0)
40
+ print(f"booted: {state.width}x{state.height} @ {state.fps:g}fps")
41
+ except PicoKVMError as exc:
42
+ sys.stderr.write(f"error: {exc}\n")
43
+ return exc.exit_code
44
+
45
+ return 0
46
+
47
+
48
+ if __name__ == "__main__":
49
+ raise SystemExit(main())
@@ -0,0 +1,85 @@
1
+ [project]
2
+ name = "picokvm-client"
3
+ version = "0.1.0"
4
+ description = "Unofficial Python client for the Luckfox PicoKVM JSON-RPC API"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = { text = "MIT" }
8
+ authors = [
9
+ { name = "Onur Celep", email = "onurcelep@gmail.com" },
10
+ ]
11
+ keywords = ["picokvm", "kvm", "jsonrpc", "jetkvm", "automation", "hid"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: Software Development :: Testing",
21
+ "Topic :: System :: Hardware",
22
+ ]
23
+
24
+ dependencies = [
25
+ "httpx>=0.27.0",
26
+ "jsonrpcclient>=4.0.0",
27
+ "pydantic>=2.0.0",
28
+ "typer>=0.9.0",
29
+ "click>=8.1,<9",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest>=7.4.0",
35
+ "pytest-mock>=3.12.0",
36
+ "respx>=0.21.0",
37
+ "ruff>=0.8.0",
38
+ "mypy>=1.0.0",
39
+ "pre-commit>=3.5.0",
40
+ "gitlint>=0.19.1",
41
+ ]
42
+
43
+ [project.scripts]
44
+ picokvm = "picokvm_client._cli:app"
45
+
46
+ [project.urls]
47
+ Homepage = "https://github.com/onurcelep/picokvm-client"
48
+ Repository = "https://github.com/onurcelep/picokvm-client"
49
+ Issues = "https://github.com/onurcelep/picokvm-client/issues"
50
+
51
+ [build-system]
52
+ requires = ["hatchling"]
53
+ build-backend = "hatchling.build"
54
+
55
+ [tool.hatch.build.targets.wheel]
56
+ packages = ["src/picokvm_client"]
57
+
58
+ [tool.hatch.build.targets.sdist]
59
+ include = ["src/picokvm_client", "tests", "examples", "README.md", "CHANGELOG.md", "LICENSE"]
60
+
61
+ [tool.pytest.ini_options]
62
+ testpaths = ["tests"]
63
+
64
+ [tool.ruff]
65
+ line-length = 120
66
+ target-version = "py311"
67
+
68
+ [tool.ruff.lint]
69
+ select = [
70
+ "E", # pycodestyle errors
71
+ "W", # pycodestyle warnings
72
+ "F", # pyflakes
73
+ "I", # isort (import sorting)
74
+ "B", # flake8-bugbear
75
+ "C4", # flake8-comprehensions
76
+ "UP", # pyupgrade
77
+ ]
78
+ ignore = ["E501"] # line length handled by the formatter
79
+
80
+ [tool.ruff.lint.isort]
81
+ known-first-party = ["picokvm_client"]
82
+
83
+ [tool.mypy]
84
+ python_version = "3.11"
85
+ strict = true
@@ -0,0 +1,38 @@
1
+ """Python client for the Luckfox PicoKVM device.
2
+
3
+ The PicoKVM exposes a JSON-RPC 2.0 API at ``POST /api/rpc`` (the same
4
+ methods the web UI drives) plus a session-cookie auth endpoint at
5
+ ``POST /auth/login-local``. This package is a thin, synchronous client
6
+ on top of :mod:`httpx` and :mod:`jsonrpcclient` covering both.
7
+
8
+ The PicoKVM firmware is a Luckfox-Pico-based fork of `JetKVM
9
+ <https://github.com/jetkvm/kvm>`_; the wire protocol is largely shared
10
+ with upstream and other forks today, but **this client makes no
11
+ compatibility commitment** to JetKVM or its other forks. The PicoKVM
12
+ is the only device this library is tested against. If you have a
13
+ JetKVM, this library *might* work for the methods that haven't
14
+ diverged — but a separate ``jetkvm-client`` package would be the right
15
+ home for that contract.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from picokvm_client.client import PicoKVMClient
21
+ from picokvm_client.exceptions import (
22
+ AuthError,
23
+ PicoKVMError,
24
+ RpcError,
25
+ TransportError,
26
+ )
27
+ from picokvm_client.methods import KeyboardLedState, UsbState, VideoState
28
+
29
+ __all__ = [
30
+ "AuthError",
31
+ "KeyboardLedState",
32
+ "PicoKVMClient",
33
+ "PicoKVMError",
34
+ "RpcError",
35
+ "TransportError",
36
+ "UsbState",
37
+ "VideoState",
38
+ ]