77from contextlib import suppress
88from enum import StrEnum
99import json
10+ import os
1011from typing import Any
1112
12- import serial .tools .list_ports
13- from serial .tools .list_ports_common import ListPortInfo
1413import voluptuous as vol
1514from zha .application .const import RadioType
1615import zigpy .backups
2524 ZigbeeFlowStrategy ,
2625)
2726from homeassistant .components .homeassistant_yellow import hardware as yellow_hardware
27+ from homeassistant .components .usb import USBDevice , scan_serial_ports
2828from homeassistant .config_entries import (
2929 SOURCE_IGNORE ,
3030 SOURCE_ZEROCONF ,
@@ -124,10 +124,10 @@ def _format_backup_choice(
124124 return f"{ dt_util .as_local (backup .backup_time ).strftime ('%c' )} ({ identifier } )"
125125
126126
127- async def list_serial_ports (hass : HomeAssistant ) -> list [ListPortInfo ]:
127+ async def list_serial_ports (hass : HomeAssistant ) -> list [USBDevice ]:
128128 """List all serial ports, including the Yellow radio and the multi-PAN addon."""
129- ports : list [ListPortInfo ] = []
130- ports .extend (await hass .async_add_executor_job (serial . tools . list_ports . comports ))
129+ ports : list [USBDevice ] = []
130+ ports .extend (await hass .async_add_executor_job (scan_serial_ports ))
131131
132132 # Add useful info to the Yellow's serial port selection screen
133133 try :
@@ -137,9 +137,14 @@ async def list_serial_ports(hass: HomeAssistant) -> list[ListPortInfo]:
137137 else :
138138 # PySerial does not properly handle the Yellow's serial port with the CM5
139139 # so we manually include it
140- port = ListPortInfo (device = "/dev/ttyAMA1" , skip_link_detection = True )
141- port .description = "Yellow Zigbee module"
142- port .manufacturer = "Nabu Casa"
140+ port = USBDevice (
141+ device = "/dev/ttyAMA1" ,
142+ vid = "ffff" , # This is technically not a USB device
143+ pid = "ffff" ,
144+ serial_number = None ,
145+ manufacturer = "Nabu Casa" ,
146+ description = "Yellow Zigbee module" ,
147+ )
143148
144149 ports = [p for p in ports if not p .device .startswith ("/dev/ttyAMA" )]
145150 ports .insert (0 , port )
@@ -156,13 +161,15 @@ async def list_serial_ports(hass: HomeAssistant) -> list[ListPortInfo]:
156161 addon_info = None
157162
158163 if addon_info is not None and addon_info .state != AddonState .NOT_INSTALLED :
159- addon_port = ListPortInfo (
164+ addon_port = USBDevice (
160165 device = silabs_multiprotocol_addon .get_zigbee_socket (),
161- skip_link_detection = True ,
166+ vid = "ffff" , # This is technically not a USB device
167+ pid = "ffff" ,
168+ serial_number = None ,
169+ manufacturer = "Nabu Casa" ,
170+ description = "Silicon Labs Multiprotocol add-on" ,
162171 )
163172
164- addon_port .description = "Multiprotocol add-on"
165- addon_port .manufacturer = "Nabu Casa"
166173 ports .append (addon_port )
167174
168175 return ports
@@ -218,8 +225,15 @@ async def async_step_choose_serial_port(
218225 ) -> ConfigFlowResult :
219226 """Choose a serial port."""
220227 ports = await list_serial_ports (self .hass )
228+
229+ # The full `/dev/serial/by-id/` path is too verbose to show
230+ resolved_paths = {
231+ p .device : await self .hass .async_add_executor_job (os .path .realpath , p .device )
232+ for p in ports
233+ }
234+
221235 list_of_ports = [
222- f"{ p } { ', s/n: ' + p .serial_number if p .serial_number else '' } "
236+ f"{ resolved_paths [ p . device ] } - { p . description } { ', s/n: ' + p .serial_number if p .serial_number else '' } "
223237 + (f" - { p .manufacturer } " if p .manufacturer else "" )
224238 for p in ports
225239 ]
0 commit comments