Bạn có thể thêm tính năng hỗ trợ cho các đặc điểm tuỳ chỉnh của riêng mình để cung cấp thêm các chức năng ngoài những chức năng do các đặc điểm tiêu chuẩn cung cấp. Các đặc điểm tuỳ chỉnh như vậy được gọi là đặc điểm dành riêng cho nhà sản xuất hoặc đặc điểm MS. Các đặc điểm MS được xác định theo định dạng IDL .matter tiêu chuẩn, sau đó được chuyển đổi thành một mô-đun iOS có thể nhập vào ứng dụng của bạn.
SDK API Home dành cho iOS bao gồm một trình tạo mã có thể thực hiện quá trình chuyển đổi này. Ngoài ra, nếu cần, bạn cũng có thể tạo các đặc điểm tạm thời bằng trình tạo mã này.
Điều kiện tiên quyết
Để sử dụng trình tạo mã, bạn cần:
- Python 3.10 trở lên.
- Tệp IDL
.mattercó định nghĩa về các đặc điểm MS. Tệp này chỉ nên chứa các định nghĩaclient cluster. Bạn có thể tự tạo một tệp hoặc sử dụng các tệp được tạo trong quá trình xây dựng Matter SDK cho phần sụn của thiết bị.
Công cụ codegen Matter có một số phần phụ thuộc Python cần được cài đặt. Đây là bước thực hiện một lần sau khi tích hợp phiên bản mới của SDK để đảm bảo công cụ này có thể chạy trong hộp cát Xcode.
swift package plugin --allow-network-connections "all" --allow-writing-to-package-directory matter-codegen-initĐể biết thêm thông tin về định dạng IDL, hãy xem
matter/idl
trên GitHub. Thư mục /tests/inputs ở đó có một số tệp IDL mẫu. Bạn có thể tìm thấy tệp IDL hoàn chỉnh cho tất cả các cụm Matter, đây
là nguồn cho các tệp được tạo trên tất cả các nền tảng (bao gồm cả các mô-đun iOS cho
API Home) tại
controller-clusters.matter.
Tạo tệp Swift
Trình tạo mã được gói cùng với SDK và tích hợp vào Trình quản lý gói (SwiftPM). Mặc dù trình tạo mã được XCode gọi là một trình bổ trợ SwiftPM, nhưng dự án của bạn không cần phải sử dụng SwiftPM để quản lý gói.
- Tích hợp SDK vào dự án của bạn. Hãy xem phần Thiết lập SDK iOS để biết hướng dẫn.
- Thiết lập trình bổ trợ. Vì trình bổ trợ chạy trong hộp cát, nên bạn cần cài đặt một số phần phụ thuộc:
swift package plugin matter-codegen-init \ --allow-network-connections all \ --allow-writing-to-package-directory Quyết định một không gian tên cho mã được tạo và thêm không gian tên đó làm
pragma swiftvào tệp IDL. Ví dụ:MyCompany:// pragma kotlin(package=com.mycompany.matter.cluster, generate_namespace=true) // pragma swift(package=MyCompany, generate_namespace=true) client cluster SimpleCustom = 4294048768 { attribute int16u clusterAttr = 1; // Global Attributes readonly attribute command_id generatedCommandList[] = 65528; readonly attribute command_id acceptedCommandList[] = 65529; readonly attribute event_id eventList[] = 65530; readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; }Chạy trình tạo:
swift package plugin matter-codegen Clusters/MyCustomCluster.matterBạn có thể thêm các tệp
.swiftđược tạo vào dự án ứng dụng hoặc thêm vào một khung nếu muốn các tệp đó nằm trong một mô-đun riêng biệt.
Cách khác: Tự động tạo các đặc điểm
Trình tạo mã bao gồm một trình bổ trợ bổ sung nhận dạng đuôi tệp .matter. Trình bổ trợ sẽ tự động gọi trình tạo mã và thêm các tệp Swift đầu ra vào mục tiêu hiện tại. Nhờ đó, bạn không cần phải cam kết các tệp được tạo để kiểm soát nguồn và đảm bảo rằng các đặc điểm luôn được tạo bằng phiên bản trình tạo được gói. Nếu ứng dụng của bạn đã sử dụng SwiftPM, bạn nên sử dụng trình bổ trợ này.
Cách sử dụng trình bổ trợ:
- Thêm các tệp
.mattervào một mục tiêu trong ứng dụng của bạn. Thêm đoạn mã trình bổ trợ sau vào mục tiêu đó:
.target( name: "MyAppTarget", plugins: [.plugin(name: "MatterCodegenPlugin")] ),
Sử dụng mô-đun
Để sử dụng đầu ra được tạo, hãy sao chép(các) tệp vào dự án Xcode. Trong trường hợp này, các tệp là MyCompany.swift và MyCustom.swift.
Nếu bạn đang sử dụng một khung riêng biệt cho các đặc điểm của mình, hãy sử dụng câu lệnh import để nhập mô-đun áp dụng.
Sau đó, các đặc điểm MS sẽ có sẵn thông qua API Home theo cách tương tự như các đặc điểm tiêu chuẩn Matter , miễn là các đặc điểm MS đó được xác định trong phần sụn Matter. Chỉ cần thay thế tên đặc điểm tiêu chuẩn bằng tên đặc điểm MS.
Ví dụ: nếu đặc điểm MS của bạn có tên là MyCustomTrait, thì lệnh gọi sau sẽ trả về tất cả các thuộc tính của MyCustomTrait:
let myCustomTrait = deviceType.traits[MyCompany.MyCustomTrait.self]
Ví dụ
Nếu bạn không quen thuộc với định dạng IDL, hãy xem các matter/idl/tests/inputs để biết các tệp mẫu.
Đầu vào IDL
Bạn có thể xác định một đặc điểm MS rất đơn giản trong IDL như sau:
// mycustom.matter
// pragma kotlin(package=com.mycompany.matter.cluster, generate_namespace=true)
// pragma swift(package=MyCompany, generate_namespace=true)
client cluster MyCustom = 4294048768 {
attribute int16u clusterAttr = 1;
// Global Attributes
readonly attribute command_id generatedCommandList[] = 65528;
readonly attribute command_id acceptedCommandList[] = 65529;
readonly attribute event_id eventList[] = 65530;
readonly attribute attrib_id attributeList[] = 65531;
readonly attribute bitmap32 featureMap = 65532;
readonly attribute int16u clusterRevision = 65533;
}
Trong ví dụ này, mã đặc điểm của 4294048768 tương ứng với 0xFFF1FC00 ở dạng
thập lục phân, trong đó tiền tố 0xFFF1 đại diện cho Mã nhà cung cấp thử nghiệm và
hậu tố 0xFC00 là giá trị dành riêng cho các đặc điểm dành riêng cho nhà sản xuất. Hãy xem
phần Giá trị nhận dạng có thể mở rộng của nhà sản xuất (MEI) của
Matter Thông số kỹ thuật để biết thêm thông tin. Hãy nhớ sử dụng mã đặc điểm thập phân thích hợp cho từng đặc điểm MS trong tệp IDL.
Nếu bạn đang sử dụng các đặc điểm MS trong thiết bị của mình, thì có thể bạn đã xác định các đặc điểm đó ở định dạng này.
Đầu ra Swift
Bạn có thể tìm thấy 2 tệp Swift, MyCustom.swift (được đặt tên theo đặc điểm) và MyCompany.swift (được đặt tên theo không gian tên) trong thư mục đầu ra đã chỉ định. Các tệp này được định dạng riêng cho việc sử dụng với API Home.
Sau khi có sẵn (chẳng hạn như trong dự án Xcode của ứng dụng), bạn có thể sử dụng các tệp như mô tả trong phần Sử dụng mô-đun.
MyCustom.swift
Nhấp để mở rộng và xem `MyCustom.swift`
// This file contains machine-generated code. public import Foundation import GoogleHomeSDK private import SwiftProtobuf /* * This file was machine generated via the code generator * in `codegen.clusters.swift.CustomGenerator` * */ extension MyCompany { /// :nodoc: public struct MyCustomTrait: MatterTrait { /// No supported events for `MyCustomTrait`. public static let supportedEventTypes: [Event.Type] = [] /// No supported commands for `MyCustomTrait`. public static let supportedCommandTypes: [Command.Type] = [] public static let identifier = MyCompany.MyCustomTrait.makeTraitID(for: 4294048768) public let metadata: TraitMetadata /// List of attributes for the `MyCustomTrait`. public let attributes: MyCompany.MyCustomTrait.Attributes private let interactionProxy: InteractionProxy public init(decoder: TraitDecoder, interactionProxy: InteractionProxy?, metadata: TraitMetadata) throws { guard let interactionProxy = interactionProxy else { throw HomeError.invalidArgument("InteractionProxy parameter required.") } let unwrappedDecoder = try decoder.unwrapPayload(namespace: Self.identifier.namespace) self.interactionProxy = interactionProxy self.attributes = try Attributes(decoder: unwrappedDecoder) self.metadata = metadata } // Internal for testing. internal init(attributes: MyCompany.MyCustomTrait.Attributes = .init(), interactionProxy: InteractionProxy?, metadata: TraitMetadata = .init()) throws { guard let interactionProxy = interactionProxy else { throw HomeError.invalidArgument("InteractionProxy parameter required.") } self.interactionProxy = interactionProxy self.attributes = attributes self.metadata = metadata } public func encode(with encoder: TraitEncoder) throws { encoder.wrapPayload(namespace: Self.identifier.namespace) try self.attributes.encode(with: encoder) } public func update(_ block: @Sendable (MutableAttributes) -> Void) async throws -> Self { let mutable = MutableAttributes(attributes: self.attributes) block(mutable) if self.interactionProxy.strictOperationValidation { guard self.attributes.$clusterAttr.isSupported || !mutable.clusterAttrIsSet else { throw HomeError.invalidArgument("clusterAttr is not supported.") } } let updatedTrait = try MyCompany.MyCustomTrait(attributes: self.attributes.apply(mutable), interactionProxy: self.interactionProxy, metadata: self.metadata) try await self.interactionProxy.update(trait: mutable, useTimedInteraction: false) return updatedTrait } } } // MARK: - ForceReadableTrait extension MyCompany.MyCustomTrait: ForceReadableTrait { public func forceRead() async throws { try await self.interactionProxy.forceRead(traitID: Self.identifier) } } // MARK: - Attributes extension MyCompany.MyCustomTrait { /// Attributes for the `MyCustomTrait`. public struct Attributes: Sendable { // Attributes required at runtime. /** A list of the attribute IDs of the attributes supported by the cluster instance. */ /// Nullable: false. @TraitAttribute public var attributeList: [UInt32]? /// Nullable: false. @TraitAttribute public var clusterAttr: UInt16? /** A list of server-generated commands (server to client) which are supported by this cluster server instance. */ /// Nullable: false. @TraitAttribute public var generatedCommandList: [UInt32]? /** A list of client-generated commands which are supported by this cluster server instance. */ /// Nullable: false. @TraitAttribute public var acceptedCommandList: [UInt32]? /** Whether the server supports zero or more optional cluster features. A cluster feature is a set of cluster elements that are mandatory or optional for a defined feature of the cluster. If a cluster feature is supported by the cluster instance, then the corresponding bit is set to 1, otherwise the bit is set to 0 (zero). */ /// Nullable: false. @TraitAttribute public var featureMap: UInt32? /** The revision of the server cluster specification supported by the cluster instance. */ /// Nullable: false. @TraitAttribute public var clusterRevision: UInt16? internal init( clusterAttr: UInt16? = nil, generatedCommandList: [UInt32]? = nil, acceptedCommandList: [UInt32]? = nil, attributeList: [UInt32]? = nil, featureMap: UInt32? = nil, clusterRevision: UInt16? = nil ) { self._clusterAttr = .init( wrappedValue: clusterAttr, isSupported: attributeList?.contains(0x01) ?? false, isNullable: false ) self._generatedCommandList = .init( wrappedValue: generatedCommandList, isSupported: attributeList?.contains(0x0FFF8) ?? false, isNullable: false ) self._acceptedCommandList = .init( wrappedValue: acceptedCommandList, isSupported: attributeList?.contains(0x0FFF9) ?? false, isNullable: false ) self._attributeList = .init( wrappedValue: attributeList, isSupported: attributeList?.contains(0x0FFFB) ?? false, isNullable: false ) self._featureMap = .init( wrappedValue: featureMap, isSupported: attributeList?.contains(0x0FFFC) ?? false, isNullable: false ) self._clusterRevision = .init( wrappedValue: clusterRevision, isSupported: attributeList?.contains(0x0FFFD) ?? false, isNullable: false ) } fileprivate init(decoder: TraitDecoder) throws { let decodedAttributeList: [UInt32] = try decoder.decodeOptionalArray(tag: 0x0FFFB) ?? [] var generatedAttributeList = [UInt32]() generatedAttributeList.append(0x0FFFB) let clusterAttrValue: UInt16? = try decoder.decodeOptional(tag: 0x01) let clusterAttrIsSupported = clusterAttrValue != nil if clusterAttrIsSupported { generatedAttributeList.append(0x01) } self._clusterAttr = .init( wrappedValue: clusterAttrIsSupported ? clusterAttrValue : nil, isSupported: clusterAttrIsSupported, isNullable: false ) let generatedCommandListValue: [UInt32]? = try decoder.decodeOptionalArray(tag: 0x0FFF8) let generatedCommandListIsSupported = generatedCommandListValue != nil if generatedCommandListIsSupported { generatedAttributeList.append(0x0FFF8) } self._generatedCommandList = .init( wrappedValue: generatedCommandListIsSupported ? generatedCommandListValue : nil, isSupported: generatedCommandListIsSupported, isNullable: false ) let acceptedCommandListValue: [UInt32]? = try decoder.decodeOptionalArray(tag: 0x0FFF9) let acceptedCommandListIsSupported = acceptedCommandListValue != nil if acceptedCommandListIsSupported { generatedAttributeList.append(0x0FFF9) } self._acceptedCommandList = .init( wrappedValue: acceptedCommandListIsSupported ? acceptedCommandListValue : nil, isSupported: acceptedCommandListIsSupported, isNullable: false ) let featureMapValue: UInt32? = try decoder.decodeOptional(tag: 0x0FFFC) let featureMapIsSupported = featureMapValue != nil if featureMapIsSupported { generatedAttributeList.append(0x0FFFC) } self._featureMap = .init( wrappedValue: featureMapIsSupported ? featureMapValue : nil, isSupported: featureMapIsSupported, isNullable: false ) let clusterRevisionValue: UInt16? = try decoder.decodeOptional(tag: 0x0FFFD) let clusterRevisionIsSupported = clusterRevisionValue != nil if clusterRevisionIsSupported { generatedAttributeList.append(0x0FFFD) } self._clusterRevision = .init( wrappedValue: clusterRevisionIsSupported ? clusterRevisionValue : nil, isSupported: clusterRevisionIsSupported, isNullable: false ) self._attributeList = .init( wrappedValue: generatedAttributeList, isSupported: true, isNullable: false ) } fileprivate func apply(_ update: MyCompany.MyCustomTrait.MutableAttributes) -> Self { let clusterAttrValue = update.clusterAttrIsSet ? update.clusterAttr : self.clusterAttr let generatedCommandListValue = self.generatedCommandList let acceptedCommandListValue = self.acceptedCommandList let attributeListValue = self.attributeList let featureMapValue = self.featureMap let clusterRevisionValue = self.clusterRevision return MyCompany.MyCustomTrait.Attributes( clusterAttr: clusterAttrValue, generatedCommandList: generatedCommandListValue, acceptedCommandList: acceptedCommandListValue, attributeList: attributeListValue, featureMap: featureMapValue, clusterRevision: clusterRevisionValue ) } } } extension MyCompany.MyCustomTrait.Attributes: TraitEncodable { public static var identifier: String { MyCompany.MyCustomTrait.identifier } public func encode(with encoder: TraitEncoder) throws { try encoder.encode(tag: 0x01, value: self.clusterAttr) try encoder.encode(tag: 0x0FFF8, value: self.generatedCommandList) try encoder.encode(tag: 0x0FFF9, value: self.acceptedCommandList) try encoder.encode(tag: 0x0FFFB, value: self.attributeList) try encoder.encode(tag: 0x0FFFC, value: self.featureMap) try encoder.encode(tag: 0x0FFFD, value: self.clusterRevision) } } // MARK: - Hashable & Equatable extension MyCompany.MyCustomTrait: Hashable { public static func ==(lhs: MyCompany.MyCustomTrait, rhs: MyCompany.MyCustomTrait) -> Bool { return lhs.identifier == rhs.identifier && lhs.attributes == rhs.attributes && lhs.metadata == rhs.metadata } public func hash(into hasher: inout Hasher) { hasher.combine(identifier) hasher.combine(attributes) hasher.combine(metadata) } } extension MyCompany.MyCustomTrait.Attributes: Hashable { public static func ==(lhs: MyCompany.MyCustomTrait.Attributes, rhs: MyCompany.MyCustomTrait.Attributes) -> Bool { var result = true result = lhs.clusterAttr == rhs.clusterAttr && result result = lhs.generatedCommandList == rhs.generatedCommandList && result result = lhs.acceptedCommandList == rhs.acceptedCommandList && result result = lhs.attributeList == rhs.attributeList && result result = lhs.featureMap == rhs.featureMap && result result = lhs.clusterRevision == rhs.clusterRevision && result return result } public func hash(into hasher: inout Hasher) { hasher.combine(self.clusterAttr) hasher.combine(self.generatedCommandList) hasher.combine(self.acceptedCommandList) hasher.combine(self.attributeList) hasher.combine(self.featureMap) hasher.combine(self.clusterRevision) } } // MARK: - MutableAttributes extension MyCompany.MyCustomTrait { public final class MutableAttributes: TraitEncodable { public static let identifier: String = MyCompany.MyCustomTrait.identifier private let baseAttributes: Attributes fileprivate var clusterAttr: UInt16? private(set) public var clusterAttrIsSet = false public func setClusterAttr(_ value: UInt16) { self.clusterAttr = value self.clusterAttrIsSet = true } public func clearClusterAttr() { self.clusterAttr = nil self.clusterAttrIsSet = false } internal init(attributes: MyCompany.MyCustomTrait.Attributes) { self.baseAttributes = attributes } public func encode(with encoder: TraitEncoder) throws { // MutableAttributes is encoded individually, e.g. through update(...), // therefore uddm wrapping needs to be applied. encoder.wrapPayload(namespace: Self.identifier.namespace) if self.clusterAttrIsSet { try encoder.encode(tag: 0x01, value: self.clusterAttr) } } } } // MARK: - Attributes definitions extension MyCompany.MyCustomTrait { public enum Attribute: UInt32, Field, CaseIterable { case clusterAttr = 1 case generatedCommandList = 65528 case acceptedCommandList = 65529 case attributeList = 65531 case featureMap = 65532 case clusterRevision = 65533 public var id: UInt32 { self.rawValue } public var type: GoogleHomeSDK.FieldType { switch self { case .clusterAttr: return .uint16 case .generatedCommandList: return .uint32 case .acceptedCommandList: return .uint32 case .attributeList: return .uint32 case .featureMap: return .uint32 case .clusterRevision: return .uint16 } } } public static func attribute(id: UInt32) -> (any Field)? { return Attribute(rawValue: id) } } // MARK: - Attribute fieldSelect definitions extension TypedReference where T == MyCompany.MyCustomTrait { public var clusterAttr: TypedExpression<UInt16> { fieldSelect(from: self, selectedField: T.Attribute.clusterAttr) } public var generatedCommandList: TypedExpression<[UInt32]> { fieldSelect(from: self, selectedField: T.Attribute.generatedCommandList) } public var acceptedCommandList: TypedExpression<[UInt32]> { fieldSelect(from: self, selectedField: T.Attribute.acceptedCommandList) } public var attributeList: TypedExpression<[UInt32]> { fieldSelect(from: self, selectedField: T.Attribute.attributeList) } public var featureMap: TypedExpression<UInt32> { fieldSelect(from: self, selectedField: T.Attribute.featureMap) } public var clusterRevision: TypedExpression<UInt16> { fieldSelect(from: self, selectedField: T.Attribute.clusterRevision) } } extension Updater where T == MyCompany.MyCustomTrait { public func setClusterAttr(_ value: UInt16) { self.set(Parameter(field: T.Attribute.clusterAttr, value: value)) } } // MARK: - Struct Fields definitions
MyCompany.swift
/// Namespace for all MyCompany Traits and DeviceTypes.
public enum MyCompany { }