Initial commit.
This commit is contained in:
commit
548f2e701f
7 files changed
+2978
No files matched your search
@@ -0,0 +1,160 @@
|
||||
# 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.
|
||||
Reference in new issue
Block a user