iobroker.go-e
Version:
go-e ist die Aufforderung, sich elektrisch zu bewegen. e-Mobilität ist unser Antrieb, wobei unsere Kernkompetenz die Ladetechnik für Elektroautos ist. Von der einzelnen Ladestation für e-Autos über Photovoltaik-Anbindung bis hin zum Lastmanagement von gan
827 lines (797 loc) • 81.6 kB
JavaScript
'use strict';
/*
* Created with @iobroker/create-adapter v1.26.0
*/
// The adapter-core module gives you access to the core ioBroker functions
// you need to create an adapter
const utils = require('@iobroker/adapter-core');
// Load your modules here, e.g.:
// const fs = require("fs");
const axios = require('axios').default;
const {default: PQueue} = require('p-queue');
const schema = require('./lib/schema.js').schema;
class GoE extends utils.Adapter {
/**
* @param {Partial<utils.AdapterOptions>} [options={}]
*/
constructor(options) {
super({
...options,
name: 'go-e',
});
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));
// Timer Object for the update interval for ampere to the adapter
this.ampTimer = null;
// Timestamp for lastSwitchRequest
this.lastPhaseSwitchRequest = null;
// Timestamp for lastStop Request
this.lastStopRequest = null;
// Translation Object
this.translationObject = {
al1: 'settings.ampere_level1',
al2: 'settings.ampere_level2',
al3: 'settings.ampere_level3',
al4: 'settings.ampere_level4',
al5: 'settings.ampere_level5',
lbr: 'settings.color.led_brightness'
};
// Translation Object API v2
this.translationObjectV2 = {
alw: {name: 'allow_charging'},
rbc: {name: 'reboot_counter'},
rbt: {name: 'reboot_timer'},
car: {name: 'car'},
amp: {name: 'ampere'}
};
// Ack alignment object
this.ackObj = {};
}
/**
* Is called when databases are connected and adapter received configuration.
*/
async onReady() {
// Initialize your adapter here
// Write it to possible values
this.log.info('Adapter is staring in Version setByGitHubActions');
// this.log.debug('Update selectable values from ' + JSON.stringify(this.config.selectedAttributes) + ' to ' + Object.keys(this.translationObjectV2) + '; Working with Version ' + this.config.apiVersion);
// this.config.possibleAttributes = Object.keys(this.translationObjectV2);
// The adapters config (in the instance object everything under the attribute "native") is accessible via
// this.config:
this.log.info('Server: ' + this.config.serverName);
this.log.info('Intervall: ' + this.config.serverInterval);
this.log.info('Calculation Method: ' + this.config.calcMethod);
// In order to get state updates, you need to subscribe to them. The following line adds a subscription for our variable we have created above.
this.subscribeStates('access_state');
this.subscribeStates('allow_charging');
this.subscribeStates('ampere');
this.subscribeStates('amperePV');
this.subscribeStates('energy.adjustAmpLevelInWatts');
this.subscribeStates('energy.max_watts');
this.subscribeStates('max_load');
this.subscribeStates('settings.ampere_level1');
this.subscribeStates('settings.ampere_level2');
this.subscribeStates('settings.ampere_level3');
this.subscribeStates('settings.ampere_level4');
this.subscribeStates('settings.ampere_level5');
this.subscribeStates('settings.color.idle');
this.subscribeStates('settings.color.charging');
this.subscribeStates('settings.color.finish');
this.subscribeStates('settings.led_save_energy');
this.subscribeStates('settings.led_brightness');
this.subscribeStates('solarLoadOnly');
this.subscribeStates('stop_state');
this.subscribeStates('unlock_state');
this.subscribeStates('phaseSwitchMode');
this.subscribeStates('schedule.allowChargeingForMins');
this.subscribeStates('schedule.stopChargeingAt');
if(this.config.calcMethod == 'iob') {
// Disable FUP (Solar überschuss)
if(this.config.apiVersion == 2)
this.setValueV2('fup', false).catch(() => {
// Do nothing
});
// get updates from a foreign adapter if it is set in Settings
if(this.config.houseBatteryForeignObjectID) {
this.subscribeForeignStates(this.config.houseBatteryForeignObjectID);
this.ackObj[this.config.houseBatteryForeignObjectID] = this.config.houseBatteryForeignObjectAck;
this.log.debug('Subscribe foreign object ' + this.config.houseBatteryForeignObjectID);
}
if(this.config.houseConsumptionForeignObjectID) {
this.subscribeForeignStates(this.config.houseConsumptionForeignObjectID);
this.ackObj[this.config.houseConsumptionForeignObjectID] = this.config.houseConsumptionForeignObjectAck;
this.log.debug('Subscribe foreign object ' + this.config.houseConsumptionForeignObjectID);
}
if(this.config.solarPowerForeignObjectID) {
this.subscribeForeignStates(this.config.solarPowerForeignObjectID);
this.ackObj[this.config.solarPowerForeignObjectID] = this.config.solarPowerForeignObjectAck;
this.log.debug('Subscribe foreign object ' + this.config.solarPowerForeignObjectID);
}
}
this.log.silly('Ack-Obj: ' + JSON.stringify(this.ackObj));
// setup axios
axios.defaults.baseURL = 'http://' + this.config.serverName;
// Get all Information for the first time.
await this.getStateFromDevice();
// Start the Adapter to sync in the interval
this.interval = setInterval(async () => {
await this.getStateFromDevice();
}, this.config.serverInterval * 1000);
if(this.config.calcMethod !== 'iob' && this.config.apiVersion == 2) {
// Start the Adapter to sync in the interval
this.interval = setInterval(async () => {
await this.writeIds();
}, 4 * 1000);
} else if (this.config.calcMethod !== 'iob' && this.config.apiVersion != 2) {
this.log.error('For Hardware use to calc PV is API V2 required. But is not enabled in settings.');
}
}
/**
* Is called when adapter shuts down - callback has to be called under any circumstances!
* @param {() => void} callback
*/
onUnload(callback) {
try {
// Here you must clear all timeouts or intervals that may still be active
// clearTimeout(timeout1);
// clearTimeout(timeout2);
// ...
// clearInterval(interval1);
// @ts-ignore
clearInterval(this.interval);
callback();
} catch (e) {
callback();
this.log.silly('callback ' + e);
}
}
// If you need to react to object changes, uncomment the following block and the corresponding line in the constructor.
// You also need to subscribe to the objects with `this.subscribeObjects`, similar to `this.subscribeStates`.
// /**
// * Is called if a subscribed object changes
// * @param {string} id
// * @param {ioBroker.Object | null | undefined} obj
// */
// onObjectChange(id, obj) {
// if (obj) {
// // The object was changed
// this.log.info(`object ${id} changed: ${JSON.stringify(obj)}`);
// } else {
// // The object was deleted
// this.log.info(`object ${id} deleted`);
// }
// }
/**
* Is called if a subscribed state changes
* @param {string} id
* @param {ioBroker.State | null | undefined} state
*/
onStateChange(id, state) {
if (state) {
// The state was changed
if (!state.ack) {
// If it is already acknoladged, we dont have to send it to the go-eCharger device. Or have to handle the change.
this.log.silly(`state ${id} changed: ${state.val} (ack = ${state.ack}) namespace: ` + this.namespace);
// Handle null values with the rejection
if(state.val === null) {
this.log.warn('Not able to handle null Values in ' + id);
return;
}
switch (id) {
// Sort by alphabet of attribute
case this.namespace + '.access_state':
if(parseInt(state.val.toString()) == 0 || parseInt(state.val.toString(), 10) == 1 || parseInt(state.val.toString(), 10) == 2 ) {
this.setValue('ast', parseInt(state.val.toString(), 10));
} else {
this.log.warn('Could not set value ' + state.val.toString() + ' in ' + id);
}
break;
case this.namespace + '.allow_charging':
if(parseInt(state.val.toString()) == 0 || parseInt(state.val.toString(), 10) == 1 ) {
this.setValue('alw', parseInt(state.val.toString(), 10));
} else {
this.log.warn('Could not set value ' + state.val.toString() + ' in ' + id);
}
break;
case this.namespace + '.ampere':
this.setValue('amp', state.val.toString());
break;
case this.namespace + '.amperePV':
this.setValue('amx', state.val.toString());
break;
case this.namespace + '.energy.adjustAmpLevelInWatts':
this.adjustAmpLevelInWatts(parseInt(state.val.toString(), 10));
this.setState('energy.adjustAmpLevelInWatts', { val: parseInt(state.val.toString(), 10), ack: true });
break;
case this.namespace + '.energy.max_watts':
this.updateAmpLevel(parseInt(state.val.toString()));
this.setState('energy.max_watts', { val: parseInt(state.val.toString(), 10), ack: true });
break;
case this.namespace + '.max_load':
this.setValue('dwo', parseInt(state.val.toString(), 10) * 10);
break;
case this.namespace + '.settings.ampere_level1':
this.setAmpLevelToButton('al1', parseInt(state.val.toString(), 10));
break;
case this.namespace + '.settings.ampere_level2':
this.setAmpLevelToButton('al2', parseInt(state.val.toString(), 10));
break;
case this.namespace + '.settings.ampere_level3':
this.setAmpLevelToButton('al3', parseInt(state.val.toString(), 10));
break;
case this.namespace + '.settings.ampere_level4':
this.setAmpLevelToButton('al4', parseInt(state.val.toString(), 10));
break;
case this.namespace + '.settings.ampere_level5':
this.setAmpLevelToButton('al5', parseInt(state.val.toString(), 10));
break;
case this.namespace + '.settings.color.idle':
// @ts-ignore // Check off null is done
this.setValue('cid', /^#?([a-f\d]{6})$/i.exec(state.val.toString()) !== null ? parseInt(/^#?([a-f\d]{6})$/i.exec(state.val.toString())[1], 16) : 0);
break;
case this.namespace + '.settings.color.charging':
// @ts-ignore // Check off null is done
// this.setValue("cch", /^#?([a-f\d]{6})$/i.exec(state.val.toString()) !== null ? parseInt(/^#?([a-f\d]{6})$/i.exec(state.val.toString())[1], 16) : 0);
// bug in versions starting 042; have to use V2
this.setValueV2('cch', encodeURIComponent(/^#?([a-f\d]{6})$/i.exec(state.val.toString()) !== null ? state.val.toString() : '#FFFFFF')).catch();
break;
case this.namespace + '.settings.color.finish':
// @ts-ignore // Check off null is done
this.setValue('cfi', /^#?([a-f\d]{6})$/i.exec(state.val.toString()) !== null ? parseInt(/^#?([a-f\d]{6})$/i.exec(state.val.toString())[1], 16) : 0);
break;
case this.namespace + '.settings.color.led_save_energy':
this.setValue('lse', parseInt(state.val.toString(), 10));
break;
case this.namespace + '.settings.color.led_brightness':
this.setValue('lbr', parseInt(state.val.toString(), 10));
break;
case this.namespace + '.solarLoadOnly':
if(this.config.calcMethod == 'iob') {
// Is solarOnly => false (off)
if(!state.val) {
this.setValue('alw', 1);
if(this.config.apiVersion == 2) {
this.setValueV2('fup', 0).catch(() => {}); // go-e Solarladen deaktivieren
this.setValueV2('psm', this.config.defaultPSM).catch(() => {}); // Phases Switch to auto
}
} else {
this.setValueV2('psm', 1).catch(() => {}); // Phases Switch to 1-phase
}
} else {
this.setValueV2('fup', state.val)
.then(() => {
this.setState('solarLoadOnly', {ack:true});
})
.catch(() => {
// Do nothing
});
}
break;
case this.namespace + '.stop_state':
if(parseInt(state.val.toString()) === 0 || parseInt(state.val.toString()) == 2 ) {
this.setValue('stp', parseInt(state.val.toString(), 10));
} else {
this.log.warn('Could not set value ' + state.val.toString() + ' into ' + id);
}
break;
case this.namespace + '.unlock_state':
if(parseInt(state.val.toString()) === 0 || parseInt(state.val.toString()) === 1 || parseInt(state.val.toString()) == 2 ) {
this.setValue('ust', parseInt(state.val.toString(), 10));
} else {
this.log.warn('Could not set value ' + state.val.toString() + ' into ' + id);
}
break;
case this.namespace + '.phaseSwitchMode':
if(parseInt(state.val.toString()) === 0 || parseInt(state.val.toString()) === 1 || parseInt(state.val.toString()) == 2 ) {
this.setValueV2('psm', parseInt(state.val.toString())).catch(() => {});
} else {
this.log.warn('Could not set value ' + state.val.toString() + ' into ' + id + ' (psm)');
}
break;
case this.namespace + '.schedule.allowChargeingForMins':
if(parseInt(state.val.toString()) > 0) {
this.setState('schedule.allowChargeingForMins', {val: state.val, ack: true});
this.setState('schedule.stopChargeingAt', {val: new Date(Date.now() + (parseInt(state.val.toString()) * 60 * 1000)).toISOString(), ack: true});
this.setState('schedule.stopChargeingEnabled', {val: true, ack:true});
if(state.val !== undefined && state.val !== null)
this.setStopTimeout = setTimeout(() => {
this.setValue('alw', 0);
this.log.info('Stop process because of schedule Minutes');
}, parseInt(state.val.toString()) * 1000);
}
break;
case this.namespace + '.schedule.stopChargeingAt':
if(!isNaN(Date.parse(state.val.toString())) && Date.parse(state.val.toString()) > Date.now()) {
this.setState('schedule.stopChargeingAt', {val: state.val, ack: true});
this.setState('schedule.stopChargeingEnabled', {val: true, ack:true});
if(state.val !== undefined && state.val !== null)
this.setStopTimeout = setTimeout(() => {
this.setValue('alw', 0);
this.log.info('Stop process because of schedule At');
}, Date.parse(state.val.toString()) - Date.now());
}
break;
case this.config.solarPowerForeignObjectID:
case this.config.houseBatteryForeignObjectID:
case this.config.houseConsumptionForeignObjectID:
if(this.ackObj[id] === false) {
this.log.silly('Will work on ' + id + ' becase ack is ' + state.ack + ' and should be ' + this.ackObj[id]);
this.calculateFromForeignObjects(id);
} else {
this.log.silly('Will NOT work on ' + id + ' becase ack is ' + state.ack + ' and should be ' + this.ackObj[id]);
}
break;
default:
this.log.error('Not developed function to write ' + id + ' with state ' + state.val.toString());
}
} else {
// Ack = true
switch (id) {
case this.config.solarPowerForeignObjectID:
case this.config.houseBatteryForeignObjectID:
case this.config.houseConsumptionForeignObjectID:
this.log.silly(`state ${id} changed: ${state.val} (ack = ${state.ack}) namespace: ` + this.namespace);
if(this.ackObj[id] === true) {
this.log.silly('Will work on ' + id + ' becase ack is ' + state.ack + ' and should be ' + this.ackObj[id]);
this.calculateFromForeignObjects(id);
} else {
this.log.silly('Will NOT work on ' + id + ' becase ack is ' + state.ack + ' and should be ' + this.ackObj[id]);
}
break;
}
}
} else {
// The state was deleted
this.log.info(`state ${id} deleted`);
}
}
// If you need to accept messages in your adapter, uncomment the following block and the corresponding line in the constructor.
// /**
// * Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...
// * Using this method requires "common.message" property to be set to true in io-package.json
// * @param {ioBroker.Message} obj
// */
// onMessage(obj) {
// if (typeof obj === "object" && obj.message) {
// if (obj.command === "send") {
// // e.g. send email or pushover or whatever
// this.log.info("send command");
// // Send response in callback if required
// if (obj.callback) this.sendTo(obj.from, obj.command, "Message received", obj.callback);
// }
// }
// }
/**
* This function get the JSON Object from the go-E Charger status API
*/
async getStateFromDevice() {
// let apiEndpoint = "/status";
if(this.config.apiVersion == 2) {
// apiEndpoint = "/api/status?filter=" + Object.keys(this.translationObjectV2).join(",");
// Get additional parameters from API v2
this.log.debug(`Starte V2 Abfrage an: http://${this.config.serverName}/api/status?filter=psm`);
axios.get('/api/status?filter=psm')
.then((o) => {
this.log.silly('Response: ' + o.status + ' - ' + o.statusText + ' with data as ' + typeof o.data);
// this.log.debug(JSON.stringify(o.data));
if(typeof o.data == 'object') {
this.setState('phaseSwitchMode', { 'val': parseInt(o.data['psm']), ack: true });
} else {
this.log.warn(`Response of psm is ${typeof o.data}`);
}
})
.catch((e) => {
this.log.error(e);
});
// Hole die benutzerdefinierten Informationen
const queryStr = this.config.selectedAttributes.join(',');
this.log.debug(`http://${this.config.serverName}/api/status?filter=${queryStr}`);
// todo abfrage erstellen.
axios.get(`/api/status?filter=${queryStr}`)
.then((o) => {
this.log.silly('Response: ' + o.status + ' - ' + o.statusText + ' with data as ' + typeof o.data);
this.log.debug(JSON.stringify(o.data));
this.processStatusObject(o.data);
})
.catch((e) => {
this.log.error(e);
});
}
// Get all other attributes from API-V1
// this.log.debug("Starte V1 Abfrage an: http://" + this.config.serverName + apiEndpoint);
axios.defaults.baseURL = 'http://' + this.config.serverName;
await axios.get('/status')
.then((o) => {
this.log.silly('Response: ' + o.status + ' - ' + o.statusText + ' with data as ' + typeof o.data);
this.log.debug(JSON.stringify(o.data));
if(typeof o.data != 'object') {
this.log.error('Respose id type ' + (typeof o.data) + '; ' + JSON.stringify(o.data));
} else {
const validation = schema.validate(o.data,{abortEarly: false});
// @ts-ignore
if (validation.error != undefined && (validation.error || validation.value === undefined)) {
if (validation.value === undefined) {
this.log.error('API send no content');
} else {
this.log.error('API response validation error: ' + JSON.stringify(validation.error.details));
this.log.info(JSON.stringify(validation.error._original));
}
} else {
this.processStatusObject(o.data);
}
}
this.setState('info.connection', true, true);
})
.catch(e => {
if(e.code == 'ENOTFOUND') {
this.setState('info.connection', false, true);
this.log.warn('Host not found: ' + this.config.serverName);
} else if(e.code == 'EAI_AGAIN') {
this.setState('info.connection', false, true);
this.log.warn('Network/DNS broken to: ' + this.config.serverName);
} else if(e.code == 'ECONNRESET') {
this.setState('info.connection', false, true);
this.log.warn('Cant connect to host ' + this.config.serverName);
} else if(e.code == 'EHOSTUNREACH') {
this.setState('info.connection', false, true);
this.log.warn('Can not route to the host ' + this.config.serverName);
} else if (e.response && e.response.status === 404) {
this.setState('info.connection', false, true);
this.log.warn('Adapter not ready ' + this.config.serverName);
} else {
this.log.error(e.message);
}
});
}
/**
* This function is writing the IDS endpoint on the go-e adapter based to given attributes
*/
async writeIds() {
let availWatts = await this.getNumberFromForeignObjectId(this.config.solarPowerForeignObjectID);
let houseBattery = await this.getNumberFromForeignObjectId(this.config.houseBatteryForeignObjectID);
if(this.config.solarPowerForeignObjectNegate) {
availWatts = availWatts * -1;
this.log.silly('Negate watts of Solar; new: ' + availWatts);
}
if(this.config.houseBatteryForeignObjectNegate) {
houseBattery = houseBattery * -1;
this.log.silly('Negate watts of HouseBattery; new: ' + houseBattery);
}
const buildObj = {
pGrid: availWatts,
pAkku: houseBattery
};
axios.get('/api/set?ids=' + JSON.stringify(buildObj))
.then((res) => {
this.log.debug('Wrote ids Object: ' + JSON.stringify(buildObj) + ' with response ' + JSON.stringify(res.data));
})
.catch((e) => {
this.log.warn('Was not able to write ids: ' + JSON.stringify(buildObj) + '; Error: ' + e.message);
});
}
/**
* Process a default status response as descibed in the api documentation of go-eCharger
* @param {object} o
*/
async processStatusObject(o) {
try {
// Const for variable pha
const postContactorPhase1 = 1;
const postContactorPhase2 = 2;
const postContactorPhase3 = 4;
const preContactorPhase1 = 8;
const preContactorPhase2 = 16;
const preContactorPhase3 = 32;
// Allows only 4 asnychronous calls others are queued
const queue = new PQueue({concurrency: 4});
for (const [key, value] of Object.entries(o)) {
//console.log(`${key}: ${value}`);
switch (key) {
case 'version':
await queue.add(() => this.setState('encryption', { val: o.version == 'C' ? true : false, ack: true })); // read
break;
case 'tme':
try {
// TME provides 2208201643
// sometimes it provides "0302-300526" see #171
// TODO: No glue what this is about.
// Realdate: 22th August 2020 at 16:43 (CET)
// this.log.debug(" Synctime: " + o.tme);
const reggie = /(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/;
// @ts-ignore
const [, day, month, year, hours, minutes] = reggie.exec(o.tme);
const dateObject = new Date(parseInt(year, 10)+2000, parseInt(month, 10)-1, parseInt(day, 10), parseInt(hours, 10), parseInt(minutes, 10), 0);
await queue.add(() => this.setState('synctime', { val: dateObject.toISOString(), ack: true }));
} catch (e) {
// @ts-ignore
this.log.info('Cloud not store synctime, because of error ' + e.message);
}
break;
case 'rbc':
await queue.add(() => this.setState('reboot_counter', { val: parseInt(o.rbc, 10), ack: true })); // read, V1, V2
break;
case 'car':
await queue.add(() => this.setState('car', { val: parseInt(o.car, 10), ack: true })); // read, V1, V2
break;
case 'rbt':
await queue.add(() => this.setState('reboot_timer', { val: parseInt(o.rbt, 10), ack: true })); // read, V1, V2
break;
case 'amp':
await queue.add(() => this.setState('ampere', { val: parseInt(o.amp, 10), ack: true })); // write, V1, V2
break;
case 'amx':
if(o.amx === undefined || o.amx == null) {
await queue.add(() => this.setState('amperePV', { val: parseInt(o.amp, 10), ack: true })); // COPY AMP Value to AMX V1
} else {
await queue.add(() => this.setState('amperePV', { val: parseInt(o.amx, 10), ack: true })); // write, V1
}
break;
case 'err':
await queue.add(() => this.setState('error', { val: parseInt(o.err, 10), ack: true })); // read
break;
case 'ast':
await queue.add(() => this.setState('access_state', { val: parseInt(o.ast, 10), ack: true })); // write
break;
case 'alw':
if(typeof o.alw == 'boolean')
await queue.add(() => this.setState('allow_charging', { val: + o.alw, ack: true })); // V2 (Convert to Integer by using +)
else
await queue.add(() => this.setState('allow_charging', { val: parseInt(o.alw, 10), ack: true })); // write, V2
break;
case 'stp':
await queue.add(() => this.setState('stop_state', { val: parseInt(o.stp, 10), ack: true })); // write
break;
case 'pha':
await queue.add(() => this.setState('phases', { val: parseInt(o.pha, 10), ack: true })); // read
// Split phases in single states
await queue.add(() => this.setState('energy.phase1.preContactorActive', { val: ((parseInt(o.pha, 10) & preContactorPhase1) == preContactorPhase1), ack: true})); //read
await queue.add(() => this.setState('energy.phase1.postContactorActive', { val: ((parseInt(o.pha, 10) & postContactorPhase1) == postContactorPhase1), ack: true})); //read
await queue.add(() => this.setState('energy.phase2.preContactorActive', { val: ((parseInt(o.pha, 10) & preContactorPhase2) == preContactorPhase2), ack: true})); //read
await queue.add(() => this.setState('energy.phase2.postContactorActive', { val: ((parseInt(o.pha, 10) & postContactorPhase2) == postContactorPhase2), ack: true})); //read
await queue.add(() => this.setState('energy.phase3.preContactorActive', { val: ((parseInt(o.pha, 10) & preContactorPhase3) == preContactorPhase3), ack: true})); //read
await queue.add(() => this.setState('energy.phase3.postContactorActive', { val: ((parseInt(o.pha, 10) & postContactorPhase3) == postContactorPhase3), ack: true})); //read
await queue.add(() => this.setState('energy.phase1.voltage', { val: parseInt(o.nrg[0], 10), ack: true })); // read
break;
case 'nrg':
await queue.add(() => this.setState('energy.phase2.voltage', { val: parseInt(o.nrg[1], 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase3.voltage', { val: parseInt(o.nrg[2], 10), ack: true })); // read
await queue.add(() => this.setState('energy.neutral.voltage', { val: parseInt(o.nrg[3], 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase1.ampere', { val: (o.nrg[4] / 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase2.ampere', { val: (o.nrg[5] / 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase3.ampere', { val: (o.nrg[6] / 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase1.power', { val: (o.nrg[7] / 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase2.power', { val: (o.nrg[8] / 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase3.power', { val: (o.nrg[9] / 10), ack: true })); // read
await queue.add(() => this.setState('energy.neutral.power', { val: (o.nrg[10] / 10), ack: true })); // read
await queue.add(() => this.setState('energy.power', { val: (o.nrg[11] / 100), ack: true })); // read
await queue.add(() => this.setState('energy.phase1.power_coefficient', { val: parseInt(o.nrg[12], 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase2.power_coefficient', { val: parseInt(o.nrg[13], 10), ack: true })); // read
await queue.add(() => this.setState('energy.phase3.power_coefficient', { val: parseInt(o.nrg[14], 10), ack: true })); // read
await queue.add(() => this.setState('energy.neutral.power_coefficient', { val: parseInt(o.nrg[15], 10), ack: true })); // read
break;
case 'cbl':
await queue.add(() => this.setState('cable_ampere_code', { val: parseInt(o.cbl), ack: true })); // read
break;
case 'amt':
await queue.add(() => this.setState('avail_ampere', { val: parseInt(o.amt, 10), ack: true }));
break;
case 'eto':
await queue.add(() => this.setState('energy.total', { val: (o.eto / 10), ack: true })); // read
break;
case 'wst':
await queue.add(() => this.setState('wifi.state', { val: parseInt(o.wst, 10), ack: true })); // read
break;
case 'txi':
await queue.add(() => this.setState('transmit_interface', { val: o.txi, ack: true }));
break;
case 'wss':
await queue.add(() => this.setState('wifi.ssid', { val: o.wss, ack: true })); // write
break;
case 'wke':
await queue.add(() => this.setState('wifi.key', { val: o.wke, ack: true })); // write
break;
case 'wen':
await queue.add(() => this.setState('wifi.enabled', { val: parseInt(o.wen, 10), ack: true })); // write
break;
case 'cdi':
await queue.add(() => this.setState('cloud_disabled', { val: parseInt(o.cdi, 10), ack: true }));
break;
case 'wak':
await queue.add(() => this.setState('wifi.hotspot_key', { val: o.wak, ack: true })); // write
break;
case 'r1x':
await queue.add(() => this.setState('http_flags', { val: parseInt(o.r1x, 10), ack: true })); // write
break;
case 'dws':
await queue.add(() => this.setState('loaded_energy', { val: parseInt(o.dws, 10), ack: true })); // read
if(/^050/.test(o.fwv)) {
await queue.add(() => this.setState('loaded_energy_kwh', { val: o.dws / 100, ack: true}));
} else {
await queue.add(() => this.setState('loaded_energy_kwh', { val: o.dws * 10 / 60 / 60 / 1000, ack: true}));
}
break;
case 'dwo':
await queue.add(() => this.setState('max_load', { val: (o.dwo / 10), ack: true })); // write
break;
case 'aho':
await queue.add(() => this.setState('electricity_exchange.min_hours', { val: parseInt(o.aho, 10), ack: true })); // write
break;
case 'afi':
await queue.add(() => this.setState('electricity_exchange.finish_hour', { val: parseInt(o.afi, 10), ack: true })); // write
break;
case 'azo':
await queue.add(() => this.setState('electricity_exchange.price_zone', { val: parseInt(o.azo, 10), ack: true }));
break;
case 'ama':
await queue.add(() => this.setState('max_ampere', { val: parseInt(o.ama, 10), ack: true }));
break;
case 'fwv':
await queue.add(() => this.setState('firmware_version', { val: o.fwv, ack: true })); // read
break;
case 'sse':
await queue.add(() => this.setState('serial_number', { val: o.sse, ack: true })); // read
break;
case 'lbr':
await queue.add(() => this.setState('settings.color.led_brightness', { val: parseInt(o.lbr, 10), ack: true })); // write
break;
case 'al1':
await queue.add(() => this.setState('settings.ampere_level1', { val: parseInt(o.al1, 10), ack: true })); // write
break;
case 'al2':
await queue.add(() => this.setState('settings.ampere_level2', { val: parseInt(o.al2, 10), ack: true })); // write
break;
case 'al3':
await queue.add(() => this.setState('settings.ampere_level3', { val: parseInt(o.al3, 10), ack: true })); // write
break;
case 'al4':
await queue.add(() => this.setState('settings.ampere_level4', { val: parseInt(o.al4, 10), ack: true })); // write
break;
case 'al5':
await queue.add(() => this.setState('settings.ampere_level5', { val: parseInt(o.al5, 10), ack: true })); // write
break;
case 'cid':
await queue.add(() => this.setState('settings.color.idle', { val: '#' + ('000000' + parseInt(o.cid, 10).toString(16)).slice(6), ack: true })); // write
break;
case 'cch':
await queue.add(() => this.setState('settings.color.charging', { val: '#' + ('000000' + parseInt(o.cch, 10).toString(16)).slice(6), ack: true })); // write
break;
case 'cfi':
await queue.add(() => this.setState('settings.color.finish', { val: '#' + ('000000' + parseInt(o.cfi, 10).toString(16)).slice(6), ack: true })); // write
break;
case 'tof':
await queue.add(() => this.setState('time_offset', { val: parseInt(o.tof, 10), ack: true})); // write
break;
case 'tds':
await queue.add(() => this.setState('time_daylight_saving', { val: parseInt(o.tds, 10), ack: true })); // write
break;
case 'eca':
// RFID Badges
await queue.add(() => this.setState('rfid.badges.1.consumption', { val: (o.eca / 10), ack: true })); // read
break;
case 'ecr':
await queue.add(() => this.setState('rfid.badges.2.consumption', { val: (o.ecr / 10), ack: true })); // read
break;
case 'ecd':
await queue.add(() => this.setState('rfid.badges.3.consumption', { val: (o.ecd / 10), ack: true })); // read
break;
case 'ec4':
await queue.add(() => this.setState('rfid.badges.4.consumption', { val: (o.ec4 / 10), ack: true })); // read
break;
case 'ec5':
await queue.add(() => this.setState('rfid.badges.5.consumption', { val: (o.ec5 / 10), ack: true })); // read
break;
case 'ec6':
await queue.add(() => this.setState('rfid.badges.6.consumption', { val: (o.ec6 / 10), ack: true })); // read
break;
case 'ec7':
await queue.add(() => this.setState('rfid.badges.7.consumption', { val: (o.ec7 / 10), ack: true })); // read
break;
case 'ec8':
await queue.add(() => this.setState('rfid.badges.8.consumption', { val: (o.ec8 / 10), ack: true })); // read
break;
case 'ec9':
await queue.add(() => this.setState('rfid.badges.9.consumption', { val: (o.ec9 / 10), ack: true })); // read
break;
case 'ec1':
await queue.add(() => this.setState('rfid.badges.10.consumption', { val: (o.ec1 / 10), ack: true })); // read
break;
case 'rca':
await queue.add(() => this.setState('rfid.badges.1.id', { val: o.rca, ack: true })); // read
break;
case 'rcr':
await queue.add(() => this.setState('rfid.badges.2.id', { val: o.rcr, ack: true })); // read
break;
case 'rcd':
await queue.add(() => this.setState('rfid.badges.3.id', { val: o.rcd, ack: true })); // read
break;
case 'rc4':
await queue.add(() => this.setState('rfid.badges.4.id', { val: o.rc4, ack: true })); // read
break;
case 'rc5':
await queue.add(() => this.setState('rfid.badges.5.id', { val: o.rc5, ack: true })); // read
break;
case 'rc6':
await queue.add(() => this.setState('rfid.badges.6.id', { val: o.rc6, ack: true })); // read
break;
case 'rc7':
await queue.add(() => this.setState('rfid.badges.7.id', { val: o.rc7, ack: true })); // read
break;
case 'rc8':
await queue.add(() => this.setState('rfid.badges.8.id', { val: o.rc8, ack: true })); // read
break;
case 'rc9':
await queue.add(() => this.setState('rfid.badges.9.id', { val: o.rc9, ack: true })); // read
break;
case 'rc1':
await queue.add(() => this.setState('rfid.badges.10.id', { val: o.rc1, ack: true })); // read
break;
case 'rna':
// RFID Name
await queue.add(() => this.setState('rfid.badges.1.name', { val: o.rna, ack: true })); // write
break;
case 'rnm':
await queue.add(() => this.setState('rfid.badges.2.name', { val: o.rnm, ack: true })); // write
break;
case 'rne':
await queue.add(() => this.setState('rfid.badges.3.name', { val: o.rne, ack: true })); // write
break;
case 'rn4':
await queue.add(() => this.setState('rfid.badges.4.name', { val: o.rn4, ack: true })); // write
break;
case 'rn5':
await queue.add(() => this.setState('rfid.badges.5.name', { val: o.rn5, ack: true })); // write
break;
case 'rn6':
await queue.add(() => this.setState('rfid.badges.6.name', { val: o.rn6, ack: true })); // write
break;
case 'rn7':
await queue.add(() => this.setState('rfid.badges.7.name', { val: o.rn7, ack: true })); // write
break;
case 'rn8':
await queue.add(() => this.setState('rfid.badges.8.name', { val: o.rn8, ack: true })); // write
break;
case 'rn9':
await queue.add(() => this.setState('rfid.badges.9.name', { val: o.rn9, ack: true })); // write
break;
case 'rn1':
await queue.add(() => this.setState('rfid.badges.10.name', { val: o.rn1, ack: true })); // write
break;
case 'mce':
await queue.add(() => this.setState('mqtt.enabled', { val: parseInt(o.mce, 10), ack: true }));
break;
case 'mcs':
await queue.add(() => this.setState('mqtt.server', { val: o.mcs, ack: true }));
break;
case 'mcp':
await queue.add(() => this.setState('mqtt.port', { val: o.mcp, ack: true }));
break;
case 'mcu':
await queue.add(() => this.setState('mqtt.user', { val: o.mcu, ack: true }));
break;
case 'mck':
await queue.add(() => this.setState('mqtt.key', { val: o.mck, ack: true }));
break;
case 'mcc':
await queue.add(() => this.setState('mqtt.connection', { val: o.mcc, ack: true }));
break;
case 'tmp':
await queue.add(() => this.setState('temperatures.maintemperature', { val: parseInt(o.tmp, 10), ack: true })); // read
break;
case 'tma':
try {
let tempArr = o.tma.toString().split(',');
for(let i = 0; i<tempArr.length; i++) {
const tmpObj = await this.getObjectAsync('temperatures.temperature' + (i+1));
this.log.silly('temperatures.temperature' + (i+1) + ': ' + JSON.stringify(tmpObj));
if ( tmpObj == null) {
const obj = {
name: 'temperatures.temperature' + (i+1),
type: 'number',
read: true,
write: false,
role: 'value.temperature',
desc: 'Temperature Sensor'
};