Руководство к устройству Light

Для идентификации светильников, переключения состояний питания и регулировки уровней яркости в осветительных приборах используются стандартные характеристики Matter.

Самый простой тип осветительного устройства – OnOffLight. Он реализуется с помощью функций OnOff и Identify. Также можно добавить функции LevelControl и LightEffects.

Другие типы освещения предлагают дополнительные возможности и реализуются с помощью различных признаков:

Тип устройства Home API Черты характера Пример приложения на Kotlin История успеха

Цветовая температура (свет)

ColorTemperatureLightDevice

home.matter.0000.types.010c

Осветительное устройство, которое можно включать и выключать, а также регулировать его яркость и цветовую температуру.

Обязательные характеристики
     matter Identify
     matter OnOff
     matter LevelControl
     matter ColorControl

Необязательные характеристики
     google LightEffects
     google ExtendedColorControl

включение и выключение, яркость
Light (Светлая).

Светильник с регулируемой яркостью

DimmableLightDevice

home.matter.0000.types.0101

Осветительное устройство, которое можно включать и выключать, регулировать интенсивность его света и управлять с помощью связанного датчика присутствия.

Обязательные свойства
     matter Identify
     matter OnOff
     matter LevelControl

Необязательные свойства
     google LightEffects

включение и выключение, яркость
Light (Светлая).

Светильник с расширенной цветовой гаммой

ExtendedColorLightDevice

home.matter.0000.types.010d

Осветительное устройство, которое можно включать и выключать, регулировать интенсивность его света и цветовую температуру. Оно может переключаться с помощью связанного датчика присутствия.

Обязательные характеристики
     matter Identify
     matter OnOff
     matter LevelControl
     matter ColorControl

Необязательные характеристики
     google LightEffects
     google ExtendedColorControl

включение и выключение, яркость
Light (Светлая).

Включение/выключение света

OnOffLightDevice

home.matter.0000.types.0100

Осветительное устройство, которое можно включать и выключать.

Обязательные характеристики
     matter Идентификация
     matter OnOff

Необязательные характеристики
     matter LevelControl
     google LightEffects

включение и выключение, яркость
Light (Светлая).

Прежде чем использовать функции или обновлять атрибуты, всегда проверяйте, поддерживаются ли они на устройстве. Подробнее о том, как управлять устройствами на платформеAndroid…

Требования

Прежде чем работать с осветительными устройствами и кривыми освещения, убедитесь, что ваше приложение инициализирует Home SDK и включает необходимые импорты:

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

Найти осветительные приборы

Чтобы найти осветительные устройства в доме, отфильтруйте список устройств по типу, например OnOffLightDevice и 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()

Вы также можете получить все осветительные устройства нескольких типов в одном фильтре:

// 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)
    }
  }

Как получить основные сведения об устройстве

   Реализовано в примере приложения для Android   

Функция BasicInformation содержит информацию об устройстве, например название и идентификатор поставщика, идентификатор и название товара (включая сведения о модели), а также версию ПО.

// 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.

Чтобы запустить режим идентификации, вызовите команду identify с длительностью identifyTime в секундах. Если передать 0, идентификация будет немедленно остановлена. Вы также можете вызвать triggerEffect, чтобы запустить определенный визуальный эффект, например Blink или 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
    )
  }
}

Проверьте подключение осветительного устройства

Подключение устройства проверяется на уровне типа устройства, поскольку некоторые устройства поддерживают несколько типов. Возвращаемое состояние представляет собой комбинацию состояний подключения для всех характеристик на этом устройстве.

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

Если у вас устройства разных типов и нет подключения к интернету, может отображаться статус partiallyOnline. Стандартные функции Matter могут быть доступны онлайн благодаря локальной маршрутизации, но облачные функции будут недоступны.

Как узнать IP-адрес устройства

Чтобы найти IP-адрес устройства, используйте атрибут networkInterfaces объекта GeneralDiagnosticsTrait. Адреса возвращаются в виде объектов Data, которые можно преобразовать в стандартные строки IPv4 или IPv6 с помощью фреймворка Network:

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
}

