Repository navigation
API Reference
Complete reference for all Matter API methods and properties available in Homebridge.
Check if Matter is available in the current version of Homebridge.
Note
In practice, you would likely want to use isMatterEnabled(), see below.
Returns: true if Homebridge version is >= 2.0.0
Usage:
if (api.isMatterAvailable()) {
log.info('Matter is available in this Homebridge version')
} else {
log.warn('Matter requires Homebridge >= 2.0.0')
}When to use:
- Plugin compatibility checks
- Conditional feature loading
- Version-specific functionality
Check if Matter is enabled for this bridge instance.
Returns: true if Matter is enabled in the bridge configuration
Configuration:
- For main bridge: add a
matterobject tobridgein config.json, for example"matter": {} - For child bridge: add a
matterobject to_bridgein the platform config - Set
"enabled": falseinside it to turn Matter off again
Usage:
if (api.isMatterEnabled()) {
// Register Matter accessories — past this guard, api.matter! is safe
await api.matter!.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessories)
} else {
log.info('Matter is not enabled for this bridge')
}When to use:
- Runtime checks before registering Matter accessories
- Conditional accessory registration
- User feedback about Matter status
Note
Requires Homebridge v2.5.0 or later.
Check whether this bridge is in Matter externals only mode: the bridge node, and so every bridged accessory, is not published, while external accessories (like a robot vacuum on its own Matter node) still are.
Returns: true if only external Matter accessories publish from this bridge. Only ever true when isMatterEnabled() is.
Usage:
if (api.isMatterExternalsOnly?.()) {
log.info('Only external Matter accessories will be published from this bridge')
}Note
Requires Homebridge v2.5.0 or later.
The HAP equivalents, for plugins that publish over both HAP and Matter.
-
isHapEnabled()returnstrueif HAP is in use on this bridge (bridge.hapor_bridge.hap). It istrueby default, and stilltruein externals only mode. It is onlyfalsewhen HAP is turned off for the bridge. -
isHapExternalsOnly()returnstrueif the bridge accessory, and so every bridged HAP accessory, is not published, while external HAP accessories still are.
Usage:
// Skip building bridged HAP accessories that would never be published
if (api.isHapEnabled?.() !== false && !api.isHapExternalsOnly?.()) {
this.setupHapAccessories()
}The optional call (?.()) keeps the check safe on Homebridge versions before v2.5.0, where these methods do not exist.
All properties below are accessed via api.matter?.* (defensive) or api.matter!.* (after a isMatterEnabled() guard) — see Working with api.matter for the type rationale and the three access patterns.
UUID generator for creating unique accessory identifiers (alias of api.hap.uuid).
Type: HAP['uuid']
Methods:
-
generate(data: string): string- Generate deterministic UUID from string -
isValid(uuid: string): boolean- Validate UUID format
Usage:
const uuid = api.matter?.uuid.generate('my-light-123')
// Output: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
if (api.matter?.uuid.isValid(uuid)) {
// UUID is valid
}Important:
- UUIDs must be deterministic (same input = same output)
- Use unique identifiers (device ID, MAC address, etc.)
- UUIDs persist across restarts for state restoration
Available Matter device types for creating accessories.
Type: typeof deviceTypes (from Matter.js)
Common Device Types:
- Lighting:
OnOffLight,DimmableLight,ColorTemperatureLight,ExtendedColorLight - Switches & Outlets:
OnOffSwitch,OnOffOutlet,GenericSwitch(stateless remote — Homebridge ≥ 2.0.0) - Sensors:
ContactSensor,MotionSensor(the Matter occupancy sensor),TemperatureSensor,HumiditySensor,LightSensor,LeakSensor,SmokeSensor,AirQualitySensor - HVAC:
Thermostat,Fan - Closure:
DoorLock,WindowCovering - Robotic:
RoboticVacuumCleaner - Appliances and energy:
RoomAirConditioner,WaterHeater,EnergyEvse,SolarPower,BatteryStorage,ElectricalMeter(Homebridge ≥ 2.5.0) - Composed devices:
BridgedNode- a parent with no clusters of its own, for devices made ofparts(see Custom Devices)
Usage:
const accessory = {
UUID: api.matter?.uuid.generate('my-light'),
displayName: 'Living Room Light',
deviceType: api.matter?.deviceTypes.DimmableLight,
// ...
}See: Getting Started for the full list
Note
Requires Homebridge v2.4.0 or later.
The matter.js requirements behind the feature-gated device types, keyed to match deviceTypes.
Homebridge picks a cluster's features from the state an accessory declares, which suits almost every device. Use deviceRequirements when it cannot - for example, a thermostat that declares both setpoints is given AutoMode, so a device that heats and cools but has no auto mode composes the cluster itself. Homebridge leaves an already-composed cluster alone.
Usage:
deviceType: api.matter.deviceTypes.Thermostat.with(
api.matter.deviceRequirements.Thermostat.ThermostatServer.with('Heating', 'Cooling'),
)See: Customising Features
Direct access to some Matter.js cluster definitions for advanced use cases.
Type: Homebridge's own set of 24 matter.js clusters - not every Matter cluster. It holds AirQuality, BooleanState, CarbonMonoxideConcentrationMeasurement, ClosureControl, ColorControl, DeviceEnergyManagement, DoorLock, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, EnergyEvse, FanControl, KeypadInput, LevelControl, MediaPlayback, NitrogenDioxideConcentrationMeasurement, OnOff, OzoneConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm25ConcentrationMeasurement, PowerTopology, RvcOperationalState, Thermostat, ValveConfigurationAndControl and WindowCovering.
Usage:
// Access cluster attributes programmatically
const onOffAttrs = api.matter?.clusters.OnOff.attributes
console.log(Object.keys(onOffAttrs))
// Output: ['onOff', 'globalSceneControl', 'onTime', 'offWaitTime', 'startUpOnOff']
// Check which features a cluster can have
const levelControlFeatures = api.matter?.clusters.LevelControl.featuresWhen to use:
- Advanced cluster introspection
- Dynamic attribute discovery
- Custom cluster implementations
Note: Most plugins should use the higher-level APIs instead.
Typed handler interfaces for all known clusters. Provides IDE autocomplete for handler method names and argument types within the handlers object.
Import:
import type { ClusterHandlerMap } from 'homebridge'Usage: You don't normally need to import this — it's applied automatically when you define handlers on a MatterAccessory. Handler arguments are inferred:
handlers: {
onOff: {
on: async () => { /* no args */ },
},
levelControl: {
moveToLevelWithOnOff: async (request) => {
// request is typed as { level, transitionTime?, ... }
},
},
}Typed state interfaces for all known clusters. Provides IDE autocomplete for attribute names when using updateAccessoryState() and getAccessoryState() with known cluster names.
Import:
import type { ClusterStateMap } from 'homebridge'Usage: Applied automatically through typed overloads on updateAccessoryState and getAccessoryState:
// TypeScript knows { onOff: boolean } is valid for the 'onOff' cluster
await api.matter?.updateAccessoryState(uuid, 'onOff', { onOff: true })
// Return type is Partial<LevelControlState> | undefined
const state = await api.matter?.getAccessoryState(uuid, 'levelControl')Cluster name constants for type safety and autocomplete with state methods.
Type: typeof clusterNames
Available Names:
-
OnOff,LevelControl,ColorControl -
DoorLock,WindowCovering -
Thermostat,FanControl -
AirQuality,Pm25ConcentrationMeasurement,Pm10ConcentrationMeasurement -
TemperatureMeasurement,RelativeHumidityMeasurement - And many more...
Usage:
// Type-safe cluster references
api.matter?.updateAccessoryState(
uuid,
api.matter?.clusterNames.OnOff, // Autocomplete available!
{ onOff: true }
)
const state = await api.matter?.getAccessoryState(
uuid,
api.matter?.clusterNames.LevelControl
)Benefits:
- Autocomplete in IDEs
- Compile-time error checking
- Prevents typos in cluster names
See: State Management for complete list
Type-safe enum values for cluster attributes (modes, states, etc.).
Type: typeof MatterTypes (from Homebridge)
Common Types:
-
DoorLock.LockState- Lock states (Locked, Unlocked, etc.) -
DoorLock.LockType- Lock types (DeadBolt, Magnetic, etc.) -
FanControl.FanMode- Fan modes (Off, Low, Medium, High, Auto, etc.) -
FanControl.FanModeSequence- Supported mode sequences -
Thermostat.SystemMode- HVAC modes (Off, Heat, Cool, Auto, etc.) -
ColorControl.ColorMode- Color modes (HS, XY, ColorTemperature) -
RvcOperationalState.OperationalState- Vacuum states (Stopped, Running, Docked, etc.)
The robot vacuum run and clean mode tags are not in types - use the numbers: run mode Idle 16384, Cleaning 16385, Mapping 16386; clean mode Vacuum 16385, Mop 16386, VacuumThenMop 16387.
Usage:
// Door lock states
clusters: {
doorLock: {
lockState: api.matter?.types.DoorLock.LockState.Unlocked,
lockType: api.matter?.types.DoorLock.LockType.DeadBolt
}
}
// Fan modes
clusters: {
fanControl: {
fanMode: api.matter?.types.FanControl.FanMode.Auto,
fanModeSequence: api.matter?.types.FanControl.FanModeSequence.OffLowMedHigh
}
}
// Color modes
clusters: {
colorControl: {
colorMode: api.matter?.types.ColorControl.ColorMode.ColorTemperatureMireds
}
}Benefits:
- Type safety prevents invalid values
- IDE autocomplete shows available options
- Self-documenting code
- Compile-time validation
See: Matter Types for detailed examples
Matter protocol error classes. Throw one from a cluster handler to send a specific status code to the controller instead of failing the endpoint.
Type: typeof MatterStatus (from Homebridge)
Important
Requires Homebridge v2.3.0 or later. Reach these classes through api.matter.status rather
than importing MatterStatus from the homebridge package — see
Error Handling for why that import can stop the
whole plugin loading on some installs.
Available Classes:
-
Busy- Device is processing another operation (156) -
Timeout- Device did not respond in time (148) -
ConstraintError- Value out of range or otherwise invalid (135) -
InvalidAction- The request itself is not valid (128) -
InvalidInState- Device state does not allow the request (203) -
Failure- Generic failure (1) -
ResourceExhausted- No capacity left (137) -
PermissionDenied- Not permitted, sent as UnsupportedAccess (126) -
NotFound- Target does not exist (139) -
MatterProtocolError- Base class, for a status code not listed above -
isMatterProtocolError(error)- Check whether an error is one of these
Usage:
handlers: {
onOff: {
on: async () => {
if (deviceIsBusy) {
throw new api.matter!.status.Busy('Device is processing another command')
}
// ... control device
}
}
}In this plugin, accessories use the this.statusError(...) wrapper instead:
throw this.statusError('Busy', 'Device is processing another command')See: Error Handling for the full guide
Register Matter accessories with the platform (standard registration method).
Signature:
async registerPlatformAccessories(
pluginIdentifier: string,
platformName: string,
accessories: MatterAccessory[]
): Promise<void>Parameters:
-
pluginIdentifier- Plugin identifier (e.g.,'homebridge-example') -
platformName- Platform name (e.g.,'ExamplePlatform') -
accessories- Array of Matter accessories to register
Usage:
const PLUGIN_NAME = 'homebridge-example'
const PLATFORM_NAME = 'ExamplePlatform'
const accessories = [
{
UUID: api.matter?.uuid.generate('my-light'),
displayName: 'Living Room Light',
deviceType: api.matter?.deviceTypes.OnOffLight,
// ...
}
]
await api.matter?.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessories)Important Differences from HAP:
- This method is async and must be awaited (HAP's version is synchronous)
-
Register every accessory on every start, including the ones handed to
configureMatterAccessory()from the cache. Handlers are functions and cannot be cached, so a restored accessory answers every command with a failure until it is registered again- In HAP, a restored accessory is ready as soon as
configureAccessory()has run - Matter is different here
- In HAP, a restored accessory is ready as soon as
- A restored accessory keeps its cached state. The
clustersvalues you pass are the starting state for a new accessory only. To set the current state of a restored accessory, callupdateAccessoryState()once this promise resolves
The features field:
Note
Requires Homebridge v2.5.0 or later.
An accessory, and each of its parts, can carry an optional features object. It turns on optional Matter cluster features that cannot be worked out from the accessory's state. Features are capabilities, not state: only turn one on when the device and your handler really implement it.
| Feature | What it does |
|---|---|
rvcCleanMode.directModeChange |
Lets a controller change a robot vacuum's clean mode while it is running, not only while it is idle. Needs a rvcCleanMode.changeToMode handler - see Robotic Devices
|
const vacuum = {
// ...
features: {
rvcCleanMode: { directModeChange: true },
},
}Changing an accessory's features changes its Matter structure, so Homebridge rebuilds it on the next registration, as it does when its parts change.
When the promise resolves:
- The promise resolves once the bridge has finished adding the accessories, so an
updateAccessoryState()straight after it reaches an accessory that exists (Homebridge v2.5.0 or later) - This is especially important for external accessories (like robot vacuums), which take longer to set up
When to use:
- Standard accessory registration
- Multiple accessories on shared bridge
- Most common use case
- On every start, for every accessory your plugin exposes
QR Codes:
Robotic vacuum cleaners are published as their own Matter node, with their own QR code for pairing, even though they use this same method. In Homebridge v2.5.0 this is the only device type that works this way.
Unregister Matter accessories by UUID.
Signature:
async unregisterPlatformAccessories(
pluginIdentifier: string,
platformName: string,
accessories: MatterAccessory[]
): Promise<void>Parameters:
-
pluginIdentifier- Plugin identifier -
platformName- Platform name -
accessories- Array of accessories to unregister. Pass the accessories you registered, or the ones handed toconfigureMatterAccessory(): Homebridge readsdeviceTypeas well asUUID, to tell external accessories (like robot vacuums) apart from bridged ones
Usage:
// Unregister cached accessories that no longer exist on the device's account
const accessoriesToRemove = [...this.cachedAccessories.values()]
.filter(accessory => !currentDeviceIds.has(accessory.context.deviceId))
await api.matter?.unregisterPlatformAccessories(
PLUGIN_NAME,
PLATFORM_NAME,
accessoriesToRemove
)Important: Unlike the HAP equivalent, this method is async and must be awaited. From Homebridge v2.5.0 the promise resolves once the bridge has finished removing the accessories, external ones included, so a registerPlatformAccessories() straight after does not overlap it.
When to use:
- Removing accessories from Homebridge
- Cleanup during plugin shutdown
- User-initiated accessory removal
Update the stored context information of an accessory.
This only updates Homebridge's cached record of the accessory. It does not rename the device or change its Matter endpoint.
Signature:
async updatePlatformAccessories(
accessories: MatterAccessory[]
): Promise<void>Parameters:
-
accessories- Array of accessories to update (must includeUUID)
Usage:
const accessoriesToUpdate = [
{
...accessory,
context: { ...accessory.context, firmware: '2.1.0' },
}
]
await api.matter?.updatePlatformAccessories(accessoriesToUpdate)Important: Unlike the HAP equivalent, this method is async and must be awaited.
When to use:
- Modifying accessory
context
Update accessory cluster state when device changes externally (Flow B).
Signature:
// Typed overload — autocomplete for known cluster names and attributes
async updateAccessoryState<K extends keyof ClusterStateMap>(
uuid: string,
cluster: K,
attributes: Partial<ClusterStateMap[K]>,
partId?: string
): Promise<void>
// Generic fallback — any cluster name with Record<string, unknown>
async updateAccessoryState(
uuid: string,
cluster: string,
attributes: Record<string, unknown>,
partId?: string
): Promise<void>When you pass a known cluster name (e.g., 'onOff', 'levelControl'), TypeScript provides autocomplete for valid attribute names and types.
Parameters:
-
uuid- Accessory UUID -
cluster- Cluster name (useapi.matter?.clusterNames.*) -
attributes- Attributes to update (key-value pairs) -
partId- (Optional) Part ID for composed devices with multiple endpoints
Usage:
// Device turned on via native app
await api.matter?.updateAccessoryState(
uuid,
api.matter?.clusterNames.OnOff,
{ onOff: true }
)
// Brightness changed via physical button
await api.matter?.updateAccessoryState(
uuid,
api.matter?.clusterNames.LevelControl,
{ currentLevel: 200 }
)
// Update multiple attributes at once
await api.matter?.updateAccessoryState(
uuid,
api.matter?.clusterNames.ColorControl,
{
colorMode: api.matter?.types.ColorControl.ColorMode.ColorTemperatureMireds,
colorTemperatureMireds: 250
}
)
// Update a specific part in a composed device (e.g., outlet 2 in a power strip)
await api.matter?.updateAccessoryState(
uuid,
api.matter?.clusterNames.OnOff,
{ onOff: true },
'outlet-2' // Part ID
)IMPORTANT:
- ❌ DO NOT use inside handlers (state updates automatically)
- ✅ DO use for external changes (webhooks, polling, events)
When to use:
- Native app controls
- Physical button presses
- Webhook notifications
- Polling results
- MQTT/WebSocket messages
See: Core Concepts for more details
Get the current cluster state from a Matter accessory.
Signature:
// Typed overload — return type is inferred from the cluster name
async getAccessoryState<K extends keyof ClusterStateMap>(
uuid: string,
cluster: K,
partId?: string
): Promise<Partial<ClusterStateMap[K]> | undefined>
// Generic fallback — any cluster name
async getAccessoryState(
uuid: string,
cluster: string,
partId?: string
): Promise<Record<string, unknown> | undefined>When you pass a known cluster name, the return type is automatically narrowed (e.g., Partial<OnOffState> | undefined for 'onOff').
Parameters:
-
uuid- Accessory UUID -
cluster- Cluster name (useapi.matter?.clusterNames.*) -
partId- (Optional) Part ID for composed devices with multiple endpoints
Returns:
- Object with current attribute values, or
undefinedif not found
Usage:
// Read OnOff state
const state = await api.matter?.getAccessoryState(uuid, api.matter?.clusterNames.OnOff)
if (state?.onOff) {
log.info('Light is currently on')
}
// Read level control state
const levelState = await api.matter?.getAccessoryState(
uuid,
api.matter?.clusterNames.LevelControl
)
log.info(`Current brightness: ${levelState?.currentLevel}`)
// Check color mode
const colorState = await api.matter?.getAccessoryState(
uuid,
api.matter?.clusterNames.ColorControl
)
if (colorState?.colorMode === api.matter?.types.ColorControl.ColorMode.ColorTemperatureMireds) {
log.info(`Color temp: ${colorState.colorTemperatureMireds} mireds`)
}
// Read state from a specific part in a composed device (e.g., outlet 2 in a power strip)
const outlet2State = await api.matter?.getAccessoryState(
uuid,
api.matter?.clusterNames.OnOff,
'outlet-2' // Part ID
)
if (outlet2State?.onOff) {
log.info('Outlet 2 is currently on')
}When to use:
- Reading state after plugin restart
- Verifying current state before changes
- Debugging and logging
- Conditional logic based on state
Note: The state is persisted across restarts automatically.
Device-type-specific helpers for GenericSwitch accessories (stateless remotes / buttons — Matter Spec § 6.6, available since Homebridge 2.0.0). Built on top of updateAccessoryState for the Switch cluster, but encapsulates the currentPosition semantics so plugins don't have to.
Emit a low-level press or release for a GenericSwitch accessory.
Signature:
async emit(
uuid: string,
action: 'press' | 'release',
options?: { position?: number, partId?: string }
): Promise<void>Parameters:
-
uuid- UUID of theGenericSwitchaccessory -
action-'press'or'release' -
options.position- Button position (1-based). Defaults to1. Use for multi-position remotes. -
options.partId- Part ID for composed devices that include aGenericSwitchpart.
What it does:
Sets the Switch cluster's currentPosition to options.position ?? 1 for press, or 0 for release. The Matter.js SwitchServer reacts to that change by automatically firing the corresponding cluster events:
currentPosition change |
Event fired |
|---|---|
0 → n (press) |
initialPress |
n → 0 (release) |
shortRelease or longRelease, depending on longPressDelay timing |
| Multiple cycles |
multiPressComplete (when within multiPressDelay) |
Matter.js defaults: longPressDelay = 2000 ms, multiPressDelay = 300 ms.
Usage:
// Single-button press and release
await api.matter?.switch.emit(uuid, 'press')
await api.matter?.switch.emit(uuid, 'release')
// Multi-button remote: button 2
await api.matter?.switch.emit(uuid, 'press', { position: 2 })
await api.matter?.switch.emit(uuid, 'release', { position: 2 })
// GenericSwitch as a part inside a composed device
await api.matter?.switch.emit(uuid, 'press', { partId: 'button-top' })
await api.matter?.switch.emit(uuid, 'release', { partId: 'button-top' })Emit a high-level gesture (single press, double press, long press) — the helper translates each gesture into the correct press/release sequence with appropriate timing, so the Matter.js SwitchServer still classifies the events correctly.
Signature:
async emitGesture(
uuid: string,
gesture: 'singlePress' | 'doublePress' | 'longPress',
options?: {
position?: number
partId?: string
longPressDelayMs?: number
multiPressDelayMs?: number
}
): Promise<void>Parameters:
-
uuid- UUID of theGenericSwitchaccessory -
gesture-'singlePress','doublePress', or'longPress' -
options.position- Button position (1-based). Defaults to1. -
options.partId- Part ID for composed devices. -
options.longPressDelayMs- How long to hold for a long press. Defaults to2500ms (above Matter.js'slongPressDelayof 2000 ms). Only relevant for'longPress'. -
options.multiPressDelayMs- Delay between cycles of a double press. Defaults to100ms (well within Matter.js'smultiPressDelayof 300 ms). Only relevant for'doublePress'.
Translation:
| Gesture | Sequence |
|---|---|
singlePress |
press → release
|
doublePress |
press → release → (multiPressDelayMs) → press → release
|
longPress |
press → (longPressDelayMs) → release
|
Usage:
// Simple gestures on a single-button remote
await api.matter?.switch.emitGesture(uuid, 'singlePress')
await api.matter?.switch.emitGesture(uuid, 'doublePress')
await api.matter?.switch.emitGesture(uuid, 'longPress')
// Multi-button remote: long press on button 3
await api.matter?.switch.emitGesture(uuid, 'longPress', { position: 3 })
// Composed device part
await api.matter?.switch.emitGesture(uuid, 'singlePress', { partId: 'button-top' })
// Custom timing for slower remotes
await api.matter?.switch.emitGesture(uuid, 'longPress', { longPressDelayMs: 3000 })When to use:
- Integrations that already classify gestures upstream (e.g. a remote that POSTs
single/double/hold). - Anywhere you'd otherwise be calling
emit('press')andemit('release')manually with timers.
See: Section 6: Switches & Controllers for the full GenericSwitch accessory pattern, Matter Types for the cluster shape.
Matter protocol errors for command handlers. Use these errors to signal specific failure conditions to Matter controllers without crashing your accessory.
Tip
See the comprehensive Error Handling Guide for detailed information, examples, and best practices.
Getting them:
Look these up lazily rather than importing MatterStatus at the top of a module - a
top-level import makes Node resolve the homebridge package at load time, which fails on
installs that keep Homebridge outside the plugin tree and stops the whole plugin loading
(#5). See
Getting the Error Classes
for the helper used throughout these docs.
throw this.statusError('Busy', 'Device is processing another command')Available Error Types:
| Error Class | Status Code | Use Case |
|---|---|---|
this.statusError('Busy', ...) |
156 | Device is busy processing another operation |
this.statusError('Timeout', ...) |
148 | Operation timed out |
this.statusError('ConstraintError', ...) |
135 | Value out of range or invalid |
this.statusError('InvalidAction', ...) |
128 | Malformed command or invalid values |
this.statusError('InvalidInState', ...) |
203 | Current state prevents the operation |
this.statusError('Failure', ...) |
1 | Generic failure |
this.statusError('ResourceExhausted', ...) |
137 | Insufficient resources |
this.statusError('PermissionDenied', ...) |
126 | Permission denied |
this.statusError('NotFound', ...) |
139 | Entity not found |
Basic Usage:
handlers: {
onOff: {
on: async () => {
try {
await myDeviceAPI.turnOn()
} catch (error) {
if (error.code === 'TIMEOUT') {
throw this.statusError('Timeout', 'Device did not respond within 5 seconds')
}
throw this.statusError('Failure', 'Failed to turn on device')
}
}
}
}Helper Function:
// Check if an error is a Matter protocol error
if (api.matter!.status.isMatterProtocolError(error)) {
throw error // Re-throw as-is
}Benefits:
- ✅ Accessory stays online when errors occur (doesn't disappear from Home app)
- ✅ Home app shows brief spinner/exclamation, then reverts to previous state
- ✅ Better debugging with specific error types in Homebridge logs
- ✅ Proper Matter protocol compliance
Note
Home App Behavior: The Home app does not display different UI for different error types. Users always see a brief spinner or exclamation icon (
Automatic Protection:
Important
All handlers are automatically protected! If you throw ANY error (not just MatterStatus errors), it will be caught and converted to Status.Failure (generic error). Your accessory will stay online. Using MatterStatus errors is optional but recommended for providing specific error information.
When to use:
- Network/API timeouts → Use
this.statusError('Timeout', ...) - Invalid value ranges → Use
this.statusError('ConstraintError', ...) - Device state conflicts → Use
this.statusError('InvalidInState', ...) - Permission/security checks → Use
this.statusError('PermissionDenied', ...) - Resource limitations → Use
this.statusError('ResourceExhausted', ...) - Generic failures → Use
this.statusError('Failure', ...)or throw any error (auto-converted)
Read More:
- Introduction
- Core Concepts
- Getting Started
- State Management
- Monitoring External Changes
- Error Handling
- Best Practices
- Advanced Patterns
- Customising Features