Getting Started - homebridge-plugins/homebridge-matter GitHub Wiki

Getting Started

Every Matter device registration follows this pattern:

import type { API } from 'homebridge'

export function registerMyDevice(api: API) {
  const accessory = {
    // Identity
    uuid: api.matter.uuid.generate('unique-device-id'),
    displayName: 'My Device',
    deviceType: api.matter.deviceTypes.OnOffLight,
    serialNumber: 'DEVICE-001',
    manufacturer: 'My Company',
    model: 'Model v1',

    // Optional: Persistent context storage
    context: {
      deviceId: 'my-device-123',
    },

    // State: Initial values for all cluster attributes
    // These values are only used when the accessory is first created
    // After that, Homebridge automatically persists and restores state across restarts
    clusters: {
      onOff: {
        onOff: false, // Initial state (only used on first creation)
      },
    },

    // Handlers: Respond to commands from Home app
    handlers: {
      onOff: {
        on: async () => {
          // Control your physical device
        },
        off: async () => {
          // Control your physical device
        },
      },
    },
  }

  return [accessory]
}

Device Identity

Every accessory needs a unique identification:

{
  uuid: api.matter.uuid.generate('unique-id'),  // Must be unique per device
  displayName: 'Living Room Light',             // Name shown in Home app
  deviceType: api.matter.deviceTypes.OnOffLight, // Matter device type
  serialNumber: 'LIGHT-001',                     // Unique serial number
  manufacturer: 'My Company',                    // Manufacturer name
  model: 'Smart Light v1',                       // Model identifier
}

Available Device Types

Access all Matter device types via api.matter.deviceTypes:

// Common examples
api.matter.deviceTypes.OnOffLight
api.matter.deviceTypes.DimmableLight
api.matter.deviceTypes.TemperatureSensor
api.matter.deviceTypes.Thermostat
api.matter.deviceTypes.DoorLock
// ... see full list below
Click to see all available device types

Lighting

api.matter.deviceTypes.OnOffLight
api.matter.deviceTypes.DimmableLight
api.matter.deviceTypes.ColorTemperatureLight
api.matter.deviceTypes.ExtendedColorLight

Switches & Outlets

api.matter.deviceTypes.OnOffSwitch
api.matter.deviceTypes.OnOffOutlet

Sensors

api.matter.deviceTypes.AirQualitySensor
api.matter.deviceTypes.TemperatureSensor
api.matter.deviceTypes.HumiditySensor
api.matter.deviceTypes.LightSensor
api.matter.deviceTypes.MotionSensor
api.matter.deviceTypes.ContactSensor
api.matter.deviceTypes.LeakSensor
api.matter.deviceTypes.SmokeSensor

HVAC

api.matter.deviceTypes.Thermostat
api.matter.deviceTypes.Fan
api.matter.deviceTypes.RoomAirConditioner

Security

api.matter.deviceTypes.DoorLock

Window Coverings

api.matter.deviceTypes.WindowCovering

Appliances

api.matter.deviceTypes.RoboticVacuumCleaner

Other

api.matter.deviceTypes.GenericSwitch
api.matter.deviceTypes.Pump

Persistent Context Storage

Store custom data that persists across Homebridge restarts:

{
  context: {
    deviceId: 'my-light-123',
    lastKnownState: true,
    customData: { /* anything */ }
  },
}

// Access later:
const deviceId = accessory.context.deviceId

Cluster Configuration

Define initial state for all clusters your device supports:

clusters: {
  onOff: {
    onOff: false,  // Boolean: true = on, false = off
  },
  levelControl: {
    currentLevel: 127,  // Number: 1-254 (127 = 50%)
    minLevel: 1,        // Minimum brightness
    maxLevel: 254,      // Maximum brightness
  },
}

Important Notes About State Persistence

  • These values are initial/default values only - used when the accessory is first created
  • Once created, Homebridge automatically persists all state changes
  • On restart, Homebridge restores the last known state, not these initial values
  • State persists across Homebridge restarts, updates, and system reboots
  • This works the same way as HAP - you don't need to manually save/restore state

Example lifecycle:

  1. First run: Accessory created with onOff: false (your initial value)
  2. User turns light on in Home app → state becomes onOff: true
  3. Homebridge restarts → state is still onOff: true (persisted)
  4. Not reset to onOff: false (initial values are ignored after first creation)

Complete Example: Simple On/Off Light

Here's a complete working example that connects to a hypothetical HTTP-based smart light:

import type { API } from 'homebridge'

import axios from 'axios'

export function registerLight(api: API, deviceId: string, ipAddress: string) {
  const accessory = {
    // Identity
    uuid: api.matter.uuid.generate(`http-light-${deviceId}`),
    displayName: `Light ${deviceId}`,
    deviceType: api.matter.deviceTypes.OnOffLight,
    serialNumber: `HTTP-${deviceId}`,
    manufacturer: 'Generic HTTP',
    model: 'v1.0',

    // Context: Store device information
    context: {
      deviceId,
      ipAddress,
    },

    // Initial state (only used on first creation)
    clusters: {
      onOff: {
        onOff: false,
      },
    },

    // Handlers: Control the physical device
    handlers: {
      onOff: {
        on: async () => {
          // Turn on the physical light
          await axios.post(`http://${ipAddress}/api/light`, {
            state: 'on',
          })
          // Homebridge automatically updates Matter state to onOff: true
        },
        off: async () => {
          // Turn off the physical light
          await axios.post(`http://${ipAddress}/api/light`, {
            state: 'off',
          })
          // Homebridge automatically updates Matter state to onOff: false
        },
      },
    },
  }

  return [accessory]
}

Registering Your Device

In your platform plugin, you must register ALL accessories (including cached ones) on every startup:

import type { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig } from 'homebridge'

export class MyPlatform implements DynamicPlatformPlugin {
  private accessories: MatterAccessory[] = []

  constructor(
    public readonly log: Logger,
    public readonly config: PlatformConfig,
    public readonly api: API,
  ) {
    this.api.on('didFinishLaunching', async () => {
      // Discover/create your Matter accessories
      const accessories = this.discoverAccessories()

      // IMPORTANT: Register ALL accessories on every startup
      // Unlike HAP, cached accessories must be re-registered
      await this.api.matter.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessories)
    })
  }

  configureAccessory(accessory: PlatformAccessory) {
    // Required by DynamicPlatformPlugin interface
    // For Matter: Store reference to rebuild and re-register in didFinishLaunching
    this.accessories.push(accessory)
  }

  private discoverAccessories(): MatterAccessory[] {
    // Discover devices from your API/network
    // Rebuild Matter accessories from cached accessories
    // Return ALL accessories (new + cached)
    return []
  }
}

Important Difference from HAP:

  • HAP: Cached accessories are automatically restored via configureAccessory() callback
  • Matter: You must call registerPlatformAccessories() for all accessories (cached + new) on every plugin startup
⚠️ **GitHub.com Fallback** ⚠️