From 276bd3b12c3518a9e903c9dca30402f295a814ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20K=C3=BChnapfel?= Date: Sun, 4 Jun 2023 16:53:27 +0200 Subject: [PATCH 1/6] Fix deprecation warning --- solax/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solax/__init__.py b/solax/__init__.py index 9450a41..5953b06 100644 --- a/solax/__init__.py +++ b/solax/__init__.py @@ -2,7 +2,7 @@ import asyncio import logging -import async_timeout +from async_timeout import timeout from solax.discovery import discover from solax.inverter import Inverter, InverterResponse @@ -22,7 +22,7 @@ async def rt_request(inv: Inverter, retry, t_wait=0) -> InverterResponse: new_wait = (t_wait * 2) + 5 retry = retry - 1 try: - with async_timeout.timeout(REQUEST_TIMEOUT): + async with timeout(REQUEST_TIMEOUT): return await inv.get_data() except asyncio.TimeoutError: if retry > 0: From 7150616176ca7a802bc48ccea709cd7a65fcb924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20K=C3=BChnapfel?= Date: Sun, 4 Jun 2023 16:54:17 +0200 Subject: [PATCH 2/6] Add support for X3 MIC/PRO G2 --- solax/discovery.py | 2 + solax/inverters/__init__.py | 2 + solax/inverters/x3_mic_pro_g2.py | 78 ++++++++++++++++++++++ tests/fixtures.py | 12 ++++ tests/samples/expected_values.py | 31 +++++++++ tests/samples/responses.py | 111 ++++++++++++++++++++++++++++++- 6 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 solax/inverters/x3_mic_pro_g2.py diff --git a/solax/discovery.py b/solax/discovery.py index ff5b5ed..9cfdd60 100644 --- a/solax/discovery.py +++ b/solax/discovery.py @@ -14,6 +14,7 @@ X1MiniV34, X1Smart, X3HybridG4, + X3MicProG2, XHybrid, ) @@ -30,6 +31,7 @@ QVOLTHYBG33P, X1Boost, X1HybridGen4, + X3MicProG2, ] diff --git a/solax/inverters/__init__.py b/solax/inverters/__init__.py index 4fd3d70..883e9d3 100644 --- a/solax/inverters/__init__.py +++ b/solax/inverters/__init__.py @@ -7,6 +7,7 @@ from .x1_smart import X1Smart from .x3 import X3 from .x3_hybrid_g4 import X3HybridG4 +from .x3_mic_pro_g2 import X3MicProG2 from .x3_v34 import X3V34 from .x_hybrid import XHybrid @@ -22,4 +23,5 @@ "X3", "X1Boost", "X1HybridGen4", + "X3MicProG2", ] diff --git a/solax/inverters/x3_mic_pro_g2.py b/solax/inverters/x3_mic_pro_g2.py new file mode 100644 index 0000000..17d2584 --- /dev/null +++ b/solax/inverters/x3_mic_pro_g2.py @@ -0,0 +1,78 @@ +import voluptuous as vol + +from solax.inverter import Inverter +from solax.units import Total, Units +from solax.utils import div10, div100, pack_u16, to_signed, to_signed32, twoway_div10 + + +class X3MicProG2(Inverter): + """X3MicProG2 v3.008.10""" + + # pylint: disable=duplicate-code + _schema = vol.Schema( + { + vol.Required("type"): vol.All(int, 16), + vol.Required("sn"): str, + vol.Required("ver"): str, + vol.Required("Data"): vol.Schema( + vol.All( + [vol.Coerce(float)], + vol.Length(min=100, max=100), + ) + ), + vol.Required("Information"): vol.Schema( + vol.All(vol.Length(min=10, max=10)) + ), + }, + extra=vol.REMOVE_EXTRA, + ) + + @classmethod + def build_all_variants(cls, host, port, pwd=""): + return [cls._build(host, port, pwd, False)] + + @classmethod + def _decode_run_mode(cls, run_mode): + return { + 0: "Wait", + 1: "Check", + 2: "Normal", + 3: "Fault", + 4: "Permanent Fault", + 5: "Update", + }.get(run_mode) + + @classmethod + def response_decoder(cls): + return { + "Grid 1 Voltage": (0, Units.V, div10), + "Grid 2 Voltage": (1, Units.V, div10), + "Grid 3 Voltage": (2, Units.V, div10), + "Grid 1 Current": (3, Units.A, twoway_div10), + "Grid 2 Current": (4, Units.A, twoway_div10), + "Grid 3 Current": (5, Units.A, twoway_div10), + "Grid 1 Power": (6, Units.W, to_signed), + "Grid 2 Power": (7, Units.W, to_signed), + "Grid 3 Power": (8, Units.W, to_signed), + "PV1 Voltage": (9, Units.V, div10), + "PV2 Voltage": (10, Units.V, div10), + "PV3 Voltage": (11, Units.V, div10), + "PV1 Current": (12, Units.A, div10), + "PV2 Current": (13, Units.A, div10), + "PV3 Current": (14, Units.A, div10), + "PV1 Power": (15, Units.W), + "PV2 Power": (16, Units.W), + "PV3 Power": (17, Units.W), + "Grid 1 Frequency": (18, Units.HZ, div100), + "Grid 2 Frequency": (19, Units.HZ, div100), + "Grid 3 Frequency": (20, Units.HZ, div100), + "Run Mode": (21, Units.NONE), + "Run Mode Text": (21, Units.NONE, X3MicProG2._decode_run_mode), + "Total Yield": (pack_u16(22, 23), Total(Units.KWH), div10), + "Daily Yield": (24, Units.KWH, div10), + "Feed-in Power ": (pack_u16(72, 73), Units.W, to_signed32), + "Total Feed-in Energy": (pack_u16(74, 75), Total(Units.KWH), div100), + "Total Consumption": (pack_u16(76, 77), Total(Units.KWH), div100), + } + + # pylint: enable=duplicate-code diff --git a/tests/fixtures.py b/tests/fixtures.py index d425616..4868ff7 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -13,6 +13,7 @@ X1_VALUES, X3_HYBRID_G4_VALUES, X3_HYBRID_VALUES, + X3_MICPRO_G2_VALUES, X3_VALUES, X3V34_HYBRID_VALUES, X3V34_HYBRID_VALUES_EPS_MODE, @@ -35,6 +36,7 @@ X3_HYBRID_G3_RESPONSE, X3_HYBRID_G4_RESPONSE, X3_MIC_RESPONSE, + X3_MICPRO_G2_RESPONSE, XHYBRID_DE01_RESPONSE, XHYBRID_DE02_RESPONSE, ) @@ -216,6 +218,16 @@ def simple_http_fixture(httpserver): headers=None, data="optType=ReadRealTimeData", ), + InverterUnderTest( + uri="/", + method="POST", + query_string=None, + response=X3_MICPRO_G2_RESPONSE, + inverter=inverter.X3MicProG2, + values=X3_MICPRO_G2_VALUES, + headers=None, + data="optType=ReadRealTimeData", + ), InverterUnderTest( uri="/", method="POST", diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index c95b2e9..576c8f8 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -264,6 +264,37 @@ "Battery Voltage": 234.6, } +X3_MICPRO_G2_VALUES = { + "Grid 1 Voltage": 237.1, + "Grid 2 Voltage": 234.5, + "Grid 3 Voltage": 237.8, + "Grid 1 Current": 4.1, + "Grid 2 Current": 4.1, + "Grid 3 Current": 4.1, + "Grid 1 Power": 1018.0, + "Grid 2 Power": 992.0, + "Grid 3 Power": 970.0, + "PV1 Voltage": 174.1, + "PV2 Voltage": 174.5, + "PV3 Voltage": 0.0, + "PV1 Current": 8.8, + "PV2 Current": 8.7, + "PV3 Current": 0.0, + "PV1 Power": 1544.0, + "PV2 Power": 1531.0, + "PV3 Power": 0.0, + "Grid 1 Frequency": 49.98, + "Grid 2 Frequency": 49.99, + "Grid 3 Frequency": 49.94, + "Run Mode": 2.0, + "Run Mode Text": "Normal", + "Total Yield": 795.7, + "Daily Yield": 20.0, + "Feed-in Power ": 2707.0, + "Total Feed-in Energy": 657.24, + "Total Consumption": 307.33, +} + X1_VALUES = { "PV1 Current": 0, "PV2 Current": 1, diff --git a/tests/samples/responses.py b/tests/samples/responses.py index 5fa9997..ad65b18 100644 --- a/tests/samples/responses.py +++ b/tests/samples/responses.py @@ -1215,7 +1215,6 @@ }, } - X1_HYBRID_G4_RESPONSE = { "type": 15, "sn": "SXxxxxxxxx", @@ -1547,7 +1546,6 @@ "Information": [0.000, 5, "X3-Hybiyd-G3", "XXXXXXX", 1, 3.00, 0.00, 3.17, 1.01], } - X3_HYBRID_G3_2X_MPPT_RESPONSE = { "type": "X3-Hybiyd-G3", "SN": "XXXXXXXXXX", @@ -2606,6 +2604,115 @@ "Information": [10.000, 14, "H34A**********", 8, 1.23, 0.00, 1.24, 1.09, 0.00, 1], } +X3_MICPRO_G2_RESPONSE = { + "sn": "SRE*******", + "ver": "3.008.10", + "type": 16, + "Data": [ + 2371, + 2345, + 2378, + 41, + 41, + 41, + 1018, + 992, + 970, + 1741, + 1745, + 0, + 88, + 87, + 0, + 1544, + 1531, + 0, + 4998, + 4999, + 4994, + 2, + 7957, + 0, + 200, + 4000, + 57, + 43, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2707, + 0, + 188, + 1, + 30733, + 0, + 2982, + 0, + 1, + 4, + 1, + 0, + 13468, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "Information": [4.000, 16, "MC20**********", 8, 1.20, 0.00, 1.18, 1.00, 0.00, 1], +} + QVOLTHYBG33P_RESPONSE_V34 = { "sn": "SWX***", "ver": "2.034.06", From 05f219dcbd68bbdd60909e398ec688a5c17a87aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20K=C3=BChnapfel?= Date: Sun, 4 Jun 2023 22:24:31 +0200 Subject: [PATCH 3/6] Fix sensor enumeration --- solax/inverter.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/solax/inverter.py b/solax/inverter.py index df3042c..e8b87b4 100644 --- a/solax/inverter.py +++ b/solax/inverter.py @@ -82,19 +82,15 @@ def sensor_map(cls) -> Dict[str, Tuple[int, Measurement]]: Warning, HA depends on this """ sensors: Dict[str, Tuple[int, Measurement]] = {} - for name, mapping in cls.response_decoder().items(): + for idx, (name, mapping) in enumerate(cls.response_decoder().items()): unit = Measurement(Units.NONE) - - (idx, unit_or_measurement, *_) = mapping + unit_or_measurement = mapping[1] if isinstance(unit_or_measurement, Units): unit = Measurement(unit_or_measurement) else: unit = unit_or_measurement - if isinstance(idx, tuple): - sensor_indexes = idx[0] - first_sensor_index = sensor_indexes[0] - idx = first_sensor_index + sensors[name] = (idx, unit) return sensors From 073c3315875ef67addfa1ddf27c9da5130367acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20K=C3=BChnapfel?= Date: Sat, 10 Jun 2023 10:13:04 +0200 Subject: [PATCH 4/6] Revert "Fix sensor enumeration" This reverts commit 05f219dcbd68bbdd60909e398ec688a5c17a87aa. --- solax/inverter.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/solax/inverter.py b/solax/inverter.py index e8b87b4..df3042c 100644 --- a/solax/inverter.py +++ b/solax/inverter.py @@ -82,15 +82,19 @@ def sensor_map(cls) -> Dict[str, Tuple[int, Measurement]]: Warning, HA depends on this """ sensors: Dict[str, Tuple[int, Measurement]] = {} - for idx, (name, mapping) in enumerate(cls.response_decoder().items()): + for name, mapping in cls.response_decoder().items(): unit = Measurement(Units.NONE) - unit_or_measurement = mapping[1] + + (idx, unit_or_measurement, *_) = mapping if isinstance(unit_or_measurement, Units): unit = Measurement(unit_or_measurement) else: unit = unit_or_measurement - + if isinstance(idx, tuple): + sensor_indexes = idx[0] + first_sensor_index = sensor_indexes[0] + idx = first_sensor_index sensors[name] = (idx, unit) return sensors From 706c051816f49979f74b82fa8c1cc62335fcc1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20K=C3=BChnapfel?= Date: Sat, 10 Jun 2023 10:19:59 +0200 Subject: [PATCH 5/6] Revert fix for async_timeout deprecation warning --- solax/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solax/__init__.py b/solax/__init__.py index 5953b06..9450a41 100644 --- a/solax/__init__.py +++ b/solax/__init__.py @@ -2,7 +2,7 @@ import asyncio import logging -from async_timeout import timeout +import async_timeout from solax.discovery import discover from solax.inverter import Inverter, InverterResponse @@ -22,7 +22,7 @@ async def rt_request(inv: Inverter, retry, t_wait=0) -> InverterResponse: new_wait = (t_wait * 2) + 5 retry = retry - 1 try: - async with timeout(REQUEST_TIMEOUT): + with async_timeout.timeout(REQUEST_TIMEOUT): return await inv.get_data() except asyncio.TimeoutError: if retry > 0: From eb7c4f741dec73c65493ce6063fba233b7ecdc7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niclas=20K=C3=BChnapfel?= Date: Sat, 10 Jun 2023 10:23:25 +0200 Subject: [PATCH 6/6] Return inverter run mode once (as text) instead of twice (as text and numerical value) --- solax/inverters/x3_mic_pro_g2.py | 4 ++-- tests/samples/expected_values.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/solax/inverters/x3_mic_pro_g2.py b/solax/inverters/x3_mic_pro_g2.py index 17d2584..76df85f 100644 --- a/solax/inverters/x3_mic_pro_g2.py +++ b/solax/inverters/x3_mic_pro_g2.py @@ -66,8 +66,8 @@ def response_decoder(cls): "Grid 1 Frequency": (18, Units.HZ, div100), "Grid 2 Frequency": (19, Units.HZ, div100), "Grid 3 Frequency": (20, Units.HZ, div100), - "Run Mode": (21, Units.NONE), - "Run Mode Text": (21, Units.NONE, X3MicProG2._decode_run_mode), + # "Run Mode": (21, Units.NONE), + "Run Mode": (21, Units.NONE, X3MicProG2._decode_run_mode), "Total Yield": (pack_u16(22, 23), Total(Units.KWH), div10), "Daily Yield": (24, Units.KWH, div10), "Feed-in Power ": (pack_u16(72, 73), Units.W, to_signed32), diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index 576c8f8..5fa385a 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -286,8 +286,7 @@ "Grid 1 Frequency": 49.98, "Grid 2 Frequency": 49.99, "Grid 3 Frequency": 49.94, - "Run Mode": 2.0, - "Run Mode Text": "Normal", + "Run Mode": "Normal", "Total Yield": 795.7, "Daily Yield": 20.0, "Feed-in Power ": 2707.0,