يمكنك إضافة دعم للسمات المخصّصة التي توفّر إمكانات إضافية تتجاوز تلك التي توفّرها السمات العادية. يُشار إلى السمات المخصّصة، مثل تلك الموضّحة أعلاه، باسم السمات الخاصة بالشركة المصنّعة أو سمات MS. يتم تحديد سمات MS
بتنسيق .matter IDL العادي، ثم يتم تحويلها إلى وحدة iOS
يمكن استيرادها إلى تطبيقك.
تتضمّن حزمة تطوير البرامج (SDK) الخاصة بواجهات برمجة التطبيقات لمنزل iOS أداة لإنشاء الرموز البرمجية يمكنها إجراء عملية التحويل هذه. بالإضافة إلى ذلك، يمكن أيضًا إنشاء سمات مؤقتة باستخدام أداة إنشاء الرموز هذه، إذا لزم الأمر.
المتطلبات الأساسية
لاستخدام أداة إنشاء الرموز، يجب توفُّر ما يلي:
- الإصدار 3.10 أو الإصدارات الأحدث من Python
- ملف
.matterIDL يتضمّن تعريف سمات "خدمة التسويق" يجب أن يحتوي هذا الملف على تعريفاتclient clusterفقط. يمكنك إنشاء شهادة يدويًا أو استخدام شهادات تم إنشاؤها كجزء من Matterعملية إنشاء حزمة SDK لبرنامج الجهاز الثابت.
تتضمّن أداة Matter codegen بعض التبعيات في Python التي يجب تثبيتها. هذه خطوة تُجرى لمرة واحدة ويجب تنفيذها بعد تضمين إصدار جديد من حزمة SDK لضمان إمكانية تشغيل الأداة في بيئة الاختبار المعزولة في Xcode.
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)، على controller-clusters.matter.
إنشاء ملفات Swift
يتم تجميع أداة إنشاء الرموز البرمجية مع حزمة تطوير البرامج (SDK) ودمجها في Swift Package Manager (SwiftPM). على الرغم من أنّ أداة إنشاء الرموز يتم استدعاؤها من خلال Xcode كإضافة SwiftPM، ليس عليك استخدام SwiftPM لإدارة الحِزم في مشروعك.
- نفِّذ عملية دمج حزمة تطوير البرامج (SDK) في مشروعك. راجِع مقالة إعداد حزمة تطوير البرامج (SDK) لنظام التشغيل 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 بالطريقة نفسها التي تتوفر بها سمات 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.
بعد توفّر الملفات (مثلما هو الحال في مشروع Xcode الخاص بتطبيقك)، يمكن استخدامها كما هو موضّح في استخدام الوحدة.
MyCustom.swift
انقر للتوسيع وعرض `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 { }