
Kurzlich habe ich über die Integration des Marstek Venus E in openHAB per modbus-Binding berichtet. Da ich bisher nur lesend auf die Register zugegriffen habe, war es jetzt an der Zeit, auch Teile der Steuerung per modbus umzusetzen. Das genutzte Gateway ist bei mir der Waveshare Rail-Mount Serial Server, RS232/485/422 to RJ45 Ethernet.
Konkreter Anwendungsfall:
- Die Steuerung des eAuto-Ladens war recht hemdsärmlig umgesetzt. Ziel war es den Speicher wegzuschalten, wenn „normal“ und nicht per Überschuss geladen wird.
Da ich – bevor ich die modbus-Anbindung vorgenommen hatte – nur über einen emulierten Shelly Werte an den Venus liefern konnte, musste ich den virtuellen Wert so setzen, dass sich der Speicher beim normalen Laden in Richtung 0W bewegt. Das ist nur in mehreren Schritten gelungen und war nicht schön umgesetzt. - Durch Modbus-Steuerung ist es leicht möglich, den Speicher in den RS485-Modus zu versetzen, so dass er den emulierten Shelly-Wert ignoriert, auf 0W in den Standby geht und nur noch auf RS485-Befehle hört. Dies macht er, bis der RS485-Modus wieder deaktiviert wird. Und genau das nutze ich nun, um den Speicher im Fall des normalen eAuto-Ladens „wegzuschalten“.
Analog zum Aktivieren / Deaktivieren des RS485-Modus kann man natürlich andere Register beschreiben und so mit all den Möglichkeiten, die aus der Register-Dokumentation ersichtlich sind, arbeiten.
Konkrete Umsetzung:
Die Registerbelegung ist im Ursprungsbeitrag einsehbar. Für das Aktivieren des RS485-Modus muss in Register 42000 (Typ uint16) der Wert 0X55AA (entspricht dezimal 21930) geschrieben werden. Für das Deaktivieren der Wert 0x55BB (entspricht dezimal 21947). Um den Wert schreiben zu können ist eine Rule die einfachste Möglichkeit.
Wir brauchen daher:
Zwei Things, eines, welches das Register via Modbus überhaupt erst zugreifbar macht (Bridge) und ein weiteres, welches den Lese- und Schreibzugriff auf das Register über die Bridge ermöglicht:

Zwei Items, eines, welches den Wert des Registers beinhaltet und beschrieben werden kann und einen Switch, der dann einfach per An- und Ausschalten den nötigen Wert schreibt:

Und zuletzt eine Rule, die das Tatsächliche Schreiben bei Bedienen des Switches übernimmt.
Thing 1 – Verbindung zum Waveshare
UID: modbus:poller:venus-waveshare:venus-holding-registers-42000-42000
label: Marstek Venus Modbus Holding Register 42000-42000
thingTypeUID: modbus:poller
configuration:
start: 42000
length: 1
refresh: 500
maxTries: 3
cacheMillis: 50
type: holding
bridgeUID: modbus:tcp:venus-waveshare
Thing 2 – Verbindung zum Register
UID: modbus:data:venus-holding-registers-42000-rs485:42000-write-rs485controlmode
label: Marstek Venus Modbus Write Register 42000 Control RS485 ON OFF
thingTypeUID: modbus:data
configuration:
readValueType: uint16
readTransform: default
writeType: holding
writeTransform: default
readStart: "42000"
updateUnchangedValuesEveryMillis: 1000
writeValueType: int16
writeMultipleEvenWithSingleRegisterOrCoil: false
writeMaxTries: 3
writeStart: "42000"
bridgeUID: modbus:poller:venus-waveshare:venus-holding-registers-42000-42000
channels:
- id: number
channelTypeUID: modbus:number-type
label: Value as Number
description: Number item channel
configuration: {}
- id: switch
channelTypeUID: modbus:switch-type
label: Value as Switch
description: Switch item channel
configuration: {}
- id: contact
channelTypeUID: modbus:contact-type
label: Value as Contact
description: Contact item channel
configuration: {}
- id: dimmer
channelTypeUID: modbus:dimmer-type
label: Value as Dimmer
description: Dimmer item channel
configuration: {}
- id: datetime
channelTypeUID: modbus:datetime-type
label: Value as DateTime
description: DateTime item channel
configuration: {}
- id: string
channelTypeUID: modbus:string-type
label: Value as String
description: String item channel
configuration: {}
- id: rollershutter
channelTypeUID: modbus:rollershutter-type
label: Value as Rollershutter
description: Rollershutter item channel
configuration: {}
- id: lastReadSuccess
channelTypeUID: modbus:last-successful-read-type
label: Last Successful Read
description: Date of last read
configuration: {}
- id: lastReadError
channelTypeUID: modbus:last-erroring-read-type
label: Last Erroring Read
description: Date of last read error
configuration: {}
- id: lastWriteSuccess
channelTypeUID: modbus:last-successful-write-type
label: Last Successful Write
description: Date of last write
configuration: {}
- id: lastWriteError
channelTypeUID: modbus:last-erroring-write-type
label: Last Erroring Write
description: Date of last write error
configuration: {}
Item 1 – Wert des Registers

