cable-station 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .DS_Store
9
+ .idea/
10
+ .vscode/
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-11-12)
4
+
5
+ First release. Merges two earlier codebases into one package: a Mac-side reader of cable e-markers through the port registry, and a Textual station around the POWER-Z KM003C tester.
6
+
7
+ - `cable-station` (the station): modes 1 tester, 2 drive, 3 dock, 4 lightning; a mode bar; preview before print; Enter, r, n, w, x, u, s, c, p keys; a highlighted table of every recorded cable.
8
+ - Thunderbolt labels TB3, TB4, and TB5 with a drawn bolt; a chip that answers with no data link reads as `PWR ONLY · no data` (data wires failed).
9
+ - Marked BETA, untested in the title, the main box, and the README.
10
+ - `cable-station snapshot [--json]`: every USB-C port's chip, link, and partner (macOS).
11
+ - `cable-station decode LOG`: the cable's e-marker from a `km003c pd capture --raw` log.
12
+ - `cable-station print ID`: reprint a recorded cable's flag.
13
+ - `cable-station doctor`: check the tools and folders.
14
+ - Records as Markdown notes with frontmatter plus a generated Obsidian Base.
15
+ - Settings from `~/.config/cable-station/config.toml` and `CABLE_STATION_*` environment variables.
16
+ - The read-only throughput test is kept as a module, off by default (`snapshot --read-test VOLUME`).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 John Randall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,202 @@
1
+ Metadata-Version: 2.5
2
+ Name: cable-station
3
+ Version: 0.1.0
4
+ Summary: BETA, untested: read USB-C cable e-markers with a POWER-Z KM003C tester or a Mac, keep one Markdown note per cable, and print a wrap-flag label for each
5
+ Project-URL: Homepage, https://github.com/johntrandall/cable-station
6
+ Project-URL: Repository, https://github.com/johntrandall/cable-station
7
+ Project-URL: Issues, https://github.com/johntrandall/cable-station/issues
8
+ Project-URL: Changelog, https://github.com/johntrandall/cable-station/blob/main/CHANGELOG.md
9
+ Author-email: John Randall <john@johnrandall.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: cable,e-marker,km003c,label,obsidian,power-z,ptouch,textual,usb-c,usb-pd
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: System :: Hardware
25
+ Classifier: Topic :: Utilities
26
+ Requires-Python: >=3.11
27
+ Requires-Dist: pillow>=10
28
+ Requires-Dist: textual<9,>=8
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
31
+ Requires-Dist: pytest>=8; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # cable-station
35
+
36
+ **BETA, untested.** Version 0.1.0 is the first public packaging of a tool that has run on one machine; expect rough edges, and check the first few labels by hand.
37
+
38
+ Read what a USB-C cable can really do, keep one note per cable, and print a small flag that says so.
39
+
40
+ **Start here.** `cable-station` is a terminal program for sorting a drawer of USB-C cables. Every full-featured USB-C cable carries an **e-marker**, a chip that states the cable's ratings: how many amps it may carry, whether it supports 240 W Extended Power Range (EPR), which data speed it is wired for, and whether it is passive or active. The station reads that chip in one of two ways, records the result as a Markdown note, and prints a two-line wrap-flag label on a Brother P-touch so the answer travels with the cable. It was built around the ChargerLAB POWER-Z KM003C, a USB-C power and Power Delivery tester, and it also works with nothing but an Apple Silicon Mac.
41
+
42
+ ## In plain terms
43
+
44
+ You have twenty black USB-C cables and no idea which one charges the laptop at full speed, which one is safe for the fast drive, and which one is a phone-charging cable that only does USB 2.0. Cables do not say. This program asks the chip inside each cable, writes down the answer, and prints a label such as `USB4 20G · USB 3.2 10G` over `100W 5A · #17` that you wrap around the cable.
45
+
46
+ - **One pass, labelled for good:** seat each cable once, press Enter, and the cable comes out of the drawer with a flag.
47
+ - **The answer comes from the cable itself:** the ratings are read from the e-marker chip, not guessed from the plug or the brand.
48
+ - **Every cable gets a number:** `#17` on the flag matches note `C17.md`, so a cable can be retested, retired, or found again.
49
+ - **Your records stay yours:** the notes are plain Markdown files in a folder you choose, readable in any editor and as a table in Obsidian.
50
+
51
+ ## What it does
52
+
53
+ The station shows one table: the pending reading on top, every recorded cable below, and the rows whose chip identity matches the pending reading highlighted. Pressing Enter opens a preview of the flag; pressing Enter again prints it and writes the note. Two paths feed the pending reading:
54
+
55
+ 1. **Tester path (primary).** The POWER-Z KM003C sits on USB with the cable under test seated in its socket. You run the tester's `USB-C eMarker` module (Application › Modules › USB-C eMarker); the station runs `km003c pd capture --raw` in the background, catches the cable's Discover Identity answer on the cable channel (SOP'), and decodes the ID Header and Cable VDO per the USB Power Delivery specification.
56
+ 2. **Mac path.** On macOS, the Mac's own port controller asks the cable the same question when a device is plugged in through it. `cable-station` reads the answer from the I/O Registry (`Port-USB-C@N / CC / SOP'`) together with the link that trained (USB 2, USB 3, Thunderbolt or USB4) and the device on the far end. Modes 2 (drive) and 3 (dock) watch the ports for a partner device you name in the config.
57
+
58
+ A cable with no chip is, by the USB-C specification, a 3 A, 60 W, USB 2.0 cable, and the label says so. A cable that connects but never carries data is labelled `PWR ONLY · no data`; when its chip still answers, the note records the claim and says the data wires failed.
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ uv tool install cable-station
64
+ ```
65
+
66
+ or, on macOS with Homebrew:
67
+
68
+ ```bash
69
+ brew install johntrandall/tap/cable-station
70
+ ```
71
+
72
+ Python 3.11 or newer, Textual 8 and Pillow are the only Python requirements. Two optional tools unlock the two optional features:
73
+
74
+ - **Tester path:** the `km003c` CLI from [okhsunrog/km003c-rs](https://github.com/okhsunrog/km003c-rs) (MIT or Apache-2.0). Build it with `cargo build --release -p km003c-cli` in a clone of that repository and put the binary on your PATH, or set `km003c = "/path/to/km003c"` in the config. The pull request [km003c-rs #67](https://github.com/okhsunrog/km003c-rs/pull/67) adds a `km003c cable` command that reads the e-marker directly; until it lands, the station uses the capture route described above.
75
+ - **Printing:** [`ptouch-label`](https://github.com/johntrandall/ptouch-label), which drives Brother PT-P750W and PT-P950NW printers over the network: `uv tool install git+https://github.com/johntrandall/ptouch-label`. Without it, the station still renders every flag as a PNG in its state folder.
76
+
77
+ `cable-station doctor` tells you which of these it can see.
78
+
79
+ ## Quick start
80
+
81
+ ```bash
82
+ cable-station doctor # what is installed, where the records go
83
+ cable-station # the station (mode 1, tester)
84
+ cable-station snapshot # macOS: every USB-C port's chip, link, and partner, right now
85
+ cable-station decode capture.log # the e-marker from a saved `km003c pd capture --raw` log
86
+ cable-station print 17 # reprint cable #17's flag
87
+ ```
88
+
89
+ A `snapshot` on a Mac with a dock on port 3 looks like this:
90
+
91
+ ```text
92
+ port 1: connected True · nothing · link DisplayPort 8.1 Gbps (HBR3) · no e-marker answered
93
+ port 2: connected False · nothing · link no link · no e-marker answered
94
+ port 3: connected True · TS5 Plus / USB3 HUB · link Thunderbolt/USB4 40 Gb/s · chip vendor 0x2B1D product 0x192B passive ('USB4 20G · USB 3.2 10G', '240W 5A EPR') VDO 0x420A4E42
95
+ ```
96
+
97
+ ## The keys
98
+
99
+ | Key | What it does |
100
+ |---|---|
101
+ | `Enter` | Preview the pending reading's flag; `Enter` again prints it with a new number and writes the note. With nothing pending, reprints the highlighted row. |
102
+ | `r` | Reprint the highlighted cable's flag (no new note). |
103
+ | `1` `2` `3` `4` | Mode: `1` tester (readings come from the POWER-Z capture), `2` drive, `3` dock (the Mac reads the cable plugged between it and the named partner), `4` lightning (an iPhone or iPad enumerating on the Mac is the reading). |
104
+ | `c` `p` | In lightning mode: `c` cycles the other end's plug (Lightning, USB-A to Lightning, USB-C to Lightning); `p` stages a power-only Lightning cable. |
105
+ | `n` | Stage the seated cable as chipless and untested (`no eMarker · USB 2.0 480M` over `60W 3A`); `Enter` prints it. |
106
+ | `w` | Write the pending reading into the highlighted row, a retest that keeps the number; press `w` twice. |
107
+ | `x` | Decommission the highlighted cable (broken or discarded); the record and number are kept; press `x` twice. `u` undoes it. |
108
+ | `s` | Discard the pending reading. |
109
+ | `q` | Quit; the capture stops with the station. |
110
+
111
+ Green rows share the pending reading's chip identity (vendor, product, cable VDO): the same cable again, or an identical one. Cyan rows are chipless cables of the same speed tier, which have no identity to compare.
112
+
113
+ ## The label
114
+
115
+ Two lines on 9 mm tape, printed twice on one strip with a 20 mm wrap zone and a dotted fold line between, so both faces of the flag read the same.
116
+
117
+ | Line | Content | Examples |
118
+ |---|---|---|
119
+ | 1, data | Every mode the cable supports, highest first, separated by a middle dot, then `Active` on an active cable; a Thunderbolt link trained on the Mac names the generation with a bolt | `USB 2.0 480M` · `USB 3.2 5G` · `USB4 20G · USB 3.2 10G` · `USB4 ≥40G · USB 3.2 10G Active` · `TB4 ⚡ 40G · USB 3.2 10G` |
120
+ | 2, power | Watts, amps, `EPR` on 240 W cables, then the cable's number | `60W 3A · #3` · `100W 5A · #17` · `240W 5A EPR · #21` |
121
+ | no chip | Line 1 names the fact and the tested data speed; line 2 the specification default | `no eMarker · USB 2.0 480M` / `60W 3A · #8` |
122
+ | power only | A cable that charges but never carries data | `PWR ONLY · no data` / `60W 3A · #9` |
123
+
124
+ `≥40G` means the chip's speed code says USB4 Gen 3: verified to 40G and possibly higher, until an 80G cable has been read on this firmware. Watts come first because that is the question at the drawer; amps second because chargers and docks talk in amps. Nothing else goes on the flag: a missing marker means passive and not EPR.
125
+
126
+ ## The data layout
127
+
128
+ Records live in the data folder (default `~/cable-station/cables`), one Markdown note per cable named `C<N>.md`. The frontmatter holds the fields; the body is free text.
129
+
130
+ ```markdown
131
+ ---
132
+ type: cable
133
+ id: C17
134
+ tested: 2026-11-12
135
+ connector: USB-C to USB-C
136
+ data_label: "USB4 20G · USB 3.2 10G"
137
+ power_label: "100W 5A · #17"
138
+ current_a: 5
139
+ epr: false
140
+ speed: "USB4 20G · USB 3.2 10G"
141
+ active: false
142
+ emarker: true
143
+ emarker_read: true
144
+ vendor: 0x2B1D
145
+ product: 0x192B
146
+ cable_vdo: 0x00084042
147
+ status: active
148
+ ---
149
+
150
+ # C17 · USB4 20G · USB 3.2 10G · 100W 5A · #17
151
+ ```
152
+
153
+ Beside the notes the station keeps `_cables.base`, an [Obsidian Base](https://help.obsidian.md/bases) that shows the folder as a table (view `1 · CABLES`). Point an Obsidian vault at the folder, or make the folder part of one. The station's own files (the capture log, each cable's raw capture, the rendered flag PNGs) go to the state folder, default `~/.local/state/cable-station`, and are not records.
154
+
155
+ ## Configuration
156
+
157
+ Settings come from `~/.config/cable-station/config.toml`, overridden by `CABLE_STATION_*` environment variables. Every key is optional.
158
+
159
+ ```toml
160
+ data_dir = "~/cable-station/cables" # the records (CABLE_STATION_DATA)
161
+ state_dir = "~/.local/state/cable-station" # logs, captures, previews (CABLE_STATION_STATE)
162
+ printer_model = "PT-P950NW" # ptouch-label --model (CABLE_STATION_PRINTER)
163
+ tape_mm = 9 # 6, 9, 12, 18 or 24 (CABLE_STATION_TAPE)
164
+ km003c = "km003c" # the tester CLI (CABLE_STATION_KM003C)
165
+ capture_hours = 3 # one capture run before it restarts
166
+ connector = "USB-C to USB-C" # recorded on new cables
167
+ vertical_offset_mm = 1.45 # your printer's vertical calibration offset
168
+
169
+ [partners]
170
+ drive = ["Envoy", "Sabrent"] # mode 2 watches ports where one of these appears (CABLE_STATION_DRIVE_PARTNERS)
171
+ dock = ["TS5"] # mode 3 (CABLE_STATION_DOCK_PARTNERS)
172
+ ```
173
+
174
+ Partner names are matched against what macOS reports for the device on the far end of the cable. With no partners configured, modes 2 and 3 watch every port that gains a device.
175
+
176
+ ## Limits
177
+
178
+ - **No throughput test yet.** A read-only "real work" test through the cable exists as a module (`cable-station snapshot --read-test VOLUME`) but is experimental and does not feed the label; its floors are provisional.
179
+ - **No power test yet.** The label's watts are what the chip claims. A worn cable still claims its rating; a voltage-drop test under load is planned, not built.
180
+ - **The Mac reader is macOS only.** It reads the I/O Registry. On Linux the tester path, the records, and printing all work.
181
+ - **The tester's eMarker module is a button press.** The KM003C sends the Discover Identity only while its `USB-C eMarker` screen is open, so each cable is one hands-on step on the tester. The vendor's serial protocol-trigger route does not reach the cable in the socket; a direct read is the subject of km003c-rs pull request #67.
182
+ - **Chipless does not mean USB 2.0.** Only the tester proves "no e-marker"; the Mac proves the link that trained. A Mac-only reading of a 20G or faster link proves a chip exists and is recorded as `emarker_read: false` until the tester reads it.
183
+ - **Verified on one machine.** The PD 2.0 versus 3.x layout of the cable VDO is chosen from ioreg's `Specification Revision`; checked on an M3 Max MacBook Pro running macOS 15, not against every Mac.
184
+
185
+ ## Status
186
+
187
+ Beta, untested beyond one machine. The station has labelled one household's cable drawer; the vocabulary and keys are settled, the packaging is new. Next: the direct `km003c cable` read once km003c-rs #67 lands, a power test, and the throughput test as an opt-in.
188
+
189
+ ## Credits
190
+
191
+ - [ChargerLAB POWER-Z KM003C](https://www.chargerlab.com/km003c-km002c-technical-support), the tester.
192
+ - [okhsunrog/km003c-rs](https://github.com/okhsunrog/km003c-rs) (MIT or Apache-2.0), the open-source driver and CLI for the tester, and [okhsunrog/km003c-protocol-research](https://github.com/okhsunrog/km003c-protocol-research), the protocol reference.
193
+ - [ptouch-label](https://github.com/johntrandall/ptouch-label), the P-touch printing tool.
194
+ - [Textual](https://textual.textualize.io/) for the terminal interface and [Pillow](https://python-pillow.org/) for the flag renderer.
195
+
196
+ ## Contributing
197
+
198
+ Issues and pull requests at [github.com/johntrandall/cable-station](https://github.com/johntrandall/cable-station/issues). A capture log from a cable the decoder gets wrong is the most useful bug report: `km003c pd capture --raw --duration 60 > capture.log` while you open the tester's eMarker module, then attach the log.
199
+
200
+ ## License
201
+
202
+ [MIT](https://github.com/johntrandall/cable-station/blob/main/LICENSE), John Randall.
@@ -0,0 +1,169 @@
1
+ # cable-station
2
+
3
+ **BETA, untested.** Version 0.1.0 is the first public packaging of a tool that has run on one machine; expect rough edges, and check the first few labels by hand.
4
+
5
+ Read what a USB-C cable can really do, keep one note per cable, and print a small flag that says so.
6
+
7
+ **Start here.** `cable-station` is a terminal program for sorting a drawer of USB-C cables. Every full-featured USB-C cable carries an **e-marker**, a chip that states the cable's ratings: how many amps it may carry, whether it supports 240 W Extended Power Range (EPR), which data speed it is wired for, and whether it is passive or active. The station reads that chip in one of two ways, records the result as a Markdown note, and prints a two-line wrap-flag label on a Brother P-touch so the answer travels with the cable. It was built around the ChargerLAB POWER-Z KM003C, a USB-C power and Power Delivery tester, and it also works with nothing but an Apple Silicon Mac.
8
+
9
+ ## In plain terms
10
+
11
+ You have twenty black USB-C cables and no idea which one charges the laptop at full speed, which one is safe for the fast drive, and which one is a phone-charging cable that only does USB 2.0. Cables do not say. This program asks the chip inside each cable, writes down the answer, and prints a label such as `USB4 20G · USB 3.2 10G` over `100W 5A · #17` that you wrap around the cable.
12
+
13
+ - **One pass, labelled for good:** seat each cable once, press Enter, and the cable comes out of the drawer with a flag.
14
+ - **The answer comes from the cable itself:** the ratings are read from the e-marker chip, not guessed from the plug or the brand.
15
+ - **Every cable gets a number:** `#17` on the flag matches note `C17.md`, so a cable can be retested, retired, or found again.
16
+ - **Your records stay yours:** the notes are plain Markdown files in a folder you choose, readable in any editor and as a table in Obsidian.
17
+
18
+ ## What it does
19
+
20
+ The station shows one table: the pending reading on top, every recorded cable below, and the rows whose chip identity matches the pending reading highlighted. Pressing Enter opens a preview of the flag; pressing Enter again prints it and writes the note. Two paths feed the pending reading:
21
+
22
+ 1. **Tester path (primary).** The POWER-Z KM003C sits on USB with the cable under test seated in its socket. You run the tester's `USB-C eMarker` module (Application › Modules › USB-C eMarker); the station runs `km003c pd capture --raw` in the background, catches the cable's Discover Identity answer on the cable channel (SOP'), and decodes the ID Header and Cable VDO per the USB Power Delivery specification.
23
+ 2. **Mac path.** On macOS, the Mac's own port controller asks the cable the same question when a device is plugged in through it. `cable-station` reads the answer from the I/O Registry (`Port-USB-C@N / CC / SOP'`) together with the link that trained (USB 2, USB 3, Thunderbolt or USB4) and the device on the far end. Modes 2 (drive) and 3 (dock) watch the ports for a partner device you name in the config.
24
+
25
+ A cable with no chip is, by the USB-C specification, a 3 A, 60 W, USB 2.0 cable, and the label says so. A cable that connects but never carries data is labelled `PWR ONLY · no data`; when its chip still answers, the note records the claim and says the data wires failed.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ uv tool install cable-station
31
+ ```
32
+
33
+ or, on macOS with Homebrew:
34
+
35
+ ```bash
36
+ brew install johntrandall/tap/cable-station
37
+ ```
38
+
39
+ Python 3.11 or newer, Textual 8 and Pillow are the only Python requirements. Two optional tools unlock the two optional features:
40
+
41
+ - **Tester path:** the `km003c` CLI from [okhsunrog/km003c-rs](https://github.com/okhsunrog/km003c-rs) (MIT or Apache-2.0). Build it with `cargo build --release -p km003c-cli` in a clone of that repository and put the binary on your PATH, or set `km003c = "/path/to/km003c"` in the config. The pull request [km003c-rs #67](https://github.com/okhsunrog/km003c-rs/pull/67) adds a `km003c cable` command that reads the e-marker directly; until it lands, the station uses the capture route described above.
42
+ - **Printing:** [`ptouch-label`](https://github.com/johntrandall/ptouch-label), which drives Brother PT-P750W and PT-P950NW printers over the network: `uv tool install git+https://github.com/johntrandall/ptouch-label`. Without it, the station still renders every flag as a PNG in its state folder.
43
+
44
+ `cable-station doctor` tells you which of these it can see.
45
+
46
+ ## Quick start
47
+
48
+ ```bash
49
+ cable-station doctor # what is installed, where the records go
50
+ cable-station # the station (mode 1, tester)
51
+ cable-station snapshot # macOS: every USB-C port's chip, link, and partner, right now
52
+ cable-station decode capture.log # the e-marker from a saved `km003c pd capture --raw` log
53
+ cable-station print 17 # reprint cable #17's flag
54
+ ```
55
+
56
+ A `snapshot` on a Mac with a dock on port 3 looks like this:
57
+
58
+ ```text
59
+ port 1: connected True · nothing · link DisplayPort 8.1 Gbps (HBR3) · no e-marker answered
60
+ port 2: connected False · nothing · link no link · no e-marker answered
61
+ port 3: connected True · TS5 Plus / USB3 HUB · link Thunderbolt/USB4 40 Gb/s · chip vendor 0x2B1D product 0x192B passive ('USB4 20G · USB 3.2 10G', '240W 5A EPR') VDO 0x420A4E42
62
+ ```
63
+
64
+ ## The keys
65
+
66
+ | Key | What it does |
67
+ |---|---|
68
+ | `Enter` | Preview the pending reading's flag; `Enter` again prints it with a new number and writes the note. With nothing pending, reprints the highlighted row. |
69
+ | `r` | Reprint the highlighted cable's flag (no new note). |
70
+ | `1` `2` `3` `4` | Mode: `1` tester (readings come from the POWER-Z capture), `2` drive, `3` dock (the Mac reads the cable plugged between it and the named partner), `4` lightning (an iPhone or iPad enumerating on the Mac is the reading). |
71
+ | `c` `p` | In lightning mode: `c` cycles the other end's plug (Lightning, USB-A to Lightning, USB-C to Lightning); `p` stages a power-only Lightning cable. |
72
+ | `n` | Stage the seated cable as chipless and untested (`no eMarker · USB 2.0 480M` over `60W 3A`); `Enter` prints it. |
73
+ | `w` | Write the pending reading into the highlighted row, a retest that keeps the number; press `w` twice. |
74
+ | `x` | Decommission the highlighted cable (broken or discarded); the record and number are kept; press `x` twice. `u` undoes it. |
75
+ | `s` | Discard the pending reading. |
76
+ | `q` | Quit; the capture stops with the station. |
77
+
78
+ Green rows share the pending reading's chip identity (vendor, product, cable VDO): the same cable again, or an identical one. Cyan rows are chipless cables of the same speed tier, which have no identity to compare.
79
+
80
+ ## The label
81
+
82
+ Two lines on 9 mm tape, printed twice on one strip with a 20 mm wrap zone and a dotted fold line between, so both faces of the flag read the same.
83
+
84
+ | Line | Content | Examples |
85
+ |---|---|---|
86
+ | 1, data | Every mode the cable supports, highest first, separated by a middle dot, then `Active` on an active cable; a Thunderbolt link trained on the Mac names the generation with a bolt | `USB 2.0 480M` · `USB 3.2 5G` · `USB4 20G · USB 3.2 10G` · `USB4 ≥40G · USB 3.2 10G Active` · `TB4 ⚡ 40G · USB 3.2 10G` |
87
+ | 2, power | Watts, amps, `EPR` on 240 W cables, then the cable's number | `60W 3A · #3` · `100W 5A · #17` · `240W 5A EPR · #21` |
88
+ | no chip | Line 1 names the fact and the tested data speed; line 2 the specification default | `no eMarker · USB 2.0 480M` / `60W 3A · #8` |
89
+ | power only | A cable that charges but never carries data | `PWR ONLY · no data` / `60W 3A · #9` |
90
+
91
+ `≥40G` means the chip's speed code says USB4 Gen 3: verified to 40G and possibly higher, until an 80G cable has been read on this firmware. Watts come first because that is the question at the drawer; amps second because chargers and docks talk in amps. Nothing else goes on the flag: a missing marker means passive and not EPR.
92
+
93
+ ## The data layout
94
+
95
+ Records live in the data folder (default `~/cable-station/cables`), one Markdown note per cable named `C<N>.md`. The frontmatter holds the fields; the body is free text.
96
+
97
+ ```markdown
98
+ ---
99
+ type: cable
100
+ id: C17
101
+ tested: 2026-11-12
102
+ connector: USB-C to USB-C
103
+ data_label: "USB4 20G · USB 3.2 10G"
104
+ power_label: "100W 5A · #17"
105
+ current_a: 5
106
+ epr: false
107
+ speed: "USB4 20G · USB 3.2 10G"
108
+ active: false
109
+ emarker: true
110
+ emarker_read: true
111
+ vendor: 0x2B1D
112
+ product: 0x192B
113
+ cable_vdo: 0x00084042
114
+ status: active
115
+ ---
116
+
117
+ # C17 · USB4 20G · USB 3.2 10G · 100W 5A · #17
118
+ ```
119
+
120
+ Beside the notes the station keeps `_cables.base`, an [Obsidian Base](https://help.obsidian.md/bases) that shows the folder as a table (view `1 · CABLES`). Point an Obsidian vault at the folder, or make the folder part of one. The station's own files (the capture log, each cable's raw capture, the rendered flag PNGs) go to the state folder, default `~/.local/state/cable-station`, and are not records.
121
+
122
+ ## Configuration
123
+
124
+ Settings come from `~/.config/cable-station/config.toml`, overridden by `CABLE_STATION_*` environment variables. Every key is optional.
125
+
126
+ ```toml
127
+ data_dir = "~/cable-station/cables" # the records (CABLE_STATION_DATA)
128
+ state_dir = "~/.local/state/cable-station" # logs, captures, previews (CABLE_STATION_STATE)
129
+ printer_model = "PT-P950NW" # ptouch-label --model (CABLE_STATION_PRINTER)
130
+ tape_mm = 9 # 6, 9, 12, 18 or 24 (CABLE_STATION_TAPE)
131
+ km003c = "km003c" # the tester CLI (CABLE_STATION_KM003C)
132
+ capture_hours = 3 # one capture run before it restarts
133
+ connector = "USB-C to USB-C" # recorded on new cables
134
+ vertical_offset_mm = 1.45 # your printer's vertical calibration offset
135
+
136
+ [partners]
137
+ drive = ["Envoy", "Sabrent"] # mode 2 watches ports where one of these appears (CABLE_STATION_DRIVE_PARTNERS)
138
+ dock = ["TS5"] # mode 3 (CABLE_STATION_DOCK_PARTNERS)
139
+ ```
140
+
141
+ Partner names are matched against what macOS reports for the device on the far end of the cable. With no partners configured, modes 2 and 3 watch every port that gains a device.
142
+
143
+ ## Limits
144
+
145
+ - **No throughput test yet.** A read-only "real work" test through the cable exists as a module (`cable-station snapshot --read-test VOLUME`) but is experimental and does not feed the label; its floors are provisional.
146
+ - **No power test yet.** The label's watts are what the chip claims. A worn cable still claims its rating; a voltage-drop test under load is planned, not built.
147
+ - **The Mac reader is macOS only.** It reads the I/O Registry. On Linux the tester path, the records, and printing all work.
148
+ - **The tester's eMarker module is a button press.** The KM003C sends the Discover Identity only while its `USB-C eMarker` screen is open, so each cable is one hands-on step on the tester. The vendor's serial protocol-trigger route does not reach the cable in the socket; a direct read is the subject of km003c-rs pull request #67.
149
+ - **Chipless does not mean USB 2.0.** Only the tester proves "no e-marker"; the Mac proves the link that trained. A Mac-only reading of a 20G or faster link proves a chip exists and is recorded as `emarker_read: false` until the tester reads it.
150
+ - **Verified on one machine.** The PD 2.0 versus 3.x layout of the cable VDO is chosen from ioreg's `Specification Revision`; checked on an M3 Max MacBook Pro running macOS 15, not against every Mac.
151
+
152
+ ## Status
153
+
154
+ Beta, untested beyond one machine. The station has labelled one household's cable drawer; the vocabulary and keys are settled, the packaging is new. Next: the direct `km003c cable` read once km003c-rs #67 lands, a power test, and the throughput test as an opt-in.
155
+
156
+ ## Credits
157
+
158
+ - [ChargerLAB POWER-Z KM003C](https://www.chargerlab.com/km003c-km002c-technical-support), the tester.
159
+ - [okhsunrog/km003c-rs](https://github.com/okhsunrog/km003c-rs) (MIT or Apache-2.0), the open-source driver and CLI for the tester, and [okhsunrog/km003c-protocol-research](https://github.com/okhsunrog/km003c-protocol-research), the protocol reference.
160
+ - [ptouch-label](https://github.com/johntrandall/ptouch-label), the P-touch printing tool.
161
+ - [Textual](https://textual.textualize.io/) for the terminal interface and [Pillow](https://python-pillow.org/) for the flag renderer.
162
+
163
+ ## Contributing
164
+
165
+ Issues and pull requests at [github.com/johntrandall/cable-station](https://github.com/johntrandall/cable-station/issues). A capture log from a cable the decoder gets wrong is the most useful bug report: `km003c pd capture --raw --duration 60 > capture.log` while you open the tester's eMarker module, then attach the log.
166
+
167
+ ## License
168
+
169
+ [MIT](https://github.com/johntrandall/cable-station/blob/main/LICENSE), John Randall.
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "cable-station"
3
+ version = "0.1.0"
4
+ description = "BETA, untested: read USB-C cable e-markers with a POWER-Z KM003C tester or a Mac, keep one Markdown note per cable, and print a wrap-flag label for each"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "John Randall", email = "john@johnrandall.com" }]
9
+ keywords = ["usb-c", "e-marker", "usb-pd", "cable", "label", "km003c", "power-z", "ptouch", "textual", "obsidian"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Environment :: Console",
13
+ "Intended Audience :: End Users/Desktop",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Operating System :: MacOS",
16
+ "Operating System :: POSIX :: Linux",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Topic :: System :: Hardware",
23
+ "Topic :: Utilities",
24
+ ]
25
+ dependencies = [
26
+ "textual>=8,<9",
27
+ "pillow>=10",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ dev = ["pytest>=8", "pytest-asyncio>=0.23"]
32
+
33
+ [project.scripts]
34
+ cable-station = "cable_station.cli:main"
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/johntrandall/cable-station"
38
+ Repository = "https://github.com/johntrandall/cable-station"
39
+ Issues = "https://github.com/johntrandall/cable-station/issues"
40
+ Changelog = "https://github.com/johntrandall/cable-station/blob/main/CHANGELOG.md"
41
+
42
+ [build-system]
43
+ requires = ["hatchling"]
44
+ build-backend = "hatchling.build"
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ packages = ["src/cable_station"]
48
+
49
+ [tool.hatch.build.targets.sdist]
50
+ include = ["src/cable_station", "tests", "README.md", "LICENSE", "CHANGELOG.md", "pyproject.toml"]
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]
54
+ asyncio_mode = "auto"
55
+ asyncio_default_fixture_loop_scope = "function"
@@ -0,0 +1,3 @@
1
+ """cable-station: read USB-C cable e-markers with a POWER-Z KM003C or a Mac, record each cable, and print a wrap-flag."""
2
+
3
+ __version__ = "0.1.0"