UNPKG

iobroker.openmediavault

Version:
343 lines (342 loc) 17.8 kB
/* * Created with @iobroker/create-adapter v2.6.5 */ // The adapter-core module gives you access to the core ioBroker functions // you need to create an adapter import * as utils from '@iobroker/adapter-core'; import _ from 'lodash'; import url from 'node:url'; import * as schedule from 'node-schedule'; import moment from 'moment'; // Load your modules here, e.g.: import { OmvApi } from './lib/omv-rpc.js'; import * as tree from './lib/tree/index.js'; import { myIob } from './lib/myIob.js'; class Openmediavault extends utils.Adapter { omvApi = undefined; myIob = undefined; updateSchedule = undefined; subscribedList = []; statesUsingValAsLastChanged = [ // id of states where lc is taken from the value ]; updateTimeout = undefined; configDevicesCache = {}; constructor(options = {}) { super({ ...options, name: 'openmediavault', useFormatDate: true }); 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)); } //#region adapter methods /** * Is called when databases are connected and adapter received configuration. */ async onReady() { const logPrefix = '[onReady]:'; try { this.connected = false; await utils.I18n.init(`${utils.getAbsoluteDefaultDataDir().replace('iobroker-data/', '')}node_modules/iobroker.${this.name}/admin`, this); this.myIob = new myIob(this, utils, this.statesUsingValAsLastChanged); if (this.config.url && this.config.user && this.config.password) { this.omvApi = new OmvApi(this); await this.updateData(true); } else { this.log.warn(`${logPrefix} url and / or login data missing!`); } this.myIob.findMissingTranslation(); // const tmp = tree.smart.getStateIDs(); // let list = [] // for (let id of tmp) { // list.push({ id: id }); // } // this.log.warn(JSON.stringify(list)); } catch (error) { this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`); } } /** * Is called when adapter shuts down - callback has to be called under any circumstances! * * @param callback */ async onUnload(callback) { try { // Here you must clear all timeouts or intervals that may still be active if (this.updateTimeout) { clearTimeout(this.updateTimeout); } if (this.updateSchedule) { this.updateSchedule.cancel(); } // clearTimeout(timeout2); // ... // clearInterval(interval1); await this.omvApi?.logout(); callback(); } catch (e) { 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 // */ // private onObjectChange(id: string, obj: ioBroker.Object | null | undefined): void { // 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 id * @param state */ onStateChange(id, state) { if (state) { // The state was changed this.log.info(`state ${id} changed: ${state.val} (ack = ${state.ack})`); } else { // The state was deleted this.log.info(`state ${id} deleted`); } } /** * 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 obj */ onMessage(obj) { const logPrefix = '[onMessage]:'; try { if (typeof obj === 'object' && this.myIob) { if (obj.command.endsWith('StateList')) { const endpoint = obj.command.replace('StateList', ''); const states = tree[endpoint].getStateIDs(); let list = []; if (states) { for (let i = 0; i <= states.length - 1; i++) { if (states[i + 1] && states[i] === this.myIob.getIdWithoutLastPart(states[i + 1])) { list.push({ label: `[Channel]\t ${states[i]}`, value: states[i], }); } else { list.push({ label: `[State]\t\t ${states[i]}`, value: states[i], }); } } } list = _.orderBy(list, ['value'], ['asc']); if (obj.callback) { this.sendTo(obj.from, obj.command, list, obj.callback); } } else if (obj.command.endsWith('BlackList')) { if (this.connected && this.omvApi?.isConnected) { let list = []; if (this.configDevicesCache[obj.command.replace('BlackList', '')]) { list = this.configDevicesCache[obj.command.replace('BlackList', '')]; list = _.orderBy(list, ['label'], ['asc']); if (obj.callback) { this.sendTo(obj.from, obj.command, list, obj.callback); } } } } } } catch (error) { this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`); } } //#endregion //#region updateData async updateData(isAdapterStart = false) { const logPrefix = '[updateData]:'; try { if (!this.config.updateMethode) { if (this.updateTimeout) { this.clearTimeout(this.updateTimeout); this.updateTimeout = undefined; } this.updateTimeout = this.setTimeout(async () => { await this.updateData(); }, this.config.updateInterval * 1000); } else { if (isAdapterStart) { this.log.info(`${logPrefix} starting cron job with parameter '${this.config.updateCron}'`); this.updateSchedule = schedule.scheduleJob(this.config.updateCron, async () => { await this.updateData(); }); } } if (this.omvApi) { if (!this.omvApi.isConnected || moment().diff(this.omvApi.lastLogin ?? 0, 'minutes') >= this.omvApi.MAX_LOGIN_AGE_MINUTES) { if (this.omvApi.lastLogin !== null && moment().diff(this.omvApi.lastLogin ?? 0, 'minutes') >= this.omvApi.MAX_LOGIN_AGE_MINUTES) { this.log.debug(`${logPrefix} re-login because session id expired`); } await this.omvApi.login(); } if (this.connected && this.omvApi?.isConnected) { for (const endpoint of Object.keys(tree)) { if (Object.hasOwn(tree[endpoint], 'iobObjectDefintions')) { await this.updateDataGeneric(endpoint, tree[endpoint], tree[endpoint].iobObjectDefintions, isAdapterStart); } else { if (this.log.level === 'debug') { this.log.warn(`${logPrefix} no iob definitions for endpoint ${endpoint} exists!`); } } } } } } catch (error) { this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`); } } /** * update data gerneric * * @param endpoint * @param treeType * @param iobObjectDefintions * @param isAdapterStart */ async updateDataGeneric(endpoint, treeType, iobObjectDefintions, isAdapterStart = false) { const logPrefix = `[updateDataGeneric]: [${endpoint}]: `; const configDynamic = this.config; try { if (this.myIob) { if (this.connected && this.omvApi?.isConnected) { if (this.config[`${endpoint}Enabled`]) { this.log.debug(`${logPrefix} [${endpoint}]: start updating data...`); if (isAdapterStart) { await this.myIob.createOrUpdateChannel(treeType.idChannel, iobObjectDefintions.channelName, undefined, true); } const data = await this.omvApi?.retrievData(endpoint); if (data) { if (Array.isArray(data)) { this.configDevicesCache[endpoint] = []; for (let device of data) { if (iobObjectDefintions.deviceIdProperty) { let deviceName = 'unknown'; if ((typeof iobObjectDefintions.deviceNameProperty === 'function')) { deviceName = iobObjectDefintions.deviceNameProperty(device, this); } else if (typeof iobObjectDefintions.deviceNameProperty === 'string') { deviceName = device[iobObjectDefintions.deviceNameProperty]; } let deviceIdProperty = ''; if ((typeof iobObjectDefintions.deviceIdProperty === 'function')) { deviceIdProperty = iobObjectDefintions.deviceIdProperty(device, this); } else { deviceIdProperty = device[iobObjectDefintions.deviceIdProperty]; } if (deviceIdProperty) { const idDevice = `${treeType.idChannel}.${deviceIdProperty}`; const isWhiteList = configDynamic[`${endpoint}IsWhiteList`] ?? false; const blackList = configDynamic[`${endpoint}BlackList`] ?? []; if ((!isWhiteList && !_.some(blackList, { id: deviceIdProperty })) || (isWhiteList && _.some(blackList, { id: deviceIdProperty }))) { if (Object.hasOwn(iobObjectDefintions, 'additionalRequest')) { if (iobObjectDefintions.additionalRequest) { for (const additionalRequest of iobObjectDefintions.additionalRequest) { if (device[additionalRequest.conditionProperty]) { const addtionalData = await this.omvApi?.retrievData(additionalRequest.endpoint, { [additionalRequest.paramsProperty]: device[additionalRequest.paramsProperty] }); if (additionalRequest.converter) { device = { ...additionalRequest.converter(addtionalData, this), ...device }; } else { device = { ...addtionalData, ...device }; } } else { this.log.debug(`${logPrefix} device '${deviceIdProperty}' - no additional data request because condition property '${additionalRequest.conditionProperty}' is '${device[additionalRequest.conditionProperty]}'`); } } } } this.log.debug(`${logPrefix} final data ${JSON.stringify(device)}`); this.configDevicesCache[endpoint].push({ label: `${deviceName} (${deviceIdProperty})`, value: deviceIdProperty, }); await this.myIob.createOrUpdateDevice(idDevice, deviceName, iobObjectDefintions.deviceIsOnlineState ? `${idDevice}.${iobObjectDefintions.deviceIsOnlineState}` : undefined, iobObjectDefintions.deviceHasErrorsState ? `${idDevice}.${iobObjectDefintions.deviceHasErrorsState}` : undefined, undefined, isAdapterStart, true); await this.myIob.createOrUpdateStates(idDevice, treeType.get(), device, device, configDynamic[`${endpoint}StatesBlackList`] ?? [], configDynamic[`${endpoint}StatesIsWhiteList`] ?? false, deviceName, isAdapterStart); this.log.debug(`${logPrefix} device '${deviceIdProperty}' data successfully updated`); } else { if (isAdapterStart) { if (await this.objectExists(idDevice)) { await this.delObjectAsync(idDevice, { recursive: true }); this.log.warn(`${logPrefix} device '${deviceName}' (id: ${deviceIdProperty}) delete, ${isWhiteList ? 'it\'s not on the whitelist' : 'it\'s on the blacklist'}`); } } } } else { this.log.error(`${logPrefix} deviceIdProperty for device not exists! ('${JSON.stringify(device)}')`); } } else { this.log.error(`${logPrefix} deviceIdProperty property not exists in tree definition!`); } } } else { await this.myIob.createOrUpdateStates(treeType.idChannel, treeType.get(), data, data, configDynamic[`${endpoint}StatesBlackList`] ?? [], configDynamic[`${endpoint}StatesIsWhiteList`] ?? false, iobObjectDefintions.channelName, isAdapterStart); this.log.debug(`${logPrefix} channel '${iobObjectDefintions.channelName}' data successfully updated`); } if (isAdapterStart) { this.log.info(`${logPrefix} data successfully updated`); } } } else { if (await this.objectExists(treeType.idChannel)) { await this.delObjectAsync(treeType.idChannel, { recursive: true }); this.log.debug(`${logPrefix} '${treeType.idChannel}' deleted`); } } } } } catch (error) { this.log.error(`${logPrefix} error: ${error}, stack: ${error.stack}`); } } } // replace only needed for dev system const modulePath = url.fileURLToPath(import.meta.url).replace('/development/', '/node_modules/'); if (process.argv[1] === modulePath) { // start the instance directly new Openmediavault(); } export default function startAdapter(options) { // compact mode return new Openmediavault(options); }