NEWS
ich brauche etwas Nachhilfe mit Node Red und RS232
-
hm eigentlich kein müll nur nicht die letzte version
hier dann noch 2 extra nodes für den hexoutput
die function node einfach mit dem ausgang der rs232 node verbinden.
da ich nicht weiß was für ein datentyp aus rs232 rauskommt kann diese mit Buffer und mit string umgehen
Ausgabe sieht dann so aus

das ist zwar gekürzt (...) aber das reicht zum analysierenbitte als markieren und als text hier reinstellen
40 0d 0d 0a 42 61 74 74 65 72 79 20 20 56 6f 6c 74 20 20 20 20 20 54 65 6d 70 72 20 20 20 20 42 61 73 65 20 53 74 61 74 65 20 20 20 56 6f 6c 74 2e 20 53 74 61 74 65 20 20 43 75 72 72 2e 20 53 74 61 74 65 20 20 54 65 6d 70 2e 20 53 74 61 74 65 20 20 43 6f 75 6c 6f 6d 62 20 20 20 20 20 0d 0d 0a 30 20 20 20 20 20 20 20 20 33 34 36 35 20 20 20 20 20 32 39 30 30 30 20 20 20 20 49 64 6c 65 20 20 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 20 39 36 25 20 20 20 20 20 20 39 35 32 35 38 20 6d 41 48 0d 0d 0a 31 20 20 20 20 20 20 20 20 33 34 36 39 20 20 20 20 20 32 39 30 30 30 20 20 20 20 49 64 6c 65 20 20 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 20 39 37 25 20 20 20 20 20 20 39 36 30 33 36 20 6d 41 48 0d 0d 0a 32 20 20 20 20 20 20 20 20 33 34 36 30 20 20 20 20 20 32 39 30 30 30 20 20 20 20 49 64 6c 65 20 20 2...Du meinst so?
Edit:
Also ich habe jetzt mal den Flow von dir einfach importiert und gestartet, das sieht im Iobroker sehr gut aus. Also müssen wir die Aufbereitung des payload aus dem RS232 Node noch irgendwie anderst machen. Ich frage mich gerade ob der aufbereitung auch gleich noch die Batterie-Nr. vom aufruf mit geben könnten? Denn es sind jetzt bei mir ja 8 Batterien an je 16 Zellen. Sprich in den Datenpunkten müsste ja dann auch irgendwo noch das "bat x" drin stehen. Ich weiss das gibt unendliche viele Datenpunkte, aber es sind da bei mir ja auch 128 einzelne Zellen. Einzelne Datenpunkte werden nie jemand interessieren, aber es wird einfacher sein, die einfach drin zu lassen? -
40 0d 0d 0a 42 61 74 74 65 72 79 20 20 56 6f 6c 74 20 20 20 20 20 54 65 6d 70 72 20 20 20 20 42 61 73 65 20 53 74 61 74 65 20 20 20 56 6f 6c 74 2e 20 53 74 61 74 65 20 20 43 75 72 72 2e 20 53 74 61 74 65 20 20 54 65 6d 70 2e 20 53 74 61 74 65 20 20 43 6f 75 6c 6f 6d 62 20 20 20 20 20 0d 0d 0a 30 20 20 20 20 20 20 20 20 33 34 36 35 20 20 20 20 20 32 39 30 30 30 20 20 20 20 49 64 6c 65 20 20 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 20 39 36 25 20 20 20 20 20 20 39 35 32 35 38 20 6d 41 48 0d 0d 0a 31 20 20 20 20 20 20 20 20 33 34 36 39 20 20 20 20 20 32 39 30 30 30 20 20 20 20 49 64 6c 65 20 20 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 4e 6f 72 6d 61 6c 20 20 20 20 20 20 20 20 39 37 25 20 20 20 20 20 20 39 36 30 33 36 20 6d 41 48 0d 0d 0a 32 20 20 20 20 20 20 20 20 33 34 36 30 20 20 20 20 20 32 39 30 30 30 20 20 20 20 49 64 6c 65 20 20 2...Du meinst so?
Edit:
Also ich habe jetzt mal den Flow von dir einfach importiert und gestartet, das sieht im Iobroker sehr gut aus. Also müssen wir die Aufbereitung des payload aus dem RS232 Node noch irgendwie anderst machen. Ich frage mich gerade ob der aufbereitung auch gleich noch die Batterie-Nr. vom aufruf mit geben könnten? Denn es sind jetzt bei mir ja 8 Batterien an je 16 Zellen. Sprich in den Datenpunkten müsste ja dann auch irgendwo noch das "bat x" drin stehen. Ich weiss das gibt unendliche viele Datenpunkte, aber es sind da bei mir ja auch 128 einzelne Zellen. Einzelne Datenpunkte werden nie jemand interessieren, aber es wird einfacher sein, die einfach drin zu lassen?ok die bestehende function node passt für die daten.
eine ungewöhlichkeit gibt es 0d 0d 0a
entweder verwendet man nur 0d oder 0d 0a,
gemischt ist ungewöhlich, aber das hat sich der liebe hersteller halt so ausgedacht.
wahrscheinlich ist@ <CR>für ihn ein art beginnzeichen.
nun zum müll.
was ist dir da aufgefallen? wieso reihenfolge?
abfolge des flows istrs232
|
tabelle parsen und jede zeile in ein battery objekt umwandeln.
jede zeile ist eine eigene message
|
für jede message (also für jede zeile) die daten für iobroker aufbereiten, so das notfalls der state angelegt werden kann, also mit unit, beschreibung, etc.
|
die letzte iobroker node hat keine konfiguration, da sie direkt die aufbereiteten daten nehemn und dann nach iobroker schreiben kann.also nimm den flow aus dem letzten post, importier ihn, beachte mein hinweis weiter oben zur konfiguration von node-red, damit er auch states nach 0_userdata schreiben darf.
lass den flow laufen. dann findest du unterhalb des datenpunkts
0_userdata.0.noderedbattery
die komplette struktur. screenshot habe ich gestern schon eingefügt.
je battery id werden nun diverse states angelegt mit den werten aus der tabelle
wenn du den datenpunktnamen ändern willst, dann kannst du das in der node zur aufbereitung der iobroker daten
-
ok die bestehende function node passt für die daten.
eine ungewöhlichkeit gibt es 0d 0d 0a
entweder verwendet man nur 0d oder 0d 0a,
gemischt ist ungewöhlich, aber das hat sich der liebe hersteller halt so ausgedacht.
wahrscheinlich ist@ <CR>für ihn ein art beginnzeichen.
nun zum müll.
was ist dir da aufgefallen? wieso reihenfolge?
abfolge des flows istrs232
|
tabelle parsen und jede zeile in ein battery objekt umwandeln.
jede zeile ist eine eigene message
|
für jede message (also für jede zeile) die daten für iobroker aufbereiten, so das notfalls der state angelegt werden kann, also mit unit, beschreibung, etc.
|
die letzte iobroker node hat keine konfiguration, da sie direkt die aufbereiteten daten nehemn und dann nach iobroker schreiben kann.also nimm den flow aus dem letzten post, importier ihn, beachte mein hinweis weiter oben zur konfiguration von node-red, damit er auch states nach 0_userdata schreiben darf.
lass den flow laufen. dann findest du unterhalb des datenpunkts
0_userdata.0.noderedbattery
die komplette struktur. screenshot habe ich gestern schon eingefügt.
je battery id werden nun diverse states angelegt mit den werten aus der tabelle
wenn du den datenpunktnamen ändern willst, dann kannst du das in der node zur aufbereitung der iobroker daten
@OliverIO
Bitte höfflichst um entschuldigung. Jetzt scheint es zu gehen.
Ich habe den letzten Flow genommen und die konvertierung an den Ausgang des Aufrufs gehängt und gestartet. Jetzt werden im Iob alle Datenpunkte sauber erstellt.

