private@homelab: ~/latest
local-first guides · privacy-aware · no noisy tracking
private@homelab:~$ cat guides/article.md
·
· ·
8–12 minutes
read

Zigbee2MQTT Install Codes: Why Some Devices Need Them

Hit \"Unsupported install code\" in Zigbee2MQTT? Which devices need one, why the printed code often isn't the whole code, and how to add it locally.

Most Zigbee devices join Zigbee2MQTT the same way: put the network in pairing mode, trigger the device, wait for the interview to finish. Then occasionally one refuses to join, or Zigbee2MQTT throws an explicit “Unsupported install code” error, and the trail leads to a printed code or QR sticker nobody remembers to look for. This is for practitioners hitting that error, or evaluating a device whose documentation mentions install codes before purchase. Everything below happens over your local Zigbee network and MQTT broker. Reading a QR sticker needs a phone camera or any scanner app, not a vendor account or companion app.

What an install code actually does

An install code is a device-specific value assigned at manufacture, which the coordinator uses to derive a link key for that one joining device. The link key encrypts the transfer of the network key during the join, as part of Zigbee 3.0’s secure commissioning. Without it, the network key is handed over under a well-known default key, which is the exposure the mechanism exists to close. Devices that mandate an install code won’t complete a secure join without one.

Note · context

Install codes are a Zigbee 3.0 security mechanism, not a Zigbee2MQTT-specific feature. Zigbee2MQTT exposes a way to register one; it doesn’t invent the concept.

Which devices actually require one

Most consumer Zigbee devices from Aqara, Tuya, Sonoff and similar ecosystems join fine without any install code, because the standard interview handles them. The requirement is set per vendor and per model, and Bosch’s Smart Home Zigbee line is the case Zigbee2MQTT’s own device pages document most consistently: pages for models such as the BMCT-SLZ light and shutter controller and the BTH-RA radiator thermostat carry explicit instructions to add the install code before joining. Some Bosch models additionally need a Zigbee 3.0-capable coordinator and won’t pair with older CC2531-based sticks at all.

The reliable signal is the device’s own documentation, its packaging, or its Zigbee2MQTT device page. If any of those name an install code or a pairing QR code, treat it as mandatory for that model rather than optional.

Where to find your device’s install code

Manufacturers print install codes physically: on the device body, inside the battery compartment, on a separate sticker in the box, or in the manual, frequently as a QR code meant to be scanned rather than read.

Scanning matters more than it looks. On several devices the short plain-text code printed on the label is not the value Zigbee2MQTT expects. The QR code encodes a longer string that carries the device’s IEEE address alongside the code itself. Zigbee2MQTT’s page for the Bosch BMCT-SLZ puts a number on the difference: scanning the QR returns roughly 95 alphanumeric characters, while the code printed in plain text on the housing is considerably shorter. Typing that shorter value is one of the most common routes to a rejected code.

Warning

Do not assume the plain-text code on the label is the whole code. Where a device carries both a printed code and a QR code, scan the QR and paste the full string. Several vendors’ devices reject the shorter printed value.

Adding the code in Zigbee2MQTT

Register the code before the device goes into pairing mode. Zigbee2MQTT offers two routes.

In the frontend, open Settings → Tools → “Add install code”, paste the scanned code and confirm. Frontend layouts shift between releases, so if that entry isn’t where you expect it, check the Tools section on your own version.

The MQTT route publishes to the bridge request topic:

register an install code via MQTT
mosquitto_pub -h 192.168.10.20 -t 'zigbee2mqtt/bridge/request/install_code/add' \
  -m '{"value":"THE_CODE"}'

The bridge answers on the matching response topic with {"data":{"value":"THE_CODE"},"status":"ok"}. This is a request/response exchange, not a file written somewhere you need to manage afterwards.

With the code registered, open the join window and pair normally. Zigbee2MQTT applies the code automatically during that device’s interview.

Install codes and permit_join are not alternatives

A registered install code does not open the network. permit_join controls whether joining is possible at all, a window Zigbee2MQTT 2.x caps at 254 seconds, while the install code governs how the network key reaches one specific device during that window. Bosch’s pairing instructions reflect the order: add the install code first, then make sure permit joining is active. If either half is missing the join fails, and from the frontend the two failures look much alike. Our permit_join guide covers the window itself and the device passlist that outlives it.

Adapter support: not every coordinator can do this

Install-code and QR-code commissioning isn’t implemented uniformly across Zigbee2MQTT’s adapter drivers. It works on zstack (Texas Instruments ZNP coordinators), ember (Silicon Labs EmberZNet), and deconz (Dresden Elektronik, on recent coordinator firmware). It is not implemented for zigate or zboss, so readers on those adapters cannot use install-code commissioning at all, whatever the device.

Adapter driver Example hardware Install code support
zstack Sonoff ZBDongle-P, TI-based coordinators Yes
ember Sonoff ZBDongle-E, SkyConnect / Connect ZBT-1 Yes
deconz ConBee II / III, recent firmware only Yes
zigate ZiGate-based coordinators No
zboss zboss-based coordinators No

