RedMatic Update 7.3.5

Node-RED als CCU3/RaspberryMatic Addon, WebApp, HomeKit, ...

Moderator: Co-Administratoren

Benutzeravatar
Henke
Beiträge: 1540
Registriert: 27.06.2022, 20:51
System: CCU
Hat sich bedankt: 144 Mal
Danksagung erhalten: 313 Mal

Re: RedMatic Update 7.3.5

Beitrag von Henke » 18.01.2024, 20:02

hkolb hat geschrieben:
18.01.2024, 19:03
Wie schon erwähnt ist das System mit Redmatic-Addon NEU installiert,
Damit bleibt dein flow, die installierten nodes usw. erhalten und damit auch eine Quelle von potentiellen Fehlern.
RedMatic deinstallieren, Ordner löschen, RedMatic installieren ist eine leere frische neue Version.

npm ist in /usr/local/lib/node_modules/node/bin/npm richtig.
Sollte aber auch ein Link in /usr/local/lib/npm sein.

Jetzt probieren node red zu aktualisieren.
Im flow "Update NodeRed" drücken.

Da dann auch unter Tools "npm doctor"

hkolb
Beiträge: 14
Registriert: 08.01.2024, 11:43
System: sonstige

Re: RedMatic Update 7.3.5

Beitrag von hkolb » 18.01.2024, 20:20

Damit bleibt dein flow, die installierten nodes usw. erhalten und damit auch eine Quelle von potentiellen Fehlern.
RedMatic deinstallieren, Ordner löschen, RedMatic installieren ist eine leere frische neue Version.
Sorry, ich hab mich schlecht ausgedrückt: Redmatic hab ich vor der Installation deinstalliert, auch den Addon-Folder gelöscht.
Sollte aber auch ein Link in /usr/local/lib/npm sein.
Aber scheinbar ist genau dieser Link gelöscht denn:

Code: Alles auswählen

/usr/local/lib/npm --version
-sh: /usr/local/lib/npm: not found
Ein Klick auf "Update NodeRed, selten nötig" produziert den (üblichen) Fehler:

Code: Alles auswählen

Update NodeRed : msg.payload : string[50]
string[50]

/usr/local/update/_run.sh: line 4: npm: not found
Kannst du mir den Befehl zum erzeugen des Links schreiben?
Ich denke mit diesem Link könnten sich die Probleme lösen!

Benutzeravatar
Henke
Beiträge: 1540
Registriert: 27.06.2022, 20:51
System: CCU
Hat sich bedankt: 144 Mal
Danksagung erhalten: 313 Mal

Re: RedMatic Update 7.3.5

Beitrag von Henke » 18.01.2024, 20:22

Im flow "Check/Install node.js" drücken.
Warten bis der durchgelaufen ist und dann CCU neu starten.

Blödsinn, npm wird ja nicht gefunden...
Also:

Code: Alles auswählen

cd /usr/local
mkdir -m a+rwx -p /usr/local/lib
mkdir -m a+rwx -p /usr/local/lib/node_modules
mkdir -m a+rwx -p /usr/local/bin
/usr/local/lib/node_modules/node/bin/npm/npm install -g node@20.10.0
/usr/local/lib/node_modules/node/bin/npm/npm install -g npm

hkolb
Beiträge: 14
Registriert: 08.01.2024, 11:43
System: sonstige

Re: RedMatic Update 7.3.5

Beitrag von hkolb » 18.01.2024, 20:53

Beim ersten "Install" folgende Meldung:

Code: Alles auswählen

/usr/local/lib/node_modules/node/bin/npm install -g node@20.10.0
npm ERR! code ENOENT
npm ERR! syscall mkdir
npm ERR! path /root/.npm/_cacache/tmp
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, mkdir '/root/.npm/_cacache/tmp'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

npm ERR! Log files were not written due to an error writing to the directory: /root/.npm/_logs
npm ERR! You can rerun the command with `--loglevel=verbose` to see the logs in your terminal

Hast du noch eine Idee?
Oder soll ich mal mit einer "ganz frischen" RaspbiMatic anfangen?
Aber eigentlich ist das "Original-Image" ja schreibgeschützt so das dem nichts passieren kann. Und die anderen Fehler kommen dann mit dem Restore eines Backups zurück. :x

Benutzeravatar
Henke
Beiträge: 1540
Registriert: 27.06.2022, 20:51
System: CCU
Hat sich bedankt: 144 Mal
Danksagung erhalten: 313 Mal

Re: RedMatic Update 7.3.5

Beitrag von Henke » 18.01.2024, 22:14

Welche RasberryMatic Version ist drauf?

hkolb
Beiträge: 14
Registriert: 08.01.2024, 11:43
System: sonstige

Re: RedMatic Update 7.3.5

Beitrag von hkolb » 18.01.2024, 22:49

Ist die neueste, V3.73.9

Benutzeravatar
Henke
Beiträge: 1540
Registriert: 27.06.2022, 20:51
System: CCU
Hat sich bedankt: 144 Mal
Danksagung erhalten: 313 Mal

Re: RedMatic Update 7.3.5

Beitrag von Henke » 18.01.2024, 23:51

Schade, dann muss auch die Datei /usr/local/etc/npmrc mit folgendem Inhalt vorhanden sein:

Code: Alles auswählen

prefix=/usr/local
cache=/tmp/npm-cache
Damit wird der cache auf /tmp/npm-cache festgelegt und ich kann mir nicht erklären warum was mit "/root/.npm/_cacache/tmp".
Außer einer neuen Installation des Patches mit "erzwungen" habe ich keine Idee mehr.

hkolb
Beiträge: 14
Registriert: 08.01.2024, 11:43
System: sonstige

Re: RedMatic Update 7.3.5

Beitrag von hkolb » 19.01.2024, 10:13

Ich habe jetzt, da eine Lösung ja scheinbar nicht so einfach möglich ist, den finalen Weg eingeschlagen und Raspberrymatic neu aufgespielt
Und dann ein aktuelles Backup (mit dem nicht mehr funktionierendem Redmatic) darauf restored.

UND: kaum zu glauben!! Alles läuft!
Ich kann in Redmatic wieder nodes installieren, auch solche die sich mit V7.2.1 nicht installieren ließen.

Aber ein paar Probleme bleiben. Hier mal die Ausgabe von dem Flow "npm doctor":

Code: Alles auswählen