Code ist recht simpel, wichtig ist natürlich die Verknüpfung mit dem Value as Number-Channel des things:
label: Venus Control RS485 Register Value
type: Number
category: ""
groupNames:
- pvspeicher
tags:
- Point
Item 2 – Switch für Darstellung und Steuerung RS485-Modus

Code:
label: Venus Control RS485 Switch
type: Switch
category: ""
groupNames:
- pvspeicher
tags: []
Rule für den Switch
Datei Venus.rules
rule "Venus Switch für RS485-Steuerung synchron zum Status halten bzw. schaltbar machen"
when Item venus_control_rs485_switch changed
then
if (venus_control_rs485_switch.state == OFF) venus_rs485_write_register.sendCommand(0x55BB) // Entspricht Dezimal 21947
else venus_rs485_write_register.sendCommand(0X55AA) // Entspricht Dezimal 21930
end
Visualisierung als Widget
die Werte meines Speichers habe ich mir dann in einem Widget visualisiert und je nachdem, ob der Modus RS485 ist oder Selbstanpassung steht dort nun „RS485“ oder „auto“:

Beispiel: Anwendungsfall für die Steuerung des Speichers bei eAuto-Laden
Konkretes Beispiel, wie ich nun meine E-Auto-Ladesteuerungslogik im Kontext des virtuellen Shellies angepasst habe. Den Ursprungsbeitrag findet ihr hier.
var boolean log = false
rule "Publish Strom saldierend bei Änderung zu virtuellem Shelly-Device und steuere Speicher AN/AUS"
when
Item strom_saldierend changed
then
var powerValue = 0.0
// Wert für virtuellen Shelly: saldierender Wert, der von realen Shellies ausgelesen wird.
powerValue = powerValue = (strom_saldierend.state as QuantityType<Power>).doubleValue
// Speicher berücksichtigen und ggf. je nach Lademodus RS485-Modus anschalten oder auch nicht
// Lädt Auto nicht? Dann sollte Speicher immer an sein.
if ((GoeCharger_Power_All.state as Number) < 1) {
if (log) logInfo("virtualShelly", "Wallbox lädt nicht, Speicher wird - wenn nicht an - aktiviert")
if (venus_control_rs485_switch.state != OFF) {
if (log) logInfo("virtualShelly","RS 485 Modus wird ausgeschalten")
venus_control_rs485_switch.sendCommand(OFF)
}
} else { // Lädt Auto? Dann prüfen, ob Überschussladen stattfindet.
if (log) logInfo("virtualShelly", "Wallbox lädt, Speicher wird ggf. aktiviert oder deaktiviert")
// Überschussladen aus? Dann speicher aus
if ((ueberschussladen_aktiv.state != ON)) {
if (log) logInfo("virtualShelly","Überschussladen ist AUS, speicher wird deaktiviert, falls an")
if (venus_control_rs485_switch.state != ON) {
if (log) logInfo("virtualShelly","Speicher wird Deaktiviert, RS485 Modus wird eingeschalten")
venus_control_rs485_switch.sendCommand(ON)
}
}
// Überschussladen an? Dann speicher ein, Speicher hat später ja Vorrang vor Überschussladen
if (ueberschussladen_aktiv.state != OFF) {
if (log) logInfo("virtualShelly","Überschussladen ist AN")
if (venus_control_rs485_switch.state != OFF) {
if (log) logInfo("virtualShelly","Speicher wird aktiviert, RS485 Modus wird ausgeschalten")
venus_control_rs485_switch.sendCommand(OFF)
}
// Wenn Auto per Überschussladen lädt, dann soll Speicher mit voller Leistung laden. Die Regelung übernimmt dann ja das Überschussladen.
// Einspeisung vorgaukeln.
powerValue = -500.0
}
}
val json = '{"power": ' + powerValue + '}'
val mqttActions = getActions("mqtt","mqtt:broker:c73084e70c")
mqttActions.publishMQTT("shellypro3em-virtual/SENSOR", json, false)
if (log) logInfo("virtualShelly", "Powervalue: " + powerValue.toString)
end