- ioBroker Community Home
- Deutsch
- Skripten / Logik
- JavaScript
- Log Error aus Script: Read-only state ... written without...
NEWS
Log Error aus Script: Read-only state ... written without...
-
Ich hab mir dieses Script kopiert: https://github.com/Mic-M/iobroker-pflanzen-giessen-script/blob/master/pflanzen-giessen-script.js
Alle 3 Stunden kommt allerdings eine Fehlermeldung im Log, den ich zwar theoretisch verstehe, aber zu unwissend bin ihn zu beheben.Kann mir hier bitte jemand helfen?
2023-02-12 21:05:00.102 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.103 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysLeft" has been written without ack-flag with value "59" 2023-02-12 21:05:00.106 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.107 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.108 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.percentElapsed" has been written without ack-flag with value "2" 2023-02-12 21:05:00.109 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.level" has been written without ack-flag with value "green" 2023-02-12 21:05:00.149 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.151 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysLeft" has been written without ack-flag with value "6" 2023-02-12 21:05:00.154 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.155 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.156 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.percentElapsed" has been written without ack-flag with value "14" 2023-02-12 21:05:00.157 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.level" has been written without ack-flag with value "green" 2023-02-12 21:05:00.157 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.158 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysLeft" has been written without ack-flag with value "6" 2023-02-12 21:05:00.158 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.159 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.160 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.percentElapsed" has been written without ack-flag with value "14" 2023-02-12 21:05:00.160 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.level" has been written without ack-flag with value "green"Mein personalisierter Code (Datenpunkt, usw). Vielleicht hab ich ja was falsch geändert?
PS: Keine Ahnung woher diese "... created this issue in" herkommt.
/************************************************************************************************** * Script "Timer" (ehemals: "Pflanzen gießen") * ------------------------------------------------------------------------------------- * Autor: Mic (ioBroker) | Mic-M (github) * Change Log * 0.3 Mic Support creating states under 0_userdata.0 * 0.2 Mic Diverse Verbesserungen * 0.1 Mic Initial version **************************************************************************************************/ /******************************************************************************* * Konfiguration: Pfade / Datenpunkte ******************************************************************************/ // Pfad, unter dem die States (Datenpunkte) in den Objekten angelegt werden. // Es wird die Anlage sowohl unterhalb '0_userdata.0' als auch 'javascript.x' unterstützt. //const STATE_PATH = 'javascript.'+ instance + '.' + 'VIS.Timer'; const STATE_PATH = '0_userdata.0.VIS.Timer'; // Beliebig viele einzelne Timer anlegen. // Zeilen leer lassen oder löschen, falls nicht benötigt. const TIMERS = [ {'state': 'PflanzenOG', 'name': 'Pflanzen OG gießen'}, {'state': 'PflanzenUG', 'name': 'Pflanzen UG gießen'}, {'state': 'Entkalken', 'name': 'Geräte entkalken'}, {'state': '', 'name': ''}, ]; // Datenpunkte: Einzelne Datenpunkte. // Kein Grund zur Änderung! Einfach so belassen. const STATE_INTERVAL = 'interval'; // Number - Wie oft gießen? Anzahl Tage const STATE_RESTART = 'reStartCounter'; // true (Button) - Neustart Zähler: Wir fangen neu an zu zählen, wird betätigt sobald die Pflanzen gegossen worden sind const STATE_OVERDUE = 'isOverdue'; // true/false. Wenn "faellig", dann müssen die Pflanzen gegossen werden. Wird false gesetzt, sobald Counter neu startet const STATE_PERCENT_ELAPSED = 'percentElapsed'; // Wann fällig, in %. D.h. bei 10 Tagen 'anzahlTageBisFaellig', und vor 3 Tagen gegossen = 30% const STATE_DAYS_OVERDUE = 'daysOverdue'; // Number. Anzahl Tage, seit dem die Pflanzen gegossen werden müssten (aber es noch nicht sind) const STATE_DAYS_LEFT = 'daysLeft'; // Number. Anzahl Tage, bis die Pflanzen gegossen werden müssen const STATE_DAYS_ELAPSED = 'daysElapsed'; // Number. Anzahl Tage, seit dem der Timer gestartet wurde const STATE_DATETIMESTART = 'dateTimeCounterStart'; // Date/Time. Wann wurde der Timer gestartet const STATE_LEVEL = 'level'; // Grün / Gelb / Rot, je nach % vergangen. Siehe Konfiguration Schwellwerte /******************************************************************************* * Konfiguration: Rest ******************************************************************************/ // Schwellwerte in %: Im Datenpunkt ".level" wird green/yellow/red ausgegeben, je nach Datenpunkt 'faelligProzent'. // Bitte einen Bereich angeben: [20, 40]: Bedeutet, dass %-Wert zwischen 20 und 40 liegen muss. const LEVEL_GREEN = [0, 85]; const LEVEL_YELLOW = [86, 99]; const LEVEL_RED = [100, 99999999]; // Wie oft aktualisieren? const PLANTS_SCHEDULE = '5 */3 * * *'; // Alle 3 Stunden. // Logeinträge anzeigen? const LOGINFO = true; // Default: Anzahl Tage. // Kann jederzeit im State 'interval' geändert werden. const INTERVAL_PRESET = 14; /************************************************************************************************************************* * Ab hier nichts mehr ändern / Stop editing here! *************************************************************************************************************************/ /**************************************************************************************** * Global variables and constants ****************************************************************************************/ // Final state path const FINAL_STATE_LOCATION = validateStatePath(STATE_PATH, false); const FINAL_STATE_PATH = validateStatePath(STATE_PATH, true) + '.'; // adding trailing dot // Schedule let mSchedule = []; /******************************************************************************* * Executed on every script start. *******************************************************************************/ init(); function init() { // Create states createUserStates(FINAL_STATE_LOCATION, false, buildScriptStates(), function() { // Main Script starten setTimeout(main, 2000); }); } /******************************************************************************* * Haupt-Skript *******************************************************************************/ function main() { for (let i = 0; i < TIMERS.length; i++) { // First: check if we have a valid configuration // TO DO: Further check TIMERS[i].state for valid state chars if (isLikeEmpty(TIMERS[i].state)) { continue; // breaks current iteration in the loop and continues with the next } /*************************** * Schedule beenden falls aktiv, dann starten. * Dies machen wir, damit z.B. bei JavaScript-Adapter-Neustart immer sichergestellt ist, dass das Schedule läuft. * Außerdem initiales Start-Datum setzen, falls nicht vorhanden. **************************/ clearSchedule(mSchedule[i]); let dtStart = getState(FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART).val; if ( (isNumber(dtStart)) && (dtStart > 0) ) { mSchedule[i] = schedule(PLANTS_SCHEDULE, function() { updateStates(TIMERS[i]); }); if (LOGINFO) log('Timer ' + TIMERS[i].name + ': Script wurde neu gestartet. Ein Timer-Startdatum ist vorhanden, also wird nur aktualisiert.'); updateStates(TIMERS[i]); } else { // Initial ist noch kein Start-Datum gesetzt, also setzen wir eines. setState(FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, Date.now()); // Dies triggert automatisch updateStates(), da State STATE_DATETIMESTART per on(id) überwacht wird. if (LOGINFO) log('Timer ' + TIMERS[i].name + ': Startdatum im State ist leer, daher werden Startdatum und Initialwerte frisch gesetzt.'); } /*************************** * Überwache State-Button reStartCounter **************************/ on({id:FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_RESTART, val:true}, function (obj) { // First_ need to get - within on...id - current state portion let tmpArray = obj.id.split('.'); let statePortion = tmpArray[(tmpArray.length - 2)] setState(FINAL_STATE_PATH + statePortion + '.' + STATE_DATETIMESTART, Date.now()); // Dies triggert automatisch updateStates(), da State STATE_DATETIMESTART per on(id) überwacht wird. setStateDelayed(FINAL_STATE_PATH + statePortion + '.' + STATE_RESTART, false, 300); // wieder zurück setzen zur schönen Darstellung. }); /*************************** * Überwache State-Button interval, da diese Anzahl Tage vom User jederzeit geändert werden können **************************/ on({id: FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_INTERVAL, change:'ne'}, function (obj) { updateStates(TIMERS[i]); }); /*************************** * Überwache Datum/Uhrzeit Counter-Start, falls dieser manuell geändert wurde. **************************/ on({id: FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, change:'ne'}, function (obj) { updateStates(TIMERS[i]); }); /*************************** * Sobald die Planzen gegossen werden müssen, führen wir folgendes aus. * @param {object} objTimes Objekt, also Array-Element von TIMERS **************************/ function updateStates(objTimes) { let now = new Date(); let nowTimeStamp = now.getTime(); let startTimeInState = new Date(getState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DATETIMESTART).val); let intervalInState = getState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_INTERVAL).val; let dateTimeDue = dateAddMinutes(startTimeInState, intervalInState*60*24); let numberOfMsDue = nowTimeStamp - dateTimeDue; // https://stackoverflow.com/questions/7709803/javascript-get-minutes-between-two-dates let numberOfMinutesDue = Math.floor((numberOfMsDue/1000)/60); let numberOfDaysDue = Math.round((numberOfMinutesDue/60)/24); let numberOfMsLeft = dateTimeDue - nowTimeStamp; let numberOfMinutesLeft = Math.floor((numberOfMsLeft/1000)/60); let numberOfDaysLeft = Math.round((numberOfMinutesLeft/60)/24); let numberOfMsElapsed = nowTimeStamp - startTimeInState.getTime(); let numberOfMinutesElapsed = Math.floor((numberOfMsElapsed/1000)/60); let numberOfDaysElapsed = Math.round((numberOfMinutesElapsed/60)/24); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_OVERDUE, numberOfDaysDue); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_LEFT, numberOfDaysLeft); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_ELAPSED, numberOfDaysElapsed); if ( dateTimeDue < nowTimeStamp ) { // Jetzt die Pflanzen gießen setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_OVERDUE, true); if (LOGINFO) log('Timer ' + objTimes.name + ': Prüfungsergebnis: Fällig seit ' + numberOfDaysDue + ' Tagen'); } else { // Pflanzen noch nicht gießen setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_OVERDUE, false); if (LOGINFO) log('Timer ' + objTimes.name + ': Prüfungsergebnis: Noch nicht fällig, erst in ' + numberOfDaysLeft + ' Tagen'); } // Setze Prozent let percent = Math.round(numberOfDaysElapsed * 100 / intervalInState); if (percent > 100) percent = 100; setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_PERCENT_ELAPSED, percent); // Setze Level (Farben) je nach % let levelResult = 'undefined'; if ( isInRange(percent, LEVEL_GREEN[0], LEVEL_GREEN[1]) ) levelResult = 'green'; if ( isInRange(percent, LEVEL_YELLOW[0], LEVEL_YELLOW[1]) ) levelResult = 'yellow'; if ( isInRange(percent, LEVEL_RED[0], LEVEL_RED[1]) ) levelResult = 'red'; setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_LEVEL, levelResult); } } // for } /******************************************************************************* * Weitere unterstützende Funktionen usw. *******************************************************************************/ /** * Add certain number of minutes to a given date/time. * @param {object} date Provided date. * @param {number} minutes number of minutes to be added to a given date * @return {object} new date with the minutes added */ function dateAddMinutes(date, minutes) { return new Date(date.getTime() + minutes*60000); } /** * Prüft ob Variableninhalt eine Zahl ist. * isNumber ('123'); // true * isNumber ('123abc'); // false * isNumber (5); // true * isNumber ('q345'); // false * isNumber(null); // false * isNumber(undefined); // false * isNumber(false); // false * isNumber(' '); // false * @source https://stackoverflow.com/questions/1303646/check-whether-variable-is-number-or-string-in-javascript * @param {any} n Variable, die zu prüfen ist auf Zahl * @return {boolean} true falls Zahl, false falls nicht. */ function isNumber(n) { return /^-?[\d.]+(?:e-?\d+)?$/.test(n); } /** * Checks if a number is within a range. Returns true if in range, and false otherwise. * @param x {number} Number to check if it is within range * @param min {number} min value * @param max {number} max value * @return {boolean} true if in range, and false otherwise. */ function isInRange(x, min, max) { return ((x-min)*(x-max) <= 0); } /** * Build states */ function buildScriptStates() { let finalStates = []; for (let i = 0; i < TIMERS.length; i++) { if (isLikeEmpty(TIMERS[i].state)) { continue; // breaks current iteration in the loop and continues with the next } finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_INTERVAL, {'name':'Set interval in days', 'type':'number', 'unit':'d', 'min':1, 'max':60, 'read':true, 'write':true, 'role':'value', 'def':INTERVAL_PRESET}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_RESTART, {'name':'Restart counter', 'type':'boolean', 'read':true, 'write':true, 'role':'button', 'def':false}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_OVERDUE, {'name':'Overdue?', 'type':'boolean', 'read':true, 'write':false, 'role':'state'}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_OVERDUE, {'name':'Number of days overdue', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_PERCENT_ELAPSED, {'name':'Elapsed in %', 'type':'number', 'unit':'%', 'min':0, 'max':100, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_ELAPSED, {'name':'Elapsed in number of days', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_LEFT, {'name':'Days left', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, {'name':'Date/Time of counter start', 'type':'number', 'read':true, 'write':true, 'role':'value.time' }]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_LEVEL, {'name':'green/yellow/red per elapsed %', 'type':'string', 'read':true, 'write':false, 'role':'value'}]); } return finalStates; } /** * Checks if Array or String is not undefined, null or empty. * 08-Sep-2019: added check for [ and ] to also catch arrays with empty strings. * @param inputVar - Input Array or String, Number, etc. * @return true if it is undefined/null/empty, false if it contains value(s) * Array or String containing just whitespaces or >'< or >"< or >[< or >]< is considered empty */ function isLikeEmpty(inputVar) { if (typeof inputVar !== 'undefined' && inputVar !== null) { let strTemp = JSON.stringify(inputVar); strTemp = strTemp.replace(/\s+/g, ''); // remove all whitespaces strTemp = strTemp.replace(/\"+/g, ""); // remove all >"< strTemp = strTemp.replace(/\'+/g, ""); // remove all >'< strTemp = strTemp.replace(/\[+/g, ""); // remove all >[< strTemp = strTemp.replace(/\]+/g, ""); // remove all >]< if (strTemp !== '') { return false; } else { return true; } } else { return true; } } /** * For a given state path, we extract the location '0_userdata.0' or 'javascript.0' or add '0_userdata.0', if missing. * @param {string} path Like: 'Computer.Control-PC', 'javascript.0.Computer.Control-PC', '0_userdata.0.Computer.Control-PC' * @param {boolean} returnFullPath If true: full path like '0_userdata.0.Computer.Control-PC', if false: just location like '0_userdata.0' or 'javascript.0' * @return {string} Path */ function validateStatePath(path, returnFullPath) { if (path.startsWith('.')) path = path.substr(1); // Remove first dot if (path.endsWith('.')) path = path.slice(0, -1); // Remove trailing dot if (path.length < 1) log('Provided state path is not valid / too short.', 'error') let match = path.match(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/); let location = (match == null) ? '0_userdata.0' : match[0].slice(0, -1); // default is '0_userdata.0'. if(returnFullPath) { return (path.indexOf(location) == 0) ? path : (location + '.' + path); } else { return location; } } /** * Create states under 0_userdata.0 or javascript.x * Support: https://forum.iobroker.net/topic/26839/ * Autor: Mic (ioBroker) | Mic-M (github) * Version: 1.1 (26 January 2020) * ----------------------------------------------- * executes the callback PRIOR to completing the state creation. Therefore, we use a setTimeout and counter. * ----------------------------------------------- * @param {string} where Where to create the state: '0_userdata.0' or 'javascript.x'. * @param {boolean} force Force state creation (overwrite), if state is existing. * @param {array} statesToCreate State(s) to create. single array or array of arrays * @param {object} [callback] Optional: a callback function -- This provided function will be executed after all states are created. */ function createUserStates(where, force, statesToCreate, callback = undefined) { const WARN = false; // Only for 0_userdata.0: Throws warning in log, if state is already existing and force=false. Default is false, so no warning in log, if state exists. const LOG_DEBUG = false; // To debug this function, set to true function setObject() executes the callback // before the state is actual created. Therefore, we use a setTimeout and counter as a workaround. const DELAY = 50; // Delay in milliseconds (ms). Increase this to 100, if it is not working. // Validate "where" if (where.endsWith('.')) where = where.slice(0, -1); // Remove trailing dot if ( (where.match(/^((javascript\.([1-9][0-9]|[0-9]))$|0_userdata\.0$)/) == null) ) { log('This script does not support to create states under [' + where + ']', 'error'); return; } // Prepare "statesToCreate" since we also allow a single state to create if(!Array.isArray(statesToCreate[0])) statesToCreate = [statesToCreate]; // wrap into array, if just one array and not inside an array // Add "where" to STATES_TO_CREATE for (let i = 0; i < statesToCreate.length; i++) { let lpPath = statesToCreate[i][0].replace(/\.*\./g, '.'); // replace all multiple dots like '..', '...' with a single '.' lpPath = lpPath.replace(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/,'') // remove any javascript.x. / 0_userdata.0. from beginning lpPath = where + '.' + lpPath; // add where to beginning of string statesToCreate[i][0] = lpPath; } if (where != '0_userdata.0') { // Create States under javascript.x let numStates = statesToCreate.length; statesToCreate.forEach(function(loopParam) { if (LOG_DEBUG) log('[Debug] Now we are creating new state [' + loopParam[0] + ']'); let loopInit = (loopParam[1]['def'] == undefined) ? null : loopParam[1]['def']; // mimic same behavior as createState if no init value is provided createState(loopParam[0], loopInit, force, loopParam[1], function() { numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states processed.'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] Function to callback parameter was provided'); return callback(); } else { return; } } }); }); } else { // Create States under 0_userdata.0 let numStates = statesToCreate.length; let counter = -1; statesToCreate.forEach(function(loopParam) { counter += 1; if (LOG_DEBUG) log ('[Debug] Currently processing following state: [' + loopParam[0] + ']'); if( ($(loopParam[0]).length > 0) && (existsState(loopParam[0])) ) { State is existing. if (WARN && !force) log('State [' + loopParam[0] + '] is already existing and will no longer be created.', 'warn'); if (!WARN && LOG_DEBUG) log('[Debug] State [' + loopParam[0] + '] is already existing. Option force (=overwrite) is set to [' + force + '].'); if(!force) { // State exists and shall not be overwritten since force=false // So, we do not proceed. numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states successfully processed!'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] An optional callback function was provided, which we are going to execute now.'); return callback(); } } else { // We need to go out and continue with next element in loop. return; // https://stackoverflow.com/questions/18452920/continue-in-cursor-foreach } } // if(!force) } // State is not existing or force = true, so we are continuing to create the state through setObject(). let obj = {}; obj.type = 'state'; obj.native = {}; obj.common = loopParam[1]; setObject(loopParam[0], obj, function (err) { if (err) { log('Cannot write object for state [' + loopParam[0] + ']: ' + err); } else { if (LOG_DEBUG) log('[Debug] Now we are creating new state [' + loopParam[0] + ']') let init = null; if(loopParam[1].def === undefined) { if(loopParam[1].type === 'number') init = 0; if(loopParam[1].type === 'boolean') init = false; if(loopParam[1].type === 'string') init = ''; } else { init = loopParam[1].def; } setTimeout(function() { setState(loopParam[0], init, true, function() { if (LOG_DEBUG) log('[Debug] setState durchgeführt: ' + loopParam[0]); numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states processed.'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] Function to callback parameter was provided'); return callback(); } } }); }, DELAY + (20 * counter) ); } }); }); } }-Peter
-
Ich hab mir dieses Script kopiert: https://github.com/Mic-M/iobroker-pflanzen-giessen-script/blob/master/pflanzen-giessen-script.js
Alle 3 Stunden kommt allerdings eine Fehlermeldung im Log, den ich zwar theoretisch verstehe, aber zu unwissend bin ihn zu beheben.Kann mir hier bitte jemand helfen?
2023-02-12 21:05:00.102 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.103 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysLeft" has been written without ack-flag with value "59" 2023-02-12 21:05:00.106 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.107 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.108 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.percentElapsed" has been written without ack-flag with value "2" 2023-02-12 21:05:00.109 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.level" has been written without ack-flag with value "green" 2023-02-12 21:05:00.149 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.151 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysLeft" has been written without ack-flag with value "6" 2023-02-12 21:05:00.154 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.155 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.156 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.percentElapsed" has been written without ack-flag with value "14" 2023-02-12 21:05:00.157 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.level" has been written without ack-flag with value "green" 2023-02-12 21:05:00.157 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.158 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysLeft" has been written without ack-flag with value "6" 2023-02-12 21:05:00.158 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.159 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.160 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.percentElapsed" has been written without ack-flag with value "14" 2023-02-12 21:05:00.160 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.level" has been written without ack-flag with value "green"Mein personalisierter Code (Datenpunkt, usw). Vielleicht hab ich ja was falsch geändert?
PS: Keine Ahnung woher diese "... created this issue in" herkommt.
/************************************************************************************************** * Script "Timer" (ehemals: "Pflanzen gießen") * ------------------------------------------------------------------------------------- * Autor: Mic (ioBroker) | Mic-M (github) * Change Log * 0.3 Mic Support creating states under 0_userdata.0 * 0.2 Mic Diverse Verbesserungen * 0.1 Mic Initial version **************************************************************************************************/ /******************************************************************************* * Konfiguration: Pfade / Datenpunkte ******************************************************************************/ // Pfad, unter dem die States (Datenpunkte) in den Objekten angelegt werden. // Es wird die Anlage sowohl unterhalb '0_userdata.0' als auch 'javascript.x' unterstützt. //const STATE_PATH = 'javascript.'+ instance + '.' + 'VIS.Timer'; const STATE_PATH = '0_userdata.0.VIS.Timer'; // Beliebig viele einzelne Timer anlegen. // Zeilen leer lassen oder löschen, falls nicht benötigt. const TIMERS = [ {'state': 'PflanzenOG', 'name': 'Pflanzen OG gießen'}, {'state': 'PflanzenUG', 'name': 'Pflanzen UG gießen'}, {'state': 'Entkalken', 'name': 'Geräte entkalken'}, {'state': '', 'name': ''}, ]; // Datenpunkte: Einzelne Datenpunkte. // Kein Grund zur Änderung! Einfach so belassen. const STATE_INTERVAL = 'interval'; // Number - Wie oft gießen? Anzahl Tage const STATE_RESTART = 'reStartCounter'; // true (Button) - Neustart Zähler: Wir fangen neu an zu zählen, wird betätigt sobald die Pflanzen gegossen worden sind const STATE_OVERDUE = 'isOverdue'; // true/false. Wenn "faellig", dann müssen die Pflanzen gegossen werden. Wird false gesetzt, sobald Counter neu startet const STATE_PERCENT_ELAPSED = 'percentElapsed'; // Wann fällig, in %. D.h. bei 10 Tagen 'anzahlTageBisFaellig', und vor 3 Tagen gegossen = 30% const STATE_DAYS_OVERDUE = 'daysOverdue'; // Number. Anzahl Tage, seit dem die Pflanzen gegossen werden müssten (aber es noch nicht sind) const STATE_DAYS_LEFT = 'daysLeft'; // Number. Anzahl Tage, bis die Pflanzen gegossen werden müssen const STATE_DAYS_ELAPSED = 'daysElapsed'; // Number. Anzahl Tage, seit dem der Timer gestartet wurde const STATE_DATETIMESTART = 'dateTimeCounterStart'; // Date/Time. Wann wurde der Timer gestartet const STATE_LEVEL = 'level'; // Grün / Gelb / Rot, je nach % vergangen. Siehe Konfiguration Schwellwerte /******************************************************************************* * Konfiguration: Rest ******************************************************************************/ // Schwellwerte in %: Im Datenpunkt ".level" wird green/yellow/red ausgegeben, je nach Datenpunkt 'faelligProzent'. // Bitte einen Bereich angeben: [20, 40]: Bedeutet, dass %-Wert zwischen 20 und 40 liegen muss. const LEVEL_GREEN = [0, 85]; const LEVEL_YELLOW = [86, 99]; const LEVEL_RED = [100, 99999999]; // Wie oft aktualisieren? const PLANTS_SCHEDULE = '5 */3 * * *'; // Alle 3 Stunden. // Logeinträge anzeigen? const LOGINFO = true; // Default: Anzahl Tage. // Kann jederzeit im State 'interval' geändert werden. const INTERVAL_PRESET = 14; /************************************************************************************************************************* * Ab hier nichts mehr ändern / Stop editing here! *************************************************************************************************************************/ /**************************************************************************************** * Global variables and constants ****************************************************************************************/ // Final state path const FINAL_STATE_LOCATION = validateStatePath(STATE_PATH, false); const FINAL_STATE_PATH = validateStatePath(STATE_PATH, true) + '.'; // adding trailing dot // Schedule let mSchedule = []; /******************************************************************************* * Executed on every script start. *******************************************************************************/ init(); function init() { // Create states createUserStates(FINAL_STATE_LOCATION, false, buildScriptStates(), function() { // Main Script starten setTimeout(main, 2000); }); } /******************************************************************************* * Haupt-Skript *******************************************************************************/ function main() { for (let i = 0; i < TIMERS.length; i++) { // First: check if we have a valid configuration // TO DO: Further check TIMERS[i].state for valid state chars if (isLikeEmpty(TIMERS[i].state)) { continue; // breaks current iteration in the loop and continues with the next } /*************************** * Schedule beenden falls aktiv, dann starten. * Dies machen wir, damit z.B. bei JavaScript-Adapter-Neustart immer sichergestellt ist, dass das Schedule läuft. * Außerdem initiales Start-Datum setzen, falls nicht vorhanden. **************************/ clearSchedule(mSchedule[i]); let dtStart = getState(FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART).val; if ( (isNumber(dtStart)) && (dtStart > 0) ) { mSchedule[i] = schedule(PLANTS_SCHEDULE, function() { updateStates(TIMERS[i]); }); if (LOGINFO) log('Timer ' + TIMERS[i].name + ': Script wurde neu gestartet. Ein Timer-Startdatum ist vorhanden, also wird nur aktualisiert.'); updateStates(TIMERS[i]); } else { // Initial ist noch kein Start-Datum gesetzt, also setzen wir eines. setState(FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, Date.now()); // Dies triggert automatisch updateStates(), da State STATE_DATETIMESTART per on(id) überwacht wird. if (LOGINFO) log('Timer ' + TIMERS[i].name + ': Startdatum im State ist leer, daher werden Startdatum und Initialwerte frisch gesetzt.'); } /*************************** * Überwache State-Button reStartCounter **************************/ on({id:FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_RESTART, val:true}, function (obj) { // First_ need to get - within on...id - current state portion let tmpArray = obj.id.split('.'); let statePortion = tmpArray[(tmpArray.length - 2)] setState(FINAL_STATE_PATH + statePortion + '.' + STATE_DATETIMESTART, Date.now()); // Dies triggert automatisch updateStates(), da State STATE_DATETIMESTART per on(id) überwacht wird. setStateDelayed(FINAL_STATE_PATH + statePortion + '.' + STATE_RESTART, false, 300); // wieder zurück setzen zur schönen Darstellung. }); /*************************** * Überwache State-Button interval, da diese Anzahl Tage vom User jederzeit geändert werden können **************************/ on({id: FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_INTERVAL, change:'ne'}, function (obj) { updateStates(TIMERS[i]); }); /*************************** * Überwache Datum/Uhrzeit Counter-Start, falls dieser manuell geändert wurde. **************************/ on({id: FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, change:'ne'}, function (obj) { updateStates(TIMERS[i]); }); /*************************** * Sobald die Planzen gegossen werden müssen, führen wir folgendes aus. * @param {object} objTimes Objekt, also Array-Element von TIMERS **************************/ function updateStates(objTimes) { let now = new Date(); let nowTimeStamp = now.getTime(); let startTimeInState = new Date(getState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DATETIMESTART).val); let intervalInState = getState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_INTERVAL).val; let dateTimeDue = dateAddMinutes(startTimeInState, intervalInState*60*24); let numberOfMsDue = nowTimeStamp - dateTimeDue; // https://stackoverflow.com/questions/7709803/javascript-get-minutes-between-two-dates let numberOfMinutesDue = Math.floor((numberOfMsDue/1000)/60); let numberOfDaysDue = Math.round((numberOfMinutesDue/60)/24); let numberOfMsLeft = dateTimeDue - nowTimeStamp; let numberOfMinutesLeft = Math.floor((numberOfMsLeft/1000)/60); let numberOfDaysLeft = Math.round((numberOfMinutesLeft/60)/24); let numberOfMsElapsed = nowTimeStamp - startTimeInState.getTime(); let numberOfMinutesElapsed = Math.floor((numberOfMsElapsed/1000)/60); let numberOfDaysElapsed = Math.round((numberOfMinutesElapsed/60)/24); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_OVERDUE, numberOfDaysDue); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_LEFT, numberOfDaysLeft); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_ELAPSED, numberOfDaysElapsed); if ( dateTimeDue < nowTimeStamp ) { // Jetzt die Pflanzen gießen setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_OVERDUE, true); if (LOGINFO) log('Timer ' + objTimes.name + ': Prüfungsergebnis: Fällig seit ' + numberOfDaysDue + ' Tagen'); } else { // Pflanzen noch nicht gießen setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_OVERDUE, false); if (LOGINFO) log('Timer ' + objTimes.name + ': Prüfungsergebnis: Noch nicht fällig, erst in ' + numberOfDaysLeft + ' Tagen'); } // Setze Prozent let percent = Math.round(numberOfDaysElapsed * 100 / intervalInState); if (percent > 100) percent = 100; setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_PERCENT_ELAPSED, percent); // Setze Level (Farben) je nach % let levelResult = 'undefined'; if ( isInRange(percent, LEVEL_GREEN[0], LEVEL_GREEN[1]) ) levelResult = 'green'; if ( isInRange(percent, LEVEL_YELLOW[0], LEVEL_YELLOW[1]) ) levelResult = 'yellow'; if ( isInRange(percent, LEVEL_RED[0], LEVEL_RED[1]) ) levelResult = 'red'; setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_LEVEL, levelResult); } } // for } /******************************************************************************* * Weitere unterstützende Funktionen usw. *******************************************************************************/ /** * Add certain number of minutes to a given date/time. * @param {object} date Provided date. * @param {number} minutes number of minutes to be added to a given date * @return {object} new date with the minutes added */ function dateAddMinutes(date, minutes) { return new Date(date.getTime() + minutes*60000); } /** * Prüft ob Variableninhalt eine Zahl ist. * isNumber ('123'); // true * isNumber ('123abc'); // false * isNumber (5); // true * isNumber ('q345'); // false * isNumber(null); // false * isNumber(undefined); // false * isNumber(false); // false * isNumber(' '); // false * @source https://stackoverflow.com/questions/1303646/check-whether-variable-is-number-or-string-in-javascript * @param {any} n Variable, die zu prüfen ist auf Zahl * @return {boolean} true falls Zahl, false falls nicht. */ function isNumber(n) { return /^-?[\d.]+(?:e-?\d+)?$/.test(n); } /** * Checks if a number is within a range. Returns true if in range, and false otherwise. * @param x {number} Number to check if it is within range * @param min {number} min value * @param max {number} max value * @return {boolean} true if in range, and false otherwise. */ function isInRange(x, min, max) { return ((x-min)*(x-max) <= 0); } /** * Build states */ function buildScriptStates() { let finalStates = []; for (let i = 0; i < TIMERS.length; i++) { if (isLikeEmpty(TIMERS[i].state)) { continue; // breaks current iteration in the loop and continues with the next } finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_INTERVAL, {'name':'Set interval in days', 'type':'number', 'unit':'d', 'min':1, 'max':60, 'read':true, 'write':true, 'role':'value', 'def':INTERVAL_PRESET}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_RESTART, {'name':'Restart counter', 'type':'boolean', 'read':true, 'write':true, 'role':'button', 'def':false}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_OVERDUE, {'name':'Overdue?', 'type':'boolean', 'read':true, 'write':false, 'role':'state'}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_OVERDUE, {'name':'Number of days overdue', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_PERCENT_ELAPSED, {'name':'Elapsed in %', 'type':'number', 'unit':'%', 'min':0, 'max':100, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_ELAPSED, {'name':'Elapsed in number of days', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_LEFT, {'name':'Days left', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, {'name':'Date/Time of counter start', 'type':'number', 'read':true, 'write':true, 'role':'value.time' }]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_LEVEL, {'name':'green/yellow/red per elapsed %', 'type':'string', 'read':true, 'write':false, 'role':'value'}]); } return finalStates; } /** * Checks if Array or String is not undefined, null or empty. * 08-Sep-2019: added check for [ and ] to also catch arrays with empty strings. * @param inputVar - Input Array or String, Number, etc. * @return true if it is undefined/null/empty, false if it contains value(s) * Array or String containing just whitespaces or >'< or >"< or >[< or >]< is considered empty */ function isLikeEmpty(inputVar) { if (typeof inputVar !== 'undefined' && inputVar !== null) { let strTemp = JSON.stringify(inputVar); strTemp = strTemp.replace(/\s+/g, ''); // remove all whitespaces strTemp = strTemp.replace(/\"+/g, ""); // remove all >"< strTemp = strTemp.replace(/\'+/g, ""); // remove all >'< strTemp = strTemp.replace(/\[+/g, ""); // remove all >[< strTemp = strTemp.replace(/\]+/g, ""); // remove all >]< if (strTemp !== '') { return false; } else { return true; } } else { return true; } } /** * For a given state path, we extract the location '0_userdata.0' or 'javascript.0' or add '0_userdata.0', if missing. * @param {string} path Like: 'Computer.Control-PC', 'javascript.0.Computer.Control-PC', '0_userdata.0.Computer.Control-PC' * @param {boolean} returnFullPath If true: full path like '0_userdata.0.Computer.Control-PC', if false: just location like '0_userdata.0' or 'javascript.0' * @return {string} Path */ function validateStatePath(path, returnFullPath) { if (path.startsWith('.')) path = path.substr(1); // Remove first dot if (path.endsWith('.')) path = path.slice(0, -1); // Remove trailing dot if (path.length < 1) log('Provided state path is not valid / too short.', 'error') let match = path.match(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/); let location = (match == null) ? '0_userdata.0' : match[0].slice(0, -1); // default is '0_userdata.0'. if(returnFullPath) { return (path.indexOf(location) == 0) ? path : (location + '.' + path); } else { return location; } } /** * Create states under 0_userdata.0 or javascript.x * Support: https://forum.iobroker.net/topic/26839/ * Autor: Mic (ioBroker) | Mic-M (github) * Version: 1.1 (26 January 2020) * ----------------------------------------------- * executes the callback PRIOR to completing the state creation. Therefore, we use a setTimeout and counter. * ----------------------------------------------- * @param {string} where Where to create the state: '0_userdata.0' or 'javascript.x'. * @param {boolean} force Force state creation (overwrite), if state is existing. * @param {array} statesToCreate State(s) to create. single array or array of arrays * @param {object} [callback] Optional: a callback function -- This provided function will be executed after all states are created. */ function createUserStates(where, force, statesToCreate, callback = undefined) { const WARN = false; // Only for 0_userdata.0: Throws warning in log, if state is already existing and force=false. Default is false, so no warning in log, if state exists. const LOG_DEBUG = false; // To debug this function, set to true function setObject() executes the callback // before the state is actual created. Therefore, we use a setTimeout and counter as a workaround. const DELAY = 50; // Delay in milliseconds (ms). Increase this to 100, if it is not working. // Validate "where" if (where.endsWith('.')) where = where.slice(0, -1); // Remove trailing dot if ( (where.match(/^((javascript\.([1-9][0-9]|[0-9]))$|0_userdata\.0$)/) == null) ) { log('This script does not support to create states under [' + where + ']', 'error'); return; } // Prepare "statesToCreate" since we also allow a single state to create if(!Array.isArray(statesToCreate[0])) statesToCreate = [statesToCreate]; // wrap into array, if just one array and not inside an array // Add "where" to STATES_TO_CREATE for (let i = 0; i < statesToCreate.length; i++) { let lpPath = statesToCreate[i][0].replace(/\.*\./g, '.'); // replace all multiple dots like '..', '...' with a single '.' lpPath = lpPath.replace(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/,'') // remove any javascript.x. / 0_userdata.0. from beginning lpPath = where + '.' + lpPath; // add where to beginning of string statesToCreate[i][0] = lpPath; } if (where != '0_userdata.0') { // Create States under javascript.x let numStates = statesToCreate.length; statesToCreate.forEach(function(loopParam) { if (LOG_DEBUG) log('[Debug] Now we are creating new state [' + loopParam[0] + ']'); let loopInit = (loopParam[1]['def'] == undefined) ? null : loopParam[1]['def']; // mimic same behavior as createState if no init value is provided createState(loopParam[0], loopInit, force, loopParam[1], function() { numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states processed.'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] Function to callback parameter was provided'); return callback(); } else { return; } } }); }); } else { // Create States under 0_userdata.0 let numStates = statesToCreate.length; let counter = -1; statesToCreate.forEach(function(loopParam) { counter += 1; if (LOG_DEBUG) log ('[Debug] Currently processing following state: [' + loopParam[0] + ']'); if( ($(loopParam[0]).length > 0) && (existsState(loopParam[0])) ) { State is existing. if (WARN && !force) log('State [' + loopParam[0] + '] is already existing and will no longer be created.', 'warn'); if (!WARN && LOG_DEBUG) log('[Debug] State [' + loopParam[0] + '] is already existing. Option force (=overwrite) is set to [' + force + '].'); if(!force) { // State exists and shall not be overwritten since force=false // So, we do not proceed. numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states successfully processed!'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] An optional callback function was provided, which we are going to execute now.'); return callback(); } } else { // We need to go out and continue with next element in loop. return; // https://stackoverflow.com/questions/18452920/continue-in-cursor-foreach } } // if(!force) } // State is not existing or force = true, so we are continuing to create the state through setObject(). let obj = {}; obj.type = 'state'; obj.native = {}; obj.common = loopParam[1]; setObject(loopParam[0], obj, function (err) { if (err) { log('Cannot write object for state [' + loopParam[0] + ']: ' + err); } else { if (LOG_DEBUG) log('[Debug] Now we are creating new state [' + loopParam[0] + ']') let init = null; if(loopParam[1].def === undefined) { if(loopParam[1].type === 'number') init = 0; if(loopParam[1].type === 'boolean') init = false; if(loopParam[1].type === 'string') init = ''; } else { init = loopParam[1].def; } setTimeout(function() { setState(loopParam[0], init, true, function() { if (LOG_DEBUG) log('[Debug] setState durchgeführt: ' + loopParam[0]); numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states processed.'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] Function to callback parameter was provided'); return callback(); } } }); }, DELAY + (20 * counter) ); } }); }); } }@rtwl
Die Datenpunkte werden als ReadOnly angelegt.finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_LEFT, {'name':'Days left', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]);Das führt dann zu dem Fehler
2023-02-12 21:05:00.103 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysLeft" has been written without ack-flag with value "59"Mach die angemeckerten Datenpunkte doch einfach beschreibbar.
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." (Martin Fowler, "Refactoring")
Proxmox 9.1.1 LXC|8 GB|Core i7-6700
HmIP|ZigBee|Tasmota|Unifi
Zabbix Certified Specialist
Konnte ich Dir helfen? Dann benutze bitte das Voting unten rechts im Beitrag -
Ich hab mir dieses Script kopiert: https://github.com/Mic-M/iobroker-pflanzen-giessen-script/blob/master/pflanzen-giessen-script.js
Alle 3 Stunden kommt allerdings eine Fehlermeldung im Log, den ich zwar theoretisch verstehe, aber zu unwissend bin ihn zu beheben.Kann mir hier bitte jemand helfen?
2023-02-12 21:05:00.102 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.103 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysLeft" has been written without ack-flag with value "59" 2023-02-12 21:05:00.106 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.107 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.108 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.percentElapsed" has been written without ack-flag with value "2" 2023-02-12 21:05:00.109 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.Entkalken.level" has been written without ack-flag with value "green" 2023-02-12 21:05:00.149 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.151 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysLeft" has been written without ack-flag with value "6" 2023-02-12 21:05:00.154 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.155 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.156 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.percentElapsed" has been written without ack-flag with value "14" 2023-02-12 21:05:00.157 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenUG.level" has been written without ack-flag with value "green" 2023-02-12 21:05:00.157 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysOverdue" has been written without ack-flag with value "0" 2023-02-12 21:05:00.158 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysLeft" has been written without ack-flag with value "6" 2023-02-12 21:05:00.158 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.daysElapsed" has been written without ack-flag with value "1" 2023-02-12 21:05:00.159 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.isOverdue" has been written without ack-flag with value "false" 2023-02-12 21:05:00.160 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.percentElapsed" has been written without ack-flag with value "14" 2023-02-12 21:05:00.160 - warn: javascript.0 (11896) Read-only state "0_userdata.0.VIS.Timer.PflanzenOG.level" has been written without ack-flag with value "green"Mein personalisierter Code (Datenpunkt, usw). Vielleicht hab ich ja was falsch geändert?
PS: Keine Ahnung woher diese "... created this issue in" herkommt.
/************************************************************************************************** * Script "Timer" (ehemals: "Pflanzen gießen") * ------------------------------------------------------------------------------------- * Autor: Mic (ioBroker) | Mic-M (github) * Change Log * 0.3 Mic Support creating states under 0_userdata.0 * 0.2 Mic Diverse Verbesserungen * 0.1 Mic Initial version **************************************************************************************************/ /******************************************************************************* * Konfiguration: Pfade / Datenpunkte ******************************************************************************/ // Pfad, unter dem die States (Datenpunkte) in den Objekten angelegt werden. // Es wird die Anlage sowohl unterhalb '0_userdata.0' als auch 'javascript.x' unterstützt. //const STATE_PATH = 'javascript.'+ instance + '.' + 'VIS.Timer'; const STATE_PATH = '0_userdata.0.VIS.Timer'; // Beliebig viele einzelne Timer anlegen. // Zeilen leer lassen oder löschen, falls nicht benötigt. const TIMERS = [ {'state': 'PflanzenOG', 'name': 'Pflanzen OG gießen'}, {'state': 'PflanzenUG', 'name': 'Pflanzen UG gießen'}, {'state': 'Entkalken', 'name': 'Geräte entkalken'}, {'state': '', 'name': ''}, ]; // Datenpunkte: Einzelne Datenpunkte. // Kein Grund zur Änderung! Einfach so belassen. const STATE_INTERVAL = 'interval'; // Number - Wie oft gießen? Anzahl Tage const STATE_RESTART = 'reStartCounter'; // true (Button) - Neustart Zähler: Wir fangen neu an zu zählen, wird betätigt sobald die Pflanzen gegossen worden sind const STATE_OVERDUE = 'isOverdue'; // true/false. Wenn "faellig", dann müssen die Pflanzen gegossen werden. Wird false gesetzt, sobald Counter neu startet const STATE_PERCENT_ELAPSED = 'percentElapsed'; // Wann fällig, in %. D.h. bei 10 Tagen 'anzahlTageBisFaellig', und vor 3 Tagen gegossen = 30% const STATE_DAYS_OVERDUE = 'daysOverdue'; // Number. Anzahl Tage, seit dem die Pflanzen gegossen werden müssten (aber es noch nicht sind) const STATE_DAYS_LEFT = 'daysLeft'; // Number. Anzahl Tage, bis die Pflanzen gegossen werden müssen const STATE_DAYS_ELAPSED = 'daysElapsed'; // Number. Anzahl Tage, seit dem der Timer gestartet wurde const STATE_DATETIMESTART = 'dateTimeCounterStart'; // Date/Time. Wann wurde der Timer gestartet const STATE_LEVEL = 'level'; // Grün / Gelb / Rot, je nach % vergangen. Siehe Konfiguration Schwellwerte /******************************************************************************* * Konfiguration: Rest ******************************************************************************/ // Schwellwerte in %: Im Datenpunkt ".level" wird green/yellow/red ausgegeben, je nach Datenpunkt 'faelligProzent'. // Bitte einen Bereich angeben: [20, 40]: Bedeutet, dass %-Wert zwischen 20 und 40 liegen muss. const LEVEL_GREEN = [0, 85]; const LEVEL_YELLOW = [86, 99]; const LEVEL_RED = [100, 99999999]; // Wie oft aktualisieren? const PLANTS_SCHEDULE = '5 */3 * * *'; // Alle 3 Stunden. // Logeinträge anzeigen? const LOGINFO = true; // Default: Anzahl Tage. // Kann jederzeit im State 'interval' geändert werden. const INTERVAL_PRESET = 14; /************************************************************************************************************************* * Ab hier nichts mehr ändern / Stop editing here! *************************************************************************************************************************/ /**************************************************************************************** * Global variables and constants ****************************************************************************************/ // Final state path const FINAL_STATE_LOCATION = validateStatePath(STATE_PATH, false); const FINAL_STATE_PATH = validateStatePath(STATE_PATH, true) + '.'; // adding trailing dot // Schedule let mSchedule = []; /******************************************************************************* * Executed on every script start. *******************************************************************************/ init(); function init() { // Create states createUserStates(FINAL_STATE_LOCATION, false, buildScriptStates(), function() { // Main Script starten setTimeout(main, 2000); }); } /******************************************************************************* * Haupt-Skript *******************************************************************************/ function main() { for (let i = 0; i < TIMERS.length; i++) { // First: check if we have a valid configuration // TO DO: Further check TIMERS[i].state for valid state chars if (isLikeEmpty(TIMERS[i].state)) { continue; // breaks current iteration in the loop and continues with the next } /*************************** * Schedule beenden falls aktiv, dann starten. * Dies machen wir, damit z.B. bei JavaScript-Adapter-Neustart immer sichergestellt ist, dass das Schedule läuft. * Außerdem initiales Start-Datum setzen, falls nicht vorhanden. **************************/ clearSchedule(mSchedule[i]); let dtStart = getState(FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART).val; if ( (isNumber(dtStart)) && (dtStart > 0) ) { mSchedule[i] = schedule(PLANTS_SCHEDULE, function() { updateStates(TIMERS[i]); }); if (LOGINFO) log('Timer ' + TIMERS[i].name + ': Script wurde neu gestartet. Ein Timer-Startdatum ist vorhanden, also wird nur aktualisiert.'); updateStates(TIMERS[i]); } else { // Initial ist noch kein Start-Datum gesetzt, also setzen wir eines. setState(FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, Date.now()); // Dies triggert automatisch updateStates(), da State STATE_DATETIMESTART per on(id) überwacht wird. if (LOGINFO) log('Timer ' + TIMERS[i].name + ': Startdatum im State ist leer, daher werden Startdatum und Initialwerte frisch gesetzt.'); } /*************************** * Überwache State-Button reStartCounter **************************/ on({id:FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_RESTART, val:true}, function (obj) { // First_ need to get - within on...id - current state portion let tmpArray = obj.id.split('.'); let statePortion = tmpArray[(tmpArray.length - 2)] setState(FINAL_STATE_PATH + statePortion + '.' + STATE_DATETIMESTART, Date.now()); // Dies triggert automatisch updateStates(), da State STATE_DATETIMESTART per on(id) überwacht wird. setStateDelayed(FINAL_STATE_PATH + statePortion + '.' + STATE_RESTART, false, 300); // wieder zurück setzen zur schönen Darstellung. }); /*************************** * Überwache State-Button interval, da diese Anzahl Tage vom User jederzeit geändert werden können **************************/ on({id: FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_INTERVAL, change:'ne'}, function (obj) { updateStates(TIMERS[i]); }); /*************************** * Überwache Datum/Uhrzeit Counter-Start, falls dieser manuell geändert wurde. **************************/ on({id: FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, change:'ne'}, function (obj) { updateStates(TIMERS[i]); }); /*************************** * Sobald die Planzen gegossen werden müssen, führen wir folgendes aus. * @param {object} objTimes Objekt, also Array-Element von TIMERS **************************/ function updateStates(objTimes) { let now = new Date(); let nowTimeStamp = now.getTime(); let startTimeInState = new Date(getState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DATETIMESTART).val); let intervalInState = getState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_INTERVAL).val; let dateTimeDue = dateAddMinutes(startTimeInState, intervalInState*60*24); let numberOfMsDue = nowTimeStamp - dateTimeDue; // https://stackoverflow.com/questions/7709803/javascript-get-minutes-between-two-dates let numberOfMinutesDue = Math.floor((numberOfMsDue/1000)/60); let numberOfDaysDue = Math.round((numberOfMinutesDue/60)/24); let numberOfMsLeft = dateTimeDue - nowTimeStamp; let numberOfMinutesLeft = Math.floor((numberOfMsLeft/1000)/60); let numberOfDaysLeft = Math.round((numberOfMinutesLeft/60)/24); let numberOfMsElapsed = nowTimeStamp - startTimeInState.getTime(); let numberOfMinutesElapsed = Math.floor((numberOfMsElapsed/1000)/60); let numberOfDaysElapsed = Math.round((numberOfMinutesElapsed/60)/24); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_OVERDUE, numberOfDaysDue); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_LEFT, numberOfDaysLeft); setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_DAYS_ELAPSED, numberOfDaysElapsed); if ( dateTimeDue < nowTimeStamp ) { // Jetzt die Pflanzen gießen setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_OVERDUE, true); if (LOGINFO) log('Timer ' + objTimes.name + ': Prüfungsergebnis: Fällig seit ' + numberOfDaysDue + ' Tagen'); } else { // Pflanzen noch nicht gießen setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_OVERDUE, false); if (LOGINFO) log('Timer ' + objTimes.name + ': Prüfungsergebnis: Noch nicht fällig, erst in ' + numberOfDaysLeft + ' Tagen'); } // Setze Prozent let percent = Math.round(numberOfDaysElapsed * 100 / intervalInState); if (percent > 100) percent = 100; setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_PERCENT_ELAPSED, percent); // Setze Level (Farben) je nach % let levelResult = 'undefined'; if ( isInRange(percent, LEVEL_GREEN[0], LEVEL_GREEN[1]) ) levelResult = 'green'; if ( isInRange(percent, LEVEL_YELLOW[0], LEVEL_YELLOW[1]) ) levelResult = 'yellow'; if ( isInRange(percent, LEVEL_RED[0], LEVEL_RED[1]) ) levelResult = 'red'; setState(FINAL_STATE_PATH + objTimes.state + '.' + STATE_LEVEL, levelResult); } } // for } /******************************************************************************* * Weitere unterstützende Funktionen usw. *******************************************************************************/ /** * Add certain number of minutes to a given date/time. * @param {object} date Provided date. * @param {number} minutes number of minutes to be added to a given date * @return {object} new date with the minutes added */ function dateAddMinutes(date, minutes) { return new Date(date.getTime() + minutes*60000); } /** * Prüft ob Variableninhalt eine Zahl ist. * isNumber ('123'); // true * isNumber ('123abc'); // false * isNumber (5); // true * isNumber ('q345'); // false * isNumber(null); // false * isNumber(undefined); // false * isNumber(false); // false * isNumber(' '); // false * @source https://stackoverflow.com/questions/1303646/check-whether-variable-is-number-or-string-in-javascript * @param {any} n Variable, die zu prüfen ist auf Zahl * @return {boolean} true falls Zahl, false falls nicht. */ function isNumber(n) { return /^-?[\d.]+(?:e-?\d+)?$/.test(n); } /** * Checks if a number is within a range. Returns true if in range, and false otherwise. * @param x {number} Number to check if it is within range * @param min {number} min value * @param max {number} max value * @return {boolean} true if in range, and false otherwise. */ function isInRange(x, min, max) { return ((x-min)*(x-max) <= 0); } /** * Build states */ function buildScriptStates() { let finalStates = []; for (let i = 0; i < TIMERS.length; i++) { if (isLikeEmpty(TIMERS[i].state)) { continue; // breaks current iteration in the loop and continues with the next } finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_INTERVAL, {'name':'Set interval in days', 'type':'number', 'unit':'d', 'min':1, 'max':60, 'read':true, 'write':true, 'role':'value', 'def':INTERVAL_PRESET}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_RESTART, {'name':'Restart counter', 'type':'boolean', 'read':true, 'write':true, 'role':'button', 'def':false}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_OVERDUE, {'name':'Overdue?', 'type':'boolean', 'read':true, 'write':false, 'role':'state'}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_OVERDUE, {'name':'Number of days overdue', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_PERCENT_ELAPSED, {'name':'Elapsed in %', 'type':'number', 'unit':'%', 'min':0, 'max':100, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_ELAPSED, {'name':'Elapsed in number of days', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DAYS_LEFT, {'name':'Days left', 'type':'number', 'unit':'d', 'min':0, 'max':9999, 'read':true, 'write':false, 'role':'value', 'def':0}]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_DATETIMESTART, {'name':'Date/Time of counter start', 'type':'number', 'read':true, 'write':true, 'role':'value.time' }]); finalStates.push([FINAL_STATE_PATH + TIMERS[i].state + '.' + STATE_LEVEL, {'name':'green/yellow/red per elapsed %', 'type':'string', 'read':true, 'write':false, 'role':'value'}]); } return finalStates; } /** * Checks if Array or String is not undefined, null or empty. * 08-Sep-2019: added check for [ and ] to also catch arrays with empty strings. * @param inputVar - Input Array or String, Number, etc. * @return true if it is undefined/null/empty, false if it contains value(s) * Array or String containing just whitespaces or >'< or >"< or >[< or >]< is considered empty */ function isLikeEmpty(inputVar) { if (typeof inputVar !== 'undefined' && inputVar !== null) { let strTemp = JSON.stringify(inputVar); strTemp = strTemp.replace(/\s+/g, ''); // remove all whitespaces strTemp = strTemp.replace(/\"+/g, ""); // remove all >"< strTemp = strTemp.replace(/\'+/g, ""); // remove all >'< strTemp = strTemp.replace(/\[+/g, ""); // remove all >[< strTemp = strTemp.replace(/\]+/g, ""); // remove all >]< if (strTemp !== '') { return false; } else { return true; } } else { return true; } } /** * For a given state path, we extract the location '0_userdata.0' or 'javascript.0' or add '0_userdata.0', if missing. * @param {string} path Like: 'Computer.Control-PC', 'javascript.0.Computer.Control-PC', '0_userdata.0.Computer.Control-PC' * @param {boolean} returnFullPath If true: full path like '0_userdata.0.Computer.Control-PC', if false: just location like '0_userdata.0' or 'javascript.0' * @return {string} Path */ function validateStatePath(path, returnFullPath) { if (path.startsWith('.')) path = path.substr(1); // Remove first dot if (path.endsWith('.')) path = path.slice(0, -1); // Remove trailing dot if (path.length < 1) log('Provided state path is not valid / too short.', 'error') let match = path.match(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/); let location = (match == null) ? '0_userdata.0' : match[0].slice(0, -1); // default is '0_userdata.0'. if(returnFullPath) { return (path.indexOf(location) == 0) ? path : (location + '.' + path); } else { return location; } } /** * Create states under 0_userdata.0 or javascript.x * Support: https://forum.iobroker.net/topic/26839/ * Autor: Mic (ioBroker) | Mic-M (github) * Version: 1.1 (26 January 2020) * ----------------------------------------------- * executes the callback PRIOR to completing the state creation. Therefore, we use a setTimeout and counter. * ----------------------------------------------- * @param {string} where Where to create the state: '0_userdata.0' or 'javascript.x'. * @param {boolean} force Force state creation (overwrite), if state is existing. * @param {array} statesToCreate State(s) to create. single array or array of arrays * @param {object} [callback] Optional: a callback function -- This provided function will be executed after all states are created. */ function createUserStates(where, force, statesToCreate, callback = undefined) { const WARN = false; // Only for 0_userdata.0: Throws warning in log, if state is already existing and force=false. Default is false, so no warning in log, if state exists. const LOG_DEBUG = false; // To debug this function, set to true function setObject() executes the callback // before the state is actual created. Therefore, we use a setTimeout and counter as a workaround. const DELAY = 50; // Delay in milliseconds (ms). Increase this to 100, if it is not working. // Validate "where" if (where.endsWith('.')) where = where.slice(0, -1); // Remove trailing dot if ( (where.match(/^((javascript\.([1-9][0-9]|[0-9]))$|0_userdata\.0$)/) == null) ) { log('This script does not support to create states under [' + where + ']', 'error'); return; } // Prepare "statesToCreate" since we also allow a single state to create if(!Array.isArray(statesToCreate[0])) statesToCreate = [statesToCreate]; // wrap into array, if just one array and not inside an array // Add "where" to STATES_TO_CREATE for (let i = 0; i < statesToCreate.length; i++) { let lpPath = statesToCreate[i][0].replace(/\.*\./g, '.'); // replace all multiple dots like '..', '...' with a single '.' lpPath = lpPath.replace(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/,'') // remove any javascript.x. / 0_userdata.0. from beginning lpPath = where + '.' + lpPath; // add where to beginning of string statesToCreate[i][0] = lpPath; } if (where != '0_userdata.0') { // Create States under javascript.x let numStates = statesToCreate.length; statesToCreate.forEach(function(loopParam) { if (LOG_DEBUG) log('[Debug] Now we are creating new state [' + loopParam[0] + ']'); let loopInit = (loopParam[1]['def'] == undefined) ? null : loopParam[1]['def']; // mimic same behavior as createState if no init value is provided createState(loopParam[0], loopInit, force, loopParam[1], function() { numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states processed.'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] Function to callback parameter was provided'); return callback(); } else { return; } } }); }); } else { // Create States under 0_userdata.0 let numStates = statesToCreate.length; let counter = -1; statesToCreate.forEach(function(loopParam) { counter += 1; if (LOG_DEBUG) log ('[Debug] Currently processing following state: [' + loopParam[0] + ']'); if( ($(loopParam[0]).length > 0) && (existsState(loopParam[0])) ) { State is existing. if (WARN && !force) log('State [' + loopParam[0] + '] is already existing and will no longer be created.', 'warn'); if (!WARN && LOG_DEBUG) log('[Debug] State [' + loopParam[0] + '] is already existing. Option force (=overwrite) is set to [' + force + '].'); if(!force) { // State exists and shall not be overwritten since force=false // So, we do not proceed. numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states successfully processed!'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] An optional callback function was provided, which we are going to execute now.'); return callback(); } } else { // We need to go out and continue with next element in loop. return; // https://stackoverflow.com/questions/18452920/continue-in-cursor-foreach } } // if(!force) } // State is not existing or force = true, so we are continuing to create the state through setObject(). let obj = {}; obj.type = 'state'; obj.native = {}; obj.common = loopParam[1]; setObject(loopParam[0], obj, function (err) { if (err) { log('Cannot write object for state [' + loopParam[0] + ']: ' + err); } else { if (LOG_DEBUG) log('[Debug] Now we are creating new state [' + loopParam[0] + ']') let init = null; if(loopParam[1].def === undefined) { if(loopParam[1].type === 'number') init = 0; if(loopParam[1].type === 'boolean') init = false; if(loopParam[1].type === 'string') init = ''; } else { init = loopParam[1].def; } setTimeout(function() { setState(loopParam[0], init, true, function() { if (LOG_DEBUG) log('[Debug] setState durchgeführt: ' + loopParam[0]); numStates--; if (numStates === 0) { if (LOG_DEBUG) log('[Debug] All states processed.'); if (typeof callback === 'function') { // execute if a function was provided to parameter callback if (LOG_DEBUG) log('[Debug] Function to callback parameter was provided'); return callback(); } } }); }, DELAY + (20 * counter) ); } }); }); } }@rtwl sagte in Log Error aus Script: Read-only state ... written without...:
PS: Keine Ahnung woher diese "... created this issue in" herkommt.
Weil der Parser hier im Forum die URL's im Code findet.
Schmeiß mal die entsprechenden Zeilen (in den Kommentaren) raus."Any fool can write code that a computer can understand. Good programmers write code that humans can understand." (Martin Fowler, "Refactoring")
Proxmox 9.1.1 LXC|8 GB|Core i7-6700
HmIP|ZigBee|Tasmota|Unifi
Zabbix Certified Specialist
Konnte ich Dir helfen? Dann benutze bitte das Voting unten rechts im Beitrag -
@rtwl sagte in Log Error aus Script: Read-only state ... written without...:
PS: Keine Ahnung woher diese "... created this issue in" herkommt.
Weil der Parser hier im Forum die URL's im Code findet.
Schmeiß mal die entsprechenden Zeilen (in den Kommentaren) raus.@codierknecht
Danke!
Hab die bestehenden Datenpunkte sowie die entsprechenden Zeilen im Skript auf'write':truegesetzt. Hoffe das passt so.
Genauso hab ich die entsprechenden Links im Spoiler-Code entfernt.-Peter
-
@rtwl
Zeilen 268 - 276 müssten auch angepasst werden.
Bei einigen steht noch:'write':falseNUC i7 64GB mit Proxmox ---- Jarvis Infos Aktualisierungen der Doku auf Instagram verfolgen -> mcuiobroker Instagram
Wenn Euch mein Vorschlag geholfen hat, bitte rechts "^" klicken.