Getting Started - homebridge-plugins/homebridge-matter GitHub Wiki
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]
}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
}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 belowClick to see all available device types
api.matter.deviceTypes.OnOffLight
api.matter.deviceTypes.DimmableLight
api.matter.deviceTypes.ColorTemperatureLight
api.matter.deviceTypes.ExtendedColorLightapi.matter.deviceTypes.OnOffSwitch
api.matter.deviceTypes.OnOffOutletapi.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.SmokeSensorapi.matter.deviceTypes.Thermostat
api.matter.deviceTypes.Fan
api.matter.deviceTypes.RoomAirConditionerapi.matter.deviceTypes.DoorLockapi.matter.deviceTypes.WindowCoveringapi.matter.deviceTypes.RoboticVacuumCleanerapi.matter.deviceTypes.GenericSwitch
api.matter.deviceTypes.PumpStore custom data that persists across Homebridge restarts:
{
context: {
deviceId: 'my-light-123',
lastKnownState: true,
customData: { /* anything */ }
},
}
// Access later:
const deviceId = accessory.context.deviceIdDefine 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
},
}- 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:
- First run: Accessory created with
onOff: false(your initial value) - User turns light on in Home app → state becomes
onOff: true - Homebridge restarts → state is still
onOff: true(persisted) - Not reset to
onOff: false(initial values are ignored after first creation)
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]
}In your platform plugin, register only new accessories. Cached accessories are automatically re-registered:
import type { API, DynamicPlatformPlugin, Logger, MatterAccessory, PlatformAccessory, PlatformConfig } from 'homebridge'
export class MyPlatform implements DynamicPlatformPlugin {
private matterAccessories: Map<string, MatterAccessory> = new Map()
constructor(
public readonly log: Logger,
public readonly config: PlatformConfig,
public readonly api: API,
) {
this.api.on('didFinishLaunching', async () => {
// Discover devices from your API/network
const newAccessories = this.discoverNewAccessories()
// Register ONLY new accessories (cached ones are automatically restored)
if (newAccessories.length > 0) {
await this.api.matter.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, newAccessories)
}
})
}
configureAccessory(accessory: PlatformAccessory) {
// Required by DynamicPlatformPlugin interface (for HAP accessories)
// Not used for Matter accessories
}
configureMatterAccessory(accessory: MatterAccessory) {
// Called for each cached Matter accessory on startup
// Re-attach handlers here, accessory is automatically re-registered
this.matterAccessories.set(accessory.UUID, accessory)
// Re-attach handlers based on accessory.context data
// (See examples below)
}
private discoverNewAccessories(): MatterAccessory[] {
// Discover devices from your API/network
const allDevices = this.discoverDevices()
// Only return devices that aren't already cached
return allDevices
.filter(device => !this.matterAccessories.has(this.generateUUID(device)))
.map(device => this.createAccessory(device))
}
}How It Works:
-
Cached accessories:
configureMatterAccessory()is called → re-attach handlers → automatically re-registered -
New accessories: Call
registerPlatformAccessories()to add them - Same as HAP: Both HAP and Matter now work identically for cached accessory restoration