میتوانید برای ویژگیهای سفارشی خودتان که قابلیتهای بیشتری فراتر از ویژگیهای استاندارد ارائه میدهند، پشتیبانی اضافه کنید. به ویژگیهای سفارشی مانند این،
ویژگیهای مختص سازنده یا ویژگیهای MS گفته میشود. ویژگیهای MS
در قالب استاندارد .matter IDL تعریف میشوند، سپس به یک ماژول iOS
تبدیل میشوند که میتواند به برنامه شما وارد شود.
کیت توسعه نرمافزار iOS Home APIs شامل یک تولیدکننده کد است که میتواند این تبدیل را انجام دهد. علاوهبراین، درصورت نیاز، ویژگیهای موقت را نیز میتوان بااستفاده از این تولیدکننده کد تولید کرد.
پیشنیازها
برای استفاده از مولد کد، به موارد زیر نیاز دارید:
- Python نسخه ۳.۱۰ یا جدیدتر.
- یک فایل IDL
.matterبا تعریف ویژگیهای MS شما. این فایل باید فقط حاوی تعاریفclient clusterباشد. میتوانید بهصورت دستی یکی ایجاد کنید یا از موارد تولیدشده بهعنوان بخشی از فرایند ساخت کیت توسعه نرمافزار Matter برای سفتافزار دستگاهتان استفاده کنید.
ابزار تولید کد Matter دارای برخی وابستگیهای Python است که باید نصب شوند. این مرحله یکبار انجام میشود و باید پساز ادغام نسخه جدید کیت توسعه نرمافزار انجام شود تا اطمینان حاصل شود که ابزار میتواند در Xcode sandbox اجرا شود.
swift package plugin --allow-network-connections "all" --allow-writing-to-package-directory matter-codegen-initبرای اطلاعات بیشتر درباره قالب IDL، به
matter/idl
در GitHub مراجعه کنید. فهرست راهنمای /tests/inputs در آنجا تعدادی فایل IDL نمونه دارد. فایل IDL کامل برای همه خوشههای Matter، که
منبع فایلهای تولیدشده در همه پلاتفرمها (ازجمله مدولهای
iOS برای
Home API) است، در
controller-clusters.matter قابلمشاهده است.
تولید فایلهای Swift
مولد کد با کیت توسعه نرمافزار همراه است و در مدیر بسته Swift (SwiftPM) ادغام شده است. اگرچه کدساز توسط XCode بهعنوان افزایه SwiftPM فراخوانده میشود، لازم نیست پروژهتان از SwiftPM برای مدیریت بسته استفاده کند.
- کیت توسعه نرمافزار را در پروژهتان ادغام کنید. برای دستورالعملها، راهاندازی کیت توسعه نرمافزار iOS را ببینید.
- افزایه را راهاندازی کنید. چون افزونه در محیط امن اجرا میشود، باید
برخیاز وابستگیها را نصب کنید:
swift package plugin matter-codegen-init \ --allow-network-connections all \ --allow-writing-to-package-directory درباره فضای نام برای کد تولیدشده تصمیم بگیرید و آن را بهعنوان
pragma swiftبه فایل IDL اضافه کنید. برای مثال،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; }تولیدکننده را اجرا کنید:
swift package plugin matter-codegen Clusters/MyCustomCluster.matterفایلهای
.swiftتولیدشده را میتوان به پروژه برنامهتان اضافه کرد یا اگر میخواهید آنها را در واحدی جداگانه داشته باشید، میتوانید آنها را به چارچوب اضافه کنید.
جایگزین: تولید خودکار ویژگیها
تولیدکننده کد شامل افزونه دیگری است که پسوند فایل .matter را تشخیص میدهد. افزونه بهطور خودکار تولیدکننده کد را فراخوانی میکند و
فایلهای Swift خروجی را به هدف فعلی اضافه میکند. این کار نیاز به ارسال فایلهای تولیدشده به کنترل منبع را ازبین میبرد و تضمین میکند که ویژگیها همیشه بااستفاده از نسخه بستهبندیشده تولیدکننده تولید میشوند. اگر برنامهتان درحالحاضر
از SwiftPM استفاده میکند، اکیداً توصیه میکنیم از این افزونه استفاده کنید.
برای استفاده از افزونه:
- فایلهای
.matterرا به هدفی در برنامهتان اضافه کنید. قطعه کد افزونه زیر را به آن هدف اضافه کنید:
.target( name: "MyAppTarget", plugins: [.plugin(name: "MatterCodegenPlugin")] ),
استفاده از واحد
برای استفاده از خروجی تولیدشده، فایل(ها) را در پروژه Xcode خود کپی کنید. در این
مورد، فایلها MyCompany.swift و MyCustom.swift هستند.
اگر از چارچوب جداگانهای برای ویژگیهایتان استفاده میکنید، از عبارت import
برای وارد کردن واحد مربوطه استفاده کنید.
سپس ویژگیهای MS باید ازطریق Home API به همان روشی که ویژگیهای استاندارد Matter دردسترس هستند، دردسترس باشند، بهشرطی که آن ویژگیهای MS در سفتافزار Matter شما تعریف شده باشند. کافی است نام ویژگی استاندارد را با نام ویژگی MS خود جایگزین کنید.
برای مثال، اگر ویژگی MS شما MyCustomTrait نام دارد، فراخوانی زیر
همه مشخصههای MyCustomTrait را برمیگرداند:
let myCustomTrait = deviceType.traits[MyCompany.MyCustomTrait.self]
مثال
اگر با قالب IDL آشنا نیستید، دایرکتوریهای matter/idl/tests/inputs را برای فایلهای نمونه ببینید.
ورودی IDL
یک ویژگی MS بسیار ساده را میتوان در IDL به این صورت تعریف کرد:
// 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;
}
در این مثال، شناسه ویژگی 4294048768 با 0xFFF1FC00 در دستگاه شانزدهشانزدهی مطابقت دارد، که در آن پیشوند 0xFFF1 نشاندهنده «شناسه ارائهدهنده» آزمایشی است و پسوند 0xFC00 مقداری است که برای ویژگیهای «مختص سازنده» رزرو شده است. برای اطلاعات بیشتر،
بخش شناسه قابلتوسعه سازنده (MEI)
مشخصات Matter را ببینید. مطمئن شوید که برای هر ویژگی MS در فایل IDL خود از یک شناسه ویژگی اعشاری مناسب استفاده میکنید.
اگر امروزه از ویژگیهای MS در دستگاهتان استفاده میشود، احتمالاً آن را ازقبل در این قالب تعریف کردهاید.
برونداد Swift
دو فایل Swift، MyCustom.swift (نامگذاریشده براساس ویژگی) و
MyCompany.swift (نامگذاریشده براساس فضای نام)، را میتوانید در
دایرکتوری خروجی مشخصشده پیدا کنید. این فایلها بهطور خاص برای استفاده با Home API قالببندی شدهاند.
وقتی دردسترس قرار گرفت (مثلاً در پروژه Xcode برنامهتان)، میتوان از فایلها همانطور که در استفاده از ماژول شرح داده شده است استفاده کرد.
MyCustom.swift
برای ازهم باز کردن و مشاهده 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 { }