-
-
Notifications
You must be signed in to change notification settings - Fork 66
[codex] Add LG power consumption sensors #170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -153,6 +153,8 @@ class LgController final : public climate::Climate, public uart::UARTDevice, pub | |
| esphome::sensor::Sensor& pipe_temp_in_; | ||
| esphome::sensor::Sensor& pipe_temp_mid_; | ||
| esphome::sensor::Sensor& pipe_temp_out_; | ||
| esphome::sensor::Sensor* power_consumption_; | ||
| esphome::sensor::Sensor* current_power_; | ||
| esphome::binary_sensor::BinarySensor& defrost_; | ||
| esphome::binary_sensor::BinarySensor& preheat_; | ||
| esphome::binary_sensor::BinarySensor& outdoor_; | ||
|
|
@@ -347,6 +349,8 @@ class LgController final : public climate::Climate, public uart::UARTDevice, pub | |
| sensor::Sensor* pipe_temp_in, | ||
| sensor::Sensor* pipe_temp_mid, | ||
| sensor::Sensor* pipe_temp_out, | ||
| sensor::Sensor* power_consumption, | ||
| sensor::Sensor* current_power, | ||
| binary_sensor::BinarySensor* defrost, | ||
| binary_sensor::BinarySensor* preheat, | ||
| binary_sensor::BinarySensor* outdoor, | ||
|
|
@@ -371,6 +375,8 @@ class LgController final : public climate::Climate, public uart::UARTDevice, pub | |
| pipe_temp_in_(*pipe_temp_in), | ||
| pipe_temp_mid_(*pipe_temp_mid), | ||
| pipe_temp_out_(*pipe_temp_out), | ||
| power_consumption_(power_consumption), | ||
| current_power_(current_power), | ||
| defrost_(*defrost), | ||
| preheat_(*preheat), | ||
| outdoor_(*outdoor), | ||
|
|
@@ -935,6 +941,12 @@ class LgController final : public climate::Climate, public uart::UARTDevice, pub | |
| case 3: // 0xCB/AB/2B | ||
| process_type_b_settings_message(*sender, buffer); | ||
| break; | ||
| case 4: // 0xCC/AC/2C | ||
| process_type_c_status_message(*sender, buffer); | ||
| break; | ||
| case 7: // 0xCF/AF/2F | ||
| process_type_f_status_message(*sender, buffer); | ||
| break; | ||
| default: | ||
| return; | ||
| } | ||
|
|
@@ -1273,6 +1285,34 @@ class LgController final : public climate::Climate, public uart::UARTDevice, pub | |
| } | ||
| } | ||
|
|
||
| static float decode_power_nibbles_(const uint8_t* buffer, size_t offset, size_t length, float divisor) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: the trailing underscore does not align with other naming conventions in this file. |
||
| // Power payloads use LG's nibble format rather than binary integers. Some documented | ||
| // current-power examples contain A-F nibbles, so this intentionally is not strict BCD. | ||
| uint32_t result = 0; | ||
| for (size_t i = 0; i < length; i++) { | ||
| result = result * 100 + ((buffer[offset + i] >> 4) & 0xF) * 10 + (buffer[offset + i] & 0xF); | ||
| } | ||
| return result / divisor; | ||
| } | ||
|
Comment on lines
+1288
to
+1296
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I'm not quite sure if this is actually how these numbers should be interpreted. The current implementation leads to a power consumption sensor that rolls back every now and then on my units, whenever the trailing nibble goes from F to 0. It might also happen for the other nibbles but I haven't seen those roll over from F to 0 yet. It surely seems like the numbers in the protocol act like normal hexidecimal numbers, i.e. 00.02.DF becomes 00.02.E0 when 1 is added. However, interpreting it as a normal hexadecimal number gives very different results from what is reported in protocol.md. Here's the moment it rolled over from 33.5 to 33.0, which probably corresponded with 00.02.CF to 00.02.D0. Didn't catch it live in the logs yet, but I'm trying to see it happen there as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other option is that the numbers should still be interpreted this way, but the implementation of it by LG in my units is flawed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just discovered that interpreting the reported current power as hexadecimal gives me the exact same numbers as ThinQ reports (in watts), so at least for current power on my units that seems to be the way. ThinQ shows at what moment it retrieved the power use, so I can align it exactly with what was logged. Example: At the moment that ThinQ retrieved a power use of 0.55kW, my logs show this message: That still leaves the question if that's also the case for total power usage. Interpretating
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the encoding can now be determined with fairly high confidence. Both values appear to be normal unsigned 24-bit big-endian binary integers, rather than LG’s nibble-decimal format. For current power:
For accumulated energy, the counter probably has a resolution of 0.1 kWh:
This also explains and fixes the observed rollback:
With the current nibble-based decoder, those values incorrectly become 33.5 and 33.0 kWh. The difference between the resulting 75.0 kWh and ThinQ’s 61.6 kWh is therefore probably not an encoding issue. It looks more like a difference in scope or reset point. Since these are multisplit indoor units, the
Alternatively, ThinQ may show consumption since a monthly/app reset, while the wired-bus counter uses a different starting point. There is another interesting clue in the same
1106 W is exactly twice 553 W. This may be a second system-wide power field, while the first field is allocated to the individual indoor unit, although more captures are needed to confirm that. My suggested decoding would therefore be:
It would be useful to monitor the deltas as well. For example, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No the other unit has a total power consumption of 28.4kWh in ThinQ, so I don't think it's a total for the whole system. For that unit the message has a value of What did cross my mind was that this power consumption counter might be non-zero out of the factory because of unknown reasons, and it's just a different offset. I'm planning to collect some more values over time and see if it's a constant offset or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have checked the values again, but there is no stable offset that makes the data I get from the unit itself match ThinQ's data. My own conclusion is that the power consumption data is wildly inaccurate, both reported by the unit and by ThinQ. One of my units was on intermittently during the night yesterday. I checked it's power consumption through four ways:
The results:
This shows that setting up an integral helper sensor on the current power sensor of the AC unit is very accurate, as it matches the increase in power consumption that the power meter of my house shows. I don't see any reason to use any data reported by LG, it's so far off that there's no point using it in my opinion. |
||
|
|
||
| void process_type_c_status_message(MessageSender sender, const uint8_t* buffer) { | ||
| if (sender != MessageSender::Unit) { | ||
| return; | ||
| } | ||
| if (power_consumption_ != nullptr) { | ||
| power_consumption_->publish_state(decode_power_nibbles_(buffer, 3, 3, 10.0f)); | ||
| } | ||
| } | ||
|
|
||
| void process_type_f_status_message(MessageSender sender, const uint8_t* buffer) { | ||
| if (sender != MessageSender::Unit) { | ||
| return; | ||
| } | ||
| if (current_power_ != nullptr) { | ||
| current_power_->publish_state(decode_power_nibbles_(buffer, 2, 3, 1000.0f)); | ||
| } | ||
| } | ||
|
|
||
| void update() { | ||
| ESP_LOGD(TAG, "update"); | ||
|
|
||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The protocol reports this in 3 decimal accuracy, so better to set it to 3 decimals.