Closed guenter-ms closed 2 years ago
Hallo @guenter-ms,
verstehe ich das richtig, dass du ein drop-in Replacement vorschlägst?
Dazu habe ich folgende Anmerkungen/Gedanken:
CCU-Jack
ist kein NodeRed Projekt, daher könnte das hier out-of-scope sein und ggfs. ein eigenes Projekt darstellennode-red-contrib-ccu
besteht aus insgesamt 16 nodes. Welche davon würden denn überhaupt Sinn machen?CCU-Jack
bietet schon jetzt Information
zu den Geräten über die VEAP
-Schnittstelle, die man in NodeRed über http-request
abfragen kannCCU-Jack
bietet schon jetzt Ereignisse
der Geräte über die MQTT
-Schnittstelle, welche über mqtt-in
empfangen werdenCCU-Jack
bietet schon jetzt Steuerung
der Geräte über die MQTT
-Schnittstelle, welche über mqtt-out
durchgeführt werdenHallo 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
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.
Hallo mdzio
Maintainer von Node-Red-Contrib-CCU gibt es leider nicht mehr. Das war ja der Hauptgrund für den Vorschlag....
gruß guenni
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:
[
{
"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": [
[]
]
}
]
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
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.
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.
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
Vielen Dank für das Einbauen und den Fork!
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ß
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.
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
Hi @guenter-ms,
ein erster Versuch eines Migrationsleitfadens ist nun verfügbar: https://github.com/ptweety/node-red-contrib-ccu/commit/da8e3a272dc99cd33681bf6c04b7998f0e2a58b7
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
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.
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