11import re
22import subprocess
3+ from typing import List , Optional
34
45UHUBCTL_BINARY = "uhubctl"
56
67
7- def __uhubctl (args : list = []) -> list :
8+ def _uhubctl (args : list = []) -> list :
89 cmd = UHUBCTL_BINARY .split (" " ) + args
910 result = subprocess .run (cmd , capture_output = True )
1011 stdout = result .stdout .decode ()
@@ -18,62 +19,123 @@ def __uhubctl(args: list = []) -> list:
1819
1920
2021def discover_hubs ():
21- hubs = []
22+ """
23+ Return list of all by uhubctl supported USB hubs with their ports
2224
23- pattern_hub = re . compile ( "Current status for hub ([\.\d-]+)" )
24- pattern_port = re . compile ( " Port (\d+): \d{4} (power|off)" )
25+ Returns:
26+ List of hubs
2527
26- for line in __uhubctl ():
27- reg_hub = pattern_hub .match (line )
28- reg_port = pattern_port .match (line )
28+ """
29+ hubs = []
2930
30- if reg_hub :
31- hub = Hub (reg_hub .group (1 ))
32- hubs .append (hub )
33- elif reg_port :
34- # assume that the port is the last detected one
35- hub = hubs [- 1 ]
31+ pattern = re .compile ("Current status for hub ([\.\d-]+)" )
3632
37- if not hub :
38- continue
33+ for line in _uhubctl () :
34+ regex = pattern . match ( line )
3935
40- port = Port (hub , reg_port .group (1 ))
41- hub .ports .append (port )
36+ if regex :
37+ hub = Hub (regex .group (1 ), enumerate = True )
38+ hubs .append (hub )
4239
4340 return hubs
4441
4542
4643class Hub :
47- def __init__ (self , path : str ) -> None :
48- self .path = path
49- self .ports = []
44+ def __init__ (self , path : str , enumerate : bool = False ) -> None :
45+ """
46+ Create new hub instance
47+
48+ Arguments:
49+ path: USB hub path identifier
50+ enumerate: Automatically enumerate ports
51+ """
52+ self .path : str = path
53+ self .ports : List [Port ] = []
54+
55+ if enumerate :
56+ self .discover_ports ()
5057
5158 def add_port (self , port_number : int ) -> 'Port' :
59+ """
60+ Add port to hub by port number
61+
62+ Arguments:
63+ port_number: Indentification number of port
64+
65+ Returns:
66+ Port
67+
68+ """
5269 port = Port (self , port_number )
5370 self .ports .append (port )
5471
5572 return port
5673
5774 def add_ports (self , port_start : int , port_end : int ):
75+ """
76+ Add multiple ports to hub
77+
78+ Arguments:
79+ port_start: First port's indentification number
80+ port_end: Last port's ndentification number
81+ """
5882 for port_number in range (port_start , port_end ):
5983 self .add_port (port_number )
6084
85+ def find_port (self , port_number : int ) -> Optional ['Port' ]:
86+ """
87+ Find port by port number
88+
89+ Arguments:
90+ port_number: Identification number of port to find
91+
92+ Returns:
93+ Port or None
94+ """
95+
96+ for port in self .ports :
97+ if port .port_number == int (port_number ):
98+ return port
99+
100+ return None
101+
102+ def discover_ports (self ) -> None :
103+ """
104+ Discover ports for this hub instance
105+ """
106+ pattern = re .compile (" Port (\d+): \d{4} (power|off)" )
107+
108+ for line in _uhubctl (["-l" , self .path ]):
109+ regex = pattern .match (line )
110+
111+ if regex :
112+ port = Port (self , regex .group (1 ))
113+ self .ports .append (port )
114+
61115 def __str__ (self ) -> str :
62116 return f"USB Hub { self .path } "
63117
64118
65119class Port :
66120 def __init__ (self , hub : Hub , port_number : int ):
121+ """
122+ Create new port instance
123+
124+ Arguments:
125+ hub: Hub to attach port to
126+ port_number: Number of port to create
127+
128+ """
67129 self .hub = hub
68- self .port_number = port_number
130+ self .port_number = int ( port_number )
69131
70132 @property
71133 def status (self ) -> bool :
72134 status = None
73135 pattern = re .compile (f" Port { self .port_number } : \d{{4}} (power|off)" )
74136
75137 args = ["-l" , self .hub .path , "-p" , self .port_number ]
76- for line in __uhubctl (args ):
138+ for line in _uhubctl (args ):
77139 reg = pattern .match (line )
78140
79141 if reg :
@@ -93,7 +155,7 @@ def status(self, status: bool) -> None:
93155 else :
94156 args .append ("off" )
95157
96- __uhubctl (args )
158+ _uhubctl (args )
97159
98160 def __str__ (self ) -> str :
99161 return f"USB Port { self .hub .path } .{ self .port_number } "
0 commit comments