Skip to content

Section 12 Robotic

Ben edited this page Feb 23, 2026 · 8 revisions

Appliances

This guide covers Matter device types for smart home appliances, including robotic vacuum cleaners.


Robotic Vacuum Cleaner

Property Value
Device Type api.matter.deviceTypes.RoboticVacuumCleaner
Description A robotic vacuum cleaner with run modes, operational states, and cleaning modes.
Matter Specification § 12.1

RvcRunMode Cluster

Controls the vacuum's run mode (Idle, Cleaning, etc.).

Attributes

Attribute Type Description
supportedModes array List of supported run modes
currentMode number Current run mode

Common Run Mode Tags:

Tag Value Name Description
16384 Idle Vacuum is idle/docked
16385 Cleaning Vacuum is cleaning
16386 Mapping Vacuum is mapping the space

RvcCleanMode Cluster

Controls the vacuum's cleaning mode (Vacuum, Mop, etc.).

Attributes

Attribute Type Description
supportedModes array List of supported clean modes
currentMode number Current clean mode

Common Clean Mode Tags:

Tag Value Name Description
16384 DeepClean Deep cleaning mode
16385 Vacuum Vacuum only mode
16386 Mop Mop only mode

RvcOperationalState Cluster

Reports the vacuum's operational state and provides control commands.

Attributes

Attribute Type Description
operationalStateList array List of supported operational states
operationalState number Current operational state ID

Common Operational State IDs:

State ID State Description
0 Stopped Vacuum is stopped
1 Running Vacuum is actively cleaning
2 Paused Vacuum is paused
3 Error Vacuum encountered an error
64 SeekingCharger Vacuum is seeking its charging dock
65 Charging Vacuum is charging on the dock
66 Docked Vacuum is docked and fully charged
67 EmptyingDustBin Vacuum is emptying dust bin (on dock)
68 CleaningMop Vacuum is cleaning mop (on dock)
69 FillingWaterTank Vacuum is filling water tank
70 UpdatingMaps Vacuum is processing map updates

Note: States 67-70 are maintenance states added in Matter 1.4.2. These represent automatic maintenance operations typically performed when the vacuum returns to its dock.

ServiceArea Cluster

Controls room/zone selection for targeted cleaning.

Attributes

Attribute Type Description
supportedMaps array List of supported maps (can be empty)
supportedAreas array List of supported areas/rooms
selectedAreas array Currently selected area IDs for cleaning
currentArea number Area ID currently being cleaned (null if none)
progress array Cleaning progress per area

Area Definition:

Each area in supportedAreas has this structure:

{
  areaId: 0,
  mapId: null,  // must be null when supportedMaps is empty
  areaInfo: {
    locationInfo: {
      locationName: 'Living Room',
      floorNumber: 0,
      areaType: 7,  // AreaNamespaceTag (e.g., 7 = LivingRoom)
    },
    landmarkInfo: null,
  },
}

PowerSource Cluster

Reports battery status, charge level, and remaining percentage.

Attributes

Attribute Type Description
status number Power source status (0 = Active)
order number Priority order (0 = Primary)
description string Power source description (e.g., "Battery")
batPercentRemaining number Battery percentage (0-200, where 200 = 100%)
batChargeLevel number Charge level indicator
batReplaceability number Whether battery is replaceable

Battery Charge Levels:

Level Name Description
0 Ok Battery charge is sufficient
1 Warning Battery charge is low
2 Critical Battery charge is critical

Battery Replaceability:

Value Name Description
0 Unspecified Replaceability not specified
1 Not replaceable Battery cannot be replaced
2 User replaceable User can replace battery
3 Factory replaceable Requires factory replacement

Note: batPercentRemaining uses 0.5% increments, where 200 = 100%. For example:

  • 200 = 100%
  • 100 = 50%
  • 50 = 25%
  • 0 = 0%

Reading State

const runMode = accessory.clusters.rvcRunMode.currentMode
const cleanMode = accessory.clusters.rvcCleanMode.currentMode
const opState = accessory.clusters.rvcOperationalState.operationalState
const battery = accessory.clusters.powerSource.batPercentRemaining

