Руководство по использованию дверного звонка для iOS

Тип устройства "Дверной звонок" реализуется с помощью трех признаков: PushAvStreamTransportTrait, который обрабатывает передачу аудио- и видеопотоков с помощью протоколов на основе push-уведомлений, WebRtcLiveViewTrait, который позволяет управлять прямыми трансляциями и обратной связью, и CameraSnapshotTrait, который предоставляет кешированные снимки предварительного просмотра и снимки в реальном времени.

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

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

Дверной звонок

GoogleDoorbellDeviceType

home.matter.6006.types.0113

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

Обязательные типажи
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

Дверной звонок

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

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

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

let vendorName = basicInfoTrait.attributes.vendorName!
let vendorID = basicInfoTrait.attributes.vendorID!
let productID = basicInfoTrait.attributes.productID!
let productName = basicInfoTrait.attributes.productName!
let softwareVersion = basicInfoTrait.attributes.softwareVersion!

Получить серийный номер

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

Чтобы получить серийный номер устройства, используйте команду GetSerialNumber типажа ExtendedBasicInformation. В примере ниже серийный номер сохраняется в переменной serialNumber:

// Assuming extendedBasicInformationTrait: Google.ExtendedBasicInformationTrait
let response = try await extendedBasicInformationTrait.getSerialNumber()
let serialNumber = response.serialNumber

быстрые ответы.

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

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

Быстрые ответы реализуются с помощью типажа PresetMessage.

Как воспроизвести заранее записанное сообщение

Чтобы воспроизвести стандартное сообщение, вызовите метод playPresetMessage, передав ему одно из строковых значений, найденных в свойстве availablePhraseTypes.


import GoogleHomeSDK
import GoogleHomeTypes

func playDoorbellPresetMessage(device: HomeDevice, phraseTypeString: String) async {
    // 1. Retrieve the GoogleDoorbellDeviceType helper on the device
    guard let doorbellDeviceType = await device.types.get(GoogleDoorbellDeviceType.self) else {
        print("This device is not a Google Doorbell or is currently uninitialized.")
        return
    }

    // 2. Extract the Google.PresetMessageTrait
    guard let presetMessageTrait = doorbellDeviceType.traits[Google.PresetMessageTrait.self] else {
        print("PresetMessageTrait is not supported on this device.")
        return
    }

    // 3. (Optional) Check available phrase types supported by the device
    if let availablePhrases = presetMessageTrait.attributes.availablePhraseTypes {
        let phraseTypeNames = availablePhrases.map { $0.phraseType }
        print("Supported quick response phrases: \(phraseTypeNames)")
    }

    // 4. Send the playPresetMessage command asynchronously
    do {
        try await presetMessageTrait.playPresetMessage(phraseType: phraseTypeString)
        print("Preset message successfully requested.")
    } catch {
        print("SDK error occurred playing preset message: \(error)")
    }
}

Выбрать язык озвучивания

Выбрать язык озвучивания

Установите активный язык устройства для определенного региона (например, "en_US"), используя метод setActiveLocale типажа LocalizationConfiguration.

// Setting the active language
// Assuming localizationConfigurationTrait: Matter.LocalizationConfigurationTrait
let selectedLocale = "en_US"
try await localizationConfigurationTrait.update {
    $0.setActiveLocale(selectedLocale)
}

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

Чтобы узнать, когда устройство в последний раз подключалось к облаку, используйте атрибут lastContactTimestamp типа ExtendedGeneralDiagnostics.

if let lastContactTimeStamp = extendedGeneralDiagnosticsTrait.attributes.lastContactTimestamp {
  self.lastContactTime = Date(timeIntervalSince1970: Double(lastConnectedTimeStamp))
}

Настройки типа крепления камеры

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

// 1. Get the Mount trait
guard let mountTrait = deviceType.traits[Google.MountTrait.self] else {
  print("Mount trait not supported or configured on this device.")
  return
}

// 2. Read the current mount state, detection type, and type name
let mountState         = mountTrait.attributes.mountState         // Type: Google.MountTrait.MountStateEnum?
let mountDetectionType = mountTrait.attributes.mountDetectionType // Type: Google.MountTrait.MountDetectionTypeEnum?
let mountTypeName      = mountTrait.attributes.mountTypeName      // Type: String?

// 3. Update the mount type override
try await mountTrait.update { mutableTrait in
  mutableTrait.setMountTypeOverride(.official)
}

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

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

let doorbellConnectivity = doorbellDeviceType.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
}

Как начать трансляцию

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

Чтобы начать трансляцию, отправьте строку SDP (Session Description Protocol) в метод startLiveView(offerSdp:) типажа WebRtcLiveViewTrait. Метод возвращает три значения:

  • SDP для сеанса.
  • Длительность сеанса в секундах.
  • Идентификатор сеанса, который можно использовать для продления или завершения сеанса.
