السمات الخاصة بالشركة المصنّعة على أجهزة iOS

يمكنك إضافة دعم للسمات المخصّصة التي توفّر إمكانات إضافية تتجاوز الإمكانات التي توفّرها السمات العادية. يُشار إلى السمات المخصّصة من هذا النوع باسم السمات الخاصة بالجهة المصنّعة أو سمات MS. يتم تحديد سمات MS بتنسيق IDL العادي .matter، ثم يتم تحويلها إلى وحدة iOS يمكن استيرادها إلى تطبيقك.

تتضمّن حزمة تطوير البرامج (SDK) لواجهات برمجة التطبيقات iOS Home نظام إنشاء الرموز يمكنه إجراء هذا التحويل. بالإضافة إلى ذلك، يمكن أيضًا إنشاء سمات مؤقتة باستخدام نظام إنشاء الرموز هذا إذا لزم الأمر.

المتطلبات الأساسية

لاستخدام نظام إنشاء الرموز، يجب توفّر ما يلي:

  • الإصدار 3.10 أو إصدار أحدث من Python
  • ملف IDL بتنسيق .matter يتضمّن تعريف سمات MS يجب أن يحتوي هذا الملف على تعريفات client cluster فقط. يمكنك إنشاء ملف يدويًا أو استخدام الملفات التي يتم إنشاؤها كجزء من عملية إنشاء حزمة تطوير البرامج (SDK) من Matter للبرامج الثابتة لجهازك.Matter

تتضمّن أداة 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 لإدارة الحزم.

  1. يمكنك دمج حزمة تطوير البرامج (SDK) في مشروعك. للحصول على التعليمات، يُرجى الاطّلاع على إعداد حزمة تطوير البرامج (SDK) لنظام التشغيل iOS.
  2. يمكنك إعداد المكوّن الإضافي. بما أنّ المكوّن الإضافي يتم تشغيله في وضع الحماية، عليك تثبيت بعض التبعيات:
    swift package plugin matter-codegen-init \
     --allow-network-connections all \
     --allow-writing-to-package-directory
  3. يمكنك اختيار مساحة اسم للرمز الذي تم إنشاؤه وإضافتها كـ 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;
    }
    
  4. يمكنك تشغيل المولّد:

    swift package plugin matter-codegen Clusters/MyCustomCluster.matter

    يمكن إضافة ملفات .swift التي تم إنشاؤها إلى مشروع تطبيقك أو إلى إطار إذا كنت تريدها في وحدة منفصلة.

بديل: إنشاء السمات تلقائيًا

يتضمّن نظام إنشاء الرموز مكوّنًا إضافيًا آخر يتعرّف على امتداد الملف .matter. سيستدعي المكوّن الإضافي تلقائيًا نظام إنشاء الرموز ويضيف ملفات Swift الناتجة إلى الهدف الحالي. يؤدي ذلك إلى تجنُّب الحاجة إلى إرسال الملفات التي تم إنشاؤها إلى نظام التحكّم بالمصادر، ويضمن إنشاء السمات دائمًا باستخدام الإصدار المجمّع من المولّد. إذا كان تطبيقك يستخدم SwiftPM حاليًا، ننصحك بشدة باستخدام هذا المكوّن الإضافي.

لاستخدام المكوّن الإضافي:

  1. يمكنك إضافة ملفات .matter إلى هدف في تطبيقك.
  2. يمكنك إضافة مقتطف المكوّن الإضافي التالي إلى هذا الهدف:

        .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
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 { }