mdzio / ccu-jack

CCU-Jack bietet einen einfachen und sicheren REST- und MQTT-basierten Zugriff auf die Datenpunkte der Zentrale (CCU) des Hausautomations-Systems HomeMatic. Zudem können einfach Fremdgeräte an die CCU angebunden werden.
GNU General Public License v3.0
121 stars 12 forks source link

Vorschlag: node-red-contrib-ccu-jack #75

Closed guenter-ms closed 2 years ago

guenter-ms commented 2 years ago

Hallo, der ccu-jack ist richtig gut!! Deshalb möchte ich anregen, das XMLAPI basierende nodered addon "node-red-contrib-ccu" (https://flows.nodered.org/node/node-red-contrib-ccu) 1:1 auf der Basis ccu-jack verfügbar zu machen. Ich selbst habe dafür leider das know how nicht, biete aber an, zu testen. M.W. kann/darf man das vorgenannte addon als Basis kopieren? Das addon ist eine Komponente von redmatic.de, wo es seit 10 Monaten still geworden ist und wohl keine Betreuung mehr erfolgt. Eine solche 1:1 Umstellung wäre einechter Knaller und würde vermutlich von sehr vielen Anwendern extrem begrüßt werden. Comments? Grüße von Guenni

ptweety commented 2 years ago

Hallo @guenter-ms,

verstehe ich das richtig, dass du ein drop-in Replacement vorschlägst?

Dazu habe ich folgende Anmerkungen/Gedanken:

guenter-ms commented 2 years ago

Hallo Matthias,

hier meine Überlegungen zu Deinen Punkten:

Bin leider kein Programmierer, aber die zentrale "CCU-Jack-Weiche" klingt für mich logisch... Viele Grüße Günni

Bildschirmfoto 2022-01-10 um 17 41 51

mdzio commented 2 years ago

Noch ein paar Verweise:

mdzio commented 2 years ago

Das weitere Vorgehen ist nun von den Maintainern von Node-Red-Contrib-CCU abhängig. Daher schließe ich das Thema hier. Vielen Dank an gnark/realgnark für den Pull-Request.

guenter-ms commented 2 years ago

Hallo mdzio

Maintainer von Node-Red-Contrib-CCU gibt es leider nicht mehr. Das war ja der Hauptgrund für den Vorschlag....

gruß guenni

ptweety commented 2 years ago

Hi @guenter-ms:

ich habe mal mit einem function node rumgespielt, welcher einen Cache für Abfragen an den CCU-Jack implementiert und das Ergebnis der Abfragen etwas aufbereitet (analog zu ccu-connection) herausgibt.

Eingesetzt wird das Ganze als einzelner Knoten hinter allen anderen MQTT-Knoten, welche Topics des CCU-Jack abonniert haben. Wenn du magst, dann kannst du gerne dazu ein Feedback geben. Es fehlen insbesondere eine Dokumentation und die Implementierung von Programmen und Systemvariablen.

Die Konfiguration erfolgt im Knoten selbst. Und dort ist die Lasche Start anzupassen:

Bildschirmfoto 2022-02-21 um 15 35 39
[
    {
        "id": "e4865d213c50805e",
        "type": "function",
        "z": "62591c5e2ae416a5",
        "name": "Jack Cache",
        "func": "const ts = Date.now();\n\nconst sortObject = context.get('util').sortObject;\nconst jackAPI = context.get('util').jackAPI;\nconst getLinks = context.get('util').getLinks;\n\n/**\n * cache: add root, domains & vendor/config information\n */\nconst root = await jackAPI('/', 1000 * 60 * 60 * 24); // 1d\n\nif (root) {\n    await Promise.all(getLinks(root, 'domain').map(async (link) => {\n        const ret = await jackAPI(link.url, 1000 * 60 * 60 * 4); // 4h\n    }));\n}\n\nconst config = await jackAPI('/~vendor/config/~pv', 1000 * 60 * 60 * 4); // 4h\n\n/**\n * msg: split, analyze and add additional data (either from cache or api call)\n */\n\nlet message = {}\nlet ids = msg.topic.split('/');\n\nswitch (ids[0]) {\n    case 'device': {\n        // mqtt topic example: device/status/${device}/${channel}/${datapoint}\n        \n        const device = await jackAPI(`/device/${ids[2]}`, 1000 * 60 * 60 * 2); // 2h\n        const channel = await jackAPI(`/device/${ids[2]}/${ids[3]}`, 1000 * 60 * 1); // 1h\n        const datapoint = await jackAPI(`/device/${ids[2]}/${ids[3]}/${ids[4]}`, 1000 * 60 * 10); // 10min\n\n        return {\n                topic: msg.topic,\n                ['~pv']: msg.payload,\n                payload: msg.payload.v,\n                qos: msg.qos,\n                retain: msg.retain,\n                ['~config']: config,\n                ccu: config.v.CCU.Address,\n                iface: device.interfaceType,\n                ['~device']: device,\n                device: device.address,\n                deviceName: device.title,\n                deviceType: device.type,\n                ['~channel']: channel,\n                channel: channel.address,\n                channelName: channel.title,\n                channelType: channel.type,\n                channelIndex: channel.index,\n                ['~datapoint']: datapoint,\n                datapoint: datapoint.id,\n                datapointName: device.interfaceType + '.' + channel.address + '.' + datapoint.id,\n                datapointType: datapoint.type,\n                datapointMin: datapoint.minimum,\n                datapointMax: datapoint.maximum,\n                datapointDefault: datapoint.default,\n                datapointControl: datapoint.control,\n                rooms: channel['~links'].filter( it => it.rel == 'room').map(x => x.title),\n                room: channel['~links'].filter( it => it.rel == 'room').map(x => x.title)[0],\n                functions: channel['~links'].filter( it => it.rel == 'function').map(x => x.title),\n                function: channel['~links'].filter( it => it.rel == 'function').map(x => x.title)[0],\n                ts: ts,\n                uncertain: (msg.payload.s !== 0),\n                value: msg.payload.v,\n            };\n        //break;\n    }\n    default: {\n        return msg;\n    }\n}\n\n/**\n * msg: output\n */\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "// Der Code hier wird ausgeführt,\n// wenn der Node gestartet wird\n\n// Please enter your <ip>, <user> & <password> below\n\n// IMPORTANT: if you use https on port 2122\n// please change <proto> & <port> as well\n// Also, you must include propper <key> & <cert>\n// This might be self-signed ones from ccu-jack\n\nnode.status({\n                fill: 'blue',\n                shape: 'dot',\n                text: 'configuration required',\n            });\n\nconst config = {\n    cache: 'jack-cache',\n    proto: 'http',\n    ip: '192.168.1.12',\n    port: 2121,\n    user: 'youruser',\n    password: 'yoursecret',\n    key: `-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----`,\n    cert: `-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\n..\n-----END CERTIFICATE-----`,\n}\n\n// no changes below this comment required\n\n//context.set('jack', config);\n\nlet cache = global.get(config.cache) || {};\n\nconst util = {\n    sortObject: obj => Object.keys(obj).sort().reduce((res, key) => (res[key] = obj[key], res), {}),\n\n\n    jackAPI: async (url, ttl) => {\n        const ts = Date.now();\n        if ( !cache[url] || cache[url].ts < (ts - ttl) ) {\n            try {\n                const { data } = await axios.get(url, {\n                    baseURL: `${config.proto}://${config.ip}:${config.port}`,\n                    auth: { username: config.user, password: config.password },\n                    httpsAgent: new https.Agent({ rejectUnauthorized: false, key: config.key, cert: config.cert }),\n                }).catch( (err) => { } ); // node.warn({url: url, error: err, ...msg})\n                cache[url] = data;\n                cache[url].ts = ts;\n                cache[url].ttl = ttl;\n                global.set(config.cache, cache);\n                cache = global.get(config.cache);\n            } catch(error) {}\n        }\n        return cache[url] || {};\n    },\n\n    getLinks: (obj, rel, ...args) => {\n        let ret = [];\n\n        obj['~links']\n        .sort((a, b) => { return a.href.localeCompare(b.href) })\n        .filter( link => link.href !== '..')\n        .filter( link => link.href !== '$MASTER')\n        .filter( link => rel !== null ? link.rel === rel : true)\n        .forEach( (link, ind, arr) => {\n            ret.push({\n                url: args.length > 0 ? '/' + args[0] + '/' + link.href : '/' + link.href,\n                request: args.length > 1 ? args[1] : link.href,\n            });\n        })\n        \n        return ret;\n    }\n}\n\ncontext.set('util', util);",
        "finalize": "",
        "libs": [
            {
                "var": "axios",
                "module": "axios"
            },
            {
                "var": "https",
                "module": "https"
            }
        ],
        "x": 810,
        "y": 1060,
        "wires": [
            []
        ]
    }
]
guenter-ms commented 2 years ago

Hi Matthias,

puuhh das ist high sofisticated.... respekt!

Wenn ich es richtig verstanden habe, bringt der function node abonnierte mqtt topics des CCU-jacks "in Form", heisst man bekommt als output ein mit CCU-connection vergleichbares array.

Das entspräche dann in etwa der Funktionalität des RPC-Event Nodes von nodered-contrib-ccu. Also ein output bei einer Status Änderung eines CCU Objektes. Korrekt?

Zurückschreiben geht damit sicherlich noch nicht oder das gezielte (realtime) abfragen des aktuellen status, den könnte man eigentlich dafür doch in dem function node direkt in global.context variablen (name=msg.topic) speichern, oder?

Der Ansatz gefällt mir, zumal er so weit schon generisch ist, also nicht auf jeden geräte typ angepasst werden muss. sind denn RF, wired, IP und cuxd Objekte schon drin?

Der function node könnte später ein "richtiger" Node werden oder zumindest ein subflow https://nodered.org/docs/user-guide/editor/workspace/subflows dann können die config items gut als sub flow properties eingerichtet werden.

Ein Beispiel flow mit z.B. 2 MQTT Nodes und debug nodes wäre schon hilfreich das mal zu testen.

Gefällt mir!

Gruß guenni

mdzio commented 2 years ago

Maintainer von Node-Red-Contrib-CCU gibt es leider nicht mehr. Das war ja der Hauptgrund für den Vorschlag....

Ist denn just-grizzle kein Maintainer? Er wollte sich doch den CCU-Jack anschauen und sich dann zurückmelden.

Ansonsten muss ein Fork von dem Projekt erstellt und gepflegt werden. Das kann ich aber nicht leisten.

guenter-ms commented 2 years ago

Meines Wissens ist node-red-contrib-ccu eine Komponente von https://github.com/rdmtc/RedMatic#readme Der Autor ist Sebastian Raff alias hobbyquaker, der da einen super Job gemacht hat. Leider steht er wohl seit 12+ Monaten nicht mehr zur Verfügung, was man ihm natürlich niemand vorwirft. Wenn es einen neuen maintainer gibt, wäre das sehr gut, denn redmatic ist mittlerweile veraltet, u.a. weil issues nicht mehr bearbeitet werden und die dependencies schon lange überholt sind. Bestes Beispiel ist das integrierte nodered. Mir selbst fehlt das know how, am Interesse fehlt es da nicht.

ptweety commented 2 years ago

Hi @guenter-ms,

ich habe die letzten Tag an einem Fork von node-red-contrig-ccu gearbeitet und dort u.a. auch die hier angesprochene Integration umgesetzt: https://github.com/ptweety/node-red-contrib-ccu/issues/6

mdzio commented 2 years ago

Vielen Dank für das Einbauen und den Fork!

guenter-ms commented 2 years ago

Hi Mathias,

wunderbar!

Wenn ich über die Palette in NR installieren möchte, gibt es einen Konflikt mit dem Original. Wenn ich das Original lösche, sind natürlich alle flows im Eimer. Bin ratlos….

Lieben Gruß

ptweety commented 2 years ago

Hi @guenter-ms,

da würde ich gerne helfen und habe auch bereits ein entsprechendes issue offen: https://github.com/ptweety/node-red-contrib-ccu/issues/3

Da ich nicht weiß, in welcher Umgebung du Node-RED laufen hast, können wir im genannten issue gerne was gemeinsam erarbeiten.

guenter-ms commented 2 years ago

Hi Mathias,

gerne. Vorher müsste ich mal eine Testinstallation machen um herauszufinden, was denn der Unterschied zwischen Original und Fork ist. Oder hast du da schon mal was beschrieben?

Ich arbeite auf der Testinstallation auf einem pi3b mit raspberrymatic + Redmatic. Später in Prod läuft NR unter Homeassistant.

Gruß G

ptweety commented 2 years ago

Hi @guenter-ms,

ein erster Versuch eines Migrationsleitfadens ist nun verfügbar: https://github.com/ptweety/node-red-contrib-ccu/commit/da8e3a272dc99cd33681bf6c04b7998f0e2a58b7

guenter-ms commented 2 years ago

Hi Mathias,

ich habe unter redmatic mal alle flows gelöscht (vorher gesichert), dann contrib-ccu gelöscht und deine version installiert, siehe install-log.

Anschließend konnte ich keinen ccu-configuration-node erfolgreich einrichten, siehe screenshot. Die Auswahlliste im Feld "Listen Adress" ist leer, man kann nichts eintragen.

Werde jetzt mal dein migration Konzept probieren.

Grüße G Bildschirmfoto 2022-10-19 um 11 31 24 Bildschirmfoto 2022-10-19 um 11 30 38

mdzio commented 2 years ago

Fragen zu node-red-contrib-ccu stellst Du am besten in dem zugehörigen Projekt . Dann lesen auch die richtigen Leute mit. Hier geht es primär um den CCU-Jack.