NEWS
setState's in Funktionen
-
@hanss Die enthaltende Funktion muss als
async
deklariert sein. Das beißt sich aber auch meit deinem .forEach, welches du am besten gegen eine for-Schleife ersetzt. Ich hab hier nur die minimal nötige Änderung gemacht - so richtig erschließt sich mir dein Umgang mit dem Array nicht (Iteration durch eins, Zugriff auf ein anderes):
async function evohome_IF(){ const EvohomeClient = require('evohome').EvohomeClient const HeatSetpointStatus = require('evohome').HeatSetpointStatus const username = 'USERNAME' const password = 'PASSWORT' const evohomeClient = new EvohomeClient(username, password) evohomeClient.getLocationsWithAutoLogin(300).then(locations => { // (x) = Session Abbruch nach x Sekunden // log('JSON OBJ: '+JSON.stringify(locations)) // Durch Array iterieren, im Kontext der aktuellen Funktion for (let index = 0; index < S_Raum.length; index++) { // z.Zt. 8 Räume 0-7 const deviceId = locations[0].devices[index].deviceID var Raum_name = locations[0].devices[index].name var Rindex = S_Raum.indexOf(Raum_name) // suchen Raumname in S_Raum Array if (Rindex != -1) { var IstTemp = locations[0].devices[index].thermostat.indoorTemperature var SollTemp = locations[0].devices[index].thermostat.changeableValues.heatSetpoint.value // log(Rindex+'-Zimmer: '+Raum_name+' Temperatur: '+IstTemp+' Soll Temp: '+SollTemp) SollTemp = Absenkung(deviceId,Rindex,SollTemp,evohomeClient,HeatSetpointStatus) await setStateAsync(Out_Root+D_Raum[Rindex]+".Temperatur",IstTemp) await setStateAsync(Out_Root+D_Raum[Rindex]+".Soll",SollTemp) await setStateAsync(Out_Root+D_Raum[Rindex]+".evoDeviceID",deviceId) // if (x==7) evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Hold, 15) // Wohnzimmer Temp. setzen // if (x==7) evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Temporary, 15,10) // Wohnzimmer auf 15° 30 min. lang // if (x==7) evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Scheduled) // Wohnzimmer auf Zeitplan zurücksetzen } else { logerror('Raum '+Raum_name+' nicht gefunden') } } // for }).catch(err => { log('Fehler bei EvoHome Abfrage: '+ err) // process.exit(3) }) } // evohome_IF
Hier noch etwas Background zum nachlesen:
https://gist.github.com/AlCalzone/d14b854b69ce5e8a03718336cc650a95# -
leider macht er die rote Fehlerlinie unter den 3 await's auch wenn .forEach durch for-next ersetzt wird.
Wenn 'for-next' und 'evohomeClient.getLocationsWithAutoLogin(300).then(locations => {'
entfernt wird, dann verschwindet auch die rote Fehlerlinie.
Ist anscheinend doch nicht so einfach. -
@hanss Whoops, den Mix aus Promises und async-Funktion hab ich übersehen.
Entweder du änderst Zeile 8 zu
evohomeClient.getLocationsWithAutoLogin(300).then(async (locations) => {
oder du machst es gleich richtig mit await:
const locations = await evohomeClient.getLocationsWithAutoLogin(300);
--> in jedem Fall mal den Link lesen
-
@alcalzone
Das war es leider auch nicht.
Ist scheinbar doch recht kompliziert mit async.
Ich glaube, ich verwende weiterhin ein setTimeout(function (){})
Vielen Dank. -
Ist scheinbar doch recht kompliziert mit async.
Eigentlich nicht... Es gibt 2 Regeln:
- Diejenige Funktion, die das
await
unmittelbar umschließt, mussasync
sein. - Wenn du auf den Abschluss einer solchen Funktion warten willst, um den folgenden Code auszuführen, musst du
await
vor den Aufruf setzen. Dazu siehe 1. - das setzt sich nach oben fort.
Schwieriger wird es nur, wenn du Sonderlocken ala
.forEach
statt Basis-Sprachfeatures wiefor
-Schleifen nutzt.Ich glaube, ich verwende weiterhin ein setTimeout(function (){})
Du glaubst? Nichtsdestotrotz, wenn in diesem Timeout ein
await
genutzt werden soll, muss es
setTimeout(async function (){})
lautenVielleicht rückst du mal mit dem gesamten Code raus, statt nur mit Schnipseln
- Diejenige Funktion, die das
-
Bitte, wenn Du dir das antun möchtest - ist recht umfangreich.
Inzwischen denke ich, dass es besser wäre, statt mit dem festen Timeout
setTimeout(function () {
darauf zu warten, bis die funktion evohome_IF() komplett fertig ist.
Der komplette Code ist weiter unten.
Danke.function RaumAbw(obj) { let Startzeit = getState(Out_Root+D_Raum[0]+".Soll").ts evohome_IF() // Ist- und Solltemperaturen abrufen, Absenkung prüfen let Endtzeit = getState(Out_Root+D_Raum[0]+".Soll").ts log('evohome_IF() Funktion Dauer: '+(Endtzeit-Startzeit)+' ms.') RTdiff = 0; TempDiff_Sum = 0; MaxTempRaum = "" RTdiff_max = 0 maxSollTemp = 0 Heizleistung_Soll = 0 var LastTS = Date.now() var SollTemp = 0, IstTemp = 0 setTimeout(function () { // 1sec. Warten auf Callback von evohome_IF()
/* https://github.com/svrooij/evohome evohome wird installiert in /root/node_modules/@svrooij/evohome und muß in /opt/iobroker/node_modules/iobroker.javascript/node_modules/evohome verschoben werden in ioBroker JS Instance das additional NPM module: evohome eintragen Berechnet die Heizlastanforderung in °C (Soll-Ist aller Räume) für WTC-EvoHome Interface Regelung der Vorlauftemperatur über WTC-COM bzw. WTC-WEM Portal EVO Home auf Ausfall Prüfung */ // Eingangsdaten var Out_Root="javascript." + instance + ".haus.Heizung." // Zimmer var S_Raum=[]; S_Raum[0]='Büro' // Büro - Raum namen müssen mit EvoHome übereinstimmen S_Raum[1]='Küche' // Küche S_Raum[2]='Bad' // Bad S_Raum[3]='Wohnzimmer' // Wohnzimmer S_Raum[4]='OG Zi links' // OG Zi Links S_Raum[5]='OG Wohnen'; // OG Wohnen S_Raum[6]='OG Küche'; // OG Küche S_Raum[7]='OG Zi rechts'; // OG Zi Rechts // Neue Alias Daten Objekte var D_Raum=[]; D_Raum[0]='Buero'; // Raumnamen für Script D_Raum[1]='Kueche'; D_Raum[2]='Bad'; D_Raum[3]='Wohnzimmer'; D_Raum[4]='OG_Zi_Links'; D_Raum[5]='OG_Wohnen'; D_Raum[6]='OG_Kueche'; D_Raum[7]='OG_Zi_Rechts'; // Korrekturwerte für Raum Temperatur (Faktor für RTdiff < 0) var K_Raum=[]; K_Raum[0]=1 // 'Buero'; K_Raum[1]=1 // Küche K_Raum[2]=1 //'Bad'; K_Raum[3]=1 //'Wohnzimmer'; K_Raum[4]=1 //'OG_Zi_Links'; K_Raum[5]=1 //'OG_Wohnen'; K_Raum[6]=1 //'OG_Kueche'; K_Raum[7]=1 //'OG_Zi_Rechts' //Konstanten für erforderliche Heizleistung const Kwert = 0.4 // kWert 1.1 = Neubau; 2.2 = Gebäude ab 1975; 3 = Altbau var V_Raum=[]; // Raum Volumen in m³ V_Raum[0]=26.00*2.53 // 'Buero' Fläche * Raumhöhe in qm*m V_Raum[1]=45.77*2.30 // 'Kueche' V_Raum[2]=18.75*2.63 // 'Bad' V_Raum[3]=72.68*2.55 // 'Wohnzimmer' V_Raum[4]=25.00*2.53 // 'OG_Zi_Links'; V_Raum[5]=12.00*2.53 // 'OG_Wohnen' V_Raum[6]=16.00*2.53 // 'OG_Kueche'; V_Raum[7]=25.00*2.53 // 'OG_Zi_Rechts' Summe Volumen: 603m³ WF UG: 163 OG: 72 var Raum_VTemp=[0,0,0,0,0,0,0,0]; var Raum_SollTemp=[0,0,0,0,0,0,0,0]; var RTdiff = 0 var RTdiff_max = 0 var maxSollTemp = 0 var TempDiff_Sum = 0; var Heizleistung_Soll = 0 var Heizleistung_Ist = 0 var MaxTempRaum = ""; var LastIndex = 0 // Alle Zimmer Tempraturen const Temp_Aus = 13.0 // Urlaub, Heizung aus const Temp_FOA = 13.6 // Fenster offen Alarm const Temp_Abw = 15.0 // abwesend (Smartphone) const Temp_Anw = 21.1 // anwesend (Smartphone) const Temp_Abs = 18.0 // Absenktemp (EvoHome) const Temp_Norm = 21.0 // Normaltemp (EvoHome) const Temp_Max = 22.5 // Max. Temperatur (Sonneneinstrahlung) var ArrayTD=[] // ------------------------- Alias Daten Objekte anlegen ----------------- S_Raum.forEach(function(element, index) { createState(Out_Root+D_Raum[index]+".Soll", { name: D_Raum[index]+"-Soll", type: 'number', unit: '°C', }); }); S_Raum.forEach(function(element, index) { createState(Out_Root+D_Raum[index]+".Temperatur", { name: D_Raum[index]+"-Ist", type: 'number', unit: '°C', }); }); S_Raum.forEach(function(element, index) { createState(Out_Root+D_Raum[index]+".FensterOffen", { name: "ist Fenster offen", type: 'boolean', unit: '', }); }); S_Raum.forEach(function(element, index) { createState(Out_Root+D_Raum[index]+".Abwesend", { name: "ist Smartphone abwesend", type: 'boolean', unit: '', }); }) S_Raum.forEach(function(element, index) { createState(Out_Root+D_Raum[index]+".evoDeviceID", { name: "EvoHome Device ID", type: 'number', unit: '', }) }) /* S_Raum.forEach(function(element, index) { createState(Out_Root+D_Raum[index]+".Steigung", { name: D_Raum[index]+"-Steigung", type: 'number', unit: 'K/Std.', }); }); */ createState(Out_Root+"Kessel.RTdiff_max_Raum", { name: "RTdiff welcher Raum", type: 'string', unit: '', }); createState(Out_Root+"Kessel.RTdiff", { name: "akt. Soll-Ist Raumtemp", type: 'number', unit: 'K', }); createState(Out_Root+"Kessel.RTdiff_max", { name: "max. Soll-Ist Raumtemp", type: 'number', unit: 'K', }); createState(Out_Root+"Kessel.Heizleistung_Soll", { name: "Summe erforderliche Heizleistung", type: 'number', unit: 'Watt', }); createState(Out_Root+"Stoerung.EHLastTS", { name: "EvoHome letzte RT Änderung vor", type: 'number', unit: 'Min.', }) createState(Out_Root+"Stoerung.EHLastTS_R", { name: "bei Raum", type: 'string', unit: 'Min.', }) createState(Out_Root+"Stoerung.EHLastTS_max", { name: "EvoHome letzte RT Änderung max.", type: 'number', unit: 'Min.', }) createState(Out_Root+"Stoerung.EHLastTS_max_R", { name: "bei Raum", type: 'string', unit: 'Min.', }) // ---- letzte Temperaturänderung feststellen function LastTempCh(LastTS, index){ let RTobj = getState(Out_Root+D_Raum[index]+'.Temperatur') if (RTobj.ts < LastTS){ LastTS = RTobj.ts let Dauer = Math.round( (Date.now()-LastTS)/1000/60 ) // log('#### '+D_Raum[index]+' EVOHome letzte Temperatur vor min.: '+Dauer ) setState("javascript.0.haus.Heizung.Stoerung.EHLastTS",Dauer ) // letzte Raumtemperatur Änderung vor x Min. setState("javascript.0.haus.Heizung.Stoerung.EHLastTS_R",D_Raum[index]) if ( Dauer >= getState('javascript.0.haus.Heizung.Stoerung.EHLastTS_max').val ){ setState('javascript.0.haus.Heizung.Stoerung.EHLastTS_max',Dauer) setState('javascript.0.haus.Heizung.Stoerung.EHLastTS_max_R',D_Raum[index]) } } } // LastTemp schedule("1 0 * * *", function () { // "1 0 * * *" um 0:01 täglich setState('javascript.0.haus.Heizung.Stoerung.EHLastTS_max',0) // max Werte Vortag löschen setState('javascript.0.haus.Heizung.Stoerung.EHLastTS_max_R','') }) //############################### Ist und Soll Temperatur abrufen ########################################### RaumAbw() // Für Programmstart setInterval(RaumAbw,5*60*1000) // Intervall wg Fenster offen u. Handy ab function RaumAbw(obj) { let Startzeit = getState(Out_Root+D_Raum[0]+".Soll").ts evohome_IF() // Ist- und Solltemperaturen abrufen, Absenkung prüfen let Endtzeit = getState(Out_Root+D_Raum[0]+".Soll").ts log('evohome_IF() Funktion Dauer: '+(Endtzeit-Startzeit)+' ms.') RTdiff = 0; TempDiff_Sum = 0; MaxTempRaum = "" RTdiff_max = 0 maxSollTemp = 0 Heizleistung_Soll = 0 var LastTS = Date.now() var SollTemp = 0, IstTemp = 0 setTimeout(function () { // 1sec. Warten auf Callback von evohome_IF() S_Raum.forEach(function(element, index) { // Schleife für jeden Raum LastTempCh(LastTS, index) // letzte Temperaturänderung feststellen SollTemp = getState(Out_Root+D_Raum[index]+".Soll").val IstTemp = getState(Out_Root+D_Raum[index]+".Temperatur").val // Berechnung der maximalen negativen Temperaturdifferenz (Ist-Soll) aller Räume if (IstTemp > Temp_Max) IstTemp = Temp_Max + 0.1 // wg. Sonneneinstrahlung RTdiff = (IstTemp-SollTemp) if (RTdiff < 0) RTdiff = RTdiff * K_Raum[index] // Temperatur für benötigte Heizleistung = RTsoll - AT let Tempdiff = SollTemp - getState('mqtt.0.haus.keller.HumiFan.Sensor.Aussen.Temperatur').val var Heizleistung = Math.round((V_Raum[index] * Kwert * Tempdiff * 1.16) ) // 1.16 = kcal in Watt if (Heizleistung > 0 ) Heizleistung_Soll += Heizleistung // summe Heizleistung in Watt Heizleistung_Ist = getState('mqtt.0.haus.keller.WMZ.WMleistung').val // log(D_Raum[index]+' RTdiff: '+RTdiff+' RTdiff_max: '+ RTdiff_max); TempDiff_Sum += RTdiff; if ( RTdiff < RTdiff_max ) { // der "kälteste Raum" wird gesucht RTdiff_max = RTdiff; // -1 = Raum zu kalt, aufheizen MaxTempRaum = D_Raum[index]; } if (SollTemp > maxSollTemp) maxSollTemp = SollTemp // aktuell max. Solltemperatur if (MaxTempRaum == 'Kueche') MaxTempRaum = 'Küche' if (MaxTempRaum == 'Buero') MaxTempRaum = 'Büro' LastIndex = index+1 }); // Schleife für jeden Raum ###################################### // TempSteigung(element, index) // Berechnet die Temperatur-Steigung des Raumes // Durchschnitt Energiebedarf - Wennn ein Raum zu kalt (<-1.1), dann bestimmt er die Temperatur RTdiff = RTdiff_max/4 if (RTdiff_max >= -0.9) RTdiff = round(TempDiff_Sum/LastIndex,1) // 19.1.2021 0.6; 4.2.2021 1.1 // RTdiff = AAverage(ArrayTD,RTdiff,5) // log('RTdiff: '+ArrayTD+' Median: '+AMedian(ArrayTD)+' Average: '+AAverage(ArrayTD)) // log('--- '+MaxTempRaum+' '+RTdiff_max+' RTDiff: '+RTdiff) setState(Out_Root+"Kessel.RTdiff",round(RTdiff,3)) // Temp.Differenz für Regelung setState(Out_Root+"Kessel.RTdiff_max",round(RTdiff_max,3)) // max. Temp Differenz setState(Out_Root+"Kessel.RTdiff_max_Raum",MaxTempRaum); // bei Raum im Klartext setState(Out_Root+"Kessel.Heizleistung_Soll",round(Heizleistung_Soll*1.1,0)) // +10% für übrige Räume },1000) } // function RaumAbw //############################### Ist und Soll Temperatur abrufen ########################################### // Berechnet die Steigung der Raumtemperatur der letzten 30min. in K/Std. // Normalwerte: Aufheizen: ca. 3K/h ; abkühlen ca. 1K/h function TempSteigung(element, index){ var IstTemp = parseFloat( getState(Out_Root+D_Raum[index]+".Temperatur").val) // ++++++++++++++++ evtl. nur bei Änderung Steigung berechnen, dann aber jede Min. aufrufen // if (Raum_VTemp[index] != IstTemp) { // Raumtemperatur geändert Raum_VTemp[index] = IstTemp // var vonDatum = new Date("2020-02-06 6:00:00").getTime() // in UnixTime ms. // var bisDatum = new Date("2020-02-06 8:00:00").getTime() var bisDatum = Date.now() var vonDatum = bisDatum - 3600*1000; // vor 1 Std. mysqlData_Steigung(vonDatum, bisDatum, Out_Root+D_Raum[index]+".Temperatur", index) // return steigung nicht möglich, weil asynchron // } } // function TempSteigung function mysqlData_Steigung(vonDatum, bisDatum, DatObj, index){ var vTemp, vDatum, bTemp, bDatum var myQuery="SELECT val AS wert, ts as datum \ FROM iobroker.ts_number \ WHERE id=(SELECT id FROM iobroker.datapoints WHERE NAME='"+DatObj+"') \ AND ts <= '"+vonDatum+"' \ ORDER BY ts DESC LIMIT 1" // ts < Datum weil das die Temperatur zur Zeit vonDatum ist // log("--->>>>"+myQuery); sendTo('sql.0', 'query', myQuery, function (result) { if (result.error) { logerror("Heizung_Alias SQL querry Fehler 1: "+result.error); } else { vTemp=result.result[0].wert; vDatum = result.result[0].datum; // var date = new Date(unix_timestamp * 1000); var myQuery="SELECT val AS wert, ts as datum \ FROM iobroker.ts_number \ WHERE id=(SELECT id FROM iobroker.datapoints WHERE NAME='"+DatObj+"') \ AND ts <= '"+bisDatum+"' \ ORDER BY ts DESC LIMIT 1" sendTo('sql.0', 'query', myQuery, function (result) { if (result.error) { logerror("Heizung_Alias SQL querry Fehler 2: "+result.error); } else { bTemp=result.result[0].wert; bDatum = result.result[0].datum; // var date = new Date(unix_timestamp * 1000); var Temperatur = (bTemp-vTemp) var Datum = (bDatum-vDatum)/1000 // in sec. var steigung = 0 if (Datum != 0) steigung = Math.round(Temperatur/Datum*36000) /10; // in K/Std. // log(D_Raum[index]+ " Steigung: "+steigung) setState(Out_Root+D_Raum[index]+".Steigung",steigung); } }); } }); } // function mysqlData_Steigung //############################ alle Raumemperaturen von EvoHome auslesen ############################ function evohome_IF(){ const EvohomeClient = require('evohome').EvohomeClient const HeatSetpointStatus = require('evohome').HeatSetpointStatus const username = 'xxxxxxxxx' const password = 'yyyyyyy' const evohomeClient = new EvohomeClient(username, password) evohomeClient.getLocationsWithAutoLogin(300).then(locations => { // (x) = Session Abbruch nach x Sekunden // log('JSON OBJ: '+JSON.stringify(locations)) S_Raum.forEach(function(element, index){ // z.Zt. 8 Räume 0-7 const deviceId = locations[0].devices[index].deviceID var Raum_name = locations[0].devices[index].name var Rindex = S_Raum.indexOf(Raum_name) // suchen Raumname in S_Raum Array if (Rindex != -1) { var IstTemp = locations[0].devices[index].thermostat.indoorTemperature var SollTemp = locations[0].devices[index].thermostat.changeableValues.heatSetpoint.value // log(Rindex+'-Zimmer: '+Raum_name+' Temperatur: '+IstTemp+' Soll Temp: '+SollTemp) SollTemp = Absenkung(deviceId,Rindex,SollTemp,evohomeClient,HeatSetpointStatus) setStateAsync(Out_Root+D_Raum[Rindex]+".Temperatur",IstTemp) setStateAsync(Out_Root+D_Raum[Rindex]+".Soll",SollTemp) setStateAsync(Out_Root+D_Raum[Rindex]+".evoDeviceID",deviceId) // if (x==7) evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Hold, 15) // Wohnzimmer Temp. setzen // if (x==7) evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Temporary, 15,10) // Wohnzimmer auf 15° 30 min. lang // if (x==7) evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Scheduled) // Wohnzimmer auf Zeitplan zurücksetzen } else { logerror('Raum '+Raum_name+' nicht gefunden') } }) // forEach }).catch(err => { log('Fehler bei EvoHome Abfrage: '+ err) // process.exit(3) }) } // evohome_IF //############################ Absenkung bei Fenster offen oder Handy ab ######################################## function Absenkung(deviceId,Rindex,SollTemp,evohomeClient,HeatSetpointStatus){ if ( getState(Out_Root+D_Raum[Rindex]+'.FensterOffen').val ){ // Fenster offen // log('Fenster offen: '+D_Raum[Rindex]) if (SollTemp != Temp_FOA) { evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Hold,Temp_FOA) // Temp. setzen SollTemp = Temp_FOA } } else { // Fenster zu // log('Fenster zu: '+D_Raum[Rindex]) if (SollTemp == Temp_FOA){ // log('Fenster zu: '+D_Raum[Rindex]) evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Scheduled) // Zeitplan Temperatur } if ( getState(Out_Root+D_Raum[Rindex]+'.Abwesend').val ){ // Handy ab if (getState('javascript.0.haus.Heizung.Stoerung.Abs_Abwesend').val) { // Absenkung ausgeschaltet? // log('Handy ab: '+D_Raum[Rindex]+' SollTemp: '+SollTemp) if (SollTemp != Temp_Abw) { evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Hold,Temp_Abw) SollTemp = Temp_Abw } } } else { if (SollTemp == Temp_Abw) { // log('Handy an-: '+D_Raum[Rindex]+' SollTemp: '+SollTemp) // Handy an evohomeClient.setHeatSetpoint(deviceId, HeatSetpointStatus.Scheduled) // Zeitplan Temperatur } } } return SollTemp } // Absenkung
-
@hanss sagte in setState's in Funktionen:
Die oben besprochenen Änderungen hast du jetzt noch nicht drin... bzw. nicht vollständig - da kann es auch nicht gehen...
Hier die Änderungen im Einzelnen:
^ Fehlerabfrage heraus gezogen, damit abgebrochen werden kann.[Grafik entfernt]
^ Unnötiges Timeout entfernt, da jetzt gewartet wird[Grafik entfernt]
^ Die zuvor besprochenen Änderungen komplett eingefügt.Anbei das vollständige Skript:
evohome-neu.jsHoffe ich hab nichts vergessen...
-
SUPER! Super Service mit dem vollständigen Script.
Hat auf Anhieb funktioniert.
Das hätte ich nie hinbekommen, da braucht es noch einige Zeit, obwohl ich
Deine Erläuterungen zweimal gelesen habe.Vielen, vielen Dank.
Noch eine Frage:
Wartet jetzt nur die komplette Funktion async function RaumAbw(obj) {
bis await evohome_IF() zurück ist?
Ich gehe davon aus, das alles Andere weiterläuft? -
@hanss Korrekt. Außer du setzt
await
vor die Aufrufe vonRaumAbw
. -
@alcalzone
Ich habe übersehen, den usernamen u. password unkenntlich zu machen.
Könntest Du das bitte machen?
Danke. -
@hanss Erledigt
-
@alcalzone
Danke, da ist noch eines vor 23 Stunden -
@hanss check!