Skip to content

Commit ade29a2

Browse files
committed
feat: Port description, vendor and product id
Make usage of port description, vendor and product id possible. Fixes #23
1 parent c2257be commit ade29a2

1 file changed

Lines changed: 64 additions & 11 deletions

File tree

uhubctl/usb.py

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ class Port:
112112
USB port representation from uhubctl
113113
"""
114114

115-
PORT_PATTERN = re.compile(r" Port (?P<port>\d): \d{4} (?P<status>[a-z ]+)\w?(\[(?:(?P<vid>[a-f0-9]{4}):(?P<pid>[a-f0-9]{4})(?P<description>.*)?)\])?")
115+
PORT_PATTERN = re.compile(
116+
r" Port (?P<port>\d): \d{4} (?P<status>[a-z ]+)\s?(\[(?:(?P<vid>[a-f0-9]{4}):(?P<pid>[a-f0-9]{4})\s?(?P<description>.*)?)\])?"
117+
)
116118

117119
def __init__(self, hub: Hub, port_number: int):
118120
"""
@@ -125,6 +127,7 @@ def __init__(self, hub: Hub, port_number: int):
125127
"""
126128
self.hub = hub
127129
self.port_number = int(port_number)
130+
self.__cache = None
128131

129132
@property
130133
def status(self) -> bool:
@@ -158,25 +161,75 @@ def status(self, status: bool) -> None:
158161

159162
UHubCtl.exec(args)
160163

161-
@property
162-
def name(self) -> str:
164+
def __attribute(self, name: str, cache: bool = True, result_filter: callable = None) -> str:
163165
"""
164-
Attached device name
166+
Get attribute from regexp parsed status string
165167
"""
166168

167-
args = ["-l", self.hub.path, "-p", str(self.port_number)]
168-
for line in UHubCtl.exec(args):
169-
reg = self.PORT_PATTERN.match(line)
169+
if name not in ("port", "status", "vid", "pid", "description"):
170+
raise AttributeError(name)
170171

171-
print(line)
172+
# use cache for port details if possible (fetching is slow)
173+
if cache and self.__cache is not None:
174+
lines = [self.__cache]
175+
else:
176+
args = ["-l", self.hub.path, "-p", str(self.port_number)]
177+
lines = UHubCtl.exec(args, description=True)
178+
179+
for line in lines:
180+
reg = self.PORT_PATTERN.match(line)
172181

173182
if reg:
174-
if reg.group("port") != self.port_number:
183+
if int(reg.group("port")) != self.port_number:
175184
continue
176185

177-
return reg.group("description")
186+
self.__cache = line
187+
188+
if callable(result_filter):
189+
return result_filter(reg.group(name))
190+
else:
191+
return reg.group(name)
192+
193+
return None
194+
195+
def description(self, cached_results=True) -> str:
196+
"""
197+
Attached device description
198+
199+
Arguments:
200+
cached_results (bool): Use cached results instead of querying every time
201+
202+
Return:
203+
Device description of the attached device
204+
"""
205+
206+
return self.__attribute("description", cached_results)
207+
208+
def vendor_id(self, cached_results=True) -> int:
209+
"""
210+
Attached device USB vendor id
211+
212+
Arguments:
213+
cached_results (bool): Use cached results instead of querying every time
214+
215+
Return:
216+
USB vendor id of the attached device
217+
"""
218+
219+
return self.__attribute("vid", cached_results, lambda x: int(x, 16) if x is not None else None)
220+
221+
def product_id(self, cached_results=True) -> int:
222+
"""
223+
Attached device USB product id
224+
225+
Arguments:
226+
cached_results (bool): Use cached results instead of querying every time
227+
228+
Return:
229+
USB product id of the attached device
230+
"""
178231

179-
# raise Exception("could not determine name")
232+
return self.__attribute("pid", cached_results, lambda x: int(x, 16) if x is not None else None)
180233

181234
@staticmethod
182235
def from_path(path: str):

0 commit comments

Comments
 (0)