public func sendOffer(offerSdp: String) async throws
-> (answerSdp: String, mediaSessionId: String, liveViewDuration: TimeInterval)
{
  do {
    // Sending StartLiveView command
    let response = try await liveViewTrait.startLiveView(
      offerSdp: offerSdp
    )
    // Received StartLiveView response
    return (
      answerSdp: response.answerSdp,
      mediaSessionId: response.mediaSessionId,
      liveViewDuration: TimeInterval(response.liveSessionDurationSeconds)
    )
  } catch {
    // Failed to send StartLiveView command
    throw error
  }
}

Как продлить трансляцию

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

У трансляций есть заданная продолжительность, по истечении которой они завершаются. Чтобы продлить активную трансляцию, отправьте запрос на продление с помощью метода extendLiveView(mediaSessionId:optionalArgsProvider:):

public func extendLiveView(mediaSessionId: String) async throws {
  do {
    // Extending live view
    let extendedDuration = try await liveViewTrait.extendLiveView(mediaSessionId: mediaSessionId)
  } catch {
    // Failed to extend live view
    throw error
  }
}

Как включить и отключить TalkBack

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

Чтобы начать обратную связь, вызовите метод startTalkback(mediaSessionId:optionalArgsProvider:) типажа WebRtcLiveViewTrait. Чтобы остановить, нажмите stopTalkback(mediaSessionId:).

public func toggleTwoWayTalk(isOn: Bool, mediaSessionId: String) async throws {
  do {
    if isOn {
      try await liveViewTrait.startTalkback(mediaSessionId: mediaSessionId)
    } else {
      try await liveViewTrait.stopTalkback(mediaSessionId: mediaSessionId)
    }
  } catch {
    throw HomeError.commandFailed("Failed to toggle twoWayTalk: \(error)")
  }
}

Управление трансляцией

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

Настройка качества трансляции позволяет оптимизировать использование пропускной способности в зависимости от контекста просмотра клиента (например, переключение на более низкое разрешение при отображении меньшего фрагмента предварительного просмотра, в виде сетки или режима "картинка в картинке").

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

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

  • Получение поддерживаемых вариантов качества: получение доступных разрешений потоковой передачи, поддерживаемых устройством. Код запрашивает свойство WebRtcLiveView, чтобы получить список поддерживаемых качеств потока в виде значений QualityHint (например, .sd, .hd, .fhd, .qhd или .uhd), используя свойство supportedQualityHints.

  • Изменить качество трансляции. Примените выбранный QualityHint, чтобы изменить разрешение активной трансляции (например, переключиться со стандартного разрешения на высокое). Функция updateQualityHint использует метод changeLiveViewQuality типажа WebRtcLiveView, чтобы применить выбранную конфигурацию QualityHint к активному сеансу воспроизведения медиаконтента.

public var supportedQualityHints: [Google.WebRtcLiveViewTrait.QualityHint] {
  return liveViewTrait?.attributes.supportedQualityHints ?? []
}

public func updateQualityHint(
  liveViewTrait: Google.WebRtcLiveViewTrait,
  hint: Google.WebRtcLiveViewTrait.QualityHint,
  mediaSessionId: String
) async {
  do {
    _ = try await liveViewTrait.changeLiveViewQuality(
      mediaSessionId: mediaSessionId,
      qualityHint: hint
    )
  } catch {
    // error
  }
}

Как включить или отключить функцию записи

Чтобы включить запись с камеры, передайте TransportStatusEnum.Active в метод setTransportStatus(transportStatus:optionalArgsProvider:) типажа PushAvStreamTransportTrait. Чтобы отключить возможность записи, передайте значение TransportStatusEnum.Inactive. В примере ниже мы объединяем эти вызовы в один, который использует Boolean для переключения возможности записи:

public func toggleIsRecording(isOn: Bool) {
  self.uiState = .loading

  guard let pushAvStreamTransportTrait else {
    // PushAvStreamTransportTrait not found.
    return
  }
  Task {
    do {
      try await pushAvStreamTransportTrait.setTransportStatus(
        transportStatus: isOn ? .active : .inactive)
      if isOn {
        do {
          self.player = try self.createWebRtcPlayer()
        } catch {
          // Failed to initialize WebRtcPlayer
          self.uiState = .disconnected
          return
        }
        await self.player?.initialize()
        self.uiState = .live
      } else {
        self.player = nil
        self.uiState = .off
      }
    } catch {
      // Failed to toggle onOff
    }
  }
}

Включение или отключение возможности записи на камере аналогично включению или отключению видео на камере. Когда видео на камере включено, она ведет запись (для целей событий и связанных с ними клипов).

Если возможность записи отключена (видео с камеры отключено):

  • Камера может по-прежнему отображаться как подключенная к интернету в соответствии с connectivityState типа устройства.
  • Прямая трансляция недоступна, и камера не обнаруживает события в облаке.

Проверьте, включена ли возможность записи

Чтобы определить, включена ли запись, проверьте, есть ли активные подключения. В примере ниже для этого определены две функции:

