Weiter zum Inhalt

JavaScript

2.5k Themen 49.2k Beiträge

Hilfe für Skripterstellung mit JavaScript

NEWS

  • Omlet Hühnerstall webhook API

    36
    0 Stimmen
    36 Beiträge
    464 Aufrufe
    J
    Hi, aktuell was ich beobachtet habe, ja. Die Rückmeldung wollte ich erst geben, wenn ich den Futterautomat erhalten habe, der aber bisher noch nicht geliefert wurde. Auch diesen kann man mittels API einbinden. Sollte es bei der Einbindung Probleme geben, melde ih mich noch mal, ansonsten aber auch so.
  • Vorschlag zu globalen Scripten

    11
    0 Stimmen
    11 Beiträge
    245 Aufrufe
    OliverIOO
    @legro Das ist eigentlich eine Grundfunktion von Javaskript/node https://developer.mozilla.org/en-US/docs/Glossary/Just_In_Time_Compilation Da jedes Script in einer eigenen VM (nicht zu verwechseln mit einer VM bspw in proxmox) Ausgeführt wird, gibt es zwischen den Skripten keine Optimierung https://nodejs.org/api/vm.html
  • FritzDect - Extended

    5
    0 Stimmen
    5 Beiträge
    37 Aufrufe
    Ro75R
    Ich habe den Code bzgl. axios angepasst. Wie gesagt, unter JS-Adapter 8.x war es nicht nötig, unter 9.x bei mir schon. Ich habe wie gesagt, den Code jetzt etwas angepasst und nun geht es auch ohne das axios als zusätzliches NPM-Paket eingetragen werden muss. Ro75.
  • E3DC Hauskraftwerk steuern

    4k
    1
    3 Stimmen
    4k Beiträge
    2m Aufrufe
    Thomas BraunT
    @Dave69-CH Ja, in der Standardansicht. Ich fahre bei mir die Experteneinstellung, da sieht das etwas anders aus. j
  • clearSchedule löscht keine Schedules (mehr)

    3
    0 Stimmen
    3 Beiträge
    65 Aufrufe
    R
    https://github.com/ioBroker/ioBroker.javascript/issues/2164
  • wo sind Scripte gespeichert?

    javascript
    7
    0 Stimmen
    7 Beiträge
    86 Aufrufe
    etvE
    klar - passiert am ioBroker Raspi täglich, aber OC holt nur und schreibt nicht 😉
  • [Gelöst] setState in "Binding"

    7
    0 Stimmen
    7 Beiträge
    183 Aufrufe
    P
    @bahnuhr zur Erläuterung meines Vorgehens: Ausgehend von einer Navigation über ein ein Widget "Universal (Schalter, Taster, Nav & mehr)" aus inventwo design... [image: 1775024353201-navigation.png] ...wähle ich eine PDF-Datei eines Kalenders, dessen Dateiname mit Pfad dann in einen Datenpunkt geschrieben wird. [image: 1775024467571-objekt.png] Dieser Datenpunkt wird dann verwendet um die ausgewählte Datei in einem iFrame anzuzeigen. [image: 1775024571909-kalender24.png]
  • Navimow Steuerung ioBroker

    7
    3
    0 Stimmen
    7 Beiträge
    113 Aufrufe
    K
    Hallo Tombox 🙋‍♂️ Kannste mir wohl noch sagen welches Widget ich haben muss mit welchen Einstellungen für den Countdown in der IoBroker Vis
  • getState liefert anders Ergebnis als bei Objekt

    Ungelöst
    21
    0 Stimmen
    21 Beiträge
    177 Aufrufe
    OliverIOO
    @paul53 sagte: @OliverIO [sagte]: Meinung das das der Adapter machen muss. Als Javascript-Programmierer sollte man wissen, dass Arrays und Objekte referenziert werden. Das weiß ich schon, Aber mehrfaches lesen eines datenpunkts sollte konsistent auch den echten Inhalt des datenpunkts liefern, auch wenn man in die variable das zwischenzeitlich was reinschreibt. Die meisten Nutzer wissen ja nix vom caching, welches sich ja auch noch an und ausschalten lässt.
  • Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

    232
    9 Stimmen
    232 Beiträge
    17k Aufrufe
    maxclaudiM
    @Daniel-8 und @mabbi und Alle habe nun endlich ein neues Gerät zum testen erhalten. Dabei fiel mir gleich in meinem bisherigen Script die unübersichtliche Struktur auf. Auch beim Start gab es Warnungen, wenn erstmals neue Verzeichnisse angelegt wurden. Hat mir niemand geschrieben. Korrigiert und optimiert. Das Script ist ein vollwertiger Adpater-Ersatz. Das -1 setzen wurde entfernt. Beim setzen eines Control-State wird der Wert gesendet und gleich mit dem bestätigten Wert aktualisiert. Intervall von 8 s (für die State-Abfrage/Report) läuft mit diesem Script problemlos (Queue Sicherung), wenn Netzwerkverbindung Zendure <> WLAN-Router ok ist. MQTT (lokal) ist per Slider (boolean default deaktiviert). Wenn deaktiviert, ist die MQTT-Script-Funktion komplett ausgeschaltet: MQTT-Intervall wird nicht ausgeführt dadurch keine MQTT-Statusabfrage kein Ein-/Auschalten von lokalem MQTT möglich MQTT- Datenfelder im Kofig-Block sind dann bedeutungslos. Keine Fehler bei Start. Fehlende Verzeichnisse und Datenpunkte werden automatisch erstellt. Sollten neue Datenpunkte hinzukommen (durch Frimwareupdate) werden sie als Datenpunkte automatisch erstellt. Neue Struktur. Haupt-Verzeichnis kann im Konfigblock leicht auf einen sinnvollen, eigenen Namen geändert werden. Hinweis: wenn man auf + SN; dort verzichtet, dann ist der Verzeichnis-Name auch ohne Seriennummer des Geräts zur Unterscheidung. Die Seriennummer muss hier nicht verwendet werden, der Verzeichnis-Name muss bei mehreren Geräten aber unterschiedlich sein. Script ist erprobt. Einige keys, die mir bekannt sind, wurden unter control nicht aufgenommen, weil sie Geräte-spezifisch sind, Gefahren darstellen und/oder (noch?) nicht offiziell unterstützt werden.... wie auch immer. Bin etwas entäuscht von zenSDK. Manche keys werden von dem Zendure-Gerät-Webserver nicht oder unregelmäßig aktualisiert. Neue Struktur des Scripts: [image: 1774891156463-script.png] minTimeBreakForSetDpSec = 5; Ist einstellbar. 5 sek. haben sich bei mir bewährt. Nach dem setzen eines Befehls müssen mindestens x sek. vergangen sein um einen neuen zu senden. Es sind mehrere Sicherungen eingebaut. Kommentare im Konfig-Bereich bitte lesen. Script ist ein Adapter-Ersatz. In meinen Augen der ehrlichste. Es werden keine Befehle und keine Befehlsketten gesendet, von denen man nichts weiß. Volle Verantwortung bei dem user. Auch smartMode wird nicht automatisch geschaltet. // ioBroker JavaScript: Zendure zenSDK Adapter-Ersatz // für alle Geräte ab 2025 die zenSDK unterstützen wie // SF800, SF800 PLUS, 800Pro, 1600AC, SF2400AC usw. // by maxclaudi 2026.03.30_19.10h for ioBroker Forum //------------------------------------------------------------------------------------ // konfiguration // // Hinweis bei mehreren Zendure-Geräten: // - Für jedes Gerät ein eigenes Script mit individueller Konfiguration verwenden! // -> IP-Adresse // -> Seriennummer (SN) // -> MQTT-Daten (Broker, Port, Benutzer, Passwort) // -> maxInputLimit / maxOutputLimit (abhängig vom Gerätetyp) // - Intervalle (Standard): intervalGet = 10s, intervalMqtt = 300s – können gleich bleiben // // Empfehlung: // • 3 Geräte: völlig unkritisch // • 4 Geräte: problemlos möglich // • mehr als 4: nicht empfohlen // // MQTT-Abfrage ist per default false=disabled. Lokales, offizielles MQTT ist mit bis zu 90sek Aktualisierungsrate zu langsam. // Empfehle zenSDK zu verwenden oder DNS auf lokalen Betrieb umzubiegen. // Wer offizielles, lokale MQTT dennoch benutzen möchte und überwachen möchte kann den Datenpunkt mit dem Slider einschalten. // Satndardist bei script-Start MQTT-Abfrage ausgeschaltet und wird dann vom script komplett deaktiviert. // Auch kein Intervall und keine Aktualisierung erfolgt dann. // // Damit neue Datenpunkte automatisch aus dem JSON erstellt werden: // -> Instanzen -> javascript-Adapter -> Allgemeine Einstellungen -> Enable command"setObject“ aktivieren! // // Datenpunkte bei set werden automatisch aktualisiert und nicht auf -1 zurück gesetzt. //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ // CONFIG //------------------------------------------------------------------------------------ // Intervalle für Abfragen (Sekunden) const intervalGet = 8; // sek Intervall für GET report (default: 8, nicht <8) intervalGet MUSS größer sein als timeoutHttp !! const intervalMqtt = 300; // sek MQTT-Status (default: 300 sek, > intervalGet) wird nur ausgeführt wenn Abfrage über SetMqttEnable eingeschaltet wird. // IP und Seriennummer Zendure Gerät const IP = "192.168.40.20"; // IP des Zendure Geräts const SN = "Exxxxxxxxxxx007"; // Seriennummer // MQTT Broker Konfiguration const mqttBrokerIp = "192.168.40.200"; // IP MQTT Broker und in App eingestellt const mqttPort = 1883; // PORT MQTT Broker und in App eingestellt const mqttUsername = "Daisy"; // MQTT Username const mqttPassword = "coco"; // MQTT Passwort //maximum inputLimit SF800 800W / SF800 PRO: 1000W / 1600AC: 1600 / SF2400AC: 2400W const maxInputLimit = 1000; //maximum outputLimit SF800 800W / SF800 PRO: 800W / 1600AC: 1600 / SF2400AC: 2400W const maxOutputLimit = 800; // Pause (Wert in Sekunden) die vergangen sein muss bevor ein neuer Befehl gesendet werden kann. const minTimeBreakForSetDpSec = 5; // 5 s hat sich bei mir bewährt, minimal 2s! // Haupt-Verzeichnis für das Zendure-Gerät // hier kann der Name für das Hauptverzeichnis für das Gerät verändert werden. // Standard wird die Seriennummer verwendet. Es kann aber durch einen beliebigen Namen ersetzt werden. // Beispiel: //const folderZendureApi = '0_userdata.0.zendure.' + "SolarFlow_800_1_Dachboden_links"; const folderZendureApi = '0_userdata.0.zendure.' + SN; // // Timout Handler HTTP GET /POST // timeoutHttp MUSS kleiner sein als intervalGet!! Besser nicht ändern. Standard: 2000ms const timeoutHttp = 2000; // Timeout in ms für alle HTTP-Anfragen (Zendure) //------------------------------------------------------------------------------------ // End of config //------------------------------------------------------------------------------------ const mqttStateAskingDefault = false; // nicht verändern! Kann bei laufendem script ein- oder ausgeschaltet werden let lastMqttSet = 0; let mqttInterval = null; //------------------------------------------------------------------------------------ // dp //------------------------------------------------------------------------------------ const dpSetSmartMode = folderZendureApi + ".control.setSmartMode"; const dpSetAcMode = folderZendureApi + ".control.setAcMode"; const dpSetInputLimit = folderZendureApi + ".control.setInputLimit"; const dpSetOutputLimit = folderZendureApi + ".control.setOutputLimit"; const dpSetSocSet = folderZendureApi + ".control.setSocSet"; const dpSetMinSoc = folderZendureApi + ".control.setMinSoc"; const dpSetGridReverse = folderZendureApi + ".control.setGridReverse"; const dpSetGridStandard = folderZendureApi + ".control.setGridStandard"; const dpSetInverseMaxPower = folderZendureApi + ".control.setInverseMaxPower"; const dpSetChargeMaxLimit = folderZendureApi + ".control.setChargeMaxLimit"; const dpSetGridOffMode = folderZendureApi + ".control.setGridOffMode"; const dpSetMqttEnable = folderZendureApi + ".control.localMqtt.EnableScriptControlLocalMqtt"; const dpSetMqttConnect = folderZendureApi + ".control.localMqtt.SetConnectlocalMqtt"; const dpMqttConnectInfo = folderZendureApi + ".control.localMqtt.InfoLocalMqttConnected"; const dpTimestamp = folderZendureApi + ".timestamp"; //------------------------------------------------------------------------------------ // mapping //------------------------------------------------------------------------------------ const dpMap = { [dpSetSmartMode]: { key: "smartMode", min: 0, max: 1 }, [dpSetAcMode]: { key: "acMode", min: 1, max: 2 }, [dpSetInputLimit]: { key: "inputLimit", min: 0, max: maxInputLimit }, [dpSetOutputLimit]: { key: "outputLimit", min: 0, max: maxOutputLimit }, [dpSetSocSet]: { key: "socSet", transform: v => v*10, min: 70, max: 100 }, [dpSetMinSoc]: { key: "minSoc", transform: v => v*10, min: 5, max: 50 }, [dpSetGridReverse]: { key: "gridReverse", min: 0, max: 2 }, [dpSetGridStandard]: { key: "gridStandard", min: 0, max: 2 }, [dpSetInverseMaxPower]: { key: "inverseMaxPower", min: 600, max: maxOutputLimit }, [dpSetChargeMaxLimit]: { key: "chargeMaxLimit", min: 0, max: maxInputLimit }, [dpSetGridOffMode]: { key: "gridOffMode", min: 0, max: 2 } }; //------------------------------------------------------------------------------------ // QUEUE //------------------------------------------------------------------------------------ const curlQueue = []; let curlRunning = false; function runQueue() { if (curlRunning || curlQueue.length === 0) return; const task = curlQueue.shift(); curlRunning = true; task.fn(() => { curlRunning = false; runQueue(); }); } //------------------------------------------------------------------------------------ // helper //------------------------------------------------------------------------------------ function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function formatTime(ts) { const d = new Date(ts * 1000); const pad = n => n.toString().padStart(2, "0"); return `${pad(d.getDate())}.${pad(d.getMonth()+1)}.${d.getFullYear().toString().slice(-2)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; } function handleHttpError(context, err, options) { const transient = [ "EHOSTUNREACH", "ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "socket hang up", "ENETUNREACH", "EAI_AGAIN", "ENOTFOUND" ]; const msg = err && err.message ? err.message : String(err); const isTransient = transient.some(code => msg.includes(code)); //info_error //log(`${context}: ${isTransient ? "war vorübergehend im Intervall nicht erfolgreich:" : "Warnung"} (${msg})`, isTransient ? "info" : "warn"); log(`${context}: ${isTransient ? "war vorübergehend im Intervall nicht erfolgreich:" : "Warnung"} (${msg})`, "info"); // set Timestamp if (options && options.timestamp) { setState(options.timestamp, formatTime(Math.floor(Date.now() / 1000)), true); } } //------------------------------------------------------------------------------------ // CREATE STATES //------------------------------------------------------------------------------------ const dataPoints = [ { id: dpSetSmartMode, val: 0, common: { name: "Smart Mode 0:FLASH 1:RAM",desc: "Flash write behavior", type: "number", role: "state", min: 0, max: 1 } }, { id: dpSetAcMode, val: 1, common: { name: "AC Mode 1:charge mode 2:discharge mode", type: "number", role: "state", min: 1, max: 2 } }, { id: dpSetInputLimit, val: 0, common: { name: "Input Limit AC charge limit", type: "number", role: "state", min: 0, max: maxInputLimit, unit: 'W' } }, { id: dpSetOutputLimit, val: 0, common: { name: "Output Limit", type: "number", role: "state", min: 0, max: maxOutputLimit, unit: 'W' } }, { id: dpSetSocSet, val: 70, common: { name: "SOC Set (Target SOC 70%-100%)", type: "number", role: "state", min: 70, max: 100, unit: '%' } }, { id: dpSetMinSoc, val: 5, common: { name: "Min SOC (Minimum SOC 5%-50%)", type: "number", role: "state", min: 5, max: 50, unit: '%' } }, { id: dpSetGridReverse, val: 0, common: { name: "Reverse flow control (0/1/2)", type: "number", role: "state", min: 0, max: 2 } }, { id: dpSetGridStandard, val: 0, common: { name: "Grid Standard (0: Germany 1: France 2: Austria...)", desc: "(3: Switzerland 4: Netherlands 5: Spain 6: Belgium 7: Greece 8: Denmark 9: Italy)", type: "number", role: "state", min: 0, max: 9 } }, { id: dpSetInverseMaxPower, val: 600, common: { name: "Max inverter output Power", type: "number", role: "state", min: 600, max: maxOutputLimit, unit: 'W' } }, { id: dpSetChargeMaxLimit, val: 0, common: { name: "Charge Max Limit", type: "number", role: "state", min: 0, max: maxInputLimit, unit: 'W' } }, { id: dpSetGridOffMode, val: 0, common: { name: "Grid Off Mode 0: Standard Mode 1: Economic Mode 2: Closure", type: "number", role: "state", min: 0, max: 2 } }, { id: dpSetMqttEnable, val: mqttStateAskingDefault, common: { name: "Enable Script-Control of Local Mqtt: Off ON", type: "boolean", role: "state", read: true, write: true } }, { id: dpSetMqttConnect, val: 0, common: { name: "Set Connect localMqtt -> only if EnableScriptControlLocalMqtt: on", type: "number", role: "indicator", read: true, write: true } }, { id: dpMqttConnectInfo, val: 0, common: { name: "Info Local Mqtt Connected 0:off 1:on -> only if EnableScriptControlLocalMqtt: on", type: "number", role: "indicator", read: true, write: false } }, { id: dpTimestamp, val: "", common: { name: "Timestamp", type: "string", role: "state", read: true, write: false } } ]; async function startScript() { console.log("Erstelle Datenpunkte..."); for (const dp of dataPoints) { await createStateAsync(dp.id, dp.val, dp.common); } setState(dpSetMqttEnable, mqttStateAskingDefault, true); console.log("Datenpunkte bereit. Starte Abfragen..."); setInterval(getReport, intervalGet * 1000); getReport(); } startScript(); //------------------------------------------------------------------------------------ // TRIGGERS (SET) //------------------------------------------------------------------------------------ Object.keys(dpMap).forEach(id => { let lastSetTime = 0; on({ id: id, ack: false }, obj => { const val = obj.state.val; const { min, max } = dpMap[id]; if (val >= min && val <= max) { const now = Date.now(); if (now - lastSetTime >= minTimeBreakForSetDpSec * 1000) { lastSetTime = now; setControlDP(id, val); } } }); }); // MQTT POST nur wenn enable=true + rate limit on({ id: dpSetMqttConnect, ack: false }, obj => { if (!getState(dpSetMqttEnable).val) return; const now = Date.now(); if (now - lastMqttSet < minTimeBreakForSetDpSec * 1000) return; lastMqttSet = now; const val = parseInt(obj.state.val, 10); if (val === 0 || val === 1) setMqttConnect(val); }); //------------------------------------------------------------------------------------ // HTTP //------------------------------------------------------------------------------------ const http = require("http"); function setControlDP(id, val) { const { key, transform } = dpMap[id]; const value = transform ? transform(val) : val; curlQueue.push({ fn: done => { const payload = JSON.stringify({ sn: SN, properties: { [key]: value } }); const req = http.request({ hostname: IP, port: 80, path: "/properties/write", method: "POST", headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) } }, res => { res.on("data", () => {}); res.on("end", () => { // nach Schreiben sofort anfordern curlQueue.unshift({ fn: d => getReportInternal(d) }); done(); // freigeben }); }); // Timeout req.setTimeout(timeoutHttp, () => { req.destroy(); // Verbindung hart abbrechen handleHttpError("POST Timeout", "Gerät antwortet nicht", { timestamp: dpTimestamp }); done(); // WICHTIG: Queue freigeben, damit nächste Befehle folgen können }); // Fehler req.on("error", err => { handleHttpError("POST Error", err, { timestamp: dpTimestamp }); done(); }); req.write(payload); req.end(); } }); runQueue(); } function getReport() { curlQueue.push({ fn: done => getReportInternal(done) }); runQueue(); } function getReportInternal(done) { const req = http.request({ hostname: IP, port: 80, path: "/properties/report", method: "GET" }, res => { let data = ""; res.on("data", chunk => data += chunk); res.on("end", async () => { try { const json = JSON.parse(data); if (json.properties) { Object.keys(dpMap).forEach(dp => { const { key } = dpMap[dp]; if (json.properties[key] !== undefined) { setState(dp, json.properties[key], true); } }); } setState(dpTimestamp, formatTime(Math.floor(Date.now()/1000)), true); await processJson(json); } catch (e) { log("GET parse error: " + e, "info"); } done(); // WICHTIG: Queue freigeben nach Erfolg }); }); // Fehler req.on("error", err => { handleHttpError("GET Error", err, { timestamp: dpTimestamp }); done(); // WICHTIG: Queue freigeben bei Fehler }); // Timeout req.setTimeout(timeoutHttp, () => { req.destroy(); // Verbindung abbrechen handleHttpError("GET Timeout", "Gerät antwortet nicht", { timestamp: dpTimestamp }); done(); // WICHTIG: Queue freigeben bei Zeitüberschreitung }); req.end(); } //------------------------------------------------------------------------------------ // MQTT //------------------------------------------------------------------------------------ // Enable-Schalter nur Steuerung, kein POST on({ id: dpSetMqttEnable, ack: false }, obj => { const enable = !!obj.state.val; setState(dpSetMqttEnable, enable, true); if (enable) { // sofort GET curlQueue.push({ fn: d => getMqttStatusInternal(d) }); runQueue(); // Intervall start if (!mqttInterval) { mqttInterval = setInterval(() => { curlQueue.push({ fn: d => getMqttStatusInternal(d) }); runQueue(); }, intervalMqtt * 1000); } } else { if (mqttInterval) { clearInterval(mqttInterval); mqttInterval = null; } } }); function getMqttStatusInternal(done) { const req = http.request({ hostname: IP, port: 80, path: "/rpc?method=HA.Mqtt.GetStatus", method: "GET" }, res => { let data = ""; res.on("data", chunk => data += chunk); res.on("end", () => { try { const json = JSON.parse(data); const state = json.connected ? 1 : 0; // readonly setState(dpMqttConnectInfo, state, true); // sync setDP (OHNE trigger) setState(dpSetMqttConnect, state, true); } catch (e) { log("MQTT parse error: " + e, "info"); } done(); }); }); req.on("error", err => { handleHttpError("MQTT GET", err, { timestamp: dpTimestamp }); done(); }); req.end(); } function setMqttConnect(enable) { const payload = JSON.stringify({ sn: SN, method: "HA.Mqtt.SetConfig", params: { config: { enable: !!enable, server: mqttBrokerIp, port: mqttPort, protocol: "mqtt", username: mqttUsername, password: mqttPassword } } }); curlQueue.push({ fn: done => { const req = http.request({ hostname: IP, port: 80, path: "/rpc", method: "POST", headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) } }, res => { res.on("data", () => {}); res.on("end", () => done()); }); req.on("error", err => { handleHttpError("MQTT POST", err, { timestamp: dpTimestamp }); done(); }); req.write(payload); req.end(); } }); runQueue(); } //------------------------------------------------------------------------------------ // JSON //------------------------------------------------------------------------------------ async function processJson(obj) { try { if (!obj || !obj.product) return; const product = obj.product; const basePath = `${folderZendureApi}.${product}`; // create product folder if not exist if (!existsObject(basePath)) { setObject(basePath, { type: 'folder', common: { name: product }, native: {}, }); } // create main keys const mainKeys = ['timestamp', 'messageId', 'sn', 'version', 'product']; for (const key of mainKeys) { if (obj[key] !== undefined) { const statePath = `${basePath}.${key}`; if (existsState(statePath)) { setState(statePath, obj[key], true); } else { createState(statePath, obj[key], { name: key, type: typeof obj[key], role: 'info', read: true, write: false, }); } } } // properties if (obj.properties) { await iter(`${basePath}.properties`, obj.properties); } // PackData if (obj.packData && Array.isArray(obj.packData)) { await iter(`${basePath}`, { packData: obj.packData, timestamp: obj.timestamp }); } } catch (e) { log(`Error processing JSON: ${e}`, 'info'); } } //------------------------------------------------------------------------------------ // Battery //------------------------------------------------------------------------------------ function getBatteryType(sn, model) { let batType = ''; if (sn?.startsWith('A')) batType = 'AB1000'; else if (sn?.startsWith('B')) batType = 'AB1000S'; else if (sn?.startsWith('C')) batType = sn[3] === 'F' ? 'AB2000S' : sn[3] === 'E' ? 'AB2000X' : 'AB2000'; else if (sn?.startsWith('F')) batType = 'AB3000X'; if (model?.trim()) batType = model.trim(); return batType || 'unknown'; } //------------------------------------------------------------------------------------ // helper Iteration //------------------------------------------------------------------------------------ async function iter(id, obj) { try { if (!obj || typeof obj !== "object") { //log("iter: Ungültiges oder leeres Objekt übersprungen", "info"); return; } for (let i in obj) { if (i === 'packData' && Array.isArray(obj[i])) { const ts = obj.timestamp ? obj.timestamp * 1000 : Date.now(); for (const pack of obj[i]) { if (!pack.sn) continue; const sn = pack.sn; const path = `${id}.packData.${sn}`; if (!existsObject(path)) { setObject(path, { type: 'folder', common: { name: sn }, native: {}, }); } for (let [key, val] of Object.entries(pack)) { const statePath = `${path}.${key}`; switch (key) { case 'batcur': val = (val << 16 >> 16) / 10; if (existsState(statePath)) { setState(statePath, val, true);; } else { createState(statePath, val, { name: 'Battery Current', type: 'number', desc: 'battery current', role: 'value', read: true, write: false, unit: 'A', }); } break; case 'heatState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Heat State, 0: Not heating, 1: heating', type: 'number', role: 'value', read: true, write: false, }); } break; case 'maxTemp': val = (val - 2731) / 10; if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Highest Akku Temperature', type: 'number', desc: 'maximum temperature', role: 'value', read: true, write: false, unit: '°C', }); } break; case 'maxVol': val = val / 100; if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Highest Cell Voltage', type: 'number', desc: 'highest cell voltage', role: 'value', read: true, write: false, unit: 'V', }); } break; case 'minVol': val = val / 100; if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Lowest Cell Voltage', type: 'number', desc: 'lowest cell voltage', role: 'value', read: true, write: false, unit: 'V', }); } break; case 'packType': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'packType', type: 'number', role: 'value', read: true, write: false, }); } break; case 'power': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Battery Power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'sn': const batType = getBatteryType(val, pack.model); // Modelltyp als Datenpunkt setzen/erstellen const modelPath = `${path}.model`; if (existsState(modelPath)) { setState(modelPath, batType, true); } else { createState(modelPath, batType, { name: 'Battery Model', type: 'string', role: 'text', read: true, write: false, }); } break; case 'socLevel': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Battery SoC Level', type: 'number', role: 'value', read: true, write: false, unit: '%', }); } break; case 'softVersion': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Battery Software Version', type: 'number', role: 'value', read: true, write: false, }); } break; case 'state': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Battery, 0: Standby, 1: Charging, 2: Discharging', type: 'number', role: 'value', read: true, write: false, }); } break; case 'totalVol': val = val / 100; if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Total Voltage', type: 'number', desc: 'total voltage', role: 'value', read: true, write: false, unit: 'V', }); } break; default: if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: key, type: typeof val, role: 'value', read: true, write: false, }); } break; } } } } else { const ts = Date.now(); const statePath = `${id}.${i}`; let val = obj[i]; switch (i) { case 'BatVolt': val = val / 100; if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Battery Voltage', type: 'number', desc: 'battery voltage', role: 'value', read: true, write: false, unit: 'V', }); } break; case 'chargeMaxLimit': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'maximum Charge Power Limit', type: 'number', desc: 'Maximum permissible charging power', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'acMode': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'acMode, 1: input mode / 2: output mode', type: 'number', desc: '1: charging / 2: discharging', role: 'value', read: true, write: false, }); } break; case 'dataReady': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Data Ready, 0: Not ready, 1: Ready', type: 'number', role: 'value', read: true, write: false, }); } break; case 'dcStatus': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'dcStatus, 0: Stopped, 1: Battery input, 2: Battery output', type: 'number', role: 'value', read: true, write: false, }); } break; case 'electricLevel': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Total Battery Charge Level', type: 'number', desc: 'SoC', role: 'value', read: true, write: false, unit: '%', }); } break; case 'FMVolt': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Voltage activation value', type: 'number', desc: 'SoC', role: 'value', read: true, write: false, unit: 'V', }); } break; case 'gridInputPower': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Grid Input Power to Battery', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'gridReverse': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: '0: Disabled, 1: Allowed reverse flow, 2: Forbidden reverse flow', type: 'number', role: 'value', read: true, write: false, }); } break; case 'gridStandard': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Grid connection standard 0: Germany 1: France 2: Austria', type: 'number', role: 'value', read: true, write: false, }); } break; case 'gridState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Grid connection state, 0: Not connected, 1: Connected', type: 'number', role: 'value', read: true, write: false, }); } break; case 'heatState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Heat State, 0: Not heating, 1: heating', type: 'number', role: 'value', read: true, write: false, }); } break; case 'hyperTmp': val = (val - 2731) / 10.0; if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Enclosure Temperature', type: 'number', role: 'value', read: true, write: false, unit: '°C', }); } break; case 'inputLimit': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'AC charging power limit to Battery', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'inverseMaxPower': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Max inverter output Power Limit', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'minSoc': val = val / 10; if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'min SoC', type: 'number', desc: 'minimum Battery SoCset', role: 'value', read: true, write: false, unit: '%', }); } break; case 'outputHomePower': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'outputHomePower', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'outputLimit': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Output power limit', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'outputPackPower': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Output power to battery pack (charging)', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'packInputPower': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Battery pack input power (discharging)', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'packNum': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Number of battery packs', type: 'number', role: 'value', read: true, write: false, }); } break; case 'packState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Battery State, 0: Standby, 1: Charging, 2: Discharging', type: 'number', role: 'value', read: true, write: false, }); } break; case 'pass': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Bypass, 0: No, 1: Yes', type: 'number', role: 'value', read: true, write: false, }); } break; case 'pvStatus': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'PV State producing, 0: Stopped, 1: Running', type: 'number', role: 'value', read: true, write: false, }); } break; case 'acStatus': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'AC state 0-2', type: 'number', role: 'value', read: true, write: false, }); } break; case 'remainOutTime': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Estimated discharge time in minutes, if not predictable: 59940', type: 'number', role: 'value', read: true, write: false, unit: 'min', }); } break; case 'reverseState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Reverse flow, 0: No, 1: Reverse flow', type: 'number', role: 'value', read: true, write: false, }); } break; case 'rssi': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Received Signal Strength Indicator', type: 'number', role: 'value', read: true, write: false, unit: 'dBm', }); } break; case 'gridOffPower': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Off-grid power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'lampSwitch': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Lamp state 0:off 1:on', type: 'number', role: 'value', read: true, write: false, }); } break; case 'gridOffMode': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Off-grid mode', type: 'number', role: 'value', read: true, write: false, }); } break; case 'IOTState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'IoT connection', type: 'number', role: 'value', read: true, write: false, }); } break; case 'fanSwitch': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Fan state 0:off 1:on', type: 'number', role: 'value', read: true, write: false, }); } break; case 'fanSpeed': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Fan level', type: 'number', role: 'value', read: true, write: false, }); } break; case 'faultLevel': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Fault severity', type: 'number', role: 'value', read: true, write: false, }); } break; case 'bindstate': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Bind state', type: 'number', role: 'value', read: true, write: false, }); } break; case 'VoltWakeup': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Voltage wake-up', type: 'number', role: 'value', read: true, write: false, }); } break; case 'OldMode': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Legacy mode', type: 'number', role: 'value', read: true, write: false, }); } break; case 'OTAState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'OTA state', type: 'number', role: 'value', read: true, write: false, }); } break; case 'LCNState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'LCN state', type: 'number', role: 'value', read: true, write: false, }); } break; case 'factoryModeState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Phase switch', type: 'number', role: 'value', read: true, write: false, }); } break; case 'phaseSwitch': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Factory mode', type: 'number', role: 'value', read: true, write: false, }); } break; case 'is_error': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Error flag 0: no Error 1: Error', type: 'number', role: 'value', read: true, write: false, }); } break; case 'smartMode': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: '1: parameter written to RAM / 0: parameter is written to flash.', type: 'number', role: 'value', read: true, write: false, }); } break; case 'socLimit': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'socLimit-Info 0: normal, 1: Charge limit reached, 2: Discharge limit reached', type: 'number', role: 'value', read: true, write: false, }); } break; case 'socSet': val = val / 10; if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'max SoC', type: 'number', desc: 'maximum Battery SoCset', role: 'value', read: true, write: false, unit: '%', }); } break; case 'socStatus': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Info of Calibrating, 0: No / 1: Calibrating', type: 'number', role: 'value', read: true, write: false, }); } break; case 'solarInputPower': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Total Solar Input Power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'solarPower1': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Solar line 1 input power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'solarPower2': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Solar line 2 input power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'solarPower3': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Solar line 3 input power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'solarPower4': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Solar line 4 input power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'solarPower5': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Solar line 5 input power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'solarPower6': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Solar line 6 input power', type: 'number', role: 'value', read: true, write: false, unit: 'W', }); } break; case 'timeZone': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Timezone', type: 'number', role: 'value', read: true, write: false, }); } break; case 'tsZone': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Timezone offset', type: 'number', role: 'value', read: true, write: false, }); } break; case 'acCouplingState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'AC Coupling State', type: 'number', role: 'value', read: true, write: false }); } let states = []; if (val & (1 << 0)) states.push("AC-coupled input present"); if (val & (1 << 1)) states.push("AC input present flag"); if (val & (1 << 2)) states.push("AC-coupled overload"); if (val & (1 << 3)) states.push("Excess AC input power"); const statusText = states.length > 0 ? states.join(", ") : "Normal / No Flags"; const stringPath = statePath + "_String"; // acCouplingState_String if (existsState(stringPath)) { setState(stringPath, statusText, true); } else { createState(stringPath, statusText, { name: 'AC Coupling State Info', type: 'string', role: 'text', read: true, write: false }); } break; case 'dryNodeState': if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: 'Dry contact status 1: Connected 0: Connected(May be reversed depending on actual wiring)', type: 'number', role: 'value', read: true, write: false, }); } break; case 'timestamp': const timestampVal = obj[i]; const timestampFormatted = formatTime(timestampVal); if (existsState(statePath)) setState(statePath, timestampVal, true); else createState(statePath, timestampVal, { name: 'System timestamp', type: 'number', desc: 'unix timestamp in Seconds since Jan 01 1970 (UTC)', role: 'value', read: true, write: false, }); if (existsState(id + '.timeUpdateTimestamp')) setState(id + '.timeUpdateTimestamp', timestampFormatted, true); else createState(id + '.timeUpdateTimestamp', timestampFormatted, { name: 'TimeUpdate (timestamp)', type: 'string', desc: 'unix timestamp in readable Format (timestamp)', read: true, write: false, }); break; case 'ts': const tsVal = obj[i]; const tsFormatted = formatTime(tsVal); if (existsState(statePath)) setState(statePath, tsVal, true); else createState(statePath, tsVal, { name: 'Unix timestamp (ts)', type: 'number', desc: 'unix timestamp in Seconds since Jan 01 1970 (UTC)', role: 'value', read: true, write: false, }); if (existsState(id + '.timeUpdateTs')) setState(id + '.timeUpdateTs', tsFormatted, true); else createState(id + '.timeUpdateTs', tsFormatted, { name: 'TimeUpdate (ts)', type: 'string', desc: 'unix timestamp in readable Format (ts)', read: true, write: false, }); break; default: if (existsState(statePath)) { setState(statePath, val, true); } else { createState(statePath, val, { name: i, type: typeof val, role: 'value', read: true, write: false, }); } break; } } } } catch (e) { log(`Error in iter: ${e}`, 'info'); } } Euch allen viel Sonne und weiterhin viel Freude.
  • ioBroker Prozess- & Gesundheitsmonitor + Grafana Dashboard

    javascript monitoring
    30
    4
    3 Stimmen
    30 Beiträge
    813 Aufrufe
    crunchipC
    Dashboard für Flux oben im ersten Beitrag ergänzt, bitte testen
  • Shelly Verbrauchs-Historie für Tag, Woche, Monat, Jahr

    144
    1
    1 Stimmen
    144 Beiträge
    26k Aufrufe
    crunchipC
    @micklafisch sagte in Shelly Verbrauchs-Historie für Tag, Woche, Monat, Jahr: von daher die offene Frage ob ich hier etwas vielleicht übersehen habe ne, nichts übersehen, außer wenn du die Datenpunkte schon bereits irgendwo in eine Datenbank schreibst, diese zu mappen
  • Skript aufteilen möglich?

    20
    0 Stimmen
    20 Beiträge
    624 Aufrufe
    OliverIOO
    @ple Du könntest ein Service Script laufen lassen, das alles mit supabase macht, also anmelden, abrufen, schreiben etc. So wie @ticaki oben vorgeschlagen hat horcht dieses Skript dann auf bestimmte befehlsworte, führt die jeweilige Aktion aus und gibt die Daten zurück, ohne das sichnjedes einzelne Skripte um die supabase Details kümmern muss. Klar kann man auch einzelne Programmatiken lokal ablegen und auch einbinden. Da gibt es hier im Forum Beispiele dazu. Müsste ich suchen wenn das das Ziel wäre. Dann könnte man noch ein öffentliches npm Paket machen, das finde ich nicht so optimal, theoretisch auch ein privates Paket, das habe ich noch nie ausprobiert. Wenn ich etwas umfangreicheres benötige, mit mehreren Dateien, dann würde ich lieber einen eigenen Adapter machen, da hat man dann alle Möglichkeiten.
  • [TypeSkript] Wetter.com Forecast/Vorhersage

    62
    4 Stimmen
    62 Beiträge
    3k Aufrufe
    nik82N
    @schimi So sorry, da hät ich natürlich selber drauf kommen können das ich den Datenpunkt auf 0 setze. Jetzt gehts wieder. Danke dir. :-)
  • [Vorlage] Denon HEOS Script

    Verschoben javascript template
    359
    1
    1 Stimmen
    359 Beiträge
    88k Aufrufe
    S
    Hi zusammen! Ich nutze einen Denon und mir fehlt der Ordner heos.0.sources.1028... Demnach kann ich die Favoriten auch nicht ansteuern. Mit dem Adapter soll das Skript aus dem ersten Post doch überflüssig sein, oder?
  • [Skript] Absolute Feuchte berechnen

    Verschoben
    741
    1
    2 Stimmen
    741 Beiträge
    243k Aufrufe
    C
    Ich bin grade total begeistert, hab die Version 0.6.8 grade kopiert, meine Werte eingefügt, und es läuft auf Anhieb! Alles was ich brauche :-) Hab in der Mietwohnung unten nach neuen Fenstern an den Kältebrücken leichte Schimmelprobleme, um dem zu begegnen gibt es jetzt a) eine Lüftungsanlage und b) in den Gefährdeten Bereichen eine kleine Heizung im Putz um die Temperatur dort über dem Taupunkt zu halten. Das Script liefert mir nun alle Werte die ich brauche, Mega!! Wem Schuld ich denn nun den Kaffee, ich mein... an dem Script schreiben mehrere?
  • [TypeSkript] Zendure SolarFlow 2400 AC - EVCC Steuerung

    65
    1 Stimmen
    65 Beiträge
    4k Aufrufe
    portyP
    Guten Morgen @schimi wie bereits im EDIT erwähnt funktionierte es ab dem nächsten Morgen doch. HEMS war deaktivert und auch die anderen Settings (SmartMode Off etc ) waren so eingestellt wie du es mal in irgend einem Post mal erwähnt hattest. Device ID etc war natürlich drin, sieht man in meinem Bild schlecht da schwarz auf schwarz ausgeblendet :) wir waren unterwegs an dem Tag und ich hatte es nur in der App gesehen das der Modus auf "Standby" stand obwohl wir zu dem Zeitpunkt einen Verbrauch von ca. 450W hatten. Habe dann in der App auf "Netzausgangsmodus" gestellt. Am nächsten Morgen dann das Script gestoppt, den Modus von Hand über den MQTT-Datenpunkt geändert was auch funktionierte. Was ich nicht drin hatte da es als "optional" eingetragen war, war der Datenpunkt "evccModus: "0_userdata.0.zendure.EVCC_Modus", diesen hatte ich noch hinzugefügt. Danach mal das Script von @maxclaudi gestartet und den Modus darüber ungestellt, auch erfolgreich. Gestern hat es mit deinem Script jedenfalls wunderbar funkioniert, wobei ich auch schon überlegt habe nich auf 0 sondern eher auf Minimalbezug zu stellen. Habe den Aktuellen Stromverbrauch mal auf meine Ulanzi TC001 gelegt und das springt nicht schlecht :) Danke fürs Update von deinem Script, schaue ich mir nachher Zuhause mal an.
  • Json für SourceAnalytics

    83
    1
    4 Stimmen
    83 Beiträge
    10k Aufrufe
    H
    @crunchip Passt, vielen Dank...
  • Skript zur Darstellung Position Sonne / Mond mit Schatten

    3
    0 Stimmen
    3 Beiträge
    136 Aufrufe
    bahnuhrB
    @MarkA sagte in Skript zur Darstellung Position Sonne / Mond mit Schatten: Kann das auch für IOBroker implementiert werden? Mir fehlen da leider die Programmierfähigkeiten... Dann musste mal die Suche benutzen. Scripte diesbezüglich wurden hier schon mehrfach kundgetan. Und in VIS dann z.B. so: [image: 1773248490490-cbdbeb92-2725-4b5a-860f-caf7ac2b2265-image.png]
  • Script für Formel 1 - Kalender, Ergebnisse und Wertungen

    40
    7
    5 Stimmen
    40 Beiträge
    5k Aufrufe
    da_WoodyD
    @stenmic sagte in Script für Formel 1 - Kalender, Ergebnisse und Wertungen: Eigentlich sollte alles fehlerfrei funktionieren aber eventuell muss ich nächste Saison nachbessern falls er zu Fehlermeldungen wegen dem Jahr kommt. Dass kann ich jetzt noch nicht wirklich alles prüfen. tja, neues jahr, neues glück. ;) rennkalender 2026 klappt. rennen 1 sieht etwas dubios aus. fahrer und konstrukteur wertung klapp nicht mehr...

257

Online

32.8k

Benutzer

82.7k

Themen

1.3m

Beiträge