Solaar/tests/logitech_receiver/hidpp.py

120 lines
5.3 KiB
Python

## Copyright (C) 2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""HID++ data and functions common to several logitech_receiver test files"""
from dataclasses import dataclass
from struct import pack
from typing import Optional
def open_path(path: Optional[str]) -> Optional[int]:
return int(path, 16) if path is not None else None
@dataclass
class Response:
response: Optional[str]
id: int
params: str = ""
handle: int = 0x11
devnumber: int = 0xFF
no_reply: bool = False
def replace_number(responses, number): # change the devnumber for a list of responses
return [Response(r.response, r.id, r.params, r.handle, number, r.no_reply) for r in responses]
def ping(responses, handle, devnumber, long_message=False):
print("PING ", hex(handle), hex(devnumber) if devnumber else devnumber)
for r in responses:
if handle == r.handle and devnumber == r.devnumber and r.id == 0x0010:
print("RESPONSE", hex(r.handle), hex(r.devnumber), r.response)
return r.response
def request(responses, handle, devnumber, id, *params, no_reply=False, return_error=False, long_message=False, protocol=1.0):
params = b"".join(pack("B", p) if isinstance(p, int) else p for p in params)
print("REQUEST ", hex(handle), hex(devnumber), hex(id), params.hex())
for r in responses:
if handle == r.handle and devnumber == r.devnumber and r.id == id and bytes.fromhex(r.params) == params:
print("RESPONSE", hex(r.handle), hex(r.devnumber), hex(r.id), r.params, r.response)
return bytes.fromhex(r.response) if r.response is not None else None
r_empty = [ # a HID++ device with no responses except for ping
Response(1.0, 0x0010), # ping
]
r_keyboard_1 = [ # a HID++ 1.0 keyboard
Response(1.0, 0x0010), # ping
Response("001234", 0x81F1, "01"), # firmware
Response("003412", 0x81F1, "02"), # firmware
Response("002345", 0x81F1, "03"), # firmware
Response("003456", 0x81F1, "04"), # firmware
]
r_keyboard_2 = [ # a HID++ 2.0 keyboard
Response(4.2, 0x0010), # ping
Response("010001", 0x0000, "0001"), # feature set at 0x01
Response("020003", 0x0000, "1000"), # battery status at 0x02
Response("030001", 0x0000, "0003"), # device information at 0x03
Response("040003", 0x0000, "0100"), # unknown 0100 at 0x04
Response("050003", 0x0000, "1B04"), # reprogrammable keys V4 at 0x05
Response("08", 0x0100), # 8 features
Response("00010001", 0x0110, "01"), # feature set at 0x01
Response("10000001", 0x0110, "02"), # battery status at 0x02
Response("00030001", 0x0110, "03"), # device information at 0x03
Response("01000003", 0x0110, "04"), # unknown 0100 at 0x04
Response("1B040003", 0x0110, "05"), # reprogrammable keys V4 at 0x05
Response("0212345678000D1234567890ABAA01", 0x0300), # device information
Response("00110012AB010203CD00", 0x0510, "00"), # reprogrammable keys V4
Response("01110022AB010203CD00", 0x0510, "01"), # reprogrammable keys V4
Response("00010111AB010203CD00", 0x0510, "02"), # reprogrammable keys V4
Response("03110032AB010204CD00", 0x0510, "03"), # reprogrammable keys V4
Response("00030333AB010203CD00", 0x0510, "04"), # reprogrammable keys V4
]
r_mouse_1 = [ # a HID++ 1.0 mouse
Response(1.0, 0x0010), # ping
]
r_mouse_2 = [ # a HID++ 2.0 mouse with few responses except for ping
Response(4.2, 0x0010), # ping
]
r_mouse_3 = [ # a HID++ 2.0 mouse
Response(4.5, 0x0010), # ping
Response("010001", 0x0000, "0001"), # feature set at 0x01
Response("020002", 0x0000, "8060"), # report rate at 0x02
Response("040001", 0x0000, "0003"), # device information at 0x04
Response("050002", 0x0000, "0005"), # device type and name at 0x05
Response("08", 0x0100), # 8 features
Response("00010001", 0x0110, "01"), # feature set at 0x01
Response("80600002", 0x0110, "02"), # report rate at 0x02
Response("00030001", 0x0110, "04"), # device information at 0x04
Response("00050002", 0x0110, "05"), # device type and name at 0x05
Response("09", 0x0210), # report rate - current rate
Response("03123456790008123456780000AA01", 0x0400), # device information
Response("0141424302030100", 0x0410, "00"), # firmware 0
Response("0241", 0x0410, "01"), # firmware 1
Response("05", 0x0410, "02"), # firmware 2
Response("12", 0x0500), # name count - 18 characters
Response("414241424142414241424142414241", 0x0510, "00"), # name - first 15 characters
Response("444544000000000000000000000000", 0x0510, "0F"), # name - last 3 characters
]