ویژگی های خاص سازنده در اندروید

ویژگی‌های مختص تولیدکننده (MS) توسط APIهای Home برای Android پشتیبانی می‌شوند و در APIها به عنوان ویژگی‌های مختص تولیدکننده شناخته می‌شوند، زیرا از قابلیت‌های اضافی فراتر از ویژگی‌های استاندارد در Android پشتیبانی می‌کنند. آن‌ها باید در قالب استاندارد .matter IDL تعریف شوند و سپس به یک بسته Android تبدیل شوند که بتوان آن را به برنامه شما وارد کرد.

برای انجام این تبدیل از مولد کد بسته‌بندی‌شده‌ی ارائه‌شده توسط گوگل استفاده کنید. علاوه بر این، در صورت نیاز، می‌توان صفات موقت را نیز با استفاده از مولد کد تولید کرد.

پیش‌نیازها

برای استفاده از مولد کد، به موارد زیر نیاز دارید:

  • یک دستگاه مبتنی بر لینوکس با پایتون ۳.۱۰ یا جدیدتر.
  • یک فایل .matter IDL با تعریف ویژگی‌های سیستم عامل شما. این فایل باید فقط شامل تعاریف client cluster باشد. می‌توانید آن را به صورت دستی ایجاد کنید یا از فایل‌هایی که به عنوان بخشی از فرآیند ساخت Matter SDK برای میان‌افزار دستگاه شما تولید می‌شوند، استفاده کنید.

برای اطلاعات بیشتر در مورد قالب IDL، به matter/idl در GitHub مراجعه کنید. دایرکتوری /tests/inputs در آنجا شامل تعدادی فایل نمونه IDL است.

تولید یک بسته

مولد کد بسته‌بندی‌شده را دریافت کنید:

دانلود مولد کد بسته‌بندی‌شده

  1. یک نام برای بسته جاوا انتخاب کنید تا کد ویژگی در آن تولید شود. برای مثال، com.mycompany.matter.cluster . این نام باید با شناسه برنامه شما مطابقت داشته باشد. برای کسب اطلاعات بیشتر در مورد قراردادهای نامگذاری بسته، به Package names مراجعه کنید.
  2. ژنراتور را استخراج و تنظیم کنید:
    mkdir -p ~/tmp/codegen_test
    cd ~/tmp/codegen_test
    tar xfvz ~/tmp/matter_codegen.tar.gz
    python3 -m venv .venv
    .venv/bin/pip install .
  3. ژنراتور را اجرا کنید:
    .venv/bin/python3 google_home_codegen.py \
      --lang kotlin \
      --output-dir ./generated/com/mycompany/matter/cluster \
      --option package:com.mycompany.matter.cluster \
      custom-cluster-idl.matter

نکته‌ای در مورد گزینه‌های کدژن

به جای ارائه گزینه‌ها به صورت --option key:value در خط فرمان، می‌توانید گزینه‌های codegen را در توضیحات pragma در بالای فایل IDL مشخص کنید. برای مثال:

// 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;
}

از بسته استفاده کنید

برای استفاده از بسته‌ی ویژگی MS خود، آن را به برنامه‌ی خود وارد کنید:

import com.mycompany.matter.cluster

سپس ویژگی‌های MS باید از طریق APIهای Home همانند ویژگی‌های استاندارد Matter در دسترس باشند، البته تا زمانی که این ویژگی‌های MS در میان‌افزار Matter شما تعریف شده باشند. کافیست نام ویژگی استاندارد را با نام ویژگی MS خود جایگزین کنید.

برای مثال، اگر ویژگی MS شما CustomTrait نام داشته باشد، فراخوانی زیر تمام ویژگی‌های CustomTrait را برمی‌گرداند:

val device = devices().get(id)?

val deviceType = devices().get(id)?.type?.value

val trait = device?.type(deviceType)?.map{it.trait(CustomTrait)}.firstOrNull()

وابستگی‌های اضافی

برای کامپایل کردن برنامه‌ای که از MS trait استفاده می‌کند، ممکن است لازم باشد وابستگی‌های زیر را نیز به فایل build.gradle برنامه خود اضافه کنید:

implementation 'com.google.errorprone:error_prone_annotations:2.35.1'

مثال

اگر با فرمت 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 در دستگاه شما استفاده می‌شود، احتمالاً آن را از قبل با این فرمت تعریف کرده‌اید.

