blob: a613bc11ca8c2ac3935c8339749fd8fec2c510a1 [file] [log] [blame]
/**
*
* Copyright (c) 2020 Silicon Labs
*
* 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.
*/
/**
* This module contains the API for templating. For more detailed instructions, read {@tutorial template-tutorial}
*
* @module Templating API: static zcl helpers
*/
const queryZcl = require('../db/query-zcl')
const queryDeviceType = require('../db/query-device-type')
const queryCommand = require('../db/query-command')
const queryEvent = require('../db/query-event')
const queryPackage = require('../db/query-package')
const dbEnum = require('../../src-shared/db-enum')
const templateUtil = require('./template-util')
const helperC = require('./helper-c')
const env = require('../util/env')
const types = require('../util/types')
const zclUtil = require('../util/zcl-util')
const upgrade = require('../sdk/matter.js')
const _ = require('lodash')
const characterStringTypes = ['CHAR_STRING', 'LONG_CHAR_STRING']
const octetStringTypes = ['OCTET_STRING', 'LONG_OCTET_STRING']
const stringShortTypes = ['CHAR_STRING', 'OCTET_STRING']
const stringLongTypes = ['LONG_CHAR_STRING', 'LONG_OCTET_STRING']
/**
* Block helper iterating over all bitmaps.
*
* @param {*} options
* @returns Promise of content.
*/
async function zcl_bitmaps(options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let ens
if (this.id != null) {
ens = await Promise.all(
packageIds.map((packageId) =>
queryZcl.selectClusterBitmaps(this.global.db, packageId, this.id)
)
).then((x) => x.flat())
} else {
ens = await Promise.all(
packageIds.map((packageId) =>
queryZcl
.selectAllBitmaps(this.global.db, packageId)
// Filtering out all atomic bitmaps
.then((bs) => bs.filter((b) => !/^bitmap\d+$/i.test(b.name)))
)
).then((x) => x.flat())
}
ens.forEach((en) => {
en.has_no_clusters = en.bitmapClusterCount < 1
en.has_one_cluster = en.bitmapClusterCount == 1
en.has_more_than_one_cluster =
en.bitmapClusterCount > 1 && en.name != 'Feature' // The various flavors of Feature bitmaps get reused everywhere, but can't be shared
})
let promise = templateUtil.collectBlocks(ens, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Iterates over enum items. Valid only inside zcl_enums.
* @param {*} options
*/
function zcl_bitmap_items(options) {
let promise = queryZcl
.selectAllBitmapFieldsById(this.global.db, this.id)
.then((items) => templateUtil.collectBlocks(items, options, this))
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all enums.
* If existing independently, it iterates over ALL the enums.
* Within a context of a cluster, it iterates only over the
* enums belonging to a cluster.
*
* @param {*} options
* @returns Promise of content.
*/
async function zcl_enums(options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let ens
if (this.id != null) {
ens = await queryZcl.selectClusterEnums(this.global.db, packageIds, this.id)
} else {
ens = await Promise.all(
packageIds.map((packageId) =>
queryZcl
.selectAllEnums(this.global.db, packageId)
//Filtering out all atomic enums
.then((es) => es.filter((e) => !/^enum\d+$/i.test(e.name)))
)
).then((x) => x.flat())
}
ens.forEach((en) => {
en.has_no_clusters = en.enumClusterCount < 1
en.has_one_cluster = en.enumClusterCount == 1
en.has_more_than_one_cluster = en.enumClusterCount > 1
})
let promise = templateUtil.collectBlocks(ens, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all structs.
* If existing independently, it iterates over ALL the structs.
* Within a context of a cluster, it iterates only over the
* structs belonging to a cluster.
*
* @param {*} options
* @returns Promise of content.
*/
async function zcl_structs(options) {
let checkForDoubleNestedArray =
options.hash.checkForDoubleNestedArray == 'true'
let packageIds = await templateUtil.ensureZclPackageIds(this)
let structs
if (this.id != null) {
structs = await queryZcl.selectClusterStructsWithItems(
this.global.db,
packageIds,
this.id
)
} else {
structs = await queryZcl.selectAllStructsWithItems(
this.global.db,
packageIds
)
}
structs = await zclUtil.sortStructsByDependency(structs)
for (const st of structs) {
st.struct_contains_array = false
st.struct_has_fabric_sensitive_fields = false
st.has_no_clusters = st.struct_cluster_count < 1
st.has_one_cluster = st.struct_cluster_count == 1
st.has_more_than_one_cluster = st.struct_cluster_count > 1
const checkForArraysTransitively = async (items) => {
let promises = []
for (const i of items) {
if (i.isArray) {
st.struct_contains_array = true
}
if (!st.struct_contains_array) {
promises.push(
queryZcl
.selectAllStructItemsById(this.global.db, i.dataTypeReference)
.then(checkForArraysTransitively)
)
}
}
if (promises.length != 0) {
await Promise.all(promises)
}
}
for (const i of st.items) {
if (i.isFabricSensitive) {
st.struct_has_fabric_sensitive_fields = true
}
}
await checkForArraysTransitively(st.items)
}
if (checkForDoubleNestedArray) {
// If this is set to true in a template, then we populate the
// struct_contains_nested_array variable with true
// if struct contains array of structs that contain an array.
// There is a hit in processing if you turn this on, so it's not automatic.
for (const st of structs) {
st.struct_contains_nested_array = false
for (const i of st.items) {
if (i.isArray) {
// Found an array. Now let's check if it points to a struct that also contains an array.
let sis = await queryZcl.selectAllStructItemsById(
this.global.db,
i.dataTypeReference
)
if (sis.length > 0) {
for (const ss of sis) {
if (ss.isArray) {
st.struct_contains_nested_array = true
}
}
}
}
}
}
}
let promise = templateUtil.collectBlocks(structs, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Iterates over enum items. Valid only inside zcl_enums.
* @param {*} options
*/
async function zcl_enum_items(options) {
let items
if (this.enum_items) {
items = this.enum_items
} else {
items = await queryZcl.selectAllEnumItemsById(this.global.db, this.id)
this.enum_items = items
}
return templateUtil.templatePromise(
this.global,
templateUtil.collectBlocks(items, options, this)
)
}
/**
* This helper prints out the first unused enum value.
* It supports mode="next_larger" and
* mode="first_unused" (which is the default).
*
* @param {*} options
* @returns the unused enum value
*/
async function first_unused_enum_value(options) {
let mode = options.hash.mode
let format = options.hash.format // "hex" or "decimal"
let items
if (this.enum_items) {
items = this.enum_items
} else {
items = await queryZcl.selectAllEnumItemsById(this.global.db, this.id)
this.enum_items = items
}
let unusedValue
if (mode == 'next_larger') {
// Find the lowest unused integer, larger than all enum values
unusedValue = 0
for (let enumItem of items) {
if (enumItem.value > unusedValue) {
unusedValue = enumItem.value
}
}
unusedValue += 1
} else {
unusedValue = -1
let isPresent
do {
unusedValue++
isPresent = false
for (let enumItem of items) {
if (enumItem.value == unusedValue) {
isPresent = true
}
}
} while (isPresent)
}
if (format == 'hex') {
let out = unusedValue.toString(16)
if (this.size && this.size > 0) {
while (out.length < this.size * 2) {
out = '0' + out
}
}
return out
} else {
return unusedValue
}
}
/**
* Block helper iterating over all struct items. Valid only inside zcl_structs.
* @param {*} options
* @returns Promise of content.
*/
async function zcl_struct_items(options) {
let checkForDoubleNestedArray =
options.hash.checkForDoubleNestedArray == 'true'
let packageIds = await templateUtil.ensureZclPackageIds(this)
let sis = await queryZcl.selectAllStructItemsById(this.global.db, this.id)
if (checkForDoubleNestedArray) {
for (const si of sis) {
si.struct_item_contains_nested_array = false
// For each item, let's check if it's a struct itself
let structItems = await queryZcl.selectAllStructItemsByStructName(
this.global.db,
si.type,
packageIds
)
if (structItems.length > 0) {
for (const s of structItems) {
if (s.isArray) si.struct_item_contains_nested_array = true
}
}
}
}
let promise = templateUtil.collectBlocks(sis, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all struct items given the struct name.
*
* @param name
* @param options
* @returns Promise of content.
*/
async function zcl_struct_items_by_struct_name(name, options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let promise = queryZcl
.selectAllStructItemsByStructName(this.global.db, name, packageIds)
.then((st) => templateUtil.collectBlocks(st, options, this))
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all struct items given the struct name and
* cluster name. The items iterated will be those that correspond to that
* struct name being used within the given cluster. That means the struct name
* must be either a global struct (in which case the cluster name is just
* ignored), or a struct associated with the given cluster.
*
* @param name
* @param clusterName
* @param options
* @returns Promise of content.
*/
async function zcl_struct_items_by_struct_and_cluster_name(
name,
clusterName,
options
) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
// Check for a global struct first.
const structObj = await queryZcl.selectStructByName(
this.global.db,
name,
packageIds
)
if (structObj.structClusterCount == 0 && structObj.structCount == 1) {
// Just ignore the cluster name.
return zcl_struct_items_by_struct_name.call(this, name, options)
}
let promise = queryZcl
.selectAllStructItemsByStructName(
this.global.db,
name,
packageIds,
clusterName
)
.then((st) => templateUtil.collectBlocks(st, options, this))
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all deviceTypes.
*
* @param {*} options
* @returns Promise of content.
*/
async function zcl_device_types(options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let deviceTypes = await Promise.all(
packageIds.map((packageId) =>
queryDeviceType.selectAllDeviceTypes(this.global.db, packageId)
)
)
let promise = templateUtil.collectBlocks(deviceTypes.flat(), options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper for use inside zcl_device_types
*
* @param {*} options
* @returns blocks for clusters
*/
async function zcl_device_type_clusters(options) {
let clusters = await queryDeviceType.selectDeviceTypeClustersByDeviceTypeRef(
this.global.db,
this.id
)
let promise = templateUtil.collectBlocks(clusters, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper for use inside zcl_device_type_clusters
*
* @param {*} options
* @returns blocks for commands
*/
async function zcl_device_type_cluster_commands(options) {
let commands = await queryDeviceType.selectDeviceTypeCommandsByDeviceTypeRef(
this.global.db,
this.deviceTypeRef
)
commands = commands.filter((x) => x.deviceTypeClusterRef == this.id)
let promise = templateUtil.collectBlocks(commands, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper for use inside zcl_device_type_clusters
*
* @param {*} options
* @returns blocks for attributes
*/
async function zcl_device_type_cluster_attributes(options) {
let attributes =
await queryDeviceType.selectDeviceTypeAttributesByDeviceTypeRef(
this.global.db,
this.deviceTypeRef
)
attributes = attributes.filter((x) => x.deviceTypeClusterRef == this.id)
let promise = templateUtil.collectBlocks(attributes, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all clusters.
*
* @param {*} options
* @returns Promise of content.
*/
async function zcl_clusters(options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let cl = await Promise.all(
packageIds.map((id) => queryZcl.selectAllClusters(this.global.db, id))
)
let promise = templateUtil.collectBlocks(cl.flat(), options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all commands.
* There are two modes of this helper:
* when used in a global context, it iterates over ALL commands in the database.
* when used inside a `zcl_cluster` block helper, it iterates only over the commands for that cluster.
*
* @param {*} options
* @returns Promise of content.
*/
function zcl_commands(options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) => {
if ('id' in this) {
// We're functioning inside a nested context with an id, so we will only query for this cluster.
return queryCommand.selectCommandsByClusterId(
this.global.db,
this.id,
packageIds
)
} else {
return queryCommand.selectAllCommands(this.global.db, packageIds)
}
})
.then((cmds) => templateUtil.collectBlocks(cmds, options, this))
return templateUtil.templatePromise(this.global, promise)
}
/**
* Returns all commands which are command responses.
* For eg, If the xml has the following:
* <command source="client" code="0x00" name="newCmd" response="newCmdResponse">
* then newCmdResponse will be included in the list of commands returned here.
*
* There are two modes of this helper:
* - when used in a global context, it iterates over ALL command responses in
* the database.
* - when used inside a `zcl_cluster` block helper, it iterates only over the
* commands responses for that cluster.
* @param {*} options
* @returns all command responses
*/
async function zcl_command_responses(options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let cmds = null
if ('id' in this) {
cmds = await queryCommand.selectCommandsByClusterId(
this.global.db,
this.id,
packageIds
)
} else {
cmds = await queryCommand.selectAllCommands(this.global.db, packageIds)
}
let commandResponses = cmds.map((cmd) => cmd.responseName)
let res = cmds.filter((cmd) => commandResponses.includes(cmd.name))
let promise = templateUtil.collectBlocks(res, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all commands with cluster information.
* Note: Similar to zcl_commands but has cluster information as well.
* @param {*} options
* @returns Promise of content.
*/
async function zcl_commands_with_cluster_info(options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let cmds = await queryCommand.selectAllCommandsWithClusterInfo(
this.global.db,
packageIds
)
let promise = templateUtil.collectBlocks(cmds, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Helper that retrieves all commands that contain arguments.
*
* @param {*} options
*/
async function zcl_commands_with_arguments(options) {
let sortBy = options.hash.sortBy
let packageIds = await templateUtil.ensureZclPackageIds(this)
let packages = await queryPackage.getPackagesByPackageIds(
this.global.db,
packageIds
)
let cmds = await Promise.all(
packages.map(async (pkg) => {
let cmdsPerPackageId = await queryCommand.selectAllCommandsWithArguments(
this.global.db,
pkg.id
)
if ('signature' == sortBy) {
for (const cmd of cmdsPerPackageId) {
let sig = await zclUtil.createCommandSignature(
this.global.db,
pkg.type === dbEnum.packageType.zclXmlStandalone
? packageIds // If it's a custom xml, we need to pass all packages to have access to atomic types
: pkg.id,
cmd
)
cmd.signature = sig.signature
cmd.isSignatureSimple = sig.isSimple
}
cmdsPerPackageId.sort((a, b) => {
if (a.isSignatureSimple && !b.isSignatureSimple) return -1
if (!a.isSignatureSimple && b.isSignatureSimple) return 1
return a.signature.localeCompare(b.signature)
})
}
return cmdsPerPackageId
})
).then((x) => x.flat())
let promise = templateUtil.collectBlocks(cmds, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all commands based on the source.
* There are two modes of this helper:
* when used in a global context, it iterates over ALL commands in the database based on the source.
* when used inside a `zcl_cluster` block helper, it iterates only over the source commands for that cluster.
*
* @param {*} options
* @param {*} source
* @returns Promise of content.
* @ignore
*/
function zcl_commands_by_source(options, source) {
let promise = templateUtil
.ensureZclPackageIds(this) // leaving to packageId since not used in template.
.then((packageIds) => {
if ('id' in this) {
// We're functioning inside a nested context with an id, so we will only query for this cluster.
return queryCommand.selectCommandsByClusterIdAndSource(
this.global.db,
this.id,
source,
packageIds
)
} else {
return queryCommand.selectAllCommandsBySource(
this.global.db,
source,
packageIds
)
}
})
.then((cmds) => templateUtil.collectBlocks(cmds, options, this))
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all client commands.
* There are two modes of this helper:
* when used in a global context, it iterates over ALL client commands in the database.
* when used inside a `zcl_cluster` block helper, it iterates only over the commands for that client cluster.
*
* @param {*} options
* @returns Promise of content.
*/
function zcl_commands_source_client(options) {
return zcl_commands_by_source.bind(this)(options, dbEnum.source.client)
}
/**
* Block helper iterating over all server commands.
* There are two modes of this helper:
* when used in a global context, it iterates over ALL server commands in the database.
* when used inside a `zcl_cluster` block helper, it iterates only over the commands for that server cluster.
*
* @param {*} options
* @returns Promise of content.
*/
function zcl_commands_source_server(options) {
return zcl_commands_by_source.bind(this)(options, dbEnum.source.server)
}
/**
* Block helper iterating over all events.
* There are two modes of this helper:
* when used in a global context, it iterates over ALL events in the database.
* when used inside a `zcl_cluster` block helper, it iterates only over the events for that cluster.
*
* @param {*} options
* @returns Promise of content.
*/
async function zcl_events(options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let events
if ('id' in this) {
// We're functioning inside a nested context with an id, so we will only query for this cluster.
events = await queryEvent.selectEventsByClusterId(
this.global.db,
this.id,
packageIds
)
} else {
events = await queryEvent.selectAllEvents(this.global.db, packageIds)
}
let ps = events.map(async (ev) => {
ev.items = await queryEvent.selectEventFieldsByEventId(
this.global.db,
ev.id
)
})
await Promise.all(ps)
let promise = templateUtil.collectBlocks(events, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all commands, including their arguments and clusters.
*
* @param {*} options
* @returns Promise of content.
*/
function zcl_command_tree(options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
queryCommand.selectCommandTree(this.global.db, packageIds)
)
.then((cmds) => {
// Now reduce the array by collecting together arguments.
let reducedCommands = []
cmds.forEach((el) => {
let newCommand
let lastCommand
if (reducedCommands.length == 0) {
newCommand = true
} else {
lastCommand = reducedCommands[reducedCommands.length - 1]
if (
el.code == lastCommand.code &&
el.clusterCode == lastCommand.clusterCode &&
el.source == lastCommand.source
) {
newCommand = false
} else {
newCommand = true
}
}
let arg
if (el.argName == null) {
arg = null
} else {
arg = {
name: el.argName,
type: el.argType,
isArray: el.argIsArray,
hasLength: el.argIsArray,
nameLength: el.argName.concat('Len')
}
if (el.argIsArray) {
arg.formatChar = 'b'
} else if (types.isOneBytePrefixedString(el.argType)) {
arg.formatChar = 's'
} else if (types.isTwoBytePrefixedString(el.argType)) {
arg.formatChar = 'l'
} else {
arg.formatChar = 'u'
}
}
if (newCommand) {
el.commandArgs = []
if (arg != null) {
el.commandArgs.push(arg)
el.argsstring = arg.formatChar
} else {
el.argsstring = ''
}
let n = ''
if (el.clusterCode == null) {
n = n.concat('Global')
} else {
n = n.concat(_.upperFirst(_.camelCase(el.clusterDefineName)))
}
if (el.source == dbEnum.source.either) {
// We will need to create two here.
n = n.concat('ClientToServer')
}
n = n.concat(el.name)
el.clientMacroName = n
el.isGlobal = el.clusterCode == null
reducedCommands.push(el)
} else {
if (arg != null) {
lastCommand.commandArgs.push(arg)
lastCommand.argsstring = lastCommand.argsstring.concat(
arg.formatChar
)
}
}
})
return reducedCommands
})
.then((cmds) => templateUtil.collectBlocks(cmds, options, this))
return templateUtil.templatePromise(this.global, promise)
}
/**
* Helper to iterate over all global commands.
*
* @param {*} options
* @returns Promise of global command iteration.
*/
function zcl_global_commands(options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
queryCommand.selectAllGlobalCommands(this.global.db, packageIds)
)
.then((cmds) => templateUtil.collectBlocks(cmds, options, this))
return templateUtil.templatePromise(this.global, promise)
}
/**
* Iterator over the attributes. If it is used at toplevel, if iterates over all the attributes
* in the database. If used within zcl_cluster context, it iterates over all the attributes
* that belong to that cluster.
*
* @param {*} options
* @returns Promise of attribute iteration.
*/
async function zcl_attributes(options) {
// If used at the toplevel, 'this' is the toplevel context object.
// when used at the cluster level, 'this' is a cluster
let packageIds = await templateUtil.ensureZclPackageIds(this)
let attributes = ''
if ('id' in this) {
// We're functioning inside a nested context with an id, so we will only query for this cluster.
attributes = await queryZcl.selectAttributesByClusterIdIncludingGlobal(
this.global.db,
this.id,
packageIds
)
attributes = await upgrade.computeStoragePolicyForGlobalAttributes(
this.global.db,
this.id,
attributes,
packageIds
)
} else {
attributes = await queryZcl.selectAllAttributes(this.global.db, packageIds)
}
let promise = templateUtil.collectBlocks(attributes, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Iterator over the client attributes. If it is used at toplevel, if iterates over all the client attributes
* in the database. If used within zcl_cluster context, it iterates over all the client attributes
* that belong to that cluster.
*
* @param {*} options
* @returns Promise of attribute iteration.
*/
async function zcl_attributes_client(options) {
// If used at the toplevel, 'this' is the toplevel context object.
// when used at the cluster level, 'this' is a cluster
let packageIds = await templateUtil.ensureZclPackageIds(this)
let clientAttributes = ''
if ('id' in this) {
// We're functioning inside a nested context with an id, so we will only query for this cluster.
clientAttributes =
await queryZcl.selectAttributesByClusterIdAndSideIncludingGlobal(
this.global.db,
this.id,
packageIds,
dbEnum.side.client
)
clientAttributes = await upgrade.computeStoragePolicyForGlobalAttributes(
this.global.db,
this.id,
clientAttributes,
packageIds
)
} else {
clientAttributes = await queryZcl.selectAllAttributesBySide(
this.global.db,
dbEnum.side.client,
packageIds
)
}
let promise = templateUtil.collectBlocks(clientAttributes, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Iterator over the server attributes. If it is used at toplevel, if iterates over all the server attributes
* in the database. If used within zcl_cluster context, it iterates over all the server attributes
* that belong to that cluster.
* Available Options:
* - removeKeys: Removes one or more keys from the map(for eg keys in db-mapping.js)
* for eg: (#zcl_attributes_server removeKeys='isOptional, isNullable') will remove 'isOptional'
* from the results
* @param {*} options
* @returns Promise of attribute iteration.
*/
async function zcl_attributes_server(options) {
// If used at the toplevel, 'this' is the toplevel context object.
// when used at the cluster level, 'this' is a cluster
let packageIds = await templateUtil.ensureZclPackageIds(this)
let serverAttributes = ''
if ('id' in this) {
// We're functioning inside a nested context with an id, so we will only query for this cluster.
serverAttributes =
await queryZcl.selectAttributesByClusterIdAndSideIncludingGlobal(
this.global.db,
this.id,
packageIds,
dbEnum.side.server
)
serverAttributes = await upgrade.computeStoragePolicyForGlobalAttributes(
this.global.db,
this.id,
serverAttributes,
packageIds
)
} else {
serverAttributes = await queryZcl.selectAllAttributesBySide(
this.global.db,
dbEnum.side.server,
packageIds
)
}
if ('removeKeys' in options.hash) {
let keys = options.hash.removeKeys.split(',')
keys.forEach((k) => serverAttributes.map((attr) => delete attr[k.trim()]))
}
let promise = templateUtil.collectBlocks(serverAttributes, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over all atomic types.
*
* @param {*} options
* @returns Promise of content.
*/
async function zcl_atomics(options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
Promise.all(
packageIds.map((packageId) =>
queryZcl.selectAllAtomics(this.global.db, packageId)
)
)
)
.then((x) => x.flat())
.then((ats) => templateUtil.collectBlocks(ats, options, this))
return templateUtil.templatePromise(this.global, promise)
}
/**
*
*
* Given: N/A
* @returns the length of largest cluster name in a list of clusters
*/
async function zcl_cluster_largest_label_length() {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
Promise.all(
packageIds.map((packageId) =>
queryZcl.selectAllClusters(this.global.db, packageId)
)
)
)
.then((cls) => cls.flat())
.then((cl) => largestLabelLength(cl))
return templateUtil.templatePromise(this.global, promise)
}
/**
*
*
* @param {*} An Array
* @returns the length of largest object name in an array. Helper for
* zcl_cluster_largest_label_length
*/
function largestLabelLength(arrayOfClusters) {
return Math.max(...arrayOfClusters.map((cl) => cl.label.length))
}
/**
* Helper to extract the number of command arguments in a command
*
* @param {*} commandId
* @returns Number of command arguments as an integer
*/
function zcl_command_arguments_count(commandId) {
let promise = queryCommand.selectCommandArgumentsCountByCommandId(
this.global.db,
commandId
)
return templateUtil.templatePromise(this.global, promise)
}
/**
*
* @param commandId
* @param fixedLengthReturn
* @param notFixedLengthReturn
* @param currentContext
* Returns fixedLengthReturn or notFixedLengthReturn based on whether the
* command is fixed length or not
*/
async function ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
fixedLengthReturn,
notFixedLengthReturn,
currentContext
) {
let commandArgs = await queryCommand.selectCommandArgumentsByCommandId(
currentContext.global.db,
commandId
)
let isFixedLength = true
for (let argIndex = 0; argIndex < commandArgs.length; argIndex++) {
if (
commandArgs[argIndex].isArray ||
types.isString(commandArgs[argIndex].type)
) {
isFixedLength = false
}
}
if (isFixedLength) {
return fixedLengthReturn
} else {
return notFixedLengthReturn
}
}
/**
*
* @param commandId
* @param fixedLengthReturn
* @param notFixedLengthReturn
* Returns fixedLengthReturn or notFixedLengthReturn based on whether the
* command is fixed length or not. Does not check if command
* arguments are always present or not.
*/
function if_command_arguments_have_fixed_length(
commandId,
fixedLengthReturn,
notFixedLengthReturn
) {
return ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
fixedLengthReturn,
notFixedLengthReturn,
this
)
}
/**
*
* @param type
* @param command
* @param commandArg
* @param appendString
* @param options
* @returns the underlying zcl type of a command argument if the argument is
* not fixed length but is always present. If the condition is not met then
* returns an empty string.
*/
function as_underlying_zcl_type_command_is_not_fixed_length_but_command_argument_is_always_present(
type,
command,
commandArg,
appendString,
options
) {
return queryCommand
.selectCommandArgumentsByCommandId(this.global.db, command)
.then(
(commandArgs) =>
new Promise((resolve, reject) => {
for (let ca of commandArgs) {
if (
ca.isArray ||
types.isString(ca.type) ||
ca.introducedInRef ||
ca.removedInRef ||
ca.presentIf
) {
resolve(false)
}
}
resolve(true)
})
)
.then((isFixedLengthCommand) => {
if (isFixedLengthCommand) {
return ''
} else if (
!(
commandArg.isArray ||
commandArg.introducedInRef ||
commandArg.removedInRef ||
commandArg.presentIf
)
) {
return templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
type,
options,
packageIds,
this
)
)
}
return ''
})
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(
'Failure in as_underlying_zcl_type_command_is_not_fixed_length_but_command_argument_is_always_present: ' +
err
)
})
}
/**
*
* @param type
* @param commandId
* @param appendString
* @param options
* Returns: Given the commandId and the type of one of its arguments, based on
* whether the command is fixed length or not either return nothing or return
* the underlying zcl type appended with the appendString.
*/
function as_underlying_zcl_type_if_command_is_not_fixed_length(
type,
commandId,
appendString,
options
) {
let promise = ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
)
.then((res) => {
if (res) {
return ''
} else {
return templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
type,
options,
packageIds,
this
)
)
}
})
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(err)
throw err
})
return templateUtil.templatePromise(this.global, promise)
}
/**
*
* @param commandId
* Returns the size of the command by calculating the sum total of the command arguments
* Note: This helper should be called on fixed length commands only. It should not be
* called with commands which do not have a fixed length.
*/
function command_arguments_total_length(commandId) {
return queryCommand
.selectCommandArgumentsByCommandId(this.global.db, commandId)
.then((commandArgs) =>
new Promise((resolve, reject) => {
let argsLength = []
for (const commandArg of commandArgs) {
let argType = commandArg.type
let argOptions = {}
argOptions.hash = {}
argOptions.hash[dbEnum.zclType.zclCharFormatter] = true
let argLength = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
argType,
argOptions,
packageIds,
this
)
)
argsLength.push(argLength)
}
resolve(argsLength)
}).then((argsLength) => {
return Promise.all(argsLength).then((lengths) =>
lengths.reduce((a, b) => a + b, 0)
)
})
)
.catch((err) =>
env.logError('Unable to get the length of the command arguments: ' + err)
)
}
/**
* Block helper iterating over command arguments within a command
* or a command tree.
*
* @param {*} options
* @returns Promise of command argument iteration.
*/
async function zcl_command_arguments(options) {
let commandArgs = this.commandArgs
let packageIds = await templateUtil.ensureZclPackageIds(this)
// When we are coming from commant_tree, then
// the commandArgs are already present and there is no need
// to do additional queries.
if (commandArgs == null) {
if ('id' in this) {
// We're functioning inside a nested context with an id, so we will only query for this cluster.
commandArgs = await queryCommand.selectCommandArgumentsByCommandId(
this.global.db,
this.id
)
} else {
commandArgs = await queryCommand.selectAllCommandArguments(
this.global.db,
packageIds
)
}
}
// Adding command argument type size and sign
for (let i = 0; i < commandArgs.length; i++) {
let sizeAndSign = await types.getSignAndSizeOfZclType(
this.global.db,
commandArgs[i].type,
packageIds,
options
)
commandArgs[i].typeSize = sizeAndSign.dataTypesize
commandArgs[i].typeIsSigned = sizeAndSign.isTypeSigned
}
let promise = templateUtil.collectBlocks(commandArgs, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Block helper iterating over the event fields inside an event.
*
* @param {*} options
*/
function zcl_event_fields(options) {
let eventFields = this.eventField
let p
if (eventFields == null) {
p = templateUtil.ensureZclPackageIds(this).then((packageIds) => {
if ('id' in this) {
return queryEvent.selectEventFieldsByEventId(this.global.db, this.id)
} else {
return queryEvent.selectAllEventFields(this.global.db, packageIds)
}
})
} else {
p = Promise.resolve(eventFields)
}
let promise = p.then((fields) =>
templateUtil.collectBlocks(fields, options, this)
)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Helper that deals with the type of the argument.
*
* @param {*} typeName
* @param {*} options
*/
function zcl_command_argument_data_type(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
Promise.all([
zclUtil.isEnum(this.global.db, type, packageIds),
zclUtil.isStruct(this.global.db, type, packageIds),
zclUtil.isBitmap(this.global.db, type, packageIds)
])
.then(
(res) =>
new Promise((resolve, reject) => {
for (let i = 0; i < res.length; i++) {
if (res[i] != 'unknown') {
resolve(res[i])
return
}
}
resolve(dbEnum.zclType.unknown)
})
)
.then((resType) => {
switch (resType) {
case dbEnum.zclType.bitmap:
return helperC.data_type_for_bitmap(
this.global.db,
type,
packageIds
)
case dbEnum.zclType.enum:
return helperC.data_type_for_enum(
this.global.db,
type,
packageIds
)
case dbEnum.zclType.struct:
return options.hash.struct
case dbEnum.zclType.atomic:
case dbEnum.zclType.unknown:
default:
return helperC.as_cli_type(type)
}
})
.catch((err) => {
env.logError(err)
throw err
})
)
.catch((err) => {
env.logError(err)
throw err
})
return templateUtil.templatePromise(this.global, promise)
}
/**
* Helper that deals with the type of the argument.
*
* @param {*} typeName
* @param {*} options
* Note: If the options has zclCharFormatter set to true then the function will
* return the user defined data associated with the zcl data type and not the
* actual data type.
*
* example:
* {{asUnderlyingZclType [array type] array="b" one_byte="u" two_byte="v" three_byte="x"
* four_byte="w" short_string="s" long_string="l" default="b"
* zclCharFormatter="true"}}
*
* For the above if asUnderlyingZclType was given [array type] then the above
* will return 'b'
*/
async function asUnderlyingZclType(type, options) {
const packageIds = await templateUtil.ensureZclPackageIds(this)
let promise = zclUtil
.asUnderlyingZclTypeWithPackageId(type, options, packageIds, this)
.catch((err) => {
env.logError(err)
throw err
})
return templateUtil.templatePromise(this.global, promise)
}
/**
*
* @param type
* @param options
* Returns the data mentioned in the helper options based on whether the type
* is short string, long string or not a string
* Example:
* {{zcl_string_type_return type short_string="short string output"
* long_string="short string output"
* default="Output when not a string")
*
*/
function zcl_string_type_return(type, options) {
if (
!(
'short_string' in options.hash &&
'long_string' in options.hash &&
'default' in options.hash
)
) {
throw new Error('Specify all options for the helper')
}
if (types.isOneBytePrefixedString(type.toLowerCase())) {
return options.hash.short_string
} else if (types.isTwoBytePrefixedString(type.toLowerCase())) {
return options.hash.long_string
} else {
return options.hash.default
}
}
/**
*
* @param type
* Return: true or false based on whether the type is a string or not.
*/
function is_zcl_string(type) {
return types.isString(type)
}
/**
* If helper that checks if a type is a string
*
* example:
* {{#if_is_number type}}
* type is number
* {{else}}
* type is not number
* {{/if_is_number}}
*
* @param {*} type
* @returns Promise of content.
*/
async function if_is_number(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectNumberByName(
this.global.db,
packageIds,
type.toLowerCase()
)
: null
)
.then((res) =>
res ? res : queryZcl.selectNumberById(this.global.db, type)
)
.then((res) => (res ? options.fn(this) : options.inverse(this)))
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper that checks if a type is a string
*
* example:
* {{#if_is_string type}}
* type is string
* {{else}}
* type is not string
* {{/if_is_string}}
*
* @param {*} type
* @returns Promise of content.
*/
function if_is_string(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectStringByName(
this.global.db,
type.toLowerCase(),
packageIds
)
: null
)
.then((res) =>
res ? res : queryZcl.selectStringById(this.global.db, type)
)
.then((res) => (res ? options.fn(this) : options.inverse(this)))
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper that checks if a string type is present in the list of char strings
* i.e. characterStringTypes
*
* example:
* {{#if_is_char_string type}}
* type is char string
* {{else}}
* type is not char string
* {{/if_is_char_string}}
*
* @param {*} type
* @returns Promise of content.
*/
function if_is_char_string(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectStringByName(
this.global.db,
type.toLowerCase(),
packageIds
)
: null
)
.then((res) =>
res ? res : queryZcl.selectStringById(this.global.db, type)
)
.then((res) =>
res && res.name && characterStringTypes.includes(res.name.toUpperCase())
? options.fn(this)
: options.inverse(this)
)
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper that checks if a string type is present in the list of octet strings
* i.e. octetStringTypes
*
* example:
* {{#if_is_octet_string type}}
* type is octet string
* {{else}}
* type is not octet string
* {{/if_is_octet_string}}
*
* @param {*} type
* @returns Promise of content.
*/
function if_is_octet_string(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectStringByName(
this.global.db,
type.toLowerCase(),
packageIds
)
: null
)
.then((res) =>
res ? res : queryZcl.selectStringById(this.global.db, type)
)
.then((res) =>
res && res.name && octetStringTypes.includes(res.name.toUpperCase())
? options.fn(this)
: options.inverse(this)
)
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper that checks if a string type is present in the list of short strings
* i.e. stringShortTypes
*
* example:
* {{#if_is_short_string type}}
* type is short string
* {{else}}
* type is not short string
* {{/if_is_short_string}}
*
* @param {*} type
* @returns Promise of content.
*/
function if_is_short_string(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectStringByName(
this.global.db,
type.toLowerCase(),
packageIds
)
: null
)
.then((res) =>
res ? res : queryZcl.selectStringById(this.global.db, type)
)
.then((res) =>
res && res.name && stringShortTypes.includes(res.name.toUpperCase())
? options.fn(this)
: options.inverse(this)
)
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper that checks if a string type is present in the list of long strings
* i.e. stringLongTypes
*
* example:
* {{#if_is_long_string type}}
* type is long string
* {{else}}
* type is not long string
* {{/if_is_long_string}}
*
* @param {*} type
* @returns Promise of content.
*/
function if_is_long_string(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectStringByName(
this.global.db,
type.toLowerCase(),
packageIds
)
: null
)
.then((res) =>
res ? res : queryZcl.selectStringById(this.global.db, type)
)
.then((res) =>
res && res.name && stringLongTypes.includes(res.name.toUpperCase())
? options.fn(this)
: options.inverse(this)
)
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper that checks if a type is an atomic
*
* example:
* {{#if_is_atomic type}}
* type is atomic
* {{else}}
* type is not atomic
* {{/if_is_atomic}}
*
* @param {*} type: string
* @returns Promise of content.
*/
async function if_is_atomic(type, options) {
let result = null
if (typeof type === 'string') {
result = await templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
queryZcl.selectAtomicType(this.global.db, packageIds, type)
)
} else {
env.logWarning(
'Passing a type which is invlaid for if_is_atomic helper: ' + type
)
}
if (result) {
return options.fn(this)
} else {
return options.inverse(this)
}
}
/**
* If helper that checks if a type is a bitmap
*
* example:
* {{#if_is_bitmap type}}
* type is bitmap
* {{else}}
* type is not bitmap
* {{/if_is_bitmap}}
*
* @param {*} type
* @returns Promise of content.
*/
async function if_is_bitmap(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectBitmapByName(this.global.db, packageIds, type)
: null
)
.then((res) =>
res ? res : queryZcl.selectBitmapById(this.global.db, type)
)
.then((res) =>
res || (typeof type === 'string' && type.startsWith('map'))
? options.fn(this)
: options.inverse(this)
)
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper that checks if a type is an enum
*
* * example:
* {{#if_is_enum type}}
* type is enum
* {{else}}
* type is not enum
* {{/if_is_enum}}
*
* @param {*} type
* @returns Promise of content.
*/
async function if_is_enum(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectEnumByName(this.global.db, type, packageIds)
: null
)
.then((res) => (res ? res : queryZcl.selectEnumById(this.global.db, type)))
.then((res) => (res ? options.fn(this) : options.inverse(this)))
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper that checks if a type is an struct
*
* * example:
* {{#if_is_struct type}}
* type is struct
* {{else}}
* type is not struct
* {{/if_is_struct}}
*
* @param type
* @returns Promise of content.
*/
async function if_is_struct(type, options) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
type && typeof type === 'string'
? queryZcl.selectStructByName(this.global.db, type, packageIds)
: null
)
.then((res) =>
res ? res : queryZcl.selectStructById(this.global.db, type)
)
.then((res) => (res ? options.fn(this) : options.inverse(this)))
return templateUtil.templatePromise(this.global, promise)
}
/**
* Checks if the side is client or not
*
* @param {*} side
* @returns boolean
*/
function isClient(side) {
return 0 == side.localeCompare(dbEnum.side.client)
}
/**
* Checks if the side is server or not
*
* @param {*} side
* @returns boolean
*/
function isServer(side) {
return 0 == side.localeCompare(dbEnum.side.server)
}
/**
* Compares 2 strings.
*
* @param {*} str1
* @param {*} str2
* @returns boolean
*/
function isStrEqual(str1, str2) {
return 0 == str1.localeCompare(str2)
}
/**
* Returns boolean based on whether the element is the last element.
* @param {*} index
* @param {*} count
* @returns boolean
*/
function isLastElement(index, count) {
return index == count - 1
}
/**
* Returns boolean based on whether the element is the first element.
* @param {*} index
* @param {*} count
* @returns boolean
*/
function isFirstElement(index, count) {
return index == 0
}
/**
* Check if enable is 1.
*
* @param {*} enable
* @returns boolean
*/
function isEnabled(enable) {
return 1 == enable
}
/**
* Returns boolean based on command being available or not.
*
* @param {*} clusterSide
* @param {*} incoming
* @param {*} outgoing
* @param {*} source
* @param {*} name
* @returns boolean
*/
function isCommandAvailable(clusterSide, incoming, outgoing, source, name) {
if (0 == clusterSide.localeCompare(source)) {
return false
}
return (
(isClient(clusterSide) && incoming) || (isServer(clusterSide) && incoming)
)
}
/**
*
*
* @param type: type of argument
* @param commandId: command id
* @param appendString: append the string to the argument
* @param introducedInRef: If the command argument is not present in all zcl
* specifications and was introduced in a certain specification version then this will not be null
* @param removedInRef: If the command argument is not present in all zcl
* specifications and was removed in a certain specification version then this will not be null
* @param presentIf: If the command argument is present conditionally then this will be a condition
* and not null
*
* @param options: options which can be passed to zclUtil.asUnderlyingZclTypeWithPackageId
* for determining the underlying zcl type for the provided argument type
* @returns A string as an underlying zcl type if the command is not fixed length and the command
* argument is always present in all zcl specifications.
*/
function as_underlying_zcl_type_command_argument_always_present(
type,
commandId,
appendString,
introducedInRef,
removedInRef,
presentIf,
options
) {
let promise = ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
)
.then((res) => {
if (res) {
return ''
} else {
// Return the underlying zcl type since command argument is always present
if (introducedInRef || removedInRef || presentIf) {
// Return nothing if the command argument is not always present
return ''
} else {
// Return the underlying zcl type if the command argument is always present.
return templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
type,
options,
packageIds,
this
)
)
}
}
})
// Adding the appendString for the underlying zcl type
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(err)
throw err
})
return templateUtil.templatePromise(this.global, promise)
}
/**
*
*
* @param commandId
* @param introducedInRef
* @param removedInRef
* @param presentIf
* @param argumentPresentReturn
* @param argumentNotPresentReturn
* @returns argumentPresentReturn if the command is not fixed length and command
* argument is always present without conditions(introducedInRef, removedInRef,
* presentIf) else returns argumentNotPresentReturn
*/
function if_command_argument_always_present(
commandId,
introducedInRef,
removedInRef,
presentIf,
argumentPresentReturn,
argumentNotPresentReturn
) {
return ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
).then((res) => {
if (res) {
return '' // Return nothing since command is a fixed length command
} else {
if (introducedInRef || removedInRef || presentIf) {
return argumentNotPresentReturn
}
return argumentPresentReturn
}
})
}
/**
*
*
* @param type: type of argument
* @param commandId: command id
* @param appendString: append the string to the argument
* @param introducedInRef: If the command argument is not present in all zcl
* specifications and was introduced in a certain specification version then this will not be null
* @param removedInRef: If the command argument is not present in all zcl
* specifications and was removed in a certain specification version then this will not be null
* @param presentIf: If the command argument is present conditionally then this will be a condition
* and not null
* @param options: options which can be passed to zclUtil.asUnderlyingZclTypeWithPackageId
* for determining the underlying zcl type for the provided argument type
* @returns A string as an underlying zcl type if the command is not fixed length, the command
* argument is not always present in all zcl specifications and there is no present if conditionality
* on the command argument.
*/
function as_underlying_zcl_type_command_argument_not_always_present_no_presentif(
type,
commandId,
appendString,
introducedInRef,
removedInRef,
presentIf,
options
) {
let promise = ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
)
.then((res) => {
if (res) {
return '' // Return nothing since the command is of fixed length
} else {
// Return the underlying zcl type since command argument is not always present and there is no present if conditionality
if ((introducedInRef || removedInRef) && !presentIf) {
return templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
type,
options,
packageIds,
this
)
)
} else {
return ''
}
}
})
// Adding the appendString for the underlying zcl type
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(err)
throw err
})
return templateUtil.templatePromise(this.global, promise)
}
/**
* @param commandArg command argument
* @param appendString append the string to the argument
* @param options options which can be passed to zclUtil.asUnderlyingZclTypeWithPackageId
* for determining the underlying zcl type for the provided argument type
* @returns A string as an underlying zcl type if the command is not fixed
* length, the command argument is not always present in all zcl specifications
* and there is no present if conditionality on the command argument.
*/
function as_underlying_zcl_type_ca_not_always_present_no_presentif(
commandArg,
appendString,
options
) {
// Return the underlying zcl type since command argument is not always
// present and there is no present if conditionality
if (
(commandArg.introducedInRef || commandArg.removedInRef) &&
!commandArg.presentIf
) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
commandArg.type,
options,
packageIds,
this
)
) // Adding the appendString for the underlying zcl type
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(
'Error in as_underlying_zcl_type_ca_not_always_present_no_presentif ' +
err
)
throw err
})
return templateUtil.templatePromise(this.global, promise)
} else {
return ''
}
}
/**
*
*
* @param commandId
* @param introducedInRef
* @param removedInRef
* @param presentIf
* @param argumentNotInAllVersionsReturn
* @param argumentInAllVersionsReturn
* @returns argumentNotInAllVersionsReturn if the command is not fixed length and command
* argument is present with conditions introducedInRef or removedInRef but no presentIf
* conditions else returns argumentNotPresentReturn
*/
function if_command_argument_not_always_present_no_presentif(
commandId,
introducedInRef,
removedInRef,
presentIf,
argumentNotInAllVersionsReturn,
argumentInAllVersionsReturn
) {
return ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
).then((res) => {
if (res) {
return '' // Return nothing since it is a fixed length command
} else {
if ((introducedInRef || removedInRef) && !presentIf) {
return argumentNotInAllVersionsReturn
}
return argumentInAllVersionsReturn
}
})
}
/**
*
*
* @param type: type of argument
* @param commandId: command id
* @param appendString: append the string to the argument
* @param introducedInRef: If the command argument is not present in all zcl
* specifications and was introduced in a certain specification version then this will not be null
* @param removedInRef: If the command argument is not present in all zcl
* specifications and was removed in a certain specification version then this will not be null
* @param presentIf: If the command argument is present conditionally then this will be a condition
* and not null
* @param options: options which can be passed to zclUtil.asUnderlyingZclTypeWithPackageId
* for determining the underlying zcl type for the provided argument type
* @returns A string as an underlying zcl type if the command is not fixed length, the command
* argument is not always present in all zcl specifications and there is a present if conditionality
* on the command argument.
*/
function as_underlying_zcl_type_command_argument_not_always_present_with_presentif(
type,
commandId,
appendString,
introducedInRef,
removedInRef,
presentIf,
options
) {
let promise = ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
)
.then((res) => {
if (res) {
return '' // Return nothing since the command is of fixed length
} else {
// Return the underlying zcl type since command argument is not always present and there is present if conditionality.
if ((introducedInRef || removedInRef) && presentIf) {
return templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
type,
options,
packageIds,
this
)
)
} else {
return ''
}
}
})
// Adding the appendString for the underlying zcl type
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(err)
throw err
})
return templateUtil.templatePromise(this.global, promise)
}
/**
* @param commandArg command argument
* @param appendString append the string to the argument
* @param options options which can be passed to zclUtil.asUnderlyingZclTypeWithPackageId
* for determining the underlying zcl type for the provided argument type
* @returns A string as an underlying zcl type if the command is not fixed
* length, the command argument is not always present in all zcl specifications
* but there is a present if conditionality on the command argument.
*/
function as_underlying_zcl_type_ca_not_always_present_with_presentif(
commandArg,
appendString,
options
) {
// Return the underlying zcl type since command argument is not always
// present and there is a present if conditionality
if (
(commandArg.introducedInRef || commandArg.removedInRef) &&
commandArg.presentIf
) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
commandArg.type,
options,
packageIds,
this
)
) // Adding the appendString for the underlying zcl type
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(
'Error in as_underlying_zcl_type_ca_not_always_present_with_presentif ' +
err
)
throw err
})
return templateUtil.templatePromise(this.global, promise)
} else {
return ''
}
}
/**
*
*
* @param commandId
* @param introducedInRef
* @param removedInRef
* @param presentIf
* @param argumentNotInAllVersionsPresentIfReturn
* @param argumentInAllVersionsReturn
* @returns argumentNotInAllVersionsReturn if the command is not fixed length, command
* argument is present with conditions introducedInRef or removedInRef and presentIf
* conditions exist as well else returns argumentNotPresentReturn
*/
function if_command_argument_not_always_present_with_presentif(
commandId,
introducedInRef,
removedInRef,
presentIf,
argumentNotInAllVersionsPresentIfReturn,
argumentInAllVersionsReturn
) {
return ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
).then((res) => {
if (res) {
return '' // Return nothing since it is a fixed length command
} else {
if ((introducedInRef || removedInRef) && presentIf) {
return argumentNotInAllVersionsPresentIfReturn
}
return argumentInAllVersionsReturn
}
})
}
/**
*
*
* @param type: type of argument
* @param commandId: command id
* @param appendString: append the string to the argument
* @param introducedInRef: If the command argument is not present in all zcl
* specifications and was introduced in a certain specification version then this will not be null
* @param removedInRef: If the command argument is not present in all zcl
* specifications and was removed in a certain specification version then this will not be null
* @param presentIf: If the command argument is present conditionally then this will be a condition
* and not null
* @param options: options which can be passed to zclUtil.asUnderlyingZclTypeWithPackageId
* for determining the underlying zcl type for the provided argument type
* @returns A string as an underlying zcl type if the command is not fixed length, the command
* argument is always present in all zcl specifications and there is a present if conditionality
* on the command argument.
*/
function as_underlying_zcl_type_command_argument_always_present_with_presentif(
type,
commandId,
appendString,
introducedInRef,
removedInRef,
presentIf,
options
) {
let promise = ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
)
.then((res) => {
if (res) {
return '' // Return nothing since the command is of fixed length
} else {
// Return the underlying zcl type since command argument is always present and there is a present if condition
if (!(introducedInRef || removedInRef) && presentIf) {
return templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
type,
options,
packageIds,
this
)
)
} else {
return ''
}
}
})
// Adding the appendString for the underlying zcl type
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(err)
throw err
})
return templateUtil.templatePromise(this.global, promise)
}
/**
* @param commandArg command argument
* @param appendString append the string to the argument
* @param options options which can be passed to zclUtil.asUnderlyingZclTypeWithPackageId
* for determining the underlying zcl type for the provided argument type
* @returns A string as an underlying zcl type if the command is not fixed
* length, the command argument is always present in all zcl specifications
* but there is a present if conditionality on the command argument.
*/
function as_underlying_zcl_type_ca_always_present_with_presentif(
commandArg,
appendString,
options
) {
// Return the underlying zcl type since command argument is always
// present and there is a present if conditionality
if (
!(commandArg.introducedInRef || commandArg.removedInRef) &&
commandArg.presentIf
) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) =>
zclUtil.asUnderlyingZclTypeWithPackageId(
commandArg.type,
options,
packageIds,
this
)
) // Adding the appendString for the underlying zcl type
.then((res) => (res ? res + appendString : res))
.catch((err) => {
env.logError(
'Error in as_underlying_zcl_type_ca_always_present_with_presentif ' +
err
)
throw err
})
return templateUtil.templatePromise(this.global, promise)
} else {
return ''
}
}
/**
*
*
* @param commandId
* @param introducedInRef
* @param removedInRef
* @param presentIf
* @param argumentNotInAllVersionsPresentIfReturn
* @param argumentInAllVersionsReturn
* @returns argumentInAllVersionsPresentIfReturn if the command is not fixed length, command
* argument is always present and presentIf conditions exist else returns argumentNotPresentReturn
*/
async function if_command_argument_always_present_with_presentif(
commandId,
introducedInRef,
removedInRef,
presentIf,
argumentInAllVersionsPresentIfReturn,
argumentNotAlwaysThereReturn
) {
let res = await ifCommandArgumentsHaveFixedLengthWithCurrentContext(
commandId,
true,
false,
this
)
if (res) {
return '' // Return nothing since it is a fixed length command
} else {
if (!(introducedInRef || removedInRef) && presentIf) {
return argumentInAllVersionsPresentIfReturn
}
return argumentNotAlwaysThereReturn
}
}
/**
*
*
* @param {*} clusterId
* @param {*} manufacturer_specific_return
* @param {*} null_manufacturer_specific_return
* @returns manufacturer_specific_return if the cluster is manufacturer
* specific or returns null_manufacturer_specific_return if cluster is
* not manufacturer specific.
*/
function if_manufacturing_specific_cluster(
clusterId,
manufacturer_specific_return,
null_manufacturer_specific_return
) {
let promise = templateUtil
.ensureZclPackageIds(this)
.then((packageIds) => queryZcl.selectClusterById(this.global.db, clusterId))
.then((res) => {
if (res.manufacturerCode != null) {
return manufacturer_specific_return
} else {
return null_manufacturer_specific_return
}
})
return templateUtil.templatePromise(this.global, promise)
}
/**
* If helper which checks if cluster is manufacturing specific or not
* example:
* {{#if_mfg_specific_cluster clusterId}}
* cluster is manufacturing specific
* {{else}}
* cluster is not manufacturing specific
* {{/if_mfg_specific_cluster}}
*
* @param clusterId
* @param options
* @returns Returns content in the handlebar template based on whether the
* command is manufacturing specific or not.
*/
async function if_mfg_specific_cluster(clusterId, options) {
let res = await queryZcl.selectClusterById(this.global.db, clusterId)
if (res.manufacturerCode != null) {
return options.fn(this)
} else {
return options.inverse(this)
}
}
/**
* Given the value and size of an attribute along with endian as an option.
* This helper returns the attribute value as big/little endian.
* Example: {{as_generated_default_macro 0x00003840 4 endian="big"}}
* will return: 0x00, 0x00, 0x38, 0x40,
* @param value
* @param attributeSize
* @param options
* @returns Formatted attribute value based on given arguments
* Available options:
* - endian: Specify 'big' or 'little' endian format
* - isCommaTerminated: '0' or '1' for output to have a ',' at the end
*/
async function as_generated_default_macro(value, attributeSize, options) {
let default_macro_signature = ''
let temp = ''
let isCommaTerminated =
'isCommaTerminated' in options.hash ? options.hash.isCommaTerminated : true
if (attributeSize > 2) {
// String value
if (isNaN(value)) {
return format_zcl_string_as_characters_for_generated_defaults(
value,
attributeSize,
options
)
}
// Float value
if (!isNaN(value) && value.toString().indexOf('.') != -1) {
temp = types.convertFloatToBigEndian(value, attributeSize)
} else {
if (value >= 0) {
// Positive or zero value - ensure proper padding for the attribute size
const ret = value.trim ? value.trim() : value.toString()
let hexValue
if (ret.startsWith('0x') || ret.startsWith('0X')) {
hexValue = ret.slice(2)
} else {
hexValue = parseInt(ret, 10).toString(16)
}
const requiredHexDigits = attributeSize * 2
temp = `0x${hexValue.toUpperCase().padStart(requiredHexDigits, '0')}`
} else {
// Negative value
temp = types.convertIntToBigEndian(value, attributeSize)
}
}
// Padding based on attribute size
let default_macro = temp.replace('0x', '').match(/.{1,2}/g)
let padding_length = attributeSize - default_macro.length
for (let i = 0; i < padding_length; i++) {
default_macro_signature += '0x00, '
}
for (let m of default_macro) {
default_macro_signature += ' 0x' + m.toUpperCase() + ','
}
}
// Applying endianess to attributes with size less than equal to 8 bytes.
// Thus only swapping int64u or smaller
if (options.hash.endian != 'big' && attributeSize <= 8) {
default_macro_signature = default_macro_signature
.split(' ')
.reverse()
.join(' ')
}
// Remove trailing spaces but preserve the comma structure
default_macro_signature = default_macro_signature.trim()
return isCommaTerminated
? default_macro_signature
: default_macro_signature.slice(0, -1)
}
/**
* Given the attributes of a zcl attribute. Creates an attribute mask based on
* the given options
* Available options:
* isClusterCodeMfgSpecific: 0/1, This is to determine if cluster code needs to
* be used to determine if a cluster is mfg specific or not.
* @param writable
* @param storageOption
* @param minMax
* @param mfgSpecific
* @param clusterCode
* @param client
* @param isSingleton
* @param prefixString
* @param postfixString
* @returns attribute mask based on given values
*/
async function attribute_mask(
writable,
storageOption,
minMax,
mfgSpecific,
clusterCode,
client,
isSingleton,
prefixString,
postfixString,
options
) {
let isClusterCodeMfgSpecific =
options && 'isClusterCodeMfgSpecific' in options.hash
? options.hash.isClusterCodeMfgSpecific
: false
let attributeMask = ''
// mask for isWritable
if (writable) {
attributeMask +=
(attributeMask ? '| ' : '') + prefixString + 'WRITABLE' + postfixString
}
// mask for storage option
if (storageOption === 'NVM') {
attributeMask +=
(attributeMask ? '| ' : '') + prefixString + 'TOKENIZE' + postfixString
} else if (storageOption === 'External') {
attributeMask +=
(attributeMask ? '| ' : '') +
prefixString +
'EXTERNAL_STORAGE' +
postfixString
}
// mask for bound
if (minMax) {
attributeMask +=
(attributeMask ? '| ' : '') + prefixString + 'MIN_MAX' + postfixString
}
// mask for manufacturing specific attributes
if (mfgSpecific || (isClusterCodeMfgSpecific && clusterCode < 64512)) {
attributeMask +=
(attributeMask ? '| ' : '') +
prefixString +
'MANUFACTURER_SPECIFIC' +
postfixString
}
// mask for client side attribute
if (client === 'client') {
attributeMask +=
(attributeMask ? '| ' : '') + prefixString + 'CLIENT' + postfixString
}
//mask for singleton attirbute
if (isSingleton) {
attributeMask +=
(attributeMask ? '| ' : '') + prefixString + 'SINGLETON' + postfixString
}
if (!attributeMask) {
attributeMask = '0x00'
}
return attributeMask
}
/**
* Given the attributes of a zcl command. Creates a command mask based on
* the given options
* @param commmandSource
* @param clusterSide
* @param isIncomingEnabled
* @param isOutgoingEnabled
* @param manufacturingCode
* @param prefixForMask
* @returns command mask based on given values
*/
async function command_mask(
commmandSource,
clusterSide,
isIncomingEnabled,
isOutgoingEnabled,
manufacturingCode,
prefixForMask
) {
let commandMask = ''
if (isClient(commmandSource)) {
if (
(isIncomingEnabled && commmandSource != clusterSide) ||
(isIncomingEnabled && clusterSide == 'either')
) {
commandMask += command_mask_sub_helper(
commandMask,
prefixForMask + 'INCOMING_SERVER'
)
}
if (
(isOutgoingEnabled && commmandSource == clusterSide) ||
(isOutgoingEnabled && clusterSide == 'either')
) {
commandMask += command_mask_sub_helper(
commandMask,
prefixForMask + 'OUTGOING_CLIENT'
)
}
} else {
if (
(isIncomingEnabled && commmandSource != clusterSide) ||
(isIncomingEnabled && clusterSide == 'either')
) {
commandMask += command_mask_sub_helper(
commandMask,
prefixForMask + 'INCOMING_CLIENT'
)
}
if (
(isOutgoingEnabled && commmandSource == clusterSide) ||
(isOutgoingEnabled && clusterSide == 'either')
) {
commandMask += command_mask_sub_helper(
commandMask,
prefixForMask + 'OUTGOING_SERVER'
)
}
}
if (manufacturingCode && commandMask) {
commandMask += command_mask_sub_helper(
commandMask,
prefixForMask + 'MANUFACTURER_SPECIFIC'
)
}
return commandMask
}
/**
* A Sub helper api for command_mask to reduce code redundancy
* @param commandMask
* @param str
* @returns command mask addition based on the arguments
*/
function command_mask_sub_helper(commandMask, str) {
if (commandMask) {
return ' | ' + str
} else {
return str
}
}
/**
* This may be used within all_user_cluster_attributes_for_generated_defaults
* for example:
* {{format_zcl_string_as_characters_for_generated_defaults 'abc' 5}}
* will return as follows:
* 3, 'a', 'b', 'c' 0, 0
*
* Available Options:
* - isOctet: 0/1 can be used to return results correctly for octet strings
* - isCommaTerminated: 0/1 can be used to return result with/without ',' at
* the end
* @param stringVal
* @param sizeOfString
* @returns Formatted string for generated defaults starting with the lenth of a
* string then each character and then filler for the size allocated for the
* string. Long strings prefixed by 2 byte length field.
*/
async function format_zcl_string_as_characters_for_generated_defaults(
stringVal,
sizeOfString,
options
) {
let isOctet = 'isOctet' in options.hash ? options.hash.isOctet : false
let isCommaTerminated =
'isCommaTerminated' in options.hash ? options.hash.isCommaTerminated : true
let lengthOfString = types.convertIntToBigEndian(
stringVal.length,
sizeOfString < 255 ? 1 : 2
)
lengthOfString = lengthOfString.replace('0x', '').match(/.{1,2}/g)
let lengthPrefix = ''
for (let m of lengthOfString) {
lengthPrefix += ' 0x' + m.toUpperCase() + ','
}
// Switching length to little endian by default.
lengthPrefix = lengthPrefix.split(' ').reverse().join(' ')
let formatted_string = lengthPrefix
for (let i = 0; i < stringVal.length; i++) {
formatted_string +=
(!isOctet
? "'" + stringVal.charAt(i) + "'"
: '0x' + stringVal.charCodeAt(i).toString(16)) + ', '
}
for (let i = stringVal.length + 1; i < sizeOfString; i++) {
formatted_string += '0' + ', '
}
return isCommaTerminated
? formatted_string
: formatted_string.trim().slice(0, -1)
}
/**
* Given a zcl data type return the min allowed value for that zcl data type
* based on the language specified in the options
* @param {*} type
* @param {*} options
* @returns max allowed value for the given zcl data type
* Available Options:
* - language: determines the output of the helper based on language
* for eg: (as_type_min_value language='c++') will give the output specific to
* the c++ language.
* Note: If language is not specified then helper throws an error.
*/
async function as_type_min_value(type, options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let signAndSize = await types.getSignAndSizeOfZclType(
this.global.db,
type,
packageIds,
{
size: 'bits'
}
)
let isTypeSigned = signAndSize.isTypeSigned
let dataTypesize = signAndSize.dataTypesize
let dataType = signAndSize.dataType
// Returning min value based on c++ language
if (options.hash.language == 'c++') {
if (!isTypeSigned) {
return 0
} else {
// Exceptions: single and double zcl types
if (dataType && dataType.name == 'single') {
return `-std::numeric_limits<float>::infinity()`
} else if (dataType && dataType.name == 'double') {
return `-std::numeric_limits<double>::infinity()`
} else {
return 'INT' + dataTypesize + '_MIN'
}
}
} else {
throw new Error(
'Minimum value cannot be determined by as_type_min_value \
due to no language option specified in the template'
)
}
}
/**
* Given a zcl data type return the max allowed value for that zcl data type
* based on the language specified in the options
* @param {*} type
* @param {*} options
* @returns max allowed value for the given zcl data type
* Available Options:
* - language: determines the output of the helper based on language
* for eg: (as_type_max_value language='c++') will give the output specific to
* the c++ language.
* Note: If language is not specified then the helper returns size of type in
* bits.
*/
async function as_type_max_value(type, options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let signAndSize = await types.getSignAndSizeOfZclType(
this.global.db,
type,
packageIds,
{
size: 'bits'
}
)
let isTypeSigned = signAndSize.isTypeSigned
let dataTypesize = signAndSize.dataTypesize
let dataType = signAndSize.dataType
// Returning max value based on c++ language
if (options.hash.language == 'c++') {
if (!isTypeSigned) {
// Exceptions: boolean type
if (dataType && dataType.name == 'boolean') {
return `1`
} else {
return 'UINT' + dataTypesize + '_MAX'
}
} else {
// Exceptions: single and double zcl types
if (dataType && dataType.name == 'single') {
return `std::numeric_limits<float>::infinity()`
} else if (dataType && dataType.name == 'double') {
return `std::numeric_limits<double>::infinity()`
} else {
return 'INT' + dataTypesize + '_MAX'
}
}
} else {
throw new Error(
'Maximum value cannot be determined by as_type_max_value \
due to no language option specified in the template'
)
}
}
/**
* Returns all structs which have clusters associated with them
* @param {*} options
* Available Options:
* - groupByStructName: Can group the query results based on struct name for
* structs which are present in more than one cluster
* eg Usage:
* {{#structs_with_clusters groupByStructName=1}}{{/structs_with_clusters}}
*/
async function structs_with_clusters(options) {
const packageIds = await templateUtil.ensureZclPackageIds(this)
let structs = await queryZcl.selectStructsWithClusterAssociation(
this.global.db,
packageIds,
options.hash.groupByStructName
)
let promise = templateUtil.collectBlocks(structs, options, this)
return templateUtil.templatePromise(this.global, promise)
}
/**
* Returns the size of the zcl type if possible else returns -1
* @param {*} type
* @param {*} options
* @returns size of zcl type
*/
async function as_zcl_type_size(type, options) {
let packageIds = await templateUtil.ensureZclPackageIds(this)
let signAndSize = await types.getSignAndSizeOfZclType(
this.global.db,
type,
packageIds
)
let dataTypesize = signAndSize.dataTypesize
if (dataTypesize != 0) {
return dataTypesize
} else {
return -1
}
}
/**
* An if helper for comparisons
* @param {*} leftValue
* @param {*} rightValue
* @param {*} options
* @returns Promise of content
* example: checking if (4 < 5)
* (if_compare 4 5 operator='<')
* Content when comparison returns true
* {{else}}
* Content when comparison returns false
* (/if_compare)
*/
function if_compare(leftValue, rightValue, options) {
if (arguments.length != 3) {
throw new Error(
'if_compare needs left value, right value and a comparison operator under operator='
)
}
let result = false
// List of valid operations
switch (options.hash.operator) {
case '==':
result = leftValue == rightValue
break
case '===':
result = leftValue === rightValue
break
case '!=':
result = leftValue != rightValue
break
case '<':
result = leftValue < rightValue
break
case '>':
result = leftValue > rightValue
break
case '<=':
result = leftValue <= rightValue
break
case '>=':
result = leftValue >= rightValue
break
case 'in':
result = rightValue.includes(leftValue)
break
default:
throw new Error(
'if_compare does not recognize the operator: ' + options.hash.operator
)
}
if (result) {
return options.fn(this)
} else {
return options.inverse(this)
}
}
/**
* Check if the given type is signed or not based on the type name and cluster
* id.
* Note: This helper needs to be used under a block helper which has a
* reference to clusterId.
* @param {*} type
* @param {*} clusterId
* @param {*} options
* @returns Promise of content
*/
async function if_is_data_type_signed(type, clusterId, options) {
// Get ZCL Data Type from the db
const packageIds = await templateUtil.ensureZclPackageIds(this)
let dataType = await queryZcl.selectDataTypeByNameAndClusterId(
this.global.db,
type,
clusterId,
packageIds
)
let sizeAndSign = await zclUtil.zcl_data_type_size_and_sign(
type,
dataType,
clusterId,
packageIds,
this
)
if (sizeAndSign.isSigned) {
return options.fn(this)
} else {
return options.inverse(this)
}
}
/**
* Fetches the size of the data type based on type name and cluster id given
* Note:
* - Size is zero for structs
* - This helper needs to be used under a block helper which has a
* reference to clusterId.
* Available Options:
* - roundUpToPowerOfTwo: Rounds the size up to the nearest power of 2
* - sizeIn: By default size is returned in bytes but it can be returned in bits
* by mentioning sizeIn="bits"
* @param {*} type
* @param {*} clusterId
* @param {*} options
* @returns size of the data type
*/
async function as_zcl_data_type_size(type, clusterId, options) {
let hash = options.hash
let sizeMultiple = 1
let result = 0
if (hash && hash.sizeIn == 'bits') {
sizeMultiple = 8
}
// Get ZCL Data Type from the db
const packageIds = await templateUtil.ensureZclPackageIds(this)
let dataType = await queryZcl.selectDataTypeByNameAndClusterId(
this.global.db,
type,
clusterId,
packageIds
)
let sizeAndSign = await zclUtil.zcl_data_type_size_and_sign(
type,
dataType,
clusterId,
packageIds,
this
)
result = sizeAndSign.size
result =
hash && hash.roundUpToPowerOfTwo
? Math.pow(2, Math.ceil(Math.log2(result)))
: result
return result * sizeMultiple
}
const dep = templateUtil.deprecatedHelper
// WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
//
// Note: these exports are public API. Templates that might have been created in the past and are
// available in the wild might depend on these names.
// If you rename the functions, you need to still maintain old exports list.
exports.zcl_bitmaps = zcl_bitmaps
exports.zcl_bitmap_items = zcl_bitmap_items
exports.zcl_enums = zcl_enums
exports.zcl_enum_items = zcl_enum_items
exports.zcl_structs = zcl_structs
exports.zcl_struct_items = zcl_struct_items
exports.zcl_struct_items_by_struct_name = zcl_struct_items_by_struct_name
exports.zcl_clusters = zcl_clusters
exports.zcl_device_types = zcl_device_types
exports.zcl_device_type_clusters = zcl_device_type_clusters
exports.zcl_device_type_cluster_commands = zcl_device_type_cluster_commands
exports.zcl_device_type_cluster_attributes = zcl_device_type_cluster_attributes
exports.zcl_commands = zcl_commands
exports.zcl_commands_source_client = zcl_commands_source_client
exports.zcl_commands_source_server = zcl_commands_source_server
exports.zcl_events = zcl_events
exports.zcl_event_fields = zcl_event_fields
exports.zcl_command_tree = zcl_command_tree
exports.zcl_attributes = zcl_attributes
exports.zcl_attributes_client = zcl_attributes_client
exports.zcl_attributes_server = zcl_attributes_server
exports.zcl_atomics = zcl_atomics
exports.zcl_global_commands = zcl_global_commands
exports.zcl_cluster_largest_label_length = zcl_cluster_largest_label_length
exports.zcl_command_arguments_count = zcl_command_arguments_count
exports.zcl_command_arguments = zcl_command_arguments
exports.zcl_command_argument_data_type = zcl_command_argument_data_type
exports.is_client = isClient
exports.isClient = dep(isClient, { to: 'is_client' })
exports.is_server = isServer
exports.isServer = dep(isServer, { to: 'is_server' })
exports.is_str_equal = isStrEqual
exports.isStrEqual = dep(isStrEqual, { to: 'is_str_equal' })
exports.is_last_element = isLastElement
exports.isLastElement = dep(isLastElement, {
to: 'is_last_element'
})
exports.is_first_element = isFirstElement
exports.isFirstElement = dep(isFirstElement, {
to: 'is_first_element'
})
exports.is_enabled = isEnabled
exports.isEnabled = dep(isEnabled, { to: 'is_enabled' })
exports.is_command_available = isCommandAvailable
exports.isCommandAvailable = dep(isCommandAvailable, {
to: 'is_command_available'
})
exports.as_underlying_zcl_type = asUnderlyingZclType
exports.asUnderlyingZclType = dep(asUnderlyingZclType, {
to: 'as_underlying_zcl_type'
})
exports.if_is_bitmap = if_is_bitmap
exports.if_is_enum = if_is_enum
exports.is_bitmap = zclUtil.isBitmap
exports.isBitmap = dep(zclUtil.isBitmap, { to: 'is_bitmap' })
exports.is_struct = zclUtil.isStruct
exports.isStruct = dep(zclUtil.isStruct, { to: 'is_struct' })
exports.is_enum = zclUtil.isEnum
exports.isEnum = dep(zclUtil.isEnum, { to: 'is_enum' })
exports.is_event = zclUtil.isEvent
exports.isEvent = dep(zclUtil.isEvent, { to: 'is_event' })
exports.if_manufacturing_specific_cluster = dep(
if_manufacturing_specific_cluster,
{ to: 'if_mfg_specific_cluster' }
)
exports.zcl_string_type_return = zcl_string_type_return
exports.is_zcl_string = is_zcl_string
exports.if_command_arguments_have_fixed_length =
if_command_arguments_have_fixed_length
exports.command_arguments_total_length = command_arguments_total_length
exports.as_underlying_zcl_type_if_command_is_not_fixed_length =
as_underlying_zcl_type_if_command_is_not_fixed_length
exports.if_command_argument_always_present = dep(
if_command_argument_always_present,
{
to: 'if_command_is_not_fixed_length_but_command_argument_is_always_present'
}
)
exports.as_underlying_zcl_type_command_argument_always_present = dep(
as_underlying_zcl_type_command_argument_always_present,
{
to: 'as_underlying_zcl_type_command_is_not_fixed_length_but_command_argument_is_always_present'
}
)
exports.if_command_argument_always_present_with_presentif = dep(
if_command_argument_always_present_with_presentif,
{ to: 'if_ca_always_present_with_presentif' }
)
exports.as_underlying_zcl_type_command_argument_always_present_with_presentif =
dep(as_underlying_zcl_type_command_argument_always_present_with_presentif, {
to: 'as_underlying_zcl_type_ca_always_present_with_presentif'
})
exports.if_command_argument_not_always_present_with_presentif = dep(
if_command_argument_not_always_present_with_presentif,
{ to: 'if_ca_not_always_present_with_presentif' }
)
exports.as_underlying_zcl_type_command_argument_not_always_present_with_presentif =
dep(
as_underlying_zcl_type_command_argument_not_always_present_with_presentif,
{ to: 'as_underlying_zcl_type_ca_not_always_present_with_presentif' }
)
exports.if_command_argument_not_always_present_no_presentif = dep(
if_command_argument_not_always_present_no_presentif,
{ to: 'if_ca_not_always_present_no_presentif' }
)
exports.as_underlying_zcl_type_command_argument_not_always_present_no_presentif =
dep(as_underlying_zcl_type_command_argument_not_always_present_no_presentif, {
to: 'as_underlying_zcl_type_ca_not_always_present_no_presentif'
})
exports.as_generated_default_macro = as_generated_default_macro
exports.attribute_mask = attribute_mask
exports.command_mask = command_mask
exports.format_zcl_string_as_characters_for_generated_defaults =
format_zcl_string_as_characters_for_generated_defaults
exports.as_underlying_zcl_type_command_is_not_fixed_length_but_command_argument_is_always_present =
dep(
as_underlying_zcl_type_command_is_not_fixed_length_but_command_argument_is_always_present,
'as_underlying_zcl_type_command_is_not_fixed_length_but_command_argument_is_always_present has been deprecated. Use as_underlying_zcl_type and if_command_not_fixed_length_command_argument_always_present instead'
)
exports.as_underlying_zcl_type_ca_not_always_present_no_presentif = dep(
as_underlying_zcl_type_ca_not_always_present_no_presentif,
'as_underlying_zcl_type_ca_not_always_present_no_presentif has been deprecated. Use as_underlying_zcl_type and if_command_arg_not_always_present_no_presentif instead'
)
exports.as_underlying_zcl_type_ca_not_always_present_with_presentif = dep(
as_underlying_zcl_type_ca_not_always_present_with_presentif,
'as_underlying_zcl_type_ca_not_always_present_with_presentif has been deprecated. Use as_underlying_zcl_type and if_command_arg_not_always_present_with_presentif instead'
)
exports.as_underlying_zcl_type_ca_always_present_with_presentif = dep(
as_underlying_zcl_type_ca_always_present_with_presentif,
'as_underlying_zcl_type_ca_always_present_with_presentif has been deprecated. Use as_underlying_zcl_type and if_command_arg_always_present_with_presentif instead.'
)
exports.if_is_struct = if_is_struct
exports.if_mfg_specific_cluster = if_mfg_specific_cluster
exports.first_unused_enum_value = first_unused_enum_value
exports.zcl_commands_with_cluster_info = zcl_commands_with_cluster_info
exports.zcl_commands_with_arguments = zcl_commands_with_arguments
exports.if_is_string = if_is_string
exports.if_is_atomic = if_is_atomic
exports.if_is_number = if_is_number
exports.if_is_char_string = if_is_char_string
exports.if_is_octet_string = if_is_octet_string
exports.if_is_short_string = if_is_short_string
exports.if_is_long_string = if_is_long_string
exports.structs_with_clusters = structs_with_clusters
exports.as_type_max_value = as_type_max_value
exports.as_type_min_value = as_type_min_value
exports.as_zcl_type_size = as_zcl_type_size
exports.if_compare = if_compare
exports.if_is_data_type_signed = if_is_data_type_signed
exports.as_zcl_data_type_size = as_zcl_data_type_size
exports.zcl_command_responses = zcl_command_responses
exports.zcl_struct_items_by_struct_and_cluster_name =
zcl_struct_items_by_struct_and_cluster_name