Bạn có thể thêm chế độ 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ư thế này được gọi là đặc điểm dành riêng cho nhà sản xuất hoặc đặc điểm dành riêng cho nhà sản xuất. 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 iOS Home API có 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.
- Một tệp IDL
.mattercó định nghĩa về các đặc điểm MS của bạn. Tệp này chỉ chứa các định nghĩaclient cluster. Bạn có thể tạo một khoá theo cách thủ công hoặc sử dụng các khoá được tạo trong quy trình tạo SDK Matter cho chương trình cơ sở của thiết bị.
Công cụ tạo mã 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 và bạn nên thực hiện bước này sau khi tích hợp một phiên bản mới của SDK để đảm bảo công cụ 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 (là nguồn cho các tệp được tạo trên mọi nền tảng, bao gồm cả các mô-đun iOS cho Home API) tại controller-clusters.matter.
Tạo các tệp Swift
Trình tạo mã đi kèm với SDK và được tích hợp vào Trình quản lý gói Swift (SwiftPM). Mặc dù trình tạo mã được XCode gọi dưới dạng một trình bổ trợ SwiftPM, nhưng dự án của bạn không bắt buộc 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 một 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 chọn một không gian tên cho mã được tạo và thêm không gian tên đó dưới dạng
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đã 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ày nằm trong một mô-đun riêng biệt.
Cách khác: Tự động tạo đặ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. Điều này giúp bạn không cần cam kết các tệp đã tạo vào tính năng 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 đi kèm của trình tạo. 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ợ này:
- Thêm tệp
.mattervào một mục tiêu trong ứng dụng của bạn. Thêm đoạn mã sau của trình bổ trợ vào mục tiêu đó:
.target( name: "MyAppTarget", plugins: [.plugin(name: "MatterCodegenPlugin")] ),
Sử dụng mô-đun
Để sử dụng đầu ra đã tạo, hãy sao chép(các) tệp vào dự án Xcode của bạn. 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 cho các đặc điểm của mình, hãy sử dụng câu lệnh import để nhập mô-đun có thể áp dụng.
Sau đó, các đặc điểm MS sẽ có sẵn thông qua Home API theo cách tương tự như các đặc điểm Matter tiêu chuẩn, miễn là các đặc điểm MS đó được xác định trong chương trình cơ sở Matter của bạn. Bạn chỉ cần thay thế tên đặc điểm chuẩn bằng tên đặc điểm của MS.
Ví dụ: nếu đặc điểm MS của bạn có tên là MyCustomTrait, thì lệnh gọi sau đây 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 chưa quen với định dạng IDL, hãy xem các thư mụ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ã nhận dạng đặ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 biểu thị mã nhận dạng 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 Mã nhận dạng có thể mở rộng của nhà sản xuất (MEI) trong Matter Quy cách để biết thêm thông tin. Đảm bảo bạn sử dụng mã nhận dạng đặc điểm thập phân thích hợp cho từng đặc điểm MS trong tệp IDL.
Nếu đang sử dụng các đặc điểm của MS trong thiết bị của mình, thì có thể bạn đã xác định đặ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 dành riêng cho việc sử dụng với Home API.
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 này 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 @_spi(GoogleHomeInternal) import GoogleHomeSDK /* * 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 { 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: 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 { }