مولد کد را اجرا کنید

با قرار دادن فایل mycustom.matter در همان دایرکتوری که مولد کد قرار دارد، آن را اجرا کنید:

google_home_codegen.py \
  --lang kotlin \
  --output-dir ./generated/com/mycompany/matter/cluster \
  mycustom.matter
2024-09-03 19:00:09 INFO    Parsing idl from mycustom.matter
2024-09-03 19:00:09 INFO    Using CustomGenerator at plugin path ..kotlin
2024-09-03 19:00:09 INFO    Running code generator CUSTOM
2024-09-03 19:00:10 INFO    File to be generated: MyCustomTrait.kt
2024-09-03 19:00:10 INFO    Template path: ClusterSerialization.kt.jinja, CWD: /usr/local/google/home/username/codegen_test
2024-09-03 19:00:11 INFO    Creating output directory: ./generated/com/mycompany/matter/cluster
2024-09-03 19:00:11 INFO    Writing new data to: ./generated/com/mycompany/matter/cluster/MyCustomTrait.kt
2024-09-03 19:00:11 INFO    File to be generated: MyCustom.kt
2024-09-03 19:00:11 INFO    Template path: Cluster.kt.jinja, CWD: /usr/local/google/home/username/codegen_test
2024-09-03 19:00:11 INFO    Writing new data to: ./generated/com/mycompany/matter/cluster/MyCustom.kt
2024-09-03 19:00:11 INFO    Done

خروجی کاتلین

اکنون دو فایل کاتلین، MyCustom.kt و MyCustomTrait.kt ، در دایرکتوری خروجی مشخص شده قابل دسترسی هستند. این فایل‌ها به طور خاص برای استفاده با APIهای Home قالب‌بندی شده‌اند.

زمانی که در دسترس قرار گرفتند (مانند پروژه Android Studio برنامه شما)، می‌توانند همانطور که در بخش «استفاده از بسته» توضیح داده شده است، مورد استفاده قرار گیرند.

MyCustom.kt

// This file contains machine-generated code.
@file:Suppress("PackageName")
package com.mycompany.matter.cluster
import com.google.home.annotation.HomeExperimentalApi
import com.google.home.BatchableCommand
import com.google.home.HomeException
import com.google.home.ClusterStruct
import com.google.home.Id
import com.google.home.Event
import com.google.home.EventFactory
import com.google.home.EventImportance
import com.google.home.Field
import com.google.home.Descriptor as HomeDescriptor
import com.google.home.NoOpDescriptor
import com.google.home.StructDescriptor
import com.google.home.Type as FieldType
import com.google.home.Trait
import com.google.home.TraitFactory
import com.google.home.Updatable
import com.google.home.toDescriptorMap
import com.google.home.DescriptorMap
import com.google.errorprone.annotations.Immutable
import com.google.home.automation.Attribute as AutomationAttribute
import com.google.home.automation.AttributeToUpdate
import com.google.home.automation.Command as AutomationCommand
import com.google.home.automation.EventField
import com.google.home.automation.TypedExpression
import com.google.home.automation.Updater
import com.google.home.automation.fieldSelect
import com.google.home.matter.EventImpl
import com.google.home.matter.MatterEventFactory
import com.google.home.matter.MatterTrait
import com.google.home.matter.MatterTraitImpl
import com.google.home.matter.MatterTraitFactory
import com.google.home.matter.serialization.BitmapAdapter
import com.google.home.matter.serialization.EnumAdapter
import com.mycompany.matter.cluster.MyCustomTrait
import com.mycompany.matter.cluster.MyCustomTrait.Attributes
import com.mycompany.matter.cluster.MyCustomTrait.AttributesImpl
import com.mycompany.matter.cluster.MyCustomTrait.MutableAttributes
import com.google.home.matter.MatterTraitClient
import com.google.home.matter.serialization.OptionalValue
import java.time.Instant
import javax.annotation.processing.Generated

/*
 * This file was machine generated via the code generator
 * in `codegen.clusters.kotlin.CustomGenerator`
 *
 */

/**
 * @suppress

 * Commands for the MyCustom trait.
 */

/**
 * API for the MyCustom trait.
 */
