iobroker.e3oncan
Version:
Collect data on CAN bus for Viessmann E3 devices, e.g. Vitocal, Vitocharge, Energy Meters E380CA and E3100CB
690 lines (625 loc) • 34.5 kB
JavaScript
'use strict';
/*
* Unveil life data of Viessmann E3 series devices via CAN bus
*
* Based on project open3e: https://github.com/open3e/open3e
*
*/
/*
* Created with @iobroker/create-adapter v2.5.0
*/
// The adapter-core module gives you access to the core ioBroker functions
// you need to create an adapter
const utils = require('@iobroker/adapter-core');
// Loading modules:
const can = require('socketcan');
const storage = require('./lib/storage');
const E3DidsDict = require('./lib/didsE3.json');
const E380DidsDict = require('./lib/didsE380.json');
const E3100CBDidsDict = require('./lib/didsE3100CB.json');
const E3DidsWritable = require('./lib/didsE3Writables.json');
const collect = require('./lib/canCollect');
const uds = require('./lib/canUds');
const udsScan = require('./lib/udsScan');
class E3oncan extends utils.Adapter {
/**
* @param {Partial<utils.AdapterOptions>} [options={}]
*/
constructor(options) {
super({
...options,
name: 'e3oncan',
});
this.stoppingInstance = false; // true during unLoad()
this.e380Collect = null; // E380 always is assigned to external bus
this.e3100cbCollect = null; // E3100CB always is assigned to external bus
this.E3CollectInt = {}; // Dict of collect devices on internal bus
this.E3CollectExt = {}; // Dict of collect devices on external bus
this.collectTimeout = 2000; // Timeout (ms) for collecting data
this.E3UdsWorkers = {}; // Dict of standard uds workers
this.E3UdsSID77Workers = {}; // Dict of uds workers for service 77
this.cntWorkersActive = 0; // Total number of active workers (collect + UDS)
this.channelExt = null;
this.channelExtName = '';
this.channelInt = null;
this.channelIntName = '';
this.cntCanConnDesired = 0; // Number of activated CAN connections in config
this.cntCanConnActual = 0; // Number if actualy connected CAN buses
this.udsWorkers = {};
this.udsTimeout = 7500; // Timeout (ms) for normal UDS communication
this.udsDevices = []; // Confirmed & edited UDS devices
this.udsTimeDelta = 50; // Time delta (ms) between UDS schedules
this.udsTimeoutHandles = [];
this.didsVersionTC = '20240309'; // Change of type of numerical dids to Number at this version
this.udsDidForScan = 256; // Busidentification is in this id
this.udsDidsVarLength = [257,258,259,260,261,262,263,264,265,266]; // Dids have variable length
this.udsScanWorker = new udsScan.udsScan();
this.udsScanDevices = []; // UDS devices found during scan
this.udsDevAddrs = [];
this.udsDevStateNames = [];
this.udsDidsMaxNmbr = 3000; // Max. number of dids per device for scan
//this.on('install', this.onInstall.bind(this));
this.on('ready', this.onReady.bind(this));
this.on('stateChange', this.onStateChange.bind(this));
//this.on('objectChange', this.onObjectChange.bind(this));
this.on('message', this.onMessage.bind(this));
this.on('unload', this.onUnload.bind(this));
}
/**
* Is called when databases are connected and adapter received configuration.
*/
async onReady() {
// Initialize your adapter here
await this.log.info('Startup of instance '+this.namespace+': Starting.');
//await this.log.debug('this.config:');
//await this.log.debug(JSON.stringify(this.config));
// Reset the connection indicator during startup
this.setState('info.connection', false, true);
// Collect known devices adresses:
for (const dev of Object.values(this.config.tableUdsDevices)) {
// @ts-ignore
this.udsDevAddrs.push(dev.devAddr);
// @ts-ignore
this.udsDevStateNames.push(dev.devStateName);
}
// Check for updates of list of datapoints and perform update if needed:
await this.updateDatapointsSpecific(this.config.tableUdsDevices); // UDS devices, specific dids
await this.updateDatapointsCommon(this.config.tableUdsDevices); // UDS devices, common dids
if ('e380Name' in this.config) {
await this.updateDatapointsCommon([{devStateName: this.config.e380Name, device:'e380'}]); // E380 Energy Meter
}
if ('e3100cbName' in this.config) {
await this.updateDatapointsCommon([{devStateName: this.config.e3100cbName, device:'e3100cb'}]); // E3100CB Energy Meter
}
// Setup external CAN bus if required
// ==================================
// @ts-ignore
if (this.config.canExtActivated) {
this.cntCanConnDesired++;
// @ts-ignore
[this.channelExt, this.channelExtName] = await this.connectToCan(this.channelExt, this.config.canExtName, this.onCanMsgExt, this.onCanExtStopped);
}
// Setup internal CAN bus if required
// ==================================
// @ts-ignore
if (this.config.canIntActivated) {
this.cntCanConnDesired++;
// @ts-ignore
[this.channelInt, this.channelIntName] = await this.connectToCan(this.channelInt, this.config.canIntName, this.onCanMsgInt, this.onCanIntStopped);
}
if (this.cntCanConnActual == this.cntCanConnDesired) {
// All configured CAN connections are established
await this.setState('info.connection', true, true);
}
// Setup E380 collect worker:
if (this.channelExt) this.e380Collect = await this.setupE380CollectWorker(this.config);
// Setup E3100CB collect worker:
if (this.channelExt) this.e3100cbCollect = await this.setupE3100cbCollectWorker(this.config);
// Setup all configured devices for collect:
// @ts-ignore
if (this.channelExt) await this.setupE3CollectWorkers(this.config.tableCollectCanExt, this.E3CollectExt, this.channelExt);
// @ts-ignore
if (this.channelInt) await this.setupE3CollectWorkers(this.config.tableCollectCanInt, this.E3CollectInt, this.channelInt);
// Initial setup all configured devices for UDS:
if (this.channelExt) await this.setupUdsWorkers();
this.log.debug('Total number of active workers: '+String(this.cntWorkersActive));
this.log.info('Startup of instance '+this.namespace+': Done.');
}
// Check for updates:
async updateDatapointsCommon(devices) {
// Update list of common datapoints of all devices during startup of adapter
for (const dev of Object.values(devices)) {
let didsDictNew = null;
let didsWritable = null;
switch (dev.device) {
case 'e380':
didsDictNew = E380DidsDict;
didsWritable = {};
break;
case 'e3100cb':
didsDictNew = E3100CBDidsDict;
didsWritable = {};
break;
default:
didsDictNew = E3DidsDict;
didsWritable = E3DidsWritable;
}
const devDids = new storage.storageDids({stateBase:dev.devStateName, device:dev.device});
await devDids.initStates(this, 'standby');
await devDids.readKnownDids(this,'standby');
if (devDids.didsDevSpecAvail) {
if ( (devDids.didsDictDevCom.Version === undefined) ||
(Number(didsDictNew.Version) > Number(devDids.didsDictDevCom.Version)) ) {
this.log.info('Updating common datapoints to version '+didsDictNew.Version+' for device '+dev.devStateName);
for (const did of Object.keys(devDids.didsDictDevCom)) {
if ( (did != 'Version') && (did in didsDictNew) ) {
// Check for changes in datapoint structure
const didStateName = await devDids.getDidStr(did)+'_'+await devDids.didsDictDevCom[did].id;
const devStruct = await devDids.getDidStruct(this,[],devDids.didsDictDevCom[did]);
const E3Struct = await devDids.getDidStruct(this,[],didsDictNew[did]);
if (JSON.stringify(devStruct) != JSON.stringify(E3Struct)) {
// Structure of datapoint has changed
// Replace .json and .tree state(s) based on raw data of did
this.log.info(' > Structure of datapoint '+didStateName+' has changed. Updating.');
// Delete tree states based on old structure:
await this.delObjectAsync(this.namespace+'.'+dev.devStateName+'.tree.'+didStateName, { recursive: true });
const raw = await devDids.getObjectVal(this, dev.devStateName+'.raw.'+didStateName);
if (raw != null) {
// Create states based on new structure if raw data is available:
const cdi = await didsDictNew[did];
const res = await devDids.decodeDid(this, dev.devStateName, did, cdi, devDids.toByteArray(raw));
await devDids.storeObjectJson(this, did, res.idStr, this.namespace+'.'+dev.devStateName+'.json.'+didStateName, res.val);
await devDids.storeObjectTree(this, did, res.idStr, this.namespace+'.'+dev.devStateName+'.tree.'+didStateName, res.val);
}
} else {
// No change of structure of datapoint
// Check for change of data type for numerical values
if (Number(devDids.didsDictDevCom.Version) < Number(this.didsVersionTC)) {
// Make sure, data type and role of tree objects are correct
// Force update of .tree state(s) based on raw data of did
if (this.udsDidsVarLength.includes(Number(did))) {
// Did with variable length has to be deleted to avoid type confilct, when length gets larger in future
this.log.silly(' > Delete datapoint '+didStateName+' to secure change of data type');
await this.delObjectAsync(this.namespace+'.'+dev.devStateName+'.tree.'+didStateName, { recursive: true });
}
const raw = await devDids.getObjectVal(this, dev.devStateName+'.raw.'+didStateName);
if (raw != null) {
// Update .tree states:
this.log.silly(' > Update type and role of datapoint '+didStateName);
const cdi = await didsDictNew[did];
const res = await devDids.decodeDid(this, dev.devStateName, did, cdi, devDids.toByteArray(raw));
await devDids.storeObjectTree(this, did, res.idStr, this.namespace+'.'+dev.devStateName+'.tree.'+didStateName, res.val, true);
}
}
}
// Update datapoint description:
devDids.didsDictDevCom[did] = await didsDictNew[did];
// Update list of writable datapoints:
if ( (did in didsWritable) && (!(did in devDids.didsWritable)) ) {
this.log.silly(' > Add '+didStateName+' to list of writable datapoints');
devDids.didsWritable[did] = await didsWritable[did];
}
}
}
devDids.didsDictDevCom['Version'] = didsDictNew.Version;
}
}
await devDids.storeKnownDids(this);
}
}
async updateDatapointsSpecific(devices) {
// Update list of device-specific datapoints of all devices during startup of adapter
for (const dev of Object.values(devices)) {
const didsDictNew = E3DidsDict;
const devDids = new storage.storageDids({stateBase:dev.devStateName, device:dev.device});
await devDids.initStates(this, 'standby');
await devDids.readKnownDids(this,'standby');
if (devDids.didsDevSpecAvail) {
if ( (devDids.didsDictDevCom.Version === undefined) ||
((Number(didsDictNew.Version) > Number(devDids.didsDictDevCom.Version)) &&
(Number(devDids.didsDictDevCom.Version) < Number(this.didsVersionTC))) ) {
this.log.info('Updating device specific datapoints to version '+didsDictNew.Version+' for device '+dev.devStateName);
for (const did of Object.keys(devDids.didsDictDevSpec)) {
if (did.length <= 4) {
try {
const didNo = Number(did);
// Make sure, data type and role of tree objects are correct
// Force update of .tree state(s) based on raw data of did
const didStateName = await devDids.getDidStr(did)+'_'+await devDids.didsDictDevSpec[did].id;
if (this.udsDidsVarLength.includes(didNo)) {
// Did with variable length has to be deleted to avoid type confilct, when length gets larger in future
this.log.silly(' > Delete datapoint '+didStateName+' to secure change of data type');
await this.delObjectAsync(this.namespace+'.'+dev.devStateName+'.tree.'+didStateName, { recursive: true });
}
const raw = await devDids.getObjectVal(this, dev.devStateName+'.raw.'+didStateName);
if (raw != null) {
// Update .tree states:
this.log.silly(' > Update type and role of datapoint '+didStateName);
const cdi = await devDids.didsDictDevSpec[did];
const res = await devDids.decodeDid(this, dev.devStateName, did, cdi, devDids.toByteArray(raw));
await devDids.storeObjectTree(this, did, res.idStr, this.namespace+'.'+dev.devStateName+'.tree.'+didStateName, res.val, true);
}
} catch {
this.log.warn(' > Could not update did '+did+' because of wrong format (expected a number).');
continue;
}
}
}
}
}
}
}
// Setup CAN busses
async connectToCan(channel, name, onMsg, onStop) {
let chName = name;
if (!channel) {
try {
channel = can.createRawChannel(name, true);
await channel.addListener('onMessage', onMsg, this);
await channel.addListener('onStopped', onStop, this);
await channel.start();
this.cntCanConnActual++;
await this.log.info('CAN-Adapter connected: '+name);
} catch (e) {
await this.log.error(`Could not connect to CAN-Adapter "${name}" - err=${e.message}`);
channel = null;
chName = '';
}
}
return([channel, chName]);
}
disconnectFromCan(channel, name) {
if (channel) {
try {
channel.stop();
this.log.info('CAN-Adapter disconnected: '+name);
channel = null;
} catch (e) {
this.log.error(`Could not disconnect from CAN "${name}" - err=${e.message}`);
channel = null;
}
}
}
// Setup E380 collect worker:
async setupE380CollectWorker(conf) {
let e380Worker = null;
if (conf.e380Active) {
e380Worker = new collect.collect({
'canID': [
0x250,0x251,
0x252,0x253,
0x254,0x255,
0x256,0x257,
0x258,0x259,
0x25A,0x25B,
0x25C,0x25D],
'stateBase': conf.e380Name,
'device': 'e380',
'delay': conf.e380Delay,
'active': conf.e380Active});
await e380Worker.initStates(this,'standby');
}
if (e380Worker) await e380Worker.startup(this);
return e380Worker;
}
// Setup E3100CB collect worker:
async setupE3100cbCollectWorker(conf) {
let e3100cbWorker = null;
if (conf.e3100cbActive) {
e3100cbWorker = new collect.collect({
'canID': [0x569],
'stateBase': conf.e3100cbName,
'device': 'e3100cb',
'delay': conf.e3100cbDelay,
'active': conf.e3100cbActive});
await e3100cbWorker.initStates(this,'standby');
}
if (e3100cbWorker) await e3100cbWorker.startup(this);
return e3100cbWorker;
}
// Setup E3 collect workers:
async setupE3CollectWorkers(conf, workers) {
if ( (conf) && (conf.length > 0) ) {
for (const workerConf of Object.values(conf)) {
if (workerConf.collectActive) {
// @ts-ignore
const devInfo = this.config.tableUdsDevices.filter(item => item.collectCanId == workerConf.collectCanId);
if (devInfo.length > 0) {
const worker = new collect.collect(
{ 'canID' : [Number(workerConf.collectCanId)],
'stateBase': devInfo[0].devStateName,
'device' : 'common',
'timeout' : this.collectTimeout,
'delay' : workerConf.collectDelayTime
});
await worker.initStates(this, 'standby');
if (worker) await worker.startup(this);
workers[Number(workerConf.collectCanId)] = worker;
}
}
}
}
}
// Setup workers for collecting data and for communication via UDS
async setupUdsWorkers() {
// Create an UDS worker for each device
// This is to allow writing of data points even when no schedule for reading is defined
for (const dev of Object.values(this.config.tableUdsDevices)) {
// @ts-ignore
const devTxAddr = Number(dev.devAddr);
const devRxAddr = devTxAddr + 16;
// @ts-ignore
this.log.silly('New UDS worker on '+String(dev.devStateName));
this.E3UdsWorkers[devRxAddr] = new uds.uds(
{ 'canID' : devTxAddr,
// @ts-ignore
'stateBase': dev.devStateName,
'device' : 'common',
'delay' : 0,
'active' : false,
'channel' : this.channelExt,
'timeout' : this.udsTimeout
});
await this.E3UdsWorkers[devRxAddr].initStates(this,'standby');
}
// @ts-ignore
if ( (this.config.tableUdsSchedules) && (this.config.tableUdsSchedules.length > 0) ) {
// @ts-ignore
for (const dev of Object.values(this.config.tableUdsSchedules)) {
if (dev.udsScheduleActive) {
const devTxAddr = Number(dev.udsSelectDevAddr);
const devRxAddr = devTxAddr + 16;
await this.E3UdsWorkers[devRxAddr].addSchedule(this, dev.udsSchedule, dev.udsScheduleDids);
this.log.silly('New Schedule ('+String(dev.udsSchedule)+'s) UDS device on '+String(dev.udsSelectDevAddr));
}
}
}
for (const worker of Object.values(this.E3UdsWorkers)) {
await worker.startup(this,'normal');
this.subscribeStates(this.namespace+'.'+worker.config.stateBase+'.*',this.onStateChange);
await this.udsScanWorker.sleep(this, this.udsTimeDelta);
}
}
/**
* Is called when adapter shuts down - callback has to be called under any circumstances!
* @param {() => void} callback
*/
async onUnload(callback) {
try {
const tStart = new Date().getTime();
this.stoppingInstance = true;
// Stop UDS workers:
for (const toh of Object.values(this.udsTimeoutHandles)) await this.clearTimeout(toh);
for (const worker of Object.values(this.E3UdsWorkers)) await worker.stop(this);
for (const worker of Object.values(this.E3UdsSID77Workers)) await worker.stop(this);
for (const worker of Object.values(this.udsScanWorker.workers)) await worker.stop(this);
// Stop Collect workers:
if (this.e380Collect) await this.e380Collect.stop(this);
if (this.e3100cbCollect) await this.e3100cbCollect.stop(this);
for (const worker of Object.values(this.E3CollectExt)) await worker.stop(this);
for (const worker of Object.values(this.E3CollectInt)) await worker.stop(this);
if (this.cntWorkersActive > 0) {
// Timeout - there are still unstopped workers
this.log.warn('Not all workers could be stopped during onOnload(). Number of still active workers: '+String(this.cntWorkersActive));
}
// Stop CAN communication:
// @ts-ignore
this.disconnectFromCan(this.channelExt,this.config.canExtName);
// @ts-ignore
this.disconnectFromCan(this.channelInt,this.config.canIntName);
this.setState('info.connection', false, true);
this.log.debug('onUnload() took '+String(new Date().getTime()-tStart)+' ms to complete.');
callback();
} catch (e) {
this.log.error('unLoad() could not be completed. err='+e.message);
callback();
}
}
// If you need to react to object changes, uncomment the following block and the corresponding line in the constructor.
// You also need to subscribe to the objects with `this.subscribeObjects`, similar to `this.subscribeStates`.
// /**
// * Is called if a subscribed object changes
// * @param {string} id
// * @param {ioBroker.Object | null | undefined} obj
// */
// @ts-ignore
/*
onObjectChange(id, obj) {
if (obj) {
// The object was changed
this.log.info(`object ${id} changed: ${JSON.stringify(obj)}`);
} else {
// The object was deleted
this.log.info(`object ${id} deleted`);
}
}
*/
/**
* Is called if a subscribed state changes
* @param {string} id
* @param {ioBroker.State | null | undefined} state
*/
onStateChange(id, state) {
if ( (state) && (!state.ack) ) {
// The state was changed and ack == false
this.log.silly(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
for (const worker of Object.values(this.E3UdsWorkers)) {
if (id.includes(this.namespace+'.'+worker.config.stateBase)) {
this.log.silly(`Call worker for ${worker.config.stateBase}`);
worker.onUdsStateChange(this, worker, id, state);
}
}
}
}
// If you need to accept messages in your adapter, uncomment the following block and the corresponding line in the constructor.
// /**
// * Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...
// * Using this method requires "common.messagebox" property to be set to true in io-package.json
// * @param {ioBroker.Message} obj
// */
async onMessage(obj) {
//await this.log.debug('this.config:');
//await this.log.debug(JSON.stringify(this.config));
if (typeof obj === 'object' && obj.message) {
this.log.silly(`command received ${obj.command}`);
if (obj.command === 'getUdsDevices') {
if (obj.callback) {
if (!this.udsDevScanIsRunning) {
this.udsDevScanIsRunning = true;
await this.log.silly(`Received data - ${JSON.stringify(obj)}`);
await this.udsScanWorker.scanUdsDevices(this);
await this.log.silly(`Data to send - ${JSON.stringify({native: {tableUdsDevices: this.udsDevices}})}`);
await this.sendTo(obj.from, obj.command, {native: {tableUdsDevices: this.udsDevices}}, obj.callback);
this.udsDevScanIsRunning = false;
} else {
await this.log.debug('Request "getUdsDevice" during running UDS scan!');
this.sendTo(obj.from, obj.command, {native: {tableUdsDevices: this.udsDevices}}, obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, {native: {tableUdsDevices: []}}, obj.callback);
}
}
if (obj.command === 'getUdsDeviceSelect') {
if (obj.callback) {
this.log.silly(`Received data - ${JSON.stringify(obj)}`);
if (Array.isArray(obj.message) ) {
const selUdsDevices = obj.message.map(item => ({label: item.devStateName, value: item.devAddr}));
this.log.silly(`Data to send - ${JSON.stringify(selUdsDevices)}`);
if (selUdsDevices) {
this.sendTo(obj.from, obj.command, selUdsDevices, obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, [{label: 'Not available', value: ''}], obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, [{label: 'Not available', value: ''}], obj.callback);
}
}
if (obj.command === 'getExtColDeviceSelect') {
if (obj.callback) {
this.log.silly(`Received data - ${JSON.stringify(obj)}`);
if (Array.isArray(obj.message) ) {
const selUdsDevices = obj.message.filter(item => item.collectCanId != '').map(item => ({label: item.devStateName, value: item.collectCanId}));
this.log.silly(`Data to send - ${JSON.stringify(selUdsDevices)}`);
if (selUdsDevices) {
this.sendTo(obj.from, obj.command, selUdsDevices, obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, [{label: 'Not available', value: ''}], obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, [{label: 'Not available', value: ''}], obj.callback);
}
}
if (obj.command === 'getIntColDeviceSelect') {
if (obj.callback) {
this.log.silly(`Received data - ${JSON.stringify(obj)}`);
if (Array.isArray(obj.message) ) {
const selUdsDevices = obj.message.filter(item => item.collectCanId != '').map(item => ({label: item.devStateName, value: item.collectCanId}));
this.log.silly(`Data to send - ${JSON.stringify(selUdsDevices)}`);
if (selUdsDevices) {
this.sendTo(obj.from, obj.command, selUdsDevices, obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, [{label: 'Not available', value: ''}], obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, [{label: 'Not available', value: ''}], obj.callback);
}
}
if (obj.command === 'startDidScan') {
if (obj.callback) {
if (!this.udsDidScanIsRunning) {
this.udsDidScanIsRunning = true;
this.log.silly(`Received data - ${JSON.stringify(obj)}`);
await this.udsScanWorker.scanUdsDids(this,this.udsDevAddrs,this.udsDidsMaxNmbr);
//await this.udsScanWorker.scanUdsDids(this,this.udsDevAddrs,300);
this.sendTo(obj.from, obj.command, this.udsDevices, obj.callback);
this.udsDidScanIsRunning = false;
} else {
this.log.silly('Request "startDidScan" during running UDS did scan!');
this.sendTo(obj.from, obj.command, obj.message, obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, obj.message, obj.callback);
}
}
if (obj.command === 'getUdsDids') {
if (obj.callback) {
this.log.silly(`Received data - ${JSON.stringify(obj)}`);
if ( (obj.message) && (this.udsDevStateNames.includes(obj.message)) ) {
const udsDids = new storage.storageDids({stateBase:obj.message, device:obj.message});
await udsDids.readKnownDids(this);
const udsDidsTable = [];
if (udsDids.didsDevSpecAvail) {
for (const [did, item] of Object.entries(udsDids.didsDictDevCom)) {
udsDidsTable.push({didId:Number(did), didLen:Number(item.len), didName:item.id, didCodec:item.codec});
//if (udsDidsTable.length >= 50) break;
}
for (const [did, item] of Object.entries(udsDids.didsDictDevSpec)) {
udsDidsTable.push({didId:Number(did), didLen:Number(item.len), didName:item.id, didCodec:item.codec});
//if (udsDidsTable.length >= 60) break;
}
udsDidsTable.sort((a,b) => a.didId-b.didId);
}
this.sendTo(obj.from, obj.command, {native: {tableUdsDids: udsDidsTable}}, obj.callback);
} else {
this.sendTo(obj.from, obj.command, {native: {tableUdsDids: []}}, obj.callback);
}
} else {
this.sendTo(obj.from, obj.command, {native: {tableUdsDids: []}}, obj.callback);
}
}
if (obj.command === 'getUdsDidsDevSelect') {
if (obj.callback) {
this.log.silly(`Received data - ${JSON.stringify(obj)}`);
// @ts-ignore
const selUdsDevices = this.config.tableUdsDevices.map(item => ({ label: item.devStateName, value: item.devStateName }));
await this.log.silly(`Data to send - ${JSON.stringify(selUdsDevices)}`);
if (selUdsDevices) {
await this.sendTo(obj.from, obj.command, selUdsDevices, obj.callback);
}
} else {
await this.sendTo(obj.from, obj.command, [{label: 'Not available', value: ''}], obj.callback);
}
}
}
}
onCanExtStopped() {
if (!this.stoppingInstance) {
// External CAN connection was terminated unexpectedly
this.log.error('External CAN bus was stopped.');
}
this.cntCanConnActual--;
this.setState('info.connection', false, true);
}
onCanIntStopped() {
if (!this.stoppingInstance) {
// External CAN connection was terminated unexpectedly
this.log.error('Internal CAN bus was stopped.');
}
this.cntCanConnActual--;
this.setState('info.connection', false, true);
}
onCanMsgExt(msg) {
if ( (this.e380Collect) && (this.e380Collect.config.canID.includes(msg.id)) ) { this.e380Collect.msgCollect(this, msg); }
if ( (this.e3100cbCollect) && (this.e3100cbCollect.config.canID.includes(msg.id)) ) { this.e3100cbCollect.msgCollect(this, msg); }
if (this.E3CollectExt[msg.id]) this.E3CollectExt[msg.id].msgCollect(this, msg);
if (this.E3UdsWorkers[msg.id]) this.E3UdsWorkers[msg.id].msgUds(this, msg);
if (this.E3UdsSID77Workers[msg.id]) this.E3UdsSID77Workers[msg.id].msgUds(this, msg);
if (this.udsScanWorker.workers[msg.id]) this.udsScanWorker.workers[msg.id].msgUds(this, msg);
}
onCanMsgInt(msg) {
if (this.E3CollectInt[msg.id]) this.E3CollectInt[msg.id].msgCollect(this, msg);
}
}
if (require.main !== module) {
// Export the constructor in compact mode
/**
* @param {Partial<utils.AdapterOptions>} [options={}]
*/
module.exports = (options) => new E3oncan(options);
} else {
// otherwise start the instance directly
new E3oncan();
}