blob: 037870ee206c5acf8e29dd777fa9bd0e74e7183c [file] [log] [blame]
/*
*
* 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;
import chip.devicecontroller.model.AttributeState;
import chip.devicecontroller.model.AttributeWriteRequest;
import chip.devicecontroller.model.ChipAttributePath;
import chip.devicecontroller.model.ChipEventPath;
import chip.devicecontroller.model.ClusterState;
import chip.devicecontroller.model.EndpointState;
import chip.devicecontroller.model.InvokeElement;
import chip.devicecontroller.model.NodeState;
import chip.devicecontroller.model.Status;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static chip.devicecontroller.ChipTLVType.*;
public class ChipClusters {
public interface BaseClusterCallback {
void onError(Exception error);
}
public interface DefaultClusterCallback extends BaseClusterCallback {
void onSuccess();
}
public interface BaseAttributeCallback {
void onError(Exception error);
default void onSubscriptionEstablished(long subscriptionId) {}
}
public interface CharStringAttributeCallback extends BaseAttributeCallback {
/** Indicates a successful read for a CHAR_STRING attribute. */
void onSuccess(String value);
}
public interface OctetStringAttributeCallback extends BaseAttributeCallback {
/** Indicates a successful read for an OCTET_STRING attribute. */
void onSuccess(byte[] value);
}
public interface IntegerAttributeCallback extends BaseAttributeCallback {
void onSuccess(int value);
}
public interface LongAttributeCallback extends BaseAttributeCallback {
void onSuccess(long value);
}
public interface BooleanAttributeCallback extends BaseAttributeCallback {
void onSuccess(boolean value);
}
public interface FloatAttributeCallback extends BaseAttributeCallback {
void onSuccess(float value);
}
public interface DoubleAttributeCallback extends BaseAttributeCallback {
void onSuccess(double value);
}
public static abstract class BaseChipCluster {
protected long chipClusterPtr;
protected long devicePtr;
protected int endpointId;
protected long clusterId;
private Optional<Long> timeoutMillis = Optional.empty();
public BaseChipCluster(long devicePtr, int endpointId, long clusterId) {
this.devicePtr = devicePtr;
this.endpointId = endpointId;
this.clusterId = clusterId;
}
/**
* Sets the timeout, in milliseconds, after which commands sent through this cluster will fail
* with a timeout (regardless of whether or not a response has been received). If set to an
* empty optional, the default timeout will be used.
*/
public void setCommandTimeout(Optional<Long> timeoutMillis) {
this.timeoutMillis = timeoutMillis;
}
/** Returns the current timeout (in milliseconds) for commands sent through this cluster. */
public Optional<Long> getCommandTimeout() {
return timeoutMillis == null ? Optional.empty() : timeoutMillis;
}
@Deprecated
public abstract long initWithDevice(long devicePtr, int endpointId);
protected void readAttribute(
ReportCallbackImpl callback,
long attributeId,
boolean isFabricFiltered) {
ReportCallbackJni jniCallback = new ReportCallbackJni(null, callback, null);
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, attributeId);
ChipInteractionClient.read(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(path), null, null, isFabricFiltered, timeoutMillis.orElse(0L).intValue(), null);
}
protected void writeAttribute(
WriteAttributesCallbackImpl callback,
long attributeId,
BaseTLVType value,
int timedRequestTimeoutMs) {
WriteAttributesCallbackJni jniCallback = new WriteAttributesCallbackJni(callback);
byte[] tlv = encodeToTlv(value);
AttributeWriteRequest writeRequest = AttributeWriteRequest.newInstance(endpointId, clusterId, attributeId, tlv);
ChipInteractionClient.write(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(writeRequest), timedRequestTimeoutMs, timeoutMillis.orElse(0L).intValue());
}
protected void subscribeAttribute(
ReportCallbackImpl callback,
long attributeId,
int minInterval,
int maxInterval) {
ReportCallbackJni jniCallback = new ReportCallbackJni(callback, callback, callback);
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, attributeId);
int fabricIndex = ChipInteractionClient.getFabricIndex(devicePtr);
long deviceId = ChipInteractionClient.getRemoteDeviceId(devicePtr);
ChipInteractionClient.subscribe(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(path), null, null, minInterval, maxInterval, false, true, timeoutMillis.orElse(0L).intValue(), null, ChipICDClient.isPeerICDClient(fabricIndex, deviceId));
}
protected void invoke(
InvokeCallbackImpl callback,
long commandId,
BaseTLVType value,
int timedRequestTimeoutMs) {
InvokeCallbackJni jniCallback = new InvokeCallbackJni(callback);
byte[] tlv = encodeToTlv(value);
InvokeElement element = InvokeElement.newInstance(endpointId, clusterId, commandId, tlv, null);
ChipInteractionClient.invoke(0, jniCallback.getCallbackHandle(), devicePtr, element, timedRequestTimeoutMs, timeoutMillis.orElse(0L).intValue());
}
private static native byte[] encodeToTlv(BaseTLVType value);
static native BaseTLVType decodeFromTlv(byte[] tlv);
@Deprecated
public void deleteCluster(long chipClusterPtr) {}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
super.finalize();
if (chipClusterPtr != 0) {
deleteCluster(chipClusterPtr);
chipClusterPtr = 0;
}
}
}
abstract static class ReportCallbackImpl implements ReportCallback, SubscriptionEstablishedCallback, ResubscriptionAttemptCallback {
private BaseAttributeCallback callback;
private ChipAttributePath path;
private static final long CHIP_ERROR_UNSUPPORTED_ATTRIBUTE = 0x86;
ReportCallbackImpl(BaseAttributeCallback callback, ChipAttributePath path) {
this.callback = callback;
this.path = path;
}
@Override
public void onError(
@Nullable ChipAttributePath attributePath,
@Nullable ChipEventPath eventPath,
@Nonnull Exception e) {
callback.onError(e);
}
@Override
public void onReport(NodeState nodeState) {
if (nodeState == null) {
callback.onError(new ChipClusterException());
return;
}
EndpointState endpointState = nodeState.getEndpointState((int)path.getEndpointId().getId());
if (endpointState == null) {
callback.onError(new ChipClusterException(CHIP_ERROR_UNSUPPORTED_ATTRIBUTE));
return;
}
ClusterState clusterState = endpointState.getClusterState(path.getClusterId().getId());
if (clusterState == null) {
callback.onError(new ChipClusterException(CHIP_ERROR_UNSUPPORTED_ATTRIBUTE));
return;
}
AttributeState attributeState = clusterState.getAttributeState(path.getAttributeId().getId());
if (attributeState == null) {
callback.onError(new ChipClusterException(CHIP_ERROR_UNSUPPORTED_ATTRIBUTE));
return;
}
byte[] tlv = attributeState.getTlv();
if (tlv == null) {
callback.onError(new ChipClusterException(CHIP_ERROR_UNSUPPORTED_ATTRIBUTE));
return;
}
onSuccess(tlv);
}
@Override
public void onSubscriptionEstablished(long subscriptionId) {
callback.onSubscriptionEstablished(subscriptionId);
}
@Override
public void onResubscriptionAttempt(long terminationCause, long nextResubscribeIntervalMsec) {}
public abstract void onSuccess(byte[] tlv);
}
static class WriteAttributesCallbackImpl implements WriteAttributesCallback {
private DefaultClusterCallback callback;
WriteAttributesCallbackImpl(DefaultClusterCallback callback) {
this.callback = callback;
}
@Override
public void onResponse(ChipAttributePath attributePath, Status status) {
if (status.getStatus() == Status.Code.Success)
{
callback.onSuccess();
}
else
{
callback.onError(new StatusException(status.getStatus()));
}
}
@Override
public void onError(@Nullable ChipAttributePath attributePath, Exception e) {
callback.onError(e);
}
}
abstract static class InvokeCallbackImpl implements InvokeCallback {
private BaseClusterCallback callback;
private static final long CHIP_ERROR_UNSUPPORTED_COMMAND = 0x81;
InvokeCallbackImpl(BaseClusterCallback callback) {
this.callback = callback;
}
public void onError(Exception e) {
callback.onError(e);
}
public void onResponse(InvokeElement invokeElement, long successCode) {
byte[] tlv = invokeElement.getTlvByteArray();
if (tlv == null) {
onResponse(null);
return;
}
BaseTLVType value = BaseChipCluster.decodeFromTlv(tlv);
if (value == null || value.type() != TLVType.Struct) {
callback.onError(new ChipClusterException(CHIP_ERROR_UNSUPPORTED_COMMAND));
return;
}
onResponse((StructType)value);
}
public abstract void onResponse(StructType value);
}
public static class FirstCluster extends BaseChipCluster {
public static final long CLUSTER_ID = 1L;
private static final long SOME_INTEGER_ATTRIBUTE_ID = 1L;
public FirstCluster(long devicePtr, int endpointId) {
super(devicePtr, endpointId, CLUSTER_ID);
}
@Override
@Deprecated
public long initWithDevice(long devicePtr, int endpointId) {
return 0L;
}
public void readSomeIntegerAttribute(
IntegerAttributeCallback callback) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, SOME_INTEGER_ATTRIBUTE_ID);
readAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, SOME_INTEGER_ATTRIBUTE_ID, true);
}
public void writeSomeIntegerAttribute(DefaultClusterCallback callback, Integer value) {
writeSomeIntegerAttribute(callback, value, 0);
}
public void writeSomeIntegerAttribute(DefaultClusterCallback callback, Integer value, int timedWriteTimeoutMs) {
BaseTLVType tlvValue = new UIntType(value);
writeAttribute(new WriteAttributesCallbackImpl(callback), SOME_INTEGER_ATTRIBUTE_ID, tlvValue, timedWriteTimeoutMs);
}
public void subscribeSomeIntegerAttribute(
IntegerAttributeCallback callback, int minInterval, int maxInterval) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, SOME_INTEGER_ATTRIBUTE_ID);
subscribeAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, SOME_INTEGER_ATTRIBUTE_ID, minInterval, maxInterval);
}
}
public static class SecondCluster extends BaseChipCluster {
public static final long CLUSTER_ID = 2L;
private static final long FABRICS_ATTRIBUTE_ID = 0L;
private static final long SOME_BYTES_ATTRIBUTE_ID = 123L;
public SecondCluster(long devicePtr, int endpointId) {
super(devicePtr, endpointId, CLUSTER_ID);
}
@Override
@Deprecated
public long initWithDevice(long devicePtr, int endpointId) {
return 0L;
}
public interface FabricsAttributeCallback extends BaseAttributeCallback {
void onSuccess(List<ChipStructs.SecondClusterFabricDescriptorStruct> value);
}
public void readFabricsAttribute(
FabricsAttributeCallback callback) {
readFabricsAttributeWithFabricFilter(callback, true);
}
public void readFabricsAttributeWithFabricFilter(
FabricsAttributeCallback callback, boolean isFabricFiltered) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, FABRICS_ATTRIBUTE_ID);
readAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
List<ChipStructs.SecondClusterFabricDescriptorStruct> value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, FABRICS_ATTRIBUTE_ID, isFabricFiltered);
}
public void subscribeFabricsAttribute(
FabricsAttributeCallback callback, int minInterval, int maxInterval) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, FABRICS_ATTRIBUTE_ID);
subscribeAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
List<ChipStructs.SecondClusterFabricDescriptorStruct> value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, FABRICS_ATTRIBUTE_ID, minInterval, maxInterval);
}
public void readSomeBytesAttribute(
OctetStringAttributeCallback callback) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, SOME_BYTES_ATTRIBUTE_ID);
readAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
byte[] value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, SOME_BYTES_ATTRIBUTE_ID, true);
}
public void subscribeSomeBytesAttribute(
OctetStringAttributeCallback callback, int minInterval, int maxInterval) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, SOME_BYTES_ATTRIBUTE_ID);
subscribeAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
byte[] value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, SOME_BYTES_ATTRIBUTE_ID, minInterval, maxInterval);
}
}
public static class ThirdCluster extends BaseChipCluster {
public static final long CLUSTER_ID = 3L;
private static final long SOME_ENUM_ATTRIBUTE_ID = 10L;
private static final long OPTIONS_ATTRIBUTE_ID = 20L;
public ThirdCluster(long devicePtr, int endpointId) {
super(devicePtr, endpointId, CLUSTER_ID);
}
@Override
@Deprecated
public long initWithDevice(long devicePtr, int endpointId) {
return 0L;
}
public void readSomeEnumAttribute(
IntegerAttributeCallback callback) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, SOME_ENUM_ATTRIBUTE_ID);
readAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, SOME_ENUM_ATTRIBUTE_ID, true);
}
public void writeSomeEnumAttribute(DefaultClusterCallback callback, Integer value) {
writeSomeEnumAttribute(callback, value, 0);
}
public void writeSomeEnumAttribute(DefaultClusterCallback callback, Integer value, int timedWriteTimeoutMs) {
BaseTLVType tlvValue = new UIntType(value);
writeAttribute(new WriteAttributesCallbackImpl(callback), SOME_ENUM_ATTRIBUTE_ID, tlvValue, timedWriteTimeoutMs);
}
public void subscribeSomeEnumAttribute(
IntegerAttributeCallback callback, int minInterval, int maxInterval) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, SOME_ENUM_ATTRIBUTE_ID);
subscribeAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, SOME_ENUM_ATTRIBUTE_ID, minInterval, maxInterval);
}
public void readOptionsAttribute(
IntegerAttributeCallback callback) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, OPTIONS_ATTRIBUTE_ID);
readAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, OPTIONS_ATTRIBUTE_ID, true);
}
public void writeOptionsAttribute(DefaultClusterCallback callback, Integer value) {
writeOptionsAttribute(callback, value, 0);
}
public void writeOptionsAttribute(DefaultClusterCallback callback, Integer value, int timedWriteTimeoutMs) {
BaseTLVType tlvValue = new UIntType(value);
writeAttribute(new WriteAttributesCallbackImpl(callback), OPTIONS_ATTRIBUTE_ID, tlvValue, timedWriteTimeoutMs);
}
public void subscribeOptionsAttribute(
IntegerAttributeCallback callback, int minInterval, int maxInterval) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, OPTIONS_ATTRIBUTE_ID);
subscribeAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, OPTIONS_ATTRIBUTE_ID, minInterval, maxInterval);
}
}
}