@Generated("GoogleHomePlatformCodegen")
interface MyCustom : 
   Attributes, MatterTrait
   , Updatable<MyCustom, MutableAttributes>
{
   /**
   * Descriptor enum for this trait's attributes.
   */
  enum class Attribute(
    override val fieldName: String,
    override val tag: UInt,
    override val typeName: String,
    override val typeEnum: FieldType,
    override val descriptor: HomeDescriptor,
    val isNullable: Boolean,
  ) : Field {
    /** The [clusterAttr][MyCustomTrait.Attributes.clusterAttr] trait attribute. */
    clusterAttr("clusterAttr", 1u, "UShort", FieldType.UShort, NoOpDescriptor, false),
    /** The [generatedCommandList][MyCustomTrait.Attributes.generatedCommandList] trait attribute. */
    generatedCommandList("generatedCommandList", 65528u, "UInt", FieldType.UInt, NoOpDescriptor, false),
    /** The [acceptedCommandList][MyCustomTrait.Attributes.acceptedCommandList] trait attribute. */
    acceptedCommandList("acceptedCommandList", 65529u, "UInt", FieldType.UInt, NoOpDescriptor, false),
    /** The [attributeList][MyCustomTrait.Attributes.attributeList] trait attribute. */
    attributeList("attributeList", 65531u, "UInt", FieldType.UInt, NoOpDescriptor, false),
    /** The [featureMap][MyCustomTrait.Attributes.featureMap] trait attribute. */
    featureMap("featureMap", 65532u, "UInt", FieldType.UInt, NoOpDescriptor, false),
    /** The [clusterRevision][MyCustomTrait.Attributes.clusterRevision] trait attribute. */
    clusterRevision("clusterRevision", 65533u, "UShort", FieldType.UShort, NoOpDescriptor, false);
    

    companion object {
      val StructDescriptor = object : StructDescriptor {
        @Suppress("Immutable")
        override val fields: DescriptorMap = entries.toDescriptorMap()

        @HomeExperimentalApi
        override fun toStruct(fields: Map<Field, Any?>): ClusterStruct {
          return AttributesImpl(
            clusterAttr = fields[clusterAttr] as UShort?,
            generatedCommandList = fields[generatedCommandList] as List<UInt>,
            acceptedCommandList = fields[acceptedCommandList] as List<UInt>,
            attributeList = fields[attributeList] as List<UInt>,
            featureMap = fields[featureMap] as UInt,
            clusterRevision = fields[clusterRevision] as UShort,
          )
        }
      }
    }
  }

  fun supports(attribute : Attribute): Boolean

  /**
   * @suppress
   */
  companion object : TraitFactory<MyCustom>(
    MatterTraitFactory(
      clusterId = MyCustomTrait.Id,
      adapter = Attributes.Adapter,
      traitDescriptor = Attribute.StructDescriptor,
      // Map of enum type name string -> EnumAdapter
      enumAdapters = mapOf<String, EnumAdapter<*>>(
      ),
      bitmapAdapters = mapOf<String, BitmapAdapter<*>>(
      ),
      creator = ::MyCustomImpl,
      supportedEvents = mapOf(
      ),
      // All Trait Commands
      commands = mapOf(
      )
    )
  ) {
    val clusterAttr: AutomationAttribute<UShort?>
      get() = AutomationAttribute<UShort?>(MyCustomTrait.Id.traitId, MyCustom.Attribute.clusterAttr.tag)
    val generatedCommandList: AutomationAttribute<List<UInt>>
      get() = AutomationAttribute<List<UInt>>(MyCustomTrait.Id.traitId, MyCustom.Attribute.generatedCommandList.tag)
    val acceptedCommandList: AutomationAttribute<List<UInt>>
      get() = AutomationAttribute<List<UInt>>(MyCustomTrait.Id.traitId, MyCustom.Attribute.acceptedCommandList.tag)
    val attributeList: AutomationAttribute<List<UInt>>
      get() = AutomationAttribute<List<UInt>>(MyCustomTrait.Id.traitId, MyCustom.Attribute.attributeList.tag)
    val featureMap: AutomationAttribute<UInt>
      get() = AutomationAttribute<UInt>(MyCustomTrait.Id.traitId, MyCustom.Attribute.featureMap.tag)
    val clusterRevision: AutomationAttribute<UShort>
      get() = AutomationAttribute<UShort>(MyCustomTrait.Id.traitId, MyCustom.Attribute.clusterRevision.tag)

    val TypedExpression<out MyCustom?>.clusterAttr: TypedExpression<UShort?>
      get() = fieldSelect<MyCustom, UShort?>(this, MyCustom.Attribute.clusterAttr)
    val TypedExpression<out MyCustom?>.generatedCommandList: TypedExpression<List<UInt>>
      get() = fieldSelect<MyCustom, List<UInt>>(this, MyCustom.Attribute.generatedCommandList)
    val TypedExpression<out MyCustom?>.acceptedCommandList: TypedExpression<List<UInt>>
      get() = fieldSelect<MyCustom, List<UInt>>(this, MyCustom.Attribute.acceptedCommandList)
    val TypedExpression<out MyCustom?>.attributeList: TypedExpression<List<UInt>>
      get() = fieldSelect<MyCustom, List<UInt>>(this, MyCustom.Attribute.attributeList)
    val TypedExpression<out MyCustom?>.featureMap: TypedExpression<UInt>
      get() = fieldSelect<MyCustom, UInt>(this, MyCustom.Attribute.featureMap)
    val TypedExpression<out MyCustom?>.clusterRevision: TypedExpression<UShort>
      get() = fieldSelect<MyCustom, UShort>(this, MyCustom.Attribute.clusterRevision)

    fun Updater<MyCustom>.setClusterAttr(value: UShort) { attributesToUpdate.add(AttributeToUpdate(Attribute.clusterAttr, value)) }

    @HomeExperimentalApi
    override fun getAttributeById(tagId: UInt): Field? {
      return Attribute.values().firstOrNull { it.tag == tagId }
    }

    @HomeExperimentalApi
    override fun getAttributeByName(name: String): Field? {
      return Attribute.values().firstOrNull { it.name == name }
    }

    override fun toString() = "MyCustom"
  }

  override val factory : TraitFactory<MyCustom> get() = Companion
}

