<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm]]></title><description><![CDATA[<p dir="auto">Nachdem der Flot-Adapter leider nicht mehr neu installiert werden kann, suche ich für folgendes Problem eine Lösung, am liebsten mit dem Echarts-Adapter. Ich möchte einen oder mehrere Datenpunkte mit verschiedenen Start und Endzeiten in einem Diagramm darstellen.<br />
Dazu ein Beispiel:<br />
<img src="/assets/uploads/files/1776337910645-2026_03_19_10.19.06-2-resized.jpg" alt="2026_03_19_10.19.06~2.jpg" class=" img-fluid img-markdown" /><br />
Die Grafik zeigt den Temperaturverlauf in meinem Pufferspeicher aktuell im Vergleich mit 24 Stunden zuvor. Das bekomme ich mit ECharts nicht hin. Man kann zwar ein X-Offset angeben, aber die Line wird dann einfach vorne platziert und nicht darunter wie das bei Flots war. Hat jemand dafür eine Lösung?  Danke im voraus.</p>
]]></description><link>https://forum.iobroker.net/topic/84324/datenpunkt-mit-start-endzeit-in-einem-diagramm</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 11:34:54 GMT</lastBuildDate><atom:link href="https://forum.iobroker.net/topic/84324.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 16 Apr 2026 11:21:30 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 16:03:32 GMT]]></title><description><![CDATA[<blockquote>
<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mcu" aria-label="Profile: MCU">@<bdi>MCU</bdi></a> <a href="/post/1335600">sagte</a>:</p>
<p dir="auto">Man kann es auch so machen,. dass man die DPs (vordefiniert im DP als Array) im Diagramm auswählen kann und dann die Diagramme angezeigt werden zum DP.</p>
</blockquote>
<p dir="auto">Das klingt für die Zukunft sehr interessant. Erstmal komme ich klar für das was mir im Moment vorschwebt. Ich habe noch ein anders Diagramm angelegt und in ein VIS 1 eingebaut, schaut ganz gut aus, denke ich 😀</p>
<p dir="auto"><img src="/assets/uploads/files/1776700951010-1000019522.jpg" alt="1000019522.jpg" class=" img-fluid img-markdown" /></p>
<p dir="auto">Ich melde mich wieder wenn ich weitere Unterstützung benötige.</p>
]]></description><link>https://forum.iobroker.net/post/1335614</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335614</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Mon, 20 Apr 2026 16:03:32 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 15:31:33 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a> Ja momentan ist das so. Wie viele Diagramme möchtest du denn haben?<br />
Alle mit den gleichen Parametern? Also heute und gestern</p>
<p dir="auto">Man kann das Programm auch anpassen für mehrere DPs und andere Optionen. man muss halt wissen, was du, wie brauchst.</p>
<p dir="auto">Man kann es auch so machen,. dass man die DPs (vordefiniert im DP als Array) im Diagramm auswählen kann und dann die Diagramme angezeigt werden zum DP.</p>
]]></description><link>https://forum.iobroker.net/post/1335600</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335600</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Mon, 20 Apr 2026 15:31:33 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 14:45:59 GMT]]></title><description><![CDATA[<p dir="auto">Ok, ich glaube ich komme der Sache näher. Habe das Script kopiert und testweise einen Parameter (Zeitraum) geändert. Hat funktioniert. <img src="/assets/uploads/files/1776696282926-1000019520-resized.jpg" alt="1000019520.jpg" class=" img-fluid img-markdown" />Morgen mach ich weiter. Vielen Dank für deine Unterstützung.</p>
]]></description><link>https://forum.iobroker.net/post/1335590</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335590</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Mon, 20 Apr 2026 14:45:59 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 14:03:27 GMT]]></title><description><![CDATA[<p dir="auto">Wow, fantastisch 😀, hat funktioniert. Was ich jetzt noch nicht verstanden habe, muss man für jedes neue Diagramm ein neues Script anlegen und entsprechend anpassen?</p>
]]></description><link>https://forum.iobroker.net/post/1335576</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335576</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Mon, 20 Apr 2026 14:03:27 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 14:00:13 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a> Weiter gekommen?</p>
]]></description><link>https://forum.iobroker.net/post/1335574</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335574</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Mon, 20 Apr 2026 14:00:13 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 13:24:11 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a> Du hast doch jetzt DPs erhalten.<br />
Im DP <strong>0_userdata.0.flexCharts.compareDays.dpId</strong> gibst du Deine ID ein, von der du jetzt die unterschiedlichen History-Werte vergleichen willst.</p>
<p dir="auto">Im DP <strong>0_userdata.0.flexCharts.compareDays.historyInstance</strong> gibst du die Instanz ein, mit der du die History speicherst. also history.0 oder sql.0 oder influxdb.0</p>
<p dir="auto">Im DP <strong>0_userdata.0.flexCharts.compareDays.urlBase</strong> musst du die ioBroker-Server-IP und den Port evtl anpassen. (http / https Protocoll)?</p>
<p dir="auto">Im DP <strong>0_userdata.0.flexCharts.compareDays.compareDays</strong> gibst du kommagetrennt an, welche Tage du mit heute vergleichen willst. Gestern -&gt; 1, vorgestern -&gt; 2 usw.<br />
willst du mehrere Kurven sehen, dann 1,2,3 usw.</p>
<p dir="auto">Wenn das alles passt hast du im DP <strong>0_userdata.0.flexCharts.compareDays.generatedUrl</strong> jetzt eine URL die du direkt im Browser nutzen kannst oder als iFrame-Url nutzt.</p>
]]></description><link>https://forum.iobroker.net/post/1335567</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335567</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Mon, 20 Apr 2026 13:24:11 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 13:10:56 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mcu" aria-label="Profile: mcu">@<bdi>mcu</bdi></a> Wirklich weiter komme auch jetzt nicht. Ich sehe keine Möglichkeit Diagramme zu erstellen bzw. zu ändern. Vielleicht kannst du mir da nochmal auf die Sprünge helfen.</p>
]]></description><link>https://forum.iobroker.net/post/1335564</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335564</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Mon, 20 Apr 2026 13:10:56 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 09:19:51 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mcu" aria-label="Profile: MCU">@<bdi>MCU</bdi></a> Jetzt kommen keine Fehlermeldungen mehr 😀</p>
<pre><code>javascript.0	11:17:31.101	info	
Stopping script
javascript.0	11:17:31.109	info	
start JavaScript (Javascript/js)
javascript.0	11:17:31.114	info	
flexCharts Compare24Times gestartet. messageName=puffervergleich
javascript.0	11:17:31.114	info	
registered 7 subscriptions, 0 schedules, 1 message, 0 logs and 0 file subscriptions
</code></pre>
]]></description><link>https://forum.iobroker.net/post/1335510</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335510</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Mon, 20 Apr 2026 09:19:51 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 09:06:42 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a> Bei dir ist noch eine Zeile drin</p>
<pre><code>log(`registered....
</code></pre>
<p dir="auto">Diese löschen.<br />
Am Besten das Skript neu laden.<br />
<a href="https://mcuiobroker.gitbook.io/iobroker-tipps/tipps/scripte-blockly-javascript/flexcharts/beispiel-comparedays/javascript-comparedays" rel="nofollow ugc">https://mcuiobroker.gitbook.io/iobroker-tipps/tipps/scripte-blockly-javascript/flexcharts/beispiel-comparedays/javascript-comparedays</a></p>
]]></description><link>https://forum.iobroker.net/post/1335509</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335509</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Mon, 20 Apr 2026 09:06:42 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 08:59:13 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mcu" aria-label="Profile: MCU">@<bdi>MCU</bdi></a> habe die # entfernt, aber jetzt schaut es so aus:<br />
<img src="/assets/uploads/files/1776675387335-1000019516-resized.jpg" alt="1000019516.jpg" class=" img-fluid img-markdown" /></p>
<pre><code>javascript.0	10:49:06.505	info	
Stopping script
javascript.0	10:49:06.512	info	
start JavaScript (Javascript/js)
javascript.0	10:49:06.515	error	
compile failed at: script.js.common.Echarts.javascript_compareDays:1207
javascript.0	10:49:06.515	error	
log(`registered ${__engine.__subscriptions} subscription${__engine.__subscriptions === 1 ? '' : 's'}, ${__engine.__schedules} schedule${__engine.__schedules === 1 ? '' : 's'}, ${__engine.__subscriptionsMessage} message${__engine.__subscriptionsMessage === 1 ? '' : 's'}, ${__engine.__subscriptionsLog} log${__engine.__subscriptionsLog === 1 ? '' : 's'} and ${__engine.__subscriptionsFile} file subscription${__engine.__subscriptionsFile === 1 ? '' : 's'}`);
javascript.0	10:49:06.515	error	
     ^^^^^^^^^^
javascript.0	10:49:06.515	error	
SyntaxError: Unexpected identifier 'registered'
javascript.0	10:49:06.515	error	
    at new Script (node:vm:117:7)
javascript.0	10:49:06.516	error	
    at JavaScript.createVM (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:2179:25)
javascript.0	10:49:06.516	error	
    at JavaScript.prepareScript (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:2481:44)
javascript.0	10:49:06.516	error	
    at processTicksAndRejections (node:internal/process/task_queues:103:5)
</code></pre>
<p dir="auto">PS: Danke auch an <a class="plugin-mentions-user plugin-mentions-a" href="/user/paul53" aria-label="Profile: paul53">@<bdi>paul53</bdi></a>  fürs mit schauen.</p>
]]></description><link>https://forum.iobroker.net/post/1335508</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335508</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Mon, 20 Apr 2026 08:59:13 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 08:35:40 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/paul53" aria-label="Profile: paul53">@<bdi>paul53</bdi></a> Stimmt.</p>
]]></description><link>https://forum.iobroker.net/post/1335498</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335498</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Mon, 20 Apr 2026 08:35:40 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 08:35:41 GMT]]></title><description><![CDATA[<blockquote>
<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mcu" aria-label="Profile: MCU">@<bdi>MCU</bdi></a> [sagte]: Im Ordner Allgemeine Skripte darf nur etwas stehen , was alle Skripte nutzen.</p>
</blockquote>
<p dir="auto">Nein, das trifft nur auf den Ordner "Globale Skripte (global)" zu.<br />
Unter "Allgemeine Skripte" können beliebige Skripte stehen.</p>
]]></description><link>https://forum.iobroker.net/post/1335497</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335497</guid><dc:creator><![CDATA[paul53]]></dc:creator><pubDate>Mon, 20 Apr 2026 08:35:41 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 08:40:18 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a> I<s>m Ordner Allgemeine Skripte darf nur etwas stehen , was alle Skripte nutzen. Also dort das compareDays löschen.</s></p>
<p dir="auto"><s>Du musst einen Ordner nicht im Allgemeine Scripte erstellen.</s><br />
<img src="/assets/uploads/files/1776673681675-9299ca0d-998c-4d38-945f-5b4cc61a492f-image.jpeg" alt="9299ca0d-998c-4d38-945f-5b4cc61a492f-image.jpeg" class=" img-fluid img-markdown" /><br />
Im Skript selbst darf kein # stehen.</p>
<p dir="auto"><img src="/assets/uploads/files/1776673802018-ab5da5a8-852b-4944-a921-595f7d80b072-image.jpeg" alt="ab5da5a8-852b-4944-a921-595f7d80b072-image.jpeg" class=" img-fluid img-markdown" /></p>
]]></description><link>https://forum.iobroker.net/post/1335495</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335495</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Mon, 20 Apr 2026 08:40:18 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 08:25:31 GMT]]></title><description><![CDATA[<p dir="auto">Unter "Allgemeine Scripte" habe ich einen Ordner "Echarts" angelegt. Darin das Script "javascript compareDays"</p>
<pre><code># javascript compareDays

## javascript compareDays

```js
/***************************************************************
 * compareDays v1.0.0 
 * Copyright ©MCU
 ***************************************************************/

const CONFIG_BASE = '0_userdata.0.flexCharts.Compare24Times';

const DEFAULT_CURRENT_STYLE = {
    type: 'line',
    color: '',
    lineStyle: 'solid',
    lineWidth: 2,
    area: false,
    smooth: false,
    symbol: 'none'
};

const DEFAULT_COMPARE_STYLE = {
    type: 'line',
    color: '',
    lineStyle: 'solid',
    lineWidth: 2,
    area: false,
    smooth: false,
    symbol: 'none'
};

const DEFAULT_DELTA_STYLE = {
    type: 'bar',
    color: '',
    lineStyle: 'solid',
    lineWidth: 1,
    area: false,
    smooth: false,
    symbol: 'none',
    yAxisIndex: 1
};

const DEFAULT_COMPARE_DAYS = [1, 2, 3];

const DEFAULTS = {
    messageName: 'puffervergleich',
    historyInstance: 'history.0',
    dpId: '',
    windowHours: 24,
    compareDays: DEFAULT_COMPARE_DAYS.join(','),
    maxCount: 2000,
    fallbackUnit: '°C',
    chartTitle: 'Pufferspeicher: aktuell vs. Vortage',
    valueName: 'Temperatur',
    urlBase: 'http://192.168.178.150:8082/flexcharts/echarts.html',
    urlRefresh: 60,
    generatedUrl: '',
    deltaEnabled: false,
    deltaSourceA: 'current',
    deltaSourceB: 'day1',
    deltaAreaEnabled: false,
    deltaSmooth: false,
    seriesConfig: JSON.stringify(buildDefaultSeriesConfigObject(DEFAULT_COMPARE_DAYS), null, 2)
};

let restartPending = false;
let syncPending = false;

start();

function start() {
    ensureConfigStates(() =&gt; {
        refreshDeltaSourceStates();
        synchronizeSeriesConfigState();
        updateGeneratedUrl();

        const config = readConfig();
        log('flexCharts Compare24Times gestartet. messageName=' + config.messageName, 'info');

        onMessage(config.messageName, (httpParams, callback) =&gt; {
            createComparisonChart(callback);
        });

        on({ id: CONFIG_BASE + '.compareDays', ack: false, change: 'ne' }, () =&gt; {
            refreshDeltaSourceStates();
            synchronizeSeriesConfigState();
            updateGeneratedUrl();
        });

        on({ id: CONFIG_BASE + '.seriesConfig', ack: false, change: 'ne' }, () =&gt; {
            synchronizeSeriesConfigState();
        });

        on({ id: CONFIG_BASE + '.deltaSourceA', ack: false, change: 'ne' }, () =&gt; {
            refreshDeltaSourceStates();
        });

        on({ id: CONFIG_BASE + '.deltaSourceB', ack: false, change: 'ne' }, () =&gt; {
            refreshDeltaSourceStates();
        });

        on({ id: CONFIG_BASE + '.urlBase', ack: false, change: 'ne' }, () =&gt; {
            updateGeneratedUrl();
        });

        on({ id: CONFIG_BASE + '.urlRefresh', ack: false, change: 'ne' }, () =&gt; {
            updateGeneratedUrl();
        });

        on({ id: CONFIG_BASE + '.messageName', ack: false, change: 'ne' }, obj =&gt; {
            if (restartPending) return;

            const newValue = obj &amp;&amp; obj.state ? String(obj.state.val || '').trim() : '';
            updateGeneratedUrl();

            if (!newValue) {
                log('messageName ist leer, Script wird nicht neu gestartet', 'warn');
                return;
            }

            restartPending = true;
            log('messageName geändert auf "' + newValue + '" -&gt; Script wird neu gestartet', 'info');

            setTimeout(() =&gt; {
                runScript(scriptName);
            }, 200);
        });
    });
}

function ensureConfigStates(done) {
    const defs = [
        {
            key: 'messageName',
            def: DEFAULTS.messageName,
            common: {
                name: 'flexCharts Message-Name',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'historyInstance',
            def: DEFAULTS.historyInstance,
            common: {
                name: 'History-Instanz',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'dpId',
            def: DEFAULTS.dpId,
            common: {
                name: 'Datenpunkt-ID',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'windowHours',
            def: DEFAULTS.windowHours,
            common: {
                name: 'Sichtbarer Zeitraum in Stunden',
                type: 'number',
                role: 'value',
                read: true,
                write: true
            }
        },
        {
            key: 'compareDays',
            def: DEFAULTS.compareDays,
            common: {
                name: 'Vergleichstage, z.B. 1,2,3',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'maxCount',
            def: DEFAULTS.maxCount,
            common: {
                name: 'Maximale Anzahl History-Werte',
                type: 'number',
                role: 'value',
                read: true,
                write: true
            }
        },
        {
            key: 'fallbackUnit',
            def: DEFAULTS.fallbackUnit,
            common: {
                name: 'Fallback-Einheit',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'chartTitle',
            def: DEFAULTS.chartTitle,
            common: {
                name: 'Chart-Titel',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'valueName',
            def: DEFAULTS.valueName,
            common: {
                name: 'Name der Messgröße',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'urlBase',
            def: DEFAULTS.urlBase,
            common: {
                name: 'Basis-URL für flexCharts',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'urlRefresh',
            def: DEFAULTS.urlRefresh,
            common: {
                name: 'Refresh in Sekunden',
                type: 'number',
                role: 'value',
                read: true,
                write: true
            }
        },
        {
            key: 'generatedUrl',
            def: DEFAULTS.generatedUrl,
            common: {
                name: 'Generierte flexCharts-URL',
                type: 'string',
                role: 'text',
                read: true,
                write: false
            }
        },
        {
            key: 'deltaEnabled',
            def: DEFAULTS.deltaEnabled,
            common: {
                name: 'Delta aktiv',
                type: 'boolean',
                role: 'switch.enable',
                read: true,
                write: true
            }
        },
        {
            key: 'deltaSourceA',
            def: DEFAULTS.deltaSourceA,
            common: {
                name: 'Delta Quelle A',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'deltaSourceB',
            def: DEFAULTS.deltaSourceB,
            common: {
                name: 'Delta Quelle B',
                type: 'string',
                role: 'text',
                read: true,
                write: true
            }
        },
        {
            key: 'deltaAreaEnabled',
            def: DEFAULTS.deltaAreaEnabled,
            common: {
                name: 'Delta als Fläche',
                type: 'boolean',
                role: 'switch.enable',
                read: true,
                write: true
            }
        },
        {
            key: 'deltaSmooth',
            def: DEFAULTS.deltaSmooth,
            common: {
                name: 'Delta geglättet',
                type: 'boolean',
                role: 'switch.enable',
                read: true,
                write: true
            }
        },
        {
            key: 'seriesConfig',
            def: DEFAULTS.seriesConfig,
            common: {
                name: 'Serien-Konfiguration als JSON',
                type: 'string',
                role: 'json',
                read: true,
                write: true
            }
        }
    ];

    let index = 0;

    function next() {
        if (index &gt;= defs.length) {
            done();
            return;
        }

        const item = defs[index++];
        const id = CONFIG_BASE + '.' + item.key;

        if (existsState(id)) {
            next();
            return;
        }

        createState(id, item.def, false, item.common, {}, () =&gt; {
            next();
        });
    }

    next();
}

function refreshDeltaSourceStates() {
    const compareDays = parseCompareDays(readString(CONFIG_BASE + '.compareDays', DEFAULTS.compareDays));
    const statesMap = buildDeltaSourceStatesMap(compareDays);

    try {
        extendObject(CONFIG_BASE + '.deltaSourceA', {
            common: {
                states: statesMap
            }
        });
    } catch (e) {
        log('deltaSourceA common.states konnte nicht aktualisiert werden: ' + e.message, 'warn');
    }

    try {
        extendObject(CONFIG_BASE + '.deltaSourceB', {
            common: {
                states: statesMap
            }
        });
    } catch (e) {
        log('deltaSourceB common.states konnte nicht aktualisiert werden: ' + e.message, 'warn');
    }

    ensureValidDeltaSources(statesMap, compareDays);
}

function buildDeltaSourceStatesMap(compareDays) {
    const map = {
        current: 'Aktuell'
    };

    for (let i = 0; i &lt; compareDays.length; i++) {
        const day = compareDays[i];
        map['day' + day] = 'Vor ' + day + ' Tag' + (day &gt; 1 ? 'en' : '');
    }

    return map;
}

function ensureValidDeltaSources(statesMap, compareDays) {
    const validKeys = Object.keys(statesMap);

    let sourceA = normalizeSeriesRef(readString(CONFIG_BASE + '.deltaSourceA', DEFAULTS.deltaSourceA));
    let sourceB = normalizeSeriesRef(readString(CONFIG_BASE + '.deltaSourceB', DEFAULTS.deltaSourceB));

    if (!validKeys.includes(sourceA)) {
        sourceA = 'current';
        setState(CONFIG_BASE + '.deltaSourceA', sourceA, true);
    }

    if (!validKeys.includes(sourceB)) {
        sourceB = compareDays.length ? ('day' + compareDays[0]) : 'current';
        setState(CONFIG_BASE + '.deltaSourceB', sourceB, true);
    }

    if (sourceA === sourceB) {
        if (sourceA !== 'current') {
            sourceA = 'current';
            setState(CONFIG_BASE + '.deltaSourceA', sourceA, true);
        } else if (compareDays.length) {
            sourceB = 'day' + compareDays[0];
            setState(CONFIG_BASE + '.deltaSourceB', sourceB, true);
        }
    }
}

function synchronizeSeriesConfigState() {
    if (syncPending) return;
    syncPending = true;

    setTimeout(() =&gt; {
        try {
            const compareDays = parseCompareDays(readString(CONFIG_BASE + '.compareDays', DEFAULTS.compareDays));
            const currentConfig = readSeriesConfig(compareDays);
            saveSeriesConfigIfChanged(currentConfig);
        } finally {
            syncPending = false;
        }
    }, 50);
}

function readConfig() {
    const compareDays = parseCompareDays(readString(CONFIG_BASE + '.compareDays', DEFAULTS.compareDays));
    const seriesConfig = readSeriesConfig(compareDays);

    return {
        messageName: readString(CONFIG_BASE + '.messageName', DEFAULTS.messageName),
        historyInstance: readString(CONFIG_BASE + '.historyInstance', DEFAULTS.historyInstance),
        dpId: readString(CONFIG_BASE + '.dpId', DEFAULTS.dpId),
        windowHours: Math.max(1, readNumber(CONFIG_BASE + '.windowHours', DEFAULTS.windowHours)),
        compareDays: compareDays,
        maxCount: Math.max(100, readNumber(CONFIG_BASE + '.maxCount', DEFAULTS.maxCount)),
        fallbackUnit: readString(CONFIG_BASE + '.fallbackUnit', DEFAULTS.fallbackUnit),
        chartTitle: readString(CONFIG_BASE + '.chartTitle', DEFAULTS.chartTitle),
        valueName: readString(CONFIG_BASE + '.valueName', DEFAULTS.valueName),
        urlBase: readString(CONFIG_BASE + '.urlBase', DEFAULTS.urlBase),
        urlRefresh: Math.max(0, readNumber(CONFIG_BASE + '.urlRefresh', DEFAULTS.urlRefresh)),
        deltaEnabled: readBoolean(CONFIG_BASE + '.deltaEnabled', DEFAULTS.deltaEnabled),
        deltaSourceA: normalizeSeriesRef(readString(CONFIG_BASE + '.deltaSourceA', DEFAULTS.deltaSourceA)),
        deltaSourceB: normalizeSeriesRef(readString(CONFIG_BASE + '.deltaSourceB', DEFAULTS.deltaSourceB)),
        deltaAreaEnabled: readBoolean(CONFIG_BASE + '.deltaAreaEnabled', DEFAULTS.deltaAreaEnabled),
        deltaSmooth: readBoolean(CONFIG_BASE + '.deltaSmooth', DEFAULTS.deltaSmooth),
        seriesConfig: seriesConfig
    };
}

function readSeriesConfig(compareDays) {
    const id = CONFIG_BASE + '.seriesConfig';
    const fallbackRaw = JSON.stringify(buildDefaultSeriesConfigObject(compareDays), null, 2);
    const raw = readString(id, fallbackRaw);

    let parsed;
    try {
        parsed = JSON.parse(raw);
    } catch (e) {
        log('seriesConfig JSON ungültig, setze Default zurück: ' + e.message, 'warn');
        parsed = buildDefaultSeriesConfigObject(compareDays);
        setState(id, JSON.stringify(parsed, null, 2), true);
    }

    const normalized = normalizeSeriesConfig(parsed);
    const synced = syncStoredSeriesConfigWithCompareDays(normalized, compareDays);

    return synced;
}

function syncStoredSeriesConfigWithCompareDays(cfg, compareDays) {
    const result = cloneJson(cfg);

    if (!result.compareByDay || typeof result.compareByDay !== 'object') {
        result.compareByDay = {};
    }

    const compareDefault = normalizeSeriesStyle(result.compareDefault, DEFAULT_COMPARE_STYLE);
    const validDayMap = {};

    for (let i = 0; i &lt; compareDays.length; i++) {
        const dayKey = String(compareDays[i]);
        validDayMap[dayKey] = true;

        if (!result.compareByDay[dayKey] || typeof result.compareByDay[dayKey] !== 'object') {
            result.compareByDay[dayKey] = cloneJson(compareDefault);
        }
    }

    Object.keys(result.compareByDay).forEach(dayKey =&gt; {
        if (validDayMap[dayKey]) {
            return;
        }

        const entry = normalizeSeriesStyle(result.compareByDay[dayKey], compareDefault);

        if (isJsonEqual(entry, compareDefault)) {
            delete result.compareByDay[dayKey];
        }
    });

    if (!result.delta || typeof result.delta !== 'object') {
        result.delta = buildDefaultDeltaStyleConfig();
    }

    return normalizeSeriesConfig(result);
}

function saveSeriesConfigIfChanged(seriesConfig) {
    const id = CONFIG_BASE + '.seriesConfig';
    const currentRaw = readString(id, DEFAULTS.seriesConfig);
    const nextRaw = JSON.stringify(seriesConfig, null, 2);

    if (normalizeJsonString(currentRaw) !== normalizeJsonString(nextRaw)) {
        setState(id, nextRaw, true);
    }
}

function updateGeneratedUrl() {
    const messageName = readString(CONFIG_BASE + '.messageName', DEFAULTS.messageName);
    const urlBase = readString(CONFIG_BASE + '.urlBase', DEFAULTS.urlBase);
    const urlRefresh = Math.max(0, readNumber(CONFIG_BASE + '.urlRefresh', DEFAULTS.urlRefresh));

    const url = buildFlexUrl(urlBase, messageName, urlRefresh);
    const id = CONFIG_BASE + '.generatedUrl';
    const current = readString(id, '');

    if (current !== url) {
        setState(id, url, true);
    }
}

function buildFlexUrl(baseUrl, messageName, refreshSeconds) {
    const base = String(baseUrl || '').trim();
    const msg = String(messageName || '').trim();
    const refresh = Number(refreshSeconds);

    if (!base || !msg) {
        return '';
    }

    const separator = base.includes('?') ? '&amp;' : '?';
    let url = base + separator + 'source=script&amp;message=' + encodeURIComponent(msg);

    if (Number.isFinite(refresh) &amp;&amp; refresh &gt; 0) {
        url += '&amp;refresh=' + encodeURIComponent(String(refresh));
    }

    return url;
}

function normalizeSeriesConfig(cfg) {
    const result = (cfg &amp;&amp; typeof cfg === 'object') ? cloneJson(cfg) : {};

    result.current = normalizeSeriesStyle(result.current, DEFAULT_CURRENT_STYLE);
    result.compareDefault = normalizeSeriesStyle(result.compareDefault, DEFAULT_COMPARE_STYLE);

    if (!result.compareByDay || typeof result.compareByDay !== 'object') {
        result.compareByDay = {};
    }

    Object.keys(result.compareByDay).forEach(day =&gt; {
        result.compareByDay[day] = normalizeSeriesStyle(result.compareByDay[day], result.compareDefault);
    });

    result.delta = normalizeDeltaStyle(result.delta, DEFAULT_DELTA_STYLE);

    return result;
}

function normalizeSeriesStyle(style, defaults) {
    const src = (style &amp;&amp; typeof style === 'object') ? style : {};
    const def = defaults || {};

    return {
        type: normalizeSeriesType(src.type !== undefined ? src.type : def.type),
        color: String(src.color !== undefined ? src.color : def.color || '').trim(),
        lineStyle: normalizeLineStyle(src.lineStyle !== undefined ? src.lineStyle : def.lineStyle),
        lineWidth: Math.max(1, toNumber(src.lineWidth, def.lineWidth || 2)),
        area: toBoolean(src.area, !!def.area),
        smooth: toBoolean(src.smooth, !!def.smooth),
        symbol: normalizeSymbol(src.symbol !== undefined ? src.symbol : def.symbol)
    };
}

function normalizeDeltaStyle(style, defaults) {
    const src = (style &amp;&amp; typeof style === 'object') ? style : {};
    const def = defaults || DEFAULT_DELTA_STYLE;

    const base = normalizeSeriesStyle(src, def);
    base.yAxisIndex = Math.max(0, parseInt(toNumber(src.yAxisIndex, def.yAxisIndex || 1), 10));

    return base;
}

function normalizeSeriesType(type) {
    const t = String(type || '').trim().toLowerCase();
    if (t === 'line' || t === 'bar' || t === 'scatter') {
        return t;
    }
    return 'line';
}

function normalizeLineStyle(style) {
    const s = String(style || '').trim().toLowerCase();
    if (s === 'solid' || s === 'dashed' || s === 'dotted') {
        return s;
    }
    return 'solid';
}

function normalizeSymbol(symbol) {
    const raw = String(symbol || '').trim();
    const lower = raw.toLowerCase();

    if (!raw || lower === 'none') {
        return 'none';
    }

    const allowed = {
        circle: 'circle',
        rect: 'rect',
        roundrect: 'roundRect',
        roundRect: 'roundRect',
        triangle: 'triangle',
        diamond: 'diamond',
        pin: 'pin',
        arrow: 'arrow'
    };

    return allowed[raw] || allowed[lower] || 'none';
}

function normalizeSeriesRef(ref) {
    const r = String(ref || '').trim().toLowerCase();

    if (r === 'current') {
        return 'current';
    }

    const m = r.match(/^day(\d+)$/);
    if (m) {
        return 'day' + parseInt(m[1], 10);
    }

    return 'current';
}

function buildDefaultSeriesConfigObject(compareDays) {
    const compareByDay = {};
    const days = Array.isArray(compareDays) &amp;&amp; compareDays.length ? compareDays : DEFAULT_COMPARE_DAYS;

    for (let i = 0; i &lt; days.length; i++) {
        compareByDay[String(days[i])] = cloneJson(DEFAULT_COMPARE_STYLE);
    }

    return {
        current: cloneJson(DEFAULT_CURRENT_STYLE),
        compareDefault: cloneJson(DEFAULT_COMPARE_STYLE),
        compareByDay: compareByDay,
        delta: buildDefaultDeltaStyleConfig()
    };
}

function buildDefaultDeltaStyleConfig() {
    return {
        type: DEFAULT_DELTA_STYLE.type,
        color: DEFAULT_DELTA_STYLE.color,
        lineStyle: DEFAULT_DELTA_STYLE.lineStyle,
        lineWidth: DEFAULT_DELTA_STYLE.lineWidth,
        area: DEFAULT_DELTA_STYLE.area,
        smooth: DEFAULT_DELTA_STYLE.smooth,
        symbol: DEFAULT_DELTA_STYLE.symbol,
        yAxisIndex: DEFAULT_DELTA_STYLE.yAxisIndex
    };
}

function isJsonEqual(a, b) {
    return normalizeJsonString(JSON.stringify(a)) === normalizeJsonString(JSON.stringify(b));
}

function normalizeJsonString(value) {
    try {
        return JSON.stringify(JSON.parse(String(value || '')));
    } catch (e) {
        return String(value || '').trim();
    }
}

function cloneJson(obj) {
    try {
        return JSON.parse(JSON.stringify(obj || {}));
    } catch (e) {
        return {};
    }
}

function toNumber(value, fallback) {
    const n = Number(value);
    return Number.isFinite(n) ? n : Number(fallback);
}

function toBoolean(value, fallback) {
    if (value === true || value === false) {
        return value;
    }

    if (typeof value === 'string') {
        const v = value.trim().toLowerCase();
        if (v === 'true' || v === '1' || v === 'yes' || v === 'on') return true;
        if (v === 'false' || v === '0' || v === 'no' || v === 'off') return false;
    }

    if (typeof value === 'number') {
        return value !== 0;
    }

    return !!fallback;
}

function readString(id, fallback) {
    try {
        if (!existsState(id)) return fallback;
        const state = getState(id);
        if (!state || state.val === null || state.val === undefined) return fallback;
        const value = String(state.val).trim();
        return value !== '' ? value : fallback;
    } catch (e) {
        return fallback;
    }
}

function readNumber(id, fallback) {
    try {
        if (!existsState(id)) return fallback;
        const state = getState(id);
        if (!state || state.val === null || state.val === undefined) return fallback;
        const value = Number(state.val);
        return Number.isFinite(value) ? value : fallback;
    } catch (e) {
        return fallback;
    }
}

function readBoolean(id, fallback) {
    try {
        if (!existsState(id)) return fallback;
        const state = getState(id);
        if (!state || state.val === null || state.val === undefined) return fallback;
        return toBoolean(state.val, fallback);
    } catch (e) {
        return fallback;
    }
}

function parseCompareDays(value) {
    const result = [];
    const used = {};

    String(value || '')
        .split(',')
        .map(s =&gt; parseInt(String(s).trim(), 10))
        .forEach(n =&gt; {
            if (Number.isFinite(n) &amp;&amp; n &gt; 0 &amp;&amp; !used[n]) {
                used[n] = true;
                result.push(n);
            }
        });

    result.sort((a, b) =&gt; a - b);

    if (!result.length) {
        return [1];
    }

    return result;
}

function createComparisonChart(callback) {
    const config = readConfig();

    saveSeriesConfigIfChanged(config.seriesConfig);
    updateGeneratedUrl();

    const now = Date.now();
    const windowMs = config.windowHours * 60 * 60 * 1000;

    const startCurrent = now - windowMs;
    const endCurrent = now;

    const unit = getUnitFromObject(config.dpId) || config.fallbackUnit;

    getHistorySeries(
        config.historyInstance,
        config.dpId,
        startCurrent,
        endCurrent,
        config.maxCount,
        (errCurrent, currentSeries) =&gt; {
            if (errCurrent) {
                callback(createErrorOption('Fehler beim Laden der aktuellen History: ' + stringifyError(errCurrent)));
                return;
            }

            loadComparisonSeries(
                config.historyInstance,
                config.dpId,
                startCurrent,
                endCurrent,
                config.compareDays,
                config.maxCount,
                (errCompare, compareSeriesList) =&gt; {
                    if (errCompare) {
                        callback(createErrorOption('Fehler beim Laden der Vergleichs-History: ' + stringifyError(errCompare)));
                        return;
                    }

                    const allSeries = [];

                    allSeries.push(buildSeriesObject(
                        'Aktuell',
                        currentSeries,
                        config.seriesConfig.current,
                        0
                    ));

                    for (let i = 0; i &lt; compareSeriesList.length; i++) {
                        const item = compareSeriesList[i];
                        const dayStyle = config.seriesConfig.compareByDay[String(item.dayOffset)] || config.seriesConfig.compareDefault;

                        allSeries.push(buildSeriesObject(
                            'Vor ' + item.dayOffset + ' Tag' + (item.dayOffset &gt; 1 ? 'en' : ''),
                            item.data,
                            dayStyle,
                            0
                        ));
                    }

                    const deltaBaseStyle = config.seriesConfig.delta;
                    let deltaData = [];

                    if (config.deltaEnabled &amp;&amp; config.deltaSourceA !== config.deltaSourceB) {
                        const sourceAData = getSeriesDataByRef(config.deltaSourceA, currentSeries, compareSeriesList);
                        const sourceBData = getSeriesDataByRef(config.deltaSourceB, currentSeries, compareSeriesList);

                        if (sourceAData.length &amp;&amp; sourceBData.length) {
                            deltaData = buildDeltaSeries(sourceAData, sourceBData);

                            if (deltaData.length) {
                                const effectiveDeltaStyle = cloneJson(deltaBaseStyle);

                                if (config.deltaAreaEnabled) {
                                    effectiveDeltaStyle.type = 'line';
                                    effectiveDeltaStyle.area = true;
                                }

                                effectiveDeltaStyle.smooth = !!config.deltaSmooth;

                                allSeries.push(buildSeriesObject(
                                    'Δ ' + getSeriesLabelByRef(config.deltaSourceA) + ' - ' + getSeriesLabelByRef(config.deltaSourceB),
                                    deltaData,
                                    effectiveDeltaStyle,
                                    effectiveDeltaStyle.yAxisIndex
                                ));
                            }
                        }
                    }

                    const yAxis = [
                        {
                            type: 'value',
                            name: config.valueName + ' [' + unit + ']',
                            scale: true,
                            axisLabel: {
                                formatter: '{value} ' + unit
                            }
                        }
                    ];

                    if (deltaData.length) {
                        yAxis.push({
                            type: 'value',
                            name: 'Δ [' + unit + ']',
                            scale: true,
                            axisLabel: {
                                formatter: '{value} ' + unit
                            }
                        });
                    }

                    const option = {
                        title: {
                            text: config.chartTitle,
                            left: 'center'
                        },
                        grid: {
                            left: 80,
                            right: 50,
                            top: 70,
                            bottom: 90
                        },
                        legend: {
                            top: 35
                        },
                        tooltip: {
                            trigger: 'axis',
                            axisPointer: {
                                type: 'cross'
                            }
                        },
                        toolbox: {
                            feature: {
                                dataZoom: { yAxisIndex: 'none' },
                                restore: {},
                                saveAsImage: {}
                            }
                        },
                        xAxis: {
                            type: 'time',
                            min: startCurrent,
                            max: endCurrent,
                            name: 'Zeit'
                        },
                        yAxis: yAxis,
                        dataZoom: [
                            {
                                type: 'inside',
                                start: 0,
                                end: 100
                            },
                            {
                                type: 'slider',
                                start: 0,
                                end: 100
                            }
                        ],
                        series: allSeries
                    };

                    callback(option);
                }
            );
        }
    );
}

function buildSeriesObject(name, data, style, yAxisIndex) {
    const series = {
        name: name,
        type: style.type,
        data: data,
        yAxisIndex: Number.isFinite(Number(yAxisIndex)) ? Number(yAxisIndex) : 0
    };

    if (style.type === 'line') {
        series.showSymbol = style.symbol !== 'none';
        series.symbol = style.symbol;
        series.smooth = !!style.smooth;
        series.connectNulls = false;
        series.lineStyle = {
            type: style.lineStyle,
            width: style.lineWidth
        };

        if (style.color) {
            series.lineStyle.color = style.color;
            series.itemStyle = { color: style.color };
        }

        if (style.area) {
            series.areaStyle = {};
        }
    }

    if (style.type === 'scatter') {
        series.symbol = style.symbol === 'none' ? 'circle' : style.symbol;
        if (style.color) {
            series.itemStyle = { color: style.color };
        }
    }

    if (style.type === 'bar') {
        if (style.color) {
            series.itemStyle = { color: style.color };
        }
    }

    return series;
}

function getSeriesDataByRef(ref, currentSeries, compareSeriesList) {
    if (ref === 'current') {
        return currentSeries;
    }

    const m = String(ref || '').match(/^day(\d+)$/);
    if (!m) {
        return [];
    }

    const day = parseInt(m[1], 10);
    const found = compareSeriesList.find(item =&gt; item.dayOffset === day);

    return found ? found.data : [];
}

function getSeriesLabelByRef(ref) {
    if (ref === 'current') {
        return 'Aktuell';
    }

    const m = String(ref || '').match(/^day(\d+)$/);
    if (!m) {
        return String(ref || '');
    }

    const day = parseInt(m[1], 10);
    return 'Vor ' + day + ' Tag' + (day &gt; 1 ? 'en' : '');
}

/**
 * Delta-Berechnung über die Vereinigungsmenge aller Zeitstempel.
 * Dadurch wird die Abweichung auch dann am Anfang gezeigt,
 * wenn nur eine der beiden Kurven dort schon einen Punkt hat.
 */
function buildDeltaSeries(seriesA, seriesB) {
    if (!Array.isArray(seriesA) || !Array.isArray(seriesB) || !seriesA.length || !seriesB.length) {
        return [];
    }

    const a = seriesA
        .map(item =&gt; [Number(item[0]), Number(item[1])])
        .filter(item =&gt; Number.isFinite(item[0]) &amp;&amp; Number.isFinite(item[1]))
        .sort((x, y) =&gt; x[0] - y[0]);

    const b = seriesB
        .map(item =&gt; [Number(item[0]), Number(item[1])])
        .filter(item =&gt; Number.isFinite(item[0]) &amp;&amp; Number.isFinite(item[1]))
        .sort((x, y) =&gt; x[0] - y[0]);

    if (!a.length || !b.length) {
        return [];
    }

    const timestamps = Array.from(new Set(
        a.map(item =&gt; item[0]).concat(b.map(item =&gt; item[0]))
    )).sort((x, y) =&gt; x - y);

    let aIndex = 0;
    let bIndex = 0;
    let lastAVal = a[0][1];
    let lastBVal = b[0][1];

    const result = [];

    for (let i = 0; i &lt; timestamps.length; i++) {
        const ts = timestamps[i];

        while (aIndex &lt; a.length &amp;&amp; a[aIndex][0] &lt;= ts) {
            lastAVal = a[aIndex][1];
            aIndex++;
        }

        while (bIndex &lt; b.length &amp;&amp; b[bIndex][0] &lt;= ts) {
            lastBVal = b[bIndex][1];
            bIndex++;
        }

        if (!Number.isFinite(lastAVal) || !Number.isFinite(lastBVal)) {
            continue;
        }

        result.push([ts, lastAVal - lastBVal]);
    }

    return result;
}

function loadComparisonSeries(historyInstance, dpId, startCurrent, endCurrent, compareDays, maxCount, cb) {
    const results = [];
    let index = 0;

    function next() {
        if (index &gt;= compareDays.length) {
            cb(null, results);
            return;
        }

        const dayOffset = compareDays[index++];
        const shiftMs = dayOffset * 24 * 60 * 60 * 1000;

        const startPrevious = startCurrent - shiftMs;
        const endPrevious = endCurrent - shiftMs;

        getHistorySeries(historyInstance, dpId, startPrevious, endPrevious, maxCount, (err, previousSeriesRaw) =&gt; {
            if (err) {
                cb(err);
                return;
            }

            const shifted = previousSeriesRaw.map(item =&gt; [item[0] + shiftMs, item[1]]);

            results.push({
                dayOffset: dayOffset,
                data: shifted
            });

            next();
        });
    }

    next();
}

function getHistorySeries(historyInstance, dpId, start, end, maxCount, cb) {
    getHistory(historyInstance, {
        id: dpId,
        start: start,
        end: end,
        ack: true,
        count: maxCount,
        aggregate: 'none'
    }, (err, result) =&gt; {
        if (err) {
            cb(err);
            return;
        }

        const series = [];

        if (Array.isArray(result)) {
            for (let i = 0; i &lt; result.length; i++) {
                const item = result[i];
                if (!item) continue;

                const ts = Number(item.ts);
                const val = Number(item.val);

                if (!Number.isFinite(ts) || !Number.isFinite(val)) {
                    continue;
                }

                series.push([ts, val]);
            }
        }

        cb(null, series);
    });
}

function getUnitFromObject(id) {
    try {
        const obj = getObject(id);
        if (
            obj &amp;&amp;
            obj.common &amp;&amp;
            typeof obj.common.unit === 'string' &amp;&amp;
            obj.common.unit.trim() !== ''
        ) {
            return obj.common.unit.trim();
        }
    } catch (e) {
        // nichts
    }
    return '';
}

function createErrorOption(message) {
    return {
        title: {
            text: 'Fehler',
            subtext: message,
            left: 'center',
            top: 'middle'
        },
        xAxis: {
            type: 'time'
        },
        yAxis: {
            type: 'value'
        },
        series: []
    };
}

function stringifyError(err) {
    try {
        return typeof err === 'string' ? err : JSON.stringify(err);
    } catch (e) {
        return String(err);
    }
}
</code></pre>
<pre><code></code></pre>
]]></description><link>https://forum.iobroker.net/post/1335491</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335491</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Mon, 20 Apr 2026 08:25:31 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 08:07:02 GMT]]></title><description><![CDATA[<p dir="auto">Du hast etwas hinzugefügt?<br />
Zeig mal ein Bild von dem was du wo geschrieben hast.</p>
]]></description><link>https://forum.iobroker.net/post/1335488</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335488</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Mon, 20 Apr 2026 08:07:02 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Mon, 20 Apr 2026 06:45:45 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a> schau ich mir an</p>
]]></description><link>https://forum.iobroker.net/post/1335475</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335475</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Mon, 20 Apr 2026 06:45:45 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Sat, 18 Apr 2026 07:23:56 GMT]]></title><description><![CDATA[<p dir="auto">Guten Morgen <a class="plugin-mentions-user plugin-mentions-a" href="/user/mcu" aria-label="Profile: mcu">@<bdi>mcu</bdi></a> habe mal mit dem javascript compareDays angefangen.<br />
Ich habe unter Allgemeine Scripte einen Ordner Echarts erstellt und das .javascript compareDays mittels copy und past einkopier.<br />
Nach dem Start kommen bei mir leider eine Reihe von roten Fehlermeldungen:</p>
<pre><code>javascript.0	09:10:59.227	info	
Stopping script
javascript.0	09:10:59.257	info	
start JavaScript (Javascript/js)
javascript.0	09:10:59.259	error	
compile failed at: script.js.common.Echarts.javascript_compareDays:2
javascript.0	09:10:59.261	error	
# javascript compareDays
javascript.0	09:10:59.264	error	
^
javascript.0	09:10:59.265	error	
SyntaxError: Invalid or unexpected token
javascript.0	09:10:59.265	error	
    at new Script (node:vm:117:7)
javascript.0	09:10:59.265	error	
    at JavaScript.createVM (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:2179:25)
javascript.0	09:10:59.265	error	
    at JavaScript.prepareScript (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:2481:44)
javascript.0	09:10:59.265	error	
    at processTicksAndRejections (node:internal/process/task_queues:103:5)
</code></pre>
<p dir="auto">Ich habe allerdings mit javascript 0 Erfahrung, nur bisschen blockly.</p>
]]></description><link>https://forum.iobroker.net/post/1335141</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335141</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Sat, 18 Apr 2026 07:23:56 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Fri, 17 Apr 2026 22:24:18 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a><br />
<a href="https://mcuiobroker.gitbook.io/iobroker-tipps/tipps/scripte-blockly-javascript/flexcharts/beispiel-comparedays" rel="nofollow ugc">https://mcuiobroker.gitbook.io/iobroker-tipps/tipps/scripte-blockly-javascript/flexcharts/beispiel-comparedays</a><br />
Ist noch nicht ganz komplett, aber man kann schon anfangen. Falls Fragen sind hier stellen. Danke.</p>
]]></description><link>https://forum.iobroker.net/post/1335125</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335125</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Fri, 17 Apr 2026 22:24:18 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Fri, 17 Apr 2026 22:04:10 GMT]]></title><description><![CDATA[<p dir="auto">Super, danke <img src="https://forum.iobroker.net/assets/plugins/nodebb-plugin-emoji/emoji/android/1f44d.png?v=ba16ebd4856" class="not-responsive emoji emoji-android emoji--+1" style="height:23px;width:auto;vertical-align:middle" title=":+1:" alt="👍" /></p>
]]></description><link>https://forum.iobroker.net/post/1335123</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335123</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Fri, 17 Apr 2026 22:04:10 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Fri, 17 Apr 2026 22:01:57 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a> Mache gerade die Doku fertig, dann kannst du Dir das morgen mal anschauen.</p>
]]></description><link>https://forum.iobroker.net/post/1335122</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335122</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Fri, 17 Apr 2026 22:01:57 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Fri, 17 Apr 2026 21:38:34 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mcu" aria-label="Profile: mcu">@<bdi>mcu</bdi></a> die Installation hat geklappt (0.7), bin jetzt hier: "Source option 1 — ioBroker state" Die Seite <a href="http://192.168.1.134:8082/flexcharts/echarts.html?source=state&amp;id=0_userdata.0.echarts.chart1" rel="nofollow ugc">http://192.168.1.134:8082/flexcharts/echarts.html?source=state&amp;id=0_userdata.0.echarts.chart1</a><br />
wird geöffnet, aber wie komme ich in einen Edit-Modus? Es eilt aber nicht, ich habe Zeit, bin Rentner 😉</p>
]]></description><link>https://forum.iobroker.net/post/1335120</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335120</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Fri, 17 Apr 2026 21:38:34 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Fri, 17 Apr 2026 21:06:19 GMT]]></title><description><![CDATA[<p dir="auto">Das wäre toll. Danke 😀</p>
]]></description><link>https://forum.iobroker.net/post/1335118</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335118</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Fri, 17 Apr 2026 21:06:19 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Fri, 17 Apr 2026 21:05:14 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/winni" aria-label="Profile: Winni">@<bdi>Winni</bdi></a> Ich mach Dir mal ein script fertig.</p>
]]></description><link>https://forum.iobroker.net/post/1335117</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335117</guid><dc:creator><![CDATA[MCU]]></dc:creator><pubDate>Fri, 17 Apr 2026 21:05:14 GMT</pubDate></item><item><title><![CDATA[Reply to Datenpunkt mit Start&#x2F; Endzeit in einem Diagramm on Fri, 17 Apr 2026 21:03:26 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/mcu" aria-label="Profile: mcu">@<bdi>mcu</bdi></a> danke für deine Antwort. Das hatte ich mir schon angeschaut, bin damit aber überhaupt nicht zurecht gekommen. Vielleicht mach ich da nochmal einen Anlauf.</p>
]]></description><link>https://forum.iobroker.net/post/1335116</link><guid isPermaLink="true">https://forum.iobroker.net/post/1335116</guid><dc:creator><![CDATA[Winni]]></dc:creator><pubDate>Fri, 17 Apr 2026 21:03:26 GMT</pubDate></item></channel></rss>