public func isDeviceRecording() -> Bool {
  guard let pushAvStreamTransportTrait else {
    // PushAvStreamTransportTrait not found.
    return false
  }
  guard
    let hasActiveConnection =
      pushAvStreamTransportTrait
      .attributes
      .currentConnections?
      .contains(where: { $0.transportStatus == .active })
  else {
    return false
  }
  return hasActiveConnection
}

Получать снимки с камеры

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

Используйте типаж CameraSnapshot, чтобы получать статические изображения с камеры. Такие снимки полезны в нескольких сценариях:

  • В качестве заполнителя в интерфейсе, пока загружается трансляция WebRTC.
  • В виде статического изображения для предварительного просмотра, которое при взаимодействии пользователя с ним заменяется прямой трансляцией.
  • В качестве фона для рисования и настройки пользовательских зон слежения.

Типаж CameraSnapshot позволяет получать снимки двумя способами в зависимости от того, нужен ли вам кешированный снимок для быстрой отрисовки или снимок в реальном времени по запросу.

Как получить снимок предварительного просмотра из кеша

Предварительный снимок – это кешированное изображение, которое SDK может получить, не активируя камеру. Это самый быстрый способ получить статичное изображение. Он идеально подходит для обзорных страниц камер или сеток панелей управления.

Чтобы получить предварительный снимок, прочитайте атрибут свойства preview_image_url:

guard let cameraSnapshotTrait = deviceType.traits[
  Google.CameraSnapshotTrait.self
] else {
  print("CameraSnapshot trait not supported on this device.")
  return
}

if let previewImageUrlString = cameraSnapshotTrait
  .attributes.preview_image_url,
   let url = URL(string: previewImageUrlString) {
  // Load the preview image from the URL
}

В зависимости от конфигурации и активности камеры URL предпросмотра указывает на кешированное изображение, созданное на основе недавней видеозаписи (первый I-кадр последнего видеофрагмента), или на снимок, периодически загружаемый камерой.

Чтобы указать актуальность изображения, облако Google Home встраивает заголовок X-Home-Image-Captured-At в обслуживаемое изображение JPEG/PNG в формате UTC. Вы можете проанализировать этот заголовок на стороне клиента, чтобы показать в приложении, когда был сделан снимок.

Как получить снимок в реальном времени по запросу

Если для пути пользователя требуется текущее изображение с камеры, например при настройке зон слежения, вы можете запросить мгновенный снимок. При запуске мгновенного снимка камера выходит из спящего режима, делает снимок и загружает его в облако.

Чтобы запросить снимок в реальном времени, используйте команду getLiveSnapshot():

do {
  let response = try await cameraSnapshotTrait.getLiveSnapshot()
  let snapshotUrlString = response.snapshot_url

  if let url = URL(string: snapshotUrlString) {
    // Load the live snapshot image from the URL
  }
} catch {
  print("Failed to get live snapshot: \(error)")
}

Аутентификация запросов на создание снимков

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

Все URL снимков указывают на защищенные медиафайлы и требуют токена OAuth 2.0 для области действия Home APIs v2. Чтобы скачать и обработать изображения, токен OAuth необходимо вставить в качестве токена на предъявителя в заголовок Authorization HTTP-запроса.

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

import UIKit

func loadSnapshotImage(from url: URL, home: Home) async -> UIImage? {
  do {
    // 1. Retrieve the OAuth access token from the Home APIs SDK
    let (accessToken, _) = try await home.permissions.authorization()

    // 2. Configure the HTTP GET request with the Bearer token
    var request = URLRequest(url: url)
    request.setValue(
      "Bearer \(accessToken)",
      forHTTPHeaderField: "Authorization"
    )

    // 3. Fetch the image data
    let (data, _) = try await URLSession.shared.data(for: request)
    return UIImage(data: data)
  } catch {
    print("Error loading snapshot image: \(error)")
    return nil
  }
}

Настройки батареи

Home API позволяют управлять различными настройками батареи.

Как настроить расход заряда батареи

Настройка баланса энергопотребления позволяет выбрать соотношение между временем работы от батареи и производительностью устройства. Вы можете создать несколько профилей, например "Экономия", "Сбалансированный" и "Производительность", и переключаться между ними.

Для этого нужно обновить атрибут currentEnergyBalance типажа EnergyPreference. Атрибут принимает целочисленный индекс, соответствующий определенному профилю, заданному в списке energyBalances устройства (например, 0 для EXTENDED, 1 для BALANCED и 2 для PERFORMANCE).

Значение null для currentEnergyBalance указывает, что на устройстве используется пользовательский профиль. Это состояние доступно только для чтения.

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

// Example energyBalances list
{
  "energy_balances": [
    {
      "step": 0,
      "label": "EXTENDED"
    },
    {
      "step": 50,
      "label": "BALANCED"
    },
    {
      "step": 100,
      "label": "PERFORMANCE"
    }
  ]
}
private func setBatteryUsage(to option: UInt8) async throws {
  _ = try await energyPreferenceTrait.update {
    $0.setCurrentEnergyBalance(option)
  }
}