Управление осветительными приборами

Типы устройств OnOffLight и DimmableLight используют стандартные типажи Matter, чтобы идентифицировать светильники, переключать режимы питания и настраивать уровни яркости.

Включить или выключить свет

Используйте типаж OnOff, чтобы узнать текущее состояние питания устройства OnOffLight или DimmableLight и включить или выключить свет.

Прежде чем считывать атрибут onOff, убедитесь, что устройство его поддерживает. Некоторые устройства работают только как конечные точки для команд и не сообщают о своем состоянии.

// 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!")
}

Если onOff – true, индикатор горит. Если onOff – false, индикатор не горит.

Чтобы изменить состояние питания светильника, убедитесь, что типаж поддерживает нужную команду, а затем вызовите on(), off() или 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
}

Вы также можете напрямую вызвать on() и off(), чтобы задать явное состояние:

onOffTrait.off()
onOffTrait.on()

Чтобы выключить свет с постепенным визуальным переходом, вызовите offWithEffect() и укажите значение EffectIdentifierEnum:

// Turn off the light using the DyingLight effect.
onOffTrait.offWithEffect(
  effectIdentifier = OnOffTrait.EffectIdentifierEnum.DyingLight,
  effectVariant = 0u,
)

Настроить яркость

Для DimmableLight требуется свойство LevelControl, а для OnOffLight оно необязательно. Используйте этот типаж, чтобы считывать и контролировать уровень яркости света.

Атрибут currentLevel сообщает текущий уровень яркости по шкале от 0 до 254. Вы также можете проверить minLevel и maxLevel, чтобы определить допустимый диапазон яркости, поддерживаемый аппаратным обеспечением.

Чтобы установить определенный уровень яркости, вызовите moveToLevel(). Обратите внимание, что moveToLevel() меняет текущий уровень, только когда свет уже включен, если вы не настроите optionsOverride с помощью executeIfOff. Чтобы свет автоматически включался при установке ненулевого уровня яркости (или выключался при установке минимального уровня), используйте moveToLevelWithOnOff().

// Change the brightness of the light to 50%
levelControlTrait.moveToLevel(
  level = 127u.toUByte(),
  transitionTime = null,
  optionsMask = LevelControlTrait.OptionsBitmap(),
  optionsOverride = LevelControlTrait.OptionsBitmap(),
)

В следующем примере считывается текущий уровень яркости и используется moveToLevelWithOnOff(), чтобы установить яркость регулируемого источника света на 75 % (190u) за 1 секунду (10u десятых долей секунды):

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()
    )
  }
}

Вы также можете увеличить или уменьшить яркость относительно текущего уровня, вызвав stepWithOnOff() с StepModeEnum.Up или StepModeEnum.Down и stepSize.

Цвет элементов управления

Типаж ColorControl доступен как для ColorTemperatureLightDevice, так и для ExtendedColorLightDevice.

Это свойство позволяет настраивать цветовую температуру в микрообратных градусах, или майредах.

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()
  )
}

Вы также можете задать цвет, используя координаты цветности CIE 1931 и 16-битные значения оттенка и насыщенности.

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 – необязательный признак Google для типа ExtendedColorLightDevice, который упрощает управление цветом, принимая названия цветов, значения RGB или унифицированные значения HSV с записью с плавающей запятой, объединяющие цвет и яркость в одну единицу.

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

Управление кривыми освещения

Кривая освещения определяет автоматическое расписание, которое плавно меняет яркость и, если нужно, цветовую температуру целевых светильников в течение дня. Например, кривая освещения может постепенно увеличивать яркость света утром и уменьшать ее до более теплого тона вечером. Типы осветительных устройств, которые поддерживают только яркость, например регулируемый светильник, следуют кривой яркости и игнорируют цветовую температуру.

LightingCurve – это типаж Google Home platform на уровне структуры. Доступ к нему осуществляется через экземпляр Structure, а не через отдельное устройство освещения.

