شما میتوانید از ویژگیهای سفارشی خودتان که قابلیتهای اضافی فراتر از ویژگیهای استاندارد ارائه میدهند، پشتیبانی کنید. ویژگیهای سفارشی مانند این ، ویژگیهای خاص تولیدکننده یا ویژگیهای MS نامیده میشوند. ویژگیهای MS در قالب استاندارد .matter IDL تعریف میشوند و سپس به یک ماژول iOS تبدیل میشوند که میتوان آن را به برنامه شما وارد کرد.
کیت توسعه نرمافزاری رابطهای برنامهنویسی iOS Home شامل یک مولد کد است که میتواند این تبدیل را انجام دهد. علاوه بر این، در صورت نیاز، میتوان با استفاده از این مولد کد، ویژگیهای موقت (temporary trait) را نیز تولید کرد.
پیشنیازها
برای استفاده از مولد کد، به موارد زیر نیاز دارید:
- پایتون ۳.۱۰ یا جدیدتر.
- یک فایل
.matterIDL با تعریف ویژگیهای سیستم عامل شما. این فایل باید فقط شامل تعاریفclient clusterباشد. میتوانید آن را به صورت دستی ایجاد کنید یا از فایلهایی که به عنوان بخشی از فرآیند ساخت Matter SDK برای میانافزار دستگاه شما تولید میشوند، استفاده کنید.
ابزار Matter codegen دارای برخی از وابستگیهای پایتون است که باید نصب شوند. این یک مرحلهی یکباره است که باید پس از یکپارچهسازی نسخهی جدید 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 برای APIهای Home) است، را میتوانید در controller-clusters.matter پیدا کنید.
تولید فایلهای سوئیفت
مولد کد به همراه SDK ارائه شده و در Swift Package Manager (SwiftPM) ادغام شده است. اگرچه مولد کد توسط XCode به عنوان یک افزونه SwiftPM فراخوانی میشود، اما پروژه شما نیازی به استفاده از SwiftPM برای مدیریت بسته ندارد.
- SDK را در پروژه خود ادغام کنید. برای دستورالعملها به بخش راهاندازی iOS SDK مراجعه کنید.
- افزونه را راهاندازی کنید. از آنجا که افزونه در یک محیط سندباکس اجرا میشود، باید برخی از وابستگیها را نصب کنید:
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 اکنون باید از طریق APIهای 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، MyCustom.swift (که از روی ویژگی (trait) نامگذاری شدهاند) و MyCompany.swift (که از روی فضای نام (namespace) نامگذاری شدهاند)، را میتوان در دایرکتوری خروجی مشخص شده یافت. این فایلها به طور خاص برای استفاده با APIهای Home قالببندی شدهاند.
پس از در دسترس بودن (مانند پروژه Xcode برنامه شما)، فایلها میتوانند همانطور که در بخش «استفاده از ماژول» توضیح داده شده است، مورد استفاده قرار گیرند.
سفارشی من.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
شرکت من.سویفت
/// Namespace for all MyCompany Traits and DeviceTypes.
public enum MyCompany { }