Включить автоматический переход в режим энергосбережения

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

Чтобы настроить эту функцию, обновите атрибут currentLowPowerModeSensitivity типажа EnergyPreference. В этом атрибуте используется индекс для выбора уровня чувствительности, где 0 обычно означает Disabled, а 1 – Enabled или Automatic.

private func setAutoBatterySaver(to value: Bool) async throws {
  _ = try await energyPreferenceTrait.update {
    $0.setCurrentLowPowerModeSensitivity(value ? 1 : 0)
  }
}

Получить состояние зарядки батареи

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

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

self.chargingState = powerSourceTrait.attributes.batChargeState

var description: String
switch self.chargingState {
case .isCharging:
  description = "Charging"
case .isAtFullCharge:
  description = "Full"
case .isNotCharging:
  description = "Not Charging"
default:
  description = "Unknown"
}

Узнать уровень заряда батареи

Чтобы получить текущий уровень заряда батареи, используйте атрибут batChargeLevel типажа PowerSource. Уровень может быть OK, Warning (низкий) или Critical.

self.batteryLevel = powerSourceTrait.attributes.batChargeLevel

var description: String
switch self.batteryLevel {
case .ok:
  description = "OK"
case .warning:
  description = "Warning"
case .critical:
  description = "Critical"
default:
  description = "Unknown"
}

Подготовьте источник питания

Чтобы определить, какой источник питания использует устройство, используйте атрибуты BatPresent и wiredPresent типажа PowerSource.

if powerSourceTrait.attributes.wiredPresent ?? false {
  self.powerSourceType = .wired
} else if powerSourceTrait.attributes.batPresent ?? false {
  self.powerSourceType = .battery
} else {
  self.powerSourceType = nil
}

Проверьте, нужно ли заменить батарею.

Атрибут batReplaceability указывает, можно ли заменить батарею и как это сделать.

Если батарея заменяемая, атрибут batReplacementNeeded типажа PowerSource сообщает, нужно ли ее заменить.

// Get the battery replacement status
var batteryStatus = powerSourceTrait.attributes.batReplacementNeeded

if batteryStatus {
  print("Replace the battery.")
}

Если батарею может заменить пользователь, в batReplacementDescription описывается батарея, в том числе ее форм-фактор, химический состав и предпочтительный производитель.

Настройки звука

Home API позволяют управлять различными настройками звука.

Включить или выключить микрофон

Чтобы включить или выключить микрофон устройства, обновите атрибут microphoneMuted типа CameraAvStreamManagementTrait с помощью встроенной функции setMicrophoneMuted:

// Turn the device's microphone on or off
func setMicrophone(on: Bool) async {
  do {
    _ = try await self.cameraAvStreamManagementTrait?.update {
      $0.setMicrophoneMuted(!on)
    }
  } catch {
    // Error
  }
}

Как включить или отключить запись аудио

Чтобы включить или отключить запись звука на устройстве, обновите атрибут recordingMicrophoneMuted типажа CameraAvStreamManagementTrait с помощью встроенной функции setRecordingMicrophoneMuted:

// Turn audio recording on or off for the device
func setAudioRecording(on: Bool) async {
  do {
    _ = try await self.cameraAvStreamManagementTrait?.update {
      $0.setRecordingMicrophoneMuted(!on)
    }
  } catch {
    // Error
  }
}

Как настроить громкость динамика

Чтобы настроить громкость динамика устройства, обновите атрибут speakerVolumeLevel типа CameraAvStreamManagementTrait с помощью встроенной функции setSpeakerVolumeLevel:

// Adjust the camera speaker volume
func setSpeakerVolume(to value: UInt8) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setSpeakerVolumeLevel(value)
    }
  } catch {
    // Error
  }
}

Настройки зон слежения

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

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

Пользователь настраивает зоны слежения в партнерском приложении, выделяя определенные области в зоне обзора камеры. Эти определенные пользователем зоны затем преобразуются в структуры, используемые этим признаком. Подробнее о том, как настроить и использовать зоны слежения…

Обычно зоны слежения определяются с помощью двумерных декартовых координат. Типаж предоставляет TwoDCartesianVertexStruct для вершин и TwoDCartesianZoneStruct для определения зоны (название, вершины, цвет и использование).

Проверьте зоны слежения

Чтобы показать зоны слежения, проверьте атрибут zones свойства ZoneManagement.

let zoneManagementTrait: Google.ZoneManagementTrait

self.zones = zoneManagementTrait.attributes.zones ?? []

Как добавить зону слежения

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