Каждое расписание в свойстве представлено объектом LightingCurveSetting, который связывает уникальный идентификатор settingId с объектом LightingCurveConfig. В таблице ниже описаны поля конфигурации LightingCurveConfig.

Поле Описание
curvePointsList Последовательность как минимум из двух элементов CurvePoint с разным временем. Каждая точка задает time (CurveTimeOfDay с hours от 0 до 23 и minutes от 0 до 59), brightnessPercent (от 0 до 100) и необязательную colorTemperature в кельвинах. Если для какой-либо точки указано значение colorTemperature, оно должно быть положительным для всех точек.
roomId Идентификатор целевой комнаты (например, room@{uuid}). Применяет кривую освещения к совместимым светильникам с регулируемой яркостью и цветовой температурой в этой комнате. Укажите roomId или devices.
devices Объект TargetDevices, содержащий список записей TargetDevice, каждая из которых включает id устройства и type идентификатор устройства (например, home.matter.0000.types.0101 для DimmableLight или home.matter.0000.types.010c для ColorTemperatureLight). Укажите devices или roomId.
daysOfWeekList Дни недели, в которые работает кривая освещения (DayOfWeek). Если список пуст, кривая освещения работает ежедневно.
adaptive Необязательное логическое значение, указывающее, является ли кривая освещения адаптивной. Это означает, что целевой источник света возобновляет активный уровень кривой при включении в течение запланированного окна. Если включен режим false, то при ручной настройке светильник отключается от кривой освещения до конца дня. Если true, то светильник, выключенный и снова включенный в период действия расписания, снова подключается к кривой освещения, в результате чего его яркость и цветовая температура соответствуют текущим значениям кривой и следуют за ней.
enabled Необязательное логическое значение, указывающее, активна ли настройка кривой освещения. При создании новой настройки по умолчанию используется значение true. Если в команде обновления не указан параметр enabled, то его состояние enabled не меняется.

Просмотр настроек кривой освещения

Чтобы проверить кривые освещения, настроенные для структуры, получите типаж LightingCurve из Structure и прочитайте его атрибут lightingCurveSettingsList.

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"
      )
    }
  }
}

Как создать настройку кривой освещения

Чтобы создать новое расписание кривой освещения для здания, создайте LightingCurveConfig по крайней мере с двумя записями CurvePoint, целевыми светильниками (указанными в roomId или devices) и активными днями недели. Затем передайте конфигурацию в createLightingCurveSetting(). Команда возвращает уникальный идентификатор settingId, назначенный новой настройке кривой освещения.

В примере ниже создается кривая освещения для комнаты в будние дни. В 7:00 яркость составляет 30 %, а температура – 2700 К. К 9:00 яркость увеличивается до 80 %, а температура – до 4000 К. К 21:00 яркость снижается до 20 %, а температура – до 2200 К.

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)
}

Чтобы настроить определенные осветительные приборы, а не всю комнату, укажите параметр devices с помощью 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
)

Как изменить настройки кривой освещения

Чтобы изменить существующую кривую освещения, например изменить ее точки, обновить активные дни недели или включить и отключить расписание, вызовите updateLightingCurveSetting() с целевым settingId и обновленным LightingCurveConfig.

suspend fun updateLightingCurve(
  structure: Structure,
  settingId: String,
  updatedConfig: LightingCurveTrait.LightingCurveConfig
) {
  val lightingCurve = structure.trait(LightingCurve).firstOrNull() ?: return

  lightingCurve.updateLightingCurveSetting(
    settingId = settingId,
    config = updatedConfig
  )
}

Как удалить настройку кривой освещения

Чтобы удалить настройку кривой освещения из структуры, вызовите deleteLightingCurveSetting() с settingId настройки, которую нужно удалить.

suspend fun deleteLightingCurve(
  structure: Structure,
  settingId: String
) {
  val lightingCurve = structure.trait(LightingCurve).firstOrNull() ?: return

  lightingCurve.deleteLightingCurveSetting(settingId = settingId)
}