whatcable-macos-usb-inspector
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhatCable macOS USB-C Inspector
WhatCable macOS USB-C 检测工具
Skill by ara.so — Daily 2026 Skills collection.
WhatCable is a macOS 14+ menu bar app (Swift/SwiftUI) that reads IOKit services to surface what each USB-C cable plugged into your Mac can actually do — speed, power rating, e-marker data, PDO profiles, and connected device identity — in plain English.
由ara.so开发的技能应用 — 属于Daily 2026 Skills系列。
WhatCable是一款适用于macOS 14+的菜单栏应用(基于Swift/SwiftUI开发),它通过读取IOKit服务,以通俗易懂的方式展示连接到Mac的每根USB-C线缆的实际能力——包括传输速度、功率额定值、电子标记数据、PDO配置文件以及连接设备的身份信息。
Project Structure
项目结构
Sources/WhatCable/
├── WhatCableApp.swift # App entry point, menu bar setup
├── ContentView.swift # Main popover UI
├── PortSummary.swift # Plain-English logic per port
├── PDVDO.swift # PD VDO bit-twiddling / spec decoding
├── IOKitReader.swift # IOKit service queries
└── ...
scripts/
└── build-app.sh # Universal binary + notarisationSources/WhatCable/
├── WhatCableApp.swift # 应用入口,菜单栏设置
├── ContentView.swift # 主弹出窗口UI
├── PortSummary.swift # 单端口的通俗化逻辑处理
├── PDVDO.swift # PD VDO位操作/规范解码
├── IOKitReader.swift # IOKit服务查询
└── ...
scripts/
└── build-app.sh # 通用二进制包构建 + 公证Install / Build
安装/构建
bash
undefinedbash
undefinedRun locally (development)
本地运行(开发环境)
swift run WhatCable
swift run WhatCable
Build distributable universal app (arm64 + x86_64)
构建可分发的通用应用(arm64 + x86_64)
./scripts/build-app.sh
./scripts/build-app.sh
→ dist/WhatCable.app
→ dist/WhatCable.app
→ dist/WhatCable.zip
→ dist/WhatCable.zip
Requires Swift 5.9 / Xcode 15+, macOS 14 (Sonoma) or later.
需要Swift 5.9 / Xcode 15+,以及macOS 14(Sonoma)或更高版本。Signed + Notarised Build
签名+公证构建
bash
cp .env.example .envbash
cp .env.example .envEdit .env:
编辑.env:
DEVELOPER_ID="Developer ID Application: Your Name (TEAMID)"
DEVELOPER_ID="Developer ID Application: Your Name (TEAMID)"
NOTARY_PROFILE="WhatCable-notary"
NOTARY_PROFILE="WhatCable-notary"
Store notarytool credentials once
一次性存储notarytool凭证
xcrun notarytool store-credentials "WhatCable-notary"
--apple-id "$APPLE_ID"
--team-id "$TEAM_ID"
--password "$APP_SPECIFIC_PASSWORD"
--apple-id "$APPLE_ID"
--team-id "$TEAM_ID"
--password "$APP_SPECIFIC_PASSWORD"
./scripts/build-app.sh
undefinedxcrun notarytool store-credentials "WhatCable-notary"
--apple-id "$APPLE_ID"
--team-id "$TEAM_ID"
--password "$APP_SPECIFIC_PASSWORD"
--apple-id "$APPLE_ID"
--team-id "$TEAM_ID"
--password "$APP_SPECIFIC_PASSWORD"
./scripts/build-app.sh
undefinedKey Concepts
核心概念
IOKit Service Families
IOKit服务家族
| Service | Purpose |
|---|---|
| Per-port state, transports, plug orientation, e-marker presence |
| Same, older HPM interface |
| Full PDO list + live negotiated PDO |
| PD Discover Identity VDOs (SOP = partner, SOP' = cable e-marker) |
| 服务 | 用途 |
|---|---|
| 单端口状态、传输类型、插头方向、电子标记存在情况 |
| 功能同上,旧版HPM接口 |
| 完整PDO列表 + 当前协商的PDO |
| PD身份识别VDO(SOP = 对接设备,SOP' = 线缆电子标记) |
USB PD VDO Decoding (PDVDO.swift)
USB PD VDO解码(PDVDO.swift)
The core bit-twiddling follows USB Power Delivery 3.x spec. Cable e-marker VDOs encode speed and current in specific bit fields.
swift
// Example: decode cable speed from Passive Cable VDO
struct PassiveCableVDO {
let raw: UInt32
// Bits [5:3] — USB SuperSpeed signalling support
var usbSSSignalling: UInt8 {
UInt8((raw >> 3) & 0b111)
}
var dataRate: String {
switch usbSSSignalling {
case 0b000: return "USB 2.0 only"
case 0b001: return "USB 3.2 Gen 1 (5 Gbps)"
case 0b010: return "USB 3.2 Gen 2 (10 Gbps)"
case 0b011: return "USB 3.2 Gen 2x2 / USB4 Gen 2 (20 Gbps)"
case 0b100: return "USB4 Gen 3 (40 Gbps)"
default: return "Unknown"
}
}
// Bits [6:5] — VBUS current handling
var currentCapability: String {
switch (raw >> 5) & 0b11 {
case 0b00: return "USB Type-C Default (≤3A)"
case 0b01: return "3A"
case 0b10: return "5A"
default: return "Reserved"
}
}
}核心的位操作遵循USB Power Delivery 3.x规范。线缆电子标记VDO在特定位字段中编码了速度和电流信息。
swift
// 示例:从无源线缆VDO中解码线缆速度
struct PassiveCableVDO {
let raw: UInt32
// 位[5:3] — USB SuperSpeed信号支持
var usbSSSignalling: UInt8 {
UInt8((raw >> 3) & 0b111)
}
var dataRate: String {
switch usbSSSignalling {
case 0b000: return "USB 2.0 only"
case 0b001: return "USB 3.2 Gen 1 (5 Gbps)"
case 0b010: return "USB 3.2 Gen 2 (10 Gbps)"
case 0b011: return "USB 3.2 Gen 2x2 / USB4 Gen 2 (20 Gbps)"
case 0b100: return "USB4 Gen 3 (40 Gbps)"
default: return "Unknown"
}
}
// 位[6:5] — VBUS电流承载能力
var currentCapability: String {
switch (raw >> 5) & 0b11 {
case 0b00: return "USB Type-C Default (≤3A)"
case 0b01: return "3A"
case 0b10: return "5A"
default: return "Reserved"
}
}
}Reading IOKit Properties
读取IOKit属性
swift
import IOKit
func readPortProperties(serviceName: String) -> [String: Any]? {
var iterator: io_iterator_t = 0
let matchDict = IOServiceMatching(serviceName)
guard IOServiceGetMatchingServices(kIOMainPortDefault,
matchDict, &iterator) == KERN_SUCCESS else {
return nil
}
defer { IOObjectRelease(iterator) }
var service = IOIteratorNext(iterator)
var results: [String: Any] = [:]
while service != 0 {
defer {
IOObjectRelease(service)
service = IOIteratorNext(iterator)
}
if let props = copyProperties(service) {
results.merge(props) { _, new in new }
}
}
return results.isEmpty ? nil : results
}
private func copyProperties(_ service: io_service_t) -> [String: Any]? {
var propsRef: Unmanaged<CFMutableDictionary>?
guard IORegistryEntryCreateCFProperties(service, &propsRef,
kCFAllocatorDefault, 0) == KERN_SUCCESS,
let props = propsRef?.takeRetainedValue() as? [String: Any] else {
return nil
}
return props
}swift
import IOKit
func readPortProperties(serviceName: String) -> [String: Any]? {
var iterator: io_iterator_t = 0
let matchDict = IOServiceMatching(serviceName)
guard IOServiceGetMatchingServices(kIOMainPortDefault,
matchDict, &iterator) == KERN_SUCCESS else {
return nil
}
defer { IOObjectRelease(iterator) }
var service = IOIteratorNext(iterator)
var results: [String: Any] = [:]
while service != 0 {
defer {
IOObjectRelease(service)
service = IOIteratorNext(iterator)
}
if let props = copyProperties(service) {
results.merge(props) { _, new in new }
}
}
return results.isEmpty ? nil : results
}
private func copyProperties(_ service: io_service_t) -> [String: Any]? {
var propsRef: Unmanaged<CFMutableDictionary>?
guard IORegistryEntryCreateCFProperties(service, &propsRef,
kCFAllocatorDefault, 0) == KERN_SUCCESS,
let props = propsRef?.takeRetainedValue() as? [String: Any] else {
return nil
}
return props
}Port Summary Plain-English Logic (PortSummary.swift)
端口信息通俗化逻辑(PortSummary.swift)
swift
enum PortHeadline: String {
case thunderbolt = "Thunderbolt / USB4"
case usbDevice = "USB device connected"
case chargingOnly = "Charging only"
case slowCable = "Slow USB / charge-only cable"
case nothing = "Nothing connected"
}
struct PortSummary {
let headline: PortHeadline
let chargingDiagnostic: String?
let dataRate: String?
let currentRating: String?
let negotiatedPDO: PDO?
let allPDOs: [PDO]
static func from(ioKitProps: [String: Any]) -> PortSummary {
let hasThunderbolt = ioKitProps["Thunderbolt"] as? Bool ?? false
let hasUSB3 = ioKitProps["USB3"] as? Bool ?? false
let isConnected = ioKitProps["Connected"] as? Bool ?? false
let emarkerPresent = ioKitProps["CableEMarker"] as? Bool ?? false
let headline: PortHeadline
if !isConnected {
headline = .nothing
} else if hasThunderbolt {
headline = .thunderbolt
} else if hasUSB3 {
headline = .usbDevice
} else if emarkerPresent {
headline = .chargingOnly
} else {
headline = .slowCable
}
// Parse PDOs for charging diagnostic
let pdos = parsePDOs(from: ioKitProps)
let negotiated = pdos.first(where: { $0.isActive })
let diagnostic = buildChargingDiagnostic(pdos: pdos, negotiated: negotiated)
return PortSummary(
headline: headline,
chargingDiagnostic: diagnostic,
dataRate: emarkerPresent ? decodeDataRate(ioKitProps) : nil,
currentRating: emarkerPresent ? decodeCurrentRating(ioKitProps) : nil,
negotiatedPDO: negotiated,
allPDOs: pdos
)
}
}swift
enum PortHeadline: String {
case thunderbolt = "Thunderbolt / USB4"
case usbDevice = "USB device connected"
case chargingOnly = "Charging only"
case slowCable = "Slow USB / charge-only cable"
case nothing = "Nothing connected"
}
struct PortSummary {
let headline: PortHeadline
let chargingDiagnostic: String?
let dataRate: String?
let currentRating: String?
let negotiatedPDO: PDO?
let allPDOs: [PDO]
static func from(ioKitProps: [String: Any]) -> PortSummary {
let hasThunderbolt = ioKitProps["Thunderbolt"] as? Bool ?? false
let hasUSB3 = ioKitProps["USB3"] as? Bool ?? false
let isConnected = ioKitProps["Connected"] as? Bool ?? false
let emarkerPresent = ioKitProps["CableEMarker"] as? Bool ?? false
let headline: PortHeadline
if !isConnected {
headline = .nothing
} else if hasThunderbolt {
headline = .thunderbolt
} else if hasUSB3 {
headline = .usbDevice
} else if emarkerPresent {
headline = .chargingOnly
} else {
headline = .slowCable
}
// 解析PDO以生成充电诊断信息
let pdos = parsePDOs(from: ioKitProps)
let negotiated = pdos.first(where: { $0.isActive })
let diagnostic = buildChargingDiagnostic(pdos: pdos, negotiated: negotiated)
return PortSummary(
headline: headline,
chargingDiagnostic: diagnostic,
dataRate: emarkerPresent ? decodeDataRate(ioKitProps) : nil,
currentRating: emarkerPresent ? decodeCurrentRating(ioKitProps) : nil,
negotiatedPDO: negotiated,
allPDOs: pdos
)
}
}PDO Parsing (Power Data Objects)
PDO解析(电源数据对象)
swift
struct PDO {
let voltage: Double // Volts
let maxCurrent: Double // Amps
let maxWatts: Double // voltage * maxCurrent
let isActive: Bool
static func parse(raw: UInt32, isActive: Bool) -> PDO? {
// Fixed supply PDO: bits [19:10] = max current (10mA units),
// bits [29:20] = voltage (50mV units)
let currentRaw = (raw >> 10) & 0x3FF
let voltageRaw = (raw >> 20) & 0x3FF
guard voltageRaw > 0 else { return nil }
let voltage = Double(voltageRaw) * 0.05
let current = Double(currentRaw) * 0.01
return PDO(
voltage: voltage,
maxCurrent: current,
maxWatts: voltage * current,
isActive: isActive
)
}
}
func parsePDOs(from props: [String: Any]) -> [PDO] {
guard let pdoArray = props["PDOs"] as? [UInt32],
let activePDOIndex = props["ActivePDOIndex"] as? Int else {
return []
}
return pdoArray.enumerated().compactMap { idx, raw in
PDO.parse(raw: raw, isActive: idx == activePDOIndex)
}
}swift
struct PDO {
let voltage: Double // 伏特
let maxCurrent: Double // 安培
let maxWatts: Double // voltage * maxCurrent
let isActive: Bool
static func parse(raw: UInt32, isActive: Bool) -> PDO? {
// 固定供电PDO:位[19:10] = 最大电流(单位10mA),
// 位[29:20] = 电压(单位50mV)
let currentRaw = (raw >> 10) & 0x3FF
let voltageRaw = (raw >> 20) & 0x3FF
guard voltageRaw > 0 else { return nil }
let voltage = Double(voltageRaw) * 0.05
let current = Double(currentRaw) * 0.01
return PDO(
voltage: voltage,
maxCurrent: current,
maxWatts: voltage * current,
isActive: isActive
)
}
}
func parsePDOs(from props: [String: Any]) -> [PDO] {
guard let pdoArray = props["PDOs"] as? [UInt32],
let activePDOIndex = props["ActivePDOIndex"] as? Int else {
return []
}
return pdoArray.enumerated().compactMap { idx, raw in
PDO.parse(raw: raw, isActive: idx == activePDOIndex)
}
}SwiftUI Popover Pattern (ContentView.swift)
SwiftUI弹出窗口模式(ContentView.swift)
swift
import SwiftUI
struct ContentView: View {
@StateObject private var model = CableModel()
var body: some View {
VStack(alignment: .leading, spacing: 0) {
HeaderView()
Divider()
ScrollView {
VStack(alignment: .leading, spacing: 12) {
ForEach(model.ports) { port in
PortRowView(port: port)
}
}
.padding()
}
}
.frame(width: 360)
.onAppear { model.refresh() }
}
}
struct PortRowView: View {
let port: PortSummary
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Label(port.headline.rawValue, systemImage: iconName(for: port.headline))
.font(.headline)
if let diagnostic = port.chargingDiagnostic {
Text(diagnostic)
.font(.subheadline)
.foregroundStyle(.secondary)
}
if let rate = port.dataRate {
Text("Speed: \(rate)")
.font(.caption)
}
}
.padding(.vertical, 6)
}
func iconName(for headline: PortHeadline) -> String {
switch headline {
case .thunderbolt: return "bolt.fill"
case .usbDevice: return "cable.connector"
case .chargingOnly: return "battery.100.bolt"
case .slowCable: return "exclamationmark.triangle"
case .nothing: return "circle.dashed"
}
}
}swift
import SwiftUI
struct ContentView: View {
@StateObject private var model = CableModel()
var body: some View {
VStack(alignment: .leading, spacing: 0) {
HeaderView()
Divider()
ScrollView {
VStack(alignment: .leading, spacing: 12) {
ForEach(model.ports) { port in
PortRowView(port: port)
}
}
.padding()
}
}
.frame(width: 360)
.onAppear { model.refresh() }
}
}
struct PortRowView: View {
let port: PortSummary
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Label(port.headline.rawValue, systemImage: iconName(for: port.headline))
.font(.headline)
if let diagnostic = port.chargingDiagnostic {
Text(diagnostic)
.font(.subheadline)
.foregroundStyle(.secondary)
}
if let rate = port.dataRate {
Text("Speed: \(rate)")
.font(.caption)
}
}
.padding(.vertical, 6)
}
func iconName(for headline: PortHeadline) -> String {
switch headline {
case .thunderbolt: return "bolt.fill"
case .usbDevice: return "cable.connector"
case .chargingOnly: return "battery.100.bolt"
case .slowCable: return "exclamationmark.triangle"
case .nothing: return "circle.dashed"
}
}
}Menu Bar App Setup (WhatCableApp.swift)
菜单栏应用设置(WhatCableApp.swift)
swift
import SwiftUI
@main
struct WhatCableApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
// No WindowGroup — pure menu bar app
Settings { SettingsView() }
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
var statusItem: NSStatusItem?
var popover = NSPopover()
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.setActivationPolicy(.accessory) // hide from Dock by default
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.image = NSImage(systemSymbolName: "cable.connector",
accessibilityDescription: "WhatCable")
button.action = #selector(togglePopover)
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
}
popover.contentViewController = NSHostingController(rootView: ContentView())
popover.behavior = .transient
}
@objc func togglePopover(_ sender: NSStatusBarButton) {
if popover.isShown {
popover.performClose(sender)
} else {
if let button = statusItem?.button {
popover.show(relativeTo: button.bounds, of: button,
preferredEdge: .minY)
}
}
}
}swift
import SwiftUI
@main
struct WhatCableApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
// 无需WindowGroup — 纯菜单栏应用
Settings { SettingsView() }
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
var statusItem: NSStatusItem?
var popover = NSPopover()
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.setActivationPolicy(.accessory) // 默认隐藏Dock图标
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.image = NSImage(systemSymbolName: "cable.connector",
accessibilityDescription: "WhatCable")
button.action = #selector(togglePopover)
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
}
popover.contentViewController = NSHostingController(rootView: ContentView())
popover.behavior = .transient
}
@objc func togglePopover(_ sender: NSStatusBarButton) {
if popover.isShown {
popover.performClose(sender)
} else {
if let button = statusItem?.button {
popover.show(relativeTo: button.bounds, of: button,
preferredEdge: .minY)
}
}
}
}Charging Diagnostic Helper
充电诊断辅助函数
swift
func buildChargingDiagnostic(pdos: [PDO], negotiated: PDO?) -> String? {
guard let active = negotiated else { return nil }
let maxAvailable = pdos.map(\.maxWatts).max() ?? 0
let activeW = active.maxWatts
let ratio = maxAvailable > 0 ? activeW / maxAvailable : 1.0
if ratio < 0.5 {
return String(format: "Charging at %.0fW (charger can do up to %.0fW)", activeW, maxAvailable)
} else if ratio < 0.9 {
return String(format: "Cable is limiting charging speed (%.0fW of %.0fW available)", activeW, maxAvailable)
} else {
return String(format: "Charging well at %.0fW", activeW)
}
}swift
func buildChargingDiagnostic(pdos: [PDO], negotiated: PDO?) -> String? {
guard let active = negotiated else { return nil }
let maxAvailable = pdos.map(\.maxWatts).max() ?? 0
let activeW = active.maxWatts
let ratio = maxAvailable > 0 ? activeW / maxAvailable : 1.0
if ratio < 0.5 {
return String(format: "Charging at %.0fW (charger can do up to %.0fW)", activeW, maxAvailable)
} else if ratio < 0.9 {
return String(format: "Cable is limiting charging speed (%.0fW of %.0fW available)", activeW, maxAvailable)
} else {
return String(format: "Charging well at %.0fW", activeW)
}
}Common Patterns
通用开发模式
Adding a New Port Property
添加新的端口属性
- Read the raw value from IOKit props in
IOKitReader.swift - Decode it (bit-fields) in or a dedicated decoder
PDVDO.swift - Expose it on struct
PortSummary - Display it in or a detail expansion in
PortRowViewContentView.swift
- 在中从IOKit属性读取原始值
IOKitReader.swift - 在或专用解码器中解码(位字段处理)
PDVDO.swift - 在结构体中暴露该属性
PortSummary - 在或
PortRowView的详情扩展中展示ContentView.swift
Handling Different Mac Generations
适配不同Mac世代
swift
let hpmServiceNames = [
"AppleHPMInterfaceType12", // M3-era
"AppleHPMInterfaceType11",
"AppleHPMInterfaceType10",
"AppleTCControllerType10", // M1 / M2
]
func findHPMService() -> io_service_t {
for name in hpmServiceNames {
let service = IOServiceGetMatchingService(
kIOMainPortDefault,
IOServiceMatching(name)
)
if service != IO_OBJECT_NULL { return service }
}
return IO_OBJECT_NULL
}swift
let hpmServiceNames = [
"AppleHPMInterfaceType12", // M3系列
"AppleHPMInterfaceType11",
"AppleHPMInterfaceType10",
"AppleTCControllerType10", // M1 / M2
]
func findHPMService() -> io_service_t {
for name in hpmServiceNames {
let service = IOServiceGetMatchingService(
kIOMainPortDefault,
IOServiceMatching(name)
)
if service != IO_OBJECT_NULL { return service }
}
return IO_OBJECT_NULL
}Debug / Engineer Mode (⌥-click)
调试/工程师模式(⌥-点击)
swift
@State private var showRawProps = false
// In button handler:
if NSEvent.modifierFlags.contains(.option) {
showRawProps.toggle()
}
// In view:
if showRawProps, let props = port.rawIOKitProperties {
RawPropertiesView(props: props)
}swift
@State private var showRawProps = false
// 在按钮处理函数中:
if NSEvent.modifierFlags.contains(.option) {
showRawProps.toggle()
}
// 在视图中:
if showRawProps, let props = port.rawIOKitProperties {
RawPropertiesView(props: props)
}Troubleshooting
故障排除
| Symptom | Likely Cause | Fix |
|---|---|---|
| No ports shown | IOKit service name mismatch | Try all |
| E-marker data missing | Cable has no e-marker chip | Expected for cables < 60W; only marked cables have VDOs |
| PDO list empty | Device not PD-capable or port is source-only | Check |
| Build fails on Intel | Architecture flag missing | Use |
| Gatekeeper warning | Ad-hoc signature only | Set |
| Wrong wattage shown | PD 3.2 EPR AVS PDO format | EPR PDOs use different bit layout; check PD 3.2 spec §6.4.1 |
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
| 无端口显示 | IOKit服务名称不匹配 | 尝试所有 |
| 电子标记数据缺失 | 线缆无电子标记芯片 | 功率<60W的线缆通常无此芯片;仅带标记的线缆有VDO |
| PDO列表为空 | 设备不支持PD或端口仅为供电端 | 在 |
| Intel设备上构建失败 | 缺少架构标识 | 使用 |
| Gatekeeper警告 | 仅为临时签名 | 在 |
| 显示功率错误 | PD 3.2 EPR AVS PDO格式 | EPR PDO使用不同的位布局;查看PD 3.2规范§6.4.1 |
Inspect IOKit Live
实时检查IOKit
bash
undefinedbash
undefinedDump all HPM interface properties
导出所有HPM接口属性
ioreg -l -n AppleHPMInterfaceType10 | less
ioreg -l -n AppleHPMInterfaceType10 | less
Watch for USB-C connect/disconnect events
监听USB-C连接/断开事件
ioreg -w 0 -l -c IOUSBDevice | grep -E "Product|Vendor|Speed"
ioreg -w 0 -l -c IOUSBDevice | grep -E "Product|Vendor|Speed"
Check power delivery objects
检查电源传输对象
ioreg -l | grep -A 20 IOPortFeaturePowerSource
undefinedioreg -l | grep -A 20 IOPortFeaturePowerSource
undefinedKey IOKit Property Names to Watch
需要关注的关键IOKit属性名称
swift
// Connection state
"Connected" // Bool
"PlugOrientation" // Int (0 = unflipped, 1 = flipped)
// Transports
"USB2" // Bool
"USB3" // Bool
"Thunderbolt" // Bool
"DisplayPort" // Bool
// E-marker
"CableEMarker" // Bool
"CableVDO" // UInt32 — raw passive cable VDO
"ActiveCableVDO1" // UInt32 — active cable VDO
// Power
"PDOs" // [UInt32]
"ActivePDOIndex" // Int
"NegotiatedWatts" // Doubleswift
// 连接状态
"Connected" // Bool
"PlugOrientation" // Int (0 = 未翻转, 1 = 已翻转)
// 传输类型
"USB2" // Bool
"USB3" // Bool
"Thunderbolt" // Bool
"DisplayPort" // Bool
// 电子标记
"CableEMarker" // Bool
"CableVDO" // UInt32 — 原始无源线缆VDO
"ActiveCableVDO1" // UInt32 — 有源线缆VDO
// 电源
"PDOs" // [UInt32]
"ActivePDOIndex" // Int
"NegotiatedWatts" // Double