Thermostat device guide for iOS

The Thermostat device type may be implemented using several Home API traits, but the primary trait is Thermostat. Following are the required and optional traits for Thermostat devices.

Home APIs Device Type Traits Swift Sample App Use Case

Thermostat

ThermostatDeviceType

home.matter.0000.types.0301

A device that is capable of having either built-in or separate sensors for temperature, humidity or occupancy, and allows the desired temperature to be set. A Thermostat is capable of sending heating and/or cooling requirement notifications to a heating/cooling unit (for example, an indoor air handler) or is capable of including a mechanism to control a heating or cooling unit directly.

Required Traits
     matter IdentifyTrait
     matter ThermostatTrait

Optional Traits
     matter ThermostatUserInterfaceConfigurationTrait
     google ExtendedThermostatTrait
Thermostat

Automation API support

The following Thermostat traits and elements are supported in the Automation API.

Trait Trait type Element type Element
ThermostatTrait matter Attribute activePresetHandle
ThermostatTrait matter Attribute localTemperature
ThermostatTrait matter Attribute occupancy
ThermostatTrait matter Attribute occupiedCoolingSetpoint
ThermostatTrait matter Attribute occupiedHeatingSetpoint
ThermostatTrait matter Attribute outdoorTemperature
ThermostatTrait matter Attribute temperatureSetpointHoldDuration
ThermostatTrait matter Attribute unoccupiedCoolingSetpoint
ThermostatTrait matter Attribute unoccupiedHeatingSetpoint

Get the ambient temperature

To get the thermostat's ambient temperature using the ThermostatTrait, read the localTemperature attribute.

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(ThermostatDeviceType.self),
  let trait = thermostat.matterTraits.thermostatTrait
else {
  return
}
let temperature = trait.attributes.localTemperature

Automation API

let starterNode = starter(
  device1,
  ThermostatDeviceType.self,
  Matter.ThermostatTrait.self
)
let stateReaderNode =
  stateReader(device2, OnOffLightDeviceType.self, Matter.OnOffTrait.self)
let _ = automation(
  name: "Turn on a device",
  description: "Turn on a device when the temperature is too high"
) {
  starterNode
  stateReaderNode
  condition {
    // When the temperature is greater than 35 degrees C and the light is off.
    // `localTemperature` is in one hundredth of a degree Celsius.
    starterNode.localTemperature.greaterThan(3500)
      .and(stateReaderNode.onOff.equals(false))
  }
  // Turn on device 2
  action(device2, OnOffLightDeviceType.self) {
    Matter.OnOffTrait.on()
  }
}

Get the ambient temperature using TemperatureMeasurement

To get the thermostat's ambient temperature using the TemperatureMeasurement trait, read the measuredValue attribute.

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(TemperatureSensorDeviceType.self),
  let trait = thermostat.matterTraits.temperatureMeasurementTrait
else {
  return
}
let temperature = trait?.attributes.measuredValue

Automation API

let starterNode = starter(
  device1,
  TemperatureSensorDeviceType.self,
  Matter.TemperatureMeasurementTrait.self
)
let stateReaderNode = stateReader(
  device2,
  OnOffLightDeviceType.self,
  Matter.OnOffTrait.self
)
let _ = automation(
  name: "Turn on a device",
  description: "Turn on a device when the temperature is too high"
) {
  starterNode
  stateReaderNode
  condition {
    // When the temperature is greater than 35 degrees C and the light is off.
    // `measuredValue` is in one hundredth of a degree Celsius.
    starterNode.measuredValue.greaterThan(3500)
      .and(stateReaderNode.onOff.equals(false))
  }
  // Turn on device 2
  action(device2, OnOffLightDeviceType.self) {
    Matter.OnOffTrait.on()
  }
}

Get the ambient humidity

To get the thermostat's ambient humidity using the RelativeHumidityMeasurementTrait, read the measuredValue attribute.

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(HumiditySensorDeviceType.self),
  let trait = thermostat.matterTraits.relativeHumidityMeasurementTrait
else {
  return
}
let humidity = trait.attributes.measuredValue

Automation API

let starterNode = starter(
  device1,
  HumiditySensorDeviceType.self,
  Matter.RelativeHumidityMeasurementTrait.self
)
let stateReaderNode =
  stateReader(device2, OnOffLightDeviceType.self, Matter.OnOffTrait.self)
let _ = automation(
  name: "Turn on a device",
  description: "Turn on a device when the temperature is too high"
) {
  starterNode
  stateReaderNode
  condition {
    // When the humidity is greater than 50% and the light is off.
    starterNode.measuredValue.greaterThan(50)
      .and(stateReaderNode.onOff.equals(false))
  }
    // Turn on device 2
    action(device2, OnOffLightDeviceType.self) {
      Matter.OnOffTrait.on()
  }
}

Select the displayed temperature scale

To change the temperature unit of measurement that's used for the thermostat display, set the ThermostatUserInterfaceConfigurationTrait's temperatureDisplayMode attribute to either TemperatureDisplayModeEnum.Celsius or TemperatureDisplayModeEnum.Fahrenheit.

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(ThermostatDeviceType.self),
  let trait = thermostat.matterTraits.thermostatUserInterfaceConfigurationTrait
else {
  return
}
let updatedTrait = try await trait.update {
  $0.setTemperatureDisplayMode(.celsius)  // or .fahrenheit
}

Automation API

let starterNode =
  starter(structure, Google.VoiceStarterTrait.OkGoogleEvent.self) {
    Google.VoiceStarterTrait.OkGoogleEvent
    .query("Show the temperature in Fahrenheit")
  }
let stateReaderNode = stateReader(
  device1,
  ThermostatDeviceType.self,
  Matter.ThermostatUserInterfaceConfigurationTrait.self
)
let _ = automation(
  name: "Change temperature unit",
  description: "Change temperature unit to fahrenheit"
) {
  starterNode
  stateReaderNode
  condition {
  stateReaderNode.temperatureDisplayMode.notEquals(.fahrenheit)
  }
  action(device1, ThermostatDeviceType.self) {
    update(Matter.ThermostatUserInterfaceConfigurationTrait.self) {
      $0.setTemperatureDisplayMode(.fahrenheit)
    }
  }
}

Change the operating mode

The Thermostat can be limited to certain operating modes, defined by ThermostatTrait.SystemModeEnum, by setting the ThermostatTrait.Attributes.systemMode attribute, whose values are defined by ThermostatTrait.Attributes.SystemModeEnum.

Set system mode to auto

When the thermostat is set to SystemModeEnum.Auto, additional information about the running mode of the thermostat can be read from ThermostatTrait.Attributes.thermostatRunningMode which is populated with values from ThermostatRunningModeEnum.

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(ThermostatDeviceType.self),
  let trait = thermostat.matterTraits.thermostatTrait
else {
  return
}
let updatedTrait = try await trait.update {
  $0.setSystemMode(.auto)  // or .cool, .heat, .dry, etc.
}

Automation API

let starterNode = starter(
  device1,
  HumiditySensorDeviceType.self,
  Matter.RelativeHumidityMeasurementTrait.self
)
let stateReaderNode = stateReader(
  device1,
  ThermostatDeviceType.self,
  Matter.ThermostatTrait.self
)
let _ = automation(
  name: "Change mode to dry",
  description: "Change mode to dry when it's too humid"
) {
  starterNode
  stateReaderNode
  condition {
    // When the humidity is greater than 80%.
    starterNode.measuredValue.greaterThan(80)
      .and(stateReaderNode.systemMode.notEquals(.dry))
  }
  action(device1, ThermostatDeviceType.self) {
    update(Matter.ThermostatTrait.self) {
      $0.setSystemMode(.dry)
    }
  }
}

Get the running mode

The SimplifiedThermostatTrait is designed to streamline the process of setting the operating mode in automations. To change the thermostat's operating mode using the SimplifiedThermostatTrait, use the supportsSetSystemModeCommand, whose values are defined by SimplifiedThermostatTrait.SystemModeEnum.

This trait is only available for use with the Automation API.

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(ThermostatDeviceType.self),
  let trait = thermostat.matterTraits.thermostatTrait
else {
  return
}
let thermostatRunningMode = trait.attributes.thermostatRunningMode

Automation API

let scheduledTime = TimeOfDay(hours: 10, minutes: 0, seconds: 0)
let timeStarter = starter(structure, Google.TimeTrait.ScheduledEvent.self) {
  Google.TimeTrait.ScheduledEvent.clockTime(scheduledTime)
}
let stateReaderNode = stateReader(
  device1,
  ThermostatDeviceType.self,
  Matter.ThermostatTrait.self
)
let _ = automation(
  name: "Announce cooling",
  description: "Announce cooling at 10am every day when in auto mode"
) {
  timeStarter
  stateReaderNode
  condition {
    stateReaderNode.systemMode.equals(.auto)
      .and(stateReaderNode.thermostatRunningMode.equals(.cool))
  }
  action(structure) {
    Google.AssistantBroadcastTrait.broadcast(msg: "Cooling is on.")
  }
}

Get system mode with SimplifiedThermostat in an Automation

To determine the system modes that a thermostat can operate in, read the ThermostatTrait.Attributes.controlSequenceOfOperation, whose values are determined by the ThermostatTrait.ControlSequenceOfOperationEnum.

Automation API

let starterNode = starter(
  structure,
  Google.AreaPresenceStateTrait.self
)
let _ = automation(
  name: "Eco mode",
  description: "Change mode to eco when no one is around"
) {
  starterNode
  condition {
    starterNode.presenceState.equals(.presenceStateVacant)
  }
  action(device1, ThermostatDeviceType.self) {
    Google.SimplifiedThermostatTrait.setSystemMode(systemMode: .eco)
  }
}

Get the system modes the thermostat can operate in

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(ThermostatDeviceType.self),
  let trait = thermostat.matterTraits.thermostatTrait
