Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import childProcess from 'child_process'
import { Readline } from '@serialport/parser-readline'
import { PortInfo } from '@serialport/binding-abstract'
// get only serial port names
function checkPathOfDevice(path: string) {
return /(tty(S|ACM|USB|AMA|MFD|O)|rfcomm)/.test(path) && path
}
type PropName = 'DEVNAME' | 'ID_VENDOR_ENC' | 'ID_SERIAL_SHORT' | 'ID_VENDOR_ID' | 'ID_MODEL_ID' | 'DEVLINKS'
type PortInfoProp = 'comName' | 'manufacturer' | 'serialNumber' | 'vendorId' | 'productId' | 'pnpId'
type PropertyMapping = { [key in PropName]: PortInfoProp }
const map: PropertyMapping = {
DEVNAME: 'comName',
ID_VENDOR_ENC: 'manufacturer',
ID_SERIAL_SHORT: 'serialNumber',
ID_VENDOR_ID: 'vendorId',
ID_MODEL_ID: 'productId',
DEVLINKS: 'pnpId',
}
function propName(name: PropName): PortInfoProp | null {
return map[name] || null
}
function decodeHexEscape(str: string) {
return str.replace(/\\x([a-fA-F0-9]{2})/g, (a, b) => {
return String.fromCharCode(parseInt(b, 16))
})
}
function propVal(name: string, val: string) {
if (name === 'pnpId') {
const match = val.match(/\/by-id\/([^\s]+)/)
return (match && match[1]) || undefined
}
if (name === 'manufacturer') {
return decodeHexEscape(val)
}
if (/^0x/.test(val)) {
return val.substr(2)
}
return val
}
export async function linuxList(): Promise<ReadonlyArray<PortInfo>> {
const ports: PortInfo[] = []
const ude = childProcess.spawn('udevadm', ['info', '-e'])
const lines = ude.stdout.pipe(new Readline())
let port: PortInfo | null = null
let skipPort = false
lines.on('data', (line: string) => {
const lineType = line.slice(0, 1)
const data = line.slice(3)
// new port entry
if (lineType === 'P') {
port = {
comName: '',
manufacturer: undefined,
serialNumber: undefined,
pnpId: undefined,
locationId: undefined,
vendorId: undefined,
productId: undefined,
}
skipPort = false
return
}
if (skipPort) {
return
}
// Check dev name and save port if it matches flag to skip the rest of the data if not
if (lineType === 'N') {
if (port && checkPathOfDevice(data)) {
ports.push(port)
} else {
skipPort = true
}
return
}
// parse data about each port
if (port && lineType === 'E') {
const keyValue = data.match(/^(.+)=(.*)/)
if (!keyValue) {
return
}
const key = propName(keyValue[1].toUpperCase() as PropName)
if (!key) {
return
}
port = { ...port, [key]: propVal(key, keyValue[2]) }
}
})
return new Promise((resolve, reject) => {
ude.on('error', reject)
lines.on('error', reject)
lines.on('finish', () => resolve(ports))
}) as Promise<PortInfo[]>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment