Skip to main content

Matter

The Matter page lets GEM act as both a Matter bridge (exposing GEM zones to Apple Home, Google Home, and other Matter ecosystems) and a Matter controller (commissioning and controlling external Matter devices from GEM).

Tabs

The page has two tabs: Bridge and Controller.


Bridge Tab

The Bridge tab configures GEM as a Matter bridge, making selected GEM zones available to Matter-compatible smart home platforms.

Bridge Status

A status grid at the top shows the current state of the bridge:

FieldDescription
Matter BridgeRunning (green), Enabled but not started (yellow), or Disabled (gray).
PairedWhether the bridge is paired with a Matter controller/app: Yes (green) or No (gray).
Exposed ZonesThe number of GEM zones currently exposed through the bridge.
PortThe network port the bridge is listening on.

When the bridge is enabled but did not start, the reason is shown in a red panel beneath the status grid (for example, a port already in use or an mDNS startup failure). Fixing the cause and clicking Save & Apply restarts the bridge without restarting GEM.

Network Interface

By default the bridge advertises itself over mDNS on all interfaces. To pin it to one, set matter.interface in gem.json to the interface name (for example enp4s0). If the configured name does not exist on the host — a common result of moving a configuration between machines — GEM logs a warning listing the available interfaces and falls back to automatic detection rather than leaving the bridge offline.

Configuration

FieldDescription
Enable Matter BridgeToggle switch to enable or disable the bridge. When enabled, the bridge starts automatically with GEM.
PortNetwork port for the Matter bridge. Default: 5540.
Bridge NameThe name that appears in Home apps when discovering the bridge (e.g., "GEM Bridge").

Click Save & Apply to save the configuration and restart the bridge.

Pairing

The pairing section changes based on the bridge state:

When the bridge is running but not yet paired:

  • A QR code is displayed that you can scan with your Home app (Apple Home, Google Home, etc.).
  • A manual pairing code is shown alongside the QR code for apps that support manual entry.
  • Click Copy Code to copy the manual pairing code to your clipboard.

When the bridge is running and paired:

  • A "Paired" badge is shown with a confirmation message.
  • A Reset Bridge button is available. Resetting clears all pairing data and restarts the bridge. You will need to remove the old bridge from your Home app and re-pair.
warning

After resetting the bridge, you must remove the old bridge entry from your Home app before re-pairing. The Home app may not automatically detect the change.

Zone Selection

The zone selection area lets you choose which GEM zones to expose through the Matter bridge. It has two panels:

Subsystem Sidebar (left):

  • Lists all subsystems that have compatible zones, with the count of selected/total zones for each.
  • Click a subsystem name to filter the zone list. Click "All Subsystems" to show all zones.

Zone Panel (right):

  • Search field to filter zones by name.
  • Select All and Deselect All buttons that apply to the currently filtered view.
  • A checkbox list of zones. Each entry shows the zone label and its Matter device type (e.g., Light, Thermostat, Door Lock, Window Covering, Contact Sensor, Fan, Power Outlet).
ButtonDescription
Save Zone SelectionSaves the selected zones without restarting the bridge.
Save & ApplySaves the selected zones and restarts the bridge to apply changes.
note

Compatible subsystems include: Lights, Climate, Doors/Locks, Shades, Gates/Garages, Fans, Power, and Security. Zones from other subsystems are not shown.


Controller Tab

The Controller tab lets GEM act as a Matter controller, commissioning external Matter devices and creating GEM zones for them automatically.

Controller Status

A status grid shows the current state:

FieldDescription
ControllerRunning (green), Enabled but not started (yellow), or Disabled (gray).
DevicesThe number of commissioned Matter devices.
PortThe network port the controller is using.

Configuration

FieldDescription
Enable ControllerToggle switch to enable or disable the controller. When enabled, it starts automatically with GEM.
PortLocal websocket port GEM uses to talk to chip-tool. Default: 9099. This is not the Matter protocol port — Matter devices themselves use 5540.

Click Save & Apply to save the configuration and restart the controller.

note

Save & Apply merges the two fields above into the matter_controller block in gem.json. The settings described below that have no field on this page — storage_path, interface, icd_registration, poll_icd_nodes and the rest — are left exactly as you set them.

Commissioning Storage

The controller keeps its Matter fabric credentials in a chip-tool key-value store. Every commissioned device is identified against that store, so if it is lost the devices remain listed in GEM but no longer respond, and each has to be commissioned again.

chip-tool places this store in $TMPDIR (falling back to /tmp) unless told otherwise, and neither location survives a reboot. GEM therefore always starts chip-tool with an explicit storage directory, defaulting to matter_storage/chip-tool inside the GEM installation — alongside matter_storage/gem-matter-bridge, where the bridge keeps its own state.

That location is deliberate: backups capture the GEM installation directory, so a store kept there is included in every backup and comes back automatically on restore. A store outside the installation is not backed up, and restoring onto replacement hardware would bring the bridge back intact while leaving every commissioned Matter device to be commissioned again by hand.

To override it, set matter_controller.storage_path in gem.json:

"matter_controller": {
"enabled": true,
"port": 9099,
"storage_path": "/opt/gem/matter_storage/chip-tool"
}

Keep any custom path inside the installation directory for the same reason, and avoid the directories backups skip (backups, updates, node_modules, .git, .idea, dist, tmp, .cache).

Changing storage_path points the controller at a different fabric. Devices commissioned under the previous path will need to be commissioned again.

Battery sensors (intermittently connected devices)

Battery-powered Matter sensors are intermittently connected devices (ICDs). They are off the network almost all of the time, waking briefly on their own schedule or when a button is pressed. A Shelly H&T Gen3, for example, wakes every two hours.

This affects what you see when commissioning one. Pairing itself is quick and happens while the device is awake, but the step that follows — asking the device what clusters and endpoints it has — often reaches a device that has already gone back to sleep. When that happens the controller keeps the node and reports it as awaiting introspection rather than creating an empty device:

matter controller: node 1 commissioned but did not answer introspection (asleep?) - will retry until it wakes

It retries every five minutes and finishes the job the next time the sensor is awake, creating its zones then. Nothing is required of you; the zones appear on their own, typically within a few minutes of the device's next wake. Pressing the device's button wakes it immediately if you would rather not wait.

Optionally, the controller can register for check-ins, where the device announces itself when it wakes instead of waiting to be found:

"matter_controller": {
"enabled": true,
"port": 9099,
"icd_registration": true,
"icd_stay_active_ms": 30000
}

icd_registration is off by default. icd_stay_active_ms asks a long-idle device to stay awake for that many milliseconds after commissioning, which gives introspection a chance to complete on the first attempt; devices that do not support it ignore it. Both settings apply only to devices commissioned after they are enabled — an already-commissioned sensor must be commissioned again to register for check-ins.

These devices are not polled

The controller identifies an ICD by the ICD Management cluster on its root endpoint and records it against the device, then leaves it out of the polling cycle. This is not an optimisation — polling a sleeping device is actively harmful. Every read to an unreachable node holds the controller's single command channel for around a minute of retransmission, and enough of those in a row will delay the next command past its own timeout, which can make an unrelated operation report a failure that never happened. Subscriptions and check-ins deliver the readings instead.

To poll them anyway:

"matter_controller": {
"poll_icd_nodes": true
}

Devices commissioned before this behaviour existed are still polled, and are reclassified the next time they are commissioned or introspected.

How often a battery sensor reports

Reporting is driven by the device, not by GEM. When the controller subscribes it asks for a 120 second maximum interval, and the device is free to answer with its own — a Shelly H&T Gen3 negotiates 43200 seconds (12 hours). Between those, it reports when a reading moves past the threshold configured on the device itself, not in GEM. On a Shelly that is report_thr_C for temperature and report_thr for humidity, reachable through its own web interface or RPC API, and the firmware enforces its own limits (temperature will not go below 0.5 °C). Lowering a threshold means more frequent reports at the cost of battery life.

Advertising interface (hosts with VPN tunnels)

The controller advertises its own operational record over mDNS, and commissioned devices use that record to find it again for subscription reports and check-ins. chip-tool advertises on every interface it can see, which on a host with a WireGuard or NetBird tunnel includes an interface where multicast cannot work. The send fails and the whole advertisement is reported as failed:

