Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Whatcable Macos Usb Inspector

ASecurity

macOS menu bar app that identifies USB-C cable capabilities and charging diagnostics using IOKit

81 stars
0 votes
0 copies
0 views
Added 9/19/2026
developmentgoswiftbash

Works with

cli

Security Analysis

A100/100

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add reason-machines/trending-skills --skill whatcable-macos-usb-inspector --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Whatcable Macos Usb Inspector?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Whatcable Macos Usb Inspector
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/reason-machines-whatcable-macos-usb-inspector/badge)](https://www.skillsdirectory.com/skills/reason-machines-whatcable-macos-usb-inspector)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: whatcable-macos-usb-inspector
description: macOS menu bar app that identifies USB-C cable capabilities and charging diagnostics using IOKit
triggers:
  - "add whatcable feature"
  - "decode USB-C cable info"
  - "read IOKit USB port data"
  - "parse power delivery PDO"
  - "add port summary logic"
  - "whatcable menu bar app"
  - "USB-C e-marker decoding swift"
  - "thunderbolt cable detection macos"
---

# WhatCable macOS USB-C Inspector

> Skill by [ara.so](https://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.

## 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 + notarisation
```

## Install / Build

```bash
# Run locally (development)
swift run WhatCable

# Build distributable universal app (arm64 + x86_64)
./scripts/build-app.sh
# → dist/WhatCable.app
# → dist/WhatCable.zip
```

Requires Swift 5.9 / Xcode 15+, macOS 14 (Sonoma) or later.

### Signed + Notarised Build

```bash
cp .env.example .env
# Edit .env:
# DEVELOPER_ID="Developer ID Application: Your Name (TEAMID)"
# NOTARY_PROFILE="WhatCable-notary"

# Store notarytool credentials once
xcrun notarytool store-credentials "WhatCable-notary" \
    --apple-id "$APPLE_ID" \
    --team-id "$TEAM_ID" \
    --password "$APP_SPECIFIC_PASSWORD"

./scripts/build-app.sh
```

## Key Concepts

### IOKit Service Families

| Service | Purpose |
|---|---|
| `AppleHPMInterfaceType10/11/12` (M3+) | Per-port state, transports, plug orientation, e-marker presence |
| `AppleTCControllerType10` (M1/M2) | Same, older HPM interface |
| `IOPortFeaturePowerSource` | Full PDO list + live negotiated PDO |
| `IOPortTransportComponentCCUSBPDSOP` | PD Discover Identity VDOs (SOP = partner, SOP' = cable e-marker) |

### USB PD VDO Decoding (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"
        }
    }
}
```

### Reading IOKit Properties

```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)

```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
        )
    }
}
```

### PDO Parsing (Power Data Objects)

```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)
    }
}
```

### SwiftUI Popover Pattern (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"
        }
    }
}
```

### Menu Bar App Setup (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)
            }
        }
    }
}
```

### 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)
    }
}
```

## Common Patterns

### Adding a New Port Property

1. Read the raw value from IOKit props in `IOKitReader.swift`
2. Decode it (bit-fields) in `PDVDO.swift` or a dedicated decoder
3. Expose it on `PortSummary` struct
4. Display it in `PortRowView` or a detail expansion in `ContentView.swift`

### Handling Different Mac Generations

```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
}
```

### 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)
}
```

## Troubleshooting

| Symptom | Likely Cause | Fix |
|---|---|---|
| No ports shown | IOKit service name mismatch | Try all `hpmServiceNames` variants; check `ioreg -l` output |
| 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 `IOPortFeaturePowerSource` presence in `ioreg` |
| Build fails on Intel | Architecture flag missing | Use `./scripts/build-app.sh` for universal build |
| Gatekeeper warning | Ad-hoc signature only | Set `DEVELOPER_ID` in `.env` and re-run build script |
| Wrong wattage shown | PD 3.2 EPR AVS PDO format | EPR PDOs use different bit layout; check PD 3.2 spec §6.4.1 |

### Inspect IOKit Live

```bash
# Dump all HPM interface properties
ioreg -l -n AppleHPMInterfaceType10 | less

# Watch for USB-C connect/disconnect events
ioreg -w 0 -l -c IOUSBDevice | grep -E "Product|Vendor|Speed"

# Check power delivery objects
ioreg -l | grep -A 20 IOPortFeaturePowerSource
```

### Key IOKit Property Names to Watch

```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"        // Double
```

Attribution

reason-machinesreason-machines
View sourceMore from reason-machines →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

281612 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2132 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →