Again an update as my Thread network grows (I live 5min away from Ikea
)
Stability is such that I will start using the devices now.
Newest addition was 2 Grillplats (BBQ-place
) power plugs with remotes:
These, as mains powered devices, are adding more routers to the network:
Up to now, all other devices have had the 11 digit pairing code printed on the device itself together with a QR code which I have not used.
The Grillplats, however, have no 11 digit pairing code printed on the device itself, only a QR code. It was provided on a leaflet in the box, but that is risky if lost.
So, this is the way I was able to pair, using just the QR code. AI was helping along the way.
My goal was to not rely on any other smart house apps, so using an Android phone, scanning the QR code using the Binary Eye App the MT code was retrieved.
This can be parsed as follow:
omr@shs3:~$ chip-tool payload parse-setup-payload MT:EX7A042C00VL8P4PR00
[1774814657.503] [446921:446921] [DL] ChipLinuxStorage::Init: Using KVS config file: /home/omr/snap/chip-tool/common/chip_tool_kvs
[1774814657.503] [446921:446921] [SPL] Parsing base38Representation: MT:EX7A042C00VL8P4PR00
[1774814657.503] [446921:446921] [SPL] Version: 0
[1774814657.503] [446921:446921] [SPL] VendorID: 4476
[1774814657.503] [446921:446921] [SPL] ProductID: 32769
[1774814657.503] [446921:446921] [SPL] Custom flow: 0 (STANDARD)
[1774814657.503] [446921:446921] [SPL] Discovery Bitmask: 0x02 (BLE)
[1774814657.503] [446921:446921] [SPL] Long discriminator: 2472 (0x9a8)
[1774814657.503] [446921:446921] [SPL] Passcode: 34458161
omr@shs3:~$
Then, using this AI generated script:
omr@shs3:~$ ./pairnode-8-11.sh 34458161 2472
[+] Reading active Thread dataset from OTBR...
[+] Step 1: Commissioning to OTBR via BLE-Thread...
+ chip-tool pairing ble-thread 109 hex:0e08000000000001000035060004001fffe00708fd04f2301503538f0c0402a0f7f805101212abba1212abba0987654321060362030e4f70656e5468726561644754563704106cc96549133017bd3ffe4913ea8d93d40003000017020847113991121206030102abba 34458161 2472 --bypass-attestation-verifier true
.
.
.
[1774742404.363] [321420:321420] [PAF] WiFiPAF: Closing all WiFiPAF sessions to shutdown
[1774742404.363] [321420:321420] [DL] System Layer shutdown
[+] SUCCESS: Pairing code 23223234242 saved to /home/omr/.matter-pairing-history.log
[+] COMPLETE! Check /home/omr/.matter-pairing-history.log for your openHAB code.
omr@shs3:~$
Note the pairing code to use in openHAB, good for 15min.
The script will work with either the 11 digit pairing code or the passcode+discriminator:
omr@shs3:~$ ./pairnode-8-11.sh
Usage (8-digit): ./pairnode-8-11.sh <8-digit PIN> <Discriminator> [Node ID]
Usage (11-digit): ./pairnode-8-11.sh <11-digit PIN> [Node ID]
The script:
omr@shs3:~$ cat ./pairnode-8-11.sh
#!/usr/bin/env bash
set -euo pipefail
###############################################################################
# Matter-over-Thread pairing helper (Final Version - v2.0)
# Auto-detects 8-digit Passcode (ble-thread) vs 11-digit Manual Code (code-thread).
###############################################################################
# --- Configuration & Variables ---
PIN=""
DISCRIMINATOR=""
NODE_ID=""
ENDPOINT="1"
CWIN_SECS="900"
ITERATOR="1000"
CHIP_TOOL="chip-tool"
OT_CTL="sudo openthread-border-router.ot-ctl"
BYPASS_ATTESTATION="true"
REQUIRE_PAIRMODE="yes"
DO_JOINER_STEP="yes"
JOINER_PSKD="J01NME"
REGISTRY_FILE="${HOME}/.matter-node-registry.tsv"
HISTORY_LOG="${HOME}/.matter-pairing-history.log"
# --- Helper Functions ---
log() { printf "\033[1;37m[+]\033[0m %s\n" "$*" >&2; }
warn() { printf "\033[1;33m[!]\033[0m %s\n" "$*"; }
err() { printf "\033[1;31m[x]\033[0m %s\n" "$*"; }
die() { err "$*"; exit 1; }
need_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
}
validate_inputs() {
PIN=$(echo "$PIN" | tr -d ' -')
if [[ "$PIN" =~ ^[0-9]{11}$ ]]; then
CODE_TYPE="MANUAL_11"
log "Detected 11-digit Manual Code. Discriminator is not required."
elif [[ "$PIN" =~ ^[0-9]{8}$ ]]; then
CODE_TYPE="PASSCODE_8"
[[ -n "$DISCRIMINATOR" ]] || die "8-digit Passcode requires a Discriminator (e.g. 2540)."
else
die "PIN must be 8 or 11 digits (got: '$PIN')."
fi
[[ "$NODE_ID" =~ ^[0-9]+$ ]] || die "NODE_ID must be a decimal integer."
}
get_dataset_hex() {
log "Reading active Thread dataset from OTBR..."
local raw_output
raw_output="$($OT_CTL dataset active -x 2>/dev/null)"
local ds
ds=$(echo "$raw_output" | sed -n '1p' | tr -d '\r\n[:space:]')
[[ "$ds" =~ ^[0-9a-fA-F]+$ ]] || die "Dataset hex invalid. Check OTBR status."
echo "$ds"
}
get_next_node_id() {
local start_id=100
if [[ ! -f "$REGISTRY_FILE" ]]; then echo "$start_id"; return; fi
local max_id
max_id=$(awk -F'\t' '{print $2}' "$REGISTRY_FILE" | sort -n | tail -1)
[[ -z "$max_id" ]] && echo "$start_id" || echo $((max_id + 1))
}
pair_device() {
local dataset_hex="$1"
local bypass_flag=""
[[ "$BYPASS_ATTESTATION" == "true" ]] && bypass_flag="--bypass-attestation-verifier true"
if [[ "$CODE_TYPE" == "MANUAL_11" ]]; then
log "Step 1: Commissioning via code-thread (11-digit manual code)..."
set -x
"$CHIP_TOOL" pairing code-thread "$NODE_ID" "hex:${dataset_hex}" "$PIN" $bypass_flag
set +x
else
log "Step 1: Commissioning via ble-thread (8-digit passcode)..."
set -x
"$CHIP_TOOL" pairing ble-thread "$NODE_ID" "hex:${dataset_hex}" "$PIN" "$DISCRIMINATOR" $bypass_flag
set +x
fi
}
open_commissioning_window() {
log "Step 2: Opening commissioning window for openHAB..."
# For the window itself, we use a fallback discriminator if none was provided
local window_disc="${DISCRIMINATOR:-3840}"
local out
out=$("$CHIP_TOOL" pairing open-commissioning-window "$NODE_ID" "$ENDPOINT" "$CWIN_SECS" "$ITERATOR" "$window_disc" 2>&1 | tee /dev/stderr)
local setup_code
setup_code=$(echo "$out" | grep -oP 'Manual pairing code: \[\K[0-9]+(?=\])' || echo "NOT_FOUND")
if [[ "$setup_code" != "NOT_FOUND" ]]; then
local ts; ts=$(date "+%Y-%m-%d %H:%M:%S")
echo "[$ts] Node $NODE_ID (PIN: $PIN) -> openHAB Setup Code: $setup_code" >> "$HISTORY_LOG"
log "SUCCESS: Pairing code $setup_code saved to $HISTORY_LOG"
else
warn "Could not auto-extract code. Check output manually."
fi
}
main() {
if [ $# -lt 1 ]; then
echo "Usage (8-digit): $0 <8-digit PIN> <Discriminator> [Node ID]"
echo "Usage (11-digit): $0 <11-digit PIN> [Node ID]"
exit 1
fi
PIN="$1"
# Logic to handle shifts in arguments if 11-digit code is used
if [[ $(echo "$1" | tr -d ' -' | wc -c) -gt 10 ]]; then
# 11-digit path: Discriminator is not needed, $2 becomes Node ID
NODE_ID="${2:-$(get_next_node_id)}"
else
# 8-digit path: $2 is Discriminator, $3 is Node ID
DISCRIMINATOR="${2:-}"
NODE_ID="${3:-$(get_next_node_id)}"
fi
validate_inputs
need_cmd "$CHIP_TOOL"
local ds; ds="$(get_dataset_hex)"
pair_device "$ds"
printf "%s\t%s\t%s\tcommissioned\n" "$(date -Iseconds)" "$NODE_ID" "${PIN: -4}" >> "$REGISTRY_FILE"
open_commissioning_window
log "COMPLETE! Check $HISTORY_LOG for your openHAB code."
}
main "$@"
omr@shs3:~$
Hope this can be useful for somebody else as well.
Cheers.