Check                               Value   Recommendation/Notes
npm ping                            ok       
npm -v                              not ok  Use npm v10.3.0
node -v                             not ok  Use node v20.11.0 (current: v20.10.0)
npm config get registry             ok      using default registry (https://registry.npmjs.org/)
git executable in PATH              ok      /usr/local/addons/redmatic/bin/git
global bin folder in PATH           not ok  Error: Add /usr/local/lib/node_modules/node/bin to your $PATH
Perms check on cached files         ok       
Perms check on local node_modules   ok       
Perms check on global node_modules  ok       
Perms check on local bin folder     ok       
Perms check on global bin folder    ok       
Verify cache contents               ok      verified 637 tarballs
Offenbar wir eine veraltete npm verwendet. Denn im Terminal kommt bei:

Code: Alles auswählen

npm -v
9.8.1
aber bei:

Code: Alles auswählen

/usr/local/lib/node_modules/node/bin/npm -v
10.3.0
Ist das jetzt enstscheidend wichtig/schlecht?
Wenn ja, wie kann man das dauerhaft lösen?

Im laufenden System klappt das ja mit einer Änderung von Path:

Code: Alles auswählen

PATH="/usr/local/lib/node_modules/node/bin/:PATH"
(mit dem neuen Pfad vorne dran, sonst kommt wieder die "alte" npm zuerst dran.
Dann kommt auch:

Code: Alles auswählen

npm -v
10.3.0
Für eine Lösung die auch einen Neustart übersteht fand ich nur den Eintrag in versteckte Dateien die ich aber alle nicht auf der Raspberrymatic fand. :?:

Benutzeravatar
Henke
Beiträge: 1540
Registriert: 27.06.2022, 20:51
System: CCU
Hat sich bedankt: 144 Mal
Danksagung erhalten: 313 Mal

Re: RedMatic Update 7.3.5

Beitrag von Henke » 19.01.2024, 17:34

Lass diesen flow mal durchlaufen mit erzwungen:

Code: Alles auswählen

[
    {
        "id": "0ea2818262334c44",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "RedMatic aufräumen",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "plain",
        "template": "#!/bin/sh\ncd /usr/local/addons/redmatic/var\nsource /usr/local/addons/redmatic/home/.profile\nmkdir -m a+rwx -p /usr/local/lib/node_modules\nmkdir -m a+rwx -p /usr/local/bin\n# npm config set cache=/tmp/npm-cache\n\nrm /usr/local/addons/redmatic/etc/npmrc 2>/dev/null\n# rm /usr/local/addons/redmatic/bin/jq 2>/dev/null\n# rm /usr/local/addons/redmatic/bin/jo 2>/dev/null\nrm /usr/local/addons/redmatic/bin/deviceTypes 2>/dev/null\nrm /usr/local/addons/redmatic/bin/.nobackup 2>/dev/null\nrm /usr/local/addons/redmatic/include/.nobackup 2>/dev/null\nrm /usr/local/addons/redmatic/lib/.nobackup 2>/dev/null\nrm /usr/local/addons/redmatic/libexec/.nobackup 2>/dev/null\nrm /usr/local/addons/redmatic/share/.nobackup 2>/dev/null\nrm /usr/local/addons/redmatic/tmp/.nobackup 2>/dev/null\nrm /usr/local/addons/redmatic/var/node_modules/.nobackup 2>/dev/null\nrm /usr/local/addons/redmatic/www/.nobackup 2>/dev/null\n\nrm /usr/local/addons/redmatic/lib/pkg-repo.json 2> /dev/null\nrm /usr/local/addons/redmatic/CHANGELOG.md 2> /dev/null\nrm /usr/local/addons/redmatic/LICENSE 2> /dev/null\nrm /usr/local/addons/redmatic/README.md 2> /dev/null\n\nrm /usr/local/addons/redmatic/bin/corepack 2> /dev/null\nrm /usr/local/addons/redmatic/bin/node 2> /dev/null\nrm /usr/local/addons/redmatic/bin/npm 2> /dev/null\nrm /usr/local/addons/redmatic/bin/npx 2> /dev/null\nrm -r /usr/local/addons/redmatic/lib/node_modules/npm 2> /dev/null\nrm -r /usr/local/addons/redmatic/lib/node_modules/corepack 2> /dev/null\nrm -r /usr/local/addons/redmatic/lib/node_modules/ain2 2> /dev/null\nexit 0",
        "output": "str",
        "x": 480,
        "y": 480,
        "wires": [
            [
                "2d6177b5c2a97ca9"
            ]
        ]
    },
    {
        "id": "fa72f43dd0a8948b",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "logger.js",
        "field": "payload",
        "fieldType": "msg",
        "format": "javascript",
        "syntax": "plain",
        "template": "// Henke Version 1.2\n\nvar dgram = require('dgram');\n//var iconvLite= require('iconv-lite');\n//var exec = require('child_process').exec;\n\nmodule.exports = {\n    logging: {\n        //                console: {\n        //                    level: \"error\",\n        //                    metrics: false,\n        //                    audit: false\n        //                },\n        ain: {\n            //            level: 'debug',\n            //            metrics: false,\n            //            audit: false,\n            handler: function (conf) {\n                //console.log(\"----\");\n                //console.log(conf);\n                //console.log(\"----\");\n\n                // Return the function that will do the actual logging\n                return function (mg) {\n                    const levelNames = {\n                        10: { s: 2, t: 'crit' }, // fatal\n                        20: { s: 3, t: 'err' },\n                        30: { s: 4, t: 'warn' },\n                        40: { s: 6, t: 'info' },\n                        50: { s: 7, t: 'debug' },\n                        60: { s: 7, t: 'trace' },  // trace\n                        98: { s: 7, t: 'audit' }, // audit\n                        99: { s: 7, t: 'metric' } // metric\n                    };\n\n                    if (mg && typeof mg.msg != 'string') {\n                        mg.msg = JSON.stringify(mg.msg);\n                    }\n                    let tagStr = \"NodeRed\";\n                    // let b = Buffer.from(_msg.msg);\n                    // _msg.msg = Buffer.from(b.toString('utf8')).toString('ascii');\n                    // _msg.msg =iconvLite.decode(iconvLite.decode(_msg.msg , 'utf8'), 'latin1');\n                    if (mg.type) {\n                        let mName = (mg.name || mg.id);\n                        //                        mName = mName.replaceAll(/\\W/g, \"_\");\n                        //                        mName = mName.replaceAll(/[\\s><\\[\\]]/g, \"_\");\n\n                        let patt = new RegExp(\"[\\\\s><\\\\[\\\\]]\", \"g\");\n                        mName = mName.replace(patt, \"_\");\n                        tagStr = mg.type + '[' + mName + ']';\n                    }\n                    let patt = new RegExp(\"\\n\", \"g\");\n                    mg.msg = mg.msg.replace(patt, \"\");\n                    // _msg.msg = _msg.msg.replaceAll(/\\n/g, \"\");\n\n                    //                    const Out = 'logger -t ' + tagStr + ' -p daemon.' + levelNames[_msg.level].t + ' ' + _msg.msg;\n                    //                    exec(Out);\n                    //                    return;\n\n                    // Alternativ über Socket, dann die oberen 3 Zeilen auskommentieren\n                    // converts number to two-digit string\n                    function twoDigits(n) {\n                        return ('0' + n).slice(-2);\n                    }\n\n                    let time = new Date(mg.timestamp);\n                    const MONTHS = [\n                        'Jan',\n                        'Feb',\n                        'Mar',\n                        'Apr',\n                        'May',\n                        'Jun',\n                        'Jul',\n                        'Aug',\n                        'Sep',\n                        'Oct',\n                        'Nov',\n                        'Dec'\n                    ];\n\n                    // format time\n                    let month = MONTHS[time.getMonth()];\n                    let date = twoDigits(time.getDate());\n                    let hours = twoDigits(time.getHours());\n                    let minutes = twoDigits(time.getMinutes());\n                    let seconds = twoDigits(time.getSeconds());\n\n                    // format('%s %s %s:%s:%s'\n                    let timeStr = month + ' ' + date + ' ' + hours + \":\" + minutes + \":\" + seconds;\n                    //                                        console.log(\"---> \" + _msg.level + \" \" + levelNames[_msg.level].s + \" \" + levelNames[_msg.level].t + \" \" + _msg.msg);\n\n                    let PRI = (1 * 8) + levelNames[mg.level].s ;\n//                    console.log( \"Err Level: \" + mg.level + \" \" + levelNames[mg.level].s + \" \" + mg.msg);\n\n                    let message = \"<\" + PRI + \">\" + timeStr + \" NodeRed\" + \" \" + tagStr + \": \" + mg.msg;\n                    try {\n                        var port = 514;\n                        if (mg.level < 30)\n                            console.log(mg.msg);\n\n                        var Udp = dgram.createSocket('udp4');\n                        const out = Buffer.from(message);\n                        Udp.connect(port, '127.0.0.1', (err) => {\n                            Udp.send(out, (err) => {\n                                Udp.close();\n                            });\n                        });\n\n                        //                console.log(\"Logger connected\");\n                        //                        console.log(message);\n                        //                        console.log(JSON.stringify(msg));\n                    } catch (err) { console.log( \"catch: \" + err); }\n                }\n            }\n        }\n    }\n};\n",
        "output": "str",
        "x": 440,
        "y": 220,
        "wires": [
            [
                "21eee2220ba31f6a"
            ]
        ]
    },
    {
        "id": "21eee2220ba31f6a",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "lib/logger.js",
        "filename": "/usr/local/addons/redmatic/lib/logger.js",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 690,
        "y": 220,
        "wires": [
            [
                "b4e4431204918796"
            ]
        ]
    },
    {
        "id": "b4e4431204918796",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "service.cgi",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "plain",
        "template": "# Henke Version 1.0\n#!/bin/tclsh\n\nsource ../lib/querystring.tcl\n\nputs -nonewline \"Content-Type: text/plain; charset=utf-8\\r\\n\\r\\n\"\n\nif {[info exists cmd]} {\n\n    if {$cmd == \"stop\" || $cmd == \"start\" || $cmd == \"restart\"} {\n\n        source ../lib/session.tcl\n\n        if {[info exists sid] && [check_session $sid]} {\n            catch {exec /usr/local/etc/config/rc.d/redmatic $cmd} result\n            puts $result\n            exit 0\n        } else {\n            puts {error: invalid session}\n            exit 1\n        }\n    }\n\n    # No session checks for following commands to reduce costs of periodic calls, exposed information is uncritical imho\n\n    if {$cmd == \"ps\"} {\n#        puts [exec ps -o vsz,rss,comm,args | grep \"node\\\\|redmatic\"]\n# Henke Anpassung an neuere Node\n        puts [exec ps -o vsz,rss,comm,args | grep \"node-red\\\\|node-red\" ]\n        exit 0\n    }\n    if {$cmd == \"cpu\"} {\n        puts [exec top -b -n 1 | grep \"% node-red$\" | awk \"\\{print \\$7\\}\"]\n#       puts [exec ps -o vsz,rss,comm,args | grep \"node\\\\|redmatic\" ]\n       exit 0\n    }\n    if {$cmd == \"uptime\"} {\n        puts [exec /usr/local/addons/redmatic/bin/uptime.sh]\n        exit 0\n    }\n}\n\nputs {error: invalid command}\nexit 1\n",
        "output": "str",
        "x": 970,
        "y": 220,
        "wires": [
            [
                "a0554d8f2b842c0b"
            ]
        ]
    },
    {
        "id": "a0554d8f2b842c0b",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "www/service.cgi",
        "filename": "/usr/local/addons/redmatic/www/service.cgi",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1180,
        "y": 220,
        "wires": [
            [
                "a0ee823d1b868b33"
            ]
        ]
    },
    {
        "id": "3d2fc855a49dc0ab",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "script.js",
        "field": "payload",
        "fieldType": "msg",
        "format": "javascript",
        "syntax": "plain",
        "template": "// Henke Version 1.2\n$(document).ready(() => {\n    const bcrypt = dcodeIO.bcrypt;\n\n    const $loglevel = $('#loglevel');\n    const $contextStorageDefault = $('#context-storage-default');\n    const $contextStorageFilePath = $('#context-storage-file-path');\n    const $contextStorageFileInterval = $('#context-storage-file-interval');\n\n    const $adminauthType = $('#adminauth-type');\n    const $adminauthCreds = $('#adminauth-credentials');\n    const $adminauthExpiry = $('#adminauth-expiry');\n    const $adminauthSessionExpiryTime = $('#adminauth-sessionExpiryTime');\n    const $adminauthUser = $('#adminauth-user');\n    const $adminauthPass1 = $('#adminauth-pass1');\n    const $adminauthPass2 = $('#adminauth-pass2');\n    const $adminauthSet = $('#adminauth-set');\n\n    const $nodeauthType = $('#nodeauth-type');\n    const $nodeauthCreds = $('#nodeauth-credentials');\n    const $nodeauthUser = $('#nodeauth-user');\n    const $nodeauthPass1 = $('#nodeauth-pass1');\n    const $nodeauthPass2 = $('#nodeauth-pass2');\n    const $nodeauthSet = $('#nodeauth-set');\n\n    const $staticauthType = $('#staticauth-type');\n    const $staticauthCreds = $('#staticauth-credentials');\n    const $staticauthUser = $('#staticauth-user');\n    const $staticauthPass1 = $('#staticauth-pass1');\n    const $staticauthPass2 = $('#staticauth-pass2');\n    const $staticauthSet = $('#staticauth-set');\n\n    const $projects = $('#projects');\n    const $theme = $('#theme');\n    const $backup = $('#backup');\n\n    const $alertSaved = $('#alert-saved');\n    const $alertError = $('#alert-error');\n    const $alertExec = $('#alert-exec');\n\n    const $status = $('#node-red-status');\n    const $memory = $('#node-red-memory');\n    const $cpu = $('#node-red-cpu');\n\n    $alertSaved.hide();\n    $alertError.hide();\n    $alertExec.hide();\n\n    const $restart = $('#restart');\n    const $restartSafe = $('#restartSafe');\n    const $dropdownRestart = $('#dropdownRestart');\n    const $start = $('#start');\n    const $startSafe = $('#startSafe');\n    const $dropdownStart = $('#dropdownStart');\n    const $stop = $('#stop');\n\n    const $linkRed = $('#link-red');\n    const $linkUi = $('#link-ui');\n\n    const $packageTable = $('#package-table');\n\n    let config;\n\n    let noderedState;\n\n    const qs = location.search;\n    let sid = '';\n    let tmp = qs.match(/sid=(@[0-9a-zA-Z]{10}@)/);\n    if (tmp) {\n        sid = tmp[1];\n        $('#backup').removeClass('disabled').attr('href', 'backup.cgi?sid=' + sid);\n    }\n\n    $('a[href=\"' + (location.hash || '#configuration') + '\"]').tab('show');\n    if (location.hash === '#licenses' && !$('#licenses iframe').attr('src')) {\n        $('#licenses iframe').attr('src', 'licenses.html');\n    }\n\n    $('a[data-toggle=\"tab\"]').on('shown.bs.tab', function (e) {\n        if (history.pushState) {\n            history.pushState(null, null, '#' + $(e.target).attr('href').substr(1));\n        } else {\n            location.hash = '#' + $(e.target).attr('href').substr(1);\n        }\n        if ($(e.target).attr('href') === '#licenses' && !$('#licenses iframe').attr('src')) {\n            $('#licenses iframe').attr('src', 'licenses.html');\n        }\n    });\n\n    let psTimeout;\n    let psInterval = 5000;\n\n    function checkUpdate() {\n        $.getJSON(`update_check.cgi?cmd=versions&sid=${sid}`, (current, success) => {\n            $('#redmatic-version').html('RedMatic Version ' + current.redmatic);\n            $.get(`update_check.cgi?sid=${sid}`, (available, success) => {\n                available = $.trim(available);\n                if (available !== 'n/a' && current.redmatic !== available) {\n                    $('#update-link').html(`<a href=\"https://github.com/rdmtc/RedMatic/releases/latest\" target=\"_blank\">Download Version ${available}</a>`);\n                    $('#update-notify').show();\n                }\n            });\n        });\n    }\n\n    //    checkUpdate();\n\n    function pkg() {\n        let packages;\n        $.get(`pkg.cgi?sid=${sid}&cmd=repo`, data => {\n            packages = data;\n            $.get(`pkg.cgi?sid=${sid}&cmd=ls`, data => {\n                $('#pkg-spinner').hide();\n                data.split('\\n').forEach(line => {\n                    const [name, currentVersion] = line.split(' ');\n                    if (name && packages[name]) {\n                        packages[name].installed = true;\n                        packages[name].currentVersion = currentVersion;\n                    }\n                });\n                $packageTable.html('');\n                Object.keys(packages).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).forEach(name => {\n                    const installed = packages[name].installed ?\n                        (packages[name].version === packages[name].currentVersion ? '✅' : `⚠${packages[name].currentVersion}`) :\n                        '&nbsp;';\n\n                    let url = packages[name].homepage || (packages[name].repository && packages[name].repository.url) || packages[name].repository;\n                    if (url) {\n                        url = url.replace(/^git\\+/, '').replace(/\\.git$/, '');\n                    }\n\n                    $packageTable.append(`<tr><td><a href=\"${url}\" target=\"_blank\">${name}</a><br><span class=\"pkg-desc\">${packages[name].description || ''}</span></td><td>${packages[name].version}</td><td style=\"text-align: center;\">${installed}</td><td><button data-pkg=\"${name}\" type=\"button\" class=\"btn btn-primary btn-sm pkg-install\" ${packages[name].installed ? 'disabled' : ''}><span class=\"spinner-install spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\" hidden></span>\n  install</button> <button data-pkg=\"${name}\" type=\"button\" class=\"btn btn-danger btn-sm pkg-remove\" ${packages[name].installed ? '' : 'disabled'}><span class=\"spinner-remove spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\" hidden></span>\n  remove</button></td></tr>`)\n                });\n                $('.pkg-install').click(function () {\n                    $(this).attr('disabled', true);\n                    $(this).find('.spinner-install').removeAttr('hidden');\n                    $.get(`pkg.cgi?sid=${sid}&cmd=install&package=${$(this).data('pkg')}`, (data, success) => {\n                        $(this).find('.spinner-install').attr('hidden', true);\n                        if (data.includes('Done.')) {\n                            alert($alertExec);\n                        } else {\n                            alert($alertError);\n                        }\n                        pkg();\n                    });\n                });\n                $('.pkg-remove').click(function () {\n                    $(this).attr('disabled', true);\n                    $(this).find('.spinner-remove').removeAttr('hidden');\n                    $.get(`pkg.cgi?sid=${sid}&cmd=remove&package=${$(this).data('pkg')}`, (data, success) => {\n                        $(this).find('.spinner-remove').attr('hidden', true);\n                        if (data.includes('Done.')) {\n                            alert($alertExec);\n                        } else {\n                            alert($alertError);\n                        }\n                        pkg();\n                    });\n                })\n            });\n        });\n    }\n\n    pkg();\n\n    function refresh() {\n        //        checkUpdate();\n        //        pkg();\n    }\n\n    function cpu() {\n        $.get(`service.cgi?sid=${sid}&cmd=cpu`, (data, success) => {\n            data = $.trim(data);\n            $cpu.html((data ? 'cpu ' + data + ', ' : ''));\n        });\n    }\n\n    function ps() {\n        clearTimeout(psTimeout);\n        $.get(`service.cgi?sid=${sid}&cmd=ps`, (data, success) => {\n            cpu();\n            const lines = data.split('\\n');\n            let found = false;\n            lines.forEach(line => {\n                if (found) {\n                    return;\n                }\n                let match;\n                match = line.match(/([0-9]+[a-z]?)\\s+([0-9]+[a-z]?)\\s+node\\s+node-red/);\n                // Henke - Anpassung an neuere Node Versionen\n                if (!match)\n                    match = line.match(/([0-9]+[a-z]?)\\s+([0-9]+[a-z]?)\\s+node-red\\s+node-red/);\n                if (match) {\n                    let [vsz, rss] = line.split(\" \");\n                    vsz = vsz.replace('m', 'MB').replace('g', 'GB');\n                    rss = rss.replace('m', 'MB').replace('g', 'GB');\n                    if (!vsz.endsWith('B')) {\n                        vsz += 'kB';\n                    }\n                    if (!rss.endsWith('B')) {\n                        rss += 'kB';\n                    }\n                    $('#status-spinner').hide();\n                    if (noderedState !== 'running') {\n                        $status.html('<span class=\"status-running\">running</span>');\n                        noderedState = 'running';\n                        refresh();\n                        $.get(`service.cgi?sid=${sid}&cmd=uptime`, (uptime, success) => {\n                            $status.html('<span class=\"status-running\">running</span> <span id=\"uptime\">(' + $.trim(uptime) + ')');\n                        });\n                    }\n                    $memory.html(`rss ${rss}, vsz ${vsz},`);\n                    //                    $memory.html(line.split(\" \"));\n\n                    found = true;\n                    $dropdownRestart.removeClass('disabled');\n                    $stop.removeClass('disabled');\n                    $linkRed.removeClass('disabled');\n                    $linkUi.removeClass('disabled');\n                    $dropdownStart.addClass('disabled');\n                    psInterval = 5000;\n                    return;\n                }\n                match = line.match(/([0-9]+[a-z]?)\\s+([0-9]+[a-z]?)\\s+.*red.js/);\n                if (match) {\n                    noderedState = 'starting';\n                    $('#status-spinner').show();\n                    let [, vsz, rss] = match;\n                    vsz = vsz.replace('m', 'MB').replace('g', 'GB');\n                    rss = rss.replace('m', 'MB').replace('g', 'GB');\n                    if (!vsz.endsWith('B')) {\n                        vsz += 'kB';\n                    }\n                    if (!rss.endsWith('B')) {\n                        rss += 'kB';\n                    }\n                    $status.html('<span class=\"status-starting\">starting</span>');\n                    $memory.html(`rss ${rss}, vsz ${vsz}`);\n                    found = true;\n                    $dropdownRestart.addClass('disabled');\n                    $stop.addClass('disabled');\n                    $dropdownStart.addClass('disabled');\n                    $linkRed.addClass('disabled');\n                    $linkUi.addClass('disabled');\n                    psInterval = 2500;\n                    return;\n                }\n\n                /*                match = line.match(/redmatic-pkg install ([^\\s]+)/);\n                                if (match) {\n                                    noderedState = 'upgrading';\n                                    $('#status-spinner').show();\n                                    $status.html('<span class=\"status-upgrade\">Upgrading ' + match[1] + '</span>');\n                                    $memory.html('');\n                                    found = true;\n                                    $dropdownRestart.addClass('disabled');\n                                    $stop.addClass('disabled');\n                                    $dropdownStart.addClass('disabled');\n                                    $linkRed.addClass('disabled');\n                                    $linkUi.addClass('disabled');\n                                    psInterval = 5000;\n                                    return;\n                                }*/\n            });\n            if (!found) {\n                noderedState = 'stopped';\n                $('#status-spinner').hide();\n                $status.html('<span class=\"status-stopped\">stopped</span>');\n                $memory.html('');\n                $dropdownRestart.addClass('disabled');\n                $stop.addClass('disabled');\n                $dropdownStart.removeClass('disabled');\n                $linkRed.addClass('disabled');\n                $linkUi.addClass('disabled');\n                psInterval = 5000;\n            }\n            psTimeout = setTimeout(ps, psInterval);\n        });\n    }\n\n    ps();\n\n    function alert($elem, timeout = 1600) {\n        $elem.show();\n        $elem.addClass('show');\n        setTimeout(() => {\n            $elem.removeClass('show');\n            setTimeout(() => {\n                $elem.hide();\n            }, 200);\n        }, timeout);\n    }\n\n    function invalidSession() {\n        $('#invalidSession').show();\n        clearTimeout(psTimeout);\n    }\n\n    function save() {\n        console.log('save', config)\n        $.post({\n            url: 'setconfig.cgi' + location.search,\n            data: JSON.stringify(config, null, '  '),\n            success: function (data) {\n                if ($.trim(data) === 'ok') {\n                    alert($alertSaved);\n                } else {\n                    if ($.trim(data) === 'error: invalid session') {\n                        invalidSession();\n                        return;\n                    }\n                    alert($alertError);\n                }\n            }\n        }).fail(() => {\n            alert($alertError);\n        });\n    }\n\n    $.get('getnick.cgi' + location.search, (data, success) => {\n        if ($.trim(data) === 'error: invalid session') {\n            invalidSession();\n            return;\n        }\n        const nick = $.trim(data);\n        if (nick) {\n            $('#nickname').val(nick);\n            $('#log-upload').removeClass('disabled');\n        }\n    });\n\n    $('#nickname').change(() => {\n        const nick = $.trim($('#nickname').val());\n        if (nick) {\n            $('#log-upload').removeClass('disabled');\n        } else {\n            $('#log-upload').addClass('disabled');\n        }\n    });\n\n    $.get('getconfig.cgi' + location.search, (data, success) => {\n        if ($.trim(data) === 'error: invalid session') {\n            invalidSession();\n            return;\n        }\n        config = JSON.parse(data);\n        $loglevel.val(config.logging.ain.level);\n\n        if (config.adminAuth) {\n            $adminauthSessionExpiryTime.val(config.adminAuth.sessionExpiryTime || '604800');\n            $adminauthType.val(config.adminAuth.type);\n            if (config.adminAuth.type === 'credentials') {\n                $adminauthCreds.show();\n                $adminauthExpiry.show();\n                $adminauthUser.val(config.adminAuth.users[0].username);\n\n            }\n            /* else if (config.adminAuth.type === 'rega') {\n                            $adminauthExpiry.show();\n                        }*/\n        }\n\n        if (config.httpNodeAuth) {\n            $nodeauthType.val('basic');\n            $nodeauthCreds.show();\n            $nodeauthUser.val(config.httpNodeAuth.user);\n        }\n\n        if (config.httpStaticAuth) {\n            $staticauthType.val('basic');\n            $staticauthCreds.show();\n            $staticauthUser.val(config.httpStaticAuth.user);\n        }\n\n        if (!config.contextStorage) {\n            config.contextStorage = {};\n        }\n        if (!config.contextStorage.default || !config.contextStorage.default.module) {\n            config.contextStorage.default = { module: 'memory' };\n        }\n        if (!config.contextStorage.memory) {\n            config.contextStorage.memory = {\n                'module': 'memory'\n            };\n        }\n        if (!config.contextStorage.file) {\n            config.contextStorage.file = {\n                'module': 'localfilesystem'\n            };\n        }\n\n        if (!config.contextStorage.file.config) {\n            config.contextStorage.file.config = {\n                dir: '/usr/local/addons/redmatic/var',\n                flushInterval: 30\n            }\n        }\n\n        if (!config.editorTheme) {\n            config.editorTheme = {};\n        }\n        if (!config.editorTheme.projects) {\n            config.editorTheme.projects = {};\n        }\n        config.editorTheme.projects.enabled = config.editorTheme.projects.enabled || false;\n\n        $projects.val(String(config.editorTheme.projects.enabled));\n\n        if (config.editorTheme.projects.enabled) {\n            $projects.prop('disabled', true);\n        }\n\n        if (config.editorTheme.theme) {\n            $theme.val(config.editorTheme.theme);\n        }\n\n        // Migration from 1.x to 2.x\n        if (config.contextStorage.default && config.contextStorage.default.module === 'localfilesystem') {\n            config.contextStorage.default.module = 'file';\n        }\n\n        config.contextStorage.default.module = config.contextStorage.default.module || 'memory';\n\n        updateContextTitle();\n\n        $contextStorageDefault.val(config.contextStorage.default.module);\n\n        $contextStorageFilePath.val(config.contextStorage.file.config.dir);\n        $contextStorageFileInterval.val(config.contextStorage.file.config.flushInterval);\n\n        $('#autorestart').val(config.restartOnCrash);\n        $('#backup').val(config.ccuBackup || 'full');\n    });\n\n    $loglevel.change(() => {\n        config.logging.ain.level = $loglevel.val();\n        save();\n    });\n\n    function updateContextTitle() {\n        switch (config.contextStorage.default.module) {\n            case 'memory':\n                $('#context-file-title').html('file');\n                $('#context-memory-title').html('default');\n                break;\n            case 'file':\n                $('#context-file-title').html('default');\n                $('#context-memory-title').html('memory');\n                break;\n            default:\n        }\n    }\n\n    $projects.change(() => {\n        config.editorTheme.projects.enabled = $projects.val() === 'true';\n        save();\n        if (config.editorTheme.projects.enabled) {\n            $projects.prop('disabled', true)\n        }\n    });\n\n    $theme.change(() => {\n        switch ($theme.val()) {\n            case '':\n            case 'default':\n                delete config.editorTheme.theme;\n                break;\n\n            default:\n                config.editorTheme.theme = $theme.val();\n                break;\n        }\n        delete config.editorTheme.page;\n        save();\n    });\n\n    $contextStorageDefault.change(() => {\n        if (!config.contextStorage) {\n            config.contextStorage = {};\n        }\n        if (!config.contextStorage.default) {\n            config.contextStorage.default = {};\n        }\n\n        config.contextStorage.default.module = $contextStorageDefault.val();\n        updateContextTitle();\n\n        save();\n    });\n\n    $contextStorageFilePath.change(() => {\n        config.contextStorage.file.config.dir = $contextStorageFilePath.val();\n        save();\n    });\n\n    $contextStorageFileInterval.change(() => {\n        config.contextStorage.file.config.flushInterval = parseInt($contextStorageFileInterval.val(), 10);\n        save();\n    });\n\n    $adminauthSessionExpiryTime.change(() => {\n        if (!config.adminAuth) {\n            config.adminAuth = {};\n        }\n        const time = parseInt($adminauthSessionExpiryTime.val(), 10) || 604800;\n        if (config.adminAuth.sessionExpiryTime !== time) {\n            config.adminAuth.sessionExpiryTime = time;\n            save();\n        }\n    });\n\n    $adminauthType.change(() => {\n        switch ($adminauthType.val()) {\n            case 'credentials':\n                $adminauthCreds.show();\n                break;\n            /*            case 'rega':\n                            $adminauthExpiry.show();\n                            $adminauthCreds.hide();\n                            $adminauthUser.val('');\n                            if (!config.adminAuth) {\n                                config.adminAuth = {};\n                            }\n                            delete config.adminAuth.users;\n                            config.adminAuth.type = 'rega';\n                            config.adminAuth.sessionExpiryTime = parseInt($adminauthSessionExpiryTime.val(), 10) || 604800;\n                            save();\n                            break;*/\n            default:\n                $adminauthExpiry.hide();\n                $adminauthCreds.hide();\n                $adminauthUser.val('');\n                delete config.adminAuth;\n                save();\n        }\n    });\n\n    $nodeauthType.change(() => {\n        switch ($nodeauthType.val()) {\n            case 'basic':\n                $nodeauthCreds.show();\n                break;\n            default:\n                $nodeauthCreds.hide();\n                $nodeauthUser.val('');\n                delete config.httpNodeAuth;\n                save();\n        }\n    });\n\n    $staticauthType.change(() => {\n        switch ($staticauthType.val()) {\n            case 'basic':\n                $staticauthCreds.show();\n                break;\n            default:\n                $staticauthCreds.hide();\n                $staticauthUser.val('');\n                delete config.httpStaticAuth;\n                save();\n        }\n    });\n\n    $adminauthSet.click(() => {\n        const user = $.trim($adminauthUser.val());\n        const pw1 = $adminauthPass1.val();\n        const pw2 = $adminauthPass2.val();\n\n        let valid = true;\n\n        if (!user) {\n            $adminauthUser.addClass('is-invalid');\n            valid = false;\n        } else {\n            $adminauthUser.removeClass('is-invalid');\n        }\n\n        if (!pw1 || pw1 !== pw2) {\n            $adminauthPass1.addClass('is-invalid');\n            $adminauthPass2.addClass('is-invalid');\n            valid = false;\n        } else {\n            $adminauthPass1.removeClass('is-invalid');\n            $adminauthPass2.removeClass('is-invalid');\n        }\n\n        if (valid) {\n            config = Object.assign(config, {\n                adminAuth: {\n                    type: 'credentials',\n                    sessionExpiryTime: parseInt($adminauthSessionExpiryTime.val(), 10) || 604800,\n                    users: [{\n                        username: user,\n                        password: bcrypt.hashSync(pw1, 8),\n                        permissions: '*'\n                    }]\n                }\n            });\n            save();\n        }\n    });\n\n    $staticauthSet.click(() => {\n        const user = $.trim($staticauthUser.val());\n        const pw1 = $staticauthPass1.val();\n        const pw2 = $staticauthPass2.val();\n\n        let valid = true;\n\n        if (!user) {\n            $staticauthUser.addClass('is-invalid');\n            valid = false;\n        } else {\n            $staticauthUser.removeClass('is-invalid');\n        }\n\n        if (!pw1 || pw1 !== pw2) {\n            $staticauthPass1.addClass('is-invalid');\n            $staticauthPass2.addClass('is-invalid');\n            valid = false;\n        } else {\n            $staticauthPass1.removeClass('is-invalid');\n            $staticauthPass2.removeClass('is-invalid');\n        }\n\n        if (valid) {\n            config = Object.assign(config, {\n                httpStaticAuth: {\n                    user,\n                    pass: bcrypt.hashSync(pw1, 8),\n                }\n            });\n            save();\n        }\n    });\n\n    $nodeauthSet.click(() => {\n        const user = $.trim($nodeauthUser.val());\n        const pw1 = $nodeauthPass1.val();\n        const pw2 = $nodeauthPass2.val();\n\n        let valid = true;\n\n        if (!user) {\n            $nodeauthUser.addClass('is-invalid');\n            valid = false;\n        } else {\n            $nodeauthUser.removeClass('is-invalid');\n        }\n\n        if (!pw1 || pw1 !== pw2) {\n            $nodeauthPass1.addClass('is-invalid');\n            $nodeauthPass2.addClass('is-invalid');\n            valid = false;\n        } else {\n            $nodeauthPass1.removeClass('is-invalid');\n            $nodeauthPass2.removeClass('is-invalid');\n        }\n\n        if (valid) {\n            config = Object.assign(config, {\n                httpNodeAuth: {\n                    user,\n                    pass: bcrypt.hashSync(pw1, 8),\n                }\n            });\n            save();\n        }\n    });\n\n    function restart() {\n        clearTimeout(psTimeout);\n        $dropdownRestart.addClass('disabled');\n        $stop.addClass('disabled');\n        $dropdownStart.addClass('disabled');\n        $('#status-spinner').show();\n        $status.html('<span class=\"status-starting\">stopping</span>');\n        $memory.html('');\n        $cpu.html('');\n        $.get({\n            url: `service.cgi?sid=${sid}&cmd=restart`,\n            success: data => {\n                if (data.match(/Starting Node-RED: OK/)) {\n                    alert($alertExec);\n                } else if ($.trim(data) === 'error: invalid session') {\n                    invalidSession();\n                    return;\n                } else {\n                    alert($alertError);\n                }\n                psInterval = 1000;\n                setTimeout(() => {\n                    ps();\n                }, 1000);\n            }\n        });\n        setTimeout(() => {\n            ps();\n        }, 10000);\n\n    }\n\n    $restart.click(() => {\n        restart();\n    });\n\n    function safeMode(cb) {\n        $.get({\n            url: `safemode.cgi?sid=${sid}`,\n            success: cb\n        });\n    }\n\n    $restartSafe.click(() => {\n        safeMode(restart);\n    });\n\n    function start() {\n        $dropdownRestart.addClass('disabled');\n        $stop.addClass('disabled');\n        $dropdownStart.addClass('disabled');\n        $status.html('<span class=\"status-starting\">starting</span>');\n        $memory.html('');\n        $.get({\n            url: `service.cgi?sid=${sid}&cmd=start`,\n            success: data => {\n                if ($.trim(data) === 'error: invalid session') {\n                    invalidSession();\n                    return;\n                }\n\n                if (data.match(/Starting Node-RED: OK/)) {\n                    psInterval = 2000;\n                    setTimeout(() => {\n                        ps();\n                    }, 6000);\n                    alert($alertExec);\n                } else {\n                    alert($alertError);\n                }\n            }\n        });\n    }\n\n    $start.click(() => {\n        start();\n    });\n\n    $startSafe.click(() => {\n        safeMode(start);\n    });\n\n    $stop.click(() => {\n        clearTimeout(psTimeout);\n        $dropdownRestart.addClass('disabled');\n        $stop.addClass('disabled');\n        $dropdownStart.addClass('disabled');\n        $status.html('<span class=\"status-starting\">stopping</span>');\n        $memory.html('');\n        $.get({\n            url: `service.cgi?sid=${sid}&cmd=stop`,\n            success: data => {\n                if ($.trim(data) === 'error: invalid session') {\n                    invalidSession();\n                    return;\n                }\n                if (data.match(/Stopping Node-RED: OK/)) {\n                    alert($alertExec);\n                    psInterval = 2000;\n                    setTimeout(() => {\n                        ps();\n                    }, 6000);\n                } else {\n                    alert($alertError);\n                }\n            }\n        });\n    });\n\n    function setHeader(xhr) {\n        xhr.setRequestHeader('accept', 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*');\n    }\n\n    $.get('restart_count', data => {\n        $('#restarts').html(data || 'keine');\n    });\n\n    function download(filename, dataUrl) {\n        let link = document.createElement(\"a\");\n        link.download = filename;\n        link.target = \"_blank\";\n\n        link.href = dataUrl;\n        document.body.appendChild(link);\n        link.click();\n\n        document.body.removeChild(link);\n    }\n\n    $('#log').on('click', () => {\n        download('redmatic.' + (new Date()).toISOString() + '.log', 'log.cgi' + location.search);\n    });\n\n    function logUpload() {\n        $('#log-upload-spinner').show();\n        $('#log-upload').addClass('disabled');\n        $.post({\n            url: 'setnick.cgi' + location.search,\n            data: $.trim($('#nickname').val().toLowerCase()),\n            success: function (data) {\n                if ($.trim(data) === 'ok') {\n                    $.get({\n                        url: 'logupload.cgi?sid=' + sid,\n                        success: data => {\n                            $('#log-upload-spinner').hide();\n                            $('#log-upload').removeClass('disabled');\n                            $('#log-name').html(data);\n                            $('#modal-upload').modal('show')\n                        }\n                    }).fail(() => {\n                        $('#log-upload').removeClass('disabled');\n                        $('#log-upload-spinner').hide();\n                        alert($alertError);\n                    });\n                } else {\n                    $('#log-upload').removeClass('disabled');\n                    $('#log-upload-spinner').hide();\n                    if ($.trim(data) === 'error: invalid session') {\n                        invalidSession();\n                        return;\n                    }\n                    alert($alertError);\n                }\n            }\n        }).fail(() => {\n            alert($alertError);\n            $('#log-upload').removeClass('disabled');\n            $('#log-upload-spinner').hide();\n        });\n    }\n\n    $('#log-upload').on('click', () => {\n        $('#modal-nickname').modal('show');\n    });\n\n    $('#upgrade-log').on('click', () => {\n        download('redmatic-pkg-upgrade.' + (new Date()).toISOString() + '.log', 'log.cgi' + location.search + '&cmd=upgrade');\n    });\n\n    $('#autorestart').on('change', event => {\n        config.restartOnCrash = parseInt(event.target.value, 10);\n        save();\n    });\n\n    $backup.on('change', () => {\n        config.ccuBackup = $backup.val();\n        save();\n    });\n\n    $('#discard-package-hint').on('click', () => {\n        localStorage.setItem('package-hint', 'discarded');\n        $('#package-hint').hide();\n    });\n    if (localStorage.getItem('package-hint') !== 'discarded') {\n        $('#package-hint').show();\n    }\n\n    $('#package-filter').on('keyup', () => {\n        const filter = $('#package-filter').val();\n        $packageTable.find('tr').each(function () {\n            const name = $(this).find('td').html().toLowerCase();\n            if (name.includes(filter.toLowerCase())) {\n                $(this).show();\n            } else {\n                $(this).hide();\n            }\n        });\n    });\n\n});\n\n",
        "output": "str",
        "x": 440,
        "y": 300,
        "wires": [
            [
                "07089cae74365102"
            ]
        ]
    },
    {
        "id": "07089cae74365102",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "www/js/script.js",
        "filename": "/usr/local/addons/redmatic/www/js/script.js",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 700,
        "y": 300,
        "wires": [
            [
                "aecdcf17e967759b"
            ]
        ]
    },
    {
        "id": "3b213cd16eeb1a66",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "redmaticLoader",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "plain",
        "template": "# Henke Version 1.2\n#!/bin/sh\n\nlogger -t redmatic -p daemon.warn \"Henke Script redmaticLoader\"\n\nCONF_DIR=/usr/local/etc/config\nADDON_DIR=/usr/local/addons/redmatic\nWWW_DIR=/usr/local/etc/config/addons/www/redmatic\n\ncd $ADDON_DIR/bin\n# Henke\nif [ -f /usr/bin/node ]; then\n\tNODE=/usr/bin/node\nfi\nif [ -f /usr/local/bin/node ]; then\n\tNODE=/usr/local/bin/node\nfi\n\n# NODE=$ADDON_DIR/bin/node\n\nexport PATH=$ADDON_DIR/bin:$PATH\nexport LD_LIBRARY_PATH=$ADDON_DIR/lib:$LD_LIBRARY_PATH\nexport HOME=$ADDON_DIR/home\n\nexport NO_UPDATE_NOTIFIER=true\n\nRED_DIR=$ADDON_DIR/lib/node_modules/node-red\nRED=$RED_DIR/red.js\n\nSETTINGS=$ADDON_DIR/lib/settings.js\n\nstatus=1\nrestarts=0\n\nlogger -t redmatic -p daemon.warn \"Henke checkNR start - $NODE\"\nnode checkNR.js\nsource /tmp/red-settings\nlogger -t redmatic -p daemon.warn \"Henke checkNR done $RedSettings_NodeRedUpdate\"\n#logger -t redmatic -p daemon.warn \"NodeRedUpdate$RedSettings_NodeRedUpdate\"\nif [[ $RedSettings_NodeRedUpdate != \"false\" ]]; then\n  logger -t redmatic -p daemon.warn \"Update NodeRed\"\n$ADDON_DIR/bin/updateNodeRed.sh\nfi\n\n#echo \"RR $RedSettings_RestartOnCrash\"\n#echo \"BB $RedSettings_Test\"\n#limit=`jq '.restartOnCrash' $ADDON_DIR/etc/settings.json`\n\nif [[ -z $RedSettings_RestartOnCrash ]]; then\n  RedSettings_RestartOnCrash = 0\nfi\n\nwhile [[ $status != 0 ]]; do\n  echo $restarts > $ADDON_DIR/var/restart_count\n  if [[ $restarts -gt 0 ]]; then\n    echo \"Restarting Node-RED ($restarts/$RedSettings_RestartOnCrash)\" | logger -p daemon.warn -t redmatic\n  else\n    echo \"Starting Node-RED\" | logger -p daemon.info -t redmatic\n  fi\n  if [[ -f $ADDON_DIR/var/safe_mode ]]; then\n    SAFE=\"--safe\"\n    rm $ADDON_DIR/var/safe_mode\n  fi\n  set -o pipefail\n# Henke add --max-old-space-size=256 --max-semi-space-size=8 --trace-deprecation --trace-warnings \n  $NODE $RED $SAFE -s $SETTINGS 2>&1 | logger -p daemon.err -t node-red\n  status=$?\n  if [[ $status != 0 ]]; then\n    echo \"Node-RED exited with non-zero exit status $status\" | logger -p daemon.err -t node-red\n    let \"restarts=restarts+1\"\n    if [[ $restarts -gt $RedSettings_RestartOnCrash ]]; then\n        if [[ $RedSettings_RestartOnCrash -gt 0 ]]; then\n            echo \"Maximum Node-RED restarts exceeded\" | logger -p daemon.err -t redmatic\n        fi\n        status=0\n    fi\n  fi\ndone\n",
        "output": "str",
        "x": 980,
        "y": 180,
        "wires": [
            [
                "19200cbb67cc50b9"
            ]
        ]
    },
    {
        "id": "19200cbb67cc50b9",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "bin/redmaticLoader",
        "filename": "/usr/local/addons/redmatic/bin/redmaticLoader",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1190,
        "y": 180,
        "wires": [
            [
                "fa72f43dd0a8948b"
            ]
        ]
    },
    {
        "id": "e435bc41d8c9739d",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "redmatic",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "plain",
        "template": "#!/bin/sh\n\n# Henke\nlogger -t redmatic -p daemon.info \"Henke Script redmatic $1\"\n\nCONF_DIR=/usr/local/etc/config\nADDON_DIR=/usr/local/addons/redmatic\nWWW_DIR=/usr/local/etc/config/addons/www/redmatic\n\n# Henke NODE=$ADDON_DIR/bin/node\n\nSETTINGS=$ADDON_DIR/lib/settings.js\nRED_DIR=$ADDON_DIR/lib/node_modules/node-red\nRED=$RED_DIR/red.js\n\nLIGHTTPD_CONF=/etc/lighttpd/lighttpd.conf\nLIGHTTPD_SSL_CONF=/etc/lighttpd/lighttpd_ssl.conf\n#BACKUP_CGI=/www/config/cp_security.cgi\n\nexport PATH=$ADDON_DIR/bin:$PATH\nexport LD_LIBRARY_PATH=/usr/local/addons/redmatic/lib:/usr/lib:/usr/local/lib\nexport GIT_EXEC_PATH=/usr/local/addons/redmatic/libexec/git-core\nexport NO_UPDATE_NOTIFIER=true\n\nStop () {\n    if [ -f /usr/bin/monit ]; then\n        /usr/bin/monit -g redmatic unmonitor\n    fi\n\n    PSPID=`ps -o pid,comm,args | awk '{if($3 == \"node-red\" || $4 ~ /node-red/){print $1}}'`\n    if [ \"$PSPID\" != \"\" ]; then\n        echo -n \"Stopping Node-RED: \"\n        logger -t redmatic -p daemon.info \"Stopping Node-RED\"\n\n        killall redmaticLoader 2>/dev/null\n        kill -SIGINT $PSPID 2>/dev/null\n        sleep 2\n        if kill -0 $PSPID 2> /dev/null\n        then\n            sleep 4\n            if kill -0 $PSPID 2> /dev/null\n            then\n                logger -t redmatic -p daemon.warn \"Killing Node-RED\"\n                kill -SIGKILL $PSPID 2>/dev/null\n            fi\n        fi\n        echo \"OK\"\n        logger -t redmatic -p daemon.info \"Node-RED stopped\"\n        return 0\n    else\n        echo \"Node-RED not running\"\n        return 1\n    fi\n}\n\nStart () {\n#    if [ `$ADDON_DIR/bin/jq -r '.ccuBackup' $ADDON_DIR/etc/settings.json` = \"full\" ]; then\nrm $ADDON_DIR/etc/.nobackup 2>/dev/null\nrm $ADDON_DIR/bin/.nobackup 2>/dev/null\nrm $ADDON_DIR/include/.nobackup 2>/dev/null\nrm $ADDON_DIR/lib/.nobackup 2>/dev/null\nrm $ADDON_DIR/libexec/.nobackup 2>/dev/null\nrm $ADDON_DIR/share/.nobackup 2>/dev/null\nrm $ADDON_DIR/tmp/.nobackup 2>/dev/null\nrm $ADDON_DIR/var/node_modules/.nobackup 2>/dev/null\nrm $ADDON_DIR/www/.nobackup 2>/dev/null\n#    else\n#        touch $ADDON_DIR/bin/.nobackup\n#        touch $ADDON_DIR/include/.nobackup\n#        touch $ADDON_DIR/lib/.nobackup\n#        touch $ADDON_DIR/libexec/.nobackup\n#        touch $ADDON_DIR/share/.nobackup\n#        touch $ADDON_DIR/tmp/.nobackup\n#        touch $ADDON_DIR/var/node_modules/.nobackup\n#        touch $ADDON_DIR/www/.nobackup\n#    fi\n\n#    if [ -f $ADDON_DIR/var/do_pkg_upgrade ]; then\n#        echo \"Updating Packages\"\n#        date +\"%b %d %H:%M:%S\" >> $ADDON_DIR/var/pkg-upgrade.log\n#        set -o pipefail\n#        $ADDON_DIR/bin/redmatic-pkg upgrade 2>&1 | tee -a $ADDON_DIR/var/pkg-upgrade.log | logger -t redmatic-pkg -p daemon.info && rm $ADDON_DIR/var/do_pkg_upgrade\n#        echo \"\" >> $ADDON_DIR/var/pkg-upgrade.log\n#        sleep 1\n#    fi\n\n    if ! grep -Fq \"/etc/config/lighttpd/\" $LIGHTTPD_CONF\n    then\n      echo \"patching $LIGHTTPD_CONF\"\n      mount -o remount,rw /\n      cp $LIGHTTPD_CONF $LIGHTTPD_CONF.orig\n      echo \"include_shell \\\"test -d /etc/config/lighttpd && cat /etc/config/lighttpd/*.conf\\\"\" >> $LIGHTTPD_CONF\n      mount -o remount,ro /\n      /etc/init.d/S50lighttpd restart\n    fi\n\n    if [ -f $LIGHTTPD_SSL_CONF ]\n    then\n        if ! grep -Fq \"/etc/config/lighttpd/\" $LIGHTTPD_SSL_CONF\n        then\n          echo \"patching $LIGHTTPD_SSL_CONF\"\n          mount -o remount,rw /\n          cp $LIGHTTPD_SSL_CONF $LIGHTTPD_SSL_CONF.orig\n          echo \"include_shell \\\"test -d /etc/config/lighttpd && cat /etc/config/lighttpd/*.conf\\\"\" >> $LIGHTTPD_SSL_CONF\n          mount -o remount,ro /\n          /etc/init.d/S50lighttpd restart\n        fi\n    fi\n\n#    if ! grep -Fq \"exclude-tag=.nobackup\" $BACKUP_CGI\n#    then\n#        echo \"patching $BACKUP_CGI\"\n#        mount -o remount,rw /\n#        cp $BACKUP_CGI $BACKUP_CGI.orig\n#        sed \"s/exec tar czf \\/tmp\\/usr_local.tar.gz usr\\/local/exec tar --exclude-tag=.nobackup -czf \\/tmp\\/usr_local.tar.gz usr\\/local/\" $BACKUP_CGI > $BACKUP_CGI.tmp && mv $BACKUP_CGI.tmp $BACKUP_CGI\n#        chmod 755 $BACKUP_CGI\n#        mount -o remount,ro /\n#    fi\n\n    if [ ! -f /etc/config/rdmtc.uuid ]; then\n        /usr/bin/uuidgen > /etc/config/rdmtc.uuid\n    fi\n    LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/usr/local/addons/redmatic/lib ; /usr/local/addons/redmatic/bin/redmaticVersions | /usr/bin/curl -H \"Content-Type: application/json\" -H \"X-RedMatic-uuid: `cat /etc/config/rdmtc.uuid`\" --data @- -s --max-time 3 https://telemetry.redmatic.de\n\n    PSPID=`ps -o pid,comm,args | awk '{if($3 == \"node-red\" || $4 ~ /node-red/){print $1}}'`\n    if [ \"$PSPID\" != \"\" ]; then\n        echo \"Node-RED already running\"\n        logger -t redmatic -p daemon.error \"cant start - already running\"\n        exit 1\n    else\n        upSeconds=\"$(cat /proc/uptime | grep -o '^[0-9]\\+')\"\n        if [ \"${upSeconds}\" -lt \"120\" ]; then\n              logger -t redmatic -p daemon.info \"Starting Node-RED after reboot ... waiting 30 seconds...\"\n              echo \"Starting Node-RED ... waiting 30 seconds...\"\n              sleep 30\n        fi\n        echo -n \"Starting Node-RED: \"\n        source $ADDON_DIR/versions\n        logger -t redmatic -p daemon.info \"RedMatic v$VERSION_ADDON (c) Sebastian Raff https://github.com/rdmtc/RedMatic - Patch Henke\"\n        start-stop-daemon -S -q -b --exec $ADDON_DIR/bin/redmaticLoader\n        echo \"OK\"\n    fi\n\n    if [ -f /usr/bin/monit ]; then\n        nohup $ADDON_DIR/bin/monit-start > /dev/null 2>&1 &\n    fi\n}\n\ncase \"$1\" in\n\n    stop)\n        Stop || exit 1\n    ;;\n\n    start)\n        Start\n    ;;\n\n    restart)\n        Stop\n        sleep 10\n        Start\n    ;;\n\n    info)\n        source $ADDON_DIR/versions\n        echo \"Info: <div><a target=\\\"_blank\\\" href=\\\"https://github.com/rdmtc/RedMatic\\\"><img src=\\\"/addons/redmatic/redmatic5-wide.png\\\" height=\\\"48\\\"/></a></div>\"\n        echo \"Name: RedMatic\"\n        echo \"Version: $VERSION_ADDON\"\n#        echo \"Update: /addons/redmatic/update_check.cgi\"\n        echo \"Config-Url: /addons/redmatic/settings.cgi\"\n        echo \"Operations: restart uninstall\"\n    ;;\n\n    uninstall)\n        Stop\n\n        $ADDON_DIR/bin/update_addon redmatic\n        rm -r $ADDON_DIR\n        rm -r $WWW_DIR\n        rm $CONF_DIR/lighttpd/redmatic.conf\n        rm $CONF_DIR/rc.d/redmatic\n\n        if [ -f /usr/local/etc/monit-redmatic.cfg ]; then\n            rm /usr/local/etc/monit-redmatic.cfg\n            /usr/bin/monit reload\n        fi\n\n        mount -o remount,rw /\n        if [ -f $LIGHTTPD_CONF.orig ]; then\n            mv $LIGHTTPD_CONF.orig $LIGHTTPD_CONF\n        fi\n        if [ -f $LIGHTTPD_SSL_CONF.orig ]; then\n            mv $LIGHTTPD_SSL_CONF.orig $LIGHTTPD_SSL_CONF\n        fi\n#        if [ -f $BACKUP_CGI.orig ]; then\n#            mv $BACKUP_CGI.orig $BACKUP_CGI\n#        fi\n        mount -o remount,ro /\n\n        logger -t redmatic -p daemon.info \"Uninstalled RedMatic\"\n    ;;\n\n    *)\n        echo \"Usage: node-red {start|stop|restart|info|uninstall}\" >&2\n        exit 1\n    ;;\n\nesac\n\nexit 0\n",
        "output": "str",
        "x": 440,
        "y": 180,
        "wires": [
            [
                "7def8072ba56d9d1"
            ]
        ]
    },
    {
        "id": "7def8072ba56d9d1",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "bin/redmatic",
        "filename": "/usr/local/addons/redmatic/bin/redmatic",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 690,
        "y": 180,
        "wires": [
            [
                "3b213cd16eeb1a66"
            ]
        ]
    },
    {
        "id": "a0ee823d1b868b33",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "settings.js",
        "field": "payload",
        "fieldType": "msg",
        "format": "javascript",
        "syntax": "plain",
        "template": "const fs = require('fs');\n\nconst addOn = '/usr/local/addons/redmatic/';\nconst etcSettings = addOn + 'etc/settings.json';\n\nconst defaults = require(addOn + 'lib/node_modules/node-red/settings.js');\nconst settings = require(etcSettings);\nconst logging = require(addOn + 'lib/logger.js');\n\n// Migration\nif (settings.logging.console) {\n    settings.logging.ain = settings.logging.console;\n    delete settings.logging.console;\n    fs.writeFileSync(etcSettings, JSON.stringify(settings, null, '  '));\n}\n\n// Credentials encryption key\nif (fs.existsSync(addOn + 'etc/credentials.key')) {\n    settings.credentialSecret = fs.readFileSync(addOn + 'etc/credentials.key').toString();\n}\n\n// Logging\ndelete defaults.logging.console;\nObject.assign(logging.logging.ain, settings.logging.ain);\n\n// Enable Projects Feature\nif (!defaults.editorTheme) {\n    defaults.editorTheme = {};\n}\nif (!defaults.editorTheme.projects) {\n    defaults.editorTheme.projects = {};\n}\ndefaults.editorTheme.projects.enabled = defaults.editorTheme.projects.enabled || false;\n\n// Auskommentiert, M. Henke\n// Inject sessionExpiryTime to Rega Authentication\nif (settings.adminAuth && settings.adminAuth.type === 'rega') {\n    //    const regaAuth = require(addOn + 'lib/rega-auth.js');\n    delete settings.adminAuth;\n    console.log(\"ReGa Passworte nicht mehr unterstützt\");\n    /*    if (settings.adminAuth.sessionExpiryTime) {\n            regaAuth.sessionExpiryTime = settings.adminAuth.sessionExpiryTime;\n        }\n        settings.adminAuth = regaAuth;*/\n}\n\n// M. Henke\nif (settings.httpRoot) {\n    settings.httpAdminRoot = settings.httpRoot;\n    settings.httpNodeRoot = settings.httpRoot;\n    delete settings.httpRoot;\n}\nif (!settings.runtimeState)\n    settings.runtimeState = {};\nif (settings.runtimeState) {\n//    settings.runtimeState.enabled = true;\n    settings.runtimeState.ui = true;\n}\n\n// Context Storage\nif (!settings.contextStorage) {\n    settings.contextStorage = {};\n}\nif (!settings.contextStorage.default) {\n    settings.contextStorage.default = {};\n}\nif (!settings.contextStorage.default.module) {\n    settings.contextStorage.default.module = 'memory';\n}\nif (settings.contextStorage.default.module === 'localfilesystem') {\n    settings.contextStorage.default.module = 'file';\n}\n\nif (settings.contextStorage.default.module !== 'file' && settings.contextStorage.default.module !== 'memory') {\n    settings.contextStorage.default.module = 'memory';\n}\n\nif (!settings.contextStorage.memory) {\n    settings.contextStorage.memory = {\n        'module': 'memory'\n    }\n}\nif (!settings.contextStorage.file) {\n    settings.contextStorage.file = {\n        'module': 'localfilesystem',\n        config: {\n            dir: addOn + 'var',\n            flushInterval: 300\n        }\n    }\n}\n\nconst defaultContextStorage = Object.assign({}, settings.contextStorage[settings.contextStorage.default.module]);\ndelete settings.contextStorage[settings.contextStorage.default.module];\nsettings.contextStorage.default = defaultContextStorage;\n\nconst result = Object.assign(\n    defaults,\n    settings,\n    logging\n);\n\nfs.writeFileSync('/tmp/red-settings.json', JSON.stringify(result));\n\nmodule.exports = result;\n",
        "output": "str",
        "x": 440,
        "y": 260,
        "wires": [
            [
                "773c8ac10017ce00"
            ]
        ]
    },
    {
        "id": "773c8ac10017ce00",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "lib/settings.js",
        "filename": "/usr/local/addons/redmatic/lib/settings.js",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 690,
        "y": 260,
        "wires": [
            [
                "775f2a96c0c5bcc3"
            ]
        ]
    },
    {
        "id": "775f2a96c0c5bcc3",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "versions",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "mustache",
        "template": "# Henke\nexport VERSION_ADDON={{VersionAddOn}}\n",
        "output": "str",
        "x": 960,
        "y": 260,
        "wires": [
            [
                "ef6f947e639c52b9"
            ]
        ]
    },
    {
        "id": "ef6f947e639c52b9",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "versions",
        "filename": "/usr/local/addons/redmatic/versions",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1160,
        "y": 260,
        "wires": [
            [
                "3d2fc855a49dc0ab"
            ]
        ]
    },
    {
        "id": "aecdcf17e967759b",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "settings.html",
        "field": "payload",
        "fieldType": "msg",
        "format": "html",
        "syntax": "plain",
        "template": "<!DOCTYPE html>\n<meta charset=\"UTF-8\">\n\n<title>RedMatic</title>\n\n<script src=\"node_modules/jquery/dist/jquery.min.js\"></script>\n<script src=\"node_modules/bcryptjs/dist/bcrypt.min.js\"></script>\n<script src=\"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js\"></script>\n\n<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"apple-icon-180x180.png\">\n<link rel=\"icon\" type=\"image/png\" sizes=\"96x96\" href=\"favicon-96x96.png\">\n\n<link rel=\"stylesheet\" href=\"node_modules/bootstrap/dist/css/bootstrap.min.css\">\n<link rel=\"stylesheet\" href=\"node_modules/@fortawesome/fontawesome-free/css/all.css\">\n<link rel=\"stylesheet\" href=\"css/style.css\">\n\n<div id=\"invalidSession\">\n    Sitzung ung&uuml;ltig. Bitte diese Seite schlie&szlig;en.\n</div>\n\n<nav class=\"navbar sticky-top navbar-light bg-light mb-4\">\n    <a href=\"https://github.com/rdmtc/RedMatic\" target=\"_blank\"><img src=\"redmatic5-compact.png\"\n            style=\"height: 42px; \"></a>\n\n    <ul class=\"nav navbar-nav mr-auto d-flex flex-row mt-2\" role=\"tablist\" id=\"myTab\">\n        <li class=\"nav-item ml-3\">\n            <a class=\"nav-link pb-0\" id=\"configuration-tab\" data-toggle=\"tab\" role=\"tab\" aria-controls=\"configuration\"\n                aria-selected href=\"#configuration\">Konfiguration</a>\n        </li>\n<!-- Henke\n                                    <li class=\"nav-item ml-3\">\n                                        <a class=\"nav-link pb-0\" id=\"packages-tab\" data-toggle=\"tab\" role=\"tab\" aria-controls=\"packages\" aria-selected href=\"#packages\">Pakete</a>\n                                    </li>\n//-->\n        <li class=\"nav-item ml-3\">\n            <a class=\"nav-link pb-0\" id=\"versions-tab\" data-toggle=\"tab\" role=\"tab\" aria-controls=\"versions\"\n                aria-selected href=\"#versions\">Debug</a>\n        </li>\n        <li class=\"nav-item ml-3\">\n            <a class=\"nav-link pb-0\" id=\"licenses-tab\" data-toggle=\"tab\" role=\"tab\" aria-controls=\"licenses\"\n                href=\"#licenses\">Lizenzen</a>\n        </li>\n    </ul>\n    <div class=\"ml-3 mr-2 pl-2\">\n        <a id=\"link-red\" role=\"button\" class=\"btn btn-sm btn-outline-info\" target=\"_blank\" href=\"/addons/red/\"><i\n                class=\"fa fa-external-link-alt\"></i> Node-RED</a>\n        <a id=\"link-ui\" role=\"button\" class=\"btn btn-sm btn-outline-info mr-3\" target=\"_blank\" href=\"/addons/red/ui/\"><i\n                class=\"fa fa-external-link-alt\"></i> Dashboard</a>\n    </div>\n    <div style=\"min-width: 130px\" id=\"redmatic-version\"></div>\n</nav>\n\n<div id=\"container\" class=\"container\">\n\n    <div id=\"alert-saved\" style=\"display: none;\" class=\"fixed-top mx-auto alert alert-success alert-dismissible fade\"\n        role=\"alert\">\n        <strong>Settings gespeichert, Node-RED Neustart notwendig.</strong>\n    </div>\n\n    <div id=\"alert-error\" style=\"display: none;\" class=\"fixed-top mx-auto alert alert-error alert-dismissible fade\"\n        role=\"alert\">\n        <strong>Ein Fehler ist aufgetreten</strong>\n    </div>\n\n    <div id=\"alert-exec\" style=\"display: none;\" class=\"fixed-top mx-auto alert alert-success alert-dismissible fade\"\n        role=\"alert\">\n        <strong>Kommando ausgef&uuml;hrt</strong>\n    </div>\n\n    <div id=\"modal-upload\" class=\"modal\" tabindex=\"-1\" role=\"dialog\">\n        <div class=\"modal-dialog\" role=\"document\">\n            <div class=\"modal-content\">\n                <div class=\"modal-header\">\n                    <h5 class=\"modal-title\">Log Datei erfolgreich hochgeladen</h5>\n                    <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\">\n                        <span aria-hidden=\"true\">&times;</span>\n                    </button>\n                </div>\n                <div class=\"modal-body\">\n                    <p>Log Datei <strong><span id=\"log-name\"></span></strong> hochgeladen</p>\n                </div>\n                <div class=\"modal-footer\">\n                    <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Schliessen</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <div id=\"modal-nickname\" class=\"modal\" tabindex=\"-1\" role=\"dialog\">\n        <div class=\"modal-dialog\" role=\"document\">\n            <div class=\"modal-content\">\n                <div class=\"modal-header\">\n                    <h5 class=\"modal-title\">Log Versand</h5>\n                    <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\">\n                        <span aria-hidden=\"true\">&times;</span>\n                    </button>\n                </div>\n                <div class=\"modal-body\">\n                    <input id=\"nickname\" placeholder=\"Nickname\" type=\"text\" class=\"form-control\" style=\"width: 240px;\">\n                </div>\n                <div class=\"modal-footer\">\n                    <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Abbrechen</button>\n                    <button type=\"button\" class=\"btn btn-primary\" id=\"log-upload-do\">Log versenden</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"tab-content\" id=\"myTabContent\">\n        <div class=\"tab-pane fade show\" id=\"configuration\" role=\"tabpanel\" aria-labelledby=\"configuration-tab\">\n\n<!-- Henke\n            <div id=\"update-notify\" class=\"card mb-4\" style=\"border-color: orange; display: none;\">\n                <div class=\"card-body\">\n                    RedMatic Update steht zur Verf&uuml;gung: <span id=\"update-link\"></span> (siehe <a\n                        href=\"https://github.com/rdmtc/RedMatic/wiki/Update\" target=\"_blank\">Installationsanleitung\n                        im RedMatic Wiki</a>)\n                </div>\n            </div>\n//-->\n\n            <div class=\"card mb-4\">\n\n                <div class=\"card-body\">\n                    <label style=\"display: block\">Node-RED Prozess</label>\n                    <!--<a id=\"backup\" role=\"button\" class=\"btn btn-sm btn-primary mr-3 disabled\">Backup</a>-->\n                    <div class=\"dropdown\" style=\"display: inline-block\">\n                        <button type=\"button\" class=\"btn btn-sm btn-warning dropdown-toggle\" id=\"dropdownRestart\"\n                            data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n                            <i class=\"fa fa-sync-alt\"></i> Neustart\n                        </button>\n\n                        <div class=\"dropdown-menu\" aria-labelledby=\"dropdownMenuButton\">\n                            <a id=\"restart\" class=\"dropdown-item\" href=\"#\">Normal</a>\n                            <a id=\"restartSafe\" class=\"dropdown-item\" href=\"#\">Safe Mode</a>\n                        </div>\n                    </div>\n\n                    <button id=\"stop\" type=\"button\" class=\"btn btn-sm btn-warning\"><i class=\"fa fa-stop\"></i>\n                        Stop</button>\n\n                    <div class=\"dropdown\" style=\"display: inline-block\">\n                        <button type=\"button\" class=\"btn btn-sm btn-warning dropdown-toggle\" id=\"dropdownStart\"\n                            data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n                            <i class=\"fa fa-play\"></i> Start\n                        </button>\n\n                        <div class=\"dropdown-menu\" aria-labelledby=\"dropdownMenuButton\">\n                            <a id=\"start\" class=\"dropdown-item\" href=\"#\">Normal</a>\n                            <a id=\"startSafe\" class=\"dropdown-item\" href=\"#\">Safe Mode</a>\n                        </div>\n                    </div>\n\n                    <div style=\"width: 30px; display: inline-block\">\n                        <div id=\"status-spinner\" class=\"spinner-border spinner-border-sm ml-2 mr-2\" role=\"status\"\n                            style=\"color: #6a737d; display: inline-block;\">\n                            <span class=\"sr-only\">Please wait...</span>\n                        </div>\n                    </div>\n\n                    <div style=\"vertical-align: middle; display: inline-block; width: 210px; font-size: 11px; color: grey;\"\n                        class=\"\">\n                        <div><span id=\"node-red-status\" style=\"font-weight: bold;\"></span></div>\n                        <div>\n                            <span id=\"node-red-cpu\" style=\"font-weight: bold;\"></span><span id=\"node-red-memory\"\n                                style=\"font-weight: bold;\"></span>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n<!-- Henke\n            <div class=\"card mb-4 pr-4\">\n                <div class=\"card-body\">\n                    <div class=\"form-group\">\n                        <label for=\"projects\">Backup <a href=\"https://github.com/rdmtc/RedMatic/wiki/Backup\"\n                                class=\"help-link\" target=\"_blank\"><i class=\"far fa-question-circle\"></i></a></label>\n                        <select class=\"form-control\" id=\"backup\">\n                            <option value=\"reduce\">Reduziere Backupgrö&szlig;e - nur relevante Daten inkludieren\n                            </option>\n                            <option value=\"full\">Alles im Backup inkludieren - Warnung: das kann u.U. zu Problemen\n                                mit dem Backup/Restore f&uuml;hren!</option>\n                        </select>\n                    </div>\n                </div>\n            </div>\n//-->\n\n            <div class=\"card mb-4 pr-4\">\n                <div class=\"card-body\">\n                    <div class=\"form-group\">\n                        <label>Context Storage <a href=\"https://github.com/rdmtc/RedMatic/wiki/Context-Storage\"\n                                class=\"help-link\" target=\"_blank\"><i class=\"far fa-question-circle\"></i></a></label>\n                        <div class=\"row mb-2\">\n                            <div class=\"col-sm-3\" style=\"line-height: 38px;\">\n                                default\n                            </div>\n                            <div class=\"col-sm-9\">\n                                <select class=\"form-control\" id=\"context-storage-default\">\n                                    <option>memory</option>\n                                    <option>file</option>\n                                </select>\n                            </div>\n                        </div>\n                        <div class=\"row mb-2\">\n                            <div class=\"col-sm-3\" style=\"line-height: 38px;\">\n                                <code id=\"context-memory-title\">memory</code>\n                            </div>\n                            <div class=\"col-sm-9\"\n                                style=\"min-height: 26px; padding: 6px 22px; vertical-align: middle; color: grey; font-style: italic;\">\n                                memory\n                            </div>\n                        </div>\n\n                        <div class=\"row mb-2\">\n                            <div class=\"col-sm-3\" style=\"line-height: 38px;\">\n                                <code id=\"context-file-title\">file</code>\n                            </div>\n                            <div class=\"col-sm-6\">\n                                <input class=\"form-control\" id=\"context-storage-file-path\" placeholder=\"Path\" required>\n                            </div>\n                            <div class=\"col-sm-3\">\n                                <input class=\"form-control\" type=\"number\" id=\"context-storage-file-interval\"\n                                    placeholder=\"Schreibintervall\">\n                            </div>\n                        </div>\n\n                    </div>\n                </div>\n            </div>\n\n            <div class=\"card mb-4 pr-4\">\n                <div class=\"card-body\">\n                    <div class=\"form-group\">\n                        <label for=\"projects\">Projekte <a href=\"https://github.com/rdmtc/RedMatic/wiki/Projekte\"\n                                class=\"help-link\" target=\"_blank\"><i class=\"far fa-question-circle\"></i></a></label>\n                        <select class=\"form-control\" id=\"projects\">\n                            <option value=\"false\">Nicht verwenden</option>\n                            <option value=\"true\">Aktiv</option>\n                        </select>\n                    </div>\n                </div>\n            </div>\n\n            <div class=\"card mb-4 pr-4\">\n                <div class=\"card-body\">\n                    <div class=\"form-group\">\n                        <label for=\"theme\">Theme</label>\n                        <select class=\"form-control\" id=\"theme\">\n                            <option value=\"default\">standard</option>\n                            <option value=\"midnight-red\">midnight-red</option>\n                            <option value=\"totallyinformation\">totallyinformation</option>\n                        </select>\n                    </div>\n                </div>\n            </div>\n\n            <div class=\"card mb-4\">\n                <div class=\"card-body\">\n                    <div class=\"form-group\">\n                        <label>Authentifizierung <a href=\"https://github.com/rdmtc/RedMatic/wiki/Passwort\"\n                                class=\"help-link\" target=\"_blank\"><i class=\"far fa-question-circle\"></i></a></label>\n                        <div class=\"row mb-3\">\n                            <div class=\"col-sm-3\" style=\"line-height: 38px;\">\n                                Admin\n                            </div>\n                            <div class=\"col-sm-9\">\n                                <div class=\"row mb-2 col-sm-12\">\n                                    <select class=\"form-control\" id=\"adminauth-type\">\n                                        <option value=\"none\">Keine Authentifizierung</option>\n                                        <option value=\"credentials\">Username/Passwort individuell</option>\n                                        <!--                                        <option value=\"rega\">ReGaHSS (CCU WebUI User nutzen)</option> -->\n                                    </select>\n                                </div>\n                                <div id=\"adminauth-expiry\" class=\"col-sm-12 row mb-1\" style=\"display: none\">\n                                    <label class=\"col-sm-6 mt-2\" for=\"adminauth-sessionExpiryTime\">Session Timeout\n                                        (Sekunden)</label>\n                                    <input class=\"col-sm-6 form-control\" id=\"adminauth-sessionExpiryTime\" placeholder=\"\"\n                                        type=\"number\">\n                                </div>\n                                <div id=\"adminauth-credentials\" class=\"col-sm-12 row pl-0 pr-0\" style=\"display: none\">\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" id=\"adminauth-user\" placeholder=\"User\" required>\n                                    </div>\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" type=\"password\" id=\"adminauth-pass1\"\n                                            placeholder=\"Passwort\">\n                                    </div>\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" type=\"password\" id=\"adminauth-pass2\"\n                                            placeholder=\"Passwort wiederholen\">\n                                    </div>\n                                    <div class=\"col-md-auto\">\n                                        <button id=\"adminauth-set\" type=\"button\"\n                                            class=\"btn btn-primary\">&Uuml;bernehmen</button>\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"row mb-3\">\n                            <div class=\"col-sm-3\" style=\"line-height: 38px;\">\n                                Node\n                            </div>\n                            <div class=\"col-sm-9\">\n                                <div class=\"row mb-1 col-sm-12\">\n                                    <select class=\"form-control\" id=\"nodeauth-type\">\n                                        <option value=\"none\">Keine Authentifizierung</option>\n                                        <option value=\"basic\">Username/Passwort individuell</option>\n                                    </select>\n                                </div>\n                                <div id=\"nodeauth-credentials\" class=\"col-sm-12 row pl-0 pr-0\" style=\"display: none\">\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" id=\"nodeauth-user\" placeholder=\"User\">\n                                    </div>\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" type=\"password\" id=\"nodeauth-pass1\"\n                                            placeholder=\"Passwort\">\n                                    </div>\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" type=\"password\" id=\"nodeauth-pass2\"\n                                            placeholder=\"Passwort wiederholen\">\n                                    </div>\n                                    <div class=\"col-md-auto\">\n                                        <button id=\"nodeauth-set\" type=\"button\"\n                                            class=\"btn btn-primary\">&Uuml;bernehmen</button>\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"row mb-3\">\n                            <div class=\"col-sm-3\" style=\"line-height: 38px;\">\n                                Static\n                            </div>\n                            <div class=\"col-sm-9\">\n                                <div class=\"row mb-1 col-sm-12\">\n                                    <select class=\"form-control\" id=\"staticauth-type\">\n                                        <option value=\"none\">Keine Authentifizierung</option>\n                                        <option value=\"basic\">Username/Passwort individuell</option>\n                                    </select>\n                                </div>\n                                <div id=\"staticauth-credentials\" class=\"col-sm-12 row pl-0 pr-0\" style=\"display: none\">\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" id=\"staticauth-user\" placeholder=\"User\">\n                                    </div>\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" type=\"password\" id=\"staticauth-pass1\"\n                                            placeholder=\"Passwort\">\n                                    </div>\n                                    <div class=\"col-sm\">\n                                        <input class=\"form-control\" type=\"password\" id=\"staticauth-pass2\"\n                                            placeholder=\"Passwort wiederholen\">\n                                    </div>\n                                    <div class=\"col-md-auto\">\n                                        <button id=\"staticauth-set\" type=\"button\"\n                                            class=\"btn btn-primary\">&Uuml;bernehmen</button>\n                                    </div>\n                                </div>\n                            </div>\n                        </div>\n\n                    </div>\n\n                </div>\n            </div>\n\n        </div>\n\n<!-- Henke\n                                    <div class=\"tab-pane fade show\" id=\"packages\" role=\"tabpanel\" aria-labelledby=\"packages-tab\">\n\n                                        <div id=\"package-hint\" class=\"card mb-4\" style=\"border-color: blue; display: none;\">\n                                            <div class=\"card-body\">\n                                                <button type=\"button\" class=\"btn btn-mini btn-info\" id=\"discard-package-hint\" style=\"padding: 0px 8px 2px; margin-top: -4px;\">x</button>\n                                                <b>Tipp:</b> Nicht-verwendete Pakete deinstallieren, das spart Speicher und reduziert den RAM-Bedarf und die Startzeit von Node-RED.\n                                            </div>\n                                        </div>\n\n                                        <table id=\"table-packages\" class=\"table\">\n                                            <thead>\n                                                <tr>\n                                                    <th scope=\"col\" style=\"min-width: 300px;\">Paket</th>\n                                                    <th scope=\"col\" style=\"min-width: 120px;\">Version</th>\n                                                    <th scope=\"col\" style=\"min-width: 80px; width: 80px;\">Installiert</th>\n                                                    <th scope=\"col\" style=\"min-width: 300px;\"></th>\n                                                </tr>\n                                                <tr>\n                                                    <th scope=\"col\"><input class=\"form-control form-control-sm\" id=\"package-filter\"></th>\n                                                    <th scope=\"col\"></th>\n                                                    <th scope=\"col\"></th>\n                                                    <th scope=\"col\"></th>\n                                                </tr>\n                                            </thead>\n                                            <tbody id=\"package-table\">\n\n                                            </tbody>\n                                        </table>\n                                        <div id=\"pkg-spinner\" class=\"text-center\">\n                                            <div class=\"spinner-border text-center\" role=\"status\">\n                                                <span class=\"sr-only\">Lade...</span>\n                                            </div>\n                                        </div>\n                                    </div>\n//-->\n\n        <div class=\"tab-pane fade show\" id=\"versions\" role=\"tabpanel\" aria-labelledby=\"versions-tab\">\n\n            <div class=\"card mb-4\">\n                <div class=\"card-body\">\n                    <div class=\"form-group\">\n                        <label for=\"loglevel\">Log Level</label>\n                        <select class=\"form-control\" id=\"loglevel\">\n                            <option>fatal</option>\n                            <option>error</option>\n                            <option>warn</option>\n                            <option>info</option>\n                            <option>debug</option>\n                            <option>trace</option>\n                        </select>\n                    </div>\n\n                </div>\n            </div>\n\n            <div class=\"card mb-4\">\n                <div class=\"card-body\">\n                    <div class=\"form-group\">\n                        <button id=\"log\" type=\"button\" class=\"btn btn-sm btn-info\"><i class=\"fa fa-download\"></i>\n                            Log herunterladen</button>\n\n\n                        <button id=\"log-upload\" type=\"button\" class=\"btn btn-sm btn-info\" style=\"display:none;\"><i\n                                class=\"fa fa-upload\"></i> Log versenden</button>\n                        <div id=\"log-upload-spinner\" class=\"spinner-border spinner-border-sm ml-2 mr-2\" role=\"status\"\n                            style=\"color: #6a737d; display: none;\">\n                            <span class=\"sr-only\">Please wait...</span>\n                        </div>\n\n\n                    </div>\n                </div>\n            </div>\n\n\n            <div class=\"card mb-4\">\n                <div class=\"card-body\">\n                    <div class=\"form-group\">\n                        <label for=\"autorestart\">Node-RED im Falle eines Absturzes neu starten</label>\n                        <select class=\"form-control\" id=\"autorestart\">\n                            <option value=\"0\">Deaktiviert</option>\n                            <option value=\"1\">Einmal</option>\n                            <option value=\"2\">Zweimal</option>\n                            <option value=\"3\">Dreimal</option>\n                            <option value=\"4\">Viermal</option>\n                            <option value=\"5\">F&uuml;nfmal</option>\n                            <option value=\"2147483647\">Unbegrenzt</option>\n                        </select>\n                    </div>\n                    <p>Node-RED Abst&uuml;rze seit letztem RedMatic-Start: <span id=\"restarts\">keine</span></p>\n                </div>\n            </div>\n\n        </div>\n\n        <div class=\"tab-pane fade show\" id=\"licenses\" role=\"tabpanel\" aria-labelledby=\"licenses-tab\">\n            <iframe></iframe>\n        </div>\n\n\n    </div>\n</div>\n\n<script src=\"js/script.js\"></script>",
        "output": "str",
        "x": 970,
        "y": 300,
        "wires": [
            [
                "eb41e227ba32cd94"
            ]
        ]
    },
    {
        "id": "eb41e227ba32cd94",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "www/settings.html",
        "filename": "/usr/local/addons/redmatic/www/settings.html",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1190,
        "y": 300,
        "wires": [
            [
                "001c9a1b2a7e8e2e"
            ]
        ]
    },
    {
        "id": "001c9a1b2a7e8e2e",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "default-settings.json",
        "field": "payload",
        "fieldType": "msg",
        "format": "json",
        "syntax": "plain",
        "template": "{\n  \"uiPort\": 1880,\n  \"uiHost\": \"127.0.0.1\",\n  \"flowFile\": \"flows.json\",\n  \"userDir\": \"/usr/local/addons/redmatic/var\",\n  \"httpRoot\": \"/addons/red\",\n  \"logging\": {\n    \"ain\": {\n      \"level\": \"info\",\n      \"metrics\": false,\n      \"audit\": false\n    }\n  },\n  \"contextStorage\": {\n    \"default\": {\n      \"module\": \"memory\"\n    },\n    \"memory\": {\n      \"module\": \"memory\"\n    },\n    \"file\": {\n      \"module\": \"localfilesystem\",\n      \"config\": {\n        \"dir\": \"/usr/local/addons/redmatic/var\",\n        \"flushInterval\": 300\n      }\n    }\n  },\n  \"editorTheme\": {\n    \"projects\": {\n      \"enabled\": false\n    }\n  },\n  \"adminAuth\": {\n    \"sessionExpiryTime\": 86400\n  },\n  \"restartOnCrash\": 0\n}",
        "output": "str",
        "x": 480,
        "y": 340,
        "wires": [
            [
                "5b8172939d7a1e26"
            ]
        ]
    },
    {
        "id": "5b8172939d7a1e26",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "etc/default-settings.json",
        "filename": "/usr/local/addons/redmatic/etc/default-settings.json",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 730,
        "y": 340,
        "wires": [
            [
                "70bdbbad9e26b78d"
            ]
        ]
    },
    {
        "id": "4c6951d8ab085736",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "mount",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "mustache",
        "template": "#!/bin/sh\nmount -o rw,remount /\nsleep 10\nchmod -R a+rwx /usr/local/addons/redmatic",
        "output": "str",
        "x": 950,
        "y": 140,
        "wires": [
            [
                "f1c29c21b7b7338a"
            ]
        ]
    },
    {
        "id": "1900d9b72af1a2d8",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "mount",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "mustache",
        "template": "#!/bin/sh\nchmod -R a+rwx /usr/local/addons/redmatic\nmount -o ro,remount /",
        "output": "str",
        "x": 1090,
        "y": 480,
        "wires": [
            [
                "eda7f0ecc302657e"
            ]
        ]
    },
    {
        "id": "8abba785cac84dc4",
        "type": "file in",
        "z": "2ab1de6847dd7ea2",
        "name": "versions",
        "filename": "/usr/local/addons/redmatic/versions",
        "filenameType": "str",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 140,
        "y": 280,
        "wires": [
            [
                "82abbf82a75e4ea3"
            ]
        ]
    },
    {
        "id": "82abbf82a75e4ea3",
        "type": "function",
        "z": "2ab1de6847dd7ea2",
        "name": "check",
        "func": "const key = \"VERSION_ADDON=\";\nlet ist = msg.payload.substring(msg.payload.indexOf(key) + key.length);\nist = ist.substring(0, ist.indexOf(\"\\n\"));\n// msg.ist = ist;\nlet ref = msg.VersionAddOn;\nlet cmp = cmpVersion(ist, ref);\nmsg.AddOn_ok = (cmp >= 0);\nif (!msg.AddOn_ok)\n    return msg;\nreturn [null,msg];\n\nfunction cmpVersion(aa, bb) {\n    const aArr = aa.split(\".\");\n    const bArr = bb.split(\".\");\n    const min = Math.min(aArr.length, bArr.length);\n    for (let index = 0; index < min; index++) {\n        if (aArr[index] != bArr[index])\n            return (aArr[index] - bArr[index]);\n    }\n    return 0;\n}",
        "outputs": 2,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 270,
        "y": 280,
        "wires": [
            [
                "85fa60362bb04a1a"
            ],
            [
                "33e539048f9a09f1"
            ]
        ]
    },
    {
        "id": "3b8d7efc8f41e5f5",
        "type": "inject",
        "z": "2ab1de6847dd7ea2",
        "name": "erzwungen Patch 7.3.5",
        "props": [
            {
                "p": "topic",
                "vt": "str"
            },
            {
                "p": "VersionAddOn",
                "v": "7.3.5",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "Patch,einmal bei neuem Flow",
        "x": 180,
        "y": 380,
        "wires": [
            [
                "85fa60362bb04a1a"
            ]
        ],
        "icon": "font-awesome/fa-play-circle"
    },
    {
        "id": "33e539048f9a09f1",
        "type": "debug",
        "z": "2ab1de6847dd7ea2",
        "name": "Patch aktuell",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "\"Patch aktuell\"",
        "targetType": "jsonata",
        "statusVal": "msg.Process.NodeVersion",
        "statusType": "auto",
        "x": 150,
        "y": 320,
        "wires": []
    },
    {
        "id": "7d35a6579c0a2026",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": ".profile",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "plain",
        "template": "export PATH=/usr/local/addons/redmatic/bin:/usr/local/bin:/usr/bin:$PATH\nexport LD_LIBRARY_PATH=/usr/local/addons/redmatic/lib:/usr/local/lib:/usr/lib\nexport HOME=/usr/local/addons/redmatic/home\nexport GIT_EXEC_PATH=/usr/local/addons/redmatic/libexec/git-core\nexport GIT_TEMPLATE_DIR=/usr/local/addons/redmatic/share/git-core/templates\nexport NO_UPDATE_NOTIFIER=true",
        "output": "str",
        "x": 430,
        "y": 380,
        "wires": [
            [
                "dee60a8eb7a86f2b"
            ]
        ]
    },
    {
        "id": "6de4e13207fb3de5",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": ".profileRoot",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "plain",
        "template": "export PATH=/usr/local/bin\nexport LD_LIBRARY_PATH=/usr/local/lib:/usr/lib\nexport HOME=/usr/local/addons/redmatic/home\nexport GIT_EXEC_PATH=/usr/local/addons/redmatic/libexec/git-core\nexport GIT_TEMPLATE_DIR=/usr/local/addons/redmatic/share/git-core/templates\nexport NO_UPDATE_NOTIFIER=true",
        "output": "str",
        "x": 970,
        "y": 380,
        "wires": [
            [
                "f96c2071a00c11a3"
            ]
        ]
    },
    {
        "id": "70bdbbad9e26b78d",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "Update node-red",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "#!/bin/sh\ncd /usr/local/addons/redmatic/lib\nsource /usr/local/addons/redmatic/home/.profile\n\nlogger -t redmatic -p daemon.info \"Henke Script updateNodeRed.sh\"\n\nVersionWeb=cat npm view node-red version\nnpm install node-red@$VersionWeb\nlogger -t redmatic -p daemon.info \"Henke Script updateNodeRed.sh node-red done\"\nnpm install @node-red-contrib-themes/theme-collection\nlogger -t redmatic -p daemon.info \"Henke Script updateNodeRed.sh theme-collection done\"\n",
        "output": "str",
        "x": 990,
        "y": 340,
        "wires": [
            [
                "9f4a6594386fabb9"
            ]
        ]
    },
    {
        "id": "9f4a6594386fabb9",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "bin/updateNodeRed.sh",
        "filename": "/usr/local/addons/redmatic/bin/updateNodeRed.sh",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1200,
        "y": 340,
        "wires": [
            [
                "7d35a6579c0a2026"
            ]
        ]
    },
    {
        "id": "85fa60362bb04a1a",
        "type": "file in",
        "z": "2ab1de6847dd7ea2",
        "name": "",
        "filename": "/boot/VERSION",
        "filenameType": "str",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 460,
        "y": 140,
        "wires": [
            [
                "aec8a9833a7f477a"
            ]
        ]
    },
    {
        "id": "aec8a9833a7f477a",
        "type": "function",
        "z": "2ab1de6847dd7ea2",
        "name": "RasberryMatic?",
        "func": "if ( msg.payload.indexOf(\"PRODUCT=raspmatic\") < 0 )\n{\n    node.warn(\"Kein RasberryMatic\");\n    return;\n}\n\nconst key = \"VERSION=\";\nlet ist = msg.payload.substring(msg.payload.indexOf(key) + key.length);\nist = ist.substring ( 0, ist.indexOf(\"\\n\"));\n// msg.ist = ist;\nlet ref = \"3.71.12.20231013\";\nlet cmp = cmpVersion(ist, ref);\nmsg.node_js_ok = (cmp >=0);\nreturn msg;\n\nfunction cmpVersion(aa, bb) {\n    const aArr = aa.split(\".\");\n    const bArr = bb.split(\".\");\n    const min = Math.min(aArr.length, bArr.length);\n    for (let index = 0; index < min; index++) {\n        if (aArr[index] != bArr[index])\n            return (aArr[index] - bArr[index]);\n    }\n    return 0;\n}",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 700,
        "y": 140,
        "wires": [
            [
                "4c6951d8ab085736"
            ]
        ]
    },
    {
        "id": "f1c29c21b7b7338a",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "Schreibe Script",
        "filename": "/usr/local/update/_run.sh",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1180,
        "y": 140,
        "wires": [
            [
                "0f65a59c71d5cefe"
            ]
        ]
    },
    {
        "id": "0f65a59c71d5cefe",
        "type": "exec",
        "z": "2ab1de6847dd7ea2",
        "command": "sh /usr/local/update/_run.sh",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "Ausführen",
        "x": 1410,
        "y": 140,
        "wires": [
            [],
            [
                "ffa53e6eff89319c"
            ],
            [
                "e435bc41d8c9739d"
            ]
        ]
    },
    {
        "id": "ffa53e6eff89319c",
        "type": "debug",
        "z": "2ab1de6847dd7ea2",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "counter",
        "x": 1525,
        "y": 140,
        "wires": [],
        "l": false
    },
    {
        "id": "2d6177b5c2a97ca9",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "Schreibe Script",
        "filename": "/usr/local/update/_run.sh",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 700,
        "y": 480,
        "wires": [
            [
                "9e84ed95b487240c"
            ]
        ]
    },
    {
        "id": "9e84ed95b487240c",
        "type": "exec",
        "z": "2ab1de6847dd7ea2",
        "command": "sh /usr/local/update/_run.sh",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "Ausführen",
        "x": 870,
        "y": 480,
        "wires": [
            [],
            [
                "7d14f0f429c0f826"
            ],
            [
                "1900d9b72af1a2d8"
            ]
        ]
    },
    {
        "id": "7d14f0f429c0f826",
        "type": "debug",
        "z": "2ab1de6847dd7ea2",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "counter",
        "x": 975,
        "y": 480,
        "wires": [],
        "l": false
    },
    {
        "id": "eda7f0ecc302657e",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "Schreibe Script",
        "filename": "/usr/local/update/_run.sh",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1240,
        "y": 480,
        "wires": [
            [
                "591f97800f8e5ab6"
            ]
        ]
    },
    {
        "id": "591f97800f8e5ab6",
        "type": "exec",
        "z": "2ab1de6847dd7ea2",
        "command": "sh /usr/local/update/_run.sh",
        "addpay": "",
        "append": "",
        "useSpawn": "false",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "Ausführen",
        "x": 1410,
        "y": 480,
        "wires": [
            [],
            [
                "abb099706360873f"
            ],
            [
                "42584b3b89350e87"
            ]
        ]
    },
    {
        "id": "abb099706360873f",
        "type": "debug",
        "z": "2ab1de6847dd7ea2",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "counter",
        "x": 1535,
        "y": 480,
        "wires": [],
        "l": false
    },
    {
        "id": "dee60a8eb7a86f2b",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "home/.profile",
        "filename": "/usr/local/addons/redmatic/home/.profile",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 690,
        "y": 380,
        "wires": [
            [
                "6de4e13207fb3de5"
            ]
        ]
    },
    {
        "id": "f96c2071a00c11a3",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "home/.profileRoot",
        "filename": "/usr/local/addons/redmatic/home/.profileRoot",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1190,
        "y": 380,
        "wires": [
            [
                "0aa2148e71e010d7"
            ]
        ]
    },
    {
        "id": "9d7cfabd2bbf2be3",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "checkNR.js",
        "field": "payload",
        "fieldType": "msg",
        "format": "javascript",
        "syntax": "plain",
        "template": "const fs = require('fs');\n\nconst addOn = '/usr/local/addons/redmatic/';\nconst settings = addOn + '/etc/settings.json';\nconst restartOnCrash = require(settings).restartOnCrash;\n// console.log(\"RedMatic settings.js NodeRed: \" + restartOnCrash );\nlet out = \"\";\n\nlet runUpdateNodeRed = false;\n{\n    let msg = {};\n\n    try {\n        const nodeRed = addOn + 'lib/node_modules/node-red/package.json';\n        const nodeRedVersion = require(nodeRed).version;\n//        console.info(\"RedMatic checkNR.js NodeRed: \" + nodeRedVersion);\n        msg.payload = nodeRedVersion;\n        msg.AddOn_ok = (cmpVersion(nodeRedVersion, \"3.0.0\") >= 0);\n        if (!msg.AddOn_ok)\n            runUpdateNodeRed = true;\n    }\n    catch\n    {\n//        console.error(\"RedMatic checkNR.js Error NodeRed fehlt?\");\n        runUpdateNodeRed = true;\n    }\n\n    function cmpVersion(aa, bb) {\n        const aArr = aa.split(\".\");\n        const bArr = bb.split(\".\");\n        const min = Math.min(aArr.length, bArr.length);\n        for (let index = 0; index < min; index++) {\n            if (aArr[index] != bArr[index])\n                return (aArr[index] - bArr[index]);\n        }\n        return 0;\n    }\n\n    const prefix = \"RedSettings_\";\n    out += prefix + \"RestartOnCrash\" + \"=\" + restartOnCrash + \"\\n\";\n    out += prefix + \"NodeRedUpdate\" + \"=\" + runUpdateNodeRed + \"\\n\";\n\n    fs.writeFileSync('/tmp/red-settings', out);\n}\n\n//process.stdout.write(out);",
        "output": "str",
        "x": 450,
        "y": 420,
        "wires": [
            [
                "227744a2ca3ffd24"
            ]
        ]
    },
    {
        "id": "227744a2ca3ffd24",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "bin/checkNR.js",
        "filename": "/usr/local/addons/redmatic/bin/checkNR.js",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 700,
        "y": 420,
        "wires": [
            [
                "372b8ef8e105c0ba"
            ]
        ]
    },
    {
        "id": "61fccb767b94a8ac",
        "type": "inject",
        "z": "2ab1de6847dd7ea2",
        "name": "Patch 7.3.5",
        "props": [
            {
                "p": "topic",
                "vt": "str"
            },
            {
                "p": "VersionAddOn",
                "v": "7.3.5",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "Patch,einmal bei neuem Flow",
        "x": 270,
        "y": 220,
        "wires": [
            [
                "8abba785cac84dc4"
            ]
        ],
        "icon": "font-awesome/fa-play-circle"
    },
    {
        "id": "1e9fdeab1ba13c95",
        "type": "comment",
        "z": "2ab1de6847dd7ea2",
        "name": "Hit me...",
        "info": "",
        "x": 100,
        "y": 220,
        "wires": []
    },
    {
        "id": "5ada11e830a3b224",
        "type": "comment",
        "z": "2ab1de6847dd7ea2",
        "name": "Warnung: NodeRed wird neu gestartet!",
        "info": "",
        "x": 190,
        "y": 180,
        "wires": []
    },
    {
        "id": "372b8ef8e105c0ba",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": "redmaticVersions",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "plain",
        "template": "#!/bin/sh\n\n# Henke Version 1.1\nlogger -t redmatic -p daemon.info \"Henke Script redmaticVersions $1\"\n\nADDON_DIR=/usr/local/addons/redmatic\nGLOBAL_MODULES=$ADDON_DIR/lib/node_modules\nLOCAL_MODULES=$ADDON_DIR/var/node_modules\n\n#NODE=$ADDON_DIR/bin/node\n\nexport PATH=$ADDON_DIR/bin:$PATH\nexport LD_LIBRARY_PATH=$ADDON_DIR/lib:$LD_LIBRARY_PATH\nexport NO_UPDATE_NOTIFIER=true\n\nsource $ADDON_DIR/versions\n\nJO_ARGS=\"\"\n\n#scan_dir()\n#{\n#    for dir in $1/*\n#    do\n#        dir=${dir%*/}\n#        PKG=${dir##*/}\n#        PKGJSON=$1/$PKG/package.json\n#        if [[ -f \"$PKGJSON\" ]]; then\n#            JO_ARGS=\"$JO_ARGS `jq -r '.name' $1/$PKG/package.json`=`jq -r '.version' $1/$PKG/package.json`\"\n#        fi\n#        case $PKG in @*)\n#            scan_dir \"$1/$PKG\"\n#        esac\n#    done\n#}\n\n#scan_dir $GLOBAL_MODULES\n#scan_dir $LOCAL_MODULES\n\nsource /VERSION\n\nif [ -d /etc/piVCCU3 ]; then\n    PRODUCT=\"pivccu3\"\nfi\n\nPLATFORM=\"$PLATFORM-`uname -m`\"\nnodejs=`node --version | cut -c 2-`\necho '{\"ccu\": {\"VERSION\": \"'$VERSION'\", \"PRODUCT\": \"'$PRODUCT'\",\"PLATFORM\": \"'$PLATFORM'\"},\"redmatic\": \"'$VERSION_ADDON'\",\"nodejs\": \"'$nodejs'\"}'\n\n#jo -p ccu=`(echo \"VERSION=$VERSION\"; echo \"PRODUCT=$PRODUCT\"; echo \"PLATFORM=$PLATFORM\" && echo -n \"deviceTypes=\" && $ADDON_DIR/bin/deviceTypes) | jo` \\\n#jo -p ccu=`(echo \"VERSION=$VERSION\"; echo \"PRODUCT=$PRODUCT\"; echo \"PLATFORM=$PLATFORM\" ) | jo` \\\n#    redmatic=$VERSION_ADDON \\\n#    nodejs=`node --version | cut -c 2-` \\\n#    $JO_ARGS\n\n",
        "output": "str",
        "x": 990,
        "y": 420,
        "wires": [
            [
                "40d4e066a89307f4"
            ]
        ]
    },
    {
        "id": "40d4e066a89307f4",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "bin/redmaticVersions",
        "filename": "/usr/local/addons/redmatic/bin/redmaticVersions",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1200,
        "y": 420,
        "wires": [
            [
                "0ea2818262334c44"
            ]
        ]
    },
    {
        "id": "0aa2148e71e010d7",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "name": ".npmrc",
        "field": "payload",
        "fieldType": "msg",
        "format": "markdown",
        "syntax": "plain",
        "template": "prefix=/usr/local\ncache=/tmp/npm-cache\n",
        "output": "str",
        "x": 1390,
        "y": 380,
        "wires": [
            [
                "c1fc15dc666dfe56"
            ]
        ]
    },
    {
        "id": "c1fc15dc666dfe56",
        "type": "file",
        "z": "2ab1de6847dd7ea2",
        "name": "home/.profileRoot",
        "filename": "/usr/local/addons/redmatic/home/.npmrc",
        "filenameType": "str",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 1550,
        "y": 380,
        "wires": [
            [
                "0ea2818262334c44"
            ]
        ]
    },
    {
        "id": "665899be2814dfb7",
        "type": "group",
        "z": "2ab1de6847dd7ea2",
        "style": {
            "stroke": "#2e333a",
            "stroke-opacity": "1",
            "fill": "#2e333a",
            "fill-opacity": "0.75",
            "label": true,
            "label-position": "nw",
            "color": "#a4a4a4"
        },
        "nodes": [
            "e28522bbbf4ccec3",
            "62227816a2ee54e2",
            "42584b3b89350e87",
            "0270d062629f4e2c"
        ],
        "x": 1494,
        "y": 619,
        "w": 452,
        "h": 122
    },
    {
        "id": "e28522bbbf4ccec3",
        "type": "inject",
        "z": "2ab1de6847dd7ea2",
        "g": "665899be2814dfb7",
        "name": "Warnung: NodeRed wird neu gestartet!",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "true",
        "payloadType": "bool",
        "x": 1690,
        "y": 660,
        "wires": [
            [
                "42584b3b89350e87"
            ]
        ]
    },
    {
        "id": "62227816a2ee54e2",
        "type": "http request",
        "z": "2ab1de6847dd7ea2",
        "g": "665899be2814dfb7",
        "name": "Local",
        "method": "POST",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "http://127.0.0.0:8181/a.exe",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [],
        "x": 1790,
        "y": 700,
        "wires": [
            [
                "0270d062629f4e2c"
            ]
        ]
    },
    {
        "id": "42584b3b89350e87",
        "type": "template",
        "z": "2ab1de6847dd7ea2",
        "g": "665899be2814dfb7",
        "name": "Restart NodeRed",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "system.Exec(\"/usr/local/addons/redmatic/bin/redmatic restart &\");",
        "output": "str",
        "x": 1630,
        "y": 700,
        "wires": [
            [
                "62227816a2ee54e2"
            ]
        ]
    },
    {
        "id": "0270d062629f4e2c",
        "type": "debug",
        "z": "2ab1de6847dd7ea2",
        "g": "665899be2814dfb7",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "counter",
        "x": 1885,
        "y": 700,
        "wires": [],
        "l": false
    }
]

hkolb
Beiträge: 14
Registriert: 08.01.2024, 11:43
System: sonstige

Re: RedMatic Update 7.3.5

Beitrag von hkolb » 19.01.2024, 18:00

Debug- Meldungen kame keine, lief recht schnell durch.
Nachdem Redmatic wieder gestartet war hab ich noch die RaspberryMatic neu gestartet.
Hier das Ergebnis deines Flows "npm Doctor":

Code: Alles auswählen

Check                               Value   Recommendation/Notes
npm ping                            ok       
npm -v                              not ok  Use npm v10.3.0
node -v                             not ok  Use node v20.11.0 (current: v20.10.0)
npm config get registry             ok      using default registry (https://registry.npmjs.org/)
git executable in PATH              ok      /usr/local/addons/redmatic/bin/git
global bin folder in PATH           ok      /usr/local/bin
Perms check on cached files         ok       
Perms check on local node_modules   ok       
Perms check on global node_modules  ok       
Perms check on local bin folder     ok       
Perms check on global bin folder    ok       
Verify cache contents               ok      verified 2 tarballs
Path ist jetzt OK, bei der Version leider keine Änderung :(

Antworten

Zurück zu „RedMatic“