161 lines
5.9 KiB
Markdown
161 lines
5.9 KiB
Markdown
# Matter QR Code Decoder
|
|
|
|
Decode the commissioning QR code that Matter / CHIP devices ship with. A Matter
|
|
device's setup QR encodes a short `MT:` URI that carries the information a
|
|
commissioning app (e.g. Apple Home, Google Home, the Matter app) needs to pair
|
|
with the device for the first time. This tool takes that URI and shows you what
|
|
is inside.
|
|
|
|
No build, no install, no package step. Two ways to use it: a browser app and a
|
|
command-line script that share the same decode logic.
|
|
|
|
## What it decodes
|
|
|
|
Given an `MT:` URI, the payload (84 bits, see [Data format](#data-format)) is
|
|
split into these fields:
|
|
|
|
| Field | Meaning |
|
|
|---|---|
|
|
| **Version** | Encoding version (3 bits) |
|
|
| **Vendor ID** | 16-bit manufacturer identifier, shown as decimal + `0x`-hex, with a name looked up from `manufacturers.tsv` when available |
|
|
| **Product ID** | 16-bit vendor-assigned product identifier, decimal + `0x`-hex |
|
|
| **Custom Flow** | Commissioning flow type: 0 = Standard, 1 = User Intent, 2 = Custom, 3 = Reserved |
|
|
| **Discovery** | How the device can be found: **SoftAP** / **BLE** / **On IP Network** (a bitset — a device may show more than one) |
|
|
| **Discriminator** | 12-bit discriminator |
|
|
| **Passcode** | 27-bit commissioning passcode |
|
|
|
|
## Components
|
|
|
|
| File | What it is |
|
|
|---|---|
|
|
| [`matter-qr-decoder.html`](matter-qr-decoder.html) | The browser app. Single self-contained HTML file (Vue 3 + Bootstrap 5.3, loaded from CDNs). |
|
|
| [`matter-decode.sh`](matter-decode.sh) | A pure-bash CLI port of the same decode logic. No camera scanning. |
|
|
| [`manufacturers.tsv`](manufacturers.tsv) | Tab-separated `vendor ID → name` table (~900 rows) used to turn a numeric vendor ID into a name. Shared by both tools; optional. |
|
|
| [`gen-manufacturers.py`](gen-manufacturers.py) | Python 3 script that regenerates `manufacturers.tsv`; pulls the upstream `manufacturers.xml` over the network by default (or a URL/local file/HTML block you pass in). |
|
|
|
|
`CLAUDE.md` documents the internals for anyone (or Claude Code) working on the
|
|
code.
|
|
|
|
---
|
|
|
|
## Browser app
|
|
|
|
### Requirements
|
|
|
|
- A modern browser with network access (the page loads Vue 3, Bootstrap 5.3, and
|
|
Bootstrap Icons from CDNs).
|
|
- Camera scanning needs a **secure context** — serve it over `HTTPS` or
|
|
`localhost`, not `file://`:
|
|
|
|
```sh
|
|
python3 -m http.server 8000
|
|
# then open http://localhost:8000/matter-qr-decoder.html
|
|
```
|
|
|
|
### Use it
|
|
|
|
1. Paste an `MT:` URI into the **Input** box — decoding happens live as you type.
|
|
2. Or click **Scan QR** and point your camera at the code. Scanning stops itself
|
|
the moment it reads an `MT:` QR.
|
|
|
|
Two caveats:
|
|
|
|
- **Scan QR is Chromium-only** (Chrome / Edge). It uses the `BarcodeDetector`
|
|
API. Firefox and Safari show a "not supported" message — **pasting a URI works
|
|
everywhere**, so use that on those browsers.
|
|
- The **Data Encoding Format** and **Discovery Capabilities Bits** sections at the
|
|
bottom of the page explain the layout shown in the output.
|
|
|
|
---
|
|
|
|
## Command line
|
|
|
|
`matter-decode.sh` decodes a URI from an argument or from stdin and prints the
|
|
same fields as the web UI:
|
|
|
|
```sh
|
|
./matter-decode.sh "MT:..."
|
|
echo "MT:..." | ./matter-decode.sh
|
|
```
|
|
|
|
Example output (run the URI through the script to reproduce):
|
|
|
|
```
|
|
$ echo "MT:O083635783973181337368400343813154179" | ./matter-decode.sh
|
|
Version: 0
|
|
Vendor ID: 13012 (0x32D4)
|
|
Product ID: 50616 (0xC5B8)
|
|
Custom Flow: User Intent Commissioning Flow
|
|
Discovery: BLE
|
|
Discriminator:1723
|
|
Passcode: 8126392
|
|
```
|
|
|
|
(The vendor ID `0x32D4` isn't in `manufacturers.tsv`, so no name is printed —
|
|
that's expected, not an error.)
|
|
|
|
The script is pure bash (3.x+). It reads `manufacturers.tsv` from its own
|
|
directory to resolve the vendor name; without that file every field still decodes
|
|
and only the vendor name is omitted.
|
|
|
|
---
|
|
|
|
## Data format
|
|
|
|
The URI is `MT:` followed by a **base38** payload (charset `0-9 A-Z - .`, 38
|
|
symbols). The payload decodes to bytes, read **little-endian**, then sliced into
|
|
the 84-bit field layout **LSB-first**:
|
|
|
|
```
|
|
version(3) | vendor ID(16) | product ID(16) | custom flow(2)
|
|
| discovery caps(8) | discriminator(12) | passcode(27)
|
|
```
|
|
|
|
Base38 decoding is greedy: 5 chars → 3 bytes, 4 → 2, 2 → 1; a length that fits
|
|
none of those groupings is an error. Both the HTML app and the shell script use
|
|
this same scheme, which is why the two stay in lockstep.
|
|
|
|
## Vendor name lookup
|
|
|
|
Both tools map a numeric **Vendor ID** to a name using
|
|
`manufacturers.tsv` (`vendor ID` in the first column, name in the second). The
|
|
table is derived from upstream
|
|
`project-chip/connectedhomeip .../zcl/data-model/manufacturers.xml`
|
|
(Apache-2.0, generated 2022-08-02). **Refresh it from upstream rather than
|
|
hand-editing individual rows.**
|
|
|
|
To refresh the table, run **`gen-manufacturers.py`** (Python 3 standard library
|
|
only). By default it **pulls the upstream `manufacturers.xml` over the network**
|
|
(via `curl`) and writes the sorted two-column TSV (`decimal id \t name`) to
|
|
`manufacturers.tsv`:
|
|
|
|
```sh
|
|
python3 gen-manufacturers.py
|
|
```
|
|
|
|
The source is pinned to the same commit the HTML block was generated from, so a
|
|
fresh pull reproduces the current `manufacturers.tsv` exactly. To refresh from a
|
|
different upstream revision, pass a source — a URL (a GitHub `blob/` URL is
|
|
auto-rewritten to its `raw.githubusercontent.com` form) or a local XML file:
|
|
|
|
```sh
|
|
python3 gen-manufacturers.py https://.../manufacturers.xml
|
|
python3 gen-manufacturers.py ./manufacturers.xml
|
|
```
|
|
|
|
No network? Use the embedded block as the source instead:
|
|
|
|
```sh
|
|
python3 gen-manufacturers.py matter-qr-decoder.html
|
|
```
|
|
|
|
The same table is also embedded as a `<map>` XML block near the end of
|
|
`matter-qr-decoder.html` (marked "do not edit manually"). This script regenerates
|
|
only the TSV, so if you refresh the TSV from new upstream XML, also paste that XML
|
|
into the HTML's `<script type="text/xml">` block to keep the two copies in sync.
|
|
|
|
## License
|
|
|
|
The vendor mapping is derived from Matter/CHIP's `manufacturers.xml`, which is
|
|
Apache-2.0. The app itself is a small single-file utility.
|