[DIS] Warning: Attempt to mDNS broadcast failed on wt0: OS Error 0x0200007E: Required key not available
[DIS] Failed to advertise records

Commissioning still works — device discovery uses a different path — but the controller's own record may not reach the LAN. Pin advertising to the interface that carries your Matter traffic:

"matter_controller": {
"enabled": true,
"port": 9099,
"interface": "eth0"
}

interface takes an interface name and is resolved to its index. Set interface_id instead to supply the index directly. Both are optional; with neither set, chip-tool keeps its default behaviour of advertising on every interface.

This applies to the controller only. The bridge has its own matter.interface setting.

chip-tool must be unconfined

The GEM installer installs chip-tool with snap install --devmode. The snap's default strict confinement limits it to its own directory under ~/snap, which is outside the installation and therefore outside backups. Installed strictly, chip-tool cannot write to the storage path above and exits at startup with:

[CTL] Init Storage failure: src/controller/ExamplePersistentStorage.cpp:89: Error 0x000000AD

If you see that, reinstall with sudo snap install --devmode chip-tool. A chip-tool built from the Matter SDK is also unconfined and works without the flag.

When chip-tool will not stay running

If chip-tool exits unexpectedly, GEM restarts it automatically, waiting longer between each attempt — 5 seconds, then 10, 20, 40, 80, 160, and 300 seconds. A process that ran for more than two minutes before dying is treated as a one-off, and the next restart starts back at 5 seconds.

After eight failed attempts in a row — roughly fifteen minutes — GEM stops trying, logs

matter controller: chip-tool failed to stay running after 8 attempts - giving up

and marks the controller device disconnected. This is deliberate: a chip-tool that cannot start at all (missing binary, strict confinement, an unwritable storage path) will not fix itself, and retrying forever would fill the log and spawn a process every few seconds indefinitely.

Fix the underlying cause, then reload the controller — Save & Apply on this page, or reloading the device from Devices — to start over.

When chip-tool stops responding

A different failure looks like nothing at all: chip-tool stays running and its connection to GEM stays open, but it stops servicing that connection. Neither the process nor the socket goes away, so commands simply sit unread until each one hits its own timeout.

GEM watches for this. When the connection has been silent for a minute it sends a harmless local command as a liveness probe; three unanswered probes in a row — about three minutes of silence — count as unresponsive. A chip-tool GEM started is then restarted (SIGTERM, escalating to SIGKILL after five seconds) and picked up by the restart backoff described above:

matter controller: chip-tool is unresponsive - restarting it

A busy controller never pays for a probe — any reply or subscription report already proves it is alive — and no probe is sent while a command is in flight, so a long commissioning is never mistaken for silence.

If chip-tool is supervised outside GEM (a development setup), GEM reconnects instead and logs what to do if that is not enough:

matter controller: chip-tool is unresponsive and runs externally - reconnecting.
if this repeats, restart it (systemctl restart chip-tool)

chip-tool serves one client at a time and does not reliably release the slot, so a wedged one will refuse the replacement connection too — it has to be restarted.

Commission Device

This section appears only when the controller is running. It lets you add new Matter devices to GEM.

FieldDescription
MethodHow to identify the device: Pairing Code (QR code string or 11-digit manual code) or Passcode + Discriminator (enter both values separately).
Pairing CodeThe QR code string (e.g., MT:Y3.13OTB...) or 11-digit manual pairing code. Shown when Method is "Pairing Code."
PasscodeThe device passcode (e.g., 20202021). Shown when Method is "Passcode + Discriminator."
DiscriminatorThe device discriminator value (e.g., 3840). Shown when Method is "Passcode + Discriminator."

Click Commission Device to pair the device. The button changes to "Commissioning..." during the process.

After successful commissioning, a result panel shows:

  • The assigned Node ID.
  • The Device ID.
  • A list of GEM zones that were automatically created for the device, with their labels and subsystems. Each zone label is a link — click it to open that zone for editing without leaving the Matter page.

When commissioning fails

A failed attempt has usually still changed something on the device: chip-tool commonly installs GEM's credentials and only then stops answering, so the device counts GEM as a joined fabric even though GEM reported a failure. Left alone that device cannot be commissioned again — every attempt claims another of the handful of fabric slots a Matter device has, and after a few it refuses to commission at all.