/**
 * @suppress
 */
class MyCustomImpl

constructor(
  override val metadata: Trait.TraitMetadata,
  client: MatterTraitClient,
  internal val attributes: Attributes
) :
  MyCustom,
  MatterTraitImpl(metadata, client),
  Attributes by attributes,
  Updatable<MyCustom, MutableAttributes>
{
  override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (other !is MyCustomImpl) return false

    if (metadata != other.metadata) return false
    if (attributes != other.attributes) return false

    return true
  }

   /**
   * Checks if the trait supports an attribute. Some devices might not
   * implement all attributes in a Trait definition.
   *
   * @param attribute The attribute to check for.
   * @return True if the attribute is supported by the trait, false if it is not.
   */
  override fun supports(attribute : MyCustom.Attribute) = attributes.attributeList.contains(attribute.tag)

  // Commands
  /**
   * @suppress
   */
  override suspend fun update(
    optimisticReturn: (MyCustom) -> Unit,
    init: MutableAttributes.() -> Unit
  ): MyCustom
   {
    val newVal = MutableAttributes(attributes).apply(init)
    val returnVal = MyCustomImpl(metadata, client, newVal)
    optimisticReturn(returnVal)
    write(MutableAttributes, newVal,
    useTimedInteraction = false
    )
    return returnVal
   }



  override fun toString() = attributes.toString()
}

MyCustomTrait.kt


// This file contains machine-generated code.
@file:Suppress("PackageName")
package com.mycompany.matter.cluster

import com.google.home.Type as FieldType
import com.google.home.annotation.HomeExperimentalApi
import com.google.errorprone.annotations.Immutable
import com.google.home.automation.TypedExpression
import com.google.home.automation.fieldSelect
import com.google.home.CommandDescriptor
import com.google.home.HomeException
import com.google.home.toDescriptorMap
import com.google.home.DescriptorMap
import com.google.home.Descriptor as HomeDescriptor
import com.google.home.ClusterStruct
import com.google.home.Tag
import com.google.home.TagId
import com.google.home.NoOpDescriptor
import com.google.home.StructDescriptor
import com.google.home.EnumDescriptor
import com.google.home.toEnumDescriptor
import com.google.home.EnumEntry
import com.google.home.Field
import com.mycompany.matter.cluster.MyCustom
import com.google.home.matter.serialization.Bitmap
import com.google.home.matter.serialization.BitmapAdapter
import com.google.home.matter.serialization.CanMutate
import com.google.home.matter.serialization.ClusterBitmap
import com.google.home.matter.serialization.ClusterEnum
import com.google.home.matter.serialization.ClusterId
import com.google.home.matter.serialization.ClusterPayloadReader
import com.google.home.matter.serialization.ClusterPayloadWriter
import com.google.home.matter.serialization.EnumAdapter
import com.google.home.matter.serialization.OptionalValue
import com.google.home.matter.serialization.MutableBitmap
import com.google.home.matter.serialization.ScopedCommandId
import com.google.home.matter.serialization.ScopedEventId
import com.google.home.matter.serialization.StructAdapter
import com.google.home.matter.serialization.unwrapPayload
import com.google.home.matter.serialization.wrapPayload
import kotlin.collections.contentDeepEquals
import kotlin.collections.contentEquals
import kotlin.collections.contentHashCode
import javax.annotation.processing.Generated