und ja, das "@" scheint eine Art Markierung für die Antwort zu sein, die kommt bei allen Abfragen immer als erstes, egal was man sich holt.
Wieso auch immer das vorher jedes Datenfeld in eine eigene msg geschrieben hat, weiss ich nicht, aber das habe ich gemeint.
Vielen Dank für deine aufwendige Arbeit, ich bin da eben echt eher der Handwerker und tue mich mit Software erstellen meist sehr schwer. Ich baue mir jetzt da mal rund um ein Flow der mir periodisch alle Zellen abholt und in die Datenpunkte schreibt.
Sobald ich das habe, stelle ich das hier zur Verfügung. -
@OliverIO
Bitte höfflichst um entschuldigung. Jetzt scheint es zu gehen.
Ich habe den letzten Flow genommen und die konvertierung an den Ausgang des Aufrufs gehängt und gestartet. Jetzt werden im Iob alle Datenpunkte sauber erstellt.

und ja, das "@" scheint eine Art Markierung für die Antwort zu sein, die kommt bei allen Abfragen immer als erstes, egal was man sich holt.
Wieso auch immer das vorher jedes Datenfeld in eine eigene msg geschrieben hat, weiss ich nicht, aber das habe ich gemeint.
Vielen Dank für deine aufwendige Arbeit, ich bin da eben echt eher der Handwerker und tue mich mit Software erstellen meist sehr schwer. Ich baue mir jetzt da mal rund um ein Flow der mir periodisch alle Zellen abholt und in die Datenpunkte schreibt.
Sobald ich das habe, stelle ich das hier zur Verfügung.@andibr
Alles gut, dadurch lernt man und evtl weckt es interesse an einem komplett neuen Gebiet. Es gibt auch andere Handwerker hier die mittlerweile komplette Adapter erstellen.Als tip. mit KI (auch schon die kostenlosen wie chatgpt,claude,gemini), kann man sich sehr viel erklären lassen.
Von den ganzen function nodes habe ich selbst keine einzige Zeile selbst geschrieben.
Ich könnte zwar, aber die KI macht das mittlerweile in sekunden. Die Kunst ist nur, es ihr richtig zu beschreiben. Im Zweifel einfach Copy/Paste und ein wenig Kontext, woher es kommt, welches Umfeld du nutzt und was du versuchst zu erreichen. Im einfachsten Fall einfach nur das Ziel eintippen und dich leiten lassen.Dann noch meine Kritik an den Low und "NoCode" Tools wie node-red, n8n,blockly.
Wenn es dir zu beginn einfacher fällt mit einer grafischen Darstellung zu arbeiten, alles gut. Aber diese Tools begrenzen dich irgendwann.
Klar könnte man durch installation weiterer nodes auch dieses Problem rein grafisch und Konfiguration lösen können, aber kürzer sind immer ein paar echte Programmzeilen.
Wenn du mal den kompletten flow bereitstellst, dann werde ich mal ein javascript für den iobroker erstellen (lassen) der das gleiche macht.
Hier haben wir eigentlich schon viel javascript verwendet, weil bei so speziellen Dingen dann die Standards schnell am Ende sind.
Aber für den Anfang um etwas über Abläufe, Struktur, Ursache/Wirkung zu lernen alles gut. -
@andibr
Alles gut, dadurch lernt man und evtl weckt es interesse an einem komplett neuen Gebiet. Es gibt auch andere Handwerker hier die mittlerweile komplette Adapter erstellen.Als tip. mit KI (auch schon die kostenlosen wie chatgpt,claude,gemini), kann man sich sehr viel erklären lassen.
Von den ganzen function nodes habe ich selbst keine einzige Zeile selbst geschrieben.
Ich könnte zwar, aber die KI macht das mittlerweile in sekunden. Die Kunst ist nur, es ihr richtig zu beschreiben. Im Zweifel einfach Copy/Paste und ein wenig Kontext, woher es kommt, welches Umfeld du nutzt und was du versuchst zu erreichen. Im einfachsten Fall einfach nur das Ziel eintippen und dich leiten lassen.Dann noch meine Kritik an den Low und "NoCode" Tools wie node-red, n8n,blockly.
Wenn es dir zu beginn einfacher fällt mit einer grafischen Darstellung zu arbeiten, alles gut. Aber diese Tools begrenzen dich irgendwann.
Klar könnte man durch installation weiterer nodes auch dieses Problem rein grafisch und Konfiguration lösen können, aber kürzer sind immer ein paar echte Programmzeilen.
Wenn du mal den kompletten flow bereitstellst, dann werde ich mal ein javascript für den iobroker erstellen (lassen) der das gleiche macht.
Hier haben wir eigentlich schon viel javascript verwendet, weil bei so speziellen Dingen dann die Standards schnell am Ende sind.
Aber für den Anfang um etwas über Abläufe, Struktur, Ursache/Wirkung zu lernen alles gut.@OliverIO
Für mich als ex. Elektroninstallateur ist es eben vielfach einfacher in einer Maske zu sehen was der Code eigentlich machen sollte, als in einem strukturierten Text.Wir verwenden bei uns im Job auch sehr viel strukturierte Text für Steuerungsfunktionen, aber das ist in der Regel eine andere Abteilung, ich bin nur zuständig, dass die Elektropläne korrekt und vollständig für alle sind.
Klar, dass interesse an den Möglichkeiten ist schon lange geweckt, aber wie bei allem, wenn man das nicht oft macht, vergisst man sehr vieles wieder. Bei mir ist entstehen solche Dinge eben manchmal aus "dummen" Ideen, oder eben aus "herausforderungen" die man nicht direkt sucht.
Ich arbeite mich jetzt mal rund um und schaue was da raus kommt.
Nochmals herzlichen DankGruss Andi
(aus der überhitzten Schweiz) -
@OliverIO
Für mich als ex. Elektroninstallateur ist es eben vielfach einfacher in einer Maske zu sehen was der Code eigentlich machen sollte, als in einem strukturierten Text.Wir verwenden bei uns im Job auch sehr viel strukturierte Text für Steuerungsfunktionen, aber das ist in der Regel eine andere Abteilung, ich bin nur zuständig, dass die Elektropläne korrekt und vollständig für alle sind.
Klar, dass interesse an den Möglichkeiten ist schon lange geweckt, aber wie bei allem, wenn man das nicht oft macht, vergisst man sehr vieles wieder. Bei mir ist entstehen solche Dinge eben manchmal aus "dummen" Ideen, oder eben aus "herausforderungen" die man nicht direkt sucht.
Ich arbeite mich jetzt mal rund um und schaue was da raus kommt.
Nochmals herzlichen DankGruss Andi
(aus der überhitzten Schweiz)Die versprochene Zusammenfassung des ganze:
Das BMS der Pytes 48100R-C16 lässt sich über den Konsolen-Anschluss parallel zum regulären Betrieb mit BMS-CAN auslesen.
Mit einem einfachen Terminal auf dem PC (Putty, Hyperterm, minicom, screen usw..) lässt sich eine einfach serielle Verbinung aufbauen und mit ein paar wenigen Befehlen bekommt man eine grosse Anzahl an Informationen. Dabei braucht es nur 1 serielle Verbindung zum Master-BMS der Rest läuft intern zwischen den einzelnen Batteriepacks ab.Ich habe mir einen USBtoRS232 Adapter (mit einem FTDI FTFS8I5L) und einem Konsolenkabel von einer Cisco-Firewall eine Verbindung mit 115200 8N1 geschaffen. Nun kann man an der Konsole im Terminal mit dem Befehl "help" folgende Anzeige auslesen:
bat Battery data show - bat [pwr][index] data History data load - data [event/history][item] datalist Show recorded data - datalist {event/history}{item}} disp Display Info at regular intervals - disp [(pwrs pwrNo)/val]/[(bats batNo)/volt/curr/temp] help Help [cmd] info Device infomation - info log Log information show - log login Login Admin mode - login [password] logout user mode - logout pwr Power data show - pwr [index] shut Shut down - shut soh State of health - soh [addr] stat Statistic data show - stat time Time - time [year] [month] [day] [hour] [minute] [second] trst Test Soft Reset - trst updata updata system - updata ver firmware info - vermit dem Befehl "bat nn" lassen sich dann die aktuellen Werte jeder einzelnen Zelle aus den Blöcke auslesen und immer pro BMS anzeigen lassen (das Master-BMS hat die Nr. 1).
Damit das ganze auch einigermassen nutzbar ist, habe ich (mit Hilfe) ein Node-Red Flow gebastelt, der mir die so erhaltenen Daten sortiert und verarbeitet und in die Iobroker Datenpunkte füllt.
Leider läuft es immer mal wieder in einen Timeout und/oder in den Fehler das er den String nicht zerlegen kann.
Ich hoffe verständlich erklärt zu haben was ich wollte und wie ich das gemacht habe? Bei fragen einfach hier schreiben.
Andi
-
Kennst du das hier:
https://forum.iobroker.net/post/1052677 -
Kennst du das hier:
https://forum.iobroker.net/post/1052677@homoran Ja das idt genau der Adapter mit den ich es fast ein ganzen Tag probiert habe und darauf beschlossen habe, es selber zu probieren.
Scheinbar gibt es eben Unterschiede zwischen Pytes und Pylontech BMS. Auf dem CAN Port scheinen sie identisch zu sein oder die Pytes sprechen die Pylontech Sprache. -
@Homoran
Ja, ich weiss. Und ich habe etliches getestet, nur er hat weder die Schnittstelle noch die Pytes gefunden.
Mit den genau gleichen Vorgaben und Linux Rechten schafft es mein Node-Red aus dem gleichen LXC Container raus das Master BMS zu erreichen. Der Adapter schaffte es aber nicht, zu dem hat er automatisch Datenpunkte angelegt beim einfügen der Instanz, die es gar nicht gibt.
Ich gehe davon aus, dass der Adapter mal geschaffen und getesten wurde mit Pylontech und weil die BMS vieleicht mal identisch gearbeitet hatten das dann so reingekommen ist. Leider hat auch der Entwickler des Adapters bis jetz keine Reaktion gezeigt, so dass ich davon aus gehe, dass kein Interesse mehr besteht. Darum habe ich eben den mir einigermassen gangbare Weg gewählt. Adapterentwicklung oder Anpassung ist ausserhalb meiner Fähigkeiten.
Ich helfe gerne wenn ich was testen kann, aber wie man im ganzen Verlauf sehen kann, begreife ich gewisse Software Dinge irgend wie nicht.
Ich weiss man könnte auch den Flow viel eleganter machen mit einer Liste und immer der gleichen Abfrage, aber ich habe es nicht hingekriegt. -
Mein Problem ist, dass ich mit node-red vielleicht vor knapp 10 Jahren zuletzt gearbeitet hatte.
Seit es Blockly gibt bin ich damit unterwegs.Aber selbst wenn es mit dem Pytes-Kabel direkt ginge, wüsste ich überhaupt nicht wie ich die Schnittstelle ansprechen müsste.
-
Die versprochene Zusammenfassung des ganze:
Das BMS der Pytes 48100R-C16 lässt sich über den Konsolen-Anschluss parallel zum regulären Betrieb mit BMS-CAN auslesen.
Mit einem einfachen Terminal auf dem PC (Putty, Hyperterm, minicom, screen usw..) lässt sich eine einfach serielle Verbinung aufbauen und mit ein paar wenigen Befehlen bekommt man eine grosse Anzahl an Informationen. Dabei braucht es nur 1 serielle Verbindung zum Master-BMS der Rest läuft intern zwischen den einzelnen Batteriepacks ab.Ich habe mir einen USBtoRS232 Adapter (mit einem FTDI FTFS8I5L) und einem Konsolenkabel von einer Cisco-Firewall eine Verbindung mit 115200 8N1 geschaffen. Nun kann man an der Konsole im Terminal mit dem Befehl "help" folgende Anzeige auslesen:
bat Battery data show - bat [pwr][index] data History data load - data [event/history][item] datalist Show recorded data - datalist {event/history}{item}} disp Display Info at regular intervals - disp [(pwrs pwrNo)/val]/[(bats batNo)/volt/curr/temp] help Help [cmd] info Device infomation - info log Log information show - log login Login Admin mode - login [password] logout user mode - logout pwr Power data show - pwr [index] shut Shut down - shut soh State of health - soh [addr] stat Statistic data show - stat time Time - time [year] [month] [day] [hour] [minute] [second] trst Test Soft Reset - trst updata updata system - updata ver firmware info - vermit dem Befehl "bat nn" lassen sich dann die aktuellen Werte jeder einzelnen Zelle aus den Blöcke auslesen und immer pro BMS anzeigen lassen (das Master-BMS hat die Nr. 1).
Damit das ganze auch einigermassen nutzbar ist, habe ich (mit Hilfe) ein Node-Red Flow gebastelt, der mir die so erhaltenen Daten sortiert und verarbeitet und in die Iobroker Datenpunkte füllt.
Leider läuft es immer mal wieder in einen Timeout und/oder in den Fehler das er den String nicht zerlegen kann.
Ich hoffe verständlich erklärt zu haben was ich wollte und wie ich das gemacht habe? Bei fragen einfach hier schreiben.
Andi
-
Mein Problem ist, dass ich mit node-red vielleicht vor knapp 10 Jahren zuletzt gearbeitet hatte.
Seit es Blockly gibt bin ich damit unterwegs.Aber selbst wenn es mit dem Pytes-Kabel direkt ginge, wüsste ich überhaupt nicht wie ich die Schnittstelle ansprechen müsste.
@Homoran
Keine Ahnung ob man mit Blockly das auch hinkriegen würde, aber grundsätzlich ist es nichts kompliziertes. Wenn du mit dem original Pytes Kabel und dem Windows BMS-Tool drauf zugreifen kannst, geht es mit allem anderen auch.
Mit Putty kannst du eine "telnet" Verbindung auf den COMx Port aufbauen und schon kannst du die Daten abholen. Sobald die Verbindung aufgebaut ist, einfach "bat 1" schreiben und ENTER drücken, dann kommen die Daten auf den Bildschirm.
Das aufwendige ist eher die angezeigten Daten in etwas vernünftiges zu verwandeln.
Die serielle Schnittstelle ist da sehr dankbar, braucht 3 Drähte und die gleiche Einstellungen und die Geräte sprechen miteinander, ist halt eben eine der ersten Computerschnittstellen die es dazumals gab.
Leider sind die Distanzen erwas gross in die Schweiz, sonst würde ich gerne auch praktisch helfen. -
Wenn es einen timeout gibt kommt wahrscheinlich kein String. Entweder eine Fehlermeldung oder nix. Die nachfolgenden nodes verarbeiten das gnadenlos.
Da müsste man dann das ausfiltern -
@OliverIO ich kopiere morgen mal die Meldungen die mir Node-Red auswirft. Es steht meistens, ....ist kein String....
Mein Flow sieht so aus:

