diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/TlsCertificateManagementClusterTLSCertStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/TlsCertificateManagementClusterTLSCertStruct.kt new file mode 100644 index 00000000000000..304c1e9eee6bef --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/TlsCertificateManagementClusterTLSCertStruct.kt @@ -0,0 +1,56 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package chip.devicecontroller.cluster.structs + +import chip.devicecontroller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class TlsCertificateManagementClusterTLSCertStruct(val caid: UInt, val certificate: ByteArray) { + override fun toString(): String = buildString { + append("TlsCertificateManagementClusterTLSCertStruct {\n") + append("\tcaid : $caid\n") + append("\tcertificate : $certificate\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_CAID), caid) + put(ContextSpecificTag(TAG_CERTIFICATE), certificate) + endStructure() + } + } + + companion object { + private const val TAG_CAID = 0 + private const val TAG_CERTIFICATE = 1 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): TlsCertificateManagementClusterTLSCertStruct { + tlvReader.enterStructure(tlvTag) + val caid = tlvReader.getUInt(ContextSpecificTag(TAG_CAID)) + val certificate = tlvReader.getByteArray(ContextSpecificTag(TAG_CERTIFICATE)) + + tlvReader.exitContainer() + + return TlsCertificateManagementClusterTLSCertStruct(caid, certificate) + } + } +} diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/TlsCertificateManagementClusterTLSClientCertificateDetailStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/TlsCertificateManagementClusterTLSClientCertificateDetailStruct.kt new file mode 100644 index 00000000000000..bc248f23194310 --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/TlsCertificateManagementClusterTLSClientCertificateDetailStruct.kt @@ -0,0 +1,83 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package chip.devicecontroller.cluster.structs + +import chip.devicecontroller.cluster.* +import matter.tlv.AnonymousTag +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class TlsCertificateManagementClusterTLSClientCertificateDetailStruct( + val ccdid: UInt, + val clientCertificate: ByteArray, + val intermediateCerts: List, +) { + override fun toString(): String = buildString { + append("TlsCertificateManagementClusterTLSClientCertificateDetailStruct {\n") + append("\tccdid : $ccdid\n") + append("\tclientCertificate : $clientCertificate\n") + append("\tintermediateCerts : $intermediateCerts\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_CCDID), ccdid) + put(ContextSpecificTag(TAG_CLIENT_CERTIFICATE), clientCertificate) + startArray(ContextSpecificTag(TAG_INTERMEDIATE_CERTS)) + for (item in intermediateCerts.iterator()) { + put(AnonymousTag, item) + } + endArray() + endStructure() + } + } + + companion object { + private const val TAG_CCDID = 0 + private const val TAG_CLIENT_CERTIFICATE = 1 + private const val TAG_INTERMEDIATE_CERTS = 2 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): TlsCertificateManagementClusterTLSClientCertificateDetailStruct { + tlvReader.enterStructure(tlvTag) + val ccdid = tlvReader.getUInt(ContextSpecificTag(TAG_CCDID)) + val clientCertificate = tlvReader.getByteArray(ContextSpecificTag(TAG_CLIENT_CERTIFICATE)) + val intermediateCerts = + buildList { + tlvReader.enterArray(ContextSpecificTag(TAG_INTERMEDIATE_CERTS)) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getByteArray(AnonymousTag)) + } + tlvReader.exitContainer() + } + + tlvReader.exitContainer() + + return TlsCertificateManagementClusterTLSClientCertificateDetailStruct( + ccdid, + clientCertificate, + intermediateCerts, + ) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/TlsCertificateManagementCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/TlsCertificateManagementCluster.kt new file mode 100644 index 00000000000000..e4329039707422 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/TlsCertificateManagementCluster.kt @@ -0,0 +1,1414 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package matter.controller.cluster.clusters + +import java.time.Duration +import java.util.logging.Level +import java.util.logging.Logger +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.transform +import matter.controller.InvokeRequest +import matter.controller.InvokeResponse +import matter.controller.MatterController +import matter.controller.ReadData +import matter.controller.ReadRequest +import matter.controller.SubscribeRequest +import matter.controller.SubscriptionState +import matter.controller.UByteSubscriptionState +import matter.controller.UIntSubscriptionState +import matter.controller.UShortSubscriptionState +import matter.controller.cluster.structs.* +import matter.controller.model.AttributePath +import matter.controller.model.CommandPath +import matter.tlv.AnonymousTag +import matter.tlv.ContextSpecificTag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class TlsCertificateManagementCluster( + private val controller: MatterController, + private val endpointId: UShort, +) { + class ProvisionRootCertificateResponse(val caid: UShort) + + class FindRootCertificateResponse( + val certificateDetails: List + ) + + class LookupRootCertificateResponse(val caid: UShort) + + class TLSClientCSRResponse(val ccdid: UShort, val csr: ByteArray, val nonce: ByteArray) + + class ProvisionClientCertificateResponse(val ccdid: UShort) + + class FindClientCertificateResponse( + val certificateDetails: List + ) + + class LookupClientCertificateResponse(val ccdid: UShort) + + class GeneratedCommandListAttribute(val value: List) + + sealed class GeneratedCommandListAttributeSubscriptionState { + data class Success(val value: List) : GeneratedCommandListAttributeSubscriptionState() + + data class Error(val exception: Exception) : GeneratedCommandListAttributeSubscriptionState() + + object SubscriptionEstablished : GeneratedCommandListAttributeSubscriptionState() + } + + class AcceptedCommandListAttribute(val value: List) + + sealed class AcceptedCommandListAttributeSubscriptionState { + data class Success(val value: List) : AcceptedCommandListAttributeSubscriptionState() + + data class Error(val exception: Exception) : AcceptedCommandListAttributeSubscriptionState() + + object SubscriptionEstablished : AcceptedCommandListAttributeSubscriptionState() + } + + class EventListAttribute(val value: List) + + sealed class EventListAttributeSubscriptionState { + data class Success(val value: List) : EventListAttributeSubscriptionState() + + data class Error(val exception: Exception) : EventListAttributeSubscriptionState() + + object SubscriptionEstablished : EventListAttributeSubscriptionState() + } + + class AttributeListAttribute(val value: List) + + sealed class AttributeListAttributeSubscriptionState { + data class Success(val value: List) : AttributeListAttributeSubscriptionState() + + data class Error(val exception: Exception) : AttributeListAttributeSubscriptionState() + + object SubscriptionEstablished : AttributeListAttributeSubscriptionState() + } + + suspend fun provisionRootCertificate( + certificate: ByteArray, + caid: UShort?, + timedInvokeTimeout: Duration? = null, + ): ProvisionRootCertificateResponse { + val commandId: UInt = 0u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_CERTIFICATE_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_CERTIFICATE_REQ), certificate) + + val TAG_CAID_REQ: Int = 1 + caid?.let { tlvWriter.put(ContextSpecificTag(TAG_CAID_REQ), caid) } + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + + val tlvReader = TlvReader(response.payload) + tlvReader.enterStructure(AnonymousTag) + val TAG_CAID: Int = 0 + var caid_decoded: UShort? = null + + while (!tlvReader.isEndOfContainer()) { + val tag = tlvReader.peekElement().tag + + if (tag == ContextSpecificTag(TAG_CAID)) { + caid_decoded = tlvReader.getUShort(tag) + } else { + tlvReader.skipElement() + } + } + + if (caid_decoded == null) { + throw IllegalStateException("caid not found in TLV") + } + + tlvReader.exitContainer() + + return ProvisionRootCertificateResponse(caid_decoded) + } + + suspend fun findRootCertificate( + caid: UShort?, + timedInvokeTimeout: Duration? = null, + ): FindRootCertificateResponse { + val commandId: UInt = 2u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_CAID_REQ: Int = 0 + caid?.let { tlvWriter.put(ContextSpecificTag(TAG_CAID_REQ), caid) } + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + + val tlvReader = TlvReader(response.payload) + tlvReader.enterStructure(AnonymousTag) + val TAG_CERTIFICATE_DETAILS: Int = 0 + var certificateDetails_decoded: List? = null + + while (!tlvReader.isEndOfContainer()) { + val tag = tlvReader.peekElement().tag + + if (tag == ContextSpecificTag(TAG_CERTIFICATE_DETAILS)) { + certificateDetails_decoded = + buildList { + tlvReader.enterArray(tag) + while (!tlvReader.isEndOfContainer()) { + add(TlsCertificateManagementClusterTLSCertStruct.fromTlv(AnonymousTag, tlvReader)) + } + tlvReader.exitContainer() + } + } else { + tlvReader.skipElement() + } + } + + if (certificateDetails_decoded == null) { + throw IllegalStateException("certificateDetails not found in TLV") + } + + tlvReader.exitContainer() + + return FindRootCertificateResponse(certificateDetails_decoded) + } + + suspend fun lookupRootCertificate( + fingerprint: ByteArray, + timedInvokeTimeout: Duration? = null, + ): LookupRootCertificateResponse { + val commandId: UInt = 4u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_FINGERPRINT_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_FINGERPRINT_REQ), fingerprint) + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + + val tlvReader = TlvReader(response.payload) + tlvReader.enterStructure(AnonymousTag) + val TAG_CAID: Int = 0 + var caid_decoded: UShort? = null + + while (!tlvReader.isEndOfContainer()) { + val tag = tlvReader.peekElement().tag + + if (tag == ContextSpecificTag(TAG_CAID)) { + caid_decoded = tlvReader.getUShort(tag) + } else { + tlvReader.skipElement() + } + } + + if (caid_decoded == null) { + throw IllegalStateException("caid not found in TLV") + } + + tlvReader.exitContainer() + + return LookupRootCertificateResponse(caid_decoded) + } + + suspend fun removeRootCertificate(caid: UShort, timedInvokeTimeout: Duration? = null) { + val commandId: UInt = 6u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_CAID_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_CAID_REQ), caid) + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + } + + suspend fun TLSClientCSR( + nonce: ByteArray, + timedInvokeTimeout: Duration? = null, + ): TLSClientCSRResponse { + val commandId: UInt = 7u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_NONCE_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_NONCE_REQ), nonce) + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + + val tlvReader = TlvReader(response.payload) + tlvReader.enterStructure(AnonymousTag) + val TAG_CCDID: Int = 0 + var ccdid_decoded: UShort? = null + + val TAG_CSR: Int = 1 + var csr_decoded: ByteArray? = null + + val TAG_NONCE: Int = 2 + var nonce_decoded: ByteArray? = null + + while (!tlvReader.isEndOfContainer()) { + val tag = tlvReader.peekElement().tag + + if (tag == ContextSpecificTag(TAG_CCDID)) { + ccdid_decoded = tlvReader.getUShort(tag) + } + + if (tag == ContextSpecificTag(TAG_CSR)) { + csr_decoded = tlvReader.getByteArray(tag) + } + + if (tag == ContextSpecificTag(TAG_NONCE)) { + nonce_decoded = tlvReader.getByteArray(tag) + } else { + tlvReader.skipElement() + } + } + + if (ccdid_decoded == null) { + throw IllegalStateException("ccdid not found in TLV") + } + + if (csr_decoded == null) { + throw IllegalStateException("csr not found in TLV") + } + + if (nonce_decoded == null) { + throw IllegalStateException("nonce not found in TLV") + } + + tlvReader.exitContainer() + + return TLSClientCSRResponse(ccdid_decoded, csr_decoded, nonce_decoded) + } + + suspend fun provisionClientCertificate( + ccdid: UShort, + clientCertificateDetails: TlsCertificateManagementClusterTLSClientCertificateDetailStruct, + timedInvokeTimeout: Duration? = null, + ): ProvisionClientCertificateResponse { + val commandId: UInt = 9u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_CCDID_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_CCDID_REQ), ccdid) + + val TAG_CLIENT_CERTIFICATE_DETAILS_REQ: Int = 1 + clientCertificateDetails.toTlv( + ContextSpecificTag(TAG_CLIENT_CERTIFICATE_DETAILS_REQ), + tlvWriter, + ) + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + + val tlvReader = TlvReader(response.payload) + tlvReader.enterStructure(AnonymousTag) + val TAG_CCDID: Int = 0 + var ccdid_decoded: UShort? = null + + while (!tlvReader.isEndOfContainer()) { + val tag = tlvReader.peekElement().tag + + if (tag == ContextSpecificTag(TAG_CCDID)) { + ccdid_decoded = tlvReader.getUShort(tag) + } else { + tlvReader.skipElement() + } + } + + if (ccdid_decoded == null) { + throw IllegalStateException("ccdid not found in TLV") + } + + tlvReader.exitContainer() + + return ProvisionClientCertificateResponse(ccdid_decoded) + } + + suspend fun findClientCertificate( + ccdid: UShort, + timedInvokeTimeout: Duration? = null, + ): FindClientCertificateResponse { + val commandId: UInt = 11u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_CCDID_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_CCDID_REQ), ccdid) + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + + val tlvReader = TlvReader(response.payload) + tlvReader.enterStructure(AnonymousTag) + val TAG_CERTIFICATE_DETAILS: Int = 0 + var certificateDetails_decoded: + List? = + null + + while (!tlvReader.isEndOfContainer()) { + val tag = tlvReader.peekElement().tag + + if (tag == ContextSpecificTag(TAG_CERTIFICATE_DETAILS)) { + certificateDetails_decoded = + buildList { + tlvReader.enterArray(tag) + while (!tlvReader.isEndOfContainer()) { + add( + TlsCertificateManagementClusterTLSClientCertificateDetailStruct.fromTlv( + AnonymousTag, + tlvReader, + ) + ) + } + tlvReader.exitContainer() + } + } else { + tlvReader.skipElement() + } + } + + if (certificateDetails_decoded == null) { + throw IllegalStateException("certificateDetails not found in TLV") + } + + tlvReader.exitContainer() + + return FindClientCertificateResponse(certificateDetails_decoded) + } + + suspend fun lookupClientCertificate( + fingerprint: ByteArray, + timedInvokeTimeout: Duration? = null, + ): LookupClientCertificateResponse { + val commandId: UInt = 13u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_FINGERPRINT_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_FINGERPRINT_REQ), fingerprint) + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + + val tlvReader = TlvReader(response.payload) + tlvReader.enterStructure(AnonymousTag) + val TAG_CCDID: Int = 0 + var ccdid_decoded: UShort? = null + + while (!tlvReader.isEndOfContainer()) { + val tag = tlvReader.peekElement().tag + + if (tag == ContextSpecificTag(TAG_CCDID)) { + ccdid_decoded = tlvReader.getUShort(tag) + } else { + tlvReader.skipElement() + } + } + + if (ccdid_decoded == null) { + throw IllegalStateException("ccdid not found in TLV") + } + + tlvReader.exitContainer() + + return LookupClientCertificateResponse(ccdid_decoded) + } + + suspend fun removeClientCertificate(ccdid: UShort, timedInvokeTimeout: Duration? = null) { + val commandId: UInt = 15u + + val tlvWriter = TlvWriter() + tlvWriter.startStructure(AnonymousTag) + + val TAG_CCDID_REQ: Int = 0 + tlvWriter.put(ContextSpecificTag(TAG_CCDID_REQ), ccdid) + tlvWriter.endStructure() + + val request: InvokeRequest = + InvokeRequest( + CommandPath(endpointId, clusterId = CLUSTER_ID, commandId), + tlvPayload = tlvWriter.getEncoded(), + timedRequest = timedInvokeTimeout, + ) + + val response: InvokeResponse = controller.invoke(request) + logger.log(Level.FINE, "Invoke command succeeded: ${response}") + } + + suspend fun readMaxRootCertificatesAttribute(): UByte { + val ATTRIBUTE_ID: UInt = 0u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Maxrootcertificates attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UByte = tlvReader.getUByte(AnonymousTag) + + return decodedValue + } + + suspend fun subscribeMaxRootCertificatesAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 0u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + UByteSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Maxrootcertificates attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UByte = tlvReader.getUByte(AnonymousTag) + + emit(UByteSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(UByteSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readCurrentRootCertificatesAttribute(): UByte { + val ATTRIBUTE_ID: UInt = 1u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Currentrootcertificates attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UByte = tlvReader.getUByte(AnonymousTag) + + return decodedValue + } + + suspend fun subscribeCurrentRootCertificatesAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 1u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + UByteSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Currentrootcertificates attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UByte = tlvReader.getUByte(AnonymousTag) + + emit(UByteSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(UByteSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readMaxClientCertificatesAttribute(): UByte { + val ATTRIBUTE_ID: UInt = 2u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Maxclientcertificates attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UByte = tlvReader.getUByte(AnonymousTag) + + return decodedValue + } + + suspend fun subscribeMaxClientCertificatesAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 2u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + UByteSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Maxclientcertificates attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UByte = tlvReader.getUByte(AnonymousTag) + + emit(UByteSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(UByteSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readCurrentClientCertificatesAttribute(): UByte { + val ATTRIBUTE_ID: UInt = 3u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Currentclientcertificates attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UByte = tlvReader.getUByte(AnonymousTag) + + return decodedValue + } + + suspend fun subscribeCurrentClientCertificatesAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 3u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + UByteSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Currentclientcertificates attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UByte = tlvReader.getUByte(AnonymousTag) + + emit(UByteSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(UByteSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readGeneratedCommandListAttribute(): GeneratedCommandListAttribute { + val ATTRIBUTE_ID: UInt = 65528u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Generatedcommandlist attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + return GeneratedCommandListAttribute(decodedValue) + } + + suspend fun subscribeGeneratedCommandListAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65528u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + GeneratedCommandListAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Generatedcommandlist attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + emit(GeneratedCommandListAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(GeneratedCommandListAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readAcceptedCommandListAttribute(): AcceptedCommandListAttribute { + val ATTRIBUTE_ID: UInt = 65529u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Acceptedcommandlist attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + return AcceptedCommandListAttribute(decodedValue) + } + + suspend fun subscribeAcceptedCommandListAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65529u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + AcceptedCommandListAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Acceptedcommandlist attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + emit(AcceptedCommandListAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(AcceptedCommandListAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readEventListAttribute(): EventListAttribute { + val ATTRIBUTE_ID: UInt = 65530u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Eventlist attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + return EventListAttribute(decodedValue) + } + + suspend fun subscribeEventListAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65530u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + EventListAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { "Eventlist attribute not found in Node State update" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + emit(EventListAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(EventListAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readAttributeListAttribute(): AttributeListAttribute { + val ATTRIBUTE_ID: UInt = 65531u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Attributelist attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + return AttributeListAttribute(decodedValue) + } + + suspend fun subscribeAttributeListAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65531u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + AttributeListAttributeSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { "Attributelist attribute not found in Node State update" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: List = + buildList { + tlvReader.enterArray(AnonymousTag) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getUInt(AnonymousTag)) + } + tlvReader.exitContainer() + } + + emit(AttributeListAttributeSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(AttributeListAttributeSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readFeatureMapAttribute(): UInt { + val ATTRIBUTE_ID: UInt = 65532u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Featuremap attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UInt = tlvReader.getUInt(AnonymousTag) + + return decodedValue + } + + suspend fun subscribeFeatureMapAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65532u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + UIntSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { "Featuremap attribute not found in Node State update" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UInt = tlvReader.getUInt(AnonymousTag) + + emit(UIntSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(UIntSubscriptionState.SubscriptionEstablished) + } + } + } + } + + suspend fun readClusterRevisionAttribute(): UShort { + val ATTRIBUTE_ID: UInt = 65533u + + val attributePath = + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + + val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath)) + + val response = controller.read(readRequest) + + if (response.successes.isEmpty()) { + logger.log(Level.WARNING, "Read command failed") + throw IllegalStateException("Read command failed with failures: ${response.failures}") + } + + logger.log(Level.FINE, "Read command succeeded") + + val attributeData = + response.successes.filterIsInstance().firstOrNull { + it.path.attributeId == ATTRIBUTE_ID + } + + requireNotNull(attributeData) { "Clusterrevision attribute not found in response" } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UShort = tlvReader.getUShort(AnonymousTag) + + return decodedValue + } + + suspend fun subscribeClusterRevisionAttribute( + minInterval: Int, + maxInterval: Int, + ): Flow { + val ATTRIBUTE_ID: UInt = 65533u + val attributePaths = + listOf( + AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID) + ) + + val subscribeRequest: SubscribeRequest = + SubscribeRequest( + eventPaths = emptyList(), + attributePaths = attributePaths, + minInterval = Duration.ofSeconds(minInterval.toLong()), + maxInterval = Duration.ofSeconds(maxInterval.toLong()), + ) + + return controller.subscribe(subscribeRequest).transform { subscriptionState -> + when (subscriptionState) { + is SubscriptionState.SubscriptionErrorNotification -> { + emit( + UShortSubscriptionState.Error( + Exception( + "Subscription terminated with error code: ${subscriptionState.terminationCause}" + ) + ) + ) + } + is SubscriptionState.NodeStateUpdate -> { + val attributeData = + subscriptionState.updateState.successes + .filterIsInstance() + .firstOrNull { it.path.attributeId == ATTRIBUTE_ID } + + requireNotNull(attributeData) { + "Clusterrevision attribute not found in Node State update" + } + + // Decode the TLV data into the appropriate type + val tlvReader = TlvReader(attributeData.data) + val decodedValue: UShort = tlvReader.getUShort(AnonymousTag) + + emit(UShortSubscriptionState.Success(decodedValue)) + } + SubscriptionState.SubscriptionEstablished -> { + emit(UShortSubscriptionState.SubscriptionEstablished) + } + } + } + } + + companion object { + private val logger = Logger.getLogger(TlsCertificateManagementCluster::class.java.name) + const val CLUSTER_ID: UInt = 2049u + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/TlsCertificateManagementClusterTLSCertStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/TlsCertificateManagementClusterTLSCertStruct.kt new file mode 100644 index 00000000000000..ff9d9f02cb12c4 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/TlsCertificateManagementClusterTLSCertStruct.kt @@ -0,0 +1,56 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package matter.controller.cluster.structs + +import matter.controller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class TlsCertificateManagementClusterTLSCertStruct(val caid: UShort, val certificate: ByteArray) { + override fun toString(): String = buildString { + append("TlsCertificateManagementClusterTLSCertStruct {\n") + append("\tcaid : $caid\n") + append("\tcertificate : $certificate\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_CAID), caid) + put(ContextSpecificTag(TAG_CERTIFICATE), certificate) + endStructure() + } + } + + companion object { + private const val TAG_CAID = 0 + private const val TAG_CERTIFICATE = 1 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): TlsCertificateManagementClusterTLSCertStruct { + tlvReader.enterStructure(tlvTag) + val caid = tlvReader.getUShort(ContextSpecificTag(TAG_CAID)) + val certificate = tlvReader.getByteArray(ContextSpecificTag(TAG_CERTIFICATE)) + + tlvReader.exitContainer() + + return TlsCertificateManagementClusterTLSCertStruct(caid, certificate) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/TlsCertificateManagementClusterTLSClientCertificateDetailStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/TlsCertificateManagementClusterTLSClientCertificateDetailStruct.kt new file mode 100644 index 00000000000000..3360c9ab7cdc20 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/TlsCertificateManagementClusterTLSClientCertificateDetailStruct.kt @@ -0,0 +1,83 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package matter.controller.cluster.structs + +import matter.controller.cluster.* +import matter.tlv.AnonymousTag +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class TlsCertificateManagementClusterTLSClientCertificateDetailStruct( + val ccdid: UShort, + val clientCertificate: ByteArray, + val intermediateCerts: List, +) { + override fun toString(): String = buildString { + append("TlsCertificateManagementClusterTLSClientCertificateDetailStruct {\n") + append("\tccdid : $ccdid\n") + append("\tclientCertificate : $clientCertificate\n") + append("\tintermediateCerts : $intermediateCerts\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_CCDID), ccdid) + put(ContextSpecificTag(TAG_CLIENT_CERTIFICATE), clientCertificate) + startArray(ContextSpecificTag(TAG_INTERMEDIATE_CERTS)) + for (item in intermediateCerts.iterator()) { + put(AnonymousTag, item) + } + endArray() + endStructure() + } + } + + companion object { + private const val TAG_CCDID = 0 + private const val TAG_CLIENT_CERTIFICATE = 1 + private const val TAG_INTERMEDIATE_CERTS = 2 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): TlsCertificateManagementClusterTLSClientCertificateDetailStruct { + tlvReader.enterStructure(tlvTag) + val ccdid = tlvReader.getUShort(ContextSpecificTag(TAG_CCDID)) + val clientCertificate = tlvReader.getByteArray(ContextSpecificTag(TAG_CLIENT_CERTIFICATE)) + val intermediateCerts = + buildList { + tlvReader.enterArray(ContextSpecificTag(TAG_INTERMEDIATE_CERTS)) + while (!tlvReader.isEndOfContainer()) { + add(tlvReader.getByteArray(AnonymousTag)) + } + tlvReader.exitContainer() + } + + tlvReader.exitContainer() + + return TlsCertificateManagementClusterTLSClientCertificateDetailStruct( + ccdid, + clientCertificate, + intermediateCerts, + ) + } + } +}