iobroker.ems-esp
Version:
EMS-ESP and KM200 Interface
1,284 lines (1,048 loc) • 42.6 kB
JavaScript
/* eslint-disable no-unused-vars */
/* eslint-disable no-empty */
/* eslint-disable no-mixed-spaces-and-tabs */
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);
url = emsesp + "/api?device=system&cmd=info";
try {
data = await ems_get(url);
ems_version = "V2";
adapter.log.error("API version V2 - V2 is not supported anymore");
adapter.log.info("last adapter version supporting V2 was v1.15.0");
adapter.log.info("install within within shell: cd /opt/iobroker; iobroker upgrade ems-esp@1.15.0");
return;
}
catch (e) { adapter.log.error("EMS-ESP Gateway IP address wrong"); 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.error("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.error("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.error("error checking ems-esp settings");
adapter.setState("info.connection_ems", false, true);
return;
}
try { ems_version = JSON.parse(data)["System Info"].version; } catch (e) { }
try { ems_version = JSON.parse(data).system.version; } catch (e) { }
adapter.log.info("EMS-ESP API version: " + ems_version);
const version = ems_version;
const devs = version.search("dev");
const vs = version.search("3.7.");
if (devs != -1) {
adapter.log.info("*** You are using a non-productive version of ems-esp gateway firmware. Use is on your own risk");
}
if (vs != -1) {
adapter.log.info("*** Version 3.7. might change structure and names of warm water / dhw objects. Please adjust your scripts / vis ui's if necessary");
}
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 = 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 = 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 (e) { }
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.error("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.error("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 (e) { }
}
function device_check(dev) {
for (const key in dev) {
switch (key) {
case "entities":
if (dev[key] > 0) return true;
break;
// eslint-disable-next-line no-fallthrough
case "type":
if (dev[key] == "Gateway") return false;
if (dev[key] == "Controller") return false;
break;
case "handlers": return true;
case "handlers_received": return true;
case "handlers_fetched": return true;
case "handlers received": return true;
case "handlers fetched": return true;
}
}
// 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 (e) { }
try { actual_version = (JSON.parse(data)).system.version; } catch (e) { }
let last_version = actual_version;
try { last_version = (await adapter.getStateAsync("esp.System Info.version")).val; } catch (e) { }
try { last_version = (await adapter.getStateAsync("esp.system.version")).val; } catch (e) { }
// 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 (e) { }
try { clearInterval(i_energy); } catch (e) { }
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 = {};
let sensors;
try {sensors = JSON.parse(data).sensor.temperatureSensors;} catch(e) {sensors = -1;}
if (data != "Invalid") {
try {
devices = JSON.parse(data).Devices;
if (devices == undefined) devices = JSON.parse(data).devices;
} catch (e) { }
try {
adapter.setState("info.connection_ems", true, true);
}
catch (error) {
adapter.setState("info.connection_ems", false, true);
//adapter.log.error("*** 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.debug("EMS-ESP http read polling error:" + url1); }
}
await adapter.delay(ems_http_wait);
}
} catch (e) { }
const t2 = new Date().getTime();
const t3 = (t2 - t1) / 1000;
if (adapter.config.statistics) {
adapter.setObjectNotExists("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 });
}
}
if (adapter.config.ems_dallas) {
if (last_version.substring(0, 3) < "3.6") {
let url = emsesp + "/api/dallassensor";
data = "";
try { data = await ems_get(url); }
catch (error) { data = "Invalid"; }
await adapter.delay(ems_http_wait);
if (data != "Invalid") {
let sensors = {};
try { sensors = JSON.parse(data); }
catch (error) { }
for (const [key, value] of Object.entries(sensors)) {
const url1 = emsesp + "/api/dallassensor/" + key;
try { data = await ems_get(url1); }
catch (error) { data = "Invalid"; }
await adapter.delay(ems_http_wait);
if (data != "Invalid") {
let def;
try { def = JSON.parse(data); }
catch (error) { }
write_sensor("dallas." + def.id, def.value, def);
}
}
}
}
if (sensors > 0) {
// new version for v >= 3.6 ...
url = emsesp + "/api/temperaturesensor";
data = "";
try { data = await ems_get(url); }
catch (error) { data = "Invalid"; }
await adapter.delay(ems_http_wait);
if (data != "Invalid") {
let sensors = {};
try { sensors = JSON.parse(data); }
catch (error) { }
for (const [key, value] of Object.entries(sensors)) {
const url1 = emsesp + "/api/temperaturesensor/" + key;
try { data = await ems_get(url1); }
catch (error) { data = "Invalid"; }
await adapter.delay(ems_http_wait);
if (data != "Invalid") {
let def;
try { def = JSON.parse(data); }
catch (error) { }
if (adapter.config.ems_dallas_old_format) write_sensor("dallas." + def.id, def.value, def);
else write_sensor("temperaturesensor." + def.id, def.value, def);
}
}
}
}
}
if (adapter.config.ems_analog) {
const url = emsesp + "/api/analogsensor";
data = "";
try { data = await ems_get(url); }
catch (error) { data = "Invalid"; }
await adapter.delay(ems_http_wait);
if (data != "Invalid") {
let analogs = {};
try { analogs = JSON.parse(data); }
catch (error) { }
for (const [key, value] of Object.entries(analogs)) {
const url1 = emsesp + "/api/analogsensor/" + key;
try { data = await ems_get(url1); }
catch (error) { data = "Invalid"; }
await adapter.delay(ems_http_wait);
if (data != "Invalid") {
let def;
try { def = JSON.parse(data); }
catch (error) { }
write_sensor("analog.gpio" + def.gpio, def.value, def);
}
}
}
}
// read custom elements
if (adapter.config.ems_custom_elements) {
const url0 = emsesp + "/api/custom";
data = "";
try { data = await ems_get(url0); }
catch (error) { data = "Invalid"; }
await adapter.delay(ems_http_wait);
if (data != "Invalid") {
let custom = {};
try { custom = JSON.parse(data); } catch (error) { }
for (const [key, value] of Object.entries(custom)) {
const url1 = emsesp + "/api/custom/" + key;
try { data = await ems_get(url1); }
catch (error) { data = "Invalid"; }
await adapter.delay(ems_http_wait);
//adapter.log.info(data);
if (data != "Invalid") {
let def = {};
try { def = JSON.parse(data); } catch (error) { }
let type = "custom";
/*
for (let i =0;i < devices.length; i++) {
//adapter.log.info(devices[i] ["device id"] +" " + def.device_id);
if (devices[i] ["device id"] == def.device_id) {type = devices[i].type;break;}
if (devices[i] ["deviceID"] == def.device_id) {type = devices[i].type;break;}
}
if (km200_structure) {
if (type == "thermostat") type = "heatingCircuits";
if (type == "boiler") type = "heatSources.hs1";
if (type == "solar") type = "solarCircuits.sc1";
try {if (def.name.substring(0,2) == "ww") type = "dhwCircuits.dhw1";} catch (e) {}
}
*/
write_custom(type + "." + def.name, def.value, def);
}
}
}
}
// 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.error("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") {
ww_state = "dhwCircuits.dhw1." + array2[1];
}
const state = await adapter.getStateAsync(ww_state);
wwa = state.val;
} catch (e) { adapter.log.error("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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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.setObjectAsync(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) try {
await adapter.sendToAsync(db, "storeState", { id: adapt + idw, state: recs });
} catch (e) { }
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 (e) { }
}
}
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 (e) { }
if (status.success == false) try { status = await adapter.sendToAsync(db, "storeState", { id: adapt + idw, state: recs[i] }); } catch (e) { }
}
}
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.error("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);
else 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 (e) { }
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 (e) { }
}
async function write_custom(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;
obj.native.ems_device = "custom";
obj.native.ems_command = def.name;
obj.native.ems_id = "";
try {
await adapter.setObjectNotExistsAsync(statename, obj);
F.enums(adapter, statename);
} catch (e) { }
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 (e) { }
}
async function write_device(device) { }
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 (e) { }
}
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;
// @ts-ignore
try {
await adapter.setObjectNotExistsAsync(statename1, obj);
if (def != "" && def != "Invalid") {
const defj = JSON.parse(def);
await adapter.setObjectAsync(statename1, obj);
if (obj.native.ems_command == "seltemp") {
obj.common.min = -1;
await adapter.setObjectAsync(statename1, obj); // reset min value for seltemp
}
F.enums(adapter, statename1);
}
} catch (e) { }
//let deb = "boiler.silentmode";
//let deb = "boiler.pumpcharacter";
try {
//if (statename1 == deb) adapter.log.info(deb+" "+ value);
const state = await adapter.getStateAsync(statename1);
const obj = await adapter.getObjectAsync(statename1);
//let objd = {"type": "device", "common": {"name": device}};
//await adapter.setObjectNotExistsAsync(device, objd);
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 (def == "" && obj.native.ems_type == "boolean") {
// remap boolean values to 0/1
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 "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 = 0; break;
case "aus": value = 0; break;
case "off": value = 0; break;
}
}
*/
//if (statename1 == deb) adapter.log.info(deb+" index "+ value);
//if (value != undefined) {
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.error("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;
}
//adapter.log.info(JSON.stringify(r));
}
} catch (e) { adapter.log.error("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 };