GEM therefore tells the device to forget it too, and hands the node ID back so a retry does not walk the numbering forward. Two cases end differently:

  • The controller could not reach the device to unpair it. GEM logs the device may still hold this fabric; factory reset it before retrying. Factory reset the device before trying again.
  • Zones were already created before the failure. The device is half-registered rather than unpaired, so GEM leaves it paired rather than stranding its zones. Remove it from Commissioned Devices below, then commission it again.

Zones created for a device

After pairing, GEM asks the device which endpoints it has and which clusters each one implements, then creates GEM zones from the answer. Zones point at the controller device and are addressed <node id>:<endpoint>. The clusters found decide the subsystem and the zone control, and drive these zone attributes:

ClusterSubsystemZone attributes
On/OffPowerstate
Level ControlLightslevel, state
Color ControlLightslevel, state
Door LockDoorsstate (locked/unlocked)
ThermostatClimatetemperature, heat_setpoint, cool_setpoint, system_mode
Window CoveringShadeslevel, state
Fan ControlFanslevel, state
Occupancy SensingSecuritystate (occupied/unoccupied)
Boolean StateSecuritystate (open/closed)
Temperature MeasurementClimatetemperature
Relative Humidity MeasurementClimatehumidity

The cluster list is also stored on the zone as its matter_clusters attribute.

Sensors that report more than one measurement

A temperature-and-humidity sensor reports each reading on its own endpoint, but it is one physical device and belongs in one zone. GEM merges endpoints that carry only read-only measurements into a single climate zone — a Shelly H&T, for example, becomes one zone with both temperature and humidity, named after the device rather than suffixed with an endpoint number.

Merging is deliberately narrow:

  • Anything controllable stays on its own zone. Two switchable endpoints are two things to control.
  • Two endpoints reporting the same measurement stay apart — two temperature probes are two readings, and merging them would have one overwrite the other.
  • Endpoints in different subsystems are never merged.

Behind the scenes each endpoint keeps its own cluster list, so GEM only ever asks an endpoint for what it actually implements. Devices commissioned before this behaviour existed keep one zone per endpoint until they are commissioned again.

Re-commissioning a device

A device that is removed and commissioned again gets a new node ID, so its zones are addressed differently the second time. Removing a device disables its zones rather than deleting them, and a re-commission reclaims them: GEM matches the zone by name, moves it to the new address and re-enables it. The zone keeps its ID, its history, and every UI and macro that references it.

The previous node is dropped from the controller's device list at the same time, since the device forgot that fabric when its commissioning window was reopened.

Commissioned Devices

A table listing all Matter devices controlled by GEM:

ColumnDescription
DeviceThe device label.
Node IDThe Matter node identifier (shown in monospace).
StatusOnline (green) or Offline (gray).
ZonesThe GEM zones associated with this device, each a link that opens that zone for editing without leaving the Matter page — the same links the commissioning result panel shows. Falls back to a count when the zones cannot be resolved.

Each row has a Remove button to decommission the device (removes it from GEM's Matter fabric). The zones it created are disabled rather than deleted, so commissioning the device again reclaims them — see Re-commissioning a device.

If no devices are commissioned, a message directs you to use the commission form above.

Devices that stop answering

A device that has gone for good — hardware removed, or a sensor factory reset and re-paired under a new node ID — stays in the controller's list, and every read aimed at it costs around 45 seconds of failed discovery. That matters more than it sounds: the controller talks to chip-tool over a single channel that serves one command at a time, so two or three dead devices can delay the next command past its own timeout and make an unrelated operation report a failure that never happened.

After three polling rounds in which a device answers nothing, GEM pauses its polls and subscriptions for thirty minutes and logs:

matter controller: node 4 has not answered 3 times - pausing its polls and subscriptions for 30m.
if the device was re-paired it has a new node id now and this one can be removed.

When the pause expires one attempt is allowed through; if it also goes unanswered the device is paused again. Any reply at all — from a poll or a subscription report, and even an unchanged reading — clears the pause immediately and is logged as node 4 is answering again. A device GEM never actually asked (no clusters recorded) is never counted against.

If the device is genuinely gone, Remove it from the table above.