HidOverI2C 0.2.4__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.
- hidoveri2c/__init__.py +386 -0
- hidoveri2c/i2c_msg.py +48 -0
- hidoveri2c/protocols/__init__.py +5 -0
- hidoveri2c/protocols/i2c_msg.py +21 -0
- hidoveri2c-0.2.4.dist-info/METADATA +709 -0
- hidoveri2c-0.2.4.dist-info/RECORD +8 -0
- hidoveri2c-0.2.4.dist-info/WHEEL +4 -0
- hidoveri2c-0.2.4.dist-info/licenses/LICENCE +674 -0
hidoveri2c/__init__.py
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import struct
|
|
2
|
+
import time
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum, Flag
|
|
5
|
+
from typing import Type
|
|
6
|
+
|
|
7
|
+
from .i2c_msg import i2c_msg as _i2c_msg
|
|
8
|
+
from .protocols import I2CMessageClass
|
|
9
|
+
|
|
10
|
+
class HidOverI2c:
|
|
11
|
+
@dataclass
|
|
12
|
+
class HidOverI2cDescriptorHeader:
|
|
13
|
+
wHIDDescLength: int
|
|
14
|
+
bcdVersion: int
|
|
15
|
+
|
|
16
|
+
STRUCT = struct.Struct("<HH")
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def unpack(cls, data: bytes):
|
|
20
|
+
values = cls.STRUCT.unpack(data)
|
|
21
|
+
return cls(*values)
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class HidOverI2cDescriptor:
|
|
25
|
+
wHIDDescLength: int
|
|
26
|
+
bcdVersion: int
|
|
27
|
+
wReportDescLength: int
|
|
28
|
+
wReportDescRegister: int
|
|
29
|
+
wInputRegister: int
|
|
30
|
+
wMaxInputLength: int
|
|
31
|
+
wOutputRegister: int
|
|
32
|
+
wMaxOutputLength: int
|
|
33
|
+
wCommandRegister: int
|
|
34
|
+
wDataRegister: int
|
|
35
|
+
wVendorID: int
|
|
36
|
+
wProductID: int
|
|
37
|
+
wVersionID: int
|
|
38
|
+
RESERVED: int
|
|
39
|
+
|
|
40
|
+
STRUCT = struct.Struct("<HHHHHHHHHHHHHI")
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def unpack(cls, data: bytes):
|
|
44
|
+
values = cls.STRUCT.unpack(data)
|
|
45
|
+
return cls(*values)
|
|
46
|
+
|
|
47
|
+
def pack(self) -> bytes:
|
|
48
|
+
return self.STRUCT.pack(
|
|
49
|
+
self.wHIDDescLength,
|
|
50
|
+
self.bcdVersion,
|
|
51
|
+
self.wReportDescLength,
|
|
52
|
+
self.wReportDescRegister,
|
|
53
|
+
self.wInputRegister,
|
|
54
|
+
self.wMaxInputLength,
|
|
55
|
+
self.wOutputRegister,
|
|
56
|
+
self.wMaxOutputLength,
|
|
57
|
+
self.wCommandRegister,
|
|
58
|
+
self.wDataRegister,
|
|
59
|
+
self.wVendorID,
|
|
60
|
+
self.wProductID,
|
|
61
|
+
self.wVersionID,
|
|
62
|
+
self.RESERVED
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
class RequestOpcode(Enum):
|
|
66
|
+
RESERVED_0 = 0b0000
|
|
67
|
+
RESET = 0b0001
|
|
68
|
+
GET_REPORT = 0b0010
|
|
69
|
+
SET_REPORT = 0b0011
|
|
70
|
+
GET_IDLE = 0b0100
|
|
71
|
+
SET_IDLE = 0b0101
|
|
72
|
+
GET_PROTOCOL = 0b0110
|
|
73
|
+
SET_PROTOCOL = 0b0111
|
|
74
|
+
SET_POWER = 0b1000
|
|
75
|
+
RESERVED_9 = 0b1001
|
|
76
|
+
RESERVED_A = 0b1010
|
|
77
|
+
RESERVED_B = 0b1011
|
|
78
|
+
RESERVED_C = 0b1100
|
|
79
|
+
RESERVED_D = 0b1101
|
|
80
|
+
VENDOR = 0b1110
|
|
81
|
+
RESERVED_F = 0b1111
|
|
82
|
+
|
|
83
|
+
class Flags(Flag):
|
|
84
|
+
ALLOW_INVALID = 0
|
|
85
|
+
|
|
86
|
+
class ReportType(Enum):
|
|
87
|
+
RESERVED = 0b00
|
|
88
|
+
Input = 0b01
|
|
89
|
+
Output = 0b10
|
|
90
|
+
Feature = 0b11
|
|
91
|
+
|
|
92
|
+
class Version:
|
|
93
|
+
def __init__(self, version_id):
|
|
94
|
+
self.major = version_id >> 8
|
|
95
|
+
self.minor = (version_id & 0xFF) >> 4
|
|
96
|
+
self.patch = (version_id & 0x0F)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
Public Methods
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(self, bus, addr, descriptor_reg, msg_type: Type[I2CMessageClass] = _i2c_msg):
|
|
104
|
+
self._bus = bus
|
|
105
|
+
self._msg = msg_type
|
|
106
|
+
self._addr = addr
|
|
107
|
+
self._descriptor_reg = descriptor_reg
|
|
108
|
+
|
|
109
|
+
read_msgs = self._prepare_register_read(self._descriptor_reg, 4)
|
|
110
|
+
self._bus.i2c_rdwr(*read_msgs)
|
|
111
|
+
descriptor_header = self.HidOverI2cDescriptorHeader.unpack(bytes(read_msgs[-1]))
|
|
112
|
+
|
|
113
|
+
read_msgs = self._prepare_register_read(self._descriptor_reg, descriptor_header.wHIDDescLength)
|
|
114
|
+
self._bus.i2c_rdwr(*read_msgs)
|
|
115
|
+
self._descriptor = self.HidOverI2cDescriptor.unpack(bytes(read_msgs[-1]))
|
|
116
|
+
|
|
117
|
+
def read(self, size: int, timeout_ms=0):
|
|
118
|
+
return self._input_read(size, timeout_ms)
|
|
119
|
+
|
|
120
|
+
def write(self, data) -> None:
|
|
121
|
+
self._output_write(data)
|
|
122
|
+
|
|
123
|
+
def get_report(self, report_type, report_id, size) -> bytes:
|
|
124
|
+
assert report_type in (self.ReportType.Input, self.ReportType.Feature)
|
|
125
|
+
if report_type == self.ReportType.Input:
|
|
126
|
+
return self.get_input_report(report_id, size)
|
|
127
|
+
elif report_type == self.ReportType.Feature:
|
|
128
|
+
return self.get_feature_report(report_id, size)
|
|
129
|
+
|
|
130
|
+
def set_report(self, report_type, report_id, data) -> None:
|
|
131
|
+
if report_type == self.ReportType.Output:
|
|
132
|
+
self._set_output_report(report_id, data)
|
|
133
|
+
elif report_type == self.ReportType.Feature:
|
|
134
|
+
self._set_feature_report(report_id, data)
|
|
135
|
+
|
|
136
|
+
def get_input_report(self, report_id, size) -> bytes:
|
|
137
|
+
raw_data = self._get_request(self.RequestOpcode.GET_REPORT, report_type=self.ReportType.Input, report_id=report_id, size=size)[2:]
|
|
138
|
+
length, data = struct.unpack("<H", raw_data[:2])[0], raw_data[2:]
|
|
139
|
+
assert length == (len(data)+2)
|
|
140
|
+
return data
|
|
141
|
+
|
|
142
|
+
def get_feature_report(self, report_id, size) -> bytes:
|
|
143
|
+
raw_data = self._get_request(self.RequestOpcode.GET_REPORT, report_type=self.ReportType.Feature, report_id=report_id, size=size)
|
|
144
|
+
length, data = struct.unpack("<H", raw_data[:2])[0], raw_data[2:]
|
|
145
|
+
assert length == (len(data)+2)
|
|
146
|
+
return data
|
|
147
|
+
|
|
148
|
+
def get_report_descriptor(self, size = 4096):
|
|
149
|
+
i2c_msgs = self._prepare_register_read(self._report_descriptor_register, size)
|
|
150
|
+
self._bus.i2c_rdwr(*i2c_msgs)
|
|
151
|
+
return bytes(i2c_msgs[-1])
|
|
152
|
+
|
|
153
|
+
def get_idle(self, report_id=0):
|
|
154
|
+
raw_data = self._get_request(self.RequestOpcode.GET_IDLE, report_type=self.ReportType.Feature, report_id=report_id, size=2)
|
|
155
|
+
length, idle = struct.unpack("<HH", raw_data)
|
|
156
|
+
assert length == (2 + 2)
|
|
157
|
+
return idle
|
|
158
|
+
|
|
159
|
+
def set_idle(self, duration, report_id=0):
|
|
160
|
+
_bytes = struct.pack("<H", duration)
|
|
161
|
+
self._set_request(self.RequestOpcode.SET_IDLE, data=_bytes, report_id=report_id)
|
|
162
|
+
|
|
163
|
+
def get_protocol(self):
|
|
164
|
+
raw_data = self._get_request(self.RequestOpcode.GET_PROTOCOL, size=2)
|
|
165
|
+
length, protocol = struct.unpack("<HH", raw_data)
|
|
166
|
+
assert length == (2 + 2)
|
|
167
|
+
return protocol
|
|
168
|
+
|
|
169
|
+
def set_protocol(self, protocol: int):
|
|
170
|
+
assert 0 <= protocol <= 1
|
|
171
|
+
_bytes = struct.pack("<H", protocol)
|
|
172
|
+
self._set_request(self.RequestOpcode.SET_PROTOCOL, data=_bytes)
|
|
173
|
+
|
|
174
|
+
def reset(self):
|
|
175
|
+
self._set_request(self.RequestOpcode.RESET)
|
|
176
|
+
|
|
177
|
+
def set_power(self, power) -> None:
|
|
178
|
+
assert 0 <= power <= 1
|
|
179
|
+
self._set_request(self.RequestOpcode.SET_POWER, report_id=power)
|
|
180
|
+
|
|
181
|
+
"""
|
|
182
|
+
Private Methods
|
|
183
|
+
"""
|
|
184
|
+
def _read(self, size: int) -> bytes:
|
|
185
|
+
"""
|
|
186
|
+
Perform an immediate, unsolicited read from HidOverI2c device
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
read = self._msg.read(self._addr, size)
|
|
190
|
+
self._bus.i2c_rdwr(read)
|
|
191
|
+
return bytes(read)
|
|
192
|
+
|
|
193
|
+
def _input_read(self, size, timeout_ms=0):
|
|
194
|
+
"""
|
|
195
|
+
Perform a read from the input register.
|
|
196
|
+
"""
|
|
197
|
+
# TODO: check if we're non blocking and whether we have IRQ signal
|
|
198
|
+
start_time = time.time()
|
|
199
|
+
while True:
|
|
200
|
+
if timeout_ms > 0:
|
|
201
|
+
elapsed = (time.time() - start_time)*1000 # ms
|
|
202
|
+
remaining = timeout_ms - elapsed
|
|
203
|
+
if remaining <= 0:
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
# wMaxInputLength includes the 2 byte length prefix, so we read that many bytes to ensure we get a full report.
|
|
207
|
+
data = self._read(self._descriptor.wMaxInputLength)
|
|
208
|
+
if len(data) <= 2:
|
|
209
|
+
continue # device initiated reset?
|
|
210
|
+
data_len = struct.unpack("<H", data[:2])[0]
|
|
211
|
+
if data_len <= 2:
|
|
212
|
+
continue # null report?
|
|
213
|
+
|
|
214
|
+
report = data[2:data_len]
|
|
215
|
+
return report
|
|
216
|
+
|
|
217
|
+
def _output_write(self, data) -> bytes:
|
|
218
|
+
i2c_msgs = self._prepare_register_write(self._output_register, data)
|
|
219
|
+
self._bus.i2c_rdwr(*i2c_msgs)
|
|
220
|
+
return bytes(i2c_msgs[-1]) # final read operation
|
|
221
|
+
|
|
222
|
+
def _set_output_report(self, report_id, data) -> None:
|
|
223
|
+
self._set_request(self.RequestOpcode.SET_REPORT, self.ReportType.Output, report_id, data)
|
|
224
|
+
|
|
225
|
+
def _set_feature_report(self, report_id, data) -> None:
|
|
226
|
+
self._set_request(self.RequestOpcode.SET_REPORT, self.ReportType.Feature, report_id=report_id, data=data)
|
|
227
|
+
|
|
228
|
+
def _get_request(self, opcode: RequestOpcode, *, report_type = ReportType.RESERVED, report_id = 0, size) -> bytes:
|
|
229
|
+
"""
|
|
230
|
+
Perform a request which will Read from the data-register.
|
|
231
|
+
This function returns the data read INCLUDING the 2-byte length prefix.
|
|
232
|
+
"""
|
|
233
|
+
_command_bytes = self._register_bytes(self._command_register) + self._pack_request(opcode, report_type, report_id)
|
|
234
|
+
_data_bytes = self._register_bytes(self._data_register)
|
|
235
|
+
write = self._msg.write(self._addr, _command_bytes + _data_bytes)
|
|
236
|
+
read = self._msg.read(self._addr, size+2) # +2 for length prefix
|
|
237
|
+
self._bus.i2c_rdwr(write, read)
|
|
238
|
+
return bytes(read)
|
|
239
|
+
|
|
240
|
+
def _set_request(self, opcode: RequestOpcode, report_type = ReportType.RESERVED, report_id = 0, data: bytes|None = None) -> None:
|
|
241
|
+
"""
|
|
242
|
+
Perform a request which will Write to the data-register.
|
|
243
|
+
"""
|
|
244
|
+
_command_bytes = self._register_bytes(self._command_register) + self._pack_request(opcode, report_type, report_id)
|
|
245
|
+
if data is not None:
|
|
246
|
+
_data_bytes = self._register_bytes(self._data_register) + struct.pack("<H", len(data)+2) + data
|
|
247
|
+
write = self._msg.write(self._addr, _command_bytes + _data_bytes)
|
|
248
|
+
else:
|
|
249
|
+
write = self._msg.write(self._addr, _command_bytes)
|
|
250
|
+
self._bus.i2c_rdwr(write)
|
|
251
|
+
|
|
252
|
+
def _read_register(self, register, size) -> bytes:
|
|
253
|
+
i2c_msgs = self._prepare_register_read(register, size)
|
|
254
|
+
self._bus.i2c_rdwr(*i2c_msgs)
|
|
255
|
+
return bytes(i2c_msgs[-1])
|
|
256
|
+
|
|
257
|
+
def _write_register(self, register, data) -> None:
|
|
258
|
+
i2c_msgs = self._prepare_register_write(register, data)
|
|
259
|
+
self._bus.i2c_rdwr(*i2c_msgs)
|
|
260
|
+
|
|
261
|
+
def _write_read_register(self, register, wr_data, rd_data):
|
|
262
|
+
i2c_msgs = self._prepare_register_write(register, wr_data)
|
|
263
|
+
i2c_msgs += self._prepare_register_read(register, rd_data)
|
|
264
|
+
self._bus.i2c_rdwr(*i2c_msgs)
|
|
265
|
+
return bytes(i2c_msgs[-1])
|
|
266
|
+
|
|
267
|
+
def _prepare_register_write(self, register, data) -> tuple[I2CMessageClass]:
|
|
268
|
+
_data = self._register_bytes(register) + struct.pack("<H", len(data)+2) + data
|
|
269
|
+
write = self._msg.write(self._addr, _data)
|
|
270
|
+
return (write,)
|
|
271
|
+
|
|
272
|
+
def _prepare_register_read(self, register, size) -> tuple[I2CMessageClass, I2CMessageClass]:
|
|
273
|
+
"""
|
|
274
|
+
Prepares a Write (regnum)+Read (regdata) I2C messages for a single transaction.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
write = self._msg.write(self._addr, self._register_bytes(register))
|
|
278
|
+
read = self._msg.read(self._addr, size)
|
|
279
|
+
return (write, read)
|
|
280
|
+
|
|
281
|
+
"""
|
|
282
|
+
Static methods
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
@staticmethod
|
|
286
|
+
def _pack_request(opcode: RequestOpcode, report_type: ReportType, report_id) -> bytes:
|
|
287
|
+
"""
|
|
288
|
+
Packs an opcode+report_type+report_id into 2 or 3 bytes, as required for the command register.
|
|
289
|
+
#7.1.1
|
|
290
|
+
"""
|
|
291
|
+
assert 0 <= report_id <= 255
|
|
292
|
+
if report_id < 15:
|
|
293
|
+
_bytes = [report_id | (report_type.value << 4), opcode.value]
|
|
294
|
+
else:
|
|
295
|
+
_bytes = [15 | (report_type.value << 4), opcode.value, report_id]
|
|
296
|
+
|
|
297
|
+
return bytes(_bytes)
|
|
298
|
+
|
|
299
|
+
@staticmethod
|
|
300
|
+
def _register_bytes(register) -> bytes:
|
|
301
|
+
"""
|
|
302
|
+
Returns the specified 16-bit value as bytes(2) as required for an i2c write register.
|
|
303
|
+
"""
|
|
304
|
+
return struct.pack("<H", register)
|
|
305
|
+
|
|
306
|
+
"""
|
|
307
|
+
Properties
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def manufacturer(self):
|
|
312
|
+
return "Microsoft"
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def product(self):
|
|
316
|
+
return "HID I2C Devic"
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
def serial(self):
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
@property
|
|
323
|
+
def vid(self):
|
|
324
|
+
return self._descriptor.wVendorID
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def pid(self):
|
|
328
|
+
return self._descriptor.wProductID
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def version(self):
|
|
332
|
+
return self.Version(self._descriptor.bcdVersion)
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def _output_register(self):
|
|
336
|
+
return self._descriptor.wOutputRegister
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def _input_register(self):
|
|
340
|
+
return self._descriptor.wInputRegister
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def _report_descriptor_register(self):
|
|
344
|
+
return self._descriptor.wReportDescRegister
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
def _command_register(self):
|
|
348
|
+
return self._descriptor.wCommandRegister
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def _data_register(self):
|
|
352
|
+
return self._descriptor.wDataRegister
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def report_descriptor_length(self):
|
|
356
|
+
return self._descriptor.wReportDescLength
|
|
357
|
+
|
|
358
|
+
class HIDAPI_HidOverI2c(HidOverI2c):
|
|
359
|
+
|
|
360
|
+
def set_feature_report(self, report, *args, **kwargs):
|
|
361
|
+
report_id = report[0]
|
|
362
|
+
return super()._set_feature_report(report_id, report, *args, **kwargs)
|
|
363
|
+
|
|
364
|
+
def set_output_report(self, report, *args, **kwargs):
|
|
365
|
+
report_id = report[0]
|
|
366
|
+
return super()._set_output_report(report_id, report, *args, **kwargs)
|
|
367
|
+
|
|
368
|
+
send_feature_report = set_feature_report # provided for compatibility.
|
|
369
|
+
send_output_report = set_output_report # provided for compatibility.
|
|
370
|
+
|
|
371
|
+
def get_manufacturer_string(self) -> str|None:
|
|
372
|
+
return self.manufacturer
|
|
373
|
+
|
|
374
|
+
def get_product_string(self) -> str|None:
|
|
375
|
+
return None
|
|
376
|
+
|
|
377
|
+
def get_serial_number_string(self):
|
|
378
|
+
return None
|
|
379
|
+
|
|
380
|
+
def get_indexed_string(self, index):
|
|
381
|
+
return None
|
|
382
|
+
|
|
383
|
+
__all__ = [
|
|
384
|
+
'HidOverI2c',
|
|
385
|
+
'HIDAPI_HidOverI2c',
|
|
386
|
+
]
|
hidoveri2c/i2c_msg.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
class i2c_msg:
|
|
3
|
+
READ = 0x01
|
|
4
|
+
WRITE = 0x00
|
|
5
|
+
buf: list[int]
|
|
6
|
+
len: int
|
|
7
|
+
addr: int
|
|
8
|
+
flags: int
|
|
9
|
+
|
|
10
|
+
def __init__(self, addr, buf, len, flags):
|
|
11
|
+
self.addr = addr
|
|
12
|
+
self.buf = buf
|
|
13
|
+
self.len = len
|
|
14
|
+
self.flags = flags
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def read(addr, size):
|
|
18
|
+
return i2c_msg(addr, [0]*size, size, i2c_msg.READ)
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def write(addr, data):
|
|
22
|
+
return i2c_msg(addr, data, len(data), i2c_msg.WRITE)
|
|
23
|
+
|
|
24
|
+
def __iter__(self):
|
|
25
|
+
""" Iterator / Generator
|
|
26
|
+
|
|
27
|
+
:return: iterates over :py:attr:`buf`
|
|
28
|
+
:rtype: :py:class:`generator` which returns int values
|
|
29
|
+
"""
|
|
30
|
+
idx = 0
|
|
31
|
+
while idx < self.len:
|
|
32
|
+
yield self.buf[idx]
|
|
33
|
+
idx += 1
|
|
34
|
+
|
|
35
|
+
def __len__(self):
|
|
36
|
+
return self.len
|
|
37
|
+
|
|
38
|
+
def __bytes__(self):
|
|
39
|
+
return bytes(self.buf[:self.len])
|
|
40
|
+
|
|
41
|
+
def __repr__(self):
|
|
42
|
+
return 'i2c_msg(%d,%d,%r)' % (self.addr, self.flags, self.__bytes__())
|
|
43
|
+
|
|
44
|
+
def __str__(self):
|
|
45
|
+
s = self.__bytes__()
|
|
46
|
+
# Throw away non-decodable bytes
|
|
47
|
+
s = s.decode(errors="ignore")
|
|
48
|
+
return s
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Protocol, runtime_checkable, Iterator
|
|
2
|
+
|
|
3
|
+
@runtime_checkable
|
|
4
|
+
class I2CMessageClass(Protocol):
|
|
5
|
+
"""
|
|
6
|
+
Using flexible signatures to match both smbus2 and alternatives
|
|
7
|
+
"""
|
|
8
|
+
addr: int
|
|
9
|
+
flags: int
|
|
10
|
+
len: int
|
|
11
|
+
def __bytes__(self) -> bytes: ...
|
|
12
|
+
|
|
13
|
+
def __iter__(self) -> Iterator[int]: ...
|
|
14
|
+
|
|
15
|
+
def __len__(self) -> int: ...
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def read(address: int, length: int) -> 'I2CMessageClass': ...
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def write(address: int, buf: bytes|str|list[int]) -> 'I2CMessageClass': ...
|