From 09da905fb448bf16b3d1ebf121fcdd93ec8439d3 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Mon, 31 Mar 2025 11:26:47 +0300 Subject: [PATCH 01/13] Add support for X1 Lite LV inverter Introduced the `X1LiteLV` class to handle X1 Lite LV inverter data and decoding logic. Updated the `__init__.py` and `setup.py` files to register the new inverter for system integration. --- setup.py | 1 + solax/inverters/__init__.py | 2 + solax/inverters/x1_lite_lv.py | 107 +++++++++++++++ tests/fixtures.py | 12 ++ tests/samples/expected_values.py | 50 ++++++- tests/samples/responses.py | 220 +++++++++++++++++++++++++++++++ 6 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 solax/inverters/x1_lite_lv.py diff --git a/setup.py b/setup.py index 296ba89..83b461d 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,7 @@ "x1_mini = solax.inverters.x1_mini:X1Mini", "x1_mini_v34 = solax.inverters.x1_mini_v34:X1MiniV34", "x1_smart = solax.inverters.x1_smart:X1Smart", + "x1_lite_lv = solax.inverters.x1_lite_lv:X1LiteLV", "x3 = solax.inverters.x3:X3", "x3_hybrid_g4 = solax.inverters.x3_hybrid_g4:X3HybridG4", "x3_ultra = solax.inverters.x3_ultra:X3Ultra", diff --git a/solax/inverters/__init__.py b/solax/inverters/__init__.py index d8c9c90..c30b304 100644 --- a/solax/inverters/__init__.py +++ b/solax/inverters/__init__.py @@ -3,6 +3,7 @@ from .x1_boost import X1Boost from .x1_g4_series import X1G4Series from .x1_hybrid_gen4 import X1HybridGen4 +from .x1_lite_lv import X1LiteLV from .x1_mini import X1Mini from .x1_mini_v34 import X1MiniV34 from .x1_smart import X1Smart @@ -21,6 +22,7 @@ "X1Mini", "X1MiniV34", "X1Smart", + "X1LiteLV", "X3V34", "X3HybridG4", "X3", diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py new file mode 100644 index 0000000..f2fef33 --- /dev/null +++ b/solax/inverters/x1_lite_lv.py @@ -0,0 +1,107 @@ +from typing import Any, Dict, Optional + +import voluptuous as vol + +from solax.inverter import Inverter +from solax.units import DailyTotal, Total, Units +from solax.utils import div10, div100, pack_u16, to_signed, to_signed32, twoway_div10 + + +class X1LiteLV(Inverter): + # pylint: disable=duplicate-code + _schema = vol.Schema( + { + vol.Required("type"): int, + vol.Required( + "sn", + ): str, + vol.Required("ver"): str, + vol.Required("data"): vol.Schema( + vol.All( + [vol.Coerce(float)], + vol.Length(min=200, max=200), + ) + ), + vol.Required("information"): vol.Schema(vol.All(vol.Length(min=1, max=15))), + }, + extra=vol.REMOVE_EXTRA, + ) + + @classmethod + def build_all_variants(cls, host, port, pwd=""): + versions = [cls._build(host, port, pwd)] + return versions + + @classmethod + def response_decoder(cls): + """ + :return: + """ + return { + "AC Voltage": (0, Units.V, div10), + "AC Output Current": (1, Units.A, twoway_div10), + "AC Output Power": (2, Units.W, to_signed), + "AC Frequency": (3, Units.HZ, div100), + "Grid PF": (4, Units.PERCENT, div10), + ################################### + "AC Voltage Out": (97, Units.V, div10), + # "AC current Out": (98, Units.A, twoway_div10), + # "AC power Out": (99, Units.W, to_signed), + "AC Frequency Out": (101, Units.HZ, div10), + # "Inverter Status 1 ?": (15, Units.NONE), # 2 - Normal Mode, 7 - EPSMode + # "Inverter Status 2 ?": (16, Units.NONE), # 4 - Normal Mode, 5 - EPSMode + ################################## + "Grid Power": (32, Units.W, twoway_div10), + # "Grid power 1": (33, Units.W, twoway_div10), + # "Grid power 2": (34, Units.W, twoway_div10), + "Hourly Energy": (51, DailyTotal(Units.KWH), div100), + ############################### + "PV1 Voltage": (5, Units.V, div10), + "PV2 Voltage": (7, Units.V, div10), + "PV3 Voltage": (9, Units.V, div10), + "PV1 Current": (6, Units.A, div10), + "PV2 Current": (8, Units.A, div10), + "PV3 Current": (10, Units.A, div10), + "PV1 Power": (11, Units.W), + "PV2 Power": (12, Units.W), + "PV3 Power": (13, Units.W), + "Total PV Power": (14, Total(Units.KWH), to_signed), + "Today's PV Energy": (52, DailyTotal(Units.KWH), twoway_div10), + "Total PV Energy": (53, Total(Units.KWH), twoway_div10), + ################################ + "Battery Type": (27, Units.NONE), # Undefined + "Battery voltage": (23, Units.V, div10), + "Battery current": (24, Units.A, twoway_div10), + "Total Battery power": (25, Units.W, twoway_div10), + ########################### + "Battery SoC": (75, Units.PERCENT, div10), + "Battery Temperature 1": (72, Units.C), + "Battery Temperature 2": (73, Units.C), + "Battery Temperature 3": (74, Units.C), + "Battery Temperature 4": (78, Units.C), + "Battery Temperature": (79, Units.C), + "Battery Charge total": (pack_u16(26, 27), Units.KWH, div10), + "Battery Discharge total": (pack_u16(28, 29), Units.KWH, div10), + "Battery Charge today": (30, Units.KWH, div10), + "Battery Discharge today": (31, Units.KWH, div10), + "Charging Duration": (pack_u16(80, 81), Units.NONE, to_signed32), + "Today's Battery Discharge": (30, DailyTotal(Units.KWH), div10), + "Total Battery Discharge": ( + pack_u16(28, 29), + Total(Units.KWH), + twoway_div10, + ), + "Today's Battery Charge": (31, DailyTotal(Units.KWH), div10), + "Total Battery Charge": (pack_u16(26, 27), Total(Units.KWH), twoway_div10), + ################ + "Today's Generated Energy": (21, DailyTotal(Units.KWH), div10), + "Total Generated Energy": (pack_u16(17, 18), Total(Units.KWH), div10), + "Today's EPS Energy": (46, DailyTotal(Units.KWH), div10), + "Total EPS Energy": (pack_u16(47, 48), Total(Units.KWH), div10), + "Today's Import Energy": (40, DailyTotal(Units.KWH), div10), + "Total Import Energy": (pack_u16(36, 37), Total(Units.KWH), div10), + } + + @classmethod + def inverter_serial_number_getter(cls, response: Dict[str, Any]) -> Optional[str]: + return response["information"][2] diff --git a/tests/fixtures.py b/tests/fixtures.py index bb051f3..0d7c2eb 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -12,6 +12,7 @@ X1_BOOST_VALUES_V3, X1_HYBRID_G4_V_3_018_VALUES, X1_HYBRID_G4_VALUES, + X1_LITE_LV_80_VALUES, X1_MINI_G4_VALUES, X1_MINI_VALUES, X1_MINI_VALUES_V34, @@ -41,6 +42,7 @@ X1_HYBRID_G3_RESPONSE, X1_HYBRID_G4_RESPONSE, X1_HYBRID_G4_V_3_018_RESPONSE, + X1_LITE_LV_80_RESPONSE, X1_MINI_G4, X1_MINI_RESPONSE_V34, X1_MINI_RESPONSE_V34_VER3, @@ -197,6 +199,16 @@ def simple_http_fixture(httpserver): headers=X_FORWARDED_HEADER, data=None, ), + InverterUnderTest( + uri="/", + method="POST", + query_string="optType=ReadRealTimeData", + response=X1_LITE_LV_80_RESPONSE, + inverter=inverter.X1LiteLV, + values=X1_LITE_LV_80_VALUES, + headers=None, + data=None, + ), InverterUnderTest( uri="/", method="POST", diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index 2ff2da2..9a1e0c5 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -427,7 +427,6 @@ "Inverter Temperature": 39.0, } - X1_MINI_G4_VALUES = { "AC Voltage": 226.1, "AC Output Current": 0.4, @@ -559,6 +558,55 @@ "Total Import Energy": 0.0, } +X1_LITE_LV_80_VALUES = { + "AC Voltage": 239.7, + "AC Output Current": 0.9, + "AC Output Power": 1280, + "AC Frequency": 50.02, + "Grid PF": 100.0, + "AC Voltage Out": 229.5, + "AC Frequency Out": 49.9, + "Grid Power": 0.0, + "Hourly Energy": 82.24, + "PV1 Voltage": 328.7, + "PV2 Voltage": 256.1, + "PV3 Voltage": 0.0, + "PV1 Current": 11.0, + "PV2 Current": 9.6, + "PV3 Current": 0.0, + "PV1 Power": 189.5, + "PV2 Power": 98.5, + "PV3 Power": 0.0, + "Total PV Power": 0.0, + "Today's PV Energy": 0.2, + "Total PV Energy": 255.0, + "Battery Type": 0.0, + "Battery voltage": 52.6, + "Battery current": -7.3, + "Total Battery power": -38.8, + "Battery SoC": 62.0, + "Battery Temperature 1": 19.0, + "Battery Temperature 2": 21.0, + "Battery Temperature 3": 22.0, + "Battery Temperature 4": 19.0, + "Battery Temperature": 19.0, + "Battery Charge total": 111.4, + "Battery Discharge total": 163.0, + "Battery Charge today": 0.0, + "Battery Discharge today": 4.0, + "Charging Duration": 0.0, + "Today's Battery Discharge": 0.0, + "Total Battery Discharge": 163.0, + "Today's Battery Charge": 4.0, + "Total Battery Charge": 111.4, + "Today's Generated Energy": 0.0, + "Total Generated Energy": 120.6, + "Today's EPS Energy": 4.7, + "Total EPS Energy": 224.5, + "Today's Import Energy": 3.2, + "Total Import Energy": 282.9, +} + QVOLTHYBG33P_VALUES = { "Network Voltage Phase 1": 221.4, "Network Voltage Phase 2": 223.8, diff --git a/tests/samples/responses.py b/tests/samples/responses.py index d56682f..88ac781 100644 --- a/tests/samples/responses.py +++ b/tests/samples/responses.py @@ -1613,6 +1613,226 @@ "Information": [3.000, 18, "XXXXXXX", 8, 0.16, 0.00, 0.23, 0.00, 0.00, 1], } +X1_LITE_LV_80_RESPONSE = { + "sn": "XXXXXXX", + "ver": "1.002.09", + "type": 103, + "Data": [ + 2397, + 9, + 1280, + 5002, + 1000, + 3287, + 110, + 2561, + 96, + 0, + 0, + 1895, + 985, + 0, + 0, + 7, + 5, + 1206, + 0, + 11, + 0, + 0, + 0, + 526, + 65463, + 65148, + 1114, + 0, + 1630, + 0, + 0, + 40, + 0, + 0, + 0, + 0, + 2829, + 0, + 0, + 0, + 32, + 0, + 2297, + 16, + 387, + 5000, + 47, + 2245, + 0, + 6403, + 7945, + 8224, + 2, + 2550, + 0, + 256, + 256, + 527, + 65460, + 65136, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 410, + 350, + 330, + 410, + 19, + 21, + 22, + 620, + 180, + 237, + 19, + 19, + 0, + 0, + 0, + 100, + 0, + 8, + 2, + 12, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2295, + 0, + 0, + 0, + 499, + 1, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 317, + 0, + 2, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "Information": [ + 8.000, + 103, + "XXXXXXX", + 13, + 14.54, + 0.00, + 14.54, + 1.13, + 0.00, + 1, + ], +} + X3_MIC_RESPONSE = { "type": "X3-MIC", "SN": "XXXXXXX", From 49f1e96fbfd05ee9605822cd004cc65e6cb3b1f3 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Mon, 31 Mar 2025 11:54:20 +0300 Subject: [PATCH 02/13] Fix incorrect PV power values in test sample data Adjusted the PV1 and PV2 power values in the test expected values to reflect accurate scaling. This ensures alignment with expected data and improves test reliability. --- tests/samples/expected_values.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index 9a1e0c5..f76f1bf 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -574,8 +574,8 @@ "PV1 Current": 11.0, "PV2 Current": 9.6, "PV3 Current": 0.0, - "PV1 Power": 189.5, - "PV2 Power": 98.5, + "PV1 Power": 1895.0, + "PV2 Power": 985.0, "PV3 Power": 0.0, "Total PV Power": 0.0, "Today's PV Energy": 0.2, From c2c85107b47dab6f00121ba95d6cea93d7a1521f Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Wed, 27 Aug 2025 22:05:08 +0300 Subject: [PATCH 03/13] Update x1_lite_lv schema validation --- solax/inverters/x1_lite_lv.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py index f2fef33..5fbbb24 100644 --- a/solax/inverters/x1_lite_lv.py +++ b/solax/inverters/x1_lite_lv.py @@ -12,14 +12,12 @@ class X1LiteLV(Inverter): _schema = vol.Schema( { vol.Required("type"): int, - vol.Required( - "sn", - ): str, + vol.Required("sn"): str, vol.Required("ver"): str, vol.Required("data"): vol.Schema( vol.All( [vol.Coerce(float)], - vol.Length(min=200, max=200), + vol.Length(min=200, max=300), ) ), vol.Required("information"): vol.Schema(vol.All(vol.Length(min=1, max=15))), From dcf0b975f6c6e0230a828e12d839e88362acad2c Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Thu, 28 Aug 2025 13:53:08 +0300 Subject: [PATCH 04/13] Add support for multiple variants in X1 Lite LV inverter configuration --- solax/inverters/x1_lite_lv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py index f2fef33..97577b0 100644 --- a/solax/inverters/x1_lite_lv.py +++ b/solax/inverters/x1_lite_lv.py @@ -29,7 +29,7 @@ class X1LiteLV(Inverter): @classmethod def build_all_variants(cls, host, port, pwd=""): - versions = [cls._build(host, port, pwd)] + versions = [cls._build(host, port, pwd), cls._build(host, port, pwd, False)] return versions @classmethod From d85c3ebf60eb8c75ecd5f709db8faa6aad204fb7 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Mon, 1 Sep 2025 22:30:00 +0300 Subject: [PATCH 05/13] Add support for X1 Lite LV v002 and v005 variants Expanded the X1 Lite LV inverter support to include handling for the v002 and v005 variants. Updated test fixtures, expected values, and response data while refining parsing logic to accommodate additional features and variations. --- solax/inverters/x1_lite_lv.py | 85 +++++++----- solax/response_parser.py | 1 + tests/fixtures.py | 23 +++- tests/samples/expected_values.py | 80 ++++++++--- tests/samples/responses.py | 223 ++----------------------------- 5 files changed, 144 insertions(+), 268 deletions(-) diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py index 2503a9d..0eefd81 100644 --- a/solax/inverters/x1_lite_lv.py +++ b/solax/inverters/x1_lite_lv.py @@ -1,9 +1,9 @@ from typing import Any, Dict, Optional - import voluptuous as vol - +from solax import utils from solax.inverter import Inverter -from solax.units import DailyTotal, Total, Units +from solax.inverter_http_client import InverterHttpClient, Method +from solax.units import Total, Units from solax.utils import div10, div100, pack_u16, to_signed, to_signed32, twoway_div10 @@ -36,15 +36,15 @@ def response_decoder(cls): :return: """ return { - "AC Voltage": (0, Units.V, div10), - "AC Output Current": (1, Units.A, twoway_div10), - "AC Output Power": (2, Units.W, to_signed), + "AC Voltage": (0, Units.V, div10), ## ++ + "AC Current": (1, Units.A, twoway_div10), + "AC Power": (2, Units.W, to_signed), "AC Frequency": (3, Units.HZ, div100), "Grid PF": (4, Units.PERCENT, div10), ################################### - "AC Voltage Out": (97, Units.V, div10), - # "AC current Out": (98, Units.A, twoway_div10), - # "AC power Out": (99, Units.W, to_signed), + "AC Voltage Out": (97, Units.V, div10), ## ++ + # "AC Current Out": (98, Units.A, twoway_div10), + # "AC Power Out": (99, Units.W, to_signed), "AC Frequency Out": (101, Units.HZ, div10), # "Inverter Status 1 ?": (15, Units.NONE), # 2 - Normal Mode, 7 - EPSMode # "Inverter Status 2 ?": (16, Units.NONE), # 4 - Normal Mode, 5 - EPSMode @@ -52,7 +52,7 @@ def response_decoder(cls): "Grid Power": (32, Units.W, twoway_div10), # "Grid power 1": (33, Units.W, twoway_div10), # "Grid power 2": (34, Units.W, twoway_div10), - "Hourly Energy": (51, DailyTotal(Units.KWH), div100), + "Hourly Energy": (51, Total(Units.KWH.value), div100), ############################### "PV1 Voltage": (5, Units.V, div10), "PV2 Voltage": (7, Units.V, div10), @@ -63,43 +63,56 @@ def response_decoder(cls): "PV1 Power": (11, Units.W), "PV2 Power": (12, Units.W), "PV3 Power": (13, Units.W), - "Total PV Power": (14, Total(Units.KWH), to_signed), - "Today's PV Energy": (52, DailyTotal(Units.KWH), twoway_div10), - "Total PV Energy": (53, Total(Units.KWH), twoway_div10), + "Total PV Power": (14, Total(Units.KWH.value), to_signed), + "Daily PV Energy": (52, Total(Units.KWH.value), twoway_div10), ## ++ + "Total PV Energy": (53, Total(Units.KWH.value), twoway_div10), ## ++ + ################################ + "Inverter Temperature": (68, Units.C, div10), + "Inverter Temperature 1": (69, Units.C, div10), + "Inverter Temperature 2": (70, Units.C, div10), + "Inverter Temperature 3": (71, Units.C, div10), ################################ "Battery Type": (27, Units.NONE), # Undefined - "Battery voltage": (23, Units.V, div10), - "Battery current": (24, Units.A, twoway_div10), + "Battery Voltage": (23, Units.V, div10), + "Battery Current": (24, Units.A, twoway_div10), "Total Battery power": (25, Units.W, twoway_div10), ########################### - "Battery SoC": (75, Units.PERCENT, div10), + "Battery SoC": (75, Units.PERCENT, div10), ## ++ "Battery Temperature 1": (72, Units.C), "Battery Temperature 2": (73, Units.C), "Battery Temperature 3": (74, Units.C), "Battery Temperature 4": (78, Units.C), "Battery Temperature": (79, Units.C), - "Battery Charge total": (pack_u16(26, 27), Units.KWH, div10), - "Battery Discharge total": (pack_u16(28, 29), Units.KWH, div10), - "Battery Charge today": (30, Units.KWH, div10), - "Battery Discharge today": (31, Units.KWH, div10), - "Charging Duration": (pack_u16(80, 81), Units.NONE, to_signed32), - "Today's Battery Discharge": (30, DailyTotal(Units.KWH), div10), - "Total Battery Discharge": ( - pack_u16(28, 29), - Total(Units.KWH), - twoway_div10, - ), - "Today's Battery Charge": (31, DailyTotal(Units.KWH), div10), - "Total Battery Charge": (pack_u16(26, 27), Total(Units.KWH), twoway_div10), + #################################### + "Daily Battery Discharge": (30, Total(Units.KWH.value), div10), + "Total Battery Discharge": (pack_u16(28, 29), Total(Units.KWH.value), twoway_div10), + "Daily Battery Charge": (31, Total(Units.KWH.value), div10), + "Total Battery Charge": (pack_u16(26, 27), Total(Units.KWH.value), twoway_div10), ################ - "Today's Generated Energy": (21, DailyTotal(Units.KWH), div10), - "Total Generated Energy": (pack_u16(17, 18), Total(Units.KWH), div10), - "Today's EPS Energy": (46, DailyTotal(Units.KWH), div10), - "Total EPS Energy": (pack_u16(47, 48), Total(Units.KWH), div10), - "Today's Import Energy": (40, DailyTotal(Units.KWH), div10), - "Total Import Energy": (pack_u16(36, 37), Total(Units.KWH), div10), + "Daily Inverter Output": (21, Total(Units.KWH.value), div10), ## ++ + "Total Inverter Output": (pack_u16(17, 18), Total(Units.KWH.value), div10), ## ++ + "Daily Inverter EPS Energy": (46, Total(Units.KWH.value), div10), ## ++ + "Total Inverter EPS Energy": (pack_u16(47, 48), Total(Units.KWH.value), div10), ## ++ + "Daily Imported Energy": (40, Total(Units.KWH.value), div10), ## ++ + "Total Imported Energy": (pack_u16(36, 37), Total(Units.KWH.value), div10), ## ++ } + @classmethod + def dongle_serial_number_getter(cls, response: Dict[str, Any]) -> Optional[str]: + info = response.get("information", []) + return info[2] if len(info) > 2 else None + @classmethod def inverter_serial_number_getter(cls, response: Dict[str, Any]) -> Optional[str]: - return response["information"][2] + info = response.get("information", []) + return info[2] if len(info) > 2 else None + + @classmethod + def inverter_versions_getter(cls, response: Dict[str, Any]) -> Optional[Dict[str, str]]: + i = response["information"] + return { + "Main DSP": f'{i[4]:03.2f}', + "Slave DSP": f'{i[5]:03.2f}', + "ARM": f'{i[6]:03.2f}-{i[7]:03.2f}', + "Module version": response['ver'] + } diff --git a/solax/response_parser.py b/solax/response_parser.py index cbccbdd..fed6997 100644 --- a/solax/response_parser.py +++ b/solax/response_parser.py @@ -126,6 +126,7 @@ def handle_response(self, resp: bytearray) -> InverterResponse: """ raw_json = resp.decode("utf-8").replace(",,", ",0.0,").replace(",,", ",0.0,") + _LOGGER.info("Received response: %s", raw_json) json_response = {} for key, value in json.loads(raw_json).items(): json_response[key.lower()] = value diff --git a/tests/fixtures.py b/tests/fixtures.py index 0d7c2eb..d18661b 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -12,7 +12,8 @@ X1_BOOST_VALUES_V3, X1_HYBRID_G4_V_3_018_VALUES, X1_HYBRID_G4_VALUES, - X1_LITE_LV_80_VALUES, + X1_LITE_LV_80_v002_VALUES, + X1_LITE_LV_80_v005_VALUES, X1_MINI_G4_VALUES, X1_MINI_VALUES, X1_MINI_VALUES_V34, @@ -29,7 +30,7 @@ X3V34_HYBRID_VALUES, X3V34_HYBRID_VALUES_EPS_MODE, X3V34_HYBRID_VALUES_NEGATIVE_POWER, - XHYBRID_VALUES, + XHYBRID_VALUES, X1_LITE_LV_80_v005_VALUES, ) from tests.samples.responses import ( QVOLTHYBG33P_RESPONSE_V34, @@ -42,7 +43,7 @@ X1_HYBRID_G3_RESPONSE, X1_HYBRID_G4_RESPONSE, X1_HYBRID_G4_V_3_018_RESPONSE, - X1_LITE_LV_80_RESPONSE, + X1_LITE_LV_80_v002_RESPONSE, X1_MINI_G4, X1_MINI_RESPONSE_V34, X1_MINI_RESPONSE_V34_VER3, @@ -59,7 +60,7 @@ X3_MICPRO_G2_RESPONSE, X3_ULTRA_RESPONSE, XHYBRID_DE01_RESPONSE, - XHYBRID_DE02_RESPONSE, + XHYBRID_DE02_RESPONSE, X1_LITE_LV_80_v005_RESPONSE, ) X_FORWARDED_HEADER = {"X-Forwarded-For": "5.8.8.8"} @@ -203,9 +204,19 @@ def simple_http_fixture(httpserver): uri="/", method="POST", query_string="optType=ReadRealTimeData", - response=X1_LITE_LV_80_RESPONSE, + response=X1_LITE_LV_80_v002_RESPONSE, inverter=inverter.X1LiteLV, - values=X1_LITE_LV_80_VALUES, + values=X1_LITE_LV_80_v002_VALUES, + headers=None, + data=None, + ), + InverterUnderTest( + uri="/", + method="POST", + query_string="optType=ReadRealTimeData", + response=X1_LITE_LV_80_v005_RESPONSE, + inverter=inverter.X1LiteLV, + values=X1_LITE_LV_80_v005_VALUES, headers=None, data=None, ), diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index f76f1bf..d41d319 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -558,7 +558,7 @@ "Total Import Energy": 0.0, } -X1_LITE_LV_80_VALUES = { +X1_LITE_LV_80_v002_VALUES = { "AC Voltage": 239.7, "AC Output Current": 0.9, "AC Output Power": 1280, @@ -578,11 +578,15 @@ "PV2 Power": 985.0, "PV3 Power": 0.0, "Total PV Power": 0.0, - "Today's PV Energy": 0.2, + "Daily PV Energy": 0.2, "Total PV Energy": 255.0, + "Inverter Temperature": 41.0, + "Inverter Temperature 1": 35.0, + "Inverter Temperature 2": 33.0, + "Inverter Temperature 3": 41.0, "Battery Type": 0.0, - "Battery voltage": 52.6, - "Battery current": -7.3, + "Battery Voltage": 52.6, + "Battery Current": -7.3, "Total Battery power": -38.8, "Battery SoC": 62.0, "Battery Temperature 1": 19.0, @@ -590,21 +594,63 @@ "Battery Temperature 3": 22.0, "Battery Temperature 4": 19.0, "Battery Temperature": 19.0, - "Battery Charge total": 111.4, - "Battery Discharge total": 163.0, - "Battery Charge today": 0.0, - "Battery Discharge today": 4.0, - "Charging Duration": 0.0, - "Today's Battery Discharge": 0.0, + "Daily Battery Discharge": 0.0, "Total Battery Discharge": 163.0, - "Today's Battery Charge": 4.0, + "Daily Battery Charge": 4.0, "Total Battery Charge": 111.4, - "Today's Generated Energy": 0.0, - "Total Generated Energy": 120.6, - "Today's EPS Energy": 4.7, - "Total EPS Energy": 224.5, - "Today's Import Energy": 3.2, - "Total Import Energy": 282.9, + "Daily Inverter Output": 0.0, + "Total Inverter Output": 120.6, + "Daily Inverter EPS Energy": 4.7, + "Total Inverter EPS Energy": 224.5, + "Daily Imported Energy": 3.2, + "Total Imported Energy": 282.9, +} +X1_LITE_LV_80_v005_VALUES = { + "AC Voltage": 0, + "AC Output Current": 0, + "AC Output Power": 0, + "AC Frequency": 0, + "Grid PF": 100.0, + "AC Voltage Out": 229.4, + "AC Frequency Out": 49.9, + "Grid Power": 0.0, + "Hourly Energy": 123.24, + "PV1 Voltage": 327.9, + "PV2 Voltage": 288.5, + "PV3 Voltage": 0.0, + "PV1 Current": 0.1, + "PV2 Current": 0.3, + "PV3 Current": 0.0, + "PV1 Power": 53.0, + "PV2 Power": 97.0, + "PV3 Power": 0.0, + "Total PV Power": 150.0, + "Daily PV Energy": 8.5, + "Total PV Energy": 1538.2, + "Inverter Temperature": 47.0, + "Inverter Temperature 1": 43.0, + "Inverter Temperature 2": 42.0, + "Inverter Temperature 3": 49.0, + "Battery Type": 0.0, + "Battery Voltage": 52.9, + "Battery Current": -17.8, + "Total Battery power": -94.3, + "Battery SoC": 96.0, + "Battery Temperature 1": 19.0, + "Battery Temperature 2": 21.0, + "Battery Temperature 3": 22.0, + "Battery Temperature 4": 19.0, + "Battery Temperature": 19.0, + "Daily Battery Discharge": 6.1, + "Total Battery Discharge": 345.0, + "Daily Battery Charge": 0.9, + "Total Battery Charge": 900.1, + "Daily Inverter Output": 1.1, + "Total Inverter Output": 581.6, + "Daily Inverter EPS Energy": 10.9, + "Total Inverter EPS Energy": 1102.7, + "Daily Imported Energy": 0.0, + "Total Imported Energy": 116.1, } QVOLTHYBG33P_VALUES = { diff --git a/tests/samples/responses.py b/tests/samples/responses.py index 88ac781..8c0c1ac 100644 --- a/tests/samples/responses.py +++ b/tests/samples/responses.py @@ -1613,223 +1613,28 @@ "Information": [3.000, 18, "XXXXXXX", 8, 0.16, 0.00, 0.23, 0.00, 0.00, 1], } -X1_LITE_LV_80_RESPONSE = { +X1_LITE_LV_80_v002_RESPONSE = { "sn": "XXXXXXX", "ver": "1.002.09", "type": 103, - "Data": [ - 2397, - 9, - 1280, - 5002, - 1000, - 3287, - 110, - 2561, - 96, - 0, - 0, - 1895, - 985, - 0, - 0, - 7, - 5, - 1206, - 0, - 11, - 0, - 0, - 0, - 526, - 65463, - 65148, - 1114, - 0, - 1630, - 0, - 0, - 40, - 0, - 0, - 0, - 0, - 2829, - 0, - 0, - 0, - 32, - 0, - 2297, - 16, - 387, - 5000, - 47, - 2245, - 0, - 6403, - 7945, - 8224, - 2, - 2550, - 0, - 256, - 256, - 527, - 65460, - 65136, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 410, - 350, - 330, - 410, - 19, - 21, - 22, - 620, - 180, - 237, - 19, - 19, - 0, - 0, - 0, - 100, - 0, - 8, - 2, - 12, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2295, - 0, - 0, - 0, - 499, - 1, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 255, - 317, - 0, - 2, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + "Data": [2397,9,1280,5002,1000,3287,110,2561,96,0,0,1895,985,0,0,7,5,1206,0,11,0,0,0,526,65463,65148,1114,0,1630,0,0,40,0,0,0,0,2829,0,0,0,32,0,2297,16,387,5000,47,2245,0,6403,7945,8224,2,2550,0,256,256,527,65460,65136,0,0,0,0,0,0,0,0,410,350,330,410,19,21,22,620,180,237,19,19,0,0,0,100,0,8,2,12,2,0,0,0,0,0,0,0,0,2295,0,0,0,499,1,2,0,0,0,0,0,0,255,317,0,2,4,5,6,7,8,9,10,11,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + "Information": [ + 8.000, + 103, + "XXXXXXX", + 13, 14.54, 0.00, 14.54, 1.13, 0.00, 1, ], +} +X1_LITE_LV_80_v005_RESPONSE = { + "sn": "XXXXXXX", + "ver": "1.005.04", + "type": 103, + "Data": [0,0,0,0,1000,3279,1,2885,3,0,0,53,97,0,150,7,5,5810,0,116,0,11,0,529,65358,64593,9001,0,3450,0,61,9,0,0,10,0,1161,0,0,0,0,0,2303,47,1093,5000,109,11027,0,6408,7954,12323,85,15382,0,256,0,529,65358,64593,0,0,0,0,0,0,0,0,470,420,410,490,24,31,34,960,171,237,24,24,0,0,0,1000,0,0,0,15,2,0,0,0,0,0,0,0,0,2294,0,0,0,499,1,2,0,0,0,0,0,0,255,188,0,0,420,980,42,45,1,1679,916,929,34043,0,0,0,0,0,0,427,0,6399,7,1208,1,0,65,0,0,3,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "Information": [ 8.000, 103, "XXXXXXX", - 13, - 14.54, - 0.00, - 14.54, - 1.13, - 0.00, - 1, + 13, 25.10, 0.00, 25.10, 1.13, 0.00, 1, ], } From d40fd572d9dfa59762c20fef12a6230026d67c4a Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Mon, 1 Sep 2025 22:52:21 +0300 Subject: [PATCH 06/13] Refactor X1 Lite LV inverter parsing logic Streamlined parsing methods and formatting in the `X1LiteLV` class. Updated test fixtures and sample responses for better clarity and structure. --- solax/inverters/x1_lite_lv.py | 64 +++-- tests/fixtures.py | 11 +- tests/samples/responses.py | 522 +++++++++++++++++++++++++++++++++- 3 files changed, 567 insertions(+), 30 deletions(-) diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py index 0eefd81..841bddd 100644 --- a/solax/inverters/x1_lite_lv.py +++ b/solax/inverters/x1_lite_lv.py @@ -1,10 +1,10 @@ from typing import Any, Dict, Optional + import voluptuous as vol -from solax import utils + from solax.inverter import Inverter -from solax.inverter_http_client import InverterHttpClient, Method from solax.units import Total, Units -from solax.utils import div10, div100, pack_u16, to_signed, to_signed32, twoway_div10 +from solax.utils import div10, div100, pack_u16, to_signed, twoway_div10 class X1LiteLV(Inverter): @@ -36,13 +36,13 @@ def response_decoder(cls): :return: """ return { - "AC Voltage": (0, Units.V, div10), ## ++ + "AC Voltage": (0, Units.V, div10), "AC Current": (1, Units.A, twoway_div10), "AC Power": (2, Units.W, to_signed), "AC Frequency": (3, Units.HZ, div100), "Grid PF": (4, Units.PERCENT, div10), ################################### - "AC Voltage Out": (97, Units.V, div10), ## ++ + "AC Voltage Out": (97, Units.V, div10), # "AC Current Out": (98, Units.A, twoway_div10), # "AC Power Out": (99, Units.W, to_signed), "AC Frequency Out": (101, Units.HZ, div10), @@ -64,8 +64,8 @@ def response_decoder(cls): "PV2 Power": (12, Units.W), "PV3 Power": (13, Units.W), "Total PV Power": (14, Total(Units.KWH.value), to_signed), - "Daily PV Energy": (52, Total(Units.KWH.value), twoway_div10), ## ++ - "Total PV Energy": (53, Total(Units.KWH.value), twoway_div10), ## ++ + "Daily PV Energy": (52, Total(Units.KWH.value), twoway_div10), + "Total PV Energy": (53, Total(Units.KWH.value), twoway_div10), ################################ "Inverter Temperature": (68, Units.C, div10), "Inverter Temperature 1": (69, Units.C, div10), @@ -77,7 +77,7 @@ def response_decoder(cls): "Battery Current": (24, Units.A, twoway_div10), "Total Battery power": (25, Units.W, twoway_div10), ########################### - "Battery SoC": (75, Units.PERCENT, div10), ## ++ + "Battery SoC": (75, Units.PERCENT, div10), "Battery Temperature 1": (72, Units.C), "Battery Temperature 2": (73, Units.C), "Battery Temperature 3": (74, Units.C), @@ -85,16 +85,36 @@ def response_decoder(cls): "Battery Temperature": (79, Units.C), #################################### "Daily Battery Discharge": (30, Total(Units.KWH.value), div10), - "Total Battery Discharge": (pack_u16(28, 29), Total(Units.KWH.value), twoway_div10), + "Total Battery Discharge": ( + pack_u16(28, 29), + Total(Units.KWH.value), + twoway_div10, + ), "Daily Battery Charge": (31, Total(Units.KWH.value), div10), - "Total Battery Charge": (pack_u16(26, 27), Total(Units.KWH.value), twoway_div10), + "Total Battery Charge": ( + pack_u16(26, 27), + Total(Units.KWH.value), + twoway_div10, + ), ################ - "Daily Inverter Output": (21, Total(Units.KWH.value), div10), ## ++ - "Total Inverter Output": (pack_u16(17, 18), Total(Units.KWH.value), div10), ## ++ - "Daily Inverter EPS Energy": (46, Total(Units.KWH.value), div10), ## ++ - "Total Inverter EPS Energy": (pack_u16(47, 48), Total(Units.KWH.value), div10), ## ++ - "Daily Imported Energy": (40, Total(Units.KWH.value), div10), ## ++ - "Total Imported Energy": (pack_u16(36, 37), Total(Units.KWH.value), div10), ## ++ + "Daily Inverter Output": (21, Total(Units.KWH.value), div10), + "Total Inverter Output": ( + pack_u16(17, 18), + Total(Units.KWH.value), + div10, + ), + "Daily Inverter EPS Energy": (46, Total(Units.KWH.value), div10), + "Total Inverter EPS Energy": ( + pack_u16(47, 48), + Total(Units.KWH.value), + div10, + ), + "Daily Imported Energy": (40, Total(Units.KWH.value), div10), + "Total Imported Energy": ( + pack_u16(36, 37), + Total(Units.KWH.value), + div10, + ), } @classmethod @@ -108,11 +128,13 @@ def inverter_serial_number_getter(cls, response: Dict[str, Any]) -> Optional[str return info[2] if len(info) > 2 else None @classmethod - def inverter_versions_getter(cls, response: Dict[str, Any]) -> Optional[Dict[str, str]]: + def inverter_versions_getter( + cls, response: Dict[str, Any] + ) -> Optional[Dict[str, str]]: i = response["information"] return { - "Main DSP": f'{i[4]:03.2f}', - "Slave DSP": f'{i[5]:03.2f}', - "ARM": f'{i[6]:03.2f}-{i[7]:03.2f}', - "Module version": response['ver'] + "Main DSP": f"{i[4]:03.2f}", + "Slave DSP": f"{i[5]:03.2f}", + "ARM": f"{i[6]:03.2f}-{i[7]:03.2f}", + "Module version": response["ver"], } diff --git a/tests/fixtures.py b/tests/fixtures.py index d18661b..62ac18c 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -12,8 +12,6 @@ X1_BOOST_VALUES_V3, X1_HYBRID_G4_V_3_018_VALUES, X1_HYBRID_G4_VALUES, - X1_LITE_LV_80_v002_VALUES, - X1_LITE_LV_80_v005_VALUES, X1_MINI_G4_VALUES, X1_MINI_VALUES, X1_MINI_VALUES_V34, @@ -30,7 +28,9 @@ X3V34_HYBRID_VALUES, X3V34_HYBRID_VALUES_EPS_MODE, X3V34_HYBRID_VALUES_NEGATIVE_POWER, - XHYBRID_VALUES, X1_LITE_LV_80_v005_VALUES, + XHYBRID_VALUES, + X1_LITE_LV_80_v002_VALUES, + X1_LITE_LV_80_v005_VALUES, ) from tests.samples.responses import ( QVOLTHYBG33P_RESPONSE_V34, @@ -43,7 +43,6 @@ X1_HYBRID_G3_RESPONSE, X1_HYBRID_G4_RESPONSE, X1_HYBRID_G4_V_3_018_RESPONSE, - X1_LITE_LV_80_v002_RESPONSE, X1_MINI_G4, X1_MINI_RESPONSE_V34, X1_MINI_RESPONSE_V34_VER3, @@ -60,7 +59,9 @@ X3_MICPRO_G2_RESPONSE, X3_ULTRA_RESPONSE, XHYBRID_DE01_RESPONSE, - XHYBRID_DE02_RESPONSE, X1_LITE_LV_80_v005_RESPONSE, + XHYBRID_DE02_RESPONSE, + X1_LITE_LV_80_v002_RESPONSE, + X1_LITE_LV_80_v005_RESPONSE, ) X_FORWARDED_HEADER = {"X-Forwarded-For": "5.8.8.8"} diff --git a/tests/samples/responses.py b/tests/samples/responses.py index 8c0c1ac..9287062 100644 --- a/tests/samples/responses.py +++ b/tests/samples/responses.py @@ -1617,24 +1617,538 @@ "sn": "XXXXXXX", "ver": "1.002.09", "type": 103, - "Data": [2397,9,1280,5002,1000,3287,110,2561,96,0,0,1895,985,0,0,7,5,1206,0,11,0,0,0,526,65463,65148,1114,0,1630,0,0,40,0,0,0,0,2829,0,0,0,32,0,2297,16,387,5000,47,2245,0,6403,7945,8224,2,2550,0,256,256,527,65460,65136,0,0,0,0,0,0,0,0,410,350,330,410,19,21,22,620,180,237,19,19,0,0,0,100,0,8,2,12,2,0,0,0,0,0,0,0,0,2295,0,0,0,499,1,2,0,0,0,0,0,0,255,317,0,2,4,5,6,7,8,9,10,11,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + "Data": [ + 2397, + 9, + 1280, + 5002, + 1000, + 3287, + 110, + 2561, + 96, + 0, + 0, + 1895, + 985, + 0, + 0, + 7, + 5, + 1206, + 0, + 11, + 0, + 0, + 0, + 526, + 65463, + 65148, + 1114, + 0, + 1630, + 0, + 0, + 40, + 0, + 0, + 0, + 0, + 2829, + 0, + 0, + 0, + 32, + 0, + 2297, + 16, + 387, + 5000, + 47, + 2245, + 0, + 6403, + 7945, + 8224, + 2, + 2550, + 0, + 256, + 256, + 527, + 65460, + 65136, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 410, + 350, + 330, + 410, + 19, + 21, + 22, + 620, + 180, + 237, + 19, + 19, + 0, + 0, + 0, + 100, + 0, + 8, + 2, + 12, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2295, + 0, + 0, + 0, + 499, + 1, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 317, + 0, + 2, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], "Information": [ 8.000, 103, "XXXXXXX", - 13, 14.54, 0.00, 14.54, 1.13, 0.00, 1, + 13, + 14.54, + 0.00, + 14.54, + 1.13, + 0.00, + 1, ], } X1_LITE_LV_80_v005_RESPONSE = { "sn": "XXXXXXX", "ver": "1.005.04", "type": 103, - "Data": [0,0,0,0,1000,3279,1,2885,3,0,0,53,97,0,150,7,5,5810,0,116,0,11,0,529,65358,64593,9001,0,3450,0,61,9,0,0,10,0,1161,0,0,0,0,0,2303,47,1093,5000,109,11027,0,6408,7954,12323,85,15382,0,256,0,529,65358,64593,0,0,0,0,0,0,0,0,470,420,410,490,24,31,34,960,171,237,24,24,0,0,0,1000,0,0,0,15,2,0,0,0,0,0,0,0,0,2294,0,0,0,499,1,2,0,0,0,0,0,0,255,188,0,0,420,980,42,45,1,1679,916,929,34043,0,0,0,0,0,0,427,0,6399,7,1208,1,0,65,0,0,3,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + "Data": [ + 0, + 0, + 0, + 0, + 1000, + 3279, + 1, + 2885, + 3, + 0, + 0, + 53, + 97, + 0, + 150, + 7, + 5, + 5810, + 0, + 116, + 0, + 11, + 0, + 529, + 65358, + 64593, + 9001, + 0, + 3450, + 0, + 61, + 9, + 0, + 0, + 10, + 0, + 1161, + 0, + 0, + 0, + 0, + 0, + 2303, + 47, + 1093, + 5000, + 109, + 11027, + 0, + 6408, + 7954, + 12323, + 85, + 15382, + 0, + 256, + 0, + 529, + 65358, + 64593, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 470, + 420, + 410, + 490, + 24, + 31, + 34, + 960, + 171, + 237, + 24, + 24, + 0, + 0, + 0, + 1000, + 0, + 0, + 0, + 15, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2294, + 0, + 0, + 0, + 499, + 1, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 188, + 0, + 0, + 420, + 980, + 42, + 45, + 1, + 1679, + 916, + 929, + 34043, + 0, + 0, + 0, + 0, + 0, + 0, + 427, + 0, + 6399, + 7, + 1208, + 1, + 0, + 65, + 0, + 0, + 3, + 30, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], "Information": [ 8.000, 103, "XXXXXXX", - 13, 25.10, 0.00, 25.10, 1.13, 0.00, 1, + 13, + 25.10, + 0.00, + 25.10, + 1.13, + 0.00, + 1, ], } From e1e2f5086edcba2c9bd512bbd48c38d8355a0a75 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Tue, 2 Sep 2025 10:38:19 +0300 Subject: [PATCH 07/13] Update X1 Lite LV expected values and refactor KWH unit usage Standardized energy unit handling in the `X1LiteLV` inverter class by removing `.value` for `Total(Units.KWH)` across all entries. Adjusted test expected values for consistency in key naming. --- solax/inverters/x1_lite_lv.py | 28 ++++++++++++++-------------- tests/samples/expected_values.py | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py index 841bddd..d99e6bf 100644 --- a/solax/inverters/x1_lite_lv.py +++ b/solax/inverters/x1_lite_lv.py @@ -52,7 +52,7 @@ def response_decoder(cls): "Grid Power": (32, Units.W, twoway_div10), # "Grid power 1": (33, Units.W, twoway_div10), # "Grid power 2": (34, Units.W, twoway_div10), - "Hourly Energy": (51, Total(Units.KWH.value), div100), + "Hourly Energy": (51, Total(Units.KWH), div100), ############################### "PV1 Voltage": (5, Units.V, div10), "PV2 Voltage": (7, Units.V, div10), @@ -63,9 +63,9 @@ def response_decoder(cls): "PV1 Power": (11, Units.W), "PV2 Power": (12, Units.W), "PV3 Power": (13, Units.W), - "Total PV Power": (14, Total(Units.KWH.value), to_signed), - "Daily PV Energy": (52, Total(Units.KWH.value), twoway_div10), - "Total PV Energy": (53, Total(Units.KWH.value), twoway_div10), + "Total PV Power": (14, Total(Units.KWH), to_signed), + "Daily PV Energy": (52, Total(Units.KWH), twoway_div10), + "Total PV Energy": (53, Total(Units.KWH), twoway_div10), ################################ "Inverter Temperature": (68, Units.C, div10), "Inverter Temperature 1": (69, Units.C, div10), @@ -84,35 +84,35 @@ def response_decoder(cls): "Battery Temperature 4": (78, Units.C), "Battery Temperature": (79, Units.C), #################################### - "Daily Battery Discharge": (30, Total(Units.KWH.value), div10), + "Daily Battery Discharge": (30, Total(Units.KWH), div10), "Total Battery Discharge": ( pack_u16(28, 29), - Total(Units.KWH.value), + Total(Units.KWH), twoway_div10, ), - "Daily Battery Charge": (31, Total(Units.KWH.value), div10), + "Daily Battery Charge": (31, Total(Units.KWH), div10), "Total Battery Charge": ( pack_u16(26, 27), - Total(Units.KWH.value), + Total(Units.KWH), twoway_div10, ), ################ - "Daily Inverter Output": (21, Total(Units.KWH.value), div10), + "Daily Inverter Output": (21, Total(Units.KWH), div10), "Total Inverter Output": ( pack_u16(17, 18), - Total(Units.KWH.value), + Total(Units.KWH), div10, ), - "Daily Inverter EPS Energy": (46, Total(Units.KWH.value), div10), + "Daily Inverter EPS Energy": (46, Total(Units.KWH), div10), "Total Inverter EPS Energy": ( pack_u16(47, 48), - Total(Units.KWH.value), + Total(Units.KWH), div10, ), - "Daily Imported Energy": (40, Total(Units.KWH.value), div10), + "Daily Imported Energy": (40, Total(Units.KWH), div10), "Total Imported Energy": ( pack_u16(36, 37), - Total(Units.KWH.value), + Total(Units.KWH), div10, ), } diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index d41d319..b2e2631 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -560,8 +560,8 @@ X1_LITE_LV_80_v002_VALUES = { "AC Voltage": 239.7, - "AC Output Current": 0.9, - "AC Output Power": 1280, + "AC Current": 0.9, + "AC Power": 1280, "AC Frequency": 50.02, "Grid PF": 100.0, "AC Voltage Out": 229.5, @@ -607,8 +607,8 @@ } X1_LITE_LV_80_v005_VALUES = { "AC Voltage": 0, - "AC Output Current": 0, - "AC Output Power": 0, + "AC Current": 0, + "AC Power": 0, "AC Frequency": 0, "Grid PF": 100.0, "AC Voltage Out": 229.4, From 2eaf8f2f6a2385a17e07a12a936a7c9442cbc638 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Tue, 2 Sep 2025 13:04:44 +0300 Subject: [PATCH 08/13] Update `Hourly Energy` expected value in test data by 0.01 for consistency --- tests/samples/expected_values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index b2e2631..175d770 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -614,7 +614,7 @@ "AC Voltage Out": 229.4, "AC Frequency Out": 49.9, "Grid Power": 0.0, - "Hourly Energy": 123.24, + "Hourly Energy": 123.23, "PV1 Voltage": 327.9, "PV2 Voltage": 288.5, "PV3 Voltage": 0.0, From dc19afe41d27a59e9555d484eb4f0c566bd66d17 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Wed, 3 Sep 2025 11:03:36 +0300 Subject: [PATCH 09/13] Update X1 Lite LV expected values and fix unit handling for Total PV Power Standardized Total PV Power unit to Watts in `X1LiteLV` inverter. Updated test expected values and sample responses to reflect corrected scaling and consistency. --- solax/inverters/x1_lite_lv.py | 2 +- tests/samples/expected_values.py | 64 ++++++++++----------- tests/samples/responses.py | 98 ++++++++++++++++---------------- 3 files changed, 82 insertions(+), 82 deletions(-) diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py index d99e6bf..c7480b6 100644 --- a/solax/inverters/x1_lite_lv.py +++ b/solax/inverters/x1_lite_lv.py @@ -63,7 +63,7 @@ def response_decoder(cls): "PV1 Power": (11, Units.W), "PV2 Power": (12, Units.W), "PV3 Power": (13, Units.W), - "Total PV Power": (14, Total(Units.KWH), to_signed), + "Total PV Power": (14, Units.W), "Daily PV Energy": (52, Total(Units.KWH), twoway_div10), "Total PV Energy": (53, Total(Units.KWH), twoway_div10), ################################ diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index 175d770..1ad7ee4 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -611,44 +611,44 @@ "AC Power": 0, "AC Frequency": 0, "Grid PF": 100.0, - "AC Voltage Out": 229.4, + "AC Voltage Out": 228.5, "AC Frequency Out": 49.9, "Grid Power": 0.0, - "Hourly Energy": 123.23, - "PV1 Voltage": 327.9, - "PV2 Voltage": 288.5, + "Hourly Energy": 97.28, + "PV1 Voltage": 346.6, + "PV2 Voltage": 287.5, "PV3 Voltage": 0.0, - "PV1 Current": 0.1, - "PV2 Current": 0.3, + "PV1 Current": 7.3, + "PV2 Current": 0.6, "PV3 Current": 0.0, - "PV1 Power": 53.0, - "PV2 Power": 97.0, + "PV1 Power": 2544.0, + "PV2 Power": 182.0, "PV3 Power": 0.0, - "Total PV Power": 150.0, - "Daily PV Energy": 8.5, - "Total PV Energy": 1538.2, - "Inverter Temperature": 47.0, - "Inverter Temperature 1": 43.0, - "Inverter Temperature 2": 42.0, - "Inverter Temperature 3": 49.0, + "Total PV Power": 2726.0, + "Daily PV Energy": 5.2, + "Total PV Energy": 1560.4, + "Inverter Temperature": 43.0, + "Inverter Temperature 1": 45.0, + "Inverter Temperature 2": 45.0, + "Inverter Temperature 3": 51.0, "Battery Type": 0.0, - "Battery Voltage": 52.9, - "Battery Current": -17.8, - "Total Battery power": -94.3, - "Battery SoC": 96.0, - "Battery Temperature 1": 19.0, - "Battery Temperature 2": 21.0, - "Battery Temperature 3": 22.0, - "Battery Temperature 4": 19.0, - "Battery Temperature": 19.0, - "Daily Battery Discharge": 6.1, - "Total Battery Discharge": 345.0, - "Daily Battery Charge": 0.9, - "Total Battery Charge": 900.1, - "Daily Inverter Output": 1.1, - "Total Inverter Output": 581.6, - "Daily Inverter EPS Energy": 10.9, - "Total Inverter EPS Energy": 1102.7, + "Battery Voltage": 53.8, + "Battery Current": 48.0, + "Total Battery power": 258.7, + "Battery SoC": 94.0, + "Battery Temperature 1": 26.0, + "Battery Temperature 2": 37.0, + "Battery Temperature 3": 39.0, + "Battery Temperature 4": 26.0, + "Battery Temperature": 26.0, + "Daily Battery Discharge": 5.2, + "Total Battery Discharge": 346.5, + "Daily Battery Charge": 0.1, + "Total Battery Charge": 919.0, + "Daily Inverter Output": 0.0, + "Total Inverter Output": 581.0, + "Daily Inverter EPS Energy": 6.3, + "Total Inverter EPS Energy": 1143.9, "Daily Imported Energy": 0.0, "Total Imported Energy": 116.1, } diff --git a/tests/samples/responses.py b/tests/samples/responses.py index 9287062..84fcf64 100644 --- a/tests/samples/responses.py +++ b/tests/samples/responses.py @@ -1842,33 +1842,33 @@ 0, 0, 1000, - 3279, - 1, - 2885, - 3, + 3466, + 73, + 2875, + 6, 0, 0, - 53, - 97, + 2544, + 182, 0, - 150, + 2726, 7, 5, 5810, 0, 116, 0, - 11, 0, - 529, - 65358, - 64593, - 9001, 0, - 3450, + 538, + 480, + 2587, + 9190, 0, - 61, - 9, + 3465, + 0, + 52, + 1, 0, 0, 10, @@ -1879,24 +1879,24 @@ 0, 0, 0, - 2303, - 47, - 1093, + 2288, + 6, + 139, 5000, - 109, - 11027, + 63, + 11439, 0, - 6408, - 7954, - 12323, - 85, - 15382, + 6409, + 778, + 9728, + 52, + 15604, 0, 256, 0, - 529, - 65358, - 64593, + 536, + 476, + 2555, 0, 0, 0, @@ -1905,18 +1905,18 @@ 0, 0, 0, - 470, - 420, - 410, - 490, - 24, - 31, - 34, - 960, + 430, + 450, + 450, + 510, + 26, + 37, + 39, + 940, 171, 237, - 24, - 24, + 26, + 26, 0, 0, 0, @@ -1934,7 +1934,7 @@ 0, 0, 0, - 2294, + 2285, 0, 0, 0, @@ -1948,17 +1948,17 @@ 0, 0, 255, - 188, + 184, 0, 0, 420, - 980, - 42, - 45, - 1, - 1679, - 916, - 929, + 1180, + 44, + 65, + 187, + 722, + 3398, + 2580, 34043, 0, 0, @@ -1967,10 +1967,10 @@ 0, 0, 427, - 0, + 4, 6399, 7, - 1208, + 2613, 1, 0, 65, From 5dfabbb5b9982b3858861cedc08d9ffc7d1160c5 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Wed, 3 Sep 2025 11:09:21 +0300 Subject: [PATCH 10/13] Fix order of battery metrics in X1 Lite LV parser and expected values --- solax/inverters/x1_lite_lv.py | 12 ++++++------ tests/samples/expected_values.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py index c7480b6..4ecd247 100644 --- a/solax/inverters/x1_lite_lv.py +++ b/solax/inverters/x1_lite_lv.py @@ -84,15 +84,15 @@ def response_decoder(cls): "Battery Temperature 4": (78, Units.C), "Battery Temperature": (79, Units.C), #################################### - "Daily Battery Discharge": (30, Total(Units.KWH), div10), - "Total Battery Discharge": ( - pack_u16(28, 29), + "Daily Battery Charge": (30, Total(Units.KWH), div10), + "Total Battery Charge": ( + pack_u16(26, 27), Total(Units.KWH), twoway_div10, ), - "Daily Battery Charge": (31, Total(Units.KWH), div10), - "Total Battery Charge": ( - pack_u16(26, 27), + "Daily Battery Discharge": (31, Total(Units.KWH), div10), + "Total Battery Discharge": ( + pack_u16(28, 29), Total(Units.KWH), twoway_div10, ), diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index 1ad7ee4..bb3afdb 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -594,10 +594,10 @@ "Battery Temperature 3": 22.0, "Battery Temperature 4": 19.0, "Battery Temperature": 19.0, - "Daily Battery Discharge": 0.0, - "Total Battery Discharge": 163.0, "Daily Battery Charge": 4.0, - "Total Battery Charge": 111.4, + "Total Battery Charge": 163.0, + "Daily Battery Discharge": 0.0, + "Total Battery Discharge": 111.4, "Daily Inverter Output": 0.0, "Total Inverter Output": 120.6, "Daily Inverter EPS Energy": 4.7, @@ -641,10 +641,10 @@ "Battery Temperature 3": 39.0, "Battery Temperature 4": 26.0, "Battery Temperature": 26.0, - "Daily Battery Discharge": 5.2, - "Total Battery Discharge": 346.5, - "Daily Battery Charge": 0.1, + "Daily Battery Charge": 5.2, "Total Battery Charge": 919.0, + "Daily Battery Discharge": 0.1, + "Total Battery Discharge": 346.5, "Daily Inverter Output": 0.0, "Total Inverter Output": 581.0, "Daily Inverter EPS Energy": 6.3, From 66e9259d5c7ff9703dac16b9546b73bfe0d9df30 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Wed, 3 Sep 2025 11:19:47 +0300 Subject: [PATCH 11/13] Update X1 Lite LV expected values: fix battery charge/discharge key order for consistency --- tests/samples/expected_values.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index bb3afdb..e716de1 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -594,10 +594,10 @@ "Battery Temperature 3": 22.0, "Battery Temperature 4": 19.0, "Battery Temperature": 19.0, - "Daily Battery Charge": 4.0, - "Total Battery Charge": 163.0, - "Daily Battery Discharge": 0.0, - "Total Battery Discharge": 111.4, + "Daily Battery Charge": 0.0, + "Total Battery Charge": 111.4, + "Daily Battery Discharge": 4.0, + "Total Battery Discharge": 163.0, "Daily Inverter Output": 0.0, "Total Inverter Output": 120.6, "Daily Inverter EPS Energy": 4.7, From f5a8736ca1600f56a9d0821b365c2d022500b988 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Wed, 3 Sep 2025 11:56:23 +0300 Subject: [PATCH 12/13] Comment out unused `inverter_versions_getter` method in X1 Lite LV inverter class for clarity. --- solax/inverters/x1_lite_lv.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/solax/inverters/x1_lite_lv.py b/solax/inverters/x1_lite_lv.py index 4ecd247..5bb0638 100644 --- a/solax/inverters/x1_lite_lv.py +++ b/solax/inverters/x1_lite_lv.py @@ -127,14 +127,14 @@ def inverter_serial_number_getter(cls, response: Dict[str, Any]) -> Optional[str info = response.get("information", []) return info[2] if len(info) > 2 else None - @classmethod - def inverter_versions_getter( - cls, response: Dict[str, Any] - ) -> Optional[Dict[str, str]]: - i = response["information"] - return { - "Main DSP": f"{i[4]:03.2f}", - "Slave DSP": f"{i[5]:03.2f}", - "ARM": f"{i[6]:03.2f}-{i[7]:03.2f}", - "Module version": response["ver"], - } + # @classmethod + # def inverter_versions_getter( + # cls, response: Dict[str, Any] + # ) -> Optional[Dict[str, str]]: + # i = response["information"] + # return { + # "Main DSP": f"{i[4]:03.2f}", + # "Slave DSP": f"{i[5]:03.2f}", + # "ARM": f"{i[6]:03.2f}-{i[7]:03.2f}", + # "Module version": response["ver"], + # } From 02346222a7d64ab609b0955a4dadb6c8e7f78fe6 Mon Sep 17 00:00:00 2001 From: Sergiy Andrusenko Date: Wed, 3 Sep 2025 12:15:29 +0300 Subject: [PATCH 13/13] Remove unused log statement in `response_parser`. --- solax/response_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/solax/response_parser.py b/solax/response_parser.py index fed6997..cbccbdd 100644 --- a/solax/response_parser.py +++ b/solax/response_parser.py @@ -126,7 +126,6 @@ def handle_response(self, resp: bytearray) -> InverterResponse: """ raw_json = resp.decode("utf-8").replace(",,", ",0.0,").replace(",,", ",0.0,") - _LOGGER.info("Received response: %s", raw_json) json_response = {} for key, value in json.loads(raw_json).items(): json_response[key.lower()] = value