Manchmal funktioniert es richtig und manchmal eben nicht. Ich habe mir überlegt, ob evt. daran liegen könnte, dass der Port nach der Abfrage nicht geschlossen wird?
Wenn ich im Minicom den Connect offen stehen lasse ist mir bis jetzt nicht aufgefallen, dass da keine Antwort kommen würde.Im Log sieht es dann so aus:

Wenn ich dann den Flow verändere und neu deploye funktioniert es eine Weile ganz gut und plötzlich geht es wieder nicht mehr. Bis jetzt habe ich noch kein Muster gefunden.
Ich habe da noch alte USR-TCP232-T2 Adapter rumliegen

Allenfalls könnte man damit ja auch etwas machen und hätte dann einfach einen Netzwerk-Port zum abfragen, aber ich denke das Thema mit dem Timeout wird gleich bleiben. So wie ich es verstehe kommt der Timeout nicht vom Port sondern ist ein "nicht reagieren des BMS".
-
Mein Flow sieht so aus:

Manchmal funktioniert es richtig und manchmal eben nicht. Ich habe mir überlegt, ob evt. daran liegen könnte, dass der Port nach der Abfrage nicht geschlossen wird?
Wenn ich im Minicom den Connect offen stehen lasse ist mir bis jetzt nicht aufgefallen, dass da keine Antwort kommen würde.Im Log sieht es dann so aus:

