blob: 66033f0100de5fc06e92cfcbf21619e0641393c9 [file] [log] [blame]
/*
*
* Copyright (c) 2020 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.
*/
// Import helpers from zap core
const zapPath = '../../../../../third_party/zap/repo/dist/src-electron/';
const templateUtil = require(zapPath + 'generator/template-util.js')
const zclHelper = require(zapPath + 'generator/helper-zcl.js')
const queryCommand = require(zapPath + 'db/query-command.js')
const zclQuery = require(zapPath + 'db/query-zcl.js')
const queryEvents = require(zapPath + 'db/query-event.js')
const cHelper = require(zapPath + 'generator/helper-c.js')
const string = require(zapPath + 'util/string.js')
const dbEnum = require(zapPath + '../src-shared/db-enum.js')
const StringHelper = require('../../common/StringHelper.js');
const ChipTypesHelper = require('../../common/ChipTypesHelper.js');
const TestHelper = require('../../common/ClusterTestGeneration.js');
zclHelper['isEvent'] = function (db, event_name, packageId) {
return queryEvents
.selectAllEvents(db, packageId)
.then(events => events.find(event => event.name == event_name))
.then(events => events ? 'event' : dbEnum.zclType.unknown);
}
// This list of attributes is taken from section '11.2. Global Attributes' of the
// Data Model specification.
const kGlobalAttributes = [
0xfff8, // GeneratedCommandList
0xfff9, // AcceptedCommandList
0xfffb, // AttributeList
0xfffc, // ClusterRevision
0xfffd, // FeatureMap
];
// Endpoint-config specific helpers
// these helpers are a Hot fix for the "GENERATED_FUNCTIONS" problem
// They should be removed or replace once issue #4369 is resolved
// These helpers only works within the endpoint_config iterator
// List of all cluster with generated functions
var endpointClusterWithInit = [
'Basic',
'Color Control',
'Groups',
'Identify',
'Level Control',
'Localization Configuration',
'Occupancy Sensing',
'On/Off',
'Pump Configuration and Control',
'Scenes',
'Time Format Localization',
'Thermostat',
'Mode Select',
];
var endpointClusterWithAttributeChanged = [
'Bridged Device Basic',
'Door Lock',
'Identify',
'Pump Configuration and Control',
'Window Covering',
'Fan Control',
];
var endpointClusterWithPreAttribute = [
'Door Lock',
'Pump Configuration and Control',
'Thermostat User Interface Configuration',
'Time Format Localization',
'Localization Configuration',
'Mode Select',
'Fan Control',
'Thermostat',
];
/**
* Populate the GENERATED_FUNCTIONS field
*/
function chip_endpoint_generated_functions()
{
let alreadySetCluster = [];
let ret = '\\\n';
this.clusterList.forEach((c) => {
let clusterName = c.clusterName;
let functionList = '';
if (alreadySetCluster.includes(clusterName)) {
// Only one array of Generated functions per cluster across all endpoints
return
}
if (c.comment.includes('server')) {
let hasFunctionArray = false
if (endpointClusterWithInit.includes(clusterName))
{
hasFunctionArray = true
functionList = functionList.concat(
` (EmberAfGenericClusterFunction) emberAf${cHelper.asCamelCased(clusterName, false)}ClusterServerInitCallback,\\\n`)
}
if (endpointClusterWithAttributeChanged.includes(clusterName)) {
functionList = functionList.concat(` (EmberAfGenericClusterFunction) Matter${
cHelper.asCamelCased(clusterName, false)}ClusterServerAttributeChangedCallback,\\\n`)
hasFunctionArray = true
}
if (endpointClusterWithPreAttribute.includes(clusterName)) {
functionList = functionList.concat(` (EmberAfGenericClusterFunction) Matter${
cHelper.asCamelCased(clusterName, false)}ClusterServerPreAttributeChangedCallback,\\\n`)
hasFunctionArray = true
}
if (hasFunctionArray) {
ret = ret.concat(
`const EmberAfGenericClusterFunction chipFuncArray${cHelper.asCamelCased(clusterName, false)}Server[] = {\\\n`)
ret = ret.concat(functionList)
ret = ret.concat(`};\\\n`)
alreadySetCluster.push(clusterName)
}
}
})
return ret.concat('\n');
}
function chip_endpoint_generated_commands_list(options)
{
let ret = [];
let index = 0;
this.clusterList.forEach((c) => {
let acceptedCommands = [];
let generatedCommands = [];
c.commands.forEach((cmd) => {
if (cmd.mask.includes('incoming_server')) {
acceptedCommands.push(`${cmd.commandId} /* ${cmd.name} */`);
if (cmd.responseId !== null) {
generatedCommands.push(`${cmd.responseId} /* ${cmd.responseName} */`);
}
}
});
generatedCommands = [...new Set(generatedCommands) ].sort();
if (acceptedCommands.length > 0 || generatedCommands.length > 0) {
ret.push({ text : ` /* ${c.comment} */\\` });
}
if (acceptedCommands.length > 0) {
acceptedCommands.push('chip::kInvalidCommandId /* end of list */')
ret.push({ text : ` /* AcceptedCommandList (index=${index}) */ \\\n ${acceptedCommands.join(', \\\n ')}, \\` });
index += acceptedCommands.length;
}
if (generatedCommands.length > 0) {
generatedCommands.push('chip::kInvalidCommandId /* end of list */')
ret.push({ text : ` /* GeneratedCommandList (index=${index})*/ \\\n ${generatedCommands.join(', \\\n ')}, \\` });
index += generatedCommands.length;
}
})
return templateUtil.collectBlocks(ret, options, this);
}
/**
* Return endpoint config GENERATED_CLUSTER MACRO
* To be used as a replacement of endpoint_cluster_list since this one
* includes the GENERATED_FUNCTIONS array
*/
function chip_endpoint_cluster_list()
{
let ret = '{ \\\n';
let totalCommands = 0;
this.clusterList.forEach((c) => {
let mask = '';
let functionArray = c.functions;
let clusterName = c.clusterName;
if (c.comment.includes('server')) {
let hasFunctionArray = false;
if (endpointClusterWithInit.includes(clusterName)) {
c.mask.push('INIT_FUNCTION')
hasFunctionArray = true
}
if (endpointClusterWithAttributeChanged.includes(clusterName)) {
c.mask.push('ATTRIBUTE_CHANGED_FUNCTION')
hasFunctionArray = true
}
if (endpointClusterWithPreAttribute.includes(clusterName)) {
c.mask.push('PRE_ATTRIBUTE_CHANGED_FUNCTION')
hasFunctionArray = true
}
if (hasFunctionArray) {
functionArray = 'chipFuncArray' + cHelper.asCamelCased(clusterName, false) + 'Server'
}
}
if (c.mask.length == 0) {
mask = '0'
} else {
mask = c.mask.map((m) => `ZAP_CLUSTER_MASK(${m.toUpperCase()})`).join(' | ')
}
let acceptedCommands = 0;
let generatedCommandList = [];
c.commands.forEach((cmd) => {
if (cmd.mask.includes('incoming_server')) {
acceptedCommands++;
if (cmd.responseId !== null) {
generatedCommandList.push(cmd.responseId);
}
}
});
let generatedCommands = new Set(generatedCommandList).size;
let acceptedCommandsListVal = "nullptr";
let generatedCommandsListVal = "nullptr";
if (acceptedCommands > 0) {
acceptedCommands++; // Leaves space for the terminator
acceptedCommandsListVal = `ZAP_GENERATED_COMMANDS_INDEX( ${totalCommands} )`;
}
if (generatedCommands > 0) {
generatedCommands++; // Leaves space for the terminator
generatedCommandsListVal = `ZAP_GENERATED_COMMANDS_INDEX( ${totalCommands + acceptedCommands} )`;
}
ret = ret.concat(` { \\
/* ${c.comment} */ \\
.clusterId = ${c.clusterId}, \\
.attributes = ZAP_ATTRIBUTE_INDEX(${c.attributeIndex}), \\
.attributeCount = ${c.attributeCount}, \\
.clusterSize = ${c.attributeSize}, \\
.mask = ${mask}, \\
.functions = ${functionArray}, \\
.acceptedCommandList = ${acceptedCommandsListVal} ,\\
.generatedCommandList = ${generatedCommandsListVal} ,\\
},\\\n`)
totalCommands = totalCommands + acceptedCommands + generatedCommands;
})
return ret.concat('}\n');
}
/**
* Return the number of data versions we need for our fixed endpoints.
*
* This is just the count of server clusters on those endpoints.
*/
function chip_endpoint_data_version_count()
{
let serverCount = 0;
for (const ep of this.endpoints) {
let epType = this.endpointTypes.find(type => type.id == ep.endpointTypeRef);
for (const cluster of epType.clusters) {
if (cluster.side == "server") {
++serverCount;
}
}
}
return serverCount;
}
// End of Endpoint-config specific helpers
async function asNativeType(type)
{
function fn(pkgId)
{
const options = { 'hash' : {} };
return zclHelper.asUnderlyingZclType.call(this, type, options).then(zclType => {
return ChipTypesHelper.asBasicType(zclType);
})
}
const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => {
console.log(err);
throw err;
});
return templateUtil.templatePromise(this.global, promise)
}
async function asTypedExpression(value, type)
{
const valueIsANumber = !isNaN(value);
if (!value || valueIsANumber) {
return asTypedLiteral.call(this, value, type);
}
const tokens = value.split(' ');
if (tokens.length < 2) {
return asTypedLiteral.call(this, value, type);
}
value = tokens
.map(token => {
if (!TestHelper.chip_tests_variables_has.call(this, token)) {
return token;
}
if (!TestHelper.chip_tests_variables_is_nullable.call(this, token)) {
return token;
}
return `${token}.Value()`;
})
.join(' ');
const resultType = await asNativeType.call(this, type);
return `static_cast<${resultType}>(${value})`;
}
async function asTypedLiteral(value, type, cookie)
{
const valueIsANumber = !isNaN(value);
if (!valueIsANumber) {
return value;
}
const basicType = await asNativeType.call(this, type);
switch (basicType) {
case 'int32_t':
return value + 'L';
case 'int64_t':
return value + 'LL';
case 'uint8_t':
case 'uint16_t':
return value + 'U';
case 'uint32_t':
return value + 'UL';
case 'uint64_t':
return value + 'ULL';
case 'float':
if (value == Infinity || value == -Infinity) {
return `${(value < 0) ? '-' : ''}INFINITY`
}
// If the number looks like an integer, append ".0" to the end;
// otherwise adding an "f" suffix makes compilers complain.
value = value.toString();
if (value.match(/^-?[0-9]+$/)) {
value = value + ".0";
}
return value + 'f';
default:
if (value == Infinity || value == -Infinity) {
return `${(value < 0) ? '-' : ''}INFINITY`
}
return value;
}
}
function hasSpecificAttributes(options)
{
return this.count > kGlobalAttributes.length;
}
function asLowerCamelCase(label)
{
let str = string.toCamelCase(label, true);
// Check for the case when we're:
// 1. A single word (that's the regexp at the beginning, which matches the
// word-splitting regexp in string.toCamelCase).
// 2. Starting with multiple capital letters in a row.
// 3. But not _all_ capital letters (which we purposefully
// convert to all-lowercase).
//
// and if all those conditions hold, preserve the leading capital letters by
// uppercasing the first one, which got lowercased.
if (!/ |_|-|\//.test(label) && label.length > 1 && label.substring(0, 2).toUpperCase() == label.substring(0, 2)
&& label.toUpperCase() != label) {
str = str[0].toUpperCase() + str.substring(1);
}
return str.replace(/[^A-Za-z0-9_]/g, '');
}
function asUpperCamelCase(label)
{
let str = string.toCamelCase(label, false);
return str.replace(/[^A-Za-z0-9_]/g, '');
}
function chip_friendly_endpoint_type_name(options)
{
var name = this.endpointTypeName;
if (name.startsWith("MA-")) {
// prefix likely for "Matter" and is redundant
name = name.substring(3);
}
return asLowerCamelCase(name);
}
function asMEI(prefix, suffix)
{
return cHelper.asHex((prefix << 16) + suffix, 8);
}
// Not to be exported.
function nsValueToNamespace(ns)
{
if (ns == "detail") {
return ns;
}
return asUpperCamelCase(ns);
}
/*
* @brief
*
* This function converts a given ZAP type to a Cluster Object
* type used by the Matter SDK.
*
* Args:
*
* type: ZAP type specified in the XML
* isDecodable: Whether to emit an Encodable or Decodable cluster
* object type.
*
* These types can be found in src/app/data-model/.
*
*/
async function zapTypeToClusterObjectType(type, isDecodable, options)
{
// Use the entryType as a type
if (type == 'array' && this.entryType) {
type = this.entryType;
}
let passByReference = false;
async function fn(pkgId)
{
const ns = options.hash.ns ? ('chip::app::Clusters::' + nsValueToNamespace(options.hash.ns) + '::') : '';
const typeChecker = async (method) => zclHelper[method](this.global.db, type, pkgId).then(zclType => zclType != 'unknown');
const types = {
isEnum : await typeChecker('isEnum'),
isBitmap : await typeChecker('isBitmap'),
isEvent : await typeChecker('isEvent'),
isStruct : await typeChecker('isStruct'),
};
const typesCount = Object.values(types).filter(isType => isType).length;
if (typesCount > 1) {
let error = type + ' is ambiguous: \n';
Object.entries(types).forEach(([ key, value ]) => {
if (value) {
error += '\t' + key + ': ' + value + '\n';
}
});
throw error;
}
if (types.isEnum) {
// Catching baseline enums and converting them into 'uint[size]_t'
let s = type.toLowerCase().match(/^enum(\d+)$/);
if (s) {
return 'uint' + s[1] + '_t';
}
return ns + type;
}
if (types.isBitmap) {
// Catching baseline bitmaps and converting them into 'uint[size]_t'
let s = type.toLowerCase().match(/^bitmap(\d+)$/);
if (s) {
return 'uint' + s[1] + '_t';
}
return 'chip::BitMask<' + ns + type + '>';
}
if (types.isStruct) {
passByReference = true;
return ns + 'Structs::' + type + '::' + (isDecodable ? 'DecodableType' : 'Type');
}
if (types.isEvent) {
passByReference = true;
return ns + 'Events::' + type + '::' + (isDecodable ? 'DecodableType' : 'Type');
}
return zclHelper.asUnderlyingZclType.call({ global : this.global }, type, options);
}
let typeStr = await templateUtil.ensureZclPackageId(this).then(fn.bind(this));
if ((this.isArray || this.entryType) && !options.hash.forceNotList) {
passByReference = true;
// If we did not have a namespace provided, we can assume we're inside
// chip::app.
let listNamespace = options.hash.ns ? "chip::app::" : ""
if (isDecodable)
{
typeStr = `${listNamespace}DataModel::DecodableList<${typeStr}>`;
}
else
{
// Use const ${typeStr} so that consumers don't have to create non-const
// data to encode.
typeStr = `${listNamespace}DataModel::List<const ${typeStr}>`;
}
}
if (this.isNullable && !options.hash.forceNotNullable) {
passByReference = true;
// If we did not have a namespace provided, we can assume we're inside
// chip::app::.
let ns = options.hash.ns ? "chip::app::" : ""
typeStr = `${ns}DataModel::Nullable<${typeStr}>`;
}
if (this.isOptional && !options.hash.forceNotOptional) {
passByReference = true;
// If we did not have a namespace provided, we can assume we're inside
// chip::.
let ns = options.hash.ns ? "chip::" : ""
typeStr = `${ns}Optional<${typeStr}>`;
}
if (options.hash.isArgument && passByReference) {
typeStr = `const ${typeStr} &`;
}
return templateUtil.templatePromise(this.global, Promise.resolve(typeStr))
}
function zapTypeToEncodableClusterObjectType(type, options)
{
return zapTypeToClusterObjectType.call(this, type, false, options)
}
function zapTypeToDecodableClusterObjectType(type, options)
{
return zapTypeToClusterObjectType.call(this, type, true, options)
}
async function _zapTypeToPythonClusterObjectType(type, options)
{
async function fn(pkgId)
{
const ns = options.hash.ns;
const typeChecker = async (method) => zclHelper[method](this.global.db, type, pkgId).then(zclType => zclType != 'unknown');
if (await typeChecker('isEnum')) {
// Catching baseline enums and converting them into 'uint'
if (type.toLowerCase().match(/^enum\d+$/g)) {
return 'uint';
}
return ns + '.Enums.' + type;
}
if (await typeChecker('isBitmap')) {
return 'uint';
}
if (await typeChecker('isStruct')) {
return ns + '.Structs.' + type;
}
if (StringHelper.isCharString(type)) {
return 'str';
}
if (StringHelper.isOctetString(type)) {
return 'bytes';
}
if (type.toLowerCase() == 'single') {
return 'float32';
}
if (type.toLowerCase() == 'double') {
return 'float';
}
if (type.toLowerCase() == 'boolean') {
return 'bool'
}
// #10748: asUnderlyingZclType will emit wrong types for int{48|56|64}(u), so we process all int values here.
if (type.toLowerCase().match(/^int\d+$/)) {
return 'int'
}
if (type.toLowerCase().match(/^int\d+u$/)) {
return 'uint'
}
resolvedType = await zclHelper.asUnderlyingZclType.call({ global : this.global }, type, options);
{
basicType = ChipTypesHelper.asBasicType(resolvedType);
if (basicType.match(/^int\d+_t$/)) {
return 'int'
}
if (basicType.match(/^uint\d+_t$/)) {
return 'uint'
}
}
}
let promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this));
if ((this.isArray || this.entryType) && !options.hash.forceNotList) {
promise = promise.then(typeStr => `typing.List[${typeStr}]`);
}
const isNull = (this.isNullable && !options.hash.forceNotNullable);
const isOptional = (this.isOptional && !options.hash.forceNotOptional);
if (isNull && isOptional) {
promise = promise.then(typeStr => `typing.Union[None, Nullable, ${typeStr}]`);
} else if (isNull) {
promise = promise.then(typeStr => `typing.Union[Nullable, ${typeStr}]`);
} else if (isOptional) {
promise = promise.then(typeStr => `typing.Optional[${typeStr}]`);
}
return templateUtil.templatePromise(this.global, promise)
}
function zapTypeToPythonClusterObjectType(type, options)
{
return _zapTypeToPythonClusterObjectType.call(this, type, options)
}
async function _getPythonFieldDefault(type, options)
{
async function fn(pkgId)
{
const ns = options.hash.ns;
const typeChecker = async (method) => zclHelper[method](this.global.db, type, pkgId).then(zclType => zclType != 'unknown');
if (await typeChecker('isEnum')) {
return '0';
}
if (await typeChecker('isBitmap')) {
return '0';
}
if (await typeChecker('isStruct')) {
return 'field(default_factory=lambda: ' + ns + '.Structs.' + type + '())';
}
if (StringHelper.isCharString(type)) {
return '""';
}
if (StringHelper.isOctetString(type)) {
return 'b""';
}
if ([ 'single', 'double' ].includes(type.toLowerCase())) {
return '0.0';
}
if (type.toLowerCase() == 'boolean') {
return 'False'
}
// #10748: asUnderlyingZclType will emit wrong types for int{48|56|64}(u), so we process all int values here.
if (type.toLowerCase().match(/^int\d+$/)) {
return '0'
}
if (type.toLowerCase().match(/^int\d+u$/)) {
return '0'
}
resolvedType = await zclHelper.asUnderlyingZclType.call({ global : this.global }, type, options);
{
basicType = ChipTypesHelper.asBasicType(resolvedType);
if (basicType.match(/^int\d+_t$/)) {
return '0'
}
if (basicType.match(/^uint\d+_t$/)) {
return '0'
}
}
}
let promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this));
if ((this.isArray || this.entryType) && !options.hash.forceNotList) {
promise = promise.then(typeStr => `field(default_factory=lambda: [])`);
}
const isNull = (this.isNullable && !options.hash.forceNotNullable);
const isOptional = (this.isOptional && !options.hash.forceNotOptional);
if (isNull && isOptional) {
promise = promise.then(typeStr => `None`);
} else if (isNull) {
promise = promise.then(typeStr => `NullValue`);
} else if (isOptional) {
promise = promise.then(typeStr => `None`);
}
return templateUtil.templatePromise(this.global, promise)
}
function getPythonFieldDefault(type, options)
{
return _getPythonFieldDefault.call(this, type, options)
}
// Allow-list of enums that we generate as enums, not enum classes. The goal is
// to drive this down to 0.
function isWeaklyTypedEnum(label)
{
return [
"AttributeWritePermission",
"BarrierControlBarrierPosition",
"BarrierControlMovingState",
"ColorControlOptions",
"ColorLoopAction",
"ColorLoopDirection",
"ColorMode",
"ContentLaunchStatus",
"ContentLaunchStreamingType",
"EnhancedColorMode",
"HardwareFaultType",
"HueDirection",
"HueMoveMode",
"HueStepMode",
"IdentifyEffectIdentifier",
"IdentifyEffectVariant",
"IdentifyIdentifyType",
"InterfaceType",
"KeypadLockout",
"LevelControlOptions",
"MoveMode",
"NetworkFaultType",
"OnOffDelayedAllOffEffectVariant",
"OnOffDyingLightEffectVariant",
"OnOffEffectIdentifier",
"PHYRateType",
"RadioFaultType",
"RoutingRole",
"SaturationMoveMode",
"SaturationStepMode",
"SecurityType",
"SetpointAdjustMode",
"StartUpOnOffValue",
"StatusCode",
"StepMode",
"TemperatureDisplayMode",
"WiFiVersionType",
].includes(label);
}
function incrementDepth(depth)
{
return depth + 1;
}
function hasProperty(obj, prop)
{
return prop in obj;
}
async function zcl_events_fields_by_event_name(name, options)
{
const { db, sessionId } = this.global;
const packageId = await templateUtil.ensureZclPackageId(this)
const promise = queryEvents.selectAllEvents(db, packageId)
.then(events => events.find(event => event.name == name))
.then(evt => queryEvents.selectEventFieldsByEventId(db, evt.id))
.then(fields => fields.map(field => {
field.label = field.name;
return field;
}))
.then(fields => templateUtil.collectBlocks(fields, options, this))
return templateUtil.templatePromise(this.global, promise)
}
// Must be used inside zcl_clusters
async function zcl_commands_that_need_timed_invoke(options)
{
const { db } = this.global;
let packageId = await templateUtil.ensureZclPackageId(this);
let commands = await queryCommand.selectCommandsByClusterId(db, this.id, packageId);
commands = commands.filter(cmd => cmd.mustUseTimedInvoke);
return templateUtil.collectBlocks(commands, options, this);
}
// Allows conditioning generation on whether the given type is a fabric-scoped
// struct.
async function if_is_fabric_scoped_struct(type, options)
{
let packageId = await templateUtil.ensureZclPackageId(this);
let st = await zclQuery.selectStructByName(this.global.db, type, packageId);
if (st) {
// TODO: Should know whether a struct is fabric-scoped without sniffing its
// members.
let fields = await zclQuery.selectAllStructItemsById(this.global.db, st.id);
if (fields.find((i) => i.type.toLowerCase() == "fabric_idx")) {
return options.fn(this);
}
}
return options.inverse(this);
}
// check if a value is numerically 0 for the purpose of default value
// interpretation. Note that this does NOT check for data type, so assumes
// a string of 'false' is 0 because boolean false is 0.
function isNonZeroValue(value)
{
if (!value) {
return false;
}
if (value === '0') {
return false;
}
// Hex value usage is inconsistent in XML. It looks we have
// all of 0x0, 0x00, 0x0000 so support all here.
if (value.match(/^0x0+$/)) {
return false;
}
// boolean 0 is false. We do not do a type check here
// so if anyone defaults a string to 'false' this will be wrong.
if (value === 'false') {
return false;
}
return true;
}
// Check if the default value is non-zero
// Generally does string checks for empty strings, numeric or hex zeroes or
// boolean values.
async function if_is_non_zero_default(value, options)
{
if (isNonZeroValue(value)) {
return options.fn(this);
} else {
return options.inverse(this);
}
}
//
// Module exports
//
exports.chip_endpoint_generated_functions = chip_endpoint_generated_functions
exports.chip_endpoint_cluster_list = chip_endpoint_cluster_list
exports.chip_endpoint_data_version_count = chip_endpoint_data_version_count;
exports.chip_endpoint_generated_commands_list = chip_endpoint_generated_commands_list
exports.asTypedExpression = asTypedExpression;
exports.asTypedLiteral = asTypedLiteral;
exports.asLowerCamelCase = asLowerCamelCase;
exports.asUpperCamelCase = asUpperCamelCase;
exports.chip_friendly_endpoint_type_name = chip_friendly_endpoint_type_name;
exports.hasProperty = hasProperty;
exports.hasSpecificAttributes = hasSpecificAttributes;
exports.asMEI = asMEI;
exports.zapTypeToEncodableClusterObjectType = zapTypeToEncodableClusterObjectType;
exports.zapTypeToDecodableClusterObjectType = zapTypeToDecodableClusterObjectType;
exports.zapTypeToPythonClusterObjectType = zapTypeToPythonClusterObjectType;
exports.isWeaklyTypedEnum = isWeaklyTypedEnum;
exports.getPythonFieldDefault = getPythonFieldDefault;
exports.incrementDepth = incrementDepth;
exports.zcl_events_fields_by_event_name = zcl_events_fields_by_event_name;
exports.zcl_commands_that_need_timed_invoke = zcl_commands_that_need_timed_invoke;
exports.if_is_fabric_scoped_struct = if_is_fabric_scoped_struct;
exports.if_is_non_zero_default = if_is_non_zero_default;