Added homekit-decode.sh to the mix, and updated the readmes.
This commit is contained in:
1 parent
27ebf13f6f
commit
589ebaff46
3 files changed
+131
-12
No files matched your search
@@ -5,13 +5,20 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
## What this is
|
## What this is
|
||||||
|
|
||||||
A collection of Matter commissioning QR decoders (the `MT:` URI format used by
|
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
|
Matter/CHIP devices), plus a standalone bash decoder for Apple **HomeKit**
|
||||||
result shows the decoded payload (version, vendor/product IDs, commissioning
|
setup codes (the `X-HM://` payload — a different on-wire format, in
|
||||||
flow, discovery capabilities, discriminator, passcode). Three artifacts:
|
`homekit-decode.sh`). For Matter, 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). Key files:
|
||||||
|
|
||||||
- `matter-qr-decoder.html` — the single-file browser app (Vue 3 + Bootstrap).
|
- `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
|
- `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).
|
scanning; takes a URI as an argument or on stdin).
|
||||||
|
- `homekit-decode.sh` — a standalone pure-bash decoder for Apple **HomeKit**
|
||||||
|
setup codes (the `X-HM://` payload an accessory shows on first setup — a
|
||||||
|
*different* format from Matter's `MT:`). No browser counterpart and no
|
||||||
|
companion TSV (the accessory-category table is inlined). See the
|
||||||
|
HomeKit setup-code decode section below.
|
||||||
- `manufacturers.tsv` — `vendor ID → name` table (~900 rows), shared by both
|
- `manufacturers.tsv` — `vendor ID → name` table (~900 rows), shared by both
|
||||||
tools and optional (a missing file only drops the vendor-name lookup).
|
tools and optional (a missing file only drops the vendor-name lookup).
|
||||||
- `gen-manufacturers.py` — Python 3 stdlib script that regenerates
|
- `gen-manufacturers.py` — Python 3 stdlib script that regenerates
|
||||||
@@ -151,6 +158,45 @@ Two helpers, defined just above `main()`:
|
|||||||
(user-intent `1`, custom `2`) append 5-digit vendor and product groups. Result:
|
(user-intent `1`, custom `2`) append 5-digit vendor and product groups. Result:
|
||||||
11 digits for the standard flow, 21 otherwise, plus the check digit.
|
11 digits for the standard flow, 21 otherwise, plus the check digit.
|
||||||
|
|
||||||
|
### HomeKit setup-code decode (bash only)
|
||||||
|
|
||||||
|
`homekit-decode.sh` is a *second, independent* decoder — for Apple **HomeKit**
|
||||||
|
setup codes, the `X-HM://` payload an accessory (or its packaging) shows on first
|
||||||
|
setup. That is a different on-wire format from Matter's `MT:`, so this is **not**
|
||||||
|
a port of the HTML app's pipeline; it is standalone, with no browser counterpart
|
||||||
|
and no companion TSV (the accessory-category table is inlined). It sits alongside
|
||||||
|
`matter-decode.sh` as the repo's other CLI decoder.
|
||||||
|
|
||||||
|
Input is an optional positional argument, defaulting to the demo payload
|
||||||
|
`005KEOUCE`. A leading `X-HM://` is stripped if present. The first 9 characters
|
||||||
|
are the fixed parameter block; anything after them is a variable-length **setup
|
||||||
|
id**, carried through verbatim (it is not part of the numeric block).
|
||||||
|
|
||||||
|
Unlike Matter's **base38** payload (charset `0-9 A-Z - .`, 38 symbols), the
|
||||||
|
HomeKit block is **base36** (`0-9 A-Z`, 36 symbols): each char does
|
||||||
|
`val = val*36 + digit`, and an out-of-range character is a hard error (to
|
||||||
|
stderr, exit 1). `val` is then sliced **LSB-first** into the HomeKit layout:
|
||||||
|
|
||||||
|
pin(27) | flag(4) | categoryId(8) | reserved(4) | version(3)
|
||||||
|
|
||||||
|
- `flag` is a 4-bit sub-flag set — `1`=NFC, `2`=IP, `4`=BLE, `8`=WAC/MFi —
|
||||||
|
printed as True/False lines.
|
||||||
|
- `categoryId` is resolved to its HAP `homekit_accessory_category_*` name (e.g.
|
||||||
|
`5` → "Lightbulb", `8` → "Switch") via an inlined `case`; an id not in the
|
||||||
|
table prints "unknown" (id `25` is reserved by HomeKit, so it is unknown by
|
||||||
|
design, and "Heater" corrects a "Header" typo in the source table). A `case`
|
||||||
|
rather than an associative array is used on purpose so the script runs on the
|
||||||
|
stock macOS bash 3.2 that `#!/usr/bin/env bash` resolves to.
|
||||||
|
- The decoded fields are packed back into a 9-char block (mirroring
|
||||||
|
`composeSetupUri`'s stepwise packing) and printed as a reconstructed
|
||||||
|
`X-HM://…` payload, so the output is a complete record the barcode can be
|
||||||
|
rebuilt from.
|
||||||
|
|
||||||
|
This is the **corrected** bit layout — the one `composeSetupUri` in the
|
||||||
|
`homekit-code` package actually encodes — rather than a misaligned earlier one.
|
||||||
|
The header comments in `homekit-decode.sh` record each fix (the categoryId / flag
|
||||||
|
/ version positions, and the dropped "Thread" misread of categoryId bits).
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
- The `BarcodeDetector` scanner polls the camera via `setInterval` every 200ms and
|
- The `BarcodeDetector` scanner polls the camera via `setInterval` every 200ms and
|
||||||
@@ -166,6 +212,11 @@ Two helpers, defined just above `main()`:
|
|||||||
mirror it in the script (and vice versa). The script's extras are the optional `manufacturers.tsv` (vendor name),
|
mirror it in the script (and vice versa). The script's extras are the optional `manufacturers.tsv` (vendor name),
|
||||||
`products.tsv` (product name), and `vendor-equivalents.tsv` (vendor
|
`products.tsv` (product name), and `vendor-equivalents.tsv` (vendor
|
||||||
equivalences); without any of them it still decodes everything else.
|
equivalences); without any of them it still decodes everything else.
|
||||||
|
- `homekit-decode.sh` is **not** part of that lockstep: it decodes a *different*
|
||||||
|
on-wire format (Apple HomeKit `X-HM://`, **base36** — not Matter's base38) and
|
||||||
|
has no HTML-app counterpart. Don't try to unify the two decoders or mirror
|
||||||
|
HomeKit changes into the HTML app; when editing it, keep the corrected bit layout
|
||||||
|
(`pin / flag / categoryId / reserved / version`) as the source of truth.
|
||||||
- `manufacturers.tsv` is regenerated with `gen-manufacturers.py` (Python 3
|
- `manufacturers.tsv` is regenerated with `gen-manufacturers.py` (Python 3
|
||||||
stdlib). Default source is the **upstream `manufacturers.xml` fetched over the
|
stdlib). Default source is the **upstream `manufacturers.xml` fetched over the
|
||||||
network** (via `curl`; a GitHub `blob/` URL is auto-rewritten to its
|
network** (via `curl`; a GitHub `blob/` URL is auto-rewritten to its
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ is inside.
|
|||||||
No build, no install, no package step. Two ways to use it: a browser app and a
|
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.
|
command-line script that share the same decode logic.
|
||||||
|
|
||||||
|
There is also a separate, standalone command-line decoder for Apple **HomeKit**
|
||||||
|
setup codes — the `X-HM://` payload an accessory shows on first setup. That is a
|
||||||
|
different format from Matter's `MT:` (see [HomeKit setup-code decoder](#homekit-setup-code-decoder));
|
||||||
|
it has no browser counterpart and no companion tables.
|
||||||
|
|
||||||
## What it decodes
|
## What it decodes
|
||||||
|
|
||||||
Given an `MT:` URI, the payload (84 bits, see [Data format](#data-format)) is
|
Given an `MT:` URI, the payload (84 bits, see [Data format](#data-format)) is
|
||||||
@@ -31,6 +36,7 @@ split into these fields:
|
|||||||
|---|---|
|
|---|---|
|
||||||
| [`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-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. |
|
| [`matter-decode.sh`](matter-decode.sh) | A pure-bash CLI port of the same decode logic. No camera scanning. |
|
||||||
|
| [`homekit-decode.sh`](homekit-decode.sh) | A standalone pure-bash decoder for Apple **HomeKit** setup codes (the `X-HM://` payload) — a different format from Matter's `MT:`, with no browser counterpart and no companion tables. |
|
||||||
| [`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. |
|
| [`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). |
|
| [`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). |
|
||||||
| [`products.tsv`](products.tsv) | Tab-separated `vendor ID → product ID → name` table (3 columns) used by `matter-decode.sh` to turn a product ID into a name, for any vendor that has a catalog (e.g. **LIFX**, vendor `0x1306` / `4870`). Optional. |
|
| [`products.tsv`](products.tsv) | Tab-separated `vendor ID → product ID → name` table (3 columns) used by `matter-decode.sh` to turn a product ID into a name, for any vendor that has a catalog (e.g. **LIFX**, vendor `0x1306` / `4870`). Optional. |
|
||||||
@@ -227,9 +233,72 @@ product name and vice-versa.
|
|||||||
The table is hand-edited and optional: a missing file simply means no equivalent
|
The table is hand-edited and optional: a missing file simply means no equivalent
|
||||||
is considered. The canonical (col-2) id should not itself appear in col-1.
|
is considered. The canonical (col-2) id should not itself appear in col-1.
|
||||||
|
|
||||||
|
## HomeKit setup-code decoder
|
||||||
|
|
||||||
|
`homekit-decode.sh` decodes a **HomeKit** setup code — the `X-HM://` payload an
|
||||||
|
Apple accessory (or its packaging) shows on first setup. It is a *different*
|
||||||
|
on-wire format from Matter's `MT:`, a standalone tool with no browser app and no
|
||||||
|
companion tables — the accessory-category names are inlined in the script.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./homekit-decode.sh # defaults to the demo payload 005KEOUCE
|
||||||
|
./homekit-decode.sh "X-HM://005KEOUCE"
|
||||||
|
```
|
||||||
|
|
||||||
|
A leading `X-HM://` is stripped if present; the first 9 characters are the fixed
|
||||||
|
parameter block and anything after them is a variable-length **setup id**, kept
|
||||||
|
verbatim (it is not part of the numeric block).
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./homekit-decode.sh
|
||||||
|
Setup PIN: 38313374
|
||||||
|
Flag: 10
|
||||||
|
Supports NFC: False
|
||||||
|
Supports IP: True
|
||||||
|
Supports BLE: False
|
||||||
|
Supports WAC/MFi: True
|
||||||
|
Category ID: 5 (Lightbulb)
|
||||||
|
Reserved: 0
|
||||||
|
Version: 0
|
||||||
|
Setup ID: <none>
|
||||||
|
Setup Payload: X-HM://005KEOUCE
|
||||||
|
```
|
||||||
|
|
||||||
|
### How it decodes
|
||||||
|
|
||||||
|
Unlike Matter's **base38** payload (`0-9 A-Z - .`), the 9-character HomeKit block
|
||||||
|
is **base36** (`0-9 A-Z`, 36 symbols) — an out-of-range character is a hard
|
||||||
|
error. The resulting integer is sliced **least-significant-bit first** into the
|
||||||
|
HomeKit field layout:
|
||||||
|
|
||||||
|
```
|
||||||
|
pin(27) | flag(4) | categoryId(8) | reserved(4) | version(3)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Flag** is a 4-bit sub-flag set: `1` = NFC, `2` = IP, `4` = BLE, `8` = WAC/MFi
|
||||||
|
(each printed True/False).
|
||||||
|
- **Category ID** is resolved to its HomeKit
|
||||||
|
[`homekit_accessory_category`](https://developer.apple.com/documentation/homekit/hmaccessorycategory)
|
||||||
|
name (e.g. `5` → Lightbulb, `8` → Switch); an id not in the table prints
|
||||||
|
"unknown".
|
||||||
|
- The fields are packed back into a 9-character block and printed as a
|
||||||
|
reconstructed `X-HM://…` payload, so the output is a complete record the barcode
|
||||||
|
can be rebuilt from.
|
||||||
|
|
||||||
|
The script uses the **corrected** bit layout that `composeSetupUri` in the
|
||||||
|
`homekit-code` package actually encodes; an earlier version had the
|
||||||
|
`categoryId` / `flag` / `version` positions misaligned. Each fix is noted in the
|
||||||
|
script's header comments.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The vendor mapping is derived from Matter/CHIP's `manufacturers.xml`, which is
|
The vendor mapping is derived from Matter/CHIP's `manufacturers.xml`, which is
|
||||||
Apache-2.0. The LIFX product mapping is derived from LIFX's `products.json`.
|
Apache-2.0. The LIFX product mapping is derived from LIFX's `products.json`.
|
||||||
Vendor equivalences (`vendor-equivalents.tsv`) are maintained locally. The app
|
Vendor equivalences (`vendor-equivalents.tsv`) are maintained locally. The
|
||||||
|
HomeKit decoder's accessory-category names follow Apple's HomeKit
|
||||||
|
`homekit_accessory_category` definitions. The app
|
||||||
itself is a small single-file utility.
|
itself is a small single-file utility.
|
||||||
+7
-8
@@ -1,10 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# homekit-decode.fixed.sh — corrected decoder for a HomeKit setup payload.
|
# homekit-decode.sh — decoder for a HomeKit setup payload.
|
||||||
#
|
#
|
||||||
# This is a fixed copy of homekit-decode.sh. It decodes using the *correct*
|
# It decodes using the *correct* HomeKit bit layout (the one composeSetupUri in
|
||||||
# HomeKit bit layout (the one composeSetupUri in the homekit-code package
|
# the homekit-code package actually encodes), instead of the misaligned bit
|
||||||
# actually encodes), instead of the misaligned bit positions in the original.
|
# positions an earlier version used.
|
||||||
#
|
#
|
||||||
# Correct layout, least-significant bit first:
|
# Correct layout, least-significant bit first:
|
||||||
# bits 0-26 pin (27 bits)
|
# bits 0-26 pin (27 bits)
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
# bits 39-42 reserved (4 bits)
|
# bits 39-42 reserved (4 bits)
|
||||||
# bits 43-45 version (3 bits)
|
# bits 43-45 version (3 bits)
|
||||||
#
|
#
|
||||||
# Fixes vs homekit-decode.sh:
|
# Fixes vs the earlier version:
|
||||||
# - categoryId was (val >> 27) & 0xFF, which mixed in the flag field; the
|
# - categoryId was (val >> 27) & 0xFF, which mixed in the flag field; the
|
||||||
# categoryId field starts at bit 31, so it is now (val >> 31) & 0xFF.
|
# categoryId field starts at bit 31, so it is now (val >> 31) & 0xFF.
|
||||||
# - The original "IP/BLE/Thread" flags read bits 35/36/37, which live inside
|
# - The original "IP/BLE/Thread" flags read bits 35/36/37, which live inside
|
||||||
@@ -25,9 +25,8 @@
|
|||||||
# - the category id is resolved to its HAP name (e.g. 8 -> "Switch"); ids not
|
# - the category id is resolved to its HAP name (e.g. 8 -> "Switch"); ids not
|
||||||
# in the known table print as "unknown".
|
# in the known table print as "unknown".
|
||||||
#
|
#
|
||||||
# Usage: ./homekit-decode.fixed.sh [PAYLOAD]
|
# Usage: ./homekit-decode.sh [PAYLOAD]
|
||||||
# Defaults to the demo payload "005KEOUCE" when no argument is given, matching
|
# Defaults to the demo payload "005KEOUCE" when no argument is given.
|
||||||
# the original so the two scripts decode the same thing by default.
|
|
||||||
# The output also prints the setup id and a reconstructed "X-HM://" payload
|
# The output also prints the setup id and a reconstructed "X-HM://" payload
|
||||||
# rebuilt from the decoded fields: a complete record for recreating the barcode.
|
# rebuilt from the decoded fields: a complete record for recreating the barcode.
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user