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

6.0 KiB

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.