Page MenuHomedesp's stash

device.py
No OneTemporary

device.py

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

Mime Type
text/x-python
Expires
Sun, Jul 6, 4:10 AM (13 h, 42 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
91/ec/bb256e81d2f715cef885d834cf0a

Event Timeline