Wenn ich dann den Flow verändere und neu deploye funktioniert es eine Weile ganz gut und plötzlich geht es wieder nicht mehr. Bis jetzt habe ich noch kein Muster gefunden.
Ich habe da noch alte USR-TCP232-T2 Adapter rumliegen

Allenfalls könnte man damit ja auch etwas machen und hätte dann einfach einen Netzwerk-Port zum abfragen, aber ich denke das Thema mit dem Timeout wird gleich bleiben. So wie ich es verstehe kommt der Timeout nicht vom Port sondern ist ein "nicht reagieren des BMS".
Als erstes muss man rausfinden an welcher Node der Fehler entsteht.
Dann in dieser function node etwas debug code einbauen
Damit man sieht was für ein datentyp da ankommt und was für Inhaltsangaben ankommt.
Dann muss man bewerten wie man damit umgeht.
Wenn es sonst funktioniert, dann kann man das einfach durch Code ausfiltern, also keine msg zurückgeben, so das die nachfolgenden nodes nicht getriggert werden.Nur mit einem Bild vom Flow kann niemand was anfangen.
Mit mehr, kann ich erst Ende nächster woche weitermachen -
Als erstes muss man rausfinden an welcher Node der Fehler entsteht.
Dann in dieser function node etwas debug code einbauen
Damit man sieht was für ein datentyp da ankommt und was für Inhaltsangaben ankommt.
Dann muss man bewerten wie man damit umgeht.
Wenn es sonst funktioniert, dann kann man das einfach durch Code ausfiltern, also keine msg zurückgeben, so das die nachfolgenden nodes nicht getriggert werden.Nur mit einem Bild vom Flow kann niemand was anfangen.
Mit mehr, kann ich erst Ende nächster woche weitermachen@OliverIO ich versuche morgen noch etwas mehr raus zu holen. Eben es verwirrt mich das es manchmal durchläuft und dann plötzlich nicht mehr.
Für mich als Laie sieht es irgendwie so aus, wie wenn das BMS irgendwie auf was warten würde und daher nicht mehr. -
Statt des Screenshots am besten den Node-Red-Export hier in Code-Tags posten. Dann kann man sehen, ob man helfen kann.
Hier ist der ganze Flow, den ich unterdessen zusammen habe:
[ { "id": "26771494c726dd85", "type": "tab", "label": "Pytes", "disabled": false, "info": "", "env": [] }, { "id": "09a7088a7c52e930", "type": "inject", "z": "26771494c726dd85", "name": "Start Impuls", "props": [ { "p": "payload" } ], "repeat": "900", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "bat", "payloadType": "str", "x": 130, "y": 60, "wires": [ [ "f6f7978549817c9b", "dd20c6bfc8c1643a", "aae8b0af1ff64ae1", "6df4f9bd092fdcd3", "d81970d6d1e4cca6", "c8cc5a91dbf6eb1a", "09faeaa564e7d7ea", "027109f5112c3c3e" ] ] }, { "id": "f6f7978549817c9b", "type": "trigger", "z": "26771494c726dd85", "name": "bat 1", "op1": "bat 1", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 690, "y": 60, "wires": [ [ "f1e94e531bb2b4f9" ] ] }, { "id": "f1e94e531bb2b4f9", "type": "serial request", "z": "26771494c726dd85", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 890, "y": 60, "wires": [ [ "79724f85531596e4", "4b31f05c7b6c5425" ] ] }, { "id": "79724f85531596e4", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1080, "y": 60, "wires": [ [ "4a8dcae7d0f7d8a8" ] ] }, { "id": "68c635b31d513d6d", "type": "debug", "z": "26771494c726dd85", "name": "debug 4", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1440, "y": 120, "wires": [] }, { "id": "6a16c00e7b0e2997", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1460, "y": 60, "wires": [] }, { "id": "4a8dcae7d0f7d8a8", "type": "function", "z": "26771494c726dd85", "name": "function 2", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.01.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1260, "y": 60, "wires": [ [ "6a16c00e7b0e2997", "68c635b31d513d6d" ] ] }, { "id": "dd20c6bfc8c1643a", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "1", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 520, "y": 200, "wires": [ [ "0dc431cc8e3444fa" ] ] }, { "id": "3cce03774442db86", "type": "debug", "z": "26771494c726dd85", "name": "debug 2", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1440, "y": 260, "wires": [] }, { "id": "0dc431cc8e3444fa", "type": "trigger", "z": "26771494c726dd85", "name": "bat 2", "op1": "bat 2", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 200, "wires": [ [ "72209bb281ee39b7" ] ] }, { "id": "72209bb281ee39b7", "type": "serial request", "z": "26771494c726dd85", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 890, "y": 200, "wires": [ [ "9441d38d2c549fb8" ] ] }, { "id": "9441d38d2c549fb8", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1080, "y": 200, "wires": [ [ "c7f18bb5735b986f" ] ] }, { "id": "5f5f0afbf22fe52b", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1460, "y": 200, "wires": [] }, { "id": "c7f18bb5735b986f", "type": "function", "z": "26771494c726dd85", "name": "function 3", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.02.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1260, "y": 200, "wires": [ [ "5f5f0afbf22fe52b", "3cce03774442db86" ] ] }, { "id": "aae8b0af1ff64ae1", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "2", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 520, "y": 340, "wires": [ [ "085f008ec32136f1" ] ] }, { "id": "acfcfbc9dab48c55", "type": "debug", "z": "26771494c726dd85", "name": "debug 5", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1440, "y": 400, "wires": [] }, { "id": "085f008ec32136f1", "type": "trigger", "z": "26771494c726dd85", "name": "bat 3", "op1": "bat 3", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 340, "wires": [ [ "7134518ae47c75db" ] ] }, { "id": "7134518ae47c75db", "type": "serial request", "z": "26771494c726dd85", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 890, "y": 340, "wires": [ [ "2819be0b93263306" ] ] }, { "id": "2819be0b93263306", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1080, "y": 340, "wires": [ [ "ca02643445158e40" ] ] }, { "id": "ea0794a8642caf84", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1460, "y": 340, "wires": [] }, { "id": "ca02643445158e40", "type": "function", "z": "26771494c726dd85", "name": "function 4", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.03.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1260, "y": 340, "wires": [ [ "ea0794a8642caf84", "acfcfbc9dab48c55" ] ] }, { "id": "6df4f9bd092fdcd3", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "3", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 520, "y": 480, "wires": [ [ "a832dedddd9504c7" ] ] }, { "id": "b7c07a571002ad06", "type": "debug", "z": "26771494c726dd85", "name": "debug 6", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1440, "y": 540, "wires": [] }, { "id": "a832dedddd9504c7", "type": "trigger", "z": "26771494c726dd85", "name": "bat 4", "op1": "bat 4", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 480, "wires": [ [ "b82258c553850a68" ] ] }, { "id": "b82258c553850a68", "type": "serial request", "z": "26771494c726dd85", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 890, "y": 480, "wires": [ [ "4387216c1822cbcf" ] ] }, { "id": "4387216c1822cbcf", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1080, "y": 480, "wires": [ [ "94e6b42f7309c443" ] ] }, { "id": "51c2e01cf0878450", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1460, "y": 480, "wires": [] }, { "id": "94e6b42f7309c443", "type": "function", "z": "26771494c726dd85", "name": "function 5", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.04.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1260, "y": 480, "wires": [ [ "51c2e01cf0878450", "b7c07a571002ad06" ] ] }, { "id": "d81970d6d1e4cca6", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "4", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 520, "y": 620, "wires": [ [ "f1fd5ef8888380ad" ] ] }, { "id": "fb84ec681c169cd6", "type": "debug", "z": "26771494c726dd85", "name": "debug 7", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1440, "y": 680, "wires": [] }, { "id": "f1fd5ef8888380ad", "type": "trigger", "z": "26771494c726dd85", "name": "bat 5", "op1": "bat 5", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 620, "wires": [ [ "75f9d95891961faf" ] ] }, { "id": "75f9d95891961faf", "type": "serial request", "z": "26771494c726dd85", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 890, "y": 620, "wires": [ [ "c4b473ff613b76e2" ] ] }, { "id": "c4b473ff613b76e2", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1080, "y": 620, "wires": [ [ "4603fd0d509f8f04" ] ] }, { "id": "d35fbb5808f3ecae", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1460, "y": 620, "wires": [] }, { "id": "4603fd0d509f8f04", "type": "function", "z": "26771494c726dd85", "name": "function 6", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.05.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1260, "y": 620, "wires": [ [ "d35fbb5808f3ecae", "fb84ec681c169cd6" ] ] }, { "id": "c8cc5a91dbf6eb1a", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "5", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 520, "y": 760, "wires": [ [ "188d8b0ef9b1ec1c" ] ] }, { "id": "810cc1a467a83b14", "type": "debug", "z": "26771494c726dd85", "name": "debug 8", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1440, "y": 820, "wires": [] }, { "id": "188d8b0ef9b1ec1c", "type": "trigger", "z": "26771494c726dd85", "name": "bat 6", "op1": "bat 6", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 760, "wires": [ [ "2fd087fa4da3841b" ] ] }, { "id": "2fd087fa4da3841b", "type": "serial request", "z": "26771494c726dd85", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 890, "y": 760, "wires": [ [ "59e325c588ae2c94" ] ] }, { "id": "59e325c588ae2c94", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1080, "y": 760, "wires": [ [ "5d321ccd3cdb9ce7" ] ] }, { "id": "b60baba38d523d1e", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1460, "y": 760, "wires": [] }, { "id": "5d321ccd3cdb9ce7", "type": "function", "z": "26771494c726dd85", "name": "function 7", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.06.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1260, "y": 760, "wires": [ [ "b60baba38d523d1e", "810cc1a467a83b14" ] ] }, { "id": "09faeaa564e7d7ea", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "6", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 520, "y": 900, "wires": [ [ "c0c69b8e170813f8" ] ] }, { "id": "0e613121bc95e046", "type": "debug", "z": "26771494c726dd85", "name": "debug 9", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1440, "y": 960, "wires": [] }, { "id": "c0c69b8e170813f8", "type": "trigger", "z": "26771494c726dd85", "name": "bat 7", "op1": "bat 7", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 900, "wires": [ [ "5e45ba1558c0e2c5" ] ] }, { "id": "5e45ba1558c0e2c5", "type": "serial request", "z": "26771494c726dd85", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 890, "y": 900, "wires": [ [ "56d333a3d08a8b60" ] ] }, { "id": "56d333a3d08a8b60", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1080, "y": 900, "wires": [ [ "8dc1347bba1166d9" ] ] }, { "id": "a2f16c5161ec9254", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1460, "y": 900, "wires": [] }, { "id": "8dc1347bba1166d9", "type": "function", "z": "26771494c726dd85", "name": "function 8", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.07.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1260, "y": 900, "wires": [ [ "a2f16c5161ec9254", "0e613121bc95e046" ] ] }, { "id": "027109f5112c3c3e", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "7", "timeoutUnits": "minutes", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 520, "y": 1040, "wires": [ [ "98d31a678f60f56d" ] ] }, { "id": "8ed399995f0adf29", "type": "debug", "z": "26771494c726dd85", "name": "debug 10", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1440, "y": 1100, "wires": [] }, { "id": "98d31a678f60f56d", "type": "trigger", "z": "26771494c726dd85", "name": "bat 8", "op1": "bat 8", "op2": "", "op1type": "str", "op2type": "nul", "duration": "250", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 1040, "wires": [ [ "abed25adf4a19908" ] ] }, { "id": "abed25adf4a19908", "type": "serial request", "z": "26771494c726dd85", "name": "BMSMaster", "serial": "d031dd424e7b1ff2", "x": 890, "y": 1040, "wires": [ [ "dfa2e714a883320c" ] ] }, { "id": "dfa2e714a883320c", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1080, "y": 1040, "wires": [ [ "4127ce3ea3ebf45b" ] ] }, { "id": "9d3e816f7aae72a8", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1460, "y": 1040, "wires": [] }, { "id": "4127ce3ea3ebf45b", "type": "function", "z": "26771494c726dd85", "name": "function 9", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.08.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1260, "y": 1040, "wires": [ [ "9d3e816f7aae72a8", "8ed399995f0adf29" ] ] }, { "id": "4b31f05c7b6c5425", "type": "debug", "z": "26771494c726dd85", "name": "debug 11", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 1060, "y": 120, "wires": [] }, { "id": "d031dd424e7b1ff2", "type": "serial-port", "name": "Pytes", "serialport": "/dev/serial/by-id/usb-FTDI_USB_Serial_Converter_FTFS8I5L-if00-port0", "serialbaud": "115200", "databits": 8, "parity": "none", "stopbits": 1, "waitfor": "@", "dtr": "none", "rts": "none", "cts": "none", "dsr": "none", "newline": "250", "bin": "false", "out": "time", "addchar": "\\r\\n", "responsetimeout": "250" }, { "id": "655fb8e61b544b29", "type": "global-config", "env": [], "modules": { "node-red-node-serialport": "2.0.3" } } ]Es ist äusserst interessant, ich habe heute mal die Schnittstellen durchreichung in den Iobroker Proxmox LXC neu, mit den offiziellen Proxmox Werkzeugen neu gemacht. Dabei ist mir aufgefallen, das der Flow mehrere male sauber läuft, plötzlich kommt die Meldung:
node-red.0 2026-08-15 16:54:00.757 error Node-RED: 15 Aug 16:54:00 - [error] [function:convert to msgs] msg.payload ist kein Stringund auch im Flow wird dann beim entsprechenden Node aus einem "verbunden" ein "Timeout". Nach einem deaktivieren des ganzen Flow und deploy und anschliessendem aktivieren und deploy läuft er wieder ein paar Stunden ohne irgendwelche Probleme. Beim Fehler kommt scheinbar einfach keine Antwort mehr und der "function-Node" wirft dann den Fehler raus. Ist ja richtig und auch verständlich.
Kann es sein, dass es mit meiner vervielfachung auch quasi mehrere offenen Schnittstellen Task gibt?
Ich habe mich gefragt ob es allenfalls besser wäre trigger-Node so umzubauen das er eben nicht einfach nur den Text sendet, sondern das er quasi eine Liste mit den Texten abarbeitet. Oder das nach dem function Node ein zusätzlicher Node angestossen würde der die Schnittstelle schliesst? Nur da habe ich bis jetzt keinerlei Hinweise gefunden wie man eine solche serielle Schnittstelle schliessen kann.
-
Ich habe das mangels Batterie nicht testen können.
Das Skript geht die Batterien nach und nach durch und startet automatisch wieder, wenn ich die Verbindung unterbreche.
Den Comport musste ich natürlich umbiegen.
[ { "id": "26771494c726dd85", "type": "tab", "label": "Pytes", "disabled": false, "info": "", "env": [] }, { "id": "fe3238beacae91ad", "type": "junction", "z": "26771494c726dd85", "x": 340, "y": 180, "wires": [ [ "b28980471e986893", "fdaee2e16d78cc87" ] ] }, { "id": "665e7ef72b226bc2", "type": "junction", "z": "26771494c726dd85", "x": 780, "y": 260, "wires": [ [ "65a7b88c58ac941a" ] ] }, { "id": "65a7b88c58ac941a", "type": "junction", "z": "26771494c726dd85", "x": 60, "y": 260, "wires": [ [ "6d9a8c4540741cbf" ] ] }, { "id": "79724f85531596e4", "type": "function", "z": "26771494c726dd85", "name": "convert to msgs", "func": "const text = msg.payload;\n\nif (typeof text !== \"string\") {\n node.error(\"msg.payload ist kein String\", msg);\n return null;\n}\n\n// In einzelne Zeilen zerlegen\nconst lines = text\n .split(/\\r?\\n/)\n .map(line => line.trim())\n .filter(line => line !== \"\");\n\n// Nur Zeilen berücksichtigen, die mit einer Batterienummer beginnen\nconst dataLines = lines.filter(line => /^\\d+\\s/.test(line));\n\nconst messages = [];\n\nfor (const line of dataLines) {\n\n // Spalten sind durch mindestens 2 Leerzeichen getrennt\n const columns = line.split(/\\s{2,}/);\n\n if (columns.length < 9) {\n node.warn(\"Zeile konnte nicht verarbeitet werden: \" + line);\n continue;\n }\n\n const battery = {\n battery: Number(columns[0]),\n volt: Number(columns[1]),\n tempr: Number(columns[2]),\n baseState: columns[3],\n voltState: columns[4],\n currState: columns[5],\n tempState: columns[6],\n coulomb: Number(columns[7].replace(\"%\", \"\")),\n capacity: Number(columns[8].replace(/[^\\d]/g, \"\"))\n };\n\n messages.push({\n ...msg,\n payload: battery\n });\n}\n\n// Alle Nachrichten nacheinander über denselben Ausgang senden\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 940, "y": 80, "wires": [ [ "4a8dcae7d0f7d8a8" ] ] }, { "id": "68c635b31d513d6d", "type": "debug", "z": "26771494c726dd85", "name": "debug 4", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1260, "y": 160, "wires": [] }, { "id": "6a16c00e7b0e2997", "type": "ioBroker out", "z": "26771494c726dd85", "name": "", "topic": "", "ack": "true", "autoCreate": "true", "stateName": "", "role": "", "payloadType": "", "readonly": "", "stateUnit": "", "stateMin": "", "stateMax": "", "x": 1280, "y": 80, "wires": [] }, { "id": "4a8dcae7d0f7d8a8", "type": "function", "z": "26771494c726dd85", "name": "function 2", "func": "const battery = msg.payload;\n\nif (!battery || battery.battery === undefined) {\n node.error(\"Ungültiges Battery-Objekt\", msg);\n return null;\n}\n\nconst batteryId = battery.battery;\n\nconst states = [\n {\n id: \"voltage\",\n value: battery.volt,\n name: \"Voltage\",\n role: \"value.voltage\",\n type: \"number\",\n readonly: true,\n unit: \"mV\",\n min: 0\n },\n {\n id: \"temperature\",\n value: battery.tempr / 1000,\n name: \"Temperature\",\n role: \"value.temperature\",\n type: \"number\",\n readonly: true,\n unit: \"°C\"\n },\n {\n id: \"baseState\",\n value: battery.baseState,\n name: \"Base State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"voltageState\",\n value: battery.voltState,\n name: \"Voltage State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"currentState\",\n value: battery.currState,\n name: \"Current State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"temperatureState\",\n value: battery.tempState,\n name: \"Temperature State\",\n role: \"text\",\n type: \"string\",\n readonly: true\n },\n {\n id: \"charge\",\n value: battery.coulomb,\n name: \"Charge\",\n role: \"value.battery\",\n type: \"number\",\n readonly: true,\n unit: \"%\",\n min: 0,\n max: 100\n },\n {\n id: \"capacity\",\n value: battery.capacity,\n name: \"Capacity\",\n role: \"value\",\n type: \"number\",\n readonly: true,\n unit: \"mAh\",\n min: 0\n }\n];\n\nconst messages = [];\n\nfor (const state of states) {\n const out = {\n topic: `0_userdata.0.pytes.01.${batteryId}.${state.id}`,\n payload: state.value,\n\n stateName: state.name,\n stateRole: state.role,\n stateType: state.type,\n stateReadonly: state.readonly\n };\n\n if (state.unit !== undefined) {\n out.stateUnit = state.unit;\n }\n\n if (state.min !== undefined) {\n out.stateMin = state.min;\n }\n\n if (state.max !== undefined) {\n out.stateMax = state.max;\n }\n\n messages.push(out);\n}\n\nreturn [messages];", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1100, "y": 80, "wires": [ [ "6a16c00e7b0e2997", "68c635b31d513d6d" ] ] }, { "id": "c0f1afd033e9efcf", "type": "inject", "z": "26771494c726dd85", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "0.1", "topic": "", "payload": "true", "payloadType": "bool", "x": 110, "y": 40, "wires": [ [ "6d9a8c4540741cbf" ] ] }, { "id": "478993d3b5047088", "type": "serial control", "z": "26771494c726dd85", "name": "", "serial": "a78922aa38d9e450", "x": 840, "y": 180, "wires": [ [] ] }, { "id": "c0ff5d2e0b5d2616", "type": "debug", "z": "26771494c726dd85", "name": "debug 14", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 800, "y": 40, "wires": [] }, { "id": "723e5a9cad6671c7", "type": "comment", "z": "26771494c726dd85", "name": "Serial-Ports", "info": "/dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0\n\n/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0", "x": 90, "y": 100, "wires": [] }, { "id": "69611b30df398dd3", "type": "serial request", "z": "26771494c726dd85", "name": "", "serial": "a78922aa38d9e450", "x": 600, "y": 80, "wires": [ [ "c0ff5d2e0b5d2616", "598d93854bab5014" ] ] }, { "id": "6d9a8c4540741cbf", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "2", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 300, "y": 80, "wires": [ [ "8979945491174ae1" ] ] }, { "id": "8979945491174ae1", "type": "function", "z": "26771494c726dd85", "name": "naechster", "func": "const batts =[\"bat 1\", \"bat 2\", \"bat 3\", \"bat 4\", \"bat 5\", \"bat 6\", \"bat 7\",\"bat 8\"];\n\nflow.set(\"batts\", batts);\n\nvar pos = parseInt(flow.get(\"pos\"));\npos++;\nif(pos > batts.length -1){pos=0}\nmsg.payload = batts[pos];\nflow.set (\"pos\", pos);\n\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "// Der Code hier wird ausgeführt,\n// wenn der Node gestartet wird\nflow.set(\"pos\", 0);\n", "finalize": "", "libs": [], "x": 460, "y": 80, "wires": [ [ "69611b30df398dd3" ] ] }, { "id": "a418a9b4681de96b", "type": "catch", "z": "26771494c726dd85", "name": "", "scope": null, "uncaught": false, "x": 220, "y": 180, "wires": [ [ "fe3238beacae91ad" ] ] }, { "id": "598d93854bab5014", "type": "switch", "z": "26771494c726dd85", "name": "", "property": "payload", "propertyType": "msg", "rules": [ { "t": "istype", "v": "string", "vt": "string" }, { "t": "else" } ], "checkall": "true", "repair": false, "outputs": 2, "x": 750, "y": 80, "wires": [ [ "79724f85531596e4", "6d9a8c4540741cbf" ], [ "fe3238beacae91ad" ] ] }, { "id": "5a79550b570ff05d", "type": "change", "z": "26771494c726dd85", "name": "Öffne Verbindung", "rules": [ { "t": "set", "p": "payload", "pt": "msg", "to": "{\"enabled\":true}", "tot": "json" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 630, "y": 220, "wires": [ [ "478993d3b5047088", "665e7ef72b226bc2" ] ] }, { "id": "b28980471e986893", "type": "change", "z": "26771494c726dd85", "name": "Schließe Verbindung", "rules": [ { "t": "set", "p": "payload", "pt": "msg", "to": "{\"enabled\":false}", "tot": "json" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 640, "y": 180, "wires": [ [ "478993d3b5047088" ] ] }, { "id": "fdaee2e16d78cc87", "type": "delay", "z": "26771494c726dd85", "name": "", "pauseType": "delay", "timeout": "4", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 440, "y": 220, "wires": [ [ "5a79550b570ff05d" ] ] }, { "id": "ecfde46ecc908741", "type": "inject", "z": "26771494c726dd85", "name": "Test", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "true", "payloadType": "bool", "x": 230, "y": 240, "wires": [ [ "fe3238beacae91ad" ] ] }, { "id": "a78922aa38d9e450", "type": "serial-port", "name": "SerialPort", "serialport": "/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0", "serialbaud": "115200", "databits": "8", "parity": "none", "stopbits": "1", "waitfor": "", "dtr": "none", "rts": "none", "cts": "none", "dsr": "none", "newline": "250", "bin": "false", "out": "time", "addchar": "\\r\\n", "responsetimeout": "10000" } ]
Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.
Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.
Mit deinem Input könnte dieser Beitrag noch besser werden 💗
Registrieren Anmelden