/*
 * Serialization object for MyCustomTrait.
 *
 * This file was machine generate via the code generator
 * in `codegen.clusters.kotlin.CustomGenerator`
 *
 */

/**
 * Attributes for MyCustomTrait.
 */
@Generated("GoogleHomePlatformCodegen")
object MyCustomTrait {
  val Id = ClusterId(4294048768u, "MyCustom")

  // Enums

  // Bitmaps

  // Events

  // Structs

  /**
   * Attributes for the MyCustom cluster.
   */
  @Generated("GoogleHomePlatformCodegen")
  interface Attributes : ClusterStruct {
    val clusterAttr: UShort?

    /** A list of server-generated commands (server to client) which are supported by this cluster server instance. */
    val generatedCommandList: List<UInt>

    /** A list of client-generated commands which are supported by this cluster server instance. */
    val acceptedCommandList: List<UInt>

    /** A list of the attribute IDs of the attributes supported by the cluster instance. */
    val attributeList: List<UInt>

    /**  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). */
    val featureMap: UInt

    /** The revision of the server cluster specification supported by the cluster instance. */
    val clusterRevision: UShort

    @HomeExperimentalApi
    override fun getDescriptor(): StructDescriptor = MyCustom.Attribute.StructDescriptor

    @HomeExperimentalApi
    override fun getFieldValueById(tagId: TagId): Any? {
      return when (tagId) {
        MyCustom.Attribute.clusterAttr.tag -> clusterAttr
        MyCustom.Attribute.generatedCommandList.tag -> generatedCommandList
        MyCustom.Attribute.acceptedCommandList.tag -> acceptedCommandList
        MyCustom.Attribute.attributeList.tag -> attributeList
        MyCustom.Attribute.featureMap.tag -> featureMap
        MyCustom.Attribute.clusterRevision.tag -> clusterRevision
        else -> null
      }
    }

    /** @suppress */
    companion object Adapter : StructAdapter<Attributes> {

      override fun write(writer: ClusterPayloadWriter, value: Attributes) {
        if (value is MutableAttributes) {
            MutableAttributes.Adapter.write(writer, value)
            return
        }
        writer.wrapPayload(id = Id)
        if (!writer.strictOperationValidation || value.attributeList.contains(1u)) {
          writer.ushort.write(1u, value.clusterAttr)
        }
        writer.uint.writeList(65528u, value.generatedCommandList)
        writer.uint.writeList(65529u, value.acceptedCommandList)
        writer.uint.writeList(65531u, value.attributeList)
        writer.uint.write(65532u, value.featureMap)
        writer.ushort.write(65533u, value.clusterRevision)
      }
      override fun read(reader: ClusterPayloadReader): Attributes {
        reader.unwrapPayload(id = Id)
        val data = reader.readPayload()
        val attributeList = mutableListOf<UInt>()
        return AttributesImpl(
          data.ushort.getOptionalNullable(1u, "ClusterAttr").also{ if (it.isPresent && it.value != null) attributeList.add(1u) }.getOrNull(),
          data.uint.getList(65528u, "GeneratedCommandList").also{ attributeList.add(65528u)},
          data.uint.getList(65529u, "AcceptedCommandList").also{ attributeList.add(65529u)},
          attributeList.also { attributeList.add(65531u) },
          data.uint.get(65532u, "FeatureMap").also{ attributeList.add(65532u)},
          data.ushort.get(65533u, "ClusterRevision").also{ attributeList.add(65533u)},
        )
      }
    }
  }

