Skip to content

API Reference

Ben edited this page Nov 1, 2026 · 18 revisions

API Reference

Complete reference for all Matter API methods and properties available in Homebridge.

Platform API Methods

api.isMatterAvailable(): boolean

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

api.isMatterEnabled(): boolean

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 matter object to bridge in config.json, for example "matter": {}
  • For child bridge: add a matter object to _bridge in the platform config
  • Set "enabled": false inside 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

api.isMatterExternalsOnly(): boolean

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')
}

api.isHapEnabled(): boolean and api.isHapExternalsOnly(): boolean

Note

Requires Homebridge v2.5.0 or later.

The HAP equivalents, for plugins that publish over both HAP and Matter.

  • isHapEnabled() returns true if HAP is in use on this bridge (bridge.hap or _bridge.hap). It is true by default, and still true in externals only mode. It is only false when HAP is turned off for the bridge.
  • isHapExternalsOnly() returns true if 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.


Matter API Properties

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.

api.matter?.uuid

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

api.matter?.deviceTypes

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 of parts (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


api.matter?.deviceRequirements

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


api.matter?.clusters

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.features

When to use:

  • Advanced cluster introspection
  • Dynamic attribute discovery
  • Custom cluster implementations

Note: Most plugins should use the higher-level APIs instead.


ClusterHandlerMap (TypeScript type)

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?, ... }
    },
  },
}

ClusterStateMap (TypeScript type)

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')

api.matter?.clusterNames

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


api.matter?.types

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


api.matter?.status

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


Matter API Methods

api.matter?.registerPlatformAccessories()

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
  • A restored accessory keeps its cached state. The clusters values you pass are the starting state for a new accessory only. To set the current state of a restored accessory, call updateAccessoryState() 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.


api.matter?.unregisterPlatformAccessories()

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 to configureMatterAccessory(): Homebridge reads deviceType as well as UUID, 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

api.matter?.updatePlatformAccessories()

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 include UUID)

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

api.matter?.updateAccessoryState()

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 (use api.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


api.matter?.getAccessoryState()

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 (use api.matter?.clusterNames.*)
  • partId - (Optional) Part ID for composed devices with multiple endpoints

Returns:

  • Object with current attribute values, or undefined if 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.


Switch Helper API (api.matter?.switch)

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.

api.matter?.switch.emit()

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 the GenericSwitch accessory
  • action - 'press' or 'release'
  • options.position - Button position (1-based). Defaults to 1. Use for multi-position remotes.
  • options.partId - Part ID for composed devices that include a GenericSwitch part.

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' })

api.matter?.switch.emitGesture()

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 the GenericSwitch accessory
  • gesture - 'singlePress', 'doublePress', or 'longPress'
  • options.position - Button position (1-based). Defaults to 1.
  • options.partId - Part ID for composed devices.
  • options.longPressDelayMs - How long to hold for a long press. Defaults to 2500 ms (above Matter.js's longPressDelay of 2000 ms). Only relevant for 'longPress'.
  • options.multiPressDelayMs - Delay between cycles of a double press. Defaults to 100 ms (well within Matter.js's multiPressDelay of 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') and emit('release') manually with timers.

See: Section 6: Switches & Controllers for the full GenericSwitch accessory pattern, Matter Types for the cluster shape.


Error Handling

MatterStatus

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 (⚠️), then the accessory reverts to its previous state. The primary benefit of using specific error types is for logging and debugging, not end-user error messages.

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:


Additional Resources

Clone this wiki locally