const runModes = ['Idle', 'Cleaning']
const cleanModes = ['Vacuum', 'Mop']
const opStates = ['Stopped', 'Running', 'Paused', 'Error']
const batteryPercent = (battery / 2).toFixed(1) // Convert to percentage

log.info(`Run: ${runModes[runMode]}, Clean: ${cleanModes[cleanMode]}, State: ${opStates[opState]}, Battery: ${batteryPercent}%`)

Handlers

// Handler types are automatically inferred from ClusterHandlerMap
handlers: {
  rvcRunMode: {
    /**
     * Called when user changes run mode via Home app.
     * Start/stop is controlled through run mode changes (Idle ↔ Cleaning).
     */
    changeToMode: async (request) => {
      const { newMode } = request
      const modeStr = ['Idle', 'Cleaning'][newMode] || 'Unknown'

      log.info(`Changing run mode to: ${modeStr}`)
      await myVacuumAPI.setRunMode(newMode)

      // Update operational state based on run mode
      if (newMode === 1) {
        // Starting to clean — set operational state to Running
        await api.matter.updateAccessoryState(
          uuid,
          api.matter.clusterNames.RvcOperationalState,
          { operationalState: 1 } // Running
        )
      } else if (newMode === 0) {
        // Switching to Idle — return to dock
        await api.matter.updateAccessoryState(
          uuid,
          api.matter.clusterNames.RvcOperationalState,
          { operationalState: 0 } // Stopped
        )
      }
      // State automatically updated by Homebridge for the run mode cluster
    },
  },

  rvcCleanMode: {
    /**
     * Called when user changes clean mode via Home app
     */
    changeToMode: async (request) => {
      const { newMode } = request
      const modeStr = ['Vacuum', 'Mop'][newMode] || 'Unknown'

      log.info(`Changing clean mode to: ${modeStr}`)
      await myVacuumAPI.setCleanMode(newMode)
      // State automatically updated by Homebridge
    },
  },

  // Note: rvcOperationalState only supports pause, resume, and goHome handlers.
  // Start/stop is controlled through rvcRunMode.changeToMode instead.
  rvcOperationalState: {
    /**
     * Called when user pauses the vacuum
     */
    pause: async () => {
      log.info('Pausing vacuum')
      await myVacuumAPI.pause()
      // Update state to Paused (2)
      await api.matter.updateAccessoryState(
        uuid,
        api.matter.clusterNames.RvcOperationalState,
        { operationalState: 2 }
      )
    },

    /**
     * Called when user resumes the vacuum
     */
    resume: async () => {
      log.info('Resuming vacuum')
      await myVacuumAPI.resume()
      // Update state to Running (1)
      await api.matter.updateAccessoryState(
        uuid,
        api.matter.clusterNames.RvcOperationalState,
        { operationalState: 1 }
      )
    },

    /**
     * Called when user sends the vacuum home
     */
    goHome: async () => {
      log.info('Sending vacuum home')
      await myVacuumAPI.goHome()
      // Update to seeking charger, then charging, then docked
      await api.matter.updateAccessoryState(
        uuid,
        api.matter.clusterNames.RvcOperationalState,
        { operationalState: 64 } // SeekingCharger
      )
    },
  },

  serviceArea: {
    /**
     * Called when user selects areas/rooms to clean
     */
    selectAreas: async (request) => {
      const { newAreas } = request
      const areaNames = newAreas.map((id: number) =>
        ['Living Room', 'Kitchen', 'Bedroom', 'Bathroom'][id] || `Area ${id}`
      )
      log.info(`Selecting areas: ${areaNames.join(', ')}`)
      await myVacuumAPI.selectAreas(newAreas)
    },

    /**
     * Called when user skips a specific area during cleaning
     */
    skipArea: async (request) => {
      const { skippedArea } = request
      log.info(`Skipping area: ${skippedArea}`)
      await myVacuumAPI.skipArea(skippedArea)
    },
  },
}

Updating State

