UNPKG

iobroker.systeminfo

Version:
805 lines (765 loc) 32.9 kB
/* eslint-disable no-useless-escape */ /** * iobroker bmw Adapter * (c) 2016- <frankjoke@hotmail.com> * MIT License */ /*jshint -W089, -W030, -W061, -W083 */ // jshint node:true, esversion:6, strict:true, undef:true, unused:true 'use strict'; //const utils = require('./lib/utils'); //const adapter = utils.Adapter('systeminfo'); const utils = require('@iobroker/adapter-core'); const adapter = utils.adapter('systeminfo'); //const dns = require('dns'); const assert = require('assert'); const A = require('./myAdapter'); const si = require('systeminformation'); const cheerio = require('cheerio'); const schedule = require('node-schedule'); const xml2js = require('xml2js'); const list = {}; const scheds = {}; const states = {}; const roleNames = ['number', 'switch', 'boolean', 'value.temperature', 'json', 'string']; const roleRoles = ['value', 'switch', 'value', 'value.temperature', 'value', 'value']; const roleTypes = ['number', 'boolean', 'boolean', 'number', 'string', 'string']; const reIsEvalWrite = /@\((.+)\)/; const reIsMultiName = /^([^\s,\[]+)\s*(\[\s*(\w+\s*\/\s*\w*|[^\s,\]]+(\s*\,\s*[^\s,\]]+)+|\*)\s*\]\s*)?(\S*)$/; const reIsInfoName = /^(\w*)\s*(\(\s*([^\(\),\s]+)\s*(\,\s*\S+\s*)*\))?$/; const reIsRegExp = /^\/(.*)\/([gimy])*$/; const reIsObjName = /\s*(\w+)\s*\/\s*(\w*)\s*/; const reStripPrefix = /(?!xmlns)^.*:/; const reIsSchedule = /^[\d\-\/\*\,]+(\s+[\d\/\-\*,]+){4,5}$/; const reIsTime = /^([\d\-\*\,\/]+)\s*:\s*([\d\-\*\,\/]+)\s*(?::\s*([\d\-\*\,\/]+))?$/; const reIsObject = /^\s*?\}.+\}$/; A.init(adapter, main); // associate adapter and main with MyAdapter function xmlParseString(body) { const valp = (str /* , name */) => !isNaN(str) ? A.number(str) : /^(?:true|false)$/i.test(str) ? str.toLowerCase() === 'true' : str, options = { explicitArray: false, explicitRoot: false, // ignoreAttrs: true, attrkey: '_', charkey: '#', childkey: '__', mergeAttrs: true, trim: true, // validator: (xpath, currentValue, newValue) => A.D(`${xpath}: ${currentValue} = ${newValue}`,newValue), // validator: (xpath, currentValue, newValue) => A.T(newValue,[]) && newValue.length==1 && A.T(newValue[0],[]) ? newValue[0] : newValue, // attrNameProcessors: [str => str === '$' ? '_' : str], tagNameProcessors: [(str) => str.replace(reStripPrefix, '')], // attrNameProcessors: [tagnames], attrValueProcessors: [valp], valueProcessors: [valp], }, parser = new xml2js.Parser(options).parseString; return A.c2p(parser)(body); } class Cache { constructor(fun) { // neue Einträge werden mit dieser Funktion kreiert assert(!fun || A.T(fun) === 'function', 'Cache arg need to be a function returning a promise!'); this._cache = {}; this._fun = fun; } get cache() { return this._cache; } get fun() { return this._fun; } set fun(newfun) { assert(!newfun || A.T(newfun) === 'function', 'Cache arg need to be a function returning a promise!'); // eslint-disable-next-line no-setter-return return (this._fun = newfun); } cacheItem(item, fun) { const that = this; assert(!fun || A.T(fun) === 'function', 'Cache arg need to be a function returning a promise!'); // A.D(`looking for ${item} in ${A.O(this._cache)}`); if (this._cache[item] !== undefined) return A.resolve(this._cache[item]); if (!fun) fun = this._fun; assert(A.T(fun) === 'function', `checkItem needs a function to fill cache!`); return fun(item).then( (res) => (that._cache[item] = res), (err) => A.D(`checkitem error ${err} finding result for ${item}`, null), ); } clear() { this._cache = {}; } isCached(x) { return this._cache[x]; } } class JsonPath { constructor(obj) { this.$ = A.T(obj, {}) || A.T(obj, []) ? A.clone(obj) : obj; } parse(expr) { this.result = []; if (this.$ && !expr) return [this.$]; else if (!this.$) return false; const subx = []; const subs = []; let res = expr.replace(/([^\\]"|^"|\\\\")(\\"|[^"])+?"/g, ($0) => { const t = $0[1] === '"' ? $0[0] : '', s = t === '' ? $0.slice(1, -1) : $0.slice(2, -1); return t + '__' + (subs.push(s.replace(/\\"/g, '"')) - 1) + '__'; }); res = res.replace(/\s*([\w\$]+)\s*(\.\s*(\w|$)+\s*)*/g, (_0) => _0 .split('.') .map((a) => a.trim()) .join('\\§'), ); res = res.replace( /\[([\?\!]?\(.+?\))\]/g, ($0, $1) => '[#' + (subx.push($1.trim().replace(/,/g, '\\#').replace(/\./g, '\\§')) - 1), ); res = res.replace(/(\.|\];?)?\s*(\[|\];?|\]\s*\[)/g, ';'); res = res.replace(/\.\.\.|\.\./g, ';;'); res = res.replace(/;;;|;;/g, ';..;'); res = res.replace(/;$|\]$/g, ''); res = res.replace(/#(\d+?)/g, ($0, $1) => subx[$1]); res = res.replace(/__(\d+?)__/g, ($0, $1) => subs[$1]); res = res.replace(/\\§/g, '.'); res = res.replace(/\\#/g, ','); res = res.replace(/^\$;/, ''); // A.D(`normalized= ${res}`, res); this.trace( res .split(';') .map((s) => s.trim()) .filter((s) => s.length), this.$, '$', ); return this.result.length ? this.result : false; } myeval(x, _v /* , _vname */) { const $ = this.$; // A.D(`eval:[${x}]`); try { const res = $ && _v && eval(x.replace(/\\\#/g, ',').replace(/@/g, '_v')); // A.D(`eval:[${x}] returns ${res} and had ${A.O(_v)}`); return res; } catch (e) { throw new SyntaxError('jsonPath: ' + e.message + ': ' + x.replace(/@/g, '_v').replace(/\^/g, '_a')); } //return null; } trace(x, val) { function walk(loc, expr, val, f) { if (val instanceof Array) val.map((c, i) => (c !== undefined ? f(i, loc, expr, val) : null)); else if (typeof val === 'object') // eslint-disable-next-line no-prototype-builtins Object.keys(val).map((m) => (val.hasOwnProperty(m) ? f(m, loc, expr, val) : null)); } const that = this; if (!x || x.length === 0) return this.result.push(val); const loc = x.shift(); // A.D(`loc: '${loc}', x:[${x}], val:${val}`); if (loc === undefined || loc.length === 0) return this.trace(x, val); if (/^\(.*?\)$/.test(loc)) // [(expr)] this.trace([that.myeval(loc, val)].concat(x), val); else if (/^\?\(.*?\)$/.test(loc)) // [?(expr)] walk(loc, x, val, (m, l, x, v) => that.myeval(l.slice(2, -1), v[m]) ? that.trace([m].concat(x), v) : null, ); else if (/^\!\(.*?\)$/.test(loc)) { // [!(expr)] const res = that.myeval(loc.slice(2, -1), val); if (res) that.trace(Array.from(x), res); } else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) { // eslint-disable-next-line no-prototype-builtins const ov = Object.keys(val).filter((k) => val.hasOwnProperty(k)); const len = ov.length; let start = 0; let end = len; let step = 1; loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, function ($0, $1, $2, $3) { start = parseInt($1 || start); end = parseInt($2 || end); step = parseInt($3 || step); }); start = start < 0 ? Math.max(0, start + len) : Math.min(len, start); end = end < 0 ? Math.max(0, end + len) : Math.min(len, end); for (let j = start; j < end; j += step) this.trace([ov[j]].concat(x), val); // eslint-disable-next-line no-prototype-builtins } else if (val && val.hasOwnProperty(loc)) this.trace(Array.from(x), val[loc]); else if (/^\d+$/.test(loc)) { // eslint-disable-next-line no-prototype-builtins const ov = Object.keys(val).filter((k) => val.hasOwnProperty(k)); this.trace([ov[parseInt(loc)]].concat(x), val); } else if (/,/.test(loc)) { // [name1,name2,...] loc.split(',').map((s) => that.trace([s.trim()].concat(x), val)); } else if (/[\w\$]+\s*\.\s*[\w\$]+/.test(loc)) { const o = loc.split('.').map((s) => s.trim()); // eslint-disable-next-line no-prototype-builtins if (val && val.hasOwnProperty(o[0])) this.trace([o.slice(1).join('.')].concat(x), val[o[0]]); } else if (loc === '*') walk(loc, x, val, (m, l, x, v) => that.trace([m].concat(x), v)); else if (loc === '..') { // A.D(`I will do '..' on [${x}]`); this.trace(Array.from(x), val); walk(loc, x, val, (m, l, x, v) => (typeof v[m] === 'object' ? that.trace(['..'].concat(x), v[m]) : null)); } } } A.stateChange = function (id, state) { // A.D(`stateChange of "${id}": ${A.O(state)}`); if (!state.ack) { const nid = id.startsWith(A.ain) ? id.slice(A.ain.length) : id; // A.D(`Somebody (${state.from}) id0 ${id0} changed ${id} of "${id0}" to ${A.O(state)}`); return A.getObject(nid) .then((obj) => obj && obj.native && obj.native.si && obj.native.si.write ? writeInfo(nid, state) : Promise.reject(A.D(`Invalid stateChange for "${nid}"`)), ) .catch((err) => A.W(`Error in StateChange for ${nid}: ${A.O(err)}`)); } }; /* A.messages = (msg) => { if (A.T(msg.message) !== 'string') return A.W(`Wrong message received: ${A.O(msg)}`); const st = { val: true, ack: false, from: msg.from }; var id = msg.message.startsWith(A.ain) ? msg.message.trim() : A.ain + (msg.message.trim()); switch (msg.command) { case 'switch_off': st.val = false; /* falls through *-/ case 'switch_on': case 'send': return A.getObject(id) .then(obj => obj.common.role === 'button' || (obj.common.role === 'switch' && msg.command.startsWith('switch')) ? A.stateChange(id, st) : Promise.reject(A.W(`Wrong id or message ${A.O(msg)} id = ${A.O(obj)}`)), err => Promise.reject(err)) .then(() => A.D(`got message sent: ${msg.message}`)); case 'send_scene': return sendScene(msg.message, st); case 'send_code': if (msg.message.startsWith(A.ain)) msg.message = msg.message.slice(A.ain.length); let ids = msg.message.split('.'), code = ids[1]; id = ids[0]; if (!id.startsWith('RM:') || !scanList[id] || !code.startsWith(codeName)) return Promise.reject(A.D(`Invalid message "${msg.message}" for "send" to ${id}${sendName}`)); return Promise.resolve(A.D(`Executed on ${id} the message "${msg.message}"`), sendCode(scanList[id], code)); case 'get': return A.getState(id); case 'switch': let idx = A.split(msg.message, '='); if (idx.length !== 2 && !idx.startsWith('SP:')) return Promise.reject(A.D(`Invalid message to "switch" ${msg.message}" to ${idx}`)); st.val = A.parseLogic(idx[1]); return A.stateChange(idx[0], st); default: return Promise.reject(A.D(`Invalid command "${msg.command}" received with message ${A.O(msg)}`)); } }; */ class WebQuery { constructor(item) { this._$ = A.T(item, '') ? cheerio.load(item) : item; } static eval(fun, _v, con) { try { const res = eval(fun.replace(/@/g, '_v')); // A.D(`eval:[${x}] returns ${res} and had ${A.O(_v)}`); return res; } catch (e) { throw new SyntaxError(`eval: ${e.message}: ${fun} on ${_v} with ${con}`); } } handle(opt, con) { // A.D(A.O(opt)); function norm(opt, name) { const copt = {}; if (A.T(opt, '')) { copt._sel = opt; opt = copt; } else if (!A.T(opt, {})) return copt; for (const i of A.ownKeys(opt)) if (!i.startsWith('_')) copt[i] = opt[i]; if (opt._notrim) copt._notrim = opt._notrim; if (name || opt._name) copt._name = name ? name : opt._name; if (opt._conv) copt._conv = opt._conv; if (opt._filter) copt._filter = opt._filter; if (opt._fun) copt._fun = opt._fun; if (opt._eq) copt._eq = opt._eq; if (opt._sel) copt._sel = opt._sel; return copt; } opt = norm(opt); const data = {}; let name; let res = opt._sel ? this._$(opt._sel, con) : con ? con : this._$('body', con); if (A.T(opt._eq, 0)) res = res.eq(opt._eq); for (name of A.ownKeys(opt)) { if (name.startsWith('_')) continue; const nopt = norm(opt[name]); const m = name.match(/^([$\w]*)(\!?)\[(.+)\]$/); let docs; let items; let item; if (m) { const nam = m[1].trim(); const ex = m[2]; let sel = m[3].trim(); const eq = parseInt(sel).toString() === sel; if (nam) name = nam; if (!ex && eq) { sel = parseInt(sel); const r = res.eq(sel); if (nam) { data[nam] = this.handle(nopt, r); continue; } else res = r; } else if (ex) { const r = WebQuery.eval(sel, res, con); if (nam) { data[nam] = this.handle(nopt, r); continue; } else res = r; } else { docs = data[name] = []; items = this._$(sel, res); if (items.length === 1) { const r = res.eq(0); if (nam) { data[nam] = this.handle(nopt, r); continue; } else res = r; } else for (let i = 0; i < items.length; ++i) { item = items.eq(i); if ( opt._filter === undefined || (A.T(opt._filter) === 'function' && opt._filter(item, this._$)) || (A.T(opt._filter, '') && WebQuery.eval(opt._filter, item, con)) ) { const cdoc = this.handle(nopt, item); docs.push(cdoc); } } continue; } } else { data[name] = this.handle(nopt, res); } } if (A.ownKeys(data).length) return data; let value = typeof opt._fun === 'function' ? opt._fun(res) : A.T(opt._fun, '') ? WebQuery.eval(opt._fun, res) : res && res.text ? res.text() : res; if (!opt._notrim && A.T(value, '')) value = value.trim().replace(/[\s\n]+/g, ' '); if (opt._conv) { if (typeof opt._conv === 'function') value = opt._conv(value, res); else if (typeof opt._conv === 'string') value = WebQuery.eval(opt._conv, value, res); } return value; } } function writeInfo(id, state) { if (!states[id] || !states[id].wfun) return Promise.reject(`Err: no write function defined for ${id}!`); const obj = states[id]; let val = state.val; A.D(`new state:${A.O(state)} for ${id}`); switch (obj.wtext) { case 'eval': val = WebQuery.eval(obj.write, val); break; default: break; } return ( obj.wfun && obj .wfun(val) .then(() => A.makeState(id, state.val, true)) .catch((e) => A.W(`wfun err ${e}`)) ); } function setItem(item, name, value) { // A.D(`setItem ${name} to ${A.O(value)} with ${item.type};${item.conv};${item.role}`); if (!states[name]) states[name] = item; if (item.conv) switch (item.conv.trim().toLowerCase()) { case 'number': value = isNaN(value) ? NaN : A.number(value); break; case 'boolean': value = A.parseLogic(value); break; case '!boolean': value = !A.parseLogic(value); break; case 'html': case 'json': case 'xml': break; default: if (item.conv.indexOf('@') >= 0) try { value = WebQuery.eval(item.conv, value); } catch (e) { A.W(`convert '${item.conv}' for item ${name} failed with: ${e}`); } break; } const o = A.clone(item.opt); o.id = name; switch (A.T(value)) { case 'object': case 'array': value = JSON.stringify(value); break; case 'function': value = `${value}`; break; default: break; } return A.makeState(o, value, true); } function doPoll(plist) { function idid(id, n) { return id.pre + n + id.post; } if (!plist) { plist = []; for (const k of A.ownKeys(list)) plist = plist.concat(list[k]); } if (plist.length === 0) return; // A.D(`I should poll ${plist.map(x => x.id)} now!`); const caches = {}; return A.seriesOf( plist, (item) => { if (!item.fun) return Promise.reject(`Undefined function in ${A.O(item)}`); const ca = item.type + item.source; if (!caches[ca]) caches[ca] = new Cache(); return caches[ca] .cacheItem(ca, item.fun) .then((res) => { let ma; let jp; const id = item.id; let typ = item.type; return A.resolve(res) .then( (res) => { switch (item.conv) { case 'html': typ = 'info'; if (A.T(item.regexp, {})) { res = item.regexp.conv(res); } break; case 'json': res = A.J(res); typ = 'info'; break; case 'xml': typ = 'info'; return xmlParseString(res).then((json) => ((res = json), json)); default: if ((jp = item.conv.match(reIsRegExp))) { jp = new RegExp(jp[1], jp[2]); jp = res.match(jp); res = jp ? jp[1] : res; // } else if ((jp = item.conv.match(reIsEvalWrite))) { // res = WebQuery.eval(jp[1],res); } break; } return res; }, (e) => A.W(`Error ${e} in doPoll for ${item.name}`), ) .then((res) => { A.D(`${item.name} received ${A.O(res, 1)}`); if (typ === 'info') { jp = new JsonPath(res); ma = jp.parse(item.conv === 'html' ? item.regexp.selection : item.regexp); if (ma && ma.length > 0) res = ma; } else { ma = item.regexp && res.match(item.regexp); ma = ma && ma.length > 1 ? ma.slice(1) : null; res = ma ? ma : res; } if (ma && A.T(item.id, {})) { const mat = A.T(ma[0]); if (mat === 'object' && ma.length === 1 && id.mid === '*') { ma = ma[0]; return A.seriesOf( // eslint-disable-next-line no-prototype-builtins Object.keys(ma).filter((x) => ma.hasOwnProperty(x)), (i) => setItem(item, idid(id, i.replace(/[\. ]/g, '_')), ma[i]), 1, ); } if (id.name && mat === 'object') { if (!id.value) return A.seriesOf( ma, (o) => A.T(o, {}) ? A.seriesOf( // eslint-disable-next-line no-prototype-builtins Object.keys(o).filter((x) => o.hasOwnProperty(x)), (i) => i !== id.name ? setItem( item, idid( id, o[id.name].replace(/[\. ]/g, '_') + '.' + i, ), o[i], ) : A.resolve(), 1, ) : A.resolve(), 1, ); return A.seriesOf( ma, (o) => setItem(item, idid(id, o[id.name].replace(/[\. ]/g, '_')), o[id.value]), 1, ); } // if (io && A.T(item.id.mid, [])) { if (id.mid === '*') return A.seriesIn(ma, (i) => setItem(item, idid(id, i), ma[parseInt(i)]), 1); if (A.T(item.id.mid, [])) return A.seriesIn( item.id.mid, (i) => { i = parseInt(i); return setItem(item, idid(id, id.mid[i]), ma[i]); }, 1, ); res = ma; } else if (ma) res = A.T(ma, []) && ma.length === 1 && typ === 'info' ? ma[0] : ma; if (A.T(item.id, '')) return setItem(item, item.id, res); return setItem(item, idid(id, '?'), res); }); }) .catch((e) => A.W(`Error ${e} in doPoll for ${item.name}`)); }, 1, ); } function main() { A.I(`Startup Systeminfo Adapter ${A.ains}: ${A.O(adapter.config)}`); if ((A.debug = adapter.config.startup.startsWith('debug!'))) adapter.config.startup = adapter.config.startup.slice(A.D(`Debug mode on!`, 6)); for (const item of adapter.config.items) { if (item.name.startsWith('-')) continue; const ni = A.clone(item); const ir = item.name.trim().match(reIsMultiName); if (!ir) { A.W(`Invalid item name in ${A.O(item)}`); continue; } if (ir[2]) { const irn = { pre: ir[1], post: ir[5], mid: ir[3], }; if (irn.mid !== '*') { const on = irn.mid.match(reIsObjName); if (on) { irn.name = on[1]; irn.value = on[2] !== '' ? on[2] : null; } else { irn.mid = A.trim(irn.mid.split(',')); } } ni.id = irn; } else ni.id = ir[1]; ni.write = ni.write && ni.write.trim(); ni.source = ni.source.trim(); ni.conv = ni.conv && ni.conv.trim(); const ra = A.trim(A.T(ni.role, '') ? ni.role.split('|') : 'value'), unit = ra.length > 1 ? ra[1] : undefined, rr = ra[0], ri = roleNames.indexOf(rr), role = ri >= 0 ? roleRoles[ri] : 'value', type = ri >= 0 ? roleTypes[ri] : 'string', opt = { id: ni.id, state: 'state', write: item.write.trim().length > 0, type: type, role: role, unit: unit, native: {}, }; if (ni.type === 'info' || ni.conv === 'xml' || ni.conv === 'json' || ni.conv === 'html') ni.regexp = ni.regexp.trim(); else { try { let r = ni.regexp && ni.regexp.trim(); const m = r.match(reIsRegExp); let o; if (m) { r = m[1]; o = m[2]; } ni.regexp = r.length > 0 ? new RegExp(r, o ? o : undefined) : null; } catch (e) { A.W(`Error ${e} in RegExp of ${A.O(ni)}`); ni.regexp = null; } } opt.native.si = A.clone(ni); switch (ni.type) { case 'exec': ni.fun = () => A.exec(ni.source).then((x) => x.trim()); ni.wfun = (val) => { let w = ni.write; let e = w.match(reIsEvalWrite); while (e) { const a = WebQuery.eval(e[1], val); w = w.replace(reIsEvalWrite, a); e = w.match(reIsEvalWrite); } return A.exec(w).then(A.nop, A.D); }; break; case 'file': ni.fun = () => A.readFile(ni.source, 'utf8').then((x) => x.trim()); ni.wfun = (val) => A.writeFile(ni.source, val.toString(), 'utf8'); ni.wtext = 'eval'; break; case 'web': ni.fun = () => { let m = ni.source.match(reIsObject); if (m) { m = WebQuery.eval(ni.source); return A.request(m, m.data); } return A.request(ni.source); }; if (ni.conv === 'html' && /^\{.+\}$/.test(ni.regexp)) try { const cmd = WebQuery.eval('(' + ni.regexp + ')'), keys = A.ownKeys(cmd), sel = keys[0], webQuery = cmd[sel]; if (keys.length === 1 && A.T(webQuery, {})) ni.regexp = { selection: sel, conv: (res) => new WebQuery(res).handle(webQuery), }; else A.W(`Invalid Web query in regexp for ${ni.name}`); } catch (e) { A.W(`Invalid Web query for ${ni.name} cased error ${e}`); } break; case 'info': ni.fun = () => { const m = ni.source.match(reIsInfoName); if (!m) return A.W(`Invalid function statement in ${ni.name} for '${reIsInfoName}'`, A.resolve(null)); if (A.T(si[m[1]]) !== 'function') return A.W( `Invalid function of 'systeminformation' in ${ni.name} for '${reIsInfoName}'`, A.resolve(null), ); return A.P(si[m[1]].apply(si, m[2] ? A.trim(m[2].slice(1, -1).split(',')) : [])).then(A.nop, A.nop); }; break; default: A.W(`Not implemented type ${ni.type}`); } ni.opt = opt; let sch = item.sched ? item.sched.trim() : ''; const scht = sch.match(reIsTime); if (scht) { if (scht[3] === undefined) scht[3] = (A.obToArray(list).length % 58) + 1; sch = `${scht[3]} ${scht[2]} ${scht[1]} * * *`; } else if (sch.match(/^\d+[smh]$/)) switch (sch.slice(-1)) { case 's': sch = `*/${sch.slice(0, -1)} * * * * *`; break; case 'm': sch = `*/${sch.slice(0, -1)} * * * *`; break; case 'h': sch = `0 */${sch.slice(0, -1)} * * *`; break; } if (sch && sch.match(reIsSchedule)) { opt.native.si.sched = sch; if (list[sch]) list[sch].push(ni); else list[sch] = [ni]; scheds[sch] = null; } else A.W(`Invalid schedule in item ${item.name}`); } if (A.debug) A.makeState( '_config', JSON.stringify({ startup: adapter.config.startup, items: adapter.config.items, }), true, ); A.seriesOf( A.trim(adapter.config.startup.split('\n')), (x) => (!x.startsWith('#') ? A.exec(x).then(A.nop, A.D) : A.resolve()), 10, ) .then(() => doPoll()) .then(() => A.getObjectList({ startkey: A.ain, endkey: A.ain + '\u9999', }) .then((res) => A.seriesOf( res.rows, (item) => A.states[item.id.slice(A.ain.length)] ? A.resolve() : A.D(`Delete unneeded state ${item.id}`, A.removeState(item.id.slice(A.ain.length))), 2, ), ) .then(() => { for (const sh in list) { A.D(`Will poll every '${sh}': ${list[sh].map((x) => x.name)}.`); scheds[sh] = schedule.scheduleJob(sh, () => doPoll(list[sh])); } }) .then(() => A.I( `Adapter ${A.ains} started and found ${A.obToArray(list).reduce( (acc, val) => acc + val.length, 0, )}/${A.obToArray(states).length} items/states to process.`, ), ) .catch((e) => A.W(`Unhandled error in main: ${e}`)), ); }