fizzctl 0.2.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/PKG-INFO +179 -0
- fizzctl-0.2.0/README.md +158 -0
- fizzctl-0.2.0/pyproject.toml +49 -0
- fizzctl-0.2.0/pyproject.toml.orig +46 -0
- fizzctl-0.2.0/src/fizzctl/__init__.py +10 -0
- fizzctl-0.2.0/src/fizzctl/animations.py +145 -0
- fizzctl-0.2.0/src/fizzctl/blobs.py +353 -0
- fizzctl-0.2.0/src/fizzctl/capture.py +214 -0
- fizzctl-0.2.0/src/fizzctl/cfg.py +118 -0
- fizzctl-0.2.0/src/fizzctl/cli.py +409 -0
- fizzctl-0.2.0/src/fizzctl/effects.py +223 -0
- fizzctl-0.2.0/src/fizzctl/hid.py +255 -0
- fizzctl-0.2.0/src/fizzctl/keymap.py +178 -0
- fizzctl-0.2.0/src/fizzctl/protocol.py +97 -0
- fizzctl-0.2.0/src/fizzctl/udev_rules.py +89 -0
fizzctl-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: fizzctl
|
|
3
|
+
Version: 0.2.0
|
|
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
|
+
Keywords: redragon,k617,fizz,rgb,keyboard,backlight,led,hid,linux
|
|
6
|
+
Author: Ayoub Dya
|
|
7
|
+
Author-email: Ayoub Dya <ayoubdya@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
|
16
|
+
Classifier: Topic :: System :: Hardware
|
|
17
|
+
Requires-Dist: dpkt>=1.9.8
|
|
18
|
+
Requires-Dist: hidapi>=0.15.0
|
|
19
|
+
Requires-Python: >=3.14
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# fizzctl
|
|
23
|
+
|
|
24
|
+
Linux tool for controlling the Redragon K617 Fizz keyboard's RGB lighting
|
|
25
|
+
and restoring keymaps from `Cfg.ini` files.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# from PyPI
|
|
31
|
+
pip install fizzctl
|
|
32
|
+
|
|
33
|
+
# from PyPI (uv)
|
|
34
|
+
uv tool install fizzctl
|
|
35
|
+
|
|
36
|
+
# from source
|
|
37
|
+
git clone https://github.com/you/fizzctl.git && cd fizzctl
|
|
38
|
+
uv build && uv tool install .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Udev rules (needs one `sudo`)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
fizzctl setup-udev
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This installs `99-k617.rules` to `/etc/udev/rules.d/`, reloads udev and
|
|
48
|
+
re-triggers device events. The command elevates its own privileged steps
|
|
49
|
+
via `sudo` (you are prompted for your password), so you do **not** need to
|
|
50
|
+
wrap it as `sudo fizzctl ...` — that fails because the binary lives in a
|
|
51
|
+
user-local path like `~/.local/bin`. Unplug/replug the keyboard afterwards if
|
|
52
|
+
the trigger did not pick it up.
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
| Command | Description | Persists |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `rgb` | Set the whole board to a solid color | yes |
|
|
59
|
+
| `effect` | Run one of 22 firmware-native effects | yes |
|
|
60
|
+
| `key` | Paint a single key | yes |
|
|
61
|
+
| `paint` | Paint multiple keys at once | yes |
|
|
62
|
+
| `keymap` | Write the keymap from a Cfg.ini | yes |
|
|
63
|
+
| `animate` | Host-streamed animation (volatile) | no |
|
|
64
|
+
|
|
65
|
+
## Examples
|
|
66
|
+
|
|
67
|
+
All commands accept short flag aliases alongside their long forms:
|
|
68
|
+
| Long | Short | Applies to |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `--color` | `-c` | `effect`, `animate` |
|
|
71
|
+
| `--speed` | `-s` | `effect`, `animate` |
|
|
72
|
+
| `--brightness` | `-b` | `effect`, `rgb` |
|
|
73
|
+
| `--fps` | `-f` | `animate` |
|
|
74
|
+
|
|
75
|
+
### Write keymap (most important)
|
|
76
|
+
|
|
77
|
+
Apply the full keymap (bindings, lighting zones, function keys) from a
|
|
78
|
+
`Cfg.ini` — the file the official Redragon software exports:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
fizzctl keymap Cfg.ini
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Set the whole board to one color
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
fizzctl rgb red
|
|
88
|
+
fizzctl rgb 00ff00 --brightness 4 # or: rgb 00ff00 -b 4
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Firmware effects
|
|
92
|
+
|
|
93
|
+
All 22 effects from the official Redragon software are supported, with full
|
|
94
|
+
speed and brightness control. `speed` and `brightness` are 5 levels (0-4,
|
|
95
|
+
matching the firmware); higher = faster / brighter. Run `fizzctl effect`
|
|
96
|
+
with no name to list every effect:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
fizzctl effect
|
|
100
|
+
fizzctl effect rainbow
|
|
101
|
+
fizzctl effect rainbow --speed 2 --brightness 4 # or: -s 2 -b 4
|
|
102
|
+
fizzctl effect fixed-on --color 00ff00 --brightness 3
|
|
103
|
+
fizzctl effect snake --color ff0000 --speed 4
|
|
104
|
+
fizzctl effect off
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Aliases are supported: `static` → `fixed-on`, `wheel` → `rainbow-wheel`,
|
|
108
|
+
`snake` → `retro-snake`, `waterfall` → `colorful-waterfall`.
|
|
109
|
+
|
|
110
|
+
### Per-key painting
|
|
111
|
+
|
|
112
|
+
Keys are named by their top-left legend. Any key *not* listed turns off
|
|
113
|
+
because the canvas is absolute:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
fizzctl key W ff0000
|
|
117
|
+
fizzctl paint W=ff0000 A=00ff00 S=ffff00 D=ff00ff Space=ffffff
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Host-side animations
|
|
121
|
+
|
|
122
|
+
Volatile — lost when the keyboard reconnects or reboots. Run `fizzctl
|
|
123
|
+
animate` with no name to list the animations:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
fizzctl animate
|
|
127
|
+
fizzctl animate rainbow --fps 30 --duration 10 # or: -f 30
|
|
128
|
+
fizzctl animate chase --color ff0000 --speed 2 # or: -c yellow -s 2
|
|
129
|
+
fizzctl animate solid --color 0000ff
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Debug mode
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
fizzctl --debug effect rainbow
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Shows the raw frames and handshake bytes sent over HID.
|
|
139
|
+
|
|
140
|
+
## Dev tools
|
|
141
|
+
|
|
142
|
+
The `fizzctl-dev` binary exposes the full reverse-engineering toolkit
|
|
143
|
+
(capture inspect, diff, export, replay) in addition to all user commands.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
fizzctl-dev inspect captures/r3.json
|
|
147
|
+
fizzctl-dev diff captures/r2.json captures/r3.json
|
|
148
|
+
fizzctl-dev export captures/r2.json frames.json
|
|
149
|
+
fizzctl-dev replay frames.json
|
|
150
|
+
fizzctl-dev cfg Cfg.ini
|
|
151
|
+
fizzctl-dev list
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## How the RGB paths work
|
|
155
|
+
|
|
156
|
+
**Flash writes** (`rgb`, `key`, `paint`, `effect`, `keymap`) send a 4-5 frame
|
|
157
|
+
burst through the vendor HID interface (`258a:0049`, interface 1, usage page
|
|
158
|
+
`0xFF00`). The sequence ends with a `5AA5` magic commit that writes to flash.
|
|
159
|
+
Colors persist across reboots. Keys not listed in a `key` or `paint` canvas
|
|
160
|
+
turn off.
|
|
161
|
+
|
|
162
|
+
**Animations** (`animate`) stream 382-byte per-key reports at the requested
|
|
163
|
+
frame rate with no flash commit. They are host-side only and lost on
|
|
164
|
+
reconnect.
|
|
165
|
+
|
|
166
|
+
## Colors
|
|
167
|
+
|
|
168
|
+
Colors can be given as a name or `RRGGBB` hex (with or without `#`). Spaces,
|
|
169
|
+
dashes and underscores in names are ignored, so `light green`, `light-green`
|
|
170
|
+
and `lightgreen` are equivalent:
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
red green blue white black off
|
|
174
|
+
yellow cyan magenta orange pink purple lime teal violet indigo coral salmon
|
|
175
|
+
navy brown gold silver gray grey olive maroon
|
|
176
|
+
lightred lightgreen lightblue darkred darkgreen darkblue
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Both `-c yellow` and `--color ffaa00` work anywhere a color flag is accepted.
|
fizzctl-0.2.0/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# fizzctl
|
|
2
|
+
|
|
3
|
+
Linux tool for controlling the Redragon K617 Fizz keyboard's RGB lighting
|
|
4
|
+
and restoring keymaps from `Cfg.ini` files.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# from PyPI
|
|
10
|
+
pip install fizzctl
|
|
11
|
+
|
|
12
|
+
# from PyPI (uv)
|
|
13
|
+
uv tool install fizzctl
|
|
14
|
+
|
|
15
|
+
# from source
|
|
16
|
+
git clone https://github.com/you/fizzctl.git && cd fizzctl
|
|
17
|
+
uv build && uv tool install .
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Udev rules (needs one `sudo`)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
fizzctl setup-udev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This installs `99-k617.rules` to `/etc/udev/rules.d/`, reloads udev and
|
|
27
|
+
re-triggers device events. The command elevates its own privileged steps
|
|
28
|
+
via `sudo` (you are prompted for your password), so you do **not** need to
|
|
29
|
+
wrap it as `sudo fizzctl ...` — that fails because the binary lives in a
|
|
30
|
+
user-local path like `~/.local/bin`. Unplug/replug the keyboard afterwards if
|
|
31
|
+
the trigger did not pick it up.
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
34
|
+
|
|
35
|
+
| Command | Description | Persists |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `rgb` | Set the whole board to a solid color | yes |
|
|
38
|
+
| `effect` | Run one of 22 firmware-native effects | yes |
|
|
39
|
+
| `key` | Paint a single key | yes |
|
|
40
|
+
| `paint` | Paint multiple keys at once | yes |
|
|
41
|
+
| `keymap` | Write the keymap from a Cfg.ini | yes |
|
|
42
|
+
| `animate` | Host-streamed animation (volatile) | no |
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
All commands accept short flag aliases alongside their long forms:
|
|
47
|
+
| Long | Short | Applies to |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `--color` | `-c` | `effect`, `animate` |
|
|
50
|
+
| `--speed` | `-s` | `effect`, `animate` |
|
|
51
|
+
| `--brightness` | `-b` | `effect`, `rgb` |
|
|
52
|
+
| `--fps` | `-f` | `animate` |
|
|
53
|
+
|
|
54
|
+
### Write keymap (most important)
|
|
55
|
+
|
|
56
|
+
Apply the full keymap (bindings, lighting zones, function keys) from a
|
|
57
|
+
`Cfg.ini` — the file the official Redragon software exports:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
fizzctl keymap Cfg.ini
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Set the whole board to one color
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
fizzctl rgb red
|
|
67
|
+
fizzctl rgb 00ff00 --brightness 4 # or: rgb 00ff00 -b 4
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Firmware effects
|
|
71
|
+
|
|
72
|
+
All 22 effects from the official Redragon software are supported, with full
|
|
73
|
+
speed and brightness control. `speed` and `brightness` are 5 levels (0-4,
|
|
74
|
+
matching the firmware); higher = faster / brighter. Run `fizzctl effect`
|
|
75
|
+
with no name to list every effect:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
fizzctl effect
|
|
79
|
+
fizzctl effect rainbow
|
|
80
|
+
fizzctl effect rainbow --speed 2 --brightness 4 # or: -s 2 -b 4
|
|
81
|
+
fizzctl effect fixed-on --color 00ff00 --brightness 3
|
|
82
|
+
fizzctl effect snake --color ff0000 --speed 4
|
|
83
|
+
fizzctl effect off
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Aliases are supported: `static` → `fixed-on`, `wheel` → `rainbow-wheel`,
|
|
87
|
+
`snake` → `retro-snake`, `waterfall` → `colorful-waterfall`.
|
|
88
|
+
|
|
89
|
+
### Per-key painting
|
|
90
|
+
|
|
91
|
+
Keys are named by their top-left legend. Any key *not* listed turns off
|
|
92
|
+
because the canvas is absolute:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
fizzctl key W ff0000
|
|
96
|
+
fizzctl paint W=ff0000 A=00ff00 S=ffff00 D=ff00ff Space=ffffff
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Host-side animations
|
|
100
|
+
|
|
101
|
+
Volatile — lost when the keyboard reconnects or reboots. Run `fizzctl
|
|
102
|
+
animate` with no name to list the animations:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
fizzctl animate
|
|
106
|
+
fizzctl animate rainbow --fps 30 --duration 10 # or: -f 30
|
|
107
|
+
fizzctl animate chase --color ff0000 --speed 2 # or: -c yellow -s 2
|
|
108
|
+
fizzctl animate solid --color 0000ff
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Debug mode
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
fizzctl --debug effect rainbow
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Shows the raw frames and handshake bytes sent over HID.
|
|
118
|
+
|
|
119
|
+
## Dev tools
|
|
120
|
+
|
|
121
|
+
The `fizzctl-dev` binary exposes the full reverse-engineering toolkit
|
|
122
|
+
(capture inspect, diff, export, replay) in addition to all user commands.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
fizzctl-dev inspect captures/r3.json
|
|
126
|
+
fizzctl-dev diff captures/r2.json captures/r3.json
|
|
127
|
+
fizzctl-dev export captures/r2.json frames.json
|
|
128
|
+
fizzctl-dev replay frames.json
|
|
129
|
+
fizzctl-dev cfg Cfg.ini
|
|
130
|
+
fizzctl-dev list
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## How the RGB paths work
|
|
134
|
+
|
|
135
|
+
**Flash writes** (`rgb`, `key`, `paint`, `effect`, `keymap`) send a 4-5 frame
|
|
136
|
+
burst through the vendor HID interface (`258a:0049`, interface 1, usage page
|
|
137
|
+
`0xFF00`). The sequence ends with a `5AA5` magic commit that writes to flash.
|
|
138
|
+
Colors persist across reboots. Keys not listed in a `key` or `paint` canvas
|
|
139
|
+
turn off.
|
|
140
|
+
|
|
141
|
+
**Animations** (`animate`) stream 382-byte per-key reports at the requested
|
|
142
|
+
frame rate with no flash commit. They are host-side only and lost on
|
|
143
|
+
reconnect.
|
|
144
|
+
|
|
145
|
+
## Colors
|
|
146
|
+
|
|
147
|
+
Colors can be given as a name or `RRGGBB` hex (with or without `#`). Spaces,
|
|
148
|
+
dashes and underscores in names are ignored, so `light green`, `light-green`
|
|
149
|
+
and `lightgreen` are equivalent:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
red green blue white black off
|
|
153
|
+
yellow cyan magenta orange pink purple lime teal violet indigo coral salmon
|
|
154
|
+
navy brown gold silver gray grey olive maroon
|
|
155
|
+
lightred lightgreen lightblue darkred darkgreen darkblue
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Both `-c yellow` and `--color ffaa00` work anywhere a color flag is accepted.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fizzctl"
|
|
3
|
+
version = "0.2.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
|
+
keywords = [
|
|
7
|
+
"redragon",
|
|
8
|
+
"k617",
|
|
9
|
+
"fizz",
|
|
10
|
+
"rgb",
|
|
11
|
+
"keyboard",
|
|
12
|
+
"backlight",
|
|
13
|
+
"led",
|
|
14
|
+
"hid",
|
|
15
|
+
"linux",
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: End Users/Desktop",
|
|
21
|
+
"Operating System :: POSIX :: Linux",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Topic :: System :: Hardware :: Hardware Drivers",
|
|
25
|
+
"Topic :: System :: Hardware",
|
|
26
|
+
]
|
|
27
|
+
requires-python = ">=3.14"
|
|
28
|
+
dependencies = [
|
|
29
|
+
"dpkt>=1.9.8",
|
|
30
|
+
"hidapi>=0.15.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.license]
|
|
34
|
+
text = "MIT"
|
|
35
|
+
|
|
36
|
+
[[project.authors]]
|
|
37
|
+
name = "Ayoub Dya"
|
|
38
|
+
email = "ayoubdya@gmail.com"
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
fizzctl = "fizzctl:main"
|
|
42
|
+
fizzctl-dev = "fizzctl:main_dev"
|
|
43
|
+
|
|
44
|
+
[build-system]
|
|
45
|
+
requires = ["uv_build>=0.12.13,<0.13.0"]
|
|
46
|
+
build-backend = "uv_build"
|
|
47
|
+
|
|
48
|
+
[tool.uv]
|
|
49
|
+
package = true
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fizzctl"
|
|
3
|
+
version = "0.2.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 = [
|
|
8
|
+
{ name = "Ayoub Dya", email = "ayoubdya@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
keywords = [
|
|
11
|
+
"redragon",
|
|
12
|
+
"k617",
|
|
13
|
+
"fizz",
|
|
14
|
+
"rgb",
|
|
15
|
+
"keyboard",
|
|
16
|
+
"backlight",
|
|
17
|
+
"led",
|
|
18
|
+
"hid",
|
|
19
|
+
"linux",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 4 - Beta",
|
|
23
|
+
"Environment :: Console",
|
|
24
|
+
"Intended Audience :: End Users/Desktop",
|
|
25
|
+
"Operating System :: POSIX :: Linux",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Topic :: System :: Hardware :: Hardware Drivers",
|
|
29
|
+
"Topic :: System :: Hardware",
|
|
30
|
+
]
|
|
31
|
+
requires-python = ">=3.14"
|
|
32
|
+
dependencies = [
|
|
33
|
+
"dpkt>=1.9.8",
|
|
34
|
+
"hidapi>=0.15.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
fizzctl = "fizzctl:main"
|
|
39
|
+
fizzctl-dev = "fizzctl:main_dev"
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["uv_build>=0.12.13,<0.13.0"]
|
|
43
|
+
build-backend = "uv_build"
|
|
44
|
+
|
|
45
|
+
[tool.uv]
|
|
46
|
+
package = true
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""fizzctl — Linux tooling for the Redragon K617 Fizz.
|
|
2
|
+
|
|
3
|
+
User commands live in the :func:`main` entry point (rgb/effect/key/paint/
|
|
4
|
+
animate/keymap/setup-udev); the full reverse-engineering toolkit is exposed
|
|
5
|
+
by :func:`main_dev` via the ``fizzctl-dev`` console script.
|
|
6
|
+
"""
|
|
7
|
+
from .cli import main, main_dev
|
|
8
|
+
|
|
9
|
+
__all__ = ["main", "main_dev"]
|
|
10
|
+
__version__ = "0.2.0"
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Host-side per-key animations streamed at N fps via the Sinodragon protocol.
|
|
2
|
+
|
|
3
|
+
These are NOT firmware effects — they render color maps on the host and push
|
|
4
|
+
one 382-byte per-key report per frame (volatile; stop with Ctrl+C). Only the
|
|
5
|
+
8 firmware-native effects (`fizzctl effect`) survive a disconnect.
|
|
6
|
+
|
|
7
|
+
Animation helpers are pure ("render_frame(t, color, speed) -> dict") so they
|
|
8
|
+
can be unit-tested without hardware.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import math
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
from .effects import PER_KEY_POS, encode_per_key_frame, parse_color
|
|
16
|
+
|
|
17
|
+
ANIMATIONS = ["solid", "blink", "pulse", "chase", "wave", "rainbow", "drop"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def hsv(h: float, s: float, v: float) -> tuple[int, int, int]:
|
|
21
|
+
"""HSV (h 0..360, s/v 0..1) -> (r,g,b) 0..255."""
|
|
22
|
+
c = v * s
|
|
23
|
+
x = c * (1 - abs((h / 60) % 2 - 1))
|
|
24
|
+
m = v - c
|
|
25
|
+
if h < 60:
|
|
26
|
+
r, g, b = c, x, 0
|
|
27
|
+
elif h < 120:
|
|
28
|
+
r, g, b = x, c, 0
|
|
29
|
+
elif h < 180:
|
|
30
|
+
r, g, b = 0, c, x
|
|
31
|
+
elif h < 240:
|
|
32
|
+
r, g, b = 0, x, c
|
|
33
|
+
elif h < 300:
|
|
34
|
+
r, g, b = x, 0, c
|
|
35
|
+
else:
|
|
36
|
+
r, g, b = c, 0, x
|
|
37
|
+
return int((r + m) * 255), int((g + m) * 255), int((b + m) * 255)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def render_frame(t: float, anim: str, color: tuple[int, int, int],
|
|
41
|
+
speed: float = 1.0) -> dict[str, tuple[int, int, int]]:
|
|
42
|
+
"""Produce a color map for animation `anim` at time `t` (seconds).
|
|
43
|
+
|
|
44
|
+
speed scales the cycle period (higher = faster). Positions are the
|
|
45
|
+
verified col*6+row raster from effects.PER_KEY_POS.
|
|
46
|
+
"""
|
|
47
|
+
r, g, b = color
|
|
48
|
+
out: dict[str, tuple[int, int, int]] = {}
|
|
49
|
+
period = max(0.1, 2.0 / speed)
|
|
50
|
+
|
|
51
|
+
if anim == "solid":
|
|
52
|
+
return {k: color for k in PER_KEY_POS}
|
|
53
|
+
|
|
54
|
+
if anim == "blink":
|
|
55
|
+
cycle = (t / period) % 1
|
|
56
|
+
return {k: color if cycle < 0.5 else (0, 0, 0) for k in PER_KEY_POS}
|
|
57
|
+
|
|
58
|
+
if anim == "pulse":
|
|
59
|
+
v = 0.5 + 0.5 * math.sin(2 * math.pi * t / period)
|
|
60
|
+
dim = tuple(int(c * v) for c in color)
|
|
61
|
+
return {k: dim for k in PER_KEY_POS}
|
|
62
|
+
|
|
63
|
+
if anim == "chase":
|
|
64
|
+
order = sorted(PER_KEY_POS, key=lambda k: PER_KEY_POS[k])
|
|
65
|
+
idx = int((t / period) * len(order)) % len(order)
|
|
66
|
+
for i, k in enumerate(order):
|
|
67
|
+
dist = (i - idx) % len(order)
|
|
68
|
+
frac = max(0.0, 1.0 - dist / 5)
|
|
69
|
+
out[k] = tuple(int(c * frac) for c in color) if dist < 5 else (0, 0, 0)
|
|
70
|
+
return out
|
|
71
|
+
|
|
72
|
+
if anim == "wave":
|
|
73
|
+
for k, pos in PER_KEY_POS.items():
|
|
74
|
+
col = pos // 6
|
|
75
|
+
phase = (t / period + col / 14) % 1
|
|
76
|
+
v = 0.5 + 0.5 * math.sin(2 * math.pi * phase)
|
|
77
|
+
out[k] = tuple(int(c * v) for c in color)
|
|
78
|
+
return out
|
|
79
|
+
|
|
80
|
+
if anim == "rainbow":
|
|
81
|
+
order = sorted(PER_KEY_POS, key=lambda k: PER_KEY_POS[k])
|
|
82
|
+
for i, k in enumerate(order):
|
|
83
|
+
hue = (360 * t / period + 360 * i / len(order)) % 360
|
|
84
|
+
out[k] = hsv(hue, 1.0, 1.0)
|
|
85
|
+
return out
|
|
86
|
+
|
|
87
|
+
if anim == "drop":
|
|
88
|
+
for k, pos in PER_KEY_POS.items():
|
|
89
|
+
col = pos // 6
|
|
90
|
+
row = pos % 6
|
|
91
|
+
phase = ((t * speed + col) % 6) - row
|
|
92
|
+
out[k] = color if 0 <= phase < 1 else (0, 0, 0)
|
|
93
|
+
return out
|
|
94
|
+
|
|
95
|
+
raise ValueError(f"unknown animation {anim!r}; choose from {', '.join(ANIMATIONS)}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def run_animation(dev, anim: str, color: tuple[int, int, int],
|
|
99
|
+
fps: int = 30, speed: float = 1.0, duration: float | None = None) -> None:
|
|
100
|
+
"""Stream `anim` until Ctrl+C (or `duration` seconds)."""
|
|
101
|
+
from .hid import send_per_key
|
|
102
|
+
|
|
103
|
+
period = 1.0 / fps
|
|
104
|
+
start = time.monotonic()
|
|
105
|
+
try:
|
|
106
|
+
while True:
|
|
107
|
+
if duration is not None and time.monotonic() - start >= duration:
|
|
108
|
+
break
|
|
109
|
+
frame = encode_per_key_frame(render_frame(time.monotonic() - start, anim, color, speed))
|
|
110
|
+
send_per_key(dev, frame)
|
|
111
|
+
time.sleep(period)
|
|
112
|
+
except KeyboardInterrupt:
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def cmd_animate(args):
|
|
117
|
+
"""fizzctl animate <name> [--color HEX] [--speed N] [--fps N] [--duration S]"""
|
|
118
|
+
from .hid import NoDeviceError, open_device
|
|
119
|
+
|
|
120
|
+
anim = args.name
|
|
121
|
+
if anim is None:
|
|
122
|
+
print("Host-side animations (volatile — lost on disconnect). Run like:")
|
|
123
|
+
print(" fizzctl animate chase --color yellow --speed 2 --fps 30")
|
|
124
|
+
for name in ANIMATIONS:
|
|
125
|
+
print(f" {name}")
|
|
126
|
+
return 0
|
|
127
|
+
if anim not in ANIMATIONS:
|
|
128
|
+
print(f"unknown animation {anim!r}. Available: {', '.join(ANIMATIONS)}")
|
|
129
|
+
return 1
|
|
130
|
+
color = parse_color(args.color or "ff0000")
|
|
131
|
+
if color is None:
|
|
132
|
+
print(f"bad color {args.color!r}")
|
|
133
|
+
return 1
|
|
134
|
+
try:
|
|
135
|
+
dev = open_device(debug=getattr(args, 'debug', False))
|
|
136
|
+
except NoDeviceError:
|
|
137
|
+
return 1
|
|
138
|
+
if dev is None:
|
|
139
|
+
return 1
|
|
140
|
+
try:
|
|
141
|
+
print(f"streaming {anim} at {args.fps}fps (Ctrl+C to stop)...")
|
|
142
|
+
run_animation(dev, anim, color, fps=args.fps, speed=args.speed, duration=args.duration)
|
|
143
|
+
finally:
|
|
144
|
+
dev.close()
|
|
145
|
+
return 0
|