blob: 954bd7de57dcbf076d93c600df8b04204f4a509f [file] [log] [blame]
{%- macro encode_value(source, encodable, depth) -%}
{%- if encodable.is_nullable -%}
@Nullable {{encode_value(source, encodable.without_nullable(), depth + 1)}}
{%- elif encodable.is_optional -%}
Optional<{{encode_value(source, encodable.without_optional(), depth + 1)}}>
{%- elif encodable.is_list -%}
ArrayList<{{encode_value(source, encodable.without_list(), depth + 1)}}>
{%- elif encodable.is_struct -%}
{%- set struct = encodable.get_underlying_struct() -%}
ChipStructs.{{source.name}}Cluster{{struct.name}}
{%- else -%}
{{encodable.boxed_java_type}}
{%- endif -%}
{%- endmacro -%}
{%- macro encode_value_without_optional(source, encodable, depth) -%}
{%- if encodable.is_nullable -%}
@Nullable {{encode_value_without_optional(source, encodable.without_nullable(), depth + 1)}}
{%- elif encodable.is_list -%}
List<{{encode_value_without_optional(source, encodable.without_list(), depth + 1)}}>
{%- elif encodable.is_struct -%}
{%- set struct = encodable.get_underlying_struct() -%}
ChipStructs.{{source.name}}Cluster{{struct.name}}
{%- else -%}
{{encodable.boxed_java_type}}
{%- endif -%}
{%- endmacro -%}
{%- macro encode_value_without_optional_nullable(source, encodable, depth) -%}
{%- if encodable.is_list -%}
ArrayList<{{encode_value_without_optional_nullable(source, encodable.without_list(), depth + 1)}}>
{%- elif encodable.is_struct -%}
{%- set struct = encodable.get_underlying_struct() -%}
ChipStructs.{{source.name}}Cluster{{struct.name}}
{%- else -%}
{{encodable.boxed_java_type}}
{%- endif -%}
{%- endmacro -%}
{%- macro encode_tlv(source, encodable, variable, depth) -%}
{%- if encodable.is_nullable -%}
{{variable}} != null ? {{encode_tlv(source, encodable.without_nullable(), variable, depth + 1)}} : new NullType()
{%- elif encodable.is_optional -%}
{{variable}}.<BaseTLVType>map(({{"nonOptional{}".format(variable)}}) -> {{encode_tlv(source, encodable.without_optional(), "nonOptional{}".format(variable), depth + 1)}}).orElse(new EmptyType())
{%- elif encodable.is_list -%}
ArrayType.generateArrayType({{variable}}, ({{"element{}".format(variable)}}) -> {{encode_tlv(source, encodable.without_list(), "element{}".format(variable), depth + 1)}})
{%- elif encodable.is_struct -%}
{%- set struct = encodable.get_underlying_struct() -%}
{{variable}}.encodeTlv()
{%- else -%}
new {{encodable.java_tlv_type}}Type({{variable}})
{%- endif -%}
{%- endmacro -%}
{%- macro decode_tlv(source, encodable, variable, depth) -%}
{%- if encodable.is_optional -%}
Optional.of({{decode_tlv(source, encodable.without_optional(), variable, depth + 1)}})
{%- elif encodable.is_list -%}
{{variable}}.map(({{"element{}".format(variable)}}) -> {{decode_tlv(source, encodable.without_list(), "element{}".format(variable), depth + 1)}})
{%- elif encodable.is_struct -%}
{%- set struct = encodable.get_underlying_struct() -%}
ChipStructs.{{source.name}}Cluster{{struct.name}}.decodeTlv({{variable}})
{%- else -%}
{{variable}}.value({{encodable.boxed_java_type}}.class)
{%- endif -%}
{%- endmacro -%}
{%- macro get_tlvType(encodable) -%}
{%- if encodable.is_list -%}
Array
{%- elif encodable.is_struct -%}
Struct
{%- else -%}
{{encodable.java_tlv_type}}
{%- endif -%}
{%- endmacro -%}
/*
*
* 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);
}
{% for cluster in clientClusters | sort(attribute='code') %}
{%- set typeLookup = idl | createLookupContext(cluster) %}
public static class {{cluster.name}}Cluster extends BaseChipCluster {
public static final long CLUSTER_ID = {{cluster.code}}L;
{% for attribute in cluster.attributes | sort(attribute='code') %}
private static final long {{attribute.definition.name | constcase}}_ATTRIBUTE_ID = {{attribute.definition.code}}L;
{%- endfor %}
public {{cluster.name}}Cluster(long devicePtr, int endpointId) {
super(devicePtr, endpointId, CLUSTER_ID);
}
@Override
@Deprecated
public long initWithDevice(long devicePtr, int endpointId) {
return 0L;
}
{% for command in cluster.commands | sort(attribute='code') -%}
{%- set callbackName = command | javaCommandCallbackName() -%}
{%- if not command.is_timed_invoke %}
public void {{command.name | lowfirst_except_acronym}}({{callbackName}}Callback callback
{%- if command.input_param -%}
{%- for field in (cluster.structs | named(command.input_param)).fields -%}
{%- set encodable = field | asEncodable(typeLookup) -%}
, {{encode_value(cluster, encodable, 0)}} {{field.name | lowfirst_except_acronym}}
{%- endfor -%}
{%- endif -%}
) {
{{command.name | lowfirst_except_acronym}}(callback
{%- if command.input_param -%}
{%- for field in (cluster.structs | named(command.input_param)).fields -%}
, {{field.name | lowfirst_except_acronym}}
{%- endfor -%}
{%- endif -%}
, 0);
}
{%- endif %}
public void {{command.name | lowfirst_except_acronym}}({{callbackName}}Callback callback
{%- if command.input_param -%}
{%- for field in (cluster.structs | named(command.input_param)).fields -%}
{%- set encodable = field | asEncodable(typeLookup) -%}
, {{encode_value(cluster, encodable, 0)}} {{field.name | lowfirst_except_acronym}}
{%- endfor -%}
{%- endif -%}
, int timedInvokeTimeoutMs) {
final long commandId = {{command.code}}L;
ArrayList<StructElement> elements = new ArrayList<>();
{%- if command.input_param -%}
{%- for field in (cluster.structs | named(command.input_param)).fields -%}
{%- set encodable = field | asEncodable(typeLookup) %}
final long {{field.name | lowfirst_except_acronym}}FieldID = {{field.code}}L;
BaseTLVType {{field.name | lowfirst_except_acronym}}tlvValue = {{encode_tlv(cluster, encodable, (field.name | lowfirst_except_acronym), 0)}};
elements.add(new StructElement({{field.name | lowfirst_except_acronym}}FieldID, {{field.name | lowfirst_except_acronym}}tlvValue));
{% endfor -%}
{%- endif %}
StructType commandArgs = new StructType(elements);
invoke(new InvokeCallbackImpl(callback) {
@Override
public void onResponse(StructType invokeStructValue) {
{%- if command | isCommandNotDefaultCallback() %}
{%- for field in (cluster.structs | named(command.output_param)).fields -%}
{%- set encodable = field | asEncodable(typeLookup) %}
{%- set encodable2 = field | asEncodable(typeLookup) %}
final long {{field.name | lowfirst_except_acronym}}FieldID = {{field.code}}L;
{{encode_value(cluster, encodable, 0)}} {{field.name | lowfirst_except_acronym}}
{%- if encodable2.is_nullable -%}
{{" = null"}}
{%- elif encodable2.is_optional -%}
{{" = Optional.empty()"}}
{%- else -%}
{{" = null"}}
{%- endif -%}
;
{%- endfor %}
for (StructElement element: invokeStructValue.value()) {
{%- for field in (cluster.structs | named(command.output_param)).fields -%}
{%- set encodable = field | asEncodable(typeLookup) %}
{% if loop.index0 > 0 -%}{{"} else "}}{%- endif -%}if (element.contextTagNum() == {{field.name | lowfirst_except_acronym}}FieldID) {
if (element.value(BaseTLVType.class).type() == TLVType.{{get_tlvType(encodable)}}) {
{{get_tlvType(encodable)}}Type castingValue = element.value({{get_tlvType(encodable)}}Type.class);
{{field.name | lowfirst_except_acronym}} = {{decode_tlv(cluster, encodable, "castingValue", 0)}};
}
{%- endfor -%}
{% raw %}
}
{%- endraw %}
}
callback.onSuccess(
{%- for field in (cluster.structs | named(command.output_param)).fields -%}
{{field.name | lowfirst_except_acronym}}
{%- if loop.index0 < loop.length - 1 -%}{{", "}}{%- endif -%}
{%- endfor -%}
);
{%- else %}
callback.onSuccess();
{%- endif %}
}}, commandId, commandArgs, timedInvokeTimeoutMs);
}
{% endfor %}
{%- set already_handled_command = [] -%}
{%- for command in cluster.commands | sort(attribute='code') -%}
{%- if command | isCommandNotDefaultCallback() -%}
{%- set callbackName = command | javaCommandCallbackName() -%}
{%- if callbackName not in already_handled_command %}
public interface {{callbackName}}Callback extends BaseClusterCallback {
void onSuccess(
{%- for field in (cluster.structs | named(command.output_param)).fields -%}
{%- set encodable = field | asEncodable(typeLookup) -%}
{{encode_value(cluster, encodable, 0)}} {{field.name | lowfirst_except_acronym}}
{%- if loop.index0 < loop.length - 1 -%}{{", "}}{%- endif -%}
{%- endfor -%}
);
}
{% if already_handled_command.append(callbackName) -%}
{#- This block does nothing, it only exists to append to already_handled_command. -#}
{%- endif -%}
{%- endif -%}
{%- endif -%}
{%- endfor %}
{%- set already_handled_attribute = [] -%}
{% for attribute in cluster.attributes | rejectattr('definition', 'is_field_global_name', typeLookup) %}
{%- set encodable = attribute.definition | asEncodable(typeLookup) -%}
{%- set interfaceName = attribute | javaAttributeCallbackName(typeLookup) -%}
{%- if interfaceName not in already_handled_attribute %}
public interface {{interfaceName}} extends BaseAttributeCallback {
void onSuccess({{encode_value_without_optional(cluster, encodable, 0)}} value);
}
{% if already_handled_attribute.append(interfaceName) -%}
{#- This block does nothing, it only exists to append to already_handled_attribute. -#}
{%- endif -%}
{%- endif -%}
{% endfor -%}
{% for attribute in cluster.attributes | sort(attribute='code') -%}
{%- if attribute | isFabricScopedList(typeLookup) %}
public void read{{attribute.definition.name | upfirst}}Attribute(
{{attribute | javaAttributeCallbackName(typeLookup) }} callback) {
read{{attribute.definition.name | upfirst}}AttributeWithFabricFilter(callback, true);
}
public void read{{attribute.definition.name | upfirst}}AttributeWithFabricFilter(
{{attribute | javaAttributeCallbackName(typeLookup) }} callback, boolean isFabricFiltered) {
{%- else %}
public void read{{attribute.definition.name | upfirst}}Attribute(
{{attribute | javaAttributeCallbackName(typeLookup) }} callback) {
{%- endif %}
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, {{attribute.definition.name | constcase}}_ATTRIBUTE_ID);
readAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
{%- set encodable = attribute.definition | asEncodable(typeLookup) %}
{{encode_value_without_optional(cluster, encodable, 0)}} value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, {{attribute.definition.name | constcase}}_ATTRIBUTE_ID, {% if attribute | isFabricScopedList(typeLookup) -%}isFabricFiltered{%- else -%}true{%- endif -%});
}
{% if attribute.is_writable %}
{%- set encodable = attribute.definition | asEncodable(typeLookup) -%}
{%- set encodable2 = attribute.definition | asEncodable(typeLookup) -%}
{%- set encodable3 = attribute.definition | asEncodable(typeLookup) -%}
{%- if not attribute.requires_timed_write %}
public void write{{attribute.definition.name | upfirst}}Attribute(DefaultClusterCallback callback, {{encode_value_without_optional_nullable(cluster, encodable, 0)}} value) {
write{{attribute.definition.name | upfirst}}Attribute(callback, value, 0);
}
{% endif %}
public void write{{attribute.definition.name | upfirst}}Attribute(DefaultClusterCallback callback, {{encode_value_without_optional_nullable(cluster, encodable2, 0)}} value, int timedWriteTimeoutMs) {
{%- if encodable3.is_optional %}
BaseTLVType tlvValue = {{encode_tlv(cluster, encodable3.without_optional(), "value", 0)}};
{%- else %}
BaseTLVType tlvValue = {{encode_tlv(cluster, encodable3, "value", 0)}};
{%- endif %}
writeAttribute(new WriteAttributesCallbackImpl(callback), {{attribute.definition.name | constcase}}_ATTRIBUTE_ID, tlvValue, timedWriteTimeoutMs);
}
{% endif %}
{%- if attribute.is_subscribable %}
public void subscribe{{attribute.definition.name | upfirst}}Attribute(
{{attribute | javaAttributeCallbackName(typeLookup) }} callback, int minInterval, int maxInterval) {
ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, {{attribute.definition.name | constcase}}_ATTRIBUTE_ID);
subscribeAttribute(new ReportCallbackImpl(callback, path) {
@Override
public void onSuccess(byte[] tlv) {
{%- set encodable = attribute.definition | asEncodable(typeLookup) %}
{{encode_value_without_optional(cluster, encodable, 0)}} value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
callback.onSuccess(value);
}
}, {{attribute.definition.name | constcase}}_ATTRIBUTE_ID, minInterval, maxInterval);
}
{% endif -%}
{%- endfor -%}
{% raw %} }
{% endraw %}
{%- endfor -%}
}