else {
  return
}
let controlSequenceOfOperation = trait.attributes.controlSequenceOfOperation

Automation API

let starterNode =
  starter(structure, Google.VoiceStarterTrait.OkGoogleEvent.self) {
    Google.VoiceStarterTrait.OkGoogleEvent.query("Switch to cool mode")
  }
let stateReaderNode = stateReader(
  device1,
  ThermostatDeviceType.self,
  Matter.ThermostatTrait.self
)
let _ = automation(
  name: "Cool mode",
  description: "Change mode to cool at user's request"
) {
  starterNode
  condition {
    stateReaderNode.controlSequenceOfOperation.notEquals(.heatingOnly)
.and(stateReaderNode.controlSequenceOfOperation.notEquals(.heatingWithReheat))
  }
  action(device1, ThermostatDeviceType.self) {
    Google.SimplifiedThermostatTrait.setSystemMode(systemMode: .cool)
  }
}

Change the programming operation mode

The programming operation mode of the thermostat may be changed using the ThermostatTrait's thermostatProgrammingOperationMode, whose values are defined by the ProgrammingOperationModeBitmap.

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(ThermostatDeviceType.self),
  let trait = thermostat.matterTraits.thermostatTrait
else {
  return
}
let updatedTrait = try await trait.update {
  $0.setThermostatProgrammingOperationMode(.economy)
}

Automation API

let starterNode =
  starter(structure, Google.VoiceStarterTrait.OkGoogleEvent.self) {
    Google.VoiceStarterTrait.OkGoogleEvent
      .query("Activate thermostat schedule")
  }
let _ = automation(
  name: "Activate thermostat schedule",
  description: "Activate thermostat schedule at user's request"
) {
  starterNode
  action(device1, ThermostatDeviceType.self) {
    update {
      // Note that this sets the .autoRecovery and .economy bits to 0.
      // Future API updates will make it possible to preserve other bits.
      $0.setThermostatProgrammingOperationMode(.scheduleActive)
    }
  }
}

Change the temperature setpoint

To change the temperature setpoint using the ThermostatTrait, call the ThermostatTrait.setpointRaiseLower(mode:amount:).

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(ThermostatDeviceType.self),
  let trait = thermostat.matterTraits.thermostatTrait
else {
  return
}
// Lower the setpoint by 10 steps (i.e. 1 degree celsius)
let response = try await trait.setpointRaiseLower(mode: .cool, amount: -10)

Automation API

let scheduledTime = TimeOfDay(hours: 22, minutes: 0, seconds: 0)
let timeStarter = starter(structure, Google.TimeTrait.ScheduledEvent.self) {
  Google.TimeTrait.ScheduledEvent.clockTime(scheduledTime)
}
let stateReaderNode = stateReader(
  device1,
  ThermostatDeviceType.self,
  Matter.ThermostatTrait.self
)
let _ = automation(
  name: "Raise cooling setpoint",
  description: "Raise cooling setpoint by 4 degrees at 10pm every day"
) {
  timeStarter
  stateReaderNode
  condition {
    // Less than 18 degrees Celsius.
    // The unit is 100th of a degree Celsius.
    stateReaderNode.occupiedCoolingSetpoint.lessThan(1800)
  }
  action(device1, ThermostatDeviceType.self) {
    // Raise the cooling setpoint by 4 degrees Celsius.
    // The amount is in 10th of a degree Celsius.
    Matter.ThermostatTrait.setpointRaiseLower(mode: .cool, amount: 40)
  }
}

Prioritize a temperature setpoint

You can make a temperature setpoint take precedence over a preprogrammed schedule by setting the ThermostatTrait's temperatureSetpointHold attribute to TemperatureSetpointHoldEnum.SetpointHoldOn, or have the schedule take precedence by setting it to TemperatureSetpointHoldEnum.SetpointHoldOff.

Set ThermostatTrait.Attributes.temperatureSetpointHoldDuration to control how many minutes a setpoint hold is active.

Device API

let device: HomeDevice = ...
guard
  let thermostat = await device.types.get(ThermostatDeviceType.self),
  let trait = thermostat.matterTraits.thermostatTrait
else {
  return
}
let updatedTrait = try await trait.update {
  $0.setTemperatureSetpointHold(.setpointHoldOn)
  // Hold for 120 minutes
  $0.setTemperatureSetpointHoldDuration(120)
}

Automation API

let starterNode =
  starter(structure,Google.VoiceStarterTrait.OkGoogleEvent.self) {
    Google.VoiceStarterTrait.OkGoogleEvent
    .query("Prioritize thermostat setpoint")
  }
let stateReaderNode = stateReader(
  device1,
  ThermostatDeviceType.self,
  Matter.ThermostatTrait.self
)
let _ = automation(
  name: "Prioritize setpoint",
  description: "Prioritize thermostat setpoint at user's request"
) {
  starterNode
  action(device1, ThermostatDeviceType.self) {
    update {
      $0.setTemperatureSetpointHold(.setpointHoldOn)
    }
  }
}