iobroker.go-e-charger
Version:
Adapter for reading go-eCharger data
594 lines • 28.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// The adapter-core module gives you access to the core ioBroker functions you need to create an adapter
const utils = __importStar(require("@iobroker/adapter-core"));
const axios_1 = __importDefault(require("axios"));
const projectUtils_1 = require("./lib/projectUtils");
const axiosInstance = axios_1.default.create({
//timeout: 5000, //by default
});
// Variablen
let FirstStart = true;
let ZielAmpere = 5;
let EnabledPhases = 0;
let OptAmpere = 6;
let MinHomeBatVal = 87;
let OffVerzoegerung = 0;
let ChargeNOW = false;
let ChargeManager = false;
let ChargeCurrent = 0;
let Charge3Phase = true;
let ChargePower = 0;
let GridPhases = 0;
let SolarPower = 0;
let HouseConsumption = 0;
let BatSoC = 0;
let Firmware = "0";
let Hardware = "V2";
let HardwareMin3 = false;
class go_e_charger extends utils.Adapter {
ProjectUtils = new projectUtils_1.ProjectUtils(this);
timeoutList;
/****************************************************************************************
* @param {Partial<utils.AdapterOptions>} [options={}]
*/
constructor(options = {}) {
super({
...options,
name: "go-e-charger",
});
this.on("ready", this.onReady.bind(this));
this.on("stateChange", this.onStateChange.bind(this));
// this.on("objectChange", this.onObjectChange.bind(this));
// this.on("message", this.onMessage.bind(this));
this.on("unload", this.onUnload.bind(this));
this.timeoutList = []; //WIP
}
/**
* Is called when databases are connected and adapter received configuration.
*/
async onReady() {
if (!this.config.ipaddress) {
this.log.warn("go-eCharger IP address not set");
}
this.subscribeStates(`Settings.*`); // all states changes inside the adapters settings namespace are subscribed
if (this.config.ipaddress) {
await this.Read_Charger();
await this.Read_ChargerAPIV2();
this.log.info(`IP address found in config: ${this.config.ipaddress}`);
if (!this.config.polltimelive) {
this.log.warn("Polltime not configured or zero - will be set to 10 seconds");
this.config.polltimelive = 10000;
}
this.log.info(`Polltime set to: ${this.config.polltimelive / 1000} seconds`);
// sentry.io ping
if (this.supportsFeature && this.supportsFeature("PLUGINS")) {
const sentryInstance = this.getPluginInstance("sentry");
if (sentryInstance) {
const Sentry = sentryInstance.getSentryObject();
Sentry &&
Sentry.withScope(scope => {
scope.setLevel("info");
scope.setTag("Charger", this.config.ipaddress);
scope.setTag("Firmware", Firmware);
scope.setTag("Hardware", Hardware);
Sentry.captureMessage("Adapter go-e-Charger started", "info"); // Level "info"
});
}
}
MinHomeBatVal = await this.ProjectUtils.getStateValue("Settings.Setpoint_HomeBatSoC"); // Get desired battery SoC
ChargeNOW = await this.ProjectUtils.getStateValue("Settings.ChargeNOW"); // Get charging override trigger
ChargeManager = await this.ProjectUtils.getStateValue("Settings.ChargeManager"); // Get enable for charge manager
ChargeCurrent = await this.ProjectUtils.getStateValue("Settings.ChargeCurrent"); // Get current for charging override
this.log.debug(`Pre-init done, launching state machine interval`);
const stateMachine = this.setTimeout(this.StateMachine.bind(this), Number(this.config.polltimelive));
this.timeoutList.push(stateMachine);
}
else {
this.log.error(`No IP address configured!! - shutting down adapter.`);
await this.setState("info.connection", { val: false, ack: true });
this.stop;
}
}
/****************************************************************************************
* Is called if a subscribed state changes
* @param { string } id
* @param { ioBroker.State | null | undefined } state */
onStateChange(id, state) {
try {
if (state) {
// The state was changed
// this.subscribeStates(`Settings.*`);
if (!state.ack) {
this.log.debug(`state change detected and parsing for id: ${id} - state: ${state.val}`);
if (id.includes(`.Settings.`)) {
const statePath = id.split(".");
const settingType = statePath[3];
if (settingType !== undefined) {
switch (settingType) {
case "Setpoint_HomeBatSoC":
// Get desired battery SoC
if (typeof state.val === "number") {
MinHomeBatVal = state.val;
this.log.debug(`settings state changed to Setpoint_HomeBatSoC: ${MinHomeBatVal}`);
void this.setState(id, state.val, true);
}
else {
this.log.warn(`Wrong type for Setpoint_HomeBatSoC: ${state.val}`);
}
break;
case "ChargeNOW":
// Get charging override trigger
if (typeof state.val === "boolean") {
ChargeNOW = state.val;
this.log.debug(`settings state changed to ChargeNOW: ${ChargeNOW}`);
void this.setState(id, state.val, true);
}
else {
this.log.warn(`Wrong type for ChargeNOW: ${state.val}`);
}
break;
case "ChargeManager":
// Get enable for charge manager
if (typeof state.val === "boolean") {
ChargeManager = state.val;
this.log.debug(`settings state changed to ChargeManager: ${ChargeManager}`);
void this.setState(id, state.val, true);
}
else {
this.log.warn(`Wrong type for ChargeManager: ${state.val}`);
}
break;
case "ChargeCurrent":
// Get current for charging override
if (typeof state.val === "number") {
ChargeCurrent = state.val;
this.log.debug(`settings state changed to ChargeCurrent: ${ChargeCurrent}`);
void this.setState(id, state.val, true);
}
else {
this.log.warn(`Wrong type for ChargeCurrent: ${state.val}`);
}
break;
case "Charge3Phase":
// Get enable of 3 phases for charging override
if (typeof state.val === "boolean") {
Charge3Phase = state.val;
this.log.debug(`settings state changed to Charge3Phase: ${Charge3Phase}`);
void this.setState(id, state.val, true);
}
else {
this.log.warn(`Wrong type for Charge3Phase: ${state.val}`);
}
break;
default:
this.log.debug(`unknown value for setting type: ${settingType}`);
}
}
}
}
}
else {
// The state was deleted
// state go-e-charger.1.Settings.ChargeManager deleted
this.log.warn(`state ${id} deleted`);
}
}
catch (e) {
this.log.error(`Unhandled exception processing onStateChange: ${e}`);
}
}
/**
* Is called when adapter shuts down - callback has to be called under any circumstances!
*
* @param callback - callback
*/
onUnload(callback) {
try {
for (const timeoutJob of this.timeoutList) {
this.clearTimeout(timeoutJob);
}
this.log.info(`Adapter go-eCharger cleaned up everything...`);
void this.setState("info.connection", false, true);
callback();
}
catch (e) {
this.log.warn(e.message);
callback();
}
}
/*****************************************************************************************/
async StateMachine() {
if (FirstStart) {
// First run of state machine after adapter start-up
this.log.debug(`Initial ReadCharger done, detected firmware ${Firmware}`);
switch (Firmware) {
case "0":
case "EHostUnreach":
// no charger found - stop adapter - only on first run
this.log.error(`No charger detected on given IP address - shutting down adapter.`);
await this.setState("info.connection", { val: false, ack: true });
this.stop;
break;
case "033":
case "040":
case "040.0":
case "041.0":
case "054.7":
case "054.11":
case "055.5":
case "055.7":
case "055.8":
case "56.1":
case "56.2":
case "56.8":
case "56.9":
case "56.11":
case "57.0":
case "57.1":
this.log.debug(`Init done, launching state machine`);
await this.setState("info.connection", { val: true, ack: true });
break;
default:
this.log.warn(`Not explicitly supported firmware ${Firmware} found!!!`);
await this.setState("info.connection", { val: true, ack: true });
// sentry.io send firmware version
if (this.supportsFeature && this.supportsFeature("PLUGINS")) {
const sentryInstance = this.getPluginInstance("sentry");
if (sentryInstance) {
const Sentry = sentryInstance.getSentryObject();
Sentry &&
Sentry.withScope(scope => {
scope.setLevel("warning");
scope.setTag("Firmware", Firmware);
Sentry.captureMessage("Adapter go-e-Charger found unknown firmware", "warning"); // Level "warning"
});
}
}
}
FirstStart = false;
}
this.log.debug(`StateMachine cycle start`);
if (ChargeNOW || ChargeManager) {
// Charge-NOW or Charge-Manager is enabled
await this.Read_Charger();
if (HardwareMin3) {
await this.Read_ChargerAPIV2();
}
}
if (ChargeNOW) {
// Charge-NOW is enabled
await this.Charge_Config("1", ChargeCurrent, "go-eCharger für erzwungene Schnellladung aktivieren"); // keep active charging current!!
await this.Switch_3Phases(Charge3Phase);
if (HardwareMin3) {
await this.Read_ChargerAPIV2();
}
}
else if (ChargeManager) {
// Charge-Manager is enabled
BatSoC = await this.ProjectUtils.asyncGetForeignStateVal(this.config.StateHomeBatSoc);
// BatSoC = await this.asyncGetForeignStateVal(this.config.StateHomeBatSoc);
this.log.debug(`Got external state of battery SoC: ${BatSoC}%`);
if (BatSoC >= MinHomeBatVal) {
// SoC of home battery sufficient?
await this.Charge_Manager();
}
else {
// FUTURE: time of day forces emptying of home battery
if ((await this.ProjectUtils.getStateValue("Power.ChargingAllowed")) == true) {
// Set to false only if still true
ZielAmpere = 6;
await this.Charge_Config("0", ZielAmpere, `Hausbatterie laden bis ${MinHomeBatVal}%`);
}
}
}
else {
// only if Power.ChargingAllowed is still set: switch OFF; set to min. current;
if ((await this.ProjectUtils.getStateValue("Power.ChargingAllowed")) == true) {
// Set to false only if still true
await this.Read_Charger();
if (HardwareMin3) {
await this.Read_ChargerAPIV2();
}
ZielAmpere = 6;
await this.Charge_Config("0", ZielAmpere, `go-eCharger abschalten`);
}
else if (Number(await this.ProjectUtils.getStateValue("Power.Charge")) > 0) {
await this.Read_Charger();
if (HardwareMin3) {
await this.Read_ChargerAPIV2();
}
}
}
const stateMachine = this.setTimeout(this.StateMachine.bind(this), Number(this.config.polltimelive));
this.timeoutList.push(stateMachine);
}
/*****************************************************************************************/
async Read_Charger() {
await axiosInstance
.get(`http://${this.config.ipaddress}/status`, { transformResponse: r => r })
.then(response => {
//.status == 200
const result = JSON.parse(response.data);
this.log.debug(`Read charger: ${response.data}`);
void this.ParseStatus(result);
})
.catch(error => {
if (error.message && error.message.includes("EHOSTUNREACH")) {
this.log.error(`Host unreachable error when calling go-eCharger API: ${error}`);
Firmware = `EHostUnreach`;
}
else {
this.log.error(`Error in calling go-eCharger API: ${error}`);
}
this.log.error(`Please verify IP address: ${this.config.ipaddress} !!!`);
});
} // END Read_Charger
/*****************************************************************************************/
async ParseStatus(status) {
await this.setState("Info.RebootCounter", Number(status.rbc), true);
await this.setState("Info.RebootTimer", Math.floor(status.rbt / 1000 / 3600), true); // trim to hours
await this.setState("Info.CarState", Number(status.car), true);
switch (status.car) {
case "1":
await this.setState("Info.CarStateString", "Wallbox ready, no car", true);
break;
case "2":
await this.setState("Info.CarStateString", "Charging...", true);
break;
case "3":
await this.setState("Info.CarStateString", "Wait for car", true);
break;
case "4":
await this.setState("Info.CarStateString", "Charge finished, car still connected", true);
break;
default:
await this.setState("Info.CarStateString", "Error", true);
}
await this.setState("Power.ChargeCurrent", Number(status.amp), true);
await this.setState("Power.ChargeCurrentVolatile", Number(status.amx), true);
switch (status.alw) {
case "0":
await this.setState("Power.ChargingAllowed", false, true);
break;
case "1":
await this.setState("Power.ChargingAllowed", true, true);
break;
}
GridPhases = ((32 & status.pha) >> 5) + ((16 & status.pha) >> 4) + ((8 & status.pha) >> 3);
await this.setState("Power.GridPhases", GridPhases, true);
await this.setState("Statistics_Total.Charged", status.eto / 10, true);
await this.setState("Power.Charge", status.nrg[11] * 10, true); // trim to Watt
await this.setState("Power.MeasuredMaxPhaseCurrent", Math.max(status.nrg[4], status.nrg[5], status.nrg[6]) / 10, true);
Firmware = status.fwv;
await this.setState("Info.FirmwareVersion", Firmware, true);
this.log.debug("got and parsed go-eCharger data");
}
/*****************************************************************************************/
async Read_ChargerAPIV2() {
await axiosInstance
.get(`http://${this.config.ipaddress}/api/status?filter=alw,acu,eto,amp,rbc,rbt,car,pha,fwv,nrg,psm,typ`, { transformResponse: r => r })
.then(response => {
//.status == 200
const result = JSON.parse(response.data);
this.log.debug(`Read charger API V2: ${response.data}`);
HardwareMin3 = true;
void this.ParseStatusAPIV2(result);
})
.catch(error => {
this.log.error(`Error in calling go-eCharger API V2: ${error}`);
this.log.warn(`If you have a charger minimum hardware version 3: please enable API V2 for IP: ${this.config.ipaddress}`);
});
}
/*****************************************************************************************/
async ParseStatusAPIV2(status) {
switch (status.psm) {
case 1:
await this.setState("Power.EnabledPhases", 1, true);
EnabledPhases = 1;
break;
case 2:
await this.setState("Power.EnabledPhases", 3, true);
EnabledPhases = 3;
break;
default:
await this.setState("Power.EnabledPhases", 0, true);
EnabledPhases = 0;
}
this.log.debug(`got enabled phases ${EnabledPhases}`);
Hardware = status.typ;
await this.setState("Info.HardwareVersion", Hardware, true);
this.log.debug(`got and parsed go-eCharger data with API V2`);
}
/*****************************************************************************************/
async Switch_3Phases(Charge3Phase) {
if (HardwareMin3) {
let psm = 1;
if (Charge3Phase) {
psm = 2;
}
await axiosInstance
.get(`http://${this.config.ipaddress}/api/set?psm=${psm}`, { transformResponse: r => r })
.then(response => {
//.status == 200
this.log.debug(`Sent: PSM=${psm} with response ${response.statusText}`);
})
.catch(error => {
this.log.warn(`Error: ${error} by writing @ ${this.config.ipaddress} 3 phases = ${Charge3Phase}`);
this.log.error(`Please verify IP address: ${this.config.ipaddress} !!!`);
});
}
}
/*****************************************************************************************/
async Charge_Config(Allow, Ampere, LogMessage) {
this.log.debug(`${LogMessage} - ${Ampere} Ampere`);
if (!this.config.ReadOnlyMode) {
await axiosInstance
.get(`http://${this.config.ipaddress}/mqtt?payload=alw=${Allow}`, { transformResponse: r => r }) // activate charging
.then(response => {
//.status == 200
this.log.debug(`Sent: ${response.data}`);
})
.catch(error => {
this.log.warn(`Error: ${error} by writing @ ${this.config.ipaddress} alw=${Allow}`);
this.log.error(`Please verify IP address: ${this.config.ipaddress} !!!`);
});
}
switch (Firmware) {
case "033":
await axiosInstance
.get(`http://${this.config.ipaddress}/mqtt?payload=amp=${Ampere}`, { transformResponse: r => r }) // set charging current
.then(response => {
//.status == 200
this.log.debug(`Sent to firmware 033: ${response.data}`);
const result = JSON.parse(response.data);
void this.setState("Power.ChargeCurrent", Number(result.amp), true); // in readcharger integriert
switch (result.alw) {
case "0":
void this.setState("Power.ChargingAllowed", false, true);
break;
case "1":
void this.setState("Power.ChargingAllowed", true, true);
break;
}
})
.catch(error => {
this.log.warn(`Error: ${error} by writing @ ${this.config.ipaddress} amp=${Ampere}`);
this.log.error(`Please verify IP address: ${this.config.ipaddress} !!!`);
});
break;
default:
// case '040', '040.0', '041.0':
// case '054.7', '054.11', '055.5', '055.7', '055.8':
// case '56.1', '56.2', '56.8', '56.9', '56.11', '57.0', '57.1':
await axiosInstance
.get(`http://${this.config.ipaddress}/mqtt?payload=amx=${Ampere}`, { transformResponse: r => r }) // set charging current
.then(response => {
//.status == 200
this.log.debug(`Sent to firmware > 033: ${response.data}`);
const result = JSON.parse(response.data);
void this.setState("Power.ChargeCurrent", Number(result.amp), true); // in readcharger integriert
switch (result.alw) {
case "0":
void this.setState("Power.ChargingAllowed", false, true);
break;
case "1":
void this.setState("Power.ChargingAllowed", true, true);
break;
}
})
.catch(error => {
this.log.warn(`Error: ${error} by writing @ ${this.config.ipaddress} amx=${Ampere}`);
this.log.error(`Please verify IP address: ${this.config.ipaddress} !!!`);
});
}
} // END Charge_Config
/*****************************************************************************************/
async Charge_Manager() {
SolarPower = await this.ProjectUtils.asyncGetForeignStateVal(this.config.StateHomeSolarPower);
this.log.debug(`Got external state of solar power: ${SolarPower} W`);
HouseConsumption = await this.ProjectUtils.asyncGetForeignStateVal(this.config.StateHomePowerConsumption);
this.log.debug(`Got external state of house power consumption: ${HouseConsumption} W`);
BatSoC = await this.ProjectUtils.asyncGetForeignStateVal(this.config.StateHomeBatSoc);
this.log.debug(`Got external state of battery SoC: ${BatSoC}%`);
ChargePower = await this.ProjectUtils.getStateValue("Power.Charge");
let Phases = 3;
if (HardwareMin3 && EnabledPhases) {
Phases = EnabledPhases;
}
else {
Phases = GridPhases;
}
OptAmpere = Math.floor((SolarPower -
HouseConsumption +
(this.config.SubtractSelfConsumption ? ChargePower : 0) - // Bedingte Einbeziehung von ChargePower
100 + // Reserve
(2000 / (100 - MinHomeBatVal)) * (BatSoC - MinHomeBatVal)) / // Batterieleerung
230 /
Phases);
if (OptAmpere > 16) {
OptAmpere = 16;
}
this.log.debug(`Optimal charging current would be: ${OptAmpere} A`);
if (ZielAmpere < OptAmpere) {
ZielAmpere++;
}
else if (ZielAmpere > OptAmpere) {
ZielAmpere--;
}
this.log.debug(`ZielAmpere: ${ZielAmpere} Ampere; Leistung DC: ${SolarPower} W; ` +
`Hausverbrauch: ${HouseConsumption} W; Gesamtleistung Charger: ${ChargePower} W`);
if (ZielAmpere > 5 + 4) {
await this.Charge_Config("1", ZielAmpere, `Charging current: ${ZielAmpere} A`); // An und Zielstrom da größer 5 + Hysterese
}
else if (ZielAmpere < 6) {
OffVerzoegerung++;
if (OffVerzoegerung > 12) {
await this.Charge_Config("0", ZielAmpere, `zu wenig Überschuss`); // Aus und Zielstrom
OffVerzoegerung = 0;
}
}
} // END Charge_Manager
isLikeEmpty(inputVar) {
if (typeof inputVar !== "undefined" && inputVar !== null) {
let sTemp = JSON.stringify(inputVar);
sTemp = sTemp.replace(/\s+/g, ""); // remove all white spaces
sTemp = sTemp.replace(/"+/g, ""); // remove all >"<
sTemp = sTemp.replace(/'+/g, ""); // remove all >'<
sTemp = sTemp.replace(/\[+/g, ""); // remove all >[<
sTemp = sTemp.replace(/\]+/g, ""); // remove all >]<
sTemp = sTemp.replace(/\{+/g, ""); // remove all >{<
sTemp = sTemp.replace(/\}+/g, ""); // remove all >}<
if (sTemp !== "") {
return false;
}
}
return true;
}
} // END Class
/*****************************************************************************************/
if (require.main !== module) {
// Export the constructor in compact mode
module.exports = (options) => new go_e_charger(options);
}
else {
// otherwise start the instance directly
(() => new go_e_charger())();
}
//# sourceMappingURL=main.js.map