3 * Copyright (c) 2020-2021 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 // Import helpers from zap core
19 const zapPath = '../../../../../third_party/zap/repo/src-electron/';
20 const queryConfig = require(zapPath + 'db/query-config.js')
21 const queryImpexp = require(zapPath + 'db/query-impexp.js')
22 const templateUtil = require(zapPath + 'generator/template-util.js')
23 const zclHelper = require(zapPath + 'generator/helper-zcl.js')
24 const zclQuery = require(zapPath + 'db/query-zcl.js')
26 const StringHelper = require('../../common/StringHelper.js');
27 const ChipTypesHelper = require('../../common/ChipTypesHelper.js');
30 * This method converts a ZCL type to the length expected for the
31 * BufferWriter.Put method.
33 * Not all types are supported at the moment, so if there is any unsupported type
34 * that we are trying to convert, it will throw an error.
36 function asPutLength(zclType)
38 const type = ChipTypesHelper.asBasicType(zclType);
48 return type.replace(/[^0-9]/g, '');
50 throw error = 'asPutLength: Unhandled type: ' + zclType;
54 function asPutCastType(zclType)
56 const type = ChipTypesHelper.asBasicType(zclType);
69 throw error = 'asPutCastType: Unhandled type: ' + zclType;
73 function getEnabledClustersForSide(clusters, side)
75 clusters = clusters.filter(cluster => cluster.enabled == 1);
81 return clusters.filter(cluster => cluster.side == side);
84 function getClusters(options, side)
86 const db = this.global.db;
87 const sessionId = this.global.sessionId;
89 return queryImpexp.exportendPointTypeIds(db, sessionId)
90 .then(endpointTypes => zclQuery.exportAllClustersDetailsFromEndpointTypes(db, endpointTypes))
91 .then(clusters => getEnabledClustersForSide(clusters, side));
94 function getClustersAsBlocks(options, side)
98 return getClusters.call(this, options, side).then(clusters => templateUtil.collectBlocks(clusters, options, this))
101 return templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
105 * Creates block iterator over the enabled server side clusters
109 function chip_server_clusters(options)
111 return getClustersAsBlocks.call(this, options, 'server');
115 * Check if there is any enabled server clusters
118 function chip_has_server_clusters(options)
120 let promise = getClusters.call(this, options, 'server').then(clusters => !!clusters.length);
121 return templateUtil.templatePromise(this.global, promise);
125 * Creates block iterator over client side enabled clusters
129 function chip_client_clusters(options)
131 return getClustersAsBlocks.call(this, options, 'client');
135 * Check if there is any enabled client clusters
138 function chip_has_client_clusters(options)
140 let promise = getClusters.call(this, options, 'client').then(clusters => !!clusters.length);
141 return templateUtil.templatePromise(this.global, promise);
145 * Creates block iterator over enabled clusters
149 function chip_clusters(options)
151 return getClustersAsBlocks.call(this, options, 'all');
155 * Check if there is any enabled clusters
158 function chip_has_clusters(options)
160 let promise = getClusters.call(this, options, 'client').then(clusters => !!clusters.length);
161 return templateUtil.templatePromise(this.global, promise);
165 * Creates block iterator over the server side cluster command
166 * for a given cluster.
168 * This function is meant to be used inside a {{#chip_server_clusters}}
169 * block. It will throws otherwise.
173 function chip_server_cluster_commands(options)
175 // Retrieve the clusterName and the clusterSide. If any if not available, an error will be thrown.
176 const clusterName = this.name;
177 const clusterSide = this.side;
178 if (clusterName == undefined || clusterSide == undefined) {
179 const error = 'chip_server_cluster_commands: Could not find relevant parent cluster.';
184 function filterCommand(cmd)
186 return cmd.clusterName == clusterName && cmd.clusterSide == 'client' && cmd.name.includes('Response') == false;
189 const db = this.global.db;
190 return queryImpexp.exportendPointTypeIds(db, this.global.sessionId)
191 .then(endpointTypes => zclQuery.exportClustersAndEndpointDetailsFromEndpointTypes(db, endpointTypes))
192 .then(endpointsAndClusters => zclQuery.exportCommandDetailsFromAllEndpointTypesAndClusters(db, endpointsAndClusters))
193 .then(endpointCommands => endpointCommands.filter(filterCommand))
194 .then(endpointCommands => templateUtil.collectBlocks(endpointCommands, options, this))
198 * Creates block iterator over the server side cluster command arguments
199 * for a given command.
201 * This function is meant to be used inside a {{#chip_server_cluster_commands}}
202 * block. It will throws otherwise.
206 function chip_server_cluster_command_arguments(options)
208 const db = this.global.db;
210 function collectItem(arg, pkgId)
212 return zclHelper.isStruct(db, arg.type, pkgId).then(result => {
213 if (result == 'unknown') {
217 return zclQuery.selectStructByName(db, arg.type, pkgId).then(rec => {
218 return zclQuery.selectAllStructItemsById(db, rec.id).then(items => items.map(item => {
219 item.name = item.label;
226 function collectItems(args, pkgId)
228 return Promise.all(args.map(arg => collectItem.call(this, arg, pkgId))).then(items => items.flat()).then(items => {
229 return Promise.all(items.map(item => {
230 if (StringHelper.isString(item.type)) {
231 item.chipType = 'chip::ByteSpan';
235 return zclHelper.asUnderlyingZclType.call(this, item.type, options).then(zclType => {
236 // Enhanced the command argument with 'chipType', 'chipTypePutLength', 'chipTypePutCastType' for conveniences.
237 item.chipType = zclType;
238 item.chipTypePutLength = asPutLength(zclType);
239 item.chipTypePutCastType = asPutCastType(zclType);
248 return zclQuery.selectCommandArgumentsByCommandId(db, this.id, pkgId)
249 .then(args => collectItems.call(this, args, pkgId))
250 .then(items => templateUtil.collectBlocks(items, options, this));
253 const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
254 return templateUtil.templatePromise(this.global, promise)
258 * Returns if a given command argument chip type is signed.
260 * This function is meant to be used inside a {{#chip_*}} block.
261 * It will throws otherwise.
265 function isSignedType()
267 const type = this.chipType;
269 const error = 'isSignedType: Could not find chipType';
286 * Returns if a given command argument chip type is discrete.
288 * This function is meant to be used inside a {{#chip_*}} block.
289 * It will throws otherwise.
293 function isDiscreteType()
295 const type = this.chipType;
297 const error = 'isDiscreteType: Could not find chipType';
302 return this.discrete;
305 function getAttributes(pkgId, options)
307 const db = this.global.db;
308 return queryConfig.getAllSessionAttributes(db, this.global.sessionId).then(atts => {
309 return Promise.all(atts.map(att => zclQuery.selectAtomicByName(db, att.type, pkgId).then(atomic => {
310 // Enhanced the attribute with 'atomidId', 'discrete', chipType properties for convenience.
311 att.atomicTypeId = atomic.atomicId;
312 att.discrete = atomic.discrete;
314 if (StringHelper.isString(att.type)) {
315 // Enhanced the command argument with 'chipType' for conveniences.
316 att.chipType = 'chip::ByteSpan';
320 return zclHelper.asUnderlyingZclType.call(this, att.type, options).then(zclType => {
321 att.chipType = zclType;
329 * Creates block iterator over the server side cluster attributes
330 * for a given cluster.
332 * This function is meant to be used inside a {{#chip_server_clusters}}
333 * block. It will throws otherwise.
337 function chip_server_cluster_attributes(options)
339 // Retrieve the clusterCode and the clusterSide. If any if not available, an error will be thrown.
340 const clusterCode = this.code;
341 const clusterSide = this.side;
342 if (clusterCode == undefined || clusterSide == undefined) {
343 const error = 'chip_server_cluster_attributes: Could not find relevant parent cluster.';
350 return getAttributes.call(this, pkgId, options).then(atts => {
351 atts = atts.filter(att => att.clusterCode == clusterCode && att.side == 'server');
352 atts.forEach(att => {
353 const sameAttributes = atts.filter(att2 => att.name == att2.name);
354 let isWritable = !!sameAttributes.find(att2 => att2.writable);
355 let isReportable = !!sameAttributes.find(att2 => att2.reportable.included);
356 if (isWritable || isReportable) {
357 if (!StringHelper.isString(att.type)) {
358 att.chipTypePutLength = asPutLength(att.chipType);
359 att.chipTypePutCastType = asPutCastType(att.chipType);
361 att.writable = isWritable;
362 att.reportable.included = isReportable;
365 atts = atts.filter((att, index) => atts.findIndex(att2 => att.name == att2.name) == index);
366 return templateUtil.collectBlocks(atts, options, this);
370 const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
371 return templateUtil.templatePromise(this.global, promise);
375 * Returns if a given attribute is writable.
377 * This function is meant to be used inside a {{#chip_server_cluster_attributes}} block.
378 * It will throws otherwise.
382 function isWritableAttribute(options)
384 if (this.attributeCode == undefined) {
385 const error = 'isWritableAttribute: missing attribute code.';
390 return this.writable == 1;
394 * Returns if a given attribute is reportable.
396 * This function is meant to be used inside a {{#chip_server_cluster_attributes}} block.
397 * It will throws otherwise.
401 function isReportableAttribute(options)
403 if (this.attributeCode == undefined) {
404 const error = 'isReportableAttribute: missing attribute code.';
409 return this.reportable.included == 1;
413 * Returns if a given command is manufacturer specific
415 * This function is meant to be used inside a {{#chip_server_cluster_commands}} block.
416 * It will throws otherwise.
420 function isManufacturerSpecificCommand()
422 if (this.commandSource == undefined) {
423 const error = 'isManufacturerSpecificCommand: Not inside a ({#chip_server_cluster_commands}} block.';
428 return !!this.mfgCode;
431 function asPythonType(zclType)
433 const type = ChipTypesHelper.asBasicType(zclType);
447 case 'chip::ByteSpan':
452 function asPythonCType(zclType)
454 const type = ChipTypesHelper.asBasicType(zclType);
464 return 'c_' + type.replace('_t', '');
471 function hasSpecificResponse(commandName)
473 // Retrieve the clusterName and the clusterSide. If any if not available, an error will be thrown.
474 const clusterName = this.parent.name;
475 const clusterSide = this.parent.side;
476 if (clusterName == undefined || clusterSide == undefined) {
477 const error = 'chip_server_cluster_commands: Could not find relevant parent cluster.';
482 function filterCommand(cmd)
484 return cmd.clusterName == clusterName && cmd.name == (commandName + "Response");
489 const db = this.global.db;
490 return queryImpexp.exportendPointTypeIds(db, this.global.sessionId)
491 .then(endpointTypes => zclQuery.exportClustersAndEndpointDetailsFromEndpointTypes(db, endpointTypes))
492 .then(endpointsAndClusters => zclQuery.exportCommandDetailsFromAllEndpointTypesAndClusters(db, endpointsAndClusters))
493 .then(endpointCommands => endpointCommands.filter(filterCommand).length)
496 const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
497 return templateUtil.templatePromise(this.global, promise);
500 function asCallbackAttributeType(attributeType)
502 switch (parseInt(attributeType)) {
503 case 0x00: // nodata / No data
504 case 0x0A: // data24 / 24-bit data
505 case 0x0C: // data40 / 40-bit data
506 case 0x0D: // data48 / 48-bit data
507 case 0x0E: // data56 / 56-bit data
508 case 0x1A: // map24 / 24-bit bitmap
509 case 0x1C: // map40 / 40-bit bitmap
510 case 0x1D: // map48 / 48-bit bitmap
511 case 0x1E: // map56 / 56-bit bitmap
512 case 0x22: // uint24 / Unsigned 24-bit integer
513 case 0x24: // uint40 / Unsigned 40-bit integer
514 case 0x25: // uint48 / Unsigned 48-bit integer
515 case 0x26: // uint56 / Unsigned 56-bit integer
516 case 0x2A: // int24 / Signed 24-bit integer
517 case 0x2C: // int40 / Signed 40-bit integer
518 case 0x2D: // int48 / Signed 48-bit integer
519 case 0x2E: // int56 / Signed 56-bit integer
520 case 0x38: // semi / Semi-precision
521 case 0x39: // single / Single precision
522 case 0x3A: // double / Double precision
523 case 0x48: // array / Array
524 case 0x49: // struct / Structure
525 case 0x50: // set / Set
526 case 0x51: // bag / Bag
527 case 0xE0: // ToD / Time of day
528 case 0xEA: // bacOID / BACnet OID
529 case 0xF1: // key128 / 128-bit security key
530 case 0xFF: // unk / Unknown
531 return 'Unsupported';
532 case 0x41: // octstr / Octet string
533 case 0x42: // string / Character string
534 case 0x43: // octstr16 / Long octet string
535 case 0x44: // string16 / Long character string
537 case 0x08: // data8 / 8-bit data
538 case 0x18: // map8 / 8-bit bitmap
539 case 0x20: // uint8 / Unsigned 8-bit integer
540 case 0x30: // enum8 / 8-bit enumeration
542 case 0x09: // data16 / 16-bit data
543 case 0x19: // map16 / 16-bit bitmap
544 case 0x21: // uint16 / Unsigned 16-bit integer
545 case 0x31: // enum16 / 16-bit enumeration
546 case 0xE8: // clusterId / Cluster ID
547 case 0xE9: // attribId / Attribute ID
549 case 0x0B: // data32 / 32-bit data
550 case 0x1B: // map32 / 32-bit bitmap
551 case 0x23: // uint32 / Unsigned 32-bit integer
552 case 0xE1: // date / Date
553 case 0xE2: // UTC / UTCTime
555 case 0x0F: // data64 / 64-bit data
556 case 0x1F: // map64 / 64-bit bitmap
557 case 0x27: // uint64 / Unsigned 64-bit integer
558 case 0xF0: // EUI64 / IEEE address
560 case 0x10: // bool / Boolean
562 case 0x28: // int8 / Signed 8-bit integer
564 case 0x29: // int16 / Signed 16-bit integer
566 case 0x2B: // int32 / Signed 32-bit integer
568 case 0x2F: // int64 / Signed 64-bit integer
571 error = 'Unhandled attribute type ' + attributeType;
576 function asObjectiveCBasicType(type)
578 if (StringHelper.isOctetString(type)) {
580 } else if (StringHelper.isCharString(type)) {
583 return ChipTypesHelper.asBasicType(this.chipType);
587 function asObjectiveCNumberType(label, type)
591 const options = { 'hash' : {} };
592 return zclHelper.asUnderlyingZclType.call(this, type, options).then(zclType => {
595 return 'UnsignedChar';
597 return 'UnsignedShort';
599 return 'UnsignedLong';
601 return 'UnsignedLongLong';
611 error = label + ': Unhandled underlying type ' + zclType + ' for original type ' + type;
617 const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err));
618 return templateUtil.templatePromise(this.global, promise)
621 function isStrEndsWith(str, substr)
623 return str.endsWith(substr);
629 exports.chip_clusters = chip_clusters;
630 exports.chip_has_clusters = chip_has_clusters;
631 exports.chip_client_clusters = chip_client_clusters;
632 exports.chip_has_client_clusters = chip_has_client_clusters;
633 exports.chip_server_clusters = chip_server_clusters;
634 exports.chip_has_server_clusters = chip_has_server_clusters;
635 exports.chip_server_cluster_commands = chip_server_cluster_commands;
636 exports.chip_server_cluster_command_arguments = chip_server_cluster_command_arguments
637 exports.asBasicType = ChipTypesHelper.asBasicType;
638 exports.asObjectiveCBasicType = asObjectiveCBasicType;
639 exports.asObjectiveCNumberType = asObjectiveCNumberType;
640 exports.isSignedType = isSignedType;
641 exports.isDiscreteType = isDiscreteType;
642 exports.chip_server_cluster_attributes = chip_server_cluster_attributes;
643 exports.isWritableAttribute = isWritableAttribute;
644 exports.isReportableAttribute = isReportableAttribute;
645 exports.isManufacturerSpecificCommand = isManufacturerSpecificCommand;
646 exports.asPythonType = asPythonType;
647 exports.asPythonCType = asPythonCType;
648 exports.asCallbackAttributeType = asCallbackAttributeType;
649 exports.hasSpecificResponse = hasSpecificResponse;
650 exports.isStrEndsWith = isStrEndsWith;