[modbus.lambda] Fix endianness for VdAE and VdAQ 32-bit registers
Description
Classification: Bugfix
The heatpumpVdAE and heatpumpVdAQ fields were using extractInt32() which reads 32-bit registers without word-swapping. These registers require word-swap for correct endianness.
Changes:
- Added
extractInt32Swap()andextractOptionalInt32Swap()methods toAbstractBaseParserusingValueType.INT32_SWAP - Updated
HeatpumpBlockParserto useextractInt32Swap()for VdAE (register 20) and VdAQ (register 22)
// Before
block.heatpumpVdAE = extractInt32(raw, 20, (long) 0);
// After
block.heatpumpVdAE = extractInt32Swap(raw, 20, (long) 0);
This ensures correct byte/word ordering when parsing these 32-bit Modbus registers.
Guidelines compliance:
- [x] Minimal, surgical changes following existing code patterns
- [x] Consistent with coding guidelines
- [x] No documentation update needed (internal implementation fix)
- [x] Signed-off work
Testing
JAR available at: https://openhab.jfrog.io/ui/native/libs-pullrequest-local/org/openhab/addons/bundles/
[!WARNING]
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
openhab.jfrog.io
- Triggering command:
/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.11/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.11/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.11 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.11/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/openhab-addons/openhab-addons org.codehaus.plexus.classworlds.launcher.Launcher clean compile -q(dns block)If you need me to access, download, or install something from one of these locations, you can either:
- Configure Actions setup steps to set up my environment, which run before the firewall is enabled
- Add the appropriate URLs or hosts to the custom allowlist in this repository's Copilot coding agent settings (admins only)
Original prompt
Replace two calls to extractInt32 with extractInt32Swap in HeatpumpBlockParser to correct endianness for 32-bit values.
File to change:
- bundles/org.openhab.binding.modbus.lambda/src/main/java/org/openhab/binding/modbus/lambda/internal/parser/HeatpumpBlockParser.java
Current lines (around line numbers shown in provided snippet): block.heatpumpVdAE = extractInt32(raw, 20, (long) 0); block.heatpumpVdAQ = extractInt32(raw, 22, (long) 0);
Desired replacement: block.heatpumpVdAE = extractInt32Swap(raw, 20, (long) 0); block.heatpumpVdAQ = extractInt32Swap(raw, 22, (long) 0);
Reason:
- The values VdAE and VdAQ are 32-bit registers that require a word-swap when parsed. Using extractInt32Swap ensures correct byte/word ordering.
Please create a new branch, apply the change, and open a pull request against the repository's default branch. Do not include any assignees in the PR. Include a short PR description summarizing the change and reason.
Do not modify other files.
This pull request was created as a result of the following prompt from Copilot chat.
Replace two calls to extractInt32 with extractInt32Swap in HeatpumpBlockParser to correct endianness for 32-bit values.
File to change:
- bundles/org.openhab.binding.modbus.lambda/src/main/java/org/openhab/binding/modbus/lambda/internal/parser/HeatpumpBlockParser.java
Current lines (around line numbers shown in provided snippet): block.heatpumpVdAE = extractInt32(raw, 20, (long) 0); block.heatpumpVdAQ = extractInt32(raw, 22, (long) 0);
Desired replacement: block.heatpumpVdAE = extractInt32Swap(raw, 20, (long) 0); block.heatpumpVdAQ = extractInt32Swap(raw, 22, (long) 0);
Reason:
- The values VdAE and VdAQ are 32-bit registers that require a word-swap when parsed. Using extractInt32Swap ensures correct byte/word ordering.
Please create a new branch, apply the change, and open a pull request against the repository's default branch. Do not include any assignees in the PR. Include a short PR description summarizing the change and reason.
Do not modify other files.
đź’¬ We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.
Cool, Copilot was able to create the changes as a bug fix. I'm amazed; I haven't been here in far too long.
@chilobo FYI
I would like to check this change first. Right now, without the change, the values shown are correct. Tomorrow afternoon I should have time.
@chilobo, first, thanks for this Binding!
Are you sure, please check it.
Here are my Tests with and without swap words:
I am confused. The values shown in "my" binding reflected the values shown in the UI of my Lambda. I had some other users with lambda heatpumps checking the binding: https://community.openhab.org/t/lambda-heat-pump-in-openhab/156799/95 They did not report any errors with vdae and vdaq either.
I got the jar of the swap-version from https://openhab.jfrog.io/ui/native/libs-pullrequest-local/org/openhab/addons/bundles/ and copied it to the addons-folder. Then I deleted the lambda-binding supplied by the addons-store and restarted openhab. Couldn't generate a thing because the modbus binding was missing. When I add the Modbus binding it adds the lambda binding from the addons store. So I cannot test the supplied jar within 5.1.0.M3.
I have the software version V1.1.3 on my lambda. Could it be that your lambda is configured different?
This pull request has been mentioned on openHAB Community. There might be relevant details there:
https://community.openhab.org/t/lambda-heat-pump-in-openhab/156799/99
Let's wait for answers in: https://community.openhab.org/t/lambda-heat-pump-in-openhab/156799/99 I have no idea why "my" binding works in my setting and not in ours.
Okay, we need to test that more thoroughly. Thank you for your feedback @chilobo. To get reasonable values, I have to swap the word. Also in Modbus Mechanic. It would be interesting if you could test this with an external tool as well. Modbus Mechanic runs on Java. Could you possibly test register 1020 on your computer against your heat pump? Here is the link: https://modbusmechanic.scifidryer.com/
Hmm, I have same software version V1.1.3 on my lambda.
@chilobo, your link to the Bundle is from 2024. I'm not sure but should be this one: https://openhab.jfrog.io/ui/native/libs-snapshot-local/org/openhab/addons/bundles/org.openhab.binding.modbus/5.1.0-SNAPSHOT/
I tested too to replace it on my OH now. It seems to be tricky. Tomorrow I will test on another blank new installation.
I will not be able to test until Friday afternoon or Saturday.
Both of my values are larger than 65535, so they use 4 byte. We definitly have to find out what's causing the different behaviour.
Up to now I only used PowerHud Modbus Tester.
Modbusmechanic in my case shows
So without swaping the value shown corresponds to the value shown in the heat pump in my case.
Do you have a Lambda or Zewotherm version? In the Modbus document on page 8 a configuration page is shown. But I guess this is only for the E-Manager. Should we / are we able to check if our values for the modbus configuration are the same? Can we access a configuration page with level 2 or 3?
I guess I can test the above linked jar by switching back to 5.0.0. This should not be a problem using docker.
I have a Lambda version EU08L. As I see it, the binding needs to be extended to allow the word to be swapped for both values. I'm not the only one who needs to swap: https://forum.timberwolf.io/viewtopic.php?t=5585
You can temporarily query directly via Modbus. I think the values for your heat pump are correct without word swap. (Disable the Lambda binding during the test, because the port can apparently only be used locally by one participant.)
Bridge modbus:tcp:lambdaWP "Lambda_WP_Bridge" [ host="10.0.0.12", port=502, id=1 ] {
// 32-Bit Energiezähler (Register 1020 & 1021 bilden einen uint32 Wert - WORD SWAP hier erforderlich)
Bridge poller energyPoller "energyPoller_Bridge" [ start=1020, length=5, refresh=15000, type="holding", maxTries=3 ] {
Thing data hp1_energyStatPel "WP1: Akkumulierte elektrische Energie" [ readStart="1020", readValueType="uint32_swap" ] // Wh
Thing data hp1_energyStatPth "WP1: Akkumulierte thermische Energie" [ readStart="1022", readValueType="uint32_swap" ] // Wh
}
// 32-Bit Energiezähler (Register 1020 & 1021 bilden einen uint32 Wert - NO WORD SWAP)
Bridge poller energyPollerUnswaped "energyPollerUnswaped_Bridge" [ start=1020, length=5, refresh=15000, type="holding", maxTries=3 ] {
Thing data hp1_energyStatPelUNSW "WP1: Akkumulierte elektrische Energie UNSWAPED" [ readStart="1020", readValueType="uint32" ] // Wh
Thing data hp1_energyStatPthUNSW "WP1: Akkumulierte thermische Energie UNSWAPED" [ readStart="1022", readValueType="uint32" ] // Wh
}
}
My results: I only get the correct values with Word Swap.
No word Swap:
Thing modbus:data:lambdaWP:energyPollerUnswaped:hp1_energyStatPthUNSW channels updated:
-> numeric value 3087925304
Thing modbus:data:lambdaWP:energyPollerUnswaped:hp1_energyStatPelUNSW channels updated:
-> numeric value 2062221321
With word swap:
Thing modbus:data:lambdaWP:energyPoller:hp1_energyStatPth channels updated:
-> numeric value 3717134
Thing modbus:data:lambdaWP:energyPoller:hp1_energyStatPel channels updated:
-> numeric value 621291
I'm slowly starting to figure out what's going on. There also seem to be differences within version number V1.1.3. Interestingly, I was wondering why connecting the Lambda heat pump to EVCC also displays completely incorrect consumption values. Now I know why. EVCC uses the software version number to decide whether to use word swap or not (line 51: >=1.1.3 Word Swap): https://github.com/evcc-io/evcc/blob/73ae64602367b187e8d30571580a3f49ac1b765f/templates/definition/charger/lambda-zewotherm.yaml#L4
To avoid a long wait, I suggest introducing a simple option that reverses the word. What do you think @chilobo?
I get different screenshots.
Your first one is shown in two seperate ones:
Did you change anything in your configuration to get the information in just one page? Do you have a Lambda or Zewotherm heat pump? The data provided in your bottom line is not shown in my UI. Why does your bottom line show V0.0.9?
Your SW version is from July, mine is from October. But my version does not need word swapping. So the approach by EVCC does not seem to be helpful.
I will ask Lamba support!
Your idea to make wordswapping configurable might be necessary. We could introduce a new variable in the file heat-pump-thing-types.xml similar to the subindex-parameter which gives access to more than one heat-pump, heating-circuit .... The subindex parameter is accessible in HeatPumpHandler.java, so a new parameter "wordswap32" yould be used to change protected State getEnergyQuantity(int high, int low) { double value = high * 1000 + low; return QuantityType.valueOf(value, KILOWATT_HOUR); }
Or we do it - as you suggested - in HeatpumpBlockParser.java. Would this parameter wordswap32 be accessible in HeatpumpBlockParser.java too?
Using this approach the user of the binding would have to change a variable when configuring the heatpump-thing.
Should we rather discuss this in german by regular email? My Mailadress: christian(at)koch-bensheim.de
I asked two friends to check their Lambdas. Both do not need swapping. Their Lambdas are were installed about one and a half year ago. I think that Lambda changed part of their machinery and control management recently: You seem to have another compressor. Your UI seems to be different from mine. In https://www.haustechnikdialog.de/Forum/t/290212/Lambda-Zeitserver-konfigurieren user lagash mentions that the newer Lambdas have another control circuit (Hainzl instead of Sigmatek). I definitely have a Sigmatek. I think we should wait for the reaction of the lambda support to my mail.
My Lambda went into operation on October 23rd, but it wasn't from the new series because it was reserved 10 months ago.
What Lambda says is not so relevant; if both versions are in circulation, then we need to provide a way to swap them. If we decide to wait, I will have to rebuild and work with the pure Modbus connection, because both at the same time are not possible due to the blocked port.
Up to now you are the only one with this problem. I am still wondering if Lambda configured your Lambda differently: Your UI seems to be different, your serial number does not fit into the usual pattern consisting only of numbers.
Why is the port blocked? I never had this problem. I guess you just have to use the same Modbus Bridge for your new poller. Or you could compile your own java ( Haven't you done that already?) and copy it to the addon-directory of openhab, delete the binding from the add-on store and restart openhab.
Working with an extra parameter in the configuration is too complicated for me - I don't really know how to get access to the config within HeatPumpBlockParser or getEnergyQuantity in HeatPumpHandler.
I had a similar problem in the E-Manager part with the channel actual-power: Some Lambdas need that value as unsigned, some as signed value. So I defined two channels actual-power and actual-power-signed.
What do you think of generating - in addtion to heat-pump-vdae and heat-pump-vdaq - two more channels: heat-pump-vdae-swap and heat-pump-vdaq-swap generating values with word swap. The user of the heatpump-thing can decide which channel to use to connect it with its corresponding item.
I got a working version of this idea: https://drive.google.com/file/d/1l_RKQMmvViOvVRyLhiv4hhoz3UgI7VBk/view?usp=sharing To test it I had to go back to 5.1.0.M2, add the modbus binding from the addon-store (still without lambda) and copy the jar to my addon-directory. Could you please test it?
That's an excellent idea, just have two versions and let the user decide. You can document it and everything is fine. No calculations, no firmware version numbers, etc. A very good solution. The values that are not exchanged can be marked as default, as a guide for non-programmers.
And I think that since evcc.io performs a version check and the problem is discussed in forums, it exists and needs to be dealt with.
I have now solved the issue for myself by connecting all documented values in native mode, as I needed more flexibility for testing with other values, especially read/write and the duration of validity.
I will close the PR in a few days.
Ja ich teste...
OK, I will open a new pull request with this idea:
Perfect @chilobo, works as expected! Thank you! :+1:
Item 'lambdaheatpump_vdae' changed from NULL to 116981.77100000001 kWh (source: org.openhab.core.thing$modbus:heat-pump:LambdaBridge:lambdaheat-pump:heat-pump-group#heat-pump-vdae)
Item 'lambdaheatpump_vdaq' changed from NULL to -359792.575 kWh (source: org.openhab.core.thing$modbus:heat-pump:LambdaBridge:lambdaheat-pump:heat-pump-group#heat-pump-vdaq)
Item 'lambdaheatpump_vdae_sw' changed from NULL to 722.681 kWh (source: org.openhab.core.thing$modbus:heat-pump:LambdaBridge:lambdaheat-pump:heat-pump-group#heat-pump-vdae-swap)
Item 'lambdaheatpump_vdaq_sw' changed from NULL to 4319.886 kWh (source: org.openhab.core.thing$modbus:heat-pump:LambdaBridge:lambdaheat-pump:heat-pump-group#heat-pump-vdaq-swap)
"Did you change anything in your configuration to get the information in just one page?" No.
Regarding how I got the information screen:
I opened a new pull request with the new version in (https://github.com/openhab/openhab-addons/pull/19835)
Succeeded by https://github.com/openhab/openhab-addons/pull/19835
My control unit produces two pages:
and no information in the lower frame part.
So you definitely have a different user interface.
A person of support answered that he is not aware that they changed the DWord ordering. He sends the question to another part of support and thinks it could have to to with different control units: Sigmatek alt, Hainzl and Sigmatek neu