Skip to main content

zwave-qr-fix

CI Python 3.9–3.13 License: MIT

Repair malformed Z-Wave SmartStart QR codes that Z-Wave JS / Home Assistant rejects with

Invalid QR code: contains invalid characters (ZW1406)

Known case: Heatit Z-TRM6 thermostats whose QR code ends in …1010.9999999999999. See DEVICES.md for all known affected devices.

Quickstart

You came from the Home Assistant forum and your device won't pair? Here's the fix:

  1. Install with pipx (keeps the tool isolated from your system Python):

    pipx install "zwave-qr-fix[all] @ git+https://github.com/AMThie/zwave-qr-fix.git"
    
  2. Repair the code. Take a photo of the QR label (jpg, png or iPhone heic) and run the tool with the DSK printed on the label as a safety check:

    zwave-qr-fix --image label.heic --dsk 12345-23456-34567-45678-56789-65535-10101-20202 -o fixed.png
    
  3. Include the device. Scan fixed.png in the Home Assistant Companion app (Settings → Devices & services → Z-Wave → Add device), or paste the printed Fixed: string into the provisioning dialog of Z-Wave JS UI.

No QR code at hand? You can also skip SmartStart.

Example

All values below are synthetic. Never post your real DSK (see Privacy).

$ zwave-qr-fix "90015755200312345234563456745678567896553510101202020010020540460802200041100048122891010.9999999999999" \
    --dsk 12345-23456-34567-45678-56789-65535-10101-20202
Input:  90015755200312345234563456745678567896553510101202020010020540460802200041100048122891010.9999999999999

• Removed garbage starting at position 89: '.9999999999999'
• ProductId truncated (19/20 digits) – rebuilding application version

Version:          1 (SmartStart)
Security classes: S2 Unauthenticated, S2 Authenticated
DSK:              12345-23456-34567-45678-56789-65535-10101-20202
PIN:              12345
ProductType:      generic 0x08, specific 0x06, icon 0x1200
ProductId:        manufacturer 0x019B, type 0x0030, id 0x3001, app version 3.242

✓ DSK matches the printed label
Fixed:  900157552003123452345634567456785678965535101012020200100205404608022000411000481228901010

Installation

# core only: repair strings you pass on the command line
pipx install "git+https://github.com/AMThie/zwave-qr-fix.git"

# with image input (--image) and PNG output (-o)
pipx install "zwave-qr-fix[all] @ git+https://github.com/AMThie/zwave-qr-fix.git"

Plain pip install works the same way. The core has no dependencies and runs on Python 3.9 or newer. The extras are optional:

Extra Adds Packages
image --image: read the QR code from a photo zxing-cpp, Pillow, pillow-heif
png -o: write the repaired QR code as PNG qrcode
all both

Usage

zwave-qr-fix CODE [options]          # repair a string read from the QR code
zwave-qr-fix --image PHOTO [options] # read it from a photo of the label
python -m zwave_qr_fix …             # same, without the console script
Option Meaning
--dsk DSK DSK printed on the label. The tool aborts if the QR code's DSK differs. Always use it.
-o FILE, --png FILE Write the repaired QR code to a PNG file.
--check Only validate, don't repair.
--app-version X.Y Override the application version in the ProductId TLV.
--version Show the version.
Exit code Meaning
0 Valid or repaired
1 Invalid and not repairable (or --check found a problem)
2 DSK mismatch, or invalid command-line arguments

How it works

A SmartStart QR code is a string of digits:

Field Digits Notes
Lead-in 2 always 90
Version 2 00 = S2 only, 01 = SmartStart
Checksum 5 first 2 bytes of SHA-1 over everything after this field
Requested keys 3 bitmask of security classes
DSK 40 8 blocks of 5 digits (block 1 = PIN)
TLVs … type·2+critical (2) + length (2) + value

The Heatit defect: when the label was generated, the application version in the ProductId TLV was apparently written as a floating-point number. The string therefore ends in …1010.9999999999999: it contains a non-digit character, the ProductId TLV is one digit short, and the checksum may no longer match.

The repair:

  1. Cut everything from the first non-digit character.
  2. Rebuild truncated ProductType/ProductId TLVs (or set the application version explicitly with --app-version).
  3. Recompute the checksum.
  4. Leave the DSK untouched. It is the only field the controller needs to identify and securely include the device, and it is correct on the label.

Every change is listed in the output (• lines). The test suite checks the output against the reference parser of Z-Wave JS (parseQRCodeString from @zwave-js/core).

FAQ

Do I even need SmartStart?

No. SmartStart is a convenience. Classic S2 inclusion with the PIN works just as well and doesn't need a QR code: start inclusion in Home Assistant / Z-Wave JS UI, put the device into inclusion mode, and enter the PIN (the first 5 digits of the DSK printed on the label) when asked.

Is the repaired code safe to use?

The tool never changes the DSK, and with --dsk it verifies that the DSK in the QR code matches the one printed on the label. If they differ, it refuses to output a code.

Which devices are affected?

See DEVICES.md. If you found another one, please report it, and please report it to the manufacturer as well.

Privacy

The DSK is part of S2 security. Do not post the full codes or DSKs of your devices in issues or forums. Use placeholders such as 12345-12345-… instead. The tool runs entirely offline. See SECURITY.md.

Contributing

Contributions are welcome. See CONTRIBUTING.md and the changelog.

Disclaimer

Unofficial tool, not affiliated with Heatit or the Z-Wave Alliance. Use at your own risk. Please also report affected devices to the manufacturer.

License

MIT

Metadata

Release files for zwave-qr-fix 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zwave-qr-fix 0.1.0
File Size Uploaded
zwave_qr_fix-0.1.0.tar.gz 14.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zwave-qr-fix 0.1.0
File Interpreter ABI Platform
zwave_qr_fix-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 25.5 kB

Release files / zwave_qr_fix-0.1.0.tar.gz

Download URL zwave_qr_fix-0.1.0.tar.gz
Size 14.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a6f9887e72e957fa0a8a5b765dcc8ade2903987bb61cb1eb0f393c44f849c8a9
BLAKE2b-256 checksum
How to use checksums
bbfeb8d450ba084fe64a1e1fe0f9250135ff29f843b2a641e7df8778359c33f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / zwave_qr_fix-0.1.0-py3-none-any.whl

Download URL zwave_qr_fix-0.1.0-py3-none-any.whl
Size 10.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bcd60ec6aee804592cee4dc6e89fe78504f96c012314744db288f1225e53affd
BLAKE2b-256 checksum
How to use checksums
ac848f0baca685e24f7c732cf634c614ff8b2b43db5f30a031cef764a623f897
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page