В примере ниже показано, как создать зону "Крыльцо" с четырьмя вершинами, окрашенную в лососевый цвет (#F439A0) и используемую для обнаружения движения.

import GoogleHomeSDK
import GoogleHomeTypes

func createFrontPorchZone(trait: Google.ZoneManagementTrait) async {
  // 1. Define the vertices for the zone (2D Cartesian coordinates)
  // Values are UInt16, typically scaled to the device's twoDCartesianMax.
  let vertices = [
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 260, y = 422),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1049, y = 0),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048, y = 0),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048, y = 950),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1630, y = 1349),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 880, y = 2048),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 0, y = 2048),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 638, y = 1090)
  ]

  // 2. Define the zone structure using the given SDK struct
  let newZone = Google.ZoneManagementTrait.TwoDCartesianZoneStruct(
    name: "Front Porch",
    use: [.motion], // ZoneUseEnum.motion
    vertices: vertices,
    // Color is a hex string (for example, Salmon/Pink)
    color: "#F439A0"
  )

  do {
    // 3. Execute the raw command to add the zone to the device
    // This returns the created zone's ID (UInt16).
    var newZoneID = try await trait.createTwoDCartesianZone(zone: newZone)
  } catch {
    // Error
  }
}

Как изменить зону слежения

Чтобы обновить существующую зону, используйте команду updateTwoDCartesianZone. Для этой команды требуется zoneId и обновленный файл TwoDCartesianZoneStruct.

let zoneManagementTrait: Google.ZoneManagementTrait
let zoneID: UInt16
let zone: Google.ZoneManagementTrait.TwoDCartesianZoneStruct

do {
  _ = try await zoneManagementTrait.updateTwoDCartesianZone(
        zoneID: zoneID, zone: zone)
} catch {
  // Error
}

Как удалить зону слежения

Чтобы удалить зону, используйте команду removeZone с определенным значением zoneId.

let zoneManagementTrait: Google.ZoneManagementTrait
let zoneID: UInt16

do {
  _ = try await zoneManagementTrait.removeZone(zoneID: zoneID)
} catch {
  // Error
}

Триггеры звуковых событий

Типаж AvStreamAnalysis предоставляет интерфейс для управления триггерами обнаружения событий на камерах и дверных звонках. Триггеры, основанные на визуальных данных (например, люди или транспортные средства), могут быть привязаны к определенной зоне, а триггеры, связанные со звуком, обычно настраиваются на уровне устройства.

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

Режим Перечисляемое значение Описание
Звук Sound Распознавание звуков.
Речь PersonTalking Распознает речь.
Лай собаки DogBark Распознает собачий лай.
Звук бьющегося стекла GlassBreak Распознает звук разбитого стекла.
Датчик дыма SmokeAlarm Распознает сигналы датчиков дыма, которые часто издают звуки по шаблону T3 (три коротких сигнала, за которыми следует пауза).
датчик угарного газа CoAlarm Распознает сигналы датчиков угарного газа (CO), которые обычно представляют собой четыре коротких звуковых сигнала, за которыми следует пауза.

Как проверить статус распознавания звуков

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

При разработке для iOS вы обычно получаете доступ к свойству AvStreamAnalysis на устройстве, чтобы прочитать эти атрибуты.

// Example struct to store event triggers
public struct EventTrigger: Equatable {
  public var id: Google.AvStreamAnalysisTrait.EventTriggerTypeEnum
  public var enabled: Bool
}

let avStreamAnalysisTrait: Google.AvStreamAnalysisTrait

let possibleEventTriggers = avStreamAnalysisTrait.attributes.supportedEventTriggers ?? []
let enabledEventTriggers = avStreamAnalysisTrait.attributes.enabledEventTriggers ?? []

let eventTriggers [EventTrigger] = []
for trigger in possibleEventTriggers {
  self.eventTriggers.append(
    EventTrigger(
      id: trigger,
      enabled: enabledEventTriggers.contains(trigger)
    )
  )
}

Обновить набор включенных триггеров

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

// Example struct to store event triggers
public struct EventTrigger: Equatable {
  public var id: Google.AvStreamAnalysisTrait.EventTriggerTypeEnum
  public var enabled: Bool
}

let avStreamAnalysisTrait: Google.AvStreamAnalysisTrait
let eventTriggers: [EventTrigger]

let enabledEventTriggers = eventTriggers.map {
  Google.AvStreamAnalysisTrait.EventTriggerEnablement(
    eventTriggerType: $0.id,
    enablementStatus: $0.enabled ? .enabled : .disabled
  )
}

try await avStreamAnalysisTrait.setOrUpdateEventDetectionTriggers(
  eventTriggerEnablements: enabledEventTriggers
)

Режимы записи

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

Типаж RecordingMode предоставляет интерфейс для управления записью видео и изображений на камерах и дверных звонках. Пользователи могут выбрать непрерывную запись, запись событий или отключить запись полностью (только просмотр в реальном времени).

RecordingModeEnum определяет доступные стратегии записи:

