@gjo said in IOBroker Anbindung an einen Kostal Plenticore:
@homeuser
Genau sowas habe ich mir vorgestellt. Wenn du das teilen könntest …
Vielen Dank
Gruß
Disclaimer und Erklärung:
Ich bin kein Javascript-Entwickler. Vielleicht sind manche Dinge daher sehr unschön gelöst. Außerdem ist es geschrieben nach dem Motto: Hauptsache es klappt.
UpdateJson(): Der erste Teil der Schleife erstellt ein Json aus den Forecast-Daten. Der zweite konsolidiert das etwas, um eine leicht verständliche Ansicht für den Rest der Familie zu haben.
UpdateFreeEnergy(): Berechnet wie viel Strom im Moment gerade übrig ist. Der "Moment" sind die letzten 10 Minuten, wobei die letzten drei doppelt gewichtet werden.
Ich habe mal alles drin gelassen, falls es nützlich ist. Wenn nicht, ist es ja einfach zu löschen
schedule ("*/1 * * * *", UpdatePlenticore)
function UpdatePlenticore() {
// free energy must be updated first because it is used by UpdateJson()
UpdateFreeEnergy();
UpdateJson();
}
function UpdateJson() {
let data = [];
let consolidatedData = [];
let time2;
let power;
let generated;
let c = 0; // used to group always 3 hours in one
let timeNow = new Date(Date.now());
let consolidatedPower = 0;
let consolidatedStars = 0;
let consolidatedTimeEnd = timeNow.getHours(); // default: if no full period is consolidated, start with now
// set value for now (based on the calculated currently free energy)
let freeEnergy = getState('0_userdata.0.Plenticore.FreeEnergy').val;
consolidatedData.push({'time' : 'jetzt','power' : Math.round(freeEnergy) + " Wh", 'stars' : ((freeEnergy-200)/500)});
// Day 1
data.length = 0;
for (let i = 1; i <= 20; i++) {
let forecastHour = "plenticore.0.forecast.day1.power." + i + "h"
// Day 1
if (existsState(forecastHour + '.time')) {
let time = new Date(getState(forecastHour + '.time').val);
// convert hourly forecast to JSON
time2 = time.toLocaleTimeString();
power = getState(forecastHour + '.power').val;
if (existsState(forecastHour + '.generated')) {
generated = getState(forecastHour + '.generated').val;
} else {
generated = 0;
}
data.push({'time' : time2,'power' : Math.round(power), 'generated' : Math.round(generated)});
// consolidate to timespan and quality (JSON)
//
// check if the period is in the future (don't show forecast for past hours)
if (time.getHours() > timeNow.getHours()) {
consolidatedPower += power;
if ( (c % 3) == 2) {
consolidatedTimeEnd = time.getHours();
time2 = (time.getHours()-2) + " - " + consolidatedTimeEnd + " Uhr";
consolidatedStars = Math.round(consolidatedPower / 2000); // translate power in stars
consolidatedData.push({'time' : time2,'power' : Math.round(consolidatedPower) + " Wh", 'stars' : consolidatedStars});
consolidatedPower = 0; // rest fo next timespan
}
c++;
}
}
}
// check if there a partially consolidated period pending
if ( (c % 3) == 1) {
time2 = (consolidatedTimeEnd + 1) + " Uhr";
consolidatedStars = Math.round(consolidatedPower / (2000/3)); // translate power in stars; use number of remaining items as factor
consolidatedData.push({'time' : time2,'power' : Math.round(consolidatedPower) + " Wh", 'stars' : consolidatedStars});
} else if ( (c % 3) == 2) {
time2 = (consolidatedTimeEnd + 1) + " - " + (consolidatedTimeEnd + 2) + " Uhr";
consolidatedStars = Math.round(consolidatedPower / ((2000*2)/3)); // translate power in stars; use number of remaining items as factor
consolidatedData.push({'time' : time2,'power' : Math.round(consolidatedPower) + " Wh", 'stars' : consolidatedStars});
}
setState('0_userdata.0.Plenticore.ForecastDay1', JSON.stringify(data), true);
setState('0_userdata.0.Plenticore.ConsolidatedDay1', JSON.stringify(consolidatedData), true);
// Day 2
c = 0; // used to group always 3 hours in one
consolidatedPower = 0;
consolidatedStars = 0;
consolidatedTimeEnd = 0;
data.length = 0;
consolidatedData.length = 0;
for (let i = 1; i <= 20; i++) {
let forecastHour = "plenticore.0.forecast.day2.power." + i + "h"
// Day 1
if (existsState(forecastHour + '.time')) {
let time = new Date(getState(forecastHour + '.time').val);
// convert hourly forecast to JSON
time2 = time.toLocaleTimeString();
power = getState(forecastHour + '.power').val;
data.push({'time' : time2,'power' : Math.round(power)})
// consolidate to timespan and quality (JSON)
consolidatedPower += power;
if ( (c % 3) == 2) {
consolidatedTimeEnd = time.getHours();
time2 = (time.getHours()-2) + " - " + consolidatedTimeEnd + " Uhr";
consolidatedStars = Math.round(consolidatedPower / 2000); // translate power in stars
consolidatedData.push({'time' : time2,'power' : Math.round(consolidatedPower) + " Wh", 'stars' : consolidatedStars});
consolidatedPower = 0; // rest fo next timespan
}
c++;
}
}
if ( (c % 3) != 0) { // not all hours are pushed to the consolidation
time2 = (consolidatedTimeEnd + 1) + " - " + (consolidatedTimeEnd + 1 + (c % 3) ) + " Uhr";
consolidatedStars = Math.round(consolidatedPower / 2000); // translate power in stars
consolidatedData.push({'time' : time2,'power' : Math.round(consolidatedPower) + " Wh", 'stars' : consolidatedStars});
consolidatedPower = 0; // rest fo next timespan
}
setState('0_userdata.0.Plenticore.ForecastDay2', JSON.stringify(data), true);
setState('0_userdata.0.Plenticore.ConsolidatedDay2', JSON.stringify(consolidatedData), true);
}
// Save free watt of past N minutes; calc average
function UpdateFreeEnergy() {
let freeEnergy = 0;
let batterySoc = 0;
const pastMinutes = 10;
// with forst call, create the array are set values to 0
if ( typeof UpdateFreeEnergy.pastValues == 'undefined' ) {
UpdateFreeEnergy.pastIndex = 0;
UpdateFreeEnergy.pastValues= [];
for (let i = 0; i < pastMinutes; i++) {
UpdateFreeEnergy.pastValues[i] = 0;
}
}
// calculate the current free value
freeEnergy = getState('plenticore.0.devices.local.Pv_P').val;
freeEnergy -= getState('plenticore.0.devices.local.Home_P').val;
if (freeEnergy < 0) {
freeEnergy = 0;
}
// put into array
UpdateFreeEnergy.pastValues[UpdateFreeEnergy.pastIndex] = freeEnergy;
// calc average of past values
for (let i = 0; i < pastMinutes; i++) {
//for the 3 most current values double the impact
if ( (i == UpdateFreeEnergy.pastIndex) || (i == (UpdateFreeEnergy.pastIndex + pastMinutes - 1) % pastMinutes) || (i == (UpdateFreeEnergy.pastIndex + pastMinutes - 2) % pastMinutes) ){
freeEnergy = freeEnergy + 2 * UpdateFreeEnergy.pastValues[i];
} else {
freeEnergy = freeEnergy + UpdateFreeEnergy.pastValues[i];
}
}
freeEnergy = freeEnergy / (pastMinutes + 3); // +3 for the doubled impact of the the 3 most current values
// if the battery is below the defined minimum, the battery has priority; so there is no free energy
// recommendation:
// Use same value as in the settings of the plenticore adapter for "minimaler SoC zur Aktivierung des Batteriemanagements".
// The plenticore adapter stops charging a little before the limit. So the value should be 2-3% lower.
batterySoc = getState('plenticore.0.devices.local.battery.SoC').val;
if (batterySoc <= 12) {
freeEnergy = 0;
}
// save calculated average
setState('0_userdata.0.Plenticore.FreeEnergy', freeEnergy, true);
// inc counter for cyclic buffer
UpdateFreeEnergy.pastIndex = (UpdateFreeEnergy.pastIndex + 1) % pastMinutes;
}