fizzctl 0.2.0__py3-none-any.whl

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/__init__.py ADDED
@@ -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"
fizzctl/animations.py ADDED
@@ -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
fizzctl/blobs.py ADDED
@@ -0,0 +1,353 @@
1
+ """Baked firmware bytes, inlined as hex so the package ships no binary files.
2
+
3
+ All constants come from USB captures of the OEM software:
4
+ * CONST_MODE / CONST_CANVAS / CONST_ROUTING / CONST_EXEC — the
5
+ four constant 1032-byte frames of the Restore sequence.
6
+ * FW_FRAMES — the 5-frame static-effect template used for RGB writes.
7
+ """
8
+
9
+ CONST_MODE = bytes.fromhex(
10
+ "0608b80040000000000000000000000000000000000000000000000000ff0000"
11
+ "0000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00"
12
+ "ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff000000"
13
+ "00ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff"
14
+ "00ffffffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000"
15
+ "ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00"
16
+ "ffffffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff"
17
+ "00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ff"
18
+ "ffffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00"
19
+ "ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ffff"
20
+ "ffffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff"
21
+ "00ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ffffff"
22
+ "ffffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00"
23
+ "ffff00ff00ff00ffffffffffff00000000ff00ff00ffff00ff00ff00ffffffff"
24
+ "ffff00000000ff00ff00ffff00ff00ff00ffffffffffff00000000ff00ff00ff"
25
+ "ff00ff00ff00ffffffffff000000000000000000000000000000000000000000"
26
+ "0000000000000000000000000000000000000000000000000000000000000000"
27
+ "0000000000000000000000000000000000000000000000000000000000000000"
28
+ "0000000000000000000000000000000000000000000000000000000000000000"
29
+ "0000000000000000000000000000000000000000000000000000000000000000"
30
+ "0000000000000000000000000000000000000000000000000000000000000000"
31
+ "0000000000000000000000000000000000000000000000000000000000000000"
32
+ "0000000000000000000000000000000000000000000000000000000000000000"
33
+ "0000000000000000000000000000000000000000000000000000000000000000"
34
+ "0000000000000000000000000000000000000000000000000000000000000000"
35
+ "0000000000000000000000000000000000000000000000000000000000000000"
36
+ "0000000000000000000000000000000000000000000000000000000000000000"
37
+ "0000000000000000000000000000000000000000000000000000000000000000"
38
+ "0000000000000000000000000000000000000000000000000000000000000000"
39
+ "0000000000000000000000000000000000000000000000000000000000000000"
40
+ "0000000000000000000000000000000000000000000000000000000000000000"
41
+ "0000000000000000000000000000000000000000000000000000000000000000"
42
+ "0000000000000000"
43
+ )
44
+
45
+ CONST_CANVAS = bytes.fromhex(
46
+ "0609bc0040000000000000000000000000000000000000000000000000000000"
47
+ "0000000000000000000000000000000000000000000000000000000000000000"
48
+ "0000000000000000000000000000000000000000000000000000000000000000"
49
+ "0000000000000000000000000000000000000000000000000000000000000000"
50
+ "0000000000000000000000000000000000000000000000000000000000000000"
51
+ "0000000000000000000000000000000000000000000000000000000000000000"
52
+ "0000000000000000000000000000000000000000000000000000000000000000"
53
+ "0000000000000000000000000000000000000000000000000000000000000000"
54
+ "0000000000000000000000000000000000000000000000000000000000000000"
55
+ "00000000000000000000000000000000ff000000000000000000000000000000"
56
+ "00000000ffffff00000000000000000000000000000000000000000000000000"
57
+ "0000000000000000000000000000000000000000000000000000000000000000"
58
+ "0000000000000000000000000000000000000000000000000000000000000000"
59
+ "0000000000000000000000000000000000000000000000000000000000000000"
60
+ "0000000000000000000000000000000000000000000000000000000000000000"
61
+ "0000000000000000000000000000000000000000000000000000000000000000"
62
+ "0000000000000000000000000000000000000000000000000000000000000000"
63
+ "0000000000000000000000000000000000000000000000000000000000000000"
64
+ "0000000000000000000000000000000000000000000000000000000000000000"
65
+ "0000000000000000000000000000000000000000000000000000000000000000"
66
+ "0000000000000000000000000000000000000000ffffffffff00000000000000"
67
+ "0000000000000000ffffffffff0000000000000000000000000000000000ffff"
68
+ "ff00ff000000000000000000000000000000ff0000000000ff00000000000000"
69
+ "00000000000000ff00ff0000ff00000000000000000000000000000000000000"
70
+ "0000000000000000000000000000000000000000000000000000000000000000"
71
+ "0000000000000000000000000000000000000000000000000000000000000000"
72
+ "0000000000000000000000000000000000000000000000000000000000000000"
73
+ "0000000000000000000000000000000000000000000000000000000000000000"
74
+ "0000000000000000000000000000000000000000000000000000000000000000"
75
+ "0000000000000000000000000000000000000000000000000000000000000000"
76
+ "0000000000000000000000000000000000000000000000000000000000000000"
77
+ "0000000000000000000000000000000000000000000000000000000000000000"
78
+ "0000000000000000"
79
+ )
80
+
81
+ CONST_ROUTING = bytes.fromhex(
82
+ "0609c00040000000000000000000ffffffffffffff0000000000000000000000"
83
+ "000000ffffffffff00000000000000000000000000000000ffffffffff000000"
84
+ "000000000000000000000000ff000000ffff0000000000000000000000000000"
85
+ "00ff000000000000000000000000000000000000000000000000000000000000"
86
+ "0000000000000000000000000000000000000000000000000000000000000000"
87
+ "0000000000000000000000000000000000000000000000000000000000000000"
88
+ "0000000000000000000000000000000000000000000000000000000000000000"
89
+ "0000000000000000000000000000000000000000000000000000000000000000"
90
+ "0000000000000000000000000000000000000000000000000000000000000000"
91
+ "0000000000000000000000000000000000000000000000000000000000000000"
92
+ "0000000000000000000000000000000000000000000000000000000000000000"
93
+ "0000000000000000000000000000000000000000000000000000000000000000"
94
+ "0000000000000000ffffffffffffff0000000000000000000000000000ffffff"
95
+ "ff0000000000000000000000000000000000ffffffff00000000000000000000"
96
+ "0000000000000000000000000000000000000000000000000000000000000000"
97
+ "0000000000000000000000000000000000000000000000000000000000000000"
98
+ "0000000000000000000000000000000000000000000000000000000000000000"
99
+ "0000000000000000000000000000000000000000000000000000000000000000"
100
+ "0000000000000000000000000000000000000000000000000000000000000000"
101
+ "0000000000000000000000000000000000000000000000000000000000000000"
102
+ "0000000000000000000000000000000000000000000000000000000000000000"
103
+ "0000000000000000000000000000000000000000000000000000000000000000"
104
+ "0000000000000000000000000000000000000000000000000000000000000000"
105
+ "0000000000000000000000000000000000000000000000000000000000000000"
106
+ "000000000000000000000000000000000000000000000000ff00ff0000000000"
107
+ "000000000000000000000000ffffff0000000000000000000000000000000000"
108
+ "ff0000000000000000000000000000000000000000ff00ff0000000000000000"
109
+ "0000000000000000000000000000000000000000000000000000000000000000"
110
+ "0000000000000000000000000000000000000000000000000000000000000000"
111
+ "0000000000000000000000000000000000000000000000000000000000000000"
112
+ "0000000000000000000000000000000000000000000000000000000000000000"
113
+ "0000000000000000000000000000000000000000000000000000000000000000"
114
+ "0000000000000000"
115
+ )
116
+
117
+ CONST_EXEC = bytes.fromhex(
118
+ "0603b600000000000000000000005aa503030300000b20010000000055550100"
119
+ "00000000ffff0733073307330733073307330733073307330733073307330733"
120
+ "07330733073307330733073307335aa500100744074407440744074407440744"
121
+ "0404040404040404040400000000000000000000000000000000000000000000"
122
+ "0000000000000000005aa5030303000000000000000000000000000000000000"
123
+ "0000000000000000000000000000000000000000000000000000000000000000"
124
+ "0000000000000000000000000000000000000000000000000000000000000000"
125
+ "0000000000000000000000000000000000000000000000000000000000000000"
126
+ "0000000000000000000000000000000000000000000000000000000000000000"
127
+ "0000000000000000000000000000000000000000000000000000000000000000"
128
+ "0000000000000000000000000000000000000000000000000000000000000000"
129
+ "0000000000000000000000000000000000000000000000000000000000000000"
130
+ "0000000000000000000000000000000000000000000000000000000000000000"
131
+ "0000000000000000000000000000000000000000000000000000000000000000"
132
+ "0000000000000000000000000000000000000000000000000000000000000000"
133
+ "0000000000000000000000000000000000000000000000000000000000000000"
134
+ "0000000000000000000000000000000000000000000000000000000000000000"
135
+ "0000000000000000000000000000000000000000000000000000000000000000"
136
+ "0000000000000000000000000000000000000000000000000000000000000000"
137
+ "0000000000000000000000000000000000000000000000000000000000000000"
138
+ "0000000000000000000000000000000000000000000000000000000000000000"
139
+ "0000000000000000000000000000000000000000000000000000000000000000"
140
+ "0000000000000000000000000000000000000000000000000000000000000000"
141
+ "0000000000000000000000000000000000000000000000000000000000000000"
142
+ "0000000000000000000000000000000000000000000000000000000000000000"
143
+ "0000000000000000000000000000000000000000000000000000000000000000"
144
+ "0000000000000000000000000000000000000000000000000000000000000000"
145
+ "0000000000000000000000000000000000000000000000000000000000000000"
146
+ "0000000000000000000000000000000000000000000000000000000000000000"
147
+ "0000000000000000000000000000000000000000000000000000000000000000"
148
+ "0000000000000000000000000000000000000000000000000000000000000000"
149
+ "0000000000000000000000000000000000000000000000000000000000000000"
150
+ "0000000000000000"
151
+ )
152
+
153
+ FW_FRAMES = [
154
+ # frame 0: 6B
155
+ bytes.fromhex(
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(
196
+ "0609bc0040000000000000000000000000000000000000000000000000000000"
197
+ "00000000000000000000000000000000000000ff00ff00ff0000000000000000"
198
+ "0000000000000000ffffff00ff0000000000000000000000000000000000ff00"
199
+ "ff00ff0000000000000000000000000000000000000000000000000000000000"
200
+ "0000000000000000000000000000000000000000000000000000000000000000"
201
+ "00000000ff00ff00000000000000000000ff00ff00ff00000000000000000000"
202
+ "000000000000ffffff00ff0000ff0000ff0000000000000000000000ff00ff00"
203
+ "ff0000ffffff0000000000000000000000000000000000000000000000000000"
204
+ "0000000000000000000000000000000000000000000000000000000000000000"
205
+ "0000ff00ff000000000000000000000000000000000000000000000000000000"
206
+ "0000000000000000000000ff0000ff0000000000000000000000000000000000"
207
+ "00ffffff00000000000000000000000000000000000000000000000000000000"
208
+ "0000000000000000000000000000000000000000000000000000000000000000"
209
+ "0000000000000000000000000000000000000000000000000000000000000000"
210
+ "0000000000000000000000000000000000000000000000000000000000000000"
211
+ "0000000000000000000000000000000000000000000000000000000000000000"
212
+ "0000000000000000000000000000000000000000000000000000000000000000"
213
+ "0000000000000000000000000000000000000000000000000000000000000000"
214
+ "0000000000000000000000000000000000000000000000000000000000000000"
215
+ "0000000000000000000000000000000000000000000000000000000000000000"
216
+ "0000000000000000000000000000000000000000ffffffffff00000000000000"
217
+ "0000000000000000ffffffffff0000000000000000000000000000000000ffff"
218
+ "ff00ff000000000000000000000000000000ff0000000000ff00000000000000"
219
+ "00000000000000ff00ff0000ff00000000000000000000000000000000000000"
220
+ "0000000000000000000000000000000000000000000000000000000000000000"
221
+ "0000000000000000000000000000000000000000000000000000000000000000"
222
+ "0000000000000000000000000000000000000000000000000000000000000000"
223
+ "0000000000000000000000000000000000000000000000000000000000000000"
224
+ "0000000000000000000000000000000000000000000000000000000000000000"
225
+ "0000000000000000000000000000000000000000000000000000000000000000"
226
+ "0000000000000000000000000000000000000000000000000000000000000000"
227
+ "0000000000000000000000000000000000000000000000000000000000000000"
228
+ "0000000000000000"
229
+ ),
230
+ # frame 3: 1032B
231
+ bytes.fromhex(
232
+ "0609c00040000000000000000000ffffffffffffff0000000000000000000000"
233
+ "000000ffffffffff00000000000000000000000000000000ffffffffff000000"
234
+ "000000000000000000000000ff000000ffff0000000000000000000000000000"
235
+ "00ff000000000000000000000000000000000000000000000000000000000000"
236
+ "0000000000000000000000000000000000000000000000000000000000000000"
237
+ "0000000000000000000000000000000000000000000000000000000000000000"
238
+ "0000000000000000000000000000000000000000000000000000000000000000"
239
+ "0000000000000000000000000000000000000000000000000000000000000000"
240
+ "0000000000000000000000000000000000000000000000000000000000000000"
241
+ "0000000000000000000000000000000000000000000000000000000000000000"
242
+ "0000000000000000000000000000000000000000000000000000000000000000"
243
+ "0000000000000000000000000000000000000000000000000000000000000000"
244
+ "0000000000000000ffffffffffffff0000000000000000000000000000ffffff"
245
+ "ff0000000000000000000000000000000000ffffffff00000000000000000000"
246
+ "0000000000000000000000000000000000000000000000000000000000000000"
247
+ "0000000000000000000000000000000000000000000000000000000000000000"
248
+ "0000000000000000000000000000000000000000000000000000000000000000"
249
+ "0000000000000000000000000000000000000000000000000000000000000000"
250
+ "0000000000000000000000000000000000000000000000000000000000000000"
251
+ "0000000000000000000000000000000000000000000000000000000000000000"
252
+ "0000000000000000000000000000000000000000000000000000000000000000"
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"
289
+ "0000000000000000000000000000000000000000000000000000000000000000"
290
+ "0000000000000000000000000000000000000000000000000000000000000000"
291
+ "0000000000000000000000000000000000000000000000000000000000000000"
292
+ "0000000000000000000000000000000000000000000000000000000000000000"
293
+ "0000000000000000000000000000000000000000000000000000000000000000"
294
+ "0000000000000000000000000000000000000000000000000000000000000000"
295
+ "0000000000000000000000000000000000000000000000000000000000000000"
296
+ "0000000000000000000000000000000000000000000000000000000000000000"
297
+ "0000000000000000000000000000000000000000000000000000000000000000"
298
+ "0000000000000000000000000000000000000000000000000000000000000000"
299
+ "0000000000000000000000000000000000000000000000000000000000000000"
300
+ "0000000000000000"
301
+ ),
302
+ ]
303
+
304
+
305
+
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
+ RGB_SEC = bytes.fromhex(
313
+ "ffffffffff000000000000000000000000000000ffffffffff00000000000000"
314
+ "00000000000000000000ffffff00ff000000000000000000000000000000ff00"
315
+ "0000000000ff0000000000000000000000000000ff00ff0000ff000000000000"
316
+ "00000000000000000000"
317
+ )
318
+
319
+ RGB_EXEC = bytes.fromhex(
320
+ "0603b600000000000000000000005aa503030300011520010000000055550100"
321
+ "00000000ffff0032073307330733074407330733073307330733073307220733"
322
+ "07330733073307330733073307335aa500100744074407440744074407440744"
323
+ "0404040404040404040400000000000000000000000000000000000000000000"
324
+ "0000000000000000005aa5030303000000000000000000000000000000000000"
325
+ "0000000000000000000000000000000000000000000000000000000000000000"
326
+ "0000000000000000000000000000000000000000000000000000000000000000"
327
+ "0000000000000000000000000000000000000000000000000000000000000000"
328
+ "0000000000000000000000000000000000000000000000000000000000000000"
329
+ "0000000000000000000000000000000000000000000000000000000000000000"
330
+ "0000000000000000000000000000000000000000000000000000000000000000"
331
+ "0000000000000000000000000000000000000000000000000000000000000000"
332
+ "0000000000000000000000000000000000000000000000000000000000000000"
333
+ "0000000000000000000000000000000000000000000000000000000000000000"
334
+ "0000000000000000000000000000000000000000000000000000000000000000"
335
+ "0000000000000000000000000000000000000000000000000000000000000000"
336
+ "0000000000000000000000000000000000000000000000000000000000000000"
337
+ "0000000000000000000000000000000000000000000000000000000000000000"
338
+ "0000000000000000000000000000000000000000000000000000000000000000"
339
+ "0000000000000000000000000000000000000000000000000000000000000000"
340
+ "0000000000000000000000000000000000000000000000000000000000000000"
341
+ "0000000000000000000000000000000000000000000000000000000000000000"
342
+ "0000000000000000000000000000000000000000000000000000000000000000"
343
+ "0000000000000000000000000000000000000000000000000000000000000000"
344
+ "0000000000000000000000000000000000000000000000000000000000000000"
345
+ "0000000000000000000000000000000000000000000000000000000000000000"
346
+ "0000000000000000000000000000000000000000000000000000000000000000"
347
+ "0000000000000000000000000000000000000000000000000000000000000000"
348
+ "0000000000000000000000000000000000000000000000000000000000000000"
349
+ "0000000000000000000000000000000000000000000000000000000000000000"
350
+ "0000000000000000000000000000000000000000000000000000000000000000"
351
+ "0000000000000000000000000000000000000000000000000000000000000000"
352
+ "0000000000000000"
353
+ )