Режим Перечисляемое значение Описание
Отключено Disabled Запись полностью отключена. В основном используется устаревшими устройствами.
НЗ (непрерывная запись) Cvr Видео записывается круглосуточно. Требуется подписка (например, Google Home Premium).
Запись событий Ebr Запись запускается событиями (человек, движение). Длительность видео зависит от продолжительности события и подписки.
ETR (запись по событию) Etr Короткая запись (например, 10 секунд), которая запускается событиями.
Улицы в AR-режиме LiveView Запись отключена, но пользователи по-прежнему могут смотреть трансляцию.
Картинки Images При возникновении событий вместо видео записываются снимки.

Проверьте режимы записи

Чтобы посмотреть текущую конфигурацию записи, проверьте атрибуты типажа RecordingMode:

// Example struct to store recording modes.
public struct RecordingMode: Hashable {
  public let id: UInt8
  public let mode: Google.RecordingModeTrait.RecordingModeEnum
}

let recordingModeTrait: Google.RecordingModeTrait

if let availableRecordingModes = recordingModeTrait.attributes.availableRecordingModes,
   let supportedRecordingModes = recordingModeTrait.attributes.supportedRecordingModes,
   let selectedRecordingMode = recordingModeTrait.attributes.selectedRecordingMode {

  var recordingModes: [RecordingMode] = []

  for recordingModeId in availableRecordingModes {
    guard Int(recordingModeId) < supportedRecordingModes.count,
          Int(recordingModeId) >= 0 else {
      // Out of bounds error
    }

    recordingModes.append(
      RecordingMode(
        id: recordingModeId,
        mode: supportedRecordingModes[Int(recordingModeId)].recordingMode,
      )
    )
  }
}

Как изменить режим записи

Перед обновлением убедитесь, что выбранный индекс из атрибута supportedRecordingModes присутствует в атрибуте availableRecordingModes.

Чтобы обновить выбранный режим, используйте функцию setSelectedRecordingMode, передав индекс выбранного режима:

let recordingModeTrait: Google.RecordingModeTrait
let recordingModeID: UInt8

_ = try await recordingModeTrait.update {
  $0.setSelectedRecordingMode(recordingModeID)
}

Другие настройки

Home API позволяют управлять и другими настройками.

Как включить или отключить ночную съемку

Чтобы включить или отключить ночное видение для камеры, используйте TriStateAutoEnum для обновления атрибута nightVision типажа CameraAvStreamManagementTrait с помощью встроенной функции setNightVision:

// Turn night vision on or off
func setNightVision(
  to value: Google.CameraAvStreamManagementTrait.TriStateAutoEnum
) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setNightVision(value)
    }
  } catch {
    // Error
  }
}

Как изменить яркость индикатора состояния

Чтобы изменить яркость индикатора состояния, используйте ThreeLevelAutoEnum для обновления атрибута statusLightBrightness типажа CameraAvStreamManagementTrait с помощью встроенной функции setStatusLightBrightness:

// Set the LED brightness
func setStatusLightBrightness(
  to value: Google.CameraAvStreamManagementTrait.ThreeLevelAutoEnum
) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setStatusLightBrightness(value)
    }
  } catch {
    // Error
  }
}

Как изменить область просмотра камеры

Область просмотра камеры – это то же самое, что и функция масштабирования и кадрирования, описанная в статье Как увеличить и улучшить видео с камеры Nest.

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

(x1,y1) -- (x2,y1)
   |          |
(x1,y2) -- (x2,y2)

Значения для ViewportStruct зависят от реализации интерфейса и камеры в приложении. Чтобы настроить область просмотра видео с камеры, обновите атрибут viewport типажа CameraAvStreamManagementTrait с помощью ViewportStruct, используя встроенную функцию setViewport.

func setCrop(x1: UInt16, y1: UInt16, x2: UInt16, y2: UInt16) {

  let viewport = Google.CameraAvStreamManagementTrait.ViewportStruct(
    x1: x1,
    y1: y1,
    x2: x2,
    y2: y2
  )

  Task {
    do {
      try await cameraAvStreamManagementTrait.update {
        $0.setViewport(viewport)
      }
    } catch {
      // Error
    }
  }

}

Создание TransportOptionsStruct

Для некоторых настроек требуется изменить свойства в TransportOptionsStruct, которые затем передаются в параметры транспорта потокового подключения. Для Swift эту структуру необходимо создать до обновления свойств.

Используйте эту вспомогательную функцию, чтобы сгенерировать структуру для следующих изменений настроек:

