USB-utilは、接続済みのUSBデバイス/BLEデバイスをスキャンし、詳細情報をGUIで閲覧できるクロスプラットフォーム対応のPythonツールです。macOS/LinuxではPyUSB+libusb、WindowsではWMI経由でCOMポート情報も取得し、usb.idsによるベンダー/製品名補完やスナップショット永続化、責務分離設計を特徴とします。
- macOS/Linux: PyUSBによるUSBデバイススキャン・メタデータ取得
- Windows: WMI経由でCOMポート突合・USB情報取得
- usb.idsによるベンダー/プロダクト名自動解決
- 取得スナップショットのusb_devices.json保存
- CustomTkinterベースのGUIでのデバイス切替・JSONプレビュー
- エラーメッセージの明示表示
- COMポート情報の自動逆引き(core/com_ports.py利用)
- Windows: ハブ/ポート連鎖・USBコントローラ名自動解析
- Serial/Port Path/BUS/Addressを組み合わせた識別タグ生成
- Python: 3.9以降
- macOS/Linux: libusb 1.0(PyUSBバックエンド)
- Windows: WMIサービス有効な環境(WinUSB/Zadig等不要)
- ネイティブライブラリのビルド/読み込みに必要な環境
依存解決の再現性向上のため、pip の index 設定は
$AILAB_ROOT/lab_automation_module/config/pip/pip.conf.local を正本とします。
ローカル環境では AILAB_ROOT を各マシンで設定してから PIP_CONFIG_FILE を指定してください。
export AILAB_ROOT=/path/to/AiLab
export PIP_CONFIG_FILE="$AILAB_ROOT/lab_automation_module/config/pip/pip.conf.local"CI では PIP_CONFIG_FILE 固定ではなく、環境変数で index を注入してください。
export PIP_INDEX_URL="http://<internal-pypi>/simple"
export PIP_EXTRA_INDEX_URL="https://pypi.org/simple"
export PIP_TRUSTED_HOST="<internal-pypi-host>"- Homebrewで
brew install libusb
python main.py
USB: VID/PID、Manufacturer/Product、Serial、Bus/Address、Port Path、Descriptor情報
BLE: BLE Address、デバイス名、RSSI、Service UUIDs
USB-util/
├── src/
├── docs/
├── scripts/
├── pyproject.toml
├── LICENSE
└── README.md
バグ報告・機能要望・プルリクエストを歓迎します。
MIT License — 詳細は LICENSE をご覧ください。
sudo apt update && sudo apt install libusb-1.0-0
- Python仮想環境有効化:
venv\Scripts\activate(PowerShell/コマンドプロンプト) - 追加パッケージ:
pip install customtkinter wmi - シリアル通信やCOMポート列挙を行う場合は
pip install pyserial
注意: Windowsは管理者権限で実行することを推奨します。
customtkinter
pyusb # macOS/LinuxでのUSBスキャン用
wmi # WindowsでのUSB情報取得用
pyserial # COMポート列挙/シリアル通信を行う場合
補足: macOSでHomebrewを利用している場合は
brew install libusb、Linuxでは各ディストリビューションのパッケージマネージャーからlibusb-1.0をインストールしてください。WindowsではWMIサービスが有効であれば追加のドライバ導入は不要です。
依存解決の再現性向上のため、pip の index 設定は
$AILAB_ROOT/lab_automation_module/config/pip/pip.conf.local を正本とします。
ローカル環境では AILAB_ROOT を各マシンで設定してから PIP_CONFIG_FILE を指定してください。
export AILAB_ROOT=/path/to/AiLab
export PIP_CONFIG_FILE="$AILAB_ROOT/lab_automation_module/config/pip/pip.conf.local"CI では PIP_CONFIG_FILE 固定ではなく、環境変数で index を注入してください。
export PIP_INDEX_URL="http://<internal-pypi>/simple"
export PIP_EXTRA_INDEX_URL="https://pypi.org/simple"
export PIP_TRUSTED_HOST="<internal-pypi-host>"- 仮想環境を作成して有効化します。
python -m venv .venv_USB-Util source .venv_USB-Util/bin/activate # Windowsは .venv_USB-Util\Scripts\activate
- プロジェクト依存をインストールします。
python -m pip install -e . python -m pip check python -m pip -Vを実行し、.venv_USB-Util配下の pip が使われていることを確認します。usb.idsをプロジェクトルートに配置するか、環境変数USB_IDS_PATHで外部のusb.idsファイルを指定します。
代表的な設置場所(Linux, macOS)は/usr/share/hwdata/usb.ids等です。
依存更新ルール(再発防止)
ak_communication/aist_guipartsを更新した場合は、対象パッケージの version を上げた上でlab_automation_libs/internal-PyPI/dist/の wheel を更新してください。同一バージョンの差し替えはキャッシュ衝突の原因になります。
- Python環境をアクティブにした状態で
usb_util_gui.pyを実行します。python usb_util_gui.py
- 初回起動時にUSBデバイスをスキャンし、結果を
usb_devices.jsonに保存します。 - GUI上部のコンボボックスからデバイス(
VID:PID形式)を切り替えると、左側に主要情報、右側に詳細JSONが表示されます(ViewModelが選択状態と表示内容を管理します)。
アプリケーションは以下の優先順位で usb.ids を探索します。
- 環境変数
USB_IDS_PATH - プロジェクトルートの
usb.ids - 現在の作業ディレクトリの
usb.ids - OS毎の代表パス(例:
/usr/share/hwdata/usb.ids、/opt/homebrew/share/hwdata/usb.idsなど)
USB-Serialデバイスは、PCから抜き差しするたびにCOMポート番号(例: COM5, /dev/ttyUSB0 など)が変わることがありますが、 VID(ベンダーID)、PID(プロダクトID)、Serial(シリアル番号)はデバイス固有で基本的に不変です。
本ツールでは、
get_com_port_for_device(vid, pid, serial=None)というAPIで、- 指定したUSBデバイス(VID/PID/Serial)に一致する現在割り当てられているCOMポート番号を自動で取得できます。
これにより、
- 物理的な抜き差しやPC再起動後でも、同じデバイスを正しく自動制御できる
- LiquidDispenser("COM5") のようなAPIの引数を自動で割り当て可能
- GUI上でもCOMポート情報が自動表示される
com_port = get_com_port_for_device(vid, pid, serial)
if com_port:
sender = LiquidDispenser(com_port)プロジェクトとは別のスクリプトから直接COMポートを解決したい場合は、usb_util_gui.py が提供するヘルパー関数をインポートして使用できます。
# other_module.py からの利用サンプル
from usb_util_gui import get_com_port_for_device
vid = "0x1234"
pid = "0x5678"
serial = "ABCDEF123"
port = get_com_port_for_device(vid, pid, serial, refresh=True)
if port:
print(f"USBデバイスのCOMポートは {port} です")
else:
print("一致するCOMポートが見つかりませんでした")refresh=True を指定すると呼び出し前にUSBデバイスの再スキャンを行います(既存スナップショットがある場合は省略可能)。戻り値は serial.Serial(port=...) に渡せる文字列です。
- デバイス固有情報(vid, pid, serial)からCOMポートを逆引きできる
- USBデバイスの抜き差しや環境変化にも柔軟に対応
- GUIでもCOMポート情報を確認可能
- Windowsでは、WMI経由でHub/Port番号とUSBコントローラ名を同時取得
- Serialが取得できないケースでも、Port PathやBUS/Addressを組み合わせた識別タグで同一機種を区別可能
実装上は
core/com_ports.pyのComPortManagerを介してアクセスします。GUIでは ViewModel からこのクラスを利用し、同期的にCOMポート情報を解決しています。
USBデバイスのVID/PID/Serialから、現在割り当てられているCOMポート番号をコマンドラインで取得できます。
usb_util_gui.py を以下のように実行します:
python usb_util_gui.py <vid> <pid> [serial]<vid>: ベンダーID(例: 0x1234)<pid>: プロダクトID(例: 0x5678)[serial]: シリアル番号(省略可能)--refreshフラグを併用するとコマンド呼び出しの直前に再スキャンを実施します。
python usb_util_gui.py 0x1234 0x5678 ABCDEF123→ 該当するCOMポート名(例: COM5, /dev/tty.usbserial-xxxx)が標準出力に表示されます。 VID/PIDに対するベンダー名・製品名(usb.ids由来)と、実機から取得した生のManufacturer/Product文字列も併記されるため、入力値の検証が容易です。識別タグ・ポートパス・バス/アドレスも併せて出力されるため、同一機種が複数ある場合でも個体を特定できます。
シリアル番号は省略可能です。
GUIを起動したい場合は引数なしで
python usb_util_gui.pyを実行してください。
python usb_util_gui.py 0x25a4 0x9311
python usb_util_gui.py 0x25a4 0x9311 --send "STATUS" --append-newline --read-until OK --baudrate 115200
python usb_util_gui.py 0x03e7 0x2485 03e72485 --refresh
python usb_util_gui.py --self-test環境セットアップが正しく行われているか確認したいときは、以下のコマンドで自己診断を実行できます。
python usb_util_gui.py --self-test- 現在保存されているUSBスナップショット(最大5件)と、pyserialが検出したCOMポートを一覧表示します。
- Windowsの場合はWMI経由の取得結果、macOS/Linuxの場合はPyUSB経由の結果が確認できます。
- 失敗時には不足しているライブラリや権限に関するヒントが表示されます。
core/
├─ com_ports.py # USB-Serialポート検出ヘルパー
├─ models.py # UsbDeviceSnapshotデータクラス
├─ scanner.py # OS毎にPyUSB/WMIを使い分けてUSBデバイスをスキャン
├─ repository.py # JSONファイル永続化
├─ service.py # UsbSnapshotService(スキャンと永続化の調停)
└─ topology_wmi.py # Windows用Hub/Port/Controller解析
ui/
└─ view_model.py # UsbDevicesViewModel(GUIの状態管理)
usb_util_gui.py # TkベースのView(UsbDevicesApp)とアプリ起動エントリ
-
サービス層 (
core/service.py)
UsbSnapshotServiceがUsbScannerとUsbSnapshotRepositoryを協調させ、スキャン結果をJSONへ保存します。またUSB接続状態確認の窓口もここに集約しています。 -
スキャナ (
core/scanner.py)
macOS/LinuxではPyUSB、WindowsではWMIを利用してUSBデバイスを列挙・詳細取得します。CLI/GUIの双方から共通利用できるよう、依存は最小限に抑えています。 -
トポロジ解析 (
core/topology_wmi.py)
Windows環境ではWMIを用いてPnPデバイスを解析し、Hub/Port連鎖とUSBホストコントローラ名を抽出します。サービス層から呼び出され、スナップショットへ追加情報として格納されます。 -
ViewModel (
ui/view_model.py)
UsbDevicesViewModelがスナップショットの並び替え、選択状態、派生情報(COMポート・表示用テキスト)を管理し、View から呼び出し可能なメソッドを提供します。 -
View (
usb_util_gui.py)
UsbDevicesAppはCustomTkinterでUI要素を構築し、ViewModelが提供する値に従って描画・再描画します。イベントハンドラはViewModelへの委譲を行うだけに簡素化されています。
この構成により、ビジネスロジック(スキャン/永続化/状態管理)と表示ロジックを明確に分離し、テストや機能拡張を容易にしています。
- PyUSBが見つからない: macOS/Linuxでは
pip install pyusbを実行してください。WindowsではPyUSB依存なしで動作します。 - libusb backend が見つからない: macOS/Linux向けにlibusb 1.0を導入します。Windowsでは不要です。
- WMIでエラーが発生する: PowerShellで
Get-Service Winmgmtが実行できるか、管理者権限でアプリを起動しているか確認してください。 - USBデバイスが表示されない: 実行ユーザーにUSBデバイスへアクセスできる権限があるか確認してください。Linuxでは
plugdevグループへの所属やudevルールの調整が必要な場合があります。 - GUIフォントが崩れる:
Meiryoが存在しない環境では、CustomTkinterが代替フォントを使用します。必要に応じてコード中のフォント設定を変更してください。
- 収集したUSBスナップショットは
usb_devices.jsonに配列形式で保存されます。内容を直接編集するとGUIにも反映されます。 - スキャン・永続化ロジックは
core/配下、GUIはViewModel+Viewに二分されています。エントリポイントはusb_util_gui.pyのmain()です。 - ViewModelはテストしやすいようにPure Pythonで実装されています。UIの結合テストを行う場合もViewModelをモック化することで効率的に検証できます。
MIT License
- TITManagement 2025
- 運用担当者: 日常運用・手順実行を行う担当者
- 開発者: 機能追加・保守を行う担当者
- 検証担当者: 実機/テスト環境で動作確認を行う担当者
- Python:
pyproject.tomlのrequires-pythonに従う - 主要ライブラリ:
pyproject.tomlのdependenciesを参照 - pip index設定:
$AILAB_ROOT/lab_automation_module/config/pip/pip.conf.localを正本とする
bash / zsh で以下を実行。
export AILAB_ROOT=/path/to/AiLab
export PIP_CONFIG_FILE="$AILAB_ROOT/lab_automation_module/config/pip/pip.conf.local"
python -m venv .venv_USB-Util
source .venv_USB-Util/bin/activate
python -m pip install -e .