ts-bacnet
Version:
The BACnet protocol library written in pure JavaScript.
566 lines (555 loc) • 24.8 kB
JavaScript
;
//@ts-nocheck
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseValue = void 0;
/**
* This script will discover all devices in the network and read out all
* properties and deliver a JSON as device description
*
* If a deviceId is given as first parameter then only this device is discovered
*/
const process_1 = __importDefault(require("process"));
const __1 = require("..");
const EngineeringUnitHumanReadable_1 = require("./EngineeringUnitHumanReadable");
// Map the Property types to their enums/bitstrings
const PropertyIdentifierToEnumMap = {};
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.OBJECT_TYPE] =
__1.enums.ObjectType;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.SEGMENTATION_SUPPORTED] =
__1.enums.Segmentation;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.EVENT_STATE] =
__1.enums.EventState;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.UNITS] =
__1.enums.EngineeringUnits;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.RELIABILITY] =
__1.enums.Reliability;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.NOTIFY_TYPE] =
__1.enums.NotifyType;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.POLARITY] = __1.enums.Polarity;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.PROTOCOL_SERVICES_SUPPORTED] = __1.enums.ServicesSupported;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.PROTOCOL_OBJECT_TYPES_SUPPORTED] = __1.enums.ObjectTypesSupported;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.STATUS_FLAGS] =
__1.enums.StatusFlags;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.LIMIT_ENABLE] =
__1.enums.LimitEnable;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.EVENT_ENABLE] =
__1.enums.EventTransitionBits;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.ACKED_TRANSITIONS] =
__1.enums.EventTransitionBits;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.SYSTEM_STATUS] =
__1.enums.DeviceStatus;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.SYSTEM_STATUS] =
__1.enums.DeviceStatus;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.ACK_REQUIRED] =
__1.enums.EventTransitionBits;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.LOGGING_TYPE] =
__1.enums.LoggingType;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.FILE_ACCESS_METHOD] =
__1.enums.FileAccessMethod;
PropertyIdentifierToEnumMap[__1.enums.PropertyIdentifier.NODE_TYPE] =
__1.enums.NodeType;
// Sometimes the Map needs to be more specific
const ObjectTypeSpecificPropertyIdentifierToEnumMap = {};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_INPUT] =
{};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_INPUT][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.BinaryPV;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_INPUT][__1.enums.PropertyIdentifier.MODE] = __1.enums.BinaryPV;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.ANALOG_INPUT] =
{};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.ANALOG_INPUT][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.BinaryPV; //????
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.ANALOG_OUTPUT] =
{};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.ANALOG_OUTPUT][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.BinaryPV; //????
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_OUTPUT] =
{};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_OUTPUT][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.BinaryPV;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_OUTPUT][__1.enums.PropertyIdentifier.RELINQUISH_DEFAULT] = __1.enums.BinaryPV;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_VALUE] =
{};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_VALUE][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.BinaryPV;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_VALUE][__1.enums.PropertyIdentifier.RELINQUISH_DEFAULT] = __1.enums.BinaryPV;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_LIGHTING_OUTPUT] = {};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_LIGHTING_OUTPUT][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.BinaryLightingPV;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BITSTRING_VALUE] = {};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.BINARY_VALUE][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.BinaryPV; // ???
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_POINT] = {};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_POINT][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.LifeSafetyState;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_POINT][__1.enums.PropertyIdentifier.TRACKING_VALUE] = __1.enums.LifeSafetyState;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_POINT][__1.enums.PropertyIdentifier.MODE] = __1.enums.LifeSafetyMode;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_POINT][__1.enums.PropertyIdentifier.ACCEPTED_MODES] = __1.enums.LifeSafetyMode;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_POINT][__1.enums.PropertyIdentifier.SILENCED] = __1.enums.LifeSafetyState;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_POINT][__1.enums.PropertyIdentifier.OPERATION_EXPECTED] = __1.enums.LifeSafetyOperation;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_ZONE] = {};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_ZONE][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.LifeSafetyState;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LIFE_SAFETY_ZONE][__1.enums.PropertyIdentifier.MODE] = __1.enums.LifeSafetyMode;
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LOAD_CONTROL] =
{};
ObjectTypeSpecificPropertyIdentifierToEnumMap[__1.enums.ObjectType.LOAD_CONTROL][__1.enums.PropertyIdentifier.PRESENT_VALUE] = __1.enums.ShedState;
// For Objects we read out All properties if cli parameter --all is provided
const propSubSet = process_1.default.argv.includes("--all")
? Object.values(__1.enums.PropertyIdentifier)
: [
/* normally supported from all devices */
__1.enums.PropertyIdentifier.OBJECT_IDENTIFIER,
__1.enums.PropertyIdentifier.OBJECT_NAME,
__1.enums.PropertyIdentifier.OBJECT_TYPE,
__1.enums.PropertyIdentifier.PRESENT_VALUE,
__1.enums.PropertyIdentifier.STATUS_FLAGS,
__1.enums.PropertyIdentifier.EVENT_STATE,
__1.enums.PropertyIdentifier.RELIABILITY,
__1.enums.PropertyIdentifier.OUT_OF_SERVICE,
__1.enums.PropertyIdentifier.UNITS,
/* other properties */
__1.enums.PropertyIdentifier.DESCRIPTION,
__1.enums.PropertyIdentifier.SYSTEM_STATUS,
__1.enums.PropertyIdentifier.VENDOR_NAME,
__1.enums.PropertyIdentifier.VENDOR_IDENTIFIER,
__1.enums.PropertyIdentifier.MODEL_NAME,
__1.enums.PropertyIdentifier.FIRMWARE_REVISION,
__1.enums.PropertyIdentifier.APPLICATION_SOFTWARE_VERSION,
__1.enums.PropertyIdentifier.LOCATION,
__1.enums.PropertyIdentifier.LOCAL_DATE,
__1.enums.PropertyIdentifier.LOCAL_TIME,
__1.enums.PropertyIdentifier.UTC_OFFSET,
__1.enums.PropertyIdentifier.DAYLIGHT_SAVINGS_STATUS,
__1.enums.PropertyIdentifier.PROTOCOL_VERSION,
__1.enums.PropertyIdentifier.PROTOCOL_REVISION,
__1.enums.PropertyIdentifier.PROTOCOL_SERVICES_SUPPORTED,
__1.enums.PropertyIdentifier.PROTOCOL_OBJECT_TYPES_SUPPORTED,
__1.enums.PropertyIdentifier.OBJECT_LIST,
__1.enums.PropertyIdentifier.MAX_APDU_LENGTH_ACCEPTED,
__1.enums.PropertyIdentifier.SEGMENTATION_SUPPORTED,
__1.enums.PropertyIdentifier.APDU_TIMEOUT,
__1.enums.PropertyIdentifier.NUMBER_OF_APDU_RETRIES,
__1.enums.PropertyIdentifier.DEVICE_ADDRESS_BINDING,
__1.enums.PropertyIdentifier.DATABASE_REVISION,
__1.enums.PropertyIdentifier.MAX_INFO_FRAMES,
__1.enums.PropertyIdentifier.MAX_MASTER,
__1.enums.PropertyIdentifier.ACTIVE_COV_SUBSCRIPTIONS,
__1.enums.PropertyIdentifier.ACTIVE_COV_MULTIPLE_SUBSCRIPTIONS,
];
const debug = process_1.default.argv.includes("--debug");
/**
* Retrieve all properties manually because ReadPropertyMultiple is not available
* @param address
* @param objectId
* @param callback
* @param propList
* @param result
* @returns {*}
*/
function getAllPropertiesManually(address, objectId, callback, propList, result) {
if (!propList) {
propList = propSubSet.map((x) => x); // Clone the array
}
if (!result) {
result = [];
}
if (!propList.length) {
return callback({
values: [
{
objectId: objectId,
values: result,
},
],
});
}
const prop = propList.shift();
// Read only object-list property
bacnetClient.readProperty(address, objectId, prop, {}, (err, value) => {
if (!err) {
if (debug) {
console.log("Handle value " + prop + ": ", JSON.stringify(value));
}
const objRes = value.property;
objRes.value = value.values;
result.push(objRes);
}
else {
// console.log('Device do not contain object ' + enums.getEnumName(enums.PropertyIdentifier, prop));
}
getAllPropertiesManually(address, objectId, callback, propList, result);
});
}
function readBit(buffer, i, bit) {
return (buffer[i] >> bit) % 2;
}
/**
* sets a bit in a buffer
* @param buffer
* @param i
* @param bit
* @param value
*/
function setBit(buffer, i, bit, value) {
if (value === 0) {
buffer[i] &= ~(1 << bit);
}
else {
buffer[i] |= 1 << bit;
}
}
function handleBitString(buffer, bitsUsed, usedEnum) {
const res = [];
for (let i = 0; i < bitsUsed; i++) {
const bufferIndex = Math.floor(i / 8);
if (readBit(buffer, bufferIndex, i % 8)) {
res.push(__1.enums.getEnumName(usedEnum, i));
}
}
return res;
}
/**
* Parses a property value
* @param address
* @param parentType
* @param value
* @param supportsMultiple
* @param callback
*/
function parseValue(address, objId, parentType, value, supportsMultiple = false, callback) {
let resValue = null;
let error = null;
if (value &&
value.type &&
value.value !== null &&
value.value !== undefined) {
switch (value.type) {
case __1.enums.ApplicationTag.NULL:
// should be null already, but set again
resValue = null;
break;
case __1.enums.ApplicationTag.BOOLEAN:
// convert number to a real boolean
resValue = !!value.value;
break;
case __1.enums.ApplicationTag.UNSIGNED_INTEGER:
case __1.enums.ApplicationTag.SIGNED_INTEGER:
case __1.enums.ApplicationTag.REAL:
case __1.enums.ApplicationTag.DOUBLE:
case __1.enums.ApplicationTag.CHARACTER_STRING:
// datatype should be correct already
resValue = value.value;
break;
case __1.enums.ApplicationTag.DATE:
case __1.enums.ApplicationTag.TIME:
case __1.enums.ApplicationTag.TIMESTAMP:
// datatype should be Date too
// Javascript do not have date/timestamp only
resValue = value.value;
break;
case __1.enums.ApplicationTag.BIT_STRING:
// handle bitstrings specific and more generic
if (ObjectTypeSpecificPropertyIdentifierToEnumMap[parentType] &&
ObjectTypeSpecificPropertyIdentifierToEnumMap[parentType][objId]) {
resValue = handleBitString(value.value.value, value.value.bitsUsed, ObjectTypeSpecificPropertyIdentifierToEnumMap[parentType][objId]);
}
else if (PropertyIdentifierToEnumMap[objId]) {
resValue = handleBitString(value.value.value, value.value.bitsUsed, PropertyIdentifierToEnumMap[objId]);
}
else {
if (parentType !== __1.enums.ObjectType.BITSTRING_VALUE) {
console.log("Unknown value for BIT_STRING type for objId " +
__1.enums.getEnumName(__1.enums.PropertyIdentifier, objId) +
" and parent type " +
__1.enums.getEnumName(__1.enums.ObjectType, parentType));
}
resValue = value.value;
}
break;
case __1.enums.ApplicationTag.ENUMERATED:
// handle enumerations specific and more generic
if (ObjectTypeSpecificPropertyIdentifierToEnumMap[parentType] &&
ObjectTypeSpecificPropertyIdentifierToEnumMap[parentType][objId]) {
resValue = __1.enums.getEnumName(ObjectTypeSpecificPropertyIdentifierToEnumMap[parentType][objId], value.value);
}
else if (PropertyIdentifierToEnumMap[objId]) {
resValue = __1.enums.getEnumName(PropertyIdentifierToEnumMap[objId], value.value);
}
else {
console.log("Unknown value for ENUMERATED type for objId " +
__1.enums.getEnumName(__1.enums.PropertyIdentifier, objId) +
" and parent type " +
__1.enums.getEnumName(__1.enums.ObjectType, parentType));
resValue = value.value;
}
break;
case __1.enums.ApplicationTag.OBJECTIDENTIFIER:
// Look up object identifiers
// Some object identifiers should not be looked up because we end in loops else
if (objId === __1.enums.PropertyIdentifier.OBJECT_IDENTIFIER ||
objId === __1.enums.PropertyIdentifier.STRUCTURED_OBJECT_LIST ||
objId === __1.enums.PropertyIdentifier.SUBORDINATE_LIST) {
resValue = value.value;
}
else if (supportsMultiple) {
const requestArray = [
{
objectId: value.value,
// meaning read all properties
properties: [{ id: 8 }],
},
];
bacnetClient.readPropertyMultiple(address, requestArray, (err, resValue) => {
//console.log(JSON.stringify(value.value) + ': ' + JSON.stringify(resValue));
parseDeviceObject(address, resValue, value.value, true, callback);
});
return;
}
else {
getAllPropertiesManually(address, value.value, (result) => {
parseDeviceObject(address, result, value.value, false, callback);
});
return;
}
break;
case __1.enums.ApplicationTag.OCTET_STRING:
// It is kind of binary data??
resValue = value.value;
break;
case __1.enums.ApplicationTag.ERROR:
// lookup error class and code
error = {
errorClass: __1.enums.getEnumName(__1.enums.ErrorClass, value.value.errorClass),
errorCode: __1.enums.getEnumName(__1.enums.ErrorCode, value.value.errorCode),
};
resValue = null;
break;
case __1.enums.ApplicationTag.OBJECT_PROPERTY_REFERENCE:
case __1.enums.ApplicationTag.DEVICE_OBJECT_PROPERTY_REFERENCE:
case __1.enums.ApplicationTag.DEVICE_OBJECT_REFERENCE:
case __1.enums.ApplicationTag.READ_ACCESS_SPECIFICATION: //???
resValue = value.value;
break;
case __1.enums.ApplicationTag.CONTEXT_SPECIFIC_DECODED:
parseValue(address, parentType, value.value, supportsMultiple, callback);
return;
case __1.enums.ApplicationTag.READ_ACCESS_RESULT: // ????
resValue = value.value;
break;
default:
console.log("unknown type " + value.type + ": " + JSON.stringify(value));
resValue = value;
}
}
// format unit to human readable string
if (objId === __1.enums.PropertyIdentifier.UNITS) {
const unit = typeof resValue === "string"
? resValue
: resValue && __1.enums.getEnumName(__1.enums.EngineeringUnits, resValue);
const formattedName = unit && (0, EngineeringUnitHumanReadable_1.getEngineeringUnitHumanReadable)(unit);
if (formattedName) {
resValue = formattedName;
}
}
if (objId === Bacnet.enum.PropertyIdentifier.UNITS) {
const unit = typeof resValue === "string"
? resValue
: resValue &&
Bacnet.enum.getEnumName(Bacnet.enum.EngineeringUnits, resValue);
const formattedName = unit && (0, EngineeringUnitHumanReadable_1.getEngineeringUnitHumanReadable)(unit);
if (formattedName) {
resValue = formattedName;
}
}
if (resValue === null || resValue === undefined) {
resValue = " ";
}
setImmediate(() => callback(resValue));
}
exports.parseValue = parseValue;
/**
* Parse an object structure
* @param address
* @param obj
* @param parent
* @param supportsMultiple
* @param callback
*/
function parseDeviceObject(address, obj, parent, supportsMultiple, callback) {
if (debug) {
console.log("START parseDeviceObject: " +
JSON.stringify(parent) +
" : " +
JSON.stringify(obj));
}
if (!obj) {
console.log("object not valid on parse device object");
return;
}
if (!obj.values || !Array.isArray(obj.values)) {
console.log("No device or invalid response");
callback({ ERROR: "No device or invalid response" });
return;
}
let cbCount = 0;
let objDef = {};
const finalize = () => {
// Normalize and remove single item arrays
Object.keys(objDef).forEach((devId) => {
Object.keys(objDef[devId]).forEach((objId) => {
if (objDef[devId][objId].length === 1) {
objDef[devId][objId] = objDef[devId][objId][0];
}
});
});
// If (standard case) only one device was in do not create sub structures)
if (obj.values.length === 1) {
objDef = objDef[obj.values[0].objectId.instance];
}
if (debug) {
console.log("END parseDeviceObject: " +
JSON.stringify(parent) +
" : " +
JSON.stringify(objDef));
}
callback(objDef);
};
obj.values.forEach((devBaseObj) => {
if (!devBaseObj.objectId) {
console.log("No device Id found in object data");
return;
}
if (devBaseObj.objectId.type === undefined ||
devBaseObj.objectId.instance === undefined) {
console.log("No device type or instance found in object data");
return;
}
if (!devBaseObj.values || !Array.isArray(devBaseObj.values)) {
console.log("No device values response");
return;
}
const deviceId = devBaseObj.objectId.instance;
objDef[deviceId] = {};
devBaseObj.values.forEach((devObj) => {
let objId = __1.enums.getEnumName(__1.enums.PropertyIdentifier, devObj.id);
if (devObj.index !== 4294967295) {
objId += "-" + devObj.index;
}
if (debug) {
console.log("Handle Object property:", deviceId, objId, devObj.value);
}
devObj.value.forEach((val) => {
if (JSON.stringify(val.value) === JSON.stringify(parent)) {
// ignore parent object
objDef[deviceId][objId] = objDef[deviceId][objId] || [];
objDef[deviceId][objId].push(val.value);
return;
}
cbCount++;
parseValue(address, parent.type, val, supportsMultiple, (parsedValue) => {
if (debug) {
console.log("RETURN parsedValue", deviceId, objId, devObj.value, parsedValue);
}
objDef[deviceId][objId] = objDef[deviceId][objId] || [];
objDef[deviceId][objId].push(parsedValue);
if (!--cbCount) {
finalize();
}
});
});
});
});
if (cbCount === 0) {
finalize();
}
}
let objectsDone = 0;
/**
* Print result info object
* @param deviceId
* @param obj
*/
function printResultObject(deviceId, obj) {
objectsDone++;
console.log(`Device ${deviceId} (${objectsDone}/${Object.keys(knownDevices).length}) read successfully ...`);
console.log(JSON.stringify(obj));
console.log();
console.log();
if (objectsDone === Object.keys(knownDevices).length) {
setTimeout(() => {
bacnetClient.close();
console.log("closed transport " + Date.now());
}, 1000);
}
}
let limitToDevice = null;
if (process_1.default.argv.length === 3) {
limitToDevice = parseInt(process_1.default.argv[2]);
if (isNaN(limitToDevice)) {
limitToDevice = null;
}
}
/* // create instance of Bacnet
const bacnetClient = new Bacnet({ apduTimeout: 4000, interface: "0.0.0.0" });
// emitted for each new message
bacnetClient.on("message", (msg, rinfo) => {
console.log(msg);
if (rinfo) console.log(rinfo);
});
// emitted on errors
bacnetClient.on("error", (err) => {
console.error(err);
bacnetClient.close();
});
// emmitted when Bacnet server listens for incoming UDP packages
bacnetClient.on("listening", () => {
console.log("sent whoIs " + Date.now());
// discover devices once we are listening
bacnetClient.whoIs();
});
const knownDevices = [];
// emitted when a new device is discovered in the network
bacnetClient.on("iAm", (device) => {
// address object of discovered device,
// just use in subsequent calls that are directed to this device
const address = device.header.sender;
//discovered device ID
const deviceId = device.payload.deviceId;
if (knownDevices.includes(deviceId)) return;
if (limitToDevice !== null && limitToDevice !== deviceId) return;
console.log("Found Device " + deviceId + " on " + JSON.stringify(address));
knownDevices.push(deviceId);
const propertyList = [];
propSubSet.forEach((item) => {
propertyList.push({ id: item });
});
const requestArray = [
{
objectId: { type: 8, instance: deviceId },
properties: propertyList,
},
];
bacnetClient.readPropertyMultiple(address, requestArray, (err, value) => {
if (err) {
console.log(deviceId, "No ReadPropertyMultiple supported:", err.message);
getAllPropertiesManually(
address,
{ type: 8, instance: deviceId },
(result) => {
parseDeviceObject(
address,
result,
{ type: 8, instance: deviceId },
false,
(res) => printResultObject(deviceId, res)
);
}
);
} else {
console.log(deviceId, "ReadPropertyMultiple supported ...");
parseDeviceObject(
address,
value,
{ type: 8, instance: deviceId },
true,
(res) => printResultObject(deviceId, res)
);
}
});
});
*/