  /** @suppress */
  open class AttributesImpl(
    override val clusterAttr: UShort? =null,
    override val generatedCommandList: List<UInt> =emptyList(),
    override val acceptedCommandList: List<UInt> =emptyList(),
    override val attributeList: List<UInt> =listOf(1u,65528u,65529u,65531u,65532u,65533u,),
    override val featureMap: UInt =0u,
    override val clusterRevision: UShort =0u,
  ) : Attributes, CanMutate<Attributes, MutableAttributes>{

    constructor(other: Attributes): this(
      clusterAttr  = other.clusterAttr,
      generatedCommandList  = other.generatedCommandList,
      acceptedCommandList  = other.acceptedCommandList,
      attributeList  = other.attributeList,
      featureMap  = other.featureMap,
      clusterRevision  = other.clusterRevision)

    override fun mutate(init: MutableAttributes.() -> Unit): Attributes =
      AttributesImpl(MutableAttributes(this).apply(init))

    companion object {
      val Adapter = Attributes.Adapter
    }

    override fun equals(other: Any?): Boolean {
      if (this === other) return true
      if (other !is Attributes) return false
      if (clusterAttr != other.clusterAttr) { return false; }
      if (generatedCommandList != other.generatedCommandList) { return false; }
      if (acceptedCommandList != other.acceptedCommandList) { return false; }
      if (attributeList != other.attributeList) { return false; }
      if (featureMap != other.featureMap) { return false; }
      if (clusterRevision != other.clusterRevision) { return false; }

      return true
    }

    override fun hashCode(): Int {
      var result = 1
      result = 31 * result + (clusterAttr?.hashCode() ?: 0)
      result = 31 * result + generatedCommandList.hashCode()
      result = 31 * result + acceptedCommandList.hashCode()
      result = 31 * result + attributeList.hashCode()
      result = 31 * result + featureMap.hashCode()
      result = 31 * result + clusterRevision.hashCode()

      return result
    }

    override fun toString(): String {
      return "MyCustom(clusterAttr=$clusterAttr, generatedCommandList=$generatedCommandList, acceptedCommandList=$acceptedCommandList, attributeList=$attributeList, featureMap=$featureMap, clusterRevision=$clusterRevision)"
    }

    fun copy(
      clusterAttr: UShort? = this.clusterAttr,
      generatedCommandList: List<UInt> = this.generatedCommandList,
      acceptedCommandList: List<UInt> = this.acceptedCommandList,
      attributeList: List<UInt> = this.attributeList,
      featureMap: UInt = this.featureMap,
      clusterRevision: UShort = this.clusterRevision,
    ) = AttributesImpl(
      clusterAttr = clusterAttr,
      generatedCommandList = generatedCommandList,
      acceptedCommandList = acceptedCommandList,
      attributeList = attributeList,
      featureMap = featureMap,
      clusterRevision = clusterRevision,
    )
  }

  /** @suppress */
  class MutableAttributes(attributes: Attributes) : 
    AttributesImpl(
      clusterAttr = attributes.clusterAttr,
      generatedCommandList = attributes.generatedCommandList,
      acceptedCommandList = attributes.acceptedCommandList,
      attributeList = attributes.attributeList,
      featureMap = attributes.featureMap,
      clusterRevision = attributes.clusterRevision,
    ) {
    internal var _clusterAttr : UShort? = null
    override val clusterAttr : UShort?
      get() {
        return _clusterAttr ?: super.clusterAttr
      }
    fun setClusterAttr(value : UShort) {
        _clusterAttr = value
    }

    override fun equals(other: Any?): Boolean {
      if (this === other) return true
      if (other !is MutableAttributes) return false
      return super.equals(other)
    }

    override fun toString(): String {
      return "MyCustom.MutableAttributes(${super.toString()})"
    }

    companion object Adapter : StructAdapter<MutableAttributes> {
      override fun write(writer: ClusterPayloadWriter, value: MutableAttributes) {
        writer.wrapPayload(id = Id)
        if (value._clusterAttr != null) {
          if (!writer.strictOperationValidation || value.attributeList.contains(1u)) {
            writer.ushort.write(1u, value._clusterAttr)
          } else {
            throw HomeException.invalidArgument("clusterAttr")
          }
        }
      }

      override fun read(reader: ClusterPayloadReader): MutableAttributes =
        MutableAttributes(Attributes.Adapter.read(reader))
    }
  }

  // Commands

}