@@ -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