func getTransportOptions(
  transportOptions: Google.PushAvStreamTransportTrait.TransportOptionsStruct,
  wakeUpSensitivity: UInt8?,
  maxEventLength: UInt32?
) async throws
  -> Google.PushAvStreamTransportTrait.TransportOptionsStruct
{

  var newMotionTimeControl:
    Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct? = nil
  if let maxEventLength {
    guard let motionTimeControl = transportOptions.triggerOptions.motionTimeControl else {
      throw HomeError.failedPrecondition(
        // Error - cannot update max event length without motion time control
    }
    newMotionTimeControl =
      Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct(
        initialDuration: motionTimeControl.initialDuration,
        augmentationDuration: motionTimeControl.augmentationDuration,
        maxDuration: maxEventLength,
        blindDuration: motionTimeControl.blindDuration
      )
  }

  return Google.PushAvStreamTransportTrait.TransportOptionsStruct(
    streamUsage: .recording,
    videoStreamID: nil,
    audioStreamID: nil,
    tlsEndpointID: transportOptions.tlsEndpointID,
    url: transportOptions.url,
    triggerOptions: Google.PushAvStreamTransportTrait.TransportTriggerOptionsStruct(
      triggerType: .motion,
      motionZones: nil,
      motionSensitivity: wakeUpSensitivity,
      motionTimeControl: newMotionTimeControl,
      maxPreRollLen: nil
    ),
    ingestMethod: .cmafIngest,
    containerOptions: Google.PushAvStreamTransportTrait.ContainerOptionsStruct(
      containerType: .cmaf,
      cmafContainerOptions: nil
    ),
    expiryTime: nil
  )
}

private func getRecordingConnection() async throws
  -> Google.PushAvStreamTransportTrait.TransportConfigurationStruct?
{
  guard let pushAvStreamTransportTrait else {
    // Error - PushAvStreamTransport trait not available
    return nil
  }

  let connections = try await pushAvStreamTransportTrait.findTransport().transportConfigurations

  for connection in connections {
    guard let transportOptions = connection.transportOptions,
      transportOptions.streamUsage == .recording
    else {
      continue
    }

    return connection
  }

  return nil
}

Как включить или отключить аналитику

Каждое устройство может отдельно согласиться на отправку подробных аналитических данных в облако Google Home (см. Облачный мониторинг для Home API).

Чтобы включить аналитику для устройства, задайте для свойства analyticsEnabled объекта ExtendedGeneralDiagnosticsTrait значение true. Когда вы задаете значение для analyticsEnabled, для другого свойства, logUploadEnabled, автоматически задается значение true, что позволяет загружать файлы журналов аналитики в облако Google Home.

// Enable analytics
_ = try await extendedGeneralDiagnosticsTrait.update {
  $0.setAnalyticsEnabled(true)
}

// Disable analytics
_ = try await extendedGeneralDiagnosticsTrait.update {
  $0.setAnalyticsEnabled(false)
}

Конфигурации передачи и записи

В этом разделе рассматриваются настройки, связанные с качеством трансляции с камеры и активацией событий. Эти настройки управляются свойством PushAvStreamTransport.

Чтение настроек транспорта

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

// Assuming access to
// var pushAvStreamTransportTrait: Google.PushAvStreamTransportTrait
let connections = try await pushAvStreamTransportTrait.findTransport().transportConfigurations

// Locate the connection designated for recording
let recordingConnection = connections.first { connection in
    guard let transportOptions = connection.transportOptions else { return false }
    return transportOptions.streamUsage == .recording
}

let options = recordingConnection?.transportOptions

// 1. Bandwidth Quality (Video Stream ID)
let videoStreamId = options?.videoStreamID

// 2. Wake-up Sensitivity (Motion Sensitivity)
let wakeUpSensitivity = options?.triggerOptions.motionSensitivity

// 3. Max Event Length (Motion Trigger Time Control)
let maxEventLength = options?.triggerOptions.motionTimeControl?.maxDuration

Обновить настройки транспорта

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

Чтобы изменить эти настройки, используйте команду modifyPushTransport с TransportOptionsStruct.

// Example: Updating to Max Quality and 30s duration
let currentOptions = recordingConnection!.transportOptions!
let newOptions = Google.PushAvStreamTransportTrait.TransportOptionsStruct(
    streamUsage: .recording,
    videoStreamID: 2, // Max Quality
    tlsEndpointID: currentOptions.tlsEndpointID,
    url: currentOptions.url,
    triggerOptions: Google.PushAvStreamTransportTrait.TransportTriggerOptionsStruct(
        triggerType: .motion,
        motionSensitivity: 5, // Medium
        motionTimeControl: Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct(
            initialDuration: currentOptions.triggerOptions.motionTimeControl?.initialDuration ?? 10,
            augmentationDuration: currentOptions.triggerOptions.motionTimeControl?.augmentationDuration ?? 5,
            maxDuration: 30,
            blindDuration: currentOptions.triggerOptions.motionTimeControl?.blindDuration ?? 0
        )
    ),
    ingestMethod: currentOptions.ingestMethod,
    containerOptions: currentOptions.containerOptions
)

try await pushAvStreamTransportTrait.modifyPushTransport(
    connectionID: recordingConnection!.connectionID,
    transportOptions: newOptions
)

Определите качество пропускной способности

Свойство videoStreamId объекта TransportOptionsStruct соответствует определенной конфигурации видеопотока.

Чтобы получить поддерживаемые видеопотоки, используйте атрибут allocatedVideoStreams, который представляет собой список VideoStreamStructs. из свойства CameraAvStreamManagement для устройства.

Как настроить автоматический выход из спящего режима

Свойство motionSensitivity объекта TransportTriggerOptionsStruct соответствует следующим значениям:

Ярлык Значение (UInt8)
Низкая 1
Средний 5
Высокий 10

Как изменить максимальную продолжительность мероприятия

Свойство maxDuration объекта TransportMotionTriggerTimeControlStruct соответствует следующим значениям продолжительности UInt32 (в секундах):

  • 10, 15, 30, 60, 120, 180

Настройки звонка

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

Как изменить звук звонка

Чтобы изменить мелодию дверного звонка, сначала получите список мелодий, установленных на устройстве, с помощью атрибута installedChimeSounds типажа ChimeTrait:

doorbellChimeTrait.attributes.installedChimeSounds?.compactMap { chimeSound in
  return chimeSound.chimeID, chimeSound.name
}

Затем обновите атрибут selectedChime типажа ChimeTrait с помощью встроенной функции setSelectedChime:

func setDoorbellChime(chimeID: UInt8) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setSelectedChime(chimeID)
    }
  } catch {
    // Error
  }
}

