UNPKG

@iobroker/adapter-react

Version:

React classes to develop admin interfaces for ioBroker with react.

1,694 lines (1,424 loc) 99.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = exports.PROGRESS = exports.ERRORS = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _propTypes = _interopRequireDefault(require("prop-types")); /** * Copyright 2020-2022, bluefox <dogafox@gmail.com> * * MIT License * **/ /** Possible progress states. */ var PROGRESS = { /** The socket is connecting. */ CONNECTING: 0, /** The socket is successfully connected. */ CONNECTED: 1, /** All objects are loaded. */ OBJECTS_LOADED: 2, /** The socket is ready for use. */ READY: 3 }; exports.PROGRESS = PROGRESS; var PERMISSION_ERROR = 'permissionError'; var NOT_CONNECTED = 'notConnectedError'; var TIMEOUT_FOR_ADMIN4 = 1300; var ERRORS = { PERMISSION_ERROR: PERMISSION_ERROR, NOT_CONNECTED: NOT_CONNECTED }; exports.ERRORS = ERRORS; function fixAdminUI(obj) { if (obj && obj.common && !obj.common.adminUI) { if (obj.common.noConfig) { obj.common.adminUI = obj.common.adminUI || {}; obj.common.adminUI.config = 'none'; } else if (obj.common.jsonConfig) { obj.common.adminUI = obj.common.adminUI || {}; obj.common.adminUI.config = 'json'; } else if (obj.common.materialize) { obj.common.adminUI = obj.common.adminUI || {}; obj.common.adminUI.config = 'materialize'; } else { obj.common.adminUI = obj.common.adminUI || {}; obj.common.adminUI.config = 'html'; } if (obj.common.jsonCustom) { obj.common.adminUI = obj.common.adminUI || {}; obj.common.adminUI.custom = 'json'; } else if (obj.common.supportCustoms) { obj.common.adminUI = obj.common.adminUI || {}; obj.common.adminUI.custom = 'json'; } if (obj.common.materializeTab && obj.common.adminTab) { obj.common.adminUI = obj.common.adminUI || {}; obj.common.adminUI.tab = 'materialize'; } else if (obj.common.adminTab) { obj.common.adminUI = obj.common.adminUI || {}; obj.common.adminUI.tab = 'html'; } obj.common.adminUI && console.debug("Please add to \"".concat(obj._id.replace(/\.\d+$/, ''), "\" common.adminUI=").concat(JSON.stringify(obj.common.adminUI))); } return obj; } var Connection = /*#__PURE__*/function () { /** * @param {import('./types').ConnectionProps} props */ function Connection(props) { var _this = this; (0, _classCallCheck2["default"])(this, Connection); props = props || { protocol: window.location.protocol, host: window.location.hostname }; this.props = props; this.autoSubscribes = this.props.autoSubscribes || []; this.autoSubscribeLog = this.props.autoSubscribeLog; this.props.protocol = this.props.protocol || window.location.protocol; this.props.host = this.props.host || window.location.hostname; this.props.port = this.props.port || (window.location.port === '3000' ? Connection.isWeb() ? 8082 : 8081 : window.location.port); this.props.ioTimeout = Math.max(this.props.ioTimeout || 20000, 20000); this.props.cmdTimeout = Math.max(this.props.cmdTimeout || 5000, 5000); // breaking change. Do not load all objects by default is true this.doNotLoadAllObjects = this.props.doNotLoadAllObjects === undefined ? true : this.props.doNotLoadAllObjects; this.doNotLoadACL = this.props.doNotLoadACL === undefined ? true : this.props.doNotLoadACL; /** @type {Record<string, ioBroker.State>} */ this.states = {}; this.objects = null; this.acl = null; this.firstConnect = true; this.waitForRestart = false; /** @type {ioBroker.Languages} */ this.systemLang = 'en'; this.connected = false; this._waitForFirstConnection = new Promise(function (resolve) { _this._waitForFirstConnectionResolve = resolve; }); /** @type {Record<string, { reg: RegExp; cbs: ioBroker.StateChangeHandler[]}>} */ this.statesSubscribes = {}; // subscribe for states /** @type {Record<string, { reg: RegExp; cbs: import('./types').ObjectChangeHandler[]}>} */ this.objectsSubscribes = {}; // subscribe for objects this.onProgress = this.props.onProgress || function () {}; this.onError = this.props.onError || function (err) { console.error(err); }; this.loaded = false; this.loadTimer = null; this.loadCounter = 0; this.admin5only = this.props.admin5only || false; /** @type {((connected: boolean) => void)[]} */ this.onConnectionHandlers = []; /** @type {((message: string) => void)[]} */ this.onLogHandlers = []; /** @type {Record<string, Promise<any>>} */ this._promises = {}; this.log.error = function (text) { return _this.log(text, 'error'); }; this.log.warn = function (text) { return _this.log(text, 'warn'); }; this.log.info = function (text) { return _this.log(text, 'info'); }; this.log.debug = function (text) { return _this.log(text, 'debug'); }; this.log.silly = function (text) { return _this.log(text, 'silly'); }; this.startSocket(); } /** * Checks if this connection is running in a web adapter and not in an admin. * @returns {boolean} True if running in a web adapter or in a socketio adapter. */ (0, _createClass2["default"])(Connection, [{ key: "startSocket", value: /** * Starts the socket.io connection. * @returns {void} */ function startSocket() { var _this2 = this; // if socket io is not yet loaded if (typeof window.io === 'undefined') { // if in index.html the onLoad function not defined if (typeof window.registerSocketOnLoad !== 'function') { // poll if loaded this.scriptLoadCounter = this.scriptLoadCounter || 0; this.scriptLoadCounter++; if (this.scriptLoadCounter < 30) { // wait till the script loaded setTimeout(function () { return _this2.startSocket(); }, 100); return; } else { window.alert('Cannot load socket.io.js!'); } } else { // register on load window.registerSocketOnLoad(function () { return _this2.startSocket(); }); } return; } else { // socket was initialized, do not repeat if (this._socket) { return; } } var host = this.props.host; var port = this.props.port; var protocol = this.props.protocol.replace(':', ''); var path = window.location.pathname; if (window.location.hostname === 'iobroker.net' || window.location.hostname === 'iobroker.pro') { path = ''; } else { // if web adapter, socket io could be on other port or even host if (window.socketUrl) { var parts = window.socketUrl.split(':'); host = parts[0] || host; port = parts[1] || port; if (host.includes('://')) { parts = host.split('://'); protocol = parts[0]; host = parts[1]; } } // get current path var pos = path.lastIndexOf('/'); if (pos !== -1) { path = path.substring(0, pos + 1); } if (Connection.isWeb()) { // remove one level, like echarts, vis, .... We have here: '/echarts/' var _parts = path.split('/'); if (_parts.length > 2) { _parts.pop(); _parts.pop(); path = _parts.join('/'); if (!path.endsWith('/')) { path += '/'; } } } } var url = port ? "".concat(protocol, "://").concat(host, ":").concat(port).concat(path) : "".concat(protocol, "://").concat(host).concat(path); this._socket = window.io.connect(url, { path: path.endsWith('/') ? path + 'socket.io' : path + '/socket.io', query: 'ws=true', name: this.props.name, timeout: this.props.ioTimeout }); this._socket.on('connect', function (noTimeout) { // If the user is not admin it takes some time to install the handlers, because all rights must be checked if (noTimeout !== true) { setTimeout(function () { return _this2.getVersion().then(function (info) { var _info$version$split = info.version.split('.'), _info$version$split2 = (0, _slicedToArray2["default"])(_info$version$split, 3), major = _info$version$split2[0], minor = _info$version$split2[1], patch = _info$version$split2[2]; var v = parseInt(major, 10) * 10000 + parseInt(minor, 10) * 100 + parseInt(patch, 10); if (v < 40102) { _this2._authTimer = null; // possible this is old version of admin _this2.onPreConnect(false, false); } else { _this2._socket.emit('authenticate', function (isOk, isSecure) { return _this2.onPreConnect(isOk, isSecure); }); } }); }, 500); } else { // iobroker websocket waits, till all handlers are installed _this2._socket.emit('authenticate', function (isOk, isSecure) { return _this2.onPreConnect(isOk, isSecure); }); } }); this._socket.on('reconnect', function () { _this2.onProgress(PROGRESS.READY); _this2.connected = true; if (_this2.waitForRestart) { window.location.reload(false); } else { _this2._subscribe(true); _this2.onConnectionHandlers.forEach(function (cb) { return cb(true); }); } }); this._socket.on('disconnect', function () { _this2.connected = false; _this2.subscribed = false; _this2.onProgress(PROGRESS.CONNECTING); _this2.onConnectionHandlers.forEach(function (cb) { return cb(false); }); }); this._socket.on('reauthenticate', function () { return _this2.authenticate(); }); this._socket.on('log', function (message) { _this2.props.onLog && _this2.props.onLog(message); _this2.onLogHandlers.forEach(function (cb) { return cb(message); }); }); this._socket.on('error', function (err) { var _err = err || ''; if (typeof _err.toString !== 'function') { _err = JSON.stringify(_err); console.error("Received strange error: ".concat(_err)); } _err = _err.toString(); if (_err.includes('User not authorized')) { _this2.authenticate(); } else { window.alert("Socket Error: ".concat(err)); } }); this._socket.on('connect_error', function (err) { return console.error("Connect error: ".concat(err)); }); this._socket.on('permissionError', function (err) { return _this2.onError({ message: 'no permission', operation: err.operation, type: err.type, id: err.id || '' }); }); this._socket.on('objectChange', function (id, obj) { return setTimeout(function () { return _this2.objectChange(id, obj); }, 0); }); this._socket.on('stateChange', function (id, state) { return setTimeout(function () { return _this2.stateChange(id, state); }, 0); }); this._socket.on('cmdStdout', function (id, text) { return _this2.onCmdStdoutHandler && _this2.onCmdStdoutHandler(id, text); }); this._socket.on('cmdStderr', function (id, text) { return _this2.onCmdStderrHandler && _this2.onCmdStderrHandler(id, text); }); this._socket.on('cmdExit', function (id, exitCode) { return _this2.onCmdExitHandler && _this2.onCmdExitHandler(id, exitCode); }); } /** * Called internally. * @private * @param {boolean} isOk * @param {boolean} isSecure */ }, { key: "onPreConnect", value: function onPreConnect(isOk, isSecure) { var _this3 = this; if (this._authTimer) { clearTimeout(this._authTimer); this._authTimer = null; } this.connected = true; this.isSecure = isSecure; if (this.waitForRestart) { window.location.reload(false); } else { if (this.firstConnect) { // retry strategy this.loadTimer = setTimeout(function () { _this3.loadTimer = null; _this3.loadCounter++; if (_this3.loadCounter < 10) { _this3.onConnect(); } }, 1000); if (!this.loaded) { this.onConnect(); } } else { this.onProgress(PROGRESS.READY); } this._subscribe(true); this.onConnectionHandlers.forEach(function (cb) { return cb(true); }); } if (this._waitForFirstConnectionResolve) { this._waitForFirstConnectionResolve(); this._waitForFirstConnectionResolve = null; } } /** * Checks if the socket is connected. * @returns {boolean} true if connected. */ }, { key: "isConnected", value: function isConnected() { return this.connected; } /** * Checks if the socket is connected. * @returns {Promise<void>} Promise resolves if once connected. */ }, { key: "waitForFirstConnection", value: function waitForFirstConnection() { return this._waitForFirstConnection; } /** * Called internally. * @private */ }, { key: "_getUserPermissions", value: function _getUserPermissions(cb) { if (this.doNotLoadACL) { return cb && cb(); } else { this._socket.emit('getUserPermissions', cb); } } /** * Called internally. * @private */ }, { key: "onConnect", value: function onConnect() { var _this4 = this; this._getUserPermissions(function (err, acl) { if (err) { return _this4.onError('Cannot read user permissions: ' + err); } else if (!_this4.doNotLoadACL) { if (_this4.loaded) { return; } _this4.loaded = true; clearTimeout(_this4.loadTimer); _this4.loadTimer = null; _this4.onProgress(PROGRESS.CONNECTED); _this4.firstConnect = false; _this4.acl = acl; } // Read system configuration return (_this4.admin5only && !window.vendorPrefix ? _this4.getCompactSystemConfig() : _this4.getSystemConfig()).then(function (data) { if (_this4.doNotLoadACL) { if (_this4.loaded) { return undefined; } _this4.loaded = true; clearTimeout(_this4.loadTimer); _this4.loadTimer = null; _this4.onProgress(PROGRESS.CONNECTED); _this4.firstConnect = false; } _this4.systemConfig = data; if (_this4.systemConfig && _this4.systemConfig.common) { _this4.systemLang = _this4.systemConfig.common.language; } else { _this4.systemLang = window.navigator.userLanguage || window.navigator.language; if (_this4.systemLang !== 'en' && _this4.systemLang !== 'de' && _this4.systemLang !== 'ru') { _this4.systemConfig.common.language = 'en'; _this4.systemLang = 'en'; } } _this4.props.onLanguage && _this4.props.onLanguage(_this4.systemLang); if (!_this4.doNotLoadAllObjects) { return _this4.getObjects().then(function () { _this4.onProgress(PROGRESS.READY); _this4.props.onReady && _this4.props.onReady(_this4.objects); }); } else { _this4.objects = _this4.admin5only ? {} : { 'system.config': data }; _this4.onProgress(PROGRESS.READY); _this4.props.onReady && _this4.props.onReady(_this4.objects); } return undefined; })["catch"](function (e) { return _this4.onError('Cannot read system config: ' + e); }); }); } /** * Called internally. * @private */ }, { key: "authenticate", value: function authenticate() { if (window.location.search.includes('&href=')) { window.location = "".concat(window.location.protocol, "//").concat(window.location.host).concat(window.location.pathname).concat(window.location.search).concat(window.location.hash); } else { window.location = "".concat(window.location.protocol, "//").concat(window.location.host).concat(window.location.pathname, "?login&href=").concat(window.location.search).concat(window.location.hash); } } /** * Subscribe to changes of the given state. * @param {string} id The ioBroker state ID. * @param {ioBroker.StateChangeHandler} cb The callback. */ /** * Subscribe to changes of the given state. * @param {string} id The ioBroker state ID. * @param {boolean} binary Set to true if the given state is binary and requires Base64 decoding. * @param {ioBroker.StateChangeHandler} cb The callback. */ }, { key: "subscribeState", value: function subscribeState(id, binary, cb) { if (typeof binary === 'function') { cb = binary; binary = false; } if (!this.statesSubscribes[id]) { var reg = id.replace(/\./g, '\\.').replace(/\*/g, '.*').replace(/\(/g, '\\(').replace(/\)/g, '\\)').replace(/\+/g, '\\+').replace(/\[/g, '\\['); if (reg.indexOf('*') === -1) { reg += '$'; } this.statesSubscribes[id] = { reg: new RegExp(reg), cbs: [] }; this.statesSubscribes[id].cbs.push(cb); if (this.connected) { this._socket.emit('subscribe', id); } } else { !this.statesSubscribes[id].cbs.includes(cb) && this.statesSubscribes[id].cbs.push(cb); } if (typeof cb === 'function' && this.connected) { if (binary) { this.getBinaryState(id).then(function (base64) { return cb(id, base64); })["catch"](function (e) { return console.error("Cannot getForeignStates \"".concat(id, "\": ").concat(JSON.stringify(e))); }); } else { if (Connection.isWeb()) { this._socket.emit('getStates', id, function (err, states) { err && console.error("Cannot getForeignStates \"".concat(id, "\": ").concat(JSON.stringify(err))); states && Object.keys(states).forEach(function (id) { return cb(id, states[id]); }); }); } else { this._socket.emit('getForeignStates', id, function (err, states) { err && console.error("Cannot getForeignStates \"".concat(id, "\": ").concat(JSON.stringify(err))); states && Object.keys(states).forEach(function (id) { return cb(id, states[id]); }); }); } } } } /** * Unsubscribes all callbacks from changes of the given state. * @param {string} id The ioBroker state ID. */ /** * Unsubscribes the given callback from changes of the given state. * @param {string} id The ioBroker state ID. * @param {ioBroker.StateChangeHandler} cb The callback. */ }, { key: "unsubscribeState", value: function unsubscribeState(id, cb) { if (this.statesSubscribes[id]) { if (cb) { var pos = this.statesSubscribes[id].cbs.indexOf(cb); pos !== -1 && this.statesSubscribes[id].cbs.splice(pos, 1); } else { this.statesSubscribes[id].cbs = []; } if (!this.statesSubscribes[id].cbs || !this.statesSubscribes[id].cbs.length) { delete this.statesSubscribes[id]; this.connected && this._socket.emit('unsubscribe', id); } } } /** * Subscribe to changes of the given object. * @param {string} id The ioBroker object ID. * @param {import('./types').ObjectChangeHandler} cb The callback. * @returns {Promise<void>} */ }, { key: "subscribeObject", value: function subscribeObject(id, cb) { if (!this.objectsSubscribes[id]) { var reg = id.replace(/\./g, '\\.').replace(/\*/g, '.*'); if (!reg.includes('*')) { reg += '$'; } this.objectsSubscribes[id] = { reg: new RegExp(reg), cbs: [] }; this.objectsSubscribes[id].cbs.push(cb); this.connected && this._socket.emit('subscribeObjects', id); } else { !this.objectsSubscribes[id].cbs.includes(cb) && this.objectsSubscribes[id].cbs.push(cb); } return Promise.resolve(); } /** * Unsubscribes all callbacks from changes of the given object. * @param {string} id The ioBroker object ID. * @returns {Promise<void>} */ /** * Unsubscribes the given callback from changes of the given object. * @param {string} id The ioBroker object ID. * @param {import('./types').ObjectChangeHandler} cb The callback. * @returns {Promise<void>} */ }, { key: "unsubscribeObject", value: function unsubscribeObject(id, cb) { if (this.objectsSubscribes[id]) { if (cb) { var pos = this.objectsSubscribes[id].cbs.indexOf(cb); pos !== -1 && this.objectsSubscribes[id].cbs.splice(pos, 1); } else { this.objectsSubscribes[id].cbs = []; } if (this.connected && (!this.objectsSubscribes[id].cbs || !this.objectsSubscribes[id].cbs.length)) { delete this.objectsSubscribes[id]; this.connected && this._socket.emit('unsubscribeObjects', id); } } return Promise.resolve(); } /** * Called internally. * @private * @param {string} id * @param {ioBroker.Object | null | undefined} obj */ }, { key: "objectChange", value: function objectChange(id, obj) { var _this5 = this; // update main.objects cache if (!this.objects) { return; } /** @type {import("./types").OldObject} */ var oldObj; var changed = false; if (obj) { if (obj._rev && this.objects[id]) { this.objects[id]._rev = obj._rev; } if (this.objects[id]) { oldObj = { _id: id, type: this.objects[id].type }; } if (!this.objects[id] || JSON.stringify(this.objects[id]) !== JSON.stringify(obj)) { this.objects[id] = obj; changed = true; } } else if (this.objects[id]) { oldObj = { _id: id, type: this.objects[id].type }; delete this.objects[id]; changed = true; } Object.keys(this.objectsSubscribes).forEach(function (_id) { if (_id === id || _this5.objectsSubscribes[_id].reg.test(id)) { //@ts-ignore _this5.objectsSubscribes[_id].cbs.forEach(function (cb) { return cb(id, obj, oldObj); }); } }); if (changed && this.props.onObjectChange) { this.props.onObjectChange(id, obj); } } /** * Called internally. * @private * @param {string} id * @param {ioBroker.State | null | undefined} state */ }, { key: "stateChange", value: function stateChange(id, state) { for (var task in this.statesSubscribes) { if (this.statesSubscribes.hasOwnProperty(task) && this.statesSubscribes[task].reg.test(id)) { this.statesSubscribes[task].cbs.forEach(function (cb) { return cb(id, state); }); } } } /** * Gets all states. * @param {boolean} disableProgressUpdate don't call onProgress() when done * @returns {Promise<Record<string, ioBroker.State>>} */ }, { key: "getStates", value: function getStates(disableProgressUpdate) { var _this6 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve, reject) { return _this6._socket.emit('getStates', function (err, res) { _this6.states = res; //@ts-ignore !disableProgressUpdate && _this6.onProgress(PROGRESS.STATES_LOADED); return err ? reject(err) : resolve(_this6.states); }); }); } /** * Gets the given state. * @param {string} id The state ID. * @returns {Promise<ioBroker.State | null | undefined>} */ }, { key: "getState", value: function getState(id) { var _this7 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve, reject) { return _this7._socket.emit('getState', id, function (err, state) { return err ? reject(err) : resolve(state); }); }); } /** * Gets the given binary state. * @param {string} id The state ID. * @returns {Promise<Buffer | undefined>} */ }, { key: "getBinaryState", value: function getBinaryState(id) { var _this8 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } // the data will come in base64 return new Promise(function (resolve, reject) { return _this8._socket.emit('getBinaryState', id, function (err, state) { return err ? reject(err) : resolve(state); }); }); } /** * Sets the given binary state. * @param {string} id The state ID. * @param {string} base64 The Base64 encoded binary data. * @returns {Promise<void>} */ }, { key: "setBinaryState", value: function setBinaryState(id, base64) { var _this9 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } // the data will come in base64 return new Promise(function (resolve, reject) { return _this9._socket.emit('setBinaryState', id, base64, function (err) { return err ? reject(err) : resolve(); }); }); } /** * Sets the given state value. * @param {string} id The state ID. * @param {string | number | boolean | ioBroker.State | ioBroker.SettableState | null} val The state value. * @returns {Promise<void>} */ }, { key: "setState", value: function setState(id, val) { var _this10 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve, reject) { return _this10._socket.emit('setState', id, val, function (err) { return err ? reject(err) : resolve(); }); }); } /** * Gets all objects. * @param {(objects?: Record<string, ioBroker.Object>) => void} update Callback that is executed when all objects are retrieved. * @returns {void} */ /** * Gets all objects. * @param {boolean} update Set to true to retrieve all objects from the server (instead of using the local cache). * @param {boolean} disableProgressUpdate don't call onProgress() when done * @returns {Promise<Record<string, ioBroker.Object>> | undefined} */ }, { key: "getObjects", value: function getObjects(update, disableProgressUpdate) { var _this11 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } else { return new Promise(function (resolve, reject) { if (!update && _this11.objects) { return resolve(_this11.objects); } _this11._socket.emit(Connection.isWeb() ? 'getObjects' : 'getAllObjects', function (err, res) { _this11.objects = res; disableProgressUpdate && _this11.onProgress(PROGRESS.OBJECTS_LOADED); err ? reject(err) : resolve(_this11.objects); }); }); } } /** * Called internally. * @private * @param {boolean} isEnable */ }, { key: "_subscribe", value: function _subscribe(isEnable) { var _this12 = this; if (isEnable && !this.subscribed) { this.subscribed = true; this.autoSubscribes.forEach(function (id) { return _this12._socket.emit('subscribeObjects', id); }); // re subscribe objects Object.keys(this.objectsSubscribes).forEach(function (id) { return _this12._socket.emit('subscribeObjects', id); }); // re-subscribe logs this.autoSubscribeLog && this._socket.emit('requireLog', true); // re subscribe states Object.keys(this.statesSubscribes).forEach(function (id) { return _this12._socket.emit('subscribe', id); }); } else if (!isEnable && this.subscribed) { this.subscribed = false; // un-subscribe objects this.autoSubscribes.forEach(function (id) { return _this12._socket.emit('unsubscribeObjects', id); }); Object.keys(this.objectsSubscribes).forEach(function (id) { return _this12._socket.emit('unsubscribeObjects', id); }); // un-subscribe logs this.autoSubscribeLog && this._socket.emit('requireLog', false); // un-subscribe states Object.keys(this.statesSubscribes).forEach(function (id) { return _this12._socket.emit('unsubscribe', id); }); } } /** * Requests log updates. * @param {boolean} isEnabled Set to true to get logs. * @returns {Promise<void>} */ }, { key: "requireLog", value: function requireLog(isEnabled) { var _this13 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve, reject) { return _this13._socket.emit('requireLog', isEnabled, function (err) { return err ? reject(err) : resolve(); }); }); } /** * Deletes the given object. * @param {string} id The object ID. * @param {boolean} maintenance Force deletion of non conform IDs. * @returns {Promise<void>} */ }, { key: "delObject", value: function delObject(id, maintenance) { var _this14 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve, reject) { return _this14._socket.emit('delObject', id, { maintenance: !!maintenance }, function (err) { return err ? reject(err) : resolve(); }); }); } /** * Deletes the given object and all its children. * @param {string} id The object ID. * @param {boolean} maintenance Force deletion of non conform IDs. * @returns {Promise<void>} */ }, { key: "delObjects", value: function delObjects(id, maintenance) { var _this15 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve, reject) { return _this15._socket.emit('delObjects', id, { maintenance: !!maintenance }, function (err) { return err ? reject(err) : resolve(); }); }); } /** * Sets the object. * @param {string} id The object ID. * @param {ioBroker.SettableObject} obj The object. * @returns {Promise<void>} */ }, { key: "setObject", value: function setObject(id, obj) { var _this16 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } if (!obj) { return Promise.reject('Null object is not allowed'); } obj = JSON.parse(JSON.stringify(obj)); if (obj.hasOwnProperty('from')) { delete obj.from; } if (obj.hasOwnProperty('user')) { delete obj.user; } if (obj.hasOwnProperty('ts')) { delete obj.ts; } return new Promise(function (resolve, reject) { return _this16._socket.emit('setObject', id, obj, function (err) { return err ? reject(err) : resolve(); }); }); } /** * Gets the object with the given id from the server. * @param {string} id The object ID. * @returns {ioBroker.GetObjectPromise} The object. */ }, { key: "getObject", value: function getObject(id) { var _this17 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve, reject) { return _this17._socket.emit('getObject', id, function (err, obj) { return err ? reject(err) : resolve(obj); }); }); } /** * Get all adapter instances. * @param {boolean} [update] Force update. * @returns {Promise<ioBroker.Object[]>} */ /** * Get all instances of the given adapter. * @param {string} adapter The name of the adapter. * @param {boolean} [update] Force update. * @returns {Promise<ioBroker.Object[]>} */ }, { key: "getAdapterInstances", value: function getAdapterInstances(adapter, update) { var _this18 = this; if (typeof adapter === 'boolean') { update = adapter; adapter = ''; } adapter = adapter || ''; if (!update && this._promises['instances_' + adapter]) { return this._promises['instances_' + adapter]; } if (!this.connected) { return Promise.reject(NOT_CONNECTED); } this._promises['instances_' + adapter] = new Promise(function (resolve, reject) { var timeout = setTimeout(function () { timeout = null; _this18.getObjectView("system.adapter.".concat(adapter ? adapter + '.' : ''), "system.adapter.".concat(adapter ? adapter + '.' : '', "\u9999"), 'instance').then(function (items) { return resolve(Object.keys(items).map(function (id) { return fixAdminUI(items[id]); })); })["catch"](function (e) { return reject(e); }); }, TIMEOUT_FOR_ADMIN4); _this18._socket.emit('getAdapterInstances', adapter, function (err, instances) { if (timeout) { clearTimeout(timeout); timeout = null; return err ? reject(err) : resolve(instances); } }); }); return this._promises['instances_' + adapter]; } /** * Get all adapters. * @param {boolean} [update] Force update. * @returns {Promise<ioBroker.Object[]>} */ /** * Get adapters with the given name. * @param {string} adapter The name of the adapter. * @param {boolean} [update] Force update. * @returns {Promise<ioBroker.Object[]>} */ }, { key: "getAdapters", value: function getAdapters(adapter, update) { var _this19 = this; if (Connection.isWeb()) { return Promise.reject('Allowed only in admin'); } if (typeof adapter === 'boolean') { update = adapter; adapter = ''; } adapter = adapter || ''; if (!update && this._promises['adapter_' + adapter]) { return this._promises['adapter_' + adapter]; } if (!this.connected) { return Promise.reject(NOT_CONNECTED); } this._promises['adapter_' + adapter] = new Promise(function (resolve, reject) { var timeout = setTimeout(function () { timeout = null; _this19.getObjectView("system.adapter.".concat(adapter, "."), "system.adapter.".concat(adapter, ".\u9999"), 'adapter').then(function (items) { return resolve(Object.keys(items).map(function (id) { return fixAdminUI(items[id]); })); })["catch"](function (e) { return reject(e); }); }, TIMEOUT_FOR_ADMIN4); _this19._socket.emit('getAdapters', adapter, function (err, adapters) { if (timeout) { clearTimeout(timeout); timeout = null; return err ? reject(err) : resolve(adapters); } }); }); return this._promises['adapter_' + adapter]; } /** * Called internally. * @private * @param {any[]} objs * @param {(err?: any) => void} cb */ }, { key: "_renameGroups", value: function _renameGroups(objs, cb) { var _this20 = this; if (!objs || !objs.length) { cb && cb(); } else { var obj = objs.pop(); var oldId = obj._id; obj._id = obj.newId; delete obj.newId; this.setObject(obj._id, obj).then(function () { return _this20.delObject(oldId); }).then(function () { return setTimeout(function () { return _this20._renameGroups(objs, cb); }, 0); })["catch"](function (err) { return cb && cb(err); }); } } /** * Rename a group. * @param {string} id The id. * @param {string} newId The new id. * @param {string | { [lang in ioBroker.Languages]?: string; }} newName The new name. */ }, { key: "renameGroup", value: function renameGroup(id, newId, newName) { var _this21 = this; if (Connection.isWeb()) { return Promise.reject('Allowed only in admin'); } return this.getGroups(true).then(function (groups) { if (groups.length) { // find all elements var groupsToRename = groups.filter(function (group) { return group._id.startsWith(id + '.'); }); groupsToRename.forEach(function (group) { return group.newId = newId + group._id.substring(id.length); }); return new Promise(function (resolve, reject) { return _this21._renameGroups(groupsToRename, function (err) { return err ? reject(err) : resolve(); }); }).then(function () { var obj = groups.find(function (group) { return group._id === id; }); if (obj) { obj._id = newId; if (newName !== undefined) { obj.common = obj.common || {}; obj.common.name = newName; } return _this21.setObject(obj._id, obj).then(function () { return _this21.delObject(id); }); } }); } }); } /** * Sends a message to a specific instance or all instances of some specific adapter. * @param {string} instance The instance to send this message to. * @param {string} [command] Command name of the target instance. * @param {ioBroker.MessagePayload} [data] The message data to send. * @returns {Promise<ioBroker.Message | undefined>} */ }, { key: "sendTo", value: function sendTo(instance, command, data) { var _this22 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve) { return _this22._socket.emit('sendTo', instance, command, data, function (result) { return resolve(result); }); }); } /** * Extend an object and create it if it might not exist. * @param {string} id The id. * @param {ioBroker.PartialObject} obj The object. */ }, { key: "extendObject", value: function extendObject(id, obj) { var _this23 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } obj = JSON.parse(JSON.stringify(obj)); if (obj.hasOwnProperty('from')) { delete obj.from; } if (obj.hasOwnProperty('user')) { delete obj.user; } if (obj.hasOwnProperty('ts')) { delete obj.ts; } return new Promise(function (resolve, reject) { return _this23._socket.emit('extendObject', id, obj, function (err) { return err ? reject(err) : resolve(); }); }); } /** * Register a handler for log messages. * @param {(message: string) => void} handler The handler. */ }, { key: "registerLogHandler", value: function registerLogHandler(handler) { !this.onLogHandlers.includes(handler) && this.onLogHandlers.push(handler); } /** * Unregister a handler for log messages. * @param {(message: string) => void} handler The handler. */ }, { key: "unregisterLogHandler", value: function unregisterLogHandler(handler) { var pos = this.onLogHandlers.indexOf(handler); pos !== -1 && this.onLogHandlers.splice(pos, 1); } /** * Register a handler for the connection state. * @param {(connected: boolean) => void} handler The handler. */ }, { key: "registerConnectionHandler", value: function registerConnectionHandler(handler) { !this.onConnectionHandlers.includes(handler) && this.onConnectionHandlers.push(handler); } /** * Unregister a handler for the connection state. * @param {(connected: boolean) => void} handler The handler. */ }, { key: "unregisterConnectionHandler", value: function unregisterConnectionHandler(handler) { var pos = this.onConnectionHandlers.indexOf(handler); pos !== -1 && this.onConnectionHandlers.splice(pos, 1); } /** * Set the handler for standard output of a command. * @param {(id: string, text: string) => void} handler The handler. */ }, { key: "registerCmdStdoutHandler", value: function registerCmdStdoutHandler(handler) { this.onCmdStdoutHandler = handler; } /** * Unset the handler for standard output of a command. * @param {(id: string, text: string) => void} handler The handler. */ }, { key: "unregisterCmdStdoutHandler", value: function unregisterCmdStdoutHandler(handler) { this.onCmdStdoutHandler = null; } /** * Set the handler for standard error of a command. * @param {(id: string, text: string) => void} handler The handler. */ }, { key: "registerCmdStderrHandler", value: function registerCmdStderrHandler(handler) { this.onCmdStderrHandler = handler; } /** * Unset the handler for standard error of a command. * @param {(id: string, text: string) => void} handler The handler. */ }, { key: "unregisterCmdStderrHandler", value: function unregisterCmdStderrHandler(handler) { this.onCmdStderrHandler = null; } /** * Set the handler for exit of a command. * @param {(id: string, exitCode: number) => void} handler The handler. */ }, { key: "registerCmdExitHandler", value: function registerCmdExitHandler(handler) { this.onCmdExitHandler = handler; } /** * Unset the handler for exit of a command. * @param {(id: string, exitCode: number) => void} handler The handler. */ }, { key: "unregisterCmdExitHandler", value: function unregisterCmdExitHandler(handler) { this.onCmdExitHandler = null; } /** * Get all enums with the given name. * @param {string} [_enum] The name of the enum * @param {boolean} [update] Force update. * @returns {Promise<Record<string, ioBroker.Object>>} */ }, { key: "getEnums", value: function getEnums(_enum, update) { var _this24 = this; if (!update && this._promises['enums_' + (_enum || 'all')]) { return this._promises['enums_' + (_enum || 'all')]; } if (!this.connected) { return Promise.reject(NOT_CONNECTED); } this._promises['enums_' + (_enum || 'all')] = new Promise(function (resolve, reject) { _this24._socket.emit('getObjectView', 'system', 'enum', { startkey: 'enum.' + (_enum || ''), endkey: 'enum.' + (_enum ? _enum + '.' : '') + "\u9999" }, function (err, res) { if (!err && res) { var _res = {}; for (var i = 0; i < res.rows.length; i++) { if (_enum && res.rows[i].id === 'enum.' + _enum) { continue; } _res[res.rows[i].id] = res.rows[i].value; } resolve(_res); } else { reject(err); } }); }); return this._promises['enums_' + (_enum || 'all')]; } /** * Query a predefined object view. * @param {string} start The start ID. * @param {string} end The end ID. * @param {string} type The type of object. * @returns {Promise<Record<string, ioBroker.Object>>} */ }, { key: "getObjectView", value: function getObjectView(start, end, type) { var _this25 = this; if (!this.connected) { return Promise.reject(NOT_CONNECTED); } start = start || ''; end = end || "\u9999"; return new Promise(function (resolve, reject) { _this25._socket.emit('getObjectView', 'system', type, { startkey: start, endkey: end }, function (err, res) { if (!err) { var _res = {}; if (res && res.rows) { for (var i = 0; i < res.rows.length; i++) { _res[res.rows[i].id] = res.rows[i].value; } } resolve(_res); } else { reject(err); } }); }); } /** * Get the stored certificates. * @param {boolean} [update] Force update. * @returns {Promise<{name: string; type: 'public' | 'private' | 'chained'}[]>} */ }, { key: "getCertificates", value: function getCertificates(update) { if (Connection.isWeb()) { return Promise.reject('Allowed only in admin'); } if (this._promises.cert && !update) { return this._promises.cert; } if (!this.connected) { return Promise.reject(NOT_CONNECTED); } this._promises.cert = this.getObject('system.certificates').then(function (res) { var certs = []; if (res && res["native"] && res["native"].certificates) { Object.keys(res["native"].certificates).forEach(function (c) { var cert = res["native"].certificates[c]; if (!cert) { return; } var _cert = { name: c, type: '' }; // If it is filename, it could be everything if (cert.length < 700 && (cert.indexOf('/') !== -1 || cert.indexOf('\\') !== -1)) { if (c.toLowerCase().includes('private')) { _cert.type = 'private'; } else if (cert.toLowerCase().includes('private')) { _cert.type = 'private'; } else if (c.toLowerCase().includes('public')) { _cert.type = 'public'; } else if (cert.toLowerCase().includes('public')) { _cert.type = 'public'; } certs.push(_cert); } else { _cert.type = cert.substring(0, '-----BEGIN RSA PRIVATE KEY'.length) === '-----BEGIN RSA PRIVATE KEY' || cert.substring(0, '-----BEGIN PRIVATE KEY'.length) === '-----BEGIN PRIVATE KEY' ? 'private' : 'public'; if (_cert.type === 'public') { var m = cert.split('-----END CERTIFICATE-----'); if (m.filter(function (t) { return t.replace(/\r\n|\r|\n/, '').trim(); }).length > 1) { _cert.type = 'chained'; } } certs.push(_cert); } }); } return certs; }); return this._promises.cert; } /** * Get the logs from a host (only for admin connection). * @param {string} host * @param {number} [linesNumber] * @returns {Promise<string[]>} */ }, { key: "getLogs", value: function getLogs(host, linesNumber) { var _this26 = this; if (Connection.isWeb()) { return Promise.reject('Allowed only in admin'); } if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve) { return _this26._socket.emit('sendToHost', host, 'getLogs', linesNumber || 200, function (lines) { return resolve(lines); }); }); } /** * Get the log files (only for admin connection). * @returns {Promise<string[]>} */ }, { key: "getLogsFiles", value: function getLogsFiles(host) { var _this27 = this; if (Connection.isWeb()) { return Promise.reject('Allowed only in admin'); } if (!this.connected) { return Promise.reject(NOT_CONNECTED); } return new Promise(function (resolve, reject) { return _this27._socket.emit('readLogs', host, function (err, files) {