Üreticiye özel (MS) özellikler, Android için Home API'leri tarafından desteklenir ve Android'daki standart özelliklerin ötesinde ek işlevleri destekledikleri için API'lerde üreticiye özel özellikler olarak adlandırılır. Bunlar standart .matter
IDL biçiminde tanımlanmalı ve ardından uygulamanıza aktarılabilen bir Android paketine dönüştürülmelidir.
Bu dönüşümü gerçekleştirmek için Google tarafından sağlanan paketlenmiş kod oluşturucuyu kullanın. Ayrıca, gerekirse kod oluşturucu kullanılarak geçici özellikler de oluşturulabilir.
Ön koşullar
Kod oluşturucuyu kullanmak için gerekenler:
- Python 3.10 veya daha yeni bir sürümün yüklü olduğu Linux tabanlı bir makine.
- MS özelliklerinizin tanımını içeren bir
.matter
IDL dosyası. Bu dosya yalnızcaclient cluster
tanımlarını içermelidir. Bu dosyaları manuel olarak oluşturabilir veya cihazınızın donanım yazılımı için Matter SDK derleme sürecinin bir parçası olarak oluşturulanları kullanabilirsiniz.
IDL biçimi hakkında daha fazla bilgi için GitHub'daki matter/idl sayfasına bakın. Buradaki /tests/inputs dizininde bir dizi örnek IDL dosyası bulunur.
Paket oluşturma
Paketlenmiş kod üretme aracını alın:
Paketlenmiş Kod Oluşturma Aracı'nı İndirme
- Özellik kodunun oluşturulacağı Java paketinin adını belirleyin. Örneğin,
com.mycompany.matter.cluster
. Bu ad, uygulamanızın uygulama kimliğiyle eşleşmelidir. Paket adlandırma kuralları hakkında daha fazla bilgi edinmek için Paket adları başlıklı makaleyi inceleyin. - Jeneratörü çıkarın ve ayarlayın:
mkdir -p ~/tmp/codegen_test
cd ~/tmp/codegen_test
tar xfvz ~/tmp/matter_codegen.tar.gz
pip install -r requirements.txt
- Oluşturma aracını çalıştırın:
./codegen.py \ --generator custom:.:kotlin \ --output-dir ./generated/com/mycompany/matter/cluster \ --option package:com.mycompany.matter.cluster \ custom-cluster-idl.matter
Paketi kullanma
MS özelliği paketinizi kullanmak için paketi uygulamanıza aktarın:
import com.mycompany.matter.cluster
Ardından, MS özellikleri Matter donanım yazılımınızda tanımlandığı sürece standart Matter özellikleri gibi Home API'leri üzerinden kullanılabilir. Standart bir özellik adını MS özellik adınızla değiştirmeniz yeterlidir.
Örneğin, MS özelliğinizin adı CustomTrait
ise aşağıdaki çağrı, CustomTrait
özelliğinin tüm özelliklerini döndürür:
val device = devices().get(id)?
val deviceType = devices().get(id)?.type?.value
val trait = device?.type(deviceType)?.map{it.trait(CustomTrait)}.firstOrNull()
Ek bağımlılıklar
Derleme için MS özelliklerini kullanan bir uygulamada, uygulamanızın build.gradle
dosyasına aşağıdaki bağımlılıkları da eklemeniz gerekebilir:
implementation 'com.google.errorprone:error_prone_annotations:2.35.1'
Örnek
IDL biçimini bilmiyorsanız örnek dosyalar için matter/idl/tests/inputs dizinlerine bakın.
IDL girişi
Çok basit bir MS özelliği, IDL'de şu şekilde tanımlanabilir:
// mycustom.matter
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;
}
Bu örnekte, 4294048768
özelliğinin kimliği onaltılık sistemde 0xFFF1FC00
değerine karşılık gelir. Burada 0xFFF1
öneki bir test tedarikçi kimliğini, 0xFC00
soneki ise üreticiye özel özellikler için ayrılmış bir değeri temsil eder. Daha fazla bilgi için Matter spesifikasyonunun Üretici Tarafından Genişletilebilir Tanımlayıcı (MEI) bölümüne bakın. IDL dosyanızdaki her MS özelliği için uygun bir ondalık özellik kimliği kullandığınızdan emin olun.
Cihazınızda bugün MS özellikleri kullanılıyorsa büyük olasılıkla bu biçimde tanımlanmıştır.
Kod üretme aracını çalıştırma
mycustom.matter
dosyasını kod üreticiyle aynı dizinde çalıştırın:
./codegen.py \
--generator custom:.:kotlin \
--output-dir ./generated/com/mycompany/matter/cluster \
--option package: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 çıktısı
MyCustom.kt
ve MyCustomTrait.kt
adlı iki Kotlin dosyası artık belirtilen çıkış dizininde bulunabilir. Bu dosyalar, Home API'leriyle kullanılmak üzere özel olarak biçimlendirilir.
Kullanıma sunulduktan sonra (ör. uygulamanızın Android Studio projesinde) Paketi kullanma bölümünde açıklandığı şekilde kullanılabilirler.
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
}