Manufacturer-specific (MS) traits are supported by the Home APIs for
Android, and are referred to as manufacturer-specific
traits in the APIs since they support additional functionality beyond standard
traits in Android. They must be defined in the standard
`.matter` IDL format, then converted into an Android
package that can be imported into your app.

Use the Google-provided packaged code generator to perform this conversion.
Additionally, if needed, provisional traits can also be generated using the
code generator.
| **Note:** The term *trait* in the Home APIs is equivalent to *cluster* in Matter. Although both terms refer to the same thing - a bundle of related device capabilities encapsulated in a reusable building block - the Home API documentation favors the use of *trait* over *cluster* . See [Definitions](https://developers.home.google.com/apis/get-started#definitions).

## Prerequisites

In order to use the code generator, you need:

- A Linux-based machine with Python 3.10 or newer.
- A `.matter` IDL file with the definition of your MS traits. This file should contain just the `client cluster` definitions. You can create one manually, or use ones generated as part of the Matter SDK build process for your device firmware.

For more information about the IDL format, see
[matter/idl](https://github.com/project-chip/connectedhomeip/tree/master/scripts/py_matter_idl/matter/idl)
on GitHub. The /tests/inputs directory there features a number of sample IDL
files.
| **Note:** The complete IDL file for all Matter clusters, which is the source for generated files on all platforms (including the [Android
| packages](https://developers.home.google.com/reference/kotlin/com/google/home/matter/standard/package-summary) for the Home APIs) can be found at [controller-clusters.matter](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/data_model/controller-clusters.matter).

## Generate a package

Get the packaged code generator:

[Download the Packaged Code
Generator](https://developers.home.google.com/static/apis/matter/matter_codegen.tar.gz)

1. Decide on a Java package name to generate the trait code in. For example, `com.mycompany.matter.cluster`. This name should align with your app's Application ID. To learn more about package naming conventions, see [Package
   names](https://developer.android.com/kotlin/style-guide#package_names).
2. Extract and set up the generator:  

       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. Run the generator:  

       .venv/bin/python3 matter_codegen.py \
         --lang kotlin \
         --output-dir ./generated/com/mycompany/matter/cluster \
         --option package:com.mycompany.matter.cluster \
         <var translate="no">custom-cluster-idl</var>.matter

### Note on codegen options

Instead of providing options as `--option key:value` on the command line, you
may specify the codegen options in `pragma` comments at the top of the IDL file.
For example:  

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

## Use the package

To use your MS trait package, import it into your app:  

    import com.mycompany.matter.cluster

Then MS traits should be available through the Home APIs the same way
standard Matter traits are, as long as those MS
traits are defined in your Matter firmware. Simply
substitute a standard trait name with your MS trait name.

For example, if your MS trait is named `CustomTrait`, the following call
returns all attributes of `CustomTrait`:  

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

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

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

### Additional dependencies

For an app using MS traits to compile, you may also need to add the following
dependencies to the `build.gradle` file for your app:  

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

## Example

If you are not familiar with the IDL format, see the
[matter/idl/tests/inputs](https://github.com/project-chip/connectedhomeip/tree/master/scripts/py_matter_idl/matter/idl/tests/inputs)
directories for sample files.

### IDL input

A very simple MS trait can be defined in the IDL like this:  

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

In this example, the trait ID of `4294048768` corresponds to `0xFFF1FC00` in
hexadecimal, where the prefix of `0xFFF1` represents a test Vendor ID and the
suffix of `0xFC00` is a value reserved for Manufacturer-Specific traits. See
the *Manufacturer Extensible Identifier (MEI)* section of the
Matter Specification for more information. Make sure to use
an appropriate decimal trait ID for each MS trait in your IDL file.

If MS traits are being used in your device today, you likely have it
defined in this format already.

### Run the code generator

With the `mycustom.matter` file in the same directory as the code generator, run
it:  

    matter_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
```

### Kotlin output

Two Kotlin files, `MyCustom.kt` and `MyCustomTrait.kt`, can now be found in
the specified output directory. These files are formatted specifically for use
with the Home APIs.

Once available (such as in your app's Android Studio project),
they can be used as described in [Use the package](https://developers.home.google.com/apis/android/manufacturer-specific-traits#use_the_package).  

### MyCustom.kt

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

    import com.google.home.BatchableCommand
    import com.google.home.HomeException
    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
     */


    /**
     * 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()
          }
        }
      }

      fun supports(attribute : Attribute): Boolean

      /**
       * @suppress
       */
      companion object : TraitFactory<MyCustom>(
        MatterTraitFactory(
          clusterId = MyCustomTrait.Id,
          adapter = Attributes.Adapter,
          // 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)) }

        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.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.TagId
    import com.google.home.NoOpDescriptor
    import com.google.home.StructDescriptor
    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 {
        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

        /** @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

    }