Truy cập vào thiết bị và siêu dữ liệu thiết bị cho iOS

Bạn có thể truy cập vào API thiết bị thông qua các API cho Home dành cho iOS. Nhập các gói sau vào ứng dụng của bạn:

import GoogleHomeSDK
import GoogleHomeTypes

Để biết thêm thông tin, hãy xem bài viết Mô hình dữ liệu trên iOS.

Xử lý lỗi

Một số phương thức trong các API cho Home sẽ gửi một HomeError, vì vậy, bạn nên sử dụng khối do-catch để phát hiện HomeError trong các lệnh gọi đó.

Khi xử lý HomeError, hãy kiểm tra các trường code và message để tìm hiểu xem đã xảy ra lỗi gì.

Mọi lỗi không được xử lý sẽ khiến ứng dụng của bạn gặp sự cố.

Để biết thêm thông tin, hãy xem phần Xử lý lỗi.

Hãy xem phần Gửi lệnh đến thiết bị để biết ví dụ.

Cuộc gọi mẫu

Lấy danh sách thiết bị

Khi tham chiếu đến đối tượng Home, hãy gọi devices() để lấy Query của các thiết bị có thể truy cập. Gọi phương thức batched() của Query. Phương thức này sẽ phát ra một Tập hợp phản ánh trạng thái hiện tại của Nhà mỗi khi siêu dữ liệu của thiết bị thay đổi. Hoặc gọi Query.list() để lấy thông tin tổng quan nhanh về các thiết bị hiện có. Đây là một phương thức tiện lợi giúp đăng ký luồng batched() và trả về giá trị đầu tiên được phát ra. Query.stream() tạo ra một luồng phát ra các giá trị mới khi siêu dữ liệu của thiết bị thay đổi, chẳng hạn như tên, phòng hoặc cấu trúc của thiết bị. Trong nội bộ, thao tác này sử dụng batched() và chỉ phát ra các thuộc tính đã thay đổi.

// Get a list of all devices accessible to the user
let homeDevices = try await self.home.devices().list()

Tại đây, bạn có thể truy cập vào trạng thái của từng thiết bị và gửi lệnh đến thiết bị.

Với bản phát hành 1.8 của các API cho Home, bạn có thể yêu cầu API biểu thị từng thiết bị nhiều phần dưới dạng một thiết bị bằng cách đặt tham số enableMultipartDevices của phương thức devices() thành true. Hãy xem phần Thiết bị nhiều phần trên iOS để biết thêm thông tin.

Lấy loại thiết bị

Để lấy các loại thiết bị liên kết với một thiết bị, hãy đọc thuộc tính types của thiết bị. Thuộc tính này sẽ trả về một DeviceTypeController.

Gọi DeviceTypeController.subscribe(_:) để đăng ký nhận thông tin cập nhật cho một loại thiết bị cụ thể:

let devices = try await self.home.devices().list()
if let device = devices.first(where: { $0.id == myDeviceId }) {
  var receivedUpdate1 = false
  var receivedUpdate2 = false
  device.types.subscribe(OnOffLightDeviceType.self)
    .assertNoFailure()
    .sink { device in
      if !receivedUpdate1 {
        receivedUpdate1 = true
        Task {
          try await device.matterTraits.onOffTrait?.on()
        }
        return
      }
      if !receivedUpdate2 {
        receivedUpdate2 = true
        return
      }
      fatalError("Received unexpected update")
    }
}

Nếu thiết bị không hỗ trợ loại thiết bị được chỉ định, thì thiết bị sẽ trả về một Empty Publisher hoàn tất ngay lập tức.

Nếu thiết bị hỗ trợ một loại thiết bị cụ thể, bạn có thể lấy tên người dùng cho loại đó bằng cách gọi get():

if let device = devices.first(where: { $0.id == myDeviceId }) {
  let _ = await device.types.get(OnOffLightDeviceType.self)
}

Nếu thiết bị không hỗ trợ loại đã chỉ định, thì phương thức này sẽ trả về nil.

Gọi DeviceTypeController.subscribeAll() để lấy Publisher của DeviceTypeCollection. Lớp này cho phép bạn kiểm tra xem thiết bị có loại thiết bị cụ thể hay không:

if let device = devices.first(where: { $0.id == myDeviceId }) {
  device.types.subscribeAll()
    .assertNoFailure()
    .sink { types in
      let lightDeviceType = types[OnOffLightDeviceType.self]
      let fanDeviceType = types[FanDeviceType.self]
    }
}

Lấy đặc điểm loại thiết bị

