[solax] Support for x1 mini inverter local connection (#16412)

Skeleton of the required classes for supporting X1 mini
Add the parser data

Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Konstantin Polihronov 2024-02-24 12:49:52 +02:00 committed by Ciprian Pascu
parent 087dedb4c6
commit a1c783dd9b
9 changed files with 310 additions and 34 deletions

View File

@ -19,6 +19,7 @@ import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
import org.openhab.binding.solax.internal.model.local.parsers.X1BoostAirMiniDataParser;
import org.openhab.binding.solax.internal.model.local.parsers.X1HybridG4DataParser;
import org.openhab.binding.solax.internal.model.local.parsers.X3HybridG4DataParser;
import org.openhab.binding.solax.internal.model.local.parsers.X3MicOrProG2DataParser;
@ -35,7 +36,7 @@ public enum InverterType {
X1_LX(1),
X_HYBRID(2),
X1_HYBRID_FIT(3),
X1_BOOST_AIR_MINI(4),
X1_BOOST_AIR_MINI(4, new X1BoostAirMiniDataParser()),
X3_HYBRID_FIT(5),
X3_20K_30K(6),
X3_MIC_PRO(7),

View File

@ -0,0 +1,95 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solax.internal.model.local;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
/**
* The {@link X1BoostAirMiniInverterData} is an implementation of the single phased inverter data interface for X1 Mini
* / X1 Air Mini or X1 Boost Mini inverter.
*
* @author Konstantin Polihronov - Initial contribution
*/
@NonNullByDefault
public class X1BoostAirMiniInverterData extends CommonLocalInverterData {
public X1BoostAirMiniInverterData(LocalConnectRawDataBean data) {
super(data);
}
@Override
public double getInverterVoltage() {
return (double) getData(0) / 10;
}
@Override
public double getInverterCurrent() {
return (double) getData(1) / 10;
}
@Override
public short getInverterOutputPower() {
return getData(2);
}
@Override
public double getPV1Voltage() {
return (double) getData(3) / 10;
}
@Override
public double getPV2Voltage() {
return (double) getData(4) / 10;
}
@Override
public double getPV1Current() {
return (double) getData(5) / 10;
}
@Override
public double getPV2Current() {
return (double) getData(6) / 10;
}
@Override
public short getPV1Power() {
return getData(7);
}
@Override
public short getPV2Power() {
return getData(8);
}
@Override
public double getInverterFrequency() {
return (double) getData(9) / 100;
}
@Override
public double getTotalEnergy() {
return (double) getData(11) / 10;
}
@Override
public double getTodayEnergy() {
return (double) getData(13) / 10;
}
@Override
public short getPowerUsage() {
return (short) Math.round((double) getData(43) / 10);
}
}

View File

@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solax.internal.model.local.parsers;
import static org.openhab.binding.solax.internal.SolaxBindingConstants.*;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
import org.openhab.binding.solax.internal.model.local.X1BoostAirMiniInverterData;
/**
* The {@link X1BoostAirMiniDataParser} is the implementation that parses raw data into a LocalInverterData for the
* X1 Mini / X1 Air Mini or X1 Boost Mini inverter.
*
* @author Konstantin Polihronov - Initial contribution
*/
@NonNullByDefault
public class X1BoostAirMiniDataParser implements RawDataParser {
private static final Set<String> X1_BOOST_AIR_MINI_SUPPORTED_CHANNELS = Set.of(CHANNEL_INVERTER_PV1_POWER,
CHANNEL_INVERTER_PV1_VOLTAGE, CHANNEL_INVERTER_PV1_CURRENT, CHANNEL_INVERTER_PV2_POWER,
CHANNEL_INVERTER_PV2_VOLTAGE, CHANNEL_INVERTER_PV2_CURRENT, CHANNEL_INVERTER_PV_TOTAL_POWER,
CHANNEL_INVERTER_PV_TOTAL_CURRENT, CHANNEL_TIMESTAMP, CHANNEL_RAW_DATA, CHANNEL_INVERTER_OUTPUT_POWER,
CHANNEL_INVERTER_OUTPUT_CURRENT, CHANNEL_INVERTER_OUTPUT_VOLTAGE, CHANNEL_INVERTER_OUTPUT_FREQUENCY,
CHANNEL_TOTAL_ENERGY, CHANNEL_TODAY_ENERGY, CHANNEL_POWER_USAGE);
@Override
public LocalInverterData getData(LocalConnectRawDataBean bean) {
return new X1BoostAirMiniInverterData(bean);
}
@Override
public Set<String> getSupportedChannels() {
return X1_BOOST_AIR_MINI_SUPPORTED_CHANNELS;
}
}

View File

@ -22,7 +22,7 @@ import org.openhab.binding.solax.internal.model.local.LocalInverterData;
import org.openhab.binding.solax.internal.model.local.X1HybridG4InverterData;
/**
* The {@link SinglePhaseDataParser} is the implementation that parses raw data into a SinglePhaseInverterData for the
* The {@link X1HybridG4DataParser} is the implementation that parses raw data into a LocalInverterData for the
* X1 Hybrid G4 inverter.
*
* @author Konstantin Polihronov - Initial contribution

View File

@ -22,7 +22,7 @@ import org.openhab.binding.solax.internal.model.local.LocalInverterData;
import org.openhab.binding.solax.internal.model.local.X3HybridG4InverterData;
/**
* The {@link X3HybridG4DataParser} is the implementation that parses raw data into a SinglePhaseInverterData for the
* The {@link X3HybridG4DataParser} is the implementation that parses raw data into a LocalInverterData for the
* X3 Hybrid G4 inverter.
*
* @author Konstantin Polihronov - Initial contribution

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solax.internal.local;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
import org.openhab.binding.solax.internal.model.InverterType;
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
/**
* The {@link AbstractParserTest} Abstract class defining the common logic for testing local connections to the various
* inverters and their parsers
*
* @author Konstantin Polihronov - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractParserTest {
@Test
public void testParser() {
LocalConnectRawDataBean bean = LocalConnectRawDataBean.fromJson(getRawData());
int type = bean.getType();
InverterType inverterType = InverterType.fromIndex(type);
assertEquals(getInverterType(), inverterType, "Inverter type not recognized properly");
RawDataParser parser = inverterType.getParser();
assertNotNull(parser);
Set<String> supportedChannels = parser.getSupportedChannels();
assertFalse(supportedChannels.isEmpty());
LocalInverterData data = parser.getData(bean);
assertParserSpecific(data);
}
protected abstract InverterType getInverterType();
protected abstract String getRawData();
protected abstract void assertParserSpecific(LocalInverterData data);
}

View File

@ -0,0 +1,79 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solax.internal.local;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solax.internal.model.InverterType;
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
/**
* The {@link TestX1BoostAirMiniDataParser} Simple test that tests for proper parsing against a real data from the
* inverter
*
* @author Konstantin Polihronov - Initial contribution
*/
@NonNullByDefault
public class TestX1BoostAirMiniDataParser extends AbstractParserTest {
private static final String RAW_DATA = """
{
sn:SR***,
ver:3.006.04,
type:4,
Data:[
2263,7,128,1519,0,9,0,138,0,5000,
2,15569,0,7,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,13,
0,4071,0,3456,0,0,0,0,0,0,
0,0,0,0,0,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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:[1.500,4,XM3A15IA669518,8,2.27,0.00,1.43,0.00,0.00,1]}
""";
@Override
protected String getRawData() {
return RAW_DATA;
}
@Override
protected void assertParserSpecific(LocalInverterData data) {
assertEquals("SR***", data.getWifiSerial());
assertEquals("3.006.04", data.getWifiVersion());
assertEquals(226.3, data.getInverterVoltage()); // [0]
assertEquals(0.7, data.getInverterCurrent()); // [1]
assertEquals(128, data.getInverterOutputPower()); // [2]
assertEquals(151.9, data.getPV1Voltage()); // [3]
assertEquals(0, data.getPV2Voltage()); // [4]
assertEquals(0.9, data.getPV1Current()); // [5]
assertEquals(0, data.getPV2Current()); // [6]
assertEquals(138, data.getPV1Power()); // [7]
assertEquals(0, data.getPV2Power()); // [8]
assertEquals(50, data.getInverterFrequency()); // [9]
assertEquals(1556.9, data.getTotalEnergy()); // [11]
assertEquals(0.7, data.getTodayEnergy()); // [13]
assertEquals(346, data.getPowerUsage()); // [43]
}
@Override
protected InverterType getInverterType() {
return InverterType.X1_BOOST_AIR_MINI;
}
}

View File

@ -12,14 +12,11 @@
*/
package org.openhab.binding.solax.internal.local;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
import org.openhab.binding.solax.internal.model.InverterType;
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
/**
* The {@link TestX1HybridG4Parser} Simple test that tests for proper parsing against a real data from the inverter
@ -27,7 +24,7 @@ import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
* @author Konstantin Polihronov - Initial contribution
*/
@NonNullByDefault
public class TestX1HybridG4Parser {
public class TestX1HybridG4Parser extends AbstractParserTest {
private static final String RAW_DATA = """
{
@ -43,17 +40,13 @@ public class TestX1HybridG4Parser {
Information:[7.500,15,H4752TI1063020,8,1.24,0.00,1.21,1.03,0.00,1]}
""";
@Test
public void testParser() {
LocalConnectRawDataBean bean = LocalConnectRawDataBean.fromJson(RAW_DATA);
int type = bean.getType();
InverterType inverterType = InverterType.fromIndex(type);
assertEquals(InverterType.X1_HYBRID_G4, inverterType, "Inverter type not recognized properly");
@Override
protected InverterType getInverterType() {
return InverterType.X1_HYBRID_G4;
}
RawDataParser parser = inverterType.getParser();
assertNotNull(parser);
LocalInverterData data = parser.getData(bean);
@Override
protected void assertParserSpecific(LocalInverterData data) {
assertEquals("SOME_SERIAL_NUMBER", data.getWifiSerial());
assertEquals("3.008.10", data.getWifiVersion());
@ -81,6 +74,11 @@ public class TestX1HybridG4Parser {
assertEquals(12, data.getFeedInPower()); // [32]
}
@Override
protected String getRawData() {
return RAW_DATA;
}
// Yield_Today: Data[13] / 10,
// Yield_Total: read32BitUnsigned(Data[11], Data[12]) / 10,
// PowerDc1: Data[8],

View File

@ -12,14 +12,11 @@
*/
package org.openhab.binding.solax.internal.local;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
import org.openhab.binding.solax.internal.model.InverterType;
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
/**
* The {@link TestX3HybridG4Parser} simple test that tests for proper parsing against a real data from the inverter
@ -27,9 +24,9 @@ import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
* @author Konstantin Polihronov - Initial contribution
*/
@NonNullByDefault
public class TestX3HybridG4Parser {
public class TestX3HybridG4Parser extends AbstractParserTest {
String rawData = """
private static final String RAW_DATA = """
{
sn:XYZ,
ver:3.005.01,
@ -50,17 +47,13 @@ public class TestX3HybridG4Parser {
}
""";
@Test
public void testParser() {
LocalConnectRawDataBean bean = LocalConnectRawDataBean.fromJson(rawData);
int type = bean.getType();
InverterType inverterType = InverterType.fromIndex(type);
assertEquals(InverterType.X3_HYBRID_G4, inverterType, "Inverter type not recognized properly");
@Override
protected InverterType getInverterType() {
return InverterType.X3_HYBRID_G4;
}
RawDataParser parser = inverterType.getParser();
assertNotNull(parser);
LocalInverterData data = parser.getData(bean);
@Override
protected void assertParserSpecific(LocalInverterData data) {
assertEquals("XYZ", data.getWifiSerial());
assertEquals("3.005.01", data.getWifiVersion());
@ -114,4 +107,9 @@ public class TestX3HybridG4Parser {
assertEquals(6.2, data.getTodayBatteryDischargeEnergy()); // [78] / 100
assertEquals(11, data.getTodayBatteryChargeEnergy()); // [79] / 100
}
@Override
protected String getRawData() {
return RAW_DATA;
}
}