firmware+controller: Initial working HID state control
This commit is contained in:
		@@ -1,17 +1,22 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
import hid
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
USB_VID = 0x6969
 | 
			
		||||
USB_PID = 0x0004
 | 
			
		||||
import valconomy
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
  hid_handle = hid.device()
 | 
			
		||||
  hid_handle.open(vendor_id=USB_VID, product_id=USB_PID)
 | 
			
		||||
  logging.basicConfig(
 | 
			
		||||
    format='%(asctime)s %(name)s %(levelname)s %(message)s', level=logging.INFO)
 | 
			
		||||
 | 
			
		||||
  h = valconomy.HIDValconomyHandler()
 | 
			
		||||
  try:
 | 
			
		||||
    hid_handle.write(b'\x00test')
 | 
			
		||||
    h.menu(None, False)
 | 
			
		||||
    h.none()
 | 
			
		||||
    h.menu(None, True)
 | 
			
		||||
    h.idle(None)
 | 
			
		||||
 | 
			
		||||
    h.service()
 | 
			
		||||
  finally:
 | 
			
		||||
    hid_handle.close()
 | 
			
		||||
    h.close()
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
  main()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,13 @@
 | 
			
		||||
import base64
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from enum import Enum
 | 
			
		||||
import errno
 | 
			
		||||
import json
 | 
			
		||||
import logging
 | 
			
		||||
from pprint import pprint
 | 
			
		||||
import os
 | 
			
		||||
import queue
 | 
			
		||||
import struct
 | 
			
		||||
import time
 | 
			
		||||
from typing import Callable
 | 
			
		||||
 | 
			
		||||
@@ -49,12 +52,12 @@ class RiotPlayerInfo:
 | 
			
		||||
    return f'{self.name}#{self.tag}'
 | 
			
		||||
 | 
			
		||||
class ValorantLocalClient:
 | 
			
		||||
  lockfile_path = os.path.join(os.environ['LOCALAPPDATA'], r'Riot Games\Riot Client\Config\lockfile')
 | 
			
		||||
  poll_interval = 0.5
 | 
			
		||||
 | 
			
		||||
  def __init__(self, callback: Callable[[RiotPlayerInfo, bool], None]):
 | 
			
		||||
    self.callback = callback
 | 
			
		||||
 | 
			
		||||
    self.lockfile_path = os.path.join(os.environ['LOCALAPPDATA'], r'Riot Games\Riot Client\Config\lockfile')
 | 
			
		||||
    self.port, self.password = None, None
 | 
			
		||||
    self.running = True
 | 
			
		||||
    self.players = {}
 | 
			
		||||
@@ -228,7 +231,78 @@ class ValconomyHandler:
 | 
			
		||||
      log.info('Hard luck...')
 | 
			
		||||
 | 
			
		||||
class HIDValconomyHandler(ValconomyHandler):
 | 
			
		||||
  pass
 | 
			
		||||
  vid = 0x6969
 | 
			
		||||
  pid = 0x0004
 | 
			
		||||
 | 
			
		||||
  def __init__(self):
 | 
			
		||||
    self.dev = None
 | 
			
		||||
    self.running = True
 | 
			
		||||
    self.queue = queue.Queue(128)
 | 
			
		||||
 | 
			
		||||
  def close(self):
 | 
			
		||||
    if self.dev is not None:
 | 
			
		||||
      self.dev.close()
 | 
			
		||||
 | 
			
		||||
  def _dev_ready(self):
 | 
			
		||||
    try:
 | 
			
		||||
      if self.dev is None:
 | 
			
		||||
        dev = hid.device()
 | 
			
		||||
        dev.open(vendor_id=self.vid, product_id=self.pid)
 | 
			
		||||
        self.dev = dev
 | 
			
		||||
        log.info(f'USB device opened')
 | 
			
		||||
 | 
			
		||||
      # 2 bytes: report ID and returned value
 | 
			
		||||
      # We get back the same report ID and value
 | 
			
		||||
      data = self.dev.get_input_report(0, 2)
 | 
			
		||||
      assert len(data) == 2
 | 
			
		||||
      return data[1] == 1
 | 
			
		||||
    except OSError:
 | 
			
		||||
      if self.dev is not None:
 | 
			
		||||
        log.warn(f'USB device lost')
 | 
			
		||||
        self.dev = None
 | 
			
		||||
      return False
 | 
			
		||||
 | 
			
		||||
  def _do(self, cmd, *vals, fmt=None):
 | 
			
		||||
    if fmt is None:
 | 
			
		||||
      fmt = ''
 | 
			
		||||
 | 
			
		||||
    fmt = '<BB' + fmt
 | 
			
		||||
    # Prepend report ID 0
 | 
			
		||||
    data = struct.pack(fmt, *(0, cmd) + vals)
 | 
			
		||||
    self.dev.write(data)
 | 
			
		||||
 | 
			
		||||
  def _enq(self, cmd, *vals, fmt=None):
 | 
			
		||||
    self.queue.put((cmd, vals, fmt))
 | 
			
		||||
 | 
			
		||||
  def service(self):
 | 
			
		||||
    while not self.queue.empty():
 | 
			
		||||
      cmd, vals, fmt = self.queue.get()
 | 
			
		||||
 | 
			
		||||
      while not self._dev_ready():
 | 
			
		||||
        if not self.running:
 | 
			
		||||
          break
 | 
			
		||||
        time.sleep(0.1)
 | 
			
		||||
 | 
			
		||||
      try:
 | 
			
		||||
        self._do(cmd, *vals, fmt=fmt)
 | 
			
		||||
      except OSError as ex:
 | 
			
		||||
        log.warn(f'USB device lost, state dequeuing stalled')
 | 
			
		||||
 | 
			
		||||
  def run(self):
 | 
			
		||||
    while self.running:
 | 
			
		||||
      while self.queue.empty():
 | 
			
		||||
        time.sleep(0.5)
 | 
			
		||||
 | 
			
		||||
      self.service()
 | 
			
		||||
 | 
			
		||||
  def none(self):
 | 
			
		||||
    self._enq(0)
 | 
			
		||||
 | 
			
		||||
  def menu(self, info: RiotPlayerInfo, was_idle: bool=False):
 | 
			
		||||
    self._enq(1, 1 if was_idle else 0, fmt='B')
 | 
			
		||||
 | 
			
		||||
  def idle(self, info: RiotPlayerInfo):
 | 
			
		||||
    self._enq(2)
 | 
			
		||||
 | 
			
		||||
class GameState(Enum):
 | 
			
		||||
  NONE = 0
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user