Loại thiết bị là điểm truy cập để đọc các đặc điểm, vì chúng phân tách một thiết bị thành các phần chức năng (như điểm cuối trong Matter).

Các đặc điểm này cũng tính đến xung đột đặc điểm trong trường hợp một thiết bị có hai loại thiết bị, cả hai đều có thể có cùng một đặc điểm. Ví dụ: nếu một thiết bị vừa là Loa vừa là Đèn có thể điều chỉnh độ sáng, thì thiết bị đó sẽ có hai đặc điểm Bật/Tắt và hai đặc điểm Kiểm soát mức độ.

Một loại xung đột đặc điểm khác có thể xảy ra khi một thiết bị có hai đặc điểm có cùng tên. Ví dụ: onOff có thể tham chiếu đến một thực thể của đặc điểm OnOff tiêu chuẩn hoặc có thể tham chiếu đến một thực thể của đặc điểm OnOff do nhà sản xuất xác định. Để loại bỏ mọi sự mơ hồ tiềm ẩn về đặc điểm dự kiến, hãy tham chiếu một đặc điểm thông qua một trong hai tập hợp đặc điểm trên mỗi loại thiết bị.

Đối với các đặc điểm tiêu chuẩn, tức là những đặc điểm tương tự như các cụm tiêu chuẩn Matter, hãy sử dụng matterTraits. Ví dụ: để lấy một đặc điểm cụ thể cho loại thiết bị Đèn có thể điều chỉnh độ sáng:

if let dimmableLightDeviceType =
  await device.types.get(DimmableLightDeviceType.self)
{
  // Accessing standard trait on the type.
  let levelControlTrait =
    dimmableLightDeviceType.matterTraits.levelControlTrait.self
}

Đối với các đặc điểm của Google, hãy sử dụng googleTraits:

if let doorbellDeviceType = await device.types.get(GoogleDoorbellDeviceType.self) {
  // Accessing Google trait on the type.
  let doorbellPressTrait =
    doorbellDeviceType.traits[Google.DoorbellPressTrait.self]
}

Để truy cập vào một đặc điểm dành riêng cho nhà sản xuất, hãy tham chiếu đặc điểm đó thông qua thuộc tính traits, nhưng hãy thêm tên gói của nhà sản xuất vào trước:

let deviceType = await device1?.types.get(OnOffLightDeviceType.self)
// Accessing custom trait on the type.
if let spinnerTrait = deviceType?.traits[ExampleOrganization.SpinnerTrait.self] {
  let rpmVal = spinnerTrait.attributes.rpm
}

Đọc trạng thái thiết bị

Hãy xem ví dụ sau đây về cách kiểm tra thuộc tính OnOff từ đặc điểm Bật/Tắt của thiết bị:

let lightDevices = devices.filter {
  $0.types.contains(OnOffLightDeviceType.self)
}
let light1 = lightDevices.first
let lightDeviceTypeOptional = await light1?.types.get(OnOffLightDeviceType.self)
if let onOffTrait = lightDeviceTypeOptional?.matterTraits.onOffTrait {
  let onOffVal = onOffTrait.attributes.onOff
}

Lấy danh sách thiết bị có một đặc điểm cụ thể

Để có danh sách thiết bị có một đặc điểm cụ thể, bạn cần lặp lại các thiết bị, loại thiết bị của từng thiết bị và đặc điểm của từng loại thiết bị. Ví dụ: để lấy danh sách các thiết bị trong nhà đều có đặc điểm Bật/Tắt:

// Get all light devices that support levelControl
var levelControlDevices: [HomeDevice] = []
let allDevices = try await home.devices().list()
for device in allDevices {
  if let deviceType = await device.types.get(OnOffLightDeviceType.self) {
    if deviceType.traits.contains(Matter.LevelControlTrait.self) {
      levelControlDevices.append(device)
    }
  }
}

Xem Chỉ mục đặc điểm trên iOS để biết danh sách đầy đủ các đặc điểm có trong các API cho Home.

Lấy danh sách các thiết bị có loại thiết bị tương tự

Cách lấy danh sách các thiết bị đại diện cho tất cả đèn trong nhà:

// Get a list of devices with similar device types (lights)
let lightDevices =
  try await self.home.devices().list().compactMap {
    $0.types.contains(DimmableLightDeviceType.self)
      || $0.types.contains(OnOffLightDeviceType.self)
      || $0.types.contains(ColorTemperatureLightDeviceType.self)
      || $0.types.contains(ExtendedColorLightDeviceType.self)
  }

