UNPKG

iobroker.lorawan

Version:

converts the desired lora gateway data to a ioBroker structure

975 lines (903 loc) 45.9 kB
const assignhandlerClass = require('./assignhandler'); const { isDeepStrictEqual } = require('util'); /** * this class handles the rekursive directories */ class directorieshandlerClass { /** * @param adapter adapter data (eg. for logging) */ constructor(adapter) { this.adapter = adapter; // used dataentries in directory structurt this.searchableAttributeNames = { applicationId: 'applicationId', applicationName: 'applicationName', deviceEUI: 'deviceEui', deviceId: 'deviceId', }; this.reachableSubfolders = { configuration: 'configuration', uplink: 'uplink', uplinkDecoded: 'uplink.decoded', uplinkRaw: 'uplink.raw', uplinkRemaining: 'uplink.remaining', downlink: 'downlink', downlinkRaw: 'downlink.raw', downlinkControl: 'downlink.control', downlinkRemaining: 'downlink.remaining', downlinkNextSend: 'downlink.nextSend', downlinkLastSend: 'downlink.lastSend', join: 'join', joinRaw: 'join.raw', }; this.icons = { wifi: 'wifi', }; this.customObject = {}; this.HABridge = { Adaptername: 'mqtt-client', TopicPrefix: 'iobroker/', Dummy: { applicationId: '942ea85a-1ea8-4dff-8900-8141897f95b9', applicationName: 'Fenster', usedApplicationName: 'FensterStaße', deviceEUI: 'a84041a52a598cc5', deviceId: 'Balkon', usedDeviceId: 'BalkonLinks', }, }; /********************************************************************* * ************** Definition Assigns (externel Module) *************** * ******************************************************************/ this.assignhandler = new assignhandlerClass(this.adapter); /********************************************************************* * ********************* Definition of Assigns ********************** * ******************************************************************/ // Definition of Assign timestamps this.timestringToDate = (value, common) => { common.role = 'date'; if (typeof value === 'string') { common.type = 'number'; return new Date(value).valueOf(); } return value; }; // Assign definitions for special states this.assigns = { time: this.timestringToDate, gwTime: this.timestringToDate, nsTime: this.timestringToDate, received_at: this.timestringToDate, }; /********************************************************************* * ************************** writecommands ************************* * ******************************************************************/ //general function for writecommands this.executeWritecommand = async (writecommand, elementName, sourceId, value) => { const activeFunction = 'executeWritecommand'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { // Check, if there is a writetrigger and the stateval is not euqal its releaseVal(still present) if (writecommand.releaseValue !== value) { const baseInfo = this.adapter.getBaseDeviceInfo(sourceId); const subfolder = sourceId.slice( baseInfo.objectStartDirectory.length + 1, sourceId.length - baseInfo.changedState.length - 1, ); if (!writecommand.approvedFolders || writecommand.approvedFolders[subfolder]) { const destinationId = `${baseInfo.objectStartDirectory}.${writecommand.destination}`; const destinationState = await this.adapter.getStateAsync(destinationId); if (destinationState?.val === writecommand.releaseValue) { this.adapter.log.debug( `writecommand ${elementName} recogniced. value ${value} will be written into ${writecommand.destination}`, ); await this.adapter.setStateAsync(destinationId, value); } } } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error}`); } }; // Define settings for writecommand devicetype this.writeCommandDeviceType = async (elementName, sourceId, value) => { const activeFunction = 'writeCommandDeviceType'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { const writecommand = { destination: 'configuration.devicetype', releaseValue: '', approvedFolders: { 'uplink.decoded': true, 'uplink.remaining.version_ids': true }, }; return await this.executeWritecommand(writecommand, elementName, sourceId, value); } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error}`); } }; this.writeCommand = { Devicetype: this.writeCommandDeviceType, devicetype: this.writeCommandDeviceType, Device: this.writeCommandDeviceType, device: this.writeCommandDeviceType, Hardware_mode: this.writeCommandDeviceType, Hardware_Mode: this.writeCommandDeviceType, model_id: this.writeCommandDeviceType, }; // declare the directory structre this.directories = { application: { objectId: (topic, message) => { return this.getAttributValue(topic, message, this.searchableAttributeNames.applicationId); }, objectCommonFromNative: { name: 'applicationName' }, objectNative: (topic, message) => { return { applicationName: this.getAttributValue( topic, message, this.searchableAttributeNames.applicationName, ), }; }, devices: { deviceEUI: { objectId: (topic, message) => { return this.getAttributValue(topic, message, this.searchableAttributeNames.deviceEUI); }, objectCommonFromNative: { name: 'deviceId' }, objectNative: (topic, message) => { return { applicationName: this.getAttributValue( topic, message, this.searchableAttributeNames.applicationName, ), deviceId: this.getAttributValue(topic, message, this.searchableAttributeNames.deviceId), }; }, objectIconPath: (topic, message) => { return this.getIconPath(topic, message, this.icons.wifi); }, objectType: 'device', configuration: { objectType: 'channel', devicetype: { isState: true, stateCommonType: 'string', stateCommonWrite: true, stateCommonRole: 'text', }, }, uplink: { raw: {}, decoded: { objectType: 'channel', }, remaining: {}, }, downlink: { raw: {}, control: { objectType: 'channel', }, nextSend: { hex: { isState: true, stateCommonDef: '0', stateCommonType: 'string', }, }, lastSend: { hex: { isState: true, stateCommonDef: '0', stateCommonType: 'string', }, }, remaining: {}, }, join: { raw: {}, }, }, }, }, }; this.ignoredElementNames = { objectId: 'objectId', objectCommonName: 'objectCommonName', objectCommonFromNative: 'objectCommonFromNative', objectType: 'objectType', objectNative: 'objectNative', objectIconPath: 'objectIconPath', }; } /********************************************************************* * ************************ Objectstring ***************************** * ******************************************************************/ /** * @param obj object for the rekursive structure * @param startDirectory director, from wich starts the rekursive object * @param topic topic of the message * @param message message from LoRaWan device * @param options deviers options (eg. ignoredElementNames) */ async generateRekursivObjects(obj, startDirectory, topic, message, options) { const activeFunction = 'generateRekursivObjects'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { // just proceed with ojects if (typeof obj === 'object') { // go to every element in the object for (const elementName in obj) { // Check the the elementname is not in ignored object if ( !this.ignoredElementNames[elementName] && (!options || !options.ignoredElementNames || !options.ignoredElementNames[elementName]) ) { // Check if the element is an object if (typeof obj[elementName] === 'object' && obj[elementName] && !obj[elementName].isState) { // Generate the desired id let objectId = `${startDirectory}.${elementName}`; let internalObjectId = elementName; if (obj[elementName].objectId) { internalObjectId = `${obj[elementName].objectId(topic, message)}`; objectId = `${startDirectory}.${internalObjectId}`; } if (objectId.indexOf('.') === 0) { objectId = objectId.substring(1, objectId.length); } let myObject = {}; // check object exists if (await this.adapter.objectExists(objectId)) { this.adapter.log.silly(`get object ${objectId}`); myObject = await this.adapter.getObjectAsync(objectId); } else { this.adapter.log.silly(`object ${objectId} not exists`); } const myObjectBefore = structuredClone(myObject); //Set type of object, if not exists, or if type in structure if (!myObject.type || obj[elementName].objectType) { myObject.type = obj[elementName].objectType ? obj[elementName].objectType : 'folder'; } //check for common if (!myObject.common) { myObject.common = {}; } // Check for icon assignment if (obj[elementName].objectIconPath) { const iconPath = obj[elementName].objectIconPath(topic, message); if (iconPath !== '') { if (myObject.common.icon) { if (myObject.common.icon.indexOf('offline') !== -1) { // create changeinfo and register notification const changeInfo = await this.adapter.getChangeInfo(objectId); if (changeInfo) { this.adapter.registerNotification( 'lorawan', 'Lorawan device back online', `The LoRaWAN device ${changeInfo.usedDeviceId} in the application ${changeInfo.usedApplicationName} is back online`, ); } } } myObject.common.icon = iconPath; } } // check whether a common name was specified if (obj[elementName].objectCommonName) { if (typeof obj[elementName].objectCommonName === 'function') { myObject.common.name = obj[elementName].objectCommonName(topic, message); } else { myObject.common.name = obj[elementName].objectCommonName; } } // Check wheter a native content was specified if (!myObject.native) { myObject.native = {}; } // Generate native if (obj[elementName].objectNative) { const objectNative = obj[elementName].objectNative(topic, message); for (const attribute in objectNative) { myObject.native[attribute] = objectNative[attribute]; } } // Assigne to common, if there are values specified if (obj[elementName].objectCommonFromNative) { for (const attribute in obj[elementName].objectCommonFromNative) { // Check, whether the actual native is present if (myObject.native[obj[elementName].objectCommonFromNative[attribute]]) { // check for old common => existing object if (myObjectBefore.common) { //check for content if ( myObjectBefore.common[attribute] === myObjectBefore.native[ obj[elementName].objectCommonFromNative[attribute] ] || myObjectBefore.common[attribute] === '' ) { this.adapter.log.silly( `set common attribut ${attribute} to ${myObject.native[obj[elementName].objectCommonFromNative[attribute]]}`, ); myObject.common[attribute] = myObject.native[obj[elementName].objectCommonFromNative[attribute]]; } } else { this.adapter.log.silly( `set common attribut ${attribute} to ${myObject.native[obj[elementName].objectCommonFromNative[attribute]]}`, ); myObject.common[attribute] = myObject.native[obj[elementName].objectCommonFromNative[attribute]]; } } } } // Check for name if (!myObject.common.name) { myObject.common.name = ''; } if (!isDeepStrictEqual(myObject, myObjectBefore)) { this.adapter.log.debug(`set object ${objectId}`); await this.adapter.setObjectAsync(objectId, myObject); } // Jump into next step (next directory / attribute) await this.generateRekursivObjects(obj[elementName], objectId, topic, message, options); } else { const common = { type: typeof obj[elementName], name: '', role: 'state', unit: obj[elementName] ? obj[elementName].CommonStateUnit ? obj[elementName].CommonStateUnit : '' : '', write: false, }; let stateVal = obj[elementName]; let objectId = `${startDirectory}.${elementName}`; let internalObjectId = elementName; if (obj[elementName]) { if (obj[elementName].isState) { stateVal = obj[elementName].stateVal !== undefined ? obj[elementName].stateVal : undefined; common.type = obj[elementName].stateCommonType ? obj[elementName].stateCommonType : typeof stateVal; common.name = obj[elementName].stateCommonName ? obj[elementName].stateCommonName : common.name; common.write = obj[elementName].stateCommonWrite ? obj[elementName].stateCommonWrite : common.write; common.role = obj[elementName].stateCommonRole ? obj[elementName].stateCommonRole : common.role; } if (obj[elementName].objectId) { internalObjectId = `${obj[elementName].objectId(topic, message)}`; objectId = `${startDirectory}.${internalObjectId}`; } } // Check for decoded (for custom values) if (objectId.indexOf(`${this.reachableSubfolders.uplinkDecoded}.`) !== -1) { // Check custom decoded json if (this.adapter.config.customDecodedJsonActive) { if (typeof this.customObject === 'object') { common.custom = this.customObject; } } } // Check for configuration (for custom values) if (objectId.indexOf(`${this.reachableSubfolders.configuration}.`) !== -1) { // Check custom configuration jsondev-server if ( this.adapter.config.customDecodedJsonActive && this.adapter.config.customConfigurationJsonActive ) { if (typeof this.customObject === 'object') { common.custom = this.customObject; } } } // Add LoRaWAN 2 HA Bridged Funktion (MQTT Topic used with Script for auto discovery) await this.setCustomForHaBridge(objectId, common); // Check for id dont starts with "." if (objectId.indexOf('.') === 0) { objectId.substring(1, objectId.length); } // Check for an assign definition (calculation and / or role assignment) if (this.assigns[elementName] && !options?.dontAssign) { this.adapter.log.debug( `the state with the id ${objectId} will be assigned by internal function`, ); stateVal = this.assigns[elementName](stateVal, common); } // Check for assign (new implemented function) if (this.assignhandler.assign[elementName] && !options?.dontAssign) { this.adapter.log.debug( `the state with the id ${objectId} will be assigned by internal assign function`, ); stateVal = this.assignhandler.executeAssign( objectId, stateVal, this.assignhandler.assign[elementName], { common: common }, ); } common.read = common.role !== 'button'; common.def = obj[elementName] || typeof obj[elementName] === 'boolean' ? obj[elementName].stateCommonDef ? obj[elementName].stateCommonDef : common.type === 'boolean' ? false : common.type === 'number' ? 0 : '' : common.type === 'number' ? 0 : ''; await this.adapter.extendObjectAsync(objectId, { type: 'state', common: common, native: {}, }); if (typeof stateVal === 'object') { stateVal = JSON.stringify(stateVal); } if (stateVal !== undefined) { // Set State from Objectpath await this.adapter.setStateAsync(`${objectId}`, stateVal, true); //Check for writecommand if (this.writeCommand[elementName]) { await this.writeCommand[elementName](elementName, objectId, stateVal); } } } } } } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Message: ${JSON.stringify(message)}`); } } /********************************************************************* * ************************** Attribute ****************************** * ******************************************************************/ /** * @param topic topic of the message * @param message message of the LoRaWAN device * @param resolvetype resolvetype of the attribute */ getAttributValue(topic, message, resolvetype) { const activeFunction = 'getAttributValue'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { // Select search in case of origin switch (this.adapter.config.origin) { case this.adapter.origin.ttn: return this.getTtnAttributValue(topic, message, resolvetype); case this.adapter.origin.chirpstack: return this.getChirpstackAttributValue(topic, message, resolvetype); } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Message: ${JSON.stringify(message)}`); } } /********************************************************************* * *************************** iconPath ****************************** * ******************************************************************/ /** * @param topic topic of the message * @param message message of the LoRaWAN device * @param icontype type of icon (eg. wifi) */ getIconPath(topic, message, icontype) { const activeFunction = 'getIconPath'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { // Select search in case of origin switch (this.adapter.config.origin) { case this.adapter.origin.ttn: return this.getTtnIconPath(topic, message, icontype); case this.adapter.origin.chirpstack: return this.getChirpstackIconPath(topic, message, icontype); } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Message: ${JSON.stringify(message)}`); } } /** * @param sf spreading factor * @param rssi rssi value of the LoRaWAN device */ analyseConnection(sf, rssi) { const activeFunction = 'getIcon'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { this.adapter.log.silly(`icon for device with sf: ${sf} and rssi: ${rssi} requested`); let iconPath = `icons/wifiSf`; if (sf === 7 || sf === 9 || sf === 12) { iconPath += `${sf}`; } else { iconPath += `X`; } if (rssi >= -70) { rssi = 5; } else if (rssi >= -80) { rssi = 4; } else if (rssi >= -90) { rssi = 3; } else if (rssi >= -100) { rssi = 2; } else if (rssi >= -110) { rssi = 1; } else { rssi = 0; } iconPath += `_${rssi}.png`; return iconPath; } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error}`); } } /********************************************************************* * ******************** Object Start Directory *********************** * ******************************************************************/ /** * @param topic topic of the message * @param message messaqge of the LoRaWAN device */ getDeviceStartDirectory(topic, message) { const activeFunction = 'getDeviceStartDirectory'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { const applicationId = `${this.getAttributValue(topic, message, this.searchableAttributeNames.applicationId)}`; const deviceEUI = `${this.getAttributValue(topic, message, this.searchableAttributeNames.deviceEUI)}`; return `${applicationId}.devices.${deviceEUI}`; } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Message: ${JSON.stringify(message)}`); } } /********************************************************************* * *********************** Topic resolved **************************** * ******************************************************************/ /** * @param topic topic of the message */ getTopicResolved(topic) { const activeFunction = 'getTopicResolved'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { this.adapter.log.silly(`topic ${topic} is requested for resolveing`); // Select in case of origin switch (this.adapter.config.origin) { case this.adapter.origin.ttn: return this.getTtnTopicResolved(topic); case this.adapter.origin.chirpstack: return this.getChirpstackTopicResolved(topic); } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Topic: ${JSON.stringify(topic)}`); } } /********************************************************************* * **************************** TTN ********************************* * ******************************************************************/ /********************************************************************* * ************************** Attribute ****************************** * ******************************************************************/ /** * @param topic topic of the message * @param message messaqge of the LoRaWAN device * @param resolvetype type to resolve to (attribute) */ getTtnAttributValue(topic, message, resolvetype) { const activeFunction = 'getTtnAttributValue'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { this.adapter.log.silly(`attribute ${resolvetype} is requested for ttn`); const topicResolved = this.getTopicResolved(topic); switch (resolvetype) { case this.searchableAttributeNames.applicationId: return topicResolved?.applicationId; case this.searchableAttributeNames.applicationName: return topicResolved?.applicationName; case this.searchableAttributeNames.deviceEUI: return message.end_device_ids.dev_eui; case this.searchableAttributeNames.deviceId: return topicResolved?.deviceId; default: this.adapter.log.warn(`No attribute with the name ${resolvetype} found.`); return ''; } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Topic: ${JSON.stringify(topic)}`); } } /********************************************************************* * *************************** iconPath ****************************** * ******************************************************************/ /** * @param topic topic of the message * @param message messaqge of the LoRaWAN device * @param icontype type of the icon (eg. wifi) */ getTtnIconPath(topic, message, icontype) { const activeFunction = 'getTtnIconPath'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { this.adapter.log.silly(`icontype ${icontype} is requested for ttn`); switch (icontype) { case this.icons.wifi: if ( message.uplink_message?.settings.data_rate.lora.spreading_factor && message.uplink_message?.rx_metadata[0].rssi ) { return this.analyseConnection( message.uplink_message.settings.data_rate.lora.spreading_factor, message.uplink_message.rx_metadata[0].rssi, ); } return ''; default: this.adapter.log.warn(`No icontype with the name ${icontype} found.`); return ''; } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Topic: ${JSON.stringify(topic)}`); } } /********************************************************************* * ************************ Object Directory ************************* * ******************************************************************/ /** * @param topic topic of the message */ getTtnTopicResolved(topic) { const activeFunction = 'getTtnTopicResolved'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { const topicElements = topic.split('/'); const topicResolved = { applicationName: '', applicationId: topicElements[1], deviceId: topicElements[3], messageType: topicElements[topicElements.length - 1], }; // clean up application id const indexOfOrigin = topicResolved.applicationId.indexOf('@'); if (indexOfOrigin !== -1) { topicResolved.applicationName = topicResolved.applicationId; topicResolved.applicationId = topicResolved.applicationId.substring(0, indexOfOrigin); } return topicResolved; } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Topic: ${JSON.stringify(topic)}`); } } /********************************************************************* * ************************ Chirpstack ****************************** * ******************************************************************/ /********************************************************************* * ************************** Attribute ****************************** * ******************************************************************/ // !!! Dont use resoletypes with message.deviceInfo in down => No deviceInfo in down message present /** * @param topic topic of the message * @param message messaqge of the LoRaWAN device * @param resolvetype type to resolve to (attribute) */ getChirpstackAttributValue(topic, message, resolvetype) { const activeFunction = 'getChirpstackAttributValue'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { this.adapter.log.silly(`attribute ${resolvetype} is requested for chirpstack`); const topicResolved = this.getTopicResolved(topic); switch (resolvetype) { case this.searchableAttributeNames.applicationId: return topicResolved?.applicationId; case this.searchableAttributeNames.applicationName: return message.deviceInfo.applicationName; case this.searchableAttributeNames.deviceEUI: return topicResolved?.deviceEUI; case this.searchableAttributeNames.deviceId: return message.deviceInfo.deviceName; default: this.adapter.log.warn(`No attribute with the name ${resolvetype} found.`); return ''; } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Message: ${JSON.stringify(message)}`); } } /********************************************************************* * *************************** iconPath ****************************** * ******************************************************************/ /** * @param topic topic of the message * @param message messaqge of the LoRaWAN device * @param icontype type of the icon (eg. wifi) */ getChirpstackIconPath(topic, message, icontype) { const activeFunction = 'getChirpstackIconPath'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { this.adapter.log.silly(`icontype ${icontype} is requested for ttn`); switch (icontype) { case this.icons.wifi: if (message.txInfo?.modulation.lora.spreadingFactor && message.rxInfo[0]?.rssi) { return this.analyseConnection( message.txInfo.modulation.lora.spreadingFactor, message.rxInfo[0].rssi, ); } return ''; default: this.adapter.log.warn(`No icontype with the name ${icontype} found.`); return ''; } } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Topic: ${JSON.stringify(topic)}`); } } /********************************************************************* * ************************ Resolved Topic *************************** * ******************************************************************/ /** * @param topic topic of the message */ getChirpstackTopicResolved(topic) { const activeFunction = 'getChirpstackTopicResolved'; this.adapter.log.debug(`Function ${activeFunction} started.`); try { const topicElements = topic.split('/'); const topicResolved = { applicationId: topicElements[1], deviceEUI: topicElements[3], messageType: topicElements[topicElements.length - 1], }; return topicResolved; } catch (error) { this.adapter.log.error(`error at ${activeFunction}: ${error} - - - Topic: ${JSON.stringify(topic)}`); } } /********************************************************************* * ************************ LoRaWAN 2 HA Bridge ********************** * ******************************************************************/ /** * @param objectId Id of actual element * @param common custom object of state */ async setCustomForHaBridge(objectId, common) { // Abfrage, ob überhaupt eine Instanz ausgewählt wurde if (this.adapter.config.MQTTInstance.indexOf(this.HABridge.Adaptername) !== -1) { // Abfrage des Decoded Ordners if (objectId.indexOf(`${this.reachableSubfolders.uplinkDecoded}.`) !== -1) { if (this.adapter.config.HABridgeDecoded !== 'off') { if (!common.custom) { common.custom = {}; } if (!common.custom[this.adapter.config.MQTTInstance]) { common.custom[this.adapter.config.MQTTInstance] = {}; } common.custom[this.adapter.config.MQTTInstance] = { enabled: true, publish: true, retain: true, pubChangesOnly: false, pubAsObject: false, qos: false, subscribe: false, subChangesOnly: false, subAsObject: false, subQos: false, setAck: false, }; const changeInfo = await this.adapter.getChangeInfo(objectId); const DeviceIdentifier = this.getDeviceIdentifier( changeInfo, this.adapter.config.DeviceIdentifiers, ); common.custom[this.adapter.config.MQTTInstance].topic = `${this.HABridge.TopicPrefix}${DeviceIdentifier}/${changeInfo.changedState}`; } // Abfrage des Control Ordners } else if (objectId.indexOf(`${this.reachableSubfolders.downlinkControl}.`) !== -1) { if (this.adapter.config.HABridgeControl !== 'off') { if (!common.custom) { common.custom = {}; } if (!common.custom[this.adapter.config.MQTTInstance]) { common.custom[this.adapter.config.MQTTInstance] = {}; } common.custom[this.adapter.config.MQTTInstance] = { enabled: true, publish: true, retain: true, pubChangesOnly: false, pubAsObject: false, qos: false, subscribe: this.adapter.config.HABridgeControl === 'publishsubscribe', subChangesOnly: false, subAsObject: false, subQos: false, setAck: false, }; const changeInfo = await this.adapter.getChangeInfo(objectId); const DeviceIdentifier = this.getDeviceIdentifier( changeInfo, this.adapter.config.DeviceIdentifiers, ); common.custom[this.adapter.config.MQTTInstance].topic = `iobroker/${DeviceIdentifier}/${changeInfo.changedState}`; } } } } /** * @param changeInfo changeinfo of the state * @param DeviceIdentifiers configed deviceidentifier */ getDeviceIdentifier(changeInfo, DeviceIdentifiers) { let DeviceIdentifier = ''; let Separator = ''; switch (this.adapter.config.separator) { case 'no': Separator = ''; break; case 'space': Separator = ' '; break; case 'underline': Separator = '_'; break; case 'minus': Separator = '-'; break; } for (const element of Object.values(DeviceIdentifiers)) { if (DeviceIdentifier !== '') { DeviceIdentifier += Separator; } switch (element.DeviceIdentifier) { case 'applicationId': DeviceIdentifier += changeInfo.applicationId; break; case 'applicationName': DeviceIdentifier += changeInfo.applicationName; break; case 'usedApplicationName': DeviceIdentifier += changeInfo.usedApplicationName; break; case 'deviceEUI': DeviceIdentifier += changeInfo.deviceEUI; break; case 'deviceId': DeviceIdentifier += changeInfo.deviceId; break; case 'usedDeviceId': DeviceIdentifier += changeInfo.usedDeviceId; break; } } return this.replaceGermanSpecific(DeviceIdentifier); } /** * @param Inputstring string to replace the vorbidden chars */ replaceGermanSpecific(Inputstring) { return Inputstring.replace(/ä/g, 'ae') .replace(/ö/g, 'oe') .replace(/ü/g, 'ue') .replace(/Ä/g, 'Ae') .replace(/Ö/g, 'Oe') .replace(/Ü/g, 'Ue') .replace(/ß/g, 'ss'); } } module.exports = directorieshandlerClass;