Light devices use standard Matter traits to identify fixtures, toggle power states, and adjust brightness levels.
The most basic light device type is the
OnOffLight
which is implemented with the
OnOff and
Identify traits
and optionally supports the
LevelControl
trait and
LightEffects
trait.
Other light types offer additional capabilities and are implemented with different traits:
| Home APIs Device Type | Traits | Kotlin Sample App | Use Case |
|---|---|---|---|
|
Color Temperature Light
A lighting device that is capable of being switched on or off, the intensity of its light adjusted, and its color temperature adjusted. |
Required Traits matter Identify matter OnOff matter LevelControl matter ColorControl |
on and off, brightness |
Light |
|
Dimmable Light
A lighting device that is capable of being switched on or off, the intensity of its light adjusted, and being switched by means of a bound occupancy sensor. |
Required Traits matter Identify matter OnOff matter LevelControl |
on and off, brightness |
Light |
|
Extended Color Light
A lighting device that is capable of being switched on or off, the intensity of its light adjusted, its color temperature adjusted, and being switched by means of a bound occupancy sensor. |
Required Traits matter Identify matter OnOff matter LevelControl matter ColorControl |
on and off, brightness |
Light |
|
On/Off Light
A lighting device that is capable of being switched on or off. |
Required Traits matter Identify matter OnOff |
on and off, brightness |
Light |
Always check for attribute and command support for a device prior to using any features or attempting to update attributes. See Control devices on Android for more information.
Prerequisites
Before working with lighting devices and lighting curves, make sure your app initializes the Home SDK and includes the required imports:
import com.google.home.HomeDevice import com.google.home.Structure import com.google.home.google.DayOfWeek import com.google.home.google.ExtendedColorControl import com.google.home.google.ExtendedColorControlTrait import com.google.home.google.LightingCurve import com.google.home.google.LightingCurveTrait import com.google.home.matter.standard.ColorControl import com.google.home.matter.standard.ColorControlTrait import com.google.home.matter.standard.ColorTemperatureLightDevice import com.google.home.matter.standard.DimmableLightDevice import com.google.home.matter.standard.ExtendedColorLightDevice import com.google.home.matter.standard.GeneralDiagnostics import com.google.home.matter.standard.Identify import com.google.home.matter.standard.IdentifyTrait import com.google.home.matter.standard.LevelControl import com.google.home.matter.standard.LevelControlTrait import com.google.home.matter.standard.OnOff import com.google.home.matter.standard.OnOffLightDevice import com.google.home.matter.standard.OnOffTrait import com.google.home.matter.standard.RootNodeDevice import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull
Find light devices
To find lighting devices in a structure, filter the device list by device type,
such as
OnOffLightDevice
and
DimmableLightDevice
.
// Get all On/Off Light devices in the structure val onOffLights: List<HomeDevice> = home.devices().list().filter { it.has(OnOffLightDevice) } // Get all Dimmable Light devices in the structure val dimmableLights: List<HomeDevice> = home.devices().list().filter { it.has(DimmableLightDevice) } // Retrieve a specific device type instance to access its traits val onOffLightDevice: OnOffLightDevice? = onOffLights.firstOrNull()?.type(OnOffLightDevice)?.firstOrNull() val dimmableLightDevice: DimmableLightDevice? = dimmableLights.firstOrNull()?.type(DimmableLightDevice)?.firstOrNull()
You can also retrieve all lighting devices across multiple light device types in a single filter:
// Get a list of devices with similar device types (lights) val lightDevices = home.devices().map { devices -> devices.filter { it.has(DimmableLightDevice) || it.has(OnOffLightDevice) || it.has(ColorTemperatureLightDevice) || it.has(ExtendedColorLightDevice) } }
Get basic information about a device
Implemented in Sample App for Android
The BasicInformation
trait includes information like vendor name, vendor ID, product ID,
product name (includes model information), and software version for a device:
// Get device basic information. All general information traits are on the RootNodeDevice type. device.type(RootNodeDevice).first().standardTraits.basicInformation?.let { basicInformation -> println("vendorName ${basicInformation.vendorName}") println("vendorId ${basicInformation.vendorId}") println("productId ${basicInformation.productId}") println("productName ${basicInformation.productName}") println("softwareVersion ${basicInformation.softwareVersion}") }
Identify a lighting device
When a room contains multiple lights, you can help users locate a specific
physical fixture by triggering a visual identification state, such as blinking
the light for a few seconds. All four light device types require the
Identify trait.
Call the identify command with an identifyTime duration in seconds to start
the identification state. Passing 0 stops the identification state
immediately. You can also call triggerEffect to run a specific visual effect,
such as Blink or Breathe.
suspend fun identifyLight(lightDevice: DimmableLightDevice) {
val identifyTrait = lightDevice.standardTraits.identify ?: return
// Blink the light for 10 seconds so the user can locate it
if (identifyTrait.supports(Identify.Command.Identify)) {
identifyTrait.identify(identifyTime = 10u)
}
// Or trigger a specific visual identification effect
if (identifyTrait.supports(Identify.Command.TriggerEffect)) {
identifyTrait.triggerEffect(
effectIdentifier = IdentifyTrait.EffectIdentifierEnum.Blink,
effectVariant = IdentifyTrait.EffectVariantEnum.Default
)
}
}
Check the connectivity of a lighting device
Connectivity for a device is actually checked at the device type level because some devices support multiple device types. The state returned is a combination of the connectivity states for all traits on that device.
let lightConnectivity = dimmableLightDeviceType.metadata.sourceConnectivity .connectivityState
A state of partiallyOnline may be observed in the case of mixed device types
when there is no internet connectivity. Matter standard
traits may still be online due to local routing, but cloud-based traits will be
offline.
Get the IP address of the device
To find the IP address of the device, use the networkInterfaces attribute of
the
GeneralDiagnosticsTrait.
The addresses are returned as Data objects, which you can format to
standard IPv4 or IPv6 strings using the Network framework:
func getIpAddresses(trait: Matter.GeneralDiagnosticsTrait) -> [String] {
let interfaces = trait.attributes.networkInterfaces ?? []
var ipAddresses: [String] = []
for interface in interfaces {
for data in interface.iPv4Addresses {
if let ipv4 = IPv4Address(data) {
ipAddresses.append(String(describing: ipv4))
}
}
for data in interface.iPv6Addresses {
if let ipv6 = IPv6Address(data) {
ipAddresses.append(String(describing: ipv6))
}
}
}
return ipAddresses
}
Control lighting devices
The OnOffLight and DimmableLight device types use standard
Matter traits to identify fixtures, toggle power states,
and adjust brightness levels.
Turn a light on or off
Use the
OnOff
trait to read the current power state of an OnOffLight or DimmableLight and
to switch the light on or off.
Before reading the onOff attribute, verify that the device supports it. Some
devices operate as command-only endpoints and don't report state.
// Check if the OnOff trait supports the onOff attribute. if (onOffTrait.supports(OnOff.Attribute.onOff)) { println("onOffTrait supports onOff state") } else { println("onOffTrait is for a command only device!") }
When onOff is true, the light is on. When onOff is false, the light is
off.
To change the power state of the light, verify that the trait supports the
intended command and then call on(), off(), or toggle().
// Check if the OnOff trait supports the toggle command. if (onOffTrait.supports(OnOff.Command.Toggle)) { println("onOffTrait supports toggle command") } else { println("onOffTrait does not support stateful toggle command") }
// Calling a command on a trait. try { onOffTrait.toggle() } catch (e: HomeException) { // Code for handling the exception }
You can also call on() and off() directly to set an explicit state:
onOffTrait.off() onOffTrait.on()
To turn off a light with a gradual visual transition, call offWithEffect()
and specify an EffectIdentifierEnum value:
// Turn off the light using the DyingLight effect. onOffTrait.offWithEffect( effectIdentifier = OnOffTrait.EffectIdentifierEnum.DyingLight, effectVariant = 0u, )
Adjust brightness levels
A DimmableLight requires the
LevelControl
trait, and an OnOffLight can optionally support it. Use this trait to read and
control the brightness level of a light.
The currentLevel attribute reports the current brightness level on a scale
from 0 to 254. You can also inspect minLevel and maxLevel to determine
the valid brightness range supported by the hardware.
To set the brightness to a specific value, call moveToLevel(). Note that
moveToLevel() only changes the current level when the light is already on,
unless you configure optionsOverride with executeIfOff. To automatically
turn the light on when setting a non-zero brightness level (or turn it off when
setting the level to minimum), use moveToLevelWithOnOff().
// Change the brightness of the light to 50% levelControlTrait.moveToLevel( level = 127u.toUByte(), transitionTime = null, optionsMask = LevelControlTrait.OptionsBitmap(), optionsOverride = LevelControlTrait.OptionsBitmap(), )
The following example reads the current brightness level and uses
moveToLevelWithOnOff() to set a dimmable light to 75% brightness (190u) over
a 1-second (10u tenths of a second) transition:
suspend fun setLightBrightness(lightDevice: DimmableLightDevice) { val levelControl = lightDevice.standardTraits.levelControl ?: return // Read the current brightness level (0 to 254) val currentLevel: UByte? = levelControl.currentLevel println("Current brightness level: $currentLevel") // Move to ~75% brightness (190 out of 254) and turn on the light if it is off if (levelControl.supports(LevelControl.Command.MoveToLevelWithOnOff)) { levelControl.moveToLevelWithOnOff( level = 190u, transitionTime = 10u, optionsMask = LevelControlTrait.OptionsBitmap(), optionsOverride = LevelControlTrait.OptionsBitmap() ) } }
You can also increment or decrement brightness relative to the current level by
calling stepWithOnOff() with StepModeEnum.Up or StepModeEnum.Down and a
stepSize.
Control color
TheColorControl
trait is available on both the
ColorTemperatureLightDevice
and
ExtendedColorLightDevice.
This trait lets you adjust color temperature in micro-reciprocal degrees or mireds.
val colorControlTrait = colorTempLightDevice.standardTraits.colorControl ?: return // Read the current color temperature and hardware limits in mireds // (Mireds = 1,000,000 / Kelvin; e.g., 370 mireds ≈ 2700K warm white, 250 mireds = 4000K cool white) val currentMireds = colorControlTrait.colorTemperatureMireds val minMireds = colorControlTrait.colorTempPhysicalMinMireds ?: 153u // ~6500K (coolest) val maxMireds = colorControlTrait.colorTempPhysicalMaxMireds ?: 500u // 2000K (warmest) // Clamp target (370 mireds ≈ 2700K) to the device's physical bounds val targetMireds = 370u.toUShort().coerceIn(minMireds, maxMireds) if ( colorControlTrait.supports( ColorControl.Command.MoveToColorTemperature ) ) { colorControlTrait.moveToColorTemperature( colorTemperatureMireds = targetMireds, transitionTime = 20u, // 2.0 seconds (in 0.1s units) optionsMask = ColorControlTrait.OptionsBitmap(), optionsOverride = ColorControlTrait.OptionsBitmap() ) }
You can also set color using CIE 1931 chromaticity coordinates and 16-bit hue and saturation values.
val colorControlTrait = extendedColorLightDevice.standardTraits.colorControl ?: return // 1. Move to CIE 1931 xy chromaticity coordinates (scaled 0..65279) // Example: x = 0.3127 (20493), y = 0.3290 (21561) for D65 white point if (colorControlTrait.supports(ColorControl.Command.MoveToColor)) { colorControlTrait.moveToColor( colorX = 20493u, colorY = 21561u, transitionTime = 10u, // 1.0 second (in 0.1s units) optionsMask = ColorControlTrait.OptionsBitmap(), optionsOverride = ColorControlTrait.OptionsBitmap() ) } val currentX = colorControlTrait.currentX val currentY = colorControlTrait.currentY // 2. Move to 16-bit Enhanced Hue (0..65534) and 8-bit Saturation (0..254) if ( colorControlTrait.supports( ColorControl.Command.EnhancedMoveToHueAndSaturation ) ) { colorControlTrait.enhancedMoveToHueAndSaturation( enhancedHue = 43690u, // ~240° (Blue) on the 0..65534 scale saturation = 254u, // Full saturation transitionTime = 15u, // 1.5 seconds (in 0.1s units) optionsMask = ColorControlTrait.OptionsBitmap(), optionsOverride = ColorControlTrait.OptionsBitmap() ) } // 3. Start a hardware color loop across the device's hue range if (colorControlTrait.supports(ColorControl.Command.ColorLoopSet)) { colorControlTrait.colorLoopSet( updateFlags = ColorControlTrait.UpdateFlagsBitmap( updateAction = true, updateDirection = true, updateTime = true, updateStartHue = true ), action = ColorControlTrait.ColorLoopActionEnum .ActivateFromColorLoopStartEnhancedHue, direction = ColorControlTrait.ColorLoopDirectionEnum.Increment, time = 30u, // 30 seconds per full cycle startHue = 0u, optionsMask = ColorControlTrait.OptionsBitmap(), optionsOverride = ColorControlTrait.OptionsBitmap() ) }
ExtendedColorControl
is an optional Google trait on
ExtendedColorLightDevice
that simplifies color control by accepting color names, RGB values, or unified
floating-point HSV values that combine color and brightness in a single unit.
val extendedColorControlTrait = extendedColorLightDevice.googleTraits.extendedColorControl ?: return // 1. Set color by name if ( extendedColorControlTrait.supports( ExtendedColorControl.Command.MoveToColorName ) ) { extendedColorControlTrait.moveToColorName(colorName = "Lavender") } val currentColorName = extendedColorControlTrait.currentName // 2. Set color using 8-bit RGB values (0..255 per channel) // Automatically coordinates brightness (LevelControl) and color (ColorControl) if ( extendedColorControlTrait.supports( ExtendedColorControl.Command.MoveToColorRgb ) ) { extendedColorControlTrait.moveToColorRgb( red = 255u, green = 128u, blue = 0u ) } val currentRed = extendedColorControlTrait.currentRed val currentGreen = extendedColorControlTrait.currentGreen val currentBlue = extendedColorControlTrait.currentBlue // 3. Set color and brightness together using floating-point HSV // hue: 0.0f..360.0f, saturation: 0.0f..1.0f, value (brightness): 0.0f..1.0f if ( extendedColorControlTrait.supports( ExtendedColorControl.Command.MoveToColorHsv ) ) { extendedColorControlTrait.moveToColorHsv( hue = 210.0f, saturation = 0.85f, value = 0.75f ) } val currentHue = extendedColorControlTrait.currentHue val currentSaturation = extendedColorControlTrait.currentSaturation val currentValue = extendedColorControlTrait.currentValue
Manage lighting curves
A lighting curve defines an automated schedule that smoothly transitions the brightness and optional color temperature of target lights throughout the day. For example, a lighting curve can gradually brighten lights in the morning and dim them to a warmer tone in the evening. Light device types that only support brightness, such as Dimmable Light, follow the brightness curve and ignore color temperature.
The
LightingCurve
trait is a structure-level
Google Home platform trait. You access it
from a Structure instance
rather than from an individual lighting device.
Each schedule on the trait is represented by a LightingCurveSetting, which
pairs a unique settingId with a LightingCurveConfig. The following table
describes the configuration fields of LightingCurveConfig:
| Field | Description |
|---|---|
curvePointsList |
A sequence of at least two CurvePoint items with distinct times. Each point specifies a time (CurveTimeOfDay with hours from 0 to 23 and minutes from 0 to 59), a brightnessPercent (0 to 100), and an optional colorTemperature in Kelvin. If colorTemperature is specified for any point, it must be a positive value for all points. |
roomId |
The ID of the target room (for example, room@{uuid}). Applies the lighting curve to compatible dimmable and color temperature lights in that room. Specify either roomId or devices. |
devices |
A TargetDevices object containing a list of TargetDevice entries, each with a device id and device type ID (such as home.matter.0000.types.0101 for DimmableLight or home.matter.0000.types.010c for ColorTemperatureLight). Specify either devices or roomId. |
daysOfWeekList |
The days of the week on which the lighting curve runs (DayOfWeek). If the list is empty, the lighting curve runs daily. |
adaptive |
Optional boolean indicating whether the lighting curve is adaptive, meaning that a target light resumes the active curve level when turned on during the scheduled window. When false, a manual change to a light disengages the light from the lighting curve for the rest of the day. When true, a light that is turned off and then turned back on during the schedule window re-engages the light with the lighting curve, causing it to conform to the curve's current brightness and color temperature, and to follow the curve from there. |
enabled |
Optional boolean indicating whether the lighting curve setting is active. Defaults to true when creating a new setting. When enabled is omitted from an update command, the state of enabled doesn't change. |
Read lighting curve settings
To inspect the lighting curves configured for a structure, retrieve the
LightingCurve
trait from the Structure and read its
lightingCurveSettingsList
attribute.
suspend fun readLightingCurves(structure: Structure) {
val lightingCurve = structure.trait(LightingCurve).firstOrNull() ?: return
for (setting in lightingCurve.lightingCurveSettingsList) {
val settingId = setting.settingId
val config = setting.config
println("Setting ID: $settingId, enabled: ${config?.enabled}")
config?.curvePointsList?.forEach { point ->
println(
" Time: ${point.time?.hours}:${point.time?.minutes}, " +
"Brightness: ${point.brightnessPercent}%, " +
"ColorTemp: ${point.colorTemperature}K"
)
}
}
}
Create a lighting curve setting
To create a new lighting curve schedule on a structure, construct a
LightingCurveConfig with at least two CurvePoint entries, target lights
(specified by roomId or devices), and the active days of the week. Then
pass the configuration to createLightingCurveSetting(). The command returns
the unique settingId assigned to the new lighting curve setting.
The following example creates a weekday lighting curve for a room. It ramps from 30% brightness at a warm 2700K at 7:00 AM to 80% at a cooler 4000K at 9:00 AM, then dims to 20% at a very warm 2200K by 9:00 PM:
suspend fun createRoomLightingCurve(
structure: Structure,
targetRoomId: String
): String? {
val lightingCurve = structure.trait(LightingCurve).firstOrNull() ?: return null
val curvePoints = listOf(
LightingCurveTrait.CurvePoint(
time = LightingCurveTrait.CurveTimeOfDay(hours = 7, minutes = 0),
brightnessPercent = 30,
colorTemperature = 2700
),
LightingCurveTrait.CurvePoint(
time = LightingCurveTrait.CurveTimeOfDay(hours = 9, minutes = 0),
brightnessPercent = 80,
colorTemperature = 4000
),
LightingCurveTrait.CurvePoint(
time = LightingCurveTrait.CurveTimeOfDay(hours = 21, minutes = 0),
brightnessPercent = 20,
colorTemperature = 2200
)
)
val config = LightingCurveTrait.LightingCurveConfig(
curvePointsList = curvePoints,
roomId = targetRoomId,
daysOfWeekList = listOf(
DayOfWeek.MONDAY,
DayOfWeek.TUESDAY,
DayOfWeek.WEDNESDAY,
DayOfWeek.THURSDAY,
DayOfWeek.FRIDAY
),
adaptive = true,
enabled = true
)
// Returns the newly created settingId
return lightingCurve.createLightingCurveSetting(config = config)
}
To target specific lighting devices instead of an entire room, provide the
devices parameter using LightingCurveTrait.TargetDevices:
val targetDevices = LightingCurveTrait.TargetDevices(
devicesList = listOf(
LightingCurveTrait.TargetDevice(
id = dimmableLight.id.id,
type = DimmableLightDevice.TYPE_ID
)
)
)
val deviceCurveConfig = LightingCurveTrait.LightingCurveConfig(
curvePointsList = curvePoints,
devices = targetDevices,
daysOfWeekList = listOf(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY),
adaptive = true,
enabled = true
)
val settingId = lightingCurve.createLightingCurveSetting(
config = deviceCurveConfig
)
Update a lighting curve setting
To modify an existing lighting curve—such as changing its curve points,
updating the active days of the week, or enabling and disabling the
schedule—call updateLightingCurveSetting() with the target settingId and an
updated LightingCurveConfig.
suspend fun updateLightingCurve(
structure: Structure,
settingId: String,
updatedConfig: LightingCurveTrait.LightingCurveConfig
) {
val lightingCurve = structure.trait(LightingCurve).firstOrNull() ?: return
lightingCurve.updateLightingCurveSetting(
settingId = settingId,
config = updatedConfig
)
}
Delete a lighting curve setting
To remove a lighting curve setting from a structure, call
deleteLightingCurveSetting() with the settingId of the setting you want to
delete.
suspend fun deleteLightingCurve(
structure: Structure,
settingId: String
) {
val lightingCurve = structure.trait(LightingCurve).firstOrNull() ?: return
lightingCurve.deleteLightingCurveSetting(settingId = settingId)
}