fizzctl 0.2.0__tar.gz → 0.3.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.
- {fizzctl-0.2.0 → fizzctl-0.3.0}/PKG-INFO +40 -10
- {fizzctl-0.2.0 → fizzctl-0.3.0}/README.md +39 -8
- {fizzctl-0.2.0 → fizzctl-0.3.0}/pyproject.toml +2 -5
- fizzctl-0.3.0/pyproject.toml.orig +41 -0
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/__init__.py +1 -1
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/blobs.py +54 -115
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/capture.py +1 -20
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/cfg.py +0 -12
- fizzctl-0.3.0/src/fizzctl/cli.py +696 -0
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/effects.py +69 -26
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/hid.py +62 -74
- fizzctl-0.3.0/src/fizzctl/macro.py +202 -0
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/protocol.py +5 -18
- fizzctl-0.3.0/src/fizzctl/state.py +59 -0
- fizzctl-0.3.0/src/fizzctl/stock.ini +287 -0
- fizzctl-0.2.0/pyproject.toml.orig +0 -46
- fizzctl-0.2.0/src/fizzctl/cli.py +0 -409
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/animations.py +0 -0
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/keymap.py +0 -0
- {fizzctl-0.2.0 → fizzctl-0.3.0}/src/fizzctl/udev_rules.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fizzctl
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Control a Redragon K617 Fizz keyboard on Linux: solid colors, 22 firmware-native RGB effects, per-key painting, live animations, and Cfg.ini keymap restore — no vendor software required
|
|
5
5
|
Keywords: redragon,k617,fizz,rgb,keyboard,backlight,led,hid,linux
|
|
6
6
|
Author: Ayoub Dya
|
|
@@ -14,7 +14,6 @@ Classifier: Programming Language :: Python :: 3
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.14
|
|
15
15
|
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
|
16
16
|
Classifier: Topic :: System :: Hardware
|
|
17
|
-
Requires-Dist: dpkt>=1.9.8
|
|
18
17
|
Requires-Dist: hidapi>=0.15.0
|
|
19
18
|
Requires-Python: >=3.14
|
|
20
19
|
Description-Content-Type: text/markdown
|
|
@@ -60,6 +59,8 @@ the trigger did not pick it up.
|
|
|
60
59
|
| `key` | Paint a single key | yes |
|
|
61
60
|
| `paint` | Paint multiple keys at once | yes |
|
|
62
61
|
| `keymap` | Write the keymap from a Cfg.ini | yes |
|
|
62
|
+
| `macro` | Bind a key that types text on press | yes |
|
|
63
|
+
| `restore` | Reset to the factory keymap, lighting and macros | yes |
|
|
63
64
|
| `animate` | Host-streamed animation (volatile) | no |
|
|
64
65
|
|
|
65
66
|
## Examples
|
|
@@ -81,6 +82,35 @@ Apply the full keymap (bindings, lighting zones, function keys) from a
|
|
|
81
82
|
fizzctl keymap Cfg.ini
|
|
82
83
|
```
|
|
83
84
|
|
|
85
|
+
- A `keymap` write keeps your current effect/color/brightness and any macros
|
|
86
|
+
you have bound.
|
|
87
|
+
- `fizzctl restore` does the opposite: it resets the keyboard to factory
|
|
88
|
+
state by writing the packaged stock keymap and lighting and wiping every
|
|
89
|
+
macro.
|
|
90
|
+
|
|
91
|
+
### Macros
|
|
92
|
+
|
|
93
|
+
Bind a key to a macro that types text. Each macro gets its own slot (up to
|
|
94
|
+
8), and previously bound macros are kept — the device's current keymap is
|
|
95
|
+
read and reused as the base, and your lighting is untouched:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
fizzctl macro --key CapsLock rgb # type "rgb" each press
|
|
99
|
+
fizzctl macro --key LAlt --delay-ms 50 --cycles 3 hello
|
|
100
|
+
fizzctl macro --key 2 --until-released aaaa
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Options: `--delay-ms` (default 30) is the delay between typed events,
|
|
104
|
+
`--cycles` (default 1) plays the macro that many times per press, and
|
|
105
|
+
`--until-released` types in a loop until the key is let go. Macros and their
|
|
106
|
+
bindings are remembered in a local state file (`$XDG_STATE_HOME/fizzctl/`)
|
|
107
|
+
so later macro/keymap writes never drop them. To undo:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
fizzctl macro --remove-all # unbind every macro, keep keymap + lighting
|
|
111
|
+
fizzctl restore # full factory reset (keymap, lighting, macros)
|
|
112
|
+
```
|
|
113
|
+
|
|
84
114
|
### Set the whole board to one color
|
|
85
115
|
|
|
86
116
|
```bash
|
|
@@ -91,9 +121,9 @@ fizzctl rgb 00ff00 --brightness 4 # or: rgb 00ff00 -b 4
|
|
|
91
121
|
### Firmware effects
|
|
92
122
|
|
|
93
123
|
All 22 effects from the official Redragon software are supported, with full
|
|
94
|
-
speed and brightness control. `speed` and `brightness`
|
|
95
|
-
matching the firmware); higher = faster / brighter. Run
|
|
96
|
-
with no name to list every effect:
|
|
124
|
+
speed and brightness control. `speed` is 1..5 and `brightness` is 0..4 (5
|
|
125
|
+
levels each, matching the firmware); higher = faster / brighter. Run
|
|
126
|
+
`fizzctl effect` with no name to list every effect:
|
|
97
127
|
|
|
98
128
|
```bash
|
|
99
129
|
fizzctl effect
|
|
@@ -153,11 +183,11 @@ fizzctl-dev list
|
|
|
153
183
|
|
|
154
184
|
## How the RGB paths work
|
|
155
185
|
|
|
156
|
-
**Flash writes** (`rgb`, `key`, `paint`, `effect`, `keymap`
|
|
157
|
-
burst through the vendor HID interface (`258a:0049`,
|
|
158
|
-
`0xFF00`). The sequence ends with a `5AA5` magic
|
|
159
|
-
Colors persist across reboots. Keys not listed
|
|
160
|
-
turn off.
|
|
186
|
+
**Flash writes** (`rgb`, `key`, `paint`, `effect`, `keymap`, `macro`,
|
|
187
|
+
`restore`) send a burst through the vendor HID interface (`258a:0049`,
|
|
188
|
+
interface 1, usage page `0xFF00`). The sequence ends with a `5AA5` magic
|
|
189
|
+
commit that writes to flash. Colors persist across reboots. Keys not listed
|
|
190
|
+
in a `key` or `paint` canvas turn off.
|
|
161
191
|
|
|
162
192
|
**Animations** (`animate`) stream 382-byte per-key reports at the requested
|
|
163
193
|
frame rate with no flash commit. They are host-side only and lost on
|
|
@@ -39,6 +39,8 @@ the trigger did not pick it up.
|
|
|
39
39
|
| `key` | Paint a single key | yes |
|
|
40
40
|
| `paint` | Paint multiple keys at once | yes |
|
|
41
41
|
| `keymap` | Write the keymap from a Cfg.ini | yes |
|
|
42
|
+
| `macro` | Bind a key that types text on press | yes |
|
|
43
|
+
| `restore` | Reset to the factory keymap, lighting and macros | yes |
|
|
42
44
|
| `animate` | Host-streamed animation (volatile) | no |
|
|
43
45
|
|
|
44
46
|
## Examples
|
|
@@ -60,6 +62,35 @@ Apply the full keymap (bindings, lighting zones, function keys) from a
|
|
|
60
62
|
fizzctl keymap Cfg.ini
|
|
61
63
|
```
|
|
62
64
|
|
|
65
|
+
- A `keymap` write keeps your current effect/color/brightness and any macros
|
|
66
|
+
you have bound.
|
|
67
|
+
- `fizzctl restore` does the opposite: it resets the keyboard to factory
|
|
68
|
+
state by writing the packaged stock keymap and lighting and wiping every
|
|
69
|
+
macro.
|
|
70
|
+
|
|
71
|
+
### Macros
|
|
72
|
+
|
|
73
|
+
Bind a key to a macro that types text. Each macro gets its own slot (up to
|
|
74
|
+
8), and previously bound macros are kept — the device's current keymap is
|
|
75
|
+
read and reused as the base, and your lighting is untouched:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
fizzctl macro --key CapsLock rgb # type "rgb" each press
|
|
79
|
+
fizzctl macro --key LAlt --delay-ms 50 --cycles 3 hello
|
|
80
|
+
fizzctl macro --key 2 --until-released aaaa
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Options: `--delay-ms` (default 30) is the delay between typed events,
|
|
84
|
+
`--cycles` (default 1) plays the macro that many times per press, and
|
|
85
|
+
`--until-released` types in a loop until the key is let go. Macros and their
|
|
86
|
+
bindings are remembered in a local state file (`$XDG_STATE_HOME/fizzctl/`)
|
|
87
|
+
so later macro/keymap writes never drop them. To undo:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
fizzctl macro --remove-all # unbind every macro, keep keymap + lighting
|
|
91
|
+
fizzctl restore # full factory reset (keymap, lighting, macros)
|
|
92
|
+
```
|
|
93
|
+
|
|
63
94
|
### Set the whole board to one color
|
|
64
95
|
|
|
65
96
|
```bash
|
|
@@ -70,9 +101,9 @@ fizzctl rgb 00ff00 --brightness 4 # or: rgb 00ff00 -b 4
|
|
|
70
101
|
### Firmware effects
|
|
71
102
|
|
|
72
103
|
All 22 effects from the official Redragon software are supported, with full
|
|
73
|
-
speed and brightness control. `speed` and `brightness`
|
|
74
|
-
matching the firmware); higher = faster / brighter. Run
|
|
75
|
-
with no name to list every effect:
|
|
104
|
+
speed and brightness control. `speed` is 1..5 and `brightness` is 0..4 (5
|
|
105
|
+
levels each, matching the firmware); higher = faster / brighter. Run
|
|
106
|
+
`fizzctl effect` with no name to list every effect:
|
|
76
107
|
|
|
77
108
|
```bash
|
|
78
109
|
fizzctl effect
|
|
@@ -132,11 +163,11 @@ fizzctl-dev list
|
|
|
132
163
|
|
|
133
164
|
## How the RGB paths work
|
|
134
165
|
|
|
135
|
-
**Flash writes** (`rgb`, `key`, `paint`, `effect`, `keymap`
|
|
136
|
-
burst through the vendor HID interface (`258a:0049`,
|
|
137
|
-
`0xFF00`). The sequence ends with a `5AA5` magic
|
|
138
|
-
Colors persist across reboots. Keys not listed
|
|
139
|
-
turn off.
|
|
166
|
+
**Flash writes** (`rgb`, `key`, `paint`, `effect`, `keymap`, `macro`,
|
|
167
|
+
`restore`) send a burst through the vendor HID interface (`258a:0049`,
|
|
168
|
+
interface 1, usage page `0xFF00`). The sequence ends with a `5AA5` magic
|
|
169
|
+
commit that writes to flash. Colors persist across reboots. Keys not listed
|
|
170
|
+
in a `key` or `paint` canvas turn off.
|
|
140
171
|
|
|
141
172
|
**Animations** (`animate`) stream 382-byte per-key reports at the requested
|
|
142
173
|
frame rate with no flash commit. They are host-side only and lost on
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fizzctl"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Control a Redragon K617 Fizz keyboard on Linux: solid colors, 22 firmware-native RGB effects, per-key painting, live animations, and Cfg.ini keymap restore — no vendor software required"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
keywords = [
|
|
@@ -25,10 +25,7 @@ classifiers = [
|
|
|
25
25
|
"Topic :: System :: Hardware",
|
|
26
26
|
]
|
|
27
27
|
requires-python = ">=3.14"
|
|
28
|
-
dependencies = [
|
|
29
|
-
"dpkt>=1.9.8",
|
|
30
|
-
"hidapi>=0.15.0",
|
|
31
|
-
]
|
|
28
|
+
dependencies = ["hidapi>=0.15.0"]
|
|
32
29
|
|
|
33
30
|
[project.license]
|
|
34
31
|
text = "MIT"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fizzctl"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Control a Redragon K617 Fizz keyboard on Linux: solid colors, 22 firmware-native RGB effects, per-key painting, live animations, and Cfg.ini keymap restore — no vendor software required"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
authors = [{ name = "Ayoub Dya", email = "ayoubdya@gmail.com" }]
|
|
8
|
+
keywords = [
|
|
9
|
+
"redragon",
|
|
10
|
+
"k617",
|
|
11
|
+
"fizz",
|
|
12
|
+
"rgb",
|
|
13
|
+
"keyboard",
|
|
14
|
+
"backlight",
|
|
15
|
+
"led",
|
|
16
|
+
"hid",
|
|
17
|
+
"linux",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Environment :: Console",
|
|
22
|
+
"Intended Audience :: End Users/Desktop",
|
|
23
|
+
"Operating System :: POSIX :: Linux",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
"Topic :: System :: Hardware :: Hardware Drivers",
|
|
27
|
+
"Topic :: System :: Hardware",
|
|
28
|
+
]
|
|
29
|
+
requires-python = ">=3.14"
|
|
30
|
+
dependencies = ["hidapi>=0.15.0"]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
fizzctl = "fizzctl:main"
|
|
34
|
+
fizzctl-dev = "fizzctl:main_dev"
|
|
35
|
+
|
|
36
|
+
[build-system]
|
|
37
|
+
requires = ["uv_build>=0.12.13,<0.13.0"]
|
|
38
|
+
build-backend = "uv_build"
|
|
39
|
+
|
|
40
|
+
[tool.uv]
|
|
41
|
+
package = true
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
"""Baked firmware bytes, inlined as hex so the package ships no binary files.
|
|
2
2
|
|
|
3
3
|
All constants come from USB captures of the OEM software:
|
|
4
|
-
* CONST_MODE / CONST_CANVAS / CONST_ROUTING / CONST_EXEC
|
|
4
|
+
* CONST_MODE / CONST_CANVAS / CONST_ROUTING / CONST_EXEC — the
|
|
5
5
|
four constant 1032-byte frames of the Restore sequence.
|
|
6
|
-
*
|
|
6
|
+
* FW_TEMPLATE — the 5-frame effect template: shared MODE and ROUTING,
|
|
7
|
+
a distinct CANVAS, and five byte overrides of CONST_EXEC.
|
|
8
|
+
* RGB_EXEC / RGB_SEC — the whole-board solid-color variant.
|
|
9
|
+
* CONST_KEYMAP — a clean 1032-byte 06 04 d4 keymap block (no macro
|
|
10
|
+
bindings), captured from the device. The macro command uses it as the
|
|
11
|
+
default base; KeymapEncoder can rebuild the same block from a Cfg.ini.
|
|
7
12
|
"""
|
|
8
13
|
|
|
14
|
+
|
|
15
|
+
def _patch(base: bytes, diffs: dict[int, int]) -> bytes:
|
|
16
|
+
"""Copy `base`, overriding a handful of byte offsets."""
|
|
17
|
+
b = bytearray(base)
|
|
18
|
+
for off, val in diffs.items():
|
|
19
|
+
b[off] = val
|
|
20
|
+
return bytes(b)
|
|
21
|
+
|
|
22
|
+
|
|
9
23
|
CONST_MODE = bytes.fromhex(
|
|
10
24
|
"0608b80040000000000000000000000000000000000000000000000000ff0000"
|
|
11
25
|
"0000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00"
|
|
@@ -150,49 +164,9 @@ CONST_EXEC = bytes.fromhex(
|
|
|
150
164
|
"0000000000000000"
|
|
151
165
|
)
|
|
152
166
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
"0583b6000000"
|
|
157
|
-
),
|
|
158
|
-
# frame 1: 1032B
|
|
159
|
-
bytes.fromhex(
|
|
160
|
-
"0608b80040000000000000000000000000000000000000000000000000ff0000"
|
|
161
|
-
"0000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00"
|
|
162
|
-
"ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff000000"
|
|
163
|
-
"00ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff"
|
|
164
|
-
"00ffffffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000"
|
|
165
|
-
"ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00"
|
|
166
|
-
"ffffffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff"
|
|
167
|
-
"00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ff"
|
|
168
|
-
"ffffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00"
|
|
169
|
-
"ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ffff"
|
|
170
|
-
"ffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff"
|
|
171
|
-
"00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ffffff"
|
|
172
|
-
"ffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00"
|
|
173
|
-
"ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ffffffff"
|
|
174
|
-
"ffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ff"
|
|
175
|
-
"ff00ff00ff00ffffffffff000000000000000000000000000000000000000000"
|
|
176
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
177
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
178
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
179
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
180
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
181
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
182
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
183
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
184
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
185
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
186
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
187
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
188
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
189
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
190
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
191
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
192
|
-
"0000000000000000"
|
|
193
|
-
),
|
|
194
|
-
# frame 2: 1032B
|
|
195
|
-
bytes.fromhex(
|
|
167
|
+
INIT = bytes.fromhex("0583b6000000")
|
|
168
|
+
|
|
169
|
+
FW_CANVAS = bytes.fromhex(
|
|
196
170
|
"0609bc0040000000000000000000000000000000000000000000000000000000"
|
|
197
171
|
"00000000000000000000000000000000000000ff00ff00ff0000000000000000"
|
|
198
172
|
"0000000000000000ffffff00ff0000000000000000000000000000000000ff00"
|
|
@@ -226,89 +200,54 @@ FW_FRAMES = [
|
|
|
226
200
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
227
201
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
228
202
|
"0000000000000000"
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
"
|
|
243
|
-
"
|
|
244
|
-
"
|
|
245
|
-
"
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
249
|
-
"
|
|
250
|
-
"
|
|
251
|
-
"
|
|
252
|
-
"
|
|
253
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
254
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
255
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
256
|
-
"000000000000000000000000000000000000000000000000ff00ff0000000000"
|
|
257
|
-
"000000000000000000000000ffffff0000000000000000000000000000000000"
|
|
258
|
-
"ff0000000000000000000000000000000000000000ff00ff0000000000000000"
|
|
259
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
260
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
261
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
262
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
263
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
264
|
-
"0000000000000000"
|
|
265
|
-
),
|
|
266
|
-
# frame 4: 1032B
|
|
267
|
-
bytes.fromhex(
|
|
268
|
-
"0603b600000000000000000000005aa503030000000120010000000055550100"
|
|
269
|
-
"00000000ffff0034073307330733073307330733073307330733073307330733"
|
|
270
|
-
"07330733073307330733073307335aa500100744074407440744074407440744"
|
|
271
|
-
"0404040404040404040400000000000000000000000000000000000000000000"
|
|
272
|
-
"0000000000000000005aa5030300000000000000000000000000000000000000"
|
|
273
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
274
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
275
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
276
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
277
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
278
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
279
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
280
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
281
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
282
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
283
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
284
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
285
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
286
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
287
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
288
|
-
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
EXEC_DIFFS = {18: 0, 21: 1, 38: 0, 39: 52, 141: 0}
|
|
206
|
+
|
|
207
|
+
FW_TEMPLATE = [
|
|
208
|
+
INIT,
|
|
209
|
+
CONST_MODE,
|
|
210
|
+
FW_CANVAS,
|
|
211
|
+
CONST_ROUTING,
|
|
212
|
+
_patch(CONST_EXEC, EXEC_DIFFS),
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
CONST_KEYMAP = bytes.fromhex(
|
|
216
|
+
"0604d4004000000000000000020000000200000d02000014060000e1060000e0"
|
|
217
|
+
"00000000020000010200000e020000150000001d0200001e0000000002000002"
|
|
218
|
+
"0200000f020000160000001b0200001f00000000020000030200001002000017"
|
|
219
|
+
"0000000600000000000000000200000400000015000000090000001900000000"
|
|
220
|
+
"0000000002000005000000170000000a00000005020000200000000002000006"
|
|
221
|
+
"0000001c0000000b00000011000000000000000002000007000000180000000d"
|
|
222
|
+
"0200001a0000000000000000020000080000000c0000000e0200001b20000000"
|
|
223
|
+
"0000000002000009000000120000000f0200001c00000050000000000200000a"
|
|
224
|
+
"02000011020000180200001d00000000000000000200000b0200001202000019"
|
|
225
|
+
"0000000000000000000000000200000c02000013000000000000000000000051"
|
|
226
|
+
"000000000000002a0000003100000028000000520000004f0000000000000000"
|
|
289
227
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
290
228
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
291
229
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
292
230
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
293
231
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
232
|
+
"000000000000000000000000000000000000000000000000000000290000001e"
|
|
233
|
+
"0000001f00000020000000210000002200000023000000240000002500000026"
|
|
234
|
+
"000000270000002d0000002e0000002b000000140000001a0000000800000013"
|
|
235
|
+
"0000002f00000030000000390000000400000016000000070000003300000034"
|
|
236
|
+
"00000010000000360000003700000038060000e3060000e20000002c00000000"
|
|
294
237
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
238
|
+
"000000000000000000000000000000000000000000000000000000350000003a"
|
|
239
|
+
"0000003b0000003c0000003d0000003e0000003f000000400000004100000042"
|
|
240
|
+
"000000430000004400000045040000cd040000ea00000052040000e900000046"
|
|
241
|
+
"000000490000004c040000e200000050000000510000004f0000004a0000004d"
|
|
242
|
+
"0c0003000d0003000000004b0000004e0e0000010b0003000e00000700000000"
|
|
295
243
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
296
244
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
297
245
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
298
246
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
299
247
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
300
248
|
"0000000000000000"
|
|
301
|
-
|
|
302
|
-
]
|
|
303
|
-
|
|
304
|
-
|
|
249
|
+
)
|
|
305
250
|
|
|
306
|
-
# --- RGB static-color protocol ---
|
|
307
|
-
RGB_INIT = bytes.fromhex("0583b6000000")
|
|
308
|
-
# P1 canvas is BUILT (header + split-plane colors + SEC at 660), not taken from
|
|
309
|
-
# a template. P2 routing is the same 06 09 c0 block used by the Restore
|
|
310
|
-
# sequence (matches RESTORE_CONSTANT_FRAMES[2] exactly). EXEC must be the
|
|
311
|
-
# static-color variant: effect_id=0x01, brightness=0x15.
|
|
312
251
|
RGB_SEC = bytes.fromhex(
|
|
313
252
|
"ffffffffff000000000000000000000000000000ffffffffff00000000000000"
|
|
314
253
|
"00000000000000000000ffffff00ff000000000000000000000000000000ff00"
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"""USB capture import/diff/export for the K617 OEM software.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Input accepted:
|
|
4
4
|
* Wireshark JSON : `tshark -r cap.pcapng -T json > cap.json` (recommended)
|
|
5
|
-
* raw .pcap : Linux usbmon capture, parsed with dpkt
|
|
6
5
|
|
|
7
6
|
The job:
|
|
8
7
|
1. pull every HID feature-report payload out of the USB stream,
|
|
@@ -147,24 +146,6 @@ def _parse_usb_layer(u: dict, setup: dict | None = None) -> FrameCapture | None:
|
|
|
147
146
|
)
|
|
148
147
|
|
|
149
148
|
|
|
150
|
-
def load_pcap_usbmon(path: str) -> list[FrameCapture]:
|
|
151
|
-
"""Minimal Linux usbmon .pcap reader (dpkt)."""
|
|
152
|
-
import dpkt
|
|
153
|
-
|
|
154
|
-
out: list[FrameCapture] = []
|
|
155
|
-
with open(path, "rb") as fh:
|
|
156
|
-
pcap = dpkt.pcap.Reader(fh)
|
|
157
|
-
for _, buf in pcap:
|
|
158
|
-
try:
|
|
159
|
-
u = dpkt.usbmon.LinuxUSB(buf)
|
|
160
|
-
except Exception:
|
|
161
|
-
continue
|
|
162
|
-
data = bytes(u.transfer_buffer) if u.xfer_type & 0x80 else None
|
|
163
|
-
direction = "IN" if u.urb_type == 0x55 else "OUT"
|
|
164
|
-
out.append(FrameCapture(direction=direction, transfer="urb", request=None, report_id=None, data=data or b""))
|
|
165
|
-
return out
|
|
166
|
-
|
|
167
|
-
|
|
168
149
|
def significant(records: list[FrameCapture], direction: str = "OUT") -> list[FrameCapture]:
|
|
169
150
|
"""Keep only payload-bearing writes from the host (the interesting direction)."""
|
|
170
151
|
return [r for r in records if r.direction == direction and len(r.data) > 0]
|
|
@@ -104,15 +104,3 @@ class CfgIni:
|
|
|
104
104
|
@property
|
|
105
105
|
def key_entries(self) -> list[tuple[int, KeyEntry]]:
|
|
106
106
|
return sorted(self.keys.items())
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if __name__ == "__main__":
|
|
110
|
-
import sys
|
|
111
|
-
cfg = CfgIni(sys.argv[1] if len(sys.argv) > 1 else "Cfg.ini")
|
|
112
|
-
print(f"OPT: {len(cfg.opt)} keys")
|
|
113
|
-
print(f"[FN] {len(cfg.fn)} mappings")
|
|
114
|
-
for idx, be in cfg.fn_entries:
|
|
115
|
-
print(f" K{idx:<3} = {', '.join(f'0x{b:02X}' for b in be)}")
|
|
116
|
-
print(f"[KEY] {len(cfg.keys)} keys")
|
|
117
|
-
for idx, e in cfg.key_entries:
|
|
118
|
-
print(f" K{idx:<3} = {e.matrix} {', '.join(f'0x{b:02X}' for b in e.behavior)}")
|