Files
matterdecode/CLAUDE.md
T
2026-08-23 20:48:38 -04:00

102 lines
6.0 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this is
A collection of Matter commissioning QR decoders (the `MT:` URI format used by
Matter/CHIP devices). Users either paste an `MT:` URI or scan a QR code; the
result shows the decoded payload (version, vendor/product IDs, commissioning
flow, discovery capabilities, discriminator, passcode). Three artifacts:
- `matter-qr-decoder.html` — the single-file browser app (Vue 3 + Bootstrap).
- `matter-decode.sh` — a pure-bash CLI port of the same decode logic (no camera
scanning; takes a URI as an argument or on stdin).
- `manufacturers.tsv` — `vendor ID → name` table (~900 rows), shared by both
tools and optional (a missing file only drops the vendor-name lookup).
- `gen-manufacturers.py` — Python 3 stdlib script that regenerates
`manufacturers.tsv`. Default source is the upstream `manufacturers.xml`
**fetched over the network** (via `curl`; a GitHub `blob/` URL is auto-rewritten
to its `raw.githubusercontent.com` form). A positional arg overrides with
another URL or a local XML file; passing `matter-qr-decoder.html` uses the
embedded `<map>` block as an offline fallback. Reproduces the committed TSV
byte-for-byte.
- `README.md` — user-facing docs; this file documents the internals.
## Commands
There is **no build, test, lint, or package step** — this is not a git repo and has no
`package.json`. The entire app is one HTML file that runs in the browser:
- Run/develop: open `matter-qr-decoder.html` in a modern browser and edit it directly.
- Camera scanning (`getUserMedia`) requires a **secure context** — serve over HTTPS or
`localhost` (e.g. `python3 -m http.server` then open `http://localhost:8000`).
- The "Scan QR" feature uses the `BarcodeDetector` API, which is **Chromium-only**
(Chrome/Edge). Firefox/Safari fall back to a "not supported" message; paste-input works everywhere.
- The page loads Vue 3, Bootstrap 5.3, and Bootstrap Icons from CDNs, so it needs network access.
## Architecture
Everything lives in `matter-qr-decoder.html` (~1500 lines). Two regions matter:
1. **App logic** (the `<script>` before `#app` is mounted, ~lines 255-559): a single
`Vue.createApp({...})` mounted to `#app`, with all state in `setup()` via `ref`/`computed`.
The UI template (lines ~26-253) is plain HTML using `v-model`/`v-if`/`v-on` bindings,
no SFC or build step.
2. **Vendor map** (`<script type="text/xml">`, ~lines 560-1495): a `<map>` of
`<mapping code="0xXXXX" translation="Name"/>` entries. This block is the bulk of the
file. It is **derived from upstream**
`project-chip/connectedhomeip .../zcl/data-model/manufacturers.xml` (Apache-2.0,
generated 2022-08-02) and carries an "do not edit manually" note — refresh it from
upstream rather than hand-editing individual rows.
### Decode pipeline (the core)
`decodeUri()` (triggered live on input) runs, in order:
1. Validate the `MT:` prefix, then take the base38 payload after it.
2. `base38Decode(str)` → `Uint8Array`. Charset is `0-9A-Z-.` (38 chars). Chunking is
**greedy**: 5 chars→3 bytes, 4→2, 2→1; a length that doesn't fit those groupings
throws `Invalid base38 string length`. Bytes are extracted **little-endian**
(`value & 0xFF`, then `>>= 8`).
3. `extractBits(bytes, startBit, bitCount)` reads the 84-bit field layout **LSB-first**.
Fixed field order/widths (see the in-page "Data Encoding Format" table for the source of truth):
version(3), vendor ID(16), product ID(16), custom flow(2), discovery caps(8),
discriminator(12), passcode(27).
4. Display helpers translate raw numbers: `getCustomFlowName`, `getDiscoveryCapabilities`
(bit flags: SoftAP / BLE / On IP Network), `formatVendorId`/`formatProductId`
(decimal + `0x`-hex padded to 4).
### Vendor name lookup
`getVendorName(id)` → `parseVendorMappings()` **lazily parses the embedded XML once**
(cached on `vendorMap`, a `let` inside `setup()`) via `DOMParser`, keyed by
`parseInt(code, 16)`. It is exposed reactively through the `vendorName` `computed`, so
the lookup re-runs when the decoded vendor ID changes.
## Gotchas
- The `BarcodeDetector` scanner polls the camera via `setInterval` every 200ms and
auto-stops the first time it sees a QR starting with `MT:`; `stopScanning()` clears
the interval **and** stops the media tracks — keep those paired when changing scanning.
- `parseVendorMappings` short-circuits on a non-empty `vendorMap`; if you add a mechanism
to update the map, clear/reset that cache or new codes won't be seen.
- The relative links `/tools/` and `/` (header/footer) imply this page is deployed as part
of a larger static site (Tommie Gannert's blog/tools), but that site isn't in this repo.
- `matter-decode.sh` is a bash port of the HTML app's decode pipeline, kept in lockstep
with it — the base38 charset/chunking, little-endian byte extraction, and the 84-bit
LSB-first field layout are identical. When you change the decode logic in the HTML app,
mirror it in the script (and vice versa). The script's only extra is a `manufacturers.tsv`
lookup for the vendor name; without that file it still decodes everything else.
- `manufacturers.tsv` is regenerated with `gen-manufacturers.py` (Python 3
stdlib). Default source is the **upstream `manufacturers.xml` fetched over the
network** (via `curl`; a GitHub `blob/` URL is auto-rewritten to its
`raw.githubusercontent.com` form). A positional arg overrides with another URL
or a local XML file, and passing `matter-qr-decoder.html` uses the embedded
`<map>` block as an **offline** fallback. The default fetch is pinned to the
same commit the HTML block came from, so it reproduces the committed TSV
byte-for-byte. Caveat: the script writes **only the TSV** — the embedded
`<script type="text/xml">` block in the HTML app has the same upstream
provenance and a "do not edit manually" note, so refreshing the TSV from new
upstream XML leaves the HTML copy stale. Refresh both from the same source.