Có nhiều loại thiết bị trong các API cho Home có thể đại diện cho một loại thiết bị chính. Ví dụ: không có loại thiết bị "Đèn". Thay vào đó, có 4 loại thiết bị khác nhau có thể đại diện cho một đèn, như trong ví dụ trước. Do đó, để có được thông tin toàn diện về loại thiết bị cấp cao hơn trong nhà, bạn phải đưa vào nhiều loại thiết bị.

Hãy xem Các loại thiết bị được hỗ trợ trên iOS để biết danh sách đầy đủ các loại thiết bị và đặc điểm của chúng có trong các API cho Home.

Lấy Tên nhà cung cấp, Mã nhà cung cấp hoặc Mã sản phẩm của một thiết bị

Đặc điểm BasicInformationTrait bao gồm thông tin như Mã nhà cung cấp, Mã sản phẩm, Tên sản phẩm và Số sê-ri của thiết bị:

guard
  let vendorName =
    basicInfoTrait.attributes.vendorName
else {
  fatalError("Failed to get vendorName")
}
guard
  let vendorID =
    basicInfoTrait.attributes.vendorID
else {
  fatalError("Failed to get vendorID")
}
guard
  let productID =
    basicInfoTrait.attributes.productID
else {
  fatalError("Failed to get productID")
}

Nhận dạng thiết bị từ đám mây đến đám mây cho nhà sản xuất thiết bị

Nếu bạn là nhà sản xuất thiết bị và chế tạo thiết bị Cloud-to-cloud, để xác định thiết bị Cloud-to-cloud thông qua đặc điểm BasicInformation, bạn có thể đưa các trường chuỗi này vào phản hồi SYNC của thiết bị:

  • Mã nhà cung cấp do Connectivity Standards Alliance (Alliance) cấp: "matterOriginalVendorId": "0xfff1",

  • Mã nhận dạng sản phẩm giúp xác định duy nhất một sản phẩm của nhà cung cấp: "matterOriginalProductId": "0x1234",

  • Giá trị nhận dạng riêng biệt của thiết bị, được tạo theo cách thức riêng của nhà sản xuất: "matterUniqueId": "matter-device-id",

Khi nhập các trường chuỗi này, hãy sử dụng Matter Mã nhà cung cấp và mã sản phẩm nếu bạn có. Nếu bạn không phải là thành viên của Alliance và chưa được chỉ định các mã nhận dạng này, bạn có thể để trống các trường matterOriginalVendorId và matterOriginalProductId, đồng thời cung cấp matterUniqueId làm giá trị nhận dạng.

Ví dụ về phản hồi SYNC cho thấy cách sử dụng các trường này:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "agentUserId": "1836.15267389",
    "devices": [
      {
        "id": "456",
        "type": "action.devices.types.LIGHT",
        "traits": [
          "action.devices.traits.OnOff",
          "action.devices.traits.Brightness",
          "action.devices.traits.ColorSetting",
        ],
        "willReportState": true,
        "deviceInfo": { ... },
        "matterOriginalVendorId": "0xfff1",
        "matterOriginalProductId": "0x1234",
        "matterUniqueId": "matter-device-id",
        "otherDeviceIds": [
          {
            "deviceId": "local-device-id",
          }
        ]
      }
    ]
  }
}

Để biết thêm thông tin, hãy xem tài liệu về Cloud-to-cloud SYNC.

Siêu dữ liệu về thiết bị và đặc điểm

Các thiết bị và đặc điểm trong các API cho Home có siêu dữ liệu liên kết với chúng, có thể giúp quản lý trải nghiệm người dùng trong một ứng dụng.

Mỗi đặc điểm trong các API cho Home đều chứa một thuộc tính sourceConnectivity, trong đó có thông tin về trạng thái trực tuyến và vị trí của một đặc điểm (định tuyến cục bộ hoặc từ xa).

Lấy loại chính của thiết bị

Một số thiết bị có thể hiển thị nhiều loại thiết bị thông qua các API cho Home. Để đảm bảo người dùng được cung cấp các lựa chọn phù hợp trong một ứng dụng (chẳng hạn như chế độ điều khiển thiết bị và các quy trình tự động hoá được đề xuất) cho thiết bị của họ, bạn nên kiểm tra xem loại thiết bị có phải là loại chính của thiết bị hay không.

if let deviceType =
  await device?.types.get(HumiditySensorDeviceType.self)
{
  if deviceType.metadata.isPrimaryType {
    print("Humidity Sensor is the primary type on this device.")
  } else {
    print("Humidity Sensor isn't the primary type on this device.")
  }
}

Kiểm tra xem một đặc điểm có đang trực tuyến hay không

