Page Menu
Home
desp's stash
Search
Configure Global Search
Log In
Files
F369005
device.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Subscribers
None
device.py
View Options
import
hid
import
winreg
from
.datatypes
import
*
class
FusionDevice
():
VID
=
0x1044
PID
=
[
0x7a39
,
0x7a3a
]
#unsure what these devices are for yet
DEVICES
=
[
RawInputDevice
(
1
,
6
,
768
),
RawInputDevice
(
1
,
2
,
256
),
RawInputDevice
(
65281
,
8713
,
256
),
RawInputDevice
(
65282
,
1
,
256
),
RawInputDevice
(
65280
,
65280
,
256
)]
def
__init__
(
self
,
path
:
bytes
)
->
None
:
self
.
h
=
hid
.
device
()
self
.
h
.
open_path
(
path
)
self
.
path
=
path
def
compute_simple_checksum
(
self
,
buf
):
acc
=
0
for
i
in
range
(
8
):
acc
+=
buf
[
i
]
acc
%=
0xFF
buf
[
8
]
=
0xFF
-
acc
def
send_simple_command_and_recv
(
self
,
opcode
:
int
,
report_num
=
0
):
buf
=
bytearray
(
9
)
buf
[
1
]
=
opcode
self
.
compute_simple_checksum
(
buf
)
sent
=
self
.
h
.
send_feature_report
(
buf
)
if
sent
==
-
1
:
raise
IOError
(
self
.
h
.
error
())
buf
=
self
.
h
.
get_feature_report
(
0
,
9
)
return
buf
def
get_firmware_version
(
self
)
->
bytes
:
return
self
.
send_simple_command_and_recv
(
0x80
)
def
get_version_string
(
self
)
->
str
:
buf
=
self
.
get_firmware_version
()
minor
=
list
(
str
(
buf
[
3
]))
return
f
'{buf[2]}.{"0" if len(minor) == 1 else minor.pop(0)}.{minor.pop(0)}'
def
get_light_effect
(
self
)
->
FusionLightData
:
buf
=
self
.
send_simple_command_and_recv
(
0x88
)
return
FusionLightData
(
FusionLightEffect
(
buf
[
3
]),
buf
[
4
],
buf
[
5
],
FusionLightColor
(
buf
[
6
]),
buf
[
7
])
# this is needed to set the current working profile to custom too (set once only is enough)
def
set_simple_light_effect
(
self
,
effect
:
FusionLightData
)
->
None
:
buf
=
bytearray
(
9
)
buf
[
1
]
=
0x8
buf
[
3
]
=
int
(
effect
.
fusion_effect
)
buf
[
4
]
=
effect
.
fusion_speed
buf
[
5
]
=
effect
.
fusion_brightness
buf
[
6
]
=
int
(
effect
.
fusion_color
)
buf
[
7
]
=
effect
.
fusion_direction
self
.
compute_simple_checksum
(
buf
)
sent
=
self
.
h
.
send_feature_report
(
buf
)
if
sent
==
-
1
:
raise
IOError
(
self
.
h
.
error
())
#aka SetPictureMatrix2Device
#TODO seems to not be persistent, but also nice since we can just reset by hibernating + devmgmt disable/enable when it breaks
def
set_custom_light_effect
(
self
,
profile
:
int
,
pixels
:
PictureMatrix
):
if
profile
not
in
range
(
5
):
raise
TypeError
(
'Invalid profile number!'
)
buf
=
bytearray
(
9
)
buf
[
1
]
=
0x12
buf
[
3
]
=
profile
buf
[
4
]
=
8
#why?
self
.
compute_simple_checksum
(
buf
)
sent
=
self
.
h
.
send_feature_report
(
buf
)
if
sent
==
-
1
:
raise
IOError
(
self
.
h
.
error
())
data
=
pixels
.
to_bytes
()
for
split
in
[
data
[
i
:
i
+
64
]
for
i
in
range
(
0
,
len
(
data
),
64
)]:
self
.
h
.
write
(
b
'
\0
'
+
split
)
#writing all at once would just freeze the keyboard
#self.h.write(b'\0' + b'\0'.join([data[i:i+64] for i in range(0, len(data), 64)]))
#aka LoadPictureMatrixValue
#use PictureMatrix.from_bytes to convert it back into a usable PictureMatrix
def
get_custom_light_effect
(
self
,
profile
:
int
)
->
bytes
:
if
profile
not
in
range
(
5
):
raise
TypeError
(
'Invalid profile number!'
)
buf
=
bytearray
(
9
)
buf
[
1
]
=
0x92
buf
[
3
]
=
profile
self
.
compute_simple_checksum
(
buf
)
sent
=
self
.
h
.
send_feature_report
(
buf
)
if
sent
==
-
1
:
raise
IOError
(
self
.
h
.
error
())
buf
=
self
.
h
.
get_feature_report
(
0
,
9
)
print
(
buf
)
data
=
b
''
for
i
in
range
(
8
):
print
(
i
)
data
+=
bytes
(
self
.
h
.
read
(
65
))
return
data
#TODO seems like a no-op? but even in FusionKeyboard.dll it's immediately succeeded by a call to set static white anyways hm
#maybe it's for clearing custom profiles in firmware memory (or the unimplemented macro stuff)
def
reset
(
self
)
->
None
:
buf
=
bytearray
(
9
)
buf
[
1
]
=
0x13
buf
[
2
]
=
0xFF
self
.
compute_simple_checksum
(
buf
)
sent
=
self
.
h
.
send_feature_report
(
buf
)
if
sent
==
-
1
:
raise
IOError
(
self
.
h
.
error
())
#TODO untested classes below
class
FusionDeviceIone
(
FusionDevice
):
PID
=
[
0x7a3c
,
0x7a3d
,
0x7a3e
]
#normal, UK, JP
def
get_version_string
(
self
)
->
str
:
buf
=
bytearray
(
264
)
buf
[
0
]
=
0x7
buf
[
1
]
=
0x17
report_num
=
self
.
h
.
send_feature_report
(
buf
)
print
(
report_num
)
if
report_num
==
-
1
:
raise
IOError
(
self
.
h
.
error
())
with
winreg
.
OpenKey
(
winreg
.
HKEY_LOCAL_MACHINE
,
"Software
\\
GigabyteFusion"
)
as
key
:
return
winreg
.
QueryValue
(
key
,
'Version'
)
def
get_firmware_version
(
self
)
->
bytes
:
#theres actually no raw response directly so just fake it with the version string
return
self
.
get_version_string
()
.
encode
()
#TODO changeIoneAnimationListUI, changeIoneAnimationTrigger, changeIoneColorGroupUI
#TODO seems like the way to do complex profile is changeLightEffect_Details for ione
class
FusionDeviceIte
(
FusionDevice
):
PID
=
[
0x7a38
,
0x7a3b
,
0x7a3f
]
class
FusionDeviceX9
(
FusionDevice
):
VID
=
0x4D9
PID
=
[
0x8008
]
def
get_version_string
(
self
)
->
str
:
buf
=
self
.
get_firmware_version
()
return
f
'{buf[2]}.{"0" if len(str(buf[3])) == 1 else buf[3] // 16}.{buf[3] % 16}'
def
get_device
()
->
FusionDevice
:
for
dev
in
hid
.
enumerate
():
for
type
in
[
FusionDevice
,
FusionDeviceX9
,
FusionDeviceIone
,
FusionDeviceIte
]:
#print(type, type.VID, type.PID, dev['vendor_id'], dev['product_id'])
if
dev
[
'vendor_id'
]
==
type
.
VID
and
dev
[
'product_id'
]
in
type
.
PID
:
try
:
obj
=
type
(
dev
[
'path'
])
#there should only be one that we can get the firmware version successfully from
obj
.
get_firmware_version
()
return
obj
except
IOError
:
continue
File Metadata
Details
Attached
Mime Type
text/x-python
Expires
Sun, Jul 6, 4:10 AM (21 h, 52 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
91/ec/bb256e81d2f715cef885d834cf0a
Attached To
rAERO GIGABYTE Aero Fusion controller lib
Event Timeline
Log In to Comment