Как использовать внешний звонок

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

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

Чтобы указать тип установленного внешнего звонка, используйте ExternalChimeType для обновления атрибута externalChime типажа ChimeTrait с помощью встроенной функции setExternalChime:

// Indicate the external chime is mechanical
func setExternalChime(to value: Google.ChimeTrait.ExternalChimeType) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setExternalChime(value)
    }
  } catch {
    // Error
  }
}

Как изменить длительность сигнала внешнего звонка

Продолжительность звонка в секундах можно настроить с помощью Home API. Если внешний звонок поддерживает настройку длительности, пользователь может захотеть ее изменить.

Значение зависит от характеристик внешнего звонка и рекомендуемой длительности сигнала.

Чтобы изменить длительность звонка, обновите атрибут externalChimeDurationSeconds типажа ChimeTrait с помощью встроенной функции setExternalChimeDurationSeconds:

// Change the external chime duration
func setExternalChimeDuration(to value: UInt16) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setExternalChimeDuration(value)
    }
  } catch {
    // Error
  }
}

Как включить тему звонка

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

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

Чтобы узнать, какие темы звонка доступны пользователю, создайте фильтр Timebox и используйте его для фильтрации результатов команды getAvailableThemes из типажа ChimeThemes. Вы увидите список доступных тем с их названиями.

В примере ниже показано, как отфильтровать список. Тема считается активной, если текущее время находится между временем ее начала и окончания (значения startTimeSeconds и endTimeSeconds соответственно). Если время начала не задано, то считается, что оно активно с самого начала. Если время окончания не задано, то заказ будет активен бессрочно. Если оба параметра отсутствуют, тема активна всегда.

let chimeThemes = try await chimeThemeTrait.getAvailableThemes().themes

if !chimeThemes.isEmpty {
  var chimeThemeSettings = []
  for chimeTheme in chimeThemes {
    let currentDateTime = UInt64(Date().timeIntervalSince1970)

    // Only show chime themes that are active.
    if chimeTheme.startTimeSeconds ?? 0 &lt;= currentDateTime
      &amp;&amp; chimeTheme.endTimeSeconds ?? UInt64.max &gt;= currentDateTime
    {
      self.chimeThemeSettings.append(chimeTheme.name)
    }
  }
}

Узнав название нужной темы, например Christmas, вы можете выбрать ее с помощью функции setSelectedTimeboxedThemeName() в свойстве ChimeThemes ChimeThemes.

private func setChimeTheme(to value: String) async throws {
  _ = try await chimeThemeTrait.update {
    $0.setSelectedTimeboxedThemeName(value)
  }
}

Настройки уведомлений о посетителях

Вы можете запрашивать и изменять настройки уведомлений о посетителях для дверных звонков с помощью типажа VisitorAnnouncement в Home API. Эта характеристика определяет, будет ли на умных колонках или дисплеях Google объявляться о присутствии посетителя, когда кто-то звонит в дверной звонок.

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

let visitorAnnouncementsEnabled: Bool = visitorAnnouncementTrait.attributes.visitorAnnouncementsEnabled

let value: Bool
_ = try await self.visitorAnnouncementTrait?.update {
  $0.setVisitorAnnouncementsEnabled(value)
}

Как вывести устройство из эксплуатации

Чтобы удалить дверной звонок из дома, вызовите метод device.decommission(). При выводе из эксплуатации пользователь увидит экран подтверждения от Google с предупреждением о том, что вся непрерывная видеозапись, история событий, клипы и данные библиотеки знакомых лиц будут навсегда удалены из структуры.

Подробные инструкции и примеры кода приведены в статье Как удалить устройство.