The deconz firmware caveat isn’t theoretical. ConBee II owners filed repeated “Add install code is not supported” reports (GH#14510, #16502, #17492) before firmware support landed, so check your coordinator’s firmware rather than treating the driver name as sufficient. If you’re choosing hardware and expect install-code-mandatory devices, that rules out zigate and zboss outright; our ZBDongle-E vs ZBDongle-P comparison covers the two mainstream ember and zstack sticks.

Warning

If your coordinator uses the zigate or zboss driver, a device that mandates an install code will not join. No configuration workaround exists on the Zigbee2MQTT side; the fix is a different coordinator, not a settings change.

Why formats vary, and decoding “Unsupported install code”

The install code itself is specified, with fixed lengths and a checksum. How vendors print it and wrap it in a QR string is not. Zigbee2MQTT validates what you paste against the lengths it expects and rejects anything that doesn’t match, which is what surfaces as an unsupported-code or length-mismatch error rather than a silent failure.

Home Assistant’s ZHA integration takes a different approach, carrying separate regular expressions for several vendors’ QR formats. Consciot, Enbrighten, Aqara and Bosch are the ones named in the Zigbee2MQTT discussion where this comes up. That per-vendor format handling belongs to ZHA rather than Zigbee2MQTT, which is why the same sticker can be accepted by one and rejected by the other.

A practical order of checks: scan the QR rather than typing the printed code, confirm your adapter supports install codes at all, then search the Zigbee2MQTT discussions for your specific manufacturer. Participants in that discussion report getting a rejected code accepted by wrapping the IEEE address and the code in the Aqara-style QR string, which bypasses the length check, and by zero-padding a short code. Both are community workarounds reported for specific devices, not documented behaviour.

When it’s not the install code at all

Plenty of pairing failures happen on devices that never needed a code. Rule out the ordinary causes before chasing the code.

Keep the device physically close to the coordinator during the initial join. Confirm the coordinator runs Zigbee-only firmware rather than an accidentally-flashed multiprotocol build; our SkyConnect and ZBT-1 multiprotocol piece covers why that state is easy to end up in. Check for 2.4GHz Wi-Fi overlap with the Zigbee channel in use. And keep sleepy battery devices awake through the interview with short button presses about once a second. Zigbee2MQTT’s device pages for Aqara contact sensors specify that cadence, which is considerably more frequent than the every-30-seconds figure circulating in forum threads.

One frequently-cited GitHub report, #18881 (“Several Aqara Devices can not join or left the network”), is worth reading narrowly. It came from a single reporter migrating from ZHA to Zigbee2MQTT, and it was closed as not planned. That makes it one account of an old-network join problem rather than a documented model-wide defect. Our separate guide on Zigbee2MQTT interview failures covers the general pairing-failure playbook in depth.

Frequently asked questions

Do all Zigbee devices need an install code to pair? No. Most consumer devices join through the standard Zigbee2MQTT interview without one. Install codes are required only by devices whose vendor enforces Zigbee 3.0’s stricter secure-commissioning path, with Bosch’s Smart Home Zigbee line the most consistently documented example.

What happens if I don’t have the install code for a device that requires one? The device will not complete a secure join. You need to locate the code, on the device body, in the battery bay, on a box sticker, or in the manual, before pairing can proceed.

Where do I find my device’s install code or QR code? Manufacturers print it physically on the unit or packaging, usually as a QR code. There is no centralized lookup independent of the device and its documentation.

Why does Zigbee2MQTT say “Unsupported install code”? Most often because the value pasted in isn’t the full code. Zigbee2MQTT validates the code’s length and rejects what it doesn’t recognise, and on several devices the short plain-text code on the label is not the string the QR encodes. Scan the QR first. Vendor QR formats also differ, which is why ZHA carries per-vendor parsing that Zigbee2MQTT does not.

Does my coordinator support install codes? Only if it runs the zstack, ember, or deconz driver, and on deconz only with recent coordinator firmware. zigate- and zboss-based coordinators do not support install-code commissioning at all.

What this verified, what stays local, and what to check yourself

This article is research-synthesis from Zigbee2MQTT’s official documentation (the securing and adapters guides), Zigbee2MQTT’s own Bosch device pages, a GitHub discussion on install-code format mismatches, a closed GitHub issue on a separate Aqara join-failure report, and community reports on ConBee II adapter support. No claim here depends on hardware this site owns, and none is presented as tested.

Verified against those sources: what install codes do, where they are found, that the QR code on some devices encodes a longer string than the plain-text code printed beside it, both methods for registering a code, and the adapter-driver support matrix (zstack, ember and deconz yes; zigate and zboss no).

Sourced but not independently re-verified for current status: the format-mismatch discussion and the vendor QR formats named in it, which are ZHA’s parsing rather than Zigbee2MQTT’s; the Aqara-wrapper and zero-padding workarounds reported there, which are community findings for specific devices; and issue #18881’s closed-as-not-planned state, which reflects the state found during research rather than necessarily today’s.

Everything described here, from registering an install code through pairing and troubleshooting, happens over your local Zigbee network and MQTT broker. No coordinator vendor cloud service, account, or companion app is involved at any step.

What to check yourself: confirm your coordinator’s adapter driver and its firmware version before assuming install-code support either way; check a device’s documentation or box for a mandatory install-code notice before purchase if this matters to you; and if you hit the error, scan the QR code and paste the full string before concluding the code itself is wrong.

local-firstHome Assistantno-cloud