// Update operational state
async function updateOperationalState(state: number) {
  await api.matter.updateAccessoryState(
    uuid,
    api.matter.clusterNames.RvcOperationalState,
    { operationalState: state }
  )

  const states = ['Stopped', 'Running', 'Paused', 'Error']
  log.info(`Operational state: ${states[state]}`)
}

// Update run mode
async function updateRunMode(mode: number) {
  await api.matter.updateAccessoryState(
    uuid,
    api.matter.clusterNames.RvcRunMode,
    { currentMode: mode }
  )

  const modes = ['Idle', 'Cleaning']
  log.info(`Run mode: ${modes[mode]}`)
}

// Update clean mode
async function updateCleanMode(mode: number) {
  await api.matter.updateAccessoryState(
    uuid,
    api.matter.clusterNames.RvcCleanMode,
    { currentMode: mode }
  )

  const modes = ['Vacuum', 'Mop']
  log.info(`Clean mode: ${modes[mode]}`)
}

// Update battery status
async function updateBatteryStatus(percent: number, chargeLevel: number) {
  const batPercentRemaining = percent * 2 // Convert percentage to 0-200 scale

  await api.matter.updateAccessoryState(
    uuid,
    api.matter.clusterNames.PowerSource,
    {
      batPercentRemaining,
      batChargeLevel: chargeLevel // 0 = Ok, 1 = Warning, 2 = Critical
    }
  )

  const levels = ['Ok', 'Warning', 'Critical']
  log.info(`Battery: ${percent}% (${levels[chargeLevel]})`)
}

// Update selected areas
async function updateSelectedAreas(areaIds: number[]) {
  await api.matter.updateAccessoryState(
    uuid,
    'serviceArea',
    { selectedAreas: areaIds }
  )
}

// Update current area being cleaned
async function updateCurrentArea(areaId: number | null) {
  await api.matter.updateAccessoryState(
    uuid,
    'serviceArea',
    { currentArea: areaId }
  )
}

Initial State:

clusters: {
  rvcRunMode: {
    supportedModes: [
      { label: 'Idle', mode: 0, modeTags: [{ value: 16384 }] },
      { label: 'Cleaning', mode: 1, modeTags: [{ value: 16385 }] },
    ],
    currentMode: 0,  // Idle
  },
  rvcCleanMode: {
    supportedModes: [
      { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
      { label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] },
    ],
    currentMode: 0,  // Vacuum
  },
  rvcOperationalState: {
    operationalStateList: [
      { operationalStateId: 0 },  // stopped
      { operationalStateId: 1 },  // running
      { operationalStateId: 2 },  // paused
      { operationalStateId: 3 },  // error
    ],
    operationalState: 0,  // Stopped
  },
  powerSource: {
    status: 0,  // 0 = Active
    order: 0,  // Primary power source
    description: 'Battery',
    batPercentRemaining: 200,  // 200 = 100% (use percent * 2)
    batChargeLevel: 0,  // 0 = Ok, 1 = Warning, 2 = Critical
    batReplaceability: 1,  // 0 = Unspecified, 1 = Not replaceable, 2 = User replaceable, 3 = Factory replaceable
  },
  serviceArea: {
    supportedMaps: [],  // empty - no map features
    supportedAreas: [
      {
        areaId: 0,
        mapId: null,  // must be null when supportedMaps is empty
        areaInfo: {
          locationInfo: { locationName: 'Living Room', floorNumber: 0, areaType: 7 },
          landmarkInfo: null,
        },
      },
      {
        areaId: 1,
        mapId: null,
        areaInfo: {
          locationInfo: { locationName: 'Kitchen', floorNumber: 0, areaType: 10 },
          landmarkInfo: null,
        },
      },
    ],
    selectedAreas: [0, 1],  // all areas selected by default
  },
}

Publishing:

const accessories = [
  {
    UUID: api.matter.uuid.generate('robot-vacuum'),
    displayName: 'Robot Vacuum',
    deviceType: api.matter.deviceTypes.RoboticVacuumCleaner,
    // ... configuration
  }
]

// Register using the standard method
await api.matter.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessories)

QR Codes:

Robotic vacuum cleaners registered via registerPlatformAccessories() receive their own unique QR code for pairing, separate from the main bridge QR code.


Clone this wiki locally