UNPKG

iobroker.ems-esp

Version:
1,437 lines (1,273 loc) 47.6 kB
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-empty */ const { default: axios } = require('axios'); const F = require('./functions.js'); let unloaded = false; let emsesp, ems_token = '', ems_http_wait = 100, ems_polling = 60; let ems_version = 'V3'; let km200_structure = true; let energy_init = true; let database = 'iobroker'; let i_poll, i_energy; let adapter, intervals; const init = async function (a, i) { adapter = a; intervals = i; emsesp = adapter.config.emsesp_ip; adapter.setState('info.connection_ems', false, true); if (emsesp.substr(0, 3) == '***') { emsesp = emsesp.substr(3); } if (emsesp.substr(0, 7) != 'http://') { emsesp = `http://${emsesp}`; } ems_token = adapter.config.ems_token.trim(); ems_http_wait = adapter.config.ems_http_wait; ems_polling = adapter.config.ems_polling; if (ems_polling < 15) { ems_polling = 15; } km200_structure = adapter.config.km200_structure; // Testing API Version let url = `${emsesp}/api/system`; let data; try { data = await ems_get(url); ems_version = 'V3'; adapter.setState('info.connection_ems', true, true); } catch (error) { adapter.setState('info.connection_ems', false, true); adapter.log.info('EMS-ESP Gateway connection error'); return; } // checking ems-esp gateway formatting options since users do not read documentation try { const settings = JSON.parse(data).Settings; const settings1 = JSON.parse(data).settings; if (settings == undefined && settings1 == undefined) { adapter.log.info( 'ems-esp api read error on system settings - esp-esp firmware structure has changed or api error', ); adapter.setState('info.connection_ems', false, true); return; } let enum_format = 0; let bool_format = 0; try { enum_format = settings['enum format']; } catch (ee) {} try { enum_format = settings1.enumFormat; } catch (ee) {} try { bool_format = settings['bool format']; } catch (ee) {} try { bool_format = settings1.boolFormat; } catch (ee) {} if (bool_format != 6) { adapter.log.info('wrong formatting options for api/mqtt bool format in ems-esp settings -> correct: 1/0 '); adapter.setState('info.connection_ems', false, true); return; } adapter.log.info('formatting options for ems-esp gateway checked --> OK'); } catch (e) { adapter.log.info('error checking ems-esp settings'); adapter.setState('info.connection_ems', false, true); return; } try { ems_version = JSON.parse(data).system.version; } catch {} adapter.log.info(`EMS-ESP API version: ${ems_version}`); const vers = ems_version.substring(0, 5); if (vers < '3.7.2') { adapter.log.error('ems-esp version is too old and not supported anymore'); adapter.setState('info.connection_ems', false, true); return; } const version = ems_version; const devs = version.search('dev'); const tests = version.search('test'); const vs = version.search('3.7.'); if (devs != -1) { adapter.log.info( '*** You are using a non-productive development version of ems-esp gateway firmware. Use is on your own risk', ); } if (tests != -1) { adapter.log.info( '*** You are using a non-productive test version of ems-esp gateway firmware. Use is on your own risk', ); } if (adapter.config.ems_energy == true && adapter.config.db == '') { adapter.log.warn('no database selected for energy statistics'); adapter.config.ems_energy = false; } if (!unloaded) { await init_states_emsesp(version); } if (!unloaded && adapter.config.ems_energy) { let db; if (adapter.config.db.trim() == '') { db = ''; } else { db = adapter.config.db; } const obj = await adapter.getForeignObjectAsync(`system.adapter.${db}`); database = obj.native.dbname; //adapter.log.info("dbname: "+database); await init_energy(); energy_init = false; if (ems_polling != 15) { ems_polling = 15; adapter.log.info('ems : set polling to 15 seconds due to energy statistics'); } await ems_read(version); await read_energy(); i.ems_energy = adapter.setInterval(function () { read_energy(); }, 600 * 1000); // every 10 minutes i_energy = i.energy; adapter.log.info('ems : energy polling every 10 minutes'); } if (!unloaded) { adapter.log.info(`ems : polling every ${ems_polling} secs`); } if (!unloaded) { i.ems = adapter.setInterval(function () { ems_read(version); }, ems_polling * 1000); } i_poll = i.ems; intervals = i; }; async function init_states_emsesp(version) { adapter.log.info('start initializing EMS-ESP states '); const url = `${emsesp}/api/system`; write_state('esp.api', ems_version, ''); adapter.log.info(`url:${url}`); let data = ''; try { data = await ems_get(url); } catch (error) { adapter.log.warn('EMS-ESP read system error - wrong ip address?'); data = 'Invalid'; } await adapter.delay(ems_http_wait); if (data != 'Invalid') { let devices = {}, devices_count = 0; try { devices = JSON.parse(data).Devices; if (devices == undefined) { devices = JSON.parse(data).devices; } } catch {} devices_count = devices.length; await delete_esp(); read_status(data, 'esp'); for (let i = 0; i < devices_count; i++) { if (device_check(devices[i])) { const device = devices[i].type.toLowerCase(); const url1 = `${emsesp}/api/${device}`; adapter.log.info(`url1:${url1}`); data = ''; try { data = await ems_get(url1); } catch (error) { if (error != null) { //adapter.log.info(`EMS-ESP http read error init:${device} --> ${error} - ${url1}`); } } let fields = {}; if (data != '') { try { fields = JSON.parse(data); } catch (e) { adapter.log.warn(`EMS-ESP parse error device ${device} ${url1}:${data}`); } } //adapter.log.info(JSON.stringify(fields)); for (const [key, value] of Object.entries(fields)) { if (typeof value !== 'object') { const url2 = `${emsesp}/api/${device}/${key}`; let def; try { def = await ems_get(url2); await write_state(`${device}.${key}`, value, def); } catch (error) { await write_state(`${device}.${key}`, value, ''); } // V2 } else { const key1 = key; const wert = JSON.parse(JSON.stringify(value)); for (const [key2, value2] of Object.entries(wert)) { const url2 = `${emsesp}/api/${device}/${key1}/${key2}`; let def; try { def = await ems_get(url2); await write_state(`${device}.${key1}.${key2}`, value2, def); } catch (error) { await write_state(`${device}.${key1}.${key2}`, value2, ''); } // V2 } } await adapter.delay(ems_http_wait); } } } } adapter.log.info('end of initializing EMS-ESP states '); } async function read_status(data, entry) { try { const datap = JSON.parse(data); for (const [key, value] of Object.entries(datap)) { if (typeof value !== 'object') { write_status(`${entry}.${key}`, value); } else { const key1 = key; const wert = JSON.parse(JSON.stringify(value)); for (const [key2, value2] of Object.entries(wert)) { if (typeof value2 !== 'object') { write_status(`${entry}.${key1}.${key2}`, value2); } else { const wert2 = JSON.parse(JSON.stringify(value2)); for (const [key3, value3] of Object.entries(wert2)) { if (key1 == 'Devices' || key1 == 'devices') { const key2a = `${wert2.type} ${key2}`; write_status(`${entry}.${key1}.${key2a}.${key3}`, value3); const pos = wert2.name.indexOf('DeviceID') + 9; const id = wert2.name.substr(pos, 4); write_status(`${entry}.${key1}.${key2a}.busid`, id); } else { write_status(`${entry}.${key1}.${key2}.${key3}`, value3); } } } } } } } catch (e) { adapter.log.info('error EMS-ESP read status'); } } async function write_status(statename, value) { const obj = { _id: statename, type: 'state', common: {}, native: {} }; obj.common.id = statename; obj.common.name = `ems:${statename}`; obj.common.type = 'mixed'; obj.common.unit = ''; obj.common.read = true; obj.common.write = false; await adapter.setObjectNotExistsAsync(statename, obj); try { const state = await adapter.getStateAsync(statename); if (state == null) { await adapter.setStateAsync(statename, { ack: true, val: value }); } else { if (state.val != value || state.ack == false) { await adapter.setStateAsync(statename, { ack: true, val: value }); } } } catch {} } function device_check(dev) { for (const key in dev) { switch (key) { case 'entities': if (dev[key] > 0) { return true; } if (dev[key] == 0) { return false; } break; case 'type': if (dev[key] == 'Gateway') { return false; } if (dev[key] == 'Controller') { return false; } break; } } // adapter.log.warn("unclear device attributes " + JSON.stringify(dev)); return false; } async function ems_read(version) { const t1 = new Date().getTime(); let url = `${emsesp}/api/system`; //adapter.log.info(version + " " + url); let data = ''; try { data = await ems_get(url); } catch (error) { adapter.log.debug(`EMS-ESP read system error:${url} - wrong ip address?`); data = 'Invalid'; } let actual_version = ''; try { actual_version = JSON.parse(data)['System Info'].version; } catch {} try { actual_version = JSON.parse(data).system.version; } catch {} let last_version = actual_version; try { last_version = (await adapter.getStateAsync('esp.System Info.version')).val; } catch {} try { last_version = (await adapter.getStateAsync('esp.system.version')).val; } catch {} // adapter.log.debug("versions actual / last : "+actual_version + " / "+ last_version); if (actual_version != '' && last_version != actual_version) { adapter.log.warn('new ems-esp firmware version'); adapter.log.warn(`versions actual / last : ${actual_version} / ${last_version}`); adapter.log.warn('please stop adapter instance next time before uploading new ems-esp firmware'); try { clearInterval(i_poll); } catch {} try { clearInterval(i_energy); } catch {} adapter.log.warn('.... waiting 90 seconds before restarting ems-esp init and polling'); await adapter.delay(90000); init(adapter, intervals); } await adapter.delay(ems_http_wait); let devices = {}; if (data != 'Invalid') { try { devices = JSON.parse(data).Devices; if (devices == undefined) { devices = JSON.parse(data).devices; } } catch {} try { adapter.setState('info.connection_ems', true, true); } catch (error) { adapter.setState('info.connection_ems', false, true); //adapter.log.info("*** error can't read system information") } read_status(data, 'esp'); try { for (let i = 0; i < devices.length; i++) { if (device_check(devices[i])) { const device = devices[i].type.toLowerCase(); let url1 = `${emsesp}/api?device=${device}&cmd=info`; url1 = `${emsesp}/api/${device}`; try { data = await ems_get(url1); const fields = JSON.parse(data); for (const [key, value] of Object.entries(fields)) { if (typeof value !== 'object') { write_state(`${device}.${key}`, value, ''); } else { const key1 = key; const wert = JSON.parse(JSON.stringify(value)); for (const [key2, value2] of Object.entries(wert)) { write_state(`${device}.${key1}.${key2}`, value2, ''); } } } } catch (error) { //adapter.log.info(`EMS-ESP http read polling error:${url1}`); } } await adapter.delay(ems_http_wait); } } catch {} const t2 = new Date().getTime(); const t3 = (t2 - t1) / 1000; if (adapter.config.statistics) { await adapter.setObjectNotExistsAsync('statistics.ems-read', { type: 'state', common: { type: 'number', name: 'ems read time for polling', unit: 'seconds', role: 'value', read: true, write: true, }, native: {}, }); adapter.setStateAsync('statistics.ems-read', { ack: true, val: t3 }); } } // Energy statistics for ems-esp if (adapter.config.ems_energy) { const power = adapter.config.ems_nominalpower; let powera = 0; try { let rec_state = adapter.config.ems_modulation; const array = rec_state.split('.'); if (adapter.config.km200_structure == true && array[0] == 'boiler') { rec_state = `heatSources.hs1.${array[1]}`; } const state = await adapter.getStateAsync(rec_state); const mod = state.val; powera = (mod * power) / 100; } catch (e) { adapter.log.info('State modulation for energy statistics does not exist'); adapter.config.ems_energy = false; } let wwa = 0; try { let ww_state = adapter.config.ems_wwactive; const array2 = ww_state.split('.'); if (km200_structure && array2[0] == 'boiler' && array2[1] == 'dhw1') { ww_state = `dhwCircuits.dhw1.${array2[2]}`; } if (km200_structure && array2[0] == 'boiler' && array2[1] != 'dhw1') { ww_state = `dhwCircuits.dhw1.${array2[1]}`; } const state = await adapter.getStateAsync(ww_state); wwa = state.val; } catch (e) { adapter.log.info('State wwactive for energy statistics does not exist'); adapter.config.ems_energy = false; wwa = 0; } let statename = 'energy.actualPower.power'; await adapter.setStateAsync(statename, { ack: true, val: powera }); if ( wwa == 1 || wwa.toString() == '1' || wwa.toString() == 'on' || wwa.toString() == 'ON' || wwa.toString() == 'true' ) { statename = 'energy.actualDHWPower.power'; await adapter.setStateAsync(statename, { ack: true, val: powera }); statename = 'energy.actualCHPower.power'; await adapter.setStateAsync(statename, { ack: true, val: 0 }); } else { statename = 'energy.actualDHWPower.power'; await adapter.setStateAsync(statename, { ack: true, val: 0 }); statename = 'energy.actualCHPower.power'; await adapter.setStateAsync(statename, { ack: true, val: powera }); } } } async function init_energy() { let statename = 'energy.actualPower.power'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy power', unit: 'kW', role: 'value', read: true, write: false }, native: {}, }); enable_state(statename); statename = 'energy.actualCHPower.power'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy power', unit: 'kW', role: 'value', read: true, write: false }, native: {}, }); enable_state(statename); statename = 'energy.actualDHWPower.power'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy power', unit: 'kW', role: 'value', read: true, write: false }, native: {}, }); enable_state(statename); statename = 'energy.actualPower._Hours'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualPower._Days'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualPower._Months'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualCHPower._Hours'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualCHPower._Days'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualCHPower._Months'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualDHWPower._Hours'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualDHWPower._Days'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualDHWPower._Months'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'number', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false, }, native: {}, }); enable_state(statename); statename = 'energy.actualPower.Hours'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); statename = 'energy.actualPower.Days'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); statename = 'energy.actualPower.Months'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); statename = 'energy.actualCHPower.Hours'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); statename = 'energy.actualCHPower.Days'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); statename = 'energy.actualCHPower.Months'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); statename = 'energy.actualDHWPower.Hours'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); statename = 'energy.actualDHWPower.Days'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); statename = 'energy.actualDHWPower.Months'; await adapter.setObjectNotExistsAsync(statename, { type: 'state', common: { type: 'json', name: 'ems: energy consumption', unit: 'kWh', role: 'value', read: true, write: false }, native: {}, }); } async function read_energy() { const hours = 24 * 60; let db; if (adapter.config.db.trim() == '') { db = ''; } else { db = adapter.config.db; } let end = Date.now(); let end_ = new Date(end); const year = end_.getFullYear(); const month = end_.getMonth() + 1; const date = end_.getDate(); const hour = end_.getHours(); end_ = new Date(`${year}-${month}-${date} ${hour}:00`); //end = end_.getTime() + 3600000; end = end_.getTime(); let intervall = 24 * 7 * 3600000; // hourly values for 7 days let step = 3600000; // one hour steps await energy(db, 'energy.actualPower.power', 'energy.actualPower._Hours', end, intervall, step, 'hh'); await energy(db, 'energy.actualCHPower.power', 'energy.actualCHPower._Hours', end, intervall, step, 'hh'); await energy(db, 'energy.actualDHWPower.power', 'energy.actualDHWPower._Hours', end, intervall, step, 'hh'); intervall = 24 * 30 * 3600000; // daily values for 30 days step = 3600000 * 24; // 24 hour steps end_ = new Date(`${year}-${month}-${date}`); end = end_.getTime() + 3600000 * 24; await energy(db, 'energy.actualPower.power', 'energy.actualPower._Days', end, intervall, step, 'dd'); await energy(db, 'energy.actualCHPower.power', 'energy.actualCHPower._Days', end, intervall, step, 'dd'); await energy(db, 'energy.actualDHWPower.power', 'energy.actualDHWPower._Days', end, intervall, step, 'dd'); await energy(db, 'energy.actualPower.power', 'energy.actualPower._Months', 0, 0, 0, 'mm'); await energy(db, 'energy.actualCHPower.power', 'energy.actualCHPower._Months', 0, 0, 0, 'mm'); await energy(db, 'energy.actualDHWPower.power', 'energy.actualDHWPower._Months', 0, 0, 0, 'mm'); } async function energy(db, idr, idw, end, intervall, step, t) { const adapt = `${adapter.namespace}.`; let recs = []; let res = []; if (t != 'mm') { const result = await adapter.sendToAsync(db, 'getHistory', { id: adapt + idr, options: { start: end - intervall, end: end, step: step, integralUnit: 3600, aggregate: 'total' }, }); res = result.result; } else { const datum = new Date(); let year = datum.getFullYear(); let month = datum.getMonth() + 1; let year1 = year; let month1 = month + 1; if (month1 == 13) { year1 = year1 + 1; month1 = 1; } let start, start_, end, end_; for (let i = 0; i < 3; i++) { end_ = new Date(`${year1}-${month1}`); end = end_.getTime() - 1000; //end = end + (end_.getTimezoneOffset() * 60000); start_ = new Date(`${year}-${month}`); start = start_.getTime(); //start = start + (start_.getTimezoneOffset() * 60000); intervall = end - start; const result = await adapter.sendToAsync(db, 'getHistory', { id: adapt + idr, options: { start: start, end: end, aggregate: 'total' }, }); const ts = end - intervall / 2; let sum = 0; const r = result.result; if (r.length > 1) { for (let i = 0; i < r.length; i++) { sum += r[i].val; } } res.push({ ts: ts, val: sum }); if (month == 1) { year = year - 1; month = 12; } else if (month > 1) { month = month - 1; } if (month1 == 1) { year1 = year1 - 1; month1 = 12; } else if (month1 > 1) { month1 = month1 - 1; } } } recs = []; if (res.length > 0) { for (let i = 0; i < res.length; i++) { const ts = res[i].ts; const val = Math.round((res[i].val / 240) * 100) / 100; const tsa = new Date(); const date = new Date(ts); const h = date.getHours(); const m = date.getMinutes(); if (t == 'hh' && m == 30 && ts <= tsa) { recs.push({ ts: ts, val: val, ack: true }); } if (t == 'dd' && ts <= end && i > 0) { recs.push({ ts: ts, val: val, ack: true }); } if (t == 'mm') { recs.push({ ts: ts, val: val, ack: true }); } } } if (db.substring(0, 3) == 'sql') { const id = await getid(adapt + idw, db); const src = await getsource(db); if (id == 0) { await adapter.sendToAsync(db, 'storeState', { id: adapt + idw, state: recs }); } else { for (let i = 0; i < recs.length; i++) { let values = '', command = ''; values = `(${id},${recs[i].val},${recs[i].ts},1,${src},0)`; command = `INSERT INTO ${database}.ts_number (id, val, ts, ack, _from, q) VALUES ${values}`; command += 'ON DUPLICATE KEY UPDATE val=values(val)' + ';'; await adapter.sendToAsync(db, 'query', command); //adapter.log.info("ems: db insert "+adapt+idw + " ts:"+recs[i].ts); } } } if (db.substring(0, 8) == 'influxdb') { //await adapter.sendToAsync(db,"deleteAll",[{id: adapt+idw}]); for (let i = 0; i < recs.length; i++) { try { await adapter.sendToAsync(db, 'storeState', { id: adapt + idw, state: recs[i] }); } catch {} } } if (db.substring(0, 7) == 'history') { for (let i = 0; i < recs.length; i++) { let status; try { status = await adapter.sendToAsync(db, 'update', { id: adapt + idw, state: recs[i] }); } catch {} if (status.success == false) { try { status = await adapter.sendToAsync(db, 'storeState', { id: adapt + idw, state: recs[i] }); } catch {} } } } const v = []; for (let i = 0; i < recs.length; i++) { v.push({ ts: recs[i].ts, val: recs[i].val }); } function SortArray(x, y) { if (x.ts < y.ts) { return 1; } if (x.ts > y.ts) { return -1; } return 0; } const s = v.sort(SortArray); const ss = [], sss = []; for (let i = 0; i < s.length; i++) { const date = new Date(s[i].ts); const m = date.getMonth() + 1; let mm = m.toString(); if (m < 10) { mm = `0${mm}`; } const d = date.getDate(); let dd = d.toString(); if (d < 10) { dd = `0${dd}`; } let ddd = ''; if (t == 'hh') { ddd = `${date.getFullYear()}-${mm}-${dd} ${date.getHours()} hrs`; } if (t == 'dd') { ddd = `${date.getFullYear()}-${mm}-${dd}`; } if (t == 'mm') { ddd = `${date.getFullYear()}-${mm}`; } ss.push({ date: ddd, val: s[i].val }); sss.push(s[i].val); } let field1 = idw; field1 = field1.replace(/_Days/g, 'Days'); field1 = field1.replace(/_Hours/g, 'Hours'); field1 = field1.replace(/_Months/g, 'Months'); if (adapter.config.recordings_format == 0) { await adapter.setStateAsync(field1, { ack: true, val: JSON.stringify(sss) }); } if (adapter.config.recordings_format == 1) { await adapter.setStateAsync(field1, { ack: true, val: JSON.stringify(s) }); } if (adapter.config.recordings_format == 2) { await adapter.setStateAsync(field1, { ack: true, val: JSON.stringify(ss) }); } } function enable_state(statename) { let db; if (adapter.config.db.trim() == '') { db = ''; } else { db = adapter.config.db; } if (db != '' && energy_init == true) { const id = `${adapter.namespace}.${statename}`; adapter.sendTo( db, 'enableHistory', { id: id, options: { changesOnly: false, debounce: 0, retention: 86400 * 365, changesRelogInterval: 0, maxLength: 100, changesMinDelta: 0, aliasId: '', }, }, function (result) { if (result.error) { adapter.log.info(`enable history error ${id}`); } }, ); } } async function ems_get(url) { const options = { url: url, charset: 'utf-8', method: 'GET', status: [200], timeout: 5000, port: 80 }; try { const b = await axios(options); if (b.data.message != undefined || b.data == {}) { throw new Error('Request failed'); } const data = JSON.stringify(b.data); if (b.status == 200) { return data; } throw new Error(`Request failed with status ${b.status}`); } catch (e) { throw new Error(`ems_get error:${e}`); } } async function write_sensor(statename, value, def) { //adapter.log.info("custom elements: "+statename); const array = statename.split('.'); const obj = { _id: statename, type: 'state', common: {}, native: {} }; obj.common.role = 'value'; obj.common.name = `ems: ${statename}`; obj.common.read = true; obj.common.write = def.writeable; obj.common.unit = def.uom; obj.common.type = def.type; obj.native = def; obj.native.ems_api = ems_version; try { await adapter.setObjectNotExistsAsync(statename, obj); F.enums(adapter, statename); } catch {} try { const state = await adapter.getStateAsync(statename); if (state == null) { await adapter.setStateAsync(statename, { ack: true, val: value }); } else { if (state.val != value) { await adapter.setStateAsync(statename, { ack: true, val: value }); } } } catch {} } async function write_state(statename, value, def) { if (!unloaded) { const array = statename.split('.'); let device = '', device_ems = '', command = '', device_id = ''; let statename1 = statename; device = array[0]; device_ems = device; if (def == 'Invalid') { adapter.log.warn(`Invalid:${statename}`); } if (km200_structure) { if (array[0] == 'thermostat') { device = 'heatingCircuits'; } if (array[0] == 'thermostat' && array[1].substring(0, 2) == 'ww') { device = 'dhwCircuits'; } if (array[0] == 'thermostat' && array[1].substring(0, 3) == 'hm_') { device = 'system.holidayModes'; } if (array[0] == 'mixer' && array[1].substring(0, 2) == 'hc') { device = 'heatingCircuits'; } if (array[0] == 'mixer' && array[1].substring(0, 3) == 'dhw') { device = 'dhwCircuits'; } if (array[0] == 'mixer' && array[1] == 'wwc1') { device = 'dhwCircuits.dhw1'; } if (array[0] == 'mixer' && array[1] == 'wwc2') { device = 'dhwCircuits.dhw2'; } if (array[0] == 'solar') { device = 'solarCircuits.sc1'; } if (array[0] == 'boiler') { device = 'heatSources.hs1'; if (array[1].substring(0, 2) == 'hs') { device = 'heatSources'; } if (array[1].substring(0, 2) == 'ww' || array[1].substring(0, 2) == 'wW') { device = 'dhwCircuits.dhw1'; } //if (array[1] == "ahs1" ) device = "heatSources.hsa"; } if (array[1] == 'dhw') { device = 'dhwCircuits'; } if (array[1] == 'dhw2') { device = 'dhwCircuits'; } if (array[0] == 'heatsource') { device = 'heatSources'; } } else { if (array[0] == 'thermostat' && array[1].substring(0, 3) == 'hm_') { device = 'thermostat.hm'; } } command = array[1]; if (array.length == 3) { if ( array[1].substr(0, 2) == 'hc' || array[1].substr(0, 3) == 'ahs' || array[1].substr(0, 2) == 'hs' || array[1].substr(0, 3) == 'wwc' || array[1].substr(0, 3) == 'dhw' ) { device_id = array[1]; command = array[2]; } try { command = command.toLowerCase(); } catch (e) { command = ''; } } if (device_id == '') { statename1 = `${device}.${command}`; } else { statename1 = `${device}.${device_id}.${command}`; if (device_id == 'dhw') { statename1 = `${device}.dhw1.${command}`; } if (device_id == 'dhw2') { statename1 = `${device}.dhw2.${command}`; } } // same entityname for boiler and thermostat - change boiler entity name if (km200_structure && array[0] == 'boiler' && command == 'circmode' && device_id == 'dhw') { statename1 = `${device}.dhw1.circmode2`; } if (km200_structure && array[0] == 'boiler' && command == 'circmode' && device_id == 'dhw2') { statename1 = `${device}.dhw2.circmode2`; } if (km200_structure && array[0] == 'boiler' && command == 'disinfecting' && device_id == 'dhw') { statename1 = `${device}.dhw1.disinfecting2`; } if (km200_structure && array[0] == 'boiler' && command == 'disinfecting' && device_id == 'dhw2') { statename1 = `${device}.dhw2.disinfecting2`; } statename1 = statename1.replace('#', ''); const obj = { _id: statename1, type: 'state', common: {}, native: {} }; const obj1 = { _id: statename1, type: 'state', common: {}, native: {} }; obj.common.id = statename; obj.common.name = `ems:${statename}`; if (km200_structure) { obj.common.name = `ems:${statename} (${array[0]})`; } obj.common.type = 'mixed'; obj.common.unit = ''; obj.common.read = true; obj.common.write = false; obj.common.role = 'value'; let defj = {}; if (def != '' && def != 'Invalid') { try { defj = JSON.parse(def); } catch (e) { def = def.replace('.,', ','); } /* 2nd try with corrected numeric values */ try { defj = JSON.parse(def); } catch (e) { adapter.log.warn(`wrong ems-esp state definition: ${statename} ${def}`); def = ''; } } if (def != '' && def != 'Invalid') { obj.common.role = F.roles(adapter, device, defj.type, defj.uom, defj.writeable); obj.common.name = `ems: ${defj.fullname}`; if (km200_structure) { obj.common.name = `ems:${defj.fullname} (${array[0]})`; } if (defj.writeable == true) { obj.common.write = true; } obj.common.unit = defj.uom; if (defj.writeable == true) { obj.common.min = defj.min; } if (defj.writeable == true) { obj.common.max = defj.max; } if (defj.type == 'number' && value == '') { value = null; } if (defj.type == 'text') { defj.type = 'string'; } obj.common.type = defj.type; if (defj.type == 'enum') { obj.common.type = 'mixed'; obj.common.states = {}; obj.native.ems_enum = defj.enum; for (let ii = 0; ii < defj.enum.length; ii++) { let index = ii; if (defj.min == 1) { index = ii + 1; } obj.common.states[index] = defj.enum[ii]; } } if (defj.type == 'boolean') { obj.common.type = 'number'; switch (value) { case true: value = 1; break; case 'true': value = 1; break; case '1': value = 1; break; case 'ON': value = 1; break; case 'AN': value = 1; break; case 'on': value = 1; break; case false: value = 0; break; case 'false': value = 0; break; case '0': value = 0; break; case 'OFF': value = 0; break; case 'AUS': value = 1; break; case 'off': value = 0; break; } obj.common.states = { 0: 'Off', 1: 'On' }; obj.common.min = 0; obj.common.max = 1; } obj.native.ems_type = defj.type; try { obj.native.visible = defj.visible; } catch {} } if (device_ems == 'ems') { obj.common.write = false; } //obj.native.source = "ems-esp"; obj.native.ems_command = command; obj.native.ems_device = device_ems; obj.native.ems_id = device_id; obj.native.ems_api = ems_version; try { await adapter.setObjectNotExistsAsync(statename1, obj); if (def != '' && def != 'Invalid') { const defj = JSON.parse(def); await adapter.setObjectNotExistsAsync(statename1, obj); if (obj.native.ems_command == 'seltemp') { obj.common.min = -1; await adapter.setObjectNotExistsAsync(statename1, obj); // reset min value for seltemp } F.enums(adapter, statename1); } } catch {} try { const state = await adapter.getStateAsync(statename1); const obj = await adapter.getObjectAsync(statename1); if (obj.native.ems_type == 'enum') { if (isNaN(value)) { //value not index of enum -> search number let found = -1; for (let ii = 0; ii < 10; ii++) { if (obj.common.states[ii] == value) { found = ii; break; } } value = found; } } if (state == null) { await adapter.setStateAsync(statename1, { ack: true, val: value }); } else { if (state.val != value || state.ack == false) { await adapter.setStateAsync(statename1, { ack: true, val: value }); } } } catch (e) { adapter.log.info(`state write error: ${statename1} ${e}`); } } } const state_change = async function (id, state, obj) { if (unloaded) { return; } const value = state.val; let url; adapter.log.debug(`write change to ems-esp1: ${id}: ${value}`); try { ems_version = obj.native.ems_api; if (obj.native.ems_device != null) { url = `${emsesp}/api/${obj.native.ems_device}`; if (obj.native.ems_id == '') { url += `/${obj.native.ems_command}`; } else { url += `/${obj.native.ems_id}/${obj.native.ems_command}`; } adapter.log.debug(`write change to ems-esp: ${id}: ${value}`); const data = { value: value }; const headers = { 'Content-Type': 'application/json', Authorization: `Bearer ${ems_token}` }; const options = { url: url, headers: headers, data: data, method: 'POST' }; let r = (await axios(options)).data; if (obj.native.ems_command == 'switchprog' || obj.native.ems_command == 'circswitchprog') { // send 3 times await adapter.delay(5000); r = (await axios(options)).data; await adapter.delay(5000); r = (await axios(options)).data; } } } catch (e) { adapter.log.info(`post: ${url} ${e}`); } }; async function getsource(db) { return new Promise(function (resolve) { const query = `select id from ${database}.sources where name = "system.adapter.ems-esp.${adapter.instance}";`; adapter.sendTo(db, 'query', query, function (result) { if (result.error || result.result[0] == null) { resolve(0); } else { resolve(result.result[0].id); } }); }); } async function getid(field, db) { return new Promise(function (resolve) { const query = `select id from ${database}.datapoints where name = "${field}";`; adapter.sendTo(db, 'query', query, function (result, reject) { if (result.error || result.result[0] == null) { resolve(0); } else { resolve(result.result[0].id); } }); }); } async function delete_esp() { const pattern = `${adapter.namespace}.esp.*`; const states = await adapter.getStatesAsync(pattern); for (const id in states) { const obj = await adapter.getObjectAsync(id); if (obj.common.custom == undefined) { await adapter.delObjectAsync(id); } } } const unload = function (u) { unloaded = u; }; module.exports = { init, state_change, unload };