Đọc thuộc tính connectivityState để kiểm tra khả năng kết nối của một đặc điểm:

let levelControlConnectivity =
  levelControlTrait.metadata.sourceConnectivity
  .connectivityState

Một số đặc điểm, thường là các đặc điểm của Google smart home, có thể hiển thị ngoại tuyến nếu thiết bị không có kết nối Internet. Điều này là do các đặc điểm này dựa trên đám mây và không có định tuyến cục bộ.

Kiểm tra khả năng kết nối của thiết bị

Khả năng kết nối của một thiết bị thực sự được kiểm tra ở cấp loại thiết bị vì một số thiết bị hỗ trợ nhiều loại thiết bị. Trạng thái được trả về là sự kết hợp của các trạng thái kết nối cho tất cả các đặc điểm trên thiết bị đó.

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

Bạn có thể thấy trạng thái partiallyOnline trong trường hợp có nhiều loại thiết bị khi không có kết nối Internet. Các đặc điểm tiêu chuẩn của Matter vẫn có thể hoạt động trực tuyến nhờ tính năng định tuyến cục bộ, nhưng các đặc điểm dựa trên đám mây sẽ không hoạt động.

Lấy địa chỉ IP của thiết bị

Để tìm địa chỉ IP của thiết bị, hãy sử dụng thuộc tính networkInterfaces của GeneralDiagnosticsTrait. Địa chỉ được trả về dưới dạng đối tượng Data. Bạn có thể định dạng các đối tượng này thành chuỗi IPv4 hoặc IPv6 chuẩn bằng khung 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
}

Kiểm tra định tuyến mạng của một đặc điểm

Vị trí của một đặc điểm cũng có trong các API cho Home. dataSourceLocality cho biết đặc điểm được định tuyến từ xa (thông qua đám mây), cục bộ (thông qua trung tâm cục bộ) hay ngang hàng (trực tiếp từ thiết bị đến thiết bị, không có trung tâm).

Giá trị ngôn ngữ không xác định unspecified có thể xảy ra, chẳng hạn như khi một ứng dụng đang khởi động và chưa kết nối được với một trung tâm hoặc máy chủ để kết nối thiết bị. Không thể kết nối với các thiết bị này và các thiết bị này sẽ không thực hiện được yêu cầu tương tác từ các lệnh hoặc sự kiện. Ứng dụng khách sẽ quyết định cách xử lý các thiết bị như vậy.

let levelControlLocality =
  levelControlTrait.metadata.sourceConnectivity
  .dataSourceLocality

Kiểm tra định tuyến mạng cho thiết bị

Tương tự như khả năng kết nối, tính cục bộ được kiểm tra ở cấp loại thiết bị. Trạng thái được trả về là sự kết hợp của vị trí cho tất cả các đặc điểm trên thiết bị đó.

let lightLocality =
  dimmableLightDeviceType.metadata.sourceConnectivity.dataSourceLocality

Bạn có thể thấy trạng thái mixed trong một trường hợp tương tự như trường hợp kết nối partiallyOnline: một số đặc điểm dựa trên đám mây trong khi một số đặc điểm khác là cục bộ.

Thay đổi tên thiết bị

Gọi phương thức setName(_:) để thay đổi tên của một thiết bị:

let updatedDevice = try await theDevice.setName("new device name")

Khi thay đổi tên của một thiết bị, cấu trúc HomeDevice ban đầu sẽ giữ nguyên và thay đổi sẽ được phản ánh trong đối tượng HomeDevice được cập nhật và trả về.

Tên sẽ bị cắt bớt nếu vượt quá giới hạn 60 điểm mã Unicode (ký tự) và không có lỗi nào được gửi. Nhà phát triển chịu trách nhiệm xử lý tên dài và có thể quyết định xem họ có muốn thông báo cho người dùng rằng tên sẽ bị cắt bớt hay không.

Kiểm tra tư cách thành viên nhóm của một thiết bị

Một thiết bị thực có thể thuộc về một hoặc nhiều nhóm thiết bị. Để kiểm tra xem thiết bị thuộc nhóm nào, hãy kiểm tra GroupMembershipTrait trên thiết bị:

if let trait = await device.traits.get(Google.GroupMembershipTrait.self) {
  for groupId in trait.attributes.groupObjectIdsArray {
    print("Device belongs to group: \(groupId)")
  }
}

Để kiểm tra các loại thiết bị được hỗ trợ bởi một DeviceGroup và gửi lệnh đến nhóm dưới dạng một thực thể duy nhất, hãy xem phần Điều khiển một nhóm thiết bị.