Skip to content

using asyncio in modbus_tcp#1019

Open
CaeruleusAqua wants to merge 1 commit intosmarthomeNG:developfrom
CaeruleusAqua:modbus_tcp_async
Open

using asyncio in modbus_tcp#1019
CaeruleusAqua wants to merge 1 commit intosmarthomeNG:developfrom
CaeruleusAqua:modbus_tcp_async

Conversation

@CaeruleusAqua
Copy link
Contributor

PR Summary

Refactor the modbus_tcp SmartHomeNG plugin to use the pymodbus Async API (AsyncModbusTcpClient) with a plugin-owned asyncio event loop (separate thread). All Modbus operations (connect/read/write) are now non-blocking.

What changed

  • Replaced synchronous ModbusTcpClient with AsyncModbusTcpClient
  • Added persistent connection handling with automatic reconnect/backoff
  • Removed classic scheduler polling for reads; reads run continuously async
  • Reinterpreted cycle:
    • None / 0: no reads, no item updates
    • > 0: buffer latest values, flush to items every cycle seconds (scheduler used only for flushing)
    • < 0: write to items immediately (no buffering)

Why this is useful

  • Enables more accurate timestamps: values are acquired continuously and timestamped at read time, rather than being tied to scheduler execution jitter.
  • Prevents data loss with short cycle times: acquisition runs independently of the flush interval, and the latest value is always available even if scheduler callbacks are delayed.

@CaeruleusAqua
Copy link
Contributor Author

As discussed, we will stick with pymodbus 3.10. However, if you prefer, I can also update all plugins to 4.x. I have some time over the holidays.

@Morg42
Copy link
Member

Morg42 commented Jan 4, 2026

Sadly, I can't test this, but it sounds promising!

@onkelandy
Copy link
Member

Da gäbs jetzt seit 1.11 neue Funktionen für asyncio: https://smarthomeng.github.io/dev_doc/referenz/plugins/asyncio_support.html
Das hue3 Plugin hat das umgesetzt. Ich hab versucht, das hier auch umzusetzen: https://github.com/onkelandy/huawei_sun2000
Habe keine Ahnung, ob ich das richtig gemacht habe, vielleicht kannst du mal drüber schauen? Scheinst dich ja mit dem Kram auszukennen @CaeruleusAqua

Und evtl. bietet es sich an, das modbus Plugin noch etwas zu vereinfachen mit den integrierten shng Methoden?

@CaeruleusAqua
Copy link
Contributor Author

Hhmm das mit dem async io hab ich garnicht gesehen...

Eigentlich gehört es hier nicht her.

Aber ich sehe in deinem Code ne Menge probleme, fehlende returns fehlende awaits...

Z.b. awaits self.inverter_write(register

Warum ?
len(ex) == 101 and ex[86:-1] == 'IllegalAddress

Bin gerade nur am Handy daher ist viel schreiben gerade schwer.

Schön, dass es für den inverter ne python lib gibt. Ich würde aber sagen es ist einfacher die Modbusregister selber aus dem Datenblatt zu holen und das modbus plugin zu nehmen, statt nen eigenes Plugin dafür zu schreiben ..

@bmxp
Copy link
Member

bmxp commented Jan 24, 2026

Ich denke auch das wir von dem ganzen Modbus Wirrwar eher wegkommen sollten als noch was neues dazuzudichten.

@Morg42
Copy link
Member

Morg42 commented Jan 25, 2026

@CaeruleusAqua was meinst du mit fehlenden returns?

@CaeruleusAqua
Copy link
Contributor Author

CaeruleusAqua commented Jan 25, 2026

async def check_forstop(self):

return True oder None.... (weil return für fehlt)

Geht sicher auch so, schön ists aber nicht...

@Morg42
Copy link
Member

Morg42 commented Jan 25, 2026

Am Ende von jedem def:-Block wird implizit return None ausgeführt. Alle Methode mit return oder return None abzuschließen, ist nur notwendig, wenn man etwas anderes als None zurückgeben möchte.

@CaeruleusAqua
Copy link
Contributor Author

Schon klar, die Funktion sollte aber False zurückgeben und nicht None. Funktioniert trotzdem aber schön ist es nicht. Aber vielleicht bin auch nicht Pythonic genug ;-)

@Morg42
Copy link
Member

Morg42 commented Jan 25, 2026

Wenn der falsche Typ zurückgegeben wird, hast du völlig Recht. So im Detail habe ich nicht in den Code geschaut...

@onkelandy
Copy link
Member

Ich denke auch das wir von dem ganzen Modbus Wirrwar eher wegkommen sollten als noch was neues dazuzudichten.

Worauf beziehst du dich?

@Morg42
Copy link
Member

Morg42 commented Jan 26, 2026

Wahrscheinlich darauf, dass ein generelles ModBus-Plugin wartbarer und über die Breite stabiler gehalten werden kann, als gesonderte Plugins für jedes ModBus-angebundene Gerät zu erstellen...

@Tom-Bom-badil
Copy link
Contributor

Ich denke auch das wir von dem ganzen Modbus Wirrwar eher wegkommen sollten als noch was neues dazuzudichten.

Offtopic, aber passend zum Thema 'Wirrwarr' : Hendrik (@henfri) hat zu pymodbus kurz vor Weihnachten einen recht interessanten Post geschrieben.

Nachdem Jan Iversen seinen entsprechenden PR gemerged hat (siehe Github-Link im Post), ist es jetzt möglich, für jedes Plugin eine eigene pymodbus-Instanz zu verwenden. Somit kann es eine generelle pymodbus-shNG-Version geben, aber Plugins können individuell von dieser abweichen.

Das sollte zumindest helfen, den gleichzeitigen Updatezwang aus der Vergangenheit (alle Plugins müssen gleichzeitig hochgezogen werden) zu entschärfen. Instanz der jeweils funktionierenden Version im Plugin 'festtackern' sollte reichen (?ggf alternatives pymodbus als Unterverzeichnis im Plugin mitliefern?).

Natürlich hat das noch diverse andere Implikationen (Code-Aktualität, veraltete Bibliotheken, potentielle Inkompatibilitäten zwischen verschiedenen pymodbus- und Python-Versionen); aber zumindest ist es ein denkbarer Ausweg aus dem 'Chaos' und dem Updatezwang - also eine Tür, die vorher noch nicht offen war.

/tom

@onkelandy
Copy link
Member

Wahrscheinlich darauf, dass ein generelles ModBus-Plugin wartbarer und über die Breite stabiler gehalten werden kann, als gesonderte Plugins für jedes ModBus-angebundene Gerät zu erstellen...

Ja, das würde ich eh gleich sehen.. ich hätte ja mal visioniert, Modbus ins SDP-Konstrukt zu bringen, aber ich denke, das ist mit asyncio und Co etwas zu kompliziert und wenig zielführend.

Frage ist für mich nur.. das Plugin hier ist ja wirklich nur TCP. Was ist mit Modbus_RTU? Ließe sich das integrieren oder ist das eine ganz andere Sache? Das ist nämlich durch das huawei-solar Modul auch abgedeckt. Inwieweit hier noch andere Vorteile existieren, kann ich schwer abschätzen. Wenn "nichts", dann würde ich auch eher sagen, ein Zusammenführen mit Modbus-Plugin(s)? wäre ein guter Ansatz.

@onkelandy
Copy link
Member

Hhmm das mit dem async io hab ich garnicht gesehen...
Warum ? len(ex) == 101 and ex[86:-1] == 'IllegalAddress
Schön, dass es für den inverter ne python lib gibt. Ich würde aber sagen es ist einfacher die Modbusregister selber aus dem Datenblatt zu holen und das modbus plugin zu nehmen, statt nen eigenes Plugin dafür zu schreiben ..

Ich muss dazu sagen, dass das Plugin eigentlich von @CannonRS kommt. Würde aber auch vorschlagen, Infos zum Code am besten in meinem Repo weiterzuführen und uns hier lediglich darüber zu unterhalten, ob eigene Plugins für versch. Modbus-Geräte überhaupt Sinn machen und wenn ja wann.

Die eine Zeile mit der IllegalAddress macht schon Sinn. Das Plugin liefert verschiedene Structs, aber je nach eingesetztem Gerät, sind trotzdem nicht alle Register vorhanden. Da kommt dann "Illegal" retour. Und damit das nicht jedes Mal abgefragt wird, kommt das in eine Skip Liste.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants