zigbee-herdsman
Version:
An open source ZigBee gateway solution with node.js.
289 lines • 10 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDataTypeClass = getDataTypeClass;
exports.getCluster = getCluster;
exports.getGlobalCommand = getGlobalCommand;
exports.isClusterName = isClusterName;
exports.getFoundationCommand = getFoundationCommand;
exports.isFoundationDiscoverRsp = isFoundationDiscoverRsp;
const cluster_1 = require("./definition/cluster");
const enums_1 = require("./definition/enums");
const foundation_1 = require("./definition/foundation");
const DATA_TYPE_CLASS_DISCRETE = [
enums_1.DataType.DATA8,
enums_1.DataType.DATA16,
enums_1.DataType.DATA24,
enums_1.DataType.DATA32,
enums_1.DataType.DATA40,
enums_1.DataType.DATA48,
enums_1.DataType.DATA56,
enums_1.DataType.DATA64,
enums_1.DataType.BOOLEAN,
enums_1.DataType.BITMAP8,
enums_1.DataType.BITMAP16,
enums_1.DataType.BITMAP24,
enums_1.DataType.BITMAP32,
enums_1.DataType.BITMAP40,
enums_1.DataType.BITMAP48,
enums_1.DataType.BITMAP56,
enums_1.DataType.BITMAP64,
enums_1.DataType.ENUM8,
enums_1.DataType.ENUM16,
enums_1.DataType.OCTET_STR,
enums_1.DataType.CHAR_STR,
enums_1.DataType.LONG_OCTET_STR,
enums_1.DataType.LONG_CHAR_STR,
enums_1.DataType.ARRAY,
enums_1.DataType.STRUCT,
enums_1.DataType.SET,
enums_1.DataType.BAG,
enums_1.DataType.CLUSTER_ID,
enums_1.DataType.ATTR_ID,
enums_1.DataType.BAC_OID,
enums_1.DataType.IEEE_ADDR,
enums_1.DataType.SEC_KEY,
];
const DATA_TYPE_CLASS_ANALOG = [
enums_1.DataType.UINT8,
enums_1.DataType.UINT16,
enums_1.DataType.UINT24,
enums_1.DataType.UINT32,
enums_1.DataType.UINT40,
enums_1.DataType.UINT48,
enums_1.DataType.UINT56,
enums_1.DataType.INT8,
enums_1.DataType.INT16,
enums_1.DataType.INT24,
enums_1.DataType.INT32,
enums_1.DataType.INT40,
enums_1.DataType.INT48,
enums_1.DataType.INT56,
enums_1.DataType.SEMI_PREC,
enums_1.DataType.SINGLE_PREC,
enums_1.DataType.DOUBLE_PREC,
enums_1.DataType.TOD,
enums_1.DataType.DATE,
enums_1.DataType.UTC,
];
const FOUNDATION_DISCOVER_RSP_IDS = [
foundation_1.Foundation.discoverRsp.ID,
foundation_1.Foundation.discoverCommandsRsp.ID,
foundation_1.Foundation.discoverCommandsGenRsp.ID,
foundation_1.Foundation.discoverExtRsp.ID,
];
function getDataTypeClass(dataType) {
if (DATA_TYPE_CLASS_DISCRETE.includes(dataType)) {
return enums_1.DataTypeClass.DISCRETE;
}
if (DATA_TYPE_CLASS_ANALOG.includes(dataType)) {
return enums_1.DataTypeClass.ANALOG;
}
throw new Error(`Don't know value type for '${enums_1.DataType[dataType]}'`);
}
function hasCustomClusters(customClusters) {
// XXX: was there a good reason to not set the parameter `customClusters` optional? it would allow simple undefined check
// below is twice faster than checking `Object.keys(customClusters).length`
// biome-ignore lint/style/useNamingConvention: not working properly
for (const _k in customClusters)
return true;
return false;
}
function findClusterNameByID(id, manufacturerCode, clusters) {
let name;
// if manufacturer code is given, consider partial match if didn't match against manufacturer code
let partialMatch = Boolean(manufacturerCode);
for (const clusterName in clusters) {
const cluster = clusters[clusterName];
if (cluster.ID === id) {
// priority on first match when matching only ID
if (name === undefined) {
name = clusterName;
}
if (manufacturerCode && cluster.manufacturerCode === manufacturerCode) {
name = clusterName;
partialMatch = false;
break;
}
if (!cluster.manufacturerCode) {
name = clusterName;
break;
}
}
}
return [name, partialMatch];
}
function getClusterDefinition(key, manufacturerCode, customClusters) {
let name;
if (typeof key === "number") {
let partialMatch;
// custom clusters have priority over Zcl clusters, except in case of better match (see below)
[name, partialMatch] = findClusterNameByID(key, manufacturerCode, customClusters);
if (!name) {
[name, partialMatch] = findClusterNameByID(key, manufacturerCode, cluster_1.Clusters);
}
else if (partialMatch) {
let zclName;
[zclName, partialMatch] = findClusterNameByID(key, manufacturerCode, cluster_1.Clusters);
// Zcl clusters contain a better match, use that one
if (zclName !== undefined && !partialMatch) {
name = zclName;
}
}
}
else {
name = key;
}
let cluster = name !== undefined && hasCustomClusters(customClusters)
? {
...cluster_1.Clusters[name],
...customClusters[name], // should override Zcl clusters
}
: cluster_1.Clusters[name];
if (!cluster) {
if (typeof key === "number") {
name = key.toString();
cluster = { attributes: {}, commands: {}, commandsResponse: {}, manufacturerCode: undefined, ID: key };
}
else {
name = undefined;
}
}
if (!name) {
throw new Error(`Cluster with name '${key}' does not exist`);
}
return { name, cluster };
}
function createCluster(name, cluster, manufacturerCode) {
const attributes = Object.assign({}, ...Object.entries(cluster.attributes).map(([k, v]) => ({ [k]: { ...v, name: k } })));
const commands = Object.assign({}, ...Object.entries(cluster.commands).map(([k, v]) => ({ [k]: { ...v, name: k } })));
const commandsResponse = Object.assign({}, ...Object.entries(cluster.commandsResponse).map(([k, v]) => ({ [k]: { ...v, name: k } })));
const getAttributeInternal = (key) => {
if (typeof key === "number") {
let partialMatchAttr;
for (const attrKey in attributes) {
const attr = attributes[attrKey];
if (attr.ID === key) {
if (manufacturerCode && attr.manufacturerCode === manufacturerCode) {
return attr;
}
if (attr.manufacturerCode === undefined) {
partialMatchAttr = attr;
}
}
}
return partialMatchAttr;
}
for (const attrKey in attributes) {
const attr = attributes[attrKey];
if (attr.name === key) {
return attr;
}
}
return undefined;
};
const getAttribute = (key) => {
const result = getAttributeInternal(key);
if (!result) {
throw new Error(`Cluster '${name}' has no attribute '${key}'`);
}
return result;
};
const hasAttribute = (key) => {
const result = getAttributeInternal(key);
return !!result;
};
const getCommand = (key) => {
if (typeof key === "number") {
for (const cmdKey in commands) {
const cmd = commands[cmdKey];
if (cmd.ID === key) {
return cmd;
}
}
}
else {
for (const cmdKey in commands) {
const cmd = commands[cmdKey];
if (cmd.name === key) {
return cmd;
}
}
}
throw new Error(`Cluster '${name}' has no command '${key}'`);
};
const getCommandResponse = (key) => {
if (typeof key === "number") {
for (const cmdKey in commandsResponse) {
const cmd = commandsResponse[cmdKey];
if (cmd.ID === key) {
return cmd;
}
}
}
else {
for (const cmdKey in commandsResponse) {
const cmd = commandsResponse[cmdKey];
if (cmd.name === key) {
return cmd;
}
}
}
throw new Error(`Cluster '${name}' has no command response '${key}'`);
};
return {
ID: cluster.ID,
attributes,
manufacturerCode: cluster.manufacturerCode,
name,
commands,
commandsResponse,
getAttribute,
hasAttribute,
getCommand,
getCommandResponse,
};
}
function getCluster(key, manufacturerCode, customClusters) {
const { name, cluster } = getClusterDefinition(key, manufacturerCode, customClusters);
return createCluster(name, cluster, manufacturerCode);
}
function getGlobalCommandNameById(id) {
for (const commandName in foundation_1.Foundation) {
if (foundation_1.Foundation[commandName].ID === id) {
return commandName;
}
}
throw new Error(`Global command with id '${id}' does not exist.`);
}
function getGlobalCommand(key) {
const name = typeof key === "number" ? getGlobalCommandNameById(key) : key;
const command = foundation_1.Foundation[name];
if (!command) {
throw new Error(`Global command with key '${key}' does not exist`);
}
const result = {
ID: command.ID,
name,
parameters: command.parameters,
};
if (command.response !== undefined) {
result.response = command.response;
}
return result;
}
function isClusterName(name) {
return name in cluster_1.Clusters;
}
function getFoundationCommand(id) {
for (const commandName in foundation_1.Foundation) {
const command = foundation_1.Foundation[commandName];
if (command.ID === id) {
return command;
}
}
throw new Error(`Foundation command '${id}' does not exist.`);
}
function isFoundationDiscoverRsp(id) {
return FOUNDATION_DISCOVER_RSP_IDS.includes(id);
